diff options
Diffstat (limited to 'tools')
| -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}`; } |
