Communitygithub.com

sound-skill

A full video sound pipeline: search/download sound effects and BGM from free sources, synthesize offline loopable background music, clean vocals, sidechain duck mixing, and loudness normalization. For sound design, background music, voice processing, mixing, and loudness compliance.

What is sound-skill?

This skill provides a complete toolchain for video sound production without any third-party dependencies, API keys, or login requirements—only Python 3 and FFmpeg. It covers three core workflows: (1) sound effect selection with multi-source search (Freesound, Wikimedia Commons, Mixkit, Kenney), human-feedback-based filtering, download, and analysis; (2) background music via offline synthesis of seamless loopable tracks (ambient, lofi, upbeat styles) or retrieval of long audio beds from Commons; (3) vocal cleaning and mixing including noise reduction, de-esser, compression, sidechain ducking, and two-pass loudness normalization (EBU R128). All scripts are MIT-licensed and fully transparent, with detailed parameter rules, license grading (CC0, BY, any), and measurement unit clarifications to prevent common mistakes. It is designed for developers and content creators using Claude Code, Cursor, Codex, or similar AI agents, and the entire implementation is open-source for modification and redistribution.

Works with✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ruodou233/sound-skill/tree/HEAD

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

sound (Full Version)

A complete toolchain for adding sound to videos, with three capability lines:

  1. Sound Effect Selection — Multi-source search, filtering by human feedback signals, download, and analysis.
  2. BGM (Background Music) — Two paths: offline synthesis of seamless loopable music, or retrieval of long audio beds from libraries.
  3. Vocals & Mixing — Noise reduction/EQ/de-esser/compression, sidechain ducking, static mixing, two-pass loudness normalization.

Environment: Python 3 + FFmpeg (ffmpeg / ffprobe in PATH). Pure standard library, no third-party packages, no API keys, no login required.

All capabilities of this skill are open-source (MIT licensed) and implemented in this repository: all commands, rules, parameters are documented below and in references/, freely modifiable and distributable.

After installation, set variables according to the actual location. The examples below use the in-project installation path:

S=.claude/skills/sound/scripts/sfx.py       # Search / Download / Analysis
V=.claude/skills/sound/scripts/vocal.py     # Vocals / Mixing
C=.claude/skills/sound/scripts/compose.py   # Offline BGM synthesis

For global installation, replace .claude/skills/sound/ with ~/.claude/skills/sound/.


1. Sound Effect Selection

Workflow

  1. Convert visual actions into English keywords ("card flies in" → short whoosh, "switch toggle" → soft click).
  2. all searches across sources for candidates; use single-source search when necessary; --urls provides user audition links.
  3. First judge whether it matches the scene, then check popularity; provide candidates and reasoning—don't treat numbers as "I listened to it."
  4. Determine license based on usage, download, then use analyze to verify duration and energy distribution.
  5. When delivering, retain the source URL, author, and license; for non-CC0, include attribution.
