diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-05 21:13:26 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-05 21:13:26 +0100 |
| commit | a794102f59e8baf654b8807cf74fdc014830fb6f (patch) | |
| tree | 56ab1b660589460df621d0120c112c918e806e21 /tools/timeline_editor | |
| parent | a38359aeb4ab74814ee523fff02dcc8a3eee7efd (diff) | |
fix(timeline-editor): Fix parser to handle actual demo.seq format
Fixed critical parsing bugs that prevented loading real demo.seq files.
Issues fixed:
1. Beat notation support: Parse '0b', '4b' as beats and convert to seconds
- Added BPM parsing from '# BPM 120' comments
- Helper function: parseTime() converts beats to seconds using BPM
2. Priority modifiers: Handle '+', '=', '-' modifiers correctly
- '+' increments priority
- '=' keeps same priority
- '-' decrements priority (for background effects)
- Store both absolute priority and modifier for round-trip
3. Inline comments: Strip comments from end of lines with '#'
- Effect lines like 'EFFECT + FlashEffect 0.0 1.0 # comment' now parse
4. Sequence names: Parse optional sequence names in quotes
- Format: SEQUENCE <time> <priority> "name" [end]
5. Updated serializer: Output correct format with modifiers
- Generate 'EFFECT + ClassName ...' instead of 'EFFECT ClassName ... priority'
- Preserve BPM comment in output
- Preserve sequence names
Parser now correctly handles:
- SEQUENCE 0b 0 → Start at beat 0, priority 0
- SEQUENCE 4b 0 → Start at beat 4 (2 seconds @ 120 BPM)
- EFFECT - FlashCubeEffect .2 3 → Background effect (priority -1)
- EFFECT + FlashEffect 0.0 1. → Normal effect (priority 0)
- EFFECT = GaussianBlur 0 8 → Same priority as previous
Testing: Load assets/demo.seq should now work correctly.
Diffstat (limited to 'tools/timeline_editor')
| -rw-r--r-- | tools/timeline_editor/index.html | 83 |
1 files changed, 68 insertions, 15 deletions
diff --git a/tools/timeline_editor/index.html b/tools/timeline_editor/index.html index 0d10e4a..293a03f 100644 --- a/tools/timeline_editor/index.html +++ b/tools/timeline_editor/index.html @@ -285,35 +285,85 @@ const sequences = []; const lines = content.split('\n'); let currentSequence = null; + let bpm = 120; // Default BPM + let currentPriority = 0; // Track priority for + = - modifiers + + // Helper: Convert time notation to seconds + function parseTime(timeStr) { + if (timeStr.endsWith('b')) { + // Beat notation: "4b" = 4 beats + const beats = parseFloat(timeStr.slice(0, -1)); + return beats * (60.0 / bpm); + } + return parseFloat(timeStr); + } + + // Helper: Strip inline comments + function stripComment(line) { + const commentIdx = line.indexOf('#'); + if (commentIdx >= 0) { + return line.slice(0, commentIdx).trim(); + } + return line; + } for (let line of lines) { line = line.trim(); - // Skip empty lines and comments - if (!line || line.startsWith('#')) continue; + // Skip empty lines + if (!line) continue; - // Parse SEQUENCE line - const seqMatch = line.match(/^SEQUENCE\s+([\d.]+)\s+(\d+)$/); + // Parse BPM comment + if (line.startsWith('# BPM ')) { + const bpmMatch = line.match(/# BPM (\d+)/); + if (bpmMatch) { + bpm = parseInt(bpmMatch[1]); + } + continue; + } + + // Skip other comments + if (line.startsWith('#')) continue; + + // Strip inline comments + line = stripComment(line); + if (!line) continue; + + // Parse SEQUENCE line: SEQUENCE <time> <priority> [name] [end] + const seqMatch = line.match(/^SEQUENCE\s+(\S+)\s+(\d+)(?:\s+"([^"]+)")?(?:\s+(\S+))?$/); if (seqMatch) { currentSequence = { type: 'sequence', - startTime: parseFloat(seqMatch[1]), + startTime: parseTime(seqMatch[1]), priority: parseInt(seqMatch[2]), - effects: [] + effects: [], + name: seqMatch[3] || '' }; sequences.push(currentSequence); + currentPriority = -1; // Reset effect priority for new sequence continue; } - // Parse EFFECT line - const effectMatch = line.match(/^EFFECT\s+(\w+)\s+([\d.]+)\s+([\d.]+)\s+(-?\d+)(?:\s+(.*))?$/); + // Parse EFFECT line: EFFECT <modifier> <ClassName> <start> <end> [args] + const effectMatch = line.match(/^EFFECT\s+([+=-])\s+(\w+)\s+(\S+)\s+(\S+)(?:\s+(.*))?$/); if (effectMatch && currentSequence) { + const modifier = effectMatch[1]; + + // Update priority based on modifier + if (modifier === '+') { + currentPriority++; + } else if (modifier === '-') { + currentPriority--; + } + // '=' keeps current priority + const effect = { type: 'effect', - className: effectMatch[1], - startTime: parseFloat(effectMatch[2]), - endTime: parseFloat(effectMatch[3]), - priority: parseInt(effectMatch[4]), + className: effectMatch[2], + startTime: parseTime(effectMatch[3]), + endTime: parseTime(effectMatch[4]), + priority: currentPriority, + priorityModifier: modifier, args: effectMatch[5] || '' }; currentSequence.effects.push(effect); @@ -326,13 +376,16 @@ // Serializer: JavaScript objects → demo.seq function serializeSeqFile(sequences) { let output = '# Demo Timeline\n'; - output += '# Generated by Timeline Editor\n\n'; + output += '# Generated by Timeline Editor\n'; + output += '# BPM 120\n\n'; for (const seq of sequences) { - output += `SEQUENCE ${seq.startTime.toFixed(2)} ${seq.priority}\n`; + const seqLine = `SEQUENCE ${seq.startTime.toFixed(2)} ${seq.priority}`; + output += seq.name ? `${seqLine} "${seq.name}"\n` : `${seqLine}\n`; for (const effect of seq.effects) { - output += ` EFFECT ${effect.className} ${effect.startTime.toFixed(2)} ${effect.endTime.toFixed(2)} ${effect.priority}`; + const modifier = effect.priorityModifier || '+'; + output += ` EFFECT ${modifier} ${effect.className} ${effect.startTime.toFixed(2)} ${effect.endTime.toFixed(2)}`; if (effect.args) { output += ` ${effect.args}`; } |
