Skip to main content

nightshade/ecs/audio/
components.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(
4    Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize, enum2schema::Schema,
5)]
6#[schema(string_enum)]
7pub enum AudioBus {
8    Master,
9    Music,
10    #[default]
11    Sfx,
12    Ambient,
13    Voice,
14    Ui,
15}
16
17#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, enum2schema::Schema)]
18pub struct AudioSource {
19    pub audio_ref: Option<String>,
20    pub volume: f32,
21    pub looping: bool,
22    pub playing: bool,
23    /// When true while `playing` is also true, the running sound is held at
24    /// its current position. Clearing it resumes from where it stopped, unlike
25    /// `playing = false`, which frees the sound and restarts from the beginning.
26    #[serde(default)]
27    pub paused: bool,
28    pub spatial: bool,
29    pub bus: AudioBus,
30    pub min_distance: f32,
31    pub max_distance: f32,
32    pub reverb_zones: Vec<(String, f32)>,
33    /// Optional list of additional clip paths. When non-empty and
34    /// `random_pick` is true, the audio system selects one of
35    /// `audio_ref` + `random_clips` at random each time playback
36    /// transitions from stopped to playing. Useful for footstep packs,
37    /// random stingers, or one-shot scares.
38    #[serde(default)]
39    pub random_clips: Vec<String>,
40    /// When true, the source treats `audio_ref` plus `random_clips` as
41    /// a pool and chooses one at random when starting playback.
42    #[serde(default)]
43    pub random_pick: bool,
44    /// Playback rate factor applied when the sound starts. 1.0 plays at
45    /// the original pitch and speed. 2.0 plays an octave higher and
46    /// twice as fast. 0.5 plays an octave lower and at half speed.
47    #[serde(default = "default_playback_rate")]
48    pub playback_rate: f64,
49}
50
51fn default_playback_rate() -> f64 {
52    1.0
53}
54
55impl Default for AudioSource {
56    fn default() -> Self {
57        Self {
58            audio_ref: None,
59            volume: 1.0,
60            looping: false,
61            playing: false,
62            paused: false,
63            spatial: false,
64            bus: AudioBus::Sfx,
65            min_distance: 1.0,
66            max_distance: 50.0,
67            reverb_zones: Vec::new(),
68            random_clips: Vec::new(),
69            random_pick: false,
70            playback_rate: 1.0,
71        }
72    }
73}
74
75impl AudioSource {
76    pub fn new(audio_ref: impl Into<String>) -> Self {
77        Self {
78            audio_ref: Some(audio_ref.into()),
79            ..Default::default()
80        }
81    }
82
83    pub fn with_volume(mut self, volume: f32) -> Self {
84        self.volume = volume;
85        self
86    }
87
88    pub fn with_looping(mut self, looping: bool) -> Self {
89        self.looping = looping;
90        self
91    }
92
93    pub fn with_spatial(mut self, spatial: bool) -> Self {
94        self.spatial = spatial;
95        self
96    }
97
98    pub fn with_bus(mut self, bus: AudioBus) -> Self {
99        self.bus = bus;
100        self
101    }
102
103    pub fn with_distance(mut self, min: f32, max: f32) -> Self {
104        self.min_distance = min;
105        self.max_distance = max;
106        self
107    }
108
109    pub fn with_reverb(mut self, reverb: bool) -> Self {
110        self.reverb_zones.retain(|(name, _)| name != "default");
111        if reverb {
112            self.reverb_zones.push(("default".to_string(), 0.0));
113        }
114        self
115    }
116
117    pub fn with_reverb_zone(mut self, zone: impl Into<String>, send_decibels: f32) -> Self {
118        let zone = zone.into();
119        self.reverb_zones.retain(|(name, _)| name != &zone);
120        self.reverb_zones.push((zone, send_decibels));
121        self
122    }
123
124    pub fn playing(mut self) -> Self {
125        self.playing = true;
126        self
127    }
128
129    pub fn with_paused(mut self, paused: bool) -> Self {
130        self.paused = paused;
131        self
132    }
133
134    pub fn with_playback_rate(mut self, rate: f64) -> Self {
135        self.playback_rate = rate;
136        self
137    }
138}
139
140#[derive(Debug, Clone, Copy, Default)]
141pub struct AudioListener;