#include "macro-condition-midi.hpp" #include "layout-helpers.hpp" #include "macro-helpers.hpp" #include "ui-helpers.hpp" namespace advss { const std::string MacroConditionMidi::id = "midi"; bool MacroConditionMidi::_registered = MacroConditionFactory::Register( MacroConditionMidi::id, {MacroConditionMidi::Create, MacroConditionMidiEdit::Create, "AdvSceneSwitcher.condition.midi"}); bool MacroConditionMidi::CheckCondition() { if (!_messageBuffer) { return false; } const bool macroWasPausedSinceLastCheck = MacroWasPausedSince(GetMacro(), _lastCheck); _lastCheck = std::chrono::high_resolution_clock::now(); if (macroWasPausedSinceLastCheck) { _messageBuffer->Clear(); return false; } while (!_messageBuffer->Empty()) { auto message = _messageBuffer->ConsumeMessage(); if (!message) { continue; } if (message->Matches(_message)) { SetVariableValues(*message); if (_clearBufferOnMatch) { _messageBuffer->Clear(); } return true; } } return false; } bool MacroConditionMidi::Save(obs_data_t *obj) const { MacroCondition::Save(obj); _message.Save(obj); _device.Save(obj); obs_data_set_bool(obj, "clearBufferOnMatch", _clearBufferOnMatch); obs_data_set_int(obj, "version", 1); return true; } bool MacroConditionMidi::Load(obs_data_t *obj) { MacroCondition::Load(obj); _message.Load(obj); _device.Load(obj); _messageBuffer = _device.RegisterForMidiMessages(); _clearBufferOnMatch = obs_data_get_bool(obj, "clearBufferOnMatch"); if (!obs_data_has_user_value(obj, "version")) { _clearBufferOnMatch = true; } return true; } std::string MacroConditionMidi::GetShortDesc() const { return _device.Name(); } void MacroConditionMidi::SetDevice(const MidiDevice &dev) { _device = dev; _messageBuffer = dev.RegisterForMidiMessages(); } void MacroConditionMidi::SetupTempVars() { MacroCondition::SetupTempVars(); AddTempvar("type", obs_module_text("AdvSceneSwitcher.tempVar.midi.type")); AddTempvar("channel", obs_module_text("AdvSceneSwitcher.tempVar.midi.channel")); AddTempvar("note", obs_module_text("AdvSceneSwitcher.tempVar.midi.note")); AddTempvar("value1", obs_module_text("AdvSceneSwitcher.tempVar.midi.value1")); AddTempvar("value2", obs_module_text("AdvSceneSwitcher.tempVar.midi.value2")); } void MacroConditionMidi::SetVariableValues(const MidiMessage &m) { SetVariableValue(std::to_string(m.Note()) + " " + std::to_string(m.Value())); SetTempVarValue("type", m.MidiTypeToString(m.Type())); SetTempVarValue("channel", std::to_string(m.Channel())); try { SetTempVarValue("note", GetAllNotes().at(m.Note()).toStdString()); } catch (...) { } SetTempVarValue("value1", std::to_string(m.Note())); SetTempVarValue("value2", std::to_string(m.Value())); } MacroConditionMidiEdit::MacroConditionMidiEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), _devices(new MidiDeviceSelection(this, MidiDeviceType::INPUT)), _message(new MidiMessageSelection(this)), _resetMidiDevices(new QPushButton( obs_module_text("AdvSceneSwitcher.midi.resetDevices"))), _listen(new QPushButton( obs_module_text("AdvSceneSwitcher.midi.startListen"))), _clearBufferOnMatch(new QCheckBox( obs_module_text("AdvSceneSwitcher.clearBufferOnMatch"))) { QWidget::connect(_devices, SIGNAL(DeviceSelectionChanged(const MidiDevice &)), this, SLOT(DeviceSelectionChanged(const MidiDevice &))); QWidget::connect(_message, SIGNAL(MidiMessageChanged(const MidiMessage &)), this, SLOT(MidiMessageChanged(const MidiMessage &))); QWidget::connect(_resetMidiDevices, SIGNAL(clicked()), this, SLOT(ResetMidiDevices())); QWidget::connect(_listen, SIGNAL(clicked()), this, SLOT(ToggleListen())); QWidget::connect(_clearBufferOnMatch, SIGNAL(stateChanged(int)), this, SLOT(ClearBufferOnMatchChanged(int))); QWidget::connect(&_listenTimer, SIGNAL(timeout()), this, SLOT(SetMessageSelectionToLastReceived())); auto entryLayout = new QHBoxLayout; PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.midi.entry"), entryLayout, {{"{{device}}", _devices}}); auto listenLayout = new QHBoxLayout; PlaceWidgets( obs_module_text("AdvSceneSwitcher.condition.midi.entry.listen"), listenLayout, {{"{{listenButton}}", _listen}}); auto mainLayout = new QVBoxLayout; mainLayout->addLayout(entryLayout); mainLayout->addWidget(_message); mainLayout->addLayout(listenLayout); mainLayout->addWidget(_resetMidiDevices); mainLayout->addWidget(_clearBufferOnMatch); setLayout(mainLayout); _listenTimer.setInterval(100); _entryData = entryData; UpdateEntryData(); _loading = false; } MacroConditionMidiEdit::~MacroConditionMidiEdit() { EnableListening(false); } void MacroConditionMidiEdit::UpdateEntryData() { if (!_entryData) { return; } _message->SetMessage(_entryData->_message); _devices->SetDevice(_entryData->GetDevice()); _clearBufferOnMatch->setChecked(_entryData->_clearBufferOnMatch); adjustSize(); updateGeometry(); } void MacroConditionMidiEdit::DeviceSelectionChanged(const MidiDevice &device) { if (_loading || !_entryData) { return; } if (_currentlyListening) { ToggleListen(); } { auto lock = LockContext(); _entryData->SetDevice(device); } emit HeaderInfoChanged( QString::fromStdString(_entryData->GetShortDesc())); } void MacroConditionMidiEdit::MidiMessageChanged(const MidiMessage &message) { GUARD_LOADING_AND_LOCK(); _entryData->_message = message; } void MacroConditionMidiEdit::ClearBufferOnMatchChanged(int value) { GUARD_LOADING_AND_LOCK(); _entryData->_clearBufferOnMatch = value; } void MacroConditionMidiEdit::ResetMidiDevices() { auto lock = LockContext(); MidiDeviceInstance::ResetAllDevices(); } void MacroConditionMidiEdit::EnableListening(bool enable) { if (_currentlyListening == enable) { return; } if (enable) { _messageBuffer = _entryData->GetDevice().RegisterForMidiMessages(); _listenTimer.start(); } else { _messageBuffer.reset(); _listenTimer.stop(); } } void MacroConditionMidiEdit::ToggleListen() { if (!_entryData) { return; } _listen->setText( _currentlyListening ? obs_module_text("AdvSceneSwitcher.midi.startListen") : obs_module_text("AdvSceneSwitcher.midi.stopListen")); EnableListening(!_currentlyListening); _currentlyListening = !_currentlyListening; _message->setDisabled(_currentlyListening); } void MacroConditionMidiEdit::SetMessageSelectionToLastReceived() { auto lock = LockContext(); if (!_entryData || !_messageBuffer || _messageBuffer->Empty()) { return; } std::optional message; while (!_messageBuffer->Empty()) { message = _messageBuffer->ConsumeMessage(); if (!message) { continue; } } if (!message) { return; } _message->SetMessage(*message); _entryData->_message = *message; } } // namespace advss