import numpy as np from mixer import normalize_rms, mix_streams, soft_limit TARGET_DBFS = -20 class TestNormalizeRms: def test_silence_stays_silent(self, silence): result = normalize_rms(silence, TARGET_DBFS) assert np.allclose(result, 0.0) def test_quiet_tone_gets_louder(self, quiet_tone): original_rms = np.sqrt(np.mean(quiet_tone ** 2)) result = normalize_rms(quiet_tone, TARGET_DBFS) result_rms = np.sqrt(np.mean(result ** 2)) assert result_rms > original_rms def test_loud_tone_gets_quieter(self, loud_tone): original_rms = np.sqrt(np.mean(loud_tone ** 2)) result = normalize_rms(loud_tone, TARGET_DBFS) result_rms = np.sqrt(np.mean(result ** 2)) assert result_rms < original_rms def test_normalized_to_target(self, loud_tone): result = normalize_rms(loud_tone, TARGET_DBFS) result_rms = np.sqrt(np.mean(result ** 2)) result_dbfs = 20 * np.log10(result_rms + 1e-10) assert abs(result_dbfs - TARGET_DBFS) < 1.0 # within 1 dB class TestMixStreams: def test_single_stream_unchanged(self, loud_tone): result = mix_streams([loud_tone]) assert np.allclose(result, loud_tone) def test_two_streams_summed(self, loud_tone): result = mix_streams([loud_tone, loud_tone]) # Two identical streams summed should be louder assert np.max(np.abs(result)) > np.max(np.abs(loud_tone)) def test_empty_list_returns_silence(self, silence): result = mix_streams([]) assert result.shape[0] == 960 assert np.allclose(result, 0.0) def test_different_lengths_uses_shortest(self): short = np.ones(480, dtype=np.float32) * 0.5 long = np.ones(960, dtype=np.float32) * 0.5 result = mix_streams([short, long]) assert result.shape[0] == 480 class TestSoftLimit: def test_quiet_signal_unchanged(self, quiet_tone): result = soft_limit(quiet_tone) assert np.allclose(result, quiet_tone, atol=0.001) def test_clipping_signal_contained(self, clipping_tone): result = soft_limit(clipping_tone) assert np.max(np.abs(result)) <= 1.0 def test_preserves_sign(self, clipping_tone): result = soft_limit(clipping_tone) # Signs should match where input is non-zero nonzero = np.abs(clipping_tone) > 0.01 assert np.all(np.sign(result[nonzero]) == np.sign(clipping_tone[nonzero]))