MayaFlux 0.2.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(backend_type))
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
94 std::vector<std::span<const double>> buffer_data(num_channels);
95 std::vector<std::vector<std::vector<double>>> all_network_outputs(num_channels);
96 bool has_underrun = false;
97
99
100 for (uint32_t channel = 0; channel < num_channels; channel++) {
101 m_handle->buffers.process_channel(channel, num_frames);
102 all_network_outputs[channel] = m_handle->nodes.process_audio_networks(num_frames, channel);
103
104 auto channel_data = m_handle->buffers.read_channel_data(channel);
105
106 if (channel_data.size() < num_frames) {
108 "Channel buffer underrun");
109 has_underrun = true;
110
111 buffer_data[channel] = std::span<const double>();
112 } else {
113 buffer_data[channel] = channel_data;
114 }
115 }
116
117 for (size_t i = 0; i < num_frames; ++i) {
118
120
121 for (size_t j = 0; j < num_channels; ++j) {
122 double buffer_sample = 0.0;
123 if (!buffer_data[j].empty() && i < buffer_data[j].size()) {
124 buffer_sample = buffer_data[j][i];
125 }
126
127 double sample = m_handle->nodes.process_sample(j) + buffer_sample;
128
129 for (const auto& network_buffer : all_network_outputs[j]) {
130 if (i < network_buffer.size()) {
131 sample += network_buffer[i];
132 }
133 }
134
135 size_t index = i * num_channels + j;
136 output_span[index] = std::clamp(sample, -1., 1.);
137 }
138 }
139
140 m_callback_active.fetch_sub(1, std::memory_order_release);
141 return has_underrun ? 1 : 0;
142
143 } catch (const std::exception& e) {
145 "Exception during audio output processing: {}", e.what());
146
147 if (output_buffer) {
148 auto total_samples = static_cast<uint32_t>(num_frames * m_stream_info.output.channels);
149 std::memset(output_buffer, 0, total_samples * sizeof(double));
150 }
151
152 m_callback_active.fetch_sub(1, std::memory_order_release);
153 return 1;
154 }
155}
156
157int AudioSubsystem::process_input(double* input_buffer, unsigned int num_frames)
158{
159 m_callback_active.fetch_add(1, std::memory_order_acquire);
160 if (m_handle == nullptr) {
162 "Invalid processing handle");
163 m_callback_active.fetch_sub(1, std::memory_order_release);
164 return 1;
165 }
166
167 if (!m_is_running.load(std::memory_order_acquire)) {
168 if (input_buffer) {
169 auto total_samples = static_cast<uint32_t>(num_frames * m_stream_info.input.channels);
170 std::memset(input_buffer, 0, total_samples * sizeof(double));
171 }
172 m_callback_active.fetch_sub(1, std::memory_order_release);
173 return 0;
174 }
175
176 m_handle->buffers.process_input(input_buffer, m_stream_info.input.channels, num_frames);
177
178 m_callback_active.fetch_sub(1, std::memory_order_release);
179 return 0;
180}
181
182int AudioSubsystem::process_audio(double* input_buffer, double* output_buffer, unsigned int num_frames)
183{
184 for (const auto& [name, hook] : m_handle->pre_process_hooks) {
185 hook(num_frames);
186 }
187
188 process_input(input_buffer, num_frames);
189 process_output(output_buffer, num_frames);
190
191 for (const auto& [name, hook] : m_handle->post_process_hooks) {
192 hook(num_frames);
193 }
194
195 return 0;
196}
197
199{
200 if (!m_is_ready || !m_audio_stream) {
201 error<std::runtime_error>(
204 std::source_location::current(),
205 "Cannot start AudioSubsystem: not initialized");
206 }
207
208 m_audio_stream->open();
209 m_audio_stream->start();
210 m_is_running.store(true);
211}
212
214{
215 if (!m_is_running.load()) {
216 return;
217 }
218
220 "Stopping AudioSubsystem...");
221
222 m_is_running.store(false, std::memory_order_release);
223
224 if (m_audio_stream && m_audio_stream->is_running()) {
225 m_audio_stream->stop();
226 }
227
228 if (m_callback_active.load() > 0) {
229 MF_INFO(Journal::Component::Core, Journal::Context::AudioSubsystem, "Stopped while {} callback(s) active",
230 m_callback_active.load());
231 }
232
234 "AudioSubsystem stopped");
235}
236
238{
239 if (m_audio_stream && m_is_running.load()) {
240 m_audio_stream->stop();
241 m_is_paused = true;
242 }
243}
244
246{
248 m_audio_stream->start();
249 m_is_paused = false;
250 }
251}
252
254{
255 stop();
256 if (m_audio_stream) {
257 m_audio_stream->close();
258 }
259 m_audio_stream.reset();
260 m_audio_device.reset();
261 m_audiobackend->cleanup();
262 m_audiobackend.reset();
263 m_is_ready = false;
264}
265
266}
#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.
AudioSubsystem(GlobalStreamInfo &stream_info, Utils::AudioBackendType backend_type=Utils::AudioBackendType::RTAUDIO)
Constructs AudioSubsystem with stream configuration.
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.
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.