1use std::{
2 hash::Hash,
3 sync::{Arc, Mutex},
4};
5
6use cpal::{
7 traits::{DeviceTrait, HostTrait, StreamTrait},
8 SampleRate, StreamError,
9};
10
11use super::{Mixer, Sound, SoundSource};
12use crate::converter::{ChannelConverter, SampleRateConverter};
13
14use backend::Backend;
15
16#[cfg(not(target_arch = "wasm32"))]
17mod backend {
18 use super::create_device;
19 use crate::Mixer;
20 use std::{
21 hash::Hash,
22 sync::{Arc, Mutex},
23 };
24
25 struct StreamEventLoop<G: Eq + Hash + Send + 'static> {
26 mixer: Arc<Mutex<Mixer<G>>>,
27 stream: Option<cpal::platform::Stream>,
28 }
29
30 impl<G: Eq + Hash + Send + 'static> StreamEventLoop<G> {
31 fn run(
32 &mut self,
33 event_channel: std::sync::mpsc::Sender<StreamEvent>,
34 stream_event_receiver: std::sync::mpsc::Receiver<StreamEvent>,
35 ) {
36 event_channel.send(StreamEvent::RecreateStream).unwrap();
38
39 let mut handled = false;
40 let error_callback = move |err| {
41 log::error!("stream error: {}", err);
42 if !handled {
43 handled = true;
46 event_channel.send(StreamEvent::RecreateStream).unwrap()
47 }
48 };
49
50 while let Ok(event) = stream_event_receiver.recv() {
51 match event {
52 StreamEvent::RecreateStream => {
53 log::debug!("recreating audio device");
54
55 #[cfg(target_os = "android")]
58 std::mem::forget(self.stream.take());
59
60 #[cfg(not(target_os = "android"))]
61 drop(self.stream.take());
62
63 let stream = create_device(&self.mixer, error_callback.clone());
64 let stream = match stream {
65 Ok(x) => x,
66 Err(x) => {
67 log::error!("creating audio device failed: {}", x);
68 return;
69 }
70 };
71 self.stream = Some(stream);
72 }
73 StreamEvent::Drop => {
74 #[cfg(target_os = "android")]
77 std::mem::forget(self.stream.take());
78
79 return;
80 }
81 }
82 }
83 }
84 }
85
86 enum StreamEvent {
87 RecreateStream,
88 Drop,
89 }
90
91 pub struct Backend {
92 join: Option<std::thread::JoinHandle<()>>,
93 sender: std::sync::mpsc::Sender<StreamEvent>,
94 }
95 impl Backend {
96 pub(super) fn start<G: Eq + Hash + Send + 'static>(
97 mixer: Arc<Mutex<Mixer<G>>>,
98 ) -> Result<Self, &'static str> {
99 let (sender, receiver) = std::sync::mpsc::channel::<StreamEvent>();
100 let join = {
101 let sender = sender.clone();
102 std::thread::spawn(move || {
103 log::trace!("starting thread");
104 StreamEventLoop {
105 mixer,
106 stream: None,
107 }
108 .run(sender, receiver)
109 })
110 };
111 Ok(Self {
112 join: Some(join),
113 sender,
114 })
115 }
116 }
117
118 impl Drop for Backend {
119 fn drop(&mut self) {
120 self.sender.send(StreamEvent::Drop).unwrap();
121 self.join.take().unwrap().join().unwrap();
122 }
123 }
124}
125#[cfg(target_arch = "wasm32")]
126mod backend {
127 use super::create_device;
128 use crate::Mixer;
129 use std::{
130 hash::Hash,
131 sync::{Arc, Mutex},
132 };
133
134 pub struct Backend {
135 _stream: cpal::Stream,
136 }
137 impl Backend {
138 pub(super) fn start<G: Eq + Hash + Send + 'static>(
139 mixer: Arc<Mutex<Mixer<G>>>,
140 ) -> Result<Self, &'static str> {
141 let stream = create_device(&mixer, |err| log::error!("stream error: {err}"));
145 let stream = match stream {
146 Ok(x) => x,
147 Err(x) => {
148 log::error!("creating audio device failed: {}", x);
149 return Err(x);
150 }
151 };
152 Ok(Self { _stream: stream })
153 }
154
155 pub(super) fn resume(&self) {
156 match self._stream.as_inner() {
157 cpal::platform::StreamInner::WebAudio(x) => {
158 let _ = x.audio_context().resume();
159 }
160 #[allow(unreachable_patterns)]
161 _ => {}
162 }
163 }
164 }
165}
166
167pub struct AudioEngine<G: Eq + Hash + Send + 'static = ()> {
174 mixer: Arc<Mutex<Mixer<G>>>,
175 _backend: Backend,
176}
177impl<G: Default + Eq + Hash + Send> AudioEngine<G> {
178 pub fn new_sound<T: SoundSource + Send + 'static>(
184 &self,
185 source: T,
186 ) -> Result<Sound<G>, &'static str> {
187 self.new_sound_with_group(G::default(), source)
188 }
189}
190impl AudioEngine {
191 pub fn new() -> Result<Self, &'static str> {
196 AudioEngine::with_groups::<()>()
197 }
198
199 pub fn with_groups<G: Eq + Hash + Send>() -> Result<AudioEngine<G>, &'static str> {
231 let mixer = Arc::new(Mutex::new(Mixer::<G>::new(2, super::SampleRate(48000))));
232 let backend = Backend::start(mixer.clone())?;
233
234 Ok(AudioEngine::<G> {
235 mixer,
236 _backend: backend,
237 })
238 }
239}
240impl<G: Eq + Hash + Send> AudioEngine<G> {
241 #[cfg(target_arch = "wasm32")]
248 pub fn resume(&self) {
249 self._backend.resume()
250 }
251
252 pub fn sample_rate(&self) -> u32 {
254 self.mixer.lock().unwrap().sample_rate()
255 }
256
257 pub fn channels(&self) -> u16 {
261 self.mixer.lock().unwrap().channels()
262 }
263
264 pub fn new_sound_with_group<T: SoundSource + Send + 'static>(
275 &self,
276 group: G,
277 source: T,
278 ) -> Result<Sound<G>, &'static str> {
279 let mut mixer = self.mixer.lock().unwrap();
280
281 log::debug!(
282 "adding sound: channels {}, sample_rate {}",
283 source.channels(),
284 mixer.channels()
285 );
286
287 let sound: Box<dyn SoundSource + Send> = if source.sample_rate() != mixer.sample_rate() {
288 if source.channels() == mixer.channels() {
289 Box::new(SampleRateConverter::new(source, mixer.sample_rate()))
290 } else {
291 Box::new(ChannelConverter::new(
292 SampleRateConverter::new(source, mixer.sample_rate()),
293 mixer.channels(),
294 ))
295 }
296 } else if source.channels() == mixer.channels() {
297 Box::new(source)
298 } else {
299 Box::new(ChannelConverter::new(source, mixer.channels()))
300 };
301
302 let id = mixer.add_sound(group, sound);
303 mixer.mark_to_remove(id, false);
304 drop(mixer);
305
306 Ok(Sound {
307 mixer: self.mixer.clone(),
308 id,
309 })
310 }
311
312 pub fn set_group_volume(&self, group: G, volume: f32) {
316 self.mixer.lock().unwrap().set_group_volume(group, volume)
317 }
318}
319
320fn create_device<G: Eq + Hash + Send + 'static>(
321 mixer: &Arc<Mutex<Mixer<G>>>,
322 error_callback: impl FnMut(StreamError) + Send + Clone + 'static,
323) -> Result<cpal::Stream, &'static str> {
324 let host = cpal::default_host();
325 let device = host
326 .default_output_device()
327 .ok_or("no output device available")?;
328 let mut supported_configs_range = device
329 .supported_output_configs()
330 .map_err(|_| "error while querying formats")?
331 .map(|x| {
332 let sample_rate = SampleRate(48000);
333 if x.min_sample_rate() <= sample_rate && sample_rate <= x.max_sample_rate() {
334 return x.with_sample_rate(sample_rate);
335 }
336
337 let sample_rate = SampleRate(44100);
338 if x.min_sample_rate() <= sample_rate && sample_rate <= x.max_sample_rate() {
339 return x.with_sample_rate(sample_rate);
340 }
341
342 x.with_max_sample_rate()
343 })
344 .collect::<Vec<_>>();
345 supported_configs_range.sort_unstable_by(|a, b| {
346 let key = |x: &cpal::SupportedStreamConfig| {
347 (
348 x.sample_rate().0 == 48000,
349 x.sample_rate().0 == 441000,
350 x.channels() == 2,
351 x.channels() == 1,
352 x.sample_format() == cpal::SampleFormat::I16,
353 x.sample_rate().0,
354 )
355 };
356 key(a).cmp(&key(b))
357 });
358 if log::max_level() >= log::LevelFilter::Trace {
359 for config in &supported_configs_range {
360 log::trace!("config {:?}", config);
361 }
362 }
363 let stream = loop {
364 let config = if let Some(config) = supported_configs_range.pop() {
365 config
366 } else {
367 return Err("no supported config");
368 };
369 let sample_format = config.sample_format();
370 let config = config.config();
371 mixer
372 .lock()
373 .unwrap()
374 .set_config(config.channels, super::SampleRate(config.sample_rate.0));
375
376 let stream = {
377 use cpal::SampleFormat::*;
378 match sample_format {
379 I16 => stream::<i16, G, _>(mixer, error_callback.clone(), &device, &config),
380 U16 => stream::<u16, G, _>(mixer, error_callback.clone(), &device, &config),
381 F32 => stream::<f32, G, _>(mixer, error_callback.clone(), &device, &config),
382 }
383 };
384 let stream = match stream {
385 Ok(x) => {
386 log::info!(
387 "created {:?} stream with config {:?}",
388 sample_format,
389 config
390 );
391 x
392 }
393 Err(e) => {
394 log::error!("failed to create stream with config {:?}: {:?}", config, e);
395 continue;
396 }
397 };
398 stream.play().unwrap();
399 break stream;
400 };
401 Ok(stream)
402}
403
404fn stream<T, G, E>(
405 mixer: &Arc<Mutex<Mixer<G>>>,
406 error_callback: E,
407 device: &cpal::Device,
408 config: &cpal::StreamConfig,
409) -> Result<cpal::Stream, cpal::BuildStreamError>
410where
411 T: cpal::Sample,
412 G: Eq + Hash + Send + 'static,
413 E: FnMut(StreamError) + Send + 'static,
414{
415 let mixer = mixer.clone();
416 let mut input_buffer = Vec::new();
417 device.build_output_stream(
418 config,
419 move |output_buffer: &mut [T], _| {
420 input_buffer.clear();
421 input_buffer.resize(output_buffer.len(), 0);
422 mixer.lock().unwrap().write_samples(&mut input_buffer);
423 output_buffer
425 .iter_mut()
426 .zip(input_buffer.iter())
427 .for_each(|(a, b)| *a = T::from(b));
428 },
429 error_callback,
430 )
431}