using System;
using TravelEar.Core;
using Xunit;
namespace TravelEar.Tests;
///
/// Two real parameter sets from M3 run 4's Environment reverb: log line (2026-09-09): the
/// starting-area hallway (Decay 1.09 s, Reverb -621 mB) and a big indoor room (Decay 0.24 s,
/// Reverb -2238 mB). The operator heard them as nearly the same because the gate cut every tail
/// at ~360 ms; the network itself must keep them far apart, or the tail fix changes nothing.
///
public class SfxReverbSceneTests
{
private const int Rate = 48_000;
private const int Block = 480;
private static readonly EnvironmentReverbParams Hallway = new(
DryLevelMb: -256, RoomMb: -262, RoomHfMb: -1000, RoomLfMb: -1257,
DecayTimeS: 1.09f, DecayHfRatio: 0.30f, ReflectionsMb: -3490, ReflectDelayS: 0.064f,
ReverbMb: -621, ReverbDelayS: 0.043f, HfReferenceHz: 4864, LfReferenceHz: 320,
DiffusionPct: 100, DensityPct: 71, MasterWetDb: 0, VoiceBusDb: 0);
private static readonly EnvironmentReverbParams BigRoom = new(
DryLevelMb: -254, RoomMb: -491, RoomHfMb: -1000, RoomLfMb: -904,
DecayTimeS: 0.24f, DecayHfRatio: 0.30f, ReflectionsMb: -4013, ReflectDelayS: 0.062f,
ReverbMb: -2238, ReverbDelayS: 0.042f, HfReferenceHz: 3709, LfReferenceHz: 494,
DiffusionPct: 100, DensityPct: 71, MasterWetDb: 0, VoiceBusDb: 0);
/// Wet energy of an impulse response in [from, to) seconds.
private static double Energy(EnvironmentReverbParams p, double from, double to)
{
var reverb = new SfxReverb(Rate);
reverb.Configure(p);
var length = (int)(to * Rate);
var input = new float[length];
input[0] = 1f;
var wet = new float[length];
for (var offset = 0; offset < length; offset += Block)
{
var n = Math.Min(Block, length - offset);
reverb.Process(input.AsSpan(offset, n), wet.AsSpan(offset, n));
}
var energy = 0.0;
for (var i = (int)(from * Rate); i < length; i++) energy += (double)wet[i] * wet[i];
return energy;
}
// [unit->REQ-MIXER-RESYNTH]
[Fact]
public void Hallway_tail_after_the_gate_would_have_closed_is_far_above_the_big_room()
{
// 0.4 s on: past the gate's hold + fade (210 + 150 ms) where run 4 cut both tails.
var hallway = Energy(Hallway, 0.4, 2.0);
var bigRoom = Energy(BigRoom, 0.4, 2.0);
var margin = 10 * Math.Log10(hallway / bigRoom);
Assert.True(margin >= 10, $"hallway tail is only {margin:F1} dB above the big room's after 0.4 s ({hallway:E2} vs {bigRoom:E2})");
}
// [unit->REQ-MIXER-RESYNTH]
[Fact]
public void Hallway_tail_is_still_ringing_a_second_in()
{
var first = Energy(Hallway, 0.1, 0.4);
var later = Energy(Hallway, 0.7, 1.0);
var decay = 10 * Math.Log10(first / later);
// RT60 1.09 s: about -33 dB per 0.6 s on average; anything under 45 dB means a tail is there.
Assert.True(decay < 45, $"hallway tail decayed {decay:F1} dB between 0.1-0.4 s and 0.7-1.0 s; expected a live tail");
Assert.True(later > 0, "no energy at all a second in");
}
}