|
- /*
- * Carla Backend
- * Copyright (C) 2011-2012 Filipe Coelho <falktx@falktx.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * any later version.
- *
- * 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 General Public License for more details.
- *
- * For a full copy of the GNU General Public License see the COPYING file
- */
-
- #ifdef CARLA_ENGINE_JACK
-
- #include "carla_engine.h"
- #include "carla_plugin.h"
-
- #include <iostream>
-
- CARLA_BACKEND_START_NAMESPACE
-
- // -------------------------------------------------------------------------------------------------------------------
- // static JACK<->Engine calls
-
- static int carla_jack_srate_callback(jack_nframes_t newSampleRate, void* arg)
- {
- CarlaEngineJack* const engine = (CarlaEngineJack*)arg;
- engine->handleSampleRateCallback(newSampleRate);
- return 0;
- }
-
- static int carla_jack_bufsize_callback(jack_nframes_t newBufferSize, void* arg)
- {
- CarlaEngineJack* const engine = (CarlaEngineJack*)arg;
- engine->handleBufferSizeCallback(newBufferSize);
- return 0;
- }
-
- static void carla_jack_freewheel_callback(int starting, void* arg)
- {
- CarlaEngineJack* const engine = (CarlaEngineJack*)arg;
- engine->handleFreewheelCallback(bool(starting));
- }
-
- static int carla_jack_process_callback(jack_nframes_t nframes, void* arg)
- {
- CarlaEngineJack* const engine = (CarlaEngineJack*)arg;
- engine->handleProcessCallback(nframes);
- return 0;
- }
-
- static int carla_jack_process_callback_plugin(jack_nframes_t nframes, void* arg)
- {
- CarlaPlugin* const plugin = (CarlaPlugin*)arg;
- if (plugin && plugin->enabled())
- {
- plugin->engineProcessLock();
- plugin->initBuffers();
- plugin->process_jack(nframes);
- plugin->engineProcessUnlock();
- }
- return 0;
- }
-
- static void carla_jack_shutdown_callback(void* arg)
- {
- CarlaEngineJack* const engine = (CarlaEngineJack*)arg;
- engine->handleShutdownCallback();
- }
-
- // -------------------------------------------------------------------------------------------------------------------
- // Carla Engine (JACK)
-
- CarlaEngineJack::CarlaEngineJack()
- : CarlaEngine()
- #ifndef BUILD_BRIDGE
- # ifdef Q_COMPILER_INITIALIZER_LISTS
- , rackJackPorts{nullptr}
- # endif
- #endif
- {
- qDebug("CarlaEngineJack::CarlaEngineJack()");
-
- type = CarlaEngineTypeJack;
-
- client = nullptr;
- state = JackTransportStopped;
- freewheel = false;
-
- memset(&pos, 0, sizeof(jack_position_t));
-
- #ifndef BUILD_BRIDGE
- # ifndef Q_COMPILER_INITIALIZER_LISTS
- for (unsigned short i=0; i < rackPortCount; i++)
- rackJackPorts[i] = nullptr;
- # endif
- #endif
- }
-
- CarlaEngineJack::~CarlaEngineJack()
- {
- qDebug("CarlaEngineJack::~CarlaEngineJack()");
- }
-
- // -------------------------------------------------------------------------------------------------------------------
-
- bool CarlaEngineJack::init(const char* const clientName)
- {
- qDebug("CarlaEngineJack::init(\"%s\")", clientName);
-
- client = jackbridge_client_open(clientName, JackNullOption, nullptr);
- state = JackTransportStopped;
- freewheel = false;
-
- if (client)
- {
- sampleRate = jackbridge_get_sample_rate(client);
- bufferSize = jackbridge_get_buffer_size(client);
-
- jackbridge_set_sample_rate_callback(client, carla_jack_srate_callback, this);
- jackbridge_set_buffer_size_callback(client, carla_jack_bufsize_callback, this);
- jackbridge_set_freewheel_callback(client, carla_jack_freewheel_callback, this);
- jackbridge_set_process_callback(client, carla_jack_process_callback, this);
- jackbridge_on_shutdown(client, carla_jack_shutdown_callback, this);
-
- #ifndef BUILD_BRIDGE
- if (carlaOptions.processMode == PROCESS_MODE_CONTINUOUS_RACK)
- {
- rackJackPorts[rackPortAudioIn1] = jackbridge_port_register(client, "in1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
- rackJackPorts[rackPortAudioIn2] = jackbridge_port_register(client, "in2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
- rackJackPorts[rackPortAudioOut1] = jackbridge_port_register(client, "out1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
- rackJackPorts[rackPortAudioOut2] = jackbridge_port_register(client, "out2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
- rackJackPorts[rackPortControlIn] = jackbridge_port_register(client, "control-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
- rackJackPorts[rackPortControlOut] = jackbridge_port_register(client, "control-out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
- rackJackPorts[rackPortMidiIn] = jackbridge_port_register(client, "midi-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
- rackJackPorts[rackPortMidiOut] = jackbridge_port_register(client, "midi-out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
- }
- #endif
-
- if (jackbridge_activate(client) == 0)
- {
- // set client name, fixed for OSC usage
- // FIXME - put this in shared?
- char* fixedName = strdup(jackbridge_get_client_name(client));
- for (size_t i=0; i < strlen(fixedName); i++)
- {
- if (! (std::isalpha(fixedName[i]) || std::isdigit(fixedName[i])))
- fixedName[i] = '_';
- }
-
- name = strdup(fixedName);
- free((void*)fixedName);
-
- CarlaEngine::init(name);
-
- return true;
- }
- else
- {
- setLastError("Failed to activate the JACK client");
- client = nullptr;
- }
- }
- else
- setLastError("Failed to create new JACK client");
-
- return false;
- }
-
- bool CarlaEngineJack::close()
- {
- qDebug("CarlaEngineJack::close()");
- CarlaEngine::close();
-
- if (name)
- {
- free((void*)name);
- name = nullptr;
- }
-
- if (jackbridge_deactivate(client) == 0)
- {
- #ifndef BUILD_BRIDGE
- if (carlaOptions.processMode == PROCESS_MODE_CONTINUOUS_RACK)
- {
- jackbridge_port_unregister(client, rackJackPorts[rackPortAudioIn1]);
- jackbridge_port_unregister(client, rackJackPorts[rackPortAudioIn2]);
- jackbridge_port_unregister(client, rackJackPorts[rackPortAudioOut1]);
- jackbridge_port_unregister(client, rackJackPorts[rackPortAudioOut2]);
- jackbridge_port_unregister(client, rackJackPorts[rackPortControlIn]);
- jackbridge_port_unregister(client, rackJackPorts[rackPortControlOut]);
- jackbridge_port_unregister(client, rackJackPorts[rackPortMidiIn]);
- jackbridge_port_unregister(client, rackJackPorts[rackPortMidiOut]);
- }
- #endif
-
- if (jackbridge_client_close(client) == 0)
- {
- client = nullptr;
- return true;
- }
- else
- setLastError("Failed to close the JACK client");
- }
- else
- setLastError("Failed to deactivate the JACK client");
-
- client = nullptr;
- return false;
- }
-
- bool CarlaEngineJack::isOffline()
- {
- return freewheel;
- }
-
- bool CarlaEngineJack::isRunning()
- {
- return bool(client);
- }
-
- CarlaEngineClient* CarlaEngineJack::addClient(CarlaPlugin* const plugin)
- {
- CarlaEngineClientNativeHandle handle;
- handle.type = type;
-
- #ifndef BUILD_BRIDGE
- if (carlaOptions.processMode == PROCESS_MODE_SINGLE_CLIENT)
- {
- handle.jackClient = client;
- }
- else if (carlaOptions.processMode == PROCESS_MODE_MULTIPLE_CLIENTS)
- #endif
- {
- handle.jackClient = jackbridge_client_open(plugin->name(), JackNullOption, nullptr);
- jackbridge_set_process_callback(handle.jackClient, carla_jack_process_callback_plugin, plugin);
- }
- //else if (carla_options.process_mode == PROCESS_MODE_CONTINUOUS_RACK)
- //{
- //}
-
- return new CarlaEngineClient(handle);
- }
-
- // -------------------------------------------------------------------------------------------------------------------
-
- void CarlaEngineJack::handleSampleRateCallback(double newSampleRate)
- {
- sampleRate = newSampleRate;
- }
-
- void CarlaEngineJack::handleBufferSizeCallback(uint32_t newBufferSize)
- {
- #ifndef BUILD_BRIDGE
- if (carlaOptions.processHighPrecision)
- return;
- #endif
-
- bufferSizeChanged(newBufferSize);
- }
-
- void CarlaEngineJack::handleFreewheelCallback(bool isFreewheel)
- {
- freewheel = isFreewheel;
- }
-
- void CarlaEngineJack::handleProcessCallback(uint32_t nframes)
- {
- if (maxPluginNumber() == 0)
- return;
-
- state = jackbridge_transport_query(client, &pos);
-
- timeInfo.playing = (state != JackTransportStopped);
-
- if (pos.unique_1 == pos.unique_2)
- {
- timeInfo.frame = pos.frame;
- timeInfo.time = pos.usecs;
-
- if (pos.valid & JackPositionBBT)
- {
- timeInfo.valid = CarlaEngineTimeBBT;
- timeInfo.bbt.bar = pos.bar;
- timeInfo.bbt.beat = pos.beat;
- timeInfo.bbt.tick = pos.tick;
- timeInfo.bbt.bar_start_tick = pos.bar_start_tick;
- timeInfo.bbt.beats_per_bar = pos.beats_per_bar;
- timeInfo.bbt.beat_type = pos.beat_type;
- timeInfo.bbt.ticks_per_beat = pos.ticks_per_beat;
- timeInfo.bbt.beats_per_minute = pos.beats_per_minute;
- }
- else
- timeInfo.valid = 0;
- }
- else
- {
- timeInfo.frame = 0;
- timeInfo.valid = 0;
- }
-
- #ifndef BUILD_BRIDGE
- if (carlaOptions.processMode == PROCESS_MODE_SINGLE_CLIENT)
- {
- for (unsigned short i=0, max=maxPluginNumber(); i < max; i++)
- {
- CarlaPlugin* const plugin = getPluginUnchecked(i);
-
- if (plugin && plugin->enabled())
- {
- plugin->engineProcessLock();
- plugin->initBuffers();
- plugin->process_jack(nframes);
- plugin->engineProcessUnlock();
- }
- }
- }
- else if (carlaOptions.processMode == PROCESS_MODE_CONTINUOUS_RACK)
- {
- // get buffers from jack
- float* audioIn1 = (float*)jackbridge_port_get_buffer(rackJackPorts[rackPortAudioIn1], nframes);
- float* audioIn2 = (float*)jackbridge_port_get_buffer(rackJackPorts[rackPortAudioIn2], nframes);
- float* audioOut1 = (float*)jackbridge_port_get_buffer(rackJackPorts[rackPortAudioOut1], nframes);
- float* audioOut2 = (float*)jackbridge_port_get_buffer(rackJackPorts[rackPortAudioOut2], nframes);
- void* controlIn = jackbridge_port_get_buffer(rackJackPorts[rackPortControlIn], nframes);
- void* controlOut = jackbridge_port_get_buffer(rackJackPorts[rackPortControlOut], nframes);
- void* midiIn = jackbridge_port_get_buffer(rackJackPorts[rackPortMidiIn], nframes);
- void* midiOut = jackbridge_port_get_buffer(rackJackPorts[rackPortMidiOut], nframes);
-
- // assert buffers
- CARLA_ASSERT(audioIn1);
- CARLA_ASSERT(audioIn2);
- CARLA_ASSERT(audioOut1);
- CARLA_ASSERT(audioOut2);
- CARLA_ASSERT(controlIn);
- CARLA_ASSERT(controlOut);
- CARLA_ASSERT(midiIn);
- CARLA_ASSERT(midiOut);
-
- // create temporary audio buffers
- float ains_tmp_buf1[nframes];
- float ains_tmp_buf2[nframes];
- float aouts_tmp_buf1[nframes];
- float aouts_tmp_buf2[nframes];
-
- float* ains_tmp[2] = { ains_tmp_buf1, ains_tmp_buf2 };
- float* aouts_tmp[2] = { aouts_tmp_buf1, aouts_tmp_buf2 };
-
- // initialize audio input
- memcpy(ains_tmp_buf1, audioIn1, sizeof(float)*nframes);
- memcpy(ains_tmp_buf2, audioIn2, sizeof(float)*nframes);
-
- // initialize control input
- memset(rackControlEventsIn, 0, sizeof(CarlaEngineControlEvent)*MAX_ENGINE_CONTROL_EVENTS);
- {
- jackbridge_midi_event_t jackEvent;
- const uint32_t jackEventCount = jackbridge_midi_get_event_count(controlIn);
-
- uint32_t carlaEventIndex = 0;
-
- for (uint32_t jackEventIndex=0; jackEventIndex < jackEventCount; jackEventIndex++)
- {
- if (jackbridge_midi_event_get(&jackEvent, controlIn, jackEventIndex) != 0)
- continue;
-
- CarlaEngineControlEvent* const carlaEvent = &rackControlEventsIn[carlaEventIndex++];
-
- uint8_t midiStatus = jackEvent.buffer[0];
- uint8_t midiChannel = midiStatus & 0x0F;
-
- carlaEvent->time = jackEvent.time;
- carlaEvent->channel = midiChannel;
-
- if (MIDI_IS_STATUS_CONTROL_CHANGE(midiStatus))
- {
- uint8_t midiControl = jackEvent.buffer[1];
-
- if (MIDI_IS_CONTROL_BANK_SELECT(midiControl))
- {
- uint8_t midiBank = jackEvent.buffer[2];
- carlaEvent->type = CarlaEngineEventMidiBankChange;
- carlaEvent->value = midiBank;
- }
- else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF)
- {
- carlaEvent->type = CarlaEngineEventAllSoundOff;
- }
- else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF)
- {
- carlaEvent->type = CarlaEngineEventAllNotesOff;
- }
- else
- {
- uint8_t midiValue = jackEvent.buffer[2];
- carlaEvent->type = CarlaEngineEventControlChange;
- carlaEvent->controller = midiControl;
- carlaEvent->value = double(midiValue)/127;
- }
- }
- else if (MIDI_IS_STATUS_PROGRAM_CHANGE(midiStatus))
- {
- uint8_t midiProgram = jackEvent.buffer[1];
- carlaEvent->type = CarlaEngineEventMidiProgramChange;
- carlaEvent->value = midiProgram;
- }
- }
- }
-
- // initialize midi input
- memset(rackMidiEventsIn, 0, sizeof(CarlaEngineMidiEvent)*MAX_ENGINE_MIDI_EVENTS);
- {
- uint32_t i = 0, j = 0;
- jackbridge_midi_event_t jackEvent;
-
- while (jackbridge_midi_event_get(&jackEvent, midiIn, j++) == 0)
- {
- if (i == MAX_ENGINE_MIDI_EVENTS)
- break;
-
- if (jackEvent.size < 4)
- {
- rackMidiEventsIn[i].time = jackEvent.time;
- rackMidiEventsIn[i].size = jackEvent.size;
- memcpy(rackMidiEventsIn[i].data, jackEvent.buffer, jackEvent.size);
- i += 1;
- }
- }
- }
-
- // initialize outputs (zero)
- zeroF(aouts_tmp_buf1, nframes);
- zeroF(aouts_tmp_buf2, nframes);
- memset(rackControlEventsOut, 0, sizeof(CarlaEngineControlEvent)*MAX_ENGINE_CONTROL_EVENTS);
- memset(rackMidiEventsOut, 0, sizeof(CarlaEngineMidiEvent)*MAX_ENGINE_MIDI_EVENTS);
-
- bool processed = false;
-
- // process plugins
- for (unsigned short i=0, max=maxPluginNumber(); i < max; i++)
- {
- CarlaPlugin* const plugin = getPluginUnchecked(i);
-
- if (plugin && plugin->enabled())
- {
- if (processed)
- {
- // initialize inputs (from previous outputs)
- memcpy(ains_tmp_buf1, aouts_tmp_buf1, sizeof(float)*nframes);
- memcpy(ains_tmp_buf2, aouts_tmp_buf2, sizeof(float)*nframes);
- memcpy(rackMidiEventsIn, rackMidiEventsOut, sizeof(CarlaEngineMidiEvent)*MAX_ENGINE_MIDI_EVENTS);
-
- // initialize outputs (zero)
- zeroF(aouts_tmp_buf1, nframes);
- zeroF(aouts_tmp_buf2, nframes);
- memset(rackMidiEventsOut, 0, sizeof(CarlaEngineMidiEvent)*MAX_ENGINE_MIDI_EVENTS);
- }
-
- // process
- plugin->engineProcessLock();
-
- plugin->initBuffers();
-
- if (carlaOptions.processHighPrecision)
- {
- float* ains_buffer2[2];
- float* aouts_buffer2[2];
-
- for (uint32_t j=0; j < nframes; j += 8)
- {
- ains_buffer2[0] = ains_tmp_buf1 + j;
- ains_buffer2[1] = ains_tmp_buf2 + j;
-
- aouts_buffer2[0] = aouts_tmp_buf1 + j;
- aouts_buffer2[1] = aouts_tmp_buf2 + j;
-
- plugin->process(ains_buffer2, aouts_buffer2, 8, j);
- }
- }
- else
- plugin->process(ains_tmp, aouts_tmp, nframes);
-
- plugin->engineProcessUnlock();
-
- // if plugin has no audio inputs, add previous buffers
- if (plugin->audioInCount() == 0)
- {
- for (uint32_t j=0; j < nframes; j++)
- {
- aouts_tmp_buf1[j] += ains_tmp_buf1[j];
- aouts_tmp_buf2[j] += ains_tmp_buf2[j];
- }
- }
-
- // if plugin has no midi output, add previous midi input
- if (plugin->midiOutCount() == 0)
- {
- memcpy(rackMidiEventsOut, rackMidiEventsIn, sizeof(CarlaEngineMidiEvent)*MAX_ENGINE_MIDI_EVENTS);
- }
-
- processed = true;
- }
- }
-
- // if no plugins in the rack, copy inputs over outputs
- if (! processed)
- {
- memcpy(aouts_tmp_buf1, ains_tmp_buf1, sizeof(float)*nframes);
- memcpy(aouts_tmp_buf2, ains_tmp_buf2, sizeof(float)*nframes);
- memcpy(rackMidiEventsOut, rackMidiEventsIn, sizeof(CarlaEngineMidiEvent)*MAX_ENGINE_MIDI_EVENTS);
- }
-
- // output audio
- memcpy(audioOut1, aouts_tmp_buf1, sizeof(float)*nframes);
- memcpy(audioOut2, aouts_tmp_buf2, sizeof(float)*nframes);
-
- // output control
- {
- jackbridge_midi_clear_buffer(controlOut);
-
- for (unsigned short i=0; i < MAX_ENGINE_CONTROL_EVENTS; i++)
- {
- CarlaEngineControlEvent* const event = &rackControlEventsOut[i];
-
- if (event->type == CarlaEngineEventControlChange && MIDI_IS_CONTROL_BANK_SELECT(event->controller))
- event->type = CarlaEngineEventMidiBankChange;
-
- uint8_t data[4] = { 0 };
-
- switch (event->type)
- {
- case CarlaEngineEventNull:
- break;
- case CarlaEngineEventControlChange:
- data[0] = MIDI_STATUS_CONTROL_CHANGE + event->channel;
- data[1] = event->controller;
- data[2] = event->value * 127;
- jackbridge_midi_event_write(controlOut, event->time, data, 3);
- break;
- case CarlaEngineEventMidiBankChange:
- data[0] = MIDI_STATUS_CONTROL_CHANGE + event->channel;
- data[1] = MIDI_CONTROL_BANK_SELECT;
- data[2] = event->value;
- jackbridge_midi_event_write(controlOut, event->time, data, 3);
- break;
- case CarlaEngineEventMidiProgramChange:
- data[0] = MIDI_STATUS_PROGRAM_CHANGE + event->channel;
- data[1] = event->value;
- jackbridge_midi_event_write(controlOut, event->time, data, 2);
- break;
- case CarlaEngineEventAllSoundOff:
- data[0] = MIDI_STATUS_CONTROL_CHANGE + event->channel;
- data[1] = MIDI_CONTROL_ALL_SOUND_OFF;
- jackbridge_midi_event_write(controlOut, event->time, data, 2);
- break;
- case CarlaEngineEventAllNotesOff:
- data[0] = MIDI_STATUS_CONTROL_CHANGE + event->channel;
- data[1] = MIDI_CONTROL_ALL_NOTES_OFF;
- jackbridge_midi_event_write(controlOut, event->time, data, 2);
- break;
- }
- }
- }
-
- // output midi
- {
- jackbridge_midi_clear_buffer(midiOut);
-
- for (unsigned short i=0; i < MAX_ENGINE_MIDI_EVENTS; i++)
- {
- if (rackMidiEventsOut[i].size == 0)
- break;
-
- jackbridge_midi_event_write(midiOut, rackMidiEventsOut[i].time, rackMidiEventsOut[i].data, rackMidiEventsOut[i].size);
- }
- }
- }
- #else
- Q_UNUSED(nframes);
- #endif
- }
-
- void CarlaEngineJack::handleShutdownCallback()
- {
- //for (unsigned short i=0, max=maxPluginNumber(); i < max; i++)
- //{
- //CarlaPlugin* const plugin = getPluginUnchecked(i);
- //plugin->x_client
- //}
-
- client = nullptr;
- callback(CALLBACK_QUIT, 0, 0, 0, 0.0);
- }
-
- CARLA_BACKEND_END_NAMESPACE
-
- #endif // CARLA_ENGINE_JACK
|