4#include <rtmidi/RtMidi.h>
14 : m_config(
std::move(config))
29 "MIDIBackend already initialized");
35 "Initializing MIDI Backend (RtMidi version: {})",
get_version());
46 }
catch (
const RtMidiError& error) {
48 "RtMidi error during initialization: {}", error.getMessage());
57 "Cannot start MIDIBackend: not initialized");
63 "MIDIBackend already running");
67 std::vector<uint32_t> inputs_to_open;
72 inputs_to_open.push_back(
id);
77 for (uint32_t
id : inputs_to_open) {
96 state->active.store(
false);
99 state->midi_in->closePort();
100 }
catch (
const RtMidiError& error) {
102 "Error closing MIDI port {}: {}", state->info.name, error.getMessage());
111 "MIDIBackend stopped");
131 "MIDIBackend shutdown complete");
138 std::vector<InputDeviceInfo> result;
142 result.push_back(info);
154 std::vector<InputDeviceInfo> newly_added_devices;
158 unsigned int port_count = midi_in.getPortCount();
163 for (
unsigned int i = 0; i < port_count; ++i) {
164 std::string port_name = midi_in.getPortName(i);
174 info.name = port_name;
176 info.is_connected =
true;
177 info.is_input =
true;
178 info.is_output =
false;
179 info.port_number =
static_cast<uint8_t
>(i);
180 info.rtmidi_port_number = i;
187 newly_added_devices.push_back(info);
192 for (
const auto& info : newly_added_devices) {
194 "MIDI port found: {}", info.name);
202 }
catch (
const RtMidiError& error) {
204 "Error enumerating MIDI ports: {}", error.getMessage());
215 "MIDI port {} already open", device_id);
222 "MIDI port {} not found", device_id);
227 auto state = std::make_shared<MIDIPortState>();
228 state->info = it->second;
229 state->device_id = device_id;
231 state->midi_in = std::make_unique<RtMidiIn>();
233 state->midi_in->openPort(state->info.rtmidi_port_number, state->info.name);
235 state->midi_in->ignoreTypes(
false,
false,
false);
236 state->active.store(
true);
241 "Opened MIDI port {}: {}", device_id, state->info.name);
245 }
catch (
const RtMidiError& error) {
247 "Failed to open MIDI port {}: {}", it->second.name, error.getMessage());
261 it->second->active.store(
false);
262 if (it->second->midi_in) {
264 it->second->midi_in->closePort();
265 }
catch (
const RtMidiError& error) {
267 "Error closing MIDI port {}: {}", it->second->info.name, error.getMessage());
272 "Closed MIDI port {}: {}", device_id, it->second->info.name);
287 std::vector<uint32_t> result;
291 result.push_back(
id);
311 return std::string(RtMidi::getVersion());
325 [&port_name](
const std::string& filter) {
326 return port_name.find(filter) != std::string::npos;
333 if (info.rtmidi_port_number == rtmidi_port) {
353 info.is_connected =
true;
354 info.is_input =
true;
355 info.is_output =
false;
356 info.port_number = 255;
357 info.rtmidi_port_number = 0;
364 }
catch (
const RtMidiError& error) {
366 "Failed to create virtual MIDI port: {}", error.getMessage());
373 if (!state || !message || message->empty()) {
379 message->size() > 1 ? message->at(1) : 0,
380 message->size() > 2 ? message->at(2) : 0,
383 if (state->input_callback) {
384 state->input_callback(value);
398 if (message.empty()) {
402 uint8_t status = message[0];
403 uint8_t data1 = (message.size() > 1) ? message[1] : 0;
404 uint8_t data2 = (message.size() > 2) ? message[2] : 0;
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
uint32_t find_or_assign_device_id(unsigned int rtmidi_port)
void create_virtual_port_if_enabled()
std::string get_version() const override
Get backend version string.
std::unordered_map< uint32_t, MIDIPortInfo > m_enumerated_devices
bool is_device_open(uint32_t device_id) const override
Check if a device is currently open.
bool port_matches_filter(const std::string &port_name) const
std::atomic< bool > m_running
uint32_t m_next_device_id
void start() override
Start listening for input events.
InputCallback m_input_callback
size_t refresh_devices() override
Refresh the device list.
std::unordered_map< uint32_t, std::shared_ptr< MIDIPortState > > m_open_devices
void set_input_callback(InputCallback callback) override
Register callback for input values.
DeviceCallback m_device_callback
std::atomic< bool > m_initialized
static void rtmidi_callback(double timestamp, std::vector< unsigned char > *message, void *user_data)
std::vector< uint32_t > get_open_devices() const override
Get list of currently open device IDs.
bool initialize() override
Initialize the input backend.
std::vector< InputDeviceInfo > get_devices() const override
Get list of available devices.
void close_device(uint32_t device_id) override
Close a previously opened device.
void stop() override
Stop listening for input events.
InputValue parse_midi_message(uint32_t device_id, const std::vector< unsigned char > &message) const
std::mutex m_devices_mutex
void shutdown() override
Shutdown and release all resources.
void notify_device_change(const InputDeviceInfo &info, bool connected)
bool open_device(uint32_t device_id) override
Open a device for input.
std::mutex m_callback_mutex
void set_device_callback(DeviceCallback callback) override
Register callback for device connect/disconnect events.
RtMidi-based MIDI input backend.
@ MIDI
MIDI controllers and instruments.
std::function< void(const InputValue &)> InputCallback
Callback signature for input events.
std::function< void(const InputDeviceInfo &, bool connected)> DeviceCallback
Callback signature for device connection/disconnection events.
@ InputBackend
Input device backend (HID, MIDI, OSC)
@ Core
Core engine, backend, subsystems.
std::vector< std::string > input_port_filters
std::string virtual_port_name