DSPark 1.8.0
Header-only C++20 DSP for real-time and offline audio
Loading...
Searching...
No Matches
dspark::KeyDetector< T > Class Template Referencefinal

Key estimation by profile correlation over an accumulated chroma. More...

#include <KeyDetector.h>

Classes

struct  Key
 One key estimate. More...
 

Public Types

enum class  Profile : std::uint8_t { KrumhanslKessler = 0 , Temperley }
 Which published key profile to correlate against. More...
 

Public Member Functions

void prepare (const AudioSpec &spec, int windowSize=0)
 Prepares the analysis pipeline.
 
void reset () noexcept
 Clears the accumulated chroma and forgets the estimate.
 
void setProfile (Profile p) noexcept
 Selects the key profile (default KrumhanslKessler).
 
Profile getProfile () const noexcept
 
int getWindowSize () const noexcept
 
void processBlock (AudioBufferView< const T > buffer) noexcept
 Feeds a block (channels averaged to mono). Allocation-free.
 
void pushSamples (std::span< const T > samples) noexcept
 Feeds mono samples directly. Allocation-free.
 
Key getKey () const noexcept
 
const std::array< T, 12 > & chroma () const noexcept
 The accumulated chroma the estimate was formed from.
 
std::uint64_t getFrameCount () const noexcept
 

Static Public Member Functions

static int getKeyName (const Key &key, char *dest, int size) noexcept
 Writes a key name ("C", "F#m", "Bbm") into dest.
 

Static Public Attributes

static constexpr std::array< double, 12 > kKrumhanslKesslerMajor
 Krumhansl & Kessler (1982) probe-tone ratings, major.
 
static constexpr std::array< double, 12 > kKrumhanslKesslerMinor
 Krumhansl & Kessler (1982) probe-tone ratings, minor.
 
static constexpr std::array< double, 12 > kTemperleyMajor
 Temperley (1999) revised profiles, major.
 
static constexpr std::array< double, 12 > kTemperleyMinor
 Temperley (1999) revised profiles, minor.
 

Detailed Description

template<FloatType T>
class dspark::KeyDetector< T >

Key estimation by profile correlation over an accumulated chroma.

Threading: getKey() is the atomic foreign-thread publication. chroma() is a stream-owner reference readout and is valid only on the thread that owns processBlock() / pushSamples(), between that thread's own calls. Calling chroma() from another thread while processing runs would race the non-atomic chroma storage.

Template Parameters
TSample type (float or double).

Definition at line 92 of file KeyDetector.h.

Member Enumeration Documentation

◆ Profile

template<FloatType T>
enum class dspark::KeyDetector::Profile : std::uint8_t
strong

Which published key profile to correlate against.

The two are different objects, not two spellings of one:

  • KrumhanslKessler: the probe-tone ratings of Krumhansl and Kessler (1982), listed as the "K-S model" profiles in Temperley, "Bayesian Models of Musical Structure and Cognition", Musicae Scientiae 8(2) (2004), Table 1. Perceptual data, unequal spacing, and the widest spread between diatonic and chromatic degrees.
  • Temperley: the revised profiles of Temperley, "What's Key for Key? The Krumhansl-Schmuckler Key-Finding Algorithm Reconsidered", Music Perception 17(1) (1999), Figure 4, reprinted as the "CBMS model" column of Temperley 2004 Table 1. Hand-adjusted rather than measured, with every chromatic degree flattened to one value and the leading tone raised.

Beware the name in other software: several widely used libraries label their "Temperley" profile with the corpus-derived Kostka-Payne probabilities of Temperley, "Music and Probability" (2007), which are a different vector entirely. The values here are the 1999 ones the architecture's citation names.

Enumerator
KrumhanslKessler 

Krumhansl & Kessler 1982 probe-tone ratings.

Temperley 

Temperley 1999 revised profiles.

Definition at line 118 of file KeyDetector.h.

Member Function Documentation

◆ chroma()

template<FloatType T>
const std::array< T, 12 > & dspark::KeyDetector< T >::chroma ( ) const
inlinenoexcept

The accumulated chroma the estimate was formed from.

Twelve bins, index 0 = C, normalized to sum to 1 so it can be compared against a profile directly and does not grow with programme length. That normalization is a property of this readout and of nothing else: the correlation behind getKey() is computed from the unnormalized accumulator, and scaling a chroma cannot move a Pearson correlation. All zeros before the first frame is accumulated.

