| @@ -62,7 +62,7 @@ public: | |||
| idle() is called at regular intervals. | |||
| @note This function is meant for standalones only, *never* call this from plugins. | |||
| */ | |||
| void exec(); | |||
| void exec(int idleTime = 10); | |||
| /** | |||
| Quit the application. | |||
| @@ -44,12 +44,12 @@ void Application::idle() | |||
| } | |||
| } | |||
| void Application::exec() | |||
| void Application::exec(int idleTime) | |||
| { | |||
| for (; pData->doLoop;) | |||
| { | |||
| idle(); | |||
| d_msleep(10); | |||
| d_msleep(idleTime); | |||
| } | |||
| } | |||
| @@ -472,8 +472,8 @@ void NanoVG::skewY(float angle) | |||
| void NanoVG::scale(float x, float y) | |||
| { | |||
| if (fContext == nullptr) return; | |||
| DISTRHO_SAFE_ASSERT_RETURN(x > 0.0f,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(y > 0.0f,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(d_isNotZero(x),); | |||
| DISTRHO_SAFE_ASSERT_RETURN(d_isNotZero(y),); | |||
| nvgScale(fContext, x, y); | |||
| } | |||
| @@ -865,7 +865,7 @@ float NanoVG::textBounds(float x, float y, const char* string, const char* end, | |||
| float b[4]; | |||
| const float ret = nvgTextBounds(fContext, x, y, string, end, b); | |||
| bounds = Rectangle<float>(b[0], b[1], b[2], b[3]); | |||
| bounds = Rectangle<float>(b[0], b[1], b[2] - b[0], b[3] - b[1]); | |||
| return ret; | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 | |||
| @@ -63,9 +63,9 @@ struct Widget::PrivateData { | |||
| subWidgets.clear(); | |||
| } | |||
| void display(const uint width, const uint height) | |||
| void display(const uint width, const uint height, const bool renderingSubWidget) | |||
| { | |||
| if (skipDisplay || ! visible) | |||
| if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible) | |||
| return; | |||
| bool needsDisableScissor = false; | |||
| @@ -123,7 +123,7 @@ struct Widget::PrivateData { | |||
| Widget* const widget(*it); | |||
| DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this); | |||
| widget->pData->display(width, height); | |||
| widget->pData->display(width, height, true); | |||
| } | |||
| } | |||
| @@ -33,6 +33,8 @@ | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| # include "pugl/pugl_win.cpp" | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| # define PuglWindow DISTRHO_JOIN_MACRO(PuglWindow, DGL_NAMESPACE) | |||
| # define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE) | |||
| # include "pugl/pugl_osx.m" | |||
| #else | |||
| # include <sys/types.h> | |||
| @@ -721,7 +723,7 @@ struct Window::PrivateData { | |||
| FOR_EACH_WIDGET(it) | |||
| { | |||
| Widget* const widget(*it); | |||
| widget->pData->display(fWidth, fHeight); | |||
| widget->pData->display(fWidth, fHeight, false); | |||
| } | |||
| fSelf->onDisplayAfter(); | |||
| @@ -24,9 +24,6 @@ | |||
| #include "pugl_internal.h" | |||
| #define PuglWindow PuglWindow ## DGL_NAMESPACE | |||
| #define PuglOpenGLView PuglOpenGLView ## DGL_NAMESPACE | |||
| @interface PuglWindow : NSWindow | |||
| { | |||
| @public | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 @@ public: | |||
| 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 | |||
| @@ -90,10 +90,11 @@ public: | |||
| void setState(const char* key, const char* value); | |||
| #endif | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| /** | |||
| sendNote. | |||
| @TODO Document this. | |||
| @note Work in progress. Implemented for DSSI and LV2 formats. | |||
| */ | |||
| void sendNote(uint8_t channel, uint8_t note, uint8_t velocity); | |||
| #endif | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 @@ public: | |||
| 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 @@ public: | |||
| 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 @@ public: | |||
| 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 @@ private: | |||
| // ------------------------------------------------------------------- | |||
| // 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 | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 @@ public: | |||
| 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) | |||
| { | |||
| @@ -196,11 +204,24 @@ public: | |||
| fLastOutputValues = nullptr; | |||
| } | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| if (fParametersChanged != nullptr) | |||
| { | |||
| delete[] fParametersChanged; | |||
| fParametersChanged = nullptr; | |||
| } | |||
| #endif | |||
| fPlugin.deactivate(); | |||
| 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 +351,11 @@ protected: | |||
| 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 +434,12 @@ protected: | |||
| #else | |||
| fPlugin.run(audioIns, audioOuts, nframes); | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| fPortMidiOutBuffer = nullptr; | |||
| #endif | |||
| updateParameterTriggers(); | |||
| } | |||
| void jackShutdown() | |||
| @@ -440,6 +472,35 @@ protected: | |||
| } | |||
| #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 +518,10 @@ private: | |||
| 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 +540,58 @@ private: | |||
| // ------------------------------------------------------------------- | |||
| // 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 | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 @@ public: | |||
| { | |||
| // pre-roll | |||
| if (sampleCount == 0) | |||
| return updateParameterOutputs(); | |||
| return updateParameterOutputsAndTriggers(); | |||
| // Check for updated parameters | |||
| float curValue; | |||
| @@ -183,7 +187,7 @@ public: | |||
| 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 @@ public: | |||
| 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 @@ private: | |||
| // ------------------------------------------------------------------- | |||
| 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 @@ public: | |||
| // 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; | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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<const String, String> 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 @@ public: | |||
| #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 @@ public: | |||
| #if DISTRHO_LV2_USE_EVENTS_OUT | |||
| if (port == index++) | |||
| { | |||
| fPortEventsOut = (LV2_Atom_Sequence*)dataLocation; | |||
| fEventsOutData.port = (LV2_Atom_Sequence*)dataLocation; | |||
| return; | |||
| } | |||
| #endif | |||
| @@ -517,14 +519,12 @@ public: | |||
| 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); | |||
| } | |||
| @@ -607,20 +607,14 @@ public: | |||
| #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 @@ public: | |||
| break; | |||
| // reserve msg space | |||
| // FIXME create a large enough buffer beforehand | |||
| char msgBuf[msgSize]; | |||
| std::memset(msgBuf, 0, msgSize); | |||
| @@ -653,21 +648,23 @@ public: | |||
| 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 @@ private: | |||
| #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 @@ private: | |||
| } 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 @@ private: | |||
| } | |||
| #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 @@ private: | |||
| *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; | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2017 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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: <http://moddevices.com/ns/mod#> .\n"; | |||
| #endif | |||
| pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; | |||
| pluginString += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"; | |||
| pluginString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\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"; | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 <clocale> | |||
| #include <map> | |||
| #include <string> | |||
| #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<const String, String> StringMap; | |||
| static const int kVstMidiEventSize = static_cast<int>(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 @@ public: | |||
| } | |||
| } | |||
| 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 @@ private: | |||
| // Vst stuff | |||
| const audioMasterCallback fAudioMaster; | |||
| AEffect* const fEffect; | |||
| UiHelper* const fUiHelper; | |||
| ParameterCheckHelper* const fUiHelper; | |||
| PluginExporter* const fPlugin; | |||
| // Plugin UI | |||
| @@ -343,15 +373,12 @@ private: | |||
| // ----------------------------------------------------------------------- | |||
| #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 @@ public: | |||
| 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 @@ public: | |||
| { | |||
| 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 @@ public: | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| case effGetChunk: | |||
| { | |||
| if (ptr == nullptr) | |||
| return 0; | |||
| @@ -616,7 +655,9 @@ public: | |||
| 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 @@ public: | |||
| 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<paramCount; ++i) | |||
| { | |||
| if (fPlugin.isParameterOutputOrTrigger(i)) | |||
| continue; | |||
| // join key and value | |||
| String tmpStr; | |||
| tmpStr = fPlugin.getParameterSymbol(i); | |||
| tmpStr += "\xff"; | |||
| tmpStr += String(fPlugin.getParameterValue(i)); | |||
| tmpStr += "\xff"; | |||
| chunkStr += tmpStr; | |||
| } | |||
| } | |||
| const std::size_t chunkSize(chunkStr.length()+1); | |||
| fStateChunk = new char[chunkSize]; | |||
| @@ -667,21 +732,27 @@ public: | |||
| *(void**)ptr = fStateChunk; | |||
| return ret; | |||
| } | |||
| case effSetChunk: | |||
| { | |||
| if (value <= 1 || ptr == nullptr) | |||
| return 0; | |||
| const size_t chunkSize = static_cast<size_t>(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 @@ public: | |||
| # 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<paramCount; ++i) | |||
| { | |||
| if (fPlugin.isParameterOutputOrTrigger(i)) | |||
| continue; | |||
| if (fPlugin.getParameterSymbol(i) != key) | |||
| continue; | |||
| fvalue = std::atof(value); | |||
| fPlugin.setParameterValue(i, fvalue); | |||
| # if DISTRHO_PLUGIN_HAS_UI | |||
| if (fVstUI != nullptr) | |||
| setParameterValueFromPlugin(i, fvalue); | |||
| # endif | |||
| break; | |||
| } | |||
| // get next key | |||
| size = std::strlen(value)+1; | |||
| key = value + size; | |||
| bytesRead += size; | |||
| } | |||
| } | |||
| return 1; | |||
| @@ -805,7 +921,10 @@ public: | |||
| void vst_processReplacing(const float** const inputs, float** const outputs, const int32_t sampleFrames) | |||
| { | |||
| if (sampleFrames <= 0) | |||
| { | |||
| updateParameterOutputsAndTriggers(); | |||
| return; | |||
| } | |||
| if (! fPlugin.isActive()) | |||
| { | |||
| @@ -864,16 +983,7 @@ public: | |||
| fPlugin.run(inputs, outputs, sampleFrames); | |||
| #endif | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| if (fVstUI == nullptr) | |||
| return; | |||
| for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||
| { | |||
| if (fPlugin.isParameterOutput(i)) | |||
| setParameterValueFromPlugin(i, fPlugin.getParameterValue(i)); | |||
| } | |||
| #endif | |||
| updateParameterOutputsAndTriggers(); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| @@ -881,13 +991,13 @@ public: | |||
| friend class UIVst; | |||
| private: | |||
| // Plugin | |||
| PluginExporter fPlugin; | |||
| // VST stuff | |||
| const audioMasterCallback fAudioMaster; | |||
| AEffect* const fEffect; | |||
| // Plugin | |||
| PluginExporter fPlugin; | |||
| // Temporary data | |||
| char fProgramName[32+1]; | |||
| @@ -929,6 +1039,56 @@ private: | |||
| // ------------------------------------------------------------------- | |||
| // functions called from the plugin side, RT no block | |||
| void updateParameterOutputsAndTriggers() | |||
| { | |||
| float curValue; | |||
| for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |||
| { | |||
| if (fPlugin.isParameterOutput(i)) | |||
| { | |||
| // NOTE: no output parameter support in VST, simulate it here | |||
| curValue = fPlugin.getParameterValue(i); | |||
| if (d_isEqual(curValue, parameterValues[i])) | |||
| continue; | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| if (fVstUI != nullptr) | |||
| setParameterValueFromPlugin(i, curValue); | |||
| else | |||
| #endif | |||
| parameterValues[i] = curValue; | |||
| #ifndef DPF_VST_SHOW_PARAMETER_OUTPUTS | |||
| // skip automating parameter outputs from plugin if we disable them on VST | |||
| continue; | |||
| #endif | |||
| } | |||
| else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger) | |||
| { | |||
| // NOTE: no trigger support in VST parameters, simulate it here | |||
| curValue = fPlugin.getParameterValue(i); | |||
| if (d_isEqual(curValue, fPlugin.getParameterRanges(i).def)) | |||
| continue; | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| if (fVstUI != nullptr) | |||
| setParameterValueFromPlugin(i, curValue); | |||
| #endif | |||
| fPlugin.setParameterValue(i, curValue); | |||
| } | |||
| else | |||
| { | |||
| continue; | |||
| } | |||
| const ParameterRanges& ranges(fPlugin.getParameterRanges(i)); | |||
| hostCallback(audioMasterAutomate, i, 0, nullptr, ranges.getNormalizedValue(curValue)); | |||
| } | |||
| } | |||
| #if DISTRHO_PLUGIN_HAS_UI | |||
| void setParameterValueFromPlugin(const uint32_t index, const float realValue) | |||
| { | |||
| @@ -937,6 +1097,37 @@ private: | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
| bool writeMidi(const MidiEvent& midiEvent) | |||
| { | |||
| if (midiEvent.size > 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; i<midiEvent.size; ++i) | |||
| vstMidiEvent.midiData[i] = midiEvent.data[i]; | |||
| return hostCallback(audioMasterProcessEvents, 0, 0, &vstEvents) == 1; | |||
| } | |||
| static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) | |||
| { | |||
| return ((PluginVst*)ptr)->writeMidi(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<int32_t>(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,11 +1457,9 @@ 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; | |||
| } | |||
| @@ -82,7 +82,7 @@ void UI::setState(const char* key, const char* value) | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_IS_SYNTH | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) | |||
| { | |||
| pData->sendNoteCallback(channel, note, velocity); | |||
| @@ -26,6 +26,10 @@ | |||
| START_NAMESPACE_DISTRHO | |||
| #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| static const sendNoteFunc sendNoteCallback = nullptr; | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| struct OscData { | |||
| @@ -180,6 +184,7 @@ protected: | |||
| fOscData.send_configure(key, value); | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |||
| { | |||
| if (fOscData.server == nullptr) | |||
| @@ -187,11 +192,15 @@ protected: | |||
| if (channel > 0xF) | |||
| return; | |||
| uint8_t mdata[4] = { 0, channel, note, velocity }; | |||
| mdata[1] += (velocity != 0) ? 0x90 : 0x80; | |||
| uint8_t mdata[4] = { | |||
| 0, | |||
| channel + (velocity != 0 ? 0x90 : 0x80), | |||
| note, | |||
| velocity | |||
| }; | |||
| fOscData.send_midi(mdata); | |||
| } | |||
| #endif | |||
| void setSize(const uint width, const uint height) | |||
| { | |||
| @@ -219,10 +228,12 @@ private: | |||
| uiPtr->setState(key, value); | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |||
| { | |||
| uiPtr->sendNote(channel, note, velocity); | |||
| } | |||
| #endif | |||
| static void setSizeCallback(void* ptr, uint width, uint height) | |||
| { | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 | |||
| @@ -61,12 +61,12 @@ struct UI::PrivateData { | |||
| #endif | |||
| // Callbacks | |||
| void* callbacksPtr; | |||
| editParamFunc editParamCallbackFunc; | |||
| setParamFunc setParamCallbackFunc; | |||
| setStateFunc setStateCallbackFunc; | |||
| sendNoteFunc sendNoteCallbackFunc; | |||
| setSizeFunc setSizeCallbackFunc; | |||
| void* ptr; | |||
| PrivateData() noexcept | |||
| : sampleRate(d_lastUiSampleRate), | |||
| @@ -74,12 +74,12 @@ struct UI::PrivateData { | |||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
| dspPtr(d_lastUiDspPtr), | |||
| #endif | |||
| callbacksPtr(nullptr), | |||
| editParamCallbackFunc(nullptr), | |||
| setParamCallbackFunc(nullptr), | |||
| setStateCallbackFunc(nullptr), | |||
| sendNoteCallbackFunc(nullptr), | |||
| setSizeCallbackFunc(nullptr), | |||
| ptr(nullptr) | |||
| setSizeCallbackFunc(nullptr) | |||
| { | |||
| DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); | |||
| @@ -103,31 +103,31 @@ struct UI::PrivateData { | |||
| void editParamCallback(const uint32_t rindex, const bool started) | |||
| { | |||
| if (editParamCallbackFunc != nullptr) | |||
| editParamCallbackFunc(ptr, rindex, started); | |||
| editParamCallbackFunc(callbacksPtr, rindex, started); | |||
| } | |||
| void setParamCallback(const uint32_t rindex, const float value) | |||
| { | |||
| if (setParamCallbackFunc != nullptr) | |||
| setParamCallbackFunc(ptr, rindex, value); | |||
| setParamCallbackFunc(callbacksPtr, rindex, value); | |||
| } | |||
| void setStateCallback(const char* const key, const char* const value) | |||
| { | |||
| if (setStateCallbackFunc != nullptr) | |||
| setStateCallbackFunc(ptr, key, value); | |||
| setStateCallbackFunc(callbacksPtr, key, value); | |||
| } | |||
| void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |||
| { | |||
| if (sendNoteCallbackFunc != nullptr) | |||
| sendNoteCallbackFunc(ptr, channel, note, velocity); | |||
| sendNoteCallbackFunc(callbacksPtr, channel, note, velocity); | |||
| } | |||
| void setSizeCallback(const uint width, const uint height) | |||
| { | |||
| if (setSizeCallbackFunc != nullptr) | |||
| setSizeCallbackFunc(ptr, width, height); | |||
| setSizeCallbackFunc(callbacksPtr, width, height); | |||
| } | |||
| }; | |||
| @@ -221,8 +221,13 @@ UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const char* const | |||
| class UIExporter | |||
| { | |||
| public: | |||
| UIExporter(void* const ptr, const intptr_t winId, | |||
| const editParamFunc editParamCall, const setParamFunc setParamCall, const setStateFunc setStateCall, const sendNoteFunc sendNoteCall, const setSizeFunc setSizeCall, | |||
| UIExporter(void* const callbacksPtr, | |||
| const intptr_t winId, | |||
| const editParamFunc editParamCall, | |||
| const setParamFunc setParamCall, | |||
| const setStateFunc setStateCall, | |||
| const sendNoteFunc sendNoteCall, | |||
| const setSizeFunc setSizeCall, | |||
| void* const dspPtr = nullptr, | |||
| const char* const bundlePath = nullptr) | |||
| #ifdef HAVE_DGL | |||
| @@ -238,7 +243,7 @@ public: | |||
| DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||
| fData->ptr = ptr; | |||
| fData->callbacksPtr = callbacksPtr; | |||
| fData->editParamCallbackFunc = editParamCall; | |||
| fData->setParamCallbackFunc = setParamCall; | |||
| fData->setStateCallbackFunc = setStateCall; | |||
| @@ -22,6 +22,7 @@ | |||
| #include "lv2/atom-util.h" | |||
| #include "lv2/data-access.h" | |||
| #include "lv2/instance-access.h" | |||
| #include "lv2/midi.h" | |||
| #include "lv2/options.h" | |||
| #include "lv2/parameters.h" | |||
| #include "lv2/ui.h" | |||
| @@ -35,6 +36,15 @@ | |||
| START_NAMESPACE_DISTRHO | |||
| typedef struct _LV2_Atom_MidiEvent { | |||
| LV2_Atom atom; /**< Atom header. */ | |||
| uint8_t data[3]; /**< MIDI data (body). */ | |||
| } LV2_Atom_MidiEvent; | |||
| #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| static const sendNoteFunc sendNoteCallback = nullptr; | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| class UiLv2 | |||
| @@ -51,6 +61,7 @@ public: | |||
| fController(controller), | |||
| fWriteFunction(writeFunc), | |||
| fEventTransferURID(uridMap->map(uridMap->handle, LV2_ATOM__eventTransfer)), | |||
| fMidiEventURID(uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent)), | |||
| fKeyValueURID(uridMap->map(uridMap->handle, DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||
| fWinIdWasNull(winId == 0) | |||
| { | |||
| @@ -256,9 +267,28 @@ protected: | |||
| fWriteFunction(fController, eventInPortIndex, atomSize, fEventTransferURID, atom); | |||
| } | |||
| void sendNote(const uint8_t /*channel*/, const uint8_t /*note*/, const uint8_t /*velocity*/) | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | |||
| if (channel > 0xF) | |||
| return; | |||
| const uint32_t eventInPortIndex(DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS); | |||
| LV2_Atom_MidiEvent atomMidiEvent; | |||
| atomMidiEvent.atom.size = 3; | |||
| atomMidiEvent.atom.type = fMidiEventURID; | |||
| atomMidiEvent.data[0] = channel + (velocity != 0 ? 0x90 : 0x80); | |||
| atomMidiEvent.data[1] = note; | |||
| atomMidiEvent.data[2] = velocity; | |||
| // send to DSP side | |||
| fWriteFunction(fController, eventInPortIndex, sizeof(LV2_Atom_MidiEvent), fEventTransferURID, &atomMidiEvent); | |||
| } | |||
| #endif | |||
| void setSize(const uint width, const uint height) | |||
| { | |||
| @@ -282,6 +312,7 @@ private: | |||
| // Need to save this | |||
| const LV2_URID fEventTransferURID; | |||
| const LV2_URID fMidiEventURID; | |||
| const LV2_URID fKeyValueURID; | |||
| // using ui:showInterface if true | |||
| @@ -307,10 +338,12 @@ private: | |||
| uiPtr->setState(key, value); | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
| static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |||
| { | |||
| uiPtr->sendNote(channel, note, velocity); | |||
| } | |||
| #endif | |||
| static void setSizeCallback(void* ptr, uint width, uint height) | |||
| { | |||
| @@ -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 <jasp00/at/users.sourceforge.net> | |||
| * | |||
| * 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 <stdint.h> | |||
| #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); | |||
| }; | |||
| @@ -9,13 +9,13 @@ else | |||
| exit | |||
| fi | |||
| PWD=`dirname $0` | |||
| PWD="$(dirname "$0")" | |||
| if [ -f $PWD/lv2_ttl_generator.exe ]; then | |||
| GEN=$PWD/lv2_ttl_generator.exe | |||
| if [ -f "$PWD/lv2_ttl_generator.exe" ]; then | |||
| GEN="$PWD/lv2_ttl_generator.exe" | |||
| EXT=dll | |||
| else | |||
| GEN=$PWD/lv2_ttl_generator | |||
| GEN="$PWD/lv2_ttl_generator" | |||
| if [ -d /Library/Audio ]; then | |||
| EXT=dylib | |||
| else | |||
| @@ -27,7 +27,7 @@ FOLDERS=`find . -type d -name \*.lv2` | |||
| for i in $FOLDERS; do | |||
| cd $i | |||
| FILE=`ls *.$EXT | sort | head -n 1` | |||
| $GEN ./$FILE | |||
| FILE="$(ls *.$EXT | sort | head -n 1)" | |||
| "$GEN" "./$FILE" | |||
| cd .. | |||
| done | |||