Library API
Library API
Section titled “Library API”The package exports exactly one function — generatePeaks — re-exported from lib/index.js.
import { generatePeaks } from '@arraypress/waveform-gen';
const { peaks, bpm } = await generatePeaks('./song.mp3', { samples: 1800, // optional — library now defaults to 1800 (matches the CLI) precision: 2, detectBPM: true,});
// peaks → number[] normalized 0..1 (loudest = 1.0)// bpm → number | nullgeneratePeaks(filePath, options?)
Section titled “generatePeaks(filePath, options?)”(filePath: string, options?: { samples?: number; precision?: number; detectBPM?: boolean }) => Promise<{ peaks: number[]; bpm: number | null }>
Reads and decodes the file, extracts max-amplitude peaks per bin across all channels, normalizes so the loudest peak is 1.0, and rounds to precision.
| Option | Default | Type | Description |
|---|---|---|---|
samples |
1800 |
number | Number of peaks. Falsy (0 / undefined) falls back to 1800 via options.samples || 1800. |
precision |
2 |
number | Decimal places. >= 0 rounds; < 0 returns unrounded normalized peaks. |
detectBPM |
false |
boolean | When true, also run BPM detection and return bpm (number | null). |
Returns a Promise resolving to { peaks, bpm }. bpm is null unless detectBPM is true and a stable tempo was found.
import { generatePeaks } from '@arraypress/waveform-gen';import { writeFile } from 'node:fs/promises';
const result = await generatePeaks('./track.wav', { samples: 1800 });
// Build the same JSON shape the CLI writesconst output = { peaks: result.peaks };if (result.bpm != null) output.bpm = result.bpm;
await writeFile('./waveforms/track.json', JSON.stringify(output, null, 2) + '\n');BPM detection
Section titled “BPM detection”BPM is energy/onset based (window 2048, hop 1024): it detects transients, buckets the intervals into tempos 60 < bpm < 200, picks the most common, applies octave-doubling/halving correction, and subtracts a -1 calibration offset. It returns:
- an integer when a stable tempo is found,
nullwhen there are fewer than 2 onsets or no bucket matched.
It’s a heuristic, not exact metadata — verify critical values. When null, the CLI omits bpm from the JSON entirely.
Feeding the player
Section titled “Feeding the player”The generated JSON is consumed by the player and bar via attributes (no WaveformGen-specific attributes exist — these belong to the consuming libraries):
<div data-waveform-player data-url="song.mp3" data-waveform="waveforms/song.json"></div>Or via JS — see Options:
new WaveformPlayer(el, { url: 'song.mp3', waveform: 'waveforms/song.json', }); <div data-wb-play data-url="song.mp3" data-wb-waveform="waveforms/song.json"></div>See WaveformBar.
When a player is given pre-generated peaks, it skips client-side decoding entirely and renders the waveform immediately. If the JSON also carries bpm and markers, those flow through to the player’s tempo display and marker track.
Build-pipeline usage
Section titled “Build-pipeline usage”Batch a folder into a waveforms/ directory
Section titled “Batch a folder into a waveforms/ directory”waveform-gen ./public/audio/ --recursive --output ./public/waveforms/ --samples 1800 --bpmEvery supported file under ./public/audio/ becomes ./public/waveforms/<name>.json with peaks, detected bpm, and any sidecar markers.
npm script
Section titled “npm script”{ "scripts": { "waveforms": "waveform-gen ./src/audio/ --recursive --output ./public/waveforms/ --samples 1800 --bpm --quiet", "prebuild": "npm run waveforms" }}--quiet keeps CI logs clean; wiring it to prebuild regenerates peaks before every production build.
Programmatic batch with a manifest
Section titled “Programmatic batch with a manifest”When you need more control — a manifest, custom file names, or merged metadata — drive the library directly:
import { generatePeaks } from '@arraypress/waveform-gen';import { readdir, writeFile, mkdir } from 'node:fs/promises';import { join, basename, extname } from 'node:path';
const SRC = './src/audio';const OUT = './public/waveforms';const SAMPLES = 1800; // match the CLI default explicitly
await mkdir(OUT, { recursive: true });
const manifest = {};for (const file of await readdir(SRC)) { if (!/\.(mp3|wav|flac|ogg)$/i.test(file)) continue;
const name = basename(file, extname(file)); try { const { peaks, bpm } = await generatePeaks(join(SRC, file), { samples: SAMPLES, precision: 2, detectBPM: true, });
const output = { peaks }; if (bpm != null) output.bpm = bpm;
await writeFile(join(OUT, `${name}.json`), JSON.stringify(output, null, 2) + '\n'); manifest[file] = { waveform: `waveforms/${name}.json`, bpm }; } catch (err) { console.error(`[build] ${file}: ${err.message}`); // don't abort the batch }}
await writeFile(join(OUT, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\n');