coda / test_stitch_logic.py
blackboxanalytics's picture
Rebuild CODA on Stable Audio 3 Small Music
e8b2f06
Raw
History Blame Contribute Delete
4.22 kB
"""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