Documentation
¶
Overview ¶
Package tactus estimates the tempo (BPM) of audio from mono PCM using only the Go standard library — no cgo, no external DSP dependencies.
Scope ¶
tactus does one thing: it maps a mono PCM signal to a BPM value. It deliberately does not decode audio files. Callers are expected to decode their container of choice, mix to mono ((L+R)/2 for stereo), normalise samples to the range [-1, 1], and pass the resulting slice together with the sample rate.
Entry points ¶
- Estimate folds the estimate into the default home octave of 90–180 BPM.
- EstimateWith folds into a caller-chosen home octave via Options — a single global setting (like a DJ tool's BPM range), not a per-track hint.
- EstimateCandidates and EstimateCandidatesWith return the estimate together with its half- and double-time alternates, each scored, so a caller can offer a halve/double control where the perceived tempo is ambiguous.
- EstimateAllCandidates and EstimateAllCandidatesWith return every distinct tempo the estimator considered, including non-octave metrical relatives, for a richer picker.
All return ErrTooShort when the input is too short to measure, and ErrNoBeat when it is long enough but carries no detectable beat. Each Candidate reports a Confidence in [0, 1] a caller can gate on.
Algorithm ¶
The estimator builds a spectral-flux onset detection function: a short-time Fourier transform (via a self-contained radix-2 FFT) whose per-frame positive changes in log-magnitude spectrum are summed into an onset signal. Note attacks — which fall on beats — spike this signal, whereas sustained tones and bass do not, so its autocorrelation is far cleaner than a raw energy envelope's. The onset signal is resampled to a fixed rate and its autocorrelation is swept across a wide band; each local peak marks a candidate periodicity.
Autocorrelation alone is metrically ambiguous: a signal is also self-similar at half, double, and 3:2/4:3 harmonics of its beat. Two steps resolve the octave without any per-track tuning. Each peak is first folded into the home octave by repeated halving or doubling, which collapses pure half/double errors. The surviving candidates are then scored by harmonic support — a candidate's own autocorrelation plus a weighted share from its half- and double-tempo neighbours. The true beat is periodic at those octaves too, so it outscores a lone 3:2 or 4:3 spurious peak.
Concurrency ¶
All functions are pure and re-entrant: they read only their arguments and allocate their own scratch buffers, so independent calls are safe to run concurrently.
Index ¶
- Variables
- func Estimate(mono []float64, sampleRate int) (float64, error)
- func EstimateWith(mono []float64, sampleRate int, opt Options) (float64, error)
- type Candidate
- func EstimateAllCandidates(mono []float64, sampleRate int) ([]Candidate, error)
- func EstimateAllCandidatesWith(mono []float64, sampleRate int, opt Options) ([]Candidate, error)
- func EstimateCandidates(mono []float64, sampleRate int) ([]Candidate, error)
- func EstimateCandidatesWith(mono []float64, sampleRate int, opt Options) ([]Candidate, error)
- type Options
Constants ¶
This section is empty.
Variables ¶
var ErrNoBeat = errors.New("tactus: no detectable beat")
ErrNoBeat is returned when the input is long enough but carries no detectable beat — a flat or silent onset function with no periodicity to lock onto. Callers may treat this differently from ErrTooShort (e.g. flag the track for review rather than skipping it).
var ErrTooShort = errors.New("tactus: audio too short to estimate")
ErrTooShort is returned when the input has too few samples to cover the search range (its onset function cannot span the slowest tempo's period twice).
Functions ¶
func Estimate ¶
Estimate estimates the tempo, in BPM, of mono PCM (samples in the range [-1, 1]) sampled at sampleRate Hz, folding the result into the default home octave of 90–180 BPM. Stereo sources must be mixed to mono by the caller ((L+R)/2). It returns ErrTooShort when the input is too short to measure.
func EstimateWith ¶
EstimateWith is like Estimate but folds the result into the home octave given by opt (see Options). It searches autocorrelation over a wide band, then resolves the octave: each periodicity peak is folded into the home octave and the candidate with the strongest half/double harmonic support wins. This lands on the true beat rather than a half, double, or 3:2/4:3 metrical harmonic, without any per-track tuning.
Types ¶
type Candidate ¶
Candidate is one tempo hypothesis.
Support is the harmonic support relative to the primary estimate (whose Support is 1.0) — useful for ranking the alternates against each other. Confidence is the absolute periodic strength at this tempo: the normalised autocorrelation at its beat period, in [0, 1]. A caller can gate on the primary's Confidence to decide whether to trust the estimate or flag the track for review.
func EstimateAllCandidates ¶
EstimateAllCandidates is like EstimateCandidates but returns every distinct tempo the estimator considered. See EstimateAllCandidatesWith.
func EstimateAllCandidatesWith ¶
EstimateAllCandidatesWith returns every distinct tempo hypothesis the estimator folded into the home octave, ordered by descending Support, with the primary estimate first. Unlike EstimateCandidatesWith (which gives only the octave alternates), this includes non-octave metrical relatives (a 3:2 or 4:3 reading), letting a caller build a richer "pick the tempo" UI. Support is relative to the primary and Confidence is the absolute periodic strength; see Candidate.
func EstimateCandidates ¶
EstimateCandidates is like Estimate but also returns the octave alternates of the chosen tempo. See EstimateCandidatesWith.
func EstimateCandidatesWith ¶
EstimateCandidatesWith returns the primary tempo estimate followed by its half- and double-time alternates, ordered [primary, half, double]. Each carries its harmonic support relative to the primary (primary = 1.0). The half-time almost always rivals the primary — octave relatives are equally periodic, which is exactly why perceived tempo (tactus) is ambiguous — so the support does not, and cannot, decide between them; the double is typically weaker. Surface a DJ-style halve/double control and let the user make the final octave choice rather than guessing it.
The primary's Support is 1.0 except in the degenerate case of a non-positive primary support (a beatless input that still cleared the length guard), where the raw support values are returned as-is.
type Options ¶
Options selects the home octave that estimates are folded into. The effective window is [MinBPM, 2*MinBPM): MinBPM sets the low edge and the octave spans upward from it (MaxBPM is retained for validation and backward compatibility). Zero values fall back to the default home octave [90, 180). Set MinBPM to match a collection's tempo centre — e.g. MinBPM=75 for a 75–95 BPM library — the way DJ software exposes a single global BPM range. This is not a per-track hint.