A small suite of audio-processing command-line utilities. The first tool,
scatter, generates a single audio file of a configurable length by scattering
copies of weighted input clips across a silent timeline (e.g. an ambient
soundscape where some sounds should appear more often than others).
- Python >= 3.12
- uv
ffmpegandffprobeonPATH
config/scatter.example.json references three placeholder clips that aren't
checked into the repo. Generate quick synthetic ones with ffmpeg to try it out:
mkdir -p clips
ffmpeg -y -f lavfi -i "sine=frequency=440:duration=2" -ar 44100 -ac 2 clips/beep.wav
ffmpeg -y -f lavfi -i "sine=frequency=880:duration=1" -ar 22050 -ac 1 clips/blip.wav
ffmpeg -y -f lavfi -i "sine=frequency=220:duration=3" -codec:a libmp3lame clips/hum.mp3
uv sync
uv run audio-scatter --config config/scatter.example.json--config defaults to ./config.json if omitted.
{
"clips": [
{ "path": "clips/laugh.wav", "weight": 3 },
{ "path": "clips/ding.wav", "weight": 1 },
{ "path": "clips/whoosh.mp3", "weight": 5 }
],
"gap_seconds": { "min": 5, "max": 20 },
"final_length_seconds": 300,
"output_path": "output.wav",
"sample_rate": 44100,
"channels": 2
}| Field | Required | Notes |
|---|---|---|
clips | yes | non-empty list of {path, weight} |
clips[].path | yes | resolved relative to the config file's own directory if not absolute |
clips[].weight | yes | positive number; determines selection probability at each placement slot, not a fixed time budget |
gap_seconds.min / .max | yes | random silence gap (seconds) inserted before each placed clip |
final_length_seconds | yes | total output duration |
output_path | yes | same relative-path rule as clip paths |
sample_rate | no | default 44100 |
channels | no | default 2; must be 1 or 2 (mono/stereo only) |
Placed clips never overlap. Placement stops as soon as a chosen clip would not
fit before final_length_seconds; the remaining tail is left as silence. If the
very first candidate clip doesn't fit at all, the output is pure silence of the
requested length (not an error).
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Invalid configuration |
| 2 | Clip error (missing file, unreadable/non-audio file) |
| 4 | Render or unexpected I/O failure |
- Only mono/stereo output (
channelsmust be1or2). - Each placement opens its own ffmpeg input, even when the same source file is
reused many times. This is simple and fine for realistic use (dozens/hundreds
of placements) but is bounded by the OS open-file-descriptor limit
(
ulimit -n) at very large placement counts.
uv sync
uv run pytest