stream-owner reference readout: the reference is to state owned by the thread that calls processBlock()/pushSamples(), and it is valid on that thread only, between that thread's own calls. A caller on any other thread would be reading these words while the owning thread writes them. getKey() is the readout for any other thread.

Definition at line 297 of file KeyDetector.h.

◆ getFrameCount()

template<FloatType T>
std::uint64_t dspark::KeyDetector< T >::getFrameCount ( ) const
inlinenoexcept
Returns
Analysis frames accumulated since prepare()/reset().

Definition at line 300 of file KeyDetector.h.

◆ getKey()

template<FloatType T>
Key dspark::KeyDetector< T >::getKey ( ) const
inlinenoexcept
Returns
The current key estimate. Lock-free and safe from any thread.

confidence is the numerical top-two margin, (top - numericalSecond) / top, clamped to [0, 1]. It answers one question – how far ahead the winner is – and deliberately not "is this the right key": a passage that is genuinely between two keys reports a low margin whether or not the winner is correct, and a short excerpt that happens to fit one profile well reports a high one.

runnerUpPitchClass and runnerUpMinor are always numerical rank two of the same 24-score ordering as the winner. Exact score ties follow the fixed candidate order C major through B major, then C minor through B minor. relativeAlternativePitchClass, relativeAlternativeMinor and relativeAlternativeRank separately name the winner's relative key and its actual 1-based numerical rank. This keeps estimator rank and a useful relative-key policy visible as two different facts.

The clamp is not cosmetic. The numerical runner-up correlation can be negative, in which case the raw ratio exceeds 1; and where the winning correlation is itself zero or negative – silence, noise, or a chroma with no tonal structure at all – the ratio has no meaning and the confidence is reported as 0 rather than as whatever the division produced.

Definition at line 276 of file KeyDetector.h.

◆ getKeyName()

template<FloatType T>
static int dspark::KeyDetector< T >::getKeyName ( const Key &  key,
char *  dest,
int  size 
)
inlinestaticnoexcept

Writes a key name ("C", "F#m", "Bbm") into dest.

Parameters
keyKey to name.
destDestination buffer.
sizeCapacity of dest (8+ recommended).
Returns
Characters written, excluding the terminator.

Spelling follows the framework's own convention for the tonic, so a key whose tonic is usually written flat is written flat.

Definition at line 312 of file KeyDetector.h.

◆ getProfile()

template<FloatType T>
Profile dspark::KeyDetector< T >::getProfile ( ) const
inlinenoexcept
Returns
The profile in effect.

Definition at line 200 of file KeyDetector.h.

◆ getWindowSize()

template<FloatType T>
int dspark::KeyDetector< T >::getWindowSize ( ) const
inlinenoexcept
Returns
The analysis window (samples) in effect – the automatic choice if prepare() was given windowSize <= 0, the clamped explicit request otherwise. The estimate updates every windowSize/2 samples; it is an accumulation, so it also keeps improving for as long as material is fed.

Definition at line 212 of file KeyDetector.h.

◆ prepare()

template<FloatType T>
void dspark::KeyDetector< T >::prepare ( const AudioSpec &  spec,
int  windowSize = 0 
)
inline

Prepares the analysis pipeline.

Parameters
specAudio environment specification.
windowSizeAnalysis window in samples, passed straight to the shared chroma front end. Values <= 0 (the default) select the AUTOMATIC window: the smallest power of two in [1024, 16384] spanning at least 4096/48000 s (~85 ms) at spec.sampleRate – 4096 at 44.1/48 kHz, 8192 at 88.2/96 kHz, 16384 at 176.4/192 kHz. Explicit values are clamped to [1024, 16384].

The register the estimate is trustworthy in is the front end's, because the chroma is the front end's. At the automatic window the Hann main lobe stays at or below 46.9 Hz for every rate up to 192 kHz, which keeps notes from about F#3 to E5 resolved; below that floor adjacent-semitone leakage adds pitch classes that were never played, and above MIDI 83 (B5) there are no bins at all, so an octave-6 melody contributes nothing. A key estimate is a sum over those bins and inherits every one of those limits. See ChordDetector::prepare() for the measured register table.

Definition at line 159 of file KeyDetector.h.

◆ processBlock()

template<FloatType T>
void dspark::KeyDetector< T >::processBlock ( AudioBufferView< const T >  buffer)
inlinenoexcept

Feeds a block (channels averaged to mono). Allocation-free.

