Skip to content

Commit 2ff331d

Browse files
committed
feat(streaming): Add chord progression pattern detection and improve bar chord accuracy
Enhance streaming chord analysis with known pattern recognition and voting-based bar detection: Chord Pattern Detection: - Add library of known progressions (Royal Road, Komuro, Canon, Just the Two of Us, etc.) - Compute voted pattern by aggregating chords across pattern repetitions - Score detected progression against known patterns with weighted similarity - Return all pattern scores sorted by confidence Bar Chord Improvements: - Replace chroma averaging with frame-by-frame chord voting for more accurate detection - Add retroactive bar chord detection using stored chroma history - Store full chroma history (up to ~35s) for analysis when BPM stabilizes - Compute bar chords from the start of audio retroactively API Changes: - Add votedPattern, patternLength, detectedPatternName, detectedPatternScore, allPatternScores to ProgressiveEstimate - Add sampleRate() method to StreamAnalyzer - Change stats() to non-const (triggers final pattern computation) Testing: - Add tests for C-G-Am-F progression detection - Add tests for voted pattern structure and values - Add tests for pattern detection fields - Add tests for allPatternScores ordering - Add tests for reset behavior
1 parent 591ce91 commit 2ff331d

8 files changed

Lines changed: 2071 additions & 25 deletions

File tree

js/index.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ interface WasmBarChord {
172172
confidence: number;
173173
}
174174

175+
interface WasmPatternScore {
176+
name: string;
177+
score: number;
178+
}
179+
175180
interface WasmProgressiveEstimate {
176181
bpm: number;
177182
bpmConfidence: number;
@@ -186,6 +191,11 @@ interface WasmProgressiveEstimate {
186191
barChordProgression: WasmBarChord[];
187192
currentBar: number;
188193
barDuration: number;
194+
votedPattern: WasmBarChord[];
195+
patternLength: number;
196+
detectedPatternName: string;
197+
detectedPatternScore: number;
198+
allPatternScores: WasmPatternScore[];
189199
accumulatedSeconds: number;
190200
usedFrames: number;
191201
updated: boolean;
@@ -223,6 +233,7 @@ interface WasmStreamAnalyzer {
223233
stats: () => WasmAnalyzerStats;
224234
frameCount: () => number;
225235
currentTime: () => number;
236+
sampleRate: () => number;
226237
delete: () => void;
227238
}
228239

@@ -1390,6 +1401,14 @@ export interface BarChord {
13901401
confidence: number;
13911402
}
13921403

1404+
/**
1405+
* Pattern score for known chord progressions
1406+
*/
1407+
export interface PatternScore {
1408+
name: string;
1409+
score: number;
1410+
}
1411+
13931412
/**
13941413
* Progressive estimation results for BPM, Key, and Chord
13951414
*/
@@ -1407,6 +1426,11 @@ export interface ProgressiveEstimate {
14071426
barChordProgression: BarChord[];
14081427
currentBar: number;
14091428
barDuration: number;
1429+
votedPattern: BarChord[];
1430+
patternLength: number;
1431+
detectedPatternName: string;
1432+
detectedPatternScore: number;
1433+
allPatternScores: PatternScore[];
14101434
accumulatedSeconds: number;
14111435
usedFrames: number;
14121436
updated: boolean;
@@ -1583,6 +1607,20 @@ export class StreamAnalyzer {
15831607
})),
15841608
currentBar: s.estimate.currentBar,
15851609
barDuration: s.estimate.barDuration,
1610+
votedPattern: (s.estimate.votedPattern || []).map((c) => ({
1611+
barIndex: c.barIndex,
1612+
root: c.root as PitchClass,
1613+
quality: c.quality as ChordQuality,
1614+
startTime: c.startTime,
1615+
confidence: c.confidence,
1616+
})),
1617+
patternLength: s.estimate.patternLength,
1618+
detectedPatternName: s.estimate.detectedPatternName || '',
1619+
detectedPatternScore: s.estimate.detectedPatternScore || 0,
1620+
allPatternScores: (s.estimate.allPatternScores || []).map((p) => ({
1621+
name: p.name,
1622+
score: p.score,
1623+
})),
15861624
accumulatedSeconds: s.estimate.accumulatedSeconds,
15871625
usedFrames: s.estimate.usedFrames,
15881626
updated: s.estimate.updated,
@@ -1604,6 +1642,13 @@ export class StreamAnalyzer {
16041642
return this.analyzer.currentTime();
16051643
}
16061644

1645+
/**
1646+
* Get the sample rate.
1647+
*/
1648+
sampleRate(): number {
1649+
return this.analyzer.sampleRate();
1650+
}
1651+
16071652
/**
16081653
* Release resources. Call when done using the analyzer.
16091654
*/

js/sonare.js.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,11 @@ interface WasmBarChord {
347347
confidence: number;
348348
}
349349

350+
interface WasmPatternScore {
351+
name: string;
352+
score: number;
353+
}
354+
350355
interface WasmProgressiveEstimate {
351356
bpm: number;
352357
bpmConfidence: number;
@@ -361,6 +366,11 @@ interface WasmProgressiveEstimate {
361366
barChordProgression: WasmBarChord[];
362367
currentBar: number;
363368
barDuration: number;
369+
votedPattern: WasmBarChord[];
370+
patternLength: number;
371+
detectedPatternName: string;
372+
detectedPatternScore: number;
373+
allPatternScores: WasmPatternScore[];
364374
accumulatedSeconds: number;
365375
usedFrames: number;
366376
updated: boolean;
@@ -398,6 +408,7 @@ interface WasmStreamAnalyzer {
398408
stats: () => WasmAnalyzerStats;
399409
frameCount: () => number;
400410
currentTime: () => number;
411+
sampleRate: () => number;
401412
delete: () => void;
402413
}
403414

0 commit comments

Comments
 (0)