/* * 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 #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); fUI.programChanged(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; fUI.parameterChanged(i, fPlugin.getParameterValue(i)); } } } 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; } // -----------------------------------------------------------------------