Output & formats
Output JSON shape
Section titled “Output JSON shape”In json format, each input file produces <nameNoExt>.json in the output directory. 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 JSON.stringify(peaks) — the raw array, one line per file — to stdout and writes no file. It ignores bpm and markers entirely. Use it for piping or inlining into another build step:
# 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]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) |
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.
- Output path is
<output-dir or input-dir>/<nameNoExt>.json. With--output, the directory is created recursively up front. Without it, each JSON is written next to its source audio file.