summaryrefslogtreecommitdiff
path: root/libs/distrho/src/DistrhoPluginJack.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/distrho/src/DistrhoPluginJack.cpp')
-rw-r--r--libs/distrho/src/DistrhoPluginJack.cpp425
1 files changed, 425 insertions, 0 deletions
diff --git a/libs/distrho/src/DistrhoPluginJack.cpp b/libs/distrho/src/DistrhoPluginJack.cpp
new file mode 100644
index 0000000..94155df
--- /dev/null
+++ b/libs/distrho/src/DistrhoPluginJack.cpp
@@ -0,0 +1,425 @@
+/*
+ * DISTRHO Plugin Framework (DPF)
+ * Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * For a full copy of the license see the LGPL.txt file
+ */
+
+#include "DistrhoPluginInternal.hpp"
+
+#if ! DISTRHO_PLUGIN_HAS_UI
+# error JACK export requires an UI
+#endif
+
+#include "DistrhoUIInternal.hpp"
+
+#include "jack/jack.h"
+#include "jack/midiport.h"
+#include "jack/transport.h"
+
+// -----------------------------------------------------------------------
+
+START_NAMESPACE_DISTRHO
+
+#if ! DISTRHO_PLUGIN_WANT_STATE
+static const setStateFunc setStateCallback = nullptr;
+#endif
+
+// -----------------------------------------------------------------------
+
+class PluginJack : public IdleCallback
+{
+public:
+ PluginJack(jack_client_t* const client)
+ : fPlugin(),
+ fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, nullptr, setSizeCallback, fPlugin.getInstancePointer()),
+ fClient(client)
+ {
+ char strBuf[0xff+1];
+ strBuf[0xff] = '\0';
+
+#if DISTRHO_PLUGIN_NUM_INPUTS > 0
+ for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
+ {
+ std::snprintf(strBuf, 0xff, "in%i", i+1);
+ fPortAudioIns[i] = jack_port_register(fClient, strBuf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
+ }
+#endif
+
+#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+ for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
+ {
+ std::snprintf(strBuf, 0xff, "out%i", i+1);
+ fPortAudioOuts[i] = jack_port_register(fClient, strBuf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ }
+#endif
+
+#if DISTRHO_PLUGIN_IS_SYNTH
+ fPortMidiIn = jack_port_register(fClient, "midi-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
+#endif
+
+#if DISTRHO_PLUGIN_WANT_PROGRAMS
+ if (fPlugin.getProgramCount() > 0)
+ fPlugin.setProgram(0);
+#endif
+
+ if (const uint32_t count = fPlugin.getParameterCount())
+ {
+ fLastOutputValues = new float[count];
+
+ for (uint32_t i=0; i < count; ++i)
+ {
+ if (fPlugin.isParameterOutput(i))
+ fLastOutputValues[i] = fPlugin.getParameterValue(i);
+ else
+ fLastOutputValues[i] = 0.0f;
+ }
+ }
+ else
+ {
+ fLastOutputValues = nullptr;
+ }
+
+ jack_set_buffer_size_callback(fClient, jackBufferSizeCallback, this);
+ jack_set_sample_rate_callback(fClient, jackSampleRateCallback, this);
+ jack_set_process_callback(fClient, jackProcessCallback, this);
+ jack_on_shutdown(fClient, jackShutdownCallback, this);
+
+ jack_activate(fClient);
+
+ if (const char* const name = jack_get_client_name(fClient))
+ fUI.setTitle(name);
+ else
+ fUI.setTitle(DISTRHO_PLUGIN_NAME);
+
+ fUI.exec(this);
+ }
+
+ ~PluginJack()
+ {
+ if (fClient == nullptr)
+ return;
+
+ jack_deactivate(fClient);
+
+#if DISTRHO_PLUGIN_IS_SYNTH
+ jack_port_unregister(fClient, fPortMidiIn);
+ fPortMidiIn = nullptr;
+#endif
+
+#if DISTRHO_PLUGIN_NUM_INPUTS > 0
+ for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
+ {
+ jack_port_unregister(fClient, fPortAudioIns[i]);
+ fPortAudioIns[i] = nullptr;
+ }
+#endif
+
+#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+ for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
+ {
+ jack_port_unregister(fClient, fPortAudioOuts[i]);
+ fPortAudioOuts[i] = nullptr;
+ }
+#endif
+
+ jack_client_close(fClient);
+ }
+
+ // -------------------------------------------------------------------
+
+protected:
+ void idleCallback() override
+ {
+ float value;
+
+ for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
+ {
+ if (! fPlugin.isParameterOutput(i))
+ continue;
+
+ value = fPlugin.getParameterValue(i);
+
+ if (fLastOutputValues[i] == value)
+ continue;
+
+ fLastOutputValues[i] = value;
+ fUI.parameterChanged(i, value);
+ }
+
+ fUI.exec_idle();
+ }
+
+ void jackBufferSize(const jack_nframes_t nframes)
+ {
+ fPlugin.setBufferSize(nframes, true);
+ }
+
+ void jackSampleRate(const jack_nframes_t nframes)
+ {
+ fPlugin.setSampleRate(nframes, true);
+ }
+
+ void jackProcess(const jack_nframes_t nframes)
+ {
+#if DISTRHO_PLUGIN_NUM_INPUTS > 0
+ const float* audioIns[DISTRHO_PLUGIN_NUM_INPUTS];
+
+ for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
+ audioIns[i] = (const float*)jack_port_get_buffer(fPortAudioIns[i], nframes);
+#else
+ static const float** audioIns = nullptr;
+#endif
+
+#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+ float* audioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS];
+
+ for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
+ audioOuts[i] = (float*)jack_port_get_buffer(fPortAudioOuts[i], nframes);
+#else
+ static float** audioOuts = nullptr;
+#endif
+
+#if DISTRHO_PLUGIN_WANT_TIMEPOS
+ jack_position_t pos;
+ fTimePos.playing = (jack_transport_query(fClient, &pos) == JackTransportRolling);
+
+ if (pos.unique_1 == pos.unique_2)
+ {
+ if (pos.valid & JackTransportPosition)
+ fTimePos.frame = pos.frame;
+ else
+ fTimePos.frame = 0;
+
+ if (pos.valid & JackTransportBBT)
+ {
+ fTimePos.bbt.valid = true;
+
+ fTimePos.bbt.bar = pos.bar;
+ fTimePos.bbt.beat = pos.beat;
+ fTimePos.bbt.tick = pos.tick;
+ fTimePos.bbt.barStartTick = pos.bar_start_tick;
+
+ fTimePos.bbt.beatsPerBar = pos.beats_per_bar;
+ fTimePos.bbt.beatType = pos.beat_type;
+
+ fTimePos.bbt.ticksPerBeat = pos.ticks_per_beat;
+ fTimePos.bbt.beatsPerMinute = pos.beats_per_minute;
+ }
+ else
+ fTimePos.bbt.valid = false;
+ }
+ else
+ {
+ fTimePos.bbt.valid = false;
+ fTimePos.frame = 0;
+ }
+
+ fPlugin.setTimePos(fTimePos);
+#endif
+
+#if DISTRHO_PLUGIN_IS_SYNTH
+ void* const midiBuf = jack_port_get_buffer(fPortMidiIn, nframes);
+
+ if (const uint32_t eventCount = jack_midi_get_event_count(midiBuf))
+ {
+ uint32_t midiEventCount = 0;
+ MidiEvent midiEvents[eventCount];
+
+ jack_midi_event_t jevent;
+
+ for (uint32_t i=0; i < eventCount; ++i)
+ {
+ if (jack_midi_event_get(&jevent, midiBuf, i) != 0)
+ break;
+ if (jevent.size > 4)
+ continue;
+
+ MidiEvent& midiEvent(midiEvents[midiEventCount++]);
+
+ midiEvent.frame = jevent.time;
+ midiEvent.size = jevent.size;
+ std::memcpy(midiEvent.buf, jevent.buffer, jevent.size);
+ }
+
+ fPlugin.run(audioIns, audioOuts, nframes, midiEvents, midiEventCount);
+ }
+ else
+ {
+ fPlugin.run(audioIns, audioOuts, nframes, nullptr, 0);
+ }
+#else
+ fPlugin.run(audioIns, audioOuts, nframes);
+#endif
+ }
+
+ void jackShutdown()
+ {
+ d_stderr("jack has shutdown, quitting now...");
+ fClient = nullptr;
+ fUI.quit();
+ }
+
+ // -------------------------------------------------------------------
+
+ void setParameterValue(const uint32_t index, const float value)
+ {
+ fPlugin.setParameterValue(index, value);
+ }
+
+#if DISTRHO_PLUGIN_WANT_STATE
+ void setState(const char* const key, const char* const value)
+ {
+ fPlugin.setState(key, value);
+ }
+#endif
+
+ void setSize(const uint width, const uint height)
+ {
+ fUI.setSize(width, height);
+ }
+
+ // -------------------------------------------------------------------
+
+private:
+ PluginExporter fPlugin;
+ UIExporter fUI;
+
+ jack_client_t* fClient;
+
+#if DISTRHO_PLUGIN_NUM_INPUTS > 0
+ jack_port_t* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS];
+#endif
+#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
+ jack_port_t* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS];
+#endif
+#if DISTRHO_PLUGIN_IS_SYNTH
+ jack_port_t* fPortMidiIn;
+#endif
+#if DISTRHO_PLUGIN_WANT_TIMEPOS
+ TimePos fTimePos;
+#endif
+
+ // Temporary data
+ float* fLastOutputValues;
+
+ // -------------------------------------------------------------------
+ // Callbacks
+
+ #define uiPtr ((PluginJack*)ptr)
+
+ static int jackBufferSizeCallback(jack_nframes_t nframes, void* ptr)
+ {
+ uiPtr->jackBufferSize(nframes);
+ return 0;
+ }
+
+ static int jackSampleRateCallback(jack_nframes_t nframes, void* ptr)
+ {
+ uiPtr->jackSampleRate(nframes);
+ return 0;
+ }
+
+ static int jackProcessCallback(jack_nframes_t nframes, void* ptr)
+ {
+ uiPtr->jackProcess(nframes);
+ return 0;
+ }
+
+ static void jackShutdownCallback(void* ptr)
+ {
+ uiPtr->jackShutdown();
+ }
+
+ static void setParameterValueCallback(void* ptr, uint32_t index, float value)
+ {
+ uiPtr->setParameterValue(index, value);
+ }
+
+#if DISTRHO_PLUGIN_WANT_STATE
+ static void setStateCallback(void* ptr, const char* key, const char* value)
+ {
+ uiPtr->setState(key, value);
+ }
+#endif
+
+ static void setSizeCallback(void* ptr, uint width, uint height)
+ {
+ uiPtr->setSize(width, height);
+ }
+
+ #undef uiPtr
+};
+
+END_NAMESPACE_DISTRHO
+
+// -----------------------------------------------------------------------
+
+int main()
+{
+ jack_status_t status = jack_status_t(0x0);
+ jack_client_t* client = jack_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status);
+
+ if (client == nullptr)
+ {
+ d_string errorString;
+
+ if (status & JackFailure)
+ errorString += "Overall operation failed;\n";
+ if (status & JackInvalidOption)
+ errorString += "The operation contained an invalid or unsupported option;\n";
+ if (status & JackNameNotUnique)
+ errorString += "The desired client name was not unique;\n";
+ if (status & JackServerStarted)
+ errorString += "The JACK server was started as a result of this operation;\n";
+ if (status & JackServerFailed)
+ errorString += "Unable to connect to the JACK server;\n";
+ if (status & JackServerError)
+ errorString += "Communication error with the JACK server;\n";
+ if (status & JackNoSuchClient)
+ errorString += "Requested client does not exist;\n";
+ if (status & JackLoadFailure)
+ errorString += "Unable to load internal client;\n";
+ if (status & JackInitFailure)
+ errorString += "Unable to initialize client;\n";
+ if (status & JackShmFailure)
+ errorString += "Unable to access shared memory;\n";
+ if (status & JackVersionError)
+ errorString += "Client's protocol version does not match;\n";
+ if (status & JackBackendError)
+ errorString += "Backend Error;\n";
+ if (status & JackClientZombie)
+ errorString += "Client is being shutdown against its will;\n";
+
+ if (errorString.isNotEmpty())
+ {
+ errorString[errorString.length()-2] = '.';
+ d_stderr("Failed to create jack client, reason was:\n%s", errorString.buffer());
+ }
+ else
+ d_stderr("Failed to create jack client, cannot continue!");
+
+ return 1;
+ }
+
+ USE_NAMESPACE_DISTRHO;
+
+ d_lastBufferSize = jack_get_buffer_size(client);
+ d_lastSampleRate = jack_get_sample_rate(client);
+ d_lastUiSampleRate = d_lastSampleRate;
+
+ const PluginJack p(client);
+
+ return 0;
+}
+
+// -----------------------------------------------------------------------