SHA256
forked from ADOFAIEditorForum/ADOFAIRenderer
Add Look Ahead to Audio Filter
This commit is contained in:
5 files changed
+129
-12
No files matched your search
@@ -18,5 +18,7 @@ namespace ADOFAIRenderer.Util.Audio.Filter
|
||||
}
|
||||
|
||||
public void Reset() { }
|
||||
|
||||
public double Latency => 0.0;
|
||||
}
|
||||
}
|
||||
@@ -27,5 +27,7 @@ namespace ADOFAIRenderer.Util.Audio.Filter
|
||||
}
|
||||
|
||||
public void Reset() { }
|
||||
|
||||
public double Latency => 0.0;
|
||||
}
|
||||
}
|
||||
@@ -10,5 +10,7 @@ namespace ADOFAIRenderer.Util.Audio.Filter
|
||||
void ProcessBlock(Span<float> buffer);
|
||||
|
||||
void Reset();
|
||||
|
||||
double Latency { get; }
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ namespace ADOFAIRenderer.Util.Audio.Filter
|
||||
private float _releaseTime = 0.100f; // in seconds
|
||||
private float _kneeDb = 5f; // in dB
|
||||
private float _rmsWindowTime = 0.030f; // in seconds
|
||||
private float _lookAheadTime; // in seconds
|
||||
private int _lookAheadSamples;
|
||||
|
||||
private int _sampleRate = 44100;
|
||||
private int _channels = 2;
|
||||
@@ -23,6 +25,20 @@ namespace ADOFAIRenderer.Util.Audio.Filter
|
||||
// Per-channel state
|
||||
private float[] _rmsState = Array.Empty<float>(); // running mean-square (MS) state
|
||||
private float[] _gainState = Array.Empty<float>(); // smoothed gain reduction in dB
|
||||
private float[][] _delayBuffers = Array.Empty<float[]>(); // circular delay buffers
|
||||
private int[] _delayWriteIndices = Array.Empty<int>();
|
||||
|
||||
public float LookAheadTime
|
||||
{
|
||||
get => _lookAheadTime;
|
||||
set
|
||||
{
|
||||
_lookAheadTime = Math.Max(0f, Math.Min(1.0f, value));
|
||||
UpdateLookAheadSettings();
|
||||
}
|
||||
}
|
||||
|
||||
public double Latency => _lookAheadTime;
|
||||
|
||||
public float ThresholdDb
|
||||
{
|
||||
@@ -80,6 +96,7 @@ namespace ADOFAIRenderer.Util.Audio.Filter
|
||||
UpdateAttackCoef();
|
||||
UpdateReleaseCoef();
|
||||
UpdateRmsCoef();
|
||||
UpdateLookAheadSettings();
|
||||
|
||||
_rmsState = new float[channels];
|
||||
_gainState = new float[channels];
|
||||
@@ -87,6 +104,26 @@ namespace ADOFAIRenderer.Util.Audio.Filter
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void UpdateLookAheadSettings()
|
||||
{
|
||||
_lookAheadSamples = (int)Math.Round(_sampleRate * _lookAheadTime);
|
||||
ReallocateDelayBuffers();
|
||||
}
|
||||
|
||||
private void ReallocateDelayBuffers()
|
||||
{
|
||||
if (_channels <= 0) return;
|
||||
|
||||
_delayBuffers = new float[_channels][];
|
||||
_delayWriteIndices = new int[_channels];
|
||||
|
||||
for (int i = 0; i < _channels; i++)
|
||||
{
|
||||
_delayBuffers[i] = _lookAheadSamples > 0 ? new float[_lookAheadSamples] : Array.Empty<float>();
|
||||
_delayWriteIndices[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAttackCoef()
|
||||
{
|
||||
_attackCoef = (float)Math.Exp(-1.0 / (_sampleRate * _attackTime));
|
||||
@@ -155,6 +192,16 @@ namespace ADOFAIRenderer.Util.Audio.Filter
|
||||
// Convert back to linear gain multiplier
|
||||
float linearGain = (float)Math.Pow(10.0, currentGain / 20.0);
|
||||
|
||||
if (_lookAheadSamples > 0 && _delayBuffers.Length > channel && _delayBuffers[channel].Length > 0)
|
||||
{
|
||||
var buffer = _delayBuffers[channel];
|
||||
int writeIdx = _delayWriteIndices[channel];
|
||||
float delayedSample = buffer[writeIdx];
|
||||
buffer[writeIdx] = sample;
|
||||
_delayWriteIndices[channel] = (writeIdx + 1) % _lookAheadSamples;
|
||||
return delayedSample * linearGain;
|
||||
}
|
||||
|
||||
return sample * linearGain;
|
||||
}
|
||||
|
||||
@@ -171,6 +218,12 @@ namespace ADOFAIRenderer.Util.Audio.Filter
|
||||
{
|
||||
Array.Clear(_rmsState, 0, _rmsState.Length);
|
||||
Array.Clear(_gainState, 0, _gainState.Length);
|
||||
|
||||
for (int i = 0; i < _delayBuffers.Length; i++)
|
||||
{
|
||||
Array.Clear(_delayBuffers[i], 0, _delayBuffers[i].Length);
|
||||
}
|
||||
Array.Clear(_delayWriteIndices, 0, _delayWriteIndices.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user