Implement complete search results + dual player UI

Backend Enhancements:
- Add ParsedMIDIInfo with track analysis and quality issues
- New /api/midi/parse endpoint for MIDI metadata extraction
- Enhanced MIDIService with parseMIDI method

MIDI Preview Player:
- Complete MIDIPlayer class with polyphonic playback
- Basic oscillator mapping (bass=square, mid=triangle, high=sine)
- Lookahead scheduling with proper cleanup
- Automatic stopping when MIDI ends

New UI Flow:
1. Search → Results table with metadata
2. Select MIDI → Shows parsed info (duration, tracks, tempo)
3. Dual transport: Preview Original vs Generate Motif
4. Try Next Result button for easy A/B testing

Search Results Table:
- Title, source, confidence bar, duration, track count
- Quality issues display (short duration, few notes, etc.)
- Auto-select best result, manual selection available
- Visual confidence bars and issue warnings

This makes the project feel "real" - you can now:
- See exactly what MIDI was found
- Verify it's the right song via preview
- Compare original vs procedural synthesis
- Easily try different results

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
b1rdmania
2025-12-17 21:04:29 +00:00
parent 2d4c748d4c
commit 177e8fbed3
8 changed files with 731 additions and 36 deletions
+35
View File
@@ -5,6 +5,25 @@ interface MIDISearchResult {
pageUrl: string;
midiUrl: string;
confidence: number;
parsed?: ParsedMIDIInfo;
}
interface ParsedMIDIInfo {
durationSec: number;
tempoBpm: number;
timeSig?: { num: number; den: number };
tracks: TrackInfo[];
noteCount: number;
issues: string[];
}
interface TrackInfo {
id: number;
name?: string;
program?: number;
noteCount: number;
channel?: number;
register: 'low' | 'mid' | 'high';
}
interface MIDISearchResponse {
@@ -50,6 +69,21 @@ export class MIDIService {
}
}
async parseMIDI(url: string): Promise<ParsedMIDIInfo | null> {
try {
const response = await fetch(`${this.baseUrl}/api/midi/parse?u=${encodeURIComponent(url)}`);
if (!response.ok) {
throw new Error(`Parse failed: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('MIDI parse error:', error);
return null;
}
}
async checkHealth(): Promise<boolean> {
try {
const response = await fetch(`${this.baseUrl}/health`);
@@ -58,4 +92,5 @@ export class MIDIService {
return false;
}
}
}
}