Skip to main content

euv_engine/audio/
impl.rs

1use super::*;
2
3/// Implements audio context management for `GameAudioContext`.
4impl GameAudioContext {
5    /// Creates a new audio context with default master volume.
6    ///
7    /// # Returns
8    ///
9    /// - `GameAudioContext` - The new audio context.
10    pub fn create() -> GameAudioContext {
11        let context: AudioContext = AudioContext::new().expect("should create audio context");
12        let master_gain: GainNode = context
13            .create_gain()
14            .expect("should create master gain node");
15        let _: Result<AudioNode, JsValue> =
16            master_gain.connect_with_audio_node(&context.destination());
17        master_gain.gain().set_value(AUDIO_DEFAULT_VOLUME as f32);
18        GameAudioContext::new(context, master_gain, AUDIO_DEFAULT_VOLUME)
19    }
20
21    /// Sets the master volume for all audio output.
22    ///
23    /// # Arguments
24    ///
25    /// - `f64` - The volume level in the range 0.0 to 1.0.
26    pub fn apply_master_volume(&self, volume: f64) {
27        let clamped: f64 = Numeric::clamp(volume, 0.0, 1.0);
28        self.get_master_gain().gain().set_value(clamped as f32);
29    }
30
31    /// Resumes the audio context if it was suspended.
32    pub fn resume(&self) {
33        let _: Result<Promise, JsValue> = self.get_context().resume();
34    }
35
36    /// Suspends the audio context, temporarily halting all audio processing.
37    pub fn suspend(&self) {
38        let _: Result<Promise, JsValue> = self.get_context().suspend();
39    }
40
41    /// Closes the audio context and releases all resources.
42    pub fn close(&self) {
43        let _: Result<Promise, JsValue> = self.get_context().close();
44    }
45
46    /// Returns the current sample rate of the audio context in Hz.
47    ///
48    /// # Returns
49    ///
50    /// - `f64` - The sample rate.
51    pub fn sample_rate(&self) -> f64 {
52        self.get_context().sample_rate() as f64
53    }
54
55    /// Returns the current playback time in seconds.
56    ///
57    /// # Returns
58    ///
59    /// - `f64` - The current time.
60    pub fn current_time(&self) -> f64 {
61        self.get_context().current_time()
62    }
63}
64
65/// Implements `Default` for `GameAudioContext` as a new context.
66impl Default for GameAudioContext {
67    fn default() -> GameAudioContext {
68        GameAudioContext::create()
69    }
70}
71
72/// Implements playback control for `AudioClip`.
73impl AudioClip {
74    /// Creates a new audio clip from a decoded buffer.
75    ///
76    /// # Arguments
77    ///
78    /// - `AudioBuffer` - The decoded audio data.
79    /// - `String` - The clip name.
80    ///
81    /// # Returns
82    ///
83    /// - `AudioClip` - The new clip.
84    pub fn create(buffer: AudioBuffer, name: String) -> AudioClip {
85        AudioClip::new(
86            buffer,
87            name,
88            AudioPlayState::Stopped,
89            AUDIO_DEFAULT_LOOP,
90            AUDIO_DEFAULT_VOLUME,
91            AUDIO_DEFAULT_PLAYBACK_RATE,
92        )
93    }
94
95    /// Plays this clip through the given audio context.
96    ///
97    /// Creates a new `AudioBufferSourceNode`, connects it through a gain node
98    /// to the master gain, and starts playback. If already playing, does nothing.
99    ///
100    /// # Arguments
101    ///
102    /// - `&GameAudioContext` - The audio context to play through.
103    pub fn play(&mut self, audio_context: &GameAudioContext) {
104        if self.get_state() == AudioPlayState::Playing {
105            return;
106        }
107        let source: AudioBufferSourceNode = audio_context
108            .get_context()
109            .create_buffer_source()
110            .expect("should create buffer source");
111        source.set_buffer(Some(self.get_buffer()));
112        source.set_loop(self.get_looping());
113        source
114            .playback_rate()
115            .set_value(self.get_playback_rate() as f32);
116        let gain: GainNode = audio_context
117            .get_context()
118            .create_gain()
119            .expect("should create gain node");
120        gain.gain().set_value(self.get_volume() as f32);
121        let _: Result<AudioNode, JsValue> = source.connect_with_audio_node(&gain);
122        let _: Result<AudioNode, JsValue> =
123            gain.connect_with_audio_node(audio_context.get_master_gain());
124        let _: Result<(), JsValue> = source.start();
125        self.set_source_node(Some(source));
126        self.set_state(AudioPlayState::Playing);
127    }
128
129    /// Stops playback of this clip and releases the source node.
130    pub fn stop(&mut self) {
131        if let Some(source) = self.get_mut_source_node().take() {
132            let scheduled: &AudioScheduledSourceNode = source.as_ref();
133            let _: Result<(), JsValue> = scheduled.stop_with_when(0.0);
134            let _: Result<(), JsValue> = source.disconnect();
135        }
136        self.set_state(AudioPlayState::Stopped);
137    }
138
139    /// Sets whether the clip should loop.
140    ///
141    /// # Arguments
142    ///
143    /// - `bool` - True to enable looping.
144    pub fn update_looping(&mut self, looping: bool) {
145        self.set_looping(looping);
146        if let Some(source) = self.get_mut_source_node() {
147            source.set_loop(looping);
148        }
149    }
150
151    /// Sets the volume of this clip.
152    ///
153    /// # Arguments
154    ///
155    /// - `f64` - The volume in the range 0.0 to 1.0.
156    pub fn update_volume(&mut self, volume: f64) {
157        self.set_volume(Numeric::clamp(volume, 0.0, 1.0));
158    }
159
160    /// Sets the playback rate multiplier.
161    ///
162    /// # Arguments
163    ///
164    /// - `f64` - The playback rate (1.0 = normal speed).
165    pub fn update_playback_rate(&mut self, rate: f64) {
166        self.set_playback_rate(rate);
167        if let Some(source) = self.get_mut_source_node() {
168            source.playback_rate().set_value(rate as f32);
169        }
170    }
171
172    /// Returns the duration of the audio buffer in seconds.
173    ///
174    /// # Returns
175    ///
176    /// - `f64` - The duration.
177    pub fn duration(&self) -> f64 {
178        self.get_buffer().duration()
179    }
180
181    /// Returns the number of channels in the audio buffer.
182    ///
183    /// # Returns
184    ///
185    /// - `u32` - The channel count.
186    pub fn channel_count(&self) -> u32 {
187        self.get_buffer().number_of_channels()
188    }
189}