/* * DISTHRO Plugin Toolkit (DPT) * Copyright (C) 2012 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For a full copy of the license see the GPL.txt file */ #if defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI) #include "DistrhoPluginInternal.h" #ifdef DISTRHO_PLUGIN_TARGET_DSSI # include "dssi/dssi.h" #else # include "ladspa/ladspa.h" # if DISTRHO_PLUGIN_IS_SYNTH # error Cannot build synth plugin with LADSPA # endif # if DISTRHO_PLUGIN_WANT_STATE # warning LADSPA cannot handle states # endif #endif #include typedef LADSPA_Data* LADSPA_DataPtr; typedef std::vector LADSPA_DataVector; typedef std::vector LADSPA_DataPtrVector; // ------------------------------------------------- START_NAMESPACE_DISTRHO class PluginLadspaDssi { public: PluginLadspaDssi() : lastBufferSize(d_lastBufferSize), lastSampleRate(d_lastSampleRate) { for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) portAudioIns[i] = nullptr; for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) portAudioOuts[i] = nullptr; for (uint32_t i=0; i < plugin.parameterCount(); i++) { portControls.push_back(nullptr); lastControlValues.push_back(plugin.parameterValue(i)); } #if DISTRHO_PLUGIN_WANT_LATENCY portLatency = nullptr; #endif #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI portSampleRate = nullptr; #endif } ~PluginLadspaDssi() { portControls.clear(); lastControlValues.clear(); } void ladspa_activate() { plugin.activate(); updateParameterOutputs(); } void ladspa_deactivate() { plugin.deactivate(); } void ladspa_connect_port(unsigned long port, LADSPA_DataPtr dataLocation) { unsigned long i, index = 0; for (i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++) { if (port == index++) { portAudioIns[i] = dataLocation; return; } } for (i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++) { if (port == index++) { portAudioOuts[i] = dataLocation; return; } } #if DISTRHO_PLUGIN_WANT_LATENCY if (port == index++) { portLatency = dataLocation; return; } #endif #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI if (port == index++) { portSampleRate = dataLocation; return; } #endif for (i=0; i < plugin.parameterCount(); i++) { if (port == index++) { portControls[i] = dataLocation; return; } } } #ifdef DISTRHO_PLUGIN_TARGET_DSSI # if DISTRHO_PLUGIN_WANT_STATE char* dssi_configure(const char* key, const char* value) { if (strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, strlen(DSSI_RESERVED_CONFIGURE_PREFIX) == 0)) return nullptr; if (strncmp(key, DSSI_GLOBAL_CONFIGURE_PREFIX, strlen(DSSI_GLOBAL_CONFIGURE_PREFIX) == 0)) return nullptr; plugin.setState(key, value); return nullptr; } # endif # if DISTRHO_PLUGIN_WANT_PROGRAMS const DSSI_Program_Descriptor* dssi_get_program(unsigned long index) { if (index >= plugin.programCount()) return nullptr; static DSSI_Program_Descriptor desc; desc.Bank = index / 128; desc.Program = index % 128; desc.Name = plugin.programName(index); return &desc; } void dssi_select_program(unsigned long bank, unsigned long program) { const unsigned long realProgram = bank * 128 + program; if (realProgram >= plugin.programCount()) return; plugin.setProgram(realProgram); // Update parameters for (uint32_t i=0; i < plugin.parameterCount(); i++) { if (! plugin.parameterIsOutput(i)) { lastControlValues[i] = plugin.parameterValue(i); if (portControls[i]) *portControls[i] = lastControlValues[i]; } } } # endif void ladspa_run(unsigned long bufferSize) { dssi_run_synth(bufferSize, nullptr, 0); } void dssi_run_synth(unsigned long bufferSize, snd_seq_event_t* events, unsigned long eventCount) #else void ladspa_run(unsigned long bufferSize) #endif { // Check for updated bufferSize if (bufferSize != lastBufferSize) { lastBufferSize = bufferSize; d_lastBufferSize = bufferSize; plugin.setBufferSize(bufferSize, true); } // Check for updated parameters float curValue; for (uint32_t i=0; i < plugin.parameterCount(); i++) { curValue = *portControls[i]; if (lastControlValues[i] != curValue && ! plugin.parameterIsOutput(i)) { lastControlValues[i] = curValue; plugin.setParameterValue(i, curValue); } } #ifdef DISTRHO_PLUGIN_TARGET_DSSI # if DISTRHO_PLUGIN_IS_SYNTH // Get MIDI Events uint32_t midiEventCount = 0; for (uint32_t i=0, j=0; i < eventCount && midiEventCount < MAX_MIDI_EVENTS; i++) { snd_seq_event_t* event = &events[i]; memset(&midiEvents[midiEventCount], 0, sizeof(MidiEvent)); if (event->type == SND_SEQ_EVENT_NOTEON) { j = midiEventCount++; midiEvents[j].frame = event->time.tick; midiEvents[j].buffer[0] = 0x90 + event->data.note.channel; midiEvents[j].buffer[1] = event->data.note.note; midiEvents[j].buffer[2] = event->data.note.velocity; } else if (event->type == SND_SEQ_EVENT_NOTEOFF) { j = midiEventCount++; midiEvents[j].frame = event->time.tick; midiEvents[j].buffer[0] = 0x80 + event->data.note.channel; midiEvents[j].buffer[1] = event->data.note.note; } else if (event->type == SND_SEQ_EVENT_KEYPRESS) { j = midiEventCount++; midiEvents[j].frame = event->time.tick; midiEvents[j].buffer[0] = 0xA0 + event->data.note.channel; midiEvents[j].buffer[1] = event->data.note.note; midiEvents[j].buffer[2] = event->data.note.velocity; } else if (event->type == SND_SEQ_EVENT_CONTROLLER) { j = midiEventCount++; midiEvents[j].frame = event->time.tick; midiEvents[j].buffer[0] = 0xB0 + event->data.control.channel; midiEvents[j].buffer[1] = event->data.control.param; midiEvents[j].buffer[2] = event->data.control.value; } else if (event->type == SND_SEQ_EVENT_CHANPRESS) { j = midiEventCount++; midiEvents[j].frame = event->time.tick; midiEvents[j].buffer[0] = 0xD0 + event->data.control.channel; midiEvents[j].buffer[1] = event->data.control.value; } else if (event->type == SND_SEQ_EVENT_PITCHBEND) { // TODO //j = midiEventCount++; //midiEvents[j].frame = event->time.tick; //midiEvents[j].buffer[0] = 0xE0 + event->data.control.channel; //midiEvents[j].buffer[1] = 0; //midiEvents[j].buffer[2] = 0; } } # else // unused (void)events; (void)eventCount; # endif #endif // Run plugin for this cycle #if DISTRHO_PLUGIN_IS_SYNTH plugin.run(portAudioIns, portAudioOuts, bufferSize, midiEventCount, midiEvents); #else plugin.run(portAudioIns, portAudioOuts, bufferSize, 0, nullptr); #endif updateParameterOutputs(); } private: PluginInternal plugin; // LADSPA ports LADSPA_DataPtr portAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; LADSPA_DataPtr portAudioOuts[DISTRHO_PLUGIN_NUM_INPUTS]; LADSPA_DataPtrVector portControls; #if DISTRHO_PLUGIN_WANT_LATENCY LADSPA_DataPtr portLatency; #endif #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI LADSPA_DataPtr portSampleRate; #endif // Temporary data unsigned long lastBufferSize; const double lastSampleRate; LADSPA_DataVector lastControlValues; #if DISTRHO_PLUGIN_IS_SYNTH MidiEvent midiEvents[MAX_MIDI_EVENTS]; #endif void updateParameterOutputs() { for (uint32_t i=0; i < plugin.parameterCount(); i++) { if (plugin.parameterIsOutput(i)) { lastControlValues[i] = plugin.parameterValue(i); if (portControls[i]) *portControls[i] = lastControlValues[i]; } } #if DISTRHO_PLUGIN_WANT_LATENCY if (portLatency) *portLatency = plugin.latency(); #endif #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI if (portSampleRate) *portSampleRate = lastSampleRate; #endif } }; // ------------------------------------------------- static LADSPA_Handle ladspa_instantiate(const LADSPA_Descriptor*, unsigned long sampleRate) { if (d_lastBufferSize == 0) d_lastBufferSize = 2048; d_lastSampleRate = sampleRate; return new PluginLadspaDssi(); } static void ladspa_connect_port(LADSPA_Handle instance, unsigned long port, LADSPA_Data* dataLocation) { PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; assert(plugin); plugin->ladspa_connect_port(port, dataLocation); } static void ladspa_activate(LADSPA_Handle instance) { PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; assert(plugin); plugin->ladspa_activate(); } static void ladspa_run(LADSPA_Handle instance, unsigned long sampleCount) { PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; assert(plugin); plugin->ladspa_run(sampleCount); } static void ladspa_deactivate(LADSPA_Handle instance) { PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; assert(plugin); plugin->ladspa_deactivate(); } static void ladspa_cleanup(LADSPA_Handle instance) { PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; assert(plugin); delete plugin; } #ifdef DISTRHO_PLUGIN_TARGET_DSSI # if DISTRHO_PLUGIN_WANT_STATE static char* dssi_configure(LADSPA_Handle instance, const char* key, const char* value) { PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; assert(plugin); return plugin->dssi_configure(key, value); } # endif # if DISTRHO_PLUGIN_WANT_PROGRAMS static const DSSI_Program_Descriptor* dssi_get_program(LADSPA_Handle instance, unsigned long index) { PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; assert(plugin); return plugin->dssi_get_program(index); } static void dssi_select_program(LADSPA_Handle instance, unsigned long bank, unsigned long program) { PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; assert(plugin); plugin->dssi_select_program(bank, program); } # endif # if DISTRHO_PLUGIN_IS_SYNTH static void dssi_run_synth(LADSPA_Handle instance, unsigned long sampleCount, snd_seq_event_t* events, unsigned long eventCount) { PluginLadspaDssi* plugin = (PluginLadspaDssi*)instance; assert(plugin); plugin->dssi_run_synth(sampleCount, events, eventCount); } # endif #endif // ------------------------------------------------- static LADSPA_Descriptor ldescriptor = { /* UniqueID */ 0, /* Label */ nullptr, /* Properties */ LADSPA_PROPERTY_REALTIME | LADSPA_PROPERTY_HARD_RT_CAPABLE, /* Name */ nullptr, /* Maker */ nullptr, /* Copyright */ nullptr, /* PortCount */ 0, /* PortDescriptors */ nullptr, /* PortNames */ nullptr, /* PortRangeHints */ nullptr, /* ImplementationData */ nullptr, ladspa_instantiate, ladspa_connect_port, ladspa_activate, ladspa_run, /* run_adding */ nullptr, /* set_run_adding_gain */ nullptr, ladspa_deactivate, ladspa_cleanup }; #ifdef DISTRHO_PLUGIN_TARGET_DSSI static DSSI_Descriptor descriptor = { 1, &ldescriptor, # if DISTRHO_PLUGIN_WANT_STATE dssi_configure, # else /* configure */ nullptr, # endif # if DISTRHO_PLUGIN_WANT_PROGRAMS dssi_get_program, dssi_select_program, # else /* get_program */ nullptr, /* select_program */ nullptr, # endif /* get_midi_controller_for_port */ nullptr, # if DISTRHO_PLUGIN_IS_SYNTH dssi_run_synth, # else /* run_synth */ nullptr, # endif /* run_synth_adding */ nullptr, /* run_multiple_synths */ nullptr, /* run_multiple_synths_adding */ nullptr, nullptr, nullptr }; #endif // ------------------------------------------------- class DescriptorInitializer { public: DescriptorInitializer() { // Create dummy plugin to get data from d_lastBufferSize = 512; d_lastSampleRate = 44100.0; PluginInternal plugin; d_lastBufferSize = 0; d_lastSampleRate = 0.0; // Get port count, init unsigned long i, port = 0; unsigned long portCount = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS + plugin.parameterCount(); #if DISTRHO_PLUGIN_WANT_LATENCY portCount += 1; #endif #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI portCount += 1; // sample-rate #endif const char** portNames = new const char* [portCount]; LADSPA_PortDescriptor* portDescriptors = new LADSPA_PortDescriptor [portCount]; LADSPA_PortRangeHint* portRangeHints = new LADSPA_PortRangeHint [portCount]; // Set ports for (i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++, port++) { char portName[24] = { 0 }; sprintf(portName, "Audio Input %lu", i+1); portNames[port] = strdup(portName); portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT; portRangeHints[port].HintDescriptor = 0; portRangeHints[port].LowerBound = 0.0f; portRangeHints[port].UpperBound = 1.0f; } for (i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++, port++) { char portName[24] = { 0 }; sprintf(portName, "Audio Output %lu", i+1); portNames[port] = strdup(portName); portDescriptors[port] = LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT; portRangeHints[port].HintDescriptor = 0; portRangeHints[port].LowerBound = 0.0f; portRangeHints[port].UpperBound = 1.0f; } #if DISTRHO_PLUGIN_WANT_LATENCY // Set latency port portNames[port] = strdup("_latency"); portDescriptors[port] = LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT; portRangeHints[port].HintDescriptor = LADSPA_HINT_SAMPLE_RATE; portRangeHints[port].LowerBound = 0.0f; portRangeHints[port].UpperBound = 1.0f; port++; #endif #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && DISTRHO_PLUGIN_HAS_UI // Set sample-rate port portNames[port] = strdup("_sample-rate"); portDescriptors[port] = LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT; portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE; portRangeHints[port].LowerBound = 0.0f; portRangeHints[port].UpperBound = 512000.0f; port++; #endif for (i=0; i < plugin.parameterCount(); i++, port++) { portNames[port] = strdup(plugin.parameterName(i)); portDescriptors[port] = LADSPA_PORT_CONTROL; if (plugin.parameterIsOutput(i)) portDescriptors[port] |= LADSPA_PORT_OUTPUT; else portDescriptors[port] |= LADSPA_PORT_INPUT; { const ParameterRanges* ranges = plugin.parameterRanges(i); const float defValue = ranges->def; portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE; portRangeHints[port].LowerBound = ranges->min; portRangeHints[port].UpperBound = ranges->max; if (defValue == 0.0f) portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0; else if (defValue == 1.0f) portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_1; else if (defValue == 100.0f) portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_100; else if (defValue == 440.0f) portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_440; else if (ranges->min == defValue) portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MINIMUM; else if (ranges->max == defValue) portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MAXIMUM; else { const float middleValue = ranges->min/2 + ranges->max/2; const float middleLow = (ranges->min/2 + middleValue/2)/2 + middleValue/2; const float middleHigh = (ranges->max/2 + middleValue/2)/2 + middleValue/2; if (defValue < middleLow) portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_LOW; else if (defValue > middleHigh) portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_HIGH; else portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_MIDDLE; } } { uint32_t hints = plugin.parameterHints(i); if (hints & PARAMETER_IS_BOOLEAN) portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED; if (hints & PARAMETER_IS_INTEGER) portRangeHints[port].HintDescriptor |= LADSPA_HINT_INTEGER; if (hints & PARAMETER_IS_LOGARITHMIC) portRangeHints[port].HintDescriptor |= LADSPA_HINT_LOGARITHMIC; } } // Set data ldescriptor.UniqueID = plugin.uniqueId(); ldescriptor.Label = strdup(plugin.label()); ldescriptor.Name = strdup(plugin.name()); ldescriptor.Maker = strdup(plugin.maker()); ldescriptor.Copyright = strdup(plugin.license()); ldescriptor.PortCount = portCount; ldescriptor.PortNames = portNames; ldescriptor.PortDescriptors = portDescriptors; ldescriptor.PortRangeHints = portRangeHints; } ~DescriptorInitializer() { if (ldescriptor.Label) free((void*)ldescriptor.Label); if (ldescriptor.Name) free((void*)ldescriptor.Name); if (ldescriptor.Maker) free((void*)ldescriptor.Maker); if (ldescriptor.Copyright) free((void*)ldescriptor.Copyright); if (ldescriptor.PortDescriptors) delete[] ldescriptor.PortDescriptors; if (ldescriptor.PortRangeHints) delete[] ldescriptor.PortRangeHints; if (ldescriptor.PortNames) { for (unsigned long i=0; i < ldescriptor.PortCount; i++) { if (ldescriptor.PortNames[i]) free((void*)ldescriptor.PortNames[i]); } delete[] ldescriptor.PortNames; } } }; static DescriptorInitializer init; END_NAMESPACE_DISTRHO // ------------------------------------------------- DISTRHO_PLUGIN_EXPORT const LADSPA_Descriptor* ladspa_descriptor(unsigned long index) { USE_NAMESPACE_DISTRHO return (index == 0) ? &ldescriptor : nullptr; } #ifdef DISTRHO_PLUGIN_TARGET_DSSI DISTRHO_PLUGIN_EXPORT const DSSI_Descriptor* dssi_descriptor(unsigned long index) { USE_NAMESPACE_DISTRHO return (index == 0) ? &descriptor : nullptr; } #endif // ------------------------------------------------- #endif // DISTRHO_PLUGIN_TARGET_LADSPA || DISTRHO_PLUGIN_TARGET_DSSI