|
|
@@ -0,0 +1,344 @@ |
|
|
|
/* |
|
|
|
* 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 |
|
|
|
|
|
|
|
// ----------------------------------------------------------------------- |
|
|
|
|
|
|
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
// ----------------------------------------------------------------------- |