Add offline MP3 rendering, Game Boy styling for UX test, fix 16-bit → 8-bit
- Add renderOffline() to SynthesisEngine for faster-than-realtime audio rendering - Add renderOffline() to MotifEngine wrapping the synthesis engine - Update ux-test.html MP3 download to use offline rendering (no more real-time capture) - Restyle ux-test.html with Game Boy LCD aesthetic to match main site - Fix "16-Bit" to "8-Bit" across all titles and share cards (Game Boy is 8-bit!) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -184,6 +184,49 @@ export class MotifEngine {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render audio offline (faster than real-time) to an AudioBuffer.
|
||||
* This does not require play() - it pre-schedules all events and renders in one go.
|
||||
*/
|
||||
async renderOffline(
|
||||
events: NoteEvent[],
|
||||
transformMode: 'passthrough' | 'procedural' = 'passthrough',
|
||||
sampleRate = 44100
|
||||
): Promise<AudioBuffer> {
|
||||
let roleAssignments;
|
||||
|
||||
if (transformMode === 'passthrough') {
|
||||
// Direct playback mode - play MIDI as-is without transformations
|
||||
roleAssignments = [{
|
||||
role: 'melody' as const,
|
||||
sourceTrack: 0,
|
||||
events: [...events], // Clone to avoid mutation
|
||||
chords: [],
|
||||
confidence: 1.0,
|
||||
features: {
|
||||
medianPitch: 60,
|
||||
pitchRange: 48,
|
||||
noteDensity: 1.0,
|
||||
polyphonyRatio: 0.5,
|
||||
averageDuration: 0.5,
|
||||
repetitionScore: 0.5,
|
||||
isMonophonic: false,
|
||||
hasPhraseContinuity: true,
|
||||
register: 'mid' as const
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
// Procedural mode - transform the MIDI with role mapping
|
||||
// Clone events to avoid mutating originals
|
||||
const clonedEvents = events.map(e => ({ ...e }));
|
||||
const features = this.midiProcessor.extractFeatures(clonedEvents);
|
||||
roleAssignments = this.roleMapper.assignRoles(features, clonedEvents);
|
||||
}
|
||||
|
||||
console.log(`Motif: Offline rendering in ${transformMode} mode`);
|
||||
return SynthesisEngine.renderOffline(roleAssignments, this.config, sampleRate);
|
||||
}
|
||||
|
||||
private generateSyntheticMIDI(songName: string): NoteEvent[] {
|
||||
// Generate procedural MIDI based on song name hash
|
||||
const hash = this.simpleHash(songName);
|
||||
|
||||
Reference in New Issue
Block a user