Initial Commit
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
# OpenSpec Change: enterprise-audio-analysis-beat
|
||||
|
||||
- **Change ID**: enterprise-audio-analysis-beat
|
||||
- **Package**: `@suaveplan/audio-analysis-beat`
|
||||
- **Category**: universal
|
||||
- **Target path**: `packages/media/audio/audio-analysis-beat`
|
||||
- **Status**: PROPOSED
|
||||
- **Date**: 2026-05-14
|
||||
- **Design**: `./design.md`
|
||||
- **Spec delta**: `./specs/audio-analysis-beat/spec.md`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Deliver `@suaveplan/audio-analysis-beat` on the genesis branch from scratch at enterprise quality. Beat and tempo detection plugin for the universal audio analysis framework. Detects BPM, beat locations, and onset times from `AudioDocument` streams using energy-based and spectral-flux algorithms, with optional comb-filter tempo estimation and beat-grid alignment.
|
||||
|
||||
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. The old `enterprise-browser-audio-engine` openspec was browser-only and did not include beat detection.
|
||||
|
||||
---
|
||||
|
||||
## Available workspace deps on genesis
|
||||
|
||||
| Package | Role |
|
||||
|---|---|
|
||||
| `@suaveplan/audio-core` (`workspace:^`) | `AudioDocument`, `BlockProcessor`, `BlockContext` |
|
||||
| `@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 BeatDetectorOptions {
|
||||
minBpm?: number // default 60
|
||||
maxBpm?: number // default 200
|
||||
algorithm?: 'energy' | 'spectral-flux' | 'comb' // default 'energy'
|
||||
}
|
||||
|
||||
interface BeatResult extends AnalysisResult {
|
||||
bpm: number
|
||||
beats: Float64Array // beat timestamps (seconds)
|
||||
onsets: Float64Array // onset timestamps (seconds)
|
||||
confidence: number // 0..1
|
||||
}
|
||||
|
||||
class BeatDetector implements AnalysisOp {
|
||||
readonly name = 'beat-detector'
|
||||
readonly description = 'Detect tempo and beat locations'
|
||||
|
||||
constructor(options?: BeatDetectorOptions)
|
||||
|
||||
analyze(doc: AudioDocument, opts?: BeatDetectorOptions): Promise<BeatResult>
|
||||
analyzeStream(doc: AudioDocument, opts?: BeatDetectorOptions): AsyncGenerator<BeatResult>
|
||||
analyzeBlock(block: Float32Array[], ctx: BlockContext): BeatResult
|
||||
}
|
||||
```
|
||||
|
||||
### Error codes
|
||||
|
||||
```ts
|
||||
type BEATErrorCode =
|
||||
| 'AUDIO_ANALYSIS_BEAT.INVALID_OPTIONS'
|
||||
| 'AUDIO_ANALYSIS_BEAT.EMPTY_DOCUMENT'
|
||||
| 'AUDIO_ANALYSIS_BEAT.NO_BEATS_DETECTED'
|
||||
|
||||
const AUDIO_ANALYSIS_BEAT_ERROR_METADATA: Readonly<Record<BEATErrorCode, CodeMetadata>>
|
||||
function registerAudioAnalysisErrorCodes(): void
|
||||
```
|
||||
|
||||
### Zod schemas
|
||||
|
||||
```ts
|
||||
BeatDetectorOptionsSchema
|
||||
BeatResultSchema
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## New additions for genesis
|
||||
|
||||
1. **Three algorithms** — energy ODF (fast, from existing block stats), spectral flux (FFT-based, higher precision), comb-filter tempo estimation.
|
||||
2. **Beat grid alignment** — phase-align detected onsets to a regular grid after tempo is known.
|
||||
3. **Streaming refinement** — `analyzeStream()` yields partial results that refine as more audio is processed.
|
||||
4. **Block-level real-time** — `analyzeBlock()` returns instantaneous onset detection for visualization.
|
||||
5. **OTel instrumentation** — span `audio.analysis.beat`, counter `audio.analysis.beat.runs_total`, histogram `audio.analysis.beat.duration_ms`.
|
||||
6. **Property-based tests** — synthetic metronome input yields exact BPM, silence yields no beats with graceful fallback.
|
||||
7. **Benchmark suite** — analyze 1-minute track, analyze 10-minute track.
|
||||
|
||||
---
|
||||
|
||||
## File layout
|
||||
|
||||
```text
|
||||
packages/media/audio/audio-analysis-beat/
|
||||
package.json
|
||||
tsconfig.json
|
||||
vitest.config.ts
|
||||
src/
|
||||
index.ts
|
||||
error-codes.ts
|
||||
detector/
|
||||
beat-detector.ts
|
||||
beat-detector.md
|
||||
beat-detector.types.ts
|
||||
beat-detector.test.ts
|
||||
index.ts
|
||||
algorithms/
|
||||
energy-odf.ts
|
||||
energy-odf.md
|
||||
energy-odf.test.ts
|
||||
spectral-flux.ts
|
||||
spectral-flux.md
|
||||
spectral-flux.test.ts
|
||||
comb-tempo.ts
|
||||
comb-tempo.md
|
||||
comb-tempo.test.ts
|
||||
beat-grid.ts
|
||||
beat-grid.md
|
||||
beat-grid.test.ts
|
||||
index.ts
|
||||
__benchmarks__/
|
||||
beat.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. All four already exist on genesis.
|
||||
|
||||
### Affected dependents
|
||||
|
||||
No dependents in this batch (verified by `bunx turbo ls --filter=...@suaveplan/audio-analysis-beat`). `@suaveplan/audio-analysis-beat` is a leaf Analysis Op plugin — no other package in this 13-change audio batch depends on it. It is consumed at runtime by application/timeline code that registers it as a sidecar analysis op (e.g. auto-mixing in `@suaveplan/audio-plugins-fx`, visualisation in a future `react-audio-player`, batch analysis in `@suaveplan/audio-cli`) — those consumers depend on `@suaveplan/audio-core`'s `AnalysisOp` interface, not on this package directly, and none are created or modified by this change.
|
||||
Reference in New Issue
Block a user