k-l-lambda commited on
Commit
51e9bed
·
1 Parent(s): cc88aa8

put score-player.js at front of other js scripts.

Browse files
Files changed (2) hide show
  1. app.py +14 -5
  2. web/score-player.js +29 -6
app.py CHANGED
@@ -403,17 +403,26 @@ def build_head ():
403
  the vendored copy. Gradio delivers this via its client config and injects it
404
  on the frontend, so absolute `/gradio_api/file=` URLs resolve correctly.'''
405
  vendor = os.path.join(WEB_DIR, 'vendor')
 
 
 
 
 
 
 
 
 
406
  scripts = [
407
- os.path.join(vendor, 'lilylet.bundle.js'),
408
- os.path.join(vendor, 'verovio.bundle.js'),
 
409
  os.path.join(vendor, 'musicWidgetsBrowser.umd.min.js'),
410
  # FluidSynth audio backend: js-synthesizer UMD (window.JSSynth) + our adapter
411
- # (window.LilyFluidAudio). Must precede score-player.js, which calls
412
- # LilyFluidAudio.init() in initAudio(). The libfluidsynth runtime + gm.sf3 are
413
  # loaded on demand by the adapter from web/fluid/ and web/soundfont/.
414
  os.path.join(vendor, 'js-synthesizer.min.js'),
415
  os.path.join(WEB_DIR, 'fluid-audio.js'),
416
- os.path.join(WEB_DIR, 'score-player.js'),
417
  # our own CodeMirror 6 editor (CM + grammar-derived lilylet() highlighter) and
418
  # the mount/bridge script that wires it to the hidden #ls-editor-state textbox.
419
  os.path.join(vendor, 'lyl-editor.bundle.js'),
 
403
  the vendored copy. Gradio delivers this via its client config and injects it
404
  on the frontend, so absolute `/gradio_api/file=` URLs resolve correctly.'''
405
  vendor = os.path.join(WEB_DIR, 'vendor')
