1use std::sync::Arc;
8use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
9
10use kira::sound::{Sound, SoundData};
11use kira::{Frame, info::Info};
12
13pub trait AudioStream: Send + 'static {
18 fn fill(&mut self, out: &mut [f32]);
20}
21
22impl<F: FnMut(&mut [f32]) + Send + 'static> AudioStream for F {
23 fn fill(&mut self, out: &mut [f32]) {
24 self(out);
25 }
26}
27
28pub trait StereoStream: Send + 'static {
33 fn fill(&mut self, out: &mut [[f32; 2]]);
35}
36
37impl<F: FnMut(&mut [[f32; 2]]) + Send + 'static> StereoStream for F {
38 fn fill(&mut self, out: &mut [[f32; 2]]) {
39 self(out);
40 }
41}
42
43pub(crate) enum Source {
45 Mono(Box<dyn AudioStream>, Vec<f32>),
46 Stereo(Box<dyn StereoStream>),
47}
48
49impl Source {
50 pub(crate) fn mono(stream: Box<dyn AudioStream>) -> Self {
51 Source::Mono(stream, vec![0.0; CHUNK])
52 }
53
54 fn fill(&mut self, out: &mut [[f32; 2]]) {
55 match self {
56 Source::Mono(stream, buf) => {
57 buf.resize(out.len(), 0.0);
58 stream.fill(buf);
59 for (o, &s) in out.iter_mut().zip(buf.iter()) {
60 *o = [s, s];
61 }
62 }
63 Source::Stereo(stream) => stream.fill(out),
64 }
65 }
66}
67
68const CHUNK: usize = 512;
70
71#[derive(Debug)]
73struct Shared {
74 volume: AtomicU32,
76 paused: AtomicBool,
77 stopped: AtomicBool,
78 pending: AtomicBool,
80}
81
82#[derive(Clone, Debug)]
88pub struct StreamHandle {
89 shared: Arc<Shared>,
90}
91
92impl StreamHandle {
93 fn new(volume: f32, pending: bool) -> Self {
94 Self {
95 shared: Arc::new(Shared {
96 volume: AtomicU32::new(volume.max(0.0).to_bits()),
99 paused: AtomicBool::new(false),
100 stopped: AtomicBool::new(false),
101 pending: AtomicBool::new(pending),
102 }),
103 }
104 }
105
106 pub fn set_volume(&self, volume: f32) {
108 self.shared
109 .volume
110 .store(volume.max(0.0).to_bits(), Ordering::Relaxed);
111 }
112
113 pub fn volume(&self) -> f32 {
115 f32::from_bits(self.shared.volume.load(Ordering::Relaxed))
116 }
117
118 pub fn pause(&self) {
121 self.shared.paused.store(true, Ordering::Relaxed);
122 }
123
124 pub fn resume(&self) {
126 self.shared.paused.store(false, Ordering::Relaxed);
127 }
128
129 pub fn stop(&self) {
132 self.shared.stopped.store(true, Ordering::Relaxed);
133 }
134
135 pub fn is_playing(&self) -> bool {
137 !self.is_pending() && !self.is_paused() && !self.is_finished()
138 }
139
140 pub fn is_paused(&self) -> bool {
142 self.shared.paused.load(Ordering::Relaxed)
143 }
144
145 pub fn is_pending(&self) -> bool {
147 self.shared.pending.load(Ordering::Relaxed)
148 }
149
150 pub fn is_finished(&self) -> bool {
152 self.shared.stopped.load(Ordering::Relaxed)
153 }
154
155 pub(crate) fn mark_started(&self) {
156 self.shared.pending.store(false, Ordering::Relaxed);
157 }
158}
159
160pub(crate) struct StreamData {
162 stream: Source,
163 sample_rate: u32,
164 handle: StreamHandle,
165}
166
167impl StreamData {
168 pub(crate) fn new(
169 stream: Source,
170 sample_rate: u32,
171 volume: f32,
172 pending: bool,
173 ) -> (Self, StreamHandle) {
174 let handle = StreamHandle::new(volume, pending);
175 let data = Self {
176 stream,
177 sample_rate: sample_rate.max(1),
178 handle: handle.clone(),
179 };
180 (data, handle)
181 }
182
183 pub(crate) fn handle(&self) -> &StreamHandle {
184 &self.handle
185 }
186}
187
188impl SoundData for StreamData {
189 type Error = std::convert::Infallible;
190 type Handle = ();
191
192 fn into_sound(self) -> Result<(Box<dyn Sound>, ()), Self::Error> {
193 let sound = StreamSound::new(self.stream, self.sample_rate, self.handle.shared);
194 Ok((Box::new(sound), ()))
195 }
196}
197
198struct StreamSound {
201 stream: Source,
202 sample_rate: f64,
203 shared: Arc<Shared>,
204 chunk: Vec<[f32; 2]>,
205 next_in_chunk: usize,
206 previous: [f32; 2],
208 current: [f32; 2],
209 phase: f64,
210 primed: bool,
212 applied_volume: f32,
214}
215
216impl StreamSound {
217 fn new(stream: Source, sample_rate: u32, shared: Arc<Shared>) -> Self {
218 let applied_volume = if shared.paused.load(Ordering::Relaxed) {
220 0.0
221 } else {
222 f32::from_bits(shared.volume.load(Ordering::Relaxed))
223 };
224 Self {
225 stream,
226 sample_rate: f64::from(sample_rate),
227 shared,
228 chunk: vec![[0.0; 2]; CHUNK],
229 next_in_chunk: CHUNK,
230 previous: [0.0; 2],
231 current: [0.0; 2],
232 phase: 0.0,
233 primed: false,
234 applied_volume,
235 }
236 }
237
238 fn next_sample(&mut self) -> [f32; 2] {
239 if self.next_in_chunk == self.chunk.len() {
240 self.stream.fill(&mut self.chunk);
241 self.next_in_chunk = 0;
242 }
243 let s = self.chunk[self.next_in_chunk];
244 self.next_in_chunk += 1;
245 s
246 }
247
248 fn render(&mut self, out: &mut [Frame], dt: f64) {
250 let silenced = self.shared.paused.load(Ordering::Relaxed)
251 || self.shared.stopped.load(Ordering::Relaxed);
252 let target = if silenced {
253 0.0
254 } else {
255 f32::from_bits(self.shared.volume.load(Ordering::Relaxed))
256 };
257 if silenced && self.applied_volume == 0.0 {
258 out.fill(Frame::ZERO);
259 return;
260 }
261 if !self.primed {
262 self.previous = self.next_sample();
263 self.current = self.next_sample();
264 self.primed = true;
265 }
266 let from = self.applied_volume;
267 let len = out.len() as f32;
268 let step = self.sample_rate * dt;
269 for (i, frame) in out.iter_mut().enumerate() {
270 let volume = from + (target - from) * ((i + 1) as f32 / len);
272 let t = self.phase as f32;
273 let l = self.previous[0] + (self.current[0] - self.previous[0]) * t;
274 let r = self.previous[1] + (self.current[1] - self.previous[1]) * t;
275 *frame = Frame::new(l * volume, r * volume);
276 self.phase += step;
277 while self.phase >= 1.0 {
278 self.phase -= 1.0;
279 self.previous = self.current;
280 self.current = self.next_sample();
281 }
282 }
283 if !out.is_empty() {
284 self.applied_volume = target;
285 }
286 }
287}
288
289impl Sound for StreamSound {
290 fn process(&mut self, out: &mut [Frame], dt: f64, _info: &Info<'_>) {
291 self.render(out, dt);
292 }
293
294 fn finished(&self) -> bool {
295 self.shared.stopped.load(Ordering::Relaxed) && self.applied_volume == 0.0
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303
304 fn sound(stream: impl AudioStream, rate: u32) -> (StreamSound, StreamHandle) {
305 sound_at(stream, rate, 1.0)
306 }
307
308 fn sound_at(stream: impl AudioStream, rate: u32, volume: f32) -> (StreamSound, StreamHandle) {
309 let (data, handle) = StreamData::new(Source::mono(Box::new(stream)), rate, volume, false);
310 let sound = StreamSound::new(data.stream, data.sample_rate, handle.shared.clone());
311 (sound, handle)
312 }
313
314 fn ramp() -> impl AudioStream {
315 let mut n = 0u32;
316 move |out: &mut [f32]| {
317 for s in out {
318 *s = n as f32;
319 n += 1;
320 }
321 }
322 }
323
324 #[test]
325 fn same_rate_passes_samples_through() {
326 let (mut s, _) = sound(ramp(), 100);
327 let mut out = [Frame::ZERO; 4];
328 s.render(&mut out, 1.0 / 100.0);
329 let left: Vec<f32> = out.iter().map(|f| f.left).collect();
330 assert_eq!(left, [0.0, 1.0, 2.0, 3.0]);
331 }
332
333 #[test]
334 fn half_rate_interpolates() {
335 let (mut s, _) = sound(ramp(), 50);
336 let mut out = [Frame::ZERO; 6];
337 s.render(&mut out, 1.0 / 100.0);
338 let left: Vec<f32> = out.iter().map(|f| f.left).collect();
339 assert_eq!(left, [0.0, 0.5, 1.0, 1.5, 2.0, 2.5]);
340 }
341
342 #[test]
343 fn paused_and_stopped_streams_fade_out_then_go_silent() {
344 let (mut s, handle) = sound(|out: &mut [f32]| out.fill(1.0), 100);
345 let mut out = [Frame::ZERO; 4];
346 handle.pause();
347 s.render(&mut out, 0.01);
348 let left: Vec<f32> = out.iter().map(|f| f.left).collect();
349 assert_eq!(left, [0.75, 0.5, 0.25, 0.0]);
350 s.render(&mut out, 0.01);
351 assert!(out.iter().all(|f| *f == Frame::ZERO));
352
353 handle.resume();
354 s.render(&mut out, 0.01);
355 let left: Vec<f32> = out.iter().map(|f| f.left).collect();
356 assert_eq!(left, [0.25, 0.5, 0.75, 1.0]);
357
358 handle.stop();
359 assert!(handle.is_finished() && !handle.is_playing());
360 assert!(!s.finished(), "dropped before the fade-out block");
361 s.render(&mut out, 0.01);
362 assert_eq!(out[3].left, 0.0);
363 assert!(s.finished());
364 }
365
366 #[test]
367 fn a_paused_stream_is_not_asked_for_samples() {
368 use std::sync::atomic::AtomicUsize;
369
370 let pulled = Arc::new(AtomicUsize::new(0));
371 let counter = Arc::clone(&pulled);
372 let (mut s, handle) = sound(
373 move |out: &mut [f32]| {
374 counter.fetch_add(1, Ordering::Relaxed);
375 out.fill(1.0);
376 },
377 100,
378 );
379 let mut out = [Frame::ZERO; 4];
380 handle.pause();
381 s.render(&mut out, 0.01); let after_fade = pulled.load(Ordering::Relaxed);
383 s.render(&mut out, 0.01);
384 s.render(&mut out, 0.01);
385 assert_eq!(pulled.load(Ordering::Relaxed), after_fade);
386 }
387
388 #[test]
389 fn the_mixer_pulls_samples_from_a_playing_stream() {
390 use std::sync::atomic::AtomicUsize;
391
392 use kira::backend::mock::{MockBackend, MockBackendSettings};
393 use kira::{AudioManager, AudioManagerSettings};
394
395 let pulled = Arc::new(AtomicUsize::new(0));
396 let counter = Arc::clone(&pulled);
397 let stream = move |out: &mut [f32]| {
398 counter.fetch_add(out.len(), Ordering::Relaxed);
399 out.fill(0.5);
400 };
401 let mut manager = AudioManager::<MockBackend>::new(AudioManagerSettings {
402 backend_settings: MockBackendSettings { sample_rate: 100 },
403 ..AudioManagerSettings::default()
404 })
405 .unwrap();
406 let (data, handle) = StreamData::new(Source::mono(Box::new(stream)), 100, 1.0, false);
407 manager.play(data).unwrap();
408 handle.mark_started();
409 manager.backend_mut().on_start_processing();
410 manager.backend_mut().process();
411 assert!(
412 pulled.load(Ordering::Relaxed) > 0,
413 "stream was never asked for samples"
414 );
415 assert!(handle.is_playing());
416
417 handle.stop();
418 manager.backend_mut().on_start_processing();
419 manager.backend_mut().process();
420 assert!(handle.is_finished());
421 }
422
423 #[test]
424 fn negative_start_volume_is_silence() {
425 let (mut s, handle) = sound_at(|out: &mut [f32]| out.fill(1.0), 100, -0.5);
426 assert_eq!(handle.volume(), 0.0);
427 let mut out = [Frame::ZERO; 2];
428 s.render(&mut out, 0.01);
429 assert!(out.iter().all(|f| f.left == 0.0 && f.right == 0.0));
430 }
431
432 #[test]
433 fn volume_changes_ramp_over_one_block() {
434 let (mut s, handle) = sound(|out: &mut [f32]| out.fill(1.0), 100);
435 handle.set_volume(0.25);
436 let mut out = [Frame::ZERO; 3];
437 s.render(&mut out, 0.01);
438 let left: Vec<f32> = out.iter().map(|f| f.left).collect();
439 assert_eq!(left, [0.75, 0.5, 0.25]);
440 s.render(&mut out, 0.01);
441 assert!(out.iter().all(|f| f.left == 0.25));
442 }
443
444 #[test]
445 fn a_stream_starts_at_its_volume_without_fading_in() {
446 let (mut s, _) = sound_at(|out: &mut [f32]| out.fill(1.0), 100, 0.5);
447 let mut out = [Frame::ZERO; 3];
448 s.render(&mut out, 0.01);
449 assert!(out.iter().all(|f| f.left == 0.5));
450 }
451
452 #[test]
453 fn stereo_frames_keep_their_channels() {
454 let mut n = 0u32;
455 let stream = move |out: &mut [[f32; 2]]| {
456 for f in out {
457 *f = [n as f32, -(n as f32)];
458 n += 1;
459 }
460 };
461 let (data, handle) = StreamData::new(Source::Stereo(Box::new(stream)), 50, 1.0, false);
462 let mut s = StreamSound::new(data.stream, data.sample_rate, handle.shared.clone());
463 let mut out = [Frame::ZERO; 4];
464 s.render(&mut out, 1.0 / 100.0);
465 let lr: Vec<(f32, f32)> = out.iter().map(|f| (f.left, f.right)).collect();
466 assert_eq!(lr, [(0.0, 0.0), (0.5, -0.5), (1.0, -1.0), (1.5, -1.5)]);
467 }
468}