bbx_player/backends/
rodio.rs

1use std::{
2    sync::{
3        Arc,
4        atomic::{AtomicBool, Ordering},
5    },
6    time::Duration,
7};
8
9use rodio::{OutputStream, Source};
10
11use crate::{backend::Backend, error::Result};
12
13/// High-level audio backend using rodio.
14///
15/// Rodio handles device selection and stream management automatically,
16/// making it the easiest backend to use.
17pub struct RodioBackend {
18    _private: (),
19}
20
21impl RodioBackend {
22    /// Create a new rodio backend using the default output device.
23    pub fn try_default() -> Result<Self> {
24        Ok(Self { _private: () })
25    }
26}
27
28impl Backend for RodioBackend {
29    fn play(
30        self: Box<Self>,
31        signal: Box<dyn Iterator<Item = f32> + Send>,
32        sample_rate: u32,
33        num_channels: u16,
34        stop_flag: Arc<AtomicBool>,
35    ) -> Result<()> {
36        let source = SignalSource::new(signal, sample_rate, num_channels, stop_flag.clone());
37
38        std::thread::spawn(move || {
39            let (_stream, stream_handle) = match OutputStream::try_default() {
40                Ok(s) => s,
41                Err(e) => {
42                    eprintln!("Failed to open audio output: {e}");
43                    return;
44                }
45            };
46
47            if let Err(e) = stream_handle.play_raw(source) {
48                eprintln!("Failed to start playback: {e}");
49                return;
50            }
51
52            while !stop_flag.load(Ordering::SeqCst) {
53                std::thread::sleep(Duration::from_millis(10));
54            }
55        });
56
57        Ok(())
58    }
59}
60
61struct SignalSource {
62    signal: Box<dyn Iterator<Item = f32> + Send>,
63    sample_rate: u32,
64    num_channels: u16,
65    stop_flag: Arc<AtomicBool>,
66}
67
68impl SignalSource {
69    fn new(
70        signal: Box<dyn Iterator<Item = f32> + Send>,
71        sample_rate: u32,
72        num_channels: u16,
73        stop_flag: Arc<AtomicBool>,
74    ) -> Self {
75        Self {
76            signal,
77            sample_rate,
78            num_channels,
79            stop_flag,
80        }
81    }
82}
83
84impl Iterator for SignalSource {
85    type Item = f32;
86
87    fn next(&mut self) -> Option<Self::Item> {
88        if self.stop_flag.load(Ordering::SeqCst) {
89            return None;
90        }
91        self.signal.next()
92    }
93}
94
95impl Source for SignalSource {
96    fn current_frame_len(&self) -> Option<usize> {
97        None
98    }
99
100    fn channels(&self) -> u16 {
101        self.num_channels
102    }
103
104    fn sample_rate(&self) -> u32 {
105        self.sample_rate
106    }
107
108    fn total_duration(&self) -> Option<Duration> {
109        None
110    }
111}