/* * Carla JACK Engine * Copyright (C) 2012-2013 Filipe Coelho * * 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 GPL.txt file */ #ifdef WANT_JACK #include "carla_engine_internal.hpp" #include "carla_backend_utils.hpp" #include "carla_midi.h" #include "jackbridge/jackbridge.h" CARLA_BACKEND_START_NAMESPACE #if 0 } // Fix editor indentation #endif // ------------------------------------------------------------------------------------------------------------------- // Carla Engine JACK-Audio port class CarlaEngineJackAudioPort : public CarlaEngineAudioPort { public: CarlaEngineJackAudioPort(const bool isInput, const ProcessMode processMode, jack_client_t* const client, jack_port_t* const port) : CarlaEngineAudioPort(isInput, processMode), kClient(client), kPort(port) { qDebug("CarlaEngineJackAudioPort::CarlaEngineJackAudioPort(%s, %s, %p, %p)", bool2str(isInput), ProcessMode2Str(processMode), client, port); if (processMode == PROCESS_MODE_SINGLE_CLIENT || processMode == PROCESS_MODE_MULTIPLE_CLIENTS) { CARLA_ASSERT(client != nullptr && port != nullptr); } else { CARLA_ASSERT(client == nullptr && port == nullptr); } } ~CarlaEngineJackAudioPort() { qDebug("CarlaEngineJackAudioPort::~CarlaEngineJackAudioPort()"); if (kClient != nullptr && kPort != nullptr) jackbridge_port_unregister(kClient, kPort); } void initBuffer(CarlaEngine* const engine) { CARLA_ASSERT(engine != nullptr); if (engine == nullptr) { fBuffer = nullptr; return; } if (kPort == nullptr) return CarlaEngineAudioPort::initBuffer(engine); fBuffer = (float*)jackbridge_port_get_buffer(kPort, engine->getBufferSize()); } private: jack_client_t* const kClient; jack_port_t* const kPort; CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineJackAudioPort) }; // ------------------------------------------------------------------------------------------------------------------- // Carla Engine JACK-Event port class CarlaEngineJackEventPort : public CarlaEngineEventPort { public: CarlaEngineJackEventPort(const bool isInput, const ProcessMode processMode, jack_client_t* const client, jack_port_t* const port) : CarlaEngineEventPort(isInput, processMode), kClient(client), kPort(port), fJackBuffer(nullptr) { qDebug("CarlaEngineJackEventPort::CarlaEngineJackEventPort(%s, %s, %p, %p)", bool2str(isInput), ProcessMode2Str(processMode), client, port); if (processMode == PROCESS_MODE_SINGLE_CLIENT || processMode == PROCESS_MODE_MULTIPLE_CLIENTS) { CARLA_ASSERT(client != nullptr && port != nullptr); } else { CARLA_ASSERT(client == nullptr && port == nullptr); } } ~CarlaEngineJackEventPort() { qDebug("CarlaEngineJackEventPort::~CarlaEngineJackEventPort()"); if (kClient != nullptr && kPort != nullptr) jackbridge_port_unregister(kClient, kPort); } void initBuffer(CarlaEngine* const engine) { CARLA_ASSERT(engine != nullptr); if (engine == nullptr) { fJackBuffer = nullptr; return; } if (kPort == nullptr) return CarlaEngineEventPort::initBuffer(engine); fJackBuffer = jackbridge_port_get_buffer(kPort, engine->getBufferSize()); if (! kIsInput) jackbridge_midi_clear_buffer(fJackBuffer); } uint32_t getEventCount() { if (kPort == nullptr) return CarlaEngineEventPort::getEventCount(); if (! kIsInput) return 0; CARLA_ASSERT(fJackBuffer != nullptr); if (fJackBuffer != nullptr) return 0; return jackbridge_midi_get_event_count(fJackBuffer); } const EngineEvent* getEvent(const uint32_t index) { if (kPort == nullptr) return CarlaEngineEventPort::getEvent(index); if (! kIsInput) return nullptr; CARLA_ASSERT(fJackBuffer != nullptr); if (fJackBuffer != nullptr) return nullptr; jack_midi_event_t jackEvent; if (jackbridge_midi_event_get(&jackEvent, fJackBuffer, index) != 0 || jackEvent.size > 3) return nullptr; fRetEvent.clear(); const uint8_t midiStatus = MIDI_GET_STATUS_FROM_DATA(jackEvent.buffer); const uint8_t midiChannel = MIDI_GET_CHANNEL_FROM_DATA(jackEvent.buffer); fRetEvent.time = jackEvent.time; fRetEvent.channel = midiChannel; if (MIDI_IS_STATUS_CONTROL_CHANGE(midiStatus)) { const uint8_t midiControl = jackEvent.buffer[1]; fRetEvent.type = kEngineEventTypeControl; if (MIDI_IS_CONTROL_BANK_SELECT(midiControl)) { const uint8_t midiBank = jackEvent.buffer[2]; fRetEvent.ctrl.type = kEngineControlEventTypeMidiBank; fRetEvent.ctrl.parameter = midiBank; fRetEvent.ctrl.value = 0.0; } else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF) { fRetEvent.ctrl.type = kEngineControlEventTypeAllSoundOff; fRetEvent.ctrl.parameter = 0; fRetEvent.ctrl.value = 0.0; } else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF) { fRetEvent.ctrl.type = kEngineControlEventTypeAllNotesOff; fRetEvent.ctrl.parameter = 0; fRetEvent.ctrl.value = 0.0; } else { const uint8_t midiValue = jackEvent.buffer[2]; fRetEvent.ctrl.type = kEngineControlEventTypeParameter; fRetEvent.ctrl.parameter = midiControl; fRetEvent.ctrl.value = double(midiValue)/127; } } else if (MIDI_IS_STATUS_PROGRAM_CHANGE(midiStatus)) { const uint8_t midiProgram = jackEvent.buffer[1]; fRetEvent.type = kEngineEventTypeControl; fRetEvent.ctrl.type = kEngineControlEventTypeMidiProgram; fRetEvent.ctrl.parameter = midiProgram; fRetEvent.ctrl.value = 0.0; } else { fRetEvent.type = kEngineEventTypeMidi; fRetEvent.midi.data[0] = midiStatus; fRetEvent.midi.data[1] = jackEvent.buffer[1]; fRetEvent.midi.data[2] = jackEvent.buffer[2]; fRetEvent.midi.size = jackEvent.size; } return &fRetEvent; } void writeControlEvent(const uint32_t time, const uint8_t channel, const EngineControlEventType type, const uint16_t parameter, const double value) { if (kPort == nullptr) return CarlaEngineEventPort::writeControlEvent(time, channel, type, parameter, value); if (kIsInput) return; CARLA_ASSERT(fJackBuffer != nullptr); CARLA_ASSERT(type != kEngineControlEventTypeNull); CARLA_ASSERT(channel < MAX_MIDI_CHANNELS); CARLA_ASSERT(value >= 0.0 && value <= 1.0); if (fJackBuffer == nullptr) return; if (type == kEngineControlEventTypeNull) return; if (channel >= MAX_MIDI_CHANNELS) return; if (type == kEngineControlEventTypeParameter) { CARLA_ASSERT(! MIDI_IS_CONTROL_BANK_SELECT(parameter)); } uint8_t data[3] = { 0 }; uint8_t size = 0; switch (type) { case kEngineControlEventTypeNull: break; case kEngineControlEventTypeParameter: data[0] = MIDI_STATUS_CONTROL_CHANGE + channel; data[1] = parameter; data[2] = value * 127; size = 3; break; case kEngineControlEventTypeMidiBank: data[0] = MIDI_STATUS_CONTROL_CHANGE + channel; data[1] = MIDI_CONTROL_BANK_SELECT; data[2] = value; size = 3; break; case kEngineControlEventTypeMidiProgram: data[0] = MIDI_STATUS_PROGRAM_CHANGE + channel; data[1] = value; size = 2; break; case kEngineControlEventTypeAllSoundOff: data[0] = MIDI_STATUS_CONTROL_CHANGE + channel; data[1] = MIDI_CONTROL_ALL_SOUND_OFF; size = 2; break; case kEngineControlEventTypeAllNotesOff: data[0] = MIDI_STATUS_CONTROL_CHANGE + channel; data[1] = MIDI_CONTROL_ALL_NOTES_OFF; size = 2; break; } if (size > 0) jackbridge_midi_event_write(fJackBuffer, time, data, size); } void writeMidiEvent(const uint32_t time, const uint8_t channel, const uint8_t* const data, const uint8_t size) { if (kPort == nullptr) return CarlaEngineEventPort::writeMidiEvent(time, channel, data, size); if (kIsInput) return; CARLA_ASSERT(fJackBuffer != nullptr); CARLA_ASSERT(channel < MAX_MIDI_CHANNELS); CARLA_ASSERT(data); CARLA_ASSERT(size > 0); if (fJackBuffer == nullptr) return; if (channel >= MAX_MIDI_CHANNELS) return; if (! (data && size > 0)) return; uint8_t jdata[size]; std::memcpy(jdata, data, sizeof(uint8_t)*size); jdata[0] = data[0] + channel; jackbridge_midi_event_write(fJackBuffer, time, jdata, size); } private: jack_client_t* const kClient; jack_port_t* const kPort; void* fJackBuffer; EngineEvent fRetEvent; CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineJackEventPort) }; // ------------------------------------------------------------------------------------------------------------------- // Jack Engine client class CarlaEngineJackClient : public CarlaEngineClient { public: CarlaEngineJackClient(const EngineType engineType, const ProcessMode processMode, jack_client_t* const client) : CarlaEngineClient(engineType, processMode), kClient(client), kUseClient(processMode == PROCESS_MODE_SINGLE_CLIENT || processMode == PROCESS_MODE_MULTIPLE_CLIENTS) { qDebug("CarlaEngineJackClient::CarlaEngineJackClient(%s, %s, %p)", EngineType2Str(engineType), ProcessMode2Str(processMode), client); if (kUseClient) { CARLA_ASSERT(kClient != nullptr); } else { CARLA_ASSERT(kClient == nullptr); } } ~CarlaEngineJackClient() { qDebug("CarlaEngineClient::~CarlaEngineClient()"); if (kProcessMode == PROCESS_MODE_MULTIPLE_CLIENTS) { if (kClient) jackbridge_client_close(kClient); } } void activate() { qDebug("CarlaEngineClient::activate()"); if (kProcessMode == PROCESS_MODE_MULTIPLE_CLIENTS) { CARLA_ASSERT(kClient && ! isActive()); if (kClient && ! isActive()) jackbridge_activate(kClient); } CarlaEngineClient::activate(); } void deactivate() { qDebug("CarlaEngineClient::deactivate()"); if (kProcessMode == PROCESS_MODE_MULTIPLE_CLIENTS) { CARLA_ASSERT(kClient && isActive()); if (kClient && isActive()) jackbridge_deactivate(kClient); } CarlaEngineClient::deactivate(); } bool isOk() const { qDebug("CarlaEngineClient::isOk()"); if (kUseClient) return bool(kClient); return CarlaEngineClient::isOk(); } void setLatency(const uint32_t samples) { CarlaEngineClient::setLatency(samples); if (kUseClient) jackbridge_recompute_total_latencies(kClient); } const CarlaEnginePort* addPort(const EnginePortType portType, const char* const name, const bool isInput) { qDebug("CarlaJackEngineClient::addPort(%s, \"%s\", %s)", EnginePortType2Str(portType), name, bool2str(isInput)); jack_port_t* port = nullptr; // Create Jack port if needed if (kUseClient) { switch (portType) { case kEnginePortTypeNull: break; case kEnginePortTypeAudio: port = jackbridge_port_register(kClient, name, JACK_DEFAULT_AUDIO_TYPE, isInput ? JackPortIsInput : JackPortIsOutput, 0); break; case kEnginePortTypeEvent: port = jackbridge_port_register(kClient, name, JACK_DEFAULT_MIDI_TYPE, isInput ? JackPortIsInput : JackPortIsOutput, 0); break; } } // Create Engine port switch (portType) { case kEnginePortTypeNull: break; case kEnginePortTypeAudio: return new CarlaEngineJackAudioPort(isInput, kProcessMode, kClient, port); case kEnginePortTypeEvent: return new CarlaEngineJackEventPort(isInput, kProcessMode, kClient, port); } qCritical("CarlaJackEngineClient::addPort(%s, \"%s\", %s) - invalid type", EnginePortType2Str(portType), name, bool2str(isInput)); return nullptr; } private: jack_client_t* const kClient; const bool kUseClient; CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineJackClient) }; // ------------------------------------------------------------------------------------------------------------------- // Jack Engine class CarlaEngineJack : public CarlaEngine { public: CarlaEngineJack() : CarlaEngine(), #ifdef BUILD_BRIDGE fHasQuit(false), #else fClient(nullptr), fTransportState(JackTransportStopped), fRackPorts{nullptr}, #endif fFreewheel(false) { qDebug("CarlaEngineJack::CarlaEngineJack()"); #ifdef BUILD_BRIDGE fOptions.processMode = PROCESS_MODE_MULTIPLE_CLIENTS; #endif carla_zeroStruct(fTransportPos); } ~CarlaEngineJack() { qDebug("CarlaEngineJack::~CarlaEngineJack()"); CARLA_ASSERT(fClient == nullptr); } // ------------------------------------------------------------------- // Maximum values unsigned int maxClientNameSize() { #ifndef BUILD_BRIDGE if (fOptions.processMode == PROCESS_MODE_SINGLE_CLIENT || fOptions.processMode == PROCESS_MODE_MULTIPLE_CLIENTS) #endif return static_cast(jackbridge_client_name_size()); return CarlaEngine::maxClientNameSize(); } unsigned int maxPortNameSize() { #ifndef BUILD_BRIDGE if (fOptions.processMode == PROCESS_MODE_SINGLE_CLIENT || fOptions.processMode == PROCESS_MODE_MULTIPLE_CLIENTS) #endif return static_cast(jackbridge_port_name_size()); return CarlaEngine::maxPortNameSize(); } // ------------------------------------------------------------------- // Virtual, per-engine type calls bool init(const char* const clientName) { qDebug("CarlaEngineJack::init(\"%s\")", clientName); fFreewheel = false; fTransportState = JackTransportStopped; carla_zeroStruct(fTransportPos); #ifndef BUILD_BRIDGE fClient = jackbridge_client_open(clientName, JackNullOption, nullptr); if (fClient) { fBufferSize = jackbridge_get_buffer_size(fClient); fSampleRate = jackbridge_get_sample_rate(fClient); jackbridge_set_buffer_size_callback(fClient, carla_jack_bufsize_callback, this); jackbridge_set_sample_rate_callback(fClient, carla_jack_srate_callback, this); jackbridge_set_freewheel_callback(fClient, carla_jack_freewheel_callback, this); jackbridge_set_process_callback(fClient, carla_jack_process_callback, this); jackbridge_set_latency_callback(fClient, carla_jack_latency_callback, this); jackbridge_on_shutdown(fClient, carla_jack_shutdown_callback, this); if (fOptions.processMode == PROCESS_MODE_CONTINUOUS_RACK) { fRackPorts[rackPortAudioIn1] = jackbridge_port_register(fClient, "audio-in1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); fRackPorts[rackPortAudioIn2] = jackbridge_port_register(fClient, "audio-in2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); fRackPorts[rackPortAudioOut1] = jackbridge_port_register(fClient, "audio-out1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); fRackPorts[rackPortAudioOut2] = jackbridge_port_register(fClient, "audio-out2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); fRackPorts[rackPortEventIn] = jackbridge_port_register(fClient, "events-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); fRackPorts[rackPortEventOut] = jackbridge_port_register(fClient, "events-out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); } if (jackbridge_activate(fClient) == 0) { const char* const clientName = jackbridge_get_client_name(fClient); CarlaEngine::init(clientName); return true; } else { setLastError("Failed to activate the JACK client"); fClient = nullptr; } } else setLastError("Failed to create new JACK client"); return false; #else // open temp client to get initial buffer-size and sample-rate values if (fBufferSize == 0 || fSampleRate == 0.0) { fClient = jackbridge_client_open(clientName, JackNullOption, nullptr); if (fClient) { fBufferSize = jackbridge_get_buffer_size(fClient); fSampleRate = jackbridge_get_sample_rate(fClient); jackbridge_client_close(fClient); fClient = nullptr; } } CarlaEngine::init(clientName); return true; #endif } bool close() { qDebug("CarlaEngineJack::close()"); CarlaEngine::close(); #ifdef BUILD_BRIDGE fClient = nullptr; fHasQuit = true; return true; #else if (jackbridge_deactivate(fClient) == 0) { if (fOptions.processMode == PROCESS_MODE_CONTINUOUS_RACK) { jackbridge_port_unregister(fClient, fRackPorts[rackPortAudioIn1]); jackbridge_port_unregister(fClient, fRackPorts[rackPortAudioIn2]); jackbridge_port_unregister(fClient, fRackPorts[rackPortAudioOut1]); jackbridge_port_unregister(fClient, fRackPorts[rackPortAudioOut2]); jackbridge_port_unregister(fClient, fRackPorts[rackPortEventIn]); jackbridge_port_unregister(fClient, fRackPorts[rackPortEventOut]); } if (jackbridge_client_close(fClient) == 0) { fClient = nullptr; return true; } else setLastError("Failed to close the JACK client"); } else setLastError("Failed to deactivate the JACK client"); fClient = nullptr; #endif return false; } bool isRunning() const { #ifdef BUILD_BRIDGE return (fClient != nullptr || ! fHasQuit); #else return (fClient != nullptr); #endif } bool isOffline() const { return fFreewheel; } EngineType type() const { return kEngineTypeJack; } CarlaEngineClient* addClient(CarlaPlugin* const plugin) { jack_client_t* client = nullptr; #ifdef BUILD_BRIDGE client = fClient = jackbridge_client_open(plugin->name(), JackNullOption, nullptr); fBufferSize = jackbridge_get_buffer_size(client); fSampleRate = jackbridge_get_sample_rate(client); jackbridge_set_buffer_size_callback(client, carla_jack_bufsize_callback, this); jackbridge_set_sample_rate_callback(client, carla_jack_srate_callback, this); jackbridge_set_freewheel_callback(client, carla_jack_freewheel_callback, this); jackbridge_set_process_callback(client, carla_jack_process_callback, this); jackbridge_set_latency_callback(client, carla_jack_latency_callback, this); jackbridge_on_shutdown(client, carla_jack_shutdown_callback, this); #else if (fOptions.processMode == PROCESS_MODE_SINGLE_CLIENT) { client = fClient; } else if (fOptions.processMode == PROCESS_MODE_MULTIPLE_CLIENTS) { client = jackbridge_client_open(plugin->name(), JackNullOption, nullptr); jackbridge_set_process_callback(client, carla_jack_process_callback_plugin, plugin); jackbridge_set_latency_callback(client, carla_jack_latency_callback_plugin, plugin); } #endif #ifdef BUILD_BRIDGE return new CarlaEngineJackClient(kEngineTypeJack, PROCESS_MODE_MULTIPLE_CLIENTS, client); #else return new CarlaEngineJackClient(kEngineTypeJack, fOptions.processMode, client); #endif } // ------------------------------------- protected: void handleJackBufferSizeCallback(const uint32_t newBufferSize) { if (fBufferSize != newBufferSize) { fBufferSize = newBufferSize; bufferSizeChanged(newBufferSize); } } void handleJackSampleRateCallback(const double newSampleRate) { if (fSampleRate != newSampleRate) { fSampleRate = newSampleRate; sampleRateChanged(newSampleRate); } } void handleJackFreewheelCallback(const bool isFreewheel) { fFreewheel = isFreewheel; } void handleJackProcessCallback(const uint32_t nframes) { #ifndef BUILD_BRIDGE if (fData->curPluginCount == 0) return; #endif fTransportPos.unique_1 = fTransportPos.unique_2 + 1; // invalidate fTransportState = jackbridge_transport_query(fClient, &fTransportPos); fTimeInfo.playing = (fTransportState == JackTransportRolling); if (fTransportPos.unique_1 == fTransportPos.unique_2) { fTimeInfo.frame = fTransportPos.frame; fTimeInfo.time = fTransportPos.usecs; if (fTransportPos.valid & JackPositionBBT) { fTimeInfo.valid = EngineTimeInfo::ValidBBT; fTimeInfo.bbt.bar = fTransportPos.bar; fTimeInfo.bbt.beat = fTransportPos.beat; fTimeInfo.bbt.tick = fTransportPos.tick; fTimeInfo.bbt.barStartTick = fTransportPos.bar_start_tick; fTimeInfo.bbt.beatsPerBar = fTransportPos.beats_per_bar; fTimeInfo.bbt.beatType = fTransportPos.beat_type; fTimeInfo.bbt.ticksPerBeat = fTransportPos.ticks_per_beat; fTimeInfo.bbt.beatsPerMinute = fTransportPos.beats_per_minute; } else fTimeInfo.valid = 0x0; } else { fTimeInfo.frame = 0; fTimeInfo.valid = 0x0; } #ifdef BUILD_BRIDGE CarlaPlugin* const plugin = getPluginUnchecked(0); if (plugin && plugin->enabled()) { plugin->initBuffers(); processPlugin(plugin, nframes); } #else if (fOptions.processMode == PROCESS_MODE_SINGLE_CLIENT) { for (unsigned int i=0; i < fData->curPluginCount; i++) { CarlaPlugin* const plugin = getPluginUnchecked(i); if (plugin && plugin->enabled()) { plugin->initBuffers(); processPlugin(plugin, nframes); } } } else if (fOptions.processMode == PROCESS_MODE_CONTINUOUS_RACK) { // get buffers from jack float* const audioIn1 = (float*)jackbridge_port_get_buffer(fRackPorts[rackPortAudioIn1], nframes); float* const audioIn2 = (float*)jackbridge_port_get_buffer(fRackPorts[rackPortAudioIn2], nframes); float* const audioOut1 = (float*)jackbridge_port_get_buffer(fRackPorts[rackPortAudioOut1], nframes); float* const audioOut2 = (float*)jackbridge_port_get_buffer(fRackPorts[rackPortAudioOut2], nframes); void* const eventIn = jackbridge_port_get_buffer(fRackPorts[rackPortEventIn], nframes); void* const eventOut = jackbridge_port_get_buffer(fRackPorts[rackPortEventOut], nframes); // assert buffers CARLA_ASSERT(audioIn1 != nullptr); CARLA_ASSERT(audioIn2 != nullptr); CARLA_ASSERT(audioOut1 != nullptr); CARLA_ASSERT(audioOut2 != nullptr); CARLA_ASSERT(eventIn != nullptr); CARLA_ASSERT(eventOut != nullptr); // create audio buffers float* inBuf[2] = { audioIn1, audioIn2 }; float* outBuf[2] = { audioOut1, audioOut2 }; #if 0 // initialize control input memset(rackControlEventsIn, 0, sizeof(CarlaEngineControlEvent)*MAX_CONTROL_EVENTS); { jack_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++]; const uint8_t midiStatus = jackEvent.buffer[0]; const uint8_t midiChannel = midiStatus & 0x0F; carlaEvent->time = jackEvent.time; carlaEvent->channel = midiChannel; if (MIDI_IS_STATUS_CONTROL_CHANGE(midiStatus)) { const uint8_t midiControl = jackEvent.buffer[1]; if (MIDI_IS_CONTROL_BANK_SELECT(midiControl)) { const uint8_t midiBank = jackEvent.buffer[2]; carlaEvent->type = CarlaEngineMidiBankChangeEvent; carlaEvent->value = midiBank; } else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF) { carlaEvent->type = CarlaEngineAllSoundOffEvent; } else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF) { carlaEvent->type = CarlaEngineAllNotesOffEvent; } else { const uint8_t midiValue = jackEvent.buffer[2]; carlaEvent->type = CarlaEngineParameterChangeEvent; carlaEvent->parameter = midiControl; carlaEvent->value = double(midiValue)/127; } } else if (MIDI_IS_STATUS_PROGRAM_CHANGE(midiStatus)) { const uint8_t midiProgram = jackEvent.buffer[1]; carlaEvent->type = CarlaEngineMidiProgramChangeEvent; carlaEvent->value = midiProgram; } } } // initialize midi input memset(rackMidiEventsIn, 0, sizeof(CarlaEngineMidiEvent)*MAX_MIDI_EVENTS); { uint32_t i = 0, j = 0; jack_midi_event_t jackEvent; while (jackbridge_midi_event_get(&jackEvent, midiIn, j++) == 0) { if (i == MAX_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; } } } #endif // process rack processRack(inBuf, outBuf, nframes); #if 0 // output control { jackbridge_midi_clear_buffer(controlOut); for (unsigned short i=0; i < MAX_CONTROL_EVENTS; i++) { CarlaEngineControlEvent* const event = &rackControlEventsOut[i]; if (event->type == CarlaEngineParameterChangeEvent && MIDI_IS_CONTROL_BANK_SELECT(event->parameter)) event->type = CarlaEngineMidiBankChangeEvent; uint8_t data[4] = { 0 }; switch (event->type) { case CarlaEngineNullEvent: break; case CarlaEngineParameterChangeEvent: data[0] = MIDI_STATUS_CONTROL_CHANGE + event->channel; data[1] = event->parameter; data[2] = event->value * 127; jackbridge_midi_event_write(controlOut, event->time, data, 3); break; case CarlaEngineMidiBankChangeEvent: 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 CarlaEngineMidiProgramChangeEvent: data[0] = MIDI_STATUS_PROGRAM_CHANGE + event->channel; data[1] = event->value; jackbridge_midi_event_write(controlOut, event->time, data, 2); break; case CarlaEngineAllSoundOffEvent: 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 CarlaEngineAllNotesOffEvent: 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_MIDI_EVENTS; i++) { if (rackMidiEventsOut[i].size == 0) break; jackbridge_midi_event_write(midiOut, rackMidiEventsOut[i].time, rackMidiEventsOut[i].data, rackMidiEventsOut[i].size); } } #endif } #endif proccessPendingEvents(); } void handleJackLatencyCallback(const jack_latency_callback_mode_t mode) { #ifndef BUILD_BRIDGE if (fOptions.processMode != PROCESS_MODE_SINGLE_CLIENT) return; #endif for (unsigned int i=0; i < fData->curPluginCount; i++) { CarlaPlugin* const plugin = getPluginUnchecked(i); if (plugin && plugin->enabled()) latencyPlugin(plugin, mode); } } void handleJackShutdownCallback() { for (unsigned int i=0; i < fData->curPluginCount; i++) { //CarlaPlugin* const plugin = getPluginUnchecked(i); //if (plugin) // plugin->x_client = nullptr; } fClient = nullptr; callback(CALLBACK_QUIT, 0, 0, 0, 0.0f, nullptr); } // ------------------------------------- private: jack_client_t* fClient; jack_position_t fTransportPos; jack_transport_state_t fTransportState; // ------------------------------------- #ifdef BUILD_BRIDGE bool fHasQuit; #else enum RackPorts { rackPortAudioIn1 = 0, rackPortAudioIn2 = 1, rackPortAudioOut1 = 2, rackPortAudioOut2 = 3, rackPortEventIn = 4, rackPortEventOut = 5, rackPortCount = 8 }; jack_port_t* fRackPorts[rackPortCount]; #endif bool fFreewheel; // ------------------------------------- static void processPlugin(CarlaPlugin* const p, const uint32_t nframes) { const uint32_t inCount = p->audioInCount(); const uint32_t outCount = p->audioOutCount(); float* inBuffer[inCount]; float* outBuffer[outCount]; float inPeaks[inCount]; float outPeaks[outCount]; carla_zeroFloat(inPeaks, inCount); carla_zeroFloat(outPeaks, outCount); for (uint32_t i=0; i < inCount; i++) inBuffer[i] = p->getAudioInPortBuffer(i); for (uint32_t i=0; i < outCount; i++) outBuffer[i] = p->getAudioOutPortBuffer(i); for (uint32_t i=0; i < inCount; i++) { for (uint32_t j=0; j < nframes; j++) { const float absV = std::abs(inBuffer[i][j]); if (absV > inPeaks[i]) inPeaks[i] = absV; } } p->process(inBuffer, outBuffer, nframes); for (uint32_t i=0; i < outCount; i++) { for (uint32_t j=0; j < nframes; j++) { const float absV = std::abs(outBuffer[i][j]); if (absV > outPeaks[i]) outPeaks[i] = absV; } } if (CarlaEngine* const engine = p->getEngine()) engine->setPeaks(p->id(), inPeaks, outPeaks); } static void latencyPlugin(CarlaPlugin* const p, jack_latency_callback_mode_t mode) { jack_latency_range_t range; uint32_t pluginLatency = 0; //p->getLatency(); if (pluginLatency == 0) return; if (mode == JackCaptureLatency) { #if 0 for (uint32_t i=0; i < p->aIn.count; i++) { uint aOutI = (i >= p->aOut.count) ? p->aOut.count : i; jack_port_t* const portIn = ((CarlaEngineJackAudioPort*)p->aIn.ports[i])->m_port; jack_port_t* const portOut = ((CarlaEngineJackAudioPort*)p->aOut.ports[aOutI])->m_port; jackbridge_port_get_latency_range(portIn, mode, &range); range.min += pluginLatency; range.max += pluginLatency; jackbridge_port_set_latency_range(portOut, mode, &range); } #endif } else { #if 0 for (uint32_t i=0; i < p->aOut.count; i++) { uint aInI = (i >= p->aIn.count) ? p->aIn.count : i; jack_port_t* const portIn = ((CarlaEngineJackAudioPort*)p->aIn.ports[aInI])->m_port; jack_port_t* const portOut = ((CarlaEngineJackAudioPort*)p->aOut.ports[i])->m_port; jackbridge_port_get_latency_range(portOut, mode, &range); range.min += pluginLatency; range.max += pluginLatency; jackbridge_port_set_latency_range(portIn, mode, &range); } #endif } } // ------------------------------------- static int carla_jack_srate_callback(jack_nframes_t newSampleRate, void* arg) { if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) _this_->handleJackSampleRateCallback(newSampleRate); return 0; } static int carla_jack_bufsize_callback(jack_nframes_t newBufferSize, void* arg) { if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) _this_->handleJackBufferSizeCallback(newBufferSize); return 0; } static void carla_jack_freewheel_callback(int starting, void* arg) { if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) _this_->handleJackFreewheelCallback(bool(starting)); } static int carla_jack_process_callback(jack_nframes_t nframes, void* arg) { if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) _this_->handleJackProcessCallback(nframes); return 0; } static void carla_jack_latency_callback(jack_latency_callback_mode_t mode, void* arg) { if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) _this_->handleJackLatencyCallback(mode); } static void carla_jack_shutdown_callback(void* arg) { if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) _this_->handleJackShutdownCallback(); } // ------------------------------------- #ifndef BUILD_BRIDGE static int carla_jack_process_callback_plugin(jack_nframes_t nframes, void* arg) { CarlaPlugin* const plugin = (CarlaPlugin*)arg; if (plugin && plugin->enabled()) { plugin->initBuffers(); processPlugin(plugin, nframes); } return 0; } static void carla_jack_latency_callback_plugin(jack_latency_callback_mode_t mode, void* arg) { CarlaPlugin* const plugin = (CarlaPlugin*)arg; if (plugin && plugin->enabled()) latencyPlugin(plugin, mode); } #endif CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineJack) }; // ----------------------------------------- CarlaEngine* CarlaEngine::newJack() { return new CarlaEngineJack(); } // ----------------------------------------- CARLA_BACKEND_END_NAMESPACE #endif // CARLA_ENGINE_JACK