MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
WinMmMidiBackend.hpp
Go to the documentation of this file.
1#pragma once
2
3#ifdef MAYAFLUX_PLATFORM_WINDOWS
4
5#include "InputBackend.hpp"
6
7#ifndef WIN32_LEAN_AND_MEAN
8#define WIN32_LEAN_AND_MEAN
9#endif
10#ifndef NOMINMAX
11#define NOMINMAX
12#endif
13#include <windows.h>
14#include <mmsystem.h>
15
16namespace MayaFlux::Core {
17
18/**
19 * @class WinMmMidiBackend
20 * @brief WinMM-native MIDI input backend for Windows
21 *
22 * Wraps midiIn* API directly. Zero additional dependencies beyond winmm.lib.
23 * Compatible with Windows 10 and later. On Windows 11 24H2+ with Windows MIDI
24 * Services installed, WinMM calls are automatically routed through the new
25 * service, gaining multi-client support without any code changes.
26 *
27 * Threading model:
28 * - midiInOpen uses CALLBACK_FUNCTION; Windows calls midi_callback on a
29 * dedicated MM thread per open device
30 * - Callbacks push InputValue to InputManager's queue (thread-safe)
31 * - No polling thread needed
32 * - SysEx uses double-buffered MIDIHDR; buffers are re-queued from the callback
33 */
34class MAYAFLUX_API WinMmMidiBackend : public IInputBackend {
35public:
36 struct Config {
37 std::vector<std::string> input_port_filters;
38 bool auto_open_inputs { true };
39 bool enable_sysex { true };
40 std::string virtual_port_name { "MayaFlux" };
41 };
42
43 WinMmMidiBackend();
44 explicit WinMmMidiBackend(Config config);
45 ~WinMmMidiBackend() override;
46
47 WinMmMidiBackend(const WinMmMidiBackend&) = delete;
48 WinMmMidiBackend& operator=(const WinMmMidiBackend&) = delete;
49 WinMmMidiBackend(WinMmMidiBackend&&) = delete;
50 WinMmMidiBackend& operator=(WinMmMidiBackend&&) = delete;
51
52 bool initialize() override;
53 void start() override;
54 void stop() override;
55 void shutdown() override;
56
57 [[nodiscard]] bool is_initialized() const override { return m_initialized.load(); }
58 [[nodiscard]] bool is_running() const override { return m_running.load(); }
59
60 [[nodiscard]] std::vector<InputDeviceInfo> get_devices() const override;
61 size_t refresh_devices() override;
62
63 bool open_device(uint32_t device_id) override;
64 void close_device(uint32_t device_id) override;
65 [[nodiscard]] bool is_device_open(uint32_t device_id) const override;
66 [[nodiscard]] std::vector<uint32_t> get_open_devices() const override;
67
68 void set_input_callback(InputCallback callback) override;
69 void set_device_callback(DeviceCallback callback) override;
70
71 [[nodiscard]] InputType get_type() const override { return InputType::MIDI; }
72 [[nodiscard]] std::string get_name() const override { return "MIDI (WinMM)"; }
73 [[nodiscard]] std::string get_version() const override;
74
75private:
76 static constexpr size_t k_sysex_buf_size { 256 };
77 static constexpr size_t k_sysex_buf_count { 2 };
78
79 struct MIDIPortInfo : InputDeviceInfo {
80 UINT winmm_device_id { 0 };
81 };
82
83 struct MIDIPortState {
84 MIDIPortInfo info;
85 uint32_t device_id { 0 };
86 HMIDIIN handle { nullptr };
87 std::atomic<bool> active { false };
88 std::function<void(const InputValue&)> input_callback;
89 MIDIHDR sysex_headers[k_sysex_buf_count] {};
90 std::array<std::vector<uint8_t>, k_sysex_buf_count> sysex_bufs;
91 };
92
93 Config m_config;
94 std::atomic<bool> m_initialized { false };
95 std::atomic<bool> m_running { false };
96
97 mutable std::mutex m_devices_mutex;
98 std::unordered_map<uint32_t, MIDIPortInfo> m_enumerated_devices;
99 std::unordered_map<uint32_t, std::shared_ptr<MIDIPortState>> m_open_devices;
100 uint32_t m_next_device_id { 1 };
101
102 InputCallback m_input_callback;
103 DeviceCallback m_device_callback;
104 mutable std::mutex m_callback_mutex;
105
106 bool port_matches_filter(const std::string& name) const;
107 uint32_t find_or_assign_device_id(UINT winmm_id);
108 void queue_sysex_buffers(MIDIPortState& state);
109 void notify_device_change(const InputDeviceInfo& info, bool connected);
110
111 static void CALLBACK midi_callback(HMIDIIN handle, UINT msg,
112 DWORD_PTR user_data, DWORD_PTR param1, DWORD_PTR param2);
113};
114
115} // namespace MayaFlux::Core
116
117#endif // MAYAFLUX_PLATFORM_WINDOWS
void initialize()
Definition main.cpp:11
InputType
Input backend type enumeration.
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.
@ CALLBACK
Use callback for custom transition.
void shutdown()
Release stored references.
Definition Forma.cpp:168
void stop()
Stop all Portal::Graphics operations.
Definition Graphics.cpp:69
bool is_initialized()
Checks if the default engine has been initialized.
Definition Core.cpp:52