1use std::collections::VecDeque;
4use std::sync::{Arc, Mutex};
5
6use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
7
8const TARGET_LATENCY_S: f32 = 0.06;
10const MAX_LATENCY_S: f32 = 0.20;
12
13struct Queue {
14 samples: VecDeque<f32>, primed: bool,
16}
17
18pub struct AudioSink {
19 queue: Arc<Mutex<Queue>>,
20 device_rate: u32,
21 src_rate: u32,
22 src_channels: u32,
23 t: f64,
24 prev: [f32; 2],
25 _stream: cpal::Stream,
26}
27
28impl AudioSink {
29 pub fn open() -> Result<AudioSink, String> {
30 let host = cpal::default_host();
31 let device = host.default_output_device().ok_or("no audio output device")?;
32 let supported = device.default_output_config().map_err(|e| e.to_string())?;
33 if supported.sample_format() != cpal::SampleFormat::F32 {
34 return Err(format!("unsupported device sample format {:?}", supported.sample_format()));
35 }
36 let config = supported.config();
37 let channels = config.channels as usize;
38 let device_rate = config.sample_rate;
39
40 let queue = Arc::new(Mutex::new(Queue { samples: VecDeque::new(), primed: false }));
41 let q = queue.clone();
42 let target = (device_rate as f32 * TARGET_LATENCY_S) as usize * 2;
43 let stream = device
44 .build_output_stream::<f32, _, _>(
45 config,
46 move |out: &mut [f32], _| {
47 let mut q = q.lock().unwrap();
48 if !q.primed && q.samples.len() >= target {
49 q.primed = true;
50 }
51 for frame in out.chunks_mut(channels) {
52 let (l, r) = if q.primed && q.samples.len() >= 2 {
53 (q.samples.pop_front().unwrap(), q.samples.pop_front().unwrap())
54 } else {
55 q.primed = false; (0.0, 0.0)
57 };
58 match frame.len() {
59 1 => frame[0] = 0.5 * (l + r),
60 _ => {
61 frame[0] = l;
62 frame[1] = r;
63 frame[2..].fill(0.0);
64 }
65 }
66 }
67 },
68 |e| eprintln!("[audio] stream error: {e}"),
69 None,
70 )
71 .map_err(|e| e.to_string())?;
72 stream.play().map_err(|e| e.to_string())?;
73 Ok(AudioSink {
74 queue,
75 device_rate,
76 src_rate: 44100,
77 src_channels: 2,
78 t: 0.0,
79 prev: [0.0; 2],
80 _stream: stream,
81 })
82 }
83
84 pub fn device_rate(&self) -> u32 {
85 self.device_rate
86 }
87
88 pub fn configure(&mut self, rate: u32, channels: u32) {
89 self.src_rate = rate;
90 self.src_channels = channels;
91 }
92
93 pub fn push_le_f32(&mut self, bytes: &[u8]) {
95 let step = self.src_rate as f64 / self.device_rate as f64;
96 let ch = self.src_channels as usize;
97 let mut out = Vec::with_capacity((bytes.len() / 4 / ch) * 2 * 2);
98 for frame in bytes.chunks_exact(4 * ch) {
99 let s = |i: usize| f32::from_le_bytes(frame[i * 4..i * 4 + 4].try_into().unwrap());
100 let cur = if ch == 1 { [s(0), s(0)] } else { [s(0), s(1)] };
101 while self.t < 1.0 {
102 let t = self.t as f32;
103 out.push(self.prev[0] + (cur[0] - self.prev[0]) * t);
104 out.push(self.prev[1] + (cur[1] - self.prev[1]) * t);
105 self.t += step;
106 }
107 self.t -= 1.0;
108 self.prev = cur;
109 }
110 let max = (self.device_rate as f32 * MAX_LATENCY_S) as usize * 2;
111 let target = (self.device_rate as f32 * TARGET_LATENCY_S) as usize * 2;
112 let mut q = self.queue.lock().unwrap();
113 q.samples.extend(out);
114 if q.samples.len() > max {
115 let excess = q.samples.len() - target;
116 q.samples.drain(..excess & !1);
117 }
118 }
119}