Output & formats
Output JSON shape
Section titled “Output JSON shape”In json format, each input file produces <nameNoExt>.json — next to the audio, or under --output at the same relative path it had inside its input directory (see File resolution). The object is pretty-printed (2-space indent) with a trailing newline. Key order is always peaks, then bpm, then markers:
{ "peaks": [0.2, 0.37, 0.41, 0.55, 1.0, 0.82], "bpm": 120, "markers": [ { "time": 0, "label": "Intro" }, { "time": 30, "label": "Chorus" } ]}| Key | Always present? | Shape | Notes |
|---|---|---|---|
peaks |
Yes | number[] |
Normalized amplitudes, 0..1, loudest peak is exactly 1.0. Length = --samples. |
bpm |
Only with --bpm and a detected tempo |
number |
Integer. Omitted entirely if detection returns null. |
markers |
Only if a sidecar file exists | { time: number, label: string }[] |
time in seconds. Omitted if no markers parsed. |
How peaks are computed
Section titled “How peaks are computed”- Decode the file (
audio-decode) into channel data. - Bin the samples into
samplesequal-width buckets. - Scan every frame in each bucket — unlike the live player, which skips ~9/10 frames for real-time speed — taking
max(|max|, |min|)per bucket so no transient is missed. - Mono-sum across channels: each peak is the max amplitude across all channels for that bucket.
- Normalize by dividing every peak by the loudest, so the maximum is exactly
1.0. - Round to
precisiondecimals (precision >= 0). A negativeprecisionreturns unrounded normalized peaks.
inline format
Section titled “inline format”--format inline prints peaks to stdout and writes no file. It ignores bpm and markers entirely. Use it for piping or inlining into another build step. A single file argument prints the raw array:
# Capture peaks into a variable / filewaveform-gen song.mp3 --format inline > song.peaks.json
# → [0.2,0.37,0.41,0.55,1,0.82]Several files, or any directory input, print one JSON object keyed by each file’s path relative to the working directory (forward slashes). Files that fail are left out — their errors go to stderr (2.0.0+; earlier versions printed one unlabelled array per file):
waveform-gen audio/ --format inline# → {"audio/intro.mp3":[0.2,0.37,…],"audio/outro.mp3":[0.1,0.44,…]}Markers (sidecar files)
Section titled “Markers (sidecar files)”Markers are auto-detected — there is no flag. For song.mp3, WaveformGen looks for song.markers.txt (basename without extension + .markers.txt) in the same directory as the audio.
# Lines starting with # are ignored0:00 Intro0:30 Verse 11:15 Chorus1:02:30 Bridge- Line format:
<timestamp> <label>— the first run of whitespace splits timestamp from label. - Timestamps:
SS,MM:SS, orH:MM:SS. All are converted to seconds. - Blank lines and lines starting with
#are ignored. - Invalid /
NaNtimestamps are silently skipped.
The parsed markers land in the JSON’s markers array and feed the player’s marker rendering — see Markers.
Supported audio formats
Section titled “Supported audio formats”| Format | Decodes? | Picked up as input? |
|---|---|---|
| MP3 | ✅ | ✅ |
| WAV | ✅ | ✅ |
| FLAC | ✅ | ✅ |
| OGG | ✅ | ✅ |
| M4A | ❌ | ✅ (then errors) |
| AAC | ❌ | ✅ (then errors) |
A supported file that won’t decode is reported as the file appears to be corrupt or unreadable, and a file that decodes to no audio at all is an error rather than an empty {"peaks": []} (2.0.0+).
File resolution
Section titled “File resolution”- Each positional path is resolved. Files are kept if their extension is a known audio type. Directories are scanned.
- Directories are scanned top level only unless
--recursiveis passed. - The final file list is de-duplicated.
- Without
--output, each JSON is written next to its source audio file. - With
--output, a file argument writes<output-dir>/<nameNoExt>.json. A file found by scanning a directory keeps its path relative to that directory:waveform-gen audio/ --recursive --output out/turnsaudio/a/intro.wavintoout/a/intro.json. Folders are created as needed (2.0.0+; earlier versions flattened everything into<output-dir>). - Two inputs that map to the same JSON in one run —
song.mp3andsong.wavin one folder, or two same-named file arguments with--output— don’t overwrite each other: the later one fails with a message naming the first (matched case-insensitively). Rename one of them.