Files
2026-05-27 23:29:53 +01:00

172 lines
5.6 KiB
Markdown

# OpenSpec Change: enterprise-audio-analysis-pitch
- **Change ID**: enterprise-audio-analysis-pitch
- **Package**: `@suaveplan/audio-analysis-pitch`
- **Category**: universal
- **Target path**: `packages/media/audio/audio-analysis-pitch`
- **Status**: PROPOSED
- **Date**: 2026-05-14
- **Design**: `./design.md`
- **Spec delta**: `./specs/audio-analysis-pitch/spec.md`
---
## Summary
Deliver `@suaveplan/audio-analysis-pitch` on the genesis branch from scratch at enterprise quality. Pitch, key, and chord detection plugin for the universal audio analysis framework. Detects fundamental frequency, MIDI note numbers, musical key, and chord progressions from `AudioDocument` streams using YIN/McLeod pitch methods and chromagram-based key-finding.
Runs identically in browser and server via `BlockProcessor` or direct stream iteration.
---
## Current-state audit (genesis)
The package does not exist on genesis yet — this is a fresh build. No prior source, no test results, no coverage data.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/audio-core` (`workspace:^`) | `AudioDocument`, `BlockProcessor`, `BlockContext` |
| `@suaveplan/audio-analysis-spectrum` (`workspace:^`) | FFT, chromagram (optional — can include own FFT) |
| `@suaveplan/error` (`workspace:^`) | `AudioError` base class; `AUDIO_ANALYSIS.*` codes |
| `@suaveplan/types` (`workspace:^`) | `LoggerLike` |
| `@suaveplan/opentelemetry` (`workspace:^`) | OTel spans, counters, histograms |
| `zod` (`catalog:forms`) | Zod schemas for option types |
Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check`, `mitata` (all `catalog:testing`).
---
## Public API
### Core types
```ts
interface PitchDetectorOptions {
algorithm?: 'yin' | 'mcleod' | 'cepstrum' // default 'yin'
minFreq?: number // default 20
maxFreq?: number // default 20000
}
interface PitchResult extends AnalysisResult {
pitch: number // fundamental frequency (Hz)
midiNote: number // MIDI note number
confidence: number // 0..1
}
interface KeyDetectorOptions {
profile?: 'krumhansl-schmuckler' | 'temperley' // default 'krumhansl-schmuckler'
}
interface KeyResult extends AnalysisResult {
key: string // e.g. "C major", "F# minor"
confidence: number
chromagram: Float32Array // 12-bin pitch class distribution
}
interface Chord {
timestamp: number
duration: number
root: string
quality: string // "major", "minor", "dim", etc.
confidence: number
}
interface ChordResult extends AnalysisResult {
chords: Chord[]
}
class PitchDetector implements AnalysisOp {
readonly name = 'pitch-detector'
analyze(doc: AudioDocument, opts?: PitchDetectorOptions): Promise<PitchResult>
analyzeStream(doc: AudioDocument, opts?: PitchDetectorOptions): AsyncGenerator<PitchResult>
analyzeBlock(block: Float32Array[], ctx: BlockContext): PitchResult
}
class KeyDetector implements AnalysisOp {
readonly name = 'key-detector'
analyze(doc: AudioDocument, opts?: KeyDetectorOptions): Promise<KeyResult>
analyzeStream(doc: AudioDocument, opts?: KeyDetectorOptions): AsyncGenerator<KeyResult>
}
class ChordDetector implements AnalysisOp {
readonly name = 'chord-detector'
analyze(doc: AudioDocument): Promise<ChordResult>
analyzeStream(doc: AudioDocument): AsyncGenerator<ChordResult>
}
```
---
## New additions for genesis
1. **Three pitch algorithms** — YIN, McLeod pitch method, cepstrum; selectable per detection call.
2. **Key detection** — Krumhansl-Schmuckler and Temperley profiles on aggregated chromagram.
3. **Chord progression** — template matching on per-frame chromagram with smoothing.
4. **Streaming refinement** — pitch/key confidence improves as more audio is processed.
5. **OTel instrumentation** — spans per detector, counters, histograms.
6. **Property-based tests** — sine waves at known frequencies yield exact pitch, silence yields no pitch.
7. **Benchmark suite** — analyze 1-minute track for pitch, key, and chords.
---
## File layout
```text
packages/media/audio/audio-analysis-pitch/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
detector/
pitch-detector.ts
pitch-detector.md
pitch-detector.types.ts
pitch-detector.test.ts
key-detector.ts
key-detector.md
key-detector.types.ts
key-detector.test.ts
chord-detector.ts
chord-detector.md
chord-detector.types.ts
chord-detector.test.ts
index.ts
algorithms/
yin.ts
yin.md
yin.test.ts
chromagram.ts
chromagram.md
chromagram.test.ts
key-finding.ts
key-finding.md
key-finding.test.ts
chord-matching.ts
chord-matching.md
chord-matching.test.ts
index.ts
__benchmarks__/
pitch.bench.ts
tests/
integration.test.ts
property.test.ts
```
---
## Impact
**Breaking changes**: none — fresh build, package does not yet exist on genesis.
**Prerequisites**: `@suaveplan/audio-core`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/opentelemetry` published to Verdaccio.
### Affected dependents
No dependents in this batch (verified by `bunx turbo ls --filter=...@suaveplan/audio-analysis-pitch`). `@suaveplan/audio-analysis-pitch` is a leaf Analysis Op plugin — no genesis package depends on it at the time of this change. It is consumed at runtime by application/timeline code registering it as an analysis op (e.g. `@suaveplan/audio-plugins-fx` auto-tune reading `PitchResult`, a React player surfacing `KeyResult.key`, `@suaveplan/audio-cli` batch analysis); those consumers land in later changes and are created against this package's spec delta, not retrofitted.