jam-tracks / backend /tests /test_audio_processor.py
Mina Emadi
adding post processing and increasing the number of co-current processes
184639f
"""Tests for audio processor service."""
import pytest
import numpy as np
from backend.services.audio_processor import (
shift_and_stretch_single,
process_all_stems,
mix_stems,
)
from backend.models.session import StemData
def test_pitch_shift_up_2(sample_stems):
"""Pitch shifting up 2 semitones should not crash and should return audio."""
stems, sr = sample_stems
for name, audio in stems.items():
result = shift_and_stretch_single(
audio, sr, semitones=2, tempo_ratio=1.0, stem_type=name
)
assert isinstance(result, np.ndarray)
assert len(result) > 0
# Duration should be approximately the same (no time stretch)
assert abs(len(result) - len(audio)) / sr < 0.1 # within 100ms
def test_pitch_shift_down_4(sample_stems):
"""Pitch shifting down 4 semitones should work."""
stems, sr = sample_stems
audio = stems["guitar"]
result = shift_and_stretch_single(
audio, sr, semitones=-4, tempo_ratio=1.0, stem_type="guitar"
)
assert len(result) > 0
def test_time_stretch_faster(sample_stems):
"""Speeding up by 20% should produce shorter audio."""
stems, sr = sample_stems
audio = stems["bass"]
result = shift_and_stretch_single(
audio, sr, semitones=0, tempo_ratio=1.2, stem_type="bass"
)
expected_length = len(audio) / 1.2
assert abs(len(result) - expected_length) / sr < 0.2 # within 200ms
def test_time_stretch_slower(sample_stems):
"""Slowing down by 20% should produce longer audio."""
stems, sr = sample_stems
audio = stems["bass"]
result = shift_and_stretch_single(
audio, sr, semitones=0, tempo_ratio=0.8, stem_type="bass"
)
expected_length = len(audio) / 0.8
assert abs(len(result) - expected_length) / sr < 0.2
def test_combined_shift_and_stretch(sample_stems):
"""Combined pitch+tempo should work in single pass."""
stems, sr = sample_stems
audio = stems["guitar"]
result = shift_and_stretch_single(
audio, sr, semitones=3, tempo_ratio=1.15, stem_type="guitar"
)
assert len(result) > 0
def test_no_change_passthrough(sample_stems):
"""Zero semitones + 1.0 ratio should return unchanged audio."""
stems, sr = sample_stems
audio = stems["bass"]
result = shift_and_stretch_single(
audio, sr, semitones=0, tempo_ratio=1.0, stem_type="bass"
)
np.testing.assert_array_almost_equal(result, audio, decimal=5)
def test_process_all_stems_parallel(sample_stems):
"""Processing all stems in parallel should return all stems."""
stems_dict, sr = sample_stems
stem_data = {
name: StemData(name=name, audio=audio, sample_rate=sr)
for name, audio in stems_dict.items()
}
results = process_all_stems(stem_data, semitones=2, tempo_ratio=1.1)
assert set(results.keys()) == set(stems_dict.keys())
def test_mix_stems(sample_stems):
"""Mixing stems should produce audio without clipping."""
stems, sr = sample_stems
mixed = mix_stems(stems, sr)
assert np.max(np.abs(mixed)) <= 1.0
assert len(mixed) == max(len(a) for a in stems.values())
def test_mix_stems_empty():
"""Mixing empty stems dict should return empty array."""
mixed = mix_stems({}, sample_rate=48000)
assert len(mixed) == 0
def test_process_all_stems_no_change(sample_stems):
"""Processing with no changes should return copies."""
stems_dict, sr = sample_stems
stem_data = {
name: StemData(name=name, audio=audio, sample_rate=sr)
for name, audio in stems_dict.items()
}
results = process_all_stems(stem_data, semitones=0, tempo_ratio=1.0)
for name in stems_dict:
np.testing.assert_array_almost_equal(
results[name].audio, stem_data[name].audio, decimal=5
)