Switch to message buffer / dispatcher for MIDI messages

This commit is contained in:
WarmUpTill 2024-02-21 20:29:37 +01:00 committed by WarmUpTill
parent 97983d5ac5
commit 04631964d3
6 changed files with 93 additions and 129 deletions

View File

@ -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<MidiMessage> message;
while (!_messageBuffer->Empty()) {
message = _messageBuffer->ConsumeMessage();
if (!message) {
continue;
}
}
if (!message) {
return;
}
_message->SetMessage(*message);
_entryData->_message = *message;
}
} // namespace advss

View File

@ -68,6 +68,7 @@ private:
QPushButton *_listen;
MidiDevice _listenDevice;
QTimer _listenTimer;
MidiMessageBuffer _messageBuffer;
bool _currentlyListening = false;
bool _loading = true;
};

View File

@ -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<MidiMessage> message;
while (!_messageBuffer->Empty()) {
message = _messageBuffer->ConsumeMessage();
if (!message) {
continue;
}
}
if (!message) {
return;
}
_message->SetMessage(*message);
_entryData->_message = *message;
}
} // namespace advss

View File

@ -20,13 +20,16 @@ public:
return std::make_shared<MacroConditionMidi>(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;
};

View File

@ -10,48 +10,21 @@
#include <ui-helpers.hpp>
#include <utility.hpp>
#undef DispatchMessage
namespace advss {
std::map<std::pair<MidiDeviceType, std::string>, 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<MidiMessage> &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<MidiMessage> *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

View File

@ -1,15 +1,20 @@
#pragma once
#include <variable-spinbox.hpp>
#include <variable-number.hpp>
#include <variable-string.hpp>
#include <QComboBox>
#include <message-dispatcher.hpp>
#include <obs-data.h>
#include <variable-number.hpp>
#include <variable-spinbox.hpp>
#include <variable-string.hpp>
#define LIBREMIDI_HEADER_ONLY 1
#include <libremidi/libremidi.hpp>
namespace advss {
class MidiMessage;
using MidiMessageBuffer = std::shared_ptr<MessageBuffer<MidiMessage>>;
using MidiMessageDispatcher = MessageDispatcher<MidiMessage>;
// 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<MidiMessage> &GetMessages();
[[nodiscard]] MidiMessageBuffer RegisterForMidiMessages();
void ReceiveMidiMessage(libremidi::message &&);
void ClearMessageBuffer();
static std::map<std::pair<MidiDeviceType, std::string>,
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<MidiMessage> _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<MidiMessage> * // 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;