1pub mod effects;
137#[cfg(feature = "events")]
138#[cfg_attr(docsrs, doc(cfg(feature = "events")))]
139pub mod events;
140mod limiter;
141#[cfg(feature = "music")]
142#[cfg_attr(docsrs, doc(cfg(feature = "music")))]
143pub mod music;
144mod queue;
145mod routing;
146pub use routing::{BusRouting, BusSend, MAX_BUS_ROUTES, RoutingId, SendPosition};
147pub mod spatial;
148mod status;
149mod virtualization;
150mod voice;
151pub use virtualization::{MAX_LOGICAL_VOICES, VIRTUAL_VOICE_QUANTUM, VirtualVoices};
152
153#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
155pub enum ResamplingQuality {
156 #[default]
157 Sinc,
158 Linear,
161}
162pub use spatial::{
163 Attenuation, DistanceModel, Emitter, Listener, OutputLayout, Placement, SpatialConfig,
164};
165
166use std::sync::Arc;
167use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
168
169pub use limiter::MasterLimiter;
170use queue::{Consumer, Producer, PushError};
171pub use status::{VoiceState, VoiceStatus};
172use voice::{Feed, ONE, Ramp, Resampling, SincBank, SincTable, State, Voice};
173
174use crate::{Audio, Error, ErrorCode, FadeCurve, Limits, Result, TimePosition};
175
176const BLOCK: usize = 256;
178const TAILS: usize = 8;
180const STEAL_FADE: f64 = 0.005;
181
182pub trait Stream: Send {
186 fn sample_rate(&self) -> u32;
187 fn channels(&self) -> usize;
189 fn frames(&self) -> Option<u64> {
191 None
192 }
193 fn read(&mut self, out: &mut [f32]) -> Result<usize>;
197 fn seek(&mut self, frame: u64) -> Result<()>;
199}
200
201impl<B: AsRef<[u8]> + Send + 'static> Stream for crate::stream::Decoder<B> {
202 fn sample_rate(&self) -> u32 {
203 crate::stream::Decoder::sample_rate(self)
204 }
205 fn channels(&self) -> usize {
206 crate::stream::Decoder::channels(self)
207 }
208 fn frames(&self) -> Option<u64> {
209 crate::stream::Decoder::frames(self)
210 }
211 fn read(&mut self, out: &mut [f32]) -> Result<usize> {
212 crate::stream::Decoder::read(self, out)
213 }
214 fn seek(&mut self, frame: u64) -> Result<()> {
215 crate::stream::Decoder::seek(self, frame)
216 }
217}
218
219#[cfg(feature = "synth")]
223#[cfg_attr(docsrs, doc(cfg(feature = "synth")))]
224impl Stream for crate::synth::Synth {
225 fn sample_rate(&self) -> u32 {
226 crate::synth::Synth::sample_rate(self)
227 }
228 fn channels(&self) -> usize {
229 crate::synth::Synth::channels(self)
230 }
231 fn read(&mut self, out: &mut [f32]) -> Result<usize> {
232 Ok(self.fill(out))
233 }
234 fn seek(&mut self, frame: u64) -> Result<()> {
235 if frame != 0 {
236 return Err(Error::invalid("A synthesizer can only seek to its start"));
237 }
238 self.reset();
239 Ok(())
240 }
241}
242
243pub enum Sound {
245 Memory(Arc<Audio>),
246 Stream(Box<dyn Stream>),
247}
248impl Sound {
249 pub fn stream(stream: impl Stream + 'static) -> Self {
250 Self::Stream(Box::new(stream))
251 }
252}
253impl From<Arc<Audio>> for Sound {
254 fn from(audio: Arc<Audio>) -> Self {
255 Self::Memory(audio)
256 }
257}
258impl From<Audio> for Sound {
259 fn from(audio: Audio) -> Self {
260 Self::Memory(Arc::new(audio))
261 }
262}
263impl From<Box<dyn Stream>> for Sound {
264 fn from(stream: Box<dyn Stream>) -> Self {
265 Self::Stream(stream)
266 }
267}
268impl<B: AsRef<[u8]> + Send + 'static> From<crate::stream::Decoder<B>> for Sound {
269 fn from(decoder: crate::stream::Decoder<B>) -> Self {
270 Self::stream(decoder)
271 }
272}
273#[cfg(feature = "synth")]
274#[cfg_attr(docsrs, doc(cfg(feature = "synth")))]
275impl From<crate::synth::Synth> for Sound {
276 fn from(synth: crate::synth::Synth) -> Self {
277 Self::stream(synth)
278 }
279}
280
281#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
283pub struct VoiceId(u64);
284
285#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
287pub struct BusId(usize);
288impl BusId {
289 pub const MASTER: BusId = BusId(0);
290}
291
292#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
294pub enum Loop {
295 #[default]
296 Off,
297 Whole,
299 Region { start: u64, end: u64 },
301}
302
303#[derive(Clone, Copy, Debug)]
305pub struct Fade {
306 pub seconds: f64,
307 pub curve: FadeCurve,
308}
309impl Fade {
310 pub const NONE: Fade = Fade {
311 seconds: 0.0,
312 curve: FadeCurve::Linear,
313 };
314 pub const fn linear(seconds: f64) -> Self {
315 Self {
316 seconds,
317 curve: FadeCurve::Linear,
318 }
319 }
320 pub const fn new(seconds: f64, curve: FadeCurve) -> Self {
321 Self { seconds, curve }
322 }
323}
324impl Default for Fade {
325 fn default() -> Self {
326 Self::NONE
327 }
328}
329
330#[derive(Clone, Copy, Debug)]
332pub struct Play {
333 pub resampling: ResamplingQuality,
336 pub bus: BusId,
337 pub gain: f32,
339 pub pan: f32,
341 pub looping: Loop,
342 pub fade_in: Fade,
343 pub priority: u8,
345 pub rate: f32,
348 pub pitch: f32,
352 pub variation: Variation,
354 pub start: TimePosition,
358 pub instances: Option<Instances>,
360 pub position: Option<[f32; 2]>,
364 pub emitter: Option<Emitter>,
366}
367
368#[derive(Clone, Copy, Debug, PartialEq)]
377pub struct Space2d {
378 pub width: f32,
380 pub min_distance: f32,
382 pub max_distance: f32,
384 pub model: Distance,
385}
386impl Default for Space2d {
387 fn default() -> Self {
388 Self {
389 width: 1.0,
390 min_distance: 1.0,
391 max_distance: 10.0,
392 model: Distance::Linear,
393 }
394 }
395}
396impl Space2d {
397 pub fn place(&self, [x, y]: [f32; 2]) -> (f32, f32) {
399 let (x, y) = (x as f64, y as f64);
400 let pan = (x / self.width as f64).clamp(-1.0, 1.0);
401 let d = (x * x + y * y).sqrt();
402 let (min, max) = (self.min_distance as f64, self.max_distance as f64);
403 let gain = if d <= min {
404 1.0
405 } else if d >= max {
406 0.0
407 } else {
408 match self.model {
409 Distance::Linear => 1.0 - (d - min) / (max - min),
410 Distance::Inverse => {
411 let floor = min / max;
412 (min / d - floor) / (1.0 - floor)
413 }
414 }
415 };
416 (pan as f32, gain as f32)
417 }
418 fn validate(&self) -> Result<()> {
419 let ok = self.width.is_finite()
420 && self.width > 0.0
421 && self.min_distance.is_finite()
422 && self.min_distance >= 0.0
423 && self.max_distance.is_finite()
424 && self.max_distance > self.min_distance;
425 if ok {
426 Ok(())
427 } else {
428 Err(Error::invalid(
429 "A 2D space needs a positive width and 0 <= min_distance < max_distance, all finite",
430 ))
431 }
432 }
433}
434
435#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
437pub enum Distance {
438 #[default]
439 Linear,
440 Inverse,
441}
442
443#[derive(Clone, Copy, Debug, PartialEq)]
457pub struct Instances {
458 pub key: u64,
463 pub max: u16,
465 pub min_gap: f64,
467 pub policy: OverLimit,
468}
469impl Instances {
470 pub const fn new(key: u64) -> Self {
472 Self {
473 key,
474 max: 0,
475 min_gap: 0.0,
476 policy: OverLimit::Refuse,
477 }
478 }
479 pub fn of(sound: &Audio) -> Self {
482 Self::new(sound.id())
483 }
484 pub const fn max(self, max: u16) -> Self {
485 Self { max, ..self }
486 }
487 pub const fn min_gap(self, seconds: f64) -> Self {
488 Self {
489 min_gap: seconds,
490 ..self
491 }
492 }
493 pub const fn policy(self, policy: OverLimit) -> Self {
494 Self { policy, ..self }
495 }
496}
497
498#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
500pub enum OverLimit {
501 #[default]
503 Refuse,
504 StopOldest,
506 StopQuietest,
508}
509
510const STARTS: usize = 1024;
514const MAX_GAP: f64 = 60.0;
516impl Default for Play {
517 fn default() -> Self {
518 Self {
519 resampling: ResamplingQuality::Sinc,
520 bus: BusId::MASTER,
521 gain: 1.0,
522 pan: 0.0,
523 looping: Loop::Off,
524 fade_in: Fade::NONE,
525 priority: 128,
526 rate: 1.0,
527 pitch: 0.0,
528 variation: Variation::NONE,
529 start: TimePosition::Frames(0),
530 instances: None,
531 position: None,
532 emitter: None,
533 }
534 }
535}
536
537#[derive(Clone, Copy, Debug, Default, PartialEq)]
544pub struct Variation {
545 pub pitch: f32,
547 pub gain_db: f32,
549}
550impl Variation {
551 pub const NONE: Variation = Variation {
552 pitch: 0.0,
553 gain_db: 0.0,
554 };
555}
556
557#[derive(Clone, Debug)]
558pub struct MixerConfig {
559 pub virtual_voices: Option<VirtualVoices>,
562 pub sample_rate: u32,
564 pub channels: usize,
566 pub max_voices: usize,
569 pub buses: Vec<String>,
571 pub queue: usize,
573 pub seed: u64,
575 pub space: Space2d,
577 pub listener: Listener,
578 pub spatial: SpatialConfig,
579 pub limiter: Option<MasterLimiter>,
582}
583impl Default for MixerConfig {
584 fn default() -> Self {
585 Self {
586 virtual_voices: None,
587 sample_rate: 48_000,
588 channels: 2,
589 max_voices: 32,
590 buses: Vec::new(),
591 queue: 1024,
592 seed: 0,
593 space: Space2d::default(),
594 listener: Listener::default(),
595 spatial: SpatialConfig::default(),
596 limiter: None,
597 }
598 }
599}
600
601#[derive(Clone, Copy, Debug, Eq, PartialEq)]
603#[non_exhaustive]
604pub enum MixerEvent {
605 Ended(VoiceId),
607 Stolen(VoiceId),
609 Rejected(VoiceId),
611 Limited(VoiceId),
613 Failed(VoiceId, ErrorCode),
615 EffectsApplied(BusId, u64),
617 EffectsRejected(BusId, ErrorCode),
619 RoutingApplied(RoutingId, u64),
621 RoutingRejected(RoutingId, ErrorCode),
623 #[cfg(feature = "music")]
625 MusicApplied(music::MusicId, u64),
626 #[cfg(feature = "music")]
628 MusicRejected(music::MusicId, ErrorCode),
629}
630
631enum Action {
632 Routing(Box<routing::Edit>),
633 Effects(Box<effects::Edit>),
634 Effect(usize, usize, effects::Settings, u64, FadeCurve),
635 #[cfg(feature = "music")]
636 Music(Box<music::Request>),
637 Play(Box<Voice>),
638 Stop(VoiceId, u64, FadeCurve),
639 Gain(VoiceId, f64, u64, FadeCurve),
640 Pan(VoiceId, f64, u64, FadeCurve),
641 Rate(VoiceId, f64, u64, FadeCurve),
642 Pitch(VoiceId, f64, u64, FadeCurve),
643 Position(VoiceId, f64, f64, u64, FadeCurve),
644 Emitter(VoiceId, Emitter, u64, FadeCurve),
645 Listener(Listener, u64, FadeCurve),
646 Pause(VoiceId, u64, FadeCurve),
647 Resume(VoiceId, u64, FadeCurve),
648 PauseBus(usize, u64, FadeCurve),
649 ResumeBus(usize, u64, FadeCurve),
650 BusGain(usize, f64, u64, FadeCurve),
651 StopAll(u64, FadeCurve),
652}
653struct Command {
654 frame: Option<u64>,
655 sequence: u64,
656 action: Action,
657}
658struct Returned {
659 event: Option<MixerEvent>,
660 voice: Option<Box<Voice>>,
661 #[cfg(feature = "music")]
662 music: Option<Box<music::Request>>,
663}
664
665struct Bus {
666 gain: Ramp,
667 pause: Ramp,
669 pausing: bool,
670 paused: bool,
671 buffer: Vec<f32>,
672 effects: Option<Box<effects::Chain>>,
673 effect_clock: u64,
674}
675
676pub struct Mixer {
678 virtualization: Option<virtualization::Scheduler>,
679 voice_limit: usize,
680 routing: routing::AudioRouting,
681 effects: effects::AudioEffects,
682 rate: u32,
683 channels: usize,
684 listener: Listener,
685 spatial: SpatialConfig,
686 max_voices: usize,
687 frame: u64,
688 order: u64,
689 voices: Vec<Option<Box<Voice>>>,
690 buses: Vec<Bus>,
691 pending: Vec<Command>,
692 commands: Consumer<Command>,
693 returns: Producer<Returned>,
694 retired: Vec<Returned>,
695 clock: Arc<AtomicU64>,
696 status: Arc<status::Table>,
697 steal_fade: u64,
698 starts: [(u64, Option<u64>); STARTS],
701 max_gap: u64,
703 limiter: Option<limiter::Limiter>,
704 limiter_gain: Arc<AtomicU32>,
705}
706
707pub struct MixerHandle {
709 peaks: Vec<(u64, f64)>,
711 peak_cursor: usize,
712 peak_limit: usize,
713 routing: routing::ControlRouting,
714 effects: effects::ControlEffects,
715 rate: u32,
716 channels: usize,
717 buses: Vec<String>,
718 commands: Producer<Command>,
719 returns: Consumer<Returned>,
720 clock: Arc<AtomicU64>,
721 status: Arc<status::Table>,
722 next_voice: u64,
723 next_sequence: u64,
724 tables: Vec<(u32, Arc<SincTable>)>,
725 bank: Arc<SincBank>,
726 random: SplitMix,
727 space: Space2d,
728 latency: u64,
729 limited: bool,
730 limiter_gain: Arc<AtomicU32>,
731}
732
733struct SplitMix(u64);
735impl SplitMix {
736 fn next(&mut self) -> u64 {
737 self.0 = self.0.wrapping_add(0x9e37_79b9_7f4a_7c15);
738 let mut z = self.0;
739 z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
740 z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
741 z ^ (z >> 31)
742 }
743 fn symmetric(&mut self) -> f64 {
745 (self.next() >> 11) as f64 / (1u64 << 52) as f64 - 1.0
746 }
747}
748
749impl Mixer {
750 pub fn new(config: MixerConfig) -> Result<(Mixer, MixerHandle)> {
751 let MixerConfig {
752 virtual_voices,
753 sample_rate,
754 channels,
755 max_voices,
756 buses,
757 queue,
758 seed,
759 space,
760 listener,
761 spatial,
762 limiter,
763 } = config;
764 space.validate()?;
765 listener.validate()?;
766 spatial.validate()?;
767 Limits::default().check(0, channels, sample_rate)?;
768 OutputLayout::from_channels(channels)?;
769 if !(1..=1024).contains(&max_voices) || !(1..=65536).contains(&queue) {
770 return Err(Error::invalid(
771 "max_voices must be 1..=1024 and queue 1..=65536",
772 ));
773 }
774 for (i, name) in buses.iter().enumerate() {
775 if name.is_empty() || buses[..i].contains(name) {
776 return Err(Error::invalid(format!(
777 "Bus names must be unique and not empty: {name:?}"
778 )));
779 }
780 }
781 if buses.len() > 255 {
782 return Err(Error::invalid("A mixer has at most 255 named buses"));
783 }
784 let limiter = limiter
786 .map(|settings| limiter::Limiter::new(settings, sample_rate, channels))
787 .transpose()?;
788 let latency = limiter.as_ref().map_or(0, |l| l.latency() as u64);
789 let has_limiter = limiter.is_some();
790 let limiter_gain = Arc::new(AtomicU32::new(1f32.to_bits()));
791 let voice_limit = if let Some(settings) = virtual_voices {
792 if settings.capacity < max_voices
793 || settings.capacity > MAX_LOGICAL_VOICES
794 || !(0.0..=1.0).contains(&settings.threshold)
795 {
796 return Err(Error::invalid(
797 "Virtual capacity must be max_voices..=4096 and threshold 0..=1",
798 ));
799 }
800 settings.capacity
801 } else {
802 max_voices
803 };
804 let slots = voice_limit + TAILS;
805 let (command_tx, command_rx) = queue::queue(queue);
806 #[cfg(not(feature = "music"))]
809 let returns_per_command = 1;
810 #[cfg(feature = "music")]
811 let returns_per_command = music::MAX_LAYERS + 1;
812 let returns = queue
815 .checked_mul(returns_per_command)
816 .and_then(|n| n.checked_add(slots))
817 .and_then(|n| n.checked_mul(2))
818 .ok_or_else(|| Error::limit("Mixer return capacity overflow"))?;
819 let (return_tx, return_rx) = queue::queue(returns);
820 let clock = Arc::new(AtomicU64::new(0));
821 let status = Arc::new(status::Table::new(slots));
822 let (audio_effects, control_effects) = effects::create(queue, buses.len() + 1);
823 let (audio_routing, control_routing) = routing::create(queue);
824 let mixer = Mixer {
825 virtualization: virtual_voices.map(|v| virtualization::Scheduler::new(v, slots)),
826 voice_limit,
827 routing: audio_routing,
828 effects: audio_effects,
829 rate: sample_rate,
830 channels,
831 listener,
832 spatial,
833 max_voices,
834 frame: 0,
835 order: 0,
836 voices: (0..slots).map(|_| None).collect(),
837 buses: (0..=buses.len())
838 .map(|_| Bus {
839 gain: Ramp::constant(1.0),
840 pause: Ramp::constant(1.0),
841 pausing: false,
842 paused: false,
843 buffer: vec![0.0; BLOCK * channels],
844 effects: None,
845 effect_clock: 0,
846 })
847 .collect(),
848 pending: Vec::with_capacity(queue),
849 commands: command_rx,
850 returns: return_tx,
851 retired: Vec::with_capacity(returns),
852 clock: clock.clone(),
853 status: status.clone(),
854 steal_fade: (STEAL_FADE * sample_rate as f64).round() as u64,
855 starts: [(0, None); STARTS],
856 max_gap: (MAX_GAP * sample_rate as f64).round() as u64,
857 limiter,
858 limiter_gain: limiter_gain.clone(),
859 };
860 let handle = MixerHandle {
861 peaks: Vec::with_capacity(virtual_voices.map_or(0, |v| v.capacity)),
862 peak_cursor: 0,
863 peak_limit: virtual_voices.map_or(0, |v| v.capacity),
864 routing: control_routing,
865 effects: control_effects,
866 rate: sample_rate,
867 channels,
868 buses,
869 commands: command_tx,
870 returns: return_rx,
871 clock,
872 status,
873 next_voice: 0,
874 next_sequence: 0,
875 tables: Vec::new(),
876 bank: Arc::new(SincBank::new()),
877 random: SplitMix(seed),
878 space,
879 latency,
880 limited: has_limiter,
881 limiter_gain,
882 };
883 Ok((mixer, handle))
884 }
885 pub fn sample_rate(&self) -> u32 {
886 self.rate
887 }
888 pub fn channels(&self) -> usize {
889 self.channels
890 }
891 pub fn frame(&self) -> u64 {
893 self.frame
894 }
895 pub fn active_voices(&self) -> usize {
897 self.voices.iter().flatten().count()
898 }
899
900 pub fn render(&mut self, out: &mut [f32]) {
903 let ch = self.channels;
904 let frames = out.len() / ch;
905 out.fill(0.0);
906 self.receive();
907 let start = self.frame;
908 let end = start + frames as u64;
909 let mut cursor = start;
910 loop {
911 self.apply_due(cursor);
912 if cursor >= end {
913 break;
914 }
915 let pause_ends = self
918 .buses
919 .iter()
920 .filter(|b| b.pausing)
921 .map(|b| b.pause.end());
922 let next = self
923 .pending
924 .iter()
925 .filter_map(|c| c.frame)
926 .chain(pause_ends)
927 .filter(|&f| f > cursor)
928 .min()
929 .unwrap_or(end);
930 let mut stop = next.min(end).min(cursor + BLOCK as u64);
931 if self.virtualization.is_some() {
932 stop = stop.min(cursor - cursor % VIRTUAL_VOICE_QUANTUM + VIRTUAL_VOICE_QUANTUM);
933 }
934 let from = (cursor - start) as usize * ch;
935 let to = (stop - start) as usize * ch;
936 self.mix(cursor, &mut out[from..to]);
937 cursor = stop;
938 }
939 if let Some(limiter) = &mut self.limiter {
940 let lowest = limiter.process(&mut out[..frames * ch]);
941 self.limiter_gain
942 .store((lowest as f32).to_bits(), Ordering::Relaxed);
943 }
944 self.frame = end;
945 for v in self.voices.iter_mut().flatten() {
948 v.settle();
949 }
950 self.retire_done();
951 self.select_physical(end);
952 self.publish();
953 self.send_returns();
954 self.effects.send_returns();
955 self.routing.send_returns();
956 self.clock.store(end, Ordering::Release);
957 }
958
959 fn receive(&mut self) {
960 let available = self.pending.capacity() - self.pending.len();
963 for _ in 0..available {
964 match self.commands.pop() {
965 Some(command) => {
966 #[cfg(feature = "music")]
967 let Some(command) = self.quantize_music(command) else {
968 continue;
969 };
970 self.pending.push(command);
971 }
972 None => break,
973 }
974 }
975 }
976
977 fn apply_due(&mut self, frame: u64) {
979 loop {
980 let due = self
981 .pending
982 .iter()
983 .enumerate()
984 .filter(|(_, c)| c.frame.unwrap_or(0) <= frame)
985 .min_by_key(|(_, c)| (c.frame.unwrap_or(0), c.sequence))
986 .map(|(i, _)| i);
987 let Some(index) = due else {
988 return;
989 };
990 let command = self.pending.swap_remove(index);
991 self.apply(frame, command.action);
992 }
993 }
994
995 fn voice_mut(&mut self, id: VoiceId) -> Option<&mut Voice> {
996 self.voices
997 .iter_mut()
998 .flatten()
999 .find(|v| v.id == id)
1000 .map(|v| &mut **v)
1001 }
1002
1003 fn slot_of(&self, id: VoiceId) -> Option<usize> {
1004 self.voices
1005 .iter()
1006 .position(|v| v.as_ref().is_some_and(|v| v.id == id))
1007 }
1008
1009 fn apply(&mut self, frame: u64, action: Action) {
1010 if let Some(scheduler) = &mut self.virtualization {
1011 scheduler.dirty = true;
1012 }
1013 match action {
1014 Action::Routing(edit) => self.apply_routing(frame, edit),
1015 Action::Effects(edit) => self.apply_effects(frame, edit),
1016 Action::Effect(bus, slot, settings, len, curve) => {
1017 self.apply_effect(bus, slot, settings, len, curve)
1018 }
1019 #[cfg(feature = "music")]
1020 Action::Music(request) => self.apply_music(frame, request),
1021 Action::Play(voice) => self.start(frame, voice),
1022 Action::Stop(id, len, curve) => {
1023 if let Some(slot) = self.slot_of(id) {
1024 self.stop_slot(slot, frame, len, curve);
1025 }
1026 self.retire_done();
1027 }
1028 Action::Gain(id, gain, len, curve) => {
1029 if let Some(v) = self.voice_mut(id) {
1030 v.gain.retarget(v.time(frame), gain, len, curve);
1031 }
1032 }
1033 Action::Pan(id, pan, len, curve) => {
1034 if let Some(v) = self.voice_mut(id) {
1035 v.pan.retarget(v.time(frame), pan, len, curve);
1036 }
1037 }
1038 Action::Rate(id, rate, len, curve) => {
1039 if let Some(v) = self.voice_mut(id) {
1040 v.rate.retarget(v.time(frame), rate, len, curve);
1041 }
1042 }
1043 Action::Pitch(id, pitch, len, curve) => {
1044 if let Some(v) = self.voice_mut(id) {
1045 v.pitch.retarget(v.time(frame), pitch, len, curve);
1046 }
1047 }
1048 Action::Position(id, pan, gain, len, curve) => {
1049 if let Some(v) = self.voice_mut(id) {
1050 if v.emitter.is_none() {
1051 let time = v.time(frame);
1052 v.pan.retarget(time, pan, len, curve);
1053 v.distance.retarget(time, gain, len, curve);
1054 }
1055 }
1056 }
1057 Action::Emitter(id, emitter, len, curve) => {
1058 let channels = self.channels;
1059 let placement = virtualization::placement(
1060 &self.spatial,
1061 &self.listener,
1062 &emitter,
1063 OutputLayout::from_channels(self.channels).unwrap(),
1064 self.virtualization.is_some(),
1065 );
1066 if let Some(v) = self.voice_mut(id) {
1067 v.emitter = Some(emitter);
1068 v.update_spatial(placement, frame, len, curve, channels);
1069 }
1070 }
1071 Action::Listener(listener, len, curve) => {
1072 self.listener = listener;
1073 let layout = OutputLayout::from_channels(self.channels).unwrap();
1074 for v in self.voices.iter_mut().flatten() {
1075 if let Some(emitter) = v.emitter {
1076 let placement = virtualization::placement(
1077 &self.spatial,
1078 &listener,
1079 &emitter,
1080 layout,
1081 self.virtualization.is_some(),
1082 );
1083 v.update_spatial(placement, frame, len, curve, layout.channels());
1084 }
1085 }
1086 }
1087 Action::Pause(id, len, curve) => {
1088 if let Some(v) = self.voice_mut(id) {
1089 v.pause(v.time(frame), len, curve);
1090 }
1091 }
1092 Action::Resume(id, len, curve) => {
1093 if let Some(v) = self.voice_mut(id) {
1094 v.resume(v.time(frame), len, curve);
1095 }
1096 }
1097 Action::PauseBus(b, len, curve) => {
1098 let bus = &mut self.buses[b];
1099 if !bus.paused {
1100 bus.pause.retarget(frame, 0.0, len, curve);
1101 bus.pausing = len > 0;
1102 bus.paused = len == 0;
1103 }
1104 }
1105 Action::ResumeBus(b, len, curve) => {
1106 let bus = &mut self.buses[b];
1107 if bus.paused || bus.pausing {
1108 bus.paused = false;
1109 bus.pausing = false;
1110 bus.pause.retarget(frame, 1.0, len, curve);
1111 }
1112 }
1113 Action::BusGain(bus, gain, len, curve) => {
1114 self.buses[bus].gain.retarget(frame, gain, len, curve);
1115 }
1116 Action::StopAll(len, curve) => {
1117 for slot in 0..self.voices.len() {
1118 self.stop_slot(slot, frame, len, curve);
1119 }
1120 self.retire_done();
1121 }
1122 }
1123 }
1124
1125 fn refuse(&mut self, voice: Box<Voice>, event: fn(VoiceId) -> MixerEvent) {
1127 let id = voice.id;
1128 self.retire(voice, Some(event(id)));
1129 }
1130
1131 fn stop_slot(&mut self, slot: usize, frame: u64, len: u64, curve: FadeCurve) {
1134 let master_paused = self.buses[0].paused;
1135 let buses = &self.buses;
1136 if let Some(v) = self.voices[slot].as_mut() {
1137 let frozen = buses[v.bus].paused || master_paused;
1138 v.stop(v.time(frame), len, curve, frozen);
1139 }
1140 }
1141
1142 fn start(&mut self, frame: u64, mut voice: Box<Voice>) {
1143 if let Some(emitter) = voice.emitter {
1144 let placement = virtualization::placement(
1145 &self.spatial,
1146 &self.listener,
1147 &emitter,
1148 OutputLayout::from_channels(self.channels).unwrap(),
1149 self.virtualization.is_some(),
1150 );
1151 voice.update_spatial(placement, frame, 0, FadeCurve::Linear, self.channels);
1152 }
1153 for v in self.voices.iter_mut().flatten() {
1157 v.settle();
1158 }
1159 let mut start_entry = None;
1161 if let Some(limit) = voice.instances {
1162 let own = self
1164 .starts
1165 .iter()
1166 .position(|&(key, last)| key == limit.key && last.is_some());
1167 if let Some(last) = own.and_then(|i| self.starts[i].1) {
1168 if limit.min_gap > 0 && frame < last + limit.min_gap {
1169 return self.refuse(voice, MixerEvent::Limited);
1170 }
1171 }
1172 let max_gap = self.max_gap;
1175 let entry = own.or_else(|| {
1176 self.starts
1177 .iter()
1178 .position(|&(_, last)| last.is_none_or(|last| frame >= last + max_gap))
1179 });
1180 let Some(entry) = entry else {
1181 return self.refuse(voice, MixerEvent::Limited);
1182 };
1183 start_entry = Some(entry);
1184 if limit.max > 0 {
1185 let same = |v: &Voice| v.live() && v.instances.is_some_and(|l| l.key == limit.key);
1186 let candidates = || {
1187 self.voices
1188 .iter()
1189 .enumerate()
1190 .filter_map(|(i, v)| v.as_deref().filter(|v| same(v)).map(|v| (i, v)))
1191 };
1192 let playing = candidates().count();
1195 if playing >= limit.max as usize {
1196 if limit.policy == OverLimit::Refuse {
1197 return self.refuse(voice, MixerEvent::Limited);
1198 }
1199 let priority = voice.priority;
1202 for _ in 0..playing + 1 - limit.max as usize {
1203 let voices = &self.voices;
1204 let unmarked = || {
1205 voices.iter().enumerate().filter_map(|(i, v)| {
1206 v.as_deref()
1207 .filter(|v| same(v) && !v.marked && v.priority <= priority)
1208 .map(|v| (i, v))
1209 })
1210 };
1211 let pick = match limit.policy {
1212 OverLimit::StopQuietest => unmarked().min_by(|(_, a), (_, b)| {
1213 a.level(frame)
1214 .total_cmp(&b.level(frame))
1215 .then(a.order.cmp(&b.order))
1216 }),
1217 _ => unmarked().min_by_key(|(_, v)| v.order),
1218 }
1219 .map(|(i, _)| i);
1220 match pick.and_then(|i| self.voices[i].as_mut()) {
1221 Some(v) => v.marked = true,
1222 None => {
1223 for v in self.voices.iter_mut().flatten() {
1224 v.marked = false;
1225 }
1226 return self.refuse(voice, MixerEvent::Limited);
1227 }
1228 }
1229 }
1230 }
1231 }
1232 }
1233 let others = || {
1235 self.voices
1236 .iter()
1237 .enumerate()
1238 .filter(|(_, v)| v.as_ref().is_some_and(|v| v.live() && !v.marked))
1239 };
1240 let mut global_victim = None;
1241 if others().count() >= self.voice_limit {
1242 let victim = others()
1243 .filter_map(|(i, v)| v.as_deref().map(|v| (i, v)))
1244 .min_by_key(|(_, v)| (v.priority, v.order));
1245 match victim {
1246 Some((i, v)) if v.priority <= voice.priority => global_victim = Some(i),
1247 _ => {
1248 for v in self.voices.iter_mut().flatten() {
1249 v.marked = false;
1250 }
1251 return self.refuse(voice, MixerEvent::Rejected);
1252 }
1253 }
1254 }
1255 if let Some(v) = global_victim.and_then(|i| self.voices[i].as_mut()) {
1256 v.marked = true;
1257 }
1258 for slot in 0..self.voices.len() {
1260 if self.voices[slot].as_ref().is_some_and(|v| v.marked) {
1261 self.stop_slot(slot, frame, self.steal_fade, FadeCurve::Linear);
1262 if let Some(v) = self.voices[slot].as_mut() {
1263 v.marked = false;
1264 let id = v.id;
1265 self.event(MixerEvent::Stolen(id));
1266 }
1267 }
1268 }
1269 if let (Some(entry), Some(limit)) = (start_entry, voice.instances) {
1270 self.starts[entry] = (limit.key, Some(frame));
1271 }
1272 voice.order = self.order;
1273 self.order += 1;
1274 voice.start(frame);
1275 self.retire_done();
1276 let slot = match self.voices.iter().position(Option::is_none) {
1277 Some(slot) => slot,
1278 None => {
1279 let oldest = self
1281 .voices
1282 .iter()
1283 .enumerate()
1284 .filter(|(_, v)| v.as_ref().is_some_and(|v| !v.live()))
1285 .min_by_key(|(_, v)| v.as_ref().map(|v| v.order))
1286 .map(|(i, _)| i);
1287 let Some(slot) = oldest else {
1288 let id = voice.id;
1289 self.retire(voice, Some(MixerEvent::Rejected(id)));
1290 return;
1291 };
1292 let old = self.voices[slot].take().expect("slot is occupied");
1293 let id = old.id;
1294 let position = old.source_position();
1295 self.status.publish(
1296 slot,
1297 id,
1298 VoiceStatus {
1299 state: VoiceState::Finished,
1300 position,
1301 },
1302 );
1303 self.retire(old, Some(MixerEvent::Ended(id)));
1304 slot
1305 }
1306 };
1307 self.voices[slot] = Some(voice);
1308 }
1309
1310 fn mix(&mut self, first: u64, out: &mut [f32]) {
1311 self.select_physical(first);
1312 let ch = self.channels;
1313 let frames = out.len() / ch;
1314 for bus in &mut self.buses {
1315 bus.buffer[..frames * ch].fill(0.0);
1316 }
1317 let master_paused = self.buses[0].paused;
1318 for v in self.voices.iter_mut().flatten() {
1319 if v.paused() || self.buses[v.bus].paused || master_paused {
1321 if v.state == State::Stopping {
1324 v.state = State::Finished;
1325 } else {
1326 v.freeze(frames as u64);
1327 }
1328 continue;
1329 }
1330 let buffer = &mut self.buses[v.bus].buffer[..frames * ch];
1331 if v.virtualized {
1332 v.advance(first, frames);
1333 } else {
1334 v.render(first, frames, buffer, ch);
1335 }
1336 }
1337 self.retire_done();
1338 let end = first + frames as u64;
1339 if self.routing.graph.is_some() {
1340 self.mix_routing(first, out);
1341 } else if self.buses.iter().any(|bus| bus.effects.is_some()) {
1342 self.mix_effects(first, out);
1343 } else {
1344 let (master, master_pause) = (self.buses[0].gain, self.buses[0].pause);
1345 for (b, bus) in self.buses.iter().enumerate() {
1346 let constant = !bus.gain.changing(first)
1347 && !bus.pause.changing(first)
1348 && !master.changing(first)
1349 && !master_pause.changing(first);
1350 let gain_at = |frame| {
1351 let pause = if b == 0 {
1352 master_pause.value(frame)
1353 } else {
1354 bus.pause.value(frame) * master_pause.value(frame)
1355 };
1356 bus_gain(b, &bus.gain, &master, frame) * pause
1357 };
1358 let fixed = gain_at(first);
1359 for j in 0..frames {
1360 let g = if constant {
1361 fixed
1362 } else {
1363 gain_at(first + j as u64)
1364 };
1365 for c in 0..ch {
1366 out[j * ch + c] += (bus.buffer[j * ch + c] as f64 * g) as f32;
1367 }
1368 }
1369 }
1370 }
1371 for bus in &mut self.buses {
1373 if bus.pausing && bus.pause.end() <= end {
1374 bus.pausing = false;
1375 bus.paused = true;
1376 }
1377 }
1378 }
1379
1380 fn publish(&self) {
1382 let master_paused = self.buses[0].paused;
1383 for (slot, voice) in self.voices.iter().enumerate() {
1384 if let Some(v) = voice {
1385 let state = if v.state == State::Stopping {
1386 VoiceState::Stopping
1387 } else if v.paused() || self.buses[v.bus].paused || master_paused {
1388 VoiceState::Paused
1389 } else if v.virtualized {
1390 VoiceState::Virtual
1391 } else {
1392 VoiceState::Playing
1393 };
1394 let position = v.source_position();
1395 self.status
1396 .publish(slot, v.id, VoiceStatus { state, position });
1397 }
1398 }
1399 }
1400
1401 fn retire_done(&mut self) {
1402 for slot in 0..self.voices.len() {
1403 if self.voices[slot].as_ref().is_some_and(|v| v.done()) {
1404 let voice = self.voices[slot].take().expect("slot is occupied");
1405 let position = voice.source_position();
1406 self.status.publish(
1407 slot,
1408 voice.id,
1409 VoiceStatus {
1410 state: VoiceState::Finished,
1411 position,
1412 },
1413 );
1414 let event = match voice.state {
1415 State::Failed(code) => MixerEvent::Failed(voice.id, code),
1416 _ => MixerEvent::Ended(voice.id),
1417 };
1418 self.retire(voice, Some(event));
1419 }
1420 }
1421 }
1422
1423 fn event(&mut self, event: MixerEvent) {
1424 if self.retired.len() < self.retired.capacity() {
1425 self.retired.push(Returned {
1426 event: Some(event),
1427 voice: None,
1428 #[cfg(feature = "music")]
1429 music: None,
1430 });
1431 }
1432 }
1433
1434 fn retire(&mut self, voice: Box<Voice>, event: Option<MixerEvent>) {
1437 if self.retired.len() < self.retired.capacity() {
1438 self.retired.push(Returned {
1439 event,
1440 voice: Some(voice),
1441 #[cfg(feature = "music")]
1442 music: None,
1443 });
1444 }
1445 }
1446
1447 fn send_returns(&mut self) {
1448 let mut sent = 0;
1449 while sent < self.retired.len() {
1450 let item = std::mem::replace(
1451 &mut self.retired[sent],
1452 Returned {
1453 event: None,
1454 voice: None,
1455 #[cfg(feature = "music")]
1456 music: None,
1457 },
1458 );
1459 match self.returns.push(item) {
1460 Ok(()) => sent += 1,
1461 Err(PushError::Full(item) | PushError::Disconnected(item)) => {
1462 self.retired[sent] = item;
1463 break;
1464 }
1465 }
1466 }
1467 self.retired.drain(..sent);
1468 }
1469}
1470
1471fn bus_gain(bus: usize, gain: &Ramp, master: &Ramp, frame: u64) -> f64 {
1472 if bus == 0 {
1473 master.value(frame)
1474 } else {
1475 gain.value(frame) * master.value(frame)
1476 }
1477}
1478
1479impl MixerHandle {
1480 fn source_peak(&mut self, audio: &Audio) -> f64 {
1481 if self.peak_limit == 0 {
1482 return 1.0;
1483 }
1484 if let Some((_, peak)) = self.peaks.iter().find(|(id, _)| *id == audio.id()) {
1485 return *peak;
1486 }
1487 let entry = (audio.id(), audio.peak());
1488 if self.peaks.len() < self.peak_limit {
1489 self.peaks.push(entry);
1490 } else {
1491 self.peaks[self.peak_cursor] = entry;
1492 self.peak_cursor = (self.peak_cursor + 1) % self.peak_limit;
1493 }
1494 entry.1
1495 }
1496 pub fn sample_rate(&self) -> u32 {
1497 self.rate
1498 }
1499 pub fn channels(&self) -> usize {
1500 self.channels
1501 }
1502 pub fn bus(&self, name: &str) -> Option<BusId> {
1504 self.buses
1505 .iter()
1506 .position(|b| b == name)
1507 .map(|i| BusId(i + 1))
1508 }
1509 pub fn frame(&self) -> u64 {
1511 self.clock.load(Ordering::Acquire)
1512 }
1513 pub fn voice_status(&self, voice: VoiceId) -> Option<VoiceStatus> {
1517 self.status.read(voice)
1518 }
1519 pub fn pause(&mut self, voice: VoiceId, fade: Fade) -> Result<()> {
1521 self.now().pause(voice, fade)
1522 }
1523 pub fn resume(&mut self, voice: VoiceId, fade: Fade) -> Result<()> {
1525 self.now().resume(voice, fade)
1526 }
1527 pub fn pause_bus(&mut self, bus: BusId, fade: Fade) -> Result<()> {
1532 self.now().pause_bus(bus, fade)
1533 }
1534 pub fn resume_bus(&mut self, bus: BusId, fade: Fade) -> Result<()> {
1535 self.now().resume_bus(bus, fade)
1536 }
1537 pub fn now(&mut self) -> Commands<'_> {
1539 Commands {
1540 handle: self,
1541 frame: None,
1542 }
1543 }
1544 pub fn at(&mut self, frame: u64) -> Commands<'_> {
1547 Commands {
1548 handle: self,
1549 frame: Some(frame),
1550 }
1551 }
1552 pub fn play(&mut self, sound: impl Into<Sound>, play: &Play) -> Result<VoiceId> {
1553 self.now().play(sound, play)
1554 }
1555 pub fn stop(&mut self, voice: VoiceId, fade: Fade) -> Result<()> {
1556 self.now().stop(voice, fade)
1557 }
1558 pub fn stop_all(&mut self, fade: Fade) -> Result<()> {
1559 self.now().stop_all(fade)
1560 }
1561 pub fn set_gain(&mut self, voice: VoiceId, gain: f32, fade: Fade) -> Result<()> {
1562 self.now().set_gain(voice, gain, fade)
1563 }
1564 pub fn set_pan(&mut self, voice: VoiceId, pan: f32, fade: Fade) -> Result<()> {
1566 self.now().set_pan(voice, pan, fade)
1567 }
1568 pub fn set_rate(&mut self, voice: VoiceId, rate: f32, fade: Fade) -> Result<()> {
1569 self.now().set_rate(voice, rate, fade)
1570 }
1571 pub fn set_pitch(&mut self, voice: VoiceId, semitones: f32, fade: Fade) -> Result<()> {
1572 self.now().set_pitch(voice, semitones, fade)
1573 }
1574 pub fn set_position(&mut self, voice: VoiceId, position: [f32; 2], fade: Fade) -> Result<()> {
1579 self.now().set_position(voice, position, fade)
1580 }
1581 pub fn set_emitter(&mut self, voice: VoiceId, emitter: Emitter, fade: Fade) -> Result<()> {
1584 self.now().set_emitter(voice, emitter, fade)
1585 }
1586 pub fn set_listener(&mut self, listener: Listener, fade: Fade) -> Result<()> {
1588 self.now().set_listener(listener, fade)
1589 }
1590 pub fn latency(&self) -> u64 {
1593 self.latency
1594 }
1595 pub fn limiter_gain(&self) -> Option<f32> {
1598 self.limited
1599 .then(|| f32::from_bits(self.limiter_gain.load(Ordering::Relaxed)))
1600 }
1601 pub fn space(&self) -> Space2d {
1603 self.space
1604 }
1605 fn place(&self, position: [f32; 2]) -> Result<(f64, f64)> {
1606 if !position.iter().all(|c| c.is_finite()) {
1607 return Err(Error::invalid("A position must be finite"));
1608 }
1609 let (pan, gain) = self.space.place(position);
1610 Ok((pan as f64, gain as f64))
1611 }
1612 pub fn reseed(&mut self, seed: u64) {
1614 self.random = SplitMix(seed);
1615 }
1616 pub fn set_bus_gain(&mut self, bus: BusId, gain: f32, fade: Fade) -> Result<()> {
1617 self.now().set_bus_gain(bus, gain, fade)
1618 }
1619 pub fn poll_event(&mut self) -> Option<MixerEvent> {
1623 if let Some(event) = self.effects.poll() {
1624 return Some(event);
1625 }
1626 if let Some(event) = self.routing.poll() {
1627 return Some(event);
1628 }
1629 while let Some(returned) = self.returns.pop() {
1630 drop(returned.voice);
1631 #[cfg(feature = "music")]
1632 drop(returned.music);
1633 if returned.event.is_some() {
1634 return returned.event;
1635 }
1636 }
1637 None
1638 }
1639
1640 fn frames(&self, fade: Fade) -> Result<(u64, FadeCurve)> {
1641 if !fade.seconds.is_finite() || fade.seconds < 0.0 || fade.seconds > 3600.0 {
1642 return Err(Error::invalid("A fade lasts 0 to 3600 seconds"));
1643 }
1644 Ok(((fade.seconds * self.rate as f64).round() as u64, fade.curve))
1645 }
1646 fn table(&mut self, source_rate: u32) -> Arc<SincTable> {
1647 if let Some((_, table)) = self.tables.iter().find(|(r, _)| *r == source_rate) {
1648 return table.clone();
1649 }
1650 let table = Arc::new(SincTable::new(source_rate as f64 / self.rate as f64));
1651 self.tables.push((source_rate, table.clone()));
1652 table
1653 }
1654 fn send(&mut self, frame: Option<u64>, action: Action) -> Result<()> {
1655 let sequence = self.next_sequence;
1656 self.next_sequence += 1;
1657 let command = Command {
1658 frame,
1659 sequence,
1660 action,
1661 };
1662 match self.commands.push(command) {
1663 Ok(()) => Ok(()),
1664 Err(PushError::Full(_)) => Err(Error::limit("The mixer's command queue is full")),
1665 Err(PushError::Disconnected(_)) => Err(Error::invalid("The mixer was dropped")),
1666 }
1667 }
1668}
1669
1670pub struct Commands<'a> {
1672 handle: &'a mut MixerHandle,
1673 frame: Option<u64>,
1674}
1675impl MixerHandle {
1676 fn prepare_voice(&mut self, sound: Sound, play: &Play) -> Result<Box<Voice>> {
1678 let handle = self;
1679 let (rate, channels, frames) = match &sound {
1680 Sound::Memory(audio) => (
1681 audio.sample_rate(),
1682 audio.channels(),
1683 Some(audio.frames() as u64),
1684 ),
1685 Sound::Stream(stream) => (stream.sample_rate(), stream.channels(), stream.frames()),
1686 };
1687 Limits::default().check(0, channels, rate)?;
1688 if !(1..=2).contains(&channels) {
1689 return Err(Error::invalid("A voice plays a mono or stereo sound"));
1690 }
1691 if !play.gain.is_finite() || play.gain < 0.0 || !play.pan.is_finite() {
1692 return Err(Error::invalid(
1693 "Gain must be finite and non-negative, and pan finite",
1694 ));
1695 }
1696 if play.bus.0 > handle.buses.len() {
1697 return Err(Error::invalid("Unknown bus"));
1698 }
1699 if let Some(emitter) = play.emitter {
1700 emitter.validate()?;
1701 if play.position.is_some() {
1702 return Err(Error::invalid("Choose 2D position or a 3D emitter"));
1703 }
1704 }
1705 check_rate(play.rate)?;
1706 check_pitch(play.pitch)?;
1707 let Variation {
1708 pitch: vary_pitch,
1709 gain_db: vary_gain,
1710 } = play.variation;
1711 if !(0.0..=24.0).contains(&vary_pitch) || !(0.0..=24.0).contains(&vary_gain) {
1712 return Err(Error::invalid("Variation ranges must be 0..=24"));
1713 }
1714 if let Loop::Region { start, end } = play.looping {
1715 if start >= end || frames.is_some_and(|n| end > n) {
1716 return Err(Error::invalid(
1717 "A loop region needs start < end <= the sound's frames",
1718 ));
1719 }
1720 }
1721 let fade_in = handle.frames(play.fade_in)?;
1722 let start = start_frame(play.start, rate, frames)?;
1723 let step = (((rate as u128) << 32) + handle.rate as u128 / 2) / handle.rate as u128;
1724 let step = step as u64;
1725 let table =
1726 (step != ONE && play.resampling == ResamplingQuality::Sinc).then(|| handle.table(rate));
1727 let base_ratio = rate as f64 / handle.rate as f64;
1728 let mut gain = play.gain as f64;
1729 let mut pitch = play.pitch as f64;
1730 if vary_pitch > 0.0 {
1731 pitch += handle.random.symmetric() * vary_pitch as f64;
1732 }
1733 if vary_gain > 0.0 {
1734 let db = handle.random.symmetric() * vary_gain as f64;
1735 gain *= crate::math::exp2(db * std::f64::consts::LOG2_10 / 20.0);
1736 }
1737 let id = VoiceId(handle.next_voice);
1738 handle.next_voice += 1;
1739 let source_peak = match &sound {
1740 Sound::Memory(audio) => handle.source_peak(audio),
1741 Sound::Stream(_) => 1.0,
1742 };
1743 let feed = match sound {
1744 Sound::Memory(audio) => Feed::Memory(audio),
1745 Sound::Stream(stream) => Feed::Stream(stream),
1746 };
1747 let bank = handle.bank.clone();
1748 let resampling = Resampling {
1749 quality: play.resampling,
1750 step,
1751 base_ratio,
1752 table,
1753 bank,
1754 };
1755 let mut voice = Box::new(Voice::new(id, feed, channels, play.looping, resampling));
1756 voice.source_peak = source_peak;
1757 voice.bus = play.bus.0;
1758 voice.priority = play.priority;
1759 voice.gain = Ramp::constant(gain);
1760 voice.rate = Ramp::constant(play.rate as f64);
1761 voice.pitch = Ramp::constant(pitch);
1762 voice.pan = Ramp::constant(play.pan.clamp(-1.0, 1.0) as f64);
1763 voice.emitter = play.emitter;
1764 if let Some(position) = play.position {
1765 let (pan, distance) = handle.place(position)?;
1766 voice.pan = Ramp::constant(pan);
1767 voice.distance = Ramp::constant(distance);
1768 }
1769 voice.fade_in = fade_in;
1770 voice.set_start(start)?;
1771 if let Some(limit) = play.instances {
1772 if !limit.min_gap.is_finite() || !(0.0..=MAX_GAP).contains(&limit.min_gap) {
1773 return Err(Error::invalid("An instance gap must be 0..=60 seconds"));
1774 }
1775 voice.instances = Some(voice::InstanceLimit {
1776 key: limit.key,
1777 max: limit.max,
1778 min_gap: (limit.min_gap * handle.rate as f64).round() as u64,
1779 policy: limit.policy,
1780 });
1781 }
1782 Ok(voice)
1783 }
1784}
1785
1786impl Commands<'_> {
1787 pub fn play(self, sound: impl Into<Sound>, play: &Play) -> Result<VoiceId> {
1790 let voice = self.handle.prepare_voice(sound.into(), play)?;
1791 let id = voice.id;
1792 self.handle.send(self.frame, Action::Play(voice))?;
1793 Ok(id)
1794 }
1795 pub fn stop(self, voice: VoiceId, fade: Fade) -> Result<()> {
1797 let (len, curve) = self.handle.frames(fade)?;
1798 self.handle
1799 .send(self.frame, Action::Stop(voice, len, curve))
1800 }
1801 pub fn stop_all(self, fade: Fade) -> Result<()> {
1802 let (len, curve) = self.handle.frames(fade)?;
1803 self.handle.send(self.frame, Action::StopAll(len, curve))
1804 }
1805 pub fn set_gain(self, voice: VoiceId, gain: f32, fade: Fade) -> Result<()> {
1806 if !gain.is_finite() || gain < 0.0 {
1807 return Err(Error::invalid("Gain must be finite and non-negative"));
1808 }
1809 let (len, curve) = self.handle.frames(fade)?;
1810 self.handle
1811 .send(self.frame, Action::Gain(voice, gain as f64, len, curve))
1812 }
1813 pub fn set_pan(self, voice: VoiceId, pan: f32, fade: Fade) -> Result<()> {
1815 if !pan.is_finite() {
1816 return Err(Error::invalid("Pan must be finite"));
1817 }
1818 let (len, curve) = self.handle.frames(fade)?;
1819 let pan = pan.clamp(-1.0, 1.0) as f64;
1820 self.handle
1821 .send(self.frame, Action::Pan(voice, pan, len, curve))
1822 }
1823 pub fn set_rate(self, voice: VoiceId, rate: f32, fade: Fade) -> Result<()> {
1826 check_rate(rate)?;
1827 let (len, curve) = self.handle.frames(fade)?;
1828 self.handle
1829 .send(self.frame, Action::Rate(voice, rate as f64, len, curve))
1830 }
1831 pub fn set_pitch(self, voice: VoiceId, semitones: f32, fade: Fade) -> Result<()> {
1834 check_pitch(semitones)?;
1835 let (len, curve) = self.handle.frames(fade)?;
1836 self.handle.send(
1837 self.frame,
1838 Action::Pitch(voice, semitones as f64, len, curve),
1839 )
1840 }
1841 pub fn set_position(self, voice: VoiceId, position: [f32; 2], fade: Fade) -> Result<()> {
1842 let (pan, gain) = self.handle.place(position)?;
1843 let (len, curve) = self.handle.frames(fade)?;
1844 self.handle
1845 .send(self.frame, Action::Position(voice, pan, gain, len, curve))
1846 }
1847 pub fn pause(self, voice: VoiceId, fade: Fade) -> Result<()> {
1848 let (len, curve) = self.handle.frames(fade)?;
1849 self.handle
1850 .send(self.frame, Action::Pause(voice, len, curve))
1851 }
1852 pub fn set_emitter(self, voice: VoiceId, emitter: Emitter, fade: Fade) -> Result<()> {
1855 emitter.validate()?;
1856 let (len, curve) = self.handle.frames(fade)?;
1857 self.handle
1858 .send(self.frame, Action::Emitter(voice, emitter, len, curve))
1859 }
1860 pub fn set_listener(self, listener: Listener, fade: Fade) -> Result<()> {
1862 listener.validate()?;
1863 let (len, curve) = self.handle.frames(fade)?;
1864 self.handle
1865 .send(self.frame, Action::Listener(listener, len, curve))
1866 }
1867 pub fn resume(self, voice: VoiceId, fade: Fade) -> Result<()> {
1868 let (len, curve) = self.handle.frames(fade)?;
1869 self.handle
1870 .send(self.frame, Action::Resume(voice, len, curve))
1871 }
1872 pub fn pause_bus(self, bus: BusId, fade: Fade) -> Result<()> {
1873 if bus.0 > self.handle.buses.len() {
1874 return Err(Error::invalid("Unknown bus"));
1875 }
1876 let (len, curve) = self.handle.frames(fade)?;
1877 self.handle
1878 .send(self.frame, Action::PauseBus(bus.0, len, curve))
1879 }
1880 pub fn resume_bus(self, bus: BusId, fade: Fade) -> Result<()> {
1881 if bus.0 > self.handle.buses.len() {
1882 return Err(Error::invalid("Unknown bus"));
1883 }
1884 let (len, curve) = self.handle.frames(fade)?;
1885 self.handle
1886 .send(self.frame, Action::ResumeBus(bus.0, len, curve))
1887 }
1888 pub fn set_bus_gain(self, bus: BusId, gain: f32, fade: Fade) -> Result<()> {
1890 if !gain.is_finite() || gain < 0.0 {
1891 return Err(Error::invalid("Gain must be finite and non-negative"));
1892 }
1893 if bus.0 > self.handle.buses.len() {
1894 return Err(Error::invalid("Unknown bus"));
1895 }
1896 let (len, curve) = self.handle.frames(fade)?;
1897 self.handle
1898 .send(self.frame, Action::BusGain(bus.0, gain as f64, len, curve))
1899 }
1900}
1901
1902fn check_rate(rate: f32) -> Result<()> {
1903 if (1.0 / 16.0..=8.0).contains(&rate) {
1904 Ok(())
1905 } else {
1906 Err(Error::invalid("Playback rate must be 1/16..=8"))
1907 }
1908}
1909fn check_pitch(semitones: f32) -> Result<()> {
1910 if (-48.0..=48.0).contains(&semitones) {
1911 Ok(())
1912 } else {
1913 Err(Error::invalid("Pitch must be -48..=48 semitones"))
1914 }
1915}
1916
1917fn start_frame(start: TimePosition, rate: u32, frames: Option<u64>) -> Result<u64> {
1919 let position = match start {
1920 TimePosition::Frames(n) => n as f64,
1921 TimePosition::Seconds(seconds) => seconds * rate as f64,
1922 };
1923 if !position.is_finite() {
1924 return Err(Error::invalid("The start must be finite"));
1925 }
1926 let position = position.round();
1927 let frame = if position < 0.0 {
1928 let total = frames.ok_or_else(|| {
1929 Error::invalid("A start counted from the end needs a sound of known length")
1930 })?;
1931 (total as f64 + position).max(0.0)
1932 } else {
1933 position
1934 };
1935 if frames.is_some_and(|total| frame > total as f64) {
1936 return Err(Error::invalid("The start is past the end of the sound"));
1937 }
1938 Ok(frame as u64)
1939}