BPM Detection

Tempo (BPM) detection for audio tracks.

This module provides tempo estimation using librosa’s beat tracking algorithms, with genre-aware corrections for electronic music production.

Example

>>> import numpy as np
>>> from mixref.detective.tempo import detect_bpm
>>>
>>> # Generate 120 BPM test signal
>>> sr = 44100
>>> audio = np.random.randn(2, sr * 10)  # 10 seconds stereo
>>>
>>> result = detect_bpm(audio, sr)
>>> print(f"BPM: {result.bpm:.1f} (confidence: {result.confidence:.2f})")
BPM: 120.0 (confidence: 0.95)
class mixref.detective.tempo.TempoResult[source]

Bases: object

Result of tempo detection.

bpm

Detected beats per minute (tempo).

Type:

float

confidence

Confidence score from 0.0 to 1.0. Higher values indicate more reliable detection.

Type:

float

onset_strength

Raw onset strength envelope used for detection. Useful for debugging or visualization.

Type:

Any | None

bpm: float
confidence: float
onset_strength: Any | None = None
__init__(bpm, confidence, onset_strength=None)
mixref.detective.tempo.detect_bpm(audio, sample_rate, start_bpm=120.0, include_onset_strength=False)[source]

Detect tempo (BPM) of an audio signal.

Uses librosa’s beat tracking algorithm with onset detection. Returns BPM and confidence score.

Parameters:
  • audio (Any) – Audio signal as numpy array. Shape: (samples,) for mono or (samples, channels) for multi-channel. Also accepts (channels, samples) format. Multi-channel audio will be converted to mono for analysis.

  • sample_rate (int) – Sample rate in Hz (e.g., 44100).

  • start_bpm (float) – Initial tempo estimate in BPM for the algorithm. Default: 120.0 (common for electronic music).

  • include_onset_strength (bool) – If True, include onset strength envelope in result. Useful for visualization or debugging. Default: False to save memory.

Returns:

TempoResult with detected BPM, confidence score, and optionally onset data.

Raises:

ValueError – If audio is empty or invalid.

Return type:

TempoResult

Example

>>> import numpy as np
>>> from mixref.detective.tempo import detect_bpm
>>>
>>> # Synthetic 140 BPM techno kick pattern
>>> sr = 44100
>>> duration = 8  # seconds
>>> audio = np.zeros(sr * duration)
>>>
>>> # Add kicks every 0.428 seconds (140 BPM)
>>> kick_interval = int(60 / 140 * sr)
>>> for i in range(0, len(audio), kick_interval):
...     if i + 100 < len(audio):
...         audio[i:i+100] = 0.8
>>>
>>> result = detect_bpm(audio, sr)
>>> assert 135 < result.bpm < 145  # Should be near 140