Skip to main content

oxideav_scene/
audio.rs

1//! Timeline-triggered audio cues.
2//!
3//! Audio is not positioned — there's one mix bus per [`Scene`].
4//! Each [`AudioCue`] has a trigger timestamp at which it starts
5//! playing. The renderer accumulates contributions from every live
6//! cue into the scene's output audio buffer.
7
8use std::sync::Arc;
9
10use crate::animation::Animation;
11use crate::duration::TimeStamp;
12
13/// One scheduled audio playback.
14#[derive(Clone, Debug)]
15pub struct AudioCue {
16    /// Scene-time at which this cue starts playing.
17    pub trigger: TimeStamp,
18    pub source: AudioSource,
19    /// Animated 0.0..=1.0 volume envelope. A cue with no keyframes
20    /// plays at unit gain.
21    pub volume: Animation,
22    /// Other cues / bus tags to duck while this cue is playing.
23    pub duck: Vec<DuckBus>,
24    /// Optional explicit stop time. `None` = play to the source's
25    /// natural end.
26    pub end: Option<TimeStamp>,
27}
28
29/// Audio content source.
30#[non_exhaustive]
31#[derive(Clone, Debug)]
32pub enum AudioSource {
33    Path(String),
34    EncodedBytes(Arc<[u8]>),
35    /// Pre-decoded PCM — interleaved S16.
36    PcmS16 {
37        sample_rate: u32,
38        channels: u8,
39        samples: Arc<[i16]>,
40    },
41    /// Pre-decoded PCM — interleaved F32.
42    PcmF32 {
43        sample_rate: u32,
44        channels: u8,
45        samples: Arc<[f32]>,
46    },
47    /// Generator — sine / noise / silence. Useful for placeholder
48    /// beds and quick tests.
49    Generator(Generator),
50}
51
52/// Simple procedural audio generators.
53#[non_exhaustive]
54#[derive(Clone, Copy, Debug)]
55pub enum Generator {
56    Silence,
57    SineWave { frequency_hz: f32, amplitude: f32 },
58    WhiteNoise { amplitude: f32 },
59}
60
61/// Ducking reference — attenuate all cues sharing this `bus` tag
62/// while the owning cue plays. `reduction_db` is the target level;
63/// `attack_ms` / `release_ms` control the envelope around the
64/// trigger.
65#[derive(Clone, Copy, Debug)]
66pub struct DuckBus {
67    pub bus: u32,
68    pub reduction_db: f32,
69    pub attack_ms: u32,
70    pub release_ms: u32,
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use crate::animation::{AnimatedProperty, Easing, Repeat};
77
78    #[test]
79    fn default_volume_animation_holds_unity() {
80        let cue = AudioCue {
81            trigger: 0,
82            source: AudioSource::Generator(Generator::Silence),
83            volume: Animation::new(
84                AnimatedProperty::Volume,
85                Vec::new(),
86                Easing::Linear,
87                Repeat::Once,
88            ),
89            duck: Vec::new(),
90            end: None,
91        };
92        assert!(cue.volume.sample(0).is_none());
93    }
94}