Audio analysis + commercial-grade mastering DSP for C++, Python, and browsers. Apache-2.0, dependency-free, runs anywhere — including the browser via WebAssembly.
- Analysis — BPM, key, chord, beat, section, timbre, dynamics, pitch (librosa-compatible defaults; tens of times faster than librosa/Python).
- Mastering — EQ, dynamics, multiband, stereo, saturation, repair, maximizer, reference matching. 90+ DSP modules built around published standards (ITU-R BS.1770-4 loudness/true-peak, Vicanek biquads, ADAA nonlinearities, Lemire sliding max, polyphase FIR oversampling).
- One permissive license — Apache-2.0 across the entire stack. No LGPL/GPL surface, no proprietary algorithms, no SaaS dependencies.
npm install @libraz/libsonare # JavaScript / TypeScript (WASM, takes Float32Array)
pip install libsonare # Python (WAV/MP3 — see "Supported audio formats" for M4A/AAC)For Node.js with native file decoding, build
@libraz/libsonare-native from source:
cd bindings/node
yarn install
yarn build # auto-detects FFmpeg via pkg-config (WAV/MP3 if absent, +M4A/AAC/FLAC/OGG if present)To force a specific mode:
SONARE_FFMPEG=0 yarn build # explicitly disable FFmpeg
SONARE_FFMPEG=1 yarn build # require FFmpeg (fails if dev libs missing)@libraz/libsonare accepts decoded Float32Array samples — use the Web Audio
API or a JS decoder to obtain them. Mastering DSP is included in the default WASM build.
Analysis
import { init, detectBpm, detectKey, analyze } from '@libraz/libsonare';
await init();
const bpm = detectBpm(samples, sampleRate);
const key = detectKey(samples, sampleRate); // { name: "C major", confidence: 0.95 }
const result = analyze(samples, sampleRate);Mastering
import {
init,
masteringChain,
masteringChainStereo,
masteringPairAnalyze,
masteringPairProcess,
masteringPairProcessorNames,
masteringProcess,
masteringProcessorNames,
} from '@libraz/libsonare';
await init();
const mastered = masteringChain(samples, sampleRate, {
eq: { tiltDb: 1.0 },
dynamics: { compressor: { thresholdDb: -24, ratio: 1.5 } },
saturation: { tape: { driveDb: 1.0, saturation: 0.2 } },
loudness: { targetLufs: -14, ceilingDb: -1, truePeakOversample: 4 },
});
const stereo = masteringChainStereo(left, right, sampleRate, {
stereo: { imager: { width: 1.1 }, monoMaker: { amount: 0.2 } },
loudness: { targetLufs: -14, ceilingDb: -1, truePeakOversample: 4 },
});
// Apply a single named processor
const compressed = masteringProcess('dynamics.compressor', samples, sampleRate, {
thresholdDb: -24,
ratio: 1.5,
});
// Reference-based mastering
const matched = masteringPairProcess('match.abCrossfade', source, reference, sampleRate, {
mix: 0.25,
});
const loudnessJson = masteringPairAnalyze(
'match.referenceLoudness', source, reference, sampleRate,
);
// Discover available processors
masteringProcessorNames(); // ['dynamics.compressor', 'eq.parametric', ...]
masteringPairProcessorNames(); // ['match.abCrossfade', ...]Preset-driven mastering and the block-by-block streaming variant are also
exposed. WASM uses nested config for masteringChain /
StreamingMasteringChain, while masterAudio overrides use flat
dot-notation keys (mirroring the Node and Python overrides API):
// Mastering presets (one-shot) and streaming variant
import { masterAudio, masteringPresetNames, StreamingMasteringChain } from '@libraz/libsonare';
masteringPresetNames(); // ['pop', 'edm', 'acoustic', 'hipHop', 'aiMusic', 'speech']
const out = masterAudio(samples, sampleRate, 'aiMusic', { 'loudness.targetLufs': -13 });
const chain = new StreamingMasteringChain({ eq: { tiltDb: 0.5 } });
chain.prepare(48000, 512, 1);
const block = chain.processMono(new Float32Array(512));
chain.delete();pip install libsonare ships a WAV/MP3-only wheel (matching librosa / pydub /
soundfile conventions). For M4A/AAC/FLAC/OGG either pre-convert with external
ffmpeg, or rebuild from source with FFmpeg linked:
SONARE_FFMPEG=1 pip install libsonare --no-binary libsonare
# requires system FFmpeg dev libs: brew install ffmpeg / apt install libavformat-dev libavcodec-dev libavutil-dev libswresample-devimport libsonare
audio = libsonare.Audio.from_file("song.mp3")
print(f"BPM: {audio.detect_bpm()}, Key: {audio.detect_key()}")
# Mastering chain — returns MasteringResult(samples, sample_rate,
# input_lufs, output_lufs, applied_gain_db, latency_samples)
result = audio.mastering(target_lufs=-14.0, ceiling_db=-1.0)
print(f"{result.input_lufs:.1f} LUFS → {result.output_lufs:.1f} LUFS "
f"(gain {result.applied_gain_db:+.2f} dB)")
# Single processor / reference matching
compressed = libsonare.mastering_process(
"dynamics.compressor", samples, sample_rate=44100,
params={"thresholdDb": -24, "ratio": 1.5},
)
loudness_json = libsonare.mastering_pair_analyze(
"match.referenceLoudness", source, reference, sample_rate=44100,
)
# Discover available processors
libsonare.mastering_processor_names() # ['dynamics.compressor', ...]
libsonare.mastering_pair_processor_names() # ['match.abCrossfade', ...]
# Preset-based chain (one-shot) + streaming
libsonare.mastering_preset_names() # ['pop', 'edm', 'acoustic', 'hipHop', 'aiMusic', 'speech']
result = libsonare.master_audio(samples, sample_rate=sr, preset='aiMusic',
overrides={'loudness.targetLufs': -13})
with libsonare.StreamingMasteringChain({'eq.tilt.tiltDb': 0.5}) as chain:
chain.prepare(44100, 512, 1)
out = chain.process_mono([0.0] * 512)pip install libsonare
# Analysis
sonare analyze song.mp3
# > Estimated BPM : 161.00 BPM (conf 75.0%)
# > Estimated Key : C major (conf 100.0%)
sonare bpm song.mp3 --json
# Mastering
sonare mastering song.wav -o mastered.wav --target-lufs -14
sonare mastering-processor song.wav --processor dynamics.compressor \
--params thresholdDb=-24,ratio=1.5
sonare mastering-pair-analyze source.wav --reference reference.wav \
--analysis match.referenceLoudness#include <sonare/sonare.h> // analysis + features + effects
#include <sonare/mastering/master.h> // mastering chain & processors
auto audio = sonare::Audio::from_file("music.mp3");
auto result = sonare::MusicAnalyzer(audio).analyze();
std::cout << "BPM: " << result.bpm
<< ", Key: " << result.key.to_string() << std::endl;| Music | Spectral / Pitch | Streaming |
|---|---|---|
| BPM / Tempo | STFT / iSTFT | Real-time analyzer |
| Key Detection | Mel Spectrogram | Incremental BPM |
| Beat Tracking | MFCC | Incremental key |
| Chord Recognition | Chroma | Onset events |
| Section Detection | CQT / VQT | |
| Timbre / Dynamics | Spectral Features | |
| Pitch (YIN / pYIN) | Onset Detection |
| Dynamics | EQ | Multiband / Stereo |
|---|---|---|
| Compressor | Parametric / Graphic | Multiband comp / EQ / limiter |
| Limiter / Brickwall | Linear / Minimum phase | Stereo imager / M-S |
| Expander / Gate | Dynamic EQ | Haas / phase align |
| De-esser | Pultec / API style | Mono maker / compat |
| Transient shaper | Tilt / shelving |
| Saturation / Repair | Maximizer / Match | Building blocks |
|---|---|---|
| Tape / Tube / Transformer | True-peak limiter (ITU-R BS.1770-4) | Polyphase FIR oversampler |
| Exciter / Bitcrusher | Loudness optimizer (LUFS target) | ADAA nonlinearities |
| Declick / Declip / Decrackle | Adaptive release | Vicanek biquad design |
| Denoise / Dereverb / Dehum | Reference EQ / loudness / spectrum | Partitioned convolver |
Mastering is built by default (BUILD_MASTERING=ON). Disable with
cmake -DBUILD_MASTERING=OFF to ship analysis-only builds.
Dramatically faster than Python-based alternatives. Parallelized analysis with automatic CPU detection, optimized HPSS with multi-threaded median filter. Mastering processors use ITU-spec polyphase oversampling, antiderivative anti-aliasing (ADAA), and SIMD-friendly Eigen GEMM for hot paths.
See Benchmarks for detailed comparisons.
Default parameters match librosa:
- Sample rate: 22050 Hz
- n_fft: 2048, hop_length: 512, n_mels: 128
- fmin: 0.0, fmax: sr/2
| Format | Default¹ | With FFmpeg² | WASM (@libraz/libsonare) |
|---|---|---|---|
| WAV (PCM 16/24/32, float32) | ✅ | ✅ | n/a (samples in) |
| MP3 | ✅ | ✅ | n/a |
| M4A / AAC / FLAC / OGG / Opus / WMA / ... | ❌ (clear error message) | ✅ | n/a (use Web Audio API) |
¹ Default: PyPI wheel (pip install libsonare) and source builds where FFmpeg
dev libs are not present. PyPI wheels are deterministically pinned to this mode
so installation never depends on the user's libavformat.
² With FFmpeg: source build with FFmpeg linked. CMake auto-detects via
pkg-config (-DSONARE_WITH_FFMPEG=AUTO, the default for make build), and you
can force on/off with -DSONARE_WITH_FFMPEG=ON/OFF. Python equivalent:
SONARE_FFMPEG=1 pip install libsonare --no-binary libsonare. Node native:
SONARE_FFMPEG=1 yarn build.
WASM does not bundle a file decoder by design; pass Float32Array samples obtained from
the Web Audio API or another JS decoder.
# Native (auto-detects FFmpeg; mastering on by default)
make build && make test
# Analysis-only (smaller binary)
cmake -B build -DBUILD_MASTERING=OFF && cmake --build build
# WebAssembly (mastering included)
make wasm
# Release (optimized)
make release