limnus_audio_stream/
lib.rs1use cpal::traits::{DeviceTrait, StreamTrait};
6use cpal::{Device, StreamConfig};
7use limnus_app::prelude::{App, Plugin};
8use limnus_audio_device::low_level::Audio;
9use limnus_audio_mixer::AudioMixer;
10use limnus_local_resource::prelude::LocalResource;
11use std::fmt::Debug;
12use std::sync::{Arc, Mutex};
13use tracing::error;
14
15fn start_stream(
16 device: &Device,
17 config: &StreamConfig,
18 sample_rate: u32,
19 low_level_mixer: Arc<Mutex<oddio::Mixer<[f32; 2]>>>,
20) -> cpal::Stream {
21 let stream = device
22 .build_output_stream(
23 config,
24 move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
25 let out_frames = oddio::frame_stereo(data);
26 oddio::run(
27 &mut *low_level_mixer.lock().unwrap(),
28 sample_rate,
29 out_frames,
30 );
31 },
32 move |err| {
33 error!("Stream error: {}", err);
34 },
35 None,
36 )
37 .expect("Failed to build output stream");
38
39 stream.play().expect("Failed to start stream");
40 stream
41}
42
43#[derive(LocalResource)]
44pub struct AudioStream {
45 #[allow(dead_code)]
46 stream: cpal::Stream,
47}
48
49impl Debug for AudioStream {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(f, "AudioStream")
52 }
53}
54
55pub struct AudioStreamPlugin;
56
57impl Plugin for AudioStreamPlugin {
58 fn build(&self, app: &mut App) {
59 let resources = app.local_resources();
60 let audio = resources.fetch::<Audio>();
61 let mixer = resources.fetch::<AudioMixer>();
62
63 let device = audio.device();
64 let config = audio.config();
65 let stream = start_stream(device, config, audio.sample_rate(), mixer.mixer.clone());
66 let audio_stream = AudioStream { stream };
67 app.insert_local_resource(audio_stream);
68 }
69}