e5420589f9ed06d0eb92f4935477e065941388cc
The uncommitted code from the last session was a working silence-index subcommand wired through cli.py — but it never landed on main. This commit: - Adds render_silence_index() to waveform.py (was already on disk but uncommitted) — tabular view of silence runs with start/end/duration and a position bar showing where each run falls in the audio. - Wires it through cli.py as 'silence-index' (alias: 'si') with --silence-threshold and --silence-min-duration args. - Adds 2 tests: test_render_silence_index_empty_audio (silent file produces 1 run with bar) and test_render_silence_index_no_silences_message (loud sine at -100dB threshold → 'no silence runs' message). - Updates README: 'si' added to step-by-step workflow and the subcommand table, alongside the previously-uncommitted 'mb' (multiband). 64/64 tests passing in 14.6s. Use case: when picking natural splice points to bracket a bad word, you want a tabular list of silence runs you can eyeball, not a waveform. si is the right tool for that — also faster than mb on long files since it doesn't need to render bars per band.
sermon-clean
A one-shot CLI for sermon audio editing: find bad segments, cut them out, paste in ElevenLabs replacements. Built because doing this by hand every time is unbearable.
Why
Recording a sermon is fine. Post-production is not. The current workflow needs:
- Manually identify where the bad words are (the painful part — Whisper stalls on long audio, and eyeballing a waveform is imprecise)
- Hand-write ffmpeg trim commands for each bad window
- Render replacement clips via ElevenLabs
- Hand-write the ffmpeg concat command
- Manually upload to Dropbox
sermon-clean collapses steps 1-5 into one command (or a few, if you want to eyeball the audio first).
Install
# Requires ffmpeg in PATH (sudo apt install ffmpeg on Debian/Ubuntu)
pip install sermon-clean
# Optional: for ElevenLabs auto-rendering
export ELEVENLABS_API_KEY=...
export ELEVENLABS_VOICE_ID=IYUnpZr9CQfSylOsOOBo # your cloned voice
# Optional: for auto-detect mode (transcribe + find bad words)
pip install "sermon-clean[auto]"
Usage
One-shot: explicit timestamps + replacement text
sermon-clean pipe sermon.ogg \
--bad "21:38-21:42:the actual sentence you meant to say" \
--bad "1450.3-1453.1:the corrected phrase" \
--elevenlabs-text \
-o sermon-fixed.ogg
Step-by-step workflow
# 1. Look at the audio — see its shape + find silence gaps
sermon-clean scan sermon.ogg --width 100
# 1b. Or: get a tabular index of silence runs (good for picking splice points)
sermon-clean si sermon.ogg --silence-threshold -35 --silence-min-duration 0.5
# 1c. Or: multiband waveform — N seconds per row, makes timestamp counting trivial
sermon-clean mb sermon.ogg --band-seconds 60 --width 80
# 2. Or: extract overlapping slices for manual review
sermon-clean slices sermon.ogg --output-dir ./slices \
--slice-seconds 5 --overlap-seconds 1
# Listen to ./slices/slice_000_0-00-0-05.ogg in your audio player.
# Filename embeds start/end timestamps.
# 3. Edit the JSON to mark bad segments + replacement text:
# segs.json:
# [
# {"start": 1298.0, "end": 1302.0, "reason": "misspoke", "replacement_text": "the actual sentence"},
# {"start": 1450.3, "end": 1453.1, "reason": "misspoke", "replacement_text": "the corrected phrase"}
# ]
# 4. Trim the original around the bad windows
sermon-clean cut sermon.ogg --segments-file segs.json
# 5. Render replacements (if you haven't already) and splice
sermon-clean paste sermon.ogg --segments-file segs.json \
--replacements "replacements/*.mp3" \
-o sermon-fixed.ogg
Auto-detect mode (optional, requires faster-whisper)
sermon-clean auto sermon.ogg --bad-words "fuck,shit,damn" --output segs.json
# transcribes the audio, finds timestamps for any of the bad words,
# prints a suggested JSON file you can edit before splicing
Subcommands
| Command | Purpose |
|---|---|
find |
Show audio metadata + silence gaps (no transcription) |
scan |
ASCII waveform + silence marks (no transcription) |
silence-index (si) |
Tabular list of silence runs with timestamps + position bar |
multiband (mb) |
Multi-row ASCII waveform with band-start labels for timestamp counting |
slices |
Extract overlapping audio chunks for manual review |
cut |
Trim the original around bad windows (no splice) |
paste |
Splice pre-rendered replacements into the trimmed original |
auto |
Transcribe + find bad-word timestamps |
pipe |
Run cut + paste in one command |
How it works
- Find →
ffmpeg silencedetectfor natural breath pauses, plus your explicit timestamps - Cut → trim the original into N+1 good segments using ffmpeg, re-encoding to the source codec (no
wavintermediate — bitrate-matched) - Paste → ElevenLabs renders + ffmpeg concat with bit-exact
-c copy(no audible clicks at splice boundaries) - Verify → ffprobe the output duration against expected; if it drifted >1s, the splice silently changed the runtime
Why this exists
The previous workflow (audio-splice-workflow.md in krystie-profile) was a 5-step manual recipe that's already broken twice. Each fix took 30+ minutes of bash. This package makes the same workflow a one-line command.
Performance
scanon a 27-min audio: ~50s (Opus decode is the bottleneck on this CPU)sliceson a 27-min audio with 5-sec slices: ~50sautoon a 27-min audio: ~10-20 min withtiny.enmodel on CPU. Usebase.enorsmall.enfor better accuracy at 2-4x the time. Whispertiny.enis the right model for finding a known bad-word list — you don't need higher accuracy than "did the word 'fuck' appear at all."
Development
git clone https://git.sami/sami7777/sermon-clean.git
cd sermon-clean
pip install -e ".[test,auto]"
pytest # 61 tests, ~17s
License
MIT — see LICENSE.
Languages
Python
100%