Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support decibels in bevy_audio::Volume #17605

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/bevy_audio/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl PlaybackSettings {
/// added again.
pub const ONCE: PlaybackSettings = PlaybackSettings {
mode: PlaybackMode::Once,
volume: Volume(1.0),
volume: Volume::from_linear(1.0),
speed: 1.0,
paused: false,
muted: false,
Expand Down
77 changes: 34 additions & 43 deletions crates/bevy_audio/src/sinks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,26 @@ use bevy_math::Vec3;
use bevy_transform::prelude::Transform;
use rodio::{Sink, SpatialSink};

use crate::Volume;

/// Common interactions with an audio sink.
pub trait AudioSinkPlayback {
/// Gets the volume of the sound.
///
/// The value `1.0` is the "normal" volume (unfiltered input). Any value
/// other than `1.0` will multiply each sample by this value.
/// Gets the volume of the sound as a [`Volume`].
///
/// If the sink is muted, this returns the managed volume rather than the
/// sink's actual volume. This allows you to use the volume as if the sink
/// were not muted, because a muted sink has a volume of 0.
fn volume(&self) -> f32;
/// sink's actual volume. This allows you to use the returned volume as if
/// the sink were not muted, because a muted sink has a physical volume of
/// 0.
fn volume(&self) -> Volume;

/// Changes the volume of the sound.
///
/// The value `1.0` is the "normal" volume (unfiltered input). Any value other than `1.0`
/// will multiply each sample by this value.
/// Changes the volume of the sound to the given [`Volume`].
///
/// If the sink is muted, changing the volume won't unmute it, i.e. the
/// sink's volume will remain at `0.0`. However, the sink will remember the
/// volume change and it will be used when [`unmute`](Self::unmute) is
/// called. This allows you to control the volume even when the sink is
/// muted.
///
/// # Note on Audio Volume
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this part of the docs because the function now receives a Volume not an f32 and I feel like users can go to the Volume docs to read about decibels, but on top of that I feel like they can just read about decibels externally if they want details on the math.

///
/// An increase of 10 decibels (dB) roughly corresponds to the perceived volume doubling in intensity.
/// As this function scales not the volume but the amplitude, a conversion might be necessary.
/// For example, to halve the perceived volume you need to decrease the volume by 10 dB.
/// This corresponds to 20log(x) = -10dB, solving x = 10^(-10/20) = 0.316.
/// Multiply the current volume by 0.316 to halve the perceived volume.
fn set_volume(&mut self, volume: f32);
/// sink's volume will remain "off" / "muted". However, the sink will
/// remember the volume change and it will be used when
/// [`unmute`](Self::unmute) is called. This allows you to control the
/// volume even when the sink is muted.
fn set_volume(&mut self, volume: impl Into<Volume>);

/// Gets the speed of the sound.
///
Expand Down Expand Up @@ -132,7 +121,7 @@ pub struct AudioSink {
/// If the sink is muted, this is `Some(volume)` where `volume` is the
/// user's intended volume setting, even if the underlying sink's volume is
/// 0.
pub(crate) managed_volume: Option<f32>,
pub(crate) managed_volume: Option<Volume>,
}

impl AudioSink {
Expand All @@ -146,15 +135,16 @@ impl AudioSink {
}

impl AudioSinkPlayback for AudioSink {
fn volume(&self) -> f32 {
self.managed_volume.unwrap_or_else(|| self.sink.volume())
fn volume(&self) -> Volume {
self.managed_volume
.unwrap_or_else(|| self.sink.volume().into())
}

fn set_volume(&mut self, volume: f32) {
fn set_volume(&mut self, volume: impl Into<Volume>) {
if self.is_muted() {
self.managed_volume = Some(volume);
self.managed_volume = Some(volume.into());
} else {
self.sink.set_volume(volume);
self.sink.set_volume(volume.into().to_linear());
}
}

Expand Down Expand Up @@ -197,7 +187,7 @@ impl AudioSinkPlayback for AudioSink {

fn unmute(&mut self) {
if let Some(volume) = self.managed_volume.take() {
self.sink.set_volume(volume);
self.sink.set_volume(volume.to_linear());
}
}
}
Expand Down Expand Up @@ -227,7 +217,7 @@ pub struct SpatialAudioSink {
/// If the sink is muted, this is `Some(volume)` where `volume` is the
/// user's intended volume setting, even if the underlying sink's volume is
/// 0.
pub(crate) managed_volume: Option<f32>,
pub(crate) managed_volume: Option<Volume>,
}

impl SpatialAudioSink {
Expand All @@ -241,15 +231,16 @@ impl SpatialAudioSink {
}

impl AudioSinkPlayback for SpatialAudioSink {
fn volume(&self) -> f32 {
self.managed_volume.unwrap_or_else(|| self.sink.volume())
fn volume(&self) -> Volume {
self.managed_volume
.unwrap_or_else(|| self.sink.volume().into())
}

fn set_volume(&mut self, volume: f32) {
fn set_volume(&mut self, volume: impl Into<Volume>) {
if self.is_muted() {
self.managed_volume = Some(volume);
self.managed_volume = Some(volume.into());
} else {
self.sink.set_volume(volume);
self.sink.set_volume(volume.into().to_linear());
}
}

Expand Down Expand Up @@ -292,7 +283,7 @@ impl AudioSinkPlayback for SpatialAudioSink {

fn unmute(&mut self) {
if let Some(volume) = self.managed_volume.take() {
self.sink.set_volume(volume);
self.sink.set_volume(volume.to_linear());
}
}
}
Expand Down Expand Up @@ -326,11 +317,11 @@ mod tests {

fn test_audio_sink_playback<T: AudioSinkPlayback>(mut audio_sink: T) {
// Test volume
assert_eq!(audio_sink.volume(), 1.0); // default volume
assert_eq!(audio_sink.volume(), 1.0.into()); // default volume
mgi388 marked this conversation as resolved.
Show resolved Hide resolved
audio_sink.set_volume(0.5);
assert_eq!(audio_sink.volume(), 0.5);
assert_eq!(audio_sink.volume(), 0.5.into());
audio_sink.set_volume(1.0);
assert_eq!(audio_sink.volume(), 1.0);
assert_eq!(audio_sink.volume(), 1.0.into());

// Test speed
assert_eq!(audio_sink.speed(), 1.0); // default speed
Expand Down Expand Up @@ -363,9 +354,9 @@ mod tests {
// Test volume with mute
audio_sink.set_volume(0.5);
audio_sink.mute();
assert_eq!(audio_sink.volume(), 0.5); // returns managed volume even though sink volume is 0
assert_eq!(audio_sink.volume(), 0.5.into()); // returns managed volume even though sink volume is 0
audio_sink.unmute();
assert_eq!(audio_sink.volume(), 0.5); // managed volume is restored
assert_eq!(audio_sink.volume(), 0.5.into()); // managed volume is restored

// Test toggle mute
audio_sink.toggle_mute();
Expand Down
Loading