1use alloc::boxed::Box;
21use alloc::vec::Vec;
22
23use crate::pcm::{Channels, Pcm};
24use crate::source::Source;
25
26pub mod queue;
27mod resample;
28pub mod stream;
29mod voice;
30
31#[cfg(test)]
32mod tests;
33
34#[cfg(not(target_has_atomic = "ptr"))]
36use alloc::rc::Rc as Shared;
37#[cfg(target_has_atomic = "ptr")]
38use alloc::sync::Arc as Shared;
39
40use self::queue::{Command, Inbox, Remote};
41use self::stream::Slot;
42use self::voice::{Origin, Sources, Voice};
43
44pub type SharedPcm = Shared<Pcm>;
46
47pub const UNITY: u16 = 4096;
49pub const RATE_ONE: u32 = 1 << 16;
51pub const RATE_MIN: u32 = RATE_ONE >> 8;
53pub const RATE_MAX: u32 = RATE_ONE << 8;
55
56const BLOCK_FRAMES: usize = 256;
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
62#[error("no room: every voice is busy")]
63pub struct Full;
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
67struct Handle {
68 index: u32,
69 generation: u32,
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub struct VoiceId(Handle);
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
79pub struct SoundId(Handle);
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
83pub struct StreamId(Handle);
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub struct Region {
89 start: u64,
90 end: u64,
91}
92
93impl Region {
94 pub const fn new(start: u64, end: u64) -> Option<Self> {
96 if start < end {
97 Some(Self { start, end })
98 } else {
99 None
100 }
101 }
102
103 pub const fn start(self) -> u64 {
105 self.start
106 }
107
108 pub const fn end(self) -> u64 {
110 self.end
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116#[non_exhaustive]
117pub struct Params {
118 pub volume: u16,
120 pub looped: bool,
122 pub rate: u32,
125 pub balance: (u16, u16),
127 pub region: Option<Region>,
129}
130
131impl Params {
132 pub const fn new() -> Self {
134 Self {
135 volume: UNITY,
136 looped: false,
137 rate: RATE_ONE,
138 balance: (UNITY, UNITY),
139 region: None,
140 }
141 }
142
143 #[must_use]
145 pub const fn with_volume(mut self, volume: u16) -> Self {
146 self.volume = volume;
147 self
148 }
149
150 #[must_use]
152 pub const fn looped(mut self) -> Self {
153 self.looped = true;
154 self
155 }
156
157 #[must_use]
159 pub const fn with_rate(mut self, rate: u32) -> Self {
160 self.rate = rate;
161 self
162 }
163
164 #[must_use]
166 pub const fn with_balance(mut self, left: u16, right: u16) -> Self {
167 self.balance = (left, right);
168 self
169 }
170
171 #[must_use]
173 pub const fn with_region(mut self, region: Region) -> Self {
174 self.region = Some(region);
175 self
176 }
177}
178
179impl Default for Params {
180 fn default() -> Self {
181 Self::new()
182 }
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
187#[non_exhaustive]
188pub struct Config {
189 pub rate: u32,
191 pub channels: Channels,
193 pub voices: usize,
195 pub sounds: usize,
197 pub streams: usize,
199}
200
201impl Config {
202 pub const fn new(rate: u32, channels: Channels, voices: usize) -> Self {
204 Self {
205 rate,
206 channels,
207 voices,
208 sounds: Self::DEFAULT_SOUNDS,
209 streams: Self::DEFAULT_STREAMS,
210 }
211 }
212
213 pub const DEFAULT_SOUNDS: usize = 256;
215 pub const DEFAULT_STREAMS: usize = 2;
217
218 #[must_use]
220 pub const fn with_sounds(mut self, sounds: usize) -> Self {
221 self.sounds = sounds;
222 self
223 }
224
225 #[must_use]
227 pub const fn with_streams(mut self, streams: usize) -> Self {
228 self.streams = streams;
229 self
230 }
231}
232
233pub struct Mixer {
235 config: Config,
236 channels: usize,
237 voices: Vec<Voice>,
238 sounds: Vec<Option<SharedPcm>>,
239 generations: Vec<u32>,
241 streams: Vec<Slot>,
242 block: Box<[i32]>,
244 underrun_frames: u64,
245}
246
247impl core::fmt::Debug for Mixer {
248 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
249 f.debug_struct("Mixer")
250 .field("config", &self.config)
251 .field("underrun_frames", &self.underrun_frames)
252 .finish_non_exhaustive()
253 }
254}
255
256impl Mixer {
257 pub fn new(config: Config) -> Self {
259 let channels = usize::from(config.channels.count());
260 Self {
261 config,
262 channels,
263 voices: alloc::vec![Voice::new(); config.voices],
264 sounds: alloc::vec![None; config.sounds],
265 generations: alloc::vec![0; config.sounds],
266 streams: (0..config.streams).map(|_| Slot::new()).collect(),
267 block: alloc::vec![0; BLOCK_FRAMES * channels].into_boxed_slice(),
268 underrun_frames: 0,
269 }
270 }
271
272 pub const fn config(&self) -> Config {
274 self.config
275 }
276
277 pub fn remote(&self, commands: usize) -> (Remote, Inbox) {
280 let sounds = self
281 .generations
282 .iter()
283 .zip(&self.sounds)
284 .map(|(generation, sound)| (*generation, sound.is_some()))
285 .collect();
286 let streams = self
287 .streams
288 .iter()
289 .map(|slot| (slot.generation, slot.source.is_some()))
290 .collect();
291 queue::ends(self.voices.len(), sounds, streams, commands)
292 }
293
294 pub fn put_sound(&mut self, sound: SoundId, pcm: SharedPcm) -> Option<SharedPcm> {
298 let Some(index) = usize::try_from(sound.0.index)
299 .ok()
300 .filter(|index| *index < self.sounds.len())
301 else {
302 return Some(pcm);
303 };
304 self.generations[index] = sound.0.generation;
305 self.sounds[index].replace(pcm)
306 }
307
308 pub fn put_stream(
311 &mut self,
312 stream: StreamId,
313 source: Box<dyn Source + Send>,
314 ) -> Option<Box<dyn Source + Send>> {
315 let Some(slot) = usize::try_from(stream.0.index)
316 .ok()
317 .and_then(|index| self.streams.get_mut(index))
318 else {
319 return Some(source);
320 };
321 let old = slot.source.take();
322 slot.hold(source);
323 slot.generation = stream.0.generation;
324 old
325 }
326
327 pub fn take_stream(&mut self, stream: StreamId) -> Option<Box<dyn Source + Send>> {
330 let index = usize::try_from(stream.0.index).ok()?;
331 if self.streams.get(index)?.generation != stream.0.generation {
332 return None;
333 }
334 for voice in &mut self.voices {
335 if voice.active && voice.origin == Origin::Stream(index) {
336 voice.finish(&mut self.streams);
337 }
338 }
339 let slot = &mut self.streams[index];
340 slot.retired = false;
341 slot.source.take()
342 }
343
344 pub fn add_sound(&mut self, pcm: SharedPcm) -> Result<SoundId, Full> {
346 let index = self.sounds.iter().position(Option::is_none).ok_or(Full)?;
347 self.sounds[index] = Some(pcm);
348 self.generations[index] = self.generations[index].wrapping_add(1);
349 Ok(SoundId(Handle {
350 index: u32::try_from(index).map_err(|_| Full)?,
351 generation: self.generations[index],
352 }))
353 }
354
355 pub fn remove_sound(&mut self, sound: SoundId) -> Option<SharedPcm> {
358 let index = self.sound_index(sound)?;
359 for voice in &mut self.voices {
360 if voice.active && voice.origin == Origin::Sound(index) {
361 voice.finish(&mut self.streams);
362 }
363 }
364 self.sounds[index].take()
365 }
366
367 fn sound_index(&self, sound: SoundId) -> Option<usize> {
368 let index = usize::try_from(sound.0.index).ok()?;
369 let live = self.generations.get(index) == Some(&sound.0.generation);
370 (live && self.sounds[index].is_some()).then_some(index)
371 }
372
373 pub fn add_stream(&mut self, source: Box<dyn Source + Send>) -> Result<StreamId, Full> {
377 let index = self
378 .streams
379 .iter()
380 .position(|slot| slot.source.is_none())
381 .or_else(|| self.streams.iter().position(|slot| slot.retired))
382 .ok_or(Full)?;
383 let slot = &mut self.streams[index];
384 slot.hold(source);
385 Ok(StreamId(Handle {
386 index: u32::try_from(index).map_err(|_| Full)?,
387 generation: slot.generation,
388 }))
389 }
390
391 pub fn take_finished_stream(&mut self) -> Option<Box<dyn Source + Send>> {
394 let slot = self.streams.iter_mut().find(|slot| slot.retired)?;
395 slot.retired = false;
396 slot.source.take()
397 }
398
399 fn free_voice(&self) -> Result<VoiceId, Full> {
401 let index = self
402 .voices
403 .iter()
404 .position(|voice| !voice.active)
405 .ok_or(Full)?;
406 Ok(VoiceId(Handle {
407 index: u32::try_from(index).map_err(|_| Full)?,
408 generation: self.voices[index].generation.wrapping_add(1),
409 }))
410 }
411
412 pub fn play(&mut self, sound: SoundId, params: Params) -> Result<VoiceId, Full> {
415 let voice = self.free_voice()?;
416 self.start_sound(voice, sound, params);
417 Ok(voice)
418 }
419
420 fn start_sound(&mut self, voice: VoiceId, sound: SoundId, params: Params) {
421 let (origin, rate) = match self.sound_index(sound) {
422 Some(index) => {
423 let rate = self.sounds[index].as_ref().map_or(1, |p| p.format().rate);
424 (Origin::Sound(index), rate)
425 }
426 None => (Origin::Nothing, 1),
427 };
428 self.start_voice(voice, origin, rate, params);
429 }
430
431 fn start_voice(&mut self, voice: VoiceId, origin: Origin, rate: u32, params: Params) {
432 let output_rate = self.config.rate;
433 if let Some(slot) = usize::try_from(voice.0.index)
434 .ok()
435 .and_then(|index| self.voices.get_mut(index))
436 {
437 slot.start(voice.0.generation, origin, rate, output_rate, params);
438 }
439 }
440
441 pub fn stream(
443 &mut self,
444 source: impl Source + Send + 'static,
445 params: Params,
446 ) -> Result<VoiceId, Full> {
447 self.free_voice()?;
448 let stream = self.add_stream(Box::new(source))?;
449 self.play_stream(stream, params)
450 }
451
452 pub fn play_stream(&mut self, stream: StreamId, params: Params) -> Result<VoiceId, Full> {
456 let voice = self.free_voice()?;
457 self.start_stream(voice, stream, params);
458 Ok(voice)
459 }
460
461 fn start_stream(&mut self, voice: VoiceId, stream: StreamId, params: Params) {
462 let slot = usize::try_from(stream.0.index)
463 .ok()
464 .and_then(|index| self.streams.get_mut(index).map(|slot| (index, slot)))
465 .filter(|(_, slot)| slot.generation == stream.0.generation && !slot.used);
466 let (origin, rate) = match slot {
467 Some((index, slot)) => match slot.source.as_ref() {
468 Some(source) => {
469 let rate = source.format().rate;
470 slot.used = true;
471 (Origin::Stream(index), rate)
472 }
473 None => (Origin::Nothing, 1),
474 },
475 None => (Origin::Nothing, 1),
476 };
477 self.start_voice(voice, origin, rate, params);
478 }
479
480 fn voice_mut(&mut self, id: VoiceId) -> Option<&mut Voice> {
482 let voice = self.voices.get_mut(usize::try_from(id.0.index).ok()?)?;
483 (voice.active && voice.generation == id.0.generation).then_some(voice)
484 }
485
486 fn voice(&self, id: VoiceId) -> Option<&Voice> {
487 let voice = self.voices.get(usize::try_from(id.0.index).ok()?)?;
488 (voice.active && voice.generation == id.0.generation).then_some(voice)
489 }
490
491 pub fn set_volume(&mut self, voice: VoiceId, volume: u16) {
493 if let Some(voice) = self.voice_mut(voice) {
494 voice.set_volume(volume);
495 }
496 }
497
498 pub fn fade_to(&mut self, voice: VoiceId, volume: u16, millis: u64) {
501 let frames = frames_of_millis(self.config.rate, millis);
502 if let Some(voice) = self.voice_mut(voice) {
503 voice.fade_to(volume, frames);
504 }
505 }
506
507 pub fn set_balance(&mut self, voice: VoiceId, left: u16, right: u16) {
509 if let Some(voice) = self.voice_mut(voice) {
510 voice.set_balance(left, right);
511 }
512 }
513
514 pub fn stop(&mut self, voice: VoiceId) {
516 let index = self.voice(voice).map(|_| voice.0.index);
517 if let Some(voice) = index
518 .and_then(|index| usize::try_from(index).ok())
519 .and_then(|index| self.voices.get_mut(index))
520 {
521 voice.finish(&mut self.streams);
522 }
523 }
524
525 pub fn pause(&mut self, voice: VoiceId) {
528 if let Some(voice) = self.voice_mut(voice) {
529 voice.paused = true;
530 }
531 }
532
533 pub fn resume(&mut self, voice: VoiceId) {
535 if let Some(voice) = self.voice_mut(voice) {
536 voice.paused = false;
537 }
538 }
539
540 pub fn is_playing(&self, voice: VoiceId) -> bool {
542 self.voice(voice).is_some()
543 }
544
545 pub fn is_paused(&self, voice: VoiceId) -> bool {
547 self.voice(voice).is_some_and(|voice| voice.paused)
548 }
549
550 pub fn volume(&self, voice: VoiceId) -> Option<u16> {
552 self.voice(voice).map(Voice::volume)
553 }
554
555 pub fn playing(&self) -> usize {
557 self.voices.iter().filter(|voice| voice.active).count()
558 }
559
560 pub const fn underrun_frames(&self) -> u64 {
564 self.underrun_frames
565 }
566
567 fn apply(&mut self, command: Command) {
570 match command {
571 Command::Play(voice, sound, params) => self.start_sound(voice, sound, params),
572 Command::Stream(voice, stream, params) => self.start_stream(voice, stream, params),
573 Command::SetVolume(voice, volume) => self.set_volume(voice, volume),
574 Command::FadeTo(voice, volume, millis) => self.fade_to(voice, volume, millis),
575 Command::SetBalance(voice, left, right) => self.set_balance(voice, left, right),
576 Command::Stop(voice) => self.stop(voice),
577 Command::Pause(voice) => self.pause(voice),
578 Command::Resume(voice) => self.resume(voice),
579 }
580 }
581
582 pub fn render(&mut self, out: &mut [i16]) {
586 for chunk in out.chunks_mut(self.block.len()) {
587 let whole = chunk.len() - chunk.len() % self.channels;
588 let block = &mut self.block[..whole];
589 block.fill(0);
590 let mut sources = Sources {
591 sounds: &self.sounds,
592 streams: &mut self.streams,
593 };
594 for voice in &mut self.voices {
595 if voice.active && !voice.paused {
596 self.underrun_frames += voice.mix(&mut sources, block, self.channels);
597 }
598 }
599 for (sample, mixed) in chunk.iter_mut().zip(block.iter()) {
600 *sample = clamp(*mixed);
601 }
602 chunk[whole..].fill(0);
603 }
604 }
605}
606
607fn frames_of_millis(rate: u32, millis: u64) -> u64 {
609 let rate = u64::from(rate);
610 (millis / 1000)
611 .saturating_mul(rate)
612 .saturating_add(millis % 1000 * rate / 1000)
613}
614
615fn clamp(value: i32) -> i16 {
617 i16::try_from(value.clamp(i32::from(i16::MIN), i32::from(i16::MAX))).unwrap_or(0)
618}