MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
AudioSubsystem.cpp
Go to the documentation of this file.
1#include "AudioSubsystem.hpp"
2
4
5namespace MayaFlux::Core {
6
8 : m_stream_info(stream_info)
9 , m_audiobackend(AudioBackendFactory::create_backend(stream_info.backend, stream_info.requested_api))
10 , m_audio_device(m_audiobackend->create_device_manager())
11 , m_subsystem_tokens {
12 .Buffer = MayaFlux::Buffers::ProcessingToken::AUDIO_BACKEND,
13 .Node = MayaFlux::Nodes::ProcessingToken::AUDIO_RATE,
14 .Task = MayaFlux::Vruta::ProcessingToken::SAMPLE_ACCURATE
15 }
16{
17}
18
20{
21 m_handle = &handle;
22
23 m_audio_stream = m_audiobackend->create_stream(
24 m_audio_device->get_default_output_device(),
25 m_audio_device->get_default_input_device(),
27 this);
28
29 m_is_ready = true;
30}
31
33{
34 if (!m_is_ready || !m_audio_stream) {
35 error<std::runtime_error>(
38 std::source_location::current(),
39 "AudioSubsystem not initialized");
40 }
41
42 m_audio_stream->set_process_callback(
43 [this](void* output_buffer, void* input_buffer, unsigned int num_frames) -> int {
44 auto input_ptr = static_cast<double*>(input_buffer);
45 auto output_ptr = static_cast<double*>(output_buffer);
46
47 if (input_ptr && output_ptr) {
48 return this->process_audio(input_ptr, output_ptr, num_frames);
49 }
50
51 if (output_ptr) {
52 return this->process_output(output_ptr, num_frames);
53 }
54
55 if (input_ptr) {
56 return this->process_input(input_ptr, num_frames);
57 }
58 return 0;
59 });
60}
61
62int AudioSubsystem::process_output(double* output_buffer, unsigned int num_frames)
63{
64 m_callback_active.fetch_add(1, std::memory_order_acquire);
65
66 if (output_buffer == nullptr) {
68 "No output available");
69 m_callback_active.fetch_sub(1, std::memory_order_release);
70 return 1;
71 }
72
73 if (!m_is_running.load(std::memory_order_acquire)) {
74 if (output_buffer) {
75 auto total_samples = static_cast<uint32_t>(num_frames * m_stream_info.output.channels);
76 std::memset(output_buffer, 0, total_samples * sizeof(double));
77 }
78 m_callback_active.fetch_sub(1, std::memory_order_release);
79 return 0;
80 }
81
82 if (m_handle == nullptr) {
84 "Invalid processing handle");
85 m_callback_active.fetch_sub(1, std::memory_order_release);
86 return 1;
87 }
88
89 try {
90 uint32_t num_channels = m_stream_info.output.channels;
91 size_t total_samples = static_cast<size_t>(num_frames) * num_channels;
92 std::span<double> output_span(output_buffer, total_samples);
93
96
97 std::vector<std::span<const double>> buffer_data(num_channels);
98 std::vector<std::vector<std::vector<double>>> all_network_outputs(num_channels);
99 bool has_underrun = false;
100
102
103 for (uint32_t channel = 0; channel < num_channels; channel++) {
104 m_handle->buffers.process_channel(channel, num_frames);
105 all_network_outputs[channel] = m_handle->nodes.process_audio_networks(num_frames, channel);
106
107 auto channel_data = m_handle->buffers.read_channel_data(channel);
108
109 if (channel_data.size() < num_frames) {
111 "Channel buffer underrun");
112 has_underrun = true;
113
114 buffer_data[channel] = std::span<const double>();
115 } else {
116 buffer_data[channel] = channel_data;
117 }
118 }
119
120 for (size_t i = 0; i < num_frames; ++i) {
121
123
124 for (size_t j = 0; j < num_channels; ++j) {
125 double buffer_sample = 0.0;
126 if (!buffer_data[j].empty() && i < buffer_data[j].size()) {
127 buffer_sample = buffer_data[j][i];
128 }
129
130 double sample = m_handle->nodes.process_sample(j) + buffer_sample;
131
132 for (const auto& network_buffer : all_network_outputs[j]) {
133 if (i < network_buffer.size()) {
134 sample += network_buffer[i];
135 }
136 }
137
138 size_t index = i * num_channels + j;
139 output_span[index] = std::clamp(sample, -1., 1.);
140 }
141 }
142
145
146 m_callback_active.fetch_sub(1, std::memory_order_release);
147 return has_underrun ? 1 : 0;
148
149 } catch (const std::exception& e) {
151 "Exception during audio output processing: {}", e.what());
152
153 if (output_buffer) {
154 auto total_samples = static_cast<uint32_t>(num_frames * m_stream_info.output.channels);
155 std::memset(output_buffer, 0, total_samples * sizeof(double));
156 }
157
158 m_callback_active.fetch_sub(1, std::memory_order_release);
159 return 1;
160 }
161}
162
163int AudioSubsystem::process_input(double* input_buffer, unsigned int num_frames)
164{
165 m_callback_active.fetch_add(1, std::memory_order_acquire);
166 if (m_handle == nullptr) {
168 "Invalid processing handle");
169 m_callback_active.fetch_sub(1, std::memory_order_release);
170 return 1;
171 }
172
173 if (!m_is_running.load(std::memory_order_acquire)) {
174 if (input_buffer) {
175 auto total_samples = static_cast<uint32_t>(num_frames * m_stream_info.input.channels);
176 std::memset(input_buffer, 0, total_samples * sizeof(double));
177 }
178 m_callback_active.fetch_sub(1, std::memory_order_release);
179 return 0;
180 }
181
182 m_handle->buffers.process_input(input_buffer, m_stream_info.input.channels, num_frames);
183
184 m_callback_active.fetch_sub(1, std::memory_order_release);
185 return 0;
186}
187
188int AudioSubsystem::process_audio(double* input_buffer, double* output_buffer, unsigned int num_frames)
189{
190 for (const auto& [name, hook] : m_handle->pre_process_hooks) {
191 hook(num_frames);
192 }
193
194 process_input(input_buffer, num_frames);
195 process_output(output_buffer, num_frames);
196
197 for (const auto& [name, hook] : m_handle->post_process_hooks) {
198 hook(num_frames);
199 }
200
201 return 0;
202}
203
205{
206 if (!m_is_ready || !m_audio_stream) {
207 error<std::runtime_error>(
210 std::source_location::current(),
211 "Cannot start AudioSubsystem: not initialized");
212 }
213
214 m_audio_stream->open();
215 m_audio_stream->start();
216 m_is_running.store(true);
217}
218
220{
221 if (!m_is_running.load()) {
222 return;
223 }
224
226 "Stopping AudioSubsystem...");
227
228 m_is_running.store(false, std::memory_order_release);
229
230 if (m_audio_stream && m_audio_stream->is_running()) {
231 m_audio_stream->stop();
232 }
233
234 if (m_callback_active.load() > 0) {
235 MF_INFO(Journal::Component::Core, Journal::Context::AudioSubsystem, "Stopped while {} callback(s) active",
236 m_callback_active.load());
237 }
238
240 "AudioSubsystem stopped");
241}
242
244{
245 if (m_audio_stream && m_is_running.load()) {
246 m_audio_stream->stop();
247 m_is_paused = true;
248 }
249}
250
252{
254 m_audio_stream->start();
255 m_is_paused = false;
256 }
257}
258
260{
261 stop();
262 if (m_audio_stream) {
263 m_audio_stream->close();
264 }
265 m_audio_stream.reset();
266 m_audio_device.reset();
267 m_audiobackend->cleanup();
268 m_audiobackend.reset();
269 m_is_ready = false;
270}
271
272}
#define MF_INFO(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
Factory pattern implementation for audio backend instantiation.
void initialize(SubsystemProcessingHandle &handle) override
Initialize audio processing with provided handle.
bool m_is_ready
Subsystem ready state.
void stop() override
Stop audio processing and streaming.
void shutdown() override
Shutdown and cleanup audio resources.
std::unique_ptr< IAudioBackend > m_audiobackend
Audio backend implementation.
int process_output(double *output_buffer, unsigned int num_frames)
Processes output data for audio interface.
std::atomic< int > m_callback_active
Active callback counter.
int process_audio(double *input_buffer, double *output_buffer, unsigned int num_frames)
Processes both input and output data in full-duplex mode.
void pause() override
Pause audio processing without stopping the stream.
std::atomic< bool > m_is_running
Subsystem running state.
void start() override
Start audio processing and streaming.
GlobalStreamInfo m_stream_info
Audio stream configuration.
SubsystemProcessingHandle * m_handle
Reference to processing handle.
bool m_is_paused
Subsystem paused state.
int process_input(double *input_buffer, unsigned int num_frames)
Processes input data from audio interface.
AudioSubsystem(GlobalStreamInfo &stream_info)
Constructs AudioSubsystem with stream configuration.
void resume() override
Resume audio processing after pause.
std::unique_ptr< AudioDevice > m_audio_device
Audio device manager.
std::unique_ptr< AudioStream > m_audio_stream
Audio stream manager.
void register_callbacks() override
Register audio backend callbacks for real-time processing.
std::span< const double > read_channel_data(uint32_t channel) const
Get read-only access to channel data.
void process_channel(uint32_t channel, uint32_t processing_units)
Process specific channel.
void process_input(double *input_data, uint32_t num_channels, uint32_t num_frames)
@brienf Process Input from backend into buffer manager
std::vector< std::vector< double > > process_audio_networks(uint32_t num_samples, uint32_t channel=0)
std::map< std::string, ProcessHook > post_process_hooks
NodeProcessingHandle nodes
Node processing interface.
std::map< std::string, ProcessHook > pre_process_hooks
BufferProcessingHandle buffers
Buffer processing interface.
Unified interface combining buffer and node processing for subsystems.
void process_buffer_cycle()
Process all tasks scheduled for current buffer cycle.
void process(uint64_t processing_units)
Process all tasks in token domain.
@ AudioSubsystem
Audio subsystem operations (backend, device, stream management)
@ AudioCallback
Audio callback thread - strictest real-time requirements.
@ Core
Core engine, backend, subsystems.
Main namespace for the Maya Flux audio engine.
Definition LiveAid.hpp:6
uint32_t channels
Number of discrete channels in this set.
ChannelConfig input
Configuration for input signal channels (disabled by default)
ChannelConfig output
Configuration for output signal channels.
Comprehensive configuration for digital audio stream processing.