SceneSwitcher/plugins/midi/midi-helpers.hpp
2024-03-09 12:00:52 +01:00

177 lines
4.6 KiB
C++

#pragma once
#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:
MidiMessage() = default;
MidiMessage(const libremidi::message &message);
void Save(obs_data_t *obj) const;
void Load(obs_data_t *obj);
bool Matches(const MidiMessage &) const;
static std::string ToString(const libremidi::message &msg);
static std::string MidiTypeToString(libremidi::message_type type);
static std::string GetMidiType(const libremidi::message &msg);
static int GetMidiNote(const libremidi::message &msg);
static int GetMidiValue(const libremidi::message &msg);
std::string ToString() const;
libremidi::message_type Type() const { return _type; }
int Channel() const { return _channel; }
int Note() const { return _note; }
int Value() const { return _value; }
void ResolveVariables();
private:
// Values which don't appear for channel, note, and value will be used
// to indicate whether this part of the message is optional and can be
// disregarded (e.g.during comparison using Matches())
static const int optionalChannelIndicator = 0;
static const int optionalNoteIndicator = -1;
static const int optionalValueIndicator = -1;
bool _typeIsOptional = true;
libremidi::message_type _type = libremidi::message_type::INVALID;
NumberVariable<int> _channel = optionalChannelIndicator;
NumberVariable<int> _note = optionalNoteIndicator;
NumberVariable<int> _value = optionalValueIndicator;
friend class MidiMessageSelection;
};
enum class MidiDeviceType {
INPUT,
OUTPUT,
};
class MidiDeviceInstance {
public:
static MidiDeviceInstance *GetDeviceAndOpen(MidiDeviceType type,
const std::string &);
static MidiDeviceInstance *GetDeviceAndOpen(MidiDeviceType type,
int port);
static MidiDeviceInstance *GetDevice(const libremidi::input_port &p);
static MidiDeviceInstance *GetDevice(const libremidi::output_port &p);
static void ResetAllDevices();
bool OpenPort();
void ClosePort();
private:
MidiDeviceInstance() = default;
~MidiDeviceInstance() = default;
bool IsOpened() const;
bool SendMessge(const MidiMessage &);
[[nodiscard]] MidiMessageBuffer RegisterForMidiMessages();
void ReceiveMidiMessage(libremidi::message &&);
static std::map<std::pair<MidiDeviceType, std::string>,
MidiDeviceInstance *>
devices;
MidiDeviceType _type = MidiDeviceType::INPUT;
std::string _name;
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(libremidi::output_configuration());
MidiMessageDispatcher _dispatcher;
friend class MidiDevice;
};
class MidiDevice {
public:
MidiDevice() = default;
void Save(obs_data_t *obj) const;
void Load(obs_data_t *obj);
bool SendMessge(const MidiMessage &) const;
[[nodiscard]] MidiMessageBuffer RegisterForMidiMessages() const;
std::string Name() const;
bool DeviceSelected() const { return !!_dev; }
private:
MidiDeviceType _type = MidiDeviceType::INPUT;
std::string _name;
MidiDeviceInstance *_dev = nullptr;
friend class MidiDeviceSelection;
};
class MidiMessageSelection : public QWidget {
Q_OBJECT
public:
MidiMessageSelection(QWidget *parent);
void SetMessage(const MidiMessage &);
private slots:
void TypeChanged(const QString &);
void ChannelChanged(const NumberVariable<int> &);
void NoteChanged(const NumberVariable<int> &);
void NoteStringIdxChanged(int);
void ShowNote(bool);
void ValueChanged(const NumberVariable<int> &);
signals:
void MidiMessageChanged(const MidiMessage &);
private:
static libremidi::message_type TextToMidiType(const QString &);
QComboBox *_type;
VariableSpinBox *_channel;
VariableSpinBox *_noteValue;
QComboBox *_noteString;
QPushButton *_noteValueStringToggle;
VariableSpinBox *_value;
MidiMessage _currentSelection;
};
class MidiDeviceSelection : public QComboBox {
Q_OBJECT
public:
MidiDeviceSelection(QWidget *parent, MidiDeviceType);
void SetDevice(const MidiDevice &);
private slots:
void IdxChangedHelper(int);
signals:
void DeviceSelectionChanged(const MidiDevice &);
private:
const MidiDeviceType _type;
};
QStringList GetAllNotes();
} // namespace advss