Spaces:
Paused
Paused
| """Unit tests for stitch.py — the stereo-native splice/crossfade math. No model, | |
| no GPU: stitch is pure librosa+numpy. Covers channel/rate normalization, seam | |
| length, original preservation, loudness match, closing fade and the peak guard.""" | |
| import numpy as np | |
| import pytest | |
| import stitch | |
| SR = stitch.SR | |
| def tone(freq, seconds, sr=SR, amp=0.3, phase=0.0, channels=2): | |
| t = np.arange(int(seconds * sr)) / sr | |
| y = (amp * np.sin(2 * np.pi * freq * t + phase)).astype(np.float32) | |
| if channels == 1: | |
| return y | |
| return np.stack([y, np.sin(2 * np.pi * freq * t + phase + 0.1) * amp]).astype(np.float32) | |
| def test_to_stereo_44k_from_mono_22k(): | |
| mono = tone(220, 1.0, sr=22050, channels=1) | |
| out = stitch.to_stereo_44k(mono, 22050) | |
| assert out.shape[0] == 2 # mono -> stereo | |
| assert abs(out.shape[1] - 44100) <= 2 # 22050 -> 44100 (1s) | |
| def test_to_stereo_44k_truncates_extra_channels(): | |
| five = np.zeros((5, 1000), dtype=np.float32) | |
| out = stitch.to_stereo_44k(five, SR) | |
| assert out.shape[0] == 2 | |
| def test_output_is_stereo_44k_and_unclipped(): | |
| orig = tone(220, 30, channels=2) | |
| tail = tone(220, 30, channels=2) | |
| out = stitch.stitch(orig, SR, tail, source_seconds=30) | |
| assert out.ndim == 2 and out.shape[0] == 2 | |
| peak = float(np.abs(out).max()) | |
| assert peak <= 1.0 | |
| assert peak == pytest.approx(0.891, abs=1e-3) # lifted to -1 dBFS | |
| def test_length_is_original_plus_tail_minus_crossfade(): | |
| orig = tone(220, 30, channels=2) | |
| tail = tone(330, 25, channels=2) | |
| xfade = 0.10 | |
| out = stitch.stitch(orig, SR, tail, source_seconds=30, | |
| crossfade_seconds=xfade) | |
| expected = orig.shape[-1] + tail.shape[-1] - int(xfade * SR) | |
| assert abs(out.shape[-1] - expected) <= 2 | |
| def test_original_preserved_before_seam(): | |
| orig = tone(220, 10, channels=2) | |
| tail = tone(440, 10, channels=2) | |
| out = stitch.stitch(orig, SR, tail, source_seconds=10, | |
| crossfade_seconds=0.1, end_fade_seconds=0.0, | |
| peak_ceiling=1.0) | |
| boundary = int(10 * SR) | |
| # before the crossfade region the original should match up to the global | |
| # gain stitch applies (compare shapes/correlation, not exact equality) | |
| pre = out[:, : boundary - int(0.2 * SR)] | |
| ref = orig[:, : pre.shape[-1]] | |
| # high correlation == the original content is intact (only scaled) | |
| corr = np.corrcoef(pre[0], ref[0])[0, 1] | |
| assert corr > 0.999 | |
| def test_closing_fade_ends_in_silence(): | |
| orig = tone(220, 5, channels=2) | |
| tail = tone(220, 10, channels=2) | |
| out = stitch.stitch(orig, SR, tail, source_seconds=5, end_fade_seconds=4.0) | |
| tail_peak = float(np.abs(out[:, -200:]).max()) | |
| assert tail_peak < 1e-3 # true silence at the end | |
| def test_quiet_tail_loudness_matched_up(): | |
| # a tail within the gain clamp (2x quieter) should be matched to the | |
| # original's level at the seam; extreme ratios are deliberately only | |
| # partially corrected (see the [0.4, 2.5] clamp in stitch.stitch). | |
| orig = tone(220, 10, amp=0.4, channels=2) | |
| quiet_tail = tone(220, 10, amp=0.2, channels=2) # 2x quieter — inside clamp | |
| out = stitch.stitch(orig, SR, quiet_tail, source_seconds=10, | |
| end_fade_seconds=0.0, peak_ceiling=1.0) | |
| seam = int(10 * SR) | |
| orig_rms = np.sqrt(np.mean(out[:, seam - SR: seam] ** 2)) | |
| tail_rms = np.sqrt(np.mean(out[:, seam + SR: seam + 3 * SR] ** 2)) | |
| assert tail_rms / orig_rms > 0.8 | |
| def test_mono_original_becomes_stereo(): | |
| orig = tone(220, 5, channels=1) | |
| tail = tone(220, 5, channels=2) | |
| out = stitch.stitch(orig, SR, tail, source_seconds=5) | |
| assert out.shape[0] == 2 # output is always stereo | |
| def test_no_seam_click(): | |
| orig = tone(220, 8, channels=2) | |
| tail = tone(220, 8, channels=2, phase=1.3) # different phase at join | |
| out = stitch.stitch(orig, SR, tail, source_seconds=8, | |
| crossfade_seconds=0.1, end_fade_seconds=0.0) | |
| boundary = int(8 * SR) | |
| seam = out[0, boundary - 1000: boundary + 1000] | |
| ref = np.abs(np.diff(out[0, :SR])).max() | |
| assert np.abs(np.diff(seam)).max() < ref * 3 | |