Browse Source

Initial implementation of MIDI learn (per plugin, not global)

Closes #26

Signed-off-by: falkTX <falktx@falktx.com>
tags/v2.2.0-RC1
falkTX 4 years ago
parent
commit
c3f05e4e96
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
18 changed files with 229 additions and 76 deletions
  1. +6
    -1
      source/backend/CarlaBackend.h
  2. +3
    -2
      source/backend/CarlaEngine.hpp
  3. +7
    -0
      source/backend/CarlaPlugin.hpp
  4. +5
    -0
      source/backend/engine/CarlaEngineData.cpp
  5. +6
    -4
      source/backend/engine/CarlaEngineJack.cpp
  6. +5
    -4
      source/backend/engine/CarlaEnginePorts.cpp
  7. +59
    -0
      source/backend/plugin/CarlaPlugin.cpp
  8. +11
    -0
      source/backend/plugin/CarlaPluginBridge.cpp
  9. +1
    -0
      source/backend/plugin/CarlaPluginInternal.cpp
  10. +5
    -3
      source/backend/plugin/CarlaPluginInternal.hpp
  11. +12
    -6
      source/backend/plugin/CarlaPluginJuce.cpp
  12. +12
    -6
      source/backend/plugin/CarlaPluginLADSPADSSI.cpp
  13. +12
    -6
      source/backend/plugin/CarlaPluginLV2.cpp
  14. +16
    -8
      source/backend/plugin/CarlaPluginNative.cpp
  15. +12
    -6
      source/backend/plugin/CarlaPluginVST2.cpp
  16. +10
    -4
      source/frontend/carla_backend.py
  17. +44
    -24
      source/frontend/carla_widgets.py
  18. +3
    -2
      source/utils/CarlaStateUtils.cpp

+ 6
- 1
source/backend/CarlaBackend.h View File

@@ -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;




+ 3
- 2
source/backend/CarlaEngine.hpp View File

@@ -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.


+ 7
- 0
source/backend/CarlaPlugin.hpp View File

@@ -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.


+ 5
- 0
source/backend/engine/CarlaEngineData.cpp View File

@@ -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
{ {


+ 6
- 4
source/backend/engine/CarlaEngineJack.cpp View File

@@ -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)


+ 5
- 4
source/backend/engine/CarlaEnginePorts.cpp View File

@@ -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];
} }


+ 59
- 0
source/backend/plugin/CarlaPlugin.cpp View File

@@ -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;


+ 11
- 0
source/backend/plugin/CarlaPluginBridge.cpp View File

@@ -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)
{ {


+ 1
- 0
source/backend/plugin/CarlaPluginInternal.cpp View File

@@ -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


+ 5
- 3
source/backend/plugin/CarlaPluginInternal.hpp View File

@@ -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


+ 12
- 6
source/backend/plugin/CarlaPluginJuce.cpp View File

@@ -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




+ 12
- 6
source/backend/plugin/CarlaPluginLADSPADSSI.cpp View File

@@ -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




+ 12
- 6
source/backend/plugin/CarlaPluginLV2.cpp View File

@@ -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




+ 16
- 8
source/backend/plugin/CarlaPluginNative.cpp View File

@@ -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




+ 12
- 6
source/backend/plugin/CarlaPluginVST2.cpp View File

@@ -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




+ 10
- 4
source/frontend/carla_backend.py View File

@@ -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,
} }


+ 44
- 24
source/frontend/carla_widgets.py View File

@@ -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




+ 3
- 2
source/utils/CarlaStateUtils.cpp View File

@@ -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")
{ {


Loading…
Cancel
Save