Skip to content

Output & formats

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.
  1. Decode the file (audio-decode) into channel data.
  2. Bin the samples into samples equal-width buckets.
  3. 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.
  4. Mono-sum across channels: each peak is the max amplitude across all channels for that bucket.
  5. Normalize by dividing every peak by the loudest, so the maximum is exactly 1.0.
  6. Round to precision decimals (precision >= 0). A negative precision returns unrounded normalized peaks.

--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:

Terminal window
# Capture peaks into a variable / file
waveform-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):

Terminal window
waveform-gen audio/ --format inline
# → {"audio/intro.mp3":[0.2,0.37,…],"audio/outro.mp3":[0.1,0.44,…]}

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.

song.markers.txt
# Lines starting with # are ignored
0:00 Intro
0:30 Verse 1
1:15 Chorus
1:02:30 Bridge
  • Line format: <timestamp> <label> — the first run of whitespace splits timestamp from label.
  • Timestamps: SS, MM:SS, or H:MM:SS. All are converted to seconds.
  • Blank lines and lines starting with # are ignored.
  • Invalid / NaN timestamps are silently skipped.

The parsed markers land in the JSON’s markers array and feed the player’s marker rendering — see Markers.

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+).

  • 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 --recursive is 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/ turns audio/a/intro.wav into out/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.mp3 and song.wav in 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.