@@ -62,7 +62,7 @@ public: | |||||
idle() is called at regular intervals. | idle() is called at regular intervals. | ||||
@note This function is meant for standalones only, *never* call this from plugins. | @note This function is meant for standalones only, *never* call this from plugins. | ||||
*/ | */ | ||||
void exec(); | |||||
void exec(int idleTime = 10); | |||||
/** | /** | ||||
Quit the application. | Quit the application. | ||||
@@ -44,12 +44,12 @@ void Application::idle() | |||||
} | } | ||||
} | } | ||||
void Application::exec() | |||||
void Application::exec(int idleTime) | |||||
{ | { | ||||
for (; pData->doLoop;) | for (; pData->doLoop;) | ||||
{ | { | ||||
idle(); | idle(); | ||||
d_msleep(10); | |||||
d_msleep(idleTime); | |||||
} | } | ||||
} | } | ||||
@@ -472,8 +472,8 @@ void NanoVG::skewY(float angle) | |||||
void NanoVG::scale(float x, float y) | void NanoVG::scale(float x, float y) | ||||
{ | { | ||||
if (fContext == nullptr) return; | 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); | nvgScale(fContext, x, y); | ||||
} | } | ||||
@@ -865,7 +865,7 @@ float NanoVG::textBounds(float x, float y, const char* string, const char* end, | |||||
float b[4]; | float b[4]; | ||||
const float ret = nvgTextBounds(fContext, x, y, string, end, b); | 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; | return ret; | ||||
} | } | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -63,9 +63,9 @@ struct Widget::PrivateData { | |||||
subWidgets.clear(); | 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; | return; | ||||
bool needsDisableScissor = false; | bool needsDisableScissor = false; | ||||
@@ -123,7 +123,7 @@ struct Widget::PrivateData { | |||||
Widget* const widget(*it); | Widget* const widget(*it); | ||||
DISTRHO_SAFE_ASSERT_CONTINUE(widget->pData != this); | 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) | #if defined(DISTRHO_OS_WINDOWS) | ||||
# include "pugl/pugl_win.cpp" | # include "pugl/pugl_win.cpp" | ||||
#elif defined(DISTRHO_OS_MAC) | #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" | # include "pugl/pugl_osx.m" | ||||
#else | #else | ||||
# include <sys/types.h> | # include <sys/types.h> | ||||
@@ -721,7 +723,7 @@ struct Window::PrivateData { | |||||
FOR_EACH_WIDGET(it) | FOR_EACH_WIDGET(it) | ||||
{ | { | ||||
Widget* const widget(*it); | Widget* const widget(*it); | ||||
widget->pData->display(fWidth, fHeight); | |||||
widget->pData->display(fWidth, fHeight, false); | |||||
} | } | ||||
fSelf->onDisplayAfter(); | fSelf->onDisplayAfter(); | ||||
@@ -24,9 +24,6 @@ | |||||
#include "pugl_internal.h" | #include "pugl_internal.h" | ||||
#define PuglWindow PuglWindow ## DGL_NAMESPACE | |||||
#define PuglOpenGLView PuglOpenGLView ## DGL_NAMESPACE | |||||
@interface PuglWindow : NSWindow | @interface PuglWindow : NSWindow | ||||
{ | { | ||||
@public | @public | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * 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; | 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. | Parameter. | ||||
*/ | */ | ||||
@@ -312,6 +405,12 @@ struct Parameter { | |||||
*/ | */ | ||||
ParameterRanges ranges; | ParameterRanges ranges; | ||||
/** | |||||
Enumeration values.@n | |||||
Can be used to give meaning to parameter values, working as an enumeration. | |||||
*/ | |||||
ParameterEnumerationValues enumValues; | |||||
/** | /** | ||||
Designation for this parameter. | Designation for this parameter. | ||||
*/ | */ | ||||
@@ -334,6 +433,7 @@ struct Parameter { | |||||
symbol(), | symbol(), | ||||
unit(), | unit(), | ||||
ranges(), | ranges(), | ||||
enumValues(), | |||||
designation(kParameterDesignationNull), | designation(kParameterDesignationNull), | ||||
midiCC(0) {} | midiCC(0) {} | ||||
@@ -346,6 +446,7 @@ struct Parameter { | |||||
symbol(s), | symbol(s), | ||||
unit(u), | unit(u), | ||||
ranges(def, min, max), | ranges(def, min, max), | ||||
enumValues(), | |||||
designation(kParameterDesignationNull), | designation(kParameterDesignationNull), | ||||
midiCC(0) {} | midiCC(0) {} | ||||
@@ -593,9 +694,6 @@ public: | |||||
Write a MIDI output event.@n | Write a MIDI output event.@n | ||||
This function must only be called during run().@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(). | 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; | bool writeMidiEvent(const MidiEvent& midiEvent) noexcept; | ||||
#endif | #endif | ||||
@@ -90,10 +90,11 @@ public: | |||||
void setState(const char* key, const char* value); | void setState(const char* key, const char* value); | ||||
#endif | #endif | ||||
#if DISTRHO_PLUGIN_IS_SYNTH | |||||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
/** | /** | ||||
sendNote. | sendNote. | ||||
@TODO Document this. | @TODO Document this. | ||||
@note Work in progress. Implemented for DSSI and LV2 formats. | |||||
*/ | */ | ||||
void sendNote(uint8_t channel, uint8_t note, uint8_t velocity); | void sendNote(uint8_t channel, uint8_t note, uint8_t velocity); | ||||
#endif | #endif | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * 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 */ | * 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 */ | * Plugin */ | ||||
@@ -102,10 +103,9 @@ void Plugin::setLatency(uint32_t frames) noexcept | |||||
#endif | #endif | ||||
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | #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 | #endif | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * 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 uint32_t d_lastBufferSize; | ||||
extern double d_lastSampleRate; | extern double d_lastSampleRate; | ||||
// ----------------------------------------------------------------------- | |||||
// DSP callbacks | |||||
typedef bool (*writeMidiFunc) (void* ptr, const MidiEvent& midiEvent); | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Plugin private data | // Plugin private data | ||||
@@ -65,6 +70,10 @@ struct Plugin::PrivateData { | |||||
TimePosition timePosition; | TimePosition timePosition; | ||||
#endif | #endif | ||||
// Callbacks | |||||
void* callbacksPtr; | |||||
writeMidiFunc writeMidiCallbackFunc; | |||||
uint32_t bufferSize; | uint32_t bufferSize; | ||||
double sampleRate; | double sampleRate; | ||||
@@ -88,6 +97,8 @@ struct Plugin::PrivateData { | |||||
#if DISTRHO_PLUGIN_WANT_LATENCY | #if DISTRHO_PLUGIN_WANT_LATENCY | ||||
latency(0), | latency(0), | ||||
#endif | #endif | ||||
callbacksPtr(nullptr), | |||||
writeMidiCallbackFunc(nullptr), | |||||
bufferSize(d_lastBufferSize), | bufferSize(d_lastBufferSize), | ||||
sampleRate(d_lastSampleRate) | sampleRate(d_lastSampleRate) | ||||
{ | { | ||||
@@ -149,6 +160,16 @@ struct Plugin::PrivateData { | |||||
} | } | ||||
#endif | #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 | class PluginExporter | ||||
{ | { | ||||
public: | public: | ||||
PluginExporter() | |||||
PluginExporter(void* const callbacksPtr, const writeMidiFunc writeMidiCall) | |||||
: fPlugin(createPlugin()), | : fPlugin(createPlugin()), | ||||
fData((fPlugin != nullptr) ? fPlugin->pData : nullptr), | fData((fPlugin != nullptr) ? fPlugin->pData : nullptr), | ||||
fIsActive(false) | fIsActive(false) | ||||
@@ -191,6 +212,9 @@ public: | |||||
for (uint32_t i=0, count=fData->stateCount; i < count; ++i) | for (uint32_t i=0, count=fData->stateCount; i < count; ++i) | ||||
fPlugin->initState(i, fData->stateKeys[i], fData->stateDefValues[i]); | fPlugin->initState(i, fData->stateKeys[i], fData->stateDefValues[i]); | ||||
#endif | #endif | ||||
fData->callbacksPtr = callbacksPtr; | |||||
fData->writeMidiCallbackFunc = writeMidiCall; | |||||
} | } | ||||
~PluginExporter() | ~PluginExporter() | ||||
@@ -322,9 +346,26 @@ public: | |||||
return fData->parameters[index].designation; | 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 | 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 | const String& getParameterName(const uint32_t index) const noexcept | ||||
@@ -348,6 +389,13 @@ public: | |||||
return fData->parameters[index].unit; | 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 | const ParameterRanges& getParameterRanges(const uint32_t index) const noexcept | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackRanges); | DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->parameterCount, sFallbackRanges); | ||||
@@ -601,9 +649,10 @@ private: | |||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
// Static fallback data, see DistrhoPlugin.cpp | // 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_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginExporter) | ||||
DISTRHO_PREVENT_HEAP_ALLOCATION | DISTRHO_PREVENT_HEAP_ALLOCATION | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * This program is free software; you can redistribute it and/or | ||||
* modify it under the terms of the GNU Lesser General Public | * 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 | #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_STATE | ||||
static const setStateFunc setStateCallback = nullptr; | static const setStateFunc setStateCallback = nullptr; | ||||
#endif | #endif | ||||
#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
static const writeMidiFunc writeMidiCallback = nullptr; | |||||
#endif | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -91,7 +94,7 @@ class PluginJack | |||||
{ | { | ||||
public: | public: | ||||
PluginJack(jack_client_t* const client) | PluginJack(jack_client_t* const client) | ||||
: fPlugin(), | |||||
: fPlugin(this, writeMidiCallback), | |||||
#if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, nullptr, setSizeCallback, fPlugin.getInstancePointer()), | fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, nullptr, setSizeCallback, fPlugin.getInstancePointer()), | ||||
#endif | #endif | ||||
@@ -119,6 +122,11 @@ public: | |||||
fPortEventsIn = jack_port_register(fClient, "events-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); | 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 DISTRHO_PLUGIN_WANT_PROGRAMS | ||||
if (fPlugin.getProgramCount() > 0) | if (fPlugin.getProgramCount() > 0) | ||||
{ | { | ||||
@@ -196,11 +204,24 @@ public: | |||||
fLastOutputValues = nullptr; | fLastOutputValues = nullptr; | ||||
} | } | ||||
#if DISTRHO_PLUGIN_HAS_UI | |||||
if (fParametersChanged != nullptr) | |||||
{ | |||||
delete[] fParametersChanged; | |||||
fParametersChanged = nullptr; | |||||
} | |||||
#endif | |||||
fPlugin.deactivate(); | fPlugin.deactivate(); | ||||
if (fClient == nullptr) | if (fClient == nullptr) | ||||
return; | return; | ||||
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
jack_port_unregister(fClient, fPortMidiOut); | |||||
fPortMidiOut = nullptr; | |||||
#endif | |||||
jack_port_unregister(fClient, fPortEventsIn); | jack_port_unregister(fClient, fPortEventsIn); | ||||
fPortEventsIn = nullptr; | fPortEventsIn = nullptr; | ||||
@@ -330,6 +351,11 @@ protected: | |||||
void* const midiBuf = jack_port_get_buffer(fPortEventsIn, nframes); | 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 (const uint32_t eventCount = jack_midi_get_event_count(midiBuf)) | ||||
{ | { | ||||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | ||||
@@ -408,6 +434,12 @@ protected: | |||||
#else | #else | ||||
fPlugin.run(audioIns, audioOuts, nframes); | fPlugin.run(audioIns, audioOuts, nframes); | ||||
#endif | #endif | ||||
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
fPortMidiOutBuffer = nullptr; | |||||
#endif | |||||
updateParameterTriggers(); | |||||
} | } | ||||
void jackShutdown() | void jackShutdown() | ||||
@@ -440,6 +472,35 @@ protected: | |||||
} | } | ||||
#endif | #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: | private: | ||||
@@ -457,6 +518,10 @@ private: | |||||
jack_port_t* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | jack_port_t* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | ||||
#endif | #endif | ||||
jack_port_t* fPortEventsIn; | jack_port_t* fPortEventsIn; | ||||
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
jack_port_t* fPortMidiOut; | |||||
void* fPortMidiOutBuffer; | |||||
#endif | |||||
#if DISTRHO_PLUGIN_WANT_TIMEPOS | #if DISTRHO_PLUGIN_WANT_TIMEPOS | ||||
TimePosition fTimePosition; | TimePosition fTimePosition; | ||||
#endif | #endif | ||||
@@ -475,51 +540,58 @@ private: | |||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
// Callbacks | // Callbacks | ||||
#define uiPtr ((PluginJack*)ptr) | |||||
#define thisPtr ((PluginJack*)ptr) | |||||
static int jackBufferSizeCallback(jack_nframes_t nframes, void* ptr) | static int jackBufferSizeCallback(jack_nframes_t nframes, void* ptr) | ||||
{ | { | ||||
uiPtr->jackBufferSize(nframes); | |||||
thisPtr->jackBufferSize(nframes); | |||||
return 0; | return 0; | ||||
} | } | ||||
static int jackSampleRateCallback(jack_nframes_t nframes, void* ptr) | static int jackSampleRateCallback(jack_nframes_t nframes, void* ptr) | ||||
{ | { | ||||
uiPtr->jackSampleRate(nframes); | |||||
thisPtr->jackSampleRate(nframes); | |||||
return 0; | return 0; | ||||
} | } | ||||
static int jackProcessCallback(jack_nframes_t nframes, void* ptr) | static int jackProcessCallback(jack_nframes_t nframes, void* ptr) | ||||
{ | { | ||||
uiPtr->jackProcess(nframes); | |||||
thisPtr->jackProcess(nframes); | |||||
return 0; | return 0; | ||||
} | } | ||||
static void jackShutdownCallback(void* ptr) | static void jackShutdownCallback(void* ptr) | ||||
{ | { | ||||
uiPtr->jackShutdown(); | |||||
thisPtr->jackShutdown(); | |||||
} | } | ||||
static void setParameterValueCallback(void* ptr, uint32_t index, float value) | static void setParameterValueCallback(void* ptr, uint32_t index, float value) | ||||
{ | { | ||||
uiPtr->setParameterValue(index, value); | |||||
thisPtr->setParameterValue(index, value); | |||||
} | } | ||||
#if DISTRHO_PLUGIN_WANT_STATE | #if DISTRHO_PLUGIN_WANT_STATE | ||||
static void setStateCallback(void* ptr, const char* key, const char* value) | static void setStateCallback(void* ptr, const char* key, const char* value) | ||||
{ | { | ||||
uiPtr->setState(key, value); | |||||
thisPtr->setState(key, value); | |||||
} | } | ||||
#endif | #endif | ||||
#if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
static void setSizeCallback(void* ptr, uint width, uint height) | 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 | #endif | ||||
#undef uiPtr | |||||
#undef thisPtr | |||||
}; | }; | ||||
END_NAMESPACE_DISTRHO | END_NAMESPACE_DISTRHO | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -22,9 +22,12 @@ | |||||
#ifdef DISTRHO_PLUGIN_TARGET_DSSI | #ifdef DISTRHO_PLUGIN_TARGET_DSSI | ||||
# include "dssi/dssi.h" | # include "dssi/dssi.h" | ||||
# if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
# error DSSI does not support MIDI output | |||||
# endif | |||||
#else | #else | ||||
# include "ladspa/ladspa.h" | # 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 | # error Cannot use MIDI with LADSPA | ||||
# endif | # endif | ||||
# if DISTRHO_PLUGIN_WANT_STATE | # if DISTRHO_PLUGIN_WANT_STATE | ||||
@@ -44,7 +47,8 @@ class PluginLadspaDssi | |||||
{ | { | ||||
public: | public: | ||||
PluginLadspaDssi() | PluginLadspaDssi() | ||||
: fPortControls(nullptr), | |||||
: fPlugin(nullptr, nullptr), | |||||
fPortControls(nullptr), | |||||
fLastControlValues(nullptr) | fLastControlValues(nullptr) | ||||
{ | { | ||||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | ||||
@@ -171,7 +175,7 @@ public: | |||||
{ | { | ||||
// pre-roll | // pre-roll | ||||
if (sampleCount == 0) | if (sampleCount == 0) | ||||
return updateParameterOutputs(); | |||||
return updateParameterOutputsAndTriggers(); | |||||
// Check for updated parameters | // Check for updated parameters | ||||
float curValue; | float curValue; | ||||
@@ -183,7 +187,7 @@ public: | |||||
curValue = *fPortControls[i]; | curValue = *fPortControls[i]; | ||||
if (fLastControlValues[i] != curValue && ! fPlugin.isParameterOutput(i)) | |||||
if (fPlugin.isParameterInput(i) && d_isNotEqual(fLastControlValues[i], curValue)) | |||||
{ | { | ||||
fLastControlValues[i] = curValue; | fLastControlValues[i] = curValue; | ||||
fPlugin.setParameterValue(i, curValue); | fPlugin.setParameterValue(i, curValue); | ||||
@@ -268,7 +272,7 @@ public: | |||||
fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); | fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); | ||||
#endif | #endif | ||||
updateParameterOutputs(); | |||||
updateParameterOutputsAndTriggers(); | |||||
#if defined(DISTRHO_PLUGIN_TARGET_DSSI) && ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | #if defined(DISTRHO_PLUGIN_TARGET_DSSI) && ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | ||||
return; // unused | return; // unused | ||||
@@ -371,17 +375,33 @@ private: | |||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
void updateParameterOutputs() | |||||
void updateParameterOutputsAndTriggers() | |||||
{ | { | ||||
float value; | |||||
for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | 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 | #if DISTRHO_PLUGIN_WANT_LATENCY | ||||
@@ -531,7 +551,7 @@ public: | |||||
// Create dummy plugin to get data from | // Create dummy plugin to get data from | ||||
d_lastBufferSize = 512; | d_lastBufferSize = 512; | ||||
d_lastSampleRate = 44100.0; | d_lastSampleRate = 44100.0; | ||||
PluginExporter plugin; | |||||
PluginExporter plugin(nullptr, nullptr); | |||||
d_lastBufferSize = 0; | d_lastBufferSize = 0; | ||||
d_lastSampleRate = 0.0; | d_lastSampleRate = 0.0; | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -32,7 +32,7 @@ | |||||
#include "lv2/lv2_programs.h" | #include "lv2/lv2_programs.h" | ||||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | ||||
# include "libmodauth.h" | |||||
# include "libmodla.h" | |||||
#endif | #endif | ||||
#ifdef noexcept | #ifdef noexcept | ||||
@@ -56,13 +56,18 @@ START_NAMESPACE_DISTRHO | |||||
typedef std::map<const String, String> StringMap; | typedef std::map<const String, String> StringMap; | ||||
#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
static const writeMidiFunc writeMidiCallback = nullptr; | |||||
#endif | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
class PluginLv2 | class PluginLv2 | ||||
{ | { | ||||
public: | public: | ||||
PluginLv2(const double sampleRate, const LV2_URID_Map* const uridMap, const LV2_Worker_Schedule* const worker, const bool usingNominal) | 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 | #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | ||||
fRunCount(0), | fRunCount(0), | ||||
#endif | #endif | ||||
@@ -107,9 +112,6 @@ public: | |||||
#if DISTRHO_LV2_USE_EVENTS_IN | #if DISTRHO_LV2_USE_EVENTS_IN | ||||
fPortEventsIn = nullptr; | fPortEventsIn = nullptr; | ||||
#endif | #endif | ||||
#if DISTRHO_LV2_USE_EVENTS_OUT | |||||
fPortEventsOut = nullptr; | |||||
#endif | |||||
#if DISTRHO_PLUGIN_WANT_LATENCY | #if DISTRHO_PLUGIN_WANT_LATENCY | ||||
fPortLatency = nullptr; | fPortLatency = nullptr; | ||||
#endif | #endif | ||||
@@ -226,7 +228,7 @@ public: | |||||
#if DISTRHO_LV2_USE_EVENTS_OUT | #if DISTRHO_LV2_USE_EVENTS_OUT | ||||
if (port == index++) | if (port == index++) | ||||
{ | { | ||||
fPortEventsOut = (LV2_Atom_Sequence*)dataLocation; | |||||
fEventsOutData.port = (LV2_Atom_Sequence*)dataLocation; | |||||
return; | return; | ||||
} | } | ||||
#endif | #endif | ||||
@@ -517,14 +519,12 @@ public: | |||||
curValue = *fPortControls[i]; | curValue = *fPortControls[i]; | ||||
if (fLastControlValues[i] != curValue && ! fPlugin.isParameterOutput(i)) | |||||
if (fPlugin.isParameterInput(i) && d_isNotEqual(fLastControlValues[i], curValue)) | |||||
{ | { | ||||
fLastControlValues[i] = curValue; | fLastControlValues[i] = curValue; | ||||
if (fPlugin.getParameterDesignation(i) == kParameterDesignationBypass) | if (fPlugin.getParameterDesignation(i) == kParameterDesignationBypass) | ||||
{ | |||||
curValue = 1.0f - curValue; | curValue = 1.0f - curValue; | ||||
} | |||||
fPlugin.setParameterValue(i, curValue); | fPlugin.setParameterValue(i, curValue); | ||||
} | } | ||||
@@ -607,20 +607,14 @@ public: | |||||
#endif | #endif | ||||
} | } | ||||
updateParameterOutputs(); | |||||
updateParameterOutputsAndTriggers(); | |||||
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI | #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; | 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) | for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | ||||
{ | { | ||||
@@ -645,6 +639,7 @@ public: | |||||
break; | break; | ||||
// reserve msg space | // reserve msg space | ||||
// FIXME create a large enough buffer beforehand | |||||
char msgBuf[msgSize]; | char msgBuf[msgSize]; | ||||
std::memset(msgBuf, 0, msgSize); | std::memset(msgBuf, 0, msgSize); | ||||
@@ -653,21 +648,23 @@ public: | |||||
std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()); | std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()); | ||||
// put data | // 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->time.frames = 0; | ||||
aev->body.type = fURIDs.distrhoState; | aev->body.type = fURIDs.distrhoState; | ||||
aev->body.size = msgSize; | aev->body.size = msgSize; | ||||
std::memcpy(LV2_ATOM_BODY(&aev->body), msgBuf, msgSize-1); | 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; | fNeededUiSends[i] = false; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
#if DISTRHO_LV2_USE_EVENTS_OUT | |||||
fEventsOutData.endRun(); | |||||
#endif | |||||
} | } | ||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
@@ -883,9 +880,6 @@ private: | |||||
#if DISTRHO_LV2_USE_EVENTS_IN | #if DISTRHO_LV2_USE_EVENTS_IN | ||||
LV2_Atom_Sequence* fPortEventsIn; | LV2_Atom_Sequence* fPortEventsIn; | ||||
#endif | #endif | ||||
#if DISTRHO_LV2_USE_EVENTS_OUT | |||||
LV2_Atom_Sequence* fPortEventsOut; | |||||
#endif | |||||
#if DISTRHO_PLUGIN_WANT_LATENCY | #if DISTRHO_PLUGIN_WANT_LATENCY | ||||
float* fPortLatency; | float* fPortLatency; | ||||
#endif | #endif | ||||
@@ -922,6 +916,44 @@ private: | |||||
} fLastPositionData; | } fLastPositionData; | ||||
#endif | #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 | // LV2 URIDs | ||||
struct URIDs { | struct URIDs { | ||||
LV2_URID atomBlank; | LV2_URID atomBlank; | ||||
@@ -998,17 +1030,23 @@ private: | |||||
} | } | ||||
#endif | #endif | ||||
void updateParameterOutputs() | |||||
void updateParameterOutputsAndTriggers() | |||||
{ | { | ||||
float curValue; | |||||
for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | 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 | #if DISTRHO_PLUGIN_WANT_LATENCY | ||||
@@ -1016,6 +1054,38 @@ private: | |||||
*fPortLatency = fPlugin.getLatency(); | *fPortLatency = fPlugin.getLatency(); | ||||
#endif | #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 | #endif | ||||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | ||||
mod_check_license(features, DISTRHO_PLUGIN_URI); | |||||
mod_license_check(features, DISTRHO_PLUGIN_URI); | |||||
#endif | #endif | ||||
d_lastBufferSize = 0; | d_lastBufferSize = 0; | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * 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 | // Dummy plugin to get data from | ||||
d_lastBufferSize = 512; | d_lastBufferSize = 512; | ||||
d_lastSampleRate = 44100.0; | d_lastSampleRate = 44100.0; | ||||
PluginExporter plugin; | |||||
PluginExporter plugin(nullptr, nullptr); | |||||
d_lastBufferSize = 0; | d_lastBufferSize = 0; | ||||
d_lastSampleRate = 0.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"; | pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n"; | ||||
#endif | #endif | ||||
pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; | 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 rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | ||||
pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | ||||
#if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
@@ -398,7 +399,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
bool designated = false; | bool designated = false; | ||||
// designation | // designation | ||||
if (! plugin.isParameterOutput(i)) | |||||
if (plugin.isParameterInput(i)) | |||||
{ | { | ||||
switch (plugin.getParameterDesignation(i)) | switch (plugin.getParameterDesignation(i)) | ||||
{ | { | ||||
@@ -417,10 +418,10 @@ void lv2_generate_ttl(const char* const basename) | |||||
} | } | ||||
} | } | ||||
// name and symbol | |||||
if (! designated) | if (! designated) | ||||
{ | { | ||||
pluginString += " lv2:name \"" + plugin.getParameterName(i) + "\" ;\n"; | |||||
// name and symbol | |||||
pluginString += " lv2:name \"\"\"" + plugin.getParameterName(i) + "\"\"\" ;\n"; | |||||
String symbol(plugin.getParameterSymbol(i)); | String symbol(plugin.getParameterSymbol(i)); | ||||
@@ -428,32 +429,53 @@ void lv2_generate_ttl(const char* const basename) | |||||
symbol = "lv2_port_" + String(portIndex-1); | symbol = "lv2_port_" + String(portIndex-1); | ||||
pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; | pluginString += " lv2:symbol \"" + symbol + "\" ;\n"; | ||||
} | |||||
// ranges | |||||
if (! designated) | |||||
{ | |||||
// ranges | |||||
const ParameterRanges& ranges(plugin.getParameterRanges(i)); | const ParameterRanges& ranges(plugin.getParameterRanges(i)); | ||||
if (plugin.getParameterHints(i) & kParameterIsInteger) | if (plugin.getParameterHints(i) & kParameterIsInteger) | ||||
{ | { | ||||
if (! plugin.isParameterOutput(i)) | |||||
if (plugin.isParameterInput(i)) | |||||
pluginString += " lv2:default " + String(int(plugin.getParameterValue(i))) + " ;\n"; | pluginString += " lv2:default " + String(int(plugin.getParameterValue(i))) + " ;\n"; | ||||
pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n"; | pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n"; | ||||
pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\n"; | pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\n"; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
if (! plugin.isParameterOutput(i)) | |||||
if (plugin.isParameterInput(i)) | |||||
pluginString += " lv2:default " + String(plugin.getParameterValue(i)) + " ;\n"; | pluginString += " lv2:default " + String(plugin.getParameterValue(i)) + " ;\n"; | ||||
pluginString += " lv2:minimum " + String(ranges.min) + " ;\n"; | pluginString += " lv2:minimum " + String(ranges.min) + " ;\n"; | ||||
pluginString += " lv2:maximum " + String(ranges.max) + " ;\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)); | const String& unit(plugin.getParameterUnit(i)); | ||||
if (! unit.isEmpty()) | if (! unit.isEmpty()) | ||||
@@ -495,25 +517,26 @@ void lv2_generate_ttl(const char* const basename) | |||||
pluginString += " ] ;\n"; | pluginString += " ] ;\n"; | ||||
} | } | ||||
} | } | ||||
} | |||||
// hints | |||||
if (! designated) | |||||
{ | |||||
// hints | |||||
const uint32_t hints(plugin.getParameterHints(i)); | const uint32_t hints(plugin.getParameterHints(i)); | ||||
if (hints & kParameterIsBoolean) | if (hints & kParameterIsBoolean) | ||||
{ | |||||
if ((hints & kParameterIsTrigger) == kParameterIsTrigger) | |||||
pluginString += " lv2:portProperty <" LV2_PORT_PROPS__trigger "> ;\n"; | |||||
pluginString += " lv2:portProperty lv2:toggled ;\n"; | pluginString += " lv2:portProperty lv2:toggled ;\n"; | ||||
} | |||||
if (hints & kParameterIsInteger) | if (hints & kParameterIsInteger) | ||||
pluginString += " lv2:portProperty lv2:integer ;\n"; | pluginString += " lv2:portProperty lv2:integer ;\n"; | ||||
if (hints & kParameterIsLogarithmic) | if (hints & kParameterIsLogarithmic) | ||||
pluginString += " lv2:portProperty <" LV2_PORT_PROPS__logarithmic "> ;\n"; | 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:portProperty <" LV2_PORT_PROPS__expensive "> ,\n"; | ||||
pluginString += " <" LV2_KXSTUDIO_PROPERTIES__NonAutomable "> ;\n"; | pluginString += " <" LV2_KXSTUDIO_PROPERTIES__NonAutomable "> ;\n"; | ||||
} | } | ||||
} | |||||
} // ! designated | |||||
if (i+1 == count) | if (i+1 == count) | ||||
pluginString += " ] ;\n\n"; | pluginString += " ] ;\n\n"; | ||||
@@ -537,7 +560,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
#endif | #endif | ||||
// name | // name | ||||
pluginString += " doap:name \"" + String(plugin.getName()) + "\" ;\n"; | |||||
pluginString += " doap:name \"\"\"" + String(plugin.getName()) + "\"\"\" ;\n"; | |||||
// license | // license | ||||
{ | { | ||||
@@ -546,7 +569,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
if (license.contains("://")) | if (license.contains("://")) | ||||
pluginString += " doap:license <" + license + "> ;\n\n"; | pluginString += " doap:license <" + license + "> ;\n\n"; | ||||
else | else | ||||
pluginString += " doap:license \"" + license + "\" ;\n\n"; | |||||
pluginString += " doap:license \"\"\"" + license + "\"\"\" ;\n\n"; | |||||
} | } | ||||
// developer | // developer | ||||
@@ -554,7 +577,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
const String homepage(plugin.getHomePage()); | const String homepage(plugin.getHomePage()); | ||||
pluginString += " doap:maintainer [\n"; | pluginString += " doap:maintainer [\n"; | ||||
pluginString += " foaf:name \"" + String(plugin.getMaker()) + "\" ;\n"; | |||||
pluginString += " foaf:name \"\"\"" + String(plugin.getMaker()) + "\"\"\" ;\n"; | |||||
if (homepage.isNotEmpty()) | if (homepage.isNotEmpty()) | ||||
pluginString += " foaf:homepage <" + homepage + "> ;\n"; | pluginString += " foaf:homepage <" + homepage + "> ;\n"; | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -37,11 +37,12 @@ | |||||
#define VESTIGE_HEADER | #define VESTIGE_HEADER | ||||
#define VST_FORCE_DEPRECATED 0 | #define VST_FORCE_DEPRECATED 0 | ||||
#include <clocale> | |||||
#include <map> | #include <map> | ||||
#include <string> | #include <string> | ||||
#ifdef VESTIGE_HEADER | #ifdef VESTIGE_HEADER | ||||
# include "vestige/aeffectx.h" | |||||
# include "vestige/vestige.h" | |||||
#define effFlagsProgramChunks (1 << 5) | #define effFlagsProgramChunks (1 << 5) | ||||
#define effSetProgramName 4 | #define effSetProgramName 4 | ||||
#define effGetParamLabel 6 | #define effGetParamLabel 6 | ||||
@@ -51,11 +52,8 @@ | |||||
#define effCanBeAutomated 26 | #define effCanBeAutomated 26 | ||||
#define effGetProgramNameIndexed 29 | #define effGetProgramNameIndexed 29 | ||||
#define effGetPlugCategory 35 | #define effGetPlugCategory 35 | ||||
#define effIdle 53 | |||||
#define effEditKeyDown 59 | #define effEditKeyDown 59 | ||||
#define effEditKeyUp 60 | #define effEditKeyUp 60 | ||||
#define kPlugCategEffect 1 | |||||
#define kPlugCategSynth 2 | |||||
#define kVstVersion 2400 | #define kVstVersion 2400 | ||||
struct ERect { | struct ERect { | ||||
int16_t top, left, bottom, right; | int16_t top, left, bottom, right; | ||||
@@ -68,6 +66,12 @@ START_NAMESPACE_DISTRHO | |||||
typedef std::map<const String, String> StringMap; | 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) | 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'; | 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: | public: | ||||
UiHelper() | |||||
ParameterCheckHelper() | |||||
: parameterChecks(nullptr), | : parameterChecks(nullptr), | ||||
parameterValues(nullptr) {} | parameterValues(nullptr) {} | ||||
virtual ~UiHelper() | |||||
virtual ~ParameterCheckHelper() | |||||
{ | { | ||||
if (parameterChecks != nullptr) | 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; | virtual void setStateFromUI(const char* const newKey, const char* const newValue) = 0; | ||||
# endif | |||||
#endif | |||||
}; | }; | ||||
#if DISTRHO_PLUGIN_HAS_UI | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
class UIVst | class UIVst | ||||
{ | { | ||||
public: | 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), | : fAudioMaster(audioMaster), | ||||
fEffect(effect), | fEffect(effect), | ||||
fUiHelper(uiHelper), | fUiHelper(uiHelper), | ||||
@@ -300,7 +330,7 @@ private: | |||||
// Vst stuff | // Vst stuff | ||||
const audioMasterCallback fAudioMaster; | const audioMasterCallback fAudioMaster; | ||||
AEffect* const fEffect; | AEffect* const fEffect; | ||||
UiHelper* const fUiHelper; | |||||
ParameterCheckHelper* const fUiHelper; | |||||
PluginExporter* const fPlugin; | PluginExporter* const fPlugin; | ||||
// Plugin UI | // 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: | public: | ||||
PluginVst(const audioMasterCallback audioMaster, AEffect* const effect) | PluginVst(const audioMasterCallback audioMaster, AEffect* const effect) | ||||
: fAudioMaster(audioMaster), | |||||
: fPlugin(this, writeMidiCallback), | |||||
fAudioMaster(audioMaster), | |||||
fEffect(effect) | fEffect(effect) | ||||
{ | { | ||||
std::memset(fProgramName, 0, sizeof(char)*(32+1)); | std::memset(fProgramName, 0, sizeof(char)*(32+1)); | ||||
@@ -376,14 +403,14 @@ public: | |||||
for (uint32_t i=0; i < paramCount; ++i) | for (uint32_t i=0; i < paramCount; ++i) | ||||
{ | { | ||||
parameterChecks[i] = false; | parameterChecks[i] = false; | ||||
parameterValues[i] = 0.0f; | |||||
parameterValues[i] = NAN; | |||||
} | } | ||||
} | } | ||||
# if DISTRHO_OS_MAC | # if DISTRHO_OS_MAC | ||||
# ifdef __LP64__ | # ifdef __LP64__ | ||||
fUsingNsView = true; | fUsingNsView = true; | ||||
# else | # 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; | fUsingNsView = false; | ||||
# endif | # endif | ||||
# endif // DISTRHO_OS_MAC | # endif // DISTRHO_OS_MAC | ||||
@@ -453,24 +480,35 @@ public: | |||||
{ | { | ||||
const uint32_t hints = fPlugin.getParameterHints(index); | const uint32_t hints = fPlugin.getParameterHints(index); | ||||
float value = fPlugin.getParameterValue(index); | float value = fPlugin.getParameterValue(index); | ||||
if (hints & kParameterIsBoolean) | if (hints & kParameterIsBoolean) | ||||
{ | { | ||||
const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); | const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); | ||||
const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; | const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; | ||||
value = value > midRange ? ranges.max : ranges.min; | 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; | return 1; | ||||
} | } | ||||
break; | break; | ||||
@@ -607,6 +645,7 @@ public: | |||||
#if DISTRHO_PLUGIN_WANT_STATE | #if DISTRHO_PLUGIN_WANT_STATE | ||||
case effGetChunk: | case effGetChunk: | ||||
{ | |||||
if (ptr == nullptr) | if (ptr == nullptr) | ||||
return 0; | return 0; | ||||
@@ -616,7 +655,9 @@ public: | |||||
fStateChunk = nullptr; | fStateChunk = nullptr; | ||||
} | } | ||||
if (fPlugin.getStateCount() == 0) | |||||
const uint32_t paramCount = fPlugin.getParameterCount(); | |||||
if (fPlugin.getStateCount() == 0 && paramCount == 0) | |||||
{ | { | ||||
fStateChunk = new char[1]; | fStateChunk = new char[1]; | ||||
fStateChunk[0] = '\0'; | fStateChunk[0] = '\0'; | ||||
@@ -650,6 +691,30 @@ public: | |||||
chunkStr += tmpStr; | 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); | const std::size_t chunkSize(chunkStr.length()+1); | ||||
fStateChunk = new char[chunkSize]; | fStateChunk = new char[chunkSize]; | ||||
@@ -667,21 +732,27 @@ public: | |||||
*(void**)ptr = fStateChunk; | *(void**)ptr = fStateChunk; | ||||
return ret; | return ret; | ||||
} | |||||
case effSetChunk: | case effSetChunk: | ||||
{ | { | ||||
if (value <= 1 || ptr == nullptr) | if (value <= 1 || ptr == nullptr) | ||||
return 0; | return 0; | ||||
const size_t chunkSize = static_cast<size_t>(value); | |||||
const char* key = (const char*)ptr; | const char* key = (const char*)ptr; | ||||
const char* value = nullptr; | const char* value = nullptr; | ||||
size_t size, bytesRead = 0; | |||||
for (;;) | |||||
while (bytesRead < chunkSize) | |||||
{ | { | ||||
if (key[0] == '\0') | if (key[0] == '\0') | ||||
break; | break; | ||||
value = key+(std::strlen(key)+1); | |||||
size = std::strlen(key)+1; | |||||
value = key + size; | |||||
bytesRead += size; | |||||
setStateFromUI(key, value); | setStateFromUI(key, value); | ||||
@@ -691,7 +762,52 @@ public: | |||||
# endif | # endif | ||||
// get next key | // 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; | return 1; | ||||
@@ -805,7 +921,10 @@ public: | |||||
void vst_processReplacing(const float** const inputs, float** const outputs, const int32_t sampleFrames) | void vst_processReplacing(const float** const inputs, float** const outputs, const int32_t sampleFrames) | ||||
{ | { | ||||
if (sampleFrames <= 0) | if (sampleFrames <= 0) | ||||
{ | |||||
updateParameterOutputsAndTriggers(); | |||||
return; | return; | ||||
} | |||||
if (! fPlugin.isActive()) | if (! fPlugin.isActive()) | ||||
{ | { | ||||
@@ -864,16 +983,7 @@ public: | |||||
fPlugin.run(inputs, outputs, sampleFrames); | fPlugin.run(inputs, outputs, sampleFrames); | ||||
#endif | #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; | friend class UIVst; | ||||
private: | private: | ||||
// Plugin | |||||
PluginExporter fPlugin; | |||||
// VST stuff | // VST stuff | ||||
const audioMasterCallback fAudioMaster; | const audioMasterCallback fAudioMaster; | ||||
AEffect* const fEffect; | AEffect* const fEffect; | ||||
// Plugin | |||||
PluginExporter fPlugin; | |||||
// Temporary data | // Temporary data | ||||
char fProgramName[32+1]; | char fProgramName[32+1]; | ||||
@@ -929,6 +1039,56 @@ private: | |||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
// functions called from the plugin side, RT no block | // 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 | #if DISTRHO_PLUGIN_HAS_UI | ||||
void setParameterValueFromPlugin(const uint32_t index, const float realValue) | void setParameterValueFromPlugin(const uint32_t index, const float realValue) | ||||
{ | { | ||||
@@ -937,6 +1097,37 @@ private: | |||||
} | } | ||||
#endif | #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 | #if DISTRHO_PLUGIN_WANT_STATE | ||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
// functions called from the UI side, may block | // functions called from the UI side, may block | ||||
@@ -977,17 +1168,10 @@ struct VstObject { | |||||
PluginVst* plugin; | 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) | 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 | // Create dummy plugin to get data from | ||||
static PluginExporter plugin; | |||||
static PluginExporter plugin(nullptr, nullptr); | |||||
if (doInternalInit) | 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 | /* This code invalidates the object created in VSTPluginMain | ||||
* Probably not safe against all hosts */ | * Probably not safe against all hosts */ | ||||
obj->audioMaster = nullptr; | obj->audioMaster = nullptr; | ||||
# ifdef VESTIGE_HEADER | |||||
effect->ptr3 = nullptr; | |||||
# else | |||||
vstObjectPtr = nullptr; | |||||
# endif | |||||
effect->object = nullptr; | |||||
delete obj; | delete obj; | ||||
#endif | #endif | ||||
@@ -1083,14 +1263,14 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t | |||||
return 1; | return 1; | ||||
} | } | ||||
return 0; | return 0; | ||||
case effGetParameterProperties: | case effGetParameterProperties: | ||||
if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount())) | if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount())) | ||||
{ | { | ||||
if (VstParameterProperties* const properties = (VstParameterProperties*)ptr) | if (VstParameterProperties* const properties = (VstParameterProperties*)ptr) | ||||
{ | { | ||||
memset(properties, 0, sizeof(VstParameterProperties)); | memset(properties, 0, sizeof(VstParameterProperties)); | ||||
const uint32_t hints = plugin.getParameterHints(index); | const uint32_t hints = plugin.getParameterHints(index); | ||||
if (hints & kParameterIsOutput) | if (hints & kParameterIsOutput) | ||||
@@ -1100,7 +1280,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t | |||||
{ | { | ||||
properties->flags |= kVstParameterIsSwitch; | properties->flags |= kVstParameterIsSwitch; | ||||
} | } | ||||
if (hints & kParameterIsInteger) | if (hints & kParameterIsInteger) | ||||
{ | { | ||||
properties->flags |= kVstParameterUsesIntegerMinMax; | properties->flags |= kVstParameterUsesIntegerMinMax; | ||||
@@ -1119,7 +1299,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t | |||||
} | } | ||||
} | } | ||||
return 0; | return 0; | ||||
case effGetPlugCategory: | case effGetPlugCategory: | ||||
#if DISTRHO_PLUGIN_IS_SYNTH | #if DISTRHO_PLUGIN_IS_SYNTH | ||||
return kPlugCategSynth; | return kPlugCategSynth; | ||||
@@ -1226,20 +1406,18 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) | |||||
// vst fields | // vst fields | ||||
effect->magic = kEffectMagic; | effect->magic = kEffectMagic; | ||||
effect->uniqueID = plugin->getUniqueId(); | 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; | int numParams = 0; | ||||
bool outputsReached = false; | bool outputsReached = false; | ||||
for (uint32_t i=0, count=plugin->getParameterCount(); i < count; ++i) | 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 | // parameter outputs must be all at the end | ||||
DISTRHO_SAFE_ASSERT_BREAK(! outputsReached); | DISTRHO_SAFE_ASSERT_BREAK(! outputsReached); | ||||
@@ -1248,6 +1426,7 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) | |||||
} | } | ||||
outputsReached = true; | outputsReached = true; | ||||
} | } | ||||
#endif | |||||
// plugin fields | // plugin fields | ||||
effect->numParams = numParams; | effect->numParams = numParams; | ||||
@@ -1278,11 +1457,9 @@ const AEffect* VSTPluginMain(audioMasterCallback audioMaster) | |||||
VstObject* const obj(new VstObject()); | VstObject* const obj(new VstObject()); | ||||
obj->audioMaster = audioMaster; | obj->audioMaster = audioMaster; | ||||
obj->plugin = nullptr; | obj->plugin = nullptr; | ||||
#ifdef VESTIGE_HEADER | |||||
effect->ptr3 = obj; | |||||
#else | |||||
// done | |||||
effect->object = obj; | effect->object = obj; | ||||
#endif | |||||
return effect; | return effect; | ||||
} | } | ||||
@@ -82,7 +82,7 @@ void UI::setState(const char* key, const char* value) | |||||
} | } | ||||
#endif | #endif | ||||
#if DISTRHO_PLUGIN_IS_SYNTH | |||||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) | void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) | ||||
{ | { | ||||
pData->sendNoteCallback(channel, note, velocity); | pData->sendNoteCallback(channel, note, velocity); | ||||
@@ -26,6 +26,10 @@ | |||||
START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
static const sendNoteFunc sendNoteCallback = nullptr; | |||||
#endif | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
struct OscData { | struct OscData { | ||||
@@ -180,6 +184,7 @@ protected: | |||||
fOscData.send_configure(key, value); | 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) | void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) | ||||
{ | { | ||||
if (fOscData.server == nullptr) | if (fOscData.server == nullptr) | ||||
@@ -187,11 +192,15 @@ protected: | |||||
if (channel > 0xF) | if (channel > 0xF) | ||||
return; | 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); | fOscData.send_midi(mdata); | ||||
} | } | ||||
#endif | |||||
void setSize(const uint width, const uint height) | void setSize(const uint width, const uint height) | ||||
{ | { | ||||
@@ -219,10 +228,12 @@ private: | |||||
uiPtr->setState(key, value); | uiPtr->setState(key, value); | ||||
} | } | ||||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | ||||
{ | { | ||||
uiPtr->sendNote(channel, note, velocity); | uiPtr->sendNote(channel, note, velocity); | ||||
} | } | ||||
#endif | |||||
static void setSizeCallback(void* ptr, uint width, uint height) | static void setSizeCallback(void* ptr, uint width, uint height) | ||||
{ | { | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -61,12 +61,12 @@ struct UI::PrivateData { | |||||
#endif | #endif | ||||
// Callbacks | // Callbacks | ||||
void* callbacksPtr; | |||||
editParamFunc editParamCallbackFunc; | editParamFunc editParamCallbackFunc; | ||||
setParamFunc setParamCallbackFunc; | setParamFunc setParamCallbackFunc; | ||||
setStateFunc setStateCallbackFunc; | setStateFunc setStateCallbackFunc; | ||||
sendNoteFunc sendNoteCallbackFunc; | sendNoteFunc sendNoteCallbackFunc; | ||||
setSizeFunc setSizeCallbackFunc; | setSizeFunc setSizeCallbackFunc; | ||||
void* ptr; | |||||
PrivateData() noexcept | PrivateData() noexcept | ||||
: sampleRate(d_lastUiSampleRate), | : sampleRate(d_lastUiSampleRate), | ||||
@@ -74,12 +74,12 @@ struct UI::PrivateData { | |||||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | ||||
dspPtr(d_lastUiDspPtr), | dspPtr(d_lastUiDspPtr), | ||||
#endif | #endif | ||||
callbacksPtr(nullptr), | |||||
editParamCallbackFunc(nullptr), | editParamCallbackFunc(nullptr), | ||||
setParamCallbackFunc(nullptr), | setParamCallbackFunc(nullptr), | ||||
setStateCallbackFunc(nullptr), | setStateCallbackFunc(nullptr), | ||||
sendNoteCallbackFunc(nullptr), | sendNoteCallbackFunc(nullptr), | ||||
setSizeCallbackFunc(nullptr), | |||||
ptr(nullptr) | |||||
setSizeCallbackFunc(nullptr) | |||||
{ | { | ||||
DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); | DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); | ||||
@@ -103,31 +103,31 @@ struct UI::PrivateData { | |||||
void editParamCallback(const uint32_t rindex, const bool started) | void editParamCallback(const uint32_t rindex, const bool started) | ||||
{ | { | ||||
if (editParamCallbackFunc != nullptr) | if (editParamCallbackFunc != nullptr) | ||||
editParamCallbackFunc(ptr, rindex, started); | |||||
editParamCallbackFunc(callbacksPtr, rindex, started); | |||||
} | } | ||||
void setParamCallback(const uint32_t rindex, const float value) | void setParamCallback(const uint32_t rindex, const float value) | ||||
{ | { | ||||
if (setParamCallbackFunc != nullptr) | if (setParamCallbackFunc != nullptr) | ||||
setParamCallbackFunc(ptr, rindex, value); | |||||
setParamCallbackFunc(callbacksPtr, rindex, value); | |||||
} | } | ||||
void setStateCallback(const char* const key, const char* const value) | void setStateCallback(const char* const key, const char* const value) | ||||
{ | { | ||||
if (setStateCallbackFunc != nullptr) | 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) | void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity) | ||||
{ | { | ||||
if (sendNoteCallbackFunc != nullptr) | if (sendNoteCallbackFunc != nullptr) | ||||
sendNoteCallbackFunc(ptr, channel, note, velocity); | |||||
sendNoteCallbackFunc(callbacksPtr, channel, note, velocity); | |||||
} | } | ||||
void setSizeCallback(const uint width, const uint height) | void setSizeCallback(const uint width, const uint height) | ||||
{ | { | ||||
if (setSizeCallbackFunc != nullptr) | 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 | class UIExporter | ||||
{ | { | ||||
public: | 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, | void* const dspPtr = nullptr, | ||||
const char* const bundlePath = nullptr) | const char* const bundlePath = nullptr) | ||||
#ifdef HAVE_DGL | #ifdef HAVE_DGL | ||||
@@ -238,7 +243,7 @@ public: | |||||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | ||||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | ||||
fData->ptr = ptr; | |||||
fData->callbacksPtr = callbacksPtr; | |||||
fData->editParamCallbackFunc = editParamCall; | fData->editParamCallbackFunc = editParamCall; | ||||
fData->setParamCallbackFunc = setParamCall; | fData->setParamCallbackFunc = setParamCall; | ||||
fData->setStateCallbackFunc = setStateCall; | fData->setStateCallbackFunc = setStateCall; | ||||
@@ -22,6 +22,7 @@ | |||||
#include "lv2/atom-util.h" | #include "lv2/atom-util.h" | ||||
#include "lv2/data-access.h" | #include "lv2/data-access.h" | ||||
#include "lv2/instance-access.h" | #include "lv2/instance-access.h" | ||||
#include "lv2/midi.h" | |||||
#include "lv2/options.h" | #include "lv2/options.h" | ||||
#include "lv2/parameters.h" | #include "lv2/parameters.h" | ||||
#include "lv2/ui.h" | #include "lv2/ui.h" | ||||
@@ -35,6 +36,15 @@ | |||||
START_NAMESPACE_DISTRHO | 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 | class UiLv2 | ||||
@@ -51,6 +61,7 @@ public: | |||||
fController(controller), | fController(controller), | ||||
fWriteFunction(writeFunc), | fWriteFunction(writeFunc), | ||||
fEventTransferURID(uridMap->map(uridMap->handle, LV2_ATOM__eventTransfer)), | 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")), | fKeyValueURID(uridMap->map(uridMap->handle, DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | ||||
fWinIdWasNull(winId == 0) | fWinIdWasNull(winId == 0) | ||||
{ | { | ||||
@@ -256,9 +267,28 @@ protected: | |||||
fWriteFunction(fController, eventInPortIndex, atomSize, fEventTransferURID, atom); | 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) | void setSize(const uint width, const uint height) | ||||
{ | { | ||||
@@ -282,6 +312,7 @@ private: | |||||
// Need to save this | // Need to save this | ||||
const LV2_URID fEventTransferURID; | const LV2_URID fEventTransferURID; | ||||
const LV2_URID fMidiEventURID; | |||||
const LV2_URID fKeyValueURID; | const LV2_URID fKeyValueURID; | ||||
// using ui:showInterface if true | // using ui:showInterface if true | ||||
@@ -307,10 +338,12 @@ private: | |||||
uiPtr->setState(key, value); | uiPtr->setState(key, value); | ||||
} | } | ||||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||||
static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | ||||
{ | { | ||||
uiPtr->sendNote(channel, note, velocity); | uiPtr->sendNote(channel, note, velocity); | ||||
} | } | ||||
#endif | |||||
static void setSizeCallback(void* ptr, uint width, uint height) | 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> | * 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 | * This program is free software; you can redistribute it and/or | ||||
* modify it under the terms of the GNU General Public | * modify it under the terms of the GNU General Public | ||||
@@ -22,8 +31,8 @@ | |||||
* | * | ||||
*/ | */ | ||||
#include <stdint.h> | #include <stdint.h> | ||||
#ifndef _AEFFECTX_H | |||||
#define _AEFFECTX_H | |||||
#ifndef _VESTIGE_H | |||||
#define _VESTIGE_H | |||||
#define CCONST(a, b, c, d)( ( ( (int) a ) << 24 ) | \ | #define CCONST(a, b, c, d)( ( ( (int) a ) << 24 ) | \ | ||||
( ( (int) b ) << 16 ) | \ | ( ( (int) b ) << 16 ) | \ | ||||
@@ -87,11 +96,11 @@ | |||||
#define effFlagsIsSynth (1 << 8) // currently unused | #define effFlagsIsSynth (1 << 8) // currently unused | ||||
#define effOpen 0 | #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 effSetSampleRate 10 | ||||
#define effSetBlockSize 11 | #define effSetBlockSize 11 | ||||
#define effMainsChanged 12 | #define effMainsChanged 12 | ||||
@@ -101,22 +110,42 @@ | |||||
#define effEditIdle 19 | #define effEditIdle 19 | ||||
#define effEditTop 20 | #define effEditTop 20 | ||||
#define effProcessEvents 25 | #define effProcessEvents 25 | ||||
#define effGetPlugCategory 35 | |||||
#define effGetEffectName 45 | #define effGetEffectName 45 | ||||
#define effGetVendorString 47 | #define effGetVendorString 47 | ||||
#define effGetProductString 48 | #define effGetProductString 48 | ||||
#define effGetVendorVersion 49 | #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 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 kVstLangEnglish 1 | ||||
#define kVstMidiType 1 | #define kVstMidiType 1 | ||||
struct RemoteVstPlugin; | struct RemoteVstPlugin; | ||||
#define kVstTransportChanged 1 | |||||
#define kVstTransportPlaying (1 << 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 kVstNanosValid (1 << 8) | ||||
#define kVstPpqPosValid (1 << 9) | #define kVstPpqPosValid (1 << 9) | ||||
@@ -174,26 +203,57 @@ struct _VstEvents | |||||
VstEvent * events[2]; | 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; | typedef struct _VstEvents VstEvents; | ||||
/* this struct taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ | |||||
struct _VstParameterProperties | 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; | typedef struct _VstParameterProperties VstParameterProperties; | ||||
/* this enum taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ | |||||
enum VstParameterFlags | enum VstParameterFlags | ||||
{ | { | ||||
kVstParameterIsSwitch = 1 << 0, /* parameter is a switch (on/off) */ | kVstParameterIsSwitch = 1 << 0, /* parameter is a switch (on/off) */ | ||||
@@ -231,18 +291,19 @@ struct _AEffect | |||||
// Fill somewhere 28-2b | // Fill somewhere 28-2b | ||||
void *ptr1; | void *ptr1; | ||||
void *ptr2; | 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 | // 1.0f 3c-3f | ||||
float unkown_float; | float unkown_float; | ||||
// An object? pointer 40-43 | // An object? pointer 40-43 | ||||
void *ptr3; | |||||
void *object; | |||||
// Zeroes 44-47 | // Zeroes 44-47 | ||||
void *user; | void *user; | ||||
// Id 48-4b | // Id 48-4b | ||||
int32_t uniqueID; | int32_t uniqueID; | ||||
// Don't know 4c-4f | |||||
char unknown1[4]; | |||||
// plugin version 4c-4f | |||||
int32_t version; | |||||
// processReplacing 50-53 | // processReplacing 50-53 | ||||
void (* processReplacing) (struct _AEffect *, float **, float **, int); | void (* processReplacing) (struct _AEffect *, float **, float **, int); | ||||
}; | }; |
@@ -9,13 +9,13 @@ else | |||||
exit | exit | ||||
fi | 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 | EXT=dll | ||||
else | else | ||||
GEN=$PWD/lv2_ttl_generator | |||||
GEN="$PWD/lv2_ttl_generator" | |||||
if [ -d /Library/Audio ]; then | if [ -d /Library/Audio ]; then | ||||
EXT=dylib | EXT=dylib | ||||
else | else | ||||
@@ -27,7 +27,7 @@ FOLDERS=`find . -type d -name \*.lv2` | |||||
for i in $FOLDERS; do | for i in $FOLDERS; do | ||||
cd $i | cd $i | ||||
FILE=`ls *.$EXT | sort | head -n 1` | |||||
$GEN ./$FILE | |||||
FILE="$(ls *.$EXT | sort | head -n 1)" | |||||
"$GEN" "./$FILE" | |||||
cd .. | cd .. | ||||
done | done |