@@ -46,34 +46,34 @@ CARLA_BACKEND_START_NAMESPACE | |||||
*/ | */ | ||||
enum EngineType { | enum EngineType { | ||||
/*! | /*! | ||||
* Null engine type. | |||||
*/ | |||||
* Null engine type. | |||||
*/ | |||||
kEngineTypeNull = 0, | kEngineTypeNull = 0, | ||||
/*! | /*! | ||||
* JACK engine type.\n | |||||
* Provides all processing modes. | |||||
*/ | |||||
* JACK engine type.\n | |||||
* Provides all processing modes. | |||||
*/ | |||||
kEngineTypeJack = 1, | 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, | 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, | 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, | kEngineTypePlugin = 4, | ||||
/*! | /*! | ||||
* Bridge engine type, used in BridgePlugin class. | |||||
*/ | |||||
* Bridge engine type, used in BridgePlugin class. | |||||
*/ | |||||
kEngineTypeBridge = 5 | kEngineTypeBridge = 5 | ||||
}; | }; | ||||
@@ -82,26 +82,26 @@ enum EngineType { | |||||
*/ | */ | ||||
enum EnginePortType { | enum EnginePortType { | ||||
/*! | /*! | ||||
* Null port type. | |||||
*/ | |||||
* Null port type. | |||||
*/ | |||||
kEnginePortTypeNull = 0, | kEnginePortTypeNull = 0, | ||||
/*! | /*! | ||||
* Audio port type. | |||||
* \see CarlaEngineAudioPort | |||||
*/ | |||||
* Audio port type. | |||||
* \see CarlaEngineAudioPort | |||||
*/ | |||||
kEnginePortTypeAudio = 1, | kEnginePortTypeAudio = 1, | ||||
/*! | /*! | ||||
* CV port type. | |||||
* \see CarlaEngineCVPort | |||||
*/ | |||||
* CV port type. | |||||
* \see CarlaEngineCVPort | |||||
*/ | |||||
kEnginePortTypeCV = 2, | kEnginePortTypeCV = 2, | ||||
/*! | /*! | ||||
* Event port type (Control or MIDI). | |||||
** \see CarlaEngineEventPort | |||||
*/ | |||||
* Event port type (Control or MIDI). | |||||
* \see CarlaEngineEventPort | |||||
*/ | |||||
kEnginePortTypeEvent = 3 | kEnginePortTypeEvent = 3 | ||||
}; | }; | ||||
@@ -110,20 +110,20 @@ enum EnginePortType { | |||||
*/ | */ | ||||
enum EngineEventType { | enum EngineEventType { | ||||
/*! | /*! | ||||
* Null port type. | |||||
*/ | |||||
* Null port type. | |||||
*/ | |||||
kEngineEventTypeNull = 0, | kEngineEventTypeNull = 0, | ||||
/*! | /*! | ||||
* Control event type. | |||||
* \see EngineControlEvent | |||||
*/ | |||||
* Control event type. | |||||
* \see EngineControlEvent | |||||
*/ | |||||
kEngineEventTypeControl = 1, | kEngineEventTypeControl = 1, | ||||
/*! | /*! | ||||
* MIDI event type. | |||||
* \see EngineMidiEvent | |||||
*/ | |||||
* MIDI event type. | |||||
* \see EngineMidiEvent | |||||
*/ | |||||
kEngineEventTypeMidi = 2 | kEngineEventTypeMidi = 2 | ||||
}; | }; | ||||
@@ -132,34 +132,34 @@ enum EngineEventType { | |||||
*/ | */ | ||||
enum EngineControlEventType { | enum EngineControlEventType { | ||||
/*! | /*! | ||||
* Null event type. | |||||
*/ | |||||
* Null event type. | |||||
*/ | |||||
kEngineControlEventTypeNull = 0, | 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, | kEngineControlEventTypeParameter = 1, | ||||
/*! | /*! | ||||
* MIDI Bank event type. | |||||
*/ | |||||
* MIDI Bank event type. | |||||
*/ | |||||
kEngineControlEventTypeMidiBank = 2, | kEngineControlEventTypeMidiBank = 2, | ||||
/*! | /*! | ||||
* MIDI Program change event type. | |||||
*/ | |||||
* MIDI Program change event type. | |||||
*/ | |||||
kEngineControlEventTypeMidiProgram = 3, | kEngineControlEventTypeMidiProgram = 3, | ||||
/*! | /*! | ||||
* All sound off event type. | |||||
*/ | |||||
* All sound off event type. | |||||
*/ | |||||
kEngineControlEventTypeAllSoundOff = 4, | kEngineControlEventTypeAllSoundOff = 4, | ||||
/*! | /*! | ||||
* All notes off event type. | |||||
*/ | |||||
* All notes off event type. | |||||
*/ | |||||
kEngineControlEventTypeAllNotesOff = 5 | kEngineControlEventTypeAllNotesOff = 5 | ||||
}; | }; | ||||
@@ -174,9 +174,9 @@ struct EngineControlEvent { | |||||
float value; //!< Parameter value, normalized to 0.0f<->1.0f. | 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. | * MIDI data, without channel bit. | ||||
* If size > kDataSize, dataExt is used. | |||||
* If size > kDataSize, dataExt is used (otherwise NULL). | |||||
*/ | */ | ||||
uint8_t data[kDataSize]; | uint8_t data[kDataSize]; | ||||
const uint8_t* dataExt; | 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; | 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. | * 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). | * \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 | #ifndef DOXYGEN | ||||
protected: | protected: | ||||
@@ -48,6 +48,340 @@ CARLA_BACKEND_START_NAMESPACE | |||||
} // Fix editor indentation | } // Fix editor indentation | ||||
#endif | #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 | // 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 | } // Fix editor indentation | ||||
#endif | #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 | // 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) | if (usePatchbay) | ||||
{ | |||||
CARLA_SAFE_ASSERT_RETURN(patchbay != nullptr,); | |||||
delete patchbay; | |||||
patchbay = nullptr; | |||||
} | |||||
buffer = new EnginePatchbayBuffers(bufferSize); | |||||
else | 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; | isReady = true; | ||||
} | } | ||||
void EngineInternalAudio::resize(const uint32_t bufferSize) | |||||
EngineInternalAudio::initPatchbay() noexcept | |||||
{ | { | ||||
if (usePatchbay) | if (usePatchbay) | ||||
{ | { | ||||
CARLA_SAFE_ASSERT_RETURN(patchbay != nullptr,); | 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 | 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 | CARLA_BACKEND_END_NAMESPACE |
@@ -45,125 +45,59 @@ CARLA_BACKEND_START_NAMESPACE | |||||
const unsigned short kMaxEngineEventInternalCount = 512; | 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 | // InternalAudio | ||||
struct EngineInternalAudio { | struct EngineInternalAudio { | ||||
bool isPatchbay; // can only be patchbay or rack mode | |||||
bool isReady; | bool isReady; | ||||
bool usePatchbay; | |||||
uint inCount; | uint inCount; | ||||
uint outCount; | 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) | CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalAudio) | ||||
}; | }; | ||||
@@ -175,8 +109,15 @@ struct EngineInternalEvents { | |||||
EngineEvent* in; | EngineEvent* in; | ||||
EngineEvent* out; | 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) | CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalEvents) | ||||
}; | }; | ||||
@@ -188,7 +129,9 @@ struct EngineInternalTime { | |||||
bool playing; | bool playing; | ||||
uint64_t frame; | uint64_t frame; | ||||
EngineInternalTime() noexcept; | |||||
EngineInternalTime() noexcept | |||||
: playing(false), | |||||
frame(0) {} | |||||
CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalTime) | CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalTime) | ||||
}; | }; | ||||
@@ -209,9 +152,21 @@ struct EngineNextAction { | |||||
unsigned int value; | unsigned int value; | ||||
CarlaMutex mutex; | 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) | CARLA_DECLARE_NON_COPY_STRUCT(EngineNextAction) | ||||
}; | }; | ||||
@@ -224,7 +179,12 @@ struct EnginePluginData { | |||||
float insPeak[2]; | float insPeak[2]; | ||||
float outsPeak[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 |