@@ -691,9 +691,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 | ||||
@@ -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,12 @@ struct Plugin::PrivateData { | |||||
} | } | ||||
#endif | #endif | ||||
} | } | ||||
void writeMidiCallback(const MidiEvent& midiEvent) | |||||
{ | |||||
if (writeMidiCallbackFunc != nullptr) | |||||
writeMidiCallbackFunc(callbacksPtr, midiEvent); | |||||
} | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -157,7 +174,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 +208,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() | ||||
@@ -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) | ||||
{ | { | ||||
@@ -201,6 +209,11 @@ public: | |||||
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 +343,10 @@ 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); | |||||
#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 +425,10 @@ protected: | |||||
#else | #else | ||||
fPlugin.run(audioIns, audioOuts, nframes); | fPlugin.run(audioIns, audioOuts, nframes); | ||||
#endif | #endif | ||||
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
fPortMidiOutBuffer = nullptr; | |||||
#endif | |||||
} | } | ||||
void jackShutdown() | void jackShutdown() | ||||
@@ -440,6 +461,18 @@ 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 | |||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
private: | private: | ||||
@@ -457,6 +490,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 +512,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 | ||||
@@ -531,7 +535,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 | ||||
@@ -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 | ||||
@@ -610,17 +612,11 @@ public: | |||||
updateParameterOutputs(); | updateParameterOutputs(); | ||||
#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 +641,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 +650,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 +882,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 +918,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; | ||||
@@ -1016,6 +1050,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 | |||||
}; | }; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -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; | ||||
@@ -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 | ||||
@@ -68,6 +68,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) | ||||
@@ -351,7 +357,8 @@ class PluginVst | |||||
{ | { | ||||
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)); | ||||
@@ -453,15 +460,15 @@ 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) | if (hints & kParameterIsInteger) | ||||
{ | { | ||||
DISTRHO_NAMESPACE::snprintf_iparam((char*)ptr, (int32_t)std::round(value), 24); | DISTRHO_NAMESPACE::snprintf_iparam((char*)ptr, (int32_t)std::round(value), 24); | ||||
@@ -881,13 +888,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]; | ||||
@@ -937,6 +944,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 | ||||
@@ -1002,7 +1040,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) | ||||
{ | { | ||||
@@ -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; | ||||