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

Updated VolumeLevel to use Decibel and Amplitude Ratio Units #9582

Closed
wants to merge 7 commits into from

Conversation

bushrat011899
Copy link
Contributor

Objective

Solution

  • Breaking changes in both the AudioSinkPlayback trait and VolumeLevel type.
  • Replaced VolumeLevel with an enum representing either the amplitude ratio or decibel level. Names and types were chosen to be consistent with 3rd party crates like Kira, and the internally used types in the rest of bevy_audio

Changelog

  • Moved VolumeLevel into its own file volume_level.rs to reduce clutter in audio.rs.
  • Implemented total equality and ordering for VolumeLevel, as both variants Amplitude and Decibels are isomorphic and internally possess both properties.
  • Implemented amplitude() and decibels() methods to get the desired unit type from VolumeLevel directly without a match statement.
  • Added debug_assert! statements to ensure calculated values are within their appropriate bounds.
  • Added a unit test for conversion between Amplitude and Decibels, with a truth table obtained from Wikipedia
  • Updated internal functions to either accept the VolumeLevel type, or explicitly request an amplitude which is then wrapped in a VolumeLevel::Amplitude where appropriate.
  • Removed VolumeLevel::get and VolumeLevel::new due to ambiguity in the unit to return.

Migration Guide

  • VolumeLevel::new(volume) -> VolumeLevel::Amplitude(volume)
  • VolumeLevel::get() -> VolumeLevel::amplitude()
  • AudioSinkPlayback::volume() -> AudioSinkPlayback::volume().amplitude()
  • AudioSinkPlayback::set_volume(volume) -> AudioSinkPlayback::set_volume(VolumeLevel::Amplitude(volume))

@bushrat011899 bushrat011899 changed the title Initial Commit Updated VolumeLevel to use Decibel and Amplitude Ratio Units Aug 26, 2023
@rparrett rparrett added A-Audio Sounds playback and modification M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide labels Aug 26, 2023
@hymm hymm added this to the 0.12 milestone Aug 28, 2023
@bushrat011899
Copy link
Contributor Author

This comment got me thinking, I can actually remove the bounds checks entirely. Both variants take a single f32 value:

  • Decibels(a) is defined for all a, it's just that values larger in magnitude than 800 don't really exist in f32, so they're all clamped to f32::INFINITY in amplitude. Which is defined and sound behaviour.
  • Amplitude(a) does actually exist for all a, if you consider the phase of the wave, not just its amplitude. A negative a indicates a 180° rotation of the phase, or a phase inversion. However, since nothing in the Bevy audio library works with phasing (at least that I can find), this information is largely meaningless and can just be ignored.

So I've adjusted the access and comparison methods to reflect the true statement that the amplitude of a Amplitude(a) is equal to a.abs() for all a. I've also added some unit tests to document this behaviour explicitly.

Copy link
Contributor

@hymm hymm left a comment

Choose a reason for hiding this comment

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

Do we really need 2 variants for volume? Unity, Unreal, and Godot all just use decibels. That might just be because they don't have sum types, but the current pr feels a little awkward when you want to do math on the volume.

crates/bevy_audio/src/volume_level.rs Outdated Show resolved Hide resolved
examples/audio/audio_control.rs Show resolved Hide resolved
These are not appropriate due to the possibility of `NaN` values being passed directly into the `enum`.
@bushrat011899
Copy link
Contributor Author

Do we really need 2 variants for volume? Unity, Unreal, and Godot all just use decibels. That might just be because they don't have sum types, but the current pr feels a little awkward when you want to do math on the volume.

I think using an enum is a good way to force understanding around what the volume type represents. The Amplitude variant is substantially more useful in engine code, since waveforms are just directly scaled by that amplitude value. Whereas, Decibels is more useful for user code as that unit matches real-world understanding of sound and relative "loudness".

An alternative could be to new-type f32, and just convert into either amplitude or decibels for all operations as required, but I don't know if that's substantially more ergonomic.

@alice-i-cecile alice-i-cecile removed this from the 0.12 milestone Sep 30, 2023
/// phase inversion. If `a` and `b` have the same magnitude (`a.abs() == b.abs()`), but opposite
/// sign (`a == -b`), then `Self::Amplitude(a) == Self::Amplitude(b)`, as this phase information
/// is currently ignored. As such, you should use positive values for this variant to avoid confusion.
Amplitude(f32),
Copy link
Contributor

Choose a reason for hiding this comment

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

At the risk of some bikeshedding (and probably pedantry), "amplitude" refers to the volume itself, not a specific "unit" -- Linear would be a better term, as it is the term that is most used for the unitless gain ratio (this applies to the method name line 26 too). I can imagine some users being confused when looking at this variant by itself and not knowing whether we are talking about the linear gain or the logarithmic one (in dB), and having to look up the enum definition to clear the confusion.

@@ -22,34 +21,12 @@ impl Default for Volume {

impl Volume {
/// Create a new volume level relative to the global volume.
pub fn new_relative(volume: f32) -> Self {
Self::Relative(VolumeLevel::new(volume))
pub fn new_relative(amplitude: f32) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's been kept as-is to not introduce breaking changes, but I do think it would be better to have new_relative and new_absolute use the VolumeLevel. Or we can deprecate these ones and introduce new methods (meaning 4 methods total for the cartesian product of (relative,absolute)x(amplitude,decibel)?

github-merge-queue bot pushed a commit that referenced this pull request Dec 16, 2024
# Objective

- Prework for reviving #9582.

## Solution

- Move the two types to volume.rs and made it compile.
- Also `#[reflect(Debug)]` on `Volume` while I'm here. 

## Testing

- Ran example locally.
- Rely on CI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Audio Sounds playback and modification M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Expose both linear and logarithmic volume settings
6 participants