@libraz/libcantus - v1.1.0
    Preparing search index...

    Function detectKey

    • Rank major and minor keys by how well a set of pitch classes is distributed like each key.

      The input becomes a weighted pitch-class distribution, and every one of the 24 candidates is scored by the Pearson correlation between that distribution and its key profile rotated onto the candidate tonic (see DetectKeyOptions.profile). Correlating against a profile rather than counting scale members is what tells a key apart from its relative: C major and A minor contain the same seven pitch classes, so a membership count can only separate them by a tie-break, while their profiles expect the weight on different degrees.

      Choosing the minor variant is deliberately not part of the ranking. A minor candidate is ranked once, on the minor profile, which already expects some weight on the raised sixth and seventh; only then does it report whichever of the natural, harmonic and melodic masks covers the most input weight, and fit is measured against that mask. That split is why a harmonic-minor cadence ranks its own tonic first without the harmonic variant having to win a scoring contest against the natural one.

      DetectKeyOptions.modes adds the church modes to the same contest, one candidate per mode per tonic, each ranked on a profile derived from its parallel major or minor key. A modal winner reports the mode in scaleName and still reports the key it leans on in mode. The option is off by default, so a caller that does not ask for modes gets the ranking of the 24 keys it has always got.

      Ties are broken by fit descending, then tonic pitch class ascending, then by scale — major, the minor forms, then the modes — so the order is fully determined by the input rather than by the order candidates happen to be built in. Returns all 24 keys ranked best-first, plus 12 more per enabled mode, or an empty array for an empty input (mirroring detectChord).

      DetectKeyOptions.explain adds a rationale to every candidate and, to the winner, the runners-up it beat with the link of that tie-break chain that separated each. It is off by default; see the option for why.

      Parameters

      • pitches: readonly number[]

        MIDI pitches or bare pitch classes.

      • opts: DetectKeyOptions = {}

        How to weigh the input, which profile to rank with, and whether the modes take part; see DetectKeyOptions.

      Returns KeyMatch[]

      Ranked key interpretations (empty for an empty input), each carrying a finite score in [-1, 1].

      When weights does not have one entry per pitch, profile is not a known name or a valid pair of 12-entry vectors, or modes names something that is not a church mode.

      When a pitch is outside the MIDI domain or the input exceeds the budget.

      import { detectKey } from '@libraz/libcantus';
      const keys = detectKey([60, 62, 64, 65, 67, 69, 71]); // C major scale
      keys[0].mode; // 'major', with keys[0].key.rootPc === 0
      keys[1].mode; // 'minor' — on 9: A minor, the relative, ranked just below
      const riff = detectKey([62, 62, 65, 69, 71, 69, 65, 62], { modes: true });
      riff[0].scaleName; // 'dorian', on rootPc 2, still reported as mode 'minor'