listen for midi binding control range.

This commit is contained in:
Robin E. R. Davies
2025-06-26 10:24:48 -04:00
parent 2974033128
commit 07af383211
20 changed files with 591 additions and 522 deletions
+20 -12
View File
@@ -294,12 +294,14 @@ class SystemMidiBinding
private:
MidiBinding currentBinding;
bool controlState = false;
uint8_t lastControlValue = 0;
public:
void SetBinding(const MidiBinding &binding)
{
currentBinding = binding;
controlState = false;
lastControlValue = 0;
}
bool IsMatch(const MidiEvent &event);
@@ -335,13 +337,18 @@ bool SystemMidiBinding::IsTriggered(const MidiEvent &event)
return false;
if (event.buffer[1] != currentBinding.control())
return false;
bool state = event.buffer[2] >= 0x64;
if (state != this->controlState)
uint8_t value = event.buffer[2];
bool result = false;
if (currentBinding.switchControlType() == SwitchControlTypeT::TRIGGER_ON_RISING_EDGE)
{
this->controlState = state;
return state;
result = value >= 0x64 && lastControlValue < 0x64;
} else if (currentBinding.switchControlType() == SwitchControlTypeT::TRIGGER_ON_ANY)
{
result = true;
}
return false;
lastControlValue = value;
return result;
}
break;
default:
@@ -918,11 +925,12 @@ private:
if (event.size >= 3)
{
uint8_t cmd = (uint8_t)(event.buffer[0] & 0xF0);
bool isNote = cmd == 0x90;
bool isNote = cmd == 0x90 && event.buffer[2] != 0; // note on with velocity > 0.
bool isControl = cmd == 0xB0;
if (isNote || isControl)
{
realtimeWriter.OnMidiListen(isNote, event.buffer[1]);
MidiNotifyBody notifyBody (event.buffer[0],event.buffer[1], event.buffer[2]);
realtimeWriter.OnMidiListen(notifyBody);
}
}
}
@@ -1333,11 +1341,11 @@ public:
{
if (command == RingBufferCommand::OnMidiListen)
{
uint16_t msg;
hostReader.read(&msg);
MidiNotifyBody body;
hostReader.read(&body);
if (this->pNotifyCallbacks)
{
pNotifyCallbacks->OnNotifyMidiListen((msg & 0xFF00) != 0, (uint8_t)msg);
pNotifyCallbacks->OnNotifyMidiListen(body.cc0_, body.cc1_, body.cc2_);
}
}
else if (command == RingBufferCommand::MidiValueChanged)
@@ -2080,8 +2088,8 @@ public:
return result;
}
volatile bool listenForMidiEvent = false;
volatile bool listenForAtomOutput = false;
std::atomic<bool> listenForMidiEvent = false;
std::atomic<bool> listenForAtomOutput = false;
virtual void SetListenForMidiEvent(bool listen)
{
+1 -1
View File
@@ -158,7 +158,7 @@ namespace pipedal
virtual void OnNotifyVusSubscription(const std::vector<VuUpdate> &updates) = 0;
virtual void OnNotifyMonitorPort(const MonitorPortUpdate &update) = 0;
virtual void OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value) = 0;
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl) = 0;
virtual void OnNotifyMidiListen(uint8_t cc0, uint8_t cc1, uint8_t cc2) = 0;
virtual void OnNotifyPathPatchPropertyReceived(
int64_t instanceId,
+1 -1
View File
@@ -824,7 +824,7 @@ void Lv2Pedalboard::OnMidiMessage(size_t size, uint8_t *message,
case MidiControlType::Dial:
{
IEffect *pEffect = this->realtimeEffects[mapping.effectIndex];
range = mapping.midiBinding.adjustRange(range);
float range = mapping.midiBinding.calculateRange(value);
float currentValue = mapping.pPortInfo->rangeToValue(range);
if (pEffect->GetControlValue(mapping.controlIndex) != currentValue)
{
+2
View File
@@ -58,6 +58,8 @@ JSON_MAP_BEGIN(MidiBinding)
JSON_MAP_REFERENCE(MidiBinding,bindingType)
JSON_MAP_REFERENCE(MidiBinding,note)
JSON_MAP_REFERENCE(MidiBinding,control)
JSON_MAP_REFERENCE(MidiBinding,minControlValue)
JSON_MAP_REFERENCE(MidiBinding,maxControlValue)
JSON_MAP_REFERENCE(MidiBinding,minValue)
JSON_MAP_REFERENCE(MidiBinding,maxValue)
JSON_MAP_REFERENCE(MidiBinding,rotaryScale)
+19 -2
View File
@@ -80,6 +80,8 @@ private:
int bindingType_ = BINDING_TYPE_NONE;
int note_ = 12*4+24;
int control_ = 1;
int minControlValue_ = 0;
int maxControlValue_ = 127;
float minValue_ = 0;
float maxValue_ = 1;
float rotaryScale_ = 1;
@@ -99,6 +101,8 @@ public:
&& this->bindingType_ == other.bindingType_
&& this->note_ == other.note_
&& this->control_ == other.control_
&& this->minControlValue_ == other.minControlValue_
&& this->maxControlValue_ == other.maxControlValue_
&& this->minValue_ == other.minValue_
&& this->maxValue_ == other.maxValue_
&& this->rotaryScale_ == other.rotaryScale_
@@ -111,6 +115,8 @@ public:
GETTER_SETTER(bindingType);
GETTER_SETTER(note);
GETTER_SETTER(control);
GETTER_SETTER(minControlValue);
GETTER_SETTER(maxControlValue);
GETTER_SETTER(minValue);
GETTER_SETTER(maxValue);
GETTER_SETTER(rotaryScale);
@@ -120,9 +126,20 @@ public:
void switchControlType(SwitchControlTypeT value) { switchControlType_ = (int)value; }
float adjustRange(float range)
float calculateRange(uint8_t value)
{
return maxValue_*range+minValue_*(1.0f-range);
float controlRange;
if (maxControlValue_ == minControlValue_)
{
controlRange = minControlValue_;
} else {
controlRange =
(float)((int8_t)value - (int8_t)minControlValue_) /
(float)((int8_t)maxControlValue_ - (int8_t)minControlValue_);
}
if (controlRange < 0) controlRange = 0;
else if (controlRange > 1) controlRange = 1;
return maxValue_*controlRange+minValue_*(1.0f-controlRange);
}
DECLARE_JSON_MAP(MidiBinding);
+10
View File
@@ -136,6 +136,15 @@ bool Pedalboard::SetControlValue(int64_t pedalItemId, const std::string &symbol,
return item->SetControlValue(symbol,value);
}
bool Pedalboard::SetItemTitle(int64_t pedalItemId, const std::string &title)
{
PedalboardItem*item = GetItem(pedalItemId);
if (!item) return false;
if (item->title() == title) return false; // no change.
item->title(title);
return true;
}
PedalboardItem Pedalboard::MakeEmptyItem()
{
@@ -503,6 +512,7 @@ JSON_MAP_BEGIN(PedalboardItem)
JSON_MAP_REFERENCE(PedalboardItem,lv2State)
JSON_MAP_REFERENCE(PedalboardItem,lilvPresetUri)
JSON_MAP_REFERENCE(PedalboardItem,pathProperties)
JSON_MAP_REFERENCE(PedalboardItem,title)
JSON_MAP_END()
+4
View File
@@ -98,6 +98,7 @@ public:
Lv2PluginState lv2State_;
std::string lilvPresetUri_;
std::map<std::string,std::string> pathProperties_;
std::string title_;
// non persistent state.
PropertyMap patchProperties;
@@ -126,6 +127,8 @@ public:
GETTER_SETTER_REF(midiChannelBinding)
GETTER_SETTER(stateUpdateCount)
GETTER_SETTER_REF(lv2State)
GETTER_SETTER_REF(title)
Lv2PluginState&lv2State() { return lv2State_; } // non-const version.
GETTER_SETTER_REF(lilvPresetUri)
@@ -198,6 +201,7 @@ public:
static constexpr int64_t INPUT_VOLUME_ID = -2; // synthetic PedalboardItem for input volume.
static constexpr int64_t OUTPUT_VOLUME_ID = -3; // synthetic PedalboardItem for output volume.
bool SetControlValue(int64_t pedalItemId, const std::string &symbol, float value);
bool SetItemTitle(int64_t pedalItemId, const std::string &title);
bool SetItemEnabled(int64_t pedalItemId, bool enabled);
void SetCurrentSnapshotModified(bool modified);
+34 -15
View File
@@ -2192,34 +2192,40 @@ void PiPedalModel::OnNotifyPathPatchPropertyReceived(
}
}
void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
void PiPedalModel::OnNotifyMidiListen(uint8_t cc0, uint8_t cc1, uint8_t cc2)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
bool isNote = (cc0 & 0xF0) == 0x90; // Note On
if (isNote && cc2 == 0) {
return; // Note off. Oopsie.
}
bool isControl = (cc0 & 0xF0) == 0xB0; // Control Change
if (!isNote && !isControl) {
return; // Not a note on or control change.
}
for (int i = 0; i < midiEventListeners.size(); ++i)
{
auto &listener = midiEventListeners[i];
if ((!isNote) == (listener.listenForControls))
auto subscriber = this->GetNotificationSubscriber(listener.clientId);
if (subscriber)
{
auto subscriber = this->GetNotificationSubscriber(listener.clientId);
if (subscriber)
{
subscriber->OnNotifyMidiListener(listener.clientHandle, isNote, noteOrControl);
}
else
{
midiEventListeners.erase(midiEventListeners.begin() + i);
--i;
}
subscriber->OnNotifyMidiListener(listener.clientHandle, cc0,cc1,cc2);
}
else
{
midiEventListeners.erase(midiEventListeners.begin() + i);
--i;
}
}
audioHost->SetListenForMidiEvent(midiEventListeners.size() != 0);
}
void PiPedalModel::ListenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControls)
void PiPedalModel::ListenForMidiEvent(int64_t clientId, int64_t clientHandle)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
MidiListener listener{clientId, clientHandle, listenForControls};
MidiListener listener{clientId, clientHandle};
midiEventListeners.push_back(listener);
audioHost->SetListenForMidiEvent(true);
}
@@ -2946,4 +2952,17 @@ void PiPedalModel::MoveAudioFile(
}
AudioDirectoryInfo::Ptr dir = AudioDirectoryInfo::Create(directory);
dir->MoveAudioFile(directory, fromPosition, toPosition);
}
}
void PiPedalModel::SetPedalboardItemTitle(int64_t instanceId, const std::string &title)
{
std::lock_guard<std::recursive_mutex> lock(mutex);
if (!this->pedalboard.SetItemTitle(instanceId,title))
{
return;
}
// no need to reload the pedalboard, but we do need to notify subscribers.
this->SetPresetChanged(-1, true);
this->FirePedalboardChanged(-1,false);
}
+5 -4
View File
@@ -78,7 +78,7 @@ namespace pipedal
virtual void OnJackConfigurationChanged(const JackConfiguration &jackServerConfiguration) = 0;
virtual void OnLoadPluginPreset(int64_t instanceId, const std::vector<ControlValue> &controlValues) = 0;
virtual void OnMidiValueChanged(int64_t instanceId, const std::string &symbol, float value) = 0;
virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl) = 0;
virtual void OnNotifyMidiListener(int64_t clientHandle, uint8_t cc0, uint8_t cc1, uint8_t cc2) = 0;
virtual void OnNotifyPatchProperty(int64_t clientModel, uint64_t instanceId, const std::string &propertyUri, const std::string &atomJson) = 0;
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings &wifiConfigSettings) = 0;
virtual void OnWifiDirectConfigSettingsChanged(const WifiDirectConfigSettings &wifiDirectConfigSettings) = 0;
@@ -146,7 +146,6 @@ namespace pipedal
public:
int64_t clientId;
int64_t clientHandle;
bool listenForControls;
};
class AtomOutputListener
{
@@ -234,7 +233,7 @@ namespace pipedal
virtual void OnNotifyVusSubscription(const std::vector<VuUpdate> &updates) override;
virtual void OnNotifyMonitorPort(const MonitorPortUpdate &update) override;
virtual void OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value) override;
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl) override;
virtual void OnNotifyMidiListen(uint8_t cc0, uint8_t cc1, uint8_t cc2) override;
virtual void OnPatchSetReply(uint64_t instanceId, LV2_URID patchSetProperty, const LV2_Atom *atomValue) override;
virtual void OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) override;
virtual void OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) override;
@@ -437,7 +436,7 @@ namespace pipedal
JackServerSettings GetJackServerSettings();
void SetJackServerSettings(const JackServerSettings &jackServerSettings);
void ListenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControls);
void ListenForMidiEvent(int64_t clientId, int64_t clientHandle);
void CancelListenForMidiEvent(int64_t clientId, int64_t clientHandle);
void MonitorPatchProperty(int64_t clientId, int64_t clientHandle, uint64_t instanceId, const std::string &propertyUri);
@@ -469,6 +468,8 @@ namespace pipedal
const std::string & path,
int32_t from,
int32_t to);
void SetPedalboardItemTitle(int64_t instanceId, const std::string &title);
};
} // namespace pipedal.
+33 -11
View File
@@ -174,18 +174,36 @@ JSON_MAP_REFERENCE(SetPatchPropertyBody, propertyUri)
JSON_MAP_REFERENCE(SetPatchPropertyBody, value)
JSON_MAP_END()
class SetPedalboardItemTitleBody
{
public:
uint64_t instanceId_;
std::string title_;
DECLARE_JSON_MAP(SetPedalboardItemTitleBody);
};
JSON_MAP_BEGIN(SetPedalboardItemTitleBody)
JSON_MAP_REFERENCE(SetPedalboardItemTitleBody, instanceId)
JSON_MAP_REFERENCE(SetPedalboardItemTitleBody, title)
JSON_MAP_END()
class NotifyMidiListenerBody
{
public:
int64_t clientHandle_;
bool isNote_;
int32_t noteOrControl_;
uint16_t cc0_;
uint16_t cc1_;
uint16_t cc2_;
DECLARE_JSON_MAP(NotifyMidiListenerBody);
};
JSON_MAP_BEGIN(NotifyMidiListenerBody)
JSON_MAP_REFERENCE(NotifyMidiListenerBody, clientHandle)
JSON_MAP_REFERENCE(NotifyMidiListenerBody, isNote)
JSON_MAP_REFERENCE(NotifyMidiListenerBody, noteOrControl)
JSON_MAP_REFERENCE(NotifyMidiListenerBody, cc0)
JSON_MAP_REFERENCE(NotifyMidiListenerBody, cc1)
JSON_MAP_REFERENCE(NotifyMidiListenerBody, cc2)
JSON_MAP_END()
class NotifyAtomOutputBody
@@ -208,13 +226,11 @@ JSON_MAP_END()
class ListenForMidiEventBody
{
public:
bool listenForControls_;
int64_t handle_;
DECLARE_JSON_MAP(ListenForMidiEventBody);
};
JSON_MAP_BEGIN(ListenForMidiEventBody)
JSON_MAP_REFERENCE(ListenForMidiEventBody, listenForControls)
JSON_MAP_REFERENCE(ListenForMidiEventBody, handle)
JSON_MAP_END()
@@ -1099,7 +1115,7 @@ public:
{
ListenForMidiEventBody body;
pReader->read(&body);
this->model.ListenForMidiEvent(this->clientId, body.handle_, body.listenForControls_);
this->model.ListenForMidiEvent(this->clientId, body.handle_);
}
else if (message == "cancelListenForMidiEvent")
{
@@ -1503,7 +1519,12 @@ public:
{ this->JsonReply(replyTo, "setPatchProperty", "true"); }, [this, replyTo](const std::string &error)
{ this->SendError(replyTo, error.c_str()); });
}
else if (message == "setPedalboardItemTitle")
{
SetPedalboardItemTitleBody body;
pReader->read(&body);
model.SetPedalboardItemTitle(body.instanceId_, body.title_);
}
else if (message == "getPatchProperty")
{
GetPatchPropertyBody body;
@@ -2121,12 +2142,13 @@ private:
Send("onNotifyPathPatchPropertyChanged", body);
}
virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl)
virtual void OnNotifyMidiListener(int64_t clientHandle, uint8_t cc0, uint8_t cc1, uint8_t cc2) override
{
NotifyMidiListenerBody body;
body.clientHandle_ = clientHandle;
body.isNote_ = isNote;
body.noteOrControl_ = noteOrControl;
body.cc0_ = cc0;
body.cc1_ = cc1;
body.cc2_ = cc2;
Send("onNotifyMidiListener", body);
}
+20 -8
View File
@@ -31,6 +31,20 @@ namespace pipedal
{
class IndexedSnapshot;
class MidiNotifyBody
{
public:
MidiNotifyBody() = default;
MidiNotifyBody(uint8_t cc0, uint8_t cc1, uint8_t cc2)
: cc0_(cc0), cc1_(cc1), cc2_(cc2)
{
}
uint8_t cc0_;
uint8_t cc1_;
uint8_t cc2_;
};
enum class RingBufferCommand : int64_t
{
Invalid = 0,
@@ -38,7 +52,7 @@ namespace pipedal
EffectReplaced,
SetValue,
SetBypass,
//AudioStopped,
// AudioStopped,
AudioTerminatedAbnormally, // specifically for an ALSA loss of connection.
SetVuSubscriptions,
FreeVuSubscriptions,
@@ -327,7 +341,7 @@ namespace pipedal
return;
}
}
template <typename T>
void write(RingBufferCommand command, const T &value, size_t dataLength, uint8_t *variableData)
{
@@ -374,12 +388,9 @@ namespace pipedal
write(RingBufferCommand::MidiValueChanged, body);
}
void OnMidiListen(bool isNote, uint8_t noteOrControl)
void OnMidiListen(const MidiNotifyBody &body)
{
uint16_t msg = noteOrControl;
if (isNote)
msg |= 0x100;
write(RingBufferCommand::OnMidiListen, msg);
write(RingBufferCommand::OnMidiListen, body);
}
/**
@@ -485,7 +496,8 @@ namespace pipedal
{
write(RingBufferCommand::AckMidiProgramChange, requestId);
}
void AckMidiSnapshotRequest(uint64_t snapshotRequestId) {
void AckMidiSnapshotRequest(uint64_t snapshotRequestId)
{
write(RingBufferCommand::AckMidiSnapshotRequest, snapshotRequestId);
}