406
+ # ORDER MATTERS for cold-load UX. score-player.js (28KB) defines window.LilyScore
407
+ # and self-mounts the player as soon as it executes, replacing the loading spinner.
408
+ # The heavy deps it uses — verovio.bundle.js (7.6MB!), music-widgets, the FluidSynth
409
+ # adapter — are accessed LAZILY (initVerovio/initAudio await their globals via
410
+ # waitForGlobal), so they must NOT block score-player.js from running. We therefore
411
+ # load score-player.js FIRST; if it sat behind verovio (synchronous <head> order),
412
+ # a slow cold load would leave the spinner stuck for the whole 7.6MB download before
413
+ # LilyScore — and the self-mount inside it — could run. lilylet.bundle.js stays ahead
414
+ # because lylToMei() needs window.LilyletLib synchronously at render time (no waiter).
415
  scripts = [
416
+ os.path.join(WEB_DIR, 'score-player.js'), # defines LilyScore + self-mounts (load first!)
417
+ os.path.join(vendor, 'lilylet.bundle.js'), # window.LilyletLib (used synchronously by lylToMei)
418
+ os.path.join(vendor, 'verovio.bundle.js'), # 7.6MB; awaited lazily via VerovioInit
419
  os.path.join(vendor, 'musicWidgetsBrowser.umd.min.js'),
420
  # FluidSynth audio backend: js-synthesizer UMD (window.JSSynth) + our adapter
421
+ # (window.LilyFluidAudio). score-player.js awaits these lazily in initAudio()
422
+ # (no longer a hard ordering requirement). The libfluidsynth runtime + gm.sf3 are
423
  # loaded on demand by the adapter from web/fluid/ and web/soundfont/.
424
  os.path.join(vendor, 'js-synthesizer.min.js'),
425
  os.path.join(WEB_DIR, 'fluid-audio.js'),
 
426
  # our own CodeMirror 6 editor (CM + grammar-derived lilylet() highlighter) and
427
  # the mount/bridge script that wires it to the hidden #ls-editor-state textbox.
428
  os.path.join(vendor, 'lyl-editor.bundle.js'),
web/score-player.js CHANGED
@@ -50,17 +50,38 @@
50
 
51
  function log (msg) { console.log('[LilyScore]', msg); }
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  // ---- Verovio init -------------------------------------------------------
54
 
55
  async function initVerovio () {
56
  if (state.verovioReady) return state.toolkit;
57
  if (state._verovioInitPromise) return state._verovioInitPromise;
58
- if (!window.VerovioInit) { log('VerovioInit global missing'); return null; }
59
- // VerovioInit() awaits the Emscripten WASM module's readyPromise, then
60
- // returns a constructed toolkit — the path proven by lilylet-live-editor.
61
  state._verovioInitPromise = (async function () {
 
 
 
 
 
62
  try {
63
- const tk = await window.VerovioInit();
64
  tk.setOptions({ scale: 40, adjustPageHeight: true, breaks: 'auto', pageWidth: 2100, pageHeight: 2970 });
65
  state.toolkit = tk;
66
  state.verovioReady = true;
@@ -177,8 +198,10 @@
177
 
178
  async function initAudio () {
179
  if (state.audioReady) return true;
180
- const mw = window.musicWidgetsBrowser;
181
- if (!mw) { log('musicWidgetsBrowser missing'); return false; }
 
 
182
  // MIDI/MidiPlayer/MusicNotation come from music-widgets; the *audio backend* is
183
  // FluidSynth (js-synthesizer + gm.sf3, 128 GM instruments) via LilyFluidAudio,
184
  // which falls back to music-widgets' single-piano MidiAudio while the large GM
 
50
 
51
  function log (msg) { console.log('[LilyScore]', msg); }
52
 
53
+ // Wait for a global (set by a vendored <script>) to appear. Resolves with the
54
+ // value, or null on timeout. Needed because score-player.js now loads BEFORE the
55
+ // 7.6MB verovio.bundle.js in <head> (so LilyScore is defined — and the player
56
+ // mounts — early on a cold/slow load, instead of being blocked behind verovio's
57
+ // download). The verovio-dependent paths therefore can't assume window.VerovioInit
58
+ // exists yet; they await it here. Poll is cheap (100ms) and self-clearing.
59
+ function waitForGlobal (name, timeoutMs) {
60
+ if (window[name]) return Promise.resolve(window[name]);
61
+ return new Promise(function (resolve) {
62
+ var start = Date.now();
63
+ var iv = setInterval(function () {
64
+ if (window[name]) { clearInterval(iv); resolve(window[name]); }
65
+ else if (timeoutMs && Date.now() - start > timeoutMs) { clearInterval(iv); resolve(null); }
66
+ }, 100);
67
+ });
68
+ }
69
+
70
  // ---- Verovio init -------------------------------------------------------
71
 
72
  async function initVerovio () {
73
  if (state.verovioReady) return state.toolkit;
74
  if (state._verovioInitPromise) return state._verovioInitPromise;
75
+ // Set the promise BEFORE the first await so concurrent callers (the mount-time
76
+ // warmup + the first render) share ONE init and we never construct verovio twice.
 
77
  state._verovioInitPromise = (async function () {
78
+ // verovio.bundle.js now loads after us; wait (up to 5 min) for its global.
79
+ const VInit = await waitForGlobal('VerovioInit', 300000);
80
+ if (!VInit) { log('VerovioInit global missing (timeout)'); state._verovioInitPromise = null; return null; }
81
+ // VerovioInit() awaits the Emscripten WASM module's readyPromise, then
82
+ // returns a constructed toolkit — the path proven by lilylet-live-editor.
83
  try {
84
+ const tk = await VInit();
85
  tk.setOptions({ scale: 40, adjustPageHeight: true, breaks: 'auto', pageWidth: 2100, pageHeight: 2970 });
86
  state.toolkit = tk;
87
  state.verovioReady = true;
 
198
 
199
  async function initAudio () {
200
  if (state.audioReady) return true;
201
+ // music-widgets + the FluidSynth adapter (js-synthesizer.min.js + fluid-audio.js)
202
+ // load after us in <head>; wait for the music-widgets global to appear.
203
+ const mw = await waitForGlobal('musicWidgetsBrowser', 300000);
204
+ if (!mw) { log('musicWidgetsBrowser missing (timeout)'); return false; }
205
  // MIDI/MidiPlayer/MusicNotation come from music-widgets; the *audio backend* is
206
  // FluidSynth (js-synthesizer + gm.sf3, 128 GM instruments) via LilyFluidAudio,
207
  // which falls back to music-widgets' single-piano MidiAudio while the large GM