# pitch

> Named scales from @audio-ui/engine: the steps of each scale, the pitch classes a scale on a root keeps, whether a pitch is in it and the nearest one that is, and the scale as a piano roll reads it.

```bash
npm install @audio-ui/engine
```

```ts
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`](https://audio.js.org/modules/mir-key/), [`@audio/note`](https://audio.js.org/modules/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:

```ts
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`](/docs/core/piano-roll): 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:

```tsx
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`](/docs/engine/analysis#a-recording-as-it-comes-in) times the seconds it plays. One part alone is often ambiguous, as a bass line is: pass every part of the song.

```ts
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`](/docs/core).
