diff --git a/source/backend/CarlaEngine.hpp b/source/backend/CarlaEngine.hpp index e0e97574c..b67e22e23 100644 --- a/source/backend/CarlaEngine.hpp +++ b/source/backend/CarlaEngine.hpp @@ -46,34 +46,34 @@ CARLA_BACKEND_START_NAMESPACE */ enum EngineType { /*! - * Null engine type. - */ + * Null engine type. + */ kEngineTypeNull = 0, /*! - * JACK engine type.\n - * Provides all processing modes. - */ + * JACK engine type.\n + * Provides all processing modes. + */ kEngineTypeJack = 1, /*! - * Juce engine type, used to provide Native Audio and MIDI support. - */ + * Juce engine type, used to provide Native Audio and MIDI support. + */ kEngineTypeJuce = 2, /*! - * RtAudio engine type, used to provide Native Audio and MIDI support. - */ + * RtAudio engine type, used to provide Native Audio and MIDI support. + */ kEngineTypeRtAudio = 3, /*! - * Plugin engine type, used to export the engine as a plugin. - */ + * Plugin engine type, used to export the engine as a plugin. + */ kEngineTypePlugin = 4, /*! - * Bridge engine type, used in BridgePlugin class. - */ + * Bridge engine type, used in BridgePlugin class. + */ kEngineTypeBridge = 5 }; @@ -82,26 +82,26 @@ enum EngineType { */ enum EnginePortType { /*! - * Null port type. - */ + * Null port type. + */ kEnginePortTypeNull = 0, /*! - * Audio port type. - * \see CarlaEngineAudioPort - */ + * Audio port type. + * \see CarlaEngineAudioPort + */ kEnginePortTypeAudio = 1, /*! - * CV port type. - * \see CarlaEngineCVPort - */ + * CV port type. + * \see CarlaEngineCVPort + */ kEnginePortTypeCV = 2, /*! - * Event port type (Control or MIDI). - ** \see CarlaEngineEventPort - */ + * Event port type (Control or MIDI). + * \see CarlaEngineEventPort + */ kEnginePortTypeEvent = 3 }; @@ -110,20 +110,20 @@ enum EnginePortType { */ enum EngineEventType { /*! - * Null port type. - */ + * Null port type. + */ kEngineEventTypeNull = 0, /*! - * Control event type. - * \see EngineControlEvent - */ + * Control event type. + * \see EngineControlEvent + */ kEngineEventTypeControl = 1, /*! - * MIDI event type. - * \see EngineMidiEvent - */ + * MIDI event type. + * \see EngineMidiEvent + */ kEngineEventTypeMidi = 2 }; @@ -132,34 +132,34 @@ enum EngineEventType { */ enum EngineControlEventType { /*! - * Null event type. - */ + * Null event type. + */ kEngineControlEventTypeNull = 0, /*! - * Parameter event type.\n - * \note Value uses a normalized range of 0.0f<->1.0f. - */ + * Parameter event type.\n + * \note Value uses a normalized range of 0.0f<->1.0f. + */ kEngineControlEventTypeParameter = 1, /*! - * MIDI Bank event type. - */ + * MIDI Bank event type. + */ kEngineControlEventTypeMidiBank = 2, /*! - * MIDI Program change event type. - */ + * MIDI Program change event type. + */ kEngineControlEventTypeMidiProgram = 3, /*! - * All sound off event type. - */ + * All sound off event type. + */ kEngineControlEventTypeAllSoundOff = 4, /*! - * All notes off event type. - */ + * All notes off event type. + */ kEngineControlEventTypeAllNotesOff = 5 }; @@ -174,9 +174,9 @@ struct EngineControlEvent { float value; //!< Parameter value, normalized to 0.0f<->1.0f. /*! - * Dump control event as MIDI data. + * Convert this control event to MIDI data. */ - void dumpToMidiData(const uint8_t channel, uint8_t& size, uint8_t data[3]) const noexcept; + void convertToMidiData(const uint8_t channel, uint8_t& size, uint8_t data[3]) const noexcept; }; /*! @@ -190,7 +190,7 @@ struct EngineMidiEvent { /*! * MIDI data, without channel bit. - * If size > kDataSize, dataExt is used. + * If size > kDataSize, dataExt is used (otherwise NULL). */ uint8_t data[kDataSize]; const uint8_t* dataExt; @@ -213,7 +213,7 @@ struct EngineEvent { }; /*! - * Fill event from MIDI data. + * Fill this event from MIDI data. */ void fillFromMidiData(const uint8_t size, const uint8_t* const data) noexcept; }; @@ -584,7 +584,7 @@ public: * Add a new port of type \a portType. * \note This function does nothing in rack processing mode since ports are static there (2 audio + 1 event for both input and output). */ - virtual CarlaEnginePort* addPort(const EnginePortType portType, const char* const name, const bool isInput) /*noexcept*/; + virtual CarlaEnginePort* addPort(const EnginePortType portType, const char* const name, const bool isInput) noexcept; #ifndef DOXYGEN protected: diff --git a/source/backend/engine/CarlaEngine.cpp b/source/backend/engine/CarlaEngine.cpp index 3b16f1c06..a6348998d 100644 --- a/source/backend/engine/CarlaEngine.cpp +++ b/source/backend/engine/CarlaEngine.cpp @@ -48,6 +48,340 @@ CARLA_BACKEND_START_NAMESPACE } // Fix editor indentation #endif +// ----------------------------------------------------------------------- +// EngineControlEvent + +void EngineControlEvent::convertToMidiData(const uint8_t channel, uint8_t& size, uint8_t data[3]) const noexcept +{ + size = 0; + + switch (type) + { + case kEngineControlEventTypeNull: + break; + + case kEngineControlEventTypeParameter: + if (param >= MAX_MIDI_VALUE) + { + // out of bounds. do nothing + } + else if (MIDI_IS_CONTROL_BANK_SELECT(param)) + { + size = 3; + data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); + data[1] = MIDI_CONTROL_BANK_SELECT; + data[2] = uint8_t(carla_fixValue(0.0f, float(MAX_MIDI_VALUE-1), value)); + } + else + { + size = 3; + data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); + data[1] = static_cast(param); + data[2] = uint8_t(carla_fixValue(0.0f, 1.0f, value) * float(MAX_MIDI_VALUE-1)); + } + break; + + case kEngineControlEventTypeMidiBank: + size = 3; + data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); + data[1] = MIDI_CONTROL_BANK_SELECT; + data[2] = uint8_t(carla_fixValue(0, MAX_MIDI_VALUE-1, param)); + break; + + case kEngineControlEventTypeMidiProgram: + size = 2; + data[0] = static_cast(MIDI_STATUS_PROGRAM_CHANGE | (channel & MIDI_CHANNEL_BIT)); + data[1] = uint8_t(carla_fixValue(0, MAX_MIDI_VALUE-1, param)); + break; + + case kEngineControlEventTypeAllSoundOff: + size = 2; + data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); + data[1] = MIDI_CONTROL_ALL_SOUND_OFF; + break; + + case kEngineControlEventTypeAllNotesOff: + size = 2; + data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); + data[1] = MIDI_CONTROL_ALL_NOTES_OFF; + break; + } +} + +// ----------------------------------------------------------------------- +// EngineEvent + +void EngineEvent::fillFromMidiData(const uint8_t size, const uint8_t* const data) noexcept +{ + if (size == 0 || data == nullptr || data[0] < MIDI_STATUS_NOTE_OFF) + { + type = kEngineEventTypeNull; + channel = 0; + return; + } + + // get channel + channel = uint8_t(MIDI_GET_CHANNEL_FROM_DATA(data)); + + // get status + const uint8_t midiStatus(uint8_t(MIDI_GET_STATUS_FROM_DATA(data))); + + if (midiStatus == MIDI_STATUS_CONTROL_CHANGE) + { + type = kEngineEventTypeControl; + + const uint8_t midiControl(data[1]); + + if (MIDI_IS_CONTROL_BANK_SELECT(midiControl)) + { + CARLA_SAFE_ASSERT_INT(size == 3, size); + + const uint8_t midiBank(data[2]); + + ctrl.type = kEngineControlEventTypeMidiBank; + ctrl.param = midiBank; + ctrl.value = 0.0f; + } + else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF) + { + CARLA_SAFE_ASSERT_INT(size == 2, size); + + ctrl.type = kEngineControlEventTypeAllSoundOff; + ctrl.param = 0; + ctrl.value = 0.0f; + } + else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF) + { + CARLA_SAFE_ASSERT_INT(size == 2, size); + + ctrl.type = kEngineControlEventTypeAllNotesOff; + ctrl.param = 0; + ctrl.value = 0.0f; + } + else + { + CARLA_SAFE_ASSERT_INT2(size == 3, size, midiControl); + + const uint8_t midiValue(carla_fixValue(0, 127, data[2])); // ensures 0.0<->1.0 value range + + ctrl.type = kEngineControlEventTypeParameter; + ctrl.param = midiControl; + ctrl.value = float(midiValue)/127.0f; + } + } + else if (midiStatus == MIDI_STATUS_PROGRAM_CHANGE) + { + CARLA_SAFE_ASSERT_INT2(size == 2, size, data[1]); + + type = kEngineEventTypeControl; + + const uint8_t midiProgram(data[1]); + + ctrl.type = kEngineControlEventTypeMidiProgram; + ctrl.param = midiProgram; + ctrl.value = 0.0f; + } + else + { + type = kEngineEventTypeMidi; + + midi.port = 0; + midi.size = size; + + if (size > EngineMidiEvent::kDataSize) + { + midi.dataExt = data; + std::memset(midi.data, 0, sizeof(uint8_t)*EngineMidiEvent::kDataSize); + } + else + { + midi.data[0] = midiStatus; + + uint8_t i=1; + for (; i < midi.size; ++i) + midi.data[i] = data[i]; + for (; i < EngineMidiEvent::kDataSize; ++i) + midi.data[i] = 0; + + midi.dataExt = nullptr; + } + } +} + +// ----------------------------------------------------------------------- +// EngineOptions + +EngineOptions::EngineOptions() noexcept +#ifdef CARLA_OS_LINUX + : processMode(ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS), + transportMode(ENGINE_TRANSPORT_MODE_JACK), +#else + : processMode(ENGINE_PROCESS_MODE_CONTINUOUS_RACK), + transportMode(ENGINE_TRANSPORT_MODE_INTERNAL), +#endif + forceStereo(false), + preferPluginBridges(false), + preferUiBridges(true), + uisAlwaysOnTop(true), + maxParameters(MAX_DEFAULT_PARAMETERS), + uiBridgesTimeout(4000), + audioNumPeriods(2), + audioBufferSize(512), + audioSampleRate(44100), + audioDevice(nullptr), + binaryDir(nullptr), + resourceDir(nullptr), + frontendWinId(0) {} + +EngineOptions::~EngineOptions() noexcept +{ + if (audioDevice != nullptr) + { + delete[] audioDevice; + audioDevice = nullptr; + } + + if (binaryDir != nullptr) + { + delete[] binaryDir; + binaryDir = nullptr; + } + + if (resourceDir != nullptr) + { + delete[] resourceDir; + resourceDir = nullptr; + } +} + +// ----------------------------------------------------------------------- +// EngineTimeInfoBBT + +EngineTimeInfoBBT::EngineTimeInfoBBT() noexcept + : bar(0), + beat(0), + tick(0), + barStartTick(0.0), + beatsPerBar(0.0f), + beatType(0.0f), + ticksPerBeat(0.0), + beatsPerMinute(0.0) {} + +// ----------------------------------------------------------------------- +// EngineTimeInfo + +EngineTimeInfo::EngineTimeInfo() noexcept + : playing(false), + frame(0), + usecs(0), + valid(0x0) {} + +void EngineTimeInfo::clear() noexcept +{ + playing = false; + frame = 0; + usecs = 0; + valid = 0x0; +} + +bool EngineTimeInfo::operator==(const EngineTimeInfo& timeInfo) const noexcept +{ + if (timeInfo.playing != playing || timeInfo.frame != frame || timeInfo.valid != valid) + return false; + if ((valid & kValidBBT) == 0) + return true; + if (timeInfo.bbt.beatsPerMinute != bbt.beatsPerMinute) + return false; + return true; +} + +bool EngineTimeInfo::operator!=(const EngineTimeInfo& timeInfo) const noexcept +{ + return !operator==(timeInfo); +} + +// ----------------------------------------------------------------------- +// Carla Engine client (Abstract) + +CarlaEngineClient::CarlaEngineClient(const CarlaEngine& engine) + : fEngine(engine), + fActive(false), + fLatency(0) +{ + carla_debug("CarlaEngineClient::CarlaEngineClient()"); +} + +CarlaEngineClient::~CarlaEngineClient() +{ + CARLA_SAFE_ASSERT(! fActive); + carla_debug("CarlaEngineClient::~CarlaEngineClient()"); +} + +void CarlaEngineClient::activate() noexcept +{ + CARLA_SAFE_ASSERT(! fActive); + carla_debug("CarlaEngineClient::activate()"); + + fActive = true; +} + +void CarlaEngineClient::deactivate() noexcept +{ + CARLA_SAFE_ASSERT(fActive); + carla_debug("CarlaEngineClient::deactivate()"); + + fActive = false; +} + +bool CarlaEngineClient::isActive() const noexcept +{ + return fActive; +} + +bool CarlaEngineClient::isOk() const noexcept +{ + return true; +} + +uint32_t CarlaEngineClient::getLatency() const noexcept +{ + return fLatency; +} + +void CarlaEngineClient::setLatency(const uint32_t samples) noexcept +{ + fLatency = samples; +} + +CarlaEnginePort* CarlaEngineClient::addPort(const EnginePortType portType, const char* const name, const bool isInput) noexcept +{ + CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', nullptr); + carla_debug("CarlaEngineClient::addPort(%i:%s, \"%s\", %s)", portType, EnginePortType2Str(portType), name, bool2str(isInput)); + + CarlaEnginePort* ret = nullptr; + + try { + switch (portType) + { + case kEnginePortTypeNull: + break; + case kEnginePortTypeAudio: + ret = new CarlaEngineAudioPort(fEngine, isInput); + case kEnginePortTypeCV: + ret = new CarlaEngineCVPort(fEngine, isInput); + case kEnginePortTypeEvent: + ret = new CarlaEngineEventPort(fEngine, isInput); + } + } + CARLA_SAFE_EXCEPTION_RETURN("new CarlaEnginePort", nullptr); + + if (ret != nullptr) + return ret; + + carla_stderr("CarlaEngineClient::addPort(%i, \"%s\", %s) - invalid type", portType, name, bool2str(isInput)); + return nullptr; +} + // ----------------------------------------------------------------------- // Carla Engine diff --git a/source/backend/engine/CarlaEngineClient.cpp b/source/backend/engine/CarlaEngineClient.cpp deleted file mode 100644 index 262e41901..000000000 --- a/source/backend/engine/CarlaEngineClient.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Carla Plugin Host - * Copyright (C) 2011-2014 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 doc/GPL.txt file. - */ - -#include "CarlaEngineInternal.hpp" -#include "CarlaEngineUtils.hpp" - -// ----------------------------------------------------------------------- - -CARLA_BACKEND_START_NAMESPACE - -#if 0 -} // Fix editor indentation -#endif - -// ----------------------------------------------------------------------- -// Carla Engine client (Abstract) - -CarlaEngineClient::CarlaEngineClient(const CarlaEngine& engine) - : fEngine(engine), - fActive(false), - fLatency(0) -{ - carla_debug("CarlaEngineClient::CarlaEngineClient()"); -} - -CarlaEngineClient::~CarlaEngineClient() -{ - CARLA_SAFE_ASSERT(! fActive); - carla_debug("CarlaEngineClient::~CarlaEngineClient()"); -} - -void CarlaEngineClient::activate() noexcept -{ - CARLA_SAFE_ASSERT(! fActive); - carla_debug("CarlaEngineClient::activate()"); - - fActive = true; -} - -void CarlaEngineClient::deactivate() noexcept -{ - CARLA_SAFE_ASSERT(fActive); - carla_debug("CarlaEngineClient::deactivate()"); - - fActive = false; -} - -bool CarlaEngineClient::isActive() const noexcept -{ - return fActive; -} - -bool CarlaEngineClient::isOk() const noexcept -{ - return true; -} - -uint32_t CarlaEngineClient::getLatency() const noexcept -{ - return fLatency; -} - -void CarlaEngineClient::setLatency(const uint32_t samples) noexcept -{ - fLatency = samples; -} - -CarlaEnginePort* CarlaEngineClient::addPort(const EnginePortType portType, const char* const name, const bool isInput) /*noexcept*/ -{ - CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', nullptr); - carla_debug("CarlaEngineClient::addPort(%i:%s, \"%s\", %s)", portType, EnginePortType2Str(portType), name, bool2str(isInput)); - - CarlaEnginePort* ret = nullptr; - - try { - switch (portType) - { - case kEnginePortTypeNull: - break; - case kEnginePortTypeAudio: - ret = new CarlaEngineAudioPort(fEngine, isInput); - case kEnginePortTypeCV: - ret = new CarlaEngineCVPort(fEngine, isInput); - case kEnginePortTypeEvent: - ret = new CarlaEngineEventPort(fEngine, isInput); - } - } - catch(...) { - return nullptr; - } - - if (ret != nullptr) - return ret; - - carla_stderr("CarlaEngineClient::addPort(%i, \"%s\", %s) - invalid type", portType, name, bool2str(isInput)); - return nullptr; -} - -// ----------------------------------------------------------------------- - -CARLA_BACKEND_END_NAMESPACE diff --git a/source/backend/engine/CarlaEngineData.cpp b/source/backend/engine/CarlaEngineData.cpp deleted file mode 100644 index 0de96b3a4..000000000 --- a/source/backend/engine/CarlaEngineData.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Carla Plugin Host - * Copyright (C) 2011-2014 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 doc/GPL.txt file. - */ - -#include "CarlaEngineInternal.hpp" -#include "CarlaMIDI.h" - -#include "CarlaMathUtils.hpp" - -// ----------------------------------------------------------------------- - -CARLA_BACKEND_START_NAMESPACE - -#if 0 -} // Fix editor indentation -#endif - -// ----------------------------------------------------------------------- -// EngineControlEvent - -void EngineControlEvent::dumpToMidiData(const uint8_t channel, uint8_t& size, uint8_t data[3]) const noexcept -{ - size = 0; - - switch (type) - { - case kEngineControlEventTypeNull: - break; - - case kEngineControlEventTypeParameter: - if (param >= MAX_MIDI_VALUE) - { - // out of bounds. do nothing - } - else if (MIDI_IS_CONTROL_BANK_SELECT(param)) - { - size = 3; - data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); - data[1] = MIDI_CONTROL_BANK_SELECT; - data[2] = uint8_t(carla_fixValue(0.0f, float(MAX_MIDI_VALUE-1), value)); - } - else - { - size = 3; - data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); - data[1] = static_cast(param); - data[2] = uint8_t(carla_fixValue(0.0f, 1.0f, value) * float(MAX_MIDI_VALUE-1)); - } - break; - - case kEngineControlEventTypeMidiBank: - size = 3; - data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); - data[1] = MIDI_CONTROL_BANK_SELECT; - data[2] = uint8_t(carla_fixValue(0, MAX_MIDI_VALUE-1, param)); - break; - - case kEngineControlEventTypeMidiProgram: - size = 2; - data[0] = static_cast(MIDI_STATUS_PROGRAM_CHANGE | (channel & MIDI_CHANNEL_BIT)); - data[1] = uint8_t(carla_fixValue(0, MAX_MIDI_VALUE-1, param)); - break; - - case kEngineControlEventTypeAllSoundOff: - size = 2; - data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); - data[1] = MIDI_CONTROL_ALL_SOUND_OFF; - break; - - case kEngineControlEventTypeAllNotesOff: - size = 2; - data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); - data[1] = MIDI_CONTROL_ALL_NOTES_OFF; - break; - } -} - -// ----------------------------------------------------------------------- -// EngineEvent - -void EngineEvent::fillFromMidiData(const uint8_t size, const uint8_t* const data) noexcept -{ - if (size == 0 || data == nullptr || data[0] < MIDI_STATUS_NOTE_OFF) - { - type = kEngineEventTypeNull; - channel = 0; - return; - } - - // get channel - channel = uint8_t(MIDI_GET_CHANNEL_FROM_DATA(data)); - - // get status - const uint8_t midiStatus(uint8_t(MIDI_GET_STATUS_FROM_DATA(data))); - - if (midiStatus == MIDI_STATUS_CONTROL_CHANGE) - { - type = kEngineEventTypeControl; - - const uint8_t midiControl(data[1]); - - if (MIDI_IS_CONTROL_BANK_SELECT(midiControl)) - { - CARLA_SAFE_ASSERT_INT(size == 3, size); - - const uint8_t midiBank(data[2]); - - ctrl.type = kEngineControlEventTypeMidiBank; - ctrl.param = midiBank; - ctrl.value = 0.0f; - } - else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF) - { - CARLA_SAFE_ASSERT_INT(size == 2, size); - - ctrl.type = kEngineControlEventTypeAllSoundOff; - ctrl.param = 0; - ctrl.value = 0.0f; - } - else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF) - { - CARLA_SAFE_ASSERT_INT(size == 2, size); - - ctrl.type = kEngineControlEventTypeAllNotesOff; - ctrl.param = 0; - ctrl.value = 0.0f; - } - else - { - CARLA_SAFE_ASSERT_INT2(size == 3, size, midiControl); - - const uint8_t midiValue(carla_fixValue(0, 127, data[2])); // ensures 0.0<->1.0 value range - - ctrl.type = kEngineControlEventTypeParameter; - ctrl.param = midiControl; - ctrl.value = float(midiValue)/127.0f; - } - } - else if (midiStatus == MIDI_STATUS_PROGRAM_CHANGE) - { - CARLA_SAFE_ASSERT_INT2(size == 2, size, data[1]); - - type = kEngineEventTypeControl; - - const uint8_t midiProgram(data[1]); - - ctrl.type = kEngineControlEventTypeMidiProgram; - ctrl.param = midiProgram; - ctrl.value = 0.0f; - } - else - { - type = kEngineEventTypeMidi; - - midi.port = 0; - midi.size = size; - - if (size > EngineMidiEvent::kDataSize) - { - midi.dataExt = data; - std::memset(midi.data, 0, sizeof(uint8_t)*EngineMidiEvent::kDataSize); - } - else - { - midi.data[0] = midiStatus; - - uint8_t i=1; - for (; i < midi.size; ++i) - midi.data[i] = data[i]; - for (; i < EngineMidiEvent::kDataSize; ++i) - midi.data[i] = 0; - - midi.dataExt = nullptr; - } - } -} - -// ----------------------------------------------------------------------- -// EngineOptions - -EngineOptions::EngineOptions() noexcept -#ifdef CARLA_OS_LINUX - : processMode(ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS), - transportMode(ENGINE_TRANSPORT_MODE_JACK), -#else - : processMode(ENGINE_PROCESS_MODE_CONTINUOUS_RACK), - transportMode(ENGINE_TRANSPORT_MODE_INTERNAL), -#endif - forceStereo(false), - preferPluginBridges(false), - preferUiBridges(true), - uisAlwaysOnTop(true), - maxParameters(MAX_DEFAULT_PARAMETERS), - uiBridgesTimeout(4000), - audioNumPeriods(2), - audioBufferSize(512), - audioSampleRate(44100), - audioDevice(nullptr), - binaryDir(nullptr), - resourceDir(nullptr), - frontendWinId(0) {} - -EngineOptions::~EngineOptions() noexcept -{ - if (audioDevice != nullptr) - { - delete[] audioDevice; - audioDevice = nullptr; - } - - if (binaryDir != nullptr) - { - delete[] binaryDir; - binaryDir = nullptr; - } - - if (resourceDir != nullptr) - { - delete[] resourceDir; - resourceDir = nullptr; - } -} - -// ----------------------------------------------------------------------- -// EngineTimeInfoBBT - -EngineTimeInfoBBT::EngineTimeInfoBBT() noexcept - : bar(0), - beat(0), - tick(0), - barStartTick(0.0), - beatsPerBar(0.0f), - beatType(0.0f), - ticksPerBeat(0.0), - beatsPerMinute(0.0) {} - -// ----------------------------------------------------------------------- -// EngineTimeInfo - -EngineTimeInfo::EngineTimeInfo() noexcept - : playing(false), - frame(0), - usecs(0), - valid(0x0) {} - -void EngineTimeInfo::clear() noexcept -{ - playing = false; - frame = 0; - usecs = 0; - valid = 0x0; -} - -bool EngineTimeInfo::operator==(const EngineTimeInfo& timeInfo) const noexcept -{ - if (timeInfo.playing != playing || timeInfo.frame != frame || timeInfo.valid != valid) - return false; - if ((valid & kValidBBT) == 0) - return true; - if (timeInfo.bbt.beatsPerMinute != bbt.beatsPerMinute) - return false; - return true; -} - -bool EngineTimeInfo::operator!=(const EngineTimeInfo& timeInfo) const noexcept -{ - return !operator==(timeInfo); -} - -// ----------------------------------------------------------------------- - -CARLA_BACKEND_END_NAMESPACE diff --git a/source/backend/engine/CarlaEngineInternal.cpp b/source/backend/engine/CarlaEngineInternal.cpp index 2ef87a488..6e92c735e 100644 --- a/source/backend/engine/CarlaEngineInternal.cpp +++ b/source/backend/engine/CarlaEngineInternal.cpp @@ -29,6 +29,104 @@ CARLA_BACKEND_START_NAMESPACE } // Fix editor indentation #endif +// ----------------------------------------------------------------------- +// Rack Patchbay stuff + +enum RackPatchbayGroupIds { + RACK_PATCHBAY_GROUP_CARLA = 0, + RACK_PATCHBAY_GROUP_AUDIO = 1, + RACK_PATCHBAY_GROUP_MIDI = 2, + RACK_PATCHBAY_GROUP_MAX = 3 +}; + +enum RackPatchbayCarlaPortIds { + RACK_PATCHBAY_CARLA_PORT_NULL = 0, + RACK_PATCHBAY_CARLA_PORT_AUDIO_IN1 = 1, + RACK_PATCHBAY_CARLA_PORT_AUDIO_IN2 = 2, + RACK_PATCHBAY_CARLA_PORT_AUDIO_OUT1 = 3, + RACK_PATCHBAY_CARLA_PORT_AUDIO_OUT2 = 4, + RACK_PATCHBAY_CARLA_PORT_MIDI_IN = 5, + RACK_PATCHBAY_CARLA_PORT_MIDI_OUT = 6, + RACK_PATCHBAY_CARLA_PORT_MAX = 7 +}; + +struct PortNameToId { + int portId; + char name[STR_MAX+1]; +}; + +struct ConnectionToId { + uint id; + int groupA; + int portA; + int groupB; + int portB; +}; + +static inline +int getCarlaRackPortIdFromName(const char* const shortname) noexcept +{ + if (std::strcmp(shortname, "AudioIn1") == 0) + return RACK_PATCHBAY_CARLA_PORT_AUDIO_IN1; + if (std::strcmp(shortname, "AudioIn2") == 0) + return RACK_PATCHBAY_CARLA_PORT_AUDIO_IN2; + if (std::strcmp(shortname, "AudioOut1") == 0) + return RACK_PATCHBAY_CARLA_PORT_AUDIO_OUT1; + if (std::strcmp(shortname, "AudioOut2") == 0) + return RACK_PATCHBAY_CARLA_PORT_AUDIO_OUT2; + if (std::strcmp(shortname, "MidiIn") == 0) + return RACK_PATCHBAY_CARLA_PORT_MIDI_IN; + if (std::strcmp(shortname, "MidiOut") == 0) + return RACK_PATCHBAY_CARLA_PORT_MIDI_OUT; + return RACK_PATCHBAY_CARLA_PORT_NULL; +} + +// ----------------------------------------------------------------------- +// EngineRackBuffers + +struct EngineRackBuffers { + float* in[2]; + float* out[2]; + + // connections stuff + LinkedList connectedIn1; + LinkedList connectedIn2; + LinkedList connectedOut1; + LinkedList connectedOut2; + CarlaCriticalSection connectLock; + + uint lastConnectionId; + LinkedList usedConnections; + + EngineRackBuffers(const uint32_t bufferSize); + ~EngineRackBuffers() noexcept; + void clear() noexcept; + void resize(const uint32_t bufferSize); + + bool connect(CarlaEngine* const engine, const int groupA, const int portA, const int groupB, const int port) noexcept; + bool disconnect(const uint connectionId); + + const char* const* getConnections() const; + + CARLA_DECLARE_NON_COPY_STRUCT(EngineRackBuffers) +}; + +// ----------------------------------------------------------------------- +// EnginePatchbayBuffers + +struct EnginePatchbayBuffers { + // TODO + EnginePatchbayBuffers(const uint32_t bufferSize); + ~EnginePatchbayBuffers() noexcept; + void clear() noexcept; + void resize(const uint32_t bufferSize); + + bool connect(CarlaEngine* const engine, const int groupA, const int portA, const int groupB, const int port) noexcept; + const char* const* getConnections() const; + + CARLA_DECLARE_NON_COPY_STRUCT(EnginePatchbayBuffers) +}; + // ----------------------------------------------------------------------- // EngineRackBuffers @@ -299,564 +397,37 @@ const char* const* EnginePatchbayBuffers::getConnections() const } // ----------------------------------------------------------------------- -// InternalAudio - -EngineInternalAudio::EngineInternalAudio() noexcept - : isReady(false), - usePatchbay(false), - inCount(0), - outCount(0) -{ - rack = nullptr; -} - -EngineInternalAudio::~EngineInternalAudio() noexcept -{ - CARLA_SAFE_ASSERT(! isReady); - CARLA_SAFE_ASSERT(rack == nullptr); -} - -void EngineInternalAudio::initPatchbay() noexcept -{ - if (usePatchbay) - { - CARLA_SAFE_ASSERT_RETURN(patchbay != nullptr,); - - // TODO - } - else - { - CARLA_SAFE_ASSERT_RETURN(rack != nullptr,); - - rack->lastConnectionId = 0; - rack->usedConnections.clear(); - } -} +// EnginePatchbayBuffers -void EngineInternalAudio::clear() noexcept +EngineInternalAudio::create(const uint32_t bufferSize) { - isReady = false; - inCount = 0; - outCount = 0; + CARLA_SAFE_ASSERT_RETURN(buffer == nullptr,); if (usePatchbay) - { - CARLA_SAFE_ASSERT_RETURN(patchbay != nullptr,); - delete patchbay; - patchbay = nullptr; - } + buffer = new EnginePatchbayBuffers(bufferSize); else - { - CARLA_SAFE_ASSERT_RETURN(rack != nullptr,); - delete rack; - rack = nullptr; - } -} - -void EngineInternalAudio::create(const uint32_t bufferSize) -{ - if (usePatchbay) - { - CARLA_SAFE_ASSERT_RETURN(patchbay == nullptr,); - patchbay = new EnginePatchbayBuffers(bufferSize); - } - else - { - CARLA_SAFE_ASSERT_RETURN(rack == nullptr,); - rack = new EngineRackBuffers(bufferSize); - } + buffer = new EngineRackBuffers(bufferSize); isReady = true; } -void EngineInternalAudio::resize(const uint32_t bufferSize) +EngineInternalAudio::initPatchbay() noexcept { if (usePatchbay) { CARLA_SAFE_ASSERT_RETURN(patchbay != nullptr,); - patchbay->resize(bufferSize); - } - else - { - CARLA_SAFE_ASSERT_RETURN(rack != nullptr,); - rack->resize(bufferSize); - } -} - -bool EngineInternalAudio::connect(CarlaEngine* const engine, const int groupA, const int portA, const int groupB, const int portB) noexcept -{ - if (usePatchbay) - { - CARLA_SAFE_ASSERT_RETURN(patchbay != nullptr, nullptr); - return patchbay->connect(engine, groupA, portA, groupB, portB); - } - else - { - CARLA_SAFE_ASSERT_RETURN(rack != nullptr, nullptr); - return rack->connect(engine, groupA, portA, groupB, portB); - } -} - -const char* const* EngineInternalAudio::getConnections() const -{ - if (usePatchbay) - { - CARLA_SAFE_ASSERT_RETURN(patchbay != nullptr, nullptr); - return patchbay->getConnections(); - } - else - { - CARLA_SAFE_ASSERT_RETURN(rack != nullptr, nullptr); - return rack->getConnections(); - } -} - -// ----------------------------------------------------------------------- -// InternalEvents - -EngineInternalEvents::EngineInternalEvents() noexcept - : in(nullptr), - out(nullptr) {} -EngineInternalEvents::~EngineInternalEvents() noexcept -{ - CARLA_SAFE_ASSERT(in == nullptr); - CARLA_SAFE_ASSERT(out == nullptr); -} - -// ----------------------------------------------------------------------- -// InternalTime - -EngineInternalTime::EngineInternalTime() noexcept - : playing(false), - frame(0) {} - -// ----------------------------------------------------------------------- -// NextAction - -EngineNextAction::EngineNextAction() noexcept - : opcode(kEnginePostActionNull), - pluginId(0), - value(0) {} - -EngineNextAction::~EngineNextAction() noexcept -{ - CARLA_SAFE_ASSERT(opcode == kEnginePostActionNull); -} - -void EngineNextAction::ready() noexcept -{ - mutex.lock(); - mutex.unlock(); -} - -// ----------------------------------------------------------------------- -// EnginePluginData - -void EnginePluginData::clear() noexcept -{ - plugin = nullptr; - insPeak[0] = insPeak[1] = 0.0f; - outsPeak[0] = outsPeak[1] = 0.0f; -} - -// ----------------------------------------------------------------------- -// CarlaEngineProtectedData - -CarlaEngineProtectedData::CarlaEngineProtectedData(CarlaEngine* const engine) - : osc(engine), - thread(engine), - oscData(nullptr), - callback(nullptr), - callbackPtr(nullptr), - fileCallback(nullptr), - fileCallbackPtr(nullptr), - hints(0x0), - bufferSize(0), - sampleRate(0.0), - aboutToClose(false), - curPluginCount(0), - maxPluginNumber(0), - nextPluginId(0), - plugins(nullptr) {} - -CarlaEngineProtectedData::~CarlaEngineProtectedData() noexcept -{ - CARLA_SAFE_ASSERT(curPluginCount == 0); - CARLA_SAFE_ASSERT(maxPluginNumber == 0); - CARLA_SAFE_ASSERT(nextPluginId == 0); - CARLA_SAFE_ASSERT(plugins == nullptr); -} - -// ----------------------------------------------------------------------- - -void CarlaEngineProtectedData::doPluginRemove() noexcept -{ - CARLA_SAFE_ASSERT_RETURN(curPluginCount > 0,); - CARLA_SAFE_ASSERT_RETURN(nextAction.pluginId < curPluginCount,); - --curPluginCount; - - // move all plugins 1 spot backwards - for (unsigned int i=nextAction.pluginId; i < curPluginCount; ++i) - { - CarlaPlugin* const plugin(plugins[i+1].plugin); - - CARLA_SAFE_ASSERT_BREAK(plugin != nullptr); - - plugin->setId(i); - - plugins[i].plugin = plugin; - plugins[i].insPeak[0] = 0.0f; - plugins[i].insPeak[1] = 0.0f; - plugins[i].outsPeak[0] = 0.0f; - plugins[i].outsPeak[1] = 0.0f; - } - - const unsigned int id(curPluginCount); - - // reset last plugin (now removed) - plugins[id].plugin = nullptr; - plugins[id].insPeak[0] = 0.0f; - plugins[id].insPeak[1] = 0.0f; - plugins[id].outsPeak[0] = 0.0f; - plugins[id].outsPeak[1] = 0.0f; -} - -void CarlaEngineProtectedData::doPluginsSwitch() noexcept -{ - CARLA_SAFE_ASSERT_RETURN(curPluginCount >= 2,); - - const unsigned int idA(nextAction.pluginId); - const unsigned int idB(nextAction.value); - - CARLA_SAFE_ASSERT_RETURN(idA < curPluginCount,); - CARLA_SAFE_ASSERT_RETURN(idB < curPluginCount,); - CARLA_SAFE_ASSERT_RETURN(plugins[idA].plugin != nullptr,); - CARLA_SAFE_ASSERT_RETURN(plugins[idB].plugin != nullptr,); - -#if 0 - std::swap(plugins[idA].plugin, plugins[idB].plugin); -#else - CarlaPlugin* const tmp(plugins[idA].plugin); - - plugins[idA].plugin = plugins[idB].plugin; - plugins[idB].plugin = tmp; -#endif -} - -void CarlaEngineProtectedData::doNextPluginAction(const bool unlock) noexcept -{ - switch (nextAction.opcode) - { - case kEnginePostActionNull: - break; - case kEnginePostActionZeroCount: - curPluginCount = 0; - break; - case kEnginePostActionRemovePlugin: - doPluginRemove(); - break; - case kEnginePostActionSwitchPlugins: - doPluginsSwitch(); - break; - } - - nextAction.opcode = kEnginePostActionNull; - nextAction.pluginId = 0; - nextAction.value = 0; - - if (unlock) - nextAction.mutex.unlock(); -} - -// ----------------------------------------------------------------------- - -#ifndef BUILD_BRIDGE -void CarlaEngineProtectedData::processRack(float* inBufReal[2], float* outBuf[2], const uint32_t frames, const bool isOffline) -{ - CARLA_SAFE_ASSERT_RETURN(bufEvents.in != nullptr,); - CARLA_SAFE_ASSERT_RETURN(bufEvents.out != nullptr,); - - // safe copy - float inBuf0[frames]; - float inBuf1[frames]; - float* inBuf[2] = { inBuf0, inBuf1 }; - - // initialize audio inputs - FLOAT_COPY(inBuf0, inBufReal[0], frames); - FLOAT_COPY(inBuf1, inBufReal[1], frames); - - // initialize audio outputs (zero) - FLOAT_CLEAR(outBuf[0], frames); - FLOAT_CLEAR(outBuf[1], frames); - - // initialize event outputs (zero) - carla_zeroStruct(bufEvents.out, kMaxEngineEventInternalCount); - - bool processed = false; - - uint32_t oldAudioInCount = 0; - uint32_t oldMidiOutCount = 0; - - // process plugins - for (unsigned int i=0; i < curPluginCount; ++i) - { - CarlaPlugin* const plugin = plugins[i].plugin; - - if (plugin == nullptr || ! plugin->isEnabled() || ! plugin->tryLock(isOffline)) - continue; - - if (processed) - { - // initialize audio inputs (from previous outputs) - FLOAT_COPY(inBuf0, outBuf[0], frames); - FLOAT_COPY(inBuf1, outBuf[1], frames); - - // initialize audio outputs (zero) - FLOAT_CLEAR(outBuf[0], frames); - FLOAT_CLEAR(outBuf[1], frames); - - // if plugin has no midi out, add previous events - if (oldMidiOutCount == 0 && bufEvents.in[0].type != kEngineEventTypeNull) - { - if (bufEvents.out[0].type != kEngineEventTypeNull) - { - // TODO: carefully add to input, sorted events - } - // else nothing needed - } - else - { - // initialize event inputs from previous outputs - carla_copyStruct(bufEvents.in, bufEvents.out, kMaxEngineEventInternalCount); - - // initialize event outputs (zero) - carla_zeroStruct(bufEvents.out, kMaxEngineEventInternalCount); - } - } - - oldAudioInCount = plugin->getAudioInCount(); - oldMidiOutCount = plugin->getMidiOutCount(); - - // process - plugin->initBuffers(); - plugin->process(inBuf, outBuf, frames); - plugin->unlock(); - - // if plugin has no audio inputs, add input buffer - if (oldAudioInCount == 0) - { - FLOAT_ADD(outBuf[0], inBuf0, frames); - FLOAT_ADD(outBuf[1], inBuf1, frames); - } - - // set peaks - { - EnginePluginData& pluginData(plugins[i]); - -#ifdef HAVE_JUCE - float tmpMin, tmpMax; - - if (oldAudioInCount > 0) - { - FloatVectorOperations::findMinAndMax(inBuf0, static_cast(frames), tmpMin, tmpMax); - pluginData.insPeak[0] = carla_max(std::abs(tmpMin), std::abs(tmpMax), 1.0f); - - FloatVectorOperations::findMinAndMax(inBuf1, static_cast(frames), tmpMin, tmpMax); - pluginData.insPeak[1] = carla_max(std::abs(tmpMin), std::abs(tmpMax), 1.0f); - } - else - { - pluginData.insPeak[0] = 0.0f; - pluginData.insPeak[1] = 0.0f; - } - - if (plugin->getAudioOutCount() > 0) - { - FloatVectorOperations::findMinAndMax(outBuf[0], static_cast(frames), tmpMin, tmpMax); - pluginData.outsPeak[0] = carla_max(std::abs(tmpMin), std::abs(tmpMax), 1.0f); - - FloatVectorOperations::findMinAndMax(outBuf[1], static_cast(frames), tmpMin, tmpMax); - pluginData.outsPeak[1] = carla_max(std::abs(tmpMin), std::abs(tmpMax), 1.0f); - } - else - { - pluginData.outsPeak[0] = 0.0f; - pluginData.outsPeak[1] = 0.0f; - } -#else - float peak1, peak2; - - if (oldAudioInCount > 0) - { - peak1 = peak2 = 0.0f; - - for (uint32_t k=0; k < frames; ++k) - { - peak1 = carla_max(peak1, std::fabs(inBuf0[k]), 1.0f); - peak2 = carla_max(peak2, std::fabs(inBuf1[k]), 1.0f); - } - - pluginData.insPeak[0] = peak1; - pluginData.insPeak[1] = peak2; - } - else - { - pluginData.insPeak[0] = 0.0f; - pluginData.insPeak[1] = 0.0f; - } - - if (plugin->getAudioOutCount() > 0) - { - peak1 = peak2 = 0.0f; - - for (uint32_t k=0; k < frames; ++k) - { - peak1 = carla_max(peak1, std::fabs(outBuf[0][k]), 1.0f); - peak2 = carla_max(peak2, std::fabs(outBuf[1][k]), 1.0f); - } - - pluginData.outsPeak[0] = peak1; - pluginData.outsPeak[1] = peak2; - } - else - { - pluginData.outsPeak[0] = 0.0f; - pluginData.outsPeak[1] = 0.0f; - } -#endif - } - - processed = true; - } -} - -void CarlaEngineProtectedData::processRackFull(float** const inBuf, const uint32_t inCount, float** const outBuf, const uint32_t outCount, const uint32_t nframes, const bool isOffline) -{ - EngineRackBuffers* const rack(bufAudio.rack); - - const CarlaCriticalSectionScope _cs2(rack->connectLock); - - // connect input buffers - if (rack->connectedIn1.count() == 0) - { - FLOAT_CLEAR(rack->in[0], nframes); - } - else - { - bool first = true; - - for (LinkedList::Itenerator it = rack->connectedIn1.begin(); it.valid(); it.next()) - { - const int& port(it.getValue()); - CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast(inCount)); - - if (first) - { - FLOAT_COPY(rack->in[0], inBuf[port], nframes); - first = false; - } - else - { - FLOAT_ADD(rack->in[0], inBuf[port], nframes); - } - } - - if (first) - FLOAT_CLEAR(rack->in[0], nframes); - } - - if (rack->connectedIn2.count() == 0) - { - FLOAT_CLEAR(rack->in[1], nframes); + // TODO } else { - bool first = true; - - for (LinkedList::Itenerator it = rack->connectedIn2.begin(); it.valid(); it.next()) - { - const int& port(it.getValue()); - CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast(inCount)); - - if (first) - { - FLOAT_COPY(rack->in[1], inBuf[port], nframes); - first = false; - } - else - { - FLOAT_ADD(rack->in[1], inBuf[port], nframes); - } - } - - if (first) - FLOAT_CLEAR(rack->in[1], nframes); - } - - FLOAT_CLEAR(rack->out[0], nframes); - FLOAT_CLEAR(rack->out[1], nframes); - - // process - processRack(rack->in, rack->out, nframes, isOffline); - - // connect output buffers - if (rack->connectedOut1.count() != 0) - { - for (LinkedList::Itenerator it = rack->connectedOut1.begin(); it.valid(); it.next()) - { - const int& port(it.getValue()); - CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast(outCount)); - - FLOAT_ADD(outBuf[port], rack->out[0], nframes); - } - } - - if (rack->connectedOut2.count() != 0) - { - for (LinkedList::Itenerator it = rack->connectedOut2.begin(); it.valid(); it.next()) - { - const int& port(it.getValue()); - CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast(outCount)); - - FLOAT_ADD(outBuf[port], rack->out[1], nframes); - } - } -} -#endif - -// ----------------------------------------------------------------------- -// ScopedActionLock - -CarlaEngineProtectedData::ScopedActionLock::ScopedActionLock(CarlaEngineProtectedData* const data, const EnginePostAction action, const unsigned int pluginId, const unsigned int value, const bool lockWait) noexcept - : fData(data) -{ - fData->nextAction.mutex.lock(); - - CARLA_SAFE_ASSERT_RETURN(fData->nextAction.opcode == kEnginePostActionNull,); - - fData->nextAction.opcode = action; - fData->nextAction.pluginId = pluginId; - fData->nextAction.value = value; + CARLA_SAFE_ASSERT_RETURN(rack != nullptr,); - if (lockWait) - { - // block wait for unlock on processing side - carla_stdout("ScopedPluginAction(%i) - blocking START", pluginId); - fData->nextAction.mutex.lock(); - carla_stdout("ScopedPluginAction(%i) - blocking DONE", pluginId); - } - else - { - fData->doNextPluginAction(false); + rack->lastConnectionId = 0; + rack->usedConnections.clear(); } } -CarlaEngineProtectedData::ScopedActionLock::~ScopedActionLock() noexcept -{ - fData->nextAction.mutex.unlock(); -} - // ----------------------------------------------------------------------- CARLA_BACKEND_END_NAMESPACE diff --git a/source/backend/engine/CarlaEngineInternal.hpp b/source/backend/engine/CarlaEngineInternal.hpp index 577509f72..8da9e1f36 100644 --- a/source/backend/engine/CarlaEngineInternal.hpp +++ b/source/backend/engine/CarlaEngineInternal.hpp @@ -45,125 +45,59 @@ CARLA_BACKEND_START_NAMESPACE const unsigned short kMaxEngineEventInternalCount = 512; // ----------------------------------------------------------------------- -// Rack Patchbay stuff +// AbstractEngineBuffer -enum RackPatchbayGroupIds { - RACK_PATCHBAY_GROUP_CARLA = 0, - RACK_PATCHBAY_GROUP_AUDIO = 1, - RACK_PATCHBAY_GROUP_MIDI = 2, - RACK_PATCHBAY_GROUP_MAX = 3 -}; - -enum RackPatchbayCarlaPortIds { - RACK_PATCHBAY_CARLA_PORT_NULL = 0, - RACK_PATCHBAY_CARLA_PORT_AUDIO_IN1 = 1, - RACK_PATCHBAY_CARLA_PORT_AUDIO_IN2 = 2, - RACK_PATCHBAY_CARLA_PORT_AUDIO_OUT1 = 3, - RACK_PATCHBAY_CARLA_PORT_AUDIO_OUT2 = 4, - RACK_PATCHBAY_CARLA_PORT_MIDI_IN = 5, - RACK_PATCHBAY_CARLA_PORT_MIDI_OUT = 6, - RACK_PATCHBAY_CARLA_PORT_MAX = 7 -}; - -struct PortNameToId { - int portId; - char name[STR_MAX+1]; -}; - -struct ConnectionToId { - uint id; - int groupA; - int portA; - int groupB; - int portB; -}; - -static inline -int getCarlaRackPortIdFromName(const char* const shortname) noexcept -{ - if (std::strcmp(shortname, "AudioIn1") == 0) - return RACK_PATCHBAY_CARLA_PORT_AUDIO_IN1; - if (std::strcmp(shortname, "AudioIn2") == 0) - return RACK_PATCHBAY_CARLA_PORT_AUDIO_IN2; - if (std::strcmp(shortname, "AudioOut1") == 0) - return RACK_PATCHBAY_CARLA_PORT_AUDIO_OUT1; - if (std::strcmp(shortname, "AudioOut2") == 0) - return RACK_PATCHBAY_CARLA_PORT_AUDIO_OUT2; - if (std::strcmp(shortname, "MidiIn") == 0) - return RACK_PATCHBAY_CARLA_PORT_MIDI_IN; - if (std::strcmp(shortname, "MidiOut") == 0) - return RACK_PATCHBAY_CARLA_PORT_MIDI_OUT; - return RACK_PATCHBAY_CARLA_PORT_NULL; -} - -// ----------------------------------------------------------------------- -// EngineRackBuffers - -struct EngineRackBuffers { - float* in[2]; - float* out[2]; - - // connections stuff - LinkedList connectedIn1; - LinkedList connectedIn2; - LinkedList connectedOut1; - LinkedList connectedOut2; - CarlaCriticalSection connectLock; - - uint lastConnectionId; - LinkedList usedConnections; - - EngineRackBuffers(const uint32_t bufferSize); - ~EngineRackBuffers() noexcept; - void clear() noexcept; - void resize(const uint32_t bufferSize); - - bool connect(CarlaEngine* const engine, const int groupA, const int portA, const int groupB, const int port) noexcept; - const char* const* getConnections() const; - - CARLA_DECLARE_NON_COPY_STRUCT(EngineRackBuffers) -}; - -// ----------------------------------------------------------------------- -// EnginePatchbayBuffers +struct AbstractEngineBuffer { + AbstractEngineBuffer(const uint32_t /*bufferSize*/) {} + virtual ~AbstractEngineBuffer() noexcept {} -struct EnginePatchbayBuffers { - // TODO - EnginePatchbayBuffers(const uint32_t bufferSize); - ~EnginePatchbayBuffers() noexcept; - void clear() noexcept; - void resize(const uint32_t bufferSize); + virtual void clear() noexcept = 0; + virtual void resize(const uint32_t bufferSize) = 0; - bool connect(CarlaEngine* const engine, const int groupA, const int portA, const int groupB, const int port) noexcept; - const char* const* getConnections() const; + virtual bool connect(CarlaEngine* const engine, const int groupA, const int portA, const int groupB, const int port) noexcept = 0; + virtual bool disconnect(const uint connectionId) noexcept = 0; - CARLA_DECLARE_NON_COPY_STRUCT(EnginePatchbayBuffers) + virtual const char* const* getConnections() const = 0; }; // ----------------------------------------------------------------------- // InternalAudio struct EngineInternalAudio { + bool isPatchbay; // can only be patchbay or rack mode bool isReady; - bool usePatchbay; uint inCount; uint outCount; - union { - EngineRackBuffers* rack; - EnginePatchbayBuffers* patchbay; - }; + AbstractEngineBuffer* buffer; - EngineInternalAudio() noexcept; - ~EngineInternalAudio() noexcept; - void initPatchbay() noexcept; - void clear() noexcept; - void create(const uint32_t bufferSize); - void resize(const uint32_t bufferSize); + EngineInternalAudio() noexcept + : isPatchbay(false), + isReady(false), + inCount(0), + outCount(0), + buffer(nullptr) {} + + ~EngineInternalAudio() noexcept + { + CARLA_SAFE_ASSERT(! isReady); + CARLA_SAFE_ASSERT(buffer == nullptr); + } + + void clear() noexcept + { + isReady = false; + inCount = 0; + outCount = 0; + + CARLA_SAFE_ASSERT_RETURN(buffer != nullptr,); + delete buffer; + buffer = nullptr; + } - bool connect(CarlaEngine* const engine, const int groupA, const int portA, const int groupB, const int port) noexcept; - const char* const* getConnections() const; + void create(const uint32_t bufferSize); + void initPatchbay() noexcept; CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalAudio) }; @@ -175,8 +109,15 @@ struct EngineInternalEvents { EngineEvent* in; EngineEvent* out; - EngineInternalEvents() noexcept; - ~EngineInternalEvents() noexcept; + EngineInternalEvents() noexcept + : in(nullptr), + out(nullptr) {} + + ~EngineInternalEvents() noexcept + { + CARLA_SAFE_ASSERT(in == nullptr); + CARLA_SAFE_ASSERT(out == nullptr); + } CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalEvents) }; @@ -188,7 +129,9 @@ struct EngineInternalTime { bool playing; uint64_t frame; - EngineInternalTime() noexcept; + EngineInternalTime() noexcept + : playing(false), + frame(0) {} CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalTime) }; @@ -209,9 +152,21 @@ struct EngineNextAction { unsigned int value; CarlaMutex mutex; - EngineNextAction() noexcept; - ~EngineNextAction() noexcept; - void ready() noexcept; + EngineNextAction() noexcept + : opcode(kEnginePostActionNull), + pluginId(0), + value(0) {} + + ~EngineNextAction() noexcept + { + CARLA_SAFE_ASSERT(opcode == kEnginePostActionNull); + } + + void ready() const noexcept + { + mutex.lock(); + mutex.unlock(); + } CARLA_DECLARE_NON_COPY_STRUCT(EngineNextAction) }; @@ -224,7 +179,12 @@ struct EnginePluginData { float insPeak[2]; float outsPeak[2]; - void clear() noexcept; + void clear() noexcept + { + plugin = nullptr; + insPeak[0] = insPeak[1] = 0.0f; + outsPeak[0] = outsPeak[1] = 0.0f; + } }; // ----------------------------------------------------------------------- diff --git a/source/backend/engine/CarlaEngineProtectedData.cpp b/source/backend/engine/CarlaEngineProtectedData.cpp new file mode 100644 index 000000000..c48ffa559 --- /dev/null +++ b/source/backend/engine/CarlaEngineProtectedData.cpp @@ -0,0 +1,431 @@ +/* + * Carla Plugin Host + * Copyright (C) 2011-2014 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 doc/GPL.txt file. + */ + +#include "CarlaEngineInternal.hpp" +#include "CarlaPlugin.hpp" +#include "CarlaMIDI.h" + +#include "CarlaMathUtils.hpp" + +// ----------------------------------------------------------------------- + +CARLA_BACKEND_START_NAMESPACE + +#if 0 +} // Fix editor indentation +#endif + +// ----------------------------------------------------------------------- +// CarlaEngineProtectedData + +CarlaEngineProtectedData::CarlaEngineProtectedData(CarlaEngine* const engine) + : osc(engine), + thread(engine), + oscData(nullptr), + callback(nullptr), + callbackPtr(nullptr), + fileCallback(nullptr), + fileCallbackPtr(nullptr), + hints(0x0), + bufferSize(0), + sampleRate(0.0), + aboutToClose(false), + curPluginCount(0), + maxPluginNumber(0), + nextPluginId(0), + plugins(nullptr) {} + +CarlaEngineProtectedData::~CarlaEngineProtectedData() noexcept +{ + CARLA_SAFE_ASSERT(curPluginCount == 0); + CARLA_SAFE_ASSERT(maxPluginNumber == 0); + CARLA_SAFE_ASSERT(nextPluginId == 0); + CARLA_SAFE_ASSERT(plugins == nullptr); +} + +// ----------------------------------------------------------------------- + +void CarlaEngineProtectedData::doPluginRemove() noexcept +{ + CARLA_SAFE_ASSERT_RETURN(curPluginCount > 0,); + CARLA_SAFE_ASSERT_RETURN(nextAction.pluginId < curPluginCount,); + --curPluginCount; + + // move all plugins 1 spot backwards + for (unsigned int i=nextAction.pluginId; i < curPluginCount; ++i) + { + CarlaPlugin* const plugin(plugins[i+1].plugin); + + CARLA_SAFE_ASSERT_BREAK(plugin != nullptr); + + plugin->setId(i); + + plugins[i].plugin = plugin; + plugins[i].insPeak[0] = 0.0f; + plugins[i].insPeak[1] = 0.0f; + plugins[i].outsPeak[0] = 0.0f; + plugins[i].outsPeak[1] = 0.0f; + } + + const unsigned int id(curPluginCount); + + // reset last plugin (now removed) + plugins[id].plugin = nullptr; + plugins[id].insPeak[0] = 0.0f; + plugins[id].insPeak[1] = 0.0f; + plugins[id].outsPeak[0] = 0.0f; + plugins[id].outsPeak[1] = 0.0f; +} + +void CarlaEngineProtectedData::doPluginsSwitch() noexcept +{ + CARLA_SAFE_ASSERT_RETURN(curPluginCount >= 2,); + + const unsigned int idA(nextAction.pluginId); + const unsigned int idB(nextAction.value); + + CARLA_SAFE_ASSERT_RETURN(idA < curPluginCount,); + CARLA_SAFE_ASSERT_RETURN(idB < curPluginCount,); + CARLA_SAFE_ASSERT_RETURN(plugins[idA].plugin != nullptr,); + CARLA_SAFE_ASSERT_RETURN(plugins[idB].plugin != nullptr,); + +#if 0 + std::swap(plugins[idA].plugin, plugins[idB].plugin); +#else + CarlaPlugin* const tmp(plugins[idA].plugin); + + plugins[idA].plugin = plugins[idB].plugin; + plugins[idB].plugin = tmp; +#endif +} + +void CarlaEngineProtectedData::doNextPluginAction(const bool unlock) noexcept +{ + switch (nextAction.opcode) + { + case kEnginePostActionNull: + break; + case kEnginePostActionZeroCount: + curPluginCount = 0; + break; + case kEnginePostActionRemovePlugin: + doPluginRemove(); + break; + case kEnginePostActionSwitchPlugins: + doPluginsSwitch(); + break; + } + + nextAction.opcode = kEnginePostActionNull; + nextAction.pluginId = 0; + nextAction.value = 0; + + if (unlock) + nextAction.mutex.unlock(); +} + +// ----------------------------------------------------------------------- + +#ifndef BUILD_BRIDGE +void CarlaEngineProtectedData::processRack(float* inBufReal[2], float* outBuf[2], const uint32_t frames, const bool isOffline) +{ + CARLA_SAFE_ASSERT_RETURN(bufEvents.in != nullptr,); + CARLA_SAFE_ASSERT_RETURN(bufEvents.out != nullptr,); + + // safe copy + float inBuf0[frames]; + float inBuf1[frames]; + float* inBuf[2] = { inBuf0, inBuf1 }; + + // initialize audio inputs + FLOAT_COPY(inBuf0, inBufReal[0], frames); + FLOAT_COPY(inBuf1, inBufReal[1], frames); + + // initialize audio outputs (zero) + FLOAT_CLEAR(outBuf[0], frames); + FLOAT_CLEAR(outBuf[1], frames); + + // initialize event outputs (zero) + carla_zeroStruct(bufEvents.out, kMaxEngineEventInternalCount); + + bool processed = false; + + uint32_t oldAudioInCount = 0; + uint32_t oldMidiOutCount = 0; + + // process plugins + for (unsigned int i=0; i < curPluginCount; ++i) + { + CarlaPlugin* const plugin = plugins[i].plugin; + + if (plugin == nullptr || ! plugin->isEnabled() || ! plugin->tryLock(isOffline)) + continue; + + if (processed) + { + // initialize audio inputs (from previous outputs) + FLOAT_COPY(inBuf0, outBuf[0], frames); + FLOAT_COPY(inBuf1, outBuf[1], frames); + + // initialize audio outputs (zero) + FLOAT_CLEAR(outBuf[0], frames); + FLOAT_CLEAR(outBuf[1], frames); + + // if plugin has no midi out, add previous events + if (oldMidiOutCount == 0 && bufEvents.in[0].type != kEngineEventTypeNull) + { + if (bufEvents.out[0].type != kEngineEventTypeNull) + { + // TODO: carefully add to input, sorted events + } + // else nothing needed + } + else + { + // initialize event inputs from previous outputs + carla_copyStruct(bufEvents.in, bufEvents.out, kMaxEngineEventInternalCount); + + // initialize event outputs (zero) + carla_zeroStruct(bufEvents.out, kMaxEngineEventInternalCount); + } + } + + oldAudioInCount = plugin->getAudioInCount(); + oldMidiOutCount = plugin->getMidiOutCount(); + + // process + plugin->initBuffers(); + plugin->process(inBuf, outBuf, frames); + plugin->unlock(); + + // if plugin has no audio inputs, add input buffer + if (oldAudioInCount == 0) + { + FLOAT_ADD(outBuf[0], inBuf0, frames); + FLOAT_ADD(outBuf[1], inBuf1, frames); + } + + // set peaks + { + EnginePluginData& pluginData(plugins[i]); + +#ifdef HAVE_JUCE + float tmpMin, tmpMax; + + if (oldAudioInCount > 0) + { + FloatVectorOperations::findMinAndMax(inBuf0, static_cast(frames), tmpMin, tmpMax); + pluginData.insPeak[0] = carla_max(std::abs(tmpMin), std::abs(tmpMax), 1.0f); + + FloatVectorOperations::findMinAndMax(inBuf1, static_cast(frames), tmpMin, tmpMax); + pluginData.insPeak[1] = carla_max(std::abs(tmpMin), std::abs(tmpMax), 1.0f); + } + else + { + pluginData.insPeak[0] = 0.0f; + pluginData.insPeak[1] = 0.0f; + } + + if (plugin->getAudioOutCount() > 0) + { + FloatVectorOperations::findMinAndMax(outBuf[0], static_cast(frames), tmpMin, tmpMax); + pluginData.outsPeak[0] = carla_max(std::abs(tmpMin), std::abs(tmpMax), 1.0f); + + FloatVectorOperations::findMinAndMax(outBuf[1], static_cast(frames), tmpMin, tmpMax); + pluginData.outsPeak[1] = carla_max(std::abs(tmpMin), std::abs(tmpMax), 1.0f); + } + else + { + pluginData.outsPeak[0] = 0.0f; + pluginData.outsPeak[1] = 0.0f; + } +#else + float peak1, peak2; + + if (oldAudioInCount > 0) + { + peak1 = peak2 = 0.0f; + + for (uint32_t k=0; k < frames; ++k) + { + peak1 = carla_max(peak1, std::fabs(inBuf0[k]), 1.0f); + peak2 = carla_max(peak2, std::fabs(inBuf1[k]), 1.0f); + } + + pluginData.insPeak[0] = peak1; + pluginData.insPeak[1] = peak2; + } + else + { + pluginData.insPeak[0] = 0.0f; + pluginData.insPeak[1] = 0.0f; + } + + if (plugin->getAudioOutCount() > 0) + { + peak1 = peak2 = 0.0f; + + for (uint32_t k=0; k < frames; ++k) + { + peak1 = carla_max(peak1, std::fabs(outBuf[0][k]), 1.0f); + peak2 = carla_max(peak2, std::fabs(outBuf[1][k]), 1.0f); + } + + pluginData.outsPeak[0] = peak1; + pluginData.outsPeak[1] = peak2; + } + else + { + pluginData.outsPeak[0] = 0.0f; + pluginData.outsPeak[1] = 0.0f; + } +#endif + } + + processed = true; + } +} + +void CarlaEngineProtectedData::processRackFull(float** const inBuf, const uint32_t inCount, float** const outBuf, const uint32_t outCount, const uint32_t nframes, const bool isOffline) +{ + EngineRackBuffers* const rack(bufAudio.rack); + + const CarlaCriticalSectionScope _cs2(rack->connectLock); + + // connect input buffers + if (rack->connectedIn1.count() == 0) + { + FLOAT_CLEAR(rack->in[0], nframes); + } + else + { + bool first = true; + + for (LinkedList::Itenerator it = rack->connectedIn1.begin(); it.valid(); it.next()) + { + const int& port(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast(inCount)); + + if (first) + { + FLOAT_COPY(rack->in[0], inBuf[port], nframes); + first = false; + } + else + { + FLOAT_ADD(rack->in[0], inBuf[port], nframes); + } + } + + if (first) + FLOAT_CLEAR(rack->in[0], nframes); + } + + if (rack->connectedIn2.count() == 0) + { + FLOAT_CLEAR(rack->in[1], nframes); + } + else + { + bool first = true; + + for (LinkedList::Itenerator it = rack->connectedIn2.begin(); it.valid(); it.next()) + { + const int& port(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast(inCount)); + + if (first) + { + FLOAT_COPY(rack->in[1], inBuf[port], nframes); + first = false; + } + else + { + FLOAT_ADD(rack->in[1], inBuf[port], nframes); + } + } + + if (first) + FLOAT_CLEAR(rack->in[1], nframes); + } + + FLOAT_CLEAR(rack->out[0], nframes); + FLOAT_CLEAR(rack->out[1], nframes); + + // process + processRack(rack->in, rack->out, nframes, isOffline); + + // connect output buffers + if (rack->connectedOut1.count() != 0) + { + for (LinkedList::Itenerator it = rack->connectedOut1.begin(); it.valid(); it.next()) + { + const int& port(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast(outCount)); + + FLOAT_ADD(outBuf[port], rack->out[0], nframes); + } + } + + if (rack->connectedOut2.count() != 0) + { + for (LinkedList::Itenerator it = rack->connectedOut2.begin(); it.valid(); it.next()) + { + const int& port(it.getValue()); + CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast(outCount)); + + FLOAT_ADD(outBuf[port], rack->out[1], nframes); + } + } +} +#endif + +// ----------------------------------------------------------------------- +// ScopedActionLock + +CarlaEngineProtectedData::ScopedActionLock::ScopedActionLock(CarlaEngineProtectedData* const data, const EnginePostAction action, const unsigned int pluginId, const unsigned int value, const bool lockWait) noexcept + : fData(data) +{ + fData->nextAction.mutex.lock(); + + CARLA_SAFE_ASSERT_RETURN(fData->nextAction.opcode == kEnginePostActionNull,); + + fData->nextAction.opcode = action; + fData->nextAction.pluginId = pluginId; + fData->nextAction.value = value; + + if (lockWait) + { + // block wait for unlock on processing side + carla_stdout("ScopedPluginAction(%i) - blocking START", pluginId); + fData->nextAction.mutex.lock(); + carla_stdout("ScopedPluginAction(%i) - blocking DONE", pluginId); + } + else + { + fData->doNextPluginAction(false); + } +} + +CarlaEngineProtectedData::ScopedActionLock::~ScopedActionLock() noexcept +{ + fData->nextAction.mutex.unlock(); +} + +// ----------------------------------------------------------------------- + +CARLA_BACKEND_END_NAMESPACE