aboutsummaryrefslogtreecommitdiff
path: root/lib/tests/test_pipeline.py
blob: b3f91f1129ae2e2d1cb1c6727e41e0b59f5da814 (plain)
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import numpy as np
from conftest import band_db, sine, speechish

from producer import meters, pipeline
from producer.config import Options


def test_stage_order_and_names():
    opts = Options()
    opts.denoise = "off"
    opts.enhance = "off"
    res = pipeline.run_pipeline(np.zeros(44100, dtype=np.float32), 44100, opts)
    names = [s.name for s in res.stages]
    assert names == ["denoise", "enhance", "dsp", "levelling"]
    by_name = {s.name: s for s in res.stages}
    assert by_name["denoise"].enabled is False
    assert by_name["enhance"].enabled is False
    assert by_name["dsp"].enabled is True
    assert by_name["levelling"].enabled is True


def test_denoise_stage_detail_shows_pf():
    opts = Options()
    stages = pipeline.build_stages(opts)
    assert "pf=off" in stages[0].detail
    assert "leveling" not in stages[0].detail
    opts.denoise_pf = True
    stages = pipeline.build_stages(opts)
    assert "pf=on" in stages[0].detail


def test_full_chain_profile_bounds(sr):
    opts = Options()
    opts.denoise = "off"
    opts.enhance = "off"
    x = speechish(8.0, sr, level_dbfs=-35.0)
    res = pipeline.run_pipeline(x, sr, opts)
    assert abs(meters.rms_db(res.audio) + 20.0) < 0.6
    assert meters.true_peak_db(res.audio, sr) <= -2.9
    assert res.timings.get("levelling", 0) >= 0


def test_passthrough_when_disabled(sr):
    opts = Options()
    opts.denoise = "off"
    opts.enhance = "off"
    opts.dsp = False
    opts.levelling = False
    x = speechish(4.0, sr, level_dbfs=-20.0)
    res = pipeline.run_pipeline(x, sr, opts)
    assert np.allclose(res.audio, x)


def test_knob_zero_disables_eq(sr):
    base = Options()
    base.denoise = "off"
    base.enhance = "off"
    base.levelling = False
    base.strengths["warmth"] = 0.0
    warm = Options()
    warm.denoise = "off"
    warm.enhance = "off"
    warm.levelling = False
    x = sine(60, 4.0, sr, -20.0) + sine(3000, 4.0, sr, -20.0)
    y_flat = pipeline.run_pipeline(x, sr, base).audio
    y_warm = pipeline.run_pipeline(x, sr, warm).audio
    d_flat = band_db(y_flat, sr, 50, 70) - band_db(x, sr, 50, 70)
    d_warm = band_db(y_warm, sr, 50, 70) - band_db(x, sr, 50, 70)
    assert d_warm - d_flat > 0.8


def test_podcast_profile_bounds(sr):
    opts = Options()
    opts.profile = "podcast"
    opts.denoise = "off"
    opts.enhance = "off"
    x = speechish(8.0, sr, level_dbfs=-35.0)
    res = pipeline.run_pipeline(x, sr, opts)
    assert abs(meters.lufs(res.audio, sr) + 16.0) < 0.8
    assert meters.true_peak_db(res.audio, sr) <= -1.4


def test_dsp_pregain_makes_chain_level_invariant(sr):
    """The voice chain must treat a quiet and a hot take identically.

    Denoised files often arrive far below the chain's design level; without
    the pre-gain the absolute comp thresholds would idle (or slam) depending
    on input level alone.
    """
    hot = Options()
    hot.denoise = "off"
    hot.enhance = "off"
    hot.levelling = False
    quiet = Options()
    quiet.denoise = "off"
    quiet.enhance = "off"
    quiet.levelling = False
    x_hot = speechish(6.0, sr, level_dbfs=-14.0)
    x_quiet = speechish(6.0, sr, level_dbfs=-38.0)
    y_hot = pipeline.run_pipeline(x_hot, sr, hot).audio
    y_quiet = pipeline.run_pipeline(x_quiet, sr, quiet).audio
    diff = abs(meters.rms_db(y_hot) - meters.rms_db(y_quiet))
    assert diff < 1.0


def test_dsp_pregain_note_recorded(sr):
    opts = Options()
    opts.denoise = "off"
    opts.enhance = "off"
    notes: list[str] = []
    stages = pipeline.build_stages(opts, notes)
    dsp_stage = next(s for s in stages if s.name == "dsp")
    dsp_stage.fn(speechish(4.0, sr, level_dbfs=-30.0), sr)
    assert any("pregain" in n for n in notes)