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)
|