import fs from 'node:fs'; import path from 'node:path'; const divisions = 4; const typeFor = (d) => ({16: 'whole', 8: 'half', 4: 'quarter', 2: 'eighth', 1: '16th'})[d] ?? 'quarter'; let xml = ` Morning Window Demo ยท Original music-visualizer demo generator Piano `; function note(step, octave, duration, opts = {}) { const {chord = false, staff = 1, voice = '1', tie, grace = false} = opts; const parts = []; if (grace) parts.push(''); if (chord) parts.push(''); parts.push(`${step}${octave}`); if (!grace) parts.push(`${duration}`); if (tie === 'start') parts.push(''); if (tie === 'stop') parts.push(''); parts.push(`${voice}`); if (!grace) parts.push(`${typeFor(duration)}`); else parts.push('eighth'); if (tie) { parts.push(``); } parts.push(`${staff}`); return ` ${parts.join('')}\n`; } function rest(duration, opts = {}) { const {staff = 1, voice = '1'} = opts; return ` ${duration}${voice}${typeFor(duration)}${staff}\n`; } function leftArpeggio(notes) { // 8 eighth notes = 16 divisions let out = ''; for (const [step, octave] of notes) { out += note(step, octave, 2, {staff: 2, voice: '5'}); } return out; } const measures = [ // 1 { right: note('C', 5, 4) + note('E', 5, 2) + note('G', 5, 2) + note('A', 5, 4) + note('G', 5, 4), left: leftArpeggio([ ['C', 3], ['G', 3], ['E', 4], ['G', 3], ['C', 3], ['G', 3], ['E', 4], ['G', 3], ]), }, // 2 { right: note('F', 5, 4) + note('E', 5, 2) + note('D', 5, 2) + note('C', 5, 8) + note('E', 5, 8, {chord: true}) + note('G', 5, 8, {chord: true}), left: leftArpeggio([ ['F', 3], ['C', 4], ['A', 3], ['C', 4], ['F', 3], ['C', 4], ['A', 3], ['C', 4], ]), }, // 3 { right: rest(4) + note('G', 5, 2) + note('A', 5, 2) + note('G', 5, 4) + note('E', 5, 4), left: leftArpeggio([ ['G', 3], ['D', 4], ['B', 3], ['D', 4], ['G', 3], ['D', 4], ['B', 3], ['D', 4], ]), }, // 4 { right: note('D', 5, 4) + note('E', 5, 4) + note('C', 5, 8), left: leftArpeggio([ ['C', 3], ['G', 3], ['E', 4], ['G', 3], ['C', 3], ['G', 3], ['E', 4], ['G', 3], ]), }, // 5 { right: note('E', 5, 4) + note('G', 5, 2) + note('A', 5, 2) + note('C', 6, 4) + note('B', 5, 4), left: leftArpeggio([ ['A', 3], ['E', 4], ['C', 4], ['E', 4], ['A', 3], ['E', 4], ['C', 4], ['E', 4], ]), }, // 6 { right: note('A', 5, 4) + note('G', 5, 2) + note('E', 5, 2) + note('D', 5, 4) + note('E', 5, 4), left: leftArpeggio([ ['F', 3], ['C', 4], ['A', 3], ['C', 4], ['F', 3], ['C', 4], ['A', 3], ['C', 4], ]), }, // 7 { right: note('G', 5, 4) + note('E', 5, 2) + note('D', 5, 2) + note('C', 5, 8), left: leftArpeggio([ ['G', 3], ['D', 4], ['B', 3], ['D', 4], ['G', 3], ['D', 4], ['B', 3], ['D', 4], ]), }, // 8 { right: note('D', 5, 4) + note('E', 5, 2) + note('F', 5, 2) + note('G', 5, 8, {tie: 'start'}), left: leftArpeggio([ ['C', 3], ['G', 3], ['E', 4], ['G', 3], ['C', 3], ['G', 3], ['E', 4], ['G', 3], ]), }, // 9 { right: note('G', 5, 8, {tie: 'stop'}) + note('A', 5, 4) + note('G', 5, 4), left: leftArpeggio([ ['A', 3], ['E', 4], ['C', 4], ['E', 4], ['A', 3], ['E', 4], ['C', 4], ['E', 4], ]), }, // 10 { right: note('A', 5, 2, {grace: true}) + note('G', 5, 4) + note('E', 5, 4) + note('D', 5, 8), left: leftArpeggio([ ['F', 3], ['C', 4], ['A', 3], ['C', 4], ['F', 3], ['C', 4], ['A', 3], ['C', 4], ]), }, // 11 { right: note('E', 5, 4) + note('G', 5, 2) + note('A', 5, 2) + note('C', 6, 8), left: leftArpeggio([ ['G', 3], ['D', 4], ['B', 3], ['D', 4], ['G', 3], ['D', 4], ['B', 3], ['D', 4], ]), }, // 12 { right: note('B', 5, 4) + note('A', 5, 4) + note('G', 5, 8), left: leftArpeggio([ ['C', 3], ['G', 3], ['E', 4], ['G', 3], ['C', 3], ['G', 3], ['E', 4], ['G', 3], ]), }, // 13 { right: note('C', 6, 4) + note('B', 5, 2) + note('A', 5, 2) + note('G', 5, 4) + note('E', 5, 4), left: leftArpeggio([ ['A', 3], ['E', 4], ['C', 4], ['E', 4], ['A', 3], ['E', 4], ['C', 4], ['E', 4], ]), }, // 14 { right: note('F', 5, 4) + note('E', 5, 2) + note('D', 5, 2) + note('C', 5, 8), left: leftArpeggio([ ['F', 3], ['C', 4], ['A', 3], ['C', 4], ['F', 3], ['C', 4], ['A', 3], ['C', 4], ]), }, // 15 { right: note('D', 5, 4) + note('E', 5, 4) + note('G', 5, 8), left: leftArpeggio([ ['G', 3], ['D', 4], ['B', 3], ['D', 4], ['G', 3], ['D', 4], ['B', 3], ['D', 4], ]), }, // 16 { right: note('C', 5, 16) + note('E', 5, 16, {chord: true}) + note('G', 5, 16, {chord: true}), left: note('C', 3, 16, {staff: 2, voice: '5'}) + note('E', 3, 16, {staff: 2, voice: '5', chord: true}) + note('G', 3, 16, {staff: 2, voice: '5', chord: true}), }, ]; measures.forEach((measure, index) => { const number = index + 1; xml += ` \n`; if (number === 1) { xml += ` ${divisions} 0 2 G2 F4 quarter96 `; } if (number === 9) { xml += ` quarter104 `; } xml += measure.right; xml += ` 16\n`; xml += measure.left; if (number === 4) { xml += ` \n`; } xml += ` \n`; }); xml += ` `; const out = path.resolve('public/demo/demo.musicxml'); fs.mkdirSync(path.dirname(out), {recursive: true}); fs.writeFileSync(out, xml, 'utf8'); console.log(`Wrote ${out} (${xml.length} bytes, ${measures.length} measures)`);