bbx_player/
player.rs

1use std::sync::{Arc, atomic::AtomicBool};
2
3use bbx_dsp::{graph::Graph, sample::Sample};
4
5#[cfg(feature = "rodio")]
6use crate::backends::RodioBackend;
7use crate::{
8    backend::{Backend, PlayHandle},
9    error::Result,
10    signal::Signal,
11};
12
13/// Audio player that plays a DSP graph through a configurable backend.
14///
15/// `Player` wraps a DSP [`Graph`] and handles the conversion to an
16/// audio stream that can be played through the system's audio output.
17///
18/// # Examples
19///
20/// Using the default rodio backend (requires `rodio` feature):
21///
22/// ```ignore
23/// use bbx_player::Player;
24///
25/// let player = Player::new(graph)?;
26/// let handle = player.play()?;
27///
28/// std::thread::sleep(Duration::from_secs(5));
29/// handle.stop();
30/// ```
31///
32/// Using a custom backend:
33///
34/// ```ignore
35/// use bbx_player::{Player, backends::CpalBackend};
36///
37/// let backend = CpalBackend::try_default()?;
38/// let player = Player::with_backend(graph, backend);
39/// let handle = player.play()?;
40/// ```
41pub struct Player<S: Sample> {
42    graph: Graph<S>,
43    backend: Box<dyn Backend>,
44}
45
46#[cfg(feature = "rodio")]
47impl<S: Sample> Player<S> {
48    /// Create a new player with the default rodio backend.
49    pub fn new(graph: Graph<S>) -> Result<Self> {
50        let backend = RodioBackend::try_default()?;
51        Ok(Self {
52            graph,
53            backend: Box::new(backend),
54        })
55    }
56}
57
58impl<S: Sample> Player<S> {
59    /// Create a new player with a custom backend.
60    pub fn with_backend<B: Backend>(graph: Graph<S>, backend: B) -> Self {
61        Self {
62            graph,
63            backend: Box::new(backend),
64        }
65    }
66
67    /// Start non-blocking playback.
68    ///
69    /// Returns a [`PlayHandle`] that can be used to stop playback.
70    /// The player is consumed by this method.
71    pub fn play(self) -> Result<PlayHandle> {
72        let stop_flag = Arc::new(AtomicBool::new(false));
73        let handle = PlayHandle::new(Arc::clone(&stop_flag));
74
75        let signal = Signal::new(self.graph, Arc::clone(&stop_flag));
76        let sample_rate = signal.sample_rate();
77        let num_channels = signal.num_channels() as u16;
78
79        let signal_f32: Box<dyn Iterator<Item = f32> + Send> = Box::new(signal.map(|s| s.to_f64() as f32));
80
81        self.backend.play(signal_f32, sample_rate, num_channels, stop_flag)?;
82
83        Ok(handle)
84    }
85}