Move logarithmic volume transformation to AudioMixer

Previously, the volume transformation to adapt the volume for
logarithmic hearing has been performed in the VolumeControls Rust struct
and TypeScript class each.
Since this calculation is the same on desktop and web and should be
implemented in the audio backend, it has been moved into the
AudioMixer::mix_audio method.
The VolumeControls struct and class now only calculate the linear volume
out of the checkbox and the slider.
Player::set_volume and Player::volume now don't take and return the
adapted volume, but use the linear volume (which gets saved internally).
This commit is contained in:
Kornelius Rohrschneider 2023-08-20 13:33:34 +02:00 committed by Adrian Wielgosik
parent f04470247e
commit 53ba75d587
4 changed files with 27 additions and 31 deletions

View File

@ -446,7 +446,8 @@ impl AudioMixer {
};
use std::ops::DerefMut;
let volume = volume.to_sample();
// Adapt the volume for logarithmic hearing.
let volume = ((10_f32.powf(81_f32.log10() * volume) - 1.0) / 80.0).to_sample();
// For each sample, mix the samples from all active sound instances.
for buf_frame in output_buffer

View File

@ -596,11 +596,15 @@ impl Player {
}
/// Returns the master volume of the player. 1.0 is 100% volume.
///
/// The volume is linear and not adapted for logarithmic hearing.
pub fn volume(&self) -> f32 {
self.audio.volume()
}
/// Sets the master volume of the player. 1.0 is 100% volume.
///
/// The volume should be linear and not adapted for logarithmic hearing.
pub fn set_volume(&mut self, volume: f32) {
self.audio.set_volume(volume)
}

View File

@ -190,7 +190,7 @@ impl RuffleGui {
self.locale.clone(),
);
player.set_volume(self.volume_controls.get_real_volume());
player.set_volume(self.volume_controls.get_volume());
}
/// Renders the main menu bar at the top of the window.
@ -440,7 +440,7 @@ impl RuffleGui {
if changed_checkbox || changed_slider {
if let Some(player) = player {
player.set_volume(self.volume_controls.get_real_volume());
player.set_volume(self.volume_controls.get_volume());
}
}
});
@ -555,15 +555,11 @@ impl VolumeControls {
Self { is_muted, volume }
}
/// Calculates the real volume (between 0 and 1) out of the entered volume
/// (between 0 and 100) and the mute checkbox.
///
/// This is also necessary because human hearing is logarithmic:
/// What sounds like half as loud (and should be 50% on the scale) is actually
/// about 1/10th of the real volume.
pub fn get_real_volume(&self) -> f32 {
/// Returns the volume between 0 and 1 (calculated out of the
/// checkbox and the slider).
fn get_volume(&self) -> f32 {
if !self.is_muted {
(10_f32.powf(81_f32.log10() * self.volume / 100.0) - 1.0) / 80.0
self.volume / 100.0
} else {
0.0
}

View File

@ -268,7 +268,7 @@ export class RufflePlayer extends HTMLElement {
this.addModalJavaScript(this.volumeControls);
this.addModalJavaScript(this.videoModal);
this.volumeSettings = new VolumeControls(false, 1.0);
this.volumeSettings = new VolumeControls(false, 100);
this.addVolumeControlsJavaScript(this.volumeControls);
const backupSaves = <HTMLElement>(
@ -413,12 +413,12 @@ export class RufflePlayer extends HTMLElement {
? "grey"
: "black";
this.volumeSettings.isMuted = muteCheckbox.checked;
this.instance?.set_volume(this.volumeSettings.get_real_volume());
this.instance?.set_volume(this.volumeSettings.get_volume());
});
volumeSlider.addEventListener("input", () => {
volumeSliderText.textContent = volumeSlider.value;
this.volumeSettings.volume = volumeSlider.valueAsNumber;
this.instance?.set_volume(this.volumeSettings.get_real_volume());
this.instance?.set_volume(this.volumeSettings.get_volume());
});
}
@ -649,7 +649,8 @@ export class RufflePlayer extends HTMLElement {
this,
this.loadedConfig,
);
this.instance!.set_volume(this.volumeSettings.get_real_volume());
this.instance!.set_volume(this.volumeSettings.get_volume());
this.rendererDebugInfo = this.instance!.renderer_debug_info();
const actuallyUsedRendererName = this.instance!.renderer_name();
@ -910,6 +911,8 @@ export class RufflePlayer extends HTMLElement {
/**
* Returns the master volume of the player.
*
* The volume is linear and not adapted for logarithmic hearing.
*
* @returns The volume. 1.0 is 100% volume.
*/
get volume(): number {
@ -922,6 +925,8 @@ export class RufflePlayer extends HTMLElement {
/**
* Sets the master volume of the player.
*
* The volume should be linear and not adapted for logarithmic hearing.
*
* @param value The volume. 1.0 is 100% volume.
*/
set volume(value: number) {
@ -2371,26 +2376,16 @@ class VolumeControls {
constructor(isMuted: boolean, volume: number) {
this.isMuted = isMuted;
this.volume = volume * 100;
this.volume = volume;
}
/**
* Calculates the real volume (between 0 and 1) out of the entered volume
* (between 0 and 100) and the mute checkbox.
* Returns the volume between 0 and 1 (calculated out of the
* checkbox and the slider).
*
* This is also necessary because human hearing is logarithmic:
* What sounds like half as loud (and should be 50% on the scale) is actually
* about 1/10th of the real volume.
*
* @returns The real volume.
* @returns The volume between 0 and 1.
*/
public get_real_volume(): number {
if (!this.isMuted) {
return (
(Math.pow(10, (Math.log10(81) * this.volume) / 100) - 1) / 80
);
} else {
return 0;
}
public get_volume(): number {
return !this.isMuted ? this.volume / 100 : 0;
}
}