import array import math import struct import sys import tempfile import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) import audio_report as ar def wav_bytes(frames, rate=8000, channels=2, data_size=None): raw = frames.tobytes() size = len(raw) if data_size is None else data_size return struct.pack("<4sI4s4sIHHIIHH4sI", b"RIFF", 36 + size, b"WAVE", b"fmt ", 16, 1, channels, rate, rate * channels * 2, channels * 2, 16, b"data", size) + raw class AudioReportTests(unittest.TestCase): def test_reads_to_eof_even_with_placeholder_data_size(self): frames = array.array("h", [1000, -1000] * 8000) # 1 s stereo at 8 kHz with tempfile.TemporaryDirectory() as tmp: path = Path(tmp) / "a.wav" path.write_bytes(wav_bytes(frames, data_size=0)) # killed writer: size never fixed rate, channels, samples = ar.read_wav(path) self.assertEqual((rate, channels, len(samples)), (8000, 2, 16000)) def test_levels_per_second_of_mono_mix(self): # second 0: full-scale square on both channels (0 dBFS); second 1: silence loud = [16384 if (i // 20) % 2 else -16384 for i in range(8000)] frames = array.array("h", [v for s in loud for v in (s, s)] + [0, 0] * 8000) lv = ar.levels(8000, 2, frames) self.assertEqual(len(lv), 2) self.assertAlmostEqual(lv[0][1], 20 * math.log10(0.5), places=3) # rms of ±0.5 square self.assertEqual(lv[1][1], -120.0) def test_opposite_channels_cancel_in_the_mix(self): frames = array.array("h", [8000, -8000] * 8000) self.assertEqual(ar.levels(8000, 2, frames)[0][1], -120.0) if __name__ == "__main__": unittest.main()