oxygengine_audio/
component.rs

1use core::{
2    ecs::Entity,
3    prefab::{Prefab, PrefabError, PrefabProxy},
4    state::StateToken,
5    Scalar,
6};
7use serde::{Deserialize, Serialize};
8use std::{
9    borrow::Cow,
10    collections::HashMap,
11    sync::{
12        atomic::{AtomicBool, Ordering},
13        Arc,
14    },
15};
16
17#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
18pub(crate) enum AudioSourceDirtyMode {
19    None,
20    Param,
21    All,
22}
23
24impl Default for AudioSourceDirtyMode {
25    fn default() -> Self {
26        Self::None
27    }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct AudioSourceConfig {
32    pub audio: Cow<'static, str>,
33    #[serde(default)]
34    pub streaming: bool,
35    #[serde(default)]
36    pub looped: bool,
37    #[serde(default = "AudioSourceConfig::default_playback_rate")]
38    pub playback_rate: Scalar,
39    #[serde(default = "AudioSourceConfig::default_volume")]
40    pub volume: Scalar,
41    #[serde(default)]
42    pub play: bool,
43}
44
45impl AudioSourceConfig {
46    fn default_playback_rate() -> Scalar {
47        1.0
48    }
49
50    fn default_volume() -> Scalar {
51        1.0
52    }
53
54    pub fn new(audio: Cow<'static, str>) -> Self {
55        Self {
56            audio,
57            streaming: false,
58            looped: false,
59            playback_rate: 1.0,
60            volume: 1.0,
61            play: false,
62        }
63    }
64
65    pub fn audio(mut self, value: Cow<'static, str>) -> Self {
66        self.audio = value;
67        self
68    }
69
70    pub fn streaming(mut self, value: bool) -> Self {
71        self.streaming = value;
72        self
73    }
74
75    pub fn looped(mut self, value: bool) -> Self {
76        self.looped = value;
77        self
78    }
79
80    pub fn playback_rate(mut self, value: Scalar) -> Self {
81        self.playback_rate = value;
82        self
83    }
84
85    pub fn volume(mut self, value: Scalar) -> Self {
86        self.volume = value;
87        self
88    }
89
90    pub fn play(mut self, value: bool) -> Self {
91        self.play = value;
92        self
93    }
94}
95
96impl Prefab for AudioSourceConfig {}
97
98#[derive(Debug, Clone)]
99pub struct AudioSource {
100    audio: Cow<'static, str>,
101    streaming: bool,
102    looped: bool,
103    playback_rate: Scalar,
104    volume: Scalar,
105    play: bool,
106    pub(crate) current_time: Option<Scalar>,
107
108    pub(crate) ready: Arc<AtomicBool>,
109
110    pub(crate) dirty: AudioSourceDirtyMode,
111}
112
113impl Default for AudioSource {
114    fn default() -> Self {
115        Self {
116            audio: "".into(),
117            streaming: false,
118            looped: false,
119            playback_rate: 1.0,
120            volume: 1.0,
121            play: false,
122            current_time: None,
123            ready: Arc::new(AtomicBool::new(false)),
124            dirty: AudioSourceDirtyMode::None,
125        }
126    }
127}
128
129impl From<AudioSourceConfig> for AudioSource {
130    fn from(config: AudioSourceConfig) -> Self {
131        Self::new_complex(
132            config.audio,
133            config.streaming,
134            config.looped,
135            config.playback_rate,
136            config.volume,
137            config.play,
138        )
139    }
140}
141
142impl AudioSource {
143    pub fn new(audio: Cow<'static, str>, streaming: bool) -> Self {
144        Self {
145            audio,
146            streaming,
147            looped: false,
148            playback_rate: 1.0,
149            volume: 1.0,
150            play: false,
151            current_time: None,
152            ready: Arc::new(AtomicBool::new(false)),
153            dirty: AudioSourceDirtyMode::All,
154        }
155    }
156
157    pub fn new_play(audio: Cow<'static, str>, streaming: bool, play: bool) -> Self {
158        Self {
159            audio,
160            streaming,
161            looped: false,
162            playback_rate: 1.0,
163            volume: 1.0,
164            play,
165            current_time: None,
166            ready: Arc::new(AtomicBool::new(false)),
167            dirty: AudioSourceDirtyMode::All,
168        }
169    }
170
171    pub fn new_complex(
172        audio: Cow<'static, str>,
173        streaming: bool,
174        looped: bool,
175        playback_rate: Scalar,
176        volume: Scalar,
177        play: bool,
178    ) -> Self {
179        Self {
180            audio,
181            streaming,
182            looped,
183            playback_rate,
184            volume,
185            play,
186            current_time: None,
187            ready: Arc::new(AtomicBool::new(false)),
188            dirty: AudioSourceDirtyMode::All,
189        }
190    }
191
192    pub fn audio(&self) -> &str {
193        &self.audio
194    }
195
196    pub fn streaming(&self) -> bool {
197        self.streaming
198    }
199
200    pub fn looped(&self) -> bool {
201        self.looped
202    }
203
204    pub fn set_looped(&mut self, looped: bool) {
205        self.looped = looped;
206        self.dirty = self.dirty.max(AudioSourceDirtyMode::Param);
207    }
208
209    pub fn playback_rate(&self) -> Scalar {
210        self.playback_rate
211    }
212
213    pub fn set_playback_rate(&mut self, playback_rate: Scalar) {
214        self.playback_rate = playback_rate;
215        self.dirty = self.dirty.max(AudioSourceDirtyMode::Param);
216    }
217
218    pub fn volume(&self) -> Scalar {
219        self.volume
220    }
221
222    pub fn set_volume(&mut self, volume: Scalar) {
223        self.volume = volume;
224        self.dirty = self.dirty.max(AudioSourceDirtyMode::Param);
225    }
226
227    pub fn current_time(&self) -> Option<Scalar> {
228        self.current_time
229    }
230
231    pub fn is_playing(&self) -> bool {
232        self.play
233    }
234
235    pub fn play(&mut self) {
236        self.play = true;
237        self.dirty = self.dirty.max(AudioSourceDirtyMode::All);
238    }
239
240    pub fn stop(&mut self) {
241        self.play = false;
242        self.dirty = self.dirty.max(AudioSourceDirtyMode::All);
243    }
244
245    pub fn is_ready(&self) -> bool {
246        self.ready.load(Ordering::Relaxed)
247    }
248}
249
250pub type AudioSourcePrefabProxy = AudioSourceConfig;
251
252impl PrefabProxy<AudioSourcePrefabProxy> for AudioSource {
253    fn from_proxy_with_extras(
254        proxy: AudioSourcePrefabProxy,
255        _: &HashMap<String, Entity>,
256        _: StateToken,
257    ) -> Result<Self, PrefabError> {
258        Ok(proxy.into())
259    }
260}