Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_audio/src/sinks.rs
6598 views
1
use crate::Volume;
2
use bevy_ecs::component::Component;
3
use bevy_math::Vec3;
4
use bevy_transform::prelude::Transform;
5
use core::time::Duration;
6
pub use rodio::source::SeekError;
7
use rodio::{Sink, SpatialSink};
8
9
/// Common interactions with an audio sink.
10
pub trait AudioSinkPlayback {
11
/// Gets the volume of the sound as a [`Volume`].
12
///
13
/// If the sink is muted, this returns the managed volume rather than the
14
/// sink's actual volume. This allows you to use the returned volume as if
15
/// the sink were not muted, because a muted sink has a physical volume of
16
/// 0.
17
fn volume(&self) -> Volume;
18
19
/// Changes the volume of the sound to the given [`Volume`].
20
///
21
/// If the sink is muted, changing the volume won't unmute it, i.e. the
22
/// sink's volume will remain "off" / "muted". However, the sink will
23
/// remember the volume change and it will be used when
24
/// [`unmute`](Self::unmute) is called. This allows you to control the
25
/// volume even when the sink is muted.
26
fn set_volume(&mut self, volume: Volume);
27
28
/// Gets the speed of the sound.
29
///
30
/// The value `1.0` is the "normal" speed (unfiltered input). Any value other than `1.0`
31
/// will change the play speed of the sound.
32
fn speed(&self) -> f32;
33
34
/// Changes the speed of the sound.
35
///
36
/// The value `1.0` is the "normal" speed (unfiltered input). Any value other than `1.0`
37
/// will change the play speed of the sound.
38
fn set_speed(&self, speed: f32);
39
40
/// Resumes playback of a paused sink.
41
///
42
/// No effect if not paused.
43
fn play(&self);
44
45
/// Returns the position of the sound that's being played.
46
///
47
/// This takes into account any speedup or delay applied.
48
///
49
/// Example: if you [`set_speed(2.0)`](Self::set_speed) and [`position()`](Self::position) returns *5s*,
50
/// then the position in the recording is *10s* from its start.
51
fn position(&self) -> Duration;
52
53
/// Attempts to seek to a given position in the current source.
54
///
55
/// This blocks between 0 and ~5 milliseconds.
56
///
57
/// As long as the duration of the source is known, seek is guaranteed to saturate
58
/// at the end of the source. For example given a source that reports a total duration
59
/// of 42 seconds calling `try_seek()` with 60 seconds as argument will seek to
60
/// 42 seconds.
61
///
62
/// # Errors
63
/// This function will return [`SeekError::NotSupported`] if one of the underlying
64
/// sources does not support seeking.
65
///
66
/// It will return an error if an implementation ran
67
/// into one during the seek.
68
///
69
/// When seeking beyond the end of a source, this
70
/// function might return an error if the duration of the source is not known.
71
fn try_seek(&self, pos: Duration) -> Result<(), SeekError>;
72
73
/// Pauses playback of this sink.
74
///
75
/// No effect if already paused.
76
/// A paused sink can be resumed with [`play`](Self::play).
77
fn pause(&self);
78
79
/// Toggles playback of the sink.
80
///
81
/// If the sink is paused, toggling playback resumes it. If the sink is
82
/// playing, toggling playback pauses it.
83
fn toggle_playback(&self) {
84
if self.is_paused() {
85
self.play();
86
} else {
87
self.pause();
88
}
89
}
90
91
/// Returns true if the sink is paused.
92
///
93
/// Sinks can be paused and resumed using [`pause`](Self::pause) and [`play`](Self::play).
94
fn is_paused(&self) -> bool;
95
96
/// Stops the sink.
97
///
98
/// It won't be possible to restart it afterwards.
99
fn stop(&self);
100
101
/// Returns true if this sink has no more sounds to play.
102
fn empty(&self) -> bool;
103
104
/// Returns true if the sink is muted.
105
fn is_muted(&self) -> bool;
106
107
/// Mutes the sink.
108
///
109
/// Muting a sink sets the volume to 0. Use [`unmute`](Self::unmute) to
110
/// unmute the sink and restore the original volume.
111
fn mute(&mut self);
112
113
/// Unmutes the sink.
114
///
115
/// Restores the volume to the value it was before it was muted.
116
fn unmute(&mut self);
117
118
/// Toggles whether the sink is muted or not.
119
fn toggle_mute(&mut self) {
120
if self.is_muted() {
121
self.unmute();
122
} else {
123
self.mute();
124
}
125
}
126
}
127
128
/// Used to control audio during playback.
129
///
130
/// Bevy inserts this component onto your entities when it begins playing an audio source.
131
/// Use [`AudioPlayer`][crate::AudioPlayer] to trigger that to happen.
132
///
133
/// You can use this component to modify the playback settings while the audio is playing.
134
///
135
/// If this component is removed from an entity, and an [`AudioSource`][crate::AudioSource] is
136
/// attached to that entity, that [`AudioSource`][crate::AudioSource] will start playing. If
137
/// that source is unchanged, that translates to the audio restarting.
138
#[derive(Component)]
139
pub struct AudioSink {
140
pub(crate) sink: Sink,
141
142
/// Managed volume allows the sink to be muted without losing the user's
143
/// intended volume setting.
144
///
145
/// This is used to restore the volume when [`unmute`](Self::unmute) is
146
/// called.
147
///
148
/// If the sink is not muted, this is `None`.
149
///
150
/// If the sink is muted, this is `Some(volume)` where `volume` is the
151
/// user's intended volume setting, even if the underlying sink's volume is
152
/// 0.
153
pub(crate) managed_volume: Option<Volume>,
154
}
155
156
impl AudioSink {
157
/// Create a new audio sink.
158
pub fn new(sink: Sink) -> Self {
159
Self {
160
sink,
161
managed_volume: None,
162
}
163
}
164
}
165
166
impl AudioSinkPlayback for AudioSink {
167
fn volume(&self) -> Volume {
168
self.managed_volume
169
.unwrap_or_else(|| Volume::Linear(self.sink.volume()))
170
}
171
172
fn set_volume(&mut self, volume: Volume) {
173
if self.is_muted() {
174
self.managed_volume = Some(volume);
175
} else {
176
self.sink.set_volume(volume.to_linear());
177
}
178
}
179
180
fn speed(&self) -> f32 {
181
self.sink.speed()
182
}
183
184
fn set_speed(&self, speed: f32) {
185
self.sink.set_speed(speed);
186
}
187
188
fn play(&self) {
189
self.sink.play();
190
}
191
192
fn position(&self) -> Duration {
193
self.sink.get_pos()
194
}
195
196
fn try_seek(&self, pos: Duration) -> Result<(), SeekError> {
197
self.sink.try_seek(pos)
198
}
199
200
fn pause(&self) {
201
self.sink.pause();
202
}
203
204
fn is_paused(&self) -> bool {
205
self.sink.is_paused()
206
}
207
208
fn stop(&self) {
209
self.sink.stop();
210
}
211
212
fn empty(&self) -> bool {
213
self.sink.empty()
214
}
215
216
fn is_muted(&self) -> bool {
217
self.managed_volume.is_some()
218
}
219
220
fn mute(&mut self) {
221
self.managed_volume = Some(self.volume());
222
self.sink.set_volume(0.0);
223
}
224
225
fn unmute(&mut self) {
226
if let Some(volume) = self.managed_volume.take() {
227
self.sink.set_volume(volume.to_linear());
228
}
229
}
230
}
231
232
/// Used to control spatial audio during playback.
233
///
234
/// Bevy inserts this component onto your entities when it begins playing an audio source
235
/// that's configured to use spatial audio.
236
///
237
/// You can use this component to modify the playback settings while the audio is playing.
238
///
239
/// If this component is removed from an entity, and a [`AudioSource`][crate::AudioSource] is
240
/// attached to that entity, that [`AudioSource`][crate::AudioSource] will start playing. If
241
/// that source is unchanged, that translates to the audio restarting.
242
#[derive(Component)]
243
pub struct SpatialAudioSink {
244
pub(crate) sink: SpatialSink,
245
246
/// Managed volume allows the sink to be muted without losing the user's
247
/// intended volume setting.
248
///
249
/// This is used to restore the volume when [`unmute`](Self::unmute) is
250
/// called.
251
///
252
/// If the sink is not muted, this is `None`.
253
///
254
/// If the sink is muted, this is `Some(volume)` where `volume` is the
255
/// user's intended volume setting, even if the underlying sink's volume is
256
/// 0.
257
pub(crate) managed_volume: Option<Volume>,
258
}
259
260
impl SpatialAudioSink {
261
/// Create a new spatial audio sink.
262
pub fn new(sink: SpatialSink) -> Self {
263
Self {
264
sink,
265
managed_volume: None,
266
}
267
}
268
}
269
270
impl AudioSinkPlayback for SpatialAudioSink {
271
fn volume(&self) -> Volume {
272
self.managed_volume
273
.unwrap_or_else(|| Volume::Linear(self.sink.volume()))
274
}
275
276
fn set_volume(&mut self, volume: Volume) {
277
if self.is_muted() {
278
self.managed_volume = Some(volume);
279
} else {
280
self.sink.set_volume(volume.to_linear());
281
}
282
}
283
284
fn speed(&self) -> f32 {
285
self.sink.speed()
286
}
287
288
fn set_speed(&self, speed: f32) {
289
self.sink.set_speed(speed);
290
}
291
292
fn play(&self) {
293
self.sink.play();
294
}
295
296
fn position(&self) -> Duration {
297
self.sink.get_pos()
298
}
299
300
fn try_seek(&self, pos: Duration) -> Result<(), SeekError> {
301
self.sink.try_seek(pos)
302
}
303
304
fn pause(&self) {
305
self.sink.pause();
306
}
307
308
fn is_paused(&self) -> bool {
309
self.sink.is_paused()
310
}
311
312
fn stop(&self) {
313
self.sink.stop();
314
}
315
316
fn empty(&self) -> bool {
317
self.sink.empty()
318
}
319
320
fn is_muted(&self) -> bool {
321
self.managed_volume.is_some()
322
}
323
324
fn mute(&mut self) {
325
self.managed_volume = Some(self.volume());
326
self.sink.set_volume(0.0);
327
}
328
329
fn unmute(&mut self) {
330
if let Some(volume) = self.managed_volume.take() {
331
self.sink.set_volume(volume.to_linear());
332
}
333
}
334
}
335
336
impl SpatialAudioSink {
337
/// Set the two ears position.
338
pub fn set_ears_position(&self, left_position: Vec3, right_position: Vec3) {
339
self.sink.set_left_ear_position(left_position.to_array());
340
self.sink.set_right_ear_position(right_position.to_array());
341
}
342
343
/// Set the listener position, with an ear on each side separated by `gap`.
344
pub fn set_listener_position(&self, position: Transform, gap: f32) {
345
self.set_ears_position(
346
position.translation + position.left() * gap / 2.0,
347
position.translation + position.right() * gap / 2.0,
348
);
349
}
350
351
/// Set the emitter position.
352
pub fn set_emitter_position(&self, position: Vec3) {
353
self.sink.set_emitter_position(position.to_array());
354
}
355
}
356
357
#[cfg(test)]
358
mod tests {
359
use rodio::Sink;
360
361
use super::*;
362
363
fn test_audio_sink_playback<T: AudioSinkPlayback>(mut audio_sink: T) {
364
// Test volume
365
assert_eq!(audio_sink.volume(), Volume::Linear(1.0)); // default volume
366
audio_sink.set_volume(Volume::Linear(0.5));
367
assert_eq!(audio_sink.volume(), Volume::Linear(0.5));
368
audio_sink.set_volume(Volume::Linear(1.0));
369
assert_eq!(audio_sink.volume(), Volume::Linear(1.0));
370
371
// Test speed
372
assert_eq!(audio_sink.speed(), 1.0); // default speed
373
audio_sink.set_speed(0.5);
374
assert_eq!(audio_sink.speed(), 0.5);
375
audio_sink.set_speed(1.0);
376
assert_eq!(audio_sink.speed(), 1.0);
377
378
// Test playback
379
assert!(!audio_sink.is_paused()); // default pause state
380
audio_sink.pause();
381
assert!(audio_sink.is_paused());
382
audio_sink.play();
383
assert!(!audio_sink.is_paused());
384
385
// Test toggle playback
386
audio_sink.pause(); // start paused
387
audio_sink.toggle_playback();
388
assert!(!audio_sink.is_paused());
389
audio_sink.toggle_playback();
390
assert!(audio_sink.is_paused());
391
392
// Test mute
393
assert!(!audio_sink.is_muted()); // default mute state
394
audio_sink.mute();
395
assert!(audio_sink.is_muted());
396
audio_sink.unmute();
397
assert!(!audio_sink.is_muted());
398
399
// Test volume with mute
400
audio_sink.set_volume(Volume::Linear(0.5));
401
audio_sink.mute();
402
assert_eq!(audio_sink.volume(), Volume::Linear(0.5)); // returns managed volume even though sink volume is 0
403
audio_sink.unmute();
404
assert_eq!(audio_sink.volume(), Volume::Linear(0.5)); // managed volume is restored
405
406
// Test toggle mute
407
audio_sink.toggle_mute();
408
assert!(audio_sink.is_muted());
409
audio_sink.toggle_mute();
410
assert!(!audio_sink.is_muted());
411
}
412
413
#[test]
414
fn test_audio_sink() {
415
let (sink, _queue_rx) = Sink::new_idle();
416
let audio_sink = AudioSink::new(sink);
417
test_audio_sink_playback(audio_sink);
418
}
419
}
420
421