python3 $S all "short whoosh" --license cc0 -n 10 --urls
python3 $S freesound "paper slide" --license by -n 5 --sort short
python3 $S commons "bell" -n 5 --json
python3 $S mixkit "whoosh" -n 5
python3 $S kenney "impact" -n 5
python3 $S get sfx/ fs:167563 wm:131079817 mk:1489
python3 $S analyze sfx/*.mp3 --loudness --silence --json

Selection Rules (Following Human Feedback Signals)

  • Rating count > Average rating: ★4.4(380) is stronger evidence than ★4.9(6).
  • Uploader description closest to audition:
    • Mentions like "built-in mic" / "made with my mouth" indicate lo-fi quality.
    • Named recorders (Zoom, Sound Devices) and specific sources ("cork from a wine bottle") indicate controlled conditions.
    • If source doesn't match the scene, discard regardless of popularity.
  • Popularity ≠ Fit: The most downloaded "thud" might be a 3-second movie low-frequency rumble. Always check duration, use analyze to see where energy falls and tail length, then decide on trimming.
  • Show candidates before downloading: Add --urls for user audition; don't decide for them.
  • Valuable signals differ by source:
    • Freesound: download count and rating count.
    • Commons: real duration and full license fields.
    • Mixkit: duration (whole seconds) but no popularity.
    • Kenney: entire asset packs.
    • Don't treat missing fields as zero scores.

License Rules (Strictly Graded, No Loosening)

ParameterAllowedTypical Use
--license cc0CC0 / PDM onlyCommercial use, no attribution hassle
--license byCC0 / PDM / BY / BY-SAAttribution acceptable (NC variants excluded)
--license anyAll, but results still labeledJust want to see candidates
  • BY-NC is never used commercially; scripts already exclude it under cc0/by; under any, judge yourself.
  • Mixkit uses its own "Mixkit Free License" (effectively commercial-free, no attribution), but it falls outside the CC0/BY label system, so it's excluded under cc0/by. When it's needed, use --license any explicitly and note the license name in deliverables.
  • Non-CC0 material: get prints author and license info; this is just a clue—retain the original source and license text in deliverables.
  • Kenney: all CC0, but downloads are entire ZIP packs, not individual sounds—extract and pick files manually.

Source Division

SourceStrengthLimitation
FreesoundOnly source with download/rating countsOnly downloads preview MP3, not original files; ~80 KB/s per connection
Wikimedia CommonsReal duration + complete license fields; environmental recordings, bells, long audioMatches by file description; strange keywords may fail; no popularity data
MixkitCommercial-free, no attribution; UI sound effects are consistently goodPage structure scraping; no popularity; duration labeled in whole seconds
KenneyCC0 game/UI sound packsEntire ZIP packs, no individual duration

Detailed scraping methods and license tag mappings in references/sources.md.


2. BGM: Synthesis or Bed

First determine whether the user wants "music" or "a piece that matches the mood":

ScenarioPathCommand
Needs loopable, zero-dependency, controllable mood, no copyright riskOffline Synthesiscompose.py
Needs real instruments/vocals/environmental atmosphereRetrieve bedsfx.py music
Has a short BGM and wants to fill the sceneLoop/Cutvocal.py loop / trim

Offline Synthesis (Preferred, True "BGM Synthesis")

python3 $C bgm.wav --style ambient --duration 30 --bpm 90 --key Am
python3 $C bgm.wav --style lofi    --duration 30 --bpm 85 --key C
python3 $C bgm.wav --style upbeat  --duration 30 --bpm 120 --key G
  • ambient: slow pads (bed chords), no drums, ideal for narration/product demos.
  • lofi: detuned chorus + loose kick/hat + tape noise floor.
  • upbeat: bright short chords + four-on-the-floor kick + bass line.
  • Output: 44.1 kHz / 16-bit / stereo WAV, phase-aligned at start and end + chord loop crossfade, seamlessly loopable.
  • Duration rounds to nearest whole bar (e.g., 30s @ 90 BPM actually 29.333s); for exact seconds, use a BPM divisible by 4 (e.g., 120).
  • Script prints directly pasteable --bgm-gain suggestion (in dB), consistent with vocal.py parameters.
  • --seed guarantees identical output for the same parameters, useful for version management.

Retrieve Bed

python3 $S music "ambient" --duration long --max-seconds 600 -n 8 --urls --json
python3 $S get bgm/ wm:123456
python3 $V trim bgm/xxx.ogg bed.wav --start 0 --end 30
  • music searches Commons with filetype:audio;
  • --duration long: defaults to ≥30 seconds, excludes unknown durations (add --allow-unknown to include them).
  • Commons often returns multi-hour field recordings/audiobooks; always add --max-seconds 600 to avoid downloading hundreds of MB.
  • After downloading a long audio, typically trim a segment, or loop a short segment to fill the scene length.

3. Vocal Cleaning & Mixing

Standard Chain

# 1. Vocals: noise reduction → high-pass → (de-esser) → compression → two-pass loudness normalization → fade in/out
python3 $V clean voice.wav clean.wav --deesser
#     Default target -16 LUFS; add --no-loudnorm to skip normalization

# 2. BGM alignment to scene length (choose one)
python3 $V loop  bgm.wav  bed.wav  --duration 30     # Short material, true crossfade at seam
python3 $V trim  bgm.wav  bed.wav  --start 0 --end 30

# 3. Mix: auto lower BGM when voice is present (sidechain ducking)
python3 $V duck clean.wav bed.wav mixed.wav --bgm-gain -12 --threshold -30 --ratio 8
#     For no vocals / pure ambience, use static mix instead
python3 $V mix  bed.wav clean.wav mixed.wav --bgm-gain -18

# 4. Final loudness check / normalize
python3 $V report mixed.wav
python3 $V loudnorm mixed.wav final.wav --target-lufs -16

Parameter Rules

ParameterDefaultWhen to Adjust
--bgm-gainduck -12 / mix -18 dBUse suggestion from compose.py; if BGM overpowers vocals, reduce by another 3-6 dB
--threshold-30 dBSidechain threshold is level threshold, not voice detection; lower to -36 for very quiet vocals, raise if BGM is very loud
--ratio8If ducking is too obvious or breathing is strong, reduce to 4
--target-lufs-16 LUFSUse -16 for speech/video; -14 for pure music streaming; -16 for podcasts
--true-peak-1.5 dBTPChange only if a platform has an explicit upper limit
--fadeduck 0.3 / clean 0.05 sFor intro/outro and loop seams
  • Don't mix units: I is LUFS (integrated loudness), TP is dBTP (true peak), LRA is LU (loudness range); mean_volume is dBFS average level. report clearly labels these—don't treat them as LUFS.
  • duck / mix both add apad (silence padding) and use duration=longest, so BGM shorter than vocals won't be cut off; --duration can specify final length.
  • clean's two-pass loudnorm measurement runs after the entire pre-chain, and measured I/TP/LRA are identical between passes; fade in/out comes before loudnorm to ensure measurement signal matches normalized input—otherwise measured_* won't align.
  • loudnorm's internal measurement may differ from ebur128 by about 1 LU; script performs a closed-loop verification and applies an additional gain after final output, printing "Closed-loop correction ±X.X dB". This is necessary for precise compliance—don't remove it.
  • For very short/near-silent material where valid LUFS can't be measured, it automatically downgrades to single-pass loudnorm and explicitly writes "Single pass" in output; closed-loop is skipped, so it won't blindly amplify silence.

Filter chain source, two-pass loudnorm theory, and adjustment table in references/mixing.md.


4. Common Recipes

A. 30-second product demo (fully offline, zero assets)

python3 $C bgm.wav --style ambient --duration 30 --bpm 120 --key Am
python3 $V clean voice.wav clean.wav --deesser
python3 $V duck clean.wav bgm.wav mixed.wav --bgm-gain -13
python3 $V loudnorm mixed.wav final.wav --target-lufs -16
python3 $S all "soft whoosh" --license cc0 -n 6 --urls

B. Realistic ambience bed for existing voiceover

python3 $S music "rain ambience" --duration long --max-seconds 600 --license by -n 6 --urls
python3 $S get bgm/ wm:xxx
python3 $V trim bgm/xxx bed.wav --start 10 --end 40
python3 $V clean voice.wav clean.wav
python3 $V duck clean.wav bed.wav mixed.wav --bgm-gain -18

C. Just a transition sound, commercial use allowed

python3 $S all "paper slide" --license cc0 -n 8 --sort short --urls
python3 $S get sfx/ fs:xxx
python3 $S analyze sfx/*.mp3 --loudness --silence --json

D. Volume envelope following the timeline (Remotion rendering)

Mastering uses the FFmpeg chain above; per-frame fade in/out and beat-based volume changes are done in Remotion rendering—see references/remotion-audio.md.

Related Skills