1use std::sync::{Arc, Mutex, MutexGuard};
12
13use crate::{
14 audio::{AudioBackend, AudioBuffers, AudioConfig, AudioStream},
15 error::{Error, Result},
16 plugin::Plugin,
17 realtime::{RealtimePluginRunner, RtControl},
18};
19
20pub struct AudioHandle {
26 _stream: Box<dyn AudioStream>,
29 _input_stream: Option<Box<dyn AudioStream>>,
32 plugin: Arc<Mutex<Plugin>>,
33}
34
35impl AudioHandle {
36 pub fn lock(&self) -> MutexGuard<'_, Plugin> {
41 self.plugin
42 .lock()
43 .unwrap_or_else(|poisoned| poisoned.into_inner())
44 }
45
46 pub fn plugin(&self) -> Arc<Mutex<Plugin>> {
48 Arc::clone(&self.plugin)
49 }
50
51 pub fn stop(self) {}
53}
54
55pub(crate) fn interleave_outputs(outputs: &[Vec<f32>], out: &mut [f32], channels: usize) {
62 if channels == 0 {
63 return;
64 }
65 let frames = out.len() / channels;
66 for ch in 0..channels.min(outputs.len()) {
67 let src = &outputs[ch];
68 for frame in 0..frames.min(src.len()) {
69 out[frame * channels + ch] = src[frame];
70 }
71 }
72}
73
74fn prepare_scratch(scratch: &mut AudioBuffers, frames: usize) {
76 for ch in &mut scratch.outputs {
77 if ch.len() != frames {
78 ch.resize(frames, 0.0);
79 }
80 ch.fill(0.0);
81 }
82 for ch in &mut scratch.inputs {
83 if ch.len() != frames {
84 ch.resize(frames, 0.0);
85 }
86 ch.fill(0.0);
87 }
88 scratch.block_size = frames;
89}
90
91pub fn play_with_backend<B: AudioBackend>(
100 backend: &B,
101 plugin: Plugin,
102 config: AudioConfig,
103) -> Result<AudioHandle> {
104 let device = backend
105 .default_output_device()
106 .ok_or_else(|| Error::AudioBackendError("No default output device available".into()))?;
107
108 let channels = config.output_channels;
109 let sample_rate = config.sample_rate;
110
111 let plugin = Arc::new(Mutex::new(plugin));
112 plugin
114 .lock()
115 .unwrap_or_else(|p| p.into_inner())
116 .start_processing()?;
117
118 let plugin_cb = Arc::clone(&plugin);
119 let mut scratch = AudioBuffers::new(0, channels, config.block_size, sample_rate);
121
122 let data_cb = Box::new(move |data: &mut [f32]| {
123 data.fill(0.0);
125 if channels == 0 {
126 return;
127 }
128 let frames = data.len() / channels;
129 prepare_scratch(&mut scratch, frames);
130
131 if let Ok(mut p) = plugin_cb.lock() {
132 if p.process_audio(&mut scratch).is_ok() {
133 interleave_outputs(&scratch.outputs, data, channels);
134 }
135 }
136 });
137
138 let err_cb = Box::new(|e: B::Error| {
139 log::error!("audio stream error: {}", e);
140 });
141
142 let stream = backend
143 .create_output_stream(&device, config, data_cb, err_cb)
144 .map_err(|e| Error::AudioBackendError(format!("Failed to create output stream: {}", e)))?;
145
146 stream
147 .play()
148 .map_err(|e| Error::AudioBackendError(format!("Failed to start stream: {}", e)))?;
149
150 Ok(AudioHandle {
151 _stream: Box::new(stream),
152 _input_stream: None,
153 plugin,
154 })
155}
156
157pub fn play_with_input_backend<B: AudioBackend>(
169 backend: &B,
170 plugin: Plugin,
171 config: AudioConfig,
172) -> Result<AudioHandle> {
173 let in_device = backend
174 .default_input_device()
175 .ok_or_else(|| Error::AudioBackendError("No default input device available".into()))?;
176 let out_device = backend
177 .default_output_device()
178 .ok_or_else(|| Error::AudioBackendError("No default output device available".into()))?;
179
180 let in_channels = config.input_channels.max(1);
181 let out_channels = config.output_channels;
182 let sample_rate = config.sample_rate;
183
184 let plugin = Arc::new(Mutex::new(plugin));
185 plugin
186 .lock()
187 .unwrap_or_else(|p| p.into_inner())
188 .start_processing()?;
189
190 let ring_cap = (config.block_size * in_channels * 8).max(2048);
193 let (mut producer, mut consumer) = rtrb::RingBuffer::<f32>::new(ring_cap);
194
195 let in_data_cb = Box::new(move |data: &[f32]| {
196 for &s in data {
198 let _ = producer.push(s);
199 }
200 });
201 let in_err_cb = Box::new(|e: B::Error| log::error!("input stream error: {}", e));
202 let input_stream = backend
203 .create_input_stream(&in_device, config, in_data_cb, in_err_cb)
204 .map_err(|e| Error::AudioBackendError(format!("Failed to create input stream: {}", e)))?;
205
206 let plugin_cb = Arc::clone(&plugin);
207 let mut scratch = AudioBuffers::new(in_channels, out_channels, config.block_size, sample_rate);
208 let out_data_cb = Box::new(move |data: &mut [f32]| {
209 data.fill(0.0);
210 if out_channels == 0 {
211 return;
212 }
213 let frames = data.len() / out_channels;
214 prepare_scratch(&mut scratch, frames);
215 for f in 0..frames {
218 for ch in scratch.inputs.iter_mut() {
219 ch[f] = consumer.pop().unwrap_or(0.0);
220 }
221 }
222 if let Ok(mut p) = plugin_cb.lock() {
223 if p.process_audio(&mut scratch).is_ok() {
224 interleave_outputs(&scratch.outputs, data, out_channels);
225 }
226 }
227 });
228 let out_err_cb = Box::new(|e: B::Error| log::error!("output stream error: {}", e));
229 let output_stream = backend
230 .create_output_stream(&out_device, config, out_data_cb, out_err_cb)
231 .map_err(|e| Error::AudioBackendError(format!("Failed to create output stream: {}", e)))?;
232
233 input_stream
234 .play()
235 .map_err(|e| Error::AudioBackendError(format!("Failed to start input stream: {}", e)))?;
236 output_stream
237 .play()
238 .map_err(|e| Error::AudioBackendError(format!("Failed to start output stream: {}", e)))?;
239
240 Ok(AudioHandle {
241 _stream: Box::new(output_stream),
242 _input_stream: Some(Box::new(input_stream)),
243 plugin,
244 })
245}
246
247pub struct RtAudioHandle {
251 _stream: Box<dyn AudioStream>,
252 control: RtControl,
253}
254
255impl RtAudioHandle {
256 pub fn control(&mut self) -> &mut RtControl {
259 &mut self.control
260 }
261
262 pub fn stop(self) {}
264}
265
266pub fn play_realtime_with_backend<B: AudioBackend>(
272 backend: &B,
273 plugin: Plugin,
274 config: AudioConfig,
275 command_capacity: usize,
276) -> Result<RtAudioHandle> {
277 let device = backend
278 .default_output_device()
279 .ok_or_else(|| Error::AudioBackendError("No default output device available".into()))?;
280
281 let channels = config.output_channels;
282 let sample_rate = config.sample_rate;
283
284 let (mut runner, control) = RealtimePluginRunner::new(plugin, command_capacity);
285 runner.start()?;
286
287 let mut scratch = AudioBuffers::new(0, channels, config.block_size, sample_rate);
289
290 let data_cb = Box::new(move |data: &mut [f32]| {
291 data.fill(0.0);
292 if channels == 0 {
293 return;
294 }
295 let frames = data.len() / channels;
296 prepare_scratch(&mut scratch, frames);
297
298 if runner.process(&mut scratch).is_ok() {
300 interleave_outputs(&scratch.outputs, data, channels);
301 }
302 });
303
304 let err_cb = Box::new(|e: B::Error| {
305 log::error!("audio stream error: {}", e);
306 });
307
308 let stream = backend
309 .create_output_stream(&device, config, data_cb, err_cb)
310 .map_err(|e| Error::AudioBackendError(format!("Failed to create output stream: {}", e)))?;
311
312 stream
313 .play()
314 .map_err(|e| Error::AudioBackendError(format!("Failed to start stream: {}", e)))?;
315
316 Ok(RtAudioHandle {
317 _stream: Box::new(stream),
318 control,
319 })
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325
326 #[test]
327 fn interleaves_two_channels() {
328 let outputs = vec![vec![1.0, 2.0, 3.0], vec![-1.0, -2.0, -3.0]];
330 let mut out = vec![0.0; 6]; interleave_outputs(&outputs, &mut out, 2);
332 assert_eq!(out, vec![1.0, -1.0, 2.0, -2.0, 3.0, -3.0]);
333 }
334
335 #[test]
336 fn ignores_extra_plugin_channels() {
337 let outputs = vec![vec![1.0, 2.0], vec![3.0, 4.0], vec![9.0, 9.0]];
339 let mut out = vec![0.0; 4];
340 interleave_outputs(&outputs, &mut out, 2);
341 assert_eq!(out, vec![1.0, 3.0, 2.0, 4.0]);
342 }
343
344 #[test]
345 fn leaves_missing_channels_as_silence() {
346 let outputs = vec![vec![0.5, 0.6]];
348 let mut out = vec![0.0; 4];
349 interleave_outputs(&outputs, &mut out, 2);
350 assert_eq!(out, vec![0.5, 0.0, 0.6, 0.0]);
352 }
353
354 #[test]
355 fn zero_channels_is_a_noop() {
356 let outputs = vec![vec![1.0, 2.0]];
357 let mut out = vec![7.0, 7.0];
358 interleave_outputs(&outputs, &mut out, 0);
359 assert_eq!(out, vec![7.0, 7.0]);
360 }
361
362 #[test]
363 fn prepare_scratch_resizes_and_clears() {
364 let mut scratch = AudioBuffers::new(1, 2, 4, 48000.0);
365 scratch.outputs[0][0] = 9.0;
366 prepare_scratch(&mut scratch, 8);
367 assert_eq!(scratch.block_size, 8);
368 assert!(scratch.outputs.iter().all(|c| c.len() == 8));
369 assert!(scratch.inputs.iter().all(|c| c.len() == 8));
370 assert!(scratch.outputs.iter().flatten().all(|&s| s == 0.0));
371 }
372}