| @@ -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: | |||
| @@ -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<uint8_t>(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); | |||
| data[1] = MIDI_CONTROL_BANK_SELECT; | |||
| data[2] = uint8_t(carla_fixValue<float>(0.0f, float(MAX_MIDI_VALUE-1), value)); | |||
| } | |||
| else | |||
| { | |||
| size = 3; | |||
| data[0] = static_cast<uint8_t>(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); | |||
| data[1] = static_cast<uint8_t>(param); | |||
| data[2] = uint8_t(carla_fixValue<float>(0.0f, 1.0f, value) * float(MAX_MIDI_VALUE-1)); | |||
| } | |||
| break; | |||
| case kEngineControlEventTypeMidiBank: | |||
| size = 3; | |||
| data[0] = static_cast<uint8_t>(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); | |||
| data[1] = MIDI_CONTROL_BANK_SELECT; | |||
| data[2] = uint8_t(carla_fixValue<uint16_t>(0, MAX_MIDI_VALUE-1, param)); | |||
| break; | |||
| case kEngineControlEventTypeMidiProgram: | |||
| size = 2; | |||
| data[0] = static_cast<uint8_t>(MIDI_STATUS_PROGRAM_CHANGE | (channel & MIDI_CHANNEL_BIT)); | |||
| data[1] = uint8_t(carla_fixValue<uint16_t>(0, MAX_MIDI_VALUE-1, param)); | |||
| break; | |||
| case kEngineControlEventTypeAllSoundOff: | |||
| size = 2; | |||
| data[0] = static_cast<uint8_t>(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); | |||
| data[1] = MIDI_CONTROL_ALL_SOUND_OFF; | |||
| break; | |||
| case kEngineControlEventTypeAllNotesOff: | |||
| size = 2; | |||
| data[0] = static_cast<uint8_t>(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<uint8_t>(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 | |||
| @@ -1,115 +0,0 @@ | |||
| /* | |||
| * Carla Plugin Host | |||
| * Copyright (C) 2011-2014 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU 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 | |||
| @@ -1,285 +0,0 @@ | |||
| /* | |||
| * Carla Plugin Host | |||
| * Copyright (C) 2011-2014 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU 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<uint8_t>(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); | |||
| data[1] = MIDI_CONTROL_BANK_SELECT; | |||
| data[2] = uint8_t(carla_fixValue<float>(0.0f, float(MAX_MIDI_VALUE-1), value)); | |||
| } | |||
| else | |||
| { | |||
| size = 3; | |||
| data[0] = static_cast<uint8_t>(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); | |||
| data[1] = static_cast<uint8_t>(param); | |||
| data[2] = uint8_t(carla_fixValue<float>(0.0f, 1.0f, value) * float(MAX_MIDI_VALUE-1)); | |||
| } | |||
| break; | |||
| case kEngineControlEventTypeMidiBank: | |||
| size = 3; | |||
| data[0] = static_cast<uint8_t>(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); | |||
| data[1] = MIDI_CONTROL_BANK_SELECT; | |||
| data[2] = uint8_t(carla_fixValue<uint16_t>(0, MAX_MIDI_VALUE-1, param)); | |||
| break; | |||
| case kEngineControlEventTypeMidiProgram: | |||
| size = 2; | |||
| data[0] = static_cast<uint8_t>(MIDI_STATUS_PROGRAM_CHANGE | (channel & MIDI_CHANNEL_BIT)); | |||
| data[1] = uint8_t(carla_fixValue<uint16_t>(0, MAX_MIDI_VALUE-1, param)); | |||
| break; | |||
| case kEngineControlEventTypeAllSoundOff: | |||
| size = 2; | |||
| data[0] = static_cast<uint8_t>(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); | |||
| data[1] = MIDI_CONTROL_ALL_SOUND_OFF; | |||
| break; | |||
| case kEngineControlEventTypeAllNotesOff: | |||
| size = 2; | |||
| data[0] = static_cast<uint8_t>(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<uint8_t>(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 | |||
| @@ -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<int> connectedIn1; | |||
| LinkedList<int> connectedIn2; | |||
| LinkedList<int> connectedOut1; | |||
| LinkedList<int> connectedOut2; | |||
| CarlaCriticalSection connectLock; | |||
| uint lastConnectionId; | |||
| LinkedList<ConnectionToId> 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<EngineEvent>(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<EngineEvent>(bufEvents.in, bufEvents.out, kMaxEngineEventInternalCount); | |||
| // initialize event outputs (zero) | |||
| carla_zeroStruct<EngineEvent>(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<int>(frames), tmpMin, tmpMax); | |||
| pluginData.insPeak[0] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| FloatVectorOperations::findMinAndMax(inBuf1, static_cast<int>(frames), tmpMin, tmpMax); | |||
| pluginData.insPeak[1] = carla_max<float>(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<int>(frames), tmpMin, tmpMax); | |||
| pluginData.outsPeak[0] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| FloatVectorOperations::findMinAndMax(outBuf[1], static_cast<int>(frames), tmpMin, tmpMax); | |||
| pluginData.outsPeak[1] = carla_max<float>(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<float>(peak1, std::fabs(inBuf0[k]), 1.0f); | |||
| peak2 = carla_max<float>(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<float>(peak1, std::fabs(outBuf[0][k]), 1.0f); | |||
| peak2 = carla_max<float>(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<int>::Itenerator it = rack->connectedIn1.begin(); it.valid(); it.next()) | |||
| { | |||
| const int& port(it.getValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast<int>(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<int>::Itenerator it = rack->connectedIn2.begin(); it.valid(); it.next()) | |||
| { | |||
| const int& port(it.getValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast<int>(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<int>::Itenerator it = rack->connectedOut1.begin(); it.valid(); it.next()) | |||
| { | |||
| const int& port(it.getValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast<int>(outCount)); | |||
| FLOAT_ADD(outBuf[port], rack->out[0], nframes); | |||
| } | |||
| } | |||
| if (rack->connectedOut2.count() != 0) | |||
| { | |||
| for (LinkedList<int>::Itenerator it = rack->connectedOut2.begin(); it.valid(); it.next()) | |||
| { | |||
| const int& port(it.getValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast<int>(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 | |||
| @@ -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<int> connectedIn1; | |||
| LinkedList<int> connectedIn2; | |||
| LinkedList<int> connectedOut1; | |||
| LinkedList<int> connectedOut2; | |||
| CarlaCriticalSection connectLock; | |||
| uint lastConnectionId; | |||
| LinkedList<ConnectionToId> 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; | |||
| } | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| @@ -0,0 +1,431 @@ | |||
| /* | |||
| * Carla Plugin Host | |||
| * Copyright (C) 2011-2014 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU 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<EngineEvent>(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<EngineEvent>(bufEvents.in, bufEvents.out, kMaxEngineEventInternalCount); | |||
| // initialize event outputs (zero) | |||
| carla_zeroStruct<EngineEvent>(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<int>(frames), tmpMin, tmpMax); | |||
| pluginData.insPeak[0] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| FloatVectorOperations::findMinAndMax(inBuf1, static_cast<int>(frames), tmpMin, tmpMax); | |||
| pluginData.insPeak[1] = carla_max<float>(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<int>(frames), tmpMin, tmpMax); | |||
| pluginData.outsPeak[0] = carla_max<float>(std::abs(tmpMin), std::abs(tmpMax), 1.0f); | |||
| FloatVectorOperations::findMinAndMax(outBuf[1], static_cast<int>(frames), tmpMin, tmpMax); | |||
| pluginData.outsPeak[1] = carla_max<float>(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<float>(peak1, std::fabs(inBuf0[k]), 1.0f); | |||
| peak2 = carla_max<float>(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<float>(peak1, std::fabs(outBuf[0][k]), 1.0f); | |||
| peak2 = carla_max<float>(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<int>::Itenerator it = rack->connectedIn1.begin(); it.valid(); it.next()) | |||
| { | |||
| const int& port(it.getValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast<int>(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<int>::Itenerator it = rack->connectedIn2.begin(); it.valid(); it.next()) | |||
| { | |||
| const int& port(it.getValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast<int>(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<int>::Itenerator it = rack->connectedOut1.begin(); it.valid(); it.next()) | |||
| { | |||
| const int& port(it.getValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast<int>(outCount)); | |||
| FLOAT_ADD(outBuf[port], rack->out[0], nframes); | |||
| } | |||
| } | |||
| if (rack->connectedOut2.count() != 0) | |||
| { | |||
| for (LinkedList<int>::Itenerator it = rack->connectedOut2.begin(); it.valid(); it.next()) | |||
| { | |||
| const int& port(it.getValue()); | |||
| CARLA_SAFE_ASSERT_CONTINUE(port >= 0 && port < static_cast<int>(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 | |||