# Producer Local speech cleanup and mastering for voice recordings. Noise removal uses ClearVoice or DeepFilterNet3; speech-aware pause silencing uses Silero VAD. Filtering, dynamics and loudness normalization use FFmpeg. Audio stays local. `producer.sh` bootstraps its own managed Python environments and downloads dependencies and models automatically. ## Requirements - glibc >= 2.28 for AI denoising and speech detection - `uv` (or Python 3) - NVidia driver and toolkit (optional) ## Usage ```bash git clone https://git.historia.vg/git/producer ./producer.sh setup --denoiser clearvoice ./producer.sh recording.wav ``` First run installs the runtime into `lib/runtime/` and may take a few minutes. The `process` keyword is optional; `./producer.sh process recording.wav` is equivalent. The master WAV is always **mono, 32-bit PCM**, at 48 kHz (default) or 44.1 kHz; stereo inputs must be downmixed to mono first. MP3 is encoded as constant bitrate at 192 kb/s by default. Loudness is normalized to **-19 LUFS with a -1.5 dBTP true-peak ceiling**. ### Examples ``` # Optional MP3 alongside the WAV ./producer.sh recording.wav --mp3 # Other sound profiles ./producer.sh recording.wav --profile narrator ./producer.sh recording.wav --profile radio # Process several files at once ./producer.sh take1.wav take2.wav --mp3 # Write outputs somewhere else ./producer.sh recording.wav --output-dir delivery --mp3 # Skip AI noise removal (FFT fallback or none at all) ./producer.sh recording.wav --denoiser fft ./producer.sh recording.wav --no-denoise # Compare ClearVoice MossFormer2_SE_48K with DeepFilterNet3 # (larger download; speech preservation depends on the recording) ./producer.sh recording.wav --denoiser clearvoice ./producer.sh setup --denoiser clearvoice --device cpu # Silence pauses without changing the timing (enabled in config.toml) ./producer.sh recording.wav --pause-silence --no-trim-silence # Bypass pause silencing for an A/B comparison ./producer.sh recording.wav --no-pause-silence --output-dir comparison # Optionally shorten long gaps between takes (a separate pacing edit) ./producer.sh recording.wav --trim-silence --max-gap-s 1.5 --gap-pad-s 0.6 # LibriVox-style delivery file: 44.1 kHz, 128 kbps CBR, -19 LUFS, mono ./producer.sh recording.wav \ --sample-rate 44100 --mp3 --mp3-bitrate 128 \ --target-lufs -19 --true-peak-db -1.5 ``` Edit default settings in `config.toml` ## Optional local browser GUI ```bash ./producer.sh gui # Optional alternate loopback port: ./producer.sh gui --port 8766 ``` The studio is a compact workbench: pick a WAV, select a passage, tune settings, compare, and export. Use a current desktop browser (Chromium tested); JavaScript is required. No Node/npm build, CDN, telemetry, or cloud audio service is used. The launcher installs the optional FastAPI/uvicorn extra only when `gui` is requested. Existing CLI commands do not need it. Direct package installs can use `producer[gui]` and need system FFmpeg or the `bundled-ffmpeg` extra for processing. 1. Click **Browse…** and choose a mono WAV from anywhere on this machine (the file picker can also batch-select several). The browser uploads a temporary copy for processing; your original file is never touched. The overview is a streaming scan reduced to 2,048 peak bins, not the whole recording loaded into the browser. You can enter times before scanning finishes. 2. Click or drag the waveform, or enter exact `HH:MM:SS.sss` start/end times (seconds also work). Zoom enlarges the same coarse overview; exact times are best for fine selection on multi-hour recordings. 3. Every control's help text briefly explains what it does. Turning an effect off greys out and disables its dependent values while keeping them. FFT reduction only applies to the lightweight FFT denoiser: it is scaled by cleanup strength and disappears entirely when you pick DeepFilter, ClearVoice or "none". "Pause silencing (VAD)" uses a local speech detector to zero non-speech pauses without moving anything; "Gap shortening (export only)" trims long gaps on the full export and is never auditioned in the comparison. Click **Render sound comparison**. 4. Play/loop the passage and switch A/B without losing your place. Optional matching attenuates the louder snippet for fair listening. File loudness and peak readings are separate from monitoring gain, not quality scores or full-export measurements. Changes pause playback and mark older renders; late jobs never replace the visible comparison after you edit the selection. 5. Enter an existing **absolute** output directory (default: the directory you launched Producer from — start it next to your recording and exports land beside the input), review the frozen current settings and confirm **Export full recording**. This processes the entire original even if you selected only a few seconds. Completed exports list their full local paths; find the files with your operating system's file manager. The server binds only to loopback (127.0.0.1) and has no login: **anything that can reach this port on this machine can use it**, so run it only on a machine you control and do not expose it through a proxy, port-forwarding, or `0.0.0.0`. Stop the server with Ctrl-C. **Containers/remote shells:** loopback belongs to the machine/network namespace running Producer. A browser on the host cannot normally reach a container's `127.0.0.1`; publishing a container port does not help because the server still only answers `127.0.0.1`. The simplest supported setup is to run `./producer.sh gui` directly on the machine with your browser and recordings. Settings use the same strict resolver and configuration precedence as the CLI. The initial controls include your launch TOML and CLI overrides. **Restore launch settings** resets edits. **Apply profile** resets through that precedence too: explicit launch values still win. Changing only the profile name keeps your other explicit knob values. TOML import instead loads standalone settings (package defaults/profile plus the imported TOML). Save up to 20 named settings presets and 12 immutable comparison snapshots in this browser. Snapshots retain the recording identity, excerpt times and requested settings, never audio. **Restore & render** explicitly recreates a comparison; a changed source file must be re-uploaded and re-selected. Download/import small TOML presets to move settings between browsers. Browser storage can be cleared or disabled; use TOML for backup. Uploads, waveform scans and jobs run in the background, one at a time, with up to three waiting. Activity shows per-stage progress, frozen snapshots and cancellation. Cancellation is cooperative: a model download may take up to 60 seconds to reach a safe callback, and files already published can remain. There is no total recording-duration cap; typical 1–4 hour and longer recordings still need sufficient processing time and temporary disk space. Inputs must be readable mono WAVs; stereo needs an explicit downmix outside Producer first. Uploaded copies live in a private temporary directory and are deleted when the server stops. First AI noise cleanup or pause silencing use downloads dependencies/models; processing stays local afterwards. For a quick no-model trial, launch with `./producer.sh gui --no-denoise --no-pause-silence`. These explicit flags establish initial controls, not hidden permanent bypasses. Reload reconnects to recent jobs, but does not automatically restore/play old audio. If Producer restarts, open its printed URL again. If a connection drops during an export request, check Activity before submitting another export; the first may have succeeded. Sound-comparison previews select up to 120 seconds anywhere in a recording, process with up to 10 seconds of context on each side, then crop to the selected interval. They are **approximate, not exact export previews**: chapter-level normalization and inference context differ. Gap-trimming edits are not previewed; comparisons preserve timing even when export gap trimming is selected. Normalization and other effects are never forced on for comparison. Optional loudness matching is playback-only attenuation, not a change to export settings. Exports process the whole original with the requested settings into a new, non-clobbering `producer-export-*` folder in the chosen output directory: `master.wav`, optional `master.mp3`, and `logs/master.report.json`. The API returns persistent local paths, **not browser blobs of multi-hour exports**. The browser only fetches bounded preview snippets. TOML presets can be imported as text or downloaded; no source recording is rewritten by the API. The server retains at most eight repeated-result cache entries and 128 MiB of artifacts; expired previews must be rendered again. Job history is limited to 24 entries and is lost when the server stops, while completed output files persist. Browser preview WAVs are bounded to about 11.6 MB per side (120 seconds of mono PCM16 at 48 kHz). Full exports never become browser Blobs. These are generic WAV/MP3 delivery files, not delivery-platform compliance certification. ### GUI development checks (optional) The application itself needs no browser automation or Node dependencies. With an external test environment containing `producer[gui,test]`, use: ```bash python -m pytest lib/project/tests -q node --check lib/project/src/producer/web/app.js node --test lib/project/tests/gui_state.test.mjs # Optional real headless browser checks, no AI models: python -m pip install playwright python -m playwright install chromium PRODUCER_BROWSER_TESTS=1 python -m pytest lib/project/tests/test_gui_browser*.py -q ``` Playwright may need OS-specific browser shared libraries in the test container; these are not application runtime dependencies. Browser checks generate a short test WAV, exercise real scan/preview/export and cancellation, and mock a bounded four-hour overview. They do not claim a four-hour AI processing run. Screenshots default to `/tmp/producer-browser-smoke`; set `PRODUCER_BROWSER_ARTIFACTS` to keep them elsewhere. ## Profiles | Profile | Sound | | -------------- | --------------------------------------------------------- | | `natural` | Mild EQ, 2:1 compression, light de-essing (default) | | `narrator` | Gentler compression, slower attack, wider loudness range | | `radio` | Stronger compression, more warmth/presence, tighter range | | `cleanup-only` | Denoise + highpass only; no EQ, dynamics or normalization | ## Speech starts and silent pauses Denoising and pause silencing are different jobs. Pushing a denoiser harder can damage quiet consonants without eliminating all room tone. Compression and normalization then raise the remaining noise. The shipped `config.toml` uses ClearVoice at 0.85 strength, speech-aware pause silencing, and **no gap trimming**. Bare package defaults leave pause silencing off (opt in with `--pause-silence`). Pause silencing detects speech on the **pre-denoise** audio using pinned Silero VAD v5.1 ONNX weights. First use downloads a separate CPU runtime and ~2.3 MB model; it needs no GPU or cloud service. After EQ/dynamics, gaps outside protected speech fade to zero before loudness normalization. This keeps the timeline unchanged unless you separately enable `--trim-silence`. 32-bit export uses no dither so silent cores can remain digital zero; resampling/filter tails may extend slightly into their edges. MP3 need not retain sample-exact silence near speech boundaries. Defaults protect 120 ms before and after detections, use 10 ms fades, and require an additional 80 ms silent core. Shorter gaps are left alone rather than risking chopped words. Controls: - `--pause-threshold 0.25`: lower = preserve more possible speech. - `--pause-pre-ms 180`: protect more of quiet sentence starts. - `--pause-post-ms 200`: keep longer word tails and decay. - `--pause-min-ms 40`: allow shorter silent cores after margins and fades. - `--no-pause-silence`: bypass this stage; no detector download. Combine with `--no-denoise` to avoid all AI model/runtime downloads. The detector can miss whispers, breaths, or unusual speech. If it detects **no speech**, cleanup fails open with a warning instead of muting the file. Other detector failures stop processing, not silently switch to a level gate. Reports in `logs/` include the attenuated intervals on the original, pre-trim timeline for inspection. With VAD enabled, optional trimming only shortens near-zero cores; without it, legacy `gap_threshold_db` trimming can mistake quiet speech for silence. This stage does **not** restore consonants a denoiser has already removed. Compare denoisers and try `--denoise-strength 0.65` if attacks remain soft; a dry/wet blend trades some noise reduction for speech preservation. A low, consistent room tone can sound more natural than dead silence. Professional quality requires listening for artifacts, breaths, sibilance, clipping and reverb, not just checking a meter. LUFS targets here are not ACX RMS/noise-floor certification; no one-click chain guarantees every recording meets a delivery specification. Test the difference on your own audio before committing: ```bash ./producer.sh preview recording.wav --start 10 --duration 30 ``` ## Troubleshooting - **Bootstrap fails:** the first run needs `uv` or Python 3 with venv support and network access to PyPI. Install the missing tool and rerun. - **CUDA unavailable:** run `./producer.sh doctor`, check `nvidia-smi` and the driver, then retry `./producer.sh setup --device cuda`. Or use `--device cpu`. - **Sounds watery or thin:** lower the denoise strength (`--denoise-strength 0.65`) or compare profiles with `preview`. - **Word attacks sound soft or ramp up:** AI denoisers can soften consonant onsets. Try `--denoiser clearvoice` (larger non-causal model that sees more attack context), lower `--denoise-strength`, or compare against `--no-denoise` to locate the culprit. ## License [0BSD](LICENSE). Upstream components (ClearVoice, DeepFilterNet, Silero VAD, ONNX Runtime, PyTorch, FFmpeg, uv) carry their own licenses. Silero VAD is MIT-licensed: https://github.com/snakers4/silero-vad/tree/v5.1