Documentation
¶
Overview ¶
Package mixedkey detects the musical key of audio from mono PCM using only the Go standard library — no cgo, no external DSP dependencies.
Scope ¶
mixedkey does one thing: it maps a mono PCM signal to a musical key. 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.
The result is a Key — a tonic pitch class and a mode — whose canonical string form is Camelot wheel notation (e.g. "8A" for A minor, "8B" for C major), the notation DJs use for harmonic mixing. mixedkey is the key-detection companion to a tempo estimator such as tactus (github.com/cabbagekobe/tactus): the same mono-PCM-in contract, a different musical attribute out.
Entry points ¶
- Detect and DetectWith return the single best key. DetectWith takes Options to choose the correlation profile.
- DetectCandidates and DetectCandidatesWith return the estimate with its most common confusions (relative, dominant, subdominant), each scored, so a caller can offer a "did it pick the right one?" control where the tonal centre is ambiguous.
- DetectAllCandidates and DetectAllCandidatesWith return all 24 keys ranked, for a richer picker.
All return ErrTooShort when the input is too short to analyse, and ErrNoKey when it is long enough but carries no detectable key (a flat or silent chroma). Each Candidate reports a Confidence in [0, 1] a caller can gate on.
Algorithm ¶
The detector builds a 12-bin chroma vector (pitch-class profile) from the audio: a short-time Fourier transform (via a self-contained radix-2 FFT) whose magnitude bins, over a musical frequency band, are summed into the twelve pitch classes across every frame. Each frame is L1-normalised first, so loud sections do not dominate, and the mapping is re-centred by the track's average deviation from equal temperament, so a master tuned off A=440 does not smear into neighbouring pitch classes. The chroma reflects which pitch classes carry the most energy in the track.
That vector is correlated against each of the 24 major and minor key profiles — the Aarden–Essen corpus weights by default, rotated to each tonic — and the key with the highest Pearson correlation wins. Other published profiles (Krumhansl, Temperley, Bellman–Budge) are selectable via Options. The correlation doubles as an absolute Confidence, and the alternates are ranked by their correlation relative to the winner's.
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
- type Candidate
- func DetectAllCandidates(mono []float64, sampleRate int) ([]Candidate, error)
- func DetectAllCandidatesWith(mono []float64, sampleRate int, opt Options) ([]Candidate, error)
- func DetectCandidates(mono []float64, sampleRate int) ([]Candidate, error)
- func DetectCandidatesWith(mono []float64, sampleRate int, opt Options) ([]Candidate, error)
- type Key
- type Mode
- type Options
- type PitchClass
- type Profile
Constants ¶
This section is empty.
Variables ¶
var ErrNoKey = errors.New("mixedkey: no detectable key")
ErrNoKey is returned when the input is long enough but carries no detectable key: a flat or silent chroma with no pitch class standing out, so no profile correlates with it. Callers may treat this differently from ErrTooShort (e.g. flag an atonal or percussion-only track rather than skipping it).
var ErrTooShort = errors.New("mixedkey: audio too short to detect")
ErrTooShort is returned when the input has too few samples to yield a single analysis frame — its chroma vector cannot be built.
Functions ¶
This section is empty.
Types ¶
type Candidate ¶
Candidate is one key hypothesis.
Support is this candidate's correlation relative to the primary estimate (whose Support is 1.0) — useful for ranking alternates against each other. Confidence is the absolute fit of this key: the chroma-to-profile correlation clamped to [0, 1]. A caller can gate on the primary's Confidence to decide whether to trust the detection or flag the track for review.
func DetectAllCandidates ¶
DetectAllCandidates is like DetectCandidates but returns every key the detector considered. See DetectAllCandidatesWith.
func DetectAllCandidatesWith ¶
DetectAllCandidatesWith returns all 24 keys (12 tonics × major and minor) ordered by descending Support, with the primary estimate first. Unlike DetectCandidatesWith (which curates the common confusions), this exposes the full ranking for a richer "pick the key" UI. Support is relative to the primary and Confidence is absolute; see Candidate.
func DetectCandidates ¶
DetectCandidates is like Detect but also returns the key's principal alternates. See DetectCandidatesWith.
func DetectCandidatesWith ¶
DetectCandidatesWith returns the primary key estimate followed by its three most common confusions, ordered [primary, relative, dominant, subdominant]. The relative key shares the primary's pitch-class content (the classic major/minor ambiguity); the dominant and subdominant differ from it by a single note. Each carries its Support relative to the primary and its absolute Confidence, so a caller can offer a "did it pick the right one?" control rather than trusting a single answer where the tonal centre is ambiguous.
type Key ¶
type Key struct {
Tonic PitchClass
Mode Mode
}
Key is a musical key: a tonic pitch class together with a mode. Its canonical string form is Camelot notation (see Camelot and String), the wheel used for DJ harmonic mixing.
func Detect ¶
Detect detects the musical key of mono PCM (samples in the range [-1, 1]) sampled at sampleRate Hz, using the default Aarden–Essen profile. Stereo sources must be mixed to mono by the caller ((L+R)/2). It returns ErrTooShort when the input is too short to analyse and ErrNoKey when it is long enough but carries no detectable key.
func DetectWith ¶
DetectWith is like Detect but uses the profile selected by opt (see Options). It builds a 12-bin chroma vector from the audio and picks the key whose pitch-class profile correlates best with it.
type Options ¶
type Options struct {
Profile Profile
}
Options selects the pitch-class profile used for correlation. The zero value uses ProfileAardenEssen, which gave the best harmonically- compatible agreement on a real multi-key corpus; ProfileBellmanBudge and ProfileTemperley edge it out on exact match. Profile is a single global setting — like choosing a detector, not a per-track hint.
type PitchClass ¶
type PitchClass int
PitchClass is a pitch class in [0, 12) with C = 0, C#/Db = 1, …, B = 11.
type Profile ¶
type Profile int
Profile selects the pair of pitch-class templates (one major, one minor) that a chroma vector is correlated against to pick a key. It is a single global setting on Options; the zero value is ProfileKrumhansl.
const ( // ProfileAardenEssen uses the Aarden–Essen profiles, fitted to the // large Essen folk-song corpus. It is the default (the zero value) // because on a 273-track multi-key rekordbox-labelled corpus it gave // the best harmonically-compatible agreement (85%: exact, relative, or // an adjacent Camelot key) — its errors tend to be a neighbouring key // that still mixes, which suits harmonic mixing. For maximum exact // agreement instead, ProfileBellmanBudge and ProfileTemperley score a // few points higher on exact match. ProfileAardenEssen Profile = iota // ProfileKrumhansl uses the Krumhansl–Kessler key profiles derived // from the classic probe-tone experiments. Well-documented, but the // weakest of the four on recorded popular music (~49% exact). ProfileKrumhansl // ProfileTemperley uses the Temperley–Kostka–Payne profiles, fitted to // a corpus of common-practice music. Strong on exact match (~62%). ProfileTemperley // ProfileBellmanBudge uses the Bellman–Budge profiles, fitted to a // corpus of common-practice harmony. Best on exact match (~64%) on the // reference corpus. ProfileBellmanBudge )