namespace TravelEar.Core;
///
/// The main mixer's Master group limiter (docs/reference/big-walk-voice-effects-catalog.md
/// section 5.15): a Duck Volume the group sends into itself, threshold
/// MasterLimiterThreshold (asset default -3 dB, never written by the game), ratio 10,
/// attack 0, release 0.125 s, make-up 0, knee 20 dB. Last thing a listener's mix goes through
/// before the speakers, so it caps the sum of the dry copies and the reverb on loud speech.
/// Approximation: the shared detector with FMOD's attack floor.
///
public sealed class MasterLimiter
{
public const float DefaultThresholdDb = -3f;
public const float Ratio = 10f;
public const float AttackMs = 0.1f;
public const float ReleaseMs = 125f;
public const float KneeDb = 20f;
private readonly Compressor _compressor;
public float ThresholdDb => _compressor.ThresholdDb;
public float ReductionDb => _compressor.ReductionDb;
public MasterLimiter(int sampleRate)
{
_compressor = new Compressor(sampleRate, DefaultThresholdDb, AttackMs, ReleaseMs, 0f, Ratio, KneeDb);
}
public void Reset() => _compressor.Reset();
// [impl->REQ-MIXER-RESYNTH]
/// Limits in place at (the live mixer float, or the asset default).
public void Process(Span mono, float thresholdDb = DefaultThresholdDb)
{
if (float.IsNaN(thresholdDb)) thresholdDb = DefaultThresholdDb;
if (MathF.Abs(thresholdDb - _compressor.ThresholdDb) > 0.01f)
_compressor.Set(thresholdDb, AttackMs, ReleaseMs, 0f, Ratio, KneeDb);
_compressor.Process(mono);
}
}