bpm-analyzer

Crates.iobpm-analyzer
lib.rsbpm-analyzer
version0.2.0
created_at2026-01-20 20:43:19.305636+00
updated_at2026-01-20 22:46:16.711984+00
descriptionA simple library for calculating the BPM
homepagehttps://github.com/not-jan/bpm-analyzer
repositoryhttps://github.com/not-jan/bpm-analyzer
max_upload_size
id2057531
size201,021
(not-jan)

documentation

https://docs.rs/bpm-analyzer/latest/bpm_analyzer/

README

BPM Analyzer

A real-time BPM (beats per minute) detection library and application for Rust, using wavelet decomposition and autocorrelation analysis.

Based on Audio Analysis using the Discrete Wavelet Transform by George Tzanetakis, Georg Essl and Perry Cook.

How It Works

The BPM analyzer uses a sophisticated multi-stage signal processing pipeline:

  1. Audio Capture: Captures audio from system input or loopback devices
  2. Resampling: Downsamples to 22.05 kHz for efficient processing
  3. Wavelet Decomposition: Applies 4-level Daubechies D4 wavelet transform to separate frequency bands
  4. Onset Detection: Extracts amplitude envelopes from each band using full-wave rectification and low-pass filtering
  5. Beat Detection: Identifies individual beats using onset strength analysis
  6. Autocorrelation: Computes autocorrelation on the summed envelopes to find periodic patterns
  7. Peak Detection: Identifies BPM candidates based on autocorrelation peaks within the specified range

Installation

Add this to your Cargo.toml:

[dependencies]
bpm-analyzer = "0.1"

Usage

As a Library

use bpm_analyzer::{AnalyzerConfig, begin};

fn main() -> Result<(), bpm_analyzer::Error> {
    // Configure the analyzer
    let config = AnalyzerConfig::builder()
        .min_bpm(60.0)
        .max_bpm(180.0)
        .build();

    // Start the analyzer with default device
    let bpm_receiver = begin(config)?;

    // Process BPM detections
    for detection in bpm_receiver.iter() {
        if let Some(bpm) = detection.bpm() {
            println!("Detected BPM: {:.1}", bpm);
        }
        
        // Access beat timings
        for beat in detection.beat_timings() {
            println!("Beat at {:.2}s (strength: {:.2})", beat.time_seconds, beat.strength);
        }
        
        // Get the interval between last two beats
        if let Some(interval) = detection.last_beat_interval() {
            println!("Last beat interval: {:.3}s", interval);
        }
    }

    Ok(())
}

Selecting a Specific Audio Device

use bpm_analyzer::{AnalyzerConfig, begin_with_device, list_audio_devices, get_device_by_name};

// List all available devices
let devices = list_audio_devices()?;
for device in &devices {
    println!("{} {}", device.name, if device.is_default { "(default)" } else { "" });
}

// Use a specific device by name
let config = AnalyzerConfig::electronic();
let device = get_device_by_name("BlackHole 2ch")?;
let receiver = begin_with_device(config, &device)?;

for detection in receiver.iter() {
    if let Some(bpm) = detection.bpm() {
        println!("Detected BPM: {:.1}", bpm);
    }
}

As a Standalone Application

Run the GUI application:

cargo run --release --features bin

With custom BPM range:

cargo run --release --features bin -- --min-bpm 80 --max-bpm 160

Command-Line Options

  • -m, --min-bpm <MIN_BPM>: Minimum BPM to detect (default: 40)
  • -M, --max-bpm <MAX_BPM>: Maximum BPM to detect (default: 240)

Configuration

AnalyzerConfig

The analyzer behavior can be customized using AnalyzerConfig:

let config = AnalyzerConfig::builder()
    .min_bpm(40.0)           // Minimum BPM to detect
    .max_bpm(240.0)          // Maximum BPM to detect
    .window_size(65536)      // Analysis window size (must be power of 2)
    .queue_size(4096)        // Audio queue size
    .buffer_size(256)        // Audio capture buffer size
    .build();

Parameters

  • min_bpm / max_bpm: Define the range of tempos to detect. Narrowing this range can improve accuracy for specific genres.
  • window_size: Size of the analysis window in samples. Larger windows provide better frequency resolution but slower response.
  • queue_size: Size of the inter-thread audio queue. I don't think you need to change this.
  • buffer_size: Audio capture buffer size. Smaller values reduce latency but may increase CPU usage. I'm also not convinced that this needs to be changed.

Audio Setup

macOS

For system audio capture, install BlackHole:

brew install blackhole-2ch

Then configure your system to route audio through BlackHole using a multi-output device.

Dependencies

This library builds on several excellent Rust crates:

  • fundsp - Audio processing framework
  • osclet - Wavelet transform library
  • cpal - Cross-platform audio I/O
  • resampler - Audio resampling

Limitations

  • Tempo Changes: The analyzer works best with consistent tempo. Sudden tempo changes may take time to adapt.
  • Polyrhythmic Music: Complex polyrhythmic patterns may produce multiple strong candidates.
  • Very Slow/Fast Tempos: Accuracy may decrease outside the 40-240 BPM range.
Commit count: 2

cargo fmt