MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
WasapiBackend.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "AudioBackend.hpp"
4
5#ifdef WASAPI_BACKEND
6
7#ifndef WIN32_LEAN_AND_MEAN
8#define WIN32_LEAN_AND_MEAN
9#endif
10#ifndef NOMINMAX
11#define NOMINMAX
12#endif
13
14#include <audioclient.h>
15#include <initguid.h>
16#include <mmdeviceapi.h>
17#include <propsys.h>
18#include <windows.h>
19
20namespace MayaFlux::Core {
21
22/**
23 * @class WasapiBackend
24 * @brief Native WASAPI implementation of the audio backend interface.
25 *
26 * Header-only Windows SDK COM interfaces; no external DLL dependencies beyond
27 * those already present in every Windows installation. Owns the
28 * IMMDeviceEnumerator lifetime for the process. Shared mode only.
29 */
30class WasapiBackend : public IAudioBackend {
31public:
32 WasapiBackend();
33 ~WasapiBackend() override;
34
35 std::unique_ptr<AudioDevice> create_device_manager() override;
36
37 std::unique_ptr<AudioStream> create_stream(
38 unsigned int output_device_id,
39 unsigned int input_device_id,
40 GlobalStreamInfo& stream_info,
41 void* user_data) override;
42
43 [[nodiscard]] std::string get_version_string() const override;
44
45 void cleanup() override;
46
47 [[nodiscard]] IMMDeviceEnumerator* enumerator() const { return m_enumerator; }
48
49private:
50 IMMDeviceEnumerator* m_enumerator = nullptr;
51};
52
53/**
54 * @class WasapiDevice
55 * @brief WASAPI implementation of the audio device interface.
56 *
57 * Enumerates active render and capture endpoints via IMMDeviceCollection.
58 * Device IDs are indices into the enumerated collections, offset by 1 so
59 * that 0 maps to the system default endpoint. This matches the convention
60 * used by RtAudioDevice and is consumed by WasapiStream::open().
61 */
62class WasapiDevice : public AudioDevice {
63public:
64 explicit WasapiDevice(IMMDeviceEnumerator* enumerator);
65 ~WasapiDevice() override;
66
67 [[nodiscard]] std::vector<DeviceInfo> get_output_devices() const override;
68 [[nodiscard]] std::vector<DeviceInfo> get_input_devices() const override;
69 [[nodiscard]] unsigned int get_default_output_device() const override;
70 [[nodiscard]] unsigned int get_default_input_device() const override;
71
72 /**
73 * @brief Resolves an IMMDevice* from an engine device ID.
74 * @param id Engine device ID (0 = default).
75 * @param flow EDataFlow::eRender or EDataFlow::eCapture.
76 * @return Activated IMMDevice pointer; caller must Release().
77 */
78 [[nodiscard]] IMMDevice* resolve_device(unsigned int id, EDataFlow flow) const;
79
80private:
81 struct EndpointEntry {
82 DeviceInfo info;
83 std::wstring endpoint_id;
84 };
85
86 void enumerate(IMMDeviceEnumerator* enumerator, EDataFlow flow,
87 std::vector<EndpointEntry>& out, unsigned int& default_idx);
88
89 IMMDeviceEnumerator* m_enumerator = nullptr;
90
91 std::vector<EndpointEntry> m_outputs;
92 std::vector<EndpointEntry> m_inputs;
93
94 unsigned int m_default_output = 0;
95 unsigned int m_default_input = 0;
96};
97
98/**
99 * @class WasapiStream
100 * @brief WASAPI shared-mode audio stream.
101 *
102 * Opens an IAudioClient in shared mode, negotiates WAVEFORMATEXTENSIBLE for
103 * IEEE_FLOAT with the engine's requested sample rate and channel count, then
104 * drives the render/capture loop on a dedicated high-priority thread. The
105 * thread blocks on an event handle supplied to WASAPI (SetEventHandle path),
106 * waking exactly once per buffer period with no busy-wait.
107 *
108 * The internal loop converts between WASAPI's native float32 interleaved
109 * layout and the engine's float64 interleaved layout in the hot path.
110 * Format negotiation falls back to the mix format reported by WASAPI if the
111 * requested sample rate is not supported in shared mode.
112 *
113 * Input capture is implemented when stream_info.input.enabled is true and
114 * input_device_id resolves to a valid capture endpoint.
115 */
116class WasapiStream : public AudioStream {
117public:
118 WasapiStream(
119 IMMDevice* output_device,
120 IMMDevice* input_device,
121 GlobalStreamInfo& stream_info,
122 void* user_data);
123
124 ~WasapiStream() override;
125
126 void open() override;
127 void start() override;
128 void stop() override;
129 void pause() override;
130 void resume() override;
131 void close() override;
132
133 [[nodiscard]] bool is_running() const override;
134 [[nodiscard]] bool is_open() const override;
135
136 void set_process_callback(
137 std::function<int(void*, void*, unsigned int)> cb) override;
138
139private:
140 static DWORD WINAPI render_thread_proc(LPVOID param);
141 void render_loop();
142
143 /**
144 * @brief Negotiates a WAVEFORMATEXTENSIBLE for shared-mode float32.
145 * @param client IAudioClient to negotiate against.
146 * @param channels Requested channel count.
147 * @param sample_rate Requested sample rate.
148 * @param[out] out_fmt Populated on success; caller frees with CoTaskMemFree.
149 * @return true if negotiation succeeded.
150 */
151 bool negotiate_format(
152 IAudioClient* client,
153 uint32_t channels,
154 uint32_t sample_rate,
155 WAVEFORMATEX** out_fmt);
156
157 IMMDevice* m_output_device = nullptr;
158 IMMDevice* m_input_device = nullptr;
159
160 IAudioClient* m_render_client = nullptr;
161 IAudioRenderClient* m_render_sink = nullptr;
162 IAudioClient* m_capture_client = nullptr;
163 IAudioCaptureClient* m_capture_src = nullptr;
164
165 HANDLE m_render_event = nullptr;
166 HANDLE m_capture_event = nullptr;
167 HANDLE m_stop_event = nullptr;
168
169 HANDLE m_render_thread = nullptr;
170
171 uint32_t m_render_buffer_frames = 0;
172 uint32_t m_capture_buffer_frames = 0;
173
174 uint32_t m_negotiated_output_rate = 0;
175 uint32_t m_negotiated_input_rate = 0;
176 uint32_t m_negotiated_output_channels = 0;
177 uint32_t m_negotiated_input_channels = 0;
178
179 std::vector<float> m_render_staging;
180 std::vector<double> m_output_staging;
181 std::vector<float> m_capture_staging;
182 std::vector<double> m_input_staging;
183 std::atomic<bool> m_input_ready { false };
184
185 std::vector<double> m_render_ring;
186 uint32_t m_ring_write_pos { 0 };
187 uint32_t m_ring_read_pos { 0 };
188 uint32_t m_ring_frames { 0 };
189 std::vector<double> m_capture_ring;
190 uint32_t m_cap_ring_write_pos { 0 };
191 uint32_t m_cap_ring_read_pos { 0 };
192 uint32_t m_cap_ring_frames { 0 };
193
194 GlobalStreamInfo& m_stream_info;
195 void* m_user_data = nullptr;
196
197 std::function<int(void*, void*, unsigned int)> m_process_callback;
198
199 std::atomic<bool> m_is_open { false };
200 std::atomic<bool> m_is_running { false };
201 std::atomic<bool> m_is_paused { false };
202};
203
204} // namespace MayaFlux::Core
205
206#endif // WASAPI_BACKEND
void stop()
Stop all Portal::Graphics operations.
Definition Graphics.cpp:69