Rewrite AudioFilter to be easier to follow (and fix tests)

This commit is contained in:
Dean Herbert
2021-10-13 15:13:35 +09:00
parent ae4dcbd829
commit 29dfe33465
2 changed files with 119 additions and 87 deletions

View File

@ -20,6 +20,8 @@ namespace osu.Game.Audio.Effects
private readonly BQFParameters filter;
private readonly BQFType type;
private bool isAttached;
private int cutoff;
/// <summary>
@ -33,10 +35,8 @@ namespace osu.Game.Audio.Effects
if (value == cutoff)
return;
int oldValue = cutoff;
cutoff = value;
updateFilter(oldValue, cutoff);
updateFilter(cutoff);
}
}
@ -50,73 +50,58 @@ namespace osu.Game.Audio.Effects
this.mixer = mixer;
this.type = type;
switch (type)
{
case BQFType.HighPass:
cutoff = 1;
break;
case BQFType.LowPass:
cutoff = MAX_LOWPASS_CUTOFF;
break;
default:
cutoff = 500; // A default that should ensure audio remains audible for other filters.
break;
}
filter = new BQFParameters
{
lFilter = type,
fCenter = cutoff,
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)
// 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)
fQ = 0.7f
};
// Don't start attached if this is low-pass or high-pass filter (as they have special auto-attach/detach logic)
if (type != BQFType.LowPass && type != BQFType.HighPass)
attachFilter();
Cutoff = getInitialCutoff(type);
}
private void attachFilter()
private int getInitialCutoff(BQFType type)
{
Debug.Assert(!mixer.Effects.Contains(filter));
mixer.Effects.Add(filter);
}
private void detachFilter()
{
Debug.Assert(mixer.Effects.Contains(filter));
mixer.Effects.Remove(filter);
}
private void updateFilter(int oldValue, int newValue)
{
// Workaround for weird behaviour when rapidly setting fCenter of a low-pass filter to nyquist - 1hz.
if (type == BQFType.LowPass)
switch (type)
{
if (newValue >= MAX_LOWPASS_CUTOFF)
{
detachFilter();
return;
}
case BQFType.HighPass:
return 1;
if (oldValue >= MAX_LOWPASS_CUTOFF)
attachFilter();
case BQFType.LowPass:
return MAX_LOWPASS_CUTOFF;
default:
return 500; // A default that should ensure audio remains audible for other filters.
}
}
private void updateFilter(int newValue)
{
switch (type)
{
case BQFType.LowPass:
// Workaround for weird behaviour when rapidly setting fCenter of a low-pass filter to nyquist - 1hz.
if (newValue >= MAX_LOWPASS_CUTOFF)
{
ensureDetached();
return;
}
break;
// Workaround for weird behaviour when rapidly setting fCenter of a high-pass filter to 1hz.
case BQFType.HighPass:
if (newValue <= 1)
{
ensureDetached();
return;
}
break;
}
// Workaround for weird behaviour when rapidly setting fCenter of a high-pass filter to 1hz.
if (type == BQFType.HighPass)
{
if (newValue <= 1)
{
detachFilter();
return;
}
if (oldValue <= 1)
attachFilter();
}
ensureAttached();
var filterIndex = mixer.Effects.IndexOf(filter);
@ -131,12 +116,30 @@ namespace osu.Game.Audio.Effects
}
}
private void ensureAttached()
{
if (isAttached)
return;
Debug.Assert(!mixer.Effects.Contains(filter));
mixer.Effects.Add(filter);
isAttached = true;
}
private void ensureDetached()
{
if (!isAttached)
return;
Debug.Assert(mixer.Effects.Contains(filter));
mixer.Effects.Remove(filter);
isAttached = false;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (mixer.Effects.Contains(filter))
detachFilter();
ensureDetached();
}
}
}