1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 03:22:55 +08:00

Set fQ to recommended value from BASS developer to prevent filter calculations from overflowing when approaching nyquist

This commit is contained in:
Jamie Taylor 2021-10-06 11:27:13 +09:00
parent 2a4a376b87
commit df182ba92b
No known key found for this signature in database
GPG Key ID: 2ACFA8B6370B8C8C

View File

@ -10,7 +10,7 @@ namespace osu.Game.Audio.Effects
{ {
public class Filter : Component, ITransformableFilter public class Filter : Component, ITransformableFilter
{ {
public readonly int MaxCutoff; public readonly int MaxCutoff = 22049; // nyquist - 1hz
private readonly AudioMixer mixer; private readonly AudioMixer mixer;
private readonly BQFParameters filter; private readonly BQFParameters filter;
private readonly BQFType type; private readonly BQFType type;
@ -18,36 +18,29 @@ namespace osu.Game.Audio.Effects
public BindableNumber<int> Cutoff { get; } public BindableNumber<int> Cutoff { get; }
/// <summary> /// <summary>
/// A BiQuad filter that performs a filter-sweep when toggled on or off. /// A Component that implements a BASS FX BiQuad Filter Effect.
/// </summary> /// </summary>
/// <param name="mixer">The mixer this effect should be attached to.</param> /// <param name="mixer">The mixer this effect should be applied to.</param>
/// <param name="type">The type of filter (e.g. LowPass, HighPass, etc)</param> /// <param name="type">The type of filter (e.g. LowPass, HighPass, etc)</param>
public Filter(AudioMixer mixer, BQFType type = BQFType.LowPass) public Filter(AudioMixer mixer, BQFType type = BQFType.LowPass)
{ {
this.mixer = mixer; this.mixer = mixer;
this.type = type; this.type = type;
var initialCutoff = 1; int initialCutoff;
// These max cutoff values are a work-around for BASS' BiQuad filters behaving weirdly when approaching nyquist.
// Note that these values assume a sample rate of 44100 (as per BassAudioMixer in osu.Framework)
// See also https://www.un4seen.com/forum/?topic=19542.0 for more information.
switch (type) switch (type)
{ {
case BQFType.HighPass: case BQFType.HighPass:
MaxCutoff = 21968; // beyond this value, the high-pass cuts out initialCutoff = 1;
break; break;
case BQFType.LowPass: case BQFType.LowPass:
MaxCutoff = initialCutoff = 14000; // beyond (roughly) this value, the low-pass filter audibly wraps/reflects initialCutoff = MaxCutoff;
break;
case BQFType.BandPass:
MaxCutoff = 16000; // beyond (roughly) this value, the band-pass filter audibly wraps/reflects
break; break;
default: default:
MaxCutoff = 22050; // default to nyquist for other filter types, TODO: handle quirks of other filter types initialCutoff = 500; // A default that should ensure audio remains audible for other filters.
break; break;
} }
@ -59,11 +52,12 @@ namespace osu.Game.Audio.Effects
filter = new BQFParameters filter = new BQFParameters
{ {
lFilter = type, lFilter = type,
fCenter = initialCutoff fCenter = initialCutoff,
fBandwidth = 0,
fQ = 0.7f // This allows fCenter to go up to 22049hz (nyquist - 1hz) without overflowing and causing weird filter behaviour (see: https://www.un4seen.com/forum/?topic=19542.0)
}; };
attachFilter(); attachFilter();
Cutoff.ValueChanged += updateFilter; Cutoff.ValueChanged += updateFilter;
Cutoff.Value = initialCutoff; Cutoff.Value = initialCutoff;
} }
@ -74,9 +68,7 @@ namespace osu.Game.Audio.Effects
private void updateFilter(ValueChangedEvent<int> cutoff) private void updateFilter(ValueChangedEvent<int> cutoff)
{ {
// This is another workaround for quirks in BASS' BiQuad filters. // Workaround for weird behaviour when rapidly setting fCenter of a low-pass filter to nyquist - 1hz.
// Because the cutoff can't be set above ~14khz (i.e. outside of human hearing range) without the aforementioned wrapping/reflecting quirk occuring, we instead
// remove the effect from the mixer when the cutoff is at maximum so that a LowPass filter isn't always attenuating high frequencies just by existing.
if (type == BQFType.LowPass) if (type == BQFType.LowPass)
{ {
if (cutoff.NewValue >= MaxCutoff) if (cutoff.NewValue >= MaxCutoff)
@ -89,6 +81,19 @@ namespace osu.Game.Audio.Effects
attachFilter(); attachFilter();
} }
// Workaround for weird behaviour when rapidly setting fCenter of a high-pass filter to 1hz.
if (type == BQFType.HighPass)
{
if (cutoff.NewValue <= 1)
{
detachFilter();
return;
}
if (cutoff.OldValue <= 1 && cutoff.NewValue > 1)
attachFilter();
}
var filterIndex = mixer.Effects.IndexOf(filter); var filterIndex = mixer.Effects.IndexOf(filter);
if (filterIndex < 0) return; if (filterIndex < 0) return;