diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index fcb40dd59..2fb9ec6f5 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2018 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 @@ -89,6 +89,15 @@ static const uint32_t kParameterIsLogarithmic = 0x08; */ static const uint32_t kParameterIsOutput = 0x10; +/** + Parameter value is a trigger.@n + This means the value resets back to its default after each process/run call.@n + Cannot be used for output parameters. + + @note Only officially supported under LV2. For other formats DPF simulates the behaviour. +*/ +static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean; + /** @} */ /* ------------------------------------------------------------------------------------------------------------ @@ -274,6 +283,90 @@ struct ParameterRanges { } }; +/** + Parameter enumeration value.@n + A string representation of a plugin parameter value.@n + Used together can be used to give meaning to parameter values, working as an enumeration. + */ +struct ParameterEnumerationValue { + /** + Parameter value. + */ + float value; + + /** + String representation of this value. + */ + String label; + + /** + Default constructor, using 0.0 as value and empty label. + */ + ParameterEnumerationValue() noexcept + : value(0.0f), + label() {} + + /** + Constructor using custom values. + */ + ParameterEnumerationValue(float v, const char* l) noexcept + : value(v), + label(l) {} +}; + +/** + Collection of parameter enumeration values.@n + Handy class to handle the lifetime and count of all enumeration values. + */ +struct ParameterEnumerationValues { + /** + Number of elements allocated in @values. + */ + uint8_t count; + + /** + Wherever the host is to be restricted to only use enumeration values. + + @note This mode is only a hint! Not all hosts and plugin formats support this mode. + */ + bool restrictedMode; + + /** + Array of @ParameterEnumerationValue items.@n + This pointer must be null or have been allocated on the heap with `new`. + */ + const ParameterEnumerationValue* values; + + /** + Default constructor, for zero enumeration values. + */ + ParameterEnumerationValues() noexcept + : count(0), + restrictedMode(false), + values() {} + + /** + Constructor using custom values.@n + The pointer to @values must have been allocated on the heap with `new`. + */ + ParameterEnumerationValues(uint32_t c, bool r, const ParameterEnumerationValue* v) noexcept + : count(c), + restrictedMode(r), + values(v) {} + + ~ParameterEnumerationValues() noexcept + { + count = 0; + restrictedMode = false; + + if (values != nullptr) + { + delete[] values; + values = nullptr; + } + } +}; + /** Parameter. */ @@ -312,6 +405,12 @@ struct Parameter { */ ParameterRanges ranges; + /** + Enumeration values.@n + Can be used to give meaning to parameter values, working as an enumeration. + */ + ParameterEnumerationValues enumValues; + /** Designation for this parameter. */ @@ -334,6 +433,7 @@ struct Parameter { symbol(), unit(), ranges(), + enumValues(), designation(kParameterDesignationNull), midiCC(0) {} @@ -346,6 +446,7 @@ struct Parameter { symbol(s), unit(u), ranges(def, min, max), + enumValues(), designation(kParameterDesignationNull), midiCC(0) {} @@ -593,9 +694,6 @@ class Plugin Write a MIDI output event.@n This function must only be called during run().@n Returns false when the host buffer is full, in which case do not call this again until the next run(). - @note This function is not implemented yet!@n - It's here so that developers can prepare MIDI plugins in advance.@n - If you plan to use this, please report to DPF authors so it can be implemented. */ bool writeMidiEvent(const MidiEvent& midiEvent) noexcept; #endif diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index 20cdfcb01..345987615 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2018 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 @@ -27,9 +27,10 @@ double d_lastSampleRate = 0.0; /* ------------------------------------------------------------------------------------------------------------ * Static fallback data, see DistrhoPluginInternal.hpp */ -const String PluginExporter::sFallbackString; -const AudioPort PluginExporter::sFallbackAudioPort; -const ParameterRanges PluginExporter::sFallbackRanges; +const String PluginExporter::sFallbackString; +const AudioPort PluginExporter::sFallbackAudioPort; +const ParameterRanges PluginExporter::sFallbackRanges; +const ParameterEnumerationValues PluginExporter::sFallbackEnumValues; /* ------------------------------------------------------------------------------------------------------------ * Plugin */ @@ -102,10 +103,9 @@ void Plugin::setLatency(uint32_t frames) noexcept #endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT -bool Plugin::writeMidiEvent(const MidiEvent& /*midiEvent*/) noexcept +bool Plugin::writeMidiEvent(const MidiEvent& midiEvent) noexcept { - // TODO - return false; + return pData->writeMidiCallback(midiEvent); } #endif diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index fcb3304ea..604f4d985 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2018 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 @@ -32,6 +32,11 @@ static const uint32_t kMaxMidiEvents = 512; extern uint32_t d_lastBufferSize; extern double d_lastSampleRate; +// ----------------------------------------------------------------------- +// DSP callbacks + +typedef bool (*writeMidiFunc) (void* ptr, const MidiEvent& midiEvent); + // ----------------------------------------------------------------------- // Plugin private data @@ -65,6 +70,10 @@ struct Plugin::PrivateData { TimePosition timePosition; #endif + // Callbacks + void* callbacksPtr; + writeMidiFunc writeMidiCallbackFunc; + uint32_t bufferSize; double sampleRate; @@ -88,6 +97,8 @@ struct Plugin::PrivateData { #if DISTRHO_PLUGIN_WANT_LATENCY latency(0), #endif + callbacksPtr(nullptr), + writeMidiCallbackFunc(nullptr), bufferSize(d_lastBufferSize), sampleRate(d_lastSampleRate) { @@ -149,6 +160,16 @@ struct Plugin::PrivateData { } #endif } + +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + bool writeMidiCallback(const MidiEvent& midiEvent) + { + if (writeMidiCallbackFunc != nullptr) + return writeMidiCallbackFunc(callbacksPtr, midiEvent); + + return false; + } +#endif }; // ----------------------------------------------------------------------- @@ -157,7 +178,7 @@ struct Plugin::PrivateData { class PluginExporter { public: - PluginExporter() + PluginExporter(void* const callbacksPtr, const writeMidiFunc writeMidiCall) : fPlugin(createPlugin()), fData((fPlugin != nullptr) ? fPlugin->pData : nullptr), fIsActive(false) @@ -191,6 +212,9 @@ class PluginExporter for (uint32_t i=0, count=fData->stateCount; i < count; ++i) fPlugin->initState(i, fData->stateKeys[i], fData->stateDefValues[i]); #endif + + fData->callbacksPtr = callbacksPtr; + fData->writeMidiCallbackFunc = writeMidiCall; } ~PluginExporter() @@ -322,9 +346,26 @@ class PluginExporter return fData->parameters[index].designation; } + bool isParameterInput(const uint32_t index) const noexcept + { + return (getParameterHints(index) & kParameterIsOutput) == 0x0; + } + bool isParameterOutput(const uint32_t index) const noexcept { - return (getParameterHints(index) & kParameterIsOutput); + return (getParameterHints(index) & kParameterIsOutput) != 0x0; + } + + bool isParameterOutputOrTrigger(const uint32_t index) const noexcept + { + const uint32_t hints = getParameterHints(index); + + if (hints & kParameterIsOutput) + return true; + if ((hints & kParameterIsTrigger) == kParameterIsTrigger) + return true; + + return false; } const String& getParameterName(const uint32_t index) const noexcept @@ -348,6 +389,13 @@ class PluginExporter return fData->parameters[index].unit; } + const ParameterEnumerationValues& getParameterEnumValues(const uint32_t index) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackEnumValues); + + return fData->parameters[index].enumValues; + } + const ParameterRanges& getParameterRanges(const uint32_t index) const noexcept { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackRanges); @@ -601,9 +649,10 @@ class PluginExporter // ------------------------------------------------------------------- // Static fallback data, see DistrhoPlugin.cpp - static const String sFallbackString; - static const AudioPort sFallbackAudioPort; - static const ParameterRanges sFallbackRanges; + static const String sFallbackString; + static const AudioPort sFallbackAudioPort; + static const ParameterRanges sFallbackRanges; + static const ParameterEnumerationValues sFallbackEnumValues; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginExporter) DISTRHO_PREVENT_HEAP_ALLOCATION diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp index 4b1e5d5be..1d0a8c373 100644 --- a/distrho/src/DistrhoPluginJack.cpp +++ b/distrho/src/DistrhoPluginJack.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2018 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -42,6 +42,9 @@ START_NAMESPACE_DISTRHO #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_STATE static const setStateFunc setStateCallback = nullptr; #endif +#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT +static const writeMidiFunc writeMidiCallback = nullptr; +#endif // ----------------------------------------------------------------------- @@ -91,7 +94,7 @@ class PluginJack { public: PluginJack(jack_client_t* const client) - : fPlugin(), + : fPlugin(this, writeMidiCallback), #if DISTRHO_PLUGIN_HAS_UI fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, nullptr, setSizeCallback, fPlugin.getInstancePointer()), #endif @@ -119,6 +122,11 @@ class PluginJack fPortEventsIn = jack_port_register(fClient, "events-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + fPortMidiOut = jack_port_register(fClient, "midi-out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); + fPortMidiOutBuffer = nullptr; +#endif + #if DISTRHO_PLUGIN_WANT_PROGRAMS if (fPlugin.getProgramCount() > 0) { @@ -201,6 +209,11 @@ class PluginJack if (fClient == nullptr) return; +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + jack_port_unregister(fClient, fPortMidiOut); + fPortMidiOut = nullptr; +#endif + jack_port_unregister(fClient, fPortEventsIn); fPortEventsIn = nullptr; @@ -330,6 +343,11 @@ class PluginJack void* const midiBuf = jack_port_get_buffer(fPortEventsIn, nframes); +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + fPortMidiOutBuffer = jack_port_get_buffer(fPortMidiOut, nframes); + jack_midi_clear_buffer(fPortMidiOutBuffer); +#endif + if (const uint32_t eventCount = jack_midi_get_event_count(midiBuf)) { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT @@ -408,6 +426,12 @@ class PluginJack #else fPlugin.run(audioIns, audioOuts, nframes); #endif + +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + fPortMidiOutBuffer = nullptr; +#endif + + updateParameterTriggers(); } void jackShutdown() @@ -440,6 +464,35 @@ class PluginJack } #endif +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + bool writeMidi(const MidiEvent& midiEvent) + { + DISTRHO_SAFE_ASSERT_RETURN(fPortMidiOutBuffer != nullptr, false); + + return jack_midi_event_write(fPortMidiOutBuffer, + midiEvent.frame, + midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data, + midiEvent.size) == 0; + } +#endif + + // NOTE: no trigger support for JACK, simulate it here + void updateParameterTriggers() + { + float defValue; + + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) + { + if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) != kParameterIsTrigger) + continue; + + defValue = fPlugin.getParameterRanges(i).def; + + if (d_isNotEqual(defValue, fPlugin.getParameterValue(i))) + fPlugin.setParameterValue(i, defValue); + } + } + // ------------------------------------------------------------------- private: @@ -457,6 +510,10 @@ class PluginJack jack_port_t* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; #endif jack_port_t* fPortEventsIn; +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + jack_port_t* fPortMidiOut; + void* fPortMidiOutBuffer; +#endif #if DISTRHO_PLUGIN_WANT_TIMEPOS TimePosition fTimePosition; #endif @@ -475,51 +532,58 @@ class PluginJack // ------------------------------------------------------------------- // Callbacks - #define uiPtr ((PluginJack*)ptr) + #define thisPtr ((PluginJack*)ptr) static int jackBufferSizeCallback(jack_nframes_t nframes, void* ptr) { - uiPtr->jackBufferSize(nframes); + thisPtr->jackBufferSize(nframes); return 0; } static int jackSampleRateCallback(jack_nframes_t nframes, void* ptr) { - uiPtr->jackSampleRate(nframes); + thisPtr->jackSampleRate(nframes); return 0; } static int jackProcessCallback(jack_nframes_t nframes, void* ptr) { - uiPtr->jackProcess(nframes); + thisPtr->jackProcess(nframes); return 0; } static void jackShutdownCallback(void* ptr) { - uiPtr->jackShutdown(); + thisPtr->jackShutdown(); } static void setParameterValueCallback(void* ptr, uint32_t index, float value) { - uiPtr->setParameterValue(index, value); + thisPtr->setParameterValue(index, value); } #if DISTRHO_PLUGIN_WANT_STATE static void setStateCallback(void* ptr, const char* key, const char* value) { - uiPtr->setState(key, value); + thisPtr->setState(key, value); } #endif #if DISTRHO_PLUGIN_HAS_UI static void setSizeCallback(void* ptr, uint width, uint height) { - uiPtr->setSize(width, height); + thisPtr->setSize(width, height); + } +#endif + +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) + { + return thisPtr->writeMidi(midiEvent); } #endif - #undef uiPtr + #undef thisPtr }; END_NAMESPACE_DISTRHO diff --git a/distrho/src/DistrhoPluginLADSPA+DSSI.cpp b/distrho/src/DistrhoPluginLADSPA+DSSI.cpp index 01bb19d7f..b370da809 100644 --- a/distrho/src/DistrhoPluginLADSPA+DSSI.cpp +++ b/distrho/src/DistrhoPluginLADSPA+DSSI.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2018 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 @@ -22,9 +22,12 @@ #ifdef DISTRHO_PLUGIN_TARGET_DSSI # include "dssi/dssi.h" +# if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT +# error DSSI does not support MIDI output +# endif #else # include "ladspa/ladspa.h" -# if DISTRHO_PLUGIN_WANT_MIDI_INPUT +# if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT # error Cannot use MIDI with LADSPA # endif # if DISTRHO_PLUGIN_WANT_STATE @@ -44,7 +47,8 @@ class PluginLadspaDssi { public: PluginLadspaDssi() - : fPortControls(nullptr), + : fPlugin(nullptr, nullptr), + fPortControls(nullptr), fLastControlValues(nullptr) { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 @@ -171,7 +175,7 @@ class PluginLadspaDssi { // pre-roll if (sampleCount == 0) - return updateParameterOutputs(); + return updateParameterOutputsAndTriggers(); // Check for updated parameters float curValue; @@ -183,7 +187,7 @@ class PluginLadspaDssi curValue = *fPortControls[i]; - if (fLastControlValues[i] != curValue && ! fPlugin.isParameterOutput(i)) + if (fPlugin.isParameterInput(i) && d_isNotEqual(fLastControlValues[i], curValue)) { fLastControlValues[i] = curValue; fPlugin.setParameterValue(i, curValue); @@ -268,7 +272,7 @@ class PluginLadspaDssi fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); #endif - updateParameterOutputs(); + updateParameterOutputsAndTriggers(); #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && ! DISTRHO_PLUGIN_WANT_MIDI_INPUT return; // unused @@ -371,17 +375,33 @@ class PluginLadspaDssi // ------------------------------------------------------------------- - void updateParameterOutputs() + void updateParameterOutputsAndTriggers() { + float value; + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) { - if (! fPlugin.isParameterOutput(i)) - continue; + if (fPlugin.isParameterOutput(i)) + { + value = fLastControlValues[i] = fPlugin.getParameterValue(i); - fLastControlValues[i] = fPlugin.getParameterValue(i); + if (fPortControls[i] != nullptr) + *fPortControls[i] = value; + } + else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger) + { + // NOTE: no trigger support in LADSPA control ports, simulate it here + value = fPlugin.getParameterRanges(i).def; - if (fPortControls[i] != nullptr) - *fPortControls[i] = fLastControlValues[i]; + if (d_isEqual(value, fPlugin.getParameterValue(i))) + continue; + + fLastControlValues[i] = value; + fPlugin.setParameterValue(i, value); + + if (fPortControls[i] != nullptr) + *fPortControls[i] = value; + } } #if DISTRHO_PLUGIN_WANT_LATENCY @@ -531,7 +551,7 @@ class DescriptorInitializer // Create dummy plugin to get data from d_lastBufferSize = 512; d_lastSampleRate = 44100.0; - PluginExporter plugin; + PluginExporter plugin(nullptr, nullptr); d_lastBufferSize = 0; d_lastSampleRate = 0.0; diff --git a/distrho/src/DistrhoPluginLV2.cpp b/distrho/src/DistrhoPluginLV2.cpp index 35ede034b..6eb8be9e9 100644 --- a/distrho/src/DistrhoPluginLV2.cpp +++ b/distrho/src/DistrhoPluginLV2.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2018 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 @@ -32,7 +32,7 @@ #include "lv2/lv2_programs.h" #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD -# include "libmodauth.h" +# include "libmodla.h" #endif #ifdef noexcept @@ -56,13 +56,18 @@ START_NAMESPACE_DISTRHO typedef std::map StringMap; +#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT +static const writeMidiFunc writeMidiCallback = nullptr; +#endif + // ----------------------------------------------------------------------- class PluginLv2 { public: PluginLv2(const double sampleRate, const LV2_URID_Map* const uridMap, const LV2_Worker_Schedule* const worker, const bool usingNominal) - : fUsingNominal(usingNominal), + : fPlugin(this, writeMidiCallback), + fUsingNominal(usingNominal), #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD fRunCount(0), #endif @@ -107,9 +112,6 @@ class PluginLv2 #if DISTRHO_LV2_USE_EVENTS_IN fPortEventsIn = nullptr; #endif -#if DISTRHO_LV2_USE_EVENTS_OUT - fPortEventsOut = nullptr; -#endif #if DISTRHO_PLUGIN_WANT_LATENCY fPortLatency = nullptr; #endif @@ -226,7 +228,7 @@ class PluginLv2 #if DISTRHO_LV2_USE_EVENTS_OUT if (port == index++) { - fPortEventsOut = (LV2_Atom_Sequence*)dataLocation; + fEventsOutData.port = (LV2_Atom_Sequence*)dataLocation; return; } #endif @@ -387,7 +389,7 @@ class PluginLv2 if (fLastPositionData.barBeat >= 0.0f) { const double rest = std::fmod(fLastPositionData.barBeat, 1.0f); - fTimePosition.bbt.beat = fLastPositionData.barBeat-rest+1.0; + fTimePosition.bbt.beat = std::round(fLastPositionData.barBeat-rest+1.0); fTimePosition.bbt.tick = rest*fTimePosition.bbt.ticksPerBeat+0.5; } } @@ -517,14 +519,12 @@ class PluginLv2 curValue = *fPortControls[i]; - if (fLastControlValues[i] != curValue && ! fPlugin.isParameterOutput(i)) + if (fPlugin.isParameterInput(i) && d_isNotEqual(fLastControlValues[i], curValue)) { fLastControlValues[i] = curValue; if (fPlugin.getParameterDesignation(i) == kParameterDesignationBypass) - { curValue = 1.0f - curValue; - } fPlugin.setParameterValue(i, curValue); } @@ -580,7 +580,7 @@ class PluginLv2 (double)fLastPositionData.beatsPerBar); const double rest = std::fmod(fLastPositionData.barBeat, 1.0f); - fTimePosition.bbt.beat = fLastPositionData.barBeat-rest+1.0; + fTimePosition.bbt.beat = std::round(fLastPositionData.barBeat-rest+1.0); fTimePosition.bbt.tick = rest*fTimePosition.bbt.ticksPerBeat+0.5; if (fLastPositionData.bar >= 0) @@ -607,20 +607,14 @@ class PluginLv2 #endif } - updateParameterOutputs(); + updateParameterOutputsAndTriggers(); #if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI - const uint32_t capacity = fPortEventsOut->atom.size; + fEventsOutData.initIfNeeded(fURIDs.atomSequence); - uint32_t size, offset = 0; LV2_Atom_Event* aev; - - fPortEventsOut->atom.size = sizeof(LV2_Atom_Sequence_Body); - fPortEventsOut->atom.type = fURIDs.atomSequence; - fPortEventsOut->body.unit = 0; - fPortEventsOut->body.pad = 0; - - // TODO - MIDI Output + uint32_t offset = fEventsOutData.offset; + const uint32_t capacity = fEventsOutData.capacity; for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) { @@ -645,6 +639,7 @@ class PluginLv2 break; // reserve msg space + // FIXME create a large enough buffer beforehand char msgBuf[msgSize]; std::memset(msgBuf, 0, msgSize); @@ -653,21 +648,23 @@ class PluginLv2 std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()); // put data - aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fPortEventsOut) + offset); + aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + offset); aev->time.frames = 0; aev->body.type = fURIDs.distrhoState; aev->body.size = msgSize; std::memcpy(LV2_ATOM_BODY(&aev->body), msgBuf, msgSize-1); - size = lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize); - offset += size; - fPortEventsOut->atom.size += size; + fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize)); fNeededUiSends[i] = false; break; } } #endif + +#if DISTRHO_LV2_USE_EVENTS_OUT + fEventsOutData.endRun(); +#endif } // ------------------------------------------------------------------- @@ -883,9 +880,6 @@ class PluginLv2 #if DISTRHO_LV2_USE_EVENTS_IN LV2_Atom_Sequence* fPortEventsIn; #endif -#if DISTRHO_LV2_USE_EVENTS_OUT - LV2_Atom_Sequence* fPortEventsOut; -#endif #if DISTRHO_PLUGIN_WANT_LATENCY float* fPortLatency; #endif @@ -922,6 +916,44 @@ class PluginLv2 } fLastPositionData; #endif +#if DISTRHO_LV2_USE_EVENTS_OUT + struct Lv2EventsOutData { + uint32_t capacity, offset; + LV2_Atom_Sequence* port; + + Lv2EventsOutData() + : capacity(0), + offset(0), + port(nullptr) {} + + void initIfNeeded(const LV2_URID uridAtomSequence) + { + if (capacity != 0) + return; + + capacity = port->atom.size; + + port->atom.size = sizeof(LV2_Atom_Sequence_Body); + port->atom.type = uridAtomSequence; + port->body.unit = 0; + port->body.pad = 0; + } + + void growBy(const uint32_t size) + { + offset += size; + port->atom.size += size; + } + + void endRun() + { + capacity = 0; + offset = 0; + } + + } fEventsOutData; +#endif + // LV2 URIDs struct URIDs { LV2_URID atomBlank; @@ -998,17 +1030,23 @@ class PluginLv2 } #endif - void updateParameterOutputs() + void updateParameterOutputsAndTriggers() { + float curValue; + for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) { - if (! fPlugin.isParameterOutput(i)) - continue; - - fLastControlValues[i] = fPlugin.getParameterValue(i); + if (fPlugin.isParameterOutput(i)) + { + curValue = fLastControlValues[i] = fPlugin.getParameterValue(i); - if (fPortControls[i] != nullptr) - *fPortControls[i] = fLastControlValues[i]; + if (fPortControls[i] != nullptr) + *fPortControls[i] = curValue; + } + else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger) + { + // NOTE: host is responsible for auto-updating control port buffers + } } #if DISTRHO_PLUGIN_WANT_LATENCY @@ -1016,6 +1054,38 @@ class PluginLv2 *fPortLatency = fPlugin.getLatency(); #endif } + +#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + bool writeMidi(const MidiEvent& midiEvent) + { + DISTRHO_SAFE_ASSERT_RETURN(fEventsOutData.port != nullptr, false); + + fEventsOutData.initIfNeeded(fURIDs.atomSequence); + + const uint32_t capacity = fEventsOutData.capacity; + const uint32_t offset = fEventsOutData.offset; + + if (sizeof(LV2_Atom_Event) + midiEvent.size > capacity - offset) + return false; + + LV2_Atom_Event* const aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + offset); + aev->time.frames = midiEvent.frame; + aev->body.type = fURIDs.midiEvent; + aev->body.size = midiEvent.size; + std::memcpy(LV2_ATOM_BODY(&aev->body), + midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data, + midiEvent.size); + + fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + midiEvent.size)); + + return true; + } + + static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) + { + return ((PluginLv2*)ptr)->writeMidi(midiEvent); + } +#endif }; // ----------------------------------------------------------------------- @@ -1057,7 +1127,7 @@ static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, cons #endif #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD - mod_check_license(features, DISTRHO_PLUGIN_URI); + mod_license_check(features, DISTRHO_PLUGIN_URI); #endif d_lastBufferSize = 0; diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 295525b54..6ed2f7c5a 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2017 Filipe Coelho + * Copyright (C) 2012-2018 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 @@ -80,7 +80,7 @@ void lv2_generate_ttl(const char* const basename) // Dummy plugin to get data from d_lastBufferSize = 512; d_lastSampleRate = 44100.0; - PluginExporter plugin; + PluginExporter plugin(nullptr, nullptr); d_lastBufferSize = 0; d_lastSampleRate = 0.0; @@ -202,6 +202,7 @@ void lv2_generate_ttl(const char* const basename) pluginString += "@prefix mod: .\n"; #endif pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; + pluginString += "@prefix rdf: .\n"; pluginString += "@prefix rdfs: .\n"; pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; #if DISTRHO_PLUGIN_HAS_UI @@ -398,7 +399,7 @@ void lv2_generate_ttl(const char* const basename) bool designated = false; // designation - if (! plugin.isParameterOutput(i)) + if (plugin.isParameterInput(i)) { switch (plugin.getParameterDesignation(i)) { @@ -417,10 +418,10 @@ void lv2_generate_ttl(const char* const basename) } } - // name and symbol if (! designated) { - pluginString += " lv2:name \"" + plugin.getParameterName(i) + "\" ;\n"; + // name and symbol + pluginString += " lv2:name \"\"\"" + plugin.getParameterName(i) + "\"\"\" ;\n"; String symbol(plugin.getParameterSymbol(i)); @@ -428,32 +429,53 @@ void lv2_generate_ttl(const char* const basename) symbol = "lv2_port_" + String(portIndex-1); pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; - } - // ranges - if (! designated) - { + // ranges const ParameterRanges& ranges(plugin.getParameterRanges(i)); if (plugin.getParameterHints(i) & kParameterIsInteger) { - if (! plugin.isParameterOutput(i)) + if (plugin.isParameterInput(i)) pluginString += " lv2:default " + String(int(plugin.getParameterValue(i))) + " ;\n"; pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n"; pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\n"; } else { - if (! plugin.isParameterOutput(i)) + if (plugin.isParameterInput(i)) pluginString += " lv2:default " + String(plugin.getParameterValue(i)) + " ;\n"; pluginString += " lv2:minimum " + String(ranges.min) + " ;\n"; pluginString += " lv2:maximum " + String(ranges.max) + " ;\n"; } - } - // unit - if (! designated) - { + // enumeration + const ParameterEnumerationValues& enumValues(plugin.getParameterEnumValues(i)); + + if (enumValues.count > 0) + { + if (enumValues.count >= 2 && enumValues.restrictedMode) + pluginString += " lv2:portProperty lv2:enumeration ;\n"; + + for (uint8_t j=0; j < enumValues.count; ++j) + { + const ParameterEnumerationValue& enumValue(enumValues.values[j]); + + if (j == 0) + pluginString += " lv2:scalePoint [\n"; + else + pluginString += " [\n"; + + pluginString += " rdfs:label \"\"\"" + enumValue.label + "\"\"\" ;\n"; + pluginString += " rdf:value " + String(enumValue.value) + " ;\n"; + + if (j+1 == enumValues.count) + pluginString += " ] ;\n\n"; + else + pluginString += " ] ,\n"; + } + } + + // unit const String& unit(plugin.getParameterUnit(i)); if (! unit.isEmpty()) @@ -495,25 +517,26 @@ void lv2_generate_ttl(const char* const basename) pluginString += " ] ;\n"; } } - } - // hints - if (! designated) - { + // hints const uint32_t hints(plugin.getParameterHints(i)); if (hints & kParameterIsBoolean) + { + if ((hints & kParameterIsTrigger) == kParameterIsTrigger) + pluginString += " lv2:portProperty <" LV2_PORT_PROPS__trigger "> ;\n"; pluginString += " lv2:portProperty lv2:toggled ;\n"; + } if (hints & kParameterIsInteger) pluginString += " lv2:portProperty lv2:integer ;\n"; if (hints & kParameterIsLogarithmic) pluginString += " lv2:portProperty <" LV2_PORT_PROPS__logarithmic "> ;\n"; - if ((hints & kParameterIsAutomable) == 0 && ! plugin.isParameterOutput(i)) + if ((hints & kParameterIsAutomable) == 0 && plugin.isParameterInput(i)) { pluginString += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ,\n"; pluginString += " <" LV2_KXSTUDIO_PROPERTIES__NonAutomable "> ;\n"; } - } + } // ! designated if (i+1 == count) pluginString += " ] ;\n\n"; @@ -537,7 +560,7 @@ void lv2_generate_ttl(const char* const basename) #endif // name - pluginString += " doap:name \"" + String(plugin.getName()) + "\" ;\n"; + pluginString += " doap:name \"\"\"" + String(plugin.getName()) + "\"\"\" ;\n"; // license { @@ -546,7 +569,7 @@ void lv2_generate_ttl(const char* const basename) if (license.contains("://")) pluginString += " doap:license <" + license + "> ;\n\n"; else - pluginString += " doap:license \"" + license + "\" ;\n\n"; + pluginString += " doap:license \"\"\"" + license + "\"\"\" ;\n\n"; } // developer @@ -554,7 +577,7 @@ void lv2_generate_ttl(const char* const basename) const String homepage(plugin.getHomePage()); pluginString += " doap:maintainer [\n"; - pluginString += " foaf:name \"" + String(plugin.getMaker()) + "\" ;\n"; + pluginString += " foaf:name \"\"\"" + String(plugin.getMaker()) + "\"\"\" ;\n"; if (homepage.isNotEmpty()) pluginString += " foaf:homepage <" + homepage + "> ;\n"; diff --git a/distrho/src/DistrhoPluginVST.cpp b/distrho/src/DistrhoPluginVST.cpp index b90c5cd81..2f389ed5d 100644 --- a/distrho/src/DistrhoPluginVST.cpp +++ b/distrho/src/DistrhoPluginVST.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2018 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 @@ -37,11 +37,12 @@ #define VESTIGE_HEADER #define VST_FORCE_DEPRECATED 0 +#include #include #include #ifdef VESTIGE_HEADER -# include "vestige/aeffectx.h" +# include "vestige/vestige.h" #define effFlagsProgramChunks (1 << 5) #define effSetProgramName 4 #define effGetParamLabel 6 @@ -51,11 +52,8 @@ #define effCanBeAutomated 26 #define effGetProgramNameIndexed 29 #define effGetPlugCategory 35 -#define effIdle 53 #define effEditKeyDown 59 #define effEditKeyUp 60 -#define kPlugCategEffect 1 -#define kPlugCategSynth 2 #define kVstVersion 2400 struct ERect { int16_t top, left, bottom, right; @@ -68,6 +66,12 @@ START_NAMESPACE_DISTRHO typedef std::map StringMap; +static const int kVstMidiEventSize = static_cast(sizeof(VstMidiEvent)); + +#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT +static const writeMidiFunc writeMidiCallback = nullptr; +#endif + // ----------------------------------------------------------------------- void strncpy(char* const dst, const char* const src, const size_t size) @@ -88,17 +92,42 @@ void snprintf_iparam(char* const dst, const int32_t value, const size_t size) dst[size-1] = '\0'; } -#if DISTRHO_PLUGIN_HAS_UI // ----------------------------------------------------------------------- -class UiHelper +class ScopedSafeLocale { +public: + ScopedSafeLocale() noexcept + : locale(::strdup(::setlocale(LC_NUMERIC, nullptr))) + { + ::setlocale(LC_NUMERIC, "C"); + } + + ~ScopedSafeLocale() noexcept + { + if (locale != nullptr) + { + ::setlocale(LC_NUMERIC, locale); + std::free(locale); + } + } + +private: + char* const locale; + + DISTRHO_DECLARE_NON_COPY_CLASS(ScopedSafeLocale) + DISTRHO_PREVENT_HEAP_ALLOCATION +}; + +// ----------------------------------------------------------------------- + +class ParameterCheckHelper { public: - UiHelper() + ParameterCheckHelper() : parameterChecks(nullptr), parameterValues(nullptr) {} - virtual ~UiHelper() + virtual ~ParameterCheckHelper() { if (parameterChecks != nullptr) { @@ -112,20 +141,21 @@ class UiHelper } } - bool* parameterChecks; - float* parameterValues; + bool* parameterChecks; + float* parameterValues; -# if DISTRHO_PLUGIN_WANT_STATE +#if DISTRHO_PLUGIN_WANT_STATE virtual void setStateFromUI(const char* const newKey, const char* const newValue) = 0; -# endif +#endif }; +#if DISTRHO_PLUGIN_HAS_UI // ----------------------------------------------------------------------- class UIVst { public: - UIVst(const audioMasterCallback audioMaster, AEffect* const effect, UiHelper* const uiHelper, PluginExporter* const plugin, const intptr_t winId) + UIVst(const audioMasterCallback audioMaster, AEffect* const effect, ParameterCheckHelper* const uiHelper, PluginExporter* const plugin, const intptr_t winId) : fAudioMaster(audioMaster), fEffect(effect), fUiHelper(uiHelper), @@ -300,7 +330,7 @@ class UIVst // Vst stuff const audioMasterCallback fAudioMaster; AEffect* const fEffect; - UiHelper* const fUiHelper; + ParameterCheckHelper* const fUiHelper; PluginExporter* const fPlugin; // Plugin UI @@ -343,15 +373,12 @@ class UIVst // ----------------------------------------------------------------------- -#if DISTRHO_PLUGIN_HAS_UI -class PluginVst : public UiHelper -#else -class PluginVst -#endif +class PluginVst : public ParameterCheckHelper { public: PluginVst(const audioMasterCallback audioMaster, AEffect* const effect) - : fAudioMaster(audioMaster), + : fPlugin(this, writeMidiCallback), + fAudioMaster(audioMaster), fEffect(effect) { std::memset(fProgramName, 0, sizeof(char)*(32+1)); @@ -376,14 +403,14 @@ class PluginVst for (uint32_t i=0; i < paramCount; ++i) { parameterChecks[i] = false; - parameterValues[i] = 0.0f; + parameterValues[i] = NAN; } } # if DISTRHO_OS_MAC # ifdef __LP64__ fUsingNsView = true; # else -# warning 32bit VST UIs on OSX only work if the host supports "hasCockosViewAsConfig" +# warning 32bit VST UIs on OSX only work if the host supports "hasCockosViewAsConfig" fUsingNsView = false; # endif # endif // DISTRHO_OS_MAC @@ -453,24 +480,35 @@ class PluginVst { const uint32_t hints = fPlugin.getParameterHints(index); float value = fPlugin.getParameterValue(index); - + if (hints & kParameterIsBoolean) { const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; - + value = value > midRange ? ranges.max : ranges.min; } - - if (hints & kParameterIsInteger) + else if (hints & kParameterIsInteger) { - DISTRHO_NAMESPACE::snprintf_iparam((char*)ptr, (int32_t)std::round(value), 24); + value = std::round(value); } - else + + const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index)); + + for (uint8_t i = 0; i < enumValues.count; ++i) { - DISTRHO_NAMESPACE::snprintf_param((char*)ptr, value, 24); + if (d_isNotEqual(value, enumValues.values[i].value)) + continue; + + DISTRHO_NAMESPACE::strncpy((char*)ptr, enumValues.values[i].label.buffer(), 24); + return 1; } + if (hints & kParameterIsInteger) + DISTRHO_NAMESPACE::snprintf_iparam((char*)ptr, (int32_t)value, 24); + else + DISTRHO_NAMESPACE::snprintf_param((char*)ptr, value, 24); + return 1; } break; @@ -607,6 +645,7 @@ class PluginVst #if DISTRHO_PLUGIN_WANT_STATE case effGetChunk: + { if (ptr == nullptr) return 0; @@ -616,7 +655,9 @@ class PluginVst fStateChunk = nullptr; } - if (fPlugin.getStateCount() == 0) + const uint32_t paramCount = fPlugin.getParameterCount(); + + if (fPlugin.getStateCount() == 0 && paramCount == 0) { fStateChunk = new char[1]; fStateChunk[0] = '\0'; @@ -650,6 +691,30 @@ class PluginVst chunkStr += tmpStr; } + if (paramCount != 0) + { + // add another separator + chunkStr += "\xff"; + + // temporarily set locale to "C" while converting floats + const ScopedSafeLocale ssl; + + for (uint32_t i=0; i(value); + const char* key = (const char*)ptr; const char* value = nullptr; + size_t size, bytesRead = 0; - for (;;) + while (bytesRead < chunkSize) { if (key[0] == '\0') break; - value = key+(std::strlen(key)+1); + size = std::strlen(key)+1; + value = key + size; + bytesRead += size; setStateFromUI(key, value); @@ -691,7 +762,52 @@ class PluginVst # endif // get next key - key = value+(std::strlen(value)+1); + size = std::strlen(value)+1; + key = value + size; + bytesRead += size; + } + + const uint32_t paramCount = fPlugin.getParameterCount(); + + if (bytesRead+4 < chunkSize && paramCount != 0) + { + ++key; + float fvalue; + + // temporarily set locale to "C" while converting floats + const ScopedSafeLocale ssl; + + while (bytesRead < chunkSize) + { + if (key[0] == '\0') + break; + + size = std::strlen(key)+1; + value = key + size; + bytesRead += size; + + // find parameter with this symbol, and set its value + for (uint32_t i=0; i 4) + return true; + + VstEvents vstEvents; + std::memset(&vstEvents, 0, sizeof(VstEvents)); + + VstMidiEvent vstMidiEvent; + std::memset(&vstMidiEvent, 0, sizeof(VstMidiEvent)); + + vstEvents.numEvents = 1; + vstEvents.events[0] = (VstEvent*)&vstMidiEvent; + + vstMidiEvent.type = kVstMidiType; + vstMidiEvent.byteSize = kVstMidiEventSize; + vstMidiEvent.deltaFrames = midiEvent.frame; + + for (uint8_t i=0; iwriteMidi(midiEvent); + } +#endif + #if DISTRHO_PLUGIN_WANT_STATE // ------------------------------------------------------------------- // functions called from the UI side, may block @@ -977,17 +1168,10 @@ struct VstObject { PluginVst* plugin; }; -#ifdef VESTIGE_HEADER -# define validObject effect != nullptr && effect->ptr3 != nullptr -# define validPlugin effect != nullptr && effect->ptr3 != nullptr && ((VstObject*)effect->ptr3)->plugin != nullptr -# define vstObjectPtr (VstObject*)effect->ptr3 -#else -# define validObject effect != nullptr && effect->object != nullptr -# define validPlugin effect != nullptr && effect->object != nullptr && ((VstObject*)effect->object)->plugin != nullptr -# define vstObjectPtr (VstObject*)effect->object -#endif - -#define pluginPtr (vstObjectPtr)->plugin +#define validObject effect != nullptr && effect->object != nullptr +#define validPlugin effect != nullptr && effect->object != nullptr && ((VstObject*)effect->object)->plugin != nullptr +#define vstObjectPtr (VstObject*)effect->object +#define pluginPtr (vstObjectPtr)->plugin static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) { @@ -1002,7 +1186,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t } // Create dummy plugin to get data from - static PluginExporter plugin; + static PluginExporter plugin(nullptr, nullptr); if (doInternalInit) { @@ -1055,11 +1239,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t /* This code invalidates the object created in VSTPluginMain * Probably not safe against all hosts */ obj->audioMaster = nullptr; -# ifdef VESTIGE_HEADER - effect->ptr3 = nullptr; -# else - vstObjectPtr = nullptr; -# endif + effect->object = nullptr; delete obj; #endif @@ -1083,14 +1263,14 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t return 1; } return 0; - + case effGetParameterProperties: if (ptr != nullptr && index < static_cast(plugin.getParameterCount())) { if (VstParameterProperties* const properties = (VstParameterProperties*)ptr) { memset(properties, 0, sizeof(VstParameterProperties)); - + const uint32_t hints = plugin.getParameterHints(index); if (hints & kParameterIsOutput) @@ -1100,7 +1280,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t { properties->flags |= kVstParameterIsSwitch; } - + if (hints & kParameterIsInteger) { properties->flags |= kVstParameterUsesIntegerMinMax; @@ -1119,7 +1299,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t } } return 0; - + case effGetPlugCategory: #if DISTRHO_PLUGIN_IS_SYNTH return kPlugCategSynth; @@ -1226,20 +1406,18 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) // vst fields effect->magic = kEffectMagic; effect->uniqueID = plugin->getUniqueId(); -#ifdef VESTIGE_HEADER - int32_t* const version = (int32_t*)&effect->unknown1; - *version = plugin->getVersion(); -#else - effect->version = plugin->getVersion(); -#endif + effect->version = plugin->getVersion(); - // VST doesn't support parameter outputs, hide them + // VST doesn't support parameter outputs. we can fake them, but it is a hack. Disabled by default. +#ifdef DPF_VST_SHOW_PARAMETER_OUTPUTS + const int numParams = plugin->getParameterCount(); +#else int numParams = 0; bool outputsReached = false; for (uint32_t i=0, count=plugin->getParameterCount(); i < count; ++i) { - if (! plugin->isParameterOutput(i)) + if (plugin->isParameterInput(i)) { // parameter outputs must be all at the end DISTRHO_SAFE_ASSERT_BREAK(! outputsReached); @@ -1248,6 +1426,7 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) } outputsReached = true; } +#endif // plugin fields effect->numParams = numParams; @@ -1278,13 +1457,11 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) VstObject* const obj(new VstObject()); obj->audioMaster = audioMaster; obj->plugin = nullptr; -#ifdef VESTIGE_HEADER - effect->ptr3 = obj; -#else + + // done effect->object = obj; -#endif return effect; } -// ----------------------------------------------------------------------- \ No newline at end of file +// ----------------------------------------------------------------------- diff --git a/distrho/src/vestige/aeffectx.h b/distrho/src/vestige/vestige.h similarity index 70% rename from distrho/src/vestige/aeffectx.h rename to distrho/src/vestige/vestige.h index 23f84f2e1..56135fd71 100644 --- a/distrho/src/vestige/aeffectx.h +++ b/distrho/src/vestige/vestige.h @@ -1,9 +1,18 @@ /* - * aeffectx.h - simple header to allow VeSTige compilation and eventually work + * IMPORTANT: The author of DPF has no connection with the + * author of the VeSTige VST-compatibility header, has had no + * involvement in its creation. + * + * The VeSTige header is included in this package in the good-faith + * belief that it has been cleanly and legally reverse engineered + * without reference to the official VST SDK and without its + * developer(s) having agreed to the VST SDK license agreement. + */ + +/* + * simple header to allow VeSTige compilation and eventually work * * Copyright (c) 2006 Javier Serrano Polo - * - * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public @@ -22,8 +31,8 @@ * */ #include -#ifndef _AEFFECTX_H -#define _AEFFECTX_H +#ifndef _VESTIGE_H +#define _VESTIGE_H #define CCONST(a, b, c, d)( ( ( (int) a ) << 24 ) | \ ( ( (int) b ) << 16 ) | \ @@ -87,11 +96,11 @@ #define effFlagsIsSynth (1 << 8) // currently unused #define effOpen 0 -#define effClose 1 // currently unused -#define effSetProgram 2 // currently unused -#define effGetProgram 3 // currently unused -#define effGetProgramName 5 // currently unused -#define effGetParamName 8 // currently unused +#define effClose 1 +#define effSetProgram 2 +#define effGetProgram 3 +#define effGetProgramName 5 +#define effGetParamName 8 #define effSetSampleRate 10 #define effSetBlockSize 11 #define effMainsChanged 12 @@ -101,22 +110,42 @@ #define effEditIdle 19 #define effEditTop 20 #define effProcessEvents 25 +#define effGetPlugCategory 35 #define effGetEffectName 45 #define effGetVendorString 47 #define effGetProductString 48 #define effGetVendorVersion 49 -#define effCanDo 51 // currently unused -/* from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ +#define effCanDo 51 +#define effIdle 53 #define effGetParameterProperties 56 -#define effGetVstVersion 58 // currently unused +#define effGetVstVersion 58 +#define effShellGetNextPlugin 70 +#define effStartProcess 71 +#define effStopProcess 72 + +#define effBeginSetProgram 67 +#define effEndSetProgram 68 + +#ifdef WORDS_BIGENDIAN +// "VstP" +#define kEffectMagic 0x50747356 +#else +// "PtsV" +#define kEffectMagic 0x56737450 +#endif -#define kEffectMagic (CCONST( 'V', 's', 't', 'P' )) #define kVstLangEnglish 1 #define kVstMidiType 1 struct RemoteVstPlugin; +#define kVstTransportChanged 1 #define kVstTransportPlaying (1 << 1) +#define kVstTransportCycleActive (1 << 2) +#define kVstTransportRecording (1 << 3) + +#define kVstAutomationWriting (1 << 6) +#define kVstAutomationReading (1 << 7) #define kVstNanosValid (1 << 8) #define kVstPpqPosValid (1 << 9) @@ -174,26 +203,57 @@ struct _VstEvents VstEvent * events[2]; }; +enum Vestige2StringConstants +{ + VestigeMaxNameLen = 64, + VestigeMaxLabelLen = 64, + VestigeMaxShortLabelLen = 8, + VestigeMaxCategLabelLen = 24, + VestigeMaxFileNameLen = 100 +}; + + +enum VstPlugCategory +{ + kPlugCategUnknown = 0, + kPlugCategEffect, + kPlugCategSynth, + kPlugCategAnalysis, + kPlugCategMastering, + kPlugCategSpacializer, + kPlugCategRoomFx, + kPlugSurroundFx, + kPlugCategRestoration, + kPlugCategOfflineProcess, + kPlugCategShell, + kPlugCategGenerator, + kPlugCategMaxCount +}; + typedef struct _VstEvents VstEvents; -/* this struct taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ struct _VstParameterProperties { - float stepFloat; - float smallStepFloat; - float largeStepFloat; - char label[64]; - int32_t flags; - int32_t minInteger; - int32_t maxInteger; - int32_t stepInteger; - int32_t largeStepInteger; - char shortLabel[8]; + float stepFloat; /* float step */ + float smallStepFloat; /* small float step */ + float largeStepFloat; /* large float step */ + char label[VestigeMaxLabelLen]; /* parameter label */ + int32_t flags; /* @see VstParameterFlags */ + int32_t minInteger; /* integer minimum */ + int32_t maxInteger; /* integer maximum */ + int32_t stepInteger; /* integer step */ + int32_t largeStepInteger; /* large integer step */ + char shortLabel[VestigeMaxShortLabelLen]; /* short label, recommended: 6 + delimiter */ + int16_t displayIndex; /* index where this parameter should be displayed (starting with 0) */ + int16_t category; /* 0: no category, else group index + 1 */ + int16_t numParametersInCategory; /* number of parameters in category */ + int16_t reserved; /* zero */ + char categoryLabel[VestigeMaxCategLabelLen]; /* category label, e.g. "Osc 1" */ + char future[16]; /* reserved for future use */ }; typedef struct _VstParameterProperties VstParameterProperties; -/* this enum taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ enum VstParameterFlags { kVstParameterIsSwitch = 1 << 0, /* parameter is a switch (on/off) */ @@ -231,18 +291,19 @@ struct _AEffect // Fill somewhere 28-2b void *ptr1; void *ptr2; - // Zeroes 2c-2f 30-33 34-37 38-3b - char empty3[4 + 4 + 4]; + int initialDelay; + // Zeroes 30-33 34-37 38-3b + char empty2[4 + 4]; // 1.0f 3c-3f float unkown_float; // An object? pointer 40-43 - void *ptr3; + void *object; // Zeroes 44-47 void *user; // Id 48-4b int32_t uniqueID; - // Don't know 4c-4f - char unknown1[4]; + // plugin version 4c-4f + int32_t version; // processReplacing 50-53 void (* processReplacing) (struct _AEffect *, float **, float **, int); };