Files
skills/docs/media/audio/audio-analysis-pitch/src/algorithms/key-finding.md
2026-05-29 08:20:56 +01:00

2.2 KiB
Raw Blame History

key-finding

Purpose

key-finding.ts correlates an aggregated chromagram against 24 major/minor key profiles to estimate the musical key of a passage. Two profile sets ship: the classic Krumhansl-Schmuckler perceptual weights and Temperley's revised weights which reduce relative-major/minor confusion.

The estimator returns the best-scoring key together with a confidence derived from the margin between the top and runner-up correlation, giving callers a usable proxy for how decisive the result was.

Features

  • 12 tonics × 2 modes = 24 candidate keys scored per call.
  • Two interchangeable profile sets via the profileSet argument.
  • Pearson correlation as the scoring function — robust to multiplicative gain.
  • Stable pitch-class naming via KEY_PROFILE_NAMES.
  • Numerically safe: zero-denom Pearson collapses to zero rather than NaN.

Usage: Basic

import { detectKey } from "@suaveplan/audio-analysis-pitch/algorithms/key-finding";

const chroma = new Float32Array([6.35, 2.23, 3.48, 2.33, 4.38, 4.09, 2.52, 5.19, 2.39, 3.66, 2.29, 2.88]);
const { key, confidence } = detectKey(chroma);
console.log(key); // "C major"

Usage: Advanced

import { detectKey, type KeyProfileSet } from "@suaveplan/audio-analysis-pitch/algorithms/key-finding";

function compareProfiles(chroma: Float32Array) {
    const sets: KeyProfileSet[] = ["krumhansl-schmuckler", "temperley"];
    return sets.map((set) => ({ set, ...detectKey(chroma, set) }));
}

API Reference

  • type KeyProfileSet = "krumhansl-schmuckler" | "temperley".
  • interface KeyEstimate { key: string; mode: "major" | "minor"; tonic: number; confidence: number }.
  • detectKey(chroma: Float32Array, profileSet?: KeyProfileSet): KeyEstimate.
  • KEY_PROFILE_NAMES: readonly string[] — ["C", "C#", "D", …].

Implementation Notes

Profiles are rotated to each tonic with O(1) lookup; for 24 candidates this is 24 Pearson computations per call, each O(12). The confidence is the normalised margin (best - second) / max(|best|, ε) clamped to [0, 1]. If the chromagram has zero variance the Pearson denominator collapses to zero and the function returns C major with zero confidence rather than NaN.