Adapt to new libremidi version

This commit is contained in:
WarmUpTill
2023-11-27 21:57:37 +01:00
committed by WarmUpTill
parent b1ae83e075
commit 97983d5ac5
4 changed files with 294 additions and 174 deletions

View File

@@ -13,21 +13,45 @@ if(NOT EXISTS "${LIBREMIDI_DIR}/CMakeLists.txt")
endif()
add_subdirectory("${LIBREMIDI_DIR}" "${LIBREMIDI_DIR}/build" EXCLUDE_FROM_ALL)
# --- End of section ---
add_library(${PROJECT_NAME} MODULE)
if(OS_WINDOWS)
if(MSVC)
target_compile_options(libremidi PUBLIC /wd4251 /wd4267 /wd4275)
target_compile_options(libremidi PRIVATE /wd4251 /wd4267 /wd4275 /wd4101
/wd4244 /wd4018)
target_compile_options(${PROJECT_NAME} PRIVATE /wd4101 /wd4244 /wd4018)
endif()
else()
target_compile_options(
libremidi
PUBLIC -Wno-error=conversion -Wno-error=unused-result -Wno-error=shadow
-Wno-error=unused-parameter -Wno-error=deprecated-declarations
PUBLIC -Wno-error=conversion
-Wno-error=unused-result
-Wno-error=shadow
-Wno-error=unused-parameter
-Wno-error=unused-variable
-Wno-error=deprecated-declarations
-Wno-error=sign-compare)
if(OS_MACOS)
target_compile_options(libremidi PUBLIC -Wno-error=vla-extension)
endif()
endif()
# --- End of section ---
add_library(${PROJECT_NAME} MODULE)
if(OS_MACOS)
set(PATCH_FILE
"${CMAKE_CURRENT_SOURCE_DIR}/remove-bit-cast-for-macos-build.patch")
execute_process(
COMMAND git apply ${PATCH_FILE}
WORKING_DIRECTORY ${LIBREMIDI_DIR}
RESULT_VARIABLE PATCH_RESULT)
if(PATCH_RESULT EQUAL 0)
message("Libremidi patch \"${PATCH_FILE}\" applied successfully!")
else()
message(
WARNING "Libremidi patch \"${PATCH_FILE}\" did NOT apply successfully!")
endif()
endif()
target_sources(
${PROJECT_NAME}

View File

@@ -12,16 +12,18 @@
namespace advss {
static std::map<std::pair<MidiDeviceType, int>, MidiDeviceInstance *>
SetupMidiMessageVector()
std::map<std::pair<MidiDeviceType, std::string>, MidiDeviceInstance *>
MidiDeviceInstance::devices = {};
static bool registerClearMessageBufferStep()
{
AddIntervalResetStep(
MidiDeviceInstance::ClearMessageBuffersOfAllDevices);
return {};
return true;
}
std::map<std::pair<MidiDeviceType, int>, MidiDeviceInstance *>
MidiDeviceInstance::devices = SetupMidiMessageVector();
static bool registerClearMessageBufferStepDone =
registerClearMessageBufferStep();
void MidiDeviceInstance::ClearMessageBuffersOfAllDevices()
{
@@ -61,26 +63,24 @@ MidiMessage::MidiMessage(const libremidi::message &message)
void MidiMessage::Save(obs_data_t *obj) const
{
auto data = obs_data_create();
OBSDataAutoRelease data = obs_data_create();
obs_data_set_bool(data, "typeIsOptional", _typeIsOptional);
obs_data_set_int(data, "type", static_cast<int>(_type));
_channel.Save(data, "channel");
_note.Save(data, "note");
_value.Save(data, "value");
obs_data_set_obj(obj, "midiMessage", data);
obs_data_release(data);
}
void MidiMessage::Load(obs_data_t *obj)
{
auto data = obs_data_get_obj(obj, "midiMessage");
OBSDataAutoRelease data = obs_data_get_obj(obj, "midiMessage");
_typeIsOptional = obs_data_get_bool(data, "typeIsOptional");
_type = static_cast<libremidi::message_type>(
obs_data_get_int(data, "type"));
_channel.Load(data, "channel");
_note.Load(data, "note");
_value.Load(data, "value");
obs_data_release(data);
}
int MidiMessage::GetMidiNote(const libremidi::message &msg)
@@ -232,13 +232,14 @@ bool MidiMessage::Matches(const MidiMessage &m) const
return channelMatch && noteMatch && valueMatch && typeMatch;
}
MidiDeviceInstance *MidiDeviceInstance::GetDevice(MidiDeviceType type, int port)
MidiDeviceInstance *MidiDeviceInstance::GetDevice(MidiDeviceType type,
const std::string &name)
{
if (port < 0) {
if (name.empty()) {
return nullptr;
}
auto key = std::make_pair(type, port);
auto key = std::make_pair(type, name);
auto it = devices.find(key);
if (it != devices.end()) {
it->second->OpenPort();
@@ -248,47 +249,147 @@ MidiDeviceInstance *MidiDeviceInstance::GetDevice(MidiDeviceType type, int port)
try {
auto device = new MidiDeviceInstance();
device->_type = type;
device->_port = port;
device->_name = name;
devices[key] = device;
device->OpenPort();
return device;
} catch (const libremidi::driver_error &error) {
blog(LOG_WARNING,
"Failed to create midi %s device instance for port #%d: %s",
type == MidiDeviceType::INPUT ? "input" : "output", port,
error.what());
"Failed to create midi %s device instance for port '%s': %s",
type == MidiDeviceType::INPUT ? "input" : "output",
name.c_str(), error.what());
}
return nullptr;
}
static std::string
getNameFromPortInformation(const libremidi::port_information &info)
{
std::string name = info.manufacturer + " " + info.device_name + " " +
info.port_name + "] " + info.display_name;
name.erase(name.begin(), std::find_if(name.begin(), name.end(),
[](char c) { return c != ' '; }));
return "[" + name;
}
static inline QStringList getInputDeviceNames()
{
QStringList devices;
try {
libremidi::observer obs;
for (const libremidi::input_port &port :
obs.get_input_ports()) {
devices << QString::fromStdString(
getNameFromPortInformation(port));
}
} catch (const libremidi::driver_error &error) {
blog(LOG_WARNING, "Failed to get midi input devices: %s",
error.what());
}
return devices;
}
static inline QStringList getOutputDeviceNames()
{
QStringList devices;
try {
libremidi::observer obs;
for (const libremidi::output_port &port :
obs.get_output_ports()) {
devices << QString::fromStdString(
getNameFromPortInformation(port));
}
} catch (const libremidi::driver_error &error) {
blog(LOG_WARNING, "Failed to get midi output devices: %s",
error.what());
}
return devices;
}
static std::string getPortNameFromNumber(MidiDeviceType type, int port)
{
if (port < 0) {
return "";
}
QStringList devices;
if (type == MidiDeviceType::INPUT) {
devices = getInputDeviceNames();
} else {
devices = getOutputDeviceNames();
}
if (port >= devices.size()) {
return "";
}
return devices.at(port).toStdString();
}
MidiDeviceInstance *MidiDeviceInstance::GetDevice(MidiDeviceType type, int port)
{
std::string name = getPortNameFromNumber(type, port);
return GetDevice(type, name);
}
void MidiDevice::Save(obs_data_t *obj) const
{
auto data = obs_data_create();
OBSDataAutoRelease data = obs_data_create();
obs_data_set_int(data, "type", static_cast<int>(_type));
obs_data_set_int(data, "port", _dev ? _dev->_port : -1);
obs_data_set_string(data, "portName", _dev ? _dev->_name.c_str() : "");
obs_data_set_obj(obj, "midiDevice", data);
obs_data_release(data);
}
void MidiDevice::Load(obs_data_t *obj)
{
auto data = obs_data_get_obj(obj, "midiDevice");
OBSDataAutoRelease data = obs_data_get_obj(obj, "midiDevice");
_type = static_cast<MidiDeviceType>(obs_data_get_int(data, "type"));
obs_data_set_default_int(data, "port", -1);
_port = obs_data_get_int(data, "port");
_dev = MidiDeviceInstance::GetDevice(_type, _port);
obs_data_release(data);
// TODO: Remove this fallback at some point
if (obs_data_has_user_value(data, "port")) {
auto port = obs_data_get_int(data, "port");
_dev = MidiDeviceInstance::GetDevice(_type, port);
if (_dev) {
_name = _dev->_name;
}
} else {
_name = obs_data_get_string(data, "portName");
_dev = MidiDeviceInstance::GetDevice(_type, _name);
}
}
bool MidiDevice::SendMessge(const MidiMessage &m)
{
if (_type == MidiDeviceType::INPUT || _port == -1 || !_dev) {
if (_type == MidiDeviceType::INPUT || _name.empty() || !_dev) {
return false;
}
return _dev->SendMessge(m);
}
static std::optional<libremidi::output_port>
getOutPortFromName(const std::string &name)
{
libremidi::observer obs;
for (const libremidi::output_port &port : obs.get_output_ports()) {
if (getNameFromPortInformation(port) == name) {
return port;
}
}
return {};
}
static std::optional<libremidi::input_port>
getInPortFromName(const std::string &name)
{
libremidi::observer obs;
for (const libremidi::input_port &port : obs.get_input_ports()) {
if (getNameFromPortInformation(port) == name) {
return port;
}
}
return {};
}
bool MidiDeviceInstance::OpenPort()
{
if ((_type == MidiDeviceType::INPUT && in.is_port_open()) ||
@@ -297,44 +398,61 @@ bool MidiDeviceInstance::OpenPort()
}
if (_type == MidiDeviceType::OUTPUT) {
auto port = getOutPortFromName(_name);
if (!port) {
blog(LOG_INFO,
"Could not find output port with name '%s'",
_name.c_str());
return false;
}
try {
out.open_port(_port);
blog(LOG_INFO, "Opened output midi port #%d", _port);
out.open_port(*port);
blog(LOG_INFO, "Opened output midi port '%s'",
_name.c_str());
return true;
} catch (const libremidi::driver_error &error) {
blog(LOG_WARNING,
"Failed to open output midi port #%d: %s", _port,
error.what());
"Failed to open output midi port '%s': %s",
_name.c_str(), error.what());
} catch (const libremidi::system_error &error) {
blog(LOG_WARNING,
"Failed to open output midi port #%d: %s", _port,
error.what());
"Failed to open output midi port '%s': %s",
_name.c_str(), error.what());
} catch (const libremidi::midi_exception &error) {
blog(LOG_WARNING,
"Failed to open output midi port #%d: %s", _port,
error.what());
"Failed to open output midi port '%s': %s",
_name.c_str(), error.what());
}
return false;
}
auto cb = [this](const libremidi::message &m) {
this->ReceiveMidiMessage(m);
auto cb = [this](libremidi::message &&m) {
ReceiveMidiMessage(std::move(m));
};
in.set_callback(cb);
in = libremidi::midi_in{
libremidi::input_configuration{.on_message = cb}};
auto port = getInPortFromName(_name);
if (!port) {
blog(LOG_INFO, "Could not find input port with name '%s'",
_name.c_str());
return false;
}
try {
in.open_port(_port);
blog(LOG_INFO, "Opened input midi port #%d", _port);
in.open_port(*port);
blog(LOG_INFO, "Opened input midi port '%s'", _name.c_str());
return true;
} catch (const libremidi::driver_error &error) {
blog(LOG_WARNING, "Failed to open input midi port #%d: %s",
_port, error.what());
blog(LOG_WARNING, "Failed to open input midi port '%s': %s",
_name.c_str(), error.what());
} catch (const libremidi::system_error &error) {
blog(LOG_WARNING, "Failed to open input midi port #%d: %s",
_port, error.what());
blog(LOG_WARNING, "Failed to open input midi port '%s': %s",
_name.c_str(), error.what());
} catch (const libremidi::midi_exception &error) {
blog(LOG_WARNING, "Failed to open input midi port #%d: %s",
_port, error.what());
blog(LOG_WARNING, "Failed to open input midi port '%s': %s",
_name.c_str(), error.what());
}
return false;
}
@@ -349,40 +467,36 @@ void MidiDeviceInstance::ClosePort()
if (_type == MidiDeviceType::OUTPUT) {
try {
out.close_port();
blog(LOG_INFO, "Closed output midi port #%d", _port);
blog(LOG_INFO, "Closed output midi port '%s'",
_name.c_str());
} catch (const libremidi::driver_error &error) {
blog(LOG_WARNING,
"Failed to close output midi port #%d: %s", _port,
error.what());
"Failed to close output midi port '%s': %s",
_name.c_str(), error.what());
} catch (const libremidi::system_error &error) {
blog(LOG_WARNING,
"Failed to close output midi port #%d: %s", _port,
error.what());
"Failed to close output midi port '%s': %s",
_name.c_str(), error.what());
} catch (const libremidi::midi_exception &error) {
blog(LOG_WARNING,
"Failed to close output midi port #%d: %s", _port,
error.what());
"Failed to close output midi port '%s': %s",
_name.c_str(), error.what());
}
return;
}
auto cb = [this](const libremidi::message &m) {
this->ReceiveMidiMessage(m);
};
in.set_callback(cb);
try {
in.close_port();
blog(LOG_INFO, "Closed input midi port #%d", _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 #%d: %s",
_port, error.what());
blog(LOG_WARNING, "Failed to close input midi port '%s': %s",
_name.c_str(), error.what());
} catch (const libremidi::system_error &error) {
blog(LOG_WARNING, "Failed to close input midi port #%d: %s",
_port, error.what());
blog(LOG_WARNING, "Failed to close input midi port '%s': %s",
_name.c_str(), error.what());
} catch (const libremidi::midi_exception &error) {
blog(LOG_WARNING, "Failed to close input midi port #%d: %s",
_port, error.what());
blog(LOG_WARNING, "Failed to close input midi port '%s': %s",
_name.c_str(), error.what());
}
}
@@ -395,31 +509,35 @@ bool MidiDeviceInstance::SendMessge(const MidiMessage &m)
switch (m.Type()) {
case libremidi::message_type::NOTE_OFF:
message = libremidi::message::note_off(channel, note, value);
message = libremidi::channel_events::note_off(channel, note,
value);
break;
case libremidi::message_type::NOTE_ON:
message = libremidi::message::note_on(channel, note, value);
break;
case libremidi::message_type::CONTROL_CHANGE:
message = libremidi::message::control_change(channel, note,
message = libremidi::channel_events::note_on(channel, note,
value);
break;
case libremidi::message_type::CONTROL_CHANGE:
message = libremidi::channel_events::control_change(
channel, note, value);
break;
case libremidi::message_type::PROGRAM_CHANGE:
message = libremidi::message::program_change(channel, value);
message = libremidi::channel_events::program_change(channel,
value);
break;
case libremidi::message_type::PITCH_BEND:
message = libremidi::message::pitch_bend(
message = libremidi::channel_events::pitch_bend(
channel, (value <= 64 ? 0 : value - 64) * 2, value);
break;
case libremidi::message_type::POLY_PRESSURE:
message =
libremidi::message::poly_pressure(channel, note, value);
message = libremidi::channel_events::poly_pressure(channel,
note, value);
break;
case libremidi::message_type::AFTERTOUCH:
message = libremidi::message::aftertouch(channel, value);
message = libremidi::channel_events::aftertouch(channel, value);
break;
default:
message = {libremidi::message::make_command(m.Type(), channel),
message = {libremidi::channel_events::make_command(m.Type(),
channel),
(unsigned char)note, (unsigned char)value};
blog(LOG_WARNING,
"sending midi message of non-default type \"%s\"",
@@ -442,7 +560,7 @@ const std::vector<MidiMessage> &MidiDeviceInstance::GetMessages()
return _messages;
}
void MidiDeviceInstance::ReceiveMidiMessage(const libremidi::message &msg)
void MidiDeviceInstance::ReceiveMidiMessage(libremidi::message &&msg)
{
auto lock = LockContext();
_messages.emplace_back(msg);
@@ -479,7 +597,7 @@ void MidiDevice::ClearMessageBuffer()
const std::vector<MidiMessage> *MidiDevice::GetMessages(bool ignoreSkip)
{
if (_type == MidiDeviceType::OUTPUT || _port == -1 || !_dev ||
if (_type == MidiDeviceType::OUTPUT || _name.empty() || !_dev ||
(_dev->_skipBufferClear && !ignoreSkip)) {
return nullptr;
}
@@ -487,81 +605,9 @@ const std::vector<MidiMessage> *MidiDevice::GetMessages(bool ignoreSkip)
return &_dev->GetMessages();
}
static QString portToName(bool input, int port)
{
std::string name;
try {
if (input) {
auto midiin = libremidi::midi_in();
name = midiin.get_port_name(port);
} else {
auto midiout = libremidi::midi_out();
name = midiout.get_port_name(port);
}
} catch (const libremidi::driver_error &error) {
blog(LOG_WARNING,
"Failed to get midi %s device name of port #%d: %s",
input ? "input" : "output", port, error.what());
}
const QString deviceNamePattern =
obs_module_text("AdvSceneSwitcher.midi.deviceNamePattern");
return deviceNamePattern.arg(QString::number(port),
QString::fromStdString(name));
}
std::string MidiDevice::GetInputName() const
{
return portToName(true, _port).toStdString();
}
std::string MidiDevice::GetOutputName() const
{
return portToName(false, _port).toStdString();
}
std::string MidiDevice::Name() const
{
if (_port < 0) {
return "";
}
if (_type == MidiDeviceType::INPUT) {
return GetInputName();
}
return GetOutputName();
}
static inline QStringList getInputDeviceNames()
{
QStringList devices;
try {
auto midiin = libremidi::midi_in();
auto nPorts = midiin.get_port_count();
for (unsigned i = 0; i < nPorts; i++) {
devices << portToName(true, i);
}
} catch (const libremidi::driver_error &error) {
blog(LOG_WARNING, "Failed to get midi input devices: %s",
error.what());
}
return devices;
}
static inline QStringList getOutputDeviceNames()
{
QStringList devices;
try {
auto midiout = libremidi::midi_out();
auto nPorts = midiout.get_port_count();
for (unsigned i = 0; i < nPorts; i++) {
devices << portToName(false, i);
}
} catch (const libremidi::driver_error &error) {
blog(LOG_WARNING, "Failed to get midi output devices: %s",
error.what());
}
return devices;
return _name;
}
MidiDeviceSelection::MidiDeviceSelection(QWidget *parent, MidiDeviceType t)
@@ -591,7 +637,8 @@ void MidiDeviceSelection::IdxChangedHelper(int idx)
emit DeviceSelectionChanged(MidiDevice());
}
auto devInstance = MidiDeviceInstance::GetDevice(_type, idx - 1);
auto name = currentText().toStdString();
auto devInstance = MidiDeviceInstance::GetDevice(_type, name);
if (!devInstance) {
DisplayMessage(obs_module_text(
"AdvSceneSwitcher.midi.deviceOpenFail"));
@@ -602,7 +649,7 @@ void MidiDeviceSelection::IdxChangedHelper(int idx)
MidiDevice dev;
dev._type = _type;
dev._port = idx - 1;
dev._name = name;
dev._dev = devInstance;
emit DeviceSelectionChanged(dev);
}
@@ -922,13 +969,18 @@ void MidiMessageSelection::ValueChanged(const NumberVariable<int> &v)
QStringList GetAllNotes()
{
QStringList result;
QStringList notes = {"C", "C#", "D", "D#", "E", "F",
"F#", "G", "G#", "A", "A#", "B"};
for (int octave = -1; octave <= 9; octave++) {
for (int noteIndex = 0; noteIndex < 12; noteIndex++) {
result << notes[noteIndex] + QString::number(octave);
static bool done = false;
static QStringList result;
static const QStringList notes = {"C", "C#", "D", "D#", "E", "F",
"F#", "G", "G#", "A", "A#", "B"};
if (!done) {
for (int octave = -1; octave <= 9; octave++) {
for (int noteIndex = 0; noteIndex < 12; noteIndex++) {
result << notes[noteIndex] +
QString::number(octave);
}
}
done = true;
}
return result;
}

View File

@@ -5,11 +5,8 @@
#include <QComboBox>
#include <obs-data.h>
#pragma warning(push)
#pragma warning(disable : 4005)
#define LIBREMIDI_HEADER_ONLY 1
#include <libremidi/libremidi.hpp>
#pragma warning(pop)
namespace advss {
@@ -60,6 +57,8 @@ enum class MidiDeviceType {
class MidiDeviceInstance {
public:
static MidiDeviceInstance *GetDevice(MidiDeviceType type,
const std::string &);
static MidiDeviceInstance *GetDevice(MidiDeviceType type, int port);
static void ClearMessageBuffersOfAllDevices();
static void ResetAllDevices();
@@ -71,18 +70,24 @@ private:
void ClosePort();
bool SendMessge(const MidiMessage &);
const std::vector<MidiMessage> &GetMessages();
void ReceiveMidiMessage(const libremidi::message &);
void ReceiveMidiMessage(libremidi::message &&);
void ClearMessageBuffer();
static std::map<std::pair<MidiDeviceType, int>, MidiDeviceInstance *>
static std::map<std::pair<MidiDeviceType, std::string>,
MidiDeviceInstance *>
devices;
bool _skipBufferClear = false;
MidiDeviceType _type = MidiDeviceType::INPUT;
int _port = -1;
libremidi::midi_in in;
libremidi::midi_out out;
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());
std::vector<MidiMessage> _messages;
friend class MidiDevice;
@@ -110,11 +115,8 @@ public:
bool DeviceSelected() { return !!_dev; }
private:
std::string GetInputName() const;
std::string GetOutputName() const;
MidiDeviceType _type = MidiDeviceType::INPUT;
int _port = -1;
std::string _name;
MidiDeviceInstance *_dev = nullptr;
friend class MidiDeviceSelection;

View File

@@ -0,0 +1,42 @@
diff --git a/include/libremidi/backends/coremidi/helpers.hpp b/include/libremidi/backends/coremidi/helpers.hpp
index 08e62ff..ba35ec7 100644
--- a/include/libremidi/backends/coremidi/helpers.hpp
+++ b/include/libremidi/backends/coremidi/helpers.hpp
@@ -207,7 +207,8 @@ inline CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint)
inline MIDIObjectRef
locate_object(auto& self, const port_information& info, MIDIObjectType requested_type)
{
- auto uid = std::bit_cast<std::int32_t>((uint32_t)info.port);
+ std::int32_t uid;
+ std::memcpy(&uid, &(info.port), sizeof(uint32_t));
MIDIObjectRef object{};
MIDIObjectType type{};
auto ret = MIDIObjectFindByUniqueID(uid, &object, &type);
diff --git a/include/libremidi/backends/coremidi/observer.hpp b/include/libremidi/backends/coremidi/observer.hpp
index defe623..12a936a 100644
--- a/include/libremidi/backends/coremidi/observer.hpp
+++ b/include/libremidi/backends/coremidi/observer.hpp
@@ -78,13 +78,16 @@ public:
if (!ok)
return {};
- return std::conditional_t<Input, input_port, output_port>{
- {.client = (std::uintptr_t)this->client,
- .port = std::bit_cast<uint32_t>(get_int_property(obj, kMIDIPropertyUniqueID)),
- .manufacturer = get_string_property(obj, kMIDIPropertyManufacturer),
- .device_name = get_string_property(obj, kMIDIPropertyModel),
- .port_name = get_string_property(obj, kMIDIPropertyName),
- .display_name = get_string_property(obj, kMIDIPropertyDisplayName)}};
+ std::conditional_t<Input, input_port, output_port> ret{
+ {.client = (std::uintptr_t)this->client,
+ .port = 0,
+ .manufacturer = get_string_property(obj, kMIDIPropertyManufacturer),
+ .device_name = get_string_property(obj, kMIDIPropertyModel),
+ .port_name = get_string_property(obj, kMIDIPropertyName),
+ .display_name = get_string_property(obj, kMIDIPropertyDisplayName)}};
+ auto prop = get_int_property(obj, kMIDIPropertyUniqueID);
+ std::memcpy(&ret.port, &(prop), sizeof(prop));
+ return ret;
}
std::vector<libremidi::input_port> get_input_ports() const noexcept override