1use std::collections::{BTreeMap, VecDeque};
2use std::sync::{Arc, Mutex, MutexGuard};
3
4use crate::channel::{
5 BUILT_IN, Channel, ChannelId, DuckState, Ducking, RAMP_SECONDS, clamp_volume, ramp_frames,
6};
7use crate::clip::Clip;
8use crate::envelope::{EnvState, Envelope};
9use crate::filter::Kind;
10use crate::heard::{Bounds, MusicPlace};
11use crate::interp::{Kernel, MAX_RATIO, MIN_RATIO, Resample};
12use crate::music::{Curve, Direction, MusicTrack};
13use crate::pcm::{PcmSender, pcm_pair};
14use crate::sound::{Body, Rng, SoundDef, SoundId, SoundSpec, Steal, draw};
15use crate::synth::{Synth, Wave};
16use crate::voice::{Block, History, Source, Voice};
17
18pub const FILTER_RAMP_SECONDS: f32 = 0.03;
19
20const DEFAULT_SEED: u64 = 0x5EED_A0D1_0000_0001;
21
22#[derive(Clone, Copy, Debug, PartialEq)]
23pub struct Level {
24 pub volume: f32,
25 pub pan: f32,
26 pub fade_in: u64,
27 pub fade_out: u64,
28}
29
30impl Default for Level {
31 fn default() -> Self {
32 Self {
33 volume: 1.0,
34 pan: 0.0,
35 fade_in: 0,
36 fade_out: 0,
37 }
38 }
39}
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
42pub struct VoiceId(u64);
43
44#[derive(Clone, Debug)]
45pub struct Placement {
46 pub at: u64,
47 pub clip: Clip,
48 pub level: Level,
49}
50
51#[derive(Clone, Debug)]
52pub struct Timeline {
53 pub rate: u32,
54 pub channels: u16,
55 pub placements: Vec<Placement>,
56}
57
58impl Timeline {
59 pub fn new(rate: u32, channels: u16) -> Self {
60 Self {
61 rate,
62 channels,
63 placements: Vec::new(),
64 }
65 }
66
67 pub fn place(&mut self, at: u64, clip: Clip, level: Level) {
68 self.placements.push(Placement { at, clip, level });
69 }
70}
71
72pub fn mixdown(timeline: &Timeline) -> Vec<f32> {
73 let mut mixer = Mixer::new(timeline.rate, timeline.channels);
74 let mut end = 0u64;
75 for placement in &timeline.placements {
76 let matched = placement.clip.for_rate(timeline.rate);
77 end = end.max(placement.at.saturating_add(matched.frames()));
78 mixer.play_prepared(
79 ChannelId::EFFECTS,
80 placement.at,
81 matched,
82 None,
83 placement.level,
84 );
85 }
86 mixer.render(usize::try_from(end).unwrap_or(usize::MAX))
87}
88
89pub(crate) struct PcmQueue {
90 inner: Mutex<QueueInner>,
91}
92
93struct QueueInner {
94 samples: VecDeque<f32>,
95 ended: bool,
96}
97
98impl PcmQueue {
99 pub(crate) fn new() -> Arc<Self> {
100 Arc::new(Self {
101 inner: Mutex::new(QueueInner {
102 samples: VecDeque::new(),
103 ended: false,
104 }),
105 })
106 }
107
108 pub(crate) fn push(&self, samples: &[f32]) {
109 if samples.is_empty() {
110 return;
111 }
112 lock(&self.inner).samples.extend(samples);
113 }
114
115 pub(crate) fn end(&self) {
116 lock(&self.inner).ended = true;
117 }
118
119 pub(crate) fn len(&self) -> usize {
120 lock(&self.inner).samples.len()
121 }
122
123 pub(crate) fn reader_ended(&self) -> bool {
124 lock(&self.inner).ended
125 }
126
127 pub(crate) fn pop_frames(&self, frames: usize, channels: usize) -> Pop {
128 if channels == 0 || frames == 0 {
129 return Pop {
130 samples: Vec::new(),
131 real: 0,
132 available: 0,
133 ended: lock(&self.inner).ended,
134 };
135 }
136 let mut guard = lock(&self.inner);
137 let available = guard.samples.len().checked_div(channels).unwrap_or(0);
138 let real = available.min(frames);
139 let take = real * channels;
140 let mut samples: Vec<f32> = guard.samples.drain(..take).collect();
141 let ended = guard.ended;
142 drop(guard);
143 if let Some(len) = frames.checked_mul(channels) {
144 samples.resize(len, 0.0);
145 }
146 Pop {
147 samples,
148 real,
149 available,
150 ended,
151 }
152 }
153
154 pub(crate) fn is_finished(&self, channels: u16) -> bool {
155 let guard = lock(&self.inner);
156 guard.ended && guard.samples.len() < usize::from(channels.max(1))
157 }
158}
159
160pub(crate) struct Pop {
161 pub(crate) samples: Vec<f32>,
162 pub(crate) real: usize,
163 pub(crate) available: usize,
164 pub(crate) ended: bool,
165}
166
167pub struct Mixer {
168 rate: u32,
169 channels: u16,
170 clock: u64,
171 next_id: u64,
172 voices: Vec<Voice>,
173 buses: BTreeMap<ChannelId, Channel>,
174 master: Channel,
175 limiter: Option<f32>,
176 duck: Option<DuckState>,
177 music: Option<VoiceId>,
178 sounds: Vec<SoundDef>,
179 rng: Rng,
180 kernel: Kernel,
181 scratch: Vec<f32>,
182 duck_gains: Vec<f32>,
183 activity: Vec<bool>,
184}
185
186impl Mixer {
187 pub fn new(rate: u32, channels: u16) -> Self {
188 let mut buses = BTreeMap::new();
189 for id in BUILT_IN {
190 buses.insert(id, Channel::new());
191 }
192 Self {
193 rate,
194 channels,
195 clock: 0,
196 next_id: 0,
197 voices: Vec::new(),
198 buses,
199 master: Channel::new(),
200 limiter: None,
201 duck: None,
202 music: None,
203 sounds: Vec::new(),
204 rng: Rng::new(DEFAULT_SEED),
205 kernel: Kernel::new(),
206 scratch: Vec::new(),
207 duck_gains: Vec::new(),
208 activity: Vec::new(),
209 }
210 }
211
212 pub fn rate(&self) -> u32 {
213 self.rate
214 }
215
216 pub fn channels(&self) -> u16 {
217 self.channels
218 }
219
220 pub fn clock(&self) -> u64 {
221 self.clock
222 }
223
224 pub fn set_seed(&mut self, seed: u64) {
225 self.rng = Rng::new(seed);
226 }
227
228 pub fn set_master_volume(&mut self, volume: f32) {
229 self.master.volume = clamp_volume(volume);
230 retarget(&mut self.master, self.rate);
231 }
232
233 pub fn set_master_muted(&mut self, muted: bool) {
234 self.master.muted = muted;
235 retarget(&mut self.master, self.rate);
236 }
237
238 pub fn master_volume(&self) -> f32 {
239 self.master.volume
240 }
241
242 pub fn master_muted(&self) -> bool {
243 self.master.muted
244 }
245
246 pub fn set_limiter(&mut self, threshold: Option<f32>) {
247 self.limiter = threshold.map(|value| {
248 if value.is_nan() {
249 1.0
250 } else {
251 value.clamp(0.05, 0.999)
252 }
253 });
254 }
255
256 pub fn limiter(&self) -> Option<f32> {
257 self.limiter
258 }
259
260 pub fn set_channel_volume(&mut self, channel: ChannelId, volume: f32) {
261 let rate = self.rate;
262 let volume = clamp_volume(volume);
263 match self.buses.get_mut(&channel) {
264 Some(bus) => {
265 bus.volume = volume;
266 retarget(bus, rate);
267 }
268 None => {
269 let mut bus = Channel::new();
270 bus.volume = volume;
271 bus.gain.snap(volume);
272 self.buses.insert(channel, bus);
273 }
274 }
275 }
276
277 pub fn set_channel_muted(&mut self, channel: ChannelId, muted: bool) {
278 let rate = self.rate;
279 match self.buses.get_mut(&channel) {
280 Some(bus) => {
281 bus.muted = muted;
282 retarget(bus, rate);
283 }
284 None => {
285 let mut bus = Channel::new();
286 bus.muted = muted;
287 bus.gain.snap(bus.effective());
288 self.buses.insert(channel, bus);
289 }
290 }
291 }
292
293 pub fn channel_volume(&self, channel: ChannelId) -> f32 {
294 self.buses.get(&channel).map_or(1.0, |bus| bus.volume)
295 }
296
297 pub fn channel_gain(&self, channel: ChannelId) -> f32 {
298 self.buses
299 .get(&channel)
300 .map_or(1.0, |bus| bus.gain.current())
301 }
302
303 pub fn master_gain(&self) -> f32 {
304 self.master.gain.current()
305 }
306
307 pub fn channel_muted(&self, channel: ChannelId) -> bool {
308 self.buses.get(&channel).is_some_and(|bus| bus.muted)
309 }
310
311 pub fn set_channel_pitch(&mut self, channel: ChannelId, ratio: f32) {
312 self.glide_channel_pitch(channel, ratio, RAMP_SECONDS);
313 }
314
315 pub fn glide_channel_pitch(&mut self, channel: ChannelId, ratio: f32, seconds: f32) {
316 let frames = glide_frames(seconds, self.rate);
317 let ratio = clamp_ratio(ratio);
318 match self.buses.get_mut(&channel) {
319 Some(bus) => bus.pitch.to(ratio, frames),
320 None => {
321 let mut bus = Channel::new();
322 bus.pitch.snap(ratio);
323 self.buses.insert(channel, bus);
324 }
325 }
326 }
327
328 pub fn channel_pitch(&self, channel: ChannelId) -> f32 {
329 self.buses
330 .get(&channel)
331 .map_or(1.0, |bus| bus.pitch.current())
332 }
333
334 pub fn set_channel_lowpass(&mut self, channel: ChannelId, hz: Option<f32>) {
335 self.glide_channel_filter(channel, Kind::LowPass, hz, FILTER_RAMP_SECONDS);
336 }
337
338 pub fn set_channel_highpass(&mut self, channel: ChannelId, hz: Option<f32>) {
339 self.glide_channel_filter(channel, Kind::HighPass, hz, FILTER_RAMP_SECONDS);
340 }
341
342 pub fn glide_channel_lowpass(&mut self, channel: ChannelId, hz: Option<f32>, seconds: f32) {
343 self.glide_channel_filter(channel, Kind::LowPass, hz, seconds);
344 }
345
346 pub fn glide_channel_highpass(&mut self, channel: ChannelId, hz: Option<f32>, seconds: f32) {
347 self.glide_channel_filter(channel, Kind::HighPass, hz, seconds);
348 }
349
350 pub fn channel_lowpass(&self, channel: ChannelId) -> Option<f32> {
351 self.buses
352 .get(&channel)
353 .and_then(|bus| bus.lowpass.target())
354 }
355
356 pub fn channel_highpass(&self, channel: ChannelId) -> Option<f32> {
357 self.buses
358 .get(&channel)
359 .and_then(|bus| bus.highpass.target())
360 }
361
362 fn glide_channel_filter(
363 &mut self,
364 channel: ChannelId,
365 kind: Kind,
366 hz: Option<f32>,
367 seconds: f32,
368 ) {
369 let frames = glide_frames(seconds, self.rate);
370 let rate = self.rate;
371 let bus = self.buses.entry(channel).or_insert_with(Channel::new);
372 match kind {
373 Kind::LowPass => bus.lowpass.set(hz, frames, rate),
374 Kind::HighPass => bus.highpass.set(hz, frames, rate),
375 }
376 }
377
378 pub fn set_ducking(&mut self, ducking: Option<Ducking>) {
379 self.duck = ducking.map(DuckState::new);
380 }
381
382 pub fn ducking(&self) -> Option<Ducking> {
383 self.duck.as_ref().map(|state| state.config)
384 }
385
386 pub fn ducked_db(&self) -> f32 {
387 self.duck.as_ref().map_or(0.0, |state| state.reduction_db)
388 }
389
390 pub fn play(&mut self, clip: &Clip, level: Level) -> VoiceId {
391 self.play_on(ChannelId::EFFECTS, clip, level)
392 }
393
394 pub fn play_at(&mut self, at: u64, clip: &Clip, level: Level) -> VoiceId {
395 self.play_at_on(ChannelId::EFFECTS, at, clip, level)
396 }
397
398 pub fn play_on(&mut self, channel: ChannelId, clip: &Clip, level: Level) -> VoiceId {
399 self.play_at_on(channel, self.clock, clip, level)
400 }
401
402 pub fn play_at_on(
403 &mut self,
404 channel: ChannelId,
405 at: u64,
406 clip: &Clip,
407 level: Level,
408 ) -> VoiceId {
409 self.play_prepared(channel, at, clip.for_rate(self.rate), None, level)
410 }
411
412 pub fn play_music(&mut self, track: &MusicTrack, fade_seconds: f32, level: Level) -> VoiceId {
413 let looping = track.loop_frames(self.rate);
414 let clip = track.clip.for_rate(self.rate);
415 let id = self.play_prepared(ChannelId::MUSIC, self.clock, clip, looping, level);
416 self.begin_music(id, fade_seconds);
417 id
418 }
419
420 pub fn stop_music(&mut self, fade_seconds: f32) {
421 if let Some(old) = self.music.take() {
422 self.stop(old, fade_seconds);
423 }
424 }
425
426 pub fn music(&self) -> Option<VoiceId> {
427 self.music
428 .filter(|id| self.voices.iter().any(|voice| voice.id == *id))
429 }
430
431 pub fn music_position(&self) -> Option<f64> {
432 let place = self.music_place()?;
433 Some(place.frame_after(0.0) / f64::from(self.rate.max(1)))
434 }
435
436 pub(crate) fn music_place(&self) -> Option<MusicPlace> {
437 let id = self.music?;
438 let voice = self.voices.iter().find(|voice| voice.id == id)?;
439 let bounds = match &voice.source {
440 Source::Buffer {
441 frames,
442 looping: Some((start, end)),
443 ..
444 } if *end > *start => Bounds::Loop(*start, *end),
445 Source::Buffer { frames, .. } => Bounds::Once(*frames),
446 _ => Bounds::Open,
447 };
448 let played = match bounds {
449 Bounds::Loop(start, end) if voice.laps > 0 => {
450 end as f64 + (voice.laps - 1) as f64 * (end - start) as f64 + voice.cursor
451 - start as f64
452 }
453 _ => voice.cursor,
454 };
455 let bus = self
456 .buses
457 .get(&voice.channel)
458 .map_or(1.0, |bus| bus.pitch.current());
459 let ratio = (voice.pitch.current() * bus).clamp(MIN_RATIO, MAX_RATIO);
460 Some(MusicPlace {
461 played,
462 ratio: f64::from(ratio),
463 bounds,
464 })
465 }
466
467 pub fn stop(&mut self, id: VoiceId, fade_seconds: f32) {
468 let len = seconds_to_frames(fade_seconds, self.rate);
469 let clock = self.clock;
470 if let Some(voice) = self.voices.iter_mut().find(|voice| voice.id == id) {
471 voice.ends = None;
472 }
473 if let Some(voice) = self.voices.iter_mut().find(|voice| voice.id == id)
474 && let Some(state) = voice.envelope.as_mut()
475 {
476 if voice.rendered {
477 state.release(len);
478 } else {
479 self.voices.retain(|voice| voice.id != id);
480 }
481 return;
482 }
483 if len == 0 {
484 self.voices.retain(|voice| voice.id != id);
485 return;
486 }
487 if let Some(voice) = self.voices.iter_mut().find(|voice| voice.id == id) {
488 voice.curve = Some(Curve {
489 start: clock,
490 len,
491 direction: Direction::Out,
492 });
493 }
494 }
495
496 pub fn set_volume(&mut self, id: VoiceId, volume: f32) {
497 if let Some(voice) = self.voices.iter_mut().find(|voice| voice.id == id) {
498 voice.level.volume = volume;
499 }
500 }
501
502 pub fn set_pan(&mut self, id: VoiceId, pan: f32) {
503 if let Some(voice) = self.voices.iter_mut().find(|voice| voice.id == id) {
504 voice.level.pan = pan;
505 }
506 }
507
508 pub fn set_pitch(&mut self, id: VoiceId, ratio: f32) {
509 self.glide_pitch(id, ratio, RAMP_SECONDS);
510 }
511
512 pub fn glide_pitch(&mut self, id: VoiceId, ratio: f32, seconds: f32) {
513 let frames = glide_frames(seconds, self.rate);
514 if let Some(voice) = self.voices.iter_mut().find(|voice| voice.id == id) {
515 voice.pitch.to(clamp_ratio(ratio), frames);
516 }
517 }
518
519 pub fn pitch(&self, id: VoiceId) -> Option<f32> {
520 self.voices
521 .iter()
522 .find(|voice| voice.id == id)
523 .map(|voice| voice.pitch.current())
524 }
525
526 pub fn set_resample(&mut self, id: VoiceId, resample: Resample) {
527 if let Some(voice) = self.voices.iter_mut().find(|voice| voice.id == id) {
528 voice.resample = resample;
529 }
530 }
531
532 pub fn set_gain(&mut self, id: VoiceId, gain: f32) {
533 self.glide_gain(id, gain, RAMP_SECONDS);
534 }
535
536 pub fn glide_gain(&mut self, id: VoiceId, gain: f32, seconds: f32) {
537 let frames = glide_frames(seconds, self.rate);
538 let gain = if gain.is_nan() { 0.0 } else { gain.max(0.0) };
539 if let Some(voice) = self.voices.iter_mut().find(|voice| voice.id == id) {
540 voice.gain.to(gain, frames);
541 }
542 }
543
544 pub fn tone(&mut self, channel: ChannelId, wave: Wave, hz: f32, volume: f32) -> VoiceId {
545 self.tone_in(0, channel, wave, hz, volume)
546 }
547
548 pub fn tone_in(
549 &mut self,
550 offset: u64,
551 channel: ChannelId,
552 wave: Wave,
553 hz: f32,
554 volume: f32,
555 ) -> VoiceId {
556 let level = Level {
557 volume,
558 ..Level::default()
559 };
560 let seed = self.rng.seed();
561 self.push_voice(
562 channel,
563 self.clock.saturating_add(offset),
564 Source::Tone(Synth::new(wave, hz, seed)),
565 level,
566 )
567 }
568
569 pub fn set_envelope(&mut self, id: VoiceId, envelope: Option<Envelope>) {
570 let rate = self.rate;
571 if let Some(voice) = self.voices.iter_mut().find(|voice| voice.id == id) {
572 voice.envelope = envelope.map(|envelope| EnvState::new(envelope, rate, voice.rendered));
573 }
574 }
575
576 pub fn envelope_level(&self, id: VoiceId) -> Option<f32> {
577 self.voices
578 .iter()
579 .find(|voice| voice.id == id)
580 .and_then(|voice| voice.envelope.as_ref().map(EnvState::level))
581 }
582
583 pub fn pcm_stream(
584 &mut self,
585 channel: ChannelId,
586 channels: u16,
587 capacity_seconds: f32,
588 level: Level,
589 ) -> (VoiceId, PcmSender) {
590 let capacity = seconds_to_frames(capacity_seconds, self.rate) as usize;
591 let (voice, sender) = pcm_pair(channels.max(1), capacity, self.rate);
592 let id = self.push_voice(channel, self.clock, Source::Pcm(voice), level);
593 (id, sender)
594 }
595
596 pub fn set_tone_frequency(&mut self, id: VoiceId, hz: f32) {
597 self.glide_tone_frequency(id, hz, RAMP_SECONDS);
598 }
599
600 pub fn glide_tone_frequency(&mut self, id: VoiceId, hz: f32, seconds: f32) {
601 let frames = glide_frames(seconds, self.rate);
602 let hz = if hz.is_nan() { 0.0 } else { hz.max(0.0) };
603 if let Some(voice) = self.voices.iter_mut().find(|voice| voice.id == id)
604 && let Source::Tone(synth) = &mut voice.source
605 {
606 synth.frequency.to(hz, frames);
607 }
608 }
609
610 pub fn sound(&mut self, clip: &Clip, spec: SoundSpec) -> SoundId {
611 let prepared = clip.for_rate(self.rate);
612 let frames = prepared.frames();
613 let channels = prepared.channels;
614 self.sounds.push(SoundDef {
615 body: Body::Clip {
616 samples: Arc::new(prepared.samples),
617 channels,
618 frames,
619 },
620 spec,
621 });
622 SoundId(self.sounds.len() - 1)
623 }
624
625 pub fn tone_sound(
626 &mut self,
627 wave: Wave,
628 hz: f32,
629 seconds: Option<f32>,
630 spec: SoundSpec,
631 ) -> SoundId {
632 let hz = if hz.is_nan() { 0.0 } else { hz.max(0.0) };
633 let frames = seconds.map(|seconds| seconds_to_frames(seconds, self.rate));
634 self.sounds.push(SoundDef {
635 body: Body::Tone { wave, hz, frames },
636 spec,
637 });
638 SoundId(self.sounds.len() - 1)
639 }
640
641 pub fn trigger(&mut self, sound: SoundId) -> Option<VoiceId> {
642 self.trigger_at(self.clock, sound)
643 }
644
645 pub fn trigger_in(&mut self, offset: u64, sound: SoundId) -> Option<VoiceId> {
646 self.trigger_at(self.clock.saturating_add(offset), sound)
647 }
648
649 pub fn trigger_at(&mut self, at: u64, sound: SoundId) -> Option<VoiceId> {
650 self.trigger_at_with(at, sound, 1.0)
651 }
652
653 pub fn trigger_with(&mut self, sound: SoundId, volume: f32) -> Option<VoiceId> {
654 self.trigger_at_with(self.clock, sound, volume)
655 }
656
657 pub fn trigger_in_with(&mut self, offset: u64, sound: SoundId, volume: f32) -> Option<VoiceId> {
658 self.trigger_at_with(self.clock.saturating_add(offset), sound, volume)
659 }
660
661 pub fn trigger_at_with(&mut self, at: u64, sound: SoundId, volume: f32) -> Option<VoiceId> {
662 let volume = if volume.is_nan() {
663 0.0
664 } else {
665 volume.max(0.0)
666 };
667 let spec = self.sounds.get(sound.0)?.spec;
668 if spec.max_voices == 0 {
669 return None;
670 }
671 let rate = self.rate;
672 let fade = seconds_to_frames(spec.steal_fade, rate);
673 self.make_room(sound, &spec);
674 let drawn = draw(&spec, &mut self.rng);
675 let (source, ends) = match &self.sounds[sound.0].body {
676 Body::Clip {
677 samples,
678 channels,
679 frames,
680 } => (
681 Source::Buffer {
682 samples: Arc::clone(samples),
683 channels: *channels,
684 frames: *frames,
685 looping: None,
686 },
687 None,
688 ),
689 &Body::Tone { wave, hz, frames } => (
690 Source::Tone(Synth::new(wave, hz, self.rng.seed())),
691 frames.map(|frames| (at.saturating_add(frames), fade)),
692 ),
693 };
694 let mut level = spec.level;
695 level.volume *= drawn.volume * volume;
696 let id = self.push_voice(spec.channel, at, source, level);
697 if let Some(voice) = self.voices.last_mut() {
698 voice.sound = Some(sound);
699 voice.pitch.snap(clamp_ratio(drawn.pitch));
700 voice.resample = spec.resample;
701 voice.ends = ends;
702 voice.envelope = spec
703 .envelope
704 .map(|envelope| EnvState::new(envelope, rate, false));
705 }
706 Some(id)
707 }
708
709 pub fn voices_of(&self, sound: SoundId) -> usize {
710 self.voices
711 .iter()
712 .filter(|voice| voice.sound == Some(sound) && !voice.fading())
713 .count()
714 }
715
716 pub fn voice_count(&self) -> usize {
717 self.voices.len()
718 }
719
720 fn make_room(&mut self, sound: SoundId, spec: &SoundSpec) {
721 let clock = self.clock;
722 let fade = seconds_to_frames(spec.steal_fade, self.rate);
723 let mut live = self.voices_of(sound);
724 while live >= spec.max_voices {
725 let candidates = self
726 .voices
727 .iter()
728 .enumerate()
729 .filter(|(_, voice)| voice.sound == Some(sound) && !voice.fading());
730 let pick = match spec.steal {
731 Steal::Oldest => candidates.min_by_key(|(_, voice)| (voice.start, voice.id)),
732 Steal::Quietest => candidates.min_by(|(_, a), (_, b)| {
733 a.loudness(clock)
734 .total_cmp(&b.loudness(clock))
735 .then(a.id.cmp(&b.id))
736 }),
737 };
738 let Some((index, _)) = pick else {
739 break;
740 };
741 live -= 1;
742 let voice = &mut self.voices[index];
743 voice.ends = None;
744 if voice.rendered && fade > 0 {
745 voice.curve = Some(Curve {
746 start: clock,
747 len: fade,
748 direction: Direction::Out,
749 });
750 } else {
751 self.voices.remove(index);
752 }
753 }
754 }
755
756 pub fn render(&mut self, frames: usize) -> Vec<f32> {
757 let len = frames.saturating_mul(self.channels as usize);
758 let mut out = vec![0.0; len];
759 self.render_into(&mut out);
760 out
761 }
762
763 pub fn render_into(&mut self, out: &mut [f32]) {
764 let out_channels = self.channels as usize;
765 let frames = out.len().checked_div(out_channels).unwrap_or(0);
766 let body = frames * out_channels;
767 out[..body].fill(0.0);
768 out[body..].fill(0.0);
769 if frames == 0 {
770 return;
771 }
772 let clock = self.clock;
773 for bus in self.buses.values_mut() {
774 bus.touched = false;
775 bus.prepare_pitch(frames);
776 }
777 self.fill_duck_gains(clock, frames);
778 for voice in &mut self.voices {
779 let Some(bus) = self.buses.get_mut(&voice.channel) else {
780 continue;
781 };
782 if !bus.touched {
783 bus.bus.clear();
784 bus.bus.resize(body, 0.0);
785 bus.touched = true;
786 }
787 let block = Block {
788 clock,
789 frames,
790 out_channels,
791 rate: self.rate,
792 kernel: &self.kernel,
793 pitch: bus.pitch_on.then_some(bus.pitch_curve.as_slice()),
794 };
795 voice.mix_into(&mut bus.bus, &block);
796 }
797 let duck_target = self.duck.as_ref().map(|state| state.config.target);
798 for (id, bus) in self.buses.iter_mut() {
799 if !bus.touched {
800 bus.gain.skip(frames);
801 bus.lowpass.idle(frames);
802 bus.highpass.idle(frames);
803 continue;
804 }
805 bus.lowpass
806 .process(&mut bus.bus, frames, out_channels, self.rate);
807 bus.highpass
808 .process(&mut bus.bus, frames, out_channels, self.rate);
809 let ducked = duck_target == Some(*id);
810 for frame in 0..frames {
811 let mut gain = bus.gain.next();
812 if ducked {
813 gain *= self.duck_gains[frame];
814 }
815 let base = frame * out_channels;
816 for channel in 0..out_channels {
817 out[base + channel] += bus.bus[base + channel] * gain;
818 }
819 }
820 }
821 for frame in 0..frames {
822 let gain = self.master.gain.next();
823 let base = frame * out_channels;
824 for channel in 0..out_channels {
825 out[base + channel] *= gain;
826 }
827 }
828 if let Some(threshold) = self.limiter {
829 for sample in &mut out[..body] {
830 *sample = soft_limit(*sample, threshold);
831 }
832 }
833 for sample in &mut out[..body] {
834 *sample = sample.clamp(-1.0, 1.0);
835 }
836 self.clock = self.clock.saturating_add(frames as u64);
837 let clock = self.clock;
838 self.voices.retain(|voice| !voice.finished(clock));
839 }
840
841 pub(crate) fn render_i16(&mut self, out: &mut [i16]) {
842 let channels = self.channels as usize;
843 let frames = out.len().checked_div(channels).unwrap_or(0);
844 let len = frames.saturating_mul(channels);
845 let mut scratch = std::mem::take(&mut self.scratch);
846 scratch.resize(len, 0.0);
847 self.render_into(&mut scratch);
848 for (slot, sample) in out.iter_mut().zip(scratch.iter()) {
849 *slot = (sample.clamp(-1.0, 1.0) * f32::from(i16::MAX)) as i16;
850 }
851 for slot in out.iter_mut().skip(scratch.len()) {
852 *slot = 0;
853 }
854 self.scratch = scratch;
855 }
856
857 pub(crate) fn stream(&mut self, queue: Arc<PcmQueue>, channels: u16, level: Level) -> VoiceId {
858 self.stream_on(ChannelId::EFFECTS, queue, channels, level)
859 }
860
861 pub(crate) fn stream_on(
862 &mut self,
863 channel: ChannelId,
864 queue: Arc<PcmQueue>,
865 channels: u16,
866 level: Level,
867 ) -> VoiceId {
868 self.push_voice(
869 channel,
870 self.clock,
871 Source::Stream {
872 queue,
873 channels,
874 history: History::new(),
875 },
876 level,
877 )
878 }
879
880 #[cfg_attr(not(feature = "vorbis"), allow(dead_code))]
881 pub(crate) fn stream_music(
882 &mut self,
883 queue: Arc<PcmQueue>,
884 channels: u16,
885 fade_seconds: f32,
886 level: Level,
887 ) -> VoiceId {
888 let id = self.stream_on(ChannelId::MUSIC, queue, channels, level);
889 self.begin_music(id, fade_seconds);
890 id
891 }
892
893 fn begin_music(&mut self, id: VoiceId, fade_seconds: f32) {
894 let len = seconds_to_frames(fade_seconds, self.rate);
895 let clock = self.clock;
896 if let Some(old) = self.music.replace(id) {
897 self.stop(old, fade_seconds);
898 }
899 if len > 0
900 && let Some(voice) = self.voices.iter_mut().find(|voice| voice.id == id)
901 {
902 voice.curve = Some(Curve {
903 start: clock,
904 len,
905 direction: Direction::In,
906 });
907 }
908 }
909
910 fn fill_duck_gains(&mut self, clock: u64, frames: usize) {
911 let Some(state) = self.duck.as_mut() else {
912 return;
913 };
914 self.activity.clear();
915 self.activity.resize(frames, false);
916 let end = clock + frames as u64;
917 for voice in self
918 .voices
919 .iter()
920 .filter(|voice| voice.channel == state.config.trigger)
921 {
922 let (from, to) = voice.audible_span(clock, end);
923 let from = from.max(clock);
924 let to = to.min(end);
925 for at in from..to {
926 self.activity[(at - clock) as usize] = true;
927 }
928 }
929 self.duck_gains.clear();
930 for frame in 0..frames {
931 self.duck_gains
932 .push(state.step(self.activity[frame], self.rate));
933 }
934 }
935
936 fn play_prepared(
937 &mut self,
938 channel: ChannelId,
939 at: u64,
940 clip: Clip,
941 looping: Option<(u64, u64)>,
942 level: Level,
943 ) -> VoiceId {
944 let frames = clip.frames();
945 self.push_voice(
946 channel,
947 at,
948 Source::Buffer {
949 samples: Arc::new(clip.samples),
950 channels: clip.channels,
951 frames,
952 looping,
953 },
954 level,
955 )
956 }
957
958 fn push_voice(&mut self, channel: ChannelId, at: u64, source: Source, level: Level) -> VoiceId {
959 let id = self.alloc();
960 self.buses.entry(channel).or_insert_with(Channel::new);
961 self.voices.push(Voice::new(id, channel, at, level, source));
962 id
963 }
964
965 fn alloc(&mut self) -> VoiceId {
966 self.next_id = self.next_id.saturating_add(1);
967 VoiceId(self.next_id)
968 }
969}
970
971fn retarget(channel: &mut Channel, rate: u32) {
972 let target = channel.effective();
973 channel.gain.to(target, ramp_frames(rate));
974}
975
976fn seconds_to_frames(seconds: f32, rate: u32) -> u64 {
977 if seconds.is_nan() || seconds <= 0.0 {
978 return 0;
979 }
980 (f64::from(seconds) * f64::from(rate)).round() as u64
981}
982
983fn glide_frames(seconds: f32, rate: u32) -> u32 {
984 u32::try_from(seconds_to_frames(seconds, rate))
985 .unwrap_or(u32::MAX)
986 .max(1)
987}
988
989fn clamp_ratio(ratio: f32) -> f32 {
990 if ratio.is_nan() {
991 1.0
992 } else {
993 ratio.clamp(MIN_RATIO, MAX_RATIO)
994 }
995}
996
997fn soft_limit(sample: f32, threshold: f32) -> f32 {
998 let magnitude = sample.abs();
999 if magnitude <= threshold {
1000 return sample;
1001 }
1002 let over = (magnitude - threshold) / (1.0 - threshold);
1003 let shaped = threshold + (1.0 - threshold) * over / (1.0 + over);
1004 shaped.copysign(sample)
1005}
1006
1007pub(crate) fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
1008 mutex.lock().unwrap_or_else(|poison| poison.into_inner())
1009}
1010
1011#[cfg(test)]
1012mod tests {
1013 use super::*;
1014 use crate::interp::tone_level;
1015
1016 fn bits(samples: &[f32]) -> Vec<u32> {
1017 samples.iter().map(|sample| sample.to_bits()).collect()
1018 }
1019
1020 fn mono(samples: &[f32]) -> Clip {
1021 Clip {
1022 rate: 48_000,
1023 channels: 1,
1024 samples: samples.to_vec(),
1025 }
1026 }
1027
1028 #[test]
1029 fn mixer_sums_and_clips() {
1030 let mut mixer = Mixer::new(48_000, 1);
1031 mixer.play(&mono(&[0.25]), Level::default());
1032 mixer.play(&mono(&[0.25]), Level::default());
1033 assert_eq!(bits(&mixer.render(1)), bits(&[0.5]));
1034
1035 let mut mixer = Mixer::new(48_000, 1);
1036 mixer.play(&mono(&[0.75]), Level::default());
1037 mixer.play(&mono(&[0.5]), Level::default());
1038 assert_eq!(bits(&mixer.render(1)), bits(&[1.0]));
1039
1040 let mut mixer = Mixer::new(48_000, 1);
1041 mixer.play(&mono(&[-0.75]), Level::default());
1042 mixer.play(&mono(&[-0.5]), Level::default());
1043 assert_eq!(bits(&mixer.render(1)), bits(&[-1.0]));
1044
1045 let mut mixer = Mixer::new(48_000, 1);
1046 mixer.play(
1047 &mono(&[1.0]),
1048 Level {
1049 volume: 2.0,
1050 ..Level::default()
1051 },
1052 );
1053 assert_eq!(bits(&mixer.render(1)), bits(&[1.0]));
1054
1055 let mut mixer = Mixer::new(48_000, 2);
1056 mixer.play(
1057 &mono(&[1.0]),
1058 Level {
1059 pan: -1.0,
1060 ..Level::default()
1061 },
1062 );
1063 mixer.play(
1064 &mono(&[1.0]),
1065 Level {
1066 pan: 1.0,
1067 ..Level::default()
1068 },
1069 );
1070 assert_eq!(bits(&mixer.render(1)), bits(&[1.0, 1.0]));
1071
1072 let mut mixer = Mixer::new(48_000, 2);
1073 mixer.play(&mono(&[1.0]), Level::default());
1074 mixer.play(&mono(&[1.0]), Level::default());
1075 assert_eq!(bits(&mixer.render(1)), bits(&[1.0, 1.0]));
1076
1077 let mut mixer = Mixer::new(48_000, 1);
1078 let id = mixer.play(&mono(&[0.5, 0.5]), Level::default());
1079 assert_eq!(bits(&mixer.render(1)), bits(&[0.5]));
1080 mixer.set_volume(id, 0.0);
1081 mixer.set_pan(id, -1.0);
1082 assert_eq!(bits(&mixer.render(1)), bits(&[0.0]));
1083 assert_eq!(bits(&mixer.render(1)), bits(&[0.0]));
1084 }
1085
1086 #[test]
1087 fn fades_ramp_at_the_start_and_the_end() {
1088 let clip = mono(&[1.0; 8]);
1089 let mut mixer = Mixer::new(48_000, 1);
1090 mixer.play(
1091 &clip,
1092 Level {
1093 fade_in: 4,
1094 ..Level::default()
1095 },
1096 );
1097 assert_eq!(
1098 bits(&mixer.render(8)),
1099 bits(&[0.0, 0.25, 0.5, 0.75, 1.0, 1.0, 1.0, 1.0])
1100 );
1101
1102 let mut mixer = Mixer::new(48_000, 1);
1103 mixer.play(
1104 &clip,
1105 Level {
1106 fade_out: 4,
1107 ..Level::default()
1108 },
1109 );
1110 assert_eq!(
1111 bits(&mixer.render(8)),
1112 bits(&[1.0, 1.0, 1.0, 1.0, 1.0, 0.75, 0.5, 0.25])
1113 );
1114
1115 let mut mixer = Mixer::new(48_000, 1);
1116 mixer.play(
1117 &mono(&[1.0; 4]),
1118 Level {
1119 fade_in: 4,
1120 fade_out: 4,
1121 ..Level::default()
1122 },
1123 );
1124 assert_eq!(bits(&mixer.render(4)), bits(&[0.0, 0.1875, 0.25, 0.1875]));
1125 }
1126
1127 #[test]
1128 fn matching_rates_play_the_samples_unchanged() {
1129 let samples = vec![0.0, 1.0, 0.0, -1.0];
1130 let clip = Clip {
1131 rate: 16_000,
1132 channels: 1,
1133 samples: samples.clone(),
1134 };
1135 let mut mixer = Mixer::new(16_000, 1);
1136 mixer.play(&clip, Level::default());
1137 assert_eq!(bits(&mixer.render(4)), bits(&samples));
1138
1139 let stereo = vec![0.1, -0.2, 0.3, -0.4, 0.5, -0.6];
1140 let clip = Clip {
1141 rate: 16_000,
1142 channels: 2,
1143 samples: stereo.clone(),
1144 };
1145 let mut mixer = Mixer::new(16_000, 2);
1146 mixer.play(&clip, Level::default());
1147 assert_eq!(bits(&mixer.render(3)), bits(&stereo));
1148 }
1149
1150 #[test]
1151 fn a_stream_plays_queued_samples_and_fades_once_it_ends() {
1152 let queue = PcmQueue::new();
1153 queue.push(&[0.25, -0.25, 0.5, -0.5]);
1154 let mut mixer = Mixer::new(48_000, 2);
1155 mixer.stream(Arc::clone(&queue), 2, Level::default());
1156 assert_eq!(bits(&mixer.render(2)), bits(&[0.25, -0.25, 0.5, -0.5]));
1157 assert_eq!(bits(&mixer.render(1)), bits(&[0.0, 0.0]));
1158
1159 let queue = PcmQueue::new();
1160 queue.push(&[1.0, 1.0, 1.0, 1.0]);
1161 let mut mixer = Mixer::new(48_000, 1);
1162 mixer.stream(
1163 Arc::clone(&queue),
1164 1,
1165 Level {
1166 fade_out: 2,
1167 ..Level::default()
1168 },
1169 );
1170 assert_eq!(bits(&mixer.render(4)), bits(&[1.0, 1.0, 1.0, 1.0]));
1171
1172 let queue = PcmQueue::new();
1173 queue.push(&[1.0, 1.0, 1.0, 1.0]);
1174 queue.end();
1175 let mut mixer = Mixer::new(48_000, 1);
1176 mixer.stream(
1177 queue,
1178 1,
1179 Level {
1180 fade_out: 2,
1181 ..Level::default()
1182 },
1183 );
1184 assert_eq!(bits(&mixer.render(2)), bits(&[1.0, 1.0]));
1185 assert_eq!(bits(&mixer.render(2)), bits(&[1.0, 0.5]));
1186 }
1187
1188 #[test]
1189 fn mixdown_bytes_are_the_sample_exact_sum() {
1190 let mut timeline = Timeline::new(48_000, 1);
1191 let clip = mono(&[0.5, 0.5, 0.5, 0.5]);
1192 timeline.place(0, clip.clone(), Level::default());
1193 timeline.place(2, clip, Level::default());
1194 let once = mixdown(&timeline);
1195 let twice = mixdown(&timeline);
1196 let expect = [0.5, 0.5, 1.0, 1.0, 0.5, 0.5];
1197 assert_eq!(bits(&once), bits(&expect));
1198 assert_eq!(bits(&once), bits(&twice));
1199 let wav_once = Clip {
1200 rate: 48_000,
1201 channels: 1,
1202 samples: once,
1203 }
1204 .to_wav();
1205 let wav_twice = Clip {
1206 rate: 48_000,
1207 channels: 1,
1208 samples: twice,
1209 }
1210 .to_wav();
1211 assert_eq!(wav_once, wav_twice);
1212 }
1213
1214 fn constant(value: f32, frames: usize) -> Clip {
1215 mono(&vec![value; frames])
1216 }
1217
1218 fn slow(value: f32, frames: usize) -> Clip {
1219 Clip {
1220 rate: 1000,
1221 channels: 1,
1222 samples: vec![value; frames],
1223 }
1224 }
1225
1226 fn sine(period: f32, amplitude: f32, frames: usize) -> Clip {
1227 mono(
1228 &(0..frames)
1229 .map(|i| amplitude * (std::f32::consts::TAU * i as f32 / period).sin())
1230 .collect::<Vec<_>>(),
1231 )
1232 }
1233
1234 fn power(samples: &[f32]) -> f32 {
1235 samples.iter().map(|sample| sample * sample).sum::<f32>() / samples.len() as f32
1236 }
1237
1238 fn decibels(ratio: f32) -> f32 {
1239 10.0 * ratio.log10()
1240 }
1241
1242 #[test]
1243 fn today_s_calls_play_on_the_effects_channel() {
1244 let mut mixer = Mixer::new(48_000, 1);
1245 mixer.play(&constant(0.5, 2000), Level::default());
1246 mixer.set_channel_volume(ChannelId::EFFECTS, 0.0);
1247 mixer.render(600);
1248 assert_eq!(mixer.render(1)[0], 0.0);
1249
1250 let mut mixer = Mixer::new(48_000, 1);
1251 mixer.play(&constant(0.5, 2000), Level::default());
1252 mixer.set_channel_volume(ChannelId::MUSIC, 0.0);
1253 mixer.set_channel_volume(ChannelId::DIALOG, 0.0);
1254 mixer.render(600);
1255 assert_eq!(mixer.render(1)[0], 0.5);
1256 }
1257
1258 #[test]
1259 fn channel_volumes_master_and_voice_level_multiply() {
1260 let mut mixer = Mixer::new(48_000, 1);
1261 mixer.set_master_volume(0.5);
1262 mixer.set_channel_volume(ChannelId::MUSIC, 0.5);
1263 mixer.set_channel_volume(ChannelId::EFFECTS, 0.25);
1264 mixer.set_channel_volume(ChannelId::DIALOG, 1.0);
1265 let half = Level {
1266 volume: 0.5,
1267 ..Level::default()
1268 };
1269 mixer.play_on(ChannelId::MUSIC, &constant(0.8, 2000), half);
1270 mixer.play_on(ChannelId::EFFECTS, &constant(0.8, 2000), Level::default());
1271 mixer.play_on(ChannelId::DIALOG, &constant(0.2, 2000), Level::default());
1272 mixer.render(700);
1273 let settled = mixer.render(1)[0];
1274 let expect = 0.5 * (0.8 * 0.5 * 0.5 + 0.8 * 0.25 + 0.2);
1275 assert!((settled - expect).abs() < 1e-6, "{settled} vs {expect}");
1276 assert_eq!(mixer.master_gain(), 0.5);
1277 assert_eq!(mixer.channel_gain(ChannelId::MUSIC), 0.5);
1278 }
1279
1280 #[test]
1281 fn a_custom_channel_has_its_own_volume() {
1282 let ui = ChannelId::custom(0);
1283 let mut mixer = Mixer::new(48_000, 1);
1284 mixer.set_channel_volume(ui, 0.25);
1285 mixer.play_on(ui, &constant(1.0, 10), Level::default());
1286 mixer.play(&constant(0.5, 10), Level::default());
1287 assert_eq!(mixer.render(1)[0], 0.75);
1288 assert_eq!(mixer.channel_volume(ui), 0.25);
1289 assert_eq!(mixer.channel_volume(ChannelId::custom(1)), 1.0);
1290 }
1291
1292 #[test]
1293 fn muting_a_channel_silences_only_that_channel() {
1294 let mut mixer = Mixer::new(48_000, 1);
1295 mixer.play_on(ChannelId::MUSIC, &constant(0.25, 4000), Level::default());
1296 mixer.play_on(ChannelId::EFFECTS, &constant(0.5, 4000), Level::default());
1297 mixer.set_channel_muted(ChannelId::MUSIC, true);
1298 assert!(mixer.channel_muted(ChannelId::MUSIC));
1299 assert_eq!(mixer.channel_volume(ChannelId::MUSIC), 1.0);
1300 mixer.render(600);
1301 assert_eq!(mixer.render(1)[0], 0.5);
1302 mixer.set_channel_muted(ChannelId::MUSIC, false);
1303 mixer.render(600);
1304 assert_eq!(mixer.render(1)[0], 0.75);
1305 mixer.set_master_muted(true);
1306 mixer.render(600);
1307 assert_eq!(mixer.render(1)[0], 0.0);
1308 assert!(mixer.master_muted());
1309 }
1310
1311 #[test]
1312 fn a_volume_change_ramps_in_steps_of_a_few_thousandths() {
1313 let mut mixer = Mixer::new(48_000, 1);
1314 mixer.play(&constant(1.0, 4000), Level::default());
1315 mixer.render(10);
1316 mixer.set_channel_volume(ChannelId::EFFECTS, 0.0);
1317 let down = mixer.render(600);
1318 mixer.set_channel_volume(ChannelId::EFFECTS, 1.0);
1319 let up = mixer.render(600);
1320 mixer.set_master_volume(0.0);
1321 let master = mixer.render(600);
1322 for run in [&down, &up, &master] {
1323 let largest = run
1324 .windows(2)
1325 .map(|pair| (pair[1] - pair[0]).abs())
1326 .fold(0.0, f32::max);
1327 assert!(largest < 0.003, "step {largest}");
1328 }
1329 assert_eq!(*down.last().unwrap(), 0.0);
1330 assert_eq!(*up.last().unwrap(), 1.0);
1331 assert!(down[0] > 0.0 && down[0] < 1.0);
1332 assert_eq!(*master.last().unwrap(), 0.0);
1333 }
1334
1335 #[test]
1336 fn the_music_position_follows_the_loop_and_unwraps_for_the_latency() {
1337 let track = MusicTrack::with_loop(constant(0.5, 1000), 200, 800);
1338 let mut mixer = Mixer::new(48_000, 1);
1339 assert_eq!(mixer.music_position(), None);
1340 mixer.play_music(&track, 0.0, Level::default());
1341 assert_eq!(mixer.music_position(), Some(0.0));
1342 mixer.render(1000);
1343 assert_eq!(mixer.music_position(), Some(400.0 / 48_000.0));
1344 let place = mixer.music_place().unwrap();
1345 assert_eq!(place.frame_after(-300.0), 700.0);
1346 assert_eq!(place.frame_after(-1200.0), 0.0);
1347 mixer.render(1200);
1348 assert_eq!(mixer.music_place().unwrap().frame_after(-300.0), 700.0);
1349 mixer.stop_music(0.0);
1350 assert_eq!(mixer.music_position(), None);
1351 }
1352
1353 #[test]
1354 fn a_loop_seam_is_as_smooth_as_the_tone_itself() {
1355 let tone = sine(100.0, 0.5, 1000);
1356 let track = MusicTrack::with_loop(tone.clone(), 200, 800);
1357 let mut mixer = Mixer::new(48_000, 1);
1358 mixer.play_music(&track, 0.0, Level::default());
1359 let out = mixer.render(3000);
1360 assert_eq!(bits(&out[..800]), bits(&tone.samples[..800]));
1361 assert_eq!(out[800].to_bits(), tone.samples[200].to_bits());
1362 assert_eq!(out[1400].to_bits(), tone.samples[200].to_bits());
1363 let intrinsic = 0.5 * std::f32::consts::TAU / 100.0;
1364 let largest = out
1365 .windows(2)
1366 .map(|pair| (pair[1] - pair[0]).abs())
1367 .fold(0.0, f32::max);
1368 assert!(largest <= intrinsic * 1.001, "{largest} vs {intrinsic}");
1369 assert!(mixer.music().is_some());
1370 }
1371
1372 #[test]
1373 fn a_track_without_a_loop_ends() {
1374 let mut mixer = Mixer::new(48_000, 1);
1375 mixer.play_music(&MusicTrack::once(constant(0.5, 4)), 0.0, Level::default());
1376 assert_eq!(
1377 bits(&mixer.render(6)),
1378 bits(&[0.5, 0.5, 0.5, 0.5, 0.0, 0.0])
1379 );
1380 assert!(mixer.music().is_none());
1381 }
1382
1383 #[test]
1384 fn a_crossfade_keeps_the_music_within_half_a_decibel() {
1385 let first = MusicTrack::looping(sine(100.0, 0.5, 4800));
1386 let second = MusicTrack::looping(sine(80.0, 0.5, 4800));
1387 let mut mixer = Mixer::new(48_000, 1);
1388 let old = mixer.play_music(&first, 0.0, Level::default());
1389 let before = mixer.render(4800);
1390 let reference = power(&before);
1391 let new = mixer.play_music(&second, 0.1, Level::default());
1392 assert_ne!(old, new);
1393 assert_eq!(mixer.music(), Some(new));
1394 let fade = mixer.render(4800);
1395 for window in fade.chunks(1200) {
1396 let drift = decibels(power(window) / reference);
1397 assert!(drift.abs() < 0.5, "{drift} dB");
1398 }
1399 assert_eq!(mixer.voices.len(), 1);
1400 let after = mixer.render(4800);
1401 assert!((decibels(power(&after) / reference)).abs() < 0.5);
1402 }
1403
1404 #[test]
1405 fn stopping_with_a_fade_ends_the_voice_at_silence() {
1406 let mut mixer = Mixer::new(1000, 1);
1407 let id = mixer.play(&slow(1.0, 5000), Level::default());
1408 mixer.render(10);
1409 mixer.stop(id, 0.1);
1410 let out = mixer.render(100);
1411 assert!(out[0] > 0.99 && out[99] < 0.05);
1412 assert!(mixer.voices.is_empty());
1413 let id = mixer.play(&slow(1.0, 5000), Level::default());
1414 mixer.stop(id, 0.0);
1415 assert_eq!(mixer.render(1)[0], 0.0);
1416 }
1417
1418 fn ducked_mixer() -> Mixer {
1419 let mut mixer = Mixer::new(1000, 1);
1420 mixer.play_music(&MusicTrack::looping(slow(0.5, 100)), 0.0, Level::default());
1421 mixer.play_at_on(ChannelId::DIALOG, 200, &slow(0.0, 500), Level::default());
1422 mixer
1423 }
1424
1425 #[test]
1426 fn ducking_is_off_until_a_game_sets_it() {
1427 let mut mixer = ducked_mixer();
1428 assert_eq!(mixer.ducking(), None);
1429 let out = mixer.render(900);
1430 assert!(out.iter().all(|sample| *sample == 0.5));
1431 assert_eq!(mixer.ducked_db(), 0.0);
1432 }
1433
1434 #[test]
1435 fn dialog_ducks_the_music_with_attack_and_release_timing() {
1436 let ducking = Ducking::dialog_over_music(12.0, 0.1, 0.2);
1437 let mut mixer = ducked_mixer();
1438 mixer.set_ducking(Some(ducking));
1439 assert_eq!(mixer.ducking(), Some(ducking));
1440 let out = mixer.render(1000);
1441 let full = 0.5 * 10f32.powf(-12.0 / 20.0);
1442 let half = 0.5 * 10f32.powf(-6.0 / 20.0);
1443 assert_eq!(out[199], 0.5);
1444 assert!(out[200] < 0.5 && out[200] > half);
1445 assert!((out[249] - half).abs() < 1e-4, "{}", out[249]);
1446 assert!((out[299] - full).abs() < 1e-4, "{}", out[299]);
1447 assert!((out[500] - full).abs() < 1e-4);
1448 assert!((out[699] - full).abs() < 1e-4);
1449 assert!((out[799] - half).abs() < 1e-4, "{}", out[799]);
1450 assert!((out[899] - 0.5).abs() < 1e-4, "{}", out[899]);
1451 assert_eq!(out[999], 0.5);
1452 let monotone = out[200..300].windows(2).all(|pair| pair[1] < pair[0]);
1453 assert!(monotone);
1454
1455 let mut blocks = ducked_mixer();
1456 blocks.set_ducking(Some(ducking));
1457 let mut pieces = Vec::new();
1458 for size in [37, 1, 150, 12, 800] {
1459 pieces.extend(blocks.render(size));
1460 }
1461 assert_eq!(bits(&pieces), bits(&out));
1462 }
1463
1464 #[test]
1465 fn ducking_only_dips_its_target_channel() {
1466 let mut mixer = ducked_mixer();
1467 mixer.set_ducking(Some(Ducking::dialog_over_music(12.0, 0.1, 0.2)));
1468 mixer.play(&slow(0.25, 1000), Level::default());
1469 let out = mixer.render(500);
1470 let full = 0.5 * 10f32.powf(-12.0 / 20.0);
1471 assert!((out[400] - (full + 0.25)).abs() < 1e-4);
1472 }
1473
1474 fn looped_sine(mixer: &mut Mixer, hz: u32, amplitude: f32) -> VoiceId {
1475 let rate = mixer.rate();
1476 let clip = Clip {
1477 rate,
1478 channels: 1,
1479 samples: (0..rate)
1480 .map(|i| {
1481 amplitude
1482 * (std::f64::consts::TAU * f64::from(hz) * f64::from(i) / f64::from(rate))
1483 .sin() as f32
1484 })
1485 .collect(),
1486 };
1487 mixer.play_music(&MusicTrack::looping(clip), 0.0, Level::default())
1488 }
1489
1490 fn largest_step(samples: &[f32]) -> f32 {
1491 samples
1492 .windows(2)
1493 .map(|pair| (pair[1] - pair[0]).abs())
1494 .fold(0.0, f32::max)
1495 }
1496
1497 fn up_crossings(samples: &[f32], rate: f32) -> f32 {
1498 let mut times = Vec::new();
1499 for (i, pair) in samples.windows(2).enumerate() {
1500 if pair[0] < 0.0 && pair[1] >= 0.0 {
1501 times.push(i as f32 + pair[0].abs() / (pair[1] - pair[0]));
1502 }
1503 }
1504 let span = times[times.len() - 1] - times[0];
1505 (times.len() - 1) as f32 * rate / span
1506 }
1507
1508 #[test]
1509 fn a_voice_pitch_scales_its_frequency_and_the_channel_pitch_scales_all() {
1510 let mut mixer = Mixer::new(48_000, 1);
1511 let id = looped_sine(&mut mixer, 1000, 0.5);
1512 mixer.set_pitch(id, 1.5);
1513 mixer.render(2000);
1514 let out = mixer.render(8192);
1515 assert!(tone_level(&out, 48_000.0, 1500.0) > 0.49);
1516 assert!(tone_level(&out, 48_000.0, 1000.0) < 0.005);
1517 assert_eq!(mixer.pitch(id), Some(1.5));
1518
1519 let mut mixer = Mixer::new(48_000, 1);
1520 looped_sine(&mut mixer, 1000, 0.5);
1521 mixer.set_channel_pitch(ChannelId::MUSIC, 0.5);
1522 mixer.render(2000);
1523 let out = mixer.render(8192);
1524 assert!(tone_level(&out, 48_000.0, 500.0) > 0.49);
1525 assert_eq!(mixer.channel_pitch(ChannelId::MUSIC), 0.5);
1526 assert_eq!(mixer.channel_pitch(ChannelId::EFFECTS), 1.0);
1527 }
1528
1529 #[test]
1530 fn pitch_stays_inside_its_limits() {
1531 let mut mixer = Mixer::new(48_000, 1);
1532 let id = looped_sine(&mut mixer, 100, 0.5);
1533 mixer.set_pitch(id, 1000.0);
1534 mixer.render(1000);
1535 assert_eq!(mixer.pitch(id), Some(8.0));
1536 mixer.set_pitch(id, f32::NAN);
1537 mixer.render(1000);
1538 assert_eq!(mixer.pitch(id), Some(1.0));
1539 }
1540
1541 #[test]
1542 fn interpolated_playback_matches_the_tone_at_a_fractional_rate() {
1543 let mut mixer = Mixer::new(48_000, 1);
1544 let id = looped_sine(&mut mixer, 480, 0.5);
1545 mixer.set_pitch(id, 1.25);
1546 mixer.render(2000);
1547 let out = mixer.render(4800);
1548 let worst = out
1549 .iter()
1550 .enumerate()
1551 .map(|(i, sample)| {
1552 let t = (2000 + i) as f64 + 1.0;
1553 let _ = t;
1554 sample.abs()
1555 })
1556 .fold(0.0, f32::max);
1557 assert!((worst - 0.5).abs() < 0.01, "{worst}");
1558 assert!(tone_level(&out, 48_000.0, 600.0) > 0.495);
1559 }
1560
1561 #[test]
1562 fn pitch_rate_cutoff_and_volume_ramps_never_click() {
1563 let intrinsic = |hz: f32| 0.5 * std::f32::consts::TAU * hz / 48_000.0;
1564
1565 let mut mixer = Mixer::new(48_000, 1);
1566 let id = looped_sine(&mut mixer, 440, 0.5);
1567 mixer.render(500);
1568 mixer.glide_pitch(id, 4.0, 0.3);
1569 let up = mixer.render(20_000);
1570 mixer.glide_pitch(id, 0.25, 0.3);
1571 let down = mixer.render(20_000);
1572 assert!(
1573 largest_step(&up) <= intrinsic(1760.0) * 1.05,
1574 "{}",
1575 largest_step(&up)
1576 );
1577 assert!(
1578 largest_step(&down) <= intrinsic(1760.0) * 1.05,
1579 "{}",
1580 largest_step(&down)
1581 );
1582
1583 let mut mixer = Mixer::new(48_000, 1);
1584 looped_sine(&mut mixer, 440, 0.5);
1585 mixer.render(500);
1586 mixer.glide_channel_pitch(ChannelId::MUSIC, 3.0, 0.2);
1587 let swept = mixer.render(20_000);
1588 assert!(largest_step(&swept) <= intrinsic(1320.0) * 1.05);
1589 mixer.set_channel_pitch(ChannelId::MUSIC, 1.0);
1590 let jumped = mixer.render(2000);
1591 assert!(largest_step(&jumped) <= intrinsic(1320.0) * 1.05);
1592
1593 let mut mixer = Mixer::new(48_000, 1);
1594 looped_sine(&mut mixer, 440, 0.5);
1595 mixer.render(500);
1596 mixer.set_channel_lowpass(ChannelId::MUSIC, Some(300.0));
1597 let closing = mixer.render(4000);
1598 mixer.set_channel_highpass(ChannelId::MUSIC, Some(2000.0));
1599 let high = mixer.render(4000);
1600 mixer.set_channel_lowpass(ChannelId::MUSIC, None);
1601 mixer.set_channel_highpass(ChannelId::MUSIC, None);
1602 let opening = mixer.render(4000);
1603 for run in [&closing, &high, &opening] {
1604 eprintln!(
1605 "filter run step {} vs {}",
1606 largest_step(run),
1607 intrinsic(440.0)
1608 );
1609 assert!(
1610 largest_step(run) <= intrinsic(440.0) * 1.5,
1611 "{}",
1612 largest_step(run)
1613 );
1614 }
1615
1616 let mut mixer = Mixer::new(48_000, 1);
1617 let id = looped_sine(&mut mixer, 440, 0.5);
1618 mixer.render(500);
1619 mixer.glide_gain(id, 0.0, 0.05);
1620 let fade = mixer.render(4000);
1621 mixer.glide_gain(id, 1.0, 0.05);
1622 let back = mixer.render(4000);
1623 assert!(largest_step(&fade) <= intrinsic(440.0) * 1.1);
1624 assert!(largest_step(&back) <= intrinsic(440.0) * 1.1);
1625 assert_eq!(*fade.last().unwrap(), 0.0);
1626 }
1627
1628 #[test]
1629 fn aliasing_after_a_speed_up_is_far_below_the_tone() {
1630 let rate = 48_000.0;
1631 let cases = [
1632 (2.0, 17_000u32, 14_000.0f32),
1633 (2.0, 21_000, 6_000.0),
1634 (4.0, 11_000, 4_000.0),
1635 (4.0, 21_000, 12_000.0),
1636 ];
1637 for (ratio, source, alias) in cases {
1638 let mut mixer = Mixer::new(48_000, 1);
1639 let id = looped_sine(&mut mixer, source, 0.5);
1640 mixer.set_pitch(id, ratio);
1641 mixer.render(3000);
1642 let out = mixer.render(16_384);
1643 let level = tone_level(&out, rate, alias) / 0.5;
1644 let db = 20.0 * level.max(1e-9).log10();
1645 eprintln!("alias {ratio}x {source} Hz -> {alias} Hz: {db:.1} dB");
1646 assert!(db < -60.0, "{ratio}x {source} Hz: {db} dB");
1647 }
1648 for ratio in [2.0f32, 4.0] {
1649 let source = (3000.0 / ratio) as u32;
1650 let mut mixer = Mixer::new(48_000, 1);
1651 let id = looped_sine(&mut mixer, source, 0.5);
1652 mixer.set_pitch(id, ratio);
1653 mixer.render(3000);
1654 let out = mixer.render(16_384);
1655 let gain = tone_level(&out, rate, source as f32 * ratio) / 0.5;
1656 let db = 20.0 * gain.log10();
1657 eprintln!("passband {ratio}x: {db:.3} dB");
1658 assert!(db.abs() < 0.1, "{db}");
1659 }
1660 }
1661
1662 fn hits() -> Clip {
1663 Clip {
1664 rate: 48_000,
1665 channels: 1,
1666 samples: (0..2400)
1667 .map(|i| {
1668 let decay = 1.0 - i as f32 / 2400.0;
1669 0.5 * decay * (std::f32::consts::TAU * 600.0 * i as f32 / 48_000.0).sin()
1670 })
1671 .collect(),
1672 }
1673 }
1674
1675 fn burst(seed: u64, steal: Steal, limiter: Option<f32>) -> (Vec<f32>, usize) {
1676 let mut mixer = Mixer::new(48_000, 1);
1677 mixer.set_seed(seed);
1678 mixer.set_limiter(limiter);
1679 let sound = mixer.sound(
1680 &hits(),
1681 SoundSpec {
1682 max_voices: 12,
1683 steal,
1684 pitch_jitter: 3.0,
1685 volume_jitter: 0.3,
1686 ..SoundSpec::default()
1687 },
1688 );
1689 let mut out = Vec::new();
1690 let mut worst = 0;
1691 for _ in 0..48 {
1692 for _ in 0..209 {
1693 mixer.trigger(sound);
1694 assert!(mixer.voices_of(sound) <= 12);
1695 }
1696 worst = worst.max(mixer.voice_count());
1697 out.extend(mixer.render(1000));
1698 }
1699 (out, worst)
1700 }
1701
1702 #[test]
1703 fn ten_thousand_hits_in_a_second_play_a_capped_varied_set() {
1704 let (hard, worst) = burst(1, Steal::Oldest, None);
1705 assert!(worst <= 24, "{worst} voices");
1706 assert!(hard.iter().any(|sample| sample.abs() >= 1.0));
1707 let (soft, _) = burst(1, Steal::Oldest, Some(0.8));
1708 assert!(soft.iter().all(|sample| sample.abs() < 1.0));
1709 assert!(soft.iter().any(|sample| sample.abs() > 0.8));
1710 let (quiet, _) = burst(1, Steal::Quietest, Some(0.8));
1711 assert!(quiet.iter().all(|sample| sample.abs() < 1.0));
1712 assert_ne!(bits(&soft), bits(&quiet));
1713 }
1714
1715 #[test]
1716 fn the_cap_and_the_jitter_repeat_from_the_same_seed() {
1717 let (a, _) = burst(42, Steal::Oldest, Some(0.8));
1718 let (b, _) = burst(42, Steal::Oldest, Some(0.8));
1719 let (c, _) = burst(43, Steal::Oldest, Some(0.8));
1720 assert_eq!(bits(&a), bits(&b));
1721 assert_ne!(bits(&a), bits(&c));
1722
1723 let mut mixer = Mixer::new(48_000, 1);
1724 let sound = mixer.sound(
1725 &hits(),
1726 SoundSpec {
1727 pitch_jitter: 2.0,
1728 volume_jitter: 0.5,
1729 ..SoundSpec::default()
1730 },
1731 );
1732 let ids: Vec<VoiceId> = (0..50).filter_map(|_| mixer.trigger(sound)).collect();
1733 let pitches: Vec<f32> = ids.iter().filter_map(|id| mixer.pitch(*id)).collect();
1734 let low = 2f32.powf(-2.0 / 12.0);
1735 let high = 2f32.powf(2.0 / 12.0);
1736 assert!(pitches.iter().all(|p| *p >= low && *p <= high));
1737 let spread = pitches.iter().cloned().fold(0.0, f32::max)
1738 - pitches.iter().cloned().fold(f32::MAX, f32::min);
1739 assert!(spread > 0.05, "{spread}");
1740 assert_eq!(mixer.voices_of(sound), 50);
1741 }
1742
1743 #[test]
1744 fn a_full_sound_steals_its_oldest_or_its_quietest_voice() {
1745 for (steal, loud_first) in [(Steal::Oldest, true), (Steal::Quietest, true)] {
1746 let mut mixer = Mixer::new(48_000, 1);
1747 let sound = mixer.sound(
1748 &hits(),
1749 SoundSpec {
1750 max_voices: 2,
1751 steal,
1752 ..SoundSpec::default()
1753 },
1754 );
1755 let first = mixer.trigger(sound).unwrap();
1756 let second = mixer.trigger(sound).unwrap();
1757 let (loud, soft) = if loud_first {
1758 (first, second)
1759 } else {
1760 (second, first)
1761 };
1762 mixer.set_volume(loud, 0.9);
1763 mixer.set_volume(soft, 0.2);
1764 mixer.render(10);
1765 let third = mixer.trigger(sound).unwrap();
1766 assert_eq!(mixer.voices_of(sound), 2);
1767 let victim = if steal == Steal::Oldest { first } else { soft };
1768 let fading: Vec<VoiceId> = mixer
1769 .voices
1770 .iter()
1771 .filter(|voice| voice.fading())
1772 .map(|voice| voice.id)
1773 .collect();
1774 assert_eq!(fading, vec![victim], "{steal:?}");
1775 assert!(mixer.voices.iter().any(|voice| voice.id == third));
1776 mixer.render(2000);
1777 assert!(mixer.voices.iter().all(|voice| voice.id != victim));
1778 }
1779 let mut mixer = Mixer::new(48_000, 1);
1780 let silent = mixer.sound(
1781 &hits(),
1782 SoundSpec {
1783 max_voices: 0,
1784 ..SoundSpec::default()
1785 },
1786 );
1787 assert!(mixer.trigger(silent).is_none());
1788 }
1789
1790 #[test]
1791 fn the_soft_limiter_is_off_by_default_and_never_reaches_full_scale() {
1792 let mut mixer = Mixer::new(48_000, 1);
1793 assert_eq!(mixer.limiter(), None);
1794 mixer.play(&constant(0.9, 4), Level::default());
1795 mixer.play(&constant(0.9, 4), Level::default());
1796 assert_eq!(bits(&mixer.render(4)), bits(&[1.0; 4]));
1797 mixer.set_limiter(Some(0.8));
1798 mixer.play(&constant(0.5, 4), Level::default());
1799 assert_eq!(mixer.render(1)[0], 0.5);
1800 mixer.render(3);
1801 mixer.play(&constant(0.9, 4), Level::default());
1802 mixer.play(&constant(0.9, 4), Level::default());
1803 mixer.play(&constant(-3.0, 4), Level::default());
1804 let out = mixer.render(1)[0];
1805 assert!(out < -0.8 && out > -1.0, "{out}");
1806 assert!(soft_limit(5.0, 0.8) < 1.0 && soft_limit(5.0, 0.8) > 0.95);
1807 assert_eq!(soft_limit(-0.3, 0.8), -0.3);
1808 let knee = soft_limit(0.8001, 0.8) - soft_limit(0.7999, 0.8);
1809 assert!((knee - 0.0002).abs() < 1e-4);
1810 }
1811
1812 fn tone_of(wave: Wave, hz: f32) -> f32 {
1813 let mut mixer = Mixer::new(48_000, 1);
1814 mixer.tone(ChannelId::EFFECTS, wave, hz, 0.5);
1815 let out = mixer.render(48_000);
1816 up_crossings(&out, 48_000.0)
1817 }
1818
1819 #[test]
1820 fn a_tone_voice_holds_its_frequency_within_a_tenth_of_a_percent() {
1821 for wave in [Wave::Sine, Wave::Square, Wave::Saw] {
1822 for hz in [110.0f32, 440.0, 1234.5, 4000.0] {
1823 let measured = tone_of(wave, hz);
1824 let error = (measured - hz).abs() / hz;
1825 assert!(error < 0.001, "{wave:?} {hz} Hz measured {measured}");
1826 }
1827 }
1828 let mut mixer = Mixer::new(48_000, 1);
1829 let id = mixer.tone(ChannelId::EFFECTS, Wave::Saw, 300.0, 0.5);
1830 mixer.set_pitch(id, 2.0);
1831 mixer.render(2000);
1832 let out = mixer.render(48_000);
1833 let measured = up_crossings(&out, 48_000.0);
1834 assert!((measured - 600.0).abs() / 600.0 < 0.001, "{measured}");
1835 }
1836
1837 #[test]
1838 fn a_tone_is_band_limited_and_bounded() {
1839 let mut mixer = Mixer::new(48_000, 1);
1840 mixer.tone(ChannelId::EFFECTS, Wave::Saw, 2000.0, 1.0);
1841 let out = mixer.render(16_384);
1842 assert!(out.iter().all(|sample| sample.abs() <= 1.0));
1843 let alias = tone_level(&out, 48_000.0, 20_000.0);
1844 let partial = tone_level(&out, 48_000.0, 2000.0);
1845 assert!(alias < 0.1 * partial, "{alias} vs {partial}");
1846 assert!(
1847 (partial - 2.0 / std::f32::consts::PI).abs() < 0.05,
1848 "{partial}"
1849 );
1850
1851 let mut mixer = Mixer::new(48_000, 1);
1852 mixer.tone(ChannelId::EFFECTS, Wave::Sine, 1000.0, 0.5);
1853 let out = mixer.render(8192);
1854 assert!((tone_level(&out, 48_000.0, 1000.0) - 0.5).abs() < 0.005);
1855 }
1856
1857 #[test]
1858 fn a_driven_tone_glides_and_fades_without_clicks() {
1859 let mut mixer = Mixer::new(48_000, 1);
1860 let id = mixer.tone(ChannelId::EFFECTS, Wave::Sine, 200.0, 1.0);
1861 mixer.render(1000);
1862 mixer.glide_tone_frequency(id, 2000.0, 0.5);
1863 let rising = mixer.render(30_000);
1864 assert!(largest_step(&rising) <= std::f32::consts::TAU * 2000.0 / 48_000.0 * 1.01);
1865 let settled = mixer.render(8192);
1866 assert!(tone_level(&settled, 48_000.0, 2000.0) > 0.99);
1867 mixer.glide_gain(id, 0.0, 0.05);
1868 let fade = mixer.render(4000);
1869 assert!(largest_step(&fade) <= std::f32::consts::TAU * 2000.0 / 48_000.0 * 1.01);
1870 assert_eq!(*fade.last().unwrap(), 0.0);
1871 mixer.stop(id, 0.01);
1872 mixer.render(1000);
1873 assert_eq!(mixer.voice_count(), 0);
1874 }
1875
1876 fn filtered_gain(low: bool, cutoff: f32) -> f32 {
1877 let mut mixer = Mixer::new(48_000, 1);
1878 looped_sine(&mut mixer, cutoff as u32, 0.5);
1879 if low {
1880 mixer.set_channel_lowpass(ChannelId::MUSIC, Some(cutoff));
1881 } else {
1882 mixer.set_channel_highpass(ChannelId::MUSIC, Some(cutoff));
1883 }
1884 mixer.render(8000);
1885 let out = mixer.render(9600);
1886 tone_level(&out, 48_000.0, cutoff) / 0.5
1887 }
1888
1889 #[test]
1890 fn the_filters_are_three_decibels_down_at_their_cutoff() {
1891 for cutoff in [200.0f32, 1000.0, 5000.0] {
1892 for low in [true, false] {
1893 let gain = filtered_gain(low, cutoff);
1894 let db = 20.0 * gain.log10();
1895 assert!((db + 3.01).abs() < 0.5, "low {low} {cutoff} Hz: {db} dB");
1896 }
1897 }
1898 }
1899
1900 #[test]
1901 fn the_filters_pass_and_stop_either_side_of_the_cutoff() {
1902 let response = |low: bool, cutoff: f32, tone: u32| {
1903 let mut mixer = Mixer::new(48_000, 1);
1904 looped_sine(&mut mixer, tone, 0.5);
1905 if low {
1906 mixer.set_channel_lowpass(ChannelId::MUSIC, Some(cutoff));
1907 } else {
1908 mixer.set_channel_highpass(ChannelId::MUSIC, Some(cutoff));
1909 }
1910 mixer.render(8000);
1911 let out = mixer.render(9600);
1912 tone_level(&out, 48_000.0, tone as f32) / 0.5
1913 };
1914 assert!(response(true, 2000.0, 200) > 0.99);
1915 assert!(response(true, 2000.0, 8000) < 0.1);
1916 assert!(response(false, 2000.0, 8000) > 0.99);
1917 assert!(response(false, 2000.0, 200) < 0.1);
1918
1919 let mut mixer = Mixer::new(48_000, 1);
1920 assert_eq!(mixer.channel_lowpass(ChannelId::MUSIC), None);
1921 mixer.set_channel_lowpass(ChannelId::MUSIC, Some(800.0));
1922 mixer.set_channel_highpass(ChannelId::MUSIC, Some(100.0));
1923 assert_eq!(mixer.channel_lowpass(ChannelId::MUSIC), Some(800.0));
1924 assert_eq!(mixer.channel_highpass(ChannelId::MUSIC), Some(100.0));
1925 mixer.set_channel_lowpass(ChannelId::MUSIC, None);
1926 assert_eq!(mixer.channel_lowpass(ChannelId::MUSIC), None);
1927 }
1928
1929 #[test]
1930 fn a_closed_filter_leaves_the_audio_bit_for_bit_alone() {
1931 let plain = {
1932 let mut mixer = Mixer::new(48_000, 1);
1933 looped_sine(&mut mixer, 440, 0.5);
1934 mixer.render(3000)
1935 };
1936 let mut mixer = Mixer::new(48_000, 1);
1937 looped_sine(&mut mixer, 440, 0.5);
1938 mixer.set_channel_lowpass(ChannelId::MUSIC, Some(300.0));
1939 mixer.render(10);
1940 mixer.set_channel_lowpass(ChannelId::MUSIC, None);
1941 mixer.render(2000);
1942 let after = mixer.render(1000);
1943 let clean = {
1944 let mut mixer = Mixer::new(48_000, 1);
1945 looped_sine(&mut mixer, 440, 0.5);
1946 mixer.render(2010);
1947 mixer.render(1000)
1948 };
1949 assert_eq!(bits(&after), bits(&clean));
1950 assert_eq!(plain.len(), 3000);
1951 }
1952
1953 #[test]
1954 fn a_stream_plays_faster_when_its_pitch_rises() {
1955 let queue = PcmQueue::new();
1956 queue.push(&vec![0.5; 400]);
1957 queue.end();
1958 let mut mixer = Mixer::new(48_000, 1);
1959 let id = mixer.stream(Arc::clone(&queue), 1, Level::default());
1960 mixer.set_pitch(id, 2.0);
1961 mixer.render(480);
1962 let out = mixer.render(400);
1963 let heard = out.iter().filter(|sample| sample.abs() > 0.01).count();
1964 assert!(heard < 40, "{heard}");
1965 assert_eq!(mixer.voice_count(), 0);
1966
1967 let queue = PcmQueue::new();
1968 queue.push(&vec![0.5; 4000]);
1969 queue.end();
1970 let mut mixer = Mixer::new(48_000, 1);
1971 let id = mixer.stream(queue, 1, Level::default());
1972 mixer.set_pitch(id, 0.5);
1973 mixer.render(480);
1974 let out = mixer.render(1000);
1975 assert!(out.iter().all(|sample| (sample - 0.5).abs() < 0.01));
1976 }
1977
1978 fn plain_hits() -> Clip {
1979 Clip {
1980 rate: 48_000,
1981 channels: 2,
1982 samples: (0..4800)
1983 .map(|i| {
1984 let frame = i / 2;
1985 let decay = 1.0 - frame as f32 / 2400.0;
1986 let phase = (frame % 80) as f32 / 80.0;
1987 let side = if i % 2 == 0 { 0.5 } else { 0.4 };
1988 side * decay * (1.0 - 4.0 * (phase - 0.5).abs())
1989 })
1990 .collect(),
1991 }
1992 }
1993
1994 fn fnv(samples: &[f32]) -> u64 {
1995 samples
1996 .iter()
1997 .fold(0xcbf2_9ce4_8422_2325u64, |hash, sample| {
1998 (hash ^ u64::from(sample.to_bits())).wrapping_mul(0x0100_0000_01b3)
1999 })
2000 }
2001
2002 fn hit_spec() -> SoundSpec {
2003 SoundSpec {
2004 max_voices: 6,
2005 pitch_jitter: 2.0,
2006 volume_jitter: 0.25,
2007 ..SoundSpec::default()
2008 }
2009 }
2010
2011 #[test]
2012 fn sine_tones_and_jittered_hits_keep_their_bits_on_every_platform() {
2013 let mut mixer = Mixer::new(48_000, 2);
2014 mixer.set_seed(9);
2015 mixer.tone(ChannelId::EFFECTS, Wave::Sine, 440.0, 0.5);
2016 let beep = mixer.tone_sound(Wave::Sine, 700.0, Some(0.02), hit_spec());
2017 let hit = mixer.sound(&plain_hits(), hit_spec());
2018 let mut out = Vec::new();
2019 for tick in 0..60u64 {
2020 for k in 0..9 {
2021 mixer.trigger_in(k * 88, hit);
2022 }
2023 if tick % 3 == 0 {
2024 mixer.trigger_in(tick % 400, beep);
2025 }
2026 out.extend(mixer.render(800));
2027 }
2028 assert_eq!(fnv(&out), 12_859_396_616_140_308_833);
2029 }
2030
2031 #[test]
2032 fn a_tone_or_a_hit_starts_at_its_frame_offset_inside_the_block() {
2033 let mut reference = Mixer::new(48_000, 1);
2034 reference.tone(ChannelId::EFFECTS, Wave::Saw, 330.0, 0.5);
2035 let want = reference.render(2000);
2036 let mut mixer = Mixer::new(48_000, 1);
2037 mixer.render(100);
2038 mixer.tone_in(37, ChannelId::EFFECTS, Wave::Saw, 330.0, 0.5);
2039 let mut got = mixer.render(800);
2040 got.extend(mixer.render(800));
2041 assert!(got[..37].iter().all(|sample| *sample == 0.0));
2042 assert_eq!(bits(&got[37..]), bits(&want[..1600 - 37]));
2043
2044 let mut reference = Mixer::new(48_000, 2);
2045 reference.set_seed(5);
2046 let hit = reference.sound(&plain_hits(), hit_spec());
2047 reference.trigger(hit);
2048 let want = reference.render(2000);
2049 let mut mixer = Mixer::new(48_000, 2);
2050 mixer.set_seed(5);
2051 let hit = mixer.sound(&plain_hits(), hit_spec());
2052 mixer.trigger_in(900, hit);
2053 let mut got = mixer.render(800);
2054 got.extend(mixer.render(800));
2055 assert!(got[..1800].iter().all(|sample| *sample == 0.0));
2056 assert_eq!(bits(&got[1800..]), bits(&want[..1400]));
2057 }
2058
2059 #[test]
2060 fn tone_sounds_share_the_cap_stealing_and_jitter_of_clip_sounds() {
2061 let mut mixer = Mixer::new(48_000, 1);
2062 let beep = mixer.tone_sound(
2063 Wave::Sine,
2064 500.0,
2065 Some(0.01),
2066 SoundSpec {
2067 max_voices: 2,
2068 pitch_jitter: 2.0,
2069 ..SoundSpec::default()
2070 },
2071 );
2072 let first = mixer.trigger(beep).unwrap();
2073 let second = mixer.trigger(beep).unwrap();
2074 mixer.render(10);
2075 let third = mixer.trigger(beep).unwrap();
2076 assert_eq!(mixer.voices_of(beep), 2);
2077 let fading: Vec<VoiceId> = mixer
2078 .voices
2079 .iter()
2080 .filter(|voice| voice.fading())
2081 .map(|voice| voice.id)
2082 .collect();
2083 assert_eq!(fading, vec![first]);
2084 let pitches = [mixer.pitch(second).unwrap(), mixer.pitch(third).unwrap()];
2085 assert_ne!(pitches[0], pitches[1]);
2086 let low = 2f32.powf(-2.0 / 12.0) * 0.999;
2087 let high = 2f32.powf(2.0 / 12.0) * 1.001;
2088 assert!(pitches.iter().all(|p| *p > low && *p < high));
2089 let out = mixer.render(1000);
2090 assert_eq!(mixer.voice_count(), 0);
2091 assert!(out[700..].iter().all(|sample| *sample == 0.0));
2092 }
2093}