k-l-lambda commited on
Commit
9fb0d40
·
1 Parent(s): b299707

feat: client-side score player (SVG preview + gated MIDI), vendored via LFS

Browse files

Wire the right-panel score renderer (lyl→MEI→Verovio→SVG/MIDI), bridging
lilylet-live-editor's client-side pipeline into Gradio. Vendor the browser
libs under web/ via Git LFS so the deployed Space serves them (no build step
on HF); commit the hand-written score-player.js/.css and built-in examples.

.gitattributes CHANGED
@@ -33,3 +33,9 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+
37
+ # LilyScript vendored browser libs (large JS bundles + soundfont) — Git LFS.
38
+ # Scoped to the vendor/soundfont dirs so the hand-written web/score-player.js
39
+ # stays a normal text file.
40
+ web/vendor/*.js filter=lfs diff=lfs merge=lfs -text
41
+ web/soundfont/*.js filter=lfs diff=lfs merge=lfs -text
.gitignore CHANGED
@@ -4,6 +4,11 @@ models/
4
  # Generated outputs written at runtime.
5
  outputs/
6
 
 
 
 
 
 
7
  __pycache__/
8
  *.pyc
9
  .DS_Store
 
4
  # Generated outputs written at runtime.
5
  outputs/
6
 
7
+ # Vendored browser libraries live under web/vendor/ + web/soundfont/ and ARE
8
+ # committed (via Git LFS — see .gitattributes) so the deployed Space has them;
9
+ # regenerate locally with tools.local/prepare_web.py. The hand-written
10
+ # score-player.js / .css are committed as normal text.
11
+
12
  __pycache__/
13
  *.pyc
14
  .DS_Store
app.py CHANGED
@@ -23,6 +23,7 @@ from collections import deque
23
  import gradio as gr
24
 
25
  from lilyscript.generator import StreamingLilyletGenerator
 
26
 
27
  HERE = os.path.dirname(os.path.abspath(__file__))
28
  # TODO: swap for huggingface_hub.snapshot_download(repo_id=...) to pull the int8
@@ -31,6 +32,7 @@ MODEL_DIR = os.environ.get('LILYSCRIPT_MODEL_DIR', os.path.join(HERE, 'models'))
31
  ASSET_DIR = os.path.join(HERE, 'assets')
32
  EXAMPLES_DIR = os.path.join(HERE, 'examples')
33
  OUTPUT_DIR = os.path.join(HERE, 'outputs')
 
34
 
35
  EXAMPLE_PREFIX = '\U0001F4C4 ' # 📄 examples
36
  OUTPUT_PREFIX = '✨ ' # ✨ session outputs
@@ -102,13 +104,18 @@ def get_generator ():
102
 
103
 
104
  def load_examples ():
105
- '''Read built-in example .lyl files into a {label: text} dict.'''
 
 
 
 
 
106
  store = {}
107
  if os.path.isdir(EXAMPLES_DIR):
108
  for name in sorted(os.listdir(EXAMPLES_DIR)):
109
  if name.endswith('.lyl'):
110
  with open(os.path.join(EXAMPLES_DIR, name), encoding='utf-8') as f:
111
- store[EXAMPLE_PREFIX + name] = f.read()
112
  return store
113
 
114
 
@@ -235,20 +242,94 @@ def load_file (label, store):
235
 
236
 
237
  SHEET_PLACEHOLDER = '''
238
- <div style="height:100%;min-height:600px;display:flex;align-items:center;
239
- justify-content:center;border:1px dashed #c9c9c9;border-radius:8px;
240
- color:#999;font-family:sans-serif;text-align:center;">
241
- <div>
242
- <div style="font-size:42px;margin-bottom:8px;">&#127932;</div>
243
- <div>Sheet-music preview</div>
244
- <div style="font-size:12px;margin-top:4px;">
245
- (coming soon)
246
  </div>
247
  </div>
248
  </div>
249
  '''
250
 
251
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  CUSTOM_CSS = '''
253
  /* Score List: truncate long file names to a single line with an ellipsis. */
