1use crate::{error::Error, PlaySoundParams};
4
5use quad_alsa_sys as sys;
6
7use std::sync::mpsc;
8
9pub use crate::mixer::Playback;
10
11mod consts {
12 pub const DEVICES: &[&str] = &["default\0", "pipewire\0"];
13 pub const RATE: u32 = 44100;
14 pub const CHANNELS: u32 = 2;
15 pub const PCM_BUFFER_SIZE: ::std::os::raw::c_ulong = 4096;
16}
17
18unsafe fn setup_pcm_device() -> *mut sys::snd_pcm_t {
19 let mut pcm_handle = std::ptr::null_mut();
20
21 if !consts::DEVICES.iter().any(|device| {
23 sys::snd_pcm_open(
24 &mut pcm_handle,
25 device.as_ptr() as _,
26 sys::SND_PCM_STREAM_PLAYBACK,
27 0,
28 ) >= 0
29 }) {
30 panic!("Can't open PCM device.");
31 }
32
33 let mut hw_params: *mut sys::snd_pcm_hw_params_t = std::ptr::null_mut();
34 sys::snd_pcm_hw_params_malloc(&mut hw_params);
35 sys::snd_pcm_hw_params_any(pcm_handle, hw_params);
36
37 if sys::snd_pcm_hw_params_set_access(pcm_handle, hw_params, sys::SND_PCM_ACCESS_RW_INTERLEAVED)
38 < 0
39 {
40 panic!("Can't set interleaved mode");
41 }
42
43 if sys::snd_pcm_hw_params_set_format(pcm_handle, hw_params, sys::SND_PCM_FORMAT_FLOAT_LE) < 0 {
44 panic!("Can't set SND_PCM_FORMAT_FLOAT_LE format");
45 }
46 if sys::snd_pcm_hw_params_set_buffer_size(pcm_handle, hw_params, consts::PCM_BUFFER_SIZE) < 0 {
47 panic!("Cant's set buffer size");
48 }
49 if sys::snd_pcm_hw_params_set_channels(pcm_handle, hw_params, consts::CHANNELS) < 0 {
50 panic!("Can't set channels number.");
51 }
52
53 let mut rate = consts::RATE;
54 if sys::snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, &mut rate, std::ptr::null_mut())
55 < 0
56 {
57 panic!("Can't set rate.");
58 }
59
60 if sys::snd_pcm_hw_params(pcm_handle, hw_params) < 0 {
62 panic!("Can't set harware parameters.");
63 }
64 sys::snd_pcm_hw_params_free(hw_params);
65
66 let mut sw_params: *mut sys::snd_pcm_sw_params_t = std::ptr::null_mut();
70
71 if sys::snd_pcm_sw_params_malloc(&mut sw_params) < 0 {
72 panic!("cannot allocate software parameters structure");
73 }
74 if sys::snd_pcm_sw_params_current(pcm_handle, sw_params) < 0 {
75 panic!("cannot initialize software parameters structure");
76 }
77
78 if sys::snd_pcm_sw_params_set_start_threshold(pcm_handle, sw_params, 0) < 0 {
87 panic!("cannot set start mode");
88 }
89 if sys::snd_pcm_sw_params(pcm_handle, sw_params) < 0 {
90 panic!("cannot set software parameters");
91 }
92 sys::snd_pcm_sw_params_free(sw_params);
93
94 if sys::snd_pcm_prepare(pcm_handle) < 0 {
95 panic!("cannot prepare audio interface for use");
96 }
97
98 pcm_handle
99}
100
101unsafe fn audio_thread(mut mixer: crate::mixer::Mixer) {
102 let mut buffer: Vec<f32> = vec![0.0; consts::PCM_BUFFER_SIZE as usize * 2];
103
104 let pcm_handle = setup_pcm_device();
105
106 loop {
107 if sys::snd_pcm_wait(pcm_handle, -1) < 0 {
109 panic!("PCM device is not ready");
110 }
111
112 let frames_to_deliver = consts::PCM_BUFFER_SIZE as i64;
126
127 mixer.fill_audio_buffer(&mut buffer, frames_to_deliver as usize);
129
130 let frames_writen = sys::snd_pcm_writei(
132 pcm_handle,
133 buffer.as_ptr() as *const _,
134 frames_to_deliver as _,
135 );
136 if frames_writen == -libc::EPIPE as ::std::os::raw::c_long {
137 println!("Underrun occured: -EPIPE, attempting recover");
138
139 sys::snd_pcm_recover(pcm_handle, frames_writen as _, 0);
140 }
141
142 if frames_writen > 0 && frames_writen != frames_to_deliver as _ {
143 println!("Underrun occured: frames_writen != frames_to_deliver, attempting recover");
144
145 sys::snd_pcm_recover(pcm_handle, frames_writen as _, 0);
146 }
147 }
148}
149
150pub struct AudioContext {
151 pub(crate) mixer_ctrl: crate::mixer::MixerControl,
152}
153
154impl AudioContext {
155 pub fn new() -> AudioContext {
156 use crate::mixer::Mixer;
157
158 let (mixer_builder, mixer_ctrl) = Mixer::new();
159 std::thread::spawn(move || unsafe {
160 audio_thread(mixer_builder.build());
161 });
162
163 AudioContext { mixer_ctrl }
164 }
165}
166
167pub struct Sound {
168 sound_id: u32,
169}
170
171impl Sound {
172 pub fn load(ctx: &AudioContext, data: &[u8]) -> Sound {
173 let sound_id = ctx.mixer_ctrl.load(data);
174
175 Sound { sound_id }
176 }
177
178 pub fn play(&self, ctx: &AudioContext, params: PlaySoundParams) -> Playback {
179 ctx.mixer_ctrl.play(self.sound_id, params)
180 }
181
182 pub fn stop(&self, ctx: &AudioContext) {
183 ctx.mixer_ctrl.stop_all(self.sound_id);
184 }
185
186 pub fn set_volume(&self, ctx: &AudioContext, volume: f32) {
187 ctx.mixer_ctrl.set_volume_all(self.sound_id, volume);
188 }
189
190 pub fn delete(&self, ctx: &AudioContext) {
191 ctx.mixer_ctrl.delete(self.sound_id);
192 }
193}