From b4ac0311058bd1626d00e83824ec98a23651d873 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 16 May 2014 01:21:52 +0100 Subject: [PATCH] Add JACK (standalone) export support --- dgl/Makefile.mk | 1 - distrho/DistrhoPluginMain.cpp | 2 + distrho/DistrhoUIMain.cpp | 2 + distrho/src/DistrhoPluginCarla.cpp | 4 +- distrho/src/DistrhoPluginJack.cpp | 344 +++++++++++++++++++++++++++++ distrho/src/DistrhoUIInternal.hpp | 18 +- 6 files changed, 366 insertions(+), 5 deletions(-) create mode 100644 distrho/src/DistrhoPluginJack.cpp diff --git a/dgl/Makefile.mk b/dgl/Makefile.mk index 5d8f26bd..cc943ac9 100644 --- a/dgl/Makefile.mk +++ b/dgl/Makefile.mk @@ -47,7 +47,6 @@ LINK_OPTS = else BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden CXXFLAGS += -fvisibility-inlines-hidden -LINK_OPTS += -Wl,--strip-all endif BUILD_C_FLAGS = $(BASE_FLAGS) -std=c99 -std=gnu99 $(CFLAGS) diff --git a/distrho/DistrhoPluginMain.cpp b/distrho/DistrhoPluginMain.cpp index 0671344d..8fce7dbd 100644 --- a/distrho/DistrhoPluginMain.cpp +++ b/distrho/DistrhoPluginMain.cpp @@ -18,6 +18,8 @@ #if defined(DISTRHO_PLUGIN_TARGET_CARLA) # include "src/DistrhoPluginCarla.cpp" +#elif defined(DISTRHO_PLUGIN_TARGET_JACK) +# include "src/DistrhoPluginJack.cpp" #elif (defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI)) # include "src/DistrhoPluginLADSPA+DSSI.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_LV2) diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp index 27f219ae..cb1cb6e7 100644 --- a/distrho/DistrhoUIMain.cpp +++ b/distrho/DistrhoUIMain.cpp @@ -18,6 +18,8 @@ #if defined(DISTRHO_PLUGIN_TARGET_CARLA) // nothing +#elif defined(DISTRHO_PLUGIN_TARGET_JACK) +// nothing #elif defined(DISTRHO_PLUGIN_TARGET_DSSI) # include "src/DistrhoUIDSSI.cpp" #elif defined(DISTRHO_PLUGIN_TARGET_LV2) diff --git a/distrho/src/DistrhoPluginCarla.cpp b/distrho/src/DistrhoPluginCarla.cpp index 982dbecb..b73168d3 100644 --- a/distrho/src/DistrhoPluginCarla.cpp +++ b/distrho/src/DistrhoPluginCarla.cpp @@ -332,12 +332,12 @@ protected: carla_copy(realMidiEvent.buf, midiEvent.data, midiEvent.size); } - fPlugin.run(inBuffer, outBuffer, frames, realMidiEvents, midiEventCount); + fPlugin.run(const_cast(inBuffer), outBuffer, frames, realMidiEvents, midiEventCount); } #else void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override { - fPlugin.run(inBuffer, outBuffer, frames); + fPlugin.run(const_cast(inBuffer), outBuffer, frames); } #endif diff --git a/distrho/src/DistrhoPluginJack.cpp b/distrho/src/DistrhoPluginJack.cpp new file mode 100644 index 00000000..310df509 --- /dev/null +++ b/distrho/src/DistrhoPluginJack.cpp @@ -0,0 +1,344 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2014 Filipe Coelho + * + * 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 + +// ----------------------------------------------------------------------- + +class PluginJack +{ +public: + PluginJack(jack_client_t* client) + : fPlugin(), + fUI(this, 0, nullptr, nullptr, nullptr, nullptr, uiResizeCallback, 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 + + 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(); + } + + ~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 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; + } + + 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 uiResize(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 + + // ------------------------------------------------------------------- + // 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 uiResizeCallback(void* ptr, uint width, uint height) + { + uiPtr->uiResize(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; +} + +// ----------------------------------------------------------------------- diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index e3986247..26f1de8f 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -123,7 +123,7 @@ struct UI::PrivateData { // ----------------------------------------------------------------------- // UI exporter class -class UIExporter +class UIExporter : public DGL::IdleCallback { public: UIExporter(void* const ptr, const intptr_t winId, @@ -223,6 +223,15 @@ public: // ------------------------------------------------------------------- + void exec() + { + DISTRHO_SAFE_ASSERT_RETURN(fUi != nullptr,); + + glWindow.addIdleCallback(this); + glWindow.setVisible(true); + glApp.exec(); + } + bool idle() { DISTRHO_SAFE_ASSERT_RETURN(fUi != nullptr, false); @@ -266,6 +275,12 @@ public: return ! glApp.isQuiting(); } +protected: + void idleCallback() override + { + fUi->d_uiIdle(); + } + private: // ------------------------------------------------------------------- // DGL Application and Window for this plugin @@ -280,7 +295,6 @@ private: UI::PrivateData* const fData; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UIExporter) - DISTRHO_PREVENT_HEAP_ALLOCATION }; // -----------------------------------------------------------------------