From 04631964d345739aae97894ff6f3267080fe3863 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Wed, 21 Feb 2024 20:29:37 +0100 Subject: [PATCH] Switch to message buffer / dispatcher for MIDI messages --- plugins/midi/macro-action-midi.cpp | 29 ++++---- plugins/midi/macro-action-midi.hpp | 1 + plugins/midi/macro-condition-midi.cpp | 55 ++++++++++------ plugins/midi/macro-condition-midi.hpp | 6 +- plugins/midi/midi-helpers.cpp | 95 ++++++--------------------- plugins/midi/midi-helpers.hpp | 36 +++++----- 6 files changed, 93 insertions(+), 129 deletions(-) diff --git a/plugins/midi/macro-action-midi.cpp b/plugins/midi/macro-action-midi.cpp index ab118e9c..3d1c4972 100644 --- a/plugins/midi/macro-action-midi.cpp +++ b/plugins/midi/macro-action-midi.cpp @@ -156,10 +156,11 @@ void MacroActionMidiEdit::EnableListening(bool enable) if (_currentlyListening == enable) { return; } - _listenDevice.UseForMessageSelection(enable); if (enable) { + _messageBuffer = _entryData->_device.RegisterForMidiMessages(); _listenTimer.start(); } else { + _messageBuffer.reset(); _listenTimer.stop(); } } @@ -176,12 +177,6 @@ void MacroActionMidiEdit::ToggleListen() return; } - if (!_currentlyListening && _listenDevice.IsUsedForMessageSelection()) { - DisplayMessage(obs_module_text( - "AdvSceneSwitcher.midi.startListenFail")); - return; - } - _listen->setText( _currentlyListening ? obs_module_text("AdvSceneSwitcher.midi.startListen") @@ -194,14 +189,24 @@ void MacroActionMidiEdit::ToggleListen() void MacroActionMidiEdit::SetMessageSelectionToLastReceived() { auto lock = LockContext(); - auto messages = _listenDevice.GetMessages(true); - if (!_entryData || !messages || messages->empty()) { + if (!_entryData || !_messageBuffer || _messageBuffer->Empty()) { return; } - _message->SetMessage(messages->back()); - _entryData->_message = messages->back(); - _listenDevice.ClearMessageBuffer(); + std::optional message; + while (!_messageBuffer->Empty()) { + message = _messageBuffer->ConsumeMessage(); + if (!message) { + continue; + } + } + + if (!message) { + return; + } + + _message->SetMessage(*message); + _entryData->_message = *message; } } // namespace advss diff --git a/plugins/midi/macro-action-midi.hpp b/plugins/midi/macro-action-midi.hpp index 65359fce..c04fd97b 100644 --- a/plugins/midi/macro-action-midi.hpp +++ b/plugins/midi/macro-action-midi.hpp @@ -68,6 +68,7 @@ private: QPushButton *_listen; MidiDevice _listenDevice; QTimer _listenTimer; + MidiMessageBuffer _messageBuffer; bool _currentlyListening = false; bool _loading = true; }; diff --git a/plugins/midi/macro-condition-midi.cpp b/plugins/midi/macro-condition-midi.cpp index f8395ad5..b88e9ef2 100644 --- a/plugins/midi/macro-condition-midi.cpp +++ b/plugins/midi/macro-condition-midi.cpp @@ -13,14 +13,17 @@ bool MacroConditionMidi::_registered = MacroConditionFactory::Register( bool MacroConditionMidi::CheckCondition() { - auto messages = _device.GetMessages(); - if (!messages) { + if (!_messageBuffer) { return false; } - for (const auto &m : *messages) { - if (m.Matches(_message)) { - SetVariableValues(m); + while (!_messageBuffer->Empty()) { + auto message = _messageBuffer->ConsumeMessage(); + if (!message) { + continue; + } + if (message->Matches(_message)) { + SetVariableValues(*message); return true; } } @@ -41,6 +44,7 @@ bool MacroConditionMidi::Load(obs_data_t *obj) MacroCondition::Load(obj); _message.Load(obj); _device.Load(obj); + _messageBuffer = _device.RegisterForMidiMessages(); return true; } @@ -49,6 +53,12 @@ std::string MacroConditionMidi::GetShortDesc() const return _device.Name(); } +void MacroConditionMidi::SetDevice(const MidiDevice &dev) +{ + _device = dev; + _messageBuffer = dev.RegisterForMidiMessages(); +} + void MacroConditionMidi::SetupTempVars() { MacroCondition::SetupTempVars(); @@ -137,7 +147,7 @@ void MacroConditionMidiEdit::UpdateEntryData() } _message->SetMessage(_entryData->_message); - _devices->SetDevice(_entryData->_device); + _devices->SetDevice(_entryData->GetDevice()); adjustSize(); updateGeometry(); @@ -155,7 +165,7 @@ void MacroConditionMidiEdit::DeviceSelectionChanged(const MidiDevice &device) { auto lock = LockContext(); - _entryData->_device = device; + _entryData->SetDevice(device); } emit HeaderInfoChanged( QString::fromStdString(_entryData->GetShortDesc())); @@ -182,10 +192,12 @@ void MacroConditionMidiEdit::EnableListening(bool enable) if (_currentlyListening == enable) { return; } - _entryData->_device.UseForMessageSelection(enable); if (enable) { + _messageBuffer = + _entryData->GetDevice().RegisterForMidiMessages(); _listenTimer.start(); } else { + _messageBuffer.reset(); _listenTimer.stop(); } } @@ -196,13 +208,6 @@ void MacroConditionMidiEdit::ToggleListen() return; } - if (!_currentlyListening && - _entryData->_device.IsUsedForMessageSelection()) { - DisplayMessage(obs_module_text( - "AdvSceneSwitcher.midi.startListenFail")); - return; - } - _listen->setText( _currentlyListening ? obs_module_text("AdvSceneSwitcher.midi.startListen") @@ -215,14 +220,24 @@ void MacroConditionMidiEdit::ToggleListen() void MacroConditionMidiEdit::SetMessageSelectionToLastReceived() { auto lock = LockContext(); - auto messages = _entryData->_device.GetMessages(true); - if (!_entryData || !messages || messages->empty()) { + if (!_entryData || !_messageBuffer || _messageBuffer->Empty()) { return; } - _message->SetMessage(messages->back()); - _entryData->_message = messages->back(); - _entryData->_device.ClearMessageBuffer(); + std::optional message; + while (!_messageBuffer->Empty()) { + message = _messageBuffer->ConsumeMessage(); + if (!message) { + continue; + } + } + + if (!message) { + return; + } + + _message->SetMessage(*message); + _entryData->_message = *message; } } // namespace advss diff --git a/plugins/midi/macro-condition-midi.hpp b/plugins/midi/macro-condition-midi.hpp index bfedc3e9..c70f6bce 100644 --- a/plugins/midi/macro-condition-midi.hpp +++ b/plugins/midi/macro-condition-midi.hpp @@ -20,13 +20,16 @@ public: return std::make_shared(m); } - MidiDevice _device; + void SetDevice(const MidiDevice &dev); + const MidiDevice &GetDevice() const { return _device; } MidiMessage _message; private: void SetupTempVars(); void SetVariableValues(const MidiMessage &); + MidiDevice _device; + MidiMessageBuffer _messageBuffer; static bool _registered; static const std::string id; }; @@ -67,6 +70,7 @@ private: QPushButton *_resetMidiDevices; QPushButton *_listen; QTimer _listenTimer; + MidiMessageBuffer _messageBuffer; bool _currentlyListening = false; bool _loading = true; }; diff --git a/plugins/midi/midi-helpers.cpp b/plugins/midi/midi-helpers.cpp index fb0338aa..b484b3a0 100644 --- a/plugins/midi/midi-helpers.cpp +++ b/plugins/midi/midi-helpers.cpp @@ -10,48 +10,21 @@ #include #include +#undef DispatchMessage + namespace advss { std::map, MidiDeviceInstance *> MidiDeviceInstance::devices = {}; -static bool registerClearMessageBufferStep() -{ - AddIntervalResetStep( - MidiDeviceInstance::ClearMessageBuffersOfAllDevices); - return true; -} - -static bool registerClearMessageBufferStepDone = - registerClearMessageBufferStep(); - -void MidiDeviceInstance::ClearMessageBuffersOfAllDevices() -{ - for (auto const &[_, device] : MidiDeviceInstance::devices) { - if (device->_skipBufferClear) { - continue; - } - device->ClearMessageBuffer(); - } -} - void MidiDeviceInstance::ResetAllDevices() { for (auto const &[_, device] : MidiDeviceInstance::devices) { - if (device->_skipBufferClear) { - continue; - } device->ClosePort(); - device->ClearMessageBuffer(); device->OpenPort(); } } -void MidiDeviceInstance::ClearMessageBuffer() -{ - _messages.clear(); -} - MidiMessage::MidiMessage(const libremidi::message &message) { _typeIsOptional = false; @@ -357,7 +330,7 @@ void MidiDevice::Load(obs_data_t *obj) } } -bool MidiDevice::SendMessge(const MidiMessage &m) +bool MidiDevice::SendMessge(const MidiMessage &m) const { if (_type == MidiDeviceType::INPUT || _name.empty() || !_dev) { return false; @@ -392,8 +365,8 @@ getInPortFromName(const std::string &name) bool MidiDeviceInstance::OpenPort() { - if ((_type == MidiDeviceType::INPUT && in.is_port_open()) || - (_type == MidiDeviceType::OUTPUT && out.is_port_open())) { + if ((_type == MidiDeviceType::INPUT && _in.is_port_open()) || + (_type == MidiDeviceType::OUTPUT && _out.is_port_open())) { return true; } @@ -406,7 +379,7 @@ bool MidiDeviceInstance::OpenPort() return false; } try { - out.open_port(*port); + _out.open_port(*port); blog(LOG_INFO, "Opened output midi port '%s'", _name.c_str()); return true; @@ -430,7 +403,7 @@ bool MidiDeviceInstance::OpenPort() ReceiveMidiMessage(std::move(m)); }; - in = libremidi::midi_in{ + _in = libremidi::midi_in{ libremidi::input_configuration{.on_message = cb}}; auto port = getInPortFromName(_name); @@ -441,7 +414,7 @@ bool MidiDeviceInstance::OpenPort() } try { - in.open_port(*port); + _in.open_port(*port); blog(LOG_INFO, "Opened input midi port '%s'", _name.c_str()); return true; } catch (const libremidi::driver_error &error) { @@ -459,14 +432,14 @@ bool MidiDeviceInstance::OpenPort() void MidiDeviceInstance::ClosePort() { - if ((_type == MidiDeviceType::INPUT && !in.is_port_open()) || - (_type == MidiDeviceType::OUTPUT && !out.is_port_open())) { + if ((_type == MidiDeviceType::INPUT && !_in.is_port_open()) || + (_type == MidiDeviceType::OUTPUT && !_out.is_port_open())) { return; } if (_type == MidiDeviceType::OUTPUT) { try { - out.close_port(); + _out.close_port(); blog(LOG_INFO, "Closed output midi port '%s'", _name.c_str()); } catch (const libremidi::driver_error &error) { @@ -486,7 +459,7 @@ void MidiDeviceInstance::ClosePort() } try { - in.close_port(); + _in.close_port(); blog(LOG_INFO, "Closed input midi port '%s'", _name.c_str()); } catch (const libremidi::driver_error &error) { blog(LOG_WARNING, "Failed to close input midi port '%s': %s", @@ -546,7 +519,7 @@ bool MidiDeviceInstance::SendMessge(const MidiMessage &m) } try { - out.send_message(message); + _out.send_message(message); return true; } catch (const libremidi::driver_error &err) { blog(LOG_WARNING, "%s", err.what()); @@ -555,54 +528,26 @@ bool MidiDeviceInstance::SendMessge(const MidiMessage &m) return false; } -const std::vector &MidiDeviceInstance::GetMessages() +MidiMessageBuffer MidiDeviceInstance::RegisterForMidiMessages() { - return _messages; + return _dispatcher.RegisterClient(); } void MidiDeviceInstance::ReceiveMidiMessage(libremidi::message &&msg) { auto lock = LockContext(); - _messages.emplace_back(msg); + _dispatcher.DispatchMessage(msg); vblog(LOG_INFO, "received midi: %s", MidiMessage::ToString(msg).c_str()); } -void MidiDevice::UseForMessageSelection(bool skipBufferClear) +[[nodiscard]] MidiMessageBuffer MidiDevice::RegisterForMidiMessages() const { - if (!_dev) { - return; + if (_type == MidiDeviceType::OUTPUT || _name.empty() || !_dev) { + return {}; } - blog(LOG_INFO, "%s \"listen\" mode for midi input device \"%s\"! %s", - skipBufferClear ? "Enable" : "Disable", Name().c_str(), - skipBufferClear - ? "This will block incoming messages from being processed!" - : ""); - ClearMessageBuffer(); - _dev->_skipBufferClear = skipBufferClear; -} - -bool MidiDevice::IsUsedForMessageSelection() -{ - return _dev && _dev->_skipBufferClear; -} - -void MidiDevice::ClearMessageBuffer() -{ - if (_dev) { - _dev->ClearMessageBuffer(); - } -} - -const std::vector *MidiDevice::GetMessages(bool ignoreSkip) -{ - if (_type == MidiDeviceType::OUTPUT || _name.empty() || !_dev || - (_dev->_skipBufferClear && !ignoreSkip)) { - return nullptr; - } - - return &_dev->GetMessages(); + return _dev->RegisterForMidiMessages(); } std::string MidiDevice::Name() const diff --git a/plugins/midi/midi-helpers.hpp b/plugins/midi/midi-helpers.hpp index d2f5215d..5adc7b4b 100644 --- a/plugins/midi/midi-helpers.hpp +++ b/plugins/midi/midi-helpers.hpp @@ -1,15 +1,20 @@ #pragma once -#include -#include -#include #include +#include #include +#include +#include +#include #define LIBREMIDI_HEADER_ONLY 1 #include namespace advss { +class MidiMessage; +using MidiMessageBuffer = std::shared_ptr>; +using MidiMessageDispatcher = MessageDispatcher; + // Based on https://github.com/nhielost/obs-midi-mg MMGMessage class MidiMessage { public: @@ -60,7 +65,6 @@ public: static MidiDeviceInstance *GetDevice(MidiDeviceType type, const std::string &); static MidiDeviceInstance *GetDevice(MidiDeviceType type, int port); - static void ClearMessageBuffersOfAllDevices(); static void ResetAllDevices(); private: @@ -69,26 +73,23 @@ private: bool OpenPort(); void ClosePort(); bool SendMessge(const MidiMessage &); - const std::vector &GetMessages(); + [[nodiscard]] MidiMessageBuffer RegisterForMidiMessages(); void ReceiveMidiMessage(libremidi::message &&); - void ClearMessageBuffer(); static std::map, MidiDeviceInstance *> devices; - bool _skipBufferClear = false; - MidiDeviceType _type = MidiDeviceType::INPUT; std::string _name; - libremidi::midi_in in = + libremidi::midi_in _in = libremidi::midi_in(libremidi::input_configuration{ [this](libremidi::message &&message) { ReceiveMidiMessage(std::move(message)); }}); - libremidi::midi_out out = + libremidi::midi_out _out = libremidi::midi_out(libremidi::output_configuration()); - std::vector _messages; + MidiMessageDispatcher _dispatcher; friend class MidiDevice; }; @@ -100,19 +101,12 @@ public: void Save(obs_data_t *obj) const; void Load(obs_data_t *obj); - bool SendMessge(const MidiMessage &); + bool SendMessge(const MidiMessage &) const; + [[nodiscard]] MidiMessageBuffer RegisterForMidiMessages() const; - const std::vector * // Might resize! Only call - GetMessages(bool ignoreListenMode = false); // while holding switcher - // lock! std::string Name() const; - // Used for "listen" mode of message selection - // Listen mode disables automatic clearing of buffers - void UseForMessageSelection(bool); - bool IsUsedForMessageSelection(); - void ClearMessageBuffer(); - bool DeviceSelected() { return !!_dev; } + bool DeviceSelected() const { return !!_dev; } private: MidiDeviceType _type = MidiDeviceType::INPUT;