The block is handed to the front end in chunks no longer than one hop, so every analysis frame it produces is seen: frames are exactly one hop apart, and a chunk that long can contain at most one of them.

Definition at line 223 of file KeyDetector.h.

◆ pushSamples()

template<FloatType T>
void dspark::KeyDetector< T >::pushSamples ( std::span< const T >  samples)
inlinenoexcept

Feeds mono samples directly. Allocation-free.

Definition at line 237 of file KeyDetector.h.

◆ reset()

template<FloatType T>
void dspark::KeyDetector< T >::reset ( )
inlinenoexcept

Clears the accumulated chroma and forgets the estimate.

Allocation-free, but it rewrites the stream state: call it from the thread that owns the stream (or while processing is stopped), not concurrently with processBlock()/pushSamples().

Definition at line 176 of file KeyDetector.h.

◆ setProfile()

template<FloatType T>
void dspark::KeyDetector< T >::setProfile ( Profile  p)
inlinenoexcept

Selects the key profile (default KrumhanslKessler).

Callable from any thread; it takes effect at the next analysis frame, like every other parameter in the framework. It does not disturb the accumulated chroma, so switching profiles re-reads the same evidence.

Definition at line 194 of file KeyDetector.h.

Member Data Documentation

◆ kKrumhanslKesslerMajor

template<FloatType T>
constexpr std::array<double, 12> dspark::KeyDetector< T >::kKrumhanslKesslerMajor
staticconstexpr
Initial value:
{
6.35, 2.23, 3.48, 2.33, 4.38, 4.09, 2.52, 5.19, 2.39, 3.66, 2.29, 2.88
}

Krumhansl & Kessler (1982) probe-tone ratings, major.

Index 0 is the tonic, ascending chromatically. Verified digit for digit against Temperley, Musicae Scientiae 8(2) (2004), Table 1, column "K-S model / major", which reprints them; the same twelve numbers appear again in that paper's worked example of the Krumhansl-Schmuckler model.

Definition at line 345 of file KeyDetector.h.

◆ kKrumhanslKesslerMinor

template<FloatType T>
constexpr std::array<double, 12> dspark::KeyDetector< T >::kKrumhanslKesslerMinor
staticconstexpr
Initial value:
{
6.33, 2.68, 3.52, 5.38, 2.60, 3.53, 2.54, 4.75, 3.98, 2.69, 3.34, 3.17
}

Krumhansl & Kessler (1982) probe-tone ratings, minor.

Same source and column "K-S model / minor". Note the two features that make the mode decidable at all: the raised third of the relative major (index 4) drops to 2.60 while the minor third (index 3) carries 5.38, and the subtonic (index 10, 3.34) outranks the leading tone (index 11, 3.17), which is the natural-minor scale asserting itself over the harmonic one.

Definition at line 357 of file KeyDetector.h.

◆ kTemperleyMajor

template<FloatType T>
constexpr std::array<double, 12> dspark::KeyDetector< T >::kTemperleyMajor
staticconstexpr
Initial value:
{
5.0, 2.0, 3.5, 2.0, 4.5, 4.0, 2.0, 4.5, 2.0, 3.5, 1.5, 4.0
}

Temperley (1999) revised profiles, major.

From "What's Key for Key? The Krumhansl-Schmuckler Key-Finding Algorithm Reconsidered", Music Perception 17(1), Figure 4; reprinted as the "CBMS model / major" column of Temperley 2004 Table 1. Every chromatic degree sits at 2.0 except the subtonic at 1.5, and the leading tone is raised to 4.0 – the deliberate departure from the measured ratings.

Definition at line 369 of file KeyDetector.h.

◆ kTemperleyMinor

template<FloatType T>
constexpr std::array<double, 12> dspark::KeyDetector< T >::kTemperleyMinor
staticconstexpr
Initial value:
{
5.0, 2.0, 3.5, 4.5, 2.0, 4.0, 2.0, 4.5, 3.5, 2.0, 1.5, 4.0
}

Temperley (1999) revised profiles, minor.

Same source, minor panel. It assumes the HARMONIC minor scale, so the flat sixth (index 8) is diatonic at 3.5 and the natural sixth (index 9) is chromatic at 2.0 – the opposite of the Krumhansl-Kessler minor ordering, and the reason the two profiles disagree most on modal material.

Definition at line 381 of file KeyDetector.h.


The documentation for this class was generated from the following file: