bbx_player/backends/
rodio.rs1use 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
13pub struct RodioBackend {
18 _private: (),
19}
20
21impl RodioBackend {
22 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}