uni_snd/
snd.rs

1use std;
2use std::sync::mpsc::{channel, Receiver, Sender};
3
4use super::{SoundError, SoundGenerator};
5use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
6use cpal::{self, Device, Sample, SampleFormat, Stream};
7use uni_app::App;
8
9/// This is the sound API that allows you to send events to your generator.
10pub struct SoundDriver<T: Send + 'static> {
11    config: Option<cpal::StreamConfig>,
12    tx: Option<Sender<T>>,
13    generator: Option<Box<dyn SoundGenerator<T>>>,
14    device: Option<Device>,
15    format: Option<SampleFormat>,
16    stream: Option<Stream>,
17    err: SoundError,
18    // to store events sent before start() is called
19    event_buffer: Vec<T>,
20}
21
22impl<T: Send + 'static> SoundDriver<T> {
23    /// After calling [`SoundDriver::new`], you can call this function to see if the audio initialization was a success.
24    pub fn get_error(&self) -> SoundError {
25        self.err
26    }
27
28    /// Initialize the sound device and provide the generator to the driver.
29    pub fn new(generator: Box<dyn SoundGenerator<T>>) -> Self {
30        let host = cpal::default_host();
31        let mut stream_config = None;
32        let mut err = SoundError::NoError;
33        let mut device = None;
34        let mut format = None;
35        if let Some(dev) = host.default_output_device() {
36            match dev.default_output_config() {
37                Ok(config) => {
38                    App::print(format!(
39                        "sound device : {} {:?}\n",
40                        dev.name().unwrap_or_else(|_| "?".to_owned()),
41                        config
42                    ));
43                    format = Some(config.sample_format());
44                    stream_config = Some(config.into());
45                    device = Some(dev);
46                }
47                Err(e) => {
48                    err = SoundError::UnknownStreamFormat;
49                    App::print(format!(
50                        "error : uni-snd - could not get default output configuration : {:?}\n",
51                        e
52                    ));
53                }
54            }
55        } else {
56            err = SoundError::NoDevice;
57            App::print("warning : no sound device detected\n");
58        }
59        Self {
60            config: stream_config,
61            tx: None,
62            device,
63            format,
64            stream: None,
65            generator: Some(generator),
66            err,
67            event_buffer: Vec::new(),
68        }
69    }
70    /// Send an event to the generator
71    pub fn send_event(&mut self, event: T) {
72        if let Some(ref mut tx) = self.tx {
73            tx.send(event).unwrap();
74        } else {
75            self.event_buffer.push(event);
76        }
77    }
78    fn get_sample_rate(&self) -> f32 {
79        if let Some(ref config) = self.config {
80            config.sample_rate.0 as f32
81        } else {
82            1.0
83        }
84    }
85    /// This will call the generator init function.
86    /// It starts the sound thread and the audio loop.
87    pub fn start(&mut self) {
88        if self.config.is_none() || self.device.is_none() || self.generator.is_none() {
89            App::print("no sound");
90            return;
91        }
92        let (tx, rx) = channel();
93        self.tx = Some(tx);
94        let sample_rate = self.get_sample_rate();
95        let config = self.config.take().unwrap();
96        let device = self.device.take().unwrap();
97        let mut generator = self.generator.take().unwrap();
98        generator.init(sample_rate);
99
100        // sent pending events
101        for event in self.event_buffer.drain(0..) {
102            generator.handle_event(event);
103        }
104
105        let stream_res = match self.format {
106            Some(SampleFormat::F32) => build_stream::<f32, T>(&device, &config, generator, rx),
107            Some(SampleFormat::I16) => build_stream::<i16, T>(&device, &config, generator, rx),
108            Some(SampleFormat::U16) => build_stream::<u16, T>(&device, &config, generator, rx),
109            None => Err(String::new()),
110        };
111        match stream_res {
112            Ok(str) => {
113                App::print("starting audio loop\n");
114                str.play().unwrap_or_else(|e| {
115                    App::print(format!("error : uni-snd - could not start play {}", e));
116                });
117                self.stream = Some(str);
118            }
119            Err(e) => {
120                self.err = SoundError::OutputStream;
121                App::print(format!(
122                    "error : uni-snd - could not build output stream : {}\n",
123                    e
124                ))
125            }
126        }
127    }
128}
129
130fn build_stream<S, T: Send + 'static>(
131    device: &cpal::Device,
132    config: &cpal::StreamConfig,
133    mut generator: Box<dyn SoundGenerator<T>>,
134    rx: Receiver<T>,
135) -> Result<Stream, String>
136where
137    S: cpal::Sample,
138{
139    let channels = config.channels as usize;
140    let err_fn = |err| {
141        App::print(&format!(
142            "error : uni-snd - an error occurred on stream: {}",
143            err
144        ))
145    };
146    device
147        .build_output_stream(
148            config,
149            move |data: &mut [S], _: &cpal::OutputCallbackInfo| {
150                for event in rx.try_iter() {
151                    generator.handle_event(event);
152                }
153                write_data(data, channels, &mut generator);
154            },
155            err_fn,
156        )
157        .map_err(|e| format!("error : uni-snd - could not build output stream {}", e))
158}
159
160fn write_data<S, T: Send + 'static>(
161    output: &mut [S],
162    channels: usize,
163    generator: &mut Box<dyn SoundGenerator<T>>,
164) where
165    S: Sample,
166{
167    for frame in output.chunks_mut(channels) {
168        for sample in frame.iter_mut() {
169            let val = generator.next_value();
170            *sample = Sample::from::<f32>(&val);
171        }
172    }
173}