254
  .score-list label {
@@ -320,7 +401,7 @@ def build_ui ():
320
  with gr.Group():
321
  gr.Markdown('## Lilylet editor')
322
  editor = gr.Code(show_label=False, language=None, lines=18,
323
- max_lines=18, interactive=True)
324
 
325
  # ---------------- RIGHT ----------------
326
  with gr.Column(scale=6):
@@ -329,20 +410,40 @@ def build_ui ():
329
  gr.HTML(SHEET_PLACEHOLDER)
330
 
331
  # ---- wiring ----
 
 
 
332
  # style dropdowns -> keep the metadata-prompt text box in sync
333
  for field in (composer, genre, instrument):
334
  field.change(sync_prompt, inputs=[composer, genre, instrument, prompt], outputs=[prompt])
335
 
 
 
 
 
336
  gen_event = gen_btn.click(
337
  run_generation,
338
  inputs=[prompt, measures, temperature, max_patches, seed, store],
339
  outputs=[log, editor, file_list, store],
 
340
  )
341
- stop_btn.click(None, None, None, cancels=[gen_event])
 
 
 
 
 
 
342
  file_list.select(load_file, inputs=[file_list, store], outputs=[editor])
343
 
344
  return demo
345
 
346
 
347
  if __name__ == '__main__':
348
- build_ui().queue().launch(theme=gr.themes.Soft(), css=CUSTOM_CSS)
 
 
 
 
 
 
 
23
  import gradio as gr
24
 
25
  from lilyscript.generator import StreamingLilyletGenerator
26
+ from lilyscript.postprocess import postprocess
27
 
28
  HERE = os.path.dirname(os.path.abspath(__file__))
29
  # TODO: swap for huggingface_hub.snapshot_download(repo_id=...) to pull the int8
 
32
  ASSET_DIR = os.path.join(HERE, 'assets')
33
  EXAMPLES_DIR = os.path.join(HERE, 'examples')
34
  OUTPUT_DIR = os.path.join(HERE, 'outputs')
35
+ WEB_DIR = os.path.join(HERE, 'web') # vendored browser libs + score player (gitignored bundles)
36
 
37
  EXAMPLE_PREFIX = '\U0001F4C4 ' # 📄 examples
38
  OUTPUT_PREFIX = '✨ ' # ✨ session outputs
 
104
 
105
 
106
  def load_examples ():
107
+ '''Read built-in example .lyl files into a {label: text} dict.
108
+
109
+ Examples may be stored raw (with inline `[r:x/y]` stream markers and
110
+ run-together header lines); run them through `postprocess` so the editor —
111
+ and the score renderer it feeds — get syntactically clean Lilylet. It's a
112
+ no-op on already-clean text.'''
113
  store = {}
114
  if os.path.isdir(EXAMPLES_DIR):
115
  for name in sorted(os.listdir(EXAMPLES_DIR)):
116
  if name.endswith('.lyl'):
117
  with open(os.path.join(EXAMPLES_DIR, name), encoding='utf-8') as f:
118
+ store[EXAMPLE_PREFIX + name] = postprocess(f.read())
119
  return store
120
 
121
 
 
242
 
243
 
244
  SHEET_PLACEHOLDER = '''
245
+ <div id="ls-score" class="ls-score-mount" style="height:100%;min-height:600px;">
246
+ <div style="display:flex;align-items:center;justify-content:center;height:100%;
247
+ min-height:600px;border:1px dashed #c9c9c9;border-radius:8px;color:#999;
248
+ font-family:sans-serif;text-align:center;">
249
+ <div>
250
+ <div style="font-size:42px;margin-bottom:8px;">&#127932;</div>
251
+ <div>Loading score renderer…</div>
 
252
  </div>
253
  </div>
254
  </div>
255
  '''
256
 
257
 
258
+ # Static-file URL prefix Gradio serves allowed_paths under (verified at 6.18.0).
259
+ def _file_url (path):
260
+ return '/gradio_api/file=' + path
261
+
262
+
263
+ def build_head ():
264
+ '''<head> injection: load the vendored browser libs (lilylet bundle, Verovio
265
+ WASM, music-widgets) then the score player, and point the soundfont loader at
266
+ the vendored copy. Gradio delivers this via its client config and injects it
267
+ on the frontend, so absolute `/gradio_api/file=` URLs resolve correctly.'''
268
+ vendor = os.path.join(WEB_DIR, 'vendor')
269
+ scripts = [
270
+ os.path.join(vendor, 'lilylet.bundle.js'),
271
+ os.path.join(vendor, 'verovio.bundle.js'),
272
+ os.path.join(vendor, 'musicWidgetsBrowser.umd.min.js'),
273
+ os.path.join(WEB_DIR, 'score-player.js'),
274
+ ]
275
+ tags = ['<script>window.__LILYSCRIPT_SOUNDFONT_URL=%r;</script>'
276
+ % (_file_url(os.path.join(WEB_DIR, 'soundfont')) + '/')]
277
+ tags.append('<link rel="stylesheet" href="%s">' % _file_url(os.path.join(WEB_DIR, 'score-player.css')))
278
+ for s in scripts:
279
+ tags.append('<script src="%s"></script>' % _file_url(s))
280
+ return '\n'.join(tags)
281
+
282
+
283
+ # ---- client-side glue (Gradio `js=` handlers) -------------------------------
284
+ # These run in the browser. They wait for LilyScore (score-player.js) to load,
285
+ # then mount it into #ls-score and drive render / generation-gating.
286
+
287
+ _JS_HELPERS = '''
288
+ function () {
289
+ // poll until the score player + its #ls-score mount exist, then mount once.
290
+ const tryMount = () => {
291
+ const root = document.getElementById('ls-score');
292
+ if (window.LilyScore && root) { window.LilyScore.mount(root); return true; }
293
+ return false;
294
+ };
295
+ if (!tryMount()) {
296
+ const iv = setInterval(() => { if (tryMount()) clearInterval(iv); }, 200);
297
+ setTimeout(() => clearInterval(iv), 20000);
298
+ }
299
+ }
300
+ '''
301
+
302
+ # Read the live editor text and render it to SVG. gr.Code renders a CodeMirror
303
+ # editor; its current text lives in the `.cm-content` element (textContent).
304
+ _JS_RENDER = '''
305
+ function () {
306
+ if (!window.LilyScore) return;
307
+ const root = document.querySelector('#ls-editor');
308
+ let text = '';
309
+ if (root) {
310
+ const cm = root.querySelector('.cm-content');
311
+ if (cm) text = cm.innerText;
312
+ else { const ta = root.querySelector('textarea'); if (ta) text = ta.value; }
313
+ }
314
+ window.LilyScore.render(text);
315
+ }
316
+ '''
317
+
318
+ # NB: when js= runs before a backend fn, Gradio passes the event's input values
319
+ # to the js function and uses its RETURN value as the fn's inputs. So the gate
320
+ # must return its args unchanged — returning nothing makes Gradio send null for
321
+ # every input (which breaks e.g. the temperature Slider's preprocessor).
322
+ _JS_GEN_START = '''
323
+ function (...args) { if (window.LilyScore) window.LilyScore.setGenerating(true); return args; }
324
+ '''
325
+
326
+ _JS_GEN_END = '''
327
+ function () { if (window.LilyScore) window.LilyScore.setGenerating(false); }
328
+ '''
329
+
330
+
331
+
332
+
333
  CUSTOM_CSS = '''
334
  /* Score List: truncate long file names to a single line with an ellipsis. */
335
  .score-list label {
 
401
  with gr.Group():
402
  gr.Markdown('## Lilylet editor')
403
  editor = gr.Code(show_label=False, language=None, lines=18,
404
+ max_lines=18, interactive=True, elem_id='ls-editor')
405
 
406
  # ---------------- RIGHT ----------------
407
  with gr.Column(scale=6):
 
410
  gr.HTML(SHEET_PLACEHOLDER)
411
 
412
  # ---- wiring ----
413
+ # mount the score player once the page (and LilyScore) is ready
414
+ demo.load(None, None, None, js=_JS_HELPERS)
415
+
416
  # style dropdowns -> keep the metadata-prompt text box in sync
417
  for field in (composer, genre, instrument):
418
  field.change(sync_prompt, inputs=[composer, genre, instrument, prompt], outputs=[prompt])
419
 
420
+ # Generate: a single click dep that runs the gate js (SVG-only, player
421
+ # hidden) and then the streaming model fn — `js=` on a backend click runs
422
+ # first and, returning nothing, leaves the declared `inputs` untouched. A
423
+ # trailing `.then` js lifts the gate + reveals the player when it finishes.
424
  gen_event = gen_btn.click(
425
  run_generation,
426
  inputs=[prompt, measures, temperature, max_patches, seed, store],
427
  outputs=[log, editor, file_list, store],
428
+ js=_JS_GEN_START,
429
  )
430
+ gen_event.then(None, None, None, js=_JS_GEN_END)
431
+
432
+ # every editor change (streaming syncs + manual edits + file loads) -> re-render SVG
433
+ editor.change(None, None, None, js=_JS_RENDER)
434
+
435
+ # Stop: cancel generation, then lift the gate so the player returns
436
+ stop_btn.click(None, None, None, cancels=[gen_event]).then(None, None, None, js=_JS_GEN_END)
437
  file_list.select(load_file, inputs=[file_list, store], outputs=[editor])
438
 
439
  return demo
440
 
441
 
442
  if __name__ == '__main__':
443
+ demo = build_ui()
444
+ demo.queue().launch(
445
+ theme=gr.themes.Soft(),
446
+ css=CUSTOM_CSS,
447
+ head=build_head(),
448
+ allowed_paths=[WEB_DIR],
449
+ )
examples/lilylet_generated_llama-seed0.lyl ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [composer "Smetana, Bedrich"]
2
+ [genre "Romantic"]
3
+ [instrument "Keyboard"]
4
+
5
+ \staff "1" \key e \major \time 2/4 \clef "treble" \tempo 4=120 ^\markup "Allegretto semplicel." <e gs cs e>4.\p( <gs b e gs>8 \\
6
+ \staff "2" \clef "bass" r8 <b, e>4 <b e>8 \\
7
+ \staff "2" e,,2 | % r:0/45
8
+
9
+ \staff "1" \key e \major \time 2/4 <b' e gs b>4. <gs b e gs>8 \\
10
+ \staff "2" <b, e>8 <b e>4 <b e>8 \\
11
+ \staff "2" e,,2 | % r:1/44
12
+
13
+ \staff "1" \key e \major \time 2/4 <b' e gs b>4.\>( <gs b e gs>8 \\
14
+ \staff "2" r8 <b, e b'>4 <b e b'>8 \\
15
+ \staff "2" e,,2 | % r:2/43
16
+
17
+ \staff "1" \key e \major \time 2/4 <b' e gs b>4. <gs b e gs>8\! \\
18
+ \staff "2" <b, e b'>8 <b e b'>4 <b e b'>8 \\
19
+ \staff "2" e,,2 | % r:3/42
20
+
21
+ \staff "1" \key e \major \time 2/4 <gs' e' gs>4.\p( <b e b'>8 \\
22
+ \staff "2" r8 <b, e>4 <b e>8 \\
23
+ \staff "2" e,,2 | % r:4/41
24
+
25
+ \staff "1" \key e \major \time 2/4 <e' b' e>4. <e b' e>8 \\
26
+ \staff "2" <b, e>8 <b e>4 <b e>8 \\
27
+ \staff "2" e,,2 | % r:5/40
28
+
29
+ \staff "1" \key e \major \time 2/4 <e' b' e>4.\>( <b e b'>8 \\
30
+ \staff "2" r8 <b, e b'>4 <b e b'>8 \\
31
+ \staff "2" e,,2 | % r:6/39
32
+
33
+ \staff "1" \key e \major \time 2/4 <e' b' e>4. <b e b'>8\! \bar "||" \\
34
+ \staff "2" <b, e b'>8 <b e b'>4 <b e b'>8 \bar "||" \\
35
+ \staff "2" e,,2 \bar "||" | % r:7/38
36
+
37
+ \staff "1" \key e \major \time 2/4 <e' gs e'>4.\p( <b gs' b>8 \\
38
+ \staff "2" r8 <b, e>4 <b e>8 \\
39
+ \staff "2" e,,2 | % r:8/37
40
+
41
+ \staff "1" \key e \major \time 2/4 <b'' e b'>4. <gs e' gs>8 \\
42
+ \staff "2" <b, e>8 <b e>4 <b e>8 \\
43
+ \staff "2" e,,2 | % r:9/36
44
+
45
+ \staff "1" \key e \major \time 2/4 <b'' e gs b>4.\>( <gs e' gs>8 \\
46
+ \staff "2" r8 <b, e b'>4 <b e b'>8 \\
47
+ \staff "2" e,,2 | % r:10/35
48
+
49
+ \staff "1" \key e \major \time 2/4 <gs'' e' gs>4. <b gs' b>8\! \bar "||" \\
50
+ \staff "2" <b, e b'>8 <b e b'>4 <b e b'>8 \bar "||" \\
51
+ \staff "2" e,,2 \bar "||" | % r:11/34
52
+
53
+ \staff "1" \key e \major \time 2/4 <cs'' cs'>4\p( <e e'>8 <cs cs'> \\
54
+ \staff "2" r8 <a e' g>4 <a e' g>8 \\
55
+ \staff "2" a,2 | % r:12/33
56
+
57
+ \staff "1" \key e \major \time 2/4 <a'' a'>4 <g g'>8 <a a'> \\
58
+ \staff "2" <a e' g>8 <a e' g>4 <a e' g>8 \\
59
+ \staff "2" a,2 | % r:13/32
60
+
61
+ \staff "1" \key e \major \time 2/4 <fs' fs'>4( <e e'>8 <fs fs'> \\
62
+ \staff "2" r8 <a d fs>4 <a d fs>8 \\
63
+ \staff "2" a,2 | % r:14/31
64
+
65
+ \staff "1" \key e \major \time 2/4 <e' e'>4 <fs fs'>8 <a a'> \\
66
+ \staff "2" <a d fs>8 <a d fs>4 <a d fs>8 \\
67
+ \staff "2" a,2 | % r:15/30
68
+
69
+ \staff "1" \key e \major \time 2/4 <b'' b'>4\<( <d d'>8 <fs fs'> \\
70
+ \staff "2" r8 <b d fs>4 <b d fs>8 \\
71
+ \staff "2" a,2 | % r:16/29
72
+
73
+ \staff "1" \key e \major \time 2/4 <b''' b'>4\> <d, d'> \\
74
+ \staff "2" <b d fs>8 <b d fs>4 <b d fs>8 \\
75
+ \staff "2" a,2 | % r:17/28
76
+
77
+ \staff "1" \key e \major \time 2/4 <b'' b'>4.( <d d'>8 \\
78
+ \staff "2" r8 <b d fs>4 <b d fs>8 \\
79
+ \staff "2" a,2 | % r:18/27
80
+
81
+ \staff "1" \key e \major \time 2/4 <b'' b'>4 <d d'> \\
82
+ \staff "2" <b d fs>8 <b d fs>4 <b d fs>8 \\
83
+ \staff "2" a,2 | % r:19/26
84
+
85
+ \staff "1" \key e \major \time 2/4 <c'' c'>4\p\<( <e e'>8 <a a'> \\
86
+ \staff "2" r8 <c e a>4 <c e a>8 \\
87
+ \staff "2" a,2 | % r:20/25
88
+
89
+ \staff "1" \key e \major \time 2/4 <f'' f'>4\> <e e'>8 <d d'> \\
90
+ \staff "2" <c e a>8 <c e a>4 <c e a>8 \\
91
+ \staff "2" a,2 | % r:21/24
92
+
93
+ \staff "1" \key e \major \time 2/4 <c'' c'>4( <b b'>8 <a a'> \\
94
+ \staff "2" r8 <c e a>4 <c e a>8 \\
95
+ \staff "2" a,2 | % r:22/23
96
+
97
+ \staff "1" \key e \major \time 2/4 <fs' fs'>4 <gs gs'>8 <a a'> \\
98
+ \staff "2" <c e a>8 <c e a>4 <c e a>8 \\
99
+ \staff "2" a,2 | % r:23/22
100
+
101
+ \staff "1" \key e \major \time 2/4 <b'' b'>4\<( <d d'>8 <f f'> \\
102
+ \staff "2" r8 <b d f>4 <b d f>8 \\
103
+ \staff "2" a,2 | % r:24/21
104
+
105
+ \staff "1" \key e \major \time 2/4 <a''' a'>4\> <ds, ds'> \\
106
+ \staff "2" <b d f>8 <b d f>4 <b d f>8 \\
107
+ \staff "2" a,2 | % r:25/20
108
+
109
+ \staff "1" \key e \major \time 2/4 <e'' e'>4.\pp( <g g'>8 \\
110
+ \staff "2" r8 <b e g>4 <b e g>8 \\
111
+ \staff "2" a,2 | % r:26/19
112
+
113
+ \staff "1" \key e \major \time 2/4 <e'' e'>4 <c c'> \bar "||" \\
114
+ \staff "2" <b e g>8 <b e g>4 <b e g>8 \bar "||" \\
115
+ \staff "2" a,2 \bar "||" | % r:27/18
116
+
117
+ \staff "1" \key e \major \time 2/4 <e' e'>4.\pp( <c c'>8 \\
118
+ \staff "2" r8 <a c>4 <a c>8 \\
119
+ \staff "2" a,2 | % r:28/17
120
+
121
+ \staff "1" \key e \major \time 2/4 <a' a'>4 <b b'>8 <c c'> \\
122
+ \staff "2" <a c>8 <a c>4 <a c>8 \\
123
+ \staff "2" a,2 | % r:29/16
124
+
125
+ \staff "1" \key e \major \time 2/4 <e' e'>2( \\
126
+ \staff "2" r8 <gs b>4 <gs b>8 \\
127
+ \staff "2" a,2 | % r:30/15
128
+
129
+ \staff "1" \key e \major \time 2/4 <b' b'>2 \\
130
+ \staff "2" <gs b>8 <gs b>4 <gs b>8 \\
131
+ \staff "2" a,2 | % r:31/14
132
+
133
+ \staff "1" \key e \major \time 2/4 <c' c'>4.( <a a'>8 \\
134
+ \staff "2" r8 <e, a>4 <e a>8 \\
135
+ \staff "2" a,2 | % r:32/13
136
+
137
+ \staff "1" \key e \major \time 2/4 <f f'>4 <g g'>8 <a a'> \\
138
+ \staff "2" <e, a>8 <e a>4 <e a>8 \\
139
+ \staff "2" a,2 | % r:33/12
140
+
141
+ \staff "1" \key e \major \time 2/4 <c' c'>2( \\
142
+ \staff "2" r8 <f, a>4 <f a>8 \\
143
+ \staff "2" a,2 | % r:34/11
144
+
145
+ \staff "1" \key e \major \time 2/4 <b' b'>2 \\
146
+ \staff "2" <f, a>8 <f a>4 <f a>8 \\
147
+ \staff "2" a,2 | % r:35/10
148
+
149
+ \staff "1" \key e \major \time 2/4 _\markup "poco cresc." <a' a'>4.( <fs fs'>8 \\
150
+ \staff "2" r8 <c, fs>4 <c fs>8 \\
151
+ \staff "2" a,2 | % r:36/9
152
+
153
+ \staff "1" \key e \major \time 2/4 <a' a'>4 <g g'>8 <fs fs'> \\
154
+ \staff "2" <cs, f>8 <c fs>4 <c fs>8 \\
155
+ \staff "2" a,2 | % r:37/8
156
+
157
+ \staff "1" \key e \major \time 2/4 <fs' fs'>4.( <gs, gs'>8 \\
158
+ \staff "2" r8 <b, d>4 <b d>8 \\
159
+ \staff "2" a,2 | % r:38/7
160
+
161
+ \staff "1" \key e \major \time 2/4 <b' b'>4 <a a'>8 <gs gs'> \\
162
+ \staff "2" <b, d>8 <b d>4 <b d>8 \\
163
+ \staff "2" a,2 | % r:39/6
164
+
165
+ \staff "1" \key e \major \time 2/4 <b' b'>4.\ff( <d d'>8 \\
166
+ \staff "2" r8 <b, e>4 <b e>8 \\
167
+ \staff "2" a,2 | % r:40/5
168
+
169
+ \staff "1" \key e \major \time 2/4 <b' b'>4 <c c'>8 <b b'> \\
170
+ \staff "2" <b, e>8 <b e>4 <b e>8 \\
171
+ \staff "2" a,2 | % r:41/4
172
+
173
+ \staff "1" \key e \major \time 2/4 _\markup "rit." <d' d'>4.( <gs gs'>8 \\
174
+ \staff "2" r8 <b, e>4 <b e>8 \\
175
+ \staff "2" a,2 | % r:42/3
176
+
177
+ \staff "1" \key e \major \time 2/4 <b' b'>4 <c c'>8 <b b'> \\
178
+ \staff "2" <b, e>8 <b e>4 <b e>8 \\
179
+ \staff "2" a,2 | % r:43/2
180
+
181
+ \staff "1" \key e \major \time 2/4 <e' gs b e>2\ff \\
182
+ \staff "2" <e, gs b>2 \\
183
+ \staff "2" <e,,, e'>2 | % r:44/1
184
+
185
+ \staff "1" \key e \major \time 2/4 <e' g b e>4 r \bar "|." \\
186
+ \staff "2" <e, g b>4 r \bar "|." \\
187
+ \staff "2" <e,,, e'>4 s \bar "|." | % r:45/0
examples/lilylet_generated_llama-seed1.lyl ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [composer "Berlioz, Hector"]
2
+ [genre "Romantic"]
3
+ [instrument "Choral"]
4
+
5
+ \key f \major \numericTimeSignature \time 4/4 \clef "treble" \tempo 4=60 a'4.\p b8 c4 a \\\
6
+ \clef "treble" f4.\p f8 f4 f \\\
7
+ \clef "treble_8" c'4.\p c8 c4 c \\\
8
+ \clef "bass" f,4.\p g8 a4 f | % r:0/55
9
+
10
+ \key f \major \numericTimeSignature \time 4/4 a'4 g f g8 g \\\
11
+ f4 e f e8 e \\\
12
+ c'4 c c c8 c \\\
13
+ c,4 b a g8 g | % r:1/54
14
+
15
+ \key f \major \numericTimeSignature \time 4/4 a'4\< b c\! d \\\
16
+ f4\< g f\! f \\\
17
+ c'4\< c c\! d \\\
18
+ f,,4\< e a\! b | % r:2/53
19
+
20
+ \key f \major \numericTimeSignature \time 4/4 g'4.\> g8 a4\! r8 a \\\
21
+ f4.\> e8 f4\! r8 f \\\
22
+ b'4.\> c8 c4\! r8 c \\\
23
+ c,4.\> c8 f4\! r | % r:3/52
24
+
25
+ \key f \major \numericTimeSignature \time 4/4 a'4. b8 c4 a \\\
26
+ f4. f8 f4 f \\\
27
+ c'4. c8 c4 c \\\
28
+ f,4. g8 a4 f | % r:4/51
29
+
30
+ \key f \major \numericTimeSignature \time 4/4 a'4 g f g8 g \\\
31
+ f4 e e e8 e \\\
32
+ c'4 c c c8 c \\\
33
+ c,4 b a a8 a | % r:5/50
34
+
35
+ \key f \major \numericTimeSignature \time 4/4 f8\<( a d)( f f4.)\!\> b,8 \\\
36
+ d4\< f f4.\!\> f8\! \\\
37
+ a'4\< a d\!\>( g,8\! g) \\\
38
+ d,4\< d g,4.\!\> g8\! | % r:6/49
39
+
40
+ \key f \major \numericTimeSignature \time 4/4 b'4. b8 c4 r \\\
41
+ f4. f8 e4 r \\\
42
+ g'4. g8 g4 r \\\
43
+ g,4. g8 c4 r | % r:7/48
44
+
45
+ \key f \major \numericTimeSignature \time 4/4 r1 \\\
46
+ r1 \\\
47
+ d'4\p c b a \\\
48
+ r1 | % r:8/47
49
+
50
+ \key f \major \numericTimeSignature \time 4/4 d'4\p c b a \\\
51
+ f4\p e d c \\\
52
+ g'2 g4 g \\\
53
+ r1 | % r:9/46
54
+
55
+ \key f \major \numericTimeSignature \time 4/4 g'4( d' c b) \\\
56
+ b4( f' e d) \\\
57
+ g'4( f g g) \\\
58
+ r1 | % r:10/45
59
+
60
+ \key f \major \numericTimeSignature \time 4/4 e'4 d8 c b4 b \\\
61
+ c4 e8 e e4 e \\\
62
+ c'4 b8 a g4 g \\\
63
+ r1 | % r:11/44
64
+
65
+ \key f \major \numericTimeSignature \time 4/4 c'2 c4 c \\\
66
+ e2 e4 e \\\
67
+ a'2 a4 a \\\
68
+ r1 | % r:12/43
69
+
70
+ \key f \major \numericTimeSignature \time 4/4 d'2-> d4 d8 d \\\
71
+ e2-> e4 e8 e \\\
72
+ g'2-> g4 g8 g \\\
73
+ r1 | % r:13/42
74
+
75
+ \key f \major \numericTimeSignature \time 4/4 c'2 c4 c8 c \\\
76
+ e2 e4 e8 e \\\
77
+ a'2 a4 a8 a \\\
78
+ r1 | % r:14/41
79
+
80
+ \key f \major \numericTimeSignature \time 4/4 c'2 c4 c8 c \\\
81
+ e2 e4 e8 e \\\
82
+ g'2 g4 g8 g \\\
83
+ r1 | % r:15/40
84
+
85
+ \key f \major \numericTimeSignature \time 4/4 c'2 c4 c \\\
86
+ f2 f4 f \\\
87
+ f2 f4 f \\\
88
+ r1 | % r:16/39
89
+
90
+ \key f \major \numericTimeSignature \time 4/4 b'2 b4 b \\\
91
+ f2 f4 f \\\
92
+ d'2 d4 d \\\
93
+ r1 | % r:17/38
94
+
95
+ \key f \major \numericTimeSignature \time 4/4 a'2 a4 a \\\
96
+ e2 e4 e \\\
97
+ c'2 c4 c \\\
98
+ r1 | % r:18/37
99
+
100
+ \key f \major \numericTimeSignature \time 4/4 g'2\> g4\! g \\\
101
+ d2\> d4\! d \\\
102
+ b'2\> b4\! b \\\
103
+ r1 | % r:19/36
104
+
105
+ \key f \major \numericTimeSignature \time 4/4 g'2 g4 g \\\
106
+ d2 d4 d \\\
107
+ b'2 b4 b \\\
108
+ r1 | % r:20/35
109
+
110
+ \key f \major \numericTimeSignature \time 4/4 f2 f4 f \\\
111
+ d2 d4 d \\\
112
+ b'2 b4 b \\\
113
+ r1 | % r:21/34
114
+
115
+ \key f \major \numericTimeSignature \time 4/4 g'2\> g \\\
116
+ e2\> e \\\
117
+ c'2\> c \\\
118
+ r1 | % r:22/33
119
+
120
+ \key f \major \numericTimeSignature \time 4/4 a'2 r \\\
121
+ f2 r \\\
122
+ f2 r \\\
123
+ r2 r4 f,\p | % r:23/32
124
+
125
+ \key f \major \numericTimeSignature \time 4/4 r2 r4 a'\p \\\
126
+ r2 r4 f\p \\\
127
+ r2 r4 c'\p \\\
128
+ f,4( e d)( c | % r:24/31
129
+
130
+ \key f \major \numericTimeSignature \time 4/4 a'4( g f)( g \\\
131
+ f4( e d)( e \\\
132
+ c'2 c4( b \\\
133
+ b,4( c f) e | % r:25/30
134
+
135
+ \key f \major \numericTimeSignature \time 4/4 a'4( c b) a \\\
136
+ f4( a g) f \\\
137
+ a'4( c b) c \\\
138
+ f,4( a g) f | % r:26/29
139
+
140
+ \key f \major \numericTimeSignature \time 4/4 g'4. g8 g4 g \\\
141
+ f4. f8 e4 e \\\
142
+ d'4. d8 d4 d \\\
143
+ b,4. b8 b4 b | % r:27/28
144
+
145
+ \key f \major \numericTimeSignature \time 4/4 c'2. c4 \\\
146
+ e2. e4 \\\
147
+ c'2. c4 \\\
148
+ a,2. a4 | % r:28/27
149
+
150
+ \key f \major \numericTimeSignature \time 4/4 d'4 c b a8 a \\\
151
+ d4 e f d8 d \\\
152
+ b'4 c d c8 c \\\
153
+ g,4 c f, d8 d | % r:29/26
154
+
155
+ \key f \major \numericTimeSignature \time 4/4 g'4 a b a8 a \\\
156
+ d4 f g f8 f \\\
157
+ b'4 a d c8 c \\\
158
+ g,4 d' f d8 d | % r:30/25
159
+
160
+ \key f \major \numericTimeSignature \time 4/4 g'4 g g2 \\\
161
+ f4 f8 f e2 \\\
162
+ d'4 d8 d c2 \\\
163
+ b,4 b8 b c2 | % r:31/24
164
+
165
+ \key f \major \numericTimeSignature \time 4/4 a'2 r \\\
166
+ f2 r \\\
167
+ c'2 r \\\
168
+ f,,2 r | % r:32/23
169
+
170
+ \key f \major \numericTimeSignature \time 4/4 a'4\p b c a \\\
171
+ e4.\p e8 e4 e \\\
172
+ c'4.\p c8 c4 c \\\
173
+ a,4.\p b8 c4 a | % r:33/22
174
+
175
+ \key f \major \numericTimeSignature \time 4/4 a'4 g f g8 g \\\
176
+ f4 e e e8 e \\\
177
+ c'4 c c c8 c \\\
178
+ c,4 b a g8 g | % r:34/21
179
+
180
+ \key f \major \numericTimeSignature \time 4/4 a'4\< b c\! d \\\
181
+ f4\< g f\! f \\\
182
+ c'4\< c c\! d \\\
183
+ f,,4\< e a\! b | % r:35/20
184
+
185
+ \key f \major \numericTimeSignature \time 4/4 g'4.\> g8 a4\! r8 a \\\
186
+ f4.\> e8 f4\! r8 f \\\
187
+ b'4.\> c8 c4\! r8 c \\\
188
+ c,4.\> c8 f4\! r | % r:36/19
189
+
190
+ \key f \major \numericTimeSignature \time 4/4 a'4. b8 c4 a \\\
191
+ f4. f8 f4 f \\\
192
+ c'4. c8 c4 c \\\
193
+ f,4. g8 a4 f | % r:37/18
194
+
195
+ \key f \major \numericTimeSignature \time 4/4 a'4 g f g8 g \\\
196
+ f4 e e e8 e \\\
197
+ c'4 c c c8 c \\\
198
+ c,4 b a a8 a | % r:38/17
199
+
200
+ \key f \major \numericTimeSignature \time 4/4 f8\<( a d)( f f4.)\!\> b,8\! \\\
201
+ d4\< f f4.\!\> f8\! \\\
202
+ a'4\< a d\!\>( g,8.)\! g16 \\\
203
+ d,4\< d g,4.\!\> g8\! | % r:39/16
204
+
205
+ \key f \major \numericTimeSignature \time 4/4 b'4. b8 c4 r \\\
206
+ f4. f8 e4 r \\\
207
+ g'4. g8 g4 r \\\
208
+ g,4. g8 c4 r | % r:40/15
209
+
210
+ \key f \major \numericTimeSignature \time 4/4 r1 \\\
211
+ r1 \\\
212
+ d'4\p c b a \\\
213
+ r1 | % r:41/14
214
+
215
+ \key f \major \numericTimeSignature \time 4/4 d'4\p c b a \\\
216
+ f4\p e d c \\\
217
+ g'2 g4 g \\\
218
+ r1 | % r:42/13
219
+
220
+ \key f \major \numericTimeSignature \time 4/4 g'4( d' c b) \\\
221
+ b4( f' e d) \\\
222
+ g'4( f g g) \\\
223
+ r1 | % r:43/12
224
+
225
+ \key f \major \numericTimeSignature \time 4/4 e'4 d8 c b4 b \\\
226
+ c4 e8 e e4 e \\\
227
+ c'4 b8 a g4 g \\\
228
+ r1 | % r:44/11
229
+
230
+ \key f \major \numericTimeSignature \time 4/4 c'2 c4 c \\\
231
+ e2 e4 e \\\
232
+ a'2 a4 a \\\
233
+ r1 | % r:45/10
234
+
235
+ \key f \major \numericTimeSignature \time 4/4 d'2 d4 d8 d \\\
236
+ e2 e4 e8 e \\\
237
+ g'2 g4 g8 g \\\
238
+ r1 | % r:46/9
239
+
240
+ \key f \major \numericTimeSignature \time 4/4 c'2 c4 c8 c \\\
241
+ e2 e4 e8 e \\\
242
+ a'2 a4 a8 a \\\
243
+ r1 | % r:47/8
244
+
245
+ \key f \major \numericTimeSignature \time 4/4 c'2 c4 c8 c \\\
246
+ e2 e4 e8 e \\\
247
+ g'2 g4 g8 g \\\
248
+ r1 | % r:48/7
249
+
250
+ \key f \major \numericTimeSignature \time 4/4 c'2 c4 c \\\
251
+ f2 f4 f \\\
252
+ f2 f4 f \\\
253
+ r1 | % r:49/6
254
+
255
+ \key f \major \numericTimeSignature \time 4/4 b'2 b4 b \\\
256
+ f2 f4 f \\\
257
+ d'2 d4 d \\\
258
+ r1 | % r:50/5
259
+
260
+ \key f \major \numericTimeSignature \time 4/4 a'2 a4 a \\\
261
+ e2 e4 e \\\
262
+ c'2 c4 c \\\
263
+ r1 | % r:51/4
264
+
265
+ \key f \major \numericTimeSignature \time 4/4 g'2\> g4\! g \\\
266
+ d2\> d4\! d \\\
267
+ b'2\> b4\! b \\\
268
+ r1 | % r:52/3
269
+
270
+ \key f \major \numericTimeSignature \time 4/4 g'2 g4 g \\\
271
+ d2 d4 d \\\
272
+ b'2 b4 b \\\
273
+ r1 | % r:53/2
274
+
275
+ \key f \major \numericTimeSignature \time 4/4 f2 f4 f \\\
276
+ d2 d4 d \\\
277
+ b'2 b4 b \\\
278
+ r1 | % r:54/1
279
+
280
+ \key f \major \numericTimeSignature \time 4/4 f1\>\fermata \bar "|." \\\
281
+ c1\>\fermata \bar "|." \\\
282
+ a'1\>\fermata \bar "|." \\\
283
+ r1 \bar "|." | % r:55/0
examples/lilylet_generated_llama-seed6.lyl ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [composer "Haydn, Joseph"]
2
+ [genre "Classical"]
3
+ [instrument "Keyboard"]
4
+
5
+ \staff "1" \key a \major \time 6/8 \clef "treble" \tempo 4=116 e8\p \\
6
+ \staff "2" \clef "bass" r8 | % r:0/90
7
+
8
+ \staff "1" \key a \major \time 6/8 a'8( c b16 d f,4) g16( f \\
9
+ \staff "1" c4 c8 d4 d8 \\
10
+ \staff "2" <a, e'>4 <a e'>8 <a e'>4 <a e'>8 | % r:1/89
11
+
12
+ \staff "1" \key a \major \time 6/8 <d g>4( <c a'>8 <b g'>4) e8 \\
13
+ \staff "2" <e,, e'>4. <e e'>4~ r8 | % r:2/88
14
+
15
+ \staff "1" \key a \major \time 6/8 a'8( c b16 d f,4) g16( f \\
16
+ \staff "1" c4 c8 d4 d8 \\
17
+ \staff "2" <a, e'>4 <a e'>8 <a e'>4 <a e'>8 | % r:3/87
18
+
19
+ \staff "1" \key a \major \time 6/8 <d g>4( <c a'>8 <b g'>4) b'8\f \\
20
+ \staff "2" <e,, e'>4. <e e'>4~ r8 | % r:4/86
21
+
22
+ \staff "1" \key a \major \time 6/8 g''8( e b16) g16. e16( g8 b16 g \\
23
+ \staff "2" <e, b' d>4 <e b' d>8 <e b' d>4 <e b' d>8 | % r:5/85
24
+
25
+ \staff "1" \key a \major \time 6/8 a'16( b c d e f e)( d c b a g \\
26
+ \staff "2" <e, a c>4. <e b' d> | % r:6/84
27
+
28
+ \staff "1" \key a \major \time 6/8 a'16( c e c b a g)( b d f d b \\
29
+ \staff "2" <f, a c>4 <f a c>8 <d f b>4 <d f b>8 | % r:7/83
30
+
31
+ \staff "1" \key a \major \time 6/8 a'4.( g8 r e \\
32
+ \staff "2" e,8( d e e,4) r8 | % r:8/82
33
+
34
+ \staff "1" \key a \major \time 6/8 a'8( c b16 d f,4) g16( f \\
35
+ \staff "1" c4 c8 d4 d8 \\
36
+ \staff "2" <a, e'>4 <a e'>8 <a e'>4 <a e'>8 | % r:9/81
37
+
38
+ \staff "1" \key a \major \time 6/8 <d g>4( <c a'>8 <b g'>4) e8 \\
39
+ \staff "2" <e,, e'>4. <e e'>4~ r8 | % r:10/80
40
+
41
+ \staff "1" \key a \major \time 6/8 a'8( c b16 d f,4) g16( f \\
42
+ \staff "1" c4 c8 d4 d8 \\
43
+ \staff "2" <a, e'>4 <a e'>8 <a e'>4 <a e'>8 | % r:11/79
44
+
45
+ \staff "1" \key a \major \time 6/8 <d g>4( <c a'>8 <b g'>4) e'8\f \\
46
+ \staff "2" <e,, e'>4. <e e'>4~ r8 | % r:12/78
47
+
48
+ \staff "1" \key a \major \time 6/8 d'16( e f e d c b)( c b a g f \\
49
+ \staff "2" <f, a b>4. <f a b> | % r:13/77
50
+
51
+ \staff "1" \key a \major \time 6/8 e16( f g a b c d)( e f g a f \\
52
+ \staff "2" <e, g b>4. <d a' b> | % r:14/76
53
+
54
+ \staff "1" \key a \major \time 6/8 e'16( g b g e b c)( f a f d a \\
55
+ \staff "2" <e, g b>4 <e g b>8 <b f' a>4 <b f' a>8 | % r:15/75
56
+
57
+ \staff "1" \key a \major \time 6/8 f'4.( e8 r b, \\
58
+ \staff "2" e,8( d e e,4) r8 | % r:16/74
59
+
60
+ \staff "1" \key a \major \time 6/8 e'8\p( g f16 d e4) b16( a \\
61
+ \staff "1" g'4 g8 a4 a8 \\
62
+ \staff "2" e4 e8 e4 e8 | % r:17/73
63
+
64
+ \staff "1" \key a \major \time 6/8 <a' d>4( <g e'>8 <f d'>4) a8 \\
65
+ \staff "2" e4. e4 r8 | % r:18/72
66
+
67
+ \staff "1" \key a \major \time 6/8 e'8( g f16 d e4) b16( c \\
68
+ \staff "1" g'4 g8 a4 a8 \\
69
+ \staff "2" e4 e8 e4 e8 | % r:19/71
70
+
71
+ \staff "1" \key a \major \time 6/8 <a' d>4( <g e'>8 <f d'>4) b8\f \\
72
+ \staff "2" e4. e4 r8 | % r:20/70
73
+
74
+ \staff "1" \key a \major \time 6/8 g''8( e b16) g16. e16( g8 b16 g \\
75
+ \staff "2" <e, b' d>4 <e b' d>8 <e b' d>4 <e b' d>8 | % r:21/69
76
+
77
+ \staff "1" \key a \major \time 6/8 a'16( b c d e f e)( d c b a g \\
78
+ \staff "2" <e, a c>4. <e b' d> | % r:22/68
79
+
80
+ \staff "1" \key a \major \time 6/8 a'16( c e c b a g)( b d f d b \\
81
+ \staff "2" <f, a c>4 <f a c>8 <d f b>4 <d f b>8 | % r:23/67
82
+
83
+ \staff "1" \key a \major \time 6/8 a'4.( g8 r e'\p \\
84
+ \staff "2" e,8( d e e,4) r8 | % r:24/66
85
+
86
+ \staff "1" \key a \major \time 6/8 d'16( e f e d c b)( c b a g f \\
87
+ \staff "2" <f, a b>4. <f a b> | % r:25/65
88
+
89
+ \staff "1" \key a \major \time 6/8 e16( f g a b c d)( e f g a f \\
90
+ \staff "2" <e, g b>4. <d a' b> | % r:26/64
91
+
92
+ \staff "1" \key a \major \time 6/8 e'16( g b g e b c)( f a f d a \\
93
+ \staff "2" <e, g b>4 <e g b>8 <b f' a>4 <b f' a>8 | % r:27/63
94
+
95
+ \staff "1" \key a \major \time 6/8 f'4.( e8 r e,\f \\
96
+ \staff "2" e,8( d e e,4) r8 | % r:28/62
97
+
98
+ \staff "1" \key a \major \time 6/8 d'4( c8 b4) b8 \\
99
+ \staff "2" r4 r8 r4 g8 | % r:29/61
100
+
101
+ \staff "1" \key a \major \time 6/8 \grace b'8 a( g a b) r b \\
102
+ \staff "2" f,4( e8 d4) d8 | % r:30/60
103
+
104
+ \staff "1" \key a \major \time 6/8 \grace d'8 c( b c d4) d8 \\
105
+ \staff "2" c,4( b8 a4) a8 | % r:31/59
106
+
107
+ \staff "1" \key a \major \time 6/8 \grace f'8 e( d e e4) r8 \\
108
+ \staff "2" g,4( f8 e4) r8 | % r:32/58
109
+
110
+ \staff "1" \key a \major \time 6/8 e'8\p( d e d)( e d \\
111
+ \staff "2" <g b e>4. <g b e> | % r:33/57
112
+
113
+ \staff "1" \key a \major \time 6/8 f'8( e f f)( e f \\
114
+ \staff "2" <a c e>4. <a c e> | % r:34/56
115
+
116
+ \staff "1" \key a \major \time 6/8 e'8( d e e)( d e \\
117
+ \staff "2" <b d g>4. <b d g> | % r:35/55
118
+
119
+ \staff "1" \key a \major \time 6/8 f'8( e f f)( e f \\
120
+ \staff "2" <a c f>4. <a c f> | % r:36/54
121
+
122
+ \staff "1" \key a \major \time 6/8 b''16\f( a g f e d c d e f g a \\
123
+ \staff "2" <g b e>2. | % r:37/53
124
+
125
+ \staff "1" \key a \major \time 6/8 b''16( a g f e d c d e f g a \\
126
+ \staff "2" <a c f>2. | % r:38/52
127
+
128
+ \staff "1" \key a \major \time 6/8 b''16( c b a g f e d c b a g \\
129
+ \staff "2" <g b e>4. r4 r8 | % r:39/51
130
+
131
+ \staff "1" \key a \major \time 6/8 f16( g a b c d e f g a b c \\
132
+ \staff "2" <a c f>4 r8 r4 r8 | % r:40/50
133
+
134
+ \staff "1" \key a \major \time 6/8 b''16( a g f e d c b a g f e \\
135
+ \staff "2" <b d f>4 r8 r4 r8 | % r:41/49
136
+
137
+ \staff "1" \key a \major \time 6/8 d16( e f g a b c d e f g a \\
138
+ \staff "2" <b d f>4 r8 r4 r8 | % r:42/48
139
+
140
+ \staff "1" \key a \major \time 6/8 g''16( f e d c b a g f e d c \\
141
+ \staff "2" <e, g c>4 r8 r4 r8 | % r:43/47
142
+
143
+ \staff "1" \key a \major \time 6/8 b4 r8 f''( e d \\
144
+ \staff "2" b,16( f' b d b f b,)( f' b d b f | % r:44/46
145
+
146
+ \staff "1" \key a \major \time 6/8 c'4 r8 g'( f e \\
147
+ \staff "2" b,16( g' c e c g b,)( g' c e c g | % r:45/45
148
+
149
+ \staff "1" \key a \major \time 6/8 c'4 r8 a'( g f \\
150
+ \staff "2" b,16( f' a e' a, f b,)( f' a d a f | % r:46/44
151
+
152
+ \staff "1" \key a \major \time 6/8 e'4 r8 b'( a g \\
153
+ \staff "2" b,16( g' b e b g b,)( g' b e b g | % r:47/43
154
+
155
+ \staff "1" \key a \major \time 6/8 f'8\f( g a b)( g e \\
156
+ \staff "2" <b, d g>2. | % r:48/42
157
+
158
+ \staff "1" \key a \major \time 6/8 f'8( g a b)( g e \\
159
+ \staff "2" <b, d g>2. | % r:49/41
160
+
161
+ \staff "1" \key a \major \time 6/8 f'4 r8 <as,, c f>4 r8 \\
162
+ \staff "2" <as, c f>4 r8 <f f'>4 r8 | % r:50/40
163
+
164
+ \staff "1" \key a \major \time 6/8 <b d f>4. r4 f''8\p \\
165
+ \staff "2" <b,, b'>4 r8 r4 r8 | % r:51/39
166
+
167
+ \staff "1" \key a \major \time 6/8 b''4.( d \\
168
+ \staff "2" \clef "treble" b8( d f b, d f | % r:52/38
169
+
170
+ \staff "1" \key a \major \time 6/8 c''4.( a4 f8 \\
171
+ \staff "2" a8( c f a, c f | % r:53/37
172
+
173
+ \staff "1" \key a \major \time 6/8 b''4.( d \\
174
+ \staff "2" b8( d f b, d f | % r:54/36
175
+
176
+ \staff "1" \key a \major \time 6/8 c''4.( a4 f8 \\
177
+ \staff "2" a8( c f a, c f | % r:55/35
178
+
179
+ \staff "1" \key a \major \time 6/8 b''8\f( c b a g f \\
180
+ \staff "2" \clef "bass" d,2. | % r:56/34
181
+
182
+ \staff "1" \key a \major \time 6/8 g''8( a g f e d \\
183
+ \staff "2" e,4. g | % r:57/33
184
+
185
+ \staff "1" \key a \major \time 6/8 c'8( d e a,4) c8 \\
186
+ \staff "2" a4. a, | % r:58/32
187
+
188
+ \staff "1" \key a \major \time 6/8 d'4. r4 f,8 \\
189
+ \staff "2" d,4 r8 r4 r8 | % r:59/31
190
+
191
+ \staff "1" \key a \major \time 6/8 b'4.( d \\
192
+ \staff "2" b,8( d f b, d f | % r:60/30
193
+
194
+ \staff "1" \key a \major \time 6/8 c'4.( a4 f8 \\
195
+ \staff "2" a,8( c f a, c f | % r:61/29
196
+
197
+ \staff "1" \key a \major \time 6/8 b'4.( d \\
198
+ \staff "2" b,8( d f b, d f | % r:62/28
199
+
200
+ \staff "1" \key a \major \time 6/8 c'4.( a4 f8 \\
201
+ \staff "2" a,8( c f a, c f | % r:63/27
202
+
203
+ \staff "1" \key a \major \time 6/8 b'8\f( c b a g f \\
204
+ \staff "2" d,,2. | % r:64/26
205
+
206
+ \staff "1" \key a \major \time 6/8 g'8( a g f e d \\
207
+ \staff "2" e,,4. g | % r:65/25
208
+
209
+ \staff "1" \key a \major \time 6/8 c8( d e a,4) c8 \\
210
+ \staff "2" a,4. a, | % r:66/24
211
+
212
+ \staff "1" \key a \major \time 6/8 d4 r8 r a''\p( f \\
213
+ \staff "2" d,,16 d' f a f d d,4 r8 | % r:67/23
214
+
215
+ \staff "1" \key a \major \time 6/8 d'4 r8 d'( b g \\
216
+ \staff "2" d,,16 d' g b g d d,4 r8 | % r:68/22
217
+
218
+ \staff "1" \key a \major \time 6/8 f'4 r8 d'( a f \\
219
+ \staff "2" d,,16 d' f a f d d,4 r8 | % r:69/21
220
+
221
+ \staff "1" \key a \major \time 6/8 e'4 r8 g( e c \\
222
+ \staff "2" d,,16 e' g a g e d,4 r8 | % r:70/20
223
+
224
+ \staff "1" \key a \major \time 6/8 d'4 r8 a'( f d \\
225
+ \staff "2" d,,16 d' f a f d d,4 r8 | % r:71/19
226
+
227
+ \staff "1" \key a \major \time 6/8 c'4 r8 g'( e c \\
228
+ \staff "2" d,,16 e' g a g e d,4 r8 | % r:72/18
229
+
230
+ \staff "1" \key a \major \time 6/8 d'4 r8 a'( f d \\
231
+ \staff "2" d,,16 d' f a f d d,4 r8 | % r:73/17
232
+
233
+ \staff "1" \key a \major \time 6/8 c'4 r8 g'( e c \\
234
+ \staff "2" d,,16 e' g a g e d,4 r8 | % r:74/16
235
+
236
+ \staff "1" \key a \major \time 6/8 d'8\f( f e d c b \\
237
+ \staff "2" d,,4 r8 r4 r8 | % r:75/15
238
+
239
+ \staff "1" \key a \major \time 6/8 a'8( b c d b g \\
240
+ \staff "2" r2. | % r:76/14
241
+
242
+ \staff "1" \key a \major \time 6/8 f8( e d c d e \\
243
+ \staff "2" r2. | % r:77/13
244
+
245
+ \staff "1" \key a \major \time 6/8 f8( e d c d e \\
246
+ \staff "2" r2. | % r:78/12
247
+
248
+ \staff "1" \key a \major \time 6/8 a'8\p( c b16 d f,4) g16( f \\
249
+ \staff "1" c4 c8 d4 d8 \\
250
+ \staff "2" <a, e'>4 <a e'>8 <a e'>4 <a e'>8 | % r:79/11
251
+
252
+ \staff "1" \key a \major \time 6/8 <d g>4( <c a'>8 <b g'>4) e8 \\
253
+ \staff "2" <e,, e'>4. <e e'>4~ r8 | % r:80/10
254
+
255
+ \staff "1" \key a \major \time 6/8 a'8( c b16 d f,4) g16( f \\
256
+ \staff "1" c4 c8 d4 d8 \\
257
+ \staff "2" <a, e'>4 <a e'>8 <a e'>4 <a e'>8 | % r:81/9
258
+
259
+ \staff "1" \key a \major \time 6/8 <d g>4( <c a'>8 <b g'>4) b'8\f \\
260
+ \staff "2" <e,, e'>4. <e e'>4~ r8 | % r:82/8
261
+
262
+ \staff "1" \key a \major \time 6/8 g''8( e b16) g16. e16( g8 b16 g \\
263
+ \staff "2" <e, b' d>4 <e b' d>8 <e b' d>4 <e b' d>8 | % r:83/7
264
+
265
+ \staff "1" \key a \major \time 6/8 a'16( b c d e f e)( d c b a g \\
266
+ \staff "2" <e, a c>4. <e b' d> | % r:84/6
267
+
268
+ \staff "1" \key a \major \time 6/8 a'16( c e c b a g)( b d f d b \\
269
+ \staff "2" <f, a c>4 <f a c>8 <d f b>4 <d f b>8 | % r:85/5
270
+
271
+ \staff "1" \key a \major \time 6/8 a'4.( g8 r e\p \\
272
+ \staff "2" e,8( d e e,4) r8 | % r:86/4
273
+
274
+ \staff "1" \key a \major \time 6/8 a'8( c b16 d f,4) g16( f \\
275
+ \staff "1" c4 c8 d4 d8 \\
276
+ \staff "2" <a, e'>4 <a e'>8 <a e'>4 <a e'>8 | % r:87/3
277
+
278
+ \staff "1" \key a \major \time 6/8 <d g>4( <c a'>8 <b g'>4) e8\f \\
279
+ \staff "2" <e,, e'>4. <e e'>4~ r8 | % r:88/2
280
+
281
+ \staff "1" \key a \major \time 6/8 \tempo 4=92 a'8( c b16 d f,4) g16( f \\
282
+ \staff "1" c4 c8 d4 d8 \\
283
+ \staff "2" <a, e'>4 <a e'>8 <a e'>4 <a e'>8 | % r:89/1
284
+
285
+ \staff "1" \key a \major \time 6/8 \tempo 4=80 d4( <g c>8 <d g b>4)\fermata r8 \bar "|." \\
286
+ \staff "2" <e,, e'>4. <e e'>4\fermata r8 \bar "|." | % r:90/0
web/score-player.css ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* LilyScript score player — preview + transport. Light theme to match Gradio Soft. */
2
+
3
+ .ls-score-root {
4
+ display: flex;
5
+ flex-direction: column;
6
+ height: 100%;
7
+ min-height: 600px;
8
+ }
9
+
10
+ .ls-preview {
11
+ flex: 1;
12
+ overflow: auto;
13
+ background: #fafafa;
14
+ border: 1px solid #e5e5e5;
15
+ border-radius: 8px;
16
+ padding: 12px;
17
+ position: relative;
18
+ }
19
+
20
+ .ls-status {
21
+ font-size: 12px;
22
+ color: #888;
23
+ min-height: 16px;
24
+ margin-bottom: 4px;
25
+ }
26
+ .ls-status.ls-busy { color: #2b7; }
27
+ .ls-status.ls-err { color: #c0392b; }
28
+
29
+ .ls-svg {
30
+ background: #fff;
31
+ display: inline-block;
32
+ min-width: 100%;
33
+ }
34
+ .ls-svg svg {
35
+ max-width: 100%;
36
+ height: auto;
37
+ }
38
+
39
+ /* playing-note highlight on the SVG */
40
+ .ls-hl {
41
+ fill: #ff6b35 !important;
42
+ stroke: #ff6b35 !important;
43
+ }
44
+
45
+ /* transport bar */
46
+ .ls-player {
47
+ display: flex;
48
+ align-items: center;
49
+ gap: 10px;
50
+ padding: 8px 12px;
51
+ margin-top: 8px;
52
+ background: #f3f3f5;
53
+ border: 1px solid #e5e5e5;
54
+ border-radius: 8px;
55
+ }
56
+
57
+ .ls-btn {
58
+ border: 1px solid #d0d0d6;
59
+ background: #fff;
60
+ color: #333;
61
+ width: 30px;
62
+ height: 30px;
63
+ border-radius: 6px;
64
+ cursor: pointer;
65
+ font-size: 13px;
66
+ line-height: 1;
67
+ display: inline-flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ }
71
+ .ls-btn:hover:not(:disabled) { border-color: #8aa; background: #f0f6ff; }
72
+ .ls-btn:disabled { opacity: 0.4; cursor: not-allowed; }
73
+
74
+ .ls-time {
75
+ font-family: ui-monospace, Consolas, monospace;
76
+ font-size: 12px;
77
+ color: #555;
78
+ min-width: 88px;
79
+ }
80
+
81
+ .ls-progress {
82
+ flex: 1;
83
+ height: 6px;
84
+ background: #ddd;
85
+ border-radius: 3px;
86
+ cursor: pointer;
87
+ overflow: hidden;
88
+ }
89
+ .ls-fill {
90
+ height: 100%;
91
+ width: 0;
92
+ background: #7c5cff;
93
+ transition: width 0.1s linear;
94
+ }
web/score-player.js ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* LilyScript score player — client-side bridge to the lilylet-live-editor pipeline.
2
+ *
3
+ * Pipeline (all in the browser, no server round-trip):
4
+ * lyl text --LilyletLib.parseCode + meiEncoder--> MEI XML
5
+ * MEI --Verovio WASM toolkit--> SVG (preview) + MIDI (playback)
6
+ * MIDI --music-widgets MidiPlayer + soundfont--> audio + note highlight
7
+ *
8
+ * Globals provided by the vendored scripts (loaded via <script src> in <head>):
9
+ * window.LilyletLib { parseCode, meiEncoder, serializeLilyletDoc }
10
+ * window.verovio Verovio module factory (verovio-toolkit-wasm.js)
11
+ * window.musicWidgetsBrowser { MIDI, MidiPlayer, MusicNotation, MidiAudio }
12
+ *
13
+ * Generation gate (the key UX rule): while the model is generating, we render
14
+ * SVG-only and the MIDI player is hidden/disabled. The player is built + shown
15
+ * only once generation has finished (LilyScore.setGenerating(false)). This keeps
16
+ * the live preview cheap during streaming and avoids re-initialising the MIDI
17
+ * engine on every measure-boundary editor sync.
18
+ */
19
+ (function () {
20
+ 'use strict';
21
+
22
+ const SOUNDFONT_URL = window.__LILYSCRIPT_SOUNDFONT_URL || './soundfont/';
23
+
24
+ const state = {
25
+ toolkit: null, // Verovio toolkit
26
+ verovioReady: false,
27
+ audio: null, // { MIDI, MidiPlayer, MusicNotation, MidiAudio }
28
+ audioReady: false,
29
+ player: null, // MidiPlayer instance
30
+ midiData: null,
31
+ generating: false, // gate: true while the model streams
32
+ lastCode: '', // last lyl rendered (dedupe)
33
+ lastMei: null,
34
+ // playback
35
+ isPlaying: false,
36
+ currentTime: 0,
37
+ duration: 0,
38
+ playStartTime: 0,
39
+ lastEventIndex: 0,
40
+ pausedTime: 0,
41
+ updateInterval: null,
42
+ highlighted: new Set(),
43
+ };
44
+
45
+ const els = {}; // cached DOM nodes, filled by mount()
46
+
47
+ const HIGHLIGHT_THROTTLE_MS = 50;
48
+ let lastHighlightUpdate = 0;
49
+
50
+ function log (msg) { console.log('[LilyScore]', msg); }
51
+
52
+ // ---- Verovio init -------------------------------------------------------
53
+
54
+ async function initVerovio () {
55
+ if (state.verovioReady) return state.toolkit;
56
+ if (state._verovioInitPromise) return state._verovioInitPromise;
57
+ if (!window.VerovioInit) { log('VerovioInit global missing'); return null; }
58
+ // VerovioInit() awaits the Emscripten WASM module's readyPromise, then
59
+ // returns a constructed toolkit — the path proven by lilylet-live-editor.
60
+ state._verovioInitPromise = (async function () {
61
+ try {
62
+ const tk = await window.VerovioInit();
63
+ tk.setOptions({ scale: 40, adjustPageHeight: true, breaks: 'auto', pageWidth: 2100, pageHeight: 2970 });
64
+ state.toolkit = tk;
65
+ state.verovioReady = true;
66
+ log('verovio ready ' + (tk.getVersion ? tk.getVersion() : '?'));
67
+ return tk;
68
+ } catch (e) {
69
+ log('verovio init failed: ' + (e && e.message ? e.message : e));
70
+ state._verovioInitPromise = null;
71
+ return null;
72
+ }
73
+ })();
74
+ return state._verovioInitPromise;
75
+ }
76
+
77
+ // ---- lyl -> MEI -> SVG --------------------------------------------------
78
+
79
+ function lylToMei (code) {
80
+ if (!window.LilyletLib) throw new Error('LilyletLib not loaded');
81
+ const doc = window.LilyletLib.parseCode(code);
82
+ const mei = window.LilyletLib.meiEncoder.encode(doc);
83
+ let staffCount = 1;
84
+ if (doc.measures && doc.measures.length) {
85
+ const m0 = doc.measures[0];
86
+ staffCount = m0.parts.reduce((tot, part) => {
87
+ const maxStaff = part.voices.reduce((mx, v) => Math.max(mx, v.staff || 1), 1);
88
+ return tot + maxStaff;
89
+ }, 0) || 1;
90
+ }
91
+ return { mei, measureCount: (doc.measures && doc.measures.length) || 1, staffCount };
92
+ }
93
+
94
+ function injectSvg (svgString) {
95
+ const parser = new DOMParser();
96
+ const doc = parser.parseFromString(svgString, 'image/svg+xml');
97
+ if (doc.querySelector('parsererror')) { log('svg parse error'); return; }
98
+ const svg = doc.querySelector('svg');
99
+ if (!svg) return;
100
+ els.svg.innerHTML = '';
101
+ els.svg.appendChild(document.importNode(svg, true));
102
+ }
103
+
104
+ function setStatus (text, kind) {
105
+ if (!els.status) return;
106
+ els.status.textContent = text || '';
107
+ els.status.className = 'ls-status' + (kind ? ' ls-' + kind : '');
108
+ }
109
+
110
+ // Render lyl -> SVG. Returns true on success. Does NOT touch the MIDI player
111
+ // (that is gated separately on generation state).
112
+ async function render (code) {
113
+ const tk = await initVerovio();
114
+ if (!tk) { setStatus('Verovio not ready', 'err'); return false; }
115
+ code = (code || '').trim();
116
+ if (!code) { els.svg.innerHTML = ''; state.lastCode = ''; state.lastMei = null; updatePlayerVisibility(); return false; }
117
+ if (code === state.lastCode) return true;
118
+ setStatus('Rendering…', 'busy');
119
+ try {
120
+ const { mei, measureCount, staffCount } = lylToMei(code);
121
+ const pageHeight = Math.max(2000, Math.ceil(measureCount / 20) * 2000) * 2 * staffCount;
122
+ tk.setOptions({ scale: 40, adjustPageHeight: true, pageWidth: 2100, pageHeight });
123
+ if (!tk.loadData(mei)) { setStatus('Verovio load failed', 'err'); return false; }
124
+ injectSvg(tk.renderToSVG(1));
125
+ state.lastCode = code;
126
+ state.lastMei = mei;
127
+ setStatus('', '');
128
+ // new score -> existing MIDI is stale
129
+ state.midiData = null;
130
+ if (state.player) { try { state.player.dispose(); } catch (e) {} state.player = null; }
131
+ stop();
132
+ updatePlayerVisibility();
133
+ return true;
134
+ } catch (e) {
135
+ setStatus('Parse error: ' + (e && e.message ? e.message : e), 'err');
136
+ log('render error: ' + (e && e.stack ? e.stack : e));
137
+ return false;
138
+ }
139
+ }
140
+
141
+ // ---- MIDI audio + player (gated: only when NOT generating) ---------------
142
+
143
+ async function initAudio () {
144
+ if (state.audioReady) return true;
145
+ const mw = window.musicWidgetsBrowser;
146
+ if (!mw) { log('musicWidgetsBrowser missing'); return false; }
147
+ state.audio = { MIDI: mw.MIDI, MidiPlayer: mw.MidiPlayer, MusicNotation: mw.MusicNotation, MidiAudio: mw.MidiAudio };
148
+ try {
149
+ await state.audio.MidiAudio.loadPlugin({ soundfontUrl: SOUNDFONT_URL, api: 'webaudio' });
150
+ state.audioReady = true;
151
+ log('audio ready');
152
+ return true;
153
+ } catch (e) {
154
+ log('audio load failed: ' + (e && e.message ? e.message : e));
155
+ return false;
156
+ }
157
+ }
158
+
159
+ // Build a MidiPlayer for the current score's MEI. Idempotent per MEI.
160
+ async function buildPlayer () {
161
+ if (!state.lastMei || !state.toolkit) return false;
162
+ if (!(await initAudio())) return false;
163
+ if (state.player && state.midiData) return true; // already built for this MEI
164
+ try {
165
+ const midiBase64 = state.toolkit.renderToMIDI();
166
+ const bin = atob(midiBase64);
167
+ const bytes = new Uint8Array(bin.length);
168
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
169
+ const raw = state.audio.MIDI.parseMidiData(bytes.buffer);
170
+ const notation = state.audio.MusicNotation.Notation.parseMidi(raw);
171
+ if (!notation.tempos || !notation.tempos.length) notation.tempos = [{ tempo: 500000, tick: 0, time: 0 }];
172
+ state.midiData = notation;
173
+ state.duration = notation.endTime;
174
+ if (state.player) { try { state.player.dispose(); } catch (e) {} }
175
+ state.player = new state.audio.MidiPlayer(notation, { cacheSpan: 400, onMidi: function () {} });
176
+ stop();
177
+ return true;
178
+ } catch (e) {
179
+ log('buildPlayer error: ' + (e && e.message ? e.message : e));
180
+ return false;
181
+ }
182
+ }
183
+
184
+ function findEventIndexAtTime (time) {
185
+ if (!state.midiData) return 0;
186
+ const ev = state.midiData.events;
187
+ let lo = 0, hi = ev.length;
188
+ while (lo < hi) { const mid = (lo + hi) >>> 1; if (ev[mid].time < time) lo = mid + 1; else hi = mid; }
189
+ return lo;
190
+ }
191
+
192
+ function play () {
193
+ if (!state.player || !state.midiData || state.isPlaying || state.generating) return;
194
+ state.isPlaying = true;
195
+ if (state.pausedTime > 0) {
196
+ state.playStartTime = performance.now() - state.pausedTime;
197
+ state.lastEventIndex = findEventIndexAtTime(state.pausedTime);
198
+ } else {
199
+ state.playStartTime = performance.now();
200
+ state.lastEventIndex = 0;
201
+ }
202
+ updateTransport();
203
+ state.updateInterval = setInterval(function () {
204
+ if (!state.isPlaying || !state.midiData) return;
205
+ const elapsed = performance.now() - state.playStartTime;
206
+ state.currentTime = elapsed;
207
+ const ev = state.midiData.events;
208
+ const A = state.audio.MidiAudio;
209
+ for (; state.lastEventIndex < ev.length; state.lastEventIndex++) {
210
+ const e = ev[state.lastEventIndex];
211
+ if (e.time > elapsed) break;
212
+ if (e.data.type === 'channel') {
213
+ const ts = state.playStartTime + e.time;
214
+ if (e.data.subtype === 'noteOn') A.noteOn(e.data.channel, e.data.noteNumber, e.data.velocity, ts);
215
+ else if (e.data.subtype === 'noteOff') A.noteOff(e.data.channel, e.data.noteNumber, ts);
216
+ else if (e.data.subtype === 'programChange') A.programChange(e.data.channel, e.data.programNumber);
217
+ }
218
+ }
219
+ updateHighlightsThrottled(state.currentTime);
220
+ updateProgress();
221
+ if (elapsed >= state.duration) stop();
222
+ }, 30);
223
+ }
224
+
225
+ function pause () {
226
+ if (state.updateInterval) { clearInterval(state.updateInterval); state.updateInterval = null; }
227
+ state.pausedTime = state.currentTime;
228
+ state.isPlaying = false;
229
+ state.audio && state.audio.MidiAudio.stopAllNotes && state.audio.MidiAudio.stopAllNotes();
230
+ updateTransport();
231
+ }
232
+
233
+ function stop () {
234
+ if (state.updateInterval) { clearInterval(state.updateInterval); state.updateInterval = null; }
235
+ state.isPlaying = false;
236
+ state.currentTime = 0;
237
+ state.pausedTime = 0;
238
+ state.lastEventIndex = 0;
239
+ clearHighlights();
240
+ state.audio && state.audio.MidiAudio.stopAllNotes && state.audio.MidiAudio.stopAllNotes();
241
+ updateTransport();
242
+ updateProgress();
243
+ }
244
+
245
+ function seekTo (t) {
246
+ if (!state.midiData) return;
247
+ t = Math.max(0, Math.min(t, state.duration));
248
+ state.audio && state.audio.MidiAudio.stopAllNotes && state.audio.MidiAudio.stopAllNotes();
249
+ state.currentTime = t;
250
+ state.pausedTime = t;
251
+ state.lastEventIndex = findEventIndexAtTime(t);
252
+ if (state.isPlaying) state.playStartTime = performance.now() - t;
253
+ updateHighlights(t);
254
+ updateProgress();
255
+ }
256
+
257
+ // ---- highlight + cursor -------------------------------------------------
258
+
259
+ function updateHighlightsThrottled (time) {
260
+ const now = performance.now();
261
+ if (now - lastHighlightUpdate < HIGHLIGHT_THROTTLE_MS) return;
262
+ lastHighlightUpdate = now;
263
+ updateHighlights(time);
264
+ }
265
+
266
+ function updateHighlights (time) {
267
+ if (!state.toolkit) return;
268
+ try {
269
+ const res = state.toolkit.getElementsAtTime(time);
270
+ const now = new Set(res.notes || []);
271
+ state.highlighted.forEach(function (id) {
272
+ if (!now.has(id)) { const el = document.getElementById(id); if (el) el.classList.remove('ls-hl'); }
273
+ });
274
+ now.forEach(function (id) {
275
+ if (!state.highlighted.has(id)) { const el = document.getElementById(id); if (el) el.classList.add('ls-hl'); }
276
+ });
277
+ state.highlighted = now;
278
+ } catch (e) { /* ignore */ }
279
+ }
280
+
281
+ function clearHighlights () {
282
+ state.highlighted.forEach(function (id) { const el = document.getElementById(id); if (el) el.classList.remove('ls-hl'); });
283
+ state.highlighted = new Set();
284
+ }
285
+
286
+ // ---- transport UI -------------------------------------------------------
287
+
288
+ function fmt (ms) {
289
+ const s = Math.floor(ms / 1000), m = Math.floor(s / 60);
290
+ return m + ':' + String(s % 60).padStart(2, '0');
291
+ }
292
+
293
+ function updateProgress () {
294
+ if (els.fill) els.fill.style.width = (state.duration > 0 ? (state.currentTime / state.duration) * 100 : 0) + '%';
295
+ if (els.time) els.time.textContent = fmt(state.currentTime) + ' / ' + fmt(state.duration);
296
+ }
297
+
298
+ function updateTransport () {
299
+ if (!els.playBtn) return;
300
+ els.playBtn.style.display = state.isPlaying ? 'none' : '';
301
+ els.pauseBtn.style.display = state.isPlaying ? '' : 'none';
302
+ }
303
+
304
+ // Show the player bar only when: not generating, a score is rendered, audio
305
+ // available. While generating we keep SVG visible but hide the transport.
306
+ function updatePlayerVisibility () {
307
+ if (!els.player) return;
308
+ const show = !state.generating && !!state.lastMei;
309
+ els.player.style.display = show ? '' : 'none';
310
+ if (show) buildPlayer().then(function (ok) {
311
+ if (ok) updateProgress();
312
+ els.playBtn.disabled = !ok;
313
+ });
314
+ }
315
+
316
+ // ---- mount + public API -------------------------------------------------
317
+
318
+ function mount (root) {
319
+ if (els.root === root && els.svg) return; // already mounted here
320
+ root.innerHTML = '';
321
+ root.classList.add('ls-score-root');
322
+
323
+ const wrap = document.createElement('div'); wrap.className = 'ls-preview';
324
+ const status = document.createElement('div'); status.className = 'ls-status';
325
+ const svgBox = document.createElement('div'); svgBox.className = 'ls-svg';
326
+ wrap.appendChild(status); wrap.appendChild(svgBox);
327
+
328
+ // transport bar
329
+ const player = document.createElement('div'); player.className = 'ls-player'; player.style.display = 'none';
330
+ player.innerHTML =
331
+ '<button class="ls-btn ls-play" title="Play">▶</button>' +
332
+ '<button class="ls-btn ls-pause" title="Pause" style="display:none">⏸</button>' +
333
+ '<button class="ls-btn ls-stop" title="Stop">■</button>' +
334
+ '<span class="ls-time">0:00 / 0:00</span>' +
335
+ '<div class="ls-progress"><div class="ls-fill"></div></div>';
336
+
337
+ root.appendChild(wrap); root.appendChild(player);
338
+
339
+ els.root = root; els.svg = svgBox; els.status = status; els.player = player;
340
+ els.playBtn = player.querySelector('.ls-play');
341
+ els.pauseBtn = player.querySelector('.ls-pause');
342
+ els.stopBtn = player.querySelector('.ls-stop');
343
+ els.time = player.querySelector('.ls-time');
344
+ els.progress = player.querySelector('.ls-progress');
345
+ els.fill = player.querySelector('.ls-fill');
346
+
347
+ els.playBtn.addEventListener('click', play);
348
+ els.pauseBtn.addEventListener('click', pause);
349
+ els.stopBtn.addEventListener('click', stop);
350
+ els.progress.addEventListener('click', function (e) {
351
+ if (!state.midiData) return;
352
+ const r = els.progress.getBoundingClientRect();
353
+ seekTo(state.duration * ((e.clientX - r.left) / r.width));
354
+ });
355
+ // click a note in the score -> seek there
356
+ els.svg.addEventListener('click', function (e) {
357
+ if (!state.toolkit || state.generating) return;
358
+ const t = e.target.closest && e.target.closest('.note, .chord, .rest');
359
+ if (!t || !t.id) return;
360
+ const time = state.toolkit.getTimeForElement(t.id);
361
+ if (typeof time === 'number') seekTo(time);
362
+ });
363
+
364
+ initVerovio(); // warm up early
365
+ }
366
+
367
+ // Public API consumed by app.py's injected glue.
368
+ window.LilyScore = {
369
+ mount: mount,
370
+ // Render lyl text to the SVG preview. Safe to call repeatedly (deduped).
371
+ render: function (code) { return render(code); },
372
+ // Generation gate. While generating: SVG-only, transport hidden, playback
373
+ // stopped. On finish: re-render final text and reveal the player.
374
+ setGenerating: function (flag) {
375
+ flag = !!flag;
376
+ if (flag === state.generating) return;
377
+ state.generating = flag;
378
+ if (flag) { stop(); }
379
+ updatePlayerVisibility();
380
+ },
381
+ isReady: function () { return state.verovioReady; },
382
+ };
383
+ log('score-player.js loaded');
384
+ })();
web/soundfont/acoustic_grand_piano-mp3.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0add282b7789594511eabd511bb7e2d767b3720421d827d849dc7364fb44e4c7
3
+ size 1434960
web/soundfont/acoustic_grand_piano-ogg.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4bba4b9e34e6e3508bccff9f76213d17079e1f04fd6107398350a9df1f7457a0
3
+ size 1707887
web/vendor/lilylet.bundle.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:51d680585d5628989b6d3c145a2b1a0cdd5010549c958fe512fca77d809f617a
3
+ size 586799
web/vendor/musicWidgetsBrowser.umd.min.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ddc96a5ab24a6751bb6f345255fa78bb650d01ba503261ce88d1cb3d125ccbff
3
+ size 62239
web/vendor/verovio.bundle.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5c9210b71d6ac6d8b63b6cb4e4764a36fec17b73179d1465a4e9bf1cb9f848af
3
+ size 7652579