aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-09-11 18:21:38 -0400
committerhistoria <historiavg@proton.me>2026-09-11 18:21:38 -0400
commit742093b18cd31e9d25c43f475b94c232fc28a5a7 (patch)
tree056cc617f2e32cd1d43fcf681728ece4df8af6b2 /lib
parent3eccf822e14f5f4dd1f6b96e785c0217db647c3e (diff)
downloadproducer-742093b18cd31e9d25c43f475b94c232fc28a5a7.tar.gz
feat: config.toml added
Diffstat (limited to 'lib')
-rw-r--r--lib/project/examples/voice.toml55
-rw-r--r--lib/project/pyproject.toml2
-rw-r--r--lib/project/src/producer/cli.py8
-rw-r--r--lib/project/tests/test_cli.py16
4 files changed, 25 insertions, 56 deletions
diff --git a/lib/project/examples/voice.toml b/lib/project/examples/voice.toml
deleted file mode 100644
index ba0fa5c..0000000
--- a/lib/project/examples/voice.toml
+++ /dev/null
@@ -1,55 +0,0 @@
-# Complete natural-profile defaults. Load with --config lib/project/examples/voice.toml.
-# Flat TOML: no section headers. Named CLI flags override these values;
-# --set KEY=VALUE overrides named flags. Use --profile, not --set profile=...
-# All explicit values below override preset defaults, even with --profile radio.
-# Remove settings you want a different profile to supply.
-
-profile = "natural" # natural, narrator, radio, cleanup-only
-denoiser = "deepfilter" # DeepFilterNet3 only; fft is explicit non-AI fallback; none
-device = "auto" # auto, cpu, cuda; explicit cuda fails if unavailable
-denoise_strength = 0.85 # 0..1; AI dry/wet blend; zero skips denoising and AI setup
-fft_reduction_db = 8.0 # 0.01..30; FFT reduction is scaled by denoise_strength
-
-highpass = true
-highpass_hz = 70.0 # 20..300
-lowpass = false
-lowpass_hz = 16000.0 # 4000..22000; must be below output Nyquist when enabled
-hum_hz = 0 # 0 (off), 50 or 60; notches fundamental and next two harmonics
-
-# One static gain adjustment for the whole recording, not automatic gain riding.
-leveling = true
-level_target_db = -23.0 # -40..-12; heuristic speech level in dBFS, not LUFS
-max_gain_db = 18.0 # 0..30; caps both gain and attenuation
-
-expansion = false
-expansion_threshold_db = -50.0 # -90..-20
-expansion_ratio = 1.5 # 1..4
-expansion_range_db = 12.0 # 0..40
-
-eq = true
-warmth_db = 0.5 # -12..12; 140 Hz
-mud_db = -1.5 # -12..12; 300 Hz
-presence_db = 1.0 # -12..12; 3500 Hz
-
-compression = true
-compressor_threshold_db = -21.0 # -50..-5
-compressor_ratio = 2.0 # 1..10
-compressor_attack_ms = 15.0 # 0.1..200
-compressor_release_ms = 150.0 # 10..2000
-deess = true
-deess_intensity = 0.15 # 0..1
-deess_amount = 0.4 # 0..1
-
-# Two-pass normalization plus export verification; target misses warn only.
-# Turning normalize off leaves limiter on unless you also disable limiter.
-# normalize=true requires limiter=true. Previews force both on, even cleanup-only.
-normalize = true
-target_lufs = -19.0 # -30..-12; mono delivery target, not ACX certification
-loudness_range = 9.0 # 1..20 LU
-limiter = true
-true_peak_db = -1.5 # -9..-0.5 dBTP; MP3 can overshoot, inspect report warnings
-
-# Final exports are always mono, 32-bit PCM WAV, with an optional MP3 sidecar.
-sample_rate = 48000 # 44100 or 48000; intermediate processing is 48 kHz float
-mp3 = false
-mp3_bitrate = 192 # 128, 160, 192, 224, 256 or 320 kb/s
diff --git a/lib/project/pyproject.toml b/lib/project/pyproject.toml
index 625ee23..6c42cc9 100644
--- a/lib/project/pyproject.toml
+++ b/lib/project/pyproject.toml
@@ -26,4 +26,4 @@ testpaths = ["tests"]
packages = ["src/producer"]
[tool.hatch.build.targets.sdist]
-include = ["/src", "/tests", "/examples", "/pyproject.toml"]
+include = ["/src", "/tests", "/pyproject.toml"]
diff --git a/lib/project/src/producer/cli.py b/lib/project/src/producer/cli.py
index 4788fb5..374455a 100644
--- a/lib/project/src/producer/cli.py
+++ b/lib/project/src/producer/cli.py
@@ -80,7 +80,15 @@ def require_input(source: Path) -> None:
raise ValueError(f"Input must be an existing WAV file: {source}")
+# The 'process' keyword is optional: any first argument that is not a known
+# command (or a global option) is treated as the start of a process run.
+COMMANDS = ("process", "preview", "config", "doctor", "setup")
+GLOBAL_OPTIONS = ("-h", "--help", "--version")
+
+
def main():
+ if len(sys.argv) > 1 and sys.argv[1] not in COMMANDS and sys.argv[1] not in GLOBAL_OPTIONS:
+ sys.argv.insert(1, "process")
app = parser()
args = app.parse_args()
console = Console(stderr=True)
diff --git a/lib/project/tests/test_cli.py b/lib/project/tests/test_cli.py
index e02aad1..05c8292 100644
--- a/lib/project/tests/test_cli.py
+++ b/lib/project/tests/test_cli.py
@@ -176,6 +176,22 @@ def test_default_outputs_are_written_beside_each_input(wav, tmp_path, ffmpeg, in
assert captured.err.count("Written:") == 2
+def test_process_keyword_is_optional(wav, tmp_path, ffmpeg, invoke, capsys):
+ source = wav()
+ original = source.read_bytes()
+ invoke(source, "--no-denoise")
+ assert {path.name for path in tmp_path.iterdir()} == {
+ "input.wav", "input.natural.wav", "logs"}
+ assert source.read_bytes() == original
+ assert capsys.readouterr().err.count("Written:") == 1
+
+
+def test_leading_flag_still_selects_process(wav, tmp_path, ffmpeg, invoke, capsys):
+ source = wav()
+ invoke("--no-denoise", source)
+ assert (tmp_path / "input.natural.wav").exists()
+
+
def test_output_and_output_dir_cannot_be_combined(wav, tmp_path, invoke, capsys):
with pytest.raises(SystemExit) as error:
invoke("process", wav(), "-o", tmp_path / "x.wav", "--output-dir", tmp_path / "outs")