pnpm add @audio-ui/engine
import { cents, midiToHz, detectKeys, inScale, nearestInScale, /* … */ } from "@audio-ui/engine/pitch";| Used by | arrangement, use-pitch, use-synth-voices, voice-pool |
|---|---|
| audio.js | @audio/mir-key, @audio/note |
| Export | Kind | What it is |
|---|---|---|
cents | function | A pitch's frequency, A4 at 440 Hz unless told. |
midiToHz | function | A pitch's frequency, A4 at 440 Hz unless told. |
detectKeys | function | The keys notes are likeliest in, likeliest first, at most count: each pitch class weighed by how long it sounds, against the major and minor key profiles of @audio/mir-key (Krumhansl-Schmuckler). |
KeyNote | interface | What a key is read from: a note's pitch and how long it lasts, in any unit. |
inScale | function | Whether a MIDI pitch is in a scale. |
nearestInScale | function | The pitch in a scale nearest pitch: itself when it is in it, else the nearest one, the lower of two as near. |
rollScale | function | A scale as a piano roll reads it: its pitch classes and its root, what @audio-ui/core/piano-roll takes. |
SCALES | const | The steps of each scale, as semitones above the root. |
Scale | interface | A scale on a root: C major is { name: "major", root: 0 }. |
ScaleName | type | The name of a scale of SCALES. |
scalePitchClasses | function | The pitch classes a scale keeps, from 0 (C) to 11 (B), in order. |
Usage
Pitches are MIDI note numbers, 60 being middle C. A Scale is a name of SCALES on a root pitch class, 0 being C:
import { inScale, nearestInScale, rollScale, scalePitchClasses } from "@audio-ui/engine/pitch";
const aMinor = { name: "minor", root: 9 };
scalePitchClasses(aMinor); // [0, 2, 4, 5, 7, 9, 11]
inScale(61, aMinor); // false: C# is not in A minor
nearestInScale(61, aMinor); // 60: of two as near, the lower
rollScale(aMinor); // { pitchClasses: [0, 2, 4, 5, 7, 9, 11], root: 9 }SCALES holds the steps of each scale as semitones above its root: chromatic, the major and minor scales (natural, harmonic, melodic), the two pentatonics, the blues, the whole-tone scale and the modes (dorian, phrygian, lydian, mixolydian, locrian). The tables and the snapping are @audio/note's, with the three modes it does not list added.
On a piano roll
A piano roll knows a scale only as the pitch classes it keeps and its root, a RollScale: it shades the rows out of the scale, folds onto it and fits notes into it without knowing any scale by name. rollScale(scale) gives a named scale in that shape:
const scale = useMemo(() => rollScale({ name: "dorian", root: 2 }), []);
<PianoRoll.Root scale={scale} />;Keys
detectKeys(notes, count, audio) gives the keys a set of notes is likeliest in, likeliest first, at most count (3 unless told): each pitch class weighed by how long it sounds against the major and minor key profiles of @audio/mir-key. audio adds the sound of recordings, a weight per pitch class such as an audioChroma times the seconds it plays. One part alone is often ambiguous, as a bass line is: pass every part of the song.
import { detectKeys } from "@audio-ui/engine/pitch";
detectKeys(notes.map(({ pitch, length, muted }) => ({ pitch, length, muted })));
// [{ name: "minor", root: 9 }, { name: "major", root: 0 }, …]Notes and frequencies
midiToHz(pitch) is a pitch's frequency, A4 at 440 Hz unless told, and cents(frequency) the nearest note to a frequency and how far off it is: { midi, name, hz, cents }, name with its octave, as "A#4", and cents positive when sharp. Both are @audio/note's.
A pitch's name and whether it is a black key are the keyboard's own: pitchName, pitchClassName and isBlackKey are in @audio-ui/core.