using VoiceCat.Dsp; using System.Text.Json; namespace VoiceCat.Tests; public sealed class DspTests { [Fact] public void SuppressesNoiseAndPreservesUnsupportedSampleRates() { using var processor = new RnnoiseProcessor(); short[] pcm = new short[960]; uint random = 0x12345678; double inputEnergy = 0, outputEnergy = 0; for (int frame = 0; frame < 200; frame++) { FillNoise(pcm, ref random); if (frame >= 60) foreach (short value in pcm) inputEnergy += (double)value * value; processor.Process(pcm); if (frame >= 60) foreach (short value in pcm) outputEnergy += (double)value * value; } Assert.True(Math.Sqrt(outputEnergy / inputEnergy) < 0.2); using var fixture = JsonDocument.Parse(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "rnnoise.json"))); short[] expected = fixture.RootElement.GetProperty("samples").EnumerateArray().Select(value => value.GetInt16()).ToArray(); Assert.Equal(pcm.Length, expected.Length); for (int i = 0; i < pcm.Length; i++) Assert.InRange(Math.Abs(pcm[i] - expected[i]), 0, 1); FillNoise(pcm, ref random); short[] original = (short[])pcm.Clone(); processor.Process(pcm, 16000); Assert.Equal(original, pcm); Assert.Throws(() => processor.Process(new short[481])); processor.Dispose(); Assert.Throws(() => processor.Process(pcm)); } [Fact] public void VadStartsClosedAndUsesMonotonicHangTime() { var clock = new ManualTimeProvider(); var processor = new EnergyVadProcessor(0.02f, TimeSpan.FromMilliseconds(300), clock); Assert.False(processor.Process(new short[480])); Assert.True(processor.Process(new short[] { 32767 })); clock.Advance(299); Assert.True(processor.Process([])); clock.Advance(1); Assert.False(processor.Process(new short[480])); processor.Threshold = 0.5f; Assert.False(processor.Process(new short[] { 1000 })); Assert.Throws(() => processor.Threshold = float.NaN); } internal static void FillNoise(Span pcm, ref uint random) { for (int i = 0; i < pcm.Length; i++) { random ^= random << 13; random ^= random >> 17; random ^= random << 5; pcm[i] = (short)((int)(random % 6001) - 3000); } } private sealed class ManualTimeProvider : TimeProvider { private long timestamp; public override long TimestampFrequency => 1000; public override long GetTimestamp() => timestamp; public void Advance(int milliseconds) => timestamp += milliseconds; } }