/* * DISTRHO Plugin Framework (DPF) * Copyright (C) 2012-2021 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "DistrhoPluginInternal.hpp" #include "../extra/ScopedPointer.hpp" #include "travesty/audio_processor.h" #include "travesty/component.h" #include "travesty/edit_controller.h" #include "travesty/factory.h" #include #include START_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT static const writeMidiFunc writeMidiCallback = nullptr; #endif #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; #endif // -------------------------------------------------------------------------------------------------------------------- // custom v3_tuid compatible type typedef uint32_t dpf_tuid[4]; static_assert(sizeof(v3_tuid) == sizeof(dpf_tuid), "uid size mismatch"); // -------------------------------------------------------------------------------------------------------------------- // custom, constant uids related to DPF static constexpr const uint32_t dpf_id_entry = d_cconst('D', 'P', 'F', ' '); static constexpr const uint32_t dpf_id_clas = d_cconst('c', 'l', 'a', 's'); static constexpr const uint32_t dpf_id_comp = d_cconst('c', 'o', 'm', 'p'); static constexpr const uint32_t dpf_id_ctrl = d_cconst('c', 't', 'r', 'l'); static constexpr const uint32_t dpf_id_proc = d_cconst('p', 'r', 'o', 'c'); static constexpr const uint32_t dpf_id_view = d_cconst('v', 'i', 'e', 'w'); // -------------------------------------------------------------------------------------------------------------------- // plugin specific uids (values are filled in during plugin init) static dpf_tuid dpf_tuid_class = { dpf_id_entry, dpf_id_clas, 0, 0 }; static dpf_tuid dpf_tuid_component = { dpf_id_entry, dpf_id_comp, 0, 0 }; static dpf_tuid dpf_tuid_controller = { dpf_id_entry, dpf_id_ctrl, 0, 0 }; static dpf_tuid dpf_tuid_processor = { dpf_id_entry, dpf_id_proc, 0, 0 }; static dpf_tuid dpf_tuid_view = { dpf_id_entry, dpf_id_view, 0, 0 }; // -------------------------------------------------------------------------------------------------------------------- // Utility functions const char* tuid2str(const v3_tuid iid) { if (v3_tuid_match(iid, v3_funknown_iid)) return "{v3_funknown}"; if (v3_tuid_match(iid, v3_plugin_base_iid)) return "{v3_plugin_base}"; if (v3_tuid_match(iid, v3_plugin_factory_iid)) return "{v3_plugin_factory}"; if (v3_tuid_match(iid, v3_plugin_factory_2_iid)) return "{v3_plugin_factory_2}"; if (v3_tuid_match(iid, v3_plugin_factory_3_iid)) return "{v3_plugin_factory_3}"; if (v3_tuid_match(iid, v3_component_iid)) return "{v3_component}"; if (v3_tuid_match(iid, v3_bstream_iid)) return "{v3_bstream}"; if (v3_tuid_match(iid, v3_event_list_iid)) return "{v3_event_list}"; if (v3_tuid_match(iid, v3_param_value_queue_iid)) return "{v3_param_value_queue}"; if (v3_tuid_match(iid, v3_param_changes_iid)) return "{v3_param_changes}"; if (v3_tuid_match(iid, v3_process_context_requirements_iid)) return "{v3_process_context_requirements}"; if (v3_tuid_match(iid, v3_audio_processor_iid)) return "{v3_audio_processor}"; if (v3_tuid_match(iid, v3_component_handler_iid)) return "{v3_component_handler}"; if (v3_tuid_match(iid, v3_edit_controller_iid)) return "{v3_edit_controller}"; if (v3_tuid_match(iid, v3_plugin_view_iid)) return "{v3_plugin_view}"; if (v3_tuid_match(iid, v3_plugin_frame_iid)) return "{v3_plugin_frame}"; if (v3_tuid_match(iid, v3_plugin_view_content_scale_steinberg_iid)) return "{v3_plugin_view_content_scale_steinberg}"; if (v3_tuid_match(iid, v3_plugin_view_parameter_finder_iid)) return "{v3_plugin_view_parameter_finder}"; if (std::memcmp(iid, dpf_tuid_class, sizeof(dpf_tuid)) == 0) return "{dpf_tuid_class}"; if (std::memcmp(iid, dpf_tuid_component, sizeof(dpf_tuid)) == 0) return "{dpf_tuid_component}"; if (std::memcmp(iid, dpf_tuid_controller, sizeof(dpf_tuid)) == 0) return "{dpf_tuid_controller}"; if (std::memcmp(iid, dpf_tuid_processor, sizeof(dpf_tuid)) == 0) return "{dpf_tuid_processor}"; if (std::memcmp(iid, dpf_tuid_view, sizeof(dpf_tuid)) == 0) return "{dpf_tuid_view}"; static char buf[46]; std::snprintf(buf, sizeof(buf), "{0x%08X,0x%08X,0x%08X,0x%08X}", (uint32_t)d_cconst(iid[ 0], iid[ 1], iid[ 2], iid[ 3]), (uint32_t)d_cconst(iid[ 4], iid[ 5], iid[ 6], iid[ 7]), (uint32_t)d_cconst(iid[ 8], iid[ 9], iid[10], iid[11]), (uint32_t)d_cconst(iid[12], iid[13], iid[14], iid[15])); return buf; } void strncpy(char* const dst, const char* const src, const size_t size) { DISTRHO_SAFE_ASSERT_RETURN(size > 0,); if (const size_t len = std::min(std::strlen(src), size-1U)) { std::memcpy(dst, src, len); dst[len] = '\0'; } else { dst[0] = '\0'; } } void strncpy_16from8(int16_t* const dst, const char* const src, const size_t size) { DISTRHO_SAFE_ASSERT_RETURN(size > 0,); if (const size_t len = std::min(std::strlen(src), size-1U)) { for (size_t i=0; i= 0x80) continue; dst[i] = src[i]; } dst[len] = 0; } else { dst[0] = 0; } } // -------------------------------------------------------------------------------------------------------------------- class PluginVst3 { public: PluginVst3() : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback) { } // ---------------------------------------------------------------------------------------------------------------- // stuff for vst3 interfaces uint32_t getParameterCount() const noexcept { return fPlugin.getParameterCount(); } void getParameterInfo(const uint32_t index, v3_param_info* const info) const noexcept { // set up flags int32_t flags = 0; const auto desig = fPlugin.getParameterDesignation(index); const auto hints = fPlugin.getParameterHints(index); const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); switch (desig) { case kParameterDesignationNull: break; case kParameterDesignationBypass: flags |= V3_PARAM_IS_BYPASS; break; } if (hints & kParameterIsAutomable) flags |= V3_PARAM_CAN_AUTOMATE; if (hints & kParameterIsOutput) flags |= V3_PARAM_READ_ONLY; // TODO V3_PARAM_IS_LIST // set up step_count int32_t step_count = 0; if (hints & kParameterIsBoolean) step_count = 1; if ((hints & kParameterIsInteger) && ranges.max - ranges.min > 1) step_count = ranges.max - ranges.min - 1; std::memset(info, 0, sizeof(v3_param_info)); info->param_id = index; info->flags = flags; info->step_count = step_count; info->default_normalised_value = ranges.getNormalizedValue(ranges.def); // int32_t unit_id; strncpy_16from8(info->title, fPlugin.getParameterName(index), 128); strncpy_16from8(info->short_title, fPlugin.getParameterShortName(index), 128); strncpy_16from8(info->units, fPlugin.getParameterUnit(index), 128); } double getNormalizedParameterValue(const uint32_t index) { const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); return ranges.getNormalizedValue(fPlugin.getParameterValue(index)); } void setNormalizedParameterValue(const uint32_t index, const double value) { const uint32_t hints = fPlugin.getParameterHints(index); const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); float realValue = ranges.getUnnormalizedValue(value); if (hints & kParameterIsBoolean) { const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; realValue = realValue > midRange ? ranges.max : ranges.min; } if (hints & kParameterIsInteger) { realValue = std::round(realValue); } fPlugin.setParameterValue(index, realValue); } private: // Plugin PluginExporter fPlugin; // VST3 stuff // TODO #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST bool requestParameterValueChange(uint32_t, float) { // TODO return true; } static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) { return ((PluginVst3*)ptr)->requestParameterValueChange(index, value); } #endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT bool writeMidi(const MidiEvent& midiEvent) { // TODO return true; } static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) { return ((PluginVst3*)ptr)->writeMidi(midiEvent); } #endif }; // -------------------------------------------------------------------------------------------------------------------- // Dummy plugin to get data from static ScopedPointer gPluginInfo; // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view struct v3_plugin_view_cpp : v3_funknown { v3_plugin_view view; }; struct dpf_plugin_view : v3_plugin_view_cpp { dpf_plugin_view() { static const uint8_t* kSupportedFactories[] = { v3_funknown_iid, v3_plugin_view_iid }; // ------------------------------------------------------------------------------------------------------------ // v3_funknown query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { d_stdout("dpf_plugin_view::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 41, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); for (const uint8_t* factory_iid : kSupportedFactories) { if (v3_tuid_match(factory_iid, iid)) { *iface = self; return V3_OK; } } return V3_NO_INTERFACE; }; // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_plugin_view::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); return 1; }; unref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_plugin_view::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); return 0; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_view view.is_platform_type_supported = []V3_API(void* self, const char* platform_type) -> v3_result { d_stdout("dpf_plugin_view::is_platform_type_supported => %s | %p %s", __PRETTY_FUNCTION__ + 41, self, platform_type); return V3_OK; }; view.attached = []V3_API(void* self, void* parent, const char* platform_type) -> v3_result { d_stdout("dpf_plugin_view::attached => %s | %p %p %s", __PRETTY_FUNCTION__ + 41, self, parent, platform_type); return V3_OK; }; view.removed = []V3_API(void* self) -> v3_result { d_stdout("dpf_plugin_view::removed => %s | %p", __PRETTY_FUNCTION__ + 41, self); return V3_OK; }; view.on_wheel = []V3_API(void* self, float distance) -> v3_result { d_stdout("dpf_plugin_view::on_wheel => %s | %p %f", __PRETTY_FUNCTION__ + 41, self, distance); return V3_OK; }; view.on_key_down = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result { d_stdout("dpf_plugin_view::on_key_down => %s | %p %i %i %i", __PRETTY_FUNCTION__ + 41, self, key_char, key_code, modifiers); return V3_OK; }; view.on_key_up = []V3_API(void* self, int16_t key_char, int16_t key_code, int16_t modifiers) -> v3_result { d_stdout("dpf_plugin_view::on_key_up => %s | %p %i %i %i", __PRETTY_FUNCTION__ + 41, self, key_char, key_code, modifiers); return V3_OK; }; view.get_size = []V3_API(void* self, v3_view_rect*) -> v3_result { d_stdout("dpf_plugin_view::get_size => %s | %p", __PRETTY_FUNCTION__ + 41, self); return V3_OK; }; view.set_size = []V3_API(void* self, v3_view_rect*) -> v3_result { d_stdout("dpf_plugin_view::set_size => %s | %p", __PRETTY_FUNCTION__ + 41, self); return V3_OK; }; view.on_focus = []V3_API(void* self, v3_bool state) -> v3_result { d_stdout("dpf_plugin_view::on_focus => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); return V3_OK; }; view.set_frame = []V3_API(void* self, v3_plugin_frame*) -> v3_result { d_stdout("dpf_plugin_view::set_frame => %s | %p", __PRETTY_FUNCTION__ + 41, self); return V3_OK; }; view.can_resize = []V3_API(void* self) -> v3_result { d_stdout("dpf_plugin_view::can_resize => %s | %p", __PRETTY_FUNCTION__ + 41, self); return V3_OK; }; view.check_size_constraint = []V3_API(void* self, v3_view_rect*) -> v3_result { d_stdout("dpf_plugin_view::check_size_constraint => %s | %p", __PRETTY_FUNCTION__ + 41, self); return V3_OK; }; } }; // -------------------------------------------------------------------------------------------------------------------- // dpf_edit_controller struct v3_edit_controller_cpp : v3_funknown { v3_plugin_base base; v3_edit_controller controller; }; struct dpf_edit_controller : v3_edit_controller_cpp { ScopedPointer& vst3; dpf_edit_controller(ScopedPointer& v) : vst3(v) { static const uint8_t* kSupportedFactories[] = { v3_funknown_iid, v3_edit_controller_iid }; // ------------------------------------------------------------------------------------------------------------ // v3_funknown query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { d_stdout("dpf_edit_controller::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 53, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); for (const uint8_t* factory_iid : kSupportedFactories) { if (v3_tuid_match(factory_iid, iid)) { *iface = self; return V3_OK; } } return V3_NO_INTERFACE; }; // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_edit_controller::ref => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 1; }; unref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_edit_controller::unref => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 0; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_base base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown *context) -> v3_result { d_stdout("dpf_edit_controller::initialise => %s | %p %p", __PRETTY_FUNCTION__ + 53, self, context); return V3_OK; }; base.terminate = []V3_API(void* self) -> v3_result { d_stdout("dpf_edit_controller::terminate => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_OK; }; // ------------------------------------------------------------------------------------------------------------ // v3_edit_controller controller.set_component_state = []V3_API(void* self, v3_bstream*) -> v3_result { d_stdout("dpf_edit_controller::set_component_state => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_OK; }; controller.set_state = []V3_API(void* self, v3_bstream*) -> v3_result { d_stdout("dpf_edit_controller::set_state => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_OK; }; controller.get_state = []V3_API(void* self, v3_bstream*) -> v3_result { d_stdout("dpf_edit_controller::get_state => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_OK; }; controller.get_parameter_count = []V3_API(void* self) -> int32_t { d_stdout("dpf_edit_controller::get_parameter_count => %s | %p", __PRETTY_FUNCTION__ + 53, self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); PluginVst3* const vst3 = controller->vst3.get(); DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); return vst3->getParameterCount(); }; controller.get_parameter_info = []V3_API(void* self, int32_t param_idx, v3_param_info* param_info) -> v3_result { d_stdout("dpf_edit_controller::get_parameter_info => %s | %p %i", __PRETTY_FUNCTION__ + 53, self, param_idx); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); DISTRHO_SAFE_ASSERT_RETURN(param_idx >= 0, V3_INVALID_ARG); PluginVst3* const vst3 = controller->vst3.get(); DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); const uint32_t uidx = static_cast(param_idx); DISTRHO_SAFE_ASSERT_RETURN(uidx < vst3->getParameterCount(), V3_INVALID_ARG); vst3->getParameterInfo(uidx, param_info); return V3_OK; }; controller.get_parameter_string_for_value = []V3_API(void* self, v3_param_id index, double normalised, v3_str_128 output) -> v3_result { d_stdout("dpf_edit_controller::get_parameter_string_for_value => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); const ParameterRanges& ranges(gPluginInfo->getParameterRanges(index)); const float realvalue = ranges.getUnnormalizedValue(normalised); char buf[24]; sprintf(buf, "%f", realvalue); strncpy_16from8(output, buf, 128); return V3_OK; }; controller.get_parameter_value_for_string = []V3_API(void* self, v3_param_id, int16_t* input, double* output) -> v3_result { d_stdout("dpf_edit_controller::get_parameter_value_for_string => %s | %p %p %p", __PRETTY_FUNCTION__ + 53, self, input, output); return V3_NOT_IMPLEMENTED; }; controller.normalised_parameter_to_plain = []V3_API(void* self, v3_param_id, double normalised) -> double { d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); return normalised; }; controller.plain_parameter_to_normalised = []V3_API(void* self, v3_param_id, double normalised) -> double { d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); return normalised; }; controller.get_parameter_normalised = []V3_API(void* self, v3_param_id param_idx) -> double { d_stdout("dpf_edit_controller::get_parameter_normalised => %s | %p", __PRETTY_FUNCTION__ + 53, self); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, 0.0); PluginVst3* const vst3 = controller->vst3.get(); DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0.0); DISTRHO_SAFE_ASSERT_RETURN(param_idx < vst3->getParameterCount(), 0.0); return vst3->getNormalizedParameterValue(param_idx); }; controller.set_parameter_normalised = []V3_API(void* self, v3_param_id param_idx, double normalised) -> v3_result { d_stdout("dpf_edit_controller::set_parameter_normalised => %s | %p %f", __PRETTY_FUNCTION__ + 53, self, normalised); dpf_edit_controller* const controller = *(dpf_edit_controller**)self; DISTRHO_SAFE_ASSERT_RETURN(controller != nullptr, V3_INVALID_ARG); PluginVst3* const vst3 = controller->vst3.get(); DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALISED); DISTRHO_SAFE_ASSERT_RETURN(param_idx < vst3->getParameterCount(), V3_INVALID_ARG); vst3->setNormalizedParameterValue(param_idx, normalised); return V3_OK; }; controller.set_component_handler = []V3_API(void* self, v3_component_handler**) -> v3_result { d_stdout("dpf_edit_controller::set_component_handler => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_NOT_IMPLEMENTED; }; controller.create_view = []V3_API(void* self, const char* name) -> v3_plug_view** { d_stdout("dpf_edit_controller::create_view => %s | %p %s", __PRETTY_FUNCTION__ + 53, self, name); return nullptr; }; } }; // -------------------------------------------------------------------------------------------------------------------- // dpf_audio_processor struct v3_audio_processor_cpp : v3_funknown { v3_audio_processor processor; }; struct dpf_audio_processor : v3_audio_processor_cpp { dpf_audio_processor() { static const uint8_t* kSupportedFactories[] = { v3_funknown_iid, v3_audio_processor_iid }; // ------------------------------------------------------------------------------------------------------------ // v3_funknown query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { d_stdout("dpf_audio_processor::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 53, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); for (const uint8_t* factory_iid : kSupportedFactories) { if (v3_tuid_match(factory_iid, iid)) { *iface = self; return V3_OK; } } return V3_NO_INTERFACE; }; // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_audio_processor::ref => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 1; }; unref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_audio_processor::unref => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 0; }; // ------------------------------------------------------------------------------------------------------------ // v3_audio_processor processor.set_bus_arrangements = []V3_API(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, v3_speaker_arrangement* outputs, int32_t num_outputs) -> v3_result { d_stdout("dpf_audio_processor::set_bus_arrangements => %s | %p %p %i %p %i", __PRETTY_FUNCTION__ + 53, self, inputs, num_inputs, outputs, num_outputs); return V3_OK; }; processor.get_bus_arrangement = []V3_API(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*) -> v3_result { d_stdout("dpf_audio_processor::get_bus_arrangement => %s | %p %i %i", __PRETTY_FUNCTION__ + 53, self, bus_direction, idx); return V3_OK; }; processor.can_process_sample_size = []V3_API(void* self, int32_t symbolic_sample_size) -> v3_result { d_stdout("dpf_audio_processor::can_process_sample_size => %s | %p %i", __PRETTY_FUNCTION__ + 53, self, symbolic_sample_size); return V3_OK; }; processor.get_latency_samples = []V3_API(void* self) -> uint32_t { d_stdout("dpf_audio_processor::get_latency_samples => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 0; }; processor.setup_processing = []V3_API(void* self, v3_process_setup* setup) -> v3_result { d_stdout("dpf_audio_processor::setup_processing => %s | %p", __PRETTY_FUNCTION__ + 53, self); d_lastBufferSize = setup->max_block_size; d_lastSampleRate = setup->sample_rate; return V3_OK; }; processor.set_processing = []V3_API(void* self, v3_bool state) -> v3_result { d_stdout("dpf_audio_processor::set_processing => %s | %p %u", __PRETTY_FUNCTION__ + 53, self, state); return V3_OK; }; processor.process = []V3_API(void* self, v3_process_data*) -> v3_result { d_stdout("dpf_audio_processor::process => %s | %p", __PRETTY_FUNCTION__ + 53, self); return V3_OK; }; processor.get_tail_samples = []V3_API(void* self) -> uint32_t { d_stdout("dpf_audio_processor::get_tail_samples => %s | %p", __PRETTY_FUNCTION__ + 53, self); return 0; }; } }; // -------------------------------------------------------------------------------------------------------------------- // dpf_component struct v3_component_cpp : v3_funknown { v3_plugin_base base; v3_component comp; }; struct dpf_component : v3_component_cpp { std::atomic refcounter; ScopedPointer processor; ScopedPointer controller; ScopedPointer vst3; dpf_component() : refcounter(1) { static const uint8_t* kSupportedBaseFactories[] = { v3_funknown_iid, v3_plugin_base_iid, v3_component_iid }; // ------------------------------------------------------------------------------------------------------------ // v3_funknown query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { d_stdout("dpf_component::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 41, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); for (const uint8_t* factory_iid : kSupportedBaseFactories) { if (v3_tuid_match(factory_iid, iid)) { *iface = self; return V3_OK; } } if (v3_tuid_match(v3_audio_processor_iid, iid)) { dpf_component* const component = *(dpf_component**)self; if (component->processor == nullptr) component->processor = new dpf_audio_processor(); *iface = &component->processor; return V3_OK; } if (v3_tuid_match(v3_edit_controller_iid, iid)) { dpf_component* const component = *(dpf_component**)self; if (component->controller == nullptr) component->controller = new dpf_edit_controller(component->vst3); *iface = &component->controller; return V3_OK; } return V3_NO_INTERFACE; }; // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_component::ref => %s | %p", __PRETTY_FUNCTION__ + 41, self); dpf_component* const component = *(dpf_component**)self; return ++component->refcounter; }; unref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_component::unref => %s | %p", __PRETTY_FUNCTION__ + 41, self); dpf_component* const component = *(dpf_component**)self; if (const int refcounter = --component->refcounter) return refcounter; // delete component; *(dpf_component**)self = nullptr; return 0; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_base base.initialise = []V3_API(void* self, struct v3_plugin_base::v3_funknown* context) -> v3_result { d_stdout("dpf_component::initialise => %s | %p %p", __PRETTY_FUNCTION__ + 41, self, context); dpf_component* const component = *(dpf_component**)self; // default early values if (d_lastBufferSize == 0) d_lastBufferSize = 2048; if (d_lastSampleRate <= 0.0) d_lastSampleRate = 44100.0; component->vst3 = new PluginVst3(); return V3_OK; }; base.terminate = []V3_API(void* self) -> v3_result { d_stdout("dpf_component::terminate => %s | %p", __PRETTY_FUNCTION__ + 41, self); dpf_component* const component = *(dpf_component**)self; component->vst3 = nullptr; return V3_OK; }; // ------------------------------------------------------------------------------------------------------------ // v3_component comp.get_controller_class_id = []V3_API(void* self, v3_tuid class_id) -> v3_result { d_stdout("dpf_component::get_controller_class_id => %s | %p %s", __PRETTY_FUNCTION__ + 41, self, tuid2str(class_id)); return V3_NOT_IMPLEMENTED; }; comp.set_io_mode = []V3_API(void* self, int32_t io_mode) -> v3_result { d_stdout("dpf_component::set_io_mode => %s | %p %i", __PRETTY_FUNCTION__ + 41, self, io_mode); return V3_INTERNAL_ERR; }; comp.get_bus_count = []V3_API(void* self, int32_t media_type, int32_t bus_direction) -> int32_t { d_stdout("dpf_component::get_bus_count => %s | %p %i %i", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction); return 0; }; comp.get_bus_info = []V3_API(void* self, int32_t media_type, int32_t bus_direction, int32_t bus_idx, v3_bus_info* bus_info) -> v3_result { d_stdout("dpf_component::get_bus_info => %s | %p %i %i %i %p", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, bus_info); return V3_INTERNAL_ERR; }; comp.get_routing_info = []V3_API(void* self, v3_routing_info* input, v3_routing_info* output) -> v3_result { d_stdout("dpf_component::get_routing_info => %s | %p %p %p", __PRETTY_FUNCTION__ + 41, self, input, output); return V3_INTERNAL_ERR; }; comp.activate_bus = []V3_API(void* self, int32_t media_type, int32_t bus_direction, int32_t bus_idx, v3_bool state) -> v3_result { d_stdout("dpf_component::activate_bus => %s | %p %i %i %i %u", __PRETTY_FUNCTION__ + 41, self, media_type, bus_direction, bus_idx, state); return V3_INTERNAL_ERR; }; comp.set_active = []V3_API(void* self, v3_bool state) -> v3_result { d_stdout("dpf_component::set_active => %s | %p %u", __PRETTY_FUNCTION__ + 41, self, state); return V3_INTERNAL_ERR; }; comp.set_state = []V3_API(void* self, v3_bstream**) -> v3_result { d_stdout("dpf_component::set_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); return V3_INTERNAL_ERR; }; comp.get_state = []V3_API(void* self, v3_bstream**) -> v3_result { d_stdout("dpf_component::get_state => %s | %p", __PRETTY_FUNCTION__ + 41, self); return V3_INTERNAL_ERR; }; } }; // -------------------------------------------------------------------------------------------------------------------- // dpf_factory struct v3_plugin_factory_cpp : v3_funknown { v3_plugin_factory v1; v3_plugin_factory_2 v2; v3_plugin_factory_3 v3; }; struct dpf_factory : v3_plugin_factory_cpp { std::vector components; dpf_factory() { static const uint8_t* kSupportedFactories[] = { v3_funknown_iid, v3_plugin_factory_iid, v3_plugin_factory_2_iid, v3_plugin_factory_3_iid }; // ------------------------------------------------------------------------------------------------------------ // v3_funknown query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result { d_stdout("dpf_factory::query_interface => %s | %p %s %p", __PRETTY_FUNCTION__ + 37, self, tuid2str(iid), iface); *iface = NULL; DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); for (const uint8_t* factory_iid : kSupportedFactories) { if (v3_tuid_match(factory_iid, iid)) { *iface = self; return V3_OK; } } return V3_NO_INTERFACE; }; // we only support 1 plugin per binary, so don't have to care here ref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_factory::ref => %s | %p", __PRETTY_FUNCTION__ + 37, self); return 1; }; unref = []V3_API(void* self) -> uint32_t { d_stdout("dpf_factory::unref => %s | %p", __PRETTY_FUNCTION__ + 37, self); return 0; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory v1.get_factory_info = []V3_API(void* self, struct v3_factory_info* const info) -> v3_result { d_stdout("dpf_factory::get_factory_info => %s | %p %p", __PRETTY_FUNCTION__ + 37, self, info); DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); DISTRHO_NAMESPACE::strncpy(info->url, gPluginInfo->getHomePage(), sizeof(info->url)); DISTRHO_NAMESPACE::strncpy(info->email, "", sizeof(info->email)); // TODO return V3_OK; }; v1.num_classes = []V3_API(void* self) -> int32_t { d_stdout("dpf_factory::num_classes => %s | %p", __PRETTY_FUNCTION__ + 37, self); return 1; }; v1.get_class_info = []V3_API(void* self, int32_t idx, struct v3_class_info* const info) -> v3_result { d_stdout("dpf_factory::get_class_info => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_NO_INTERFACE); memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo->getName(), sizeof(info->name)); return V3_OK; }; v1.create_instance = []V3_API(void* self, const v3_tuid class_id, const v3_tuid iid, void** instance) -> v3_result { d_stdout("dpf_factory::create_instance => %s | %p %s %s %p", __PRETTY_FUNCTION__ + 37, self, tuid2str(class_id), tuid2str(iid), instance); DISTRHO_SAFE_ASSERT_RETURN(v3_tuid_match(class_id, *(v3_tuid*)&dpf_tuid_class) && v3_tuid_match(iid, v3_component_iid), V3_NO_INTERFACE); dpf_factory* const factory = *(dpf_factory**)self; dpf_component* const component = new dpf_component(); factory->components.push_back(component); *instance = &factory->components.back(); return V3_OK; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory_2 v2.get_class_info_2 = []V3_API(void* self, int32_t idx, struct v3_class_info_2* info) -> v3_result { d_stdout("dpf_factory::get_class_info_2 => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); DISTRHO_NAMESPACE::strncpy(info->name, gPluginInfo->getName(), sizeof(info->name)); info->class_flags = 0; DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO DISTRHO_NAMESPACE::strncpy(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); std::snprintf(info->version, sizeof(info->version), "%u", gPluginInfo->getVersion()); // TODO DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", sizeof(info->sdk_version)); // TESTING use "VST 3.7" ? return V3_OK; }; // ------------------------------------------------------------------------------------------------------------ // v3_plugin_factory_3 v3.get_class_info_utf16 = []V3_API(void* self, int32_t idx, struct v3_class_info_3* info) -> v3_result { d_stdout("dpf_factory::get_class_info_utf16 => %s | %p %i %p", __PRETTY_FUNCTION__ + 37, self, idx, info); memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid)); info->cardinality = 0x7FFFFFFF; DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", sizeof(info->category)); DISTRHO_NAMESPACE::strncpy_16from8(info->name, gPluginInfo->getName(), sizeof(info->name)); info->class_flags = 0; DISTRHO_NAMESPACE::strncpy(info->sub_categories, "", sizeof(info->sub_categories)); // TODO DISTRHO_NAMESPACE::strncpy_16from8(info->vendor, gPluginInfo->getMaker(), sizeof(info->vendor)); // DISTRHO_NAMESPACE::snprintf16(info->version, sizeof(info->version)/sizeof(info->version[0]), "%u", gPluginInfo->getVersion()); // TODO DISTRHO_NAMESPACE::strncpy_16from8(info->sdk_version, "Travesty", sizeof(info->sdk_version)); // TESTING use "VST 3.7" ? return V3_OK; }; v3.set_host_context = []V3_API (void* self, struct v3_funknown* host) -> v3_result { d_stdout("dpf_factory::set_host_context => %s | %p %p", __PRETTY_FUNCTION__ + 37, self, host); return V3_NOT_IMPLEMENTED; }; } }; END_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- // VST3 entry point DISTRHO_PLUGIN_EXPORT const void* GetPluginFactory(void); const void* GetPluginFactory(void) { USE_NAMESPACE_DISTRHO; static const dpf_factory factory; static const v3_funknown* const factoryptr = &factory; return &factoryptr; } // -------------------------------------------------------------------------------------------------------------------- // OS specific module load #if defined(DISTRHO_OS_MAC) # define ENTRYFNNAME bundleEntry # define EXITFNNAME bundleExit #elif defined(DISTRHO_OS_WINDOWS) # define ENTRYFNNAME InitDll # define EXITFNNAME ExitDll #else # define ENTRYFNNAME ModuleEntry # define EXITFNNAME ModuleExit #endif DISTRHO_PLUGIN_EXPORT bool ENTRYFNNAME(void*); bool ENTRYFNNAME(void*) { USE_NAMESPACE_DISTRHO; if (gPluginInfo == nullptr) { d_lastBufferSize = 512; d_lastSampleRate = 44100.0; gPluginInfo = new PluginExporter(nullptr, nullptr, nullptr); d_lastBufferSize = 0; d_lastSampleRate = 0.0; dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2] = dpf_tuid_processor[2] = dpf_tuid_view[2] = gPluginInfo->getUniqueId(); } return true; } DISTRHO_PLUGIN_EXPORT bool EXITFNNAME(void); bool EXITFNNAME(void) { USE_NAMESPACE_DISTRHO; gPluginInfo = nullptr; return true; } #undef ENTRYFNNAME #undef EXITFNNAME // --------------------------------------------------------------------------------------------------------------------