Closes #26 Signed-off-by: falkTX <falktx@falktx.com>tags/v2.2.0-RC1
@@ -779,10 +779,15 @@ typedef enum { | |||||
*/ | */ | ||||
CONTROL_INDEX_MIDI_PITCHBEND = 131, | CONTROL_INDEX_MIDI_PITCHBEND = 131, | ||||
/*! | |||||
* Special value to indicate MIDI learn. | |||||
*/ | |||||
CONTROL_INDEX_MIDI_LEARN = 132, | |||||
/*! | /*! | ||||
* Highest index allowed for mappings. | * Highest index allowed for mappings. | ||||
*/ | */ | ||||
CONTROL_INDEX_MAX_ALLOWED = CONTROL_INDEX_MIDI_PITCHBEND | |||||
CONTROL_INDEX_MAX_ALLOWED = CONTROL_INDEX_MIDI_LEARN | |||||
} SpecialMappedControlIndex; | } SpecialMappedControlIndex; | ||||
@@ -178,6 +178,7 @@ struct CARLA_API EngineControlEvent { | |||||
EngineControlEventType type; //!< Control-Event type. | EngineControlEventType type; //!< Control-Event type. | ||||
uint16_t param; //!< Parameter Id, midi bank or midi program. | uint16_t param; //!< Parameter Id, midi bank or midi program. | ||||
float value; //!< Parameter value, normalized to 0.0f<->1.0f. | float value; //!< Parameter value, normalized to 0.0f<->1.0f. | ||||
bool handled; //!< Indicates that event was handled/received at least once. | |||||
/*! | /*! | ||||
* Convert this control event into MIDI data. | * Convert this control event into MIDI data. | ||||
@@ -575,12 +576,12 @@ public: | |||||
* Get the event at @a index. | * Get the event at @a index. | ||||
* @note You must only call this for input ports. | * @note You must only call this for input ports. | ||||
*/ | */ | ||||
virtual const EngineEvent& getEvent(uint32_t index) const noexcept; | |||||
virtual EngineEvent& getEvent(uint32_t index) const noexcept; | |||||
/*! | /*! | ||||
* Get the event at @a index, faster unchecked version. | * Get the event at @a index, faster unchecked version. | ||||
*/ | */ | ||||
virtual const EngineEvent& getEventUnchecked(uint32_t index) const noexcept; | |||||
virtual EngineEvent& getEventUnchecked(uint32_t index) const noexcept; | |||||
/*! | /*! | ||||
* Write a control event into the buffer. | * Write a control event into the buffer. | ||||
@@ -51,6 +51,7 @@ class CarlaEngineEventPort; | |||||
class CarlaEngineCVSourcePorts; | class CarlaEngineCVSourcePorts; | ||||
class CarlaEngineBridge; | class CarlaEngineBridge; | ||||
struct CarlaStateSave; | struct CarlaStateSave; | ||||
struct EngineEvent; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -891,6 +892,12 @@ public: | |||||
*/ | */ | ||||
CarlaEngineEventPort* getDefaultEventOutPort() const noexcept; | CarlaEngineEventPort* getDefaultEventOutPort() const noexcept; | ||||
/*! | |||||
* Check if the plugin is interested on MIDI learn, and if so, map this event to the parameter that wants it. | |||||
* Event MUST be of control type and not have been handled before. | |||||
*/ | |||||
void checkForMidiLearn(EngineEvent& event) noexcept; | |||||
/*! | /*! | ||||
* Get the plugin's type native handle. | * Get the plugin's type native handle. | ||||
* This will be LADSPA_Handle, LV2_Handle, etc. | * This will be LADSPA_Handle, LV2_Handle, etc. | ||||
@@ -108,18 +108,21 @@ void EngineEvent::fillFromMidiData(const uint8_t size, const uint8_t* const data | |||||
ctrl.type = kEngineControlEventTypeMidiBank; | ctrl.type = kEngineControlEventTypeMidiBank; | ||||
ctrl.param = midiBank; | ctrl.param = midiBank; | ||||
ctrl.value = 0.0f; | ctrl.value = 0.0f; | ||||
ctrl.handled = true; | |||||
} | } | ||||
else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF) | else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF) | ||||
{ | { | ||||
ctrl.type = kEngineControlEventTypeAllSoundOff; | ctrl.type = kEngineControlEventTypeAllSoundOff; | ||||
ctrl.param = 0; | ctrl.param = 0; | ||||
ctrl.value = 0.0f; | ctrl.value = 0.0f; | ||||
ctrl.handled = true; | |||||
} | } | ||||
else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF) | else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF) | ||||
{ | { | ||||
ctrl.type = kEngineControlEventTypeAllNotesOff; | ctrl.type = kEngineControlEventTypeAllNotesOff; | ||||
ctrl.param = 0; | ctrl.param = 0; | ||||
ctrl.value = 0.0f; | ctrl.value = 0.0f; | ||||
ctrl.handled = true; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -130,6 +133,7 @@ void EngineEvent::fillFromMidiData(const uint8_t size, const uint8_t* const data | |||||
ctrl.type = kEngineControlEventTypeParameter; | ctrl.type = kEngineControlEventTypeParameter; | ||||
ctrl.param = midiControl; | ctrl.param = midiControl; | ||||
ctrl.value = float(midiValue)/127.0f; | ctrl.value = float(midiValue)/127.0f; | ||||
ctrl.handled = false; | |||||
} | } | ||||
} | } | ||||
else if (midiStatus == MIDI_STATUS_PROGRAM_CHANGE) | else if (midiStatus == MIDI_STATUS_PROGRAM_CHANGE) | ||||
@@ -143,6 +147,7 @@ void EngineEvent::fillFromMidiData(const uint8_t size, const uint8_t* const data | |||||
ctrl.type = kEngineControlEventTypeMidiProgram; | ctrl.type = kEngineControlEventTypeMidiProgram; | ||||
ctrl.param = midiProgram; | ctrl.param = midiProgram; | ||||
ctrl.value = 0.0f; | ctrl.value = 0.0f; | ||||
ctrl.handled = true; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -124,7 +124,9 @@ static /* */ PortNameToId kPortNameToIdFallbackNC = { 0, 0, { '\0' }, { '\0' | |||||
static const PortNameToId kPortNameToIdFallback = { 0, 0, { '\0' }, { '\0' } }; | static const PortNameToId kPortNameToIdFallback = { 0, 0, { '\0' }, { '\0' } }; | ||||
static const ConnectionToId kConnectionToIdFallback = { 0, 0, 0, 0, 0 }; | static const ConnectionToId kConnectionToIdFallback = { 0, 0, 0, 0, 0 }; | ||||
#endif | #endif | ||||
static const EngineEvent kFallbackJackEngineEvent = { kEngineEventTypeNull, 0, 0, {{ kEngineControlEventTypeNull, 0, 0.0f }} }; | |||||
static EngineEvent kFallbackJackEngineEvent = { | |||||
kEngineEventTypeNull, 0, 0, {{ kEngineControlEventTypeNull, 0, 0.0f, true }} | |||||
}; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Carla Engine Port removal helper | // Carla Engine Port removal helper | ||||
@@ -460,7 +462,7 @@ public: | |||||
} CARLA_SAFE_EXCEPTION_RETURN("jack_midi_get_event_count", 0); | } CARLA_SAFE_EXCEPTION_RETURN("jack_midi_get_event_count", 0); | ||||
} | } | ||||
const EngineEvent& getEvent(const uint32_t index) const noexcept override | |||||
EngineEvent& getEvent(const uint32_t index) const noexcept override | |||||
{ | { | ||||
if (fJackPort == nullptr) | if (fJackPort == nullptr) | ||||
return CarlaEngineEventPort::getEvent(index); | return CarlaEngineEventPort::getEvent(index); | ||||
@@ -471,7 +473,7 @@ public: | |||||
return getEventUnchecked(index); | return getEventUnchecked(index); | ||||
} | } | ||||
const EngineEvent& getEventUnchecked(uint32_t index) const noexcept override | |||||
EngineEvent& getEventUnchecked(uint32_t index) const noexcept override | |||||
{ | { | ||||
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | ||||
if (index < fCvSourceEventCount) | if (index < fCvSourceEventCount) | ||||
@@ -529,7 +531,7 @@ public: | |||||
uint8_t data[3] = { 0, 0, 0 }; | uint8_t data[3] = { 0, 0, 0 }; | ||||
EngineControlEvent ctrlEvent = { type, param, value }; | |||||
EngineControlEvent ctrlEvent = { type, param, value, false }; | |||||
const uint8_t size = ctrlEvent.convertToMidiData(channel, data); | const uint8_t size = ctrlEvent.convertToMidiData(channel, data); | ||||
if (size == 0) | if (size == 0) | ||||
@@ -31,8 +31,9 @@ CARLA_BACKEND_START_NAMESPACE | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Fallback data | // Fallback data | ||||
static const EngineEvent kFallbackEngineEvent = { kEngineEventTypeNull, 0, 0, {{ kEngineControlEventTypeNull, 0, 0.0f }} }; | |||||
//static CarlaEngineEventCV kFallbackEngineEventCV = { nullptr, (uint32_t)-1, 0.0f }; | |||||
static EngineEvent kFallbackEngineEvent = { | |||||
kEngineEventTypeNull, 0, 0, {{ kEngineControlEventTypeNull, 0, 0.0f, true }} | |||||
}; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Carla Engine port (Abstract) | // Carla Engine port (Abstract) | ||||
@@ -169,7 +170,7 @@ uint32_t CarlaEngineEventPort::getEventCount() const noexcept | |||||
return i; | return i; | ||||
} | } | ||||
const EngineEvent& CarlaEngineEventPort::getEvent(const uint32_t index) const noexcept | |||||
EngineEvent& CarlaEngineEventPort::getEvent(const uint32_t index) const noexcept | |||||
{ | { | ||||
CARLA_SAFE_ASSERT_RETURN(kIsInput, kFallbackEngineEvent); | CARLA_SAFE_ASSERT_RETURN(kIsInput, kFallbackEngineEvent); | ||||
CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, kFallbackEngineEvent); | CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, kFallbackEngineEvent); | ||||
@@ -179,7 +180,7 @@ const EngineEvent& CarlaEngineEventPort::getEvent(const uint32_t index) const no | |||||
return fBuffer[index]; | return fBuffer[index]; | ||||
} | } | ||||
const EngineEvent& CarlaEngineEventPort::getEventUnchecked(const uint32_t index) const noexcept | |||||
EngineEvent& CarlaEngineEventPort::getEventUnchecked(const uint32_t index) const noexcept | |||||
{ | { | ||||
return fBuffer[index]; | return fBuffer[index]; | ||||
} | } | ||||
@@ -21,6 +21,7 @@ | |||||
#include "CarlaBackendUtils.hpp" | #include "CarlaBackendUtils.hpp" | ||||
#include "CarlaBase64Utils.hpp" | #include "CarlaBase64Utils.hpp" | ||||
#include "CarlaMathUtils.hpp" | #include "CarlaMathUtils.hpp" | ||||
#include "CarlaMIDI.h" | |||||
#include "CarlaPluginUI.hpp" | #include "CarlaPluginUI.hpp" | ||||
#include "CarlaScopeUtils.hpp" | #include "CarlaScopeUtils.hpp" | ||||
#include "CarlaStringList.hpp" | #include "CarlaStringList.hpp" | ||||
@@ -1789,6 +1790,11 @@ void CarlaPlugin::setParameterMappedControlIndex(const uint32_t parameterId, con | |||||
paramData.mappedControlIndex = index; | paramData.mappedControlIndex = index; | ||||
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | ||||
if (index == CONTROL_INDEX_MIDI_LEARN) | |||||
pData->midiLearnParameterIndex = static_cast<int32_t>(parameterId); | |||||
else | |||||
pData->midiLearnParameterIndex = -1; | |||||
pData->engine->callback(sendCallback, sendOsc, | pData->engine->callback(sendCallback, sendOsc, | ||||
ENGINE_CALLBACK_PARAMETER_MAPPED_CONTROL_INDEX_CHANGED, | ENGINE_CALLBACK_PARAMETER_MAPPED_CONTROL_INDEX_CHANGED, | ||||
pData->id, | pData->id, | ||||
@@ -2258,6 +2264,33 @@ void CarlaPlugin::idle() | |||||
0, 0.0f, nullptr); | 0, 0.0f, nullptr); | ||||
} | } | ||||
} break; | } break; | ||||
case kPluginPostRtEventMidiLearn: { | |||||
CARLA_SAFE_ASSERT_BREAK(event.value1 >= 0 && event.value1 < MAX_MIDI_CHANNELS); | |||||
CARLA_SAFE_ASSERT_BREAK(event.value2 >= 0 && event.value2 < MAX_MIDI_NOTE); | |||||
if (event.sendCallback) | |||||
{ | |||||
const int32_t parameterId = event.value1; | |||||
const int32_t midiCC = event.value2; | |||||
const int32_t midiChannel = event.value2; | |||||
pData->engine->callback(true, true, | |||||
ENGINE_CALLBACK_PARAMETER_MAPPED_CONTROL_INDEX_CHANGED, | |||||
pData->id, | |||||
parameterId, | |||||
midiCC, | |||||
0, 0.0f, nullptr); | |||||
pData->engine->callback(true, true, | |||||
ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED, | |||||
pData->id, | |||||
parameterId, | |||||
midiChannel, | |||||
0, 0.0f, nullptr); | |||||
} | |||||
} break; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -2405,6 +2438,7 @@ void CarlaPlugin::uiIdle() | |||||
{ | { | ||||
case kPluginPostRtEventNull: | case kPluginPostRtEventNull: | ||||
case kPluginPostRtEventDebug: | case kPluginPostRtEventDebug: | ||||
case kPluginPostRtEventMidiLearn: | |||||
break; | break; | ||||
case kPluginPostRtEventParameterChange: | case kPluginPostRtEventParameterChange: | ||||
@@ -2538,6 +2572,31 @@ CarlaEngineEventPort* CarlaPlugin::getDefaultEventOutPort() const noexcept | |||||
return pData->event.portOut; | return pData->event.portOut; | ||||
} | } | ||||
void CarlaPlugin::checkForMidiLearn(EngineEvent& event) noexcept | |||||
{ | |||||
if (pData->midiLearnParameterIndex < 0) | |||||
return; | |||||
if (event.ctrl.param == MIDI_CONTROL_BANK_SELECT || event.ctrl.param == MIDI_CONTROL_BANK_SELECT__LSB) | |||||
return; | |||||
if (event.ctrl.param >= MAX_MIDI_CONTROL) | |||||
return; | |||||
const uint32_t parameterId = static_cast<uint32_t>(pData->midiLearnParameterIndex); | |||||
CARLA_SAFE_ASSERT_UINT2_RETURN(parameterId < pData->param.count, parameterId, pData->param.count,); | |||||
ParameterData& paramData(pData->param.data[parameterId]); | |||||
CARLA_SAFE_ASSERT_INT_RETURN(paramData.mappedControlIndex == CONTROL_INDEX_MIDI_LEARN, | |||||
paramData.mappedControlIndex,); | |||||
event.ctrl.handled = true; | |||||
paramData.mappedControlIndex = static_cast<int16_t>(event.ctrl.param); | |||||
paramData.midiChannel = event.channel; | |||||
pData->postponeRtEvent(kPluginPostRtEventMidiLearn, true, | |||||
pData->midiLearnParameterIndex, event.ctrl.param, event.channel, 0.0f); | |||||
pData->midiLearnParameterIndex = -1; | |||||
} | |||||
void* CarlaPlugin::getNativeHandle() const noexcept | void* CarlaPlugin::getNativeHandle() const noexcept | ||||
{ | { | ||||
return nullptr; | return nullptr; | ||||
@@ -1365,6 +1365,17 @@ public: | |||||
case kEngineControlEventTypeParameter: | case kEngineControlEventTypeParameter: | ||||
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | ||||
// non-midi | |||||
if (event.channel == kEngineEventNonMidiChannel) | |||||
{ | |||||
const uint32_t k = ctrlEvent.param; | |||||
CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | |||||
// value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | |||||
// setParameterValueRT(k, value, true); | |||||
continue; | |||||
} | |||||
// Control backend stuff | // Control backend stuff | ||||
if (event.channel == pData->ctrlChannel) | if (event.channel == pData->ctrlChannel) | ||||
{ | { | ||||
@@ -673,6 +673,7 @@ CarlaPlugin::ProtectedData::ProtectedData(CarlaEngine* const eng, const uint idx | |||||
ctrlChannel(0), | ctrlChannel(0), | ||||
extraHints(0x0), | extraHints(0x0), | ||||
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | ||||
midiLearnParameterIndex(-1), | |||||
transientTryCounter(0), | transientTryCounter(0), | ||||
transientFirstTry(true), | transientFirstTry(true), | ||||
#endif | #endif | ||||
@@ -86,7 +86,8 @@ enum PluginPostRtEventType { | |||||
kPluginPostRtEventProgramChange, // index | kPluginPostRtEventProgramChange, // index | ||||
kPluginPostRtEventMidiProgramChange, // index | kPluginPostRtEventMidiProgramChange, // index | ||||
kPluginPostRtEventNoteOn, // channel, note, velo | kPluginPostRtEventNoteOn, // channel, note, velo | ||||
kPluginPostRtEventNoteOff // channel, note | |||||
kPluginPostRtEventNoteOff, // channel, note | |||||
kPluginPostRtEventMidiLearn // param, cc, channel | |||||
}; | }; | ||||
/*! | /*! | ||||
@@ -244,8 +245,9 @@ struct CarlaPlugin::ProtectedData { | |||||
int8_t ctrlChannel; | int8_t ctrlChannel; | ||||
uint extraHints; | uint extraHints; | ||||
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH | ||||
uint transientTryCounter; | |||||
bool transientFirstTry; | |||||
int32_t midiLearnParameterIndex; | |||||
uint transientTryCounter; | |||||
bool transientFirstTry; | |||||
#endif | #endif | ||||
// data 1 | // data 1 | ||||
@@ -864,7 +864,7 @@ public: | |||||
for (uint32_t i=0, numEvents=pData->event.portIn->getEventCount(); i < numEvents; ++i) | for (uint32_t i=0, numEvents=pData->event.portIn->getEventCount(); i < numEvents; ++i) | ||||
{ | { | ||||
const EngineEvent& event(pData->event.portIn->getEvent(i)); | |||||
EngineEvent& event(pData->event.portIn->getEvent(i)); | |||||
if (event.time >= frames) | if (event.time >= frames) | ||||
continue; | continue; | ||||
@@ -875,7 +875,7 @@ public: | |||||
break; | break; | ||||
case kEngineEventTypeControl: { | case kEngineEventTypeControl: { | ||||
const EngineControlEvent& ctrlEvent(event.ctrl); | |||||
EngineControlEvent& ctrlEvent(event.ctrl); | |||||
switch (ctrlEvent.type) | switch (ctrlEvent.type) | ||||
{ | { | ||||
@@ -892,6 +892,7 @@ public: | |||||
const uint32_t k = ctrlEvent.param; | const uint32_t k = ctrlEvent.param; | ||||
CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | ||||
ctrlEvent.handled = true; | |||||
value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | ||||
setParameterValueRT(k, value, true); | setParameterValueRT(k, value, true); | ||||
continue; | continue; | ||||
@@ -902,17 +903,17 @@ public: | |||||
{ | { | ||||
if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0) | if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0) | ||||
{ | { | ||||
ctrlEvent.handled = true; | |||||
value = ctrlEvent.value; | value = ctrlEvent.value; | ||||
setDryWetRT(value, true); | setDryWetRT(value, true); | ||||
} | } | ||||
if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0) | |||||
else if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0) | |||||
{ | { | ||||
ctrlEvent.handled = true; | |||||
value = ctrlEvent.value*127.0f/100.0f; | value = ctrlEvent.value*127.0f/100.0f; | ||||
setVolumeRT(value, true); | setVolumeRT(value, true); | ||||
} | } | ||||
if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0) | |||||
else if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0) | |||||
{ | { | ||||
float left, right; | float left, right; | ||||
value = ctrlEvent.value/0.5f - 1.0f; | value = ctrlEvent.value/0.5f - 1.0f; | ||||
@@ -933,6 +934,7 @@ public: | |||||
right = 1.0f; | right = 1.0f; | ||||
} | } | ||||
ctrlEvent.handled = true; | |||||
setBalanceLeftRT(left, true); | setBalanceLeftRT(left, true); | ||||
setBalanceRightRT(right, true); | setBalanceRightRT(right, true); | ||||
} | } | ||||
@@ -951,6 +953,7 @@ public: | |||||
if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) | if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) | ||||
continue; | continue; | ||||
ctrlEvent.handled = true; | |||||
value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | ||||
setParameterValueRT(k, value, true); | setParameterValueRT(k, value, true); | ||||
} | } | ||||
@@ -965,6 +968,9 @@ public: | |||||
fMidiBuffer.addEvent(midiData, 3, static_cast<int>(event.time)); | fMidiBuffer.addEvent(midiData, 3, static_cast<int>(event.time)); | ||||
} | } | ||||
if (! ctrlEvent.handled) | |||||
checkForMidiLearn(event); | |||||
break; | break; | ||||
} // case kEngineControlEventTypeParameter | } // case kEngineControlEventTypeParameter | ||||
@@ -1602,7 +1602,7 @@ public: | |||||
for (uint32_t i=0, numEvents=pData->event.portIn->getEventCount(); i < numEvents; ++i) | for (uint32_t i=0, numEvents=pData->event.portIn->getEventCount(); i < numEvents; ++i) | ||||
{ | { | ||||
const EngineEvent& event(pData->event.portIn->getEvent(i)); | |||||
EngineEvent& event(pData->event.portIn->getEvent(i)); | |||||
uint32_t eventTime = event.time; | uint32_t eventTime = event.time; | ||||
CARLA_SAFE_ASSERT_UINT2_CONTINUE(eventTime < frames, eventTime, frames); | CARLA_SAFE_ASSERT_UINT2_CONTINUE(eventTime < frames, eventTime, frames); | ||||
@@ -1637,7 +1637,7 @@ public: | |||||
break; | break; | ||||
case kEngineEventTypeControl: { | case kEngineEventTypeControl: { | ||||
const EngineControlEvent& ctrlEvent(event.ctrl); | |||||
EngineControlEvent& ctrlEvent(event.ctrl); | |||||
switch (ctrlEvent.type) | switch (ctrlEvent.type) | ||||
{ | { | ||||
@@ -1654,6 +1654,7 @@ public: | |||||
const uint32_t k = ctrlEvent.param; | const uint32_t k = ctrlEvent.param; | ||||
CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | ||||
ctrlEvent.handled = true; | |||||
value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | ||||
setParameterValueRT(k, value, true); | setParameterValueRT(k, value, true); | ||||
continue; | continue; | ||||
@@ -1664,17 +1665,17 @@ public: | |||||
{ | { | ||||
if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0) | if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0) | ||||
{ | { | ||||
ctrlEvent.handled = true; | |||||
value = ctrlEvent.value; | value = ctrlEvent.value; | ||||
setDryWetRT(value, true); | setDryWetRT(value, true); | ||||
} | } | ||||
if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0) | |||||
else if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0) | |||||
{ | { | ||||
ctrlEvent.handled = true; | |||||
value = ctrlEvent.value*127.0f/100.0f; | value = ctrlEvent.value*127.0f/100.0f; | ||||
setVolumeRT(value, true); | setVolumeRT(value, true); | ||||
} | } | ||||
if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0) | |||||
else if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0) | |||||
{ | { | ||||
float left, right; | float left, right; | ||||
value = ctrlEvent.value/0.5f - 1.0f; | value = ctrlEvent.value/0.5f - 1.0f; | ||||
@@ -1695,6 +1696,7 @@ public: | |||||
right = 1.0f; | right = 1.0f; | ||||
} | } | ||||
ctrlEvent.handled = true; | |||||
setBalanceLeftRT(left, true); | setBalanceLeftRT(left, true); | ||||
setBalanceRightRT(right, true); | setBalanceRightRT(right, true); | ||||
} | } | ||||
@@ -1712,6 +1714,7 @@ public: | |||||
if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) | if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) | ||||
continue; | continue; | ||||
ctrlEvent.handled = true; | |||||
value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | ||||
setParameterValueRT(k, value, true); | setParameterValueRT(k, value, true); | ||||
} | } | ||||
@@ -1731,6 +1734,9 @@ public: | |||||
seqEvent.data.control.value = int8_t(ctrlEvent.value*127.0f); | seqEvent.data.control.value = int8_t(ctrlEvent.value*127.0f); | ||||
} | } | ||||
if (! ctrlEvent.handled) | |||||
checkForMidiLearn(event); | |||||
break; | break; | ||||
} // case kEngineControlEventTypeParameter | } // case kEngineControlEventTypeParameter | ||||
@@ -3749,7 +3749,7 @@ public: | |||||
for (uint32_t i=0; i < numEvents; ++i) | for (uint32_t i=0; i < numEvents; ++i) | ||||
{ | { | ||||
const EngineEvent& event(fEventsIn.ctrl->port->getEvent(i)); | |||||
EngineEvent& event(fEventsIn.ctrl->port->getEvent(i)); | |||||
uint32_t eventTime = event.time; | uint32_t eventTime = event.time; | ||||
CARLA_SAFE_ASSERT_UINT2_CONTINUE(eventTime < frames, eventTime, frames); | CARLA_SAFE_ASSERT_UINT2_CONTINUE(eventTime < frames, eventTime, frames); | ||||
@@ -3821,7 +3821,7 @@ public: | |||||
break; | break; | ||||
case kEngineEventTypeControl: { | case kEngineEventTypeControl: { | ||||
const EngineControlEvent& ctrlEvent(event.ctrl); | |||||
EngineControlEvent& ctrlEvent(event.ctrl); | |||||
switch (ctrlEvent.type) | switch (ctrlEvent.type) | ||||
{ | { | ||||
@@ -3838,6 +3838,7 @@ public: | |||||
const uint32_t k = ctrlEvent.param; | const uint32_t k = ctrlEvent.param; | ||||
CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | ||||
ctrlEvent.handled = true; | |||||
value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | ||||
setParameterValueRT(k, value, true); | setParameterValueRT(k, value, true); | ||||
continue; | continue; | ||||
@@ -3848,17 +3849,17 @@ public: | |||||
{ | { | ||||
if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0) | if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0) | ||||
{ | { | ||||
ctrlEvent.handled = true; | |||||
value = ctrlEvent.value; | value = ctrlEvent.value; | ||||
setDryWetRT(value, true); | setDryWetRT(value, true); | ||||
} | } | ||||
if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0) | |||||
else if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0) | |||||
{ | { | ||||
ctrlEvent.handled = true; | |||||
value = ctrlEvent.value*127.0f/100.0f; | value = ctrlEvent.value*127.0f/100.0f; | ||||
setVolumeRT(value, true); | setVolumeRT(value, true); | ||||
} | } | ||||
if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0) | |||||
else if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0) | |||||
{ | { | ||||
float left, right; | float left, right; | ||||
value = ctrlEvent.value/0.5f - 1.0f; | value = ctrlEvent.value/0.5f - 1.0f; | ||||
@@ -3879,6 +3880,7 @@ public: | |||||
right = 1.0f; | right = 1.0f; | ||||
} | } | ||||
ctrlEvent.handled = true; | |||||
setBalanceLeftRT(left, true); | setBalanceLeftRT(left, true); | ||||
setBalanceRightRT(right, true); | setBalanceRightRT(right, true); | ||||
} | } | ||||
@@ -3897,6 +3899,7 @@ public: | |||||
if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) | if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) | ||||
continue; | continue; | ||||
ctrlEvent.handled = true; | |||||
value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | ||||
setParameterValueRT(k, value, true); | setParameterValueRT(k, value, true); | ||||
} | } | ||||
@@ -3920,6 +3923,9 @@ public: | |||||
lv2midi_put_event(&evInMidiStates[fEventsIn.ctrlIndex], mtime, 3, midiData); | lv2midi_put_event(&evInMidiStates[fEventsIn.ctrlIndex], mtime, 3, midiData); | ||||
} | } | ||||
if (! ctrlEvent.handled) | |||||
checkForMidiLearn(event); | |||||
break; | break; | ||||
} // case kEngineControlEventTypeParameter | } // case kEngineControlEventTypeParameter | ||||
@@ -97,7 +97,9 @@ CARLA_BACKEND_START_NAMESPACE | |||||
// Fallback data | // Fallback data | ||||
static const CustomData kCustomDataFallback = { nullptr, nullptr, nullptr }; | static const CustomData kCustomDataFallback = { nullptr, nullptr, nullptr }; | ||||
static const EngineEvent kNullEngineEvent = { kEngineEventTypeNull, 0, 0, {} }; | |||||
static EngineEvent kNullEngineEvent = { | |||||
kEngineEventTypeNull, 0, 0, {{ kEngineControlEventTypeNull, 0, 0.0f, true }} | |||||
}; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -1684,7 +1686,7 @@ public: | |||||
} | } | ||||
} | } | ||||
const EngineEvent& findNextEvent() | |||||
EngineEvent& findNextEvent() | |||||
{ | { | ||||
if (fMidiIn.count == 1) | if (fMidiIn.count == 1) | ||||
{ | { | ||||
@@ -1909,7 +1911,7 @@ public: | |||||
for (;;) | for (;;) | ||||
{ | { | ||||
const EngineEvent& event(findNextEvent()); | |||||
EngineEvent& event(findNextEvent()); | |||||
if (event.type == kEngineEventTypeNull) | if (event.type == kEngineEventTypeNull) | ||||
break; | break; | ||||
@@ -1959,7 +1961,7 @@ public: | |||||
break; | break; | ||||
case kEngineEventTypeControl: { | case kEngineEventTypeControl: { | ||||
const EngineControlEvent& ctrlEvent = event.ctrl; | |||||
EngineControlEvent& ctrlEvent(event.ctrl); | |||||
switch (ctrlEvent.type) | switch (ctrlEvent.type) | ||||
{ | { | ||||
@@ -1976,6 +1978,7 @@ public: | |||||
const uint32_t k = ctrlEvent.param; | const uint32_t k = ctrlEvent.param; | ||||
CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | ||||
ctrlEvent.handled = true; | |||||
value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | ||||
setParameterValueRT(k, value, true); | setParameterValueRT(k, value, true); | ||||
continue; | continue; | ||||
@@ -1986,17 +1989,17 @@ public: | |||||
{ | { | ||||
if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) > 0) | if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) > 0) | ||||
{ | { | ||||
ctrlEvent.handled = true; | |||||
value = ctrlEvent.value; | value = ctrlEvent.value; | ||||
setDryWetRT(value, true); | setDryWetRT(value, true); | ||||
} | } | ||||
if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) > 0) | |||||
else if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) > 0) | |||||
{ | { | ||||
ctrlEvent.handled = true; | |||||
value = ctrlEvent.value*127.0f/100.0f; | value = ctrlEvent.value*127.0f/100.0f; | ||||
setVolumeRT(value, true); | setVolumeRT(value, true); | ||||
} | } | ||||
if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) > 0) | |||||
else if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) > 0) | |||||
{ | { | ||||
float left, right; | float left, right; | ||||
value = ctrlEvent.value/0.5f - 1.0f; | value = ctrlEvent.value/0.5f - 1.0f; | ||||
@@ -2017,6 +2020,7 @@ public: | |||||
right = 1.0f; | right = 1.0f; | ||||
} | } | ||||
ctrlEvent.handled = true; | |||||
setBalanceLeftRT(left, true); | setBalanceLeftRT(left, true); | ||||
setBalanceRightRT(right, true); | setBalanceRightRT(right, true); | ||||
} | } | ||||
@@ -2034,6 +2038,7 @@ public: | |||||
if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) | if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) | ||||
continue; | continue; | ||||
ctrlEvent.handled = true; | |||||
value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | ||||
setParameterValueRT(k, value, true); | setParameterValueRT(k, value, true); | ||||
} | } | ||||
@@ -2053,6 +2058,9 @@ public: | |||||
nativeEvent.size = 3; | nativeEvent.size = 3; | ||||
} | } | ||||
if (! ctrlEvent.handled) | |||||
checkForMidiLearn(event); | |||||
break; | break; | ||||
} // case kEngineControlEventTypeParameter | } // case kEngineControlEventTypeParameter | ||||
@@ -1302,7 +1302,7 @@ public: | |||||
for (uint32_t i=0, numEvents = pData->event.portIn->getEventCount(); i < numEvents; ++i) | for (uint32_t i=0, numEvents = pData->event.portIn->getEventCount(); i < numEvents; ++i) | ||||
{ | { | ||||
const EngineEvent& event(pData->event.portIn->getEvent(i)); | |||||
EngineEvent& event(pData->event.portIn->getEvent(i)); | |||||
uint32_t eventTime = event.time; | uint32_t eventTime = event.time; | ||||
CARLA_SAFE_ASSERT_UINT2_CONTINUE(eventTime < frames, eventTime, frames); | CARLA_SAFE_ASSERT_UINT2_CONTINUE(eventTime < frames, eventTime, frames); | ||||
@@ -1337,7 +1337,7 @@ public: | |||||
break; | break; | ||||
case kEngineEventTypeControl: { | case kEngineEventTypeControl: { | ||||
const EngineControlEvent& ctrlEvent(event.ctrl); | |||||
EngineControlEvent& ctrlEvent(event.ctrl); | |||||
switch (ctrlEvent.type) | switch (ctrlEvent.type) | ||||
{ | { | ||||
@@ -1354,6 +1354,7 @@ public: | |||||
const uint32_t k = ctrlEvent.param; | const uint32_t k = ctrlEvent.param; | ||||
CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count); | ||||
ctrlEvent.handled = true; | |||||
value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | ||||
setParameterValueRT(k, value, true); | setParameterValueRT(k, value, true); | ||||
continue; | continue; | ||||
@@ -1364,17 +1365,17 @@ public: | |||||
{ | { | ||||
if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0) | if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0) | ||||
{ | { | ||||
ctrlEvent.handled = true; | |||||
value = ctrlEvent.value; | value = ctrlEvent.value; | ||||
setDryWetRT(value, true); | setDryWetRT(value, true); | ||||
} | } | ||||
if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0) | |||||
else if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0) | |||||
{ | { | ||||
ctrlEvent.handled = true; | |||||
value = ctrlEvent.value*127.0f/100.0f; | value = ctrlEvent.value*127.0f/100.0f; | ||||
setVolumeRT(value, true); | setVolumeRT(value, true); | ||||
} | } | ||||
if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0) | |||||
else if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0) | |||||
{ | { | ||||
float left, right; | float left, right; | ||||
value = ctrlEvent.value/0.5f - 1.0f; | value = ctrlEvent.value/0.5f - 1.0f; | ||||
@@ -1395,6 +1396,7 @@ public: | |||||
right = 1.0f; | right = 1.0f; | ||||
} | } | ||||
ctrlEvent.handled = true; | |||||
setBalanceLeftRT(left, true); | setBalanceLeftRT(left, true); | ||||
setBalanceRightRT(right, true); | setBalanceRightRT(right, true); | ||||
} | } | ||||
@@ -1413,6 +1415,7 @@ public: | |||||
if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) | if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) | ||||
continue; | continue; | ||||
ctrlEvent.handled = true; | |||||
value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.value); | ||||
setParameterValueRT(k, value, true); | setParameterValueRT(k, value, true); | ||||
} | } | ||||
@@ -1433,6 +1436,9 @@ public: | |||||
vstMidiEvent.midiData[2] = char(ctrlEvent.value*127.0f); | vstMidiEvent.midiData[2] = char(ctrlEvent.value*127.0f); | ||||
} | } | ||||
if (! ctrlEvent.handled) | |||||
checkForMidiLearn(event); | |||||
break; | break; | ||||
} // case kEngineControlEventTypeParameter | } // case kEngineControlEventTypeParameter | ||||
@@ -562,13 +562,19 @@ PARAMETER_MAX = -9 | |||||
# @see ParameterData::mappedControlIndex | # @see ParameterData::mappedControlIndex | ||||
# Unused control index, meaning no mapping is enabled. | # Unused control index, meaning no mapping is enabled. | ||||
CONTROL_VALUE_NONE = -1 | |||||
CONTROL_INDEX_NONE = -1 | |||||
# CV control index, meaning the parameter is exposed as CV port. | # CV control index, meaning the parameter is exposed as CV port. | ||||
CONTROL_VALUE_CV = 130 | |||||
CONTROL_INDEX_CV = 130 | |||||
# Special value to indicate MIDI pitchbend. | # Special value to indicate MIDI pitchbend. | ||||
CONTROL_VALUE_MIDI_PITCHBEND = 131 | |||||
CONTROL_INDEX_MIDI_PITCHBEND = 131 | |||||
# Special value to indicate MIDI learn. | |||||
CONTROL_INDEX_MIDI_LEARN = 132 | |||||
# Special value to indicate MIDI pitchbend. | |||||
CONTROL_INDEX_MAX_ALLOWED = CONTROL_INDEX_MIDI_LEARN | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Engine Callback Opcode | # Engine Callback Opcode | ||||
@@ -1227,7 +1233,7 @@ PyParameterData = { | |||||
'index': PARAMETER_NULL, | 'index': PARAMETER_NULL, | ||||
'rindex': -1, | 'rindex': -1, | ||||
'midiChannel': 0, | 'midiChannel': 0, | ||||
'mappedControlIndex': CONTROL_VALUE_NONE, | |||||
'mappedControlIndex': CONTROL_INDEX_NONE, | |||||
'mappedMinimum': 0.0, | 'mappedMinimum': 0.0, | ||||
'mappedMaximum': 0.0, | 'mappedMaximum': 0.0, | ||||
} | } | ||||
@@ -73,9 +73,10 @@ from carla_backend import ( | |||||
PARAMETER_USES_CUSTOM_TEXT, | PARAMETER_USES_CUSTOM_TEXT, | ||||
PARAMETER_CAN_BE_CV_CONTROLLED, | PARAMETER_CAN_BE_CV_CONTROLLED, | ||||
PARAMETER_INPUT, PARAMETER_OUTPUT, | PARAMETER_INPUT, PARAMETER_OUTPUT, | ||||
CONTROL_VALUE_NONE, | |||||
CONTROL_VALUE_MIDI_PITCHBEND, | |||||
CONTROL_VALUE_CV | |||||
CONTROL_INDEX_NONE, | |||||
CONTROL_INDEX_MIDI_PITCHBEND, | |||||
CONTROL_INDEX_MIDI_LEARN, | |||||
CONTROL_INDEX_CV | |||||
) | ) | ||||
from carla_shared import ( | from carla_shared import ( | ||||
@@ -390,10 +391,14 @@ class PluginParameter(QWidget): | |||||
self.ui.l_name.setFixedWidth(width) | self.ui.l_name.setFixedWidth(width) | ||||
def updateStatusLabel(self): | def updateStatusLabel(self): | ||||
if self.fMappedCtrl == CONTROL_VALUE_NONE: | |||||
if self.fMappedCtrl == CONTROL_INDEX_NONE: | |||||
text = self.tr("Unmapped") | text = self.tr("Unmapped") | ||||
elif self.fMappedCtrl == CONTROL_VALUE_CV: | |||||
elif self.fMappedCtrl == CONTROL_INDEX_CV: | |||||
text = self.tr("CV export") | text = self.tr("CV export") | ||||
elif self.fMappedCtrl == CONTROL_INDEX_MIDI_PITCHBEND: | |||||
text = self.tr("PBend Ch%i" % (self.fMidiChannel,)) | |||||
elif self.fMappedCtrl == CONTROL_INDEX_MIDI_LEARN: | |||||
text = self.tr("MIDI Learn") | |||||
else: | else: | ||||
text = self.tr("CC%i Ch%i" % (self.fMappedCtrl, self.fMidiChannel)) | text = self.tr("CC%i Ch%i" % (self.fMappedCtrl, self.fMidiChannel)) | ||||
@@ -403,14 +408,18 @@ class PluginParameter(QWidget): | |||||
def slot_optionsCustomMenu(self): | def slot_optionsCustomMenu(self): | ||||
menu = QMenu(self) | menu = QMenu(self) | ||||
if self.fMappedCtrl == CONTROL_VALUE_NONE: | |||||
if self.fMappedCtrl == CONTROL_INDEX_NONE: | |||||
title = self.tr("Unmapped") | title = self.tr("Unmapped") | ||||
elif self.fMappedCtrl == CONTROL_VALUE_CV: | |||||
elif self.fMappedCtrl == CONTROL_INDEX_CV: | |||||
title = self.tr("Exposed as CV port") | title = self.tr("Exposed as CV port") | ||||
elif self.fMappedCtrl == CONTROL_INDEX_MIDI_PITCHBEND: | |||||
title = self.tr("Mapped to MIDI Pitchbend, channel %i" % (self.fMidiChannel,)) | |||||
elif self.fMappedCtrl == CONTROL_INDEX_MIDI_LEARN: | |||||
title = self.tr("MIDI Learn active") | |||||
else: | else: | ||||
title = self.tr("Mapped to MIDI control %i, channel %i" % (self.fMappedCtrl, self.fMidiChannel)) | title = self.tr("Mapped to MIDI control %i, channel %i" % (self.fMappedCtrl, self.fMidiChannel)) | ||||
if self.fMappedCtrl != CONTROL_VALUE_NONE: | |||||
if self.fMappedCtrl != CONTROL_INDEX_NONE: | |||||
title += " (range: %g-%g)" % (self.fMappedMinimum, self.fMappedMaximum) | title += " (range: %g-%g)" % (self.fMappedMinimum, self.fMappedMaximum) | ||||
actTitle = menu.addAction(title) | actTitle = menu.addAction(title) | ||||
@@ -420,14 +429,14 @@ class PluginParameter(QWidget): | |||||
actUnmap = menu.addAction(self.tr("Unmap")) | actUnmap = menu.addAction(self.tr("Unmap")) | ||||
if self.fMappedCtrl == CONTROL_VALUE_NONE: | |||||
if self.fMappedCtrl == CONTROL_INDEX_NONE: | |||||
actUnmap.setCheckable(True) | actUnmap.setCheckable(True) | ||||
actUnmap.setChecked(True) | actUnmap.setChecked(True) | ||||
if self.fCanBeInCV: | if self.fCanBeInCV: | ||||
menu.addSection("CV") | menu.addSection("CV") | ||||
actCV = menu.addAction(self.tr("Expose as CV port")) | actCV = menu.addAction(self.tr("Expose as CV port")) | ||||
if self.fMappedCtrl == CONTROL_VALUE_CV: | |||||
if self.fMappedCtrl == CONTROL_INDEX_CV: | |||||
actCV.setCheckable(True) | actCV.setCheckable(True) | ||||
actCV.setChecked(True) | actCV.setChecked(True) | ||||
else: | else: | ||||
@@ -435,9 +444,18 @@ class PluginParameter(QWidget): | |||||
menu.addSection("MIDI") | menu.addSection("MIDI") | ||||
actLearn = menu.addAction(self.tr("MIDI Learn")) | |||||
if self.fMappedCtrl == CONTROL_INDEX_MIDI_LEARN: | |||||
actLearn.setCheckable(True) | |||||
actLearn.setChecked(True) | |||||
menuMIDI = menu.addMenu(self.tr("MIDI Control")) | menuMIDI = menu.addMenu(self.tr("MIDI Control")) | ||||
if self.fMappedCtrl not in (CONTROL_VALUE_NONE, CONTROL_VALUE_CV, CONTROL_VALUE_MIDI_PITCHBEND): | |||||
if self.fMappedCtrl not in (CONTROL_INDEX_NONE, | |||||
CONTROL_INDEX_CV, | |||||
CONTROL_INDEX_MIDI_PITCHBEND, | |||||
CONTROL_INDEX_MIDI_LEARN): | |||||
action = menuMIDI.menuAction() | action = menuMIDI.menuAction() | ||||
action.setCheckable(True) | action.setCheckable(True) | ||||
action.setChecked(True) | action.setChecked(True) | ||||
@@ -475,7 +493,7 @@ class PluginParameter(QWidget): | |||||
# TODO | # TODO | ||||
#actPitchbend = menu.addAction(self.tr("MIDI Pitchbend")) | #actPitchbend = menu.addAction(self.tr("MIDI Pitchbend")) | ||||
#if self.fMappedCtrl == CONTROL_VALUE_MIDI_PITCHBEND: | |||||
#if self.fMappedCtrl == CONTROL_INDEX_MIDI_PITCHBEND: | |||||
#actPitchbend.setCheckable(True) | #actPitchbend.setCheckable(True) | ||||
#actPitchbend.setChecked(True) | #actPitchbend.setChecked(True) | ||||
@@ -490,8 +508,8 @@ class PluginParameter(QWidget): | |||||
action.setCheckable(True) | action.setCheckable(True) | ||||
action.setChecked(True) | action.setChecked(True) | ||||
if self.fMappedCtrl != CONTROL_VALUE_NONE: | |||||
if self.fMappedCtrl == CONTROL_VALUE_CV: | |||||
if self.fMappedCtrl != CONTROL_INDEX_NONE: | |||||
if self.fMappedCtrl == CONTROL_INDEX_CV: | |||||
menu.addSection("Range (Scaled CV input)") | menu.addSection("Range (Scaled CV input)") | ||||
else: | else: | ||||
menu.addSection("Range (MIDI bounds)") | menu.addSection("Range (MIDI bounds)") | ||||
@@ -517,8 +535,8 @@ class PluginParameter(QWidget): | |||||
self.tr("Custom Minimum"), | self.tr("Custom Minimum"), | ||||
"Custom minimum value to use:", | "Custom minimum value to use:", | ||||
self.fMappedMinimum, | self.fMappedMinimum, | ||||
self.fMinimum if self.fMappedCtrl != CONTROL_VALUE_CV else -9e6, | |||||
self.fMaximum if self.fMappedCtrl != CONTROL_VALUE_CV else 9e6, | |||||
self.fMinimum if self.fMappedCtrl != CONTROL_INDEX_CV else -9e6, | |||||
self.fMaximum if self.fMappedCtrl != CONTROL_INDEX_CV else 9e6, | |||||
self.fDecimalPoints) | self.fDecimalPoints) | ||||
if not ok: | if not ok: | ||||
return | return | ||||
@@ -532,8 +550,8 @@ class PluginParameter(QWidget): | |||||
self.tr("Custom Maximum"), | self.tr("Custom Maximum"), | ||||
"Custom maximum value to use:", | "Custom maximum value to use:", | ||||
self.fMappedMaximum, | self.fMappedMaximum, | ||||
self.fMinimum if self.fMappedCtrl != CONTROL_VALUE_CV else -9e6, | |||||
self.fMaximum if self.fMappedCtrl != CONTROL_VALUE_CV else 9e6, | |||||
self.fMinimum if self.fMappedCtrl != CONTROL_INDEX_CV else -9e6, | |||||
self.fMaximum if self.fMappedCtrl != CONTROL_INDEX_CV else 9e6, | |||||
self.fDecimalPoints) | self.fDecimalPoints) | ||||
if not ok: | if not ok: | ||||
return | return | ||||
@@ -543,11 +561,9 @@ class PluginParameter(QWidget): | |||||
return | return | ||||
if actSel == actUnmap: | if actSel == actUnmap: | ||||
ctrl = CONTROL_VALUE_NONE | |||||
ctrl = CONTROL_INDEX_NONE | |||||
elif actSel == actCV: | elif actSel == actCV: | ||||
ctrl = CONTROL_VALUE_CV | |||||
elif actSel in actCCs: | |||||
ctrl = int(actSel.text().split(" ", 1)[0].replace("&",""), 10) | |||||
ctrl = CONTROL_INDEX_CV | |||||
elif actSel == actCustomCC: | elif actSel == actCustomCC: | ||||
value = self.fMappedCtrl if self.fMappedCtrl >= 0x01 and self.fMappedCtrl <= 0x77 else 1 | value = self.fMappedCtrl if self.fMappedCtrl >= 0x01 and self.fMappedCtrl <= 0x77 else 1 | ||||
ctrl, ok = QInputDialog.getInt(self, | ctrl, ok = QInputDialog.getInt(self, | ||||
@@ -557,8 +573,12 @@ class PluginParameter(QWidget): | |||||
0x01, 0x77, 1) | 0x01, 0x77, 1) | ||||
if not ok: | if not ok: | ||||
return | return | ||||
#elif actSel in actPitchbend: | |||||
#ctrl = CONTROL_VALUE_MIDI_PITCHBEND | |||||
#elif actSel == actPitchbend: | |||||
#ctrl = CONTROL_INDEX_MIDI_PITCHBEND | |||||
elif actSel == actLearn: | |||||
ctrl = CONTROL_INDEX_MIDI_LEARN | |||||
elif actSel in actCCs: | |||||
ctrl = int(actSel.text().split(" ", 1)[0].replace("&",""), 10) | |||||
else: | else: | ||||
return | return | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* Carla State utils | * Carla State utils | ||||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* This program is free software; you can redistribute it and/or | * This program is free software; you can redistribute it and/or | ||||
* modify it under the terms of the GNU General Public License as | * modify it under the terms of the GNU General Public License as | ||||
@@ -455,7 +455,8 @@ bool CarlaStateSave::fillFromXmlElement(const XmlElement* const xmlElement) | |||||
{ | { | ||||
const int ctrl(pText.getIntValue()); | const int ctrl(pText.getIntValue()); | ||||
if (ctrl > CONTROL_INDEX_NONE && ctrl <= CONTROL_INDEX_MAX_ALLOWED) | if (ctrl > CONTROL_INDEX_NONE && ctrl <= CONTROL_INDEX_MAX_ALLOWED) | ||||
stateParameter->mappedControlIndex = static_cast<int16_t>(ctrl); | |||||
if (ctrl != CONTROL_INDEX_MIDI_LEARN) | |||||
stateParameter->mappedControlIndex = static_cast<int16_t>(ctrl); | |||||
} | } | ||||
else if (pTag == "MappedMinimum") | else if (pTag == "MappedMinimum") | ||||
{ | { | ||||