File size: 1,943 Bytes
a9536c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import tempfile
import unittest
from pathlib import Path

import numpy as np
import soundfile as sf

from lib.mixer import mix_vocals_and_accompaniment


def _rms(audio: np.ndarray) -> float:
    return float(np.sqrt(np.mean(np.square(audio)) + 1e-12))


class MixStabilityTests(unittest.TestCase):
    def test_default_mix_does_not_duck_accompaniment_when_vocal_enters(self):
        sr = 48000
        duration = 3.0
        t = np.arange(int(sr * duration), dtype=np.float32) / sr
        accompaniment = 0.07 * np.sin(2.0 * np.pi * 220.0 * t)
        vocals = np.zeros_like(accompaniment)
        active = (t >= 1.10) & (t < 2.00)
        vocals[active] = 0.045 * np.sin(2.0 * np.pi * 440.0 * t[active])

        with tempfile.TemporaryDirectory() as tmp_dir:
            tmp = Path(tmp_dir)
            vocals_path = tmp / "vocals.wav"
            accompaniment_path = tmp / "accompaniment.wav"
            output_path = tmp / "mix.wav"
            sf.write(vocals_path, vocals, sr)
            sf.write(accompaniment_path, accompaniment, sr)

            mix_vocals_and_accompaniment(
                str(vocals_path),
                str(accompaniment_path),
                str(output_path),
                target_sr=sr,
            )

            mixed, out_sr = sf.read(output_path)

        self.assertEqual(out_sr, sr)
        if mixed.ndim > 1:
            mixed = mixed.mean(axis=1)
        residual = mixed.astype(np.float32) - vocals.astype(np.float32)
        before = residual[int(0.40 * sr): int(0.90 * sr)]
        during = residual[int(1.30 * sr): int(1.80 * sr)]
        drop_db = 20.0 * np.log10((_rms(during) + 1e-12) / (_rms(before) + 1e-12))

        self.assertGreater(
            drop_db,
            -0.25,
            f"default mix should keep accompaniment steady, got {drop_db:.2f} dB drop",
        )


if __name__ == "__main__":
    unittest.main()