mixing — router & core conventions
mixing edits audio/video from Python. This skill orients you; then jump to the
focused sub-skill for the medium you're working in.
Pick a sub-skill
| You want to… |
Sub-skill |
| Edit audio (slice, fade, crop, concat, overlay, normalize, align, segment) |
mixing-audio |
| Edit video (slice, crop, loop, speed, replace/normalize audio, Ken Burns, concat, thumbnail, subtitles) |
mixing-video |
| Transcribe speech, remove fillers, make SRT/prose, detect chapters |
mixing-transcript |
| Re-voice / translate via text-to-speech |
mixing-dubbing |
First moves (every task)
- Check ffmpeg — most operations need it:
import mixing
assert mixing.has_ffmpeg(), "install ffmpeg (brew install ffmpeg / apt install ffmpeg)"
- Import lazily / specifically.
import mixing is cheap (no moviepy/opencv
loaded until you touch a heavy name). Either use the facade
(mixing.Audio, mixing.Video, mixing.replace_audio, …) or import the
subpackage (from mixing.audio import Audio).
- Know the
output convention (below) before calling anything that writes.
The output protocol (read this once, applies everywhere)
Every function that produces a result takes one output argument:
crop_video("in.mp4", 5, 15, output="out.mp4") # write a file → returns Path
crop_video("in.mp4", 5, 15, output="clips/") # directory → auto-named file
crop_video("in.mp4", 5, 15) # None → saves beside input, returns Path
seg = mixing.Audio("song.mp3")[10:30] # object producers: build first…
seg.fade_in(2).save(output="clip.mp3") # …then save (or output=None returns Audio)
fade_in("in.mp3", output=lambda a: a.duration) # a callable sink receives the result
None → object producers return the in-memory object; file producers save
beside the input and return the Path.
- a file path → write there, return
Path.
- a directory → write an auto-named file inside, return
Path.
- a callable →
output(result) is returned (general escape hatch).
Functions that emit several artifacts qualify the destinations instead
(remove_fillers(media, output_dir=..., output_media=...)).
Sliceable media
Audio and Video are lazy, sliceable views:
from mixing.audio import Audio
from mixing.video import Video
Audio("song.mp3")[10:30].fade_out(3).save(output="end.mp3") # seconds by default
Audio("song.mp3", time_unit="samples")[0:44100] # 1s at 44.1kHz
Video("clip.mp4")[5:15].save(output="cut.mp4")
Video("clip.mp4")[100] # a single frame (np BGR)
with Video("clip.mp4") as v: dur = v.duration # context-managed
Optional extras & keys
pip install mixing[audio] (pydub/soundfile), [gen] (Veo), [llm] (aix
for chapter titles + SRT translation), [widget], [clipboard].
- ElevenLabs features (
mixing.transcript, mixing.dubbing) need
ELEVENLABS_API_KEY (or pass api_key=). Responses are cached on disk, so a
re-run of the same input is free and offline.
- Veo (
mixing.video.genai) needs Google Cloud auth (GOOGLE_CLOUD_PROJECT +
application-default or service-account credentials).
Gotchas
output=None for file producers writes next to the input — pass an
explicit output to control location.
- ElevenLabs/Veo calls cost money; rely on the disk cache and pass
cache=True (default for these) / refresh=True to force a re-call.
mixing.video.replace_audio(..., mix_ratio=): 1.0 = only the new audio,
0.0 = keep original, 0.5 = equal blend.
Source: thorwhalen/mixing — distributed by TomeVault.
1---2name: thorwhalen-mixing-mixing3description: mixing — router & core conventions4---56# mixing — router & core conventions78`mixing` edits audio/video from Python. This skill orients you; then jump to the9focused sub-skill for the medium you're working in.1011## Pick a sub-skill1213| You want to… | Sub-skill |14|---|---|15| Edit audio (slice, fade, crop, concat, overlay, normalize, align, segment) | **mixing-audio** |16| Edit video (slice, crop, loop, speed, replace/normalize audio, Ken Burns, concat, thumbnail, subtitles) | **mixing-video** |17| Transcribe speech, remove fillers, make SRT/prose, detect chapters | **mixing-transcript** |18| Re-voice / translate via text-to-speech | **mixing-dubbing** |1920## First moves (every task)21221. **Check ffmpeg** — most operations need it:23 ```python24 import mixing25 assert mixing.has_ffmpeg(), "install ffmpeg (brew install ffmpeg / apt install ffmpeg)"26 ```272. **Import lazily / specifically.** `import mixing` is cheap (no moviepy/opencv28 loaded until you touch a heavy name). Either use the facade29 (`mixing.Audio`, `mixing.Video`, `mixing.replace_audio`, …) or import the30 subpackage (`from mixing.audio import Audio`).313. **Know the `output` convention** (below) before calling anything that writes.3233## The `output` protocol (read this once, applies everywhere)3435Every function that produces a result takes **one** `output` argument:3637```python38crop_video("in.mp4", 5, 15, output="out.mp4") # write a file → returns Path39crop_video("in.mp4", 5, 15, output="clips/") # directory → auto-named file40crop_video("in.mp4", 5, 15) # None → saves beside input, returns Path41seg = mixing.Audio("song.mp3")[10:30] # object producers: build first…42seg.fade_in(2).save(output="clip.mp3") # …then save (or output=None returns Audio)43fade_in("in.mp3", output=lambda a: a.duration) # a callable sink receives the result44```4546- `None` → object producers return the in-memory object; file producers save47 beside the input and return the `Path`.48- a **file path** → write there, return `Path`.49- a **directory** → write an auto-named file inside, return `Path`.50- a **callable** → `output(result)` is returned (general escape hatch).5152Functions that emit *several* artifacts qualify the destinations instead53(`remove_fillers(media, output_dir=..., output_media=...)`).5455## Sliceable media5657`Audio` and `Video` are lazy, sliceable views:5859```python60from mixing.audio import Audio61from mixing.video import Video6263Audio("song.mp3")[10:30].fade_out(3).save(output="end.mp3") # seconds by default64Audio("song.mp3", time_unit="samples")[0:44100] # 1s at 44.1kHz65Video("clip.mp4")[5:15].save(output="cut.mp4")66Video("clip.mp4")[100] # a single frame (np BGR)67with Video("clip.mp4") as v: dur = v.duration # context-managed68```6970## Optional extras & keys7172- `pip install mixing[audio]` (pydub/soundfile), `[gen]` (Veo), `[llm]` (`aix`73 for chapter titles + SRT translation), `[widget]`, `[clipboard]`.74- ElevenLabs features (`mixing.transcript`, `mixing.dubbing`) need75 `ELEVENLABS_API_KEY` (or pass `api_key=`). Responses are cached on disk, so a76 re-run of the same input is free and offline.77- Veo (`mixing.video.genai`) needs Google Cloud auth (`GOOGLE_CLOUD_PROJECT` +78 application-default or service-account credentials).7980## Gotchas8182- `output=None` for file producers writes **next to the input** — pass an83 explicit `output` to control location.84- ElevenLabs/Veo calls cost money; rely on the disk cache and pass85 `cache=True` (default for these) / `refresh=True` to force a re-call.86- `mixing.video.replace_audio(..., mix_ratio=)`: `1.0` = only the new audio,87 `0.0` = keep original, `0.5` = equal blend.8889---90> Source: [thorwhalen/mixing](https://github.com/thorwhalen/mixing) — distributed by [TomeVault](https://tomevault.io).91<!-- tomevault:4.0:skill_md:2026-06-15 -->