import os import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) import numpy as np import pytest from app.core.sub_tab_dsp import SubTabDSPEngine def test_speed_ratio(): sr = 44100 y = np.sin(2 * np.pi * 440 * np.linspace(0, 1, sr, endpoint=False)).astype(np.float32) # Pitch-preserving speed change y_stretched = SubTabDSPEngine.change_speed(y, sr, 2.0, preserve_pitch=True) assert abs(len(y_stretched) - sr // 2) < 2000 # Librosa might have frame alignment differences # Simple resampling speed change y_resampled = SubTabDSPEngine.change_speed(y, sr, 2.0, preserve_pitch=False) assert len(y_resampled) == sr // 2 def test_normalize(): y = np.array([0.1, -0.5, 0.2, 0.4], dtype=np.float32) y_norm = SubTabDSPEngine.normalize(y, target_db=0.0) assert np.max(np.abs(y_norm)) == 1.0 def test_merge_back_to_parent(): sr = 1000 parent = np.ones(5000, dtype=np.float32) edited = np.zeros(2000, dtype=np.float32) # Merge at t=1.0s (index 1000), original duration 1.5s (1500 samples) res = SubTabDSPEngine.merge_back_to_parent( parent_track_audio=parent, sr=sr, edited_sub_audio=edited, start_seconds=1.0, original_duration_seconds=1.5 ) # Expected length: 5000 - 1500 + 2000 = 5500 assert len(res) == 5500 # Before 1.0s (1000 samples) should be mostly parent values (1.0) assert np.allclose(res[:900], 1.0) # Inside the edited range should be zero (except crossfades) assert np.allclose(res[1100:2900], 0.0) def test_apply_volume_automation_envelope(): sr = 44100 duration = 2.0 y = np.ones(int(sr * duration), dtype=np.float32) * 0.5 # Constant signal at -6dB # Simple fade in from -inf to 0dB over 1 second nodes = [ {"time": 0.0, "db": -60.0}, # Effectively -inf {"time": 1.0, "db": 0.0}, {"time": 2.0, "db": 0.0}, ] y_automated = SubTabDSPEngine.apply_volume_automation_envelope(y, sr, nodes) y_automated = SubTabDSPEngine.apply_volume_automation_envelope(y, sr, nodes) # Check start: should be 0.5 * 10**(-30/20) due to clipping expected_start_val = 0.5 * (10**(-30/20.0)) assert np.isclose(y_automated[0], expected_start_val, atol=1e-5) # Check at 0.5 seconds: interpolated to -15dB (halfway between -30dB and 0dB) # y * (10 ** (-15 / 20)) expected_mid_val = 0.5 * (10**(-15/20.0)) assert np.isclose(y_automated[int(0.5 * sr)], expected_mid_val, atol=1e-5) # Check at 1.0 seconds: should be 0.5 * (10**(0/20)) = 0.5 assert np.isclose(y_automated[int(1.0 * sr)], 0.5, atol=1e-5) # Check at end: should be 0.5 assert np.isclose(y_automated[-1], 0.5, atol=1e-5) # Test with empty nodes y_no_nodes = SubTabDSPEngine.apply_volume_automation_envelope(y, sr, []) assert np.array_equal(y_no_nodes, y)