Skip to content

Use decibel scaling for volume control #6490

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

Closed
wants to merge 10 commits into from
23 changes: 11 additions & 12 deletions osu.Framework/Audio/AudioManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,23 +90,20 @@ public class AudioManager : AudioCollectionManager<AudioComponent>
/// </summary>
public readonly Bindable<string> AudioDevice = new Bindable<string>();

/// <summary>
/// Volume of all audio game-wide.
/// </summary>
public new readonly BindableVolume Volume;

/// <summary>
/// Volume of all samples played game-wide.
/// </summary>
public readonly BindableDouble VolumeSample = new BindableDouble(1)
{
MinValue = 0,
MaxValue = 1
};
public readonly BindableVolume VolumeSample = new BindableVolume();

/// <summary>
/// Volume of all tracks played game-wide.
/// </summary>
public readonly BindableDouble VolumeTrack = new BindableDouble(1)
{
MinValue = 0,
MaxValue = 1
};
public readonly BindableVolume VolumeTrack = new BindableVolume();

/// <summary>
/// Whether a global mixer is being used for audio routing.
Expand Down Expand Up @@ -179,19 +176,21 @@ public AudioManager(AudioThread audioThread, ResourceStore<byte[]> trackStore, R
AddItem(TrackMixer = createAudioMixer(null, nameof(TrackMixer)));
AddItem(SampleMixer = createAudioMixer(null, nameof(SampleMixer)));

Volume = new BindableVolume(base.Volume);

globalTrackStore = new Lazy<TrackStore>(() =>
{
var store = new TrackStore(trackStore, TrackMixer);
AddItem(store);
store.AddAdjustment(AdjustableProperty.Volume, VolumeTrack);
store.AddAdjustment(AdjustableProperty.Volume, VolumeTrack.Linear);
return store;
});

globalSampleStore = new Lazy<SampleStore>(() =>
{
var store = new SampleStore(sampleStore, SampleMixer);
AddItem(store);
store.AddAdjustment(AdjustableProperty.Volume, VolumeSample);
store.AddAdjustment(AdjustableProperty.Volume, VolumeSample.Linear);
return store;
});

Expand Down
45 changes: 45 additions & 0 deletions osu.Framework/Audio/BindableVolume.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using osu.Framework.Bindables;

namespace osu.Framework.Audio
{
public sealed class BindableVolume
{
public const double MIN = -60;
public const double STEP = 0.5;

private static readonly double k = Math.Log(10) / 20;

public readonly BindableNumber<double> Linear;
public readonly BindableNumber<double> Decibel = new BindableNumber<double>(1)
{
MinValue = MIN,
MaxValue = 0,
Precision = STEP,
};

private double decibelToLinear(double x) => x <= MIN ? 0 : Math.Exp(k * x);

private double linearToDecibel(double x) => x <= 0 ? MIN : Math.Log(x) / k;

public BindableVolume(BindableNumber<double>? linear = null)
{
Linear = linear ?? new BindableNumber<double>(1) { MinValue = 0, MaxValue = 1 };
Decibel.BindValueChanged(x => Linear.Value = decibelToLinear(x.NewValue));
}

public void SetFromLinear(double linear)
{
Decibel.Value = linearToDecibel(linear);
}

public void Scale()
{
Decibel.Value = linearToDecibel(Linear.Value);
Decibel.Default = linearToDecibel(Linear.Default);
}
}
}
6 changes: 3 additions & 3 deletions osu.Framework/Configuration/FrameworkConfigManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ protected override void InitialiseDefaults()
SetDefault(FrameworkSetting.WindowedPositionY, 0.5, -0.5, 1.5);
SetDefault(FrameworkSetting.LastDisplayDevice, DisplayIndex.Default);
SetDefault(FrameworkSetting.AudioDevice, string.Empty);
SetDefault(FrameworkSetting.VolumeUniversal, 1.0, 0.0, 1.0, 0.01);
SetDefault(FrameworkSetting.VolumeMusic, 1.0, 0.0, 1.0, 0.01);
SetDefault(FrameworkSetting.VolumeEffect, 1.0, 0.0, 1.0, 0.01);
SetDefault(FrameworkSetting.VolumeUniversal, 1.0, 0.0, 1.0);
SetDefault(FrameworkSetting.VolumeMusic, 1.0, 0.0, 1.0);
SetDefault(FrameworkSetting.VolumeEffect, 1.0, 0.0, 1.0);
SetDefault(FrameworkSetting.HardwareVideoDecoder, HardwareVideoDecoder.Any);
SetDefault(FrameworkSetting.SizeFullscreen, new Size(9999, 9999), new Size(320, 240));
SetDefault(FrameworkSetting.MinimiseOnFocusLossInFullscreen, RuntimeInfo.IsDesktop);
Expand Down
10 changes: 7 additions & 3 deletions osu.Framework/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,13 @@ private void load(FrameworkConfigManager config)

// attach our bindables to the audio subsystem.
config.BindWith(FrameworkSetting.AudioDevice, Audio.AudioDevice);
config.BindWith(FrameworkSetting.VolumeUniversal, Audio.Volume);
config.BindWith(FrameworkSetting.VolumeEffect, Audio.VolumeSample);
config.BindWith(FrameworkSetting.VolumeMusic, Audio.VolumeTrack);
config.BindWith(FrameworkSetting.VolumeUniversal, Audio.Volume.Linear);
config.BindWith(FrameworkSetting.VolumeEffect, Audio.VolumeSample.Linear);
config.BindWith(FrameworkSetting.VolumeMusic, Audio.VolumeTrack.Linear);

Audio.Volume.Scale();
Audio.VolumeSample.Scale();
Audio.VolumeTrack.Scale();
Comment on lines +180 to +182
Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't be required. It will happen on BindWith.


Shaders = new ShaderManager(Host.Renderer, new NamespacedResourceStore<byte[]>(Resources, @"Shaders"));
dependencies.Cache(Shaders);
Expand Down