Skip to content

feat(addmusic): duck background music and schedule sound effects - #161

Merged
ConalMullan merged 9 commits into
digitalsamba:mainfrom
AsharibAli:feat/standalone-audio-mix
Nov 9, 2026
Merged

ConalMullan merged 9 commits into
digitalsamba:mainfrom
AsharibAli:feat/standalone-audio-mix

Conversation

@AsharibAli

@AsharibAli AsharibAli commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Videos with narration need music to drop during speech and recover in pauses. This extends addmusic.py with --duck and --events, preserving its existing music-file and ElevenLabs prompt workflows.

  • Reuse the existing volume and fade flags. Optional --speech supplies isolated narration, and attack/release controls use milliseconds.
  • Resolve effect paths relative to their JSON file. Loop music to the video duration and keep the video stream unchanged.
  • Run by default, overwrite completed outputs through a same-directory temporary file and os.replace, and preserve the previous output on encoder failure. --dry-run performs no writes or API calls.
  • Remove the separate audio_mix.py entry point, its registry entry, per-tool document and duplicate media_ops.py. Document the feature in the music section of CLAUDE.md and the existing addmusic registry entry.
  • Show the final qc.py --render --fix and verification commands because a peak limiter does not set integrated loudness.

Validation ran six focused tests with real FFmpeg audio measurements, timed-effect windows, default CLI reruns, silent-video music looping, failure preservation and delivery loudness after QC normalization. No paid generation was called. This uses installed FFmpeg and existing dependencies, with the existing billed ElevenLabs path remaining optional.

The five updated branches merge together without conflicts. Combined verification ran 153 Python tests, with 151 passing and two POSIX process tests skipped on Windows. The existing undefined-name lint passed. A real combined CLI flow produced a 1080x1920 Short with joined audio, ducked music, timed effects, SRT/VTT and a QC-normalized mix measured at -14.03 LUFS.

Feature inventory

Copilot AI balanced review requested due to automatic review settings November 2, 2026 20:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@ConalMullan ConalMullan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Ducking music under speech is a real gap: addmusic.py mixes at a fixed volume, and our templates don't duck at all. A few changes before it can land:

  1. Make it addmusic.py --duck (plus --events for timed effects), not a new tool. addmusic.py is where people already go to put music on a video, and a second music mixer would split the docs and the habit.
  2. Rebase onto the shared media_ops.py once #162 lands, rather than carrying a copy (or inline the few helpers you need).
  3. Run by default and overwrite outputs, writing via temp file + os.replace rather than os.link (see the conventions note on #162).
  4. Loudness: run the result through qc.py --render in a test or the docs example, so the ducked mix stays near -14 LUFS and doesn't fall well under it.
  5. A few lines in CLAUDE.md under the music section, rather than docs/audio-mix.md.

@AsharibAli AsharibAli changed the title feat(audio): duck music around speech and schedule sound effects feat(addmusic): duck background music and schedule sound effects Nov 7, 2026
@AsharibAli

Copy link
Copy Markdown
Contributor Author

@ConalMullan, I addressed your review in 386f548 and 25db4bb.

  1. Ducking and timed effects now live in addmusic.py --duck --events effects.json. The separate mixer, document and registry entry are removed.
  2. The duplicate media_ops.py is removed from this PR. The few output and audio checks needed here are local, so the shared module can land once through feat(highlights): select final takes and export reviewable cutdown ranges #162.
  3. The command runs by default and atomically overwrites completed outputs with a same-directory temporary file and os.replace. Failed renders preserve the previous output, and dry runs skip writes and paid generation.
  4. The tests run the mixed result through qc.py --render --fix and verify the normalized result near -14 LUFS. The music section also shows the QC delivery commands.
  5. Documentation is in CLAUDE.md and the existing addmusic registry entry.

All six focused audio tests passed, including measured ducking/recovery and timed effects. The five updated branches merge together without conflicts. Combined verification ran 153 Python tests, with 151 passing and two POSIX process tests skipped on Windows. The existing undefined-name lint passed. A real combined CLI flow produced a 1080x1920 Short with joined audio, ducked music, timed effects, SRT/VTT and a QC-normalized mix measured at -14.03 LUFS.

ConalMullan and others added 2 commits November 9, 2026 15:56
…o a mono or low-rate narration no longer folds stereo music to mono, and a missing effect file is named

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ConalMullan
ConalMullan merged commit 5288c44 into digitalsamba:main Nov 9, 2026
3 checks passed
@ConalMullan

Copy link
Copy Markdown
Collaborator

Thanks, this is a big step on from the first version. Ducking lives in addmusic.py, the copy of media_ops.py is gone, outputs are replaced through a temp file and os.replace, and the qc.py loudness test is a nice touch.

We found one thing and fixed it on your branch so we could merge today: amix takes the first input's format, so a mono 22.05 kHz narration with stereo music gave a mono 22.05 kHz track. Every input is now brought to 48 kHz stereo before the mix, with a test. A missing effect file now names the path instead of printing the ffprobe command, and we kept both sections where CLAUDE.md conflicted with the new sfx_search one (the effects line points to it). Merged, thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants