add dsp/ with FFT, filters, detector, stereo support; initial harness
This commit is contained in:
Executable
+108
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""bit-exact verification — прогон всех свипов, побайтовое сравнение int24 PCM."""
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import glob
|
||||
import wave
|
||||
import struct
|
||||
import sys
|
||||
|
||||
RENDERER = "/home/m/re-tools/dsp/harness"
|
||||
TEST_ROOT = "/home/m/soothe-bt"
|
||||
|
||||
def read_wav24(path):
|
||||
"""Read WAV24 file and return numpy array or list of int24 samples."""
|
||||
with wave.open(path, 'rb') as w:
|
||||
n = w.getnframes()
|
||||
data = w.readframes(n * w.getnchannels())
|
||||
sw = w.getsampwidth()
|
||||
nc = w.getnchannels()
|
||||
|
||||
if sw == 3:
|
||||
samples = []
|
||||
for i in range(0, len(data), 3):
|
||||
val = struct.unpack('<i', data[i:i+3] + b'\x00')[0]
|
||||
if val & 0x800000:
|
||||
val -= 0x1000000
|
||||
samples.append(val)
|
||||
return samples, nc
|
||||
else:
|
||||
return list(struct.unpack(f'<{len(data)//2}h', data)), nc
|
||||
|
||||
def run_harness(in_wav, out_wav):
|
||||
"""Run harness.cpp renderer."""
|
||||
cmd = [RENDERER, in_wav, out_wav]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
||||
return result.returncode == 0
|
||||
except Exception as e:
|
||||
print(f"Error running {in_wav}: {e}")
|
||||
return False
|
||||
|
||||
def compare_bytes(path1, path2):
|
||||
"""Compare two WAV files byte-by-byte. Returns (match, diff_count, max_diff)."""
|
||||
with wave.open(path1, 'rb') as w1, wave.open(path2, 'rb') as w2:
|
||||
if w1.getparams() != w2.getparams():
|
||||
return False, -1, -1
|
||||
|
||||
data1 = w1.readframes(w1.getnframes() * w1.getnchannels())
|
||||
data2 = w2.readframes(w2.getnframes() * w2.getnchannels())
|
||||
|
||||
if len(data1) != len(data2):
|
||||
return False, abs(len(data1) - len(data2)), -1
|
||||
|
||||
diff = sum(1 for i in range(len(data1)) if data1[i] != data2[i])
|
||||
return diff == 0, diff, 0
|
||||
|
||||
def test_sweep(name):
|
||||
"""Test single sweep: in.wav -> render -> out.wav, compare to ref.wav."""
|
||||
in_wav = f"{TEST_ROOT}/{name}.wav"
|
||||
ref_wav = f"{TEST_ROOT}/{name}_ref.wav"
|
||||
out_wav = f"/tmp/out_{name}.wav"
|
||||
|
||||
if not os.path.exists(in_wav):
|
||||
print(f"SKIP {name}: no input")
|
||||
return None
|
||||
|
||||
if not run_harness(in_wav, out_wav):
|
||||
print(f"FAIL {name}: renderer error")
|
||||
return False
|
||||
|
||||
match, diff, max_diff = compare_bytes(ref_wav, out_wav)
|
||||
|
||||
if match:
|
||||
print(f"PASS {name}")
|
||||
return True
|
||||
else:
|
||||
print(f"FAIL {name}: {diff} byte differences")
|
||||
return False
|
||||
|
||||
def main():
|
||||
rpps = sorted(glob.glob(f"{TEST_ROOT}/*.rpp"))
|
||||
print(f"Found {len(rpps)} RPP files")
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
skipped = 0
|
||||
|
||||
for rpp in rpps[:10]: # Test first 10
|
||||
name = os.path.splitext(os.path.basename(rpp))[0]
|
||||
wav = f"{TEST_ROOT}/{name}.wav"
|
||||
|
||||
if not os.path.exists(wav):
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
result = test_sweep(name)
|
||||
if result is True:
|
||||
passed += 1
|
||||
elif result is False:
|
||||
failed += 1
|
||||
else:
|
||||
skipped += 1
|
||||
|
||||
print(f"\nSummary: {passed} passed, {failed} failed, {skipped} skipped")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user