first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
# chromagram
## Purpose
`chromagram.ts` folds a spectral frame into the twelve pitch classes used by Western tonal analysis. Each magnitude bin is mapped to a pitch class via `pitchClass = round(12 * log2(freq / A4)) mod 12` and accumulated into a 12-element `Float32Array`. The chromagram is the shared substrate for both key-finding and chord template matching in this package.
The module relies on the vendored real FFT in `./yin.ts` so the chromagram path stays self-contained and does not require `@suaveplan/audio-analysis-spectrum` at runtime.
## Features
- `chromagramFromFrame(frame, sampleRate, minFreq?)` — per-frame 12-bin pitch-class profile.
- `normaliseChroma(chroma)` — in-place max-normalisation; idempotent on all-zero vectors.
- `accumulateChroma(acc, add)` — in-place sum, clamped to the shorter length so callers can pass differently sized buffers safely.
## Usage: Basic
```ts
import { chromagramFromFrame } from "@suaveplan/audio-analysis-pitch/algorithms/chromagram";
const chroma = chromagramFromFrame(frame, 48_000);
console.log("strongest pitch class:", chroma.indexOf(Math.max(...chroma)));
```
## Usage: Advanced
```ts
import {
accumulateChroma,
chromagramFromFrame,
normaliseChroma,
} from "@suaveplan/audio-analysis-pitch/algorithms/chromagram";
async function aggregateChroma(stream: AsyncIterable<Float32Array>, sr: number) {
const acc = new Float32Array(12);
for await (const frame of stream) {
accumulateChroma(acc, chromagramFromFrame(frame, sr));
}
normaliseChroma(acc);
return acc;
}
```
## API Reference
- `chromagramFromFrame(frame: Float32Array, sampleRate: number, minFreq = 30): Float32Array` (length 12).
- `normaliseChroma(chroma: Float32Array): Float32Array` — in-place, returns the same buffer.
- `accumulateChroma(acc: Float32Array, add: Float32Array): Float32Array` — clamped to `min(acc.length, add.length)`.
## Implementation Notes
Frames shorter than eight samples short-circuit to an all-zero chromagram. Energy below `minFreq` (default 30 Hz) and at or above the Nyquist is dropped to suppress hum and aliasing. All operations are O(N) over the FFT magnitude vector. Buffers are caller-owned — the module never allocates on the hot path beyond the result.