Signed-off-by: falkTX <falktx@falktx.com>tags/v2.1-rc1
@@ -385,6 +385,14 @@ public: | |||
return kIsInput; | |||
} | |||
/*! | |||
* Get the index offset as passed in the constructor. | |||
*/ | |||
uint32_t getIndexOffset() const noexcept | |||
{ | |||
return kIndexOffset; | |||
} | |||
/*! | |||
* Get this ports' engine client. | |||
*/ | |||
@@ -494,9 +502,24 @@ public: | |||
return fBuffer; | |||
} | |||
/*! | |||
* Get min/max range for this CV port. | |||
*/ | |||
void getRange(float& min, float& max) const noexcept | |||
{ | |||
min = fMinimum; | |||
max = fMaximum; | |||
} | |||
/*! | |||
* Set min/max range for this CV port. | |||
*/ | |||
void setRange(float min, float max) noexcept; | |||
#ifndef DOXYGEN | |||
protected: | |||
float* fBuffer; | |||
float fMinimum, fMaximum; | |||
CARLA_DECLARE_NON_COPY_CLASS(CarlaEngineCVPort) | |||
#endif | |||
@@ -537,6 +560,11 @@ public: | |||
*/ | |||
void removeCVSource(CarlaEngineCVPort* port) noexcept; | |||
/*! | |||
* Remove a CV port as a source of events. | |||
*/ | |||
void mixWithCvBuffer(const float* buffer, uint32_t frames, uint32_t indexOffset) noexcept; | |||
/*! | |||
* Initialize the port's internal buffer for @a engine. | |||
*/ | |||
@@ -215,6 +215,7 @@ struct EnginePluginData { | |||
struct CarlaEngineEventCV { | |||
CarlaEngineCVPort* cvPort; | |||
float previousValue; | |||
uint32_t indexOffset; | |||
}; | |||
struct CarlaEngineEventPort::ProtectedData { | |||
@@ -20,6 +20,8 @@ | |||
#include "CarlaMathUtils.hpp" | |||
#include "CarlaMIDI.h" | |||
#include "lv2/lv2.h" | |||
CARLA_BACKEND_START_NAMESPACE | |||
// ----------------------------------------------------------------------- | |||
@@ -71,7 +73,9 @@ void CarlaEngineAudioPort::initBuffer() noexcept | |||
CarlaEngineCVPort::CarlaEngineCVPort(const CarlaEngineClient& client, const bool isInputPort, const uint32_t indexOffset) noexcept | |||
: CarlaEnginePort(client, isInputPort, indexOffset), | |||
fBuffer(nullptr) | |||
fBuffer(nullptr), | |||
fMinimum(-1.0f), | |||
fMaximum(1.0f) | |||
{ | |||
carla_debug("CarlaEngineCVPort::CarlaEngineCVPort(%s)", bool2str(isInputPort)); | |||
} | |||
@@ -85,6 +89,26 @@ void CarlaEngineCVPort::initBuffer() noexcept | |||
{ | |||
} | |||
void CarlaEngineCVPort::setRange(const float min, const float max) noexcept | |||
{ | |||
fMinimum = min; | |||
fMaximum = max; | |||
char strBufMin[STR_MAX]; | |||
char strBufMax[STR_MAX]; | |||
carla_zeroChars(strBufMin, STR_MAX); | |||
carla_zeroChars(strBufMax, STR_MAX); | |||
{ | |||
const CarlaScopedLocale csl; | |||
std::snprintf(strBufMin, STR_MAX-1, "%f", static_cast<double>(min)); | |||
std::snprintf(strBufMax, STR_MAX-1, "%f", static_cast<double>(max)); | |||
} | |||
setMetaData(LV2_CORE__minimum, strBufMin, ""); | |||
setMetaData(LV2_CORE__maximum, strBufMax, ""); | |||
} | |||
// ----------------------------------------------------------------------- | |||
// Carla Engine Event port | |||
@@ -107,7 +131,7 @@ void CarlaEngineEventPort::addCVSource(CarlaEngineCVPort* const port) noexcept | |||
CARLA_SAFE_ASSERT_RETURN(port->isInput(),); | |||
carla_debug("CarlaEngineEventPort::addCVSource(%p)", port); | |||
const CarlaEngineEventCV ecv { port, 0.0f }; | |||
const CarlaEngineEventCV ecv { port, 0.0f, port->getIndexOffset() }; | |||
pData->cvs.append(ecv); | |||
} | |||
@@ -121,17 +145,69 @@ void CarlaEngineEventPort::removeCVSource(CarlaEngineCVPort* const port) noexcep | |||
(void)port; | |||
} | |||
static CarlaEngineEventCV kFallbackEngineEventCV = { nullptr, 0.0f, (uint32_t)-1 }; | |||
void CarlaEngineEventPort::mixWithCvBuffer(const float* const buffer, | |||
const uint32_t frames, | |||
const uint32_t indexOffset) noexcept | |||
{ | |||
CARLA_SAFE_ASSERT_RETURN(pData->buffer != nullptr,) | |||
CARLA_SAFE_ASSERT_RETURN(kIsInput,); | |||
uint32_t eventIndex = 0; | |||
float v, min, max; | |||
for (; eventIndex < kMaxEngineEventInternalCount; ++eventIndex) | |||
{ | |||
if (pData->buffer[eventIndex].type == kEngineEventTypeNull) | |||
break; | |||
} | |||
if (eventIndex == kMaxEngineEventInternalCount) | |||
return; | |||
for (LinkedList<CarlaEngineEventCV>::Itenerator it = pData->cvs.begin2(); it.valid(); it.next()) | |||
{ | |||
CarlaEngineEventCV& ecv(it.getValue(kFallbackEngineEventCV)); | |||
if (ecv.indexOffset != indexOffset) | |||
continue; | |||
CARLA_SAFE_ASSERT_RETURN(ecv.cvPort != nullptr,); | |||
float previousValue = ecv.previousValue; | |||
ecv.cvPort->getRange(min, max); | |||
for (uint32_t i=0; i<frames; ++i) | |||
{ | |||
v = buffer[i]; | |||
if (carla_isNotEqual(v, previousValue)) | |||
{ | |||
previousValue = v; | |||
EngineEvent& event(pData->buffer[i++]); | |||
event.type = kEngineEventTypeControl; | |||
event.time = i; | |||
event.channel = 0xFF; | |||
event.ctrl.type = kEngineControlEventTypeParameter; | |||
event.ctrl.param = static_cast<uint16_t>(indexOffset); | |||
event.ctrl.value = carla_fixedValue(0.0f, 1.0f, (v - min) / (max - min)); | |||
} | |||
} | |||
ecv.previousValue = previousValue; | |||
break; | |||
} | |||
} | |||
void CarlaEngineEventPort::initBuffer() noexcept | |||
{ | |||
if (pData->processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK || pData->processMode == ENGINE_PROCESS_MODE_BRIDGE) | |||
pData->buffer = kClient.getEngine().getInternalEventBuffer(kIsInput); | |||
else if (pData->processMode == ENGINE_PROCESS_MODE_PATCHBAY && ! kIsInput) | |||
carla_zeroStructs(pData->buffer, kMaxEngineEventInternalCount); | |||
for (LinkedList<CarlaEngineEventCV>::Itenerator it = pData->cvs.begin2(); it.valid(); it.next()) | |||
{ | |||
// TODO append events to buffer | |||
} | |||
} | |||
uint32_t CarlaEngineEventPort::getEventCount() const noexcept | |||
@@ -1238,6 +1238,7 @@ public: | |||
// Parameter as CV | |||
CarlaEngineCVPort* const cvPort = | |||
(CarlaEngineCVPort*)pData->client->addPort(kEnginePortTypeCV, portName, true, i); | |||
cvPort->setRange(pData->param.ranges[i].min, pData->param.ranges[i].max); | |||
pData->event.portIn->addCVSource(cvPort); | |||
} | |||
} | |||
@@ -1494,7 +1495,9 @@ public: | |||
} | |||
} | |||
void process(const float** const audioIn, float** const audioOut, const float** const, float** const, const uint32_t frames) override | |||
void process(const float** const audioIn, float** const audioOut, | |||
const float** const cvIn, float** const, | |||
const uint32_t frames) override | |||
{ | |||
// -------------------------------------------------------------------------------------------------------- | |||
// Check if active | |||
@@ -1574,6 +1577,19 @@ public: | |||
} // End of MIDI Input (External) | |||
// ---------------------------------------------------------------------------------------------------- | |||
// CV Control Input | |||
for (uint32_t i=0, j=0; i < pData->param.count; ++i) | |||
{ | |||
if (pData->param.data[i].type != PARAMETER_INPUT) | |||
continue; | |||
const uint32_t cvIndex = j++; | |||
pData->event.portIn->mixWithCvBuffer(cvIn[cvIndex], frames, i); | |||
} | |||
// ---------------------------------------------------------------------------------------------------- | |||
// Event Input (System) | |||
@@ -1636,12 +1652,37 @@ public: | |||
break; | |||
case kEngineControlEventTypeParameter: { | |||
float value; | |||
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | |||
// via CV | |||
if (event.channel == 0xFF) | |||
{ | |||
const uint32_t k = ctrlEvent.param; | |||
CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | |||
if (pData->param.data[k].hints & PARAMETER_IS_BOOLEAN) | |||
{ | |||
value = (ctrlEvent.value < 0.5f) ? pData->param.ranges[k].min : pData->param.ranges[k].max; | |||
} | |||
else | |||
{ | |||
if (pData->param.data[k].hints & PARAMETER_IS_LOGARITHMIC) | |||
value = pData->param.ranges[k].getUnnormalizedLogValue(ctrlEvent.value); | |||
else | |||
value = pData->param.ranges[k].getUnnormalizedValue(ctrlEvent.value); | |||
if (pData->param.data[k].hints & PARAMETER_IS_INTEGER) | |||
value = std::rint(value); | |||
} | |||
setParameterValueRT(k, value, true); | |||
continue; | |||
} | |||
// Control backend stuff | |||
if (event.channel == pData->ctrlChannel) | |||
{ | |||
float value; | |||
if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0) | |||
{ | |||
value = ctrlEvent.value; | |||
@@ -1692,8 +1733,6 @@ public: | |||
if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) | |||
continue; | |||
float value; | |||
if (pData->param.data[k].hints & PARAMETER_IS_BOOLEAN) | |||
{ | |||
value = (ctrlEvent.value < 0.5f) ? pData->param.ranges[k].min : pData->param.ranges[k].max; | |||