xribene commited on
Commit
a9fcdaa
·
1 Parent(s): 77e95a7

not working

Browse files
frontend/index.html CHANGED
@@ -9,10 +9,22 @@
9
 
10
  <script src="https://cdn.jsdelivr.net/npm/vexflow@4.2.3/build/cjs/vexflow.min.js"></script>
11
  <script src="https://cdn.jsdelivr.net/npm/vextab@2.0.13/releases/vextab-div.js"></script>
 
12
  <title>Guitar Diff</title>
13
  </head>
14
  <body>
15
- <div id="app"></div>
 
 
 
 
 
 
 
16
  <script type="module" src="/src/main.js"></script>
 
 
 
 
17
  </body>
18
  </html>
 
9
 
10
  <script src="https://cdn.jsdelivr.net/npm/vexflow@4.2.3/build/cjs/vexflow.min.js"></script>
11
  <script src="https://cdn.jsdelivr.net/npm/vextab@2.0.13/releases/vextab-div.js"></script>
12
+ <script src="https://cdn.jsdelivr.net/npm/@coderline/alphatab@1.3.0-alpha.874/dist/alphaTab.min.js"></script>
13
  <title>Guitar Diff</title>
14
  </head>
15
  <body>
16
+ <div id="app"></div>
17
+ <!-- <div id="alphaTab" data-tex="true">
18
+ \title "Hello alphaTab"
19
+ .
20
+ :4 0.6 1.6 3.6 0.5 2.5 3.5 0.4 2.4 |
21
+ 3.4 0.3 2.3 0.2 1.2 3.2 0.1 1.1 |
22
+ 3.1.1
23
+ </div> -->
24
  <script type="module" src="/src/main.js"></script>
25
+ <!-- <script type="text/javascript">
26
+ const element = document.getElementById('alphaTab');
27
+ const api = new alphaTab.AlphaTabApi(element);
28
+ </script> -->
29
  </body>
30
  </html>
frontend/public/Fantasia.xml ADDED
The diff for this file is too large to render. See raw diff
 
frontend/public/FantasiaBar.musicxml ADDED
@@ -0,0 +1,501 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
3
+ <score-partwise version="3.1">
4
+ <work>
5
+ <work-title>Fantasia no.2 </work-title>
6
+ </work>
7
+ <identification>
8
+ <creator type="composer">Michalis Sourvinos</creator>
9
+ <encoding>
10
+ <software>MuseScore 3.6.2</software>
11
+ <encoding-date>2024-01-02</encoding-date>
12
+ <supports element="accidental" type="yes"/>
13
+ <supports element="beam" type="yes"/>
14
+ <supports element="print" attribute="new-page" type="yes" value="yes"/>
15
+ <supports element="print" attribute="new-system" type="yes" value="yes"/>
16
+ <supports element="stem" type="yes"/>
17
+ </encoding>
18
+ </identification>
19
+ <defaults>
20
+ <scaling>
21
+ <millimeters>6.99912</millimeters>
22
+ <tenths>40</tenths>
23
+ </scaling>
24
+ <page-layout>
25
+ <page-height>1596.77</page-height>
26
+ <page-width>1233.87</page-width>
27
+ <page-margins type="even">
28
+ <left-margin>85.7251</left-margin>
29
+ <right-margin>85.7251</right-margin>
30
+ <top-margin>85.7251</top-margin>
31
+ <bottom-margin>85.7251</bottom-margin>
32
+ </page-margins>
33
+ <page-margins type="odd">
34
+ <left-margin>85.7251</left-margin>
35
+ <right-margin>85.7251</right-margin>
36
+ <top-margin>85.7251</top-margin>
37
+ <bottom-margin>85.7251</bottom-margin>
38
+ </page-margins>
39
+ </page-layout>
40
+ <word-font font-family="Edwin" font-size="10"/>
41
+ <lyric-font font-family="Edwin" font-size="10"/>
42
+ </defaults>
43
+ <credit page="1">
44
+ <credit-type>title</credit-type>
45
+ <credit-words default-x="616.935" default-y="1511.09" justify="center" valign="top" font-size="22">Fantasia no.2 </credit-words>
46
+ </credit>
47
+ <credit page="1">
48
+ <credit-type>subtitle</credit-type>
49
+ <credit-words default-x="616.935" default-y="1453.94" justify="center" valign="top" font-size="16">in A major</credit-words>
50
+ </credit>
51
+ <credit page="1">
52
+ <credit-type>composer</credit-type>
53
+ <credit-words default-x="1148.14" default-y="1411.09" justify="right" valign="top">Michalis Sourvinos</credit-words>
54
+ </credit>
55
+ <part-list>
56
+ <part-group type="start" number="1">
57
+ <group-symbol>none</group-symbol>
58
+ </part-group>
59
+ <score-part id="P1">
60
+ <part-name>Classical Guitar</part-name>
61
+ <part-abbreviation>Guit.</part-abbreviation>
62
+ <score-instrument id="P1-I1">
63
+ <instrument-name>Classical Guitar</instrument-name>
64
+ </score-instrument>
65
+ <midi-device id="P1-I1" port="1"></midi-device>
66
+ <midi-instrument id="P1-I1">
67
+ <midi-channel>1</midi-channel>
68
+ <midi-program>25</midi-program>
69
+ <volume>78.7402</volume>
70
+ <pan>0</pan>
71
+ </midi-instrument>
72
+ </score-part>
73
+ <part-group type="stop" number="1"/>
74
+ </part-list>
75
+ <part id="P1">
76
+ <measure number="0" implicit="yes" width="400.31">
77
+ <print>
78
+ <system-layout>
79
+ <system-margins>
80
+ <left-margin>50.00</left-margin>
81
+ <right-margin>0.00</right-margin>
82
+ </system-margins>
83
+ <top-system-distance>170.00</top-system-distance>
84
+ </system-layout>
85
+ <staff-layout number="2">
86
+ <staff-distance>70.86</staff-distance>
87
+ </staff-layout>
88
+ </print>
89
+ <attributes>
90
+ <divisions>4</divisions>
91
+ <key>
92
+ <fifths>3</fifths>
93
+ </key>
94
+ <time>
95
+ <beats>2</beats>
96
+ <beat-type>4</beat-type>
97
+ </time>
98
+ <staves>2</staves>
99
+ <clef number="1">
100
+ <sign>G</sign>
101
+ <line>2</line>
102
+ <clef-octave-change>-1</clef-octave-change>
103
+ </clef>
104
+ <clef number="2">
105
+ <sign>TAB</sign>
106
+ <line>5</line>
107
+ </clef>
108
+ <staff-details number="2">
109
+ <staff-lines>6</staff-lines>
110
+ <staff-tuning line="1">
111
+ <tuning-step>E</tuning-step>
112
+ <tuning-octave>2</tuning-octave>
113
+ </staff-tuning>
114
+ <staff-tuning line="2">
115
+ <tuning-step>A</tuning-step>
116
+ <tuning-octave>2</tuning-octave>
117
+ </staff-tuning>
118
+ <staff-tuning line="3">
119
+ <tuning-step>D</tuning-step>
120
+ <tuning-octave>3</tuning-octave>
121
+ </staff-tuning>
122
+ <staff-tuning line="4">
123
+ <tuning-step>G</tuning-step>
124
+ <tuning-octave>3</tuning-octave>
125
+ </staff-tuning>
126
+ <staff-tuning line="5">
127
+ <tuning-step>B</tuning-step>
128
+ <tuning-octave>3</tuning-octave>
129
+ </staff-tuning>
130
+ <staff-tuning line="6">
131
+ <tuning-step>E</tuning-step>
132
+ <tuning-octave>4</tuning-octave>
133
+ </staff-tuning>
134
+ </staff-details>
135
+ </attributes>
136
+ <direction placement="above">
137
+ <direction-type>
138
+ <metronome parentheses="no" default-x="-37.68" relative-y="20.00">
139
+ <beat-unit>quarter</beat-unit>
140
+ <per-minute>60</per-minute>
141
+ </metronome>
142
+ </direction-type>
143
+ <staff>1</staff>
144
+ <sound tempo="60"/>
145
+ </direction>
146
+ <note>
147
+ <rest/>
148
+ <duration>2</duration>
149
+ <voice>1</voice>
150
+ <type>eighth</type>
151
+ <staff>1</staff>
152
+ </note>
153
+ <note default-x="260.40" default-y="-5.00">
154
+ <pitch>
155
+ <step>E</step>
156
+ <octave>4</octave>
157
+ </pitch>
158
+ <duration>2</duration>
159
+ <voice>1</voice>
160
+ <type>eighth</type>
161
+ <stem>down</stem>
162
+ <staff>1</staff>
163
+ </note>
164
+ <backup>
165
+ <duration>4</duration>
166
+ </backup>
167
+ <note>
168
+ <rest/>
169
+ <duration>2</duration>
170
+ <voice>5</voice>
171
+ <type>eighth</type>
172
+ <staff>2</staff>
173
+ </note>
174
+ <note default-x="263.30" default-y="-110.86">
175
+ <pitch>
176
+ <step>E</step>
177
+ <octave>4</octave>
178
+ </pitch>
179
+ <duration>2</duration>
180
+ <voice>5</voice>
181
+ <type>eighth</type>
182
+ <stem>down</stem>
183
+ <staff>2</staff>
184
+ <notations>
185
+ <technical>
186
+ <string>1</string>
187
+ <fret>0</fret>
188
+ </technical>
189
+ </notations>
190
+ </note>
191
+ </measure>
192
+ <measure number="1" width="612.11">
193
+ <note default-x="16.50" default-y="30.00">
194
+ <pitch>
195
+ <step>E</step>
196
+ <octave>5</octave>
197
+ </pitch>
198
+ <duration>4</duration>
199
+ <tie type="start"/>
200
+ <voice>1</voice>
201
+ <type>quarter</type>
202
+ <stem>up</stem>
203
+ <staff>1</staff>
204
+ <notations>
205
+ <tied type="start"/>
206
+ </notations>
207
+ </note>
208
+ <note default-x="224.07" default-y="30.00">
209
+ <pitch>
210
+ <step>E</step>
211
+ <octave>5</octave>
212
+ </pitch>
213
+ <duration>1</duration>
214
+ <tie type="stop"/>
215
+ <voice>1</voice>
216
+ <type>16th</type>
217
+ <stem>up</stem>
218
+ <staff>1</staff>
219
+ <beam number="1">begin</beam>
220
+ <beam number="2">begin</beam>
221
+ <notations>
222
+ <tied type="stop"/>
223
+ </notations>
224
+ </note>
225
+ <note default-x="318.42" default-y="25.00">
226
+ <pitch>
227
+ <step>D</step>
228
+ <octave>5</octave>
229
+ </pitch>
230
+ <duration>1</duration>
231
+ <voice>1</voice>
232
+ <type>16th</type>
233
+ <stem>up</stem>
234
+ <staff>1</staff>
235
+ <beam number="1">continue</beam>
236
+ <beam number="2">continue</beam>
237
+ </note>
238
+ <note default-x="412.77" default-y="20.00">
239
+ <pitch>
240
+ <step>C</step>
241
+ <alter>1</alter>
242
+ <octave>5</octave>
243
+ </pitch>
244
+ <duration>1</duration>
245
+ <voice>1</voice>
246
+ <type>16th</type>
247
+ <stem>up</stem>
248
+ <staff>1</staff>
249
+ <beam number="1">continue</beam>
250
+ <beam number="2">continue</beam>
251
+ </note>
252
+ <note default-x="507.12" default-y="15.00">
253
+ <pitch>
254
+ <step>B</step>
255
+ <octave>4</octave>
256
+ </pitch>
257
+ <duration>1</duration>
258
+ <voice>1</voice>
259
+ <type>16th</type>
260
+ <stem>up</stem>
261
+ <staff>1</staff>
262
+ <beam number="1">end</beam>
263
+ <beam number="2">end</beam>
264
+ </note>
265
+ <backup>
266
+ <duration>8</duration>
267
+ </backup>
268
+ <note default-x="16.50" default-y="-60.00">
269
+ <pitch>
270
+ <step>A</step>
271
+ <octave>2</octave>
272
+ </pitch>
273
+ <duration>8</duration>
274
+ <voice>2</voice>
275
+ <type>half</type>
276
+ <stem>down</stem>
277
+ <staff>1</staff>
278
+ </note>
279
+ <backup>
280
+ <duration>4</duration>
281
+ </backup>
282
+ <note default-x="224.07" default-y="-20.00">
283
+ <pitch>
284
+ <step>B</step>
285
+ <octave>3</octave>
286
+ </pitch>
287
+ <duration>4</duration>
288
+ <voice>3</voice>
289
+ <type>quarter</type>
290
+ <stem>down</stem>
291
+ <staff>1</staff>
292
+ </note>
293
+ <note default-x="224.07" default-y="0.00">
294
+ <chord/>
295
+ <pitch>
296
+ <step>F</step>
297
+ <alter>1</alter>
298
+ <octave>4</octave>
299
+ </pitch>
300
+ <duration>4</duration>
301
+ <voice>3</voice>
302
+ <type>quarter</type>
303
+ <stem>down</stem>
304
+ <staff>1</staff>
305
+ </note>
306
+ <note default-x="224.07" default-y="10.00">
307
+ <chord/>
308
+ <pitch>
309
+ <step>A</step>
310
+ <octave>4</octave>
311
+ </pitch>
312
+ <duration>4</duration>
313
+ <voice>3</voice>
314
+ <type>quarter</type>
315
+ <stem>down</stem>
316
+ <staff>1</staff>
317
+ </note>
318
+ <backup>
319
+ <duration>8</duration>
320
+ </backup>
321
+ <note default-x="14.80" default-y="-110.86">
322
+ <pitch>
323
+ <step>E</step>
324
+ <octave>5</octave>
325
+ </pitch>
326
+ <duration>4</duration>
327
+ <tie type="start"/>
328
+ <voice>5</voice>
329
+ <type>quarter</type>
330
+ <stem>up</stem>
331
+ <staff>2</staff>
332
+ <notations>
333
+ <tied type="start"/>
334
+ <technical>
335
+ <string>1</string>
336
+ <fret>12</fret>
337
+ </technical>
338
+ </notations>
339
+ </note>
340
+ <note default-x="222.37" default-y="-110.86">
341
+ <pitch>
342
+ <step>E</step>
343
+ <octave>5</octave>
344
+ </pitch>
345
+ <duration>1</duration>
346
+ <tie type="stop"/>
347
+ <voice>5</voice>
348
+ <type>16th</type>
349
+ <stem>up</stem>
350
+ <staff>2</staff>
351
+ <beam number="1">begin</beam>
352
+ <beam number="2">begin</beam>
353
+ <notations>
354
+ <tied type="stop"/>
355
+ <technical>
356
+ <string>1</string>
357
+ <fret>12</fret>
358
+ </technical>
359
+ </notations>
360
+ </note>
361
+ <note default-x="316.72" default-y="-110.86">
362
+ <pitch>
363
+ <step>D</step>
364
+ <octave>5</octave>
365
+ </pitch>
366
+ <duration>1</duration>
367
+ <voice>5</voice>
368
+ <type>16th</type>
369
+ <stem>up</stem>
370
+ <staff>2</staff>
371
+ <beam number="1">continue</beam>
372
+ <beam number="2">continue</beam>
373
+ <notations>
374
+ <technical>
375
+ <string>1</string>
376
+ <fret>10</fret>
377
+ </technical>
378
+ </notations>
379
+ </note>
380
+ <note default-x="415.67" default-y="-110.86">
381
+ <pitch>
382
+ <step>C</step>
383
+ <alter>1</alter>
384
+ <octave>5</octave>
385
+ </pitch>
386
+ <duration>1</duration>
387
+ <voice>5</voice>
388
+ <type>16th</type>
389
+ <stem>up</stem>
390
+ <staff>2</staff>
391
+ <beam number="1">continue</beam>
392
+ <beam number="2">continue</beam>
393
+ <notations>
394
+ <technical>
395
+ <string>1</string>
396
+ <fret>9</fret>
397
+ </technical>
398
+ </notations>
399
+ </note>
400
+ <note default-x="510.02" default-y="-110.86">
401
+ <pitch>
402
+ <step>B</step>
403
+ <octave>4</octave>
404
+ </pitch>
405
+ <duration>1</duration>
406
+ <voice>5</voice>
407
+ <type>16th</type>
408
+ <stem>up</stem>
409
+ <staff>2</staff>
410
+ <beam number="1">end</beam>
411
+ <beam number="2">end</beam>
412
+ <notations>
413
+ <technical>
414
+ <string>1</string>
415
+ <fret>7</fret>
416
+ </technical>
417
+ </notations>
418
+ </note>
419
+ <backup>
420
+ <duration>8</duration>
421
+ </backup>
422
+ <note default-x="19.40" default-y="-170.86">
423
+ <pitch>
424
+ <step>A</step>
425
+ <octave>2</octave>
426
+ </pitch>
427
+ <duration>8</duration>
428
+ <voice>6</voice>
429
+ <type>half</type>
430
+ <stem>down</stem>
431
+ <staff>2</staff>
432
+ <notations>
433
+ <technical>
434
+ <string>5</string>
435
+ <fret>0</fret>
436
+ </technical>
437
+ </notations>
438
+ </note>
439
+ <backup>
440
+ <duration>4</duration>
441
+ </backup>
442
+ <note default-x="226.97" default-y="-155.86">
443
+ <pitch>
444
+ <step>B</step>
445
+ <octave>3</octave>
446
+ </pitch>
447
+ <duration>4</duration>
448
+ <voice>7</voice>
449
+ <type>quarter</type>
450
+ <stem>up</stem>
451
+ <staff>2</staff>
452
+ <notations>
453
+ <technical>
454
+ <string>4</string>
455
+ <fret>9</fret>
456
+ </technical>
457
+ </notations>
458
+ </note>
459
+ <note default-x="222.37" default-y="-140.86">
460
+ <chord/>
461
+ <pitch>
462
+ <step>F</step>
463
+ <alter>1</alter>
464
+ <octave>4</octave>
465
+ </pitch>
466
+ <duration>4</duration>
467
+ <voice>7</voice>
468
+ <type>quarter</type>
469
+ <stem>up</stem>
470
+ <staff>2</staff>
471
+ <notations>
472
+ <technical>
473
+ <string>3</string>
474
+ <fret>11</fret>
475
+ </technical>
476
+ </notations>
477
+ </note>
478
+ <note default-x="222.37" default-y="-125.86">
479
+ <chord/>
480
+ <pitch>
481
+ <step>A</step>
482
+ <octave>4</octave>
483
+ </pitch>
484
+ <duration>4</duration>
485
+ <voice>7</voice>
486
+ <type>quarter</type>
487
+ <stem>up</stem>
488
+ <staff>2</staff>
489
+ <notations>
490
+ <technical>
491
+ <string>2</string>
492
+ <fret>10</fret>
493
+ </technical>
494
+ </notations>
495
+ </note>
496
+ <barline location="right">
497
+ <bar-style>light-heavy</bar-style>
498
+ </barline>
499
+ </measure>
500
+ </part>
501
+ </score-partwise>
frontend/public/FantasiaBarNotes.musicxml ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
3
+ <score-partwise version="3.1">
4
+ <work>
5
+ <work-title>Fantasia no.2 </work-title>
6
+ </work>
7
+ <identification>
8
+ <creator type="composer">Michalis Sourvinos</creator>
9
+ <encoding>
10
+ <software>MuseScore 3.6.2</software>
11
+ <encoding-date>2024-01-02</encoding-date>
12
+ <supports element="accidental" type="yes"/>
13
+ <supports element="beam" type="yes"/>
14
+ <supports element="print" attribute="new-page" type="yes" value="yes"/>
15
+ <supports element="print" attribute="new-system" type="yes" value="yes"/>
16
+ <supports element="stem" type="yes"/>
17
+ </encoding>
18
+ </identification>
19
+ <defaults>
20
+ <scaling>
21
+ <millimeters>6.99912</millimeters>
22
+ <tenths>40</tenths>
23
+ </scaling>
24
+ <page-layout>
25
+ <page-height>1596.77</page-height>
26
+ <page-width>1233.87</page-width>
27
+ <page-margins type="even">
28
+ <left-margin>85.7251</left-margin>
29
+ <right-margin>85.7251</right-margin>
30
+ <top-margin>85.7251</top-margin>
31
+ <bottom-margin>85.7251</bottom-margin>
32
+ </page-margins>
33
+ <page-margins type="odd">
34
+ <left-margin>85.7251</left-margin>
35
+ <right-margin>85.7251</right-margin>
36
+ <top-margin>85.7251</top-margin>
37
+ <bottom-margin>85.7251</bottom-margin>
38
+ </page-margins>
39
+ </page-layout>
40
+ <word-font font-family="Edwin" font-size="10"/>
41
+ <lyric-font font-family="Edwin" font-size="10"/>
42
+ </defaults>
43
+ <credit page="1">
44
+ <credit-type>title</credit-type>
45
+ <credit-words default-x="616.935" default-y="1511.09" justify="center" valign="top" font-size="22">Fantasia no.2 </credit-words>
46
+ </credit>
47
+ <credit page="1">
48
+ <credit-type>subtitle</credit-type>
49
+ <credit-words default-x="616.935" default-y="1453.94" justify="center" valign="top" font-size="16">in A major</credit-words>
50
+ </credit>
51
+ <credit page="1">
52
+ <credit-type>composer</credit-type>
53
+ <credit-words default-x="1148.14" default-y="1411.09" justify="right" valign="top">Michalis Sourvinos</credit-words>
54
+ </credit>
55
+ <part-list>
56
+ <score-part id="P1">
57
+ <part-name>Classical Guitar</part-name>
58
+ <part-abbreviation>Guit.</part-abbreviation>
59
+ <score-instrument id="P1-I1">
60
+ <instrument-name>Classical Guitar</instrument-name>
61
+ </score-instrument>
62
+ <midi-device id="P1-I1" port="1"></midi-device>
63
+ <midi-instrument id="P1-I1">
64
+ <midi-channel>1</midi-channel>
65
+ <midi-program>25</midi-program>
66
+ <volume>78.7402</volume>
67
+ <pan>0</pan>
68
+ </midi-instrument>
69
+ </score-part>
70
+ </part-list>
71
+ <part id="P1">
72
+ <measure number="0" implicit="yes" width="391.89">
73
+ <print>
74
+ <system-layout>
75
+ <system-margins>
76
+ <left-margin>50.00</left-margin>
77
+ <right-margin>0.00</right-margin>
78
+ </system-margins>
79
+ <top-system-distance>170.00</top-system-distance>
80
+ </system-layout>
81
+ </print>
82
+ <attributes>
83
+ <divisions>4</divisions>
84
+ <key>
85
+ <fifths>3</fifths>
86
+ </key>
87
+ <time>
88
+ <beats>2</beats>
89
+ <beat-type>4</beat-type>
90
+ </time>
91
+ <clef>
92
+ <sign>G</sign>
93
+ <line>2</line>
94
+ <clef-octave-change>-1</clef-octave-change>
95
+ </clef>
96
+ </attributes>
97
+ <direction placement="above">
98
+ <direction-type>
99
+ <metronome parentheses="no" default-x="-30.68" relative-y="20.00">
100
+ <beat-unit>quarter</beat-unit>
101
+ <per-minute>60</per-minute>
102
+ </metronome>
103
+ </direction-type>
104
+ <sound tempo="60"/>
105
+ </direction>
106
+ <note>
107
+ <rest/>
108
+ <duration>2</duration>
109
+ <voice>1</voice>
110
+ <type>eighth</type>
111
+ </note>
112
+ <note default-x="251.79" default-y="-5.00">
113
+ <pitch>
114
+ <step>E</step>
115
+ <octave>4</octave>
116
+ </pitch>
117
+ <duration>2</duration>
118
+ <voice>1</voice>
119
+ <type>eighth</type>
120
+ <stem>down</stem>
121
+ </note>
122
+ </measure>
123
+ <measure number="1" width="620.53">
124
+ <note default-x="16.50" default-y="30.00">
125
+ <pitch>
126
+ <step>E</step>
127
+ <octave>5</octave>
128
+ </pitch>
129
+ <duration>4</duration>
130
+ <tie type="start"/>
131
+ <voice>1</voice>
132
+ <type>quarter</type>
133
+ <stem>up</stem>
134
+ <notations>
135
+ <tied type="start"/>
136
+ </notations>
137
+ </note>
138
+ <note default-x="227.05" default-y="30.00">
139
+ <pitch>
140
+ <step>E</step>
141
+ <octave>5</octave>
142
+ </pitch>
143
+ <duration>1</duration>
144
+ <tie type="stop"/>
145
+ <voice>1</voice>
146
+ <type>16th</type>
147
+ <stem>up</stem>
148
+ <beam number="1">begin</beam>
149
+ <beam number="2">begin</beam>
150
+ <notations>
151
+ <tied type="stop"/>
152
+ </notations>
153
+ </note>
154
+ <note default-x="322.76" default-y="25.00">
155
+ <pitch>
156
+ <step>D</step>
157
+ <octave>5</octave>
158
+ </pitch>
159
+ <duration>1</duration>
160
+ <voice>1</voice>
161
+ <type>16th</type>
162
+ <stem>up</stem>
163
+ <beam number="1">continue</beam>
164
+ <beam number="2">continue</beam>
165
+ </note>
166
+ <note default-x="418.46" default-y="20.00">
167
+ <pitch>
168
+ <step>C</step>
169
+ <alter>1</alter>
170
+ <octave>5</octave>
171
+ </pitch>
172
+ <duration>1</duration>
173
+ <voice>1</voice>
174
+ <type>16th</type>
175
+ <stem>up</stem>
176
+ <beam number="1">continue</beam>
177
+ <beam number="2">continue</beam>
178
+ </note>
179
+ <note default-x="514.17" default-y="15.00">
180
+ <pitch>
181
+ <step>B</step>
182
+ <octave>4</octave>
183
+ </pitch>
184
+ <duration>1</duration>
185
+ <voice>1</voice>
186
+ <type>16th</type>
187
+ <stem>up</stem>
188
+ <beam number="1">end</beam>
189
+ <beam number="2">end</beam>
190
+ </note>
191
+ <backup>
192
+ <duration>8</duration>
193
+ </backup>
194
+ <note default-x="16.50" default-y="-60.00">
195
+ <pitch>
196
+ <step>A</step>
197
+ <octave>2</octave>
198
+ </pitch>
199
+ <duration>8</duration>
200
+ <voice>2</voice>
201
+ <type>half</type>
202
+ <stem>down</stem>
203
+ </note>
204
+ <backup>
205
+ <duration>4</duration>
206
+ </backup>
207
+ <note default-x="227.05" default-y="-20.00">
208
+ <pitch>
209
+ <step>B</step>
210
+ <octave>3</octave>
211
+ </pitch>
212
+ <duration>4</duration>
213
+ <voice>3</voice>
214
+ <type>quarter</type>
215
+ <stem>down</stem>
216
+ </note>
217
+ <note default-x="227.05" default-y="0.00">
218
+ <chord/>
219
+ <pitch>
220
+ <step>F</step>
221
+ <alter>1</alter>
222
+ <octave>4</octave>
223
+ </pitch>
224
+ <duration>4</duration>
225
+ <voice>3</voice>
226
+ <type>quarter</type>
227
+ <stem>down</stem>
228
+ </note>
229
+ <note default-x="227.05" default-y="10.00">
230
+ <chord/>
231
+ <pitch>
232
+ <step>A</step>
233
+ <octave>4</octave>
234
+ </pitch>
235
+ <duration>4</duration>
236
+ <voice>3</voice>
237
+ <type>quarter</type>
238
+ <stem>down</stem>
239
+ </note>
240
+ <barline location="right">
241
+ <bar-style>light-heavy</bar-style>
242
+ </barline>
243
+ </measure>
244
+ </part>
245
+ </score-partwise>
frontend/public/Fantasia_no.2.musicxml ADDED
The diff for this file is too large to render. See raw diff
 
frontend/public/Fantasia_no.2.musicxml.gp5 ADDED
Binary file (7.5 kB). View file
 
frontend/public/Fantasia_no.2.musicxml.gpx ADDED
Binary file (56.6 kB). View file
 
frontend/public/Fantasia_no.2_GPexport.musicxml.xml ADDED
The diff for this file is too large to render. See raw diff
 
frontend/src/App.vue CHANGED
@@ -52,38 +52,43 @@ const state = ref({
52
  </template>
53
 
54
  <style scoped>
55
- .app-container {
56
- display: flex;
57
- flex-direction: column;
58
- align-items: center;
59
- }
 
60
 
61
- .navbar {
62
- background-color: #c5bdbd;
63
- padding: 1rem;
64
- width: 100%;
65
- /* margin-left: 10px; */
66
- }
 
 
67
 
68
- .nav-list {
69
- list-style: none;
70
- padding: 0;
71
- margin: 0;
72
- display: flex;
73
- /* background-color: #0a2050; */
74
- }
75
 
76
- .nav-list li {
77
- margin-right: 1rem;
78
- }
79
 
80
- .nav-list li a {
81
- text-decoration: none;
82
- color: rgb(78, 74, 74);
83
- }
84
 
85
- .content {
86
- margin: 2rem;
87
- text-align: center;
88
- }
 
 
 
 
 
 
89
  </style>
 
52
  </template>
53
 
54
  <style scoped>
55
+ .app-container {
56
+ display: block;
57
+ flex-direction: column;
58
+ /* align-items: center; */
59
+ margin: 0px;
60
+ }
61
 
62
+ .navbar {
63
+ background-color: #c5bdbd;
64
+ margin: 0px;
65
+ padding: 0px;
66
+ /* padding: 1rem; */
67
+ /* width: 100%; */
68
+ /* margin-left: 10px; */
69
+ }
70
 
71
+ .nav-list {
72
+ list-style: none;
73
+ /* padding: auto; */
74
+ /* margin: 0; */
75
+ display: flex;
 
 
76
 
77
+ /* background-color: #0a2050; */
78
+ }
 
79
 
80
+ .nav-list li {
81
+ margin-right: 1rem;
82
+ }
 
83
 
84
+ .nav-list li a {
85
+ text-decoration: none;
86
+ color: rgb(78, 74, 74);
87
+ }
88
+
89
+ .content {
90
+ /* margin: 2rem; */
91
+ text-align: center;
92
+
93
+ }
94
  </style>
frontend/src/components/AlphaTabWidget.vue ADDED
@@ -0,0 +1,756 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script setup>
2
+ import { computed, onMounted, onUpdated, ref, watch } from 'vue'
3
+ import { Pane } from 'tweakpane'
4
+
5
+ const props = defineProps({
6
+ // int id
7
+ id: {
8
+ type: String,
9
+ required: true,
10
+ },
11
+ alphtaText: {
12
+ type: String,
13
+ required: false,
14
+ },
15
+ isActive: {
16
+ type: Boolean,
17
+ required: false,
18
+ default: false,
19
+ },
20
+
21
+ })
22
+
23
+ const emit = defineEmits(['updateScoreCode'])
24
+
25
+ const openSheetMusicDisplay = null
26
+ const timeoutID = null
27
+ let pane = null
28
+ let api = null
29
+ let main = null
30
+ let wrapper = null
31
+ let settings = null
32
+
33
+ // const message = ref('hello')
34
+ const currentScoreCode = ref(props.alphtaText)
35
+ const showParsingError = ref(false)
36
+ const parsingError = ref('')
37
+ // const count = ref(0)
38
+ const settingsStatus = ref(false)
39
+ const isActive = ref(false)
40
+
41
+ const SETTINGS_PARAMS = {
42
+ scale: 1.0,
43
+ // width: 800,
44
+ // factor: 123,
45
+ // title: 'hello',
46
+ // color: '#ff0055',
47
+ // percentage: 50,
48
+ // theme: 'dark',
49
+ // prop: 'Put your\nmultiline\ntext here!'
50
+ }
51
+
52
+ const divId = computed(() => {
53
+ return `xmlScoreContainer_${props.id}`
54
+ })
55
+
56
+ function initializePane() {
57
+ pane = new Pane({
58
+ container: document.getElementById('paneContainerXml'),
59
+ title: 'MusicXML Settings',
60
+ },
61
+ )
62
+ pane.addBinding(SETTINGS_PARAMS, 'scale', {
63
+ min: 0.5,
64
+ max: 2.0,
65
+ step: 0.1,
66
+ }).on('change', (ev) => {
67
+ console.log(ev.value)
68
+ openSheetMusicDisplay.zoom = ev.value
69
+ openSheetMusicDisplay.render()
70
+ })
71
+ }
72
+
73
+ onMounted(async () => {
74
+ // console.log($(`#${divId.value}`)[0])
75
+ initializePane()
76
+ wrapper = document.querySelector('.at-wrap')
77
+ main = wrapper.querySelector('.at-main')
78
+ // initialize alphatab
79
+ settings = {
80
+ // file: 'https://www.alphatab.net/files/canon.gp',
81
+ // file: '/Fantasia_no.2.musicxml.gpx',
82
+ player: {
83
+ enablePlayer: true,
84
+ soundFont: 'https://cdn.jsdelivr.net/npm/@coderline/alphatab@latest/dist/soundfont/sonivox.sf2',
85
+ scrollElement: wrapper.querySelector('.at-viewport'),
86
+ },
87
+ }
88
+ initializeOSMD()
89
+ })
90
+
91
+ function initializeOSMD() {
92
+ api = new alphaTab.AlphaTabApi(main, settings)
93
+ api.tex(`
94
+ \\title 'Test' . 3.3.4
95
+ :4 2.3 3.3 :8 3.3 4.3 3.3 4.3 |
96
+
97
+ `)
98
+
99
+ // overlay logic
100
+ const overlay = wrapper.querySelector('.at-overlay')
101
+ api.renderStarted.on(() => {
102
+ overlay.style.display = 'flex'
103
+ })
104
+ api.renderFinished.on(() => {
105
+ overlay.style.display = 'none'
106
+ })
107
+
108
+ // track selector
109
+ function createTrackItem(track) {
110
+ const templateElement = document.querySelector('#at-track-template')
111
+ const templateContent = templateElement.firstElementChild
112
+ const clonedNode = templateContent.cloneNode(true)
113
+ const trackItem = clonedNode.children[1]
114
+ trackItem.querySelector('.at-track-name').textContent = track.name
115
+ trackItem.track = track
116
+ trackItem.onclick = (e) => {
117
+ e.stopPropagation()
118
+ api.renderTracks([track])
119
+ }
120
+ return trackItem
121
+ }
122
+ const trackList = wrapper.querySelector('.at-track-list')
123
+ api.scoreLoaded.on((score) => {
124
+ // clear items
125
+ trackList.innerHTML = ''
126
+ // generate a track item for all tracks of the score
127
+ score.tracks.forEach((track) => {
128
+ trackList.appendChild(createTrackItem(track))
129
+ })
130
+ })
131
+ api.renderStarted.on(() => {
132
+ // collect tracks being rendered
133
+ const tracks = new Map()
134
+ api.tracks.forEach((t) => {
135
+ tracks.set(t.index, t)
136
+ })
137
+ // mark the item as active or not
138
+ const trackItems = trackList.querySelectorAll('.at-track')
139
+ trackItems.forEach((trackItem) => {
140
+ if (tracks.has(trackItem.track.index))
141
+ trackItem.classList.add('active')
142
+
143
+ else
144
+ trackItem.classList.remove('active')
145
+ })
146
+ })
147
+
148
+ /** Controls */
149
+ api.scoreLoaded.on((score) => {
150
+ wrapper.querySelector('.at-song-title').innerText = score.title
151
+ wrapper.querySelector('.at-song-artist').innerText = score.artist
152
+ })
153
+
154
+ const countIn = wrapper.querySelector('.at-controls .at-count-in')
155
+ countIn.onclick = () => {
156
+ countIn.classList.toggle('active')
157
+ if (countIn.classList.contains('active'))
158
+ api.countInVolume = 1
159
+
160
+ else
161
+ api.countInVolume = 0
162
+ }
163
+
164
+ const metronome = wrapper.querySelector('.at-controls .at-metronome')
165
+ metronome.onclick = () => {
166
+ metronome.classList.toggle('active')
167
+ if (metronome.classList.contains('active'))
168
+ api.metronomeVolume = 1
169
+
170
+ else
171
+ api.metronomeVolume = 0
172
+ }
173
+
174
+ const loop = wrapper.querySelector('.at-controls .at-loop')
175
+ loop.onclick = () => {
176
+ loop.classList.toggle('active')
177
+ api.isLooping = loop.classList.contains('active')
178
+ }
179
+
180
+ wrapper.querySelector('.at-controls .at-print').onclick = () => {
181
+ api.print()
182
+ }
183
+
184
+ const zoom = wrapper.querySelector('.at-controls .at-zoom select')
185
+ zoom.onchange = () => {
186
+ const zoomLevel = Number.parseInt(zoom.value) / 100
187
+ api.settings.display.scale = zoomLevel
188
+ api.updateSettings()
189
+ api.render()
190
+ }
191
+
192
+ const layout = wrapper.querySelector('.at-controls .at-layout select')
193
+ layout.onchange = () => {
194
+ switch (layout.value) {
195
+ case 'horizontal':
196
+ api.settings.display.layoutMode = alphaTab.LayoutMode.Horizontal
197
+ break
198
+ case 'page':
199
+ api.settings.display.layoutMode = alphaTab.LayoutMode.Page
200
+ break
201
+ }
202
+ api.updateSettings()
203
+ api.render()
204
+ }
205
+
206
+ // player loading indicator
207
+ const playerIndicator = wrapper.querySelector(
208
+ '.at-controls .at-player-progress',
209
+ )
210
+ api.soundFontLoad.on((e) => {
211
+ const percentage = Math.floor((e.loaded / e.total) * 100)
212
+ playerIndicator.innerText = `${percentage}%`
213
+ })
214
+ api.playerReady.on(() => {
215
+ playerIndicator.style.display = 'none'
216
+ })
217
+
218
+ // main player controls
219
+ const playPause = wrapper.querySelector(
220
+ '.at-controls .at-player-play-pause',
221
+ )
222
+ const stop = wrapper.querySelector('.at-controls .at-player-stop')
223
+ playPause.onclick = (e) => {
224
+ if (e.target.classList.contains('disabled'))
225
+ return
226
+
227
+ api.playPause()
228
+ }
229
+ stop.onclick = (e) => {
230
+ if (e.target.classList.contains('disabled'))
231
+ return
232
+
233
+ api.stop()
234
+ }
235
+ api.playerReady.on(() => {
236
+ playPause.classList.remove('disabled')
237
+ stop.classList.remove('disabled')
238
+ })
239
+ api.playerStateChanged.on((e) => {
240
+ const icon = playPause.querySelector('i.fas')
241
+ if (e.state === alphaTab.synth.PlayerState.Playing) {
242
+ icon.classList.remove('i-fa:play')
243
+ icon.classList.add('i-fa:pause')
244
+ }
245
+ else {
246
+ icon.classList.remove('i-fa:pause')
247
+ icon.classList.add('i-fa:play')
248
+ }
249
+ })
250
+
251
+ // song position
252
+ function formatDuration(milliseconds) {
253
+ let seconds = milliseconds / 1000
254
+ const minutes = (seconds / 60) | 0
255
+ seconds = (seconds - minutes * 60) | 0
256
+ return (
257
+ `${String(minutes).padStart(2, '0')
258
+ }:${
259
+ String(seconds).padStart(2, '0')}`
260
+ )
261
+ }
262
+
263
+ const songPosition = wrapper.querySelector('.at-song-position')
264
+ const previousTime = -1
265
+ api.playerPositionChanged.on((e) => {
266
+ // reduce number of UI updates to second changes.
267
+ const currentSeconds = (e.currentTime / 1000) | 0
268
+ if (currentSeconds == previousTime)
269
+ return
270
+
271
+ songPosition.innerText
272
+ = `${formatDuration(e.currentTime)} / ${formatDuration(e.endTime)}`
273
+ })
274
+
275
+ // await fetch('/guitar-diff-example.musicxml')
276
+ // .then((response) => {
277
+ // return response.text()
278
+ // })
279
+ // .then((data) => {
280
+ // console.log('in data')
281
+ // openSheetMusicDisplay.load(data)
282
+ // .then(
283
+ // () => {
284
+ // window.osmd = openSheetMusicDisplay
285
+ // openSheetMusicDisplay.render()
286
+ // // openSheetMusicDisplay.cursor.show();
287
+ // console.log('in load')
288
+ // showParsingError.value = false
289
+ // currentScoreCode.value = data
290
+ // },
291
+ // )
292
+ // .catch((e) => {
293
+ // showParsingError.value = true
294
+ // console.log(e.message.replace(/(?:\r\n|\r|\n)/g, '<br>'))
295
+ // parsingError.value = e.message.replace(/(?:\r\n|\r|\n)/g, '<br>')
296
+ // })
297
+ // })
298
+ }
299
+
300
+ // function showScore() {
301
+ // openSheetMusicDisplay.clear()
302
+ // console.log(currentScoreCode.value)
303
+ // openSheetMusicDisplay.load(currentScoreCode.value)
304
+ // .then(
305
+ // () => {
306
+ // window.osmd = openSheetMusicDisplay
307
+ // openSheetMusicDisplay.render()
308
+ // // openSheetMusicDisplay.cursor.show();
309
+ // console.log('in load')
310
+ // showParsingError.value = false
311
+ // },
312
+ // )
313
+ // .catch((e) => {
314
+ // showParsingError.value = true
315
+ // console.log(e.message.replace(/(?:\r\n|\r|\n)/g, '<br>'))
316
+ // parsingError.value = e.message.replace(/(?:\r\n|\r|\n)/g, '<br>')
317
+ // })
318
+ // }
319
+
320
+ // function handleKeyUp(_event) {
321
+ // if (timeoutID)
322
+ // clearTimeout(timeoutID)
323
+ // timeoutID = setTimeout(showScore, 500)
324
+ // }
325
+
326
+ // function handleDrop(event) {
327
+ // event.preventDefault()
328
+ // const textData = event.dataTransfer.getData('text')
329
+ // const file = event.dataTransfer.files[0]
330
+ // console.log(textData)
331
+ // console.log(file)
332
+ // if (file) {
333
+ // const reader = new FileReader()
334
+ // reader.onload = function (e) {
335
+ // currentScoreCode.value = e.target.result
336
+ // showScore()
337
+ // }
338
+ // reader.readAsText(file)
339
+ // }
340
+ // else if (textData) {
341
+ // currentScoreCode.value = textData
342
+ // showScore()
343
+ // }
344
+ // }
345
+
346
+ function showHideSettings() {
347
+ console.log('showHideSettings')
348
+ settingsStatus.value = !settingsStatus.value
349
+ }
350
+
351
+ // invoked when there is any type of change in the component
352
+ onUpdated(() => {
353
+ if (props.isActive !== isActive.value) {
354
+ // Prop has changed, update the ref and emit an event
355
+ if (props.isActive) {
356
+ isActive.value = props.isActive
357
+ emit('updateScoreCode', currentScoreCode.value)
358
+ // console.log("emitted updateScoreCode");
359
+ // console.log(currentScoreCode.value);
360
+ }
361
+ isActive.value = props.isActive
362
+ }
363
+ })
364
+
365
+ watch(currentScoreCode, () => {
366
+ // Emit the event so that the parent component can update the tabText
367
+ // prop
368
+ emit('updateScoreCode', currentScoreCode.value)
369
+ // console.log("emitted updateScoreCode");
370
+ // console.log(currentScoreCode.value);
371
+ })
372
+
373
+ // const moveCursor = () => {
374
+ // openSheetMusicDisplay.cursor.next();
375
+ // };
376
+
377
+ // const fillScore = () => {
378
+ // // console.log(vt);
379
+ // // Perform any logic to fill the score
380
+ // response.value = 'Button pressed!'; // Update response if needed
381
+
382
+ // };
383
+ </script>
384
+
385
+ <template>
386
+ <!--
387
+ Which ever of the following elements wants to use the class
388
+ attributes coming from the parent component needs to use
389
+ :class="$attrs.class"
390
+ UNLESS I have a single root div-element
391
+ -->
392
+ <div>
393
+ <div>
394
+ <div class="at-wrap">
395
+ <div class="at-overlay">
396
+ <div class="at-overlay-content">
397
+ Music sheet is loading
398
+ </div>
399
+ </div>
400
+ <div class="at-content">
401
+ <div class="at-sidebar">
402
+ <div class="at-sidebar-content">
403
+ <div class="at-track-list" />
404
+ </div>
405
+ </div>
406
+ <div class="at-viewport">
407
+ <div class="at-main" />
408
+ </div>
409
+ </div>
410
+ <div class="at-controls">
411
+ <div class="at-controls-left">
412
+ <a class="btn at-player-stop disabled">
413
+ <i class="fas i-fa:step-backward"> Begining </i>
414
+ </a>
415
+ <a class="btn at-player-play-pause disabled">
416
+ <i class="fas i-fa:play">SkAta</i>
417
+ </a>
418
+ <span class="at-player-progress">0%</span>
419
+ <div class="at-song-info">
420
+ <span class="at-song-title" /> -
421
+ <span class="at-song-artist" />
422
+ </div>
423
+ <div class="at-song-position">
424
+ 00:00 / 00:00
425
+ </div>
426
+ </div>
427
+ <div class="at-controls-right">
428
+ <a class="btn toggle at-count-in">
429
+ <i class="fas fa-hourglass-half" />
430
+ </a>
431
+ <a class="btn at-metronome">
432
+ <i class="fas i-fa:edit" />
433
+ </a>
434
+ <a class="btn at-loop">
435
+ <i class="fas i-fa:retweet" />
436
+ </a>
437
+ <a class="btn at-print">
438
+ <i class="fas i-fa:print" />
439
+ </a>
440
+ <div class="at-zoom">
441
+ <i class="fas i-fa:search" />
442
+ <select>
443
+ <option value="25">
444
+ 25%
445
+ </option>
446
+ <option value="50">
447
+ 50%
448
+ </option>
449
+ <option value="75">
450
+ 75%
451
+ </option>
452
+ <option value="90">
453
+ 90%
454
+ </option>
455
+ <option value="100" selected>
456
+ 100%
457
+ </option>
458
+ <option value="110">
459
+ 110%
460
+ </option>
461
+ <option value="125">
462
+ 125%
463
+ </option>
464
+ <option value="150">
465
+ 150%
466
+ </option>
467
+ <option value="200">
468
+ 200%
469
+ </option>
470
+ </select>
471
+ </div>
472
+ <div class="at-layout">
473
+ <select>
474
+ <option value="horizontal">
475
+ Horizontal
476
+ </option>
477
+ <option value="page" selected>
478
+ Page
479
+ </option>
480
+ </select>
481
+ </div>
482
+ </div>
483
+ </div>
484
+ </div>
485
+ <button class="left-align" @click="showHideSettings">
486
+ Settings
487
+ </button>
488
+ <div :id="divId" class="scoreContainer22" />
489
+
490
+ <div v-show="settingsStatus" id="tabSettingsArea22">
491
+ <div id="paneContainerXml22" />
492
+ <textarea
493
+ v-model="currentScoreCode"
494
+ class="editorBox22"
495
+ placeholder="add your musicxml code here or drag a file"
496
+ @keyup="handleKeyUp"
497
+ @drop="handleDrop"
498
+ @dragover.prevent
499
+ />
500
+ </div>
501
+
502
+ <div v-if="showParsingError" class="parsing-error" v-html="parsingError" />
503
+ </div>
504
+ <template id="at-track-template" ref="trackTemplate">
505
+ <div class="at-track">
506
+ <div class="at-track-icon">
507
+ <i class="fas fa-guitar" />
508
+ </div>
509
+ <div class="at-track-details">
510
+ <div class="at-track-name" />
511
+ </div>
512
+ </div>
513
+ </template>
514
+ </div>
515
+ </template>
516
+
517
+ <style scoped>
518
+ body {
519
+ font-family: Arial, Helvetica, sans-serif;
520
+ font-size: 12px;
521
+ }
522
+
523
+ .at-wrap {
524
+ width: 80vw;
525
+ height: 80vh;
526
+ margin: 0 auto;
527
+ border: 1px solid rgba(0, 0, 0, 0.12);
528
+ display: flex;
529
+ flex-direction: column;
530
+ overflow: hidden;
531
+ position: relative;
532
+ }
533
+
534
+ .at-content {
535
+ position: relative;
536
+ overflow: hidden;
537
+ flex: 1 1 auto;
538
+ }
539
+
540
+ /** Sidebar **/
541
+ .at-sidebar {
542
+ position: absolute;
543
+ top: 0;
544
+ left: 0;
545
+ bottom: 0;
546
+ max-width: 70px;
547
+ width: auto;
548
+ display: flex;
549
+ align-content: stretch;
550
+ z-index: 1001;
551
+ overflow: hidden;
552
+ border-right: 1px solid rgba(0, 0, 0, 0.12);
553
+ background: #f7f7f7;
554
+ }
555
+
556
+ .at-sidebar:hover {
557
+ max-width: 400px;
558
+ transition: max-width 0.2s;
559
+ overflow-y: auto;
560
+ }
561
+
562
+ .at-viewport {
563
+ overflow-y: auto;
564
+ position: absolute;
565
+ top: 0;
566
+ left: 70px;
567
+ right: 0;
568
+ bottom: 0;
569
+ padding-right: 20px;
570
+ }
571
+
572
+ .at-footer {
573
+ flex: 0 0 auto;
574
+ background: #436d9d;
575
+ color: #fff;
576
+ }
577
+
578
+ /** Overlay **/
579
+
580
+ .at-overlay {
581
+ /** Fill Parent */
582
+ position: absolute;
583
+ top: 0;
584
+ left: 0;
585
+ right: 0;
586
+ bottom: 0;
587
+ z-index: 1002;
588
+
589
+ /* Blurry dark shade */
590
+ backdrop-filter: blur(3px);
591
+ background: rgba(0, 0, 0, 0.5);
592
+
593
+ /** center content */
594
+ display: flex;
595
+ justify-content: center;
596
+ align-items: flex-start;
597
+ }
598
+
599
+ .at-overlay-content {
600
+ /* white box with drop-shadow */
601
+ margin-top: 20%;
602
+ background: var(--background-light2);
603
+ box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.411);
604
+ padding: 10px;
605
+ color: var(--text-color-dark);
606
+ /* font-size: 20px; */
607
+ font-weight: 600;
608
+ /* font: 900 20px/1.5 Helvetica, Verdana, sans-serif; */
609
+ /* font-family: Helvetica, sans-serif; */
610
+ }
611
+
612
+ /** Track selector **/
613
+ .at-track {
614
+ display: flex;
615
+ position: relative;
616
+ padding: 5px;
617
+ transition: background 0.2s;
618
+ cursor: pointer;
619
+ }
620
+
621
+ .at-track:hover {
622
+ background: rgba(0, 0, 0, 0.1);
623
+ }
624
+
625
+ .at-track > .at-track-icon,
626
+ .at-track > .at-track-details {
627
+ display: flex;
628
+ flex-direction: column;
629
+ justify-content: center;
630
+ }
631
+
632
+ .at-track > .at-track-icon {
633
+ flex-shrink: 0;
634
+ font-size: 32px;
635
+ opacity: 0.5;
636
+ transition: opacity 0.2s;
637
+ width: 64px;
638
+ height: 64px;
639
+ margin-right: 5px;
640
+ align-items: center;
641
+ }
642
+
643
+ .at-track-name {
644
+ font-weight: bold;
645
+ margin-bottom: 5px;
646
+ }
647
+
648
+ .at-track:hover > .at-track-icon {
649
+ opacity: 0.8;
650
+ }
651
+
652
+ .at-track.active {
653
+ background: rgba(0, 0, 0, 0.03);
654
+ }
655
+
656
+ .at-track.active > .at-track-icon {
657
+ color: #4972a1;
658
+ opacity: 1;
659
+ }
660
+
661
+ .at-track > .at-track-name {
662
+ font-weight: 500;
663
+ }
664
+
665
+ /** Footer **/
666
+ .at-controls {
667
+ flex: 0 0 auto;
668
+ display: flex;
669
+ justify-content: space-between;
670
+ background: #436d9d;
671
+ color: #fff;
672
+ }
673
+
674
+ .at-controls > div {
675
+ display: flex;
676
+ justify-content: flex-start;
677
+ align-content: center;
678
+ align-items: center;
679
+ }
680
+
681
+ .at-controls > div > * {
682
+ display: flex;
683
+ text-align: center;
684
+ align-items: center;
685
+ justify-content: center;
686
+ cursor: pointer;
687
+ padding: 4px;
688
+ margin: 0 3px;
689
+ }
690
+
691
+ .at-controls .btn {
692
+ color: #fff;
693
+ border-radius: 0;
694
+ height: 40px;
695
+ width: 40px;
696
+ height: 40px;
697
+ font-size: 16px;
698
+ }
699
+ .at-controls .btn.disabled {
700
+ cursor: progress;
701
+ opacity: 0.5;
702
+ }
703
+
704
+ .at-controls a.active {
705
+ background: #5588c7;
706
+ text-decoration: none;
707
+ }
708
+
709
+ .at-controls .btn i {
710
+ vertical-align: top;
711
+ }
712
+
713
+ .at-controls select {
714
+ -moz-appearance: none;
715
+ -webkit-appearance: none;
716
+ appearance: none;
717
+ border: none;
718
+ width: 100%;
719
+ height: 40px;
720
+ background: #436d9d;
721
+ padding: 4px 10px;
722
+ color: #fff;
723
+ font-size: 16px;
724
+ text-align-last: center;
725
+ text-align: center;
726
+ -ms-text-align-last: center;
727
+ -moz-text-align-last: center;
728
+ cursor: pointer;
729
+ }
730
+
731
+ .at-song-title {
732
+ font-weight: bold;
733
+ }
734
+
735
+ .at-cursor-bar {
736
+ /* Defines the color of the bar background when a bar is played */
737
+ background: rgba(255, 242, 0, 0.25);
738
+ }
739
+
740
+ .at-selection div {
741
+ /* Defines the color of the selection background */
742
+ background: rgba(64, 64, 255, 0.1);
743
+ }
744
+
745
+ .at-cursor-beat {
746
+ /* Defines the beat cursor */
747
+ background: rgba(64, 64, 255, 0.75);
748
+ width: 3px;
749
+ }
750
+
751
+ .at-highlight * {
752
+ /* Defines the color of the music symbols when they are being played (svg) */
753
+ fill: #0078ff;
754
+ stroke: #0078ff;
755
+ }
756
+ </style>
frontend/src/components/MusicXmlWidget.vue CHANGED
@@ -49,7 +49,7 @@ const SETTINGS_PARAMS = {
49
  // theme: 'dark',
50
  // prop: 'Put your\nmultiline\ntext here!'
51
  }
52
-
53
  const divId = computed(() => {
54
  return `xmlScoreContainer_${props.id}`
55
  })
@@ -87,7 +87,7 @@ async function initializeOSMD() {
87
  // renderer = new Flow.Renderer($('#' + divId.value)[0], Flow.Renderer.Backends.SVG);
88
  // artist = new Artist(10, 10, 750, { scale: 0.8 });
89
  // tab = new VexTab(artist);
90
- openSheetMusicDisplay = new OpenSheetMusicDisplay(divId.value, {
91
  autoResize: false,
92
  backend: 'svg',
93
  disableCursor: false,
@@ -104,7 +104,7 @@ async function initializeOSMD() {
104
  // fingeringPositionFromXML: false, // do this if you want them always left, for example.
105
  // fingeringInsideStafflines: "true", // default: false. true draws fingerings directly above/below notes
106
  setWantedStemDirectionByXml: true, // try false, which was previously the default behavior
107
- drawUpToMeasureNumber: 20, // draws only up to measure 3, meaning it draws measure 1 to 3 of the piece.
108
  // drawFromMeasureNumber : 1,
109
  // drawUpToMeasureNumber : 3,
110
  drawMeasureNumbers: true,
@@ -278,7 +278,7 @@ watch(currentScoreCode, () => {
278
  <!-- <v-icon icon="mdi-pencil-box" /> -->
279
  Settings
280
  </button>
281
- <div :id="divId" class="scoreContainer" />
282
 
283
  <div v-show="settingsStatus" id="tabSettingsArea">
284
  <!-- <TweakComponent :visible="settingsStatus"/> -->
 
49
  // theme: 'dark',
50
  // prop: 'Put your\nmultiline\ntext here!'
51
  }
52
+ const scoreRef = ref(null)
53
  const divId = computed(() => {
54
  return `xmlScoreContainer_${props.id}`
55
  })
 
87
  // renderer = new Flow.Renderer($('#' + divId.value)[0], Flow.Renderer.Backends.SVG);
88
  // artist = new Artist(10, 10, 750, { scale: 0.8 });
89
  // tab = new VexTab(artist);
90
+ openSheetMusicDisplay = new OpenSheetMusicDisplay(scoreRef.value, {
91
  autoResize: false,
92
  backend: 'svg',
93
  disableCursor: false,
 
104
  // fingeringPositionFromXML: false, // do this if you want them always left, for example.
105
  // fingeringInsideStafflines: "true", // default: false. true draws fingerings directly above/below notes
106
  setWantedStemDirectionByXml: true, // try false, which was previously the default behavior
107
+ // drawUpToMeasureNumber: 20, // draws only up to measure 3, meaning it draws measure 1 to 3 of the piece.
108
  // drawFromMeasureNumber : 1,
109
  // drawUpToMeasureNumber : 3,
110
  drawMeasureNumbers: true,
 
278
  <!-- <v-icon icon="mdi-pencil-box" /> -->
279
  Settings
280
  </button>
281
+ <div :id="divId" ref="scoreRef" class="scoreContainer" />
282
 
283
  <div v-show="settingsStatus" id="tabSettingsArea">
284
  <!-- <TweakComponent :visible="settingsStatus"/> -->
frontend/src/components/ScoreWidgetBase.vue ADDED
@@ -0,0 +1,450 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script setup>
2
+ import { computed, onMounted, onUpdated, ref, watch } from 'vue'
3
+ import { Pane } from 'tweakpane'
4
+
5
+ const props = defineProps({
6
+ // int id
7
+ id: {
8
+ type: String,
9
+ required: true,
10
+ },
11
+ alphtaText: {
12
+ type: String,
13
+ required: false,
14
+ },
15
+ isActive: {
16
+ type: Boolean,
17
+ required: false,
18
+ default: false,
19
+ },
20
+
21
+ })
22
+
23
+ const emit = defineEmits(['updateScoreCode'])
24
+
25
+ const openSheetMusicDisplay = null
26
+ const timeoutID = null
27
+ let pane = null
28
+ let api = null
29
+ let main = null
30
+ let wrapper = null
31
+ let settings = null
32
+
33
+ // const message = ref('hello')
34
+ const currentScoreCode = ref(props.alphtaText)
35
+ const showParsingError = ref(false)
36
+ const parsingError = ref('')
37
+ // const count = ref(0)
38
+ const settingsStatus = ref(false)
39
+ const isActive = ref(false)
40
+
41
+ const SETTINGS_PARAMS = {
42
+ scale: 1.0,
43
+ // width: 800,
44
+ // factor: 123,
45
+ // title: 'hello',
46
+ // color: '#ff0055',
47
+ // percentage: 50,
48
+ // theme: 'dark',
49
+ // prop: 'Put your\nmultiline\ntext here!'
50
+ }
51
+
52
+ const divId = computed(() => {
53
+ return `xmlScoreContainer_${props.id}`
54
+ })
55
+
56
+ function initializePane() {
57
+ pane = new Pane({
58
+ container: document.getElementById('paneContainerXml'),
59
+ title: 'MusicXML Settings',
60
+ },
61
+ )
62
+ pane.addBinding(SETTINGS_PARAMS, 'scale', {
63
+ min: 0.5,
64
+ max: 2.0,
65
+ step: 0.1,
66
+ }).on('change', (ev) => {
67
+ console.log(ev.value)
68
+ openSheetMusicDisplay.zoom = ev.value
69
+ openSheetMusicDisplay.render()
70
+ })
71
+ }
72
+
73
+ onMounted(async () => {
74
+ // console.log($(`#${divId.value}`)[0])
75
+ initializePane()
76
+ wrapper = document.querySelector('.at2-wrap')
77
+ main = wrapper.querySelector('.at2-main')
78
+ // initialize alphatab
79
+ settings = {
80
+ // file: 'https://www.alphatab.net/files/canon.gp',
81
+ // file: '/Fantasia_no.2.musicxml.gpx',
82
+ player: {
83
+ enablePlayer: true,
84
+ soundFont: 'https://cdn.jsdelivr.net/npm/@coderline/alphatab@latest/dist/soundfont/sonivox.sf2',
85
+ scrollElement: wrapper.querySelector('.at2-viewport'),
86
+ },
87
+ }
88
+ initializeOSMD()
89
+ })
90
+
91
+ function initializeOSMD() {
92
+ api = new alphaTab.AlphaTabApi(main, settings)
93
+ api.tex(`
94
+ \\title 'Test' . 3.3.4
95
+ :4 2.3 3.3 :8 3.3 4.3 3.3 4.3 |
96
+
97
+ `)
98
+
99
+ // overlay logic
100
+ const overlay = wrapper.querySelector('.at2-overlay')
101
+ api.renderStarted.on(() => {
102
+ overlay.style.display = 'flex'
103
+ })
104
+ api.renderFinished.on(() => {
105
+ overlay.style.display = 'none'
106
+ })
107
+
108
+ // track selector
109
+ function createTrackItem(track) {
110
+ const templateElement = document.querySelector('#at2-track-template')
111
+ const templateContent = templateElement.firstElementChild
112
+ const clonedNode = templateContent.cloneNode(true)
113
+ const trackItem = clonedNode.children[1]
114
+ trackItem.querySelector('.at2-track-name').textContent = track.name
115
+ trackItem.track = track
116
+ trackItem.onclick = (e) => {
117
+ e.stopPropagation()
118
+ api.renderTracks([track])
119
+ }
120
+ return trackItem
121
+ }
122
+ const trackList = wrapper.querySelector('.at2-track-list')
123
+ api.scoreLoaded.on((score) => {
124
+ // clear items
125
+ trackList.innerHTML = ''
126
+ // generate a track item for all tracks of the score
127
+ score.tracks.forEach((track) => {
128
+ trackList.appendChild(createTrackItem(track))
129
+ })
130
+ })
131
+ api.renderStarted.on(() => {
132
+ // collect tracks being rendered
133
+ const tracks = new Map()
134
+ api.tracks.forEach((t) => {
135
+ tracks.set(t.index, t)
136
+ })
137
+ // mark the item as active or not
138
+ const trackItems = trackList.querySelectorAll('.at2-track')
139
+ trackItems.forEach((trackItem) => {
140
+ if (tracks.has(trackItem.track.index))
141
+ trackItem.classList.add('active')
142
+
143
+ else
144
+ trackItem.classList.remove('active')
145
+ })
146
+ })
147
+
148
+ /** Controls */
149
+ api.scoreLoaded.on((score) => {
150
+ wrapper.querySelector('.at2-song-title').innerText = score.title
151
+ wrapper.querySelector('.at2-song-artist').innerText = score.artist
152
+ })
153
+
154
+ const countIn = wrapper.querySelector('.at2-controls .at2-count-in')
155
+ countIn.onclick = () => {
156
+ countIn.classList.toggle('active')
157
+ if (countIn.classList.contains('active'))
158
+ api.countInVolume = 1
159
+
160
+ else
161
+ api.countInVolume = 0
162
+ }
163
+
164
+ const metronome = wrapper.querySelector('.at2-controls .at2-metronome')
165
+ metronome.onclick = () => {
166
+ metronome.classList.toggle('active')
167
+ if (metronome.classList.contains('active'))
168
+ api.metronomeVolume = 1
169
+
170
+ else
171
+ api.metronomeVolume = 0
172
+ }
173
+
174
+ const loop = wrapper.querySelector('.at2-controls .at2-loop')
175
+ loop.onclick = () => {
176
+ loop.classList.toggle('active')
177
+ api.isLooping = loop.classList.contains('active')
178
+ }
179
+
180
+ wrapper.querySelector('.at2-controls .at2-print').onclick = () => {
181
+ api.print()
182
+ }
183
+
184
+ const zoom = wrapper.querySelector('.at2-controls .at2-zoom select')
185
+ zoom.onchange = () => {
186
+ const zoomLevel = Number.parseInt(zoom.value) / 100
187
+ api.settings.display.scale = zoomLevel
188
+ api.updateSettings()
189
+ api.render()
190
+ }
191
+
192
+ const layout = wrapper.querySelector('.at2-controls .at2-layout select')
193
+ layout.onchange = () => {
194
+ switch (layout.value) {
195
+ case 'horizontal':
196
+ api.settings.display.layoutMode = alphaTab.LayoutMode.Horizontal
197
+ break
198
+ case 'page':
199
+ api.settings.display.layoutMode = alphaTab.LayoutMode.Page
200
+ break
201
+ }
202
+ api.updateSettings()
203
+ api.render()
204
+ }
205
+
206
+ // player loading indicator
207
+ const playerIndicator = wrapper.querySelector(
208
+ '.at2-controls .at2-player-progress',
209
+ )
210
+ api.soundFontLoad.on((e) => {
211
+ const percentage = Math.floor((e.loaded / e.total) * 100)
212
+ playerIndicator.innerText = `${percentage}%`
213
+ })
214
+ api.playerReady.on(() => {
215
+ playerIndicator.style.display = 'none'
216
+ })
217
+
218
+ // main player controls
219
+ const playPause = wrapper.querySelector(
220
+ '.at2-controls .at2-player-play-pause',
221
+ )
222
+ const stop = wrapper.querySelector('.at2-controls .at2-player-stop')
223
+ playPause.onclick = (e) => {
224
+ if (e.target.classList.contains('disabled'))
225
+ return
226
+
227
+ api.playPause()
228
+ }
229
+ stop.onclick = (e) => {
230
+ if (e.target.classList.contains('disabled'))
231
+ return
232
+
233
+ api.stop()
234
+ }
235
+ api.playerReady.on(() => {
236
+ playPause.classList.remove('disabled')
237
+ stop.classList.remove('disabled')
238
+ })
239
+ api.playerStateChanged.on((e) => {
240
+ const icon = playPause.querySelector('i.fas')
241
+ if (e.state === alphaTab.synth.PlayerState.Playing) {
242
+ icon.classList.remove('i-fa:play')
243
+ icon.classList.add('i-fa:pause')
244
+ }
245
+ else {
246
+ icon.classList.remove('i-fa:pause')
247
+ icon.classList.add('i-fa:play')
248
+ }
249
+ })
250
+
251
+ // song position
252
+ function formatDuration(milliseconds) {
253
+ let seconds = milliseconds / 1000
254
+ const minutes = (seconds / 60) | 0
255
+ seconds = (seconds - minutes * 60) | 0
256
+ return (
257
+ `${String(minutes).padStart(2, '0')
258
+ }:${
259
+ String(seconds).padStart(2, '0')}`
260
+ )
261
+ }
262
+
263
+ const songPosition = wrapper.querySelector('.at2-song-position')
264
+ const previousTime = -1
265
+ api.playerPositionChanged.on((e) => {
266
+ // reduce number of UI updates to second changes.
267
+ const currentSeconds = (e.currentTime / 1000) | 0
268
+ if (currentSeconds == previousTime)
269
+ return
270
+
271
+ songPosition.innerText
272
+ = `${formatDuration(e.currentTime)} / ${formatDuration(e.endTime)}`
273
+ })
274
+ }
275
+
276
+ function showHideSettings() {
277
+ console.log('showHideSettings')
278
+ settingsStatus.value = !settingsStatus.value
279
+ }
280
+
281
+ // invoked when there is any type of change in the component
282
+ onUpdated(() => {
283
+ if (props.isActive !== isActive.value) {
284
+ // Prop has changed, update the ref and emit an event
285
+ if (props.isActive) {
286
+ isActive.value = props.isActive
287
+ emit('updateScoreCode', currentScoreCode.value)
288
+ // console.log("emitted updateScoreCode");
289
+ // console.log(currentScoreCode.value);
290
+ }
291
+ isActive.value = props.isActive
292
+ }
293
+ })
294
+
295
+ watch(currentScoreCode, () => {
296
+ // Emit the event so that the parent component can update the tabText
297
+ // prop
298
+ emit('updateScoreCode', currentScoreCode.value)
299
+ // console.log("emitted updateScoreCode");
300
+ // console.log(currentScoreCode.value);
301
+ })
302
+
303
+ // const moveCursor = () => {
304
+ // openSheetMusicDisplay.cursor.next();
305
+ // };
306
+
307
+ // const fillScore = () => {
308
+ // // console.log(vt);
309
+ // // Perform any logic to fill the score
310
+ // response.value = 'Button pressed!'; // Update response if needed
311
+
312
+ // };
313
+ </script>
314
+
315
+ <template>
316
+ <!--
317
+ Which ever of the following elements wants to use the class
318
+ attributes coming from the parent component needs to use
319
+ :class="$attrs.class"
320
+ UNLESS I have a single root div-element
321
+ -->
322
+ <div>
323
+ <div>
324
+ <!-- <div class="at2-wrap"> -->
325
+ <div class="at2-wrap">
326
+ <div class="at2-overlay">
327
+ <div class="at2-overlay-content">
328
+ Music sheet is loading
329
+ </div>
330
+ </div>
331
+ <div class="at2-content">
332
+ <div class="at2-sidebar">
333
+ <div class="at2-sidebar-content">
334
+ <div class="at2-track-list" />
335
+ </div>
336
+ </div>
337
+ <div class="at2-viewport">
338
+ <div class="at2-main" />
339
+ </div>
340
+ </div>
341
+ <div class="at2-controls">
342
+ <div class="at2-controls-left">
343
+ <a class="btn at2-player-stop disabled">
344
+ <i class="fas i-fa:step-backward"> Begining </i>
345
+ </a>
346
+ <a class="btn at2-player-play-pause disabled">
347
+ <i class="fas i-fa:play">SkAta</i>
348
+ </a>
349
+ <span class="at2-player-progress">0%</span>
350
+ <div class="at2-song-info">
351
+ <span class="at2-song-title" /> -
352
+ <span class="at2-song-artist" />
353
+ </div>
354
+ <div class="at2-song-position">
355
+ 00:00 / 00:00
356
+ </div>
357
+ </div>
358
+ <div class="at2-controls-right">
359
+ <a class="btn toggle at2-count-in">
360
+ <i class="fas fa-hourglass-half" />
361
+ </a>
362
+ <a class="btn at2-metronome">
363
+ <i class="fas i-fa:edit" />
364
+ </a>
365
+ <a class="btn at2-loop">
366
+ <i class="fas i-fa:retweet" />
367
+ </a>
368
+ <a class="btn at2-print">
369
+ <i class="fas i-fa:print" />
370
+ </a>
371
+ <div class="at2-zoom">
372
+ <i class="fas i-fa:search" />
373
+ <select>
374
+ <option value="25">
375
+ 25%
376
+ </option>
377
+ <option value="50">
378
+ 50%
379
+ </option>
380
+ <option value="75">
381
+ 75%
382
+ </option>
383
+ <option value="90">
384
+ 90%
385
+ </option>
386
+ <option value="100" selected>
387
+ 100%
388
+ </option>
389
+ <option value="110">
390
+ 110%
391
+ </option>
392
+ <option value="125">
393
+ 125%
394
+ </option>
395
+ <option value="150">
396
+ 150%
397
+ </option>
398
+ <option value="200">
399
+ 200%
400
+ </option>
401
+ </select>
402
+ </div>
403
+ <div class="at2-layout">
404
+ <select>
405
+ <option value="horizontal">
406
+ Horizontal
407
+ </option>
408
+ <option value="page" selected>
409
+ Page
410
+ </option>
411
+ </select>
412
+ </div>
413
+ </div>
414
+ </div>
415
+ </div>
416
+ <button class="left-align" @click="showHideSettings">
417
+ Settings
418
+ </button>
419
+ <div :id="divId" class="scoreContainer22" />
420
+
421
+ <div v-show="settingsStatus" id="tabSettingsArea22">
422
+ <div id="paneContainerXml22" />
423
+ <textarea
424
+ v-model="currentScoreCode"
425
+ class="editorBox22"
426
+ placeholder="add your musicxml code here or drag a file"
427
+ @keyup="handleKeyUp"
428
+ @drop="handleDrop"
429
+ @dragover.prevent
430
+ />
431
+ </div>
432
+
433
+ <div v-if="showParsingError" class="parsing-error" v-html="parsingError" />
434
+ </div>
435
+ <template id="at2-track-template" ref="at2-trackTemplate">
436
+ <div class="at2-track">
437
+ <div class="at2-track-icon">
438
+ <i class="fas fa-guitar" />
439
+ </div>
440
+ <div class="at2-track-details">
441
+ <div class="at2-track-name" />
442
+ </div>
443
+ </div>
444
+ </template>
445
+ </div>
446
+ </template>
447
+
448
+ <style scoped>
449
+
450
+ </style>
frontend/src/style.css CHANGED
@@ -1,10 +1,9 @@
1
  :root {
2
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3
  line-height: 1.5;
4
  font-weight: 400;
5
 
6
  color-scheme: light dark;
7
- color: rgba(255, 255, 255, 0.87);
8
  background-color: #242424;
9
 
10
  font-synthesis: none;
@@ -14,10 +13,12 @@
14
 
15
  --primary-color: #3498db;
16
  --background-light: #c6dbfa;
 
17
  --background-dark: #3c3d3d;
18
  --accent-color: #b9a329;
19
  --text-color-dark: #333333;
20
  --text-color-light: #e0e0e0;
 
21
  }
22
 
23
  a {
@@ -30,31 +31,47 @@ a:hover {
30
  }
31
 
32
  body {
 
33
  margin: 0px;
34
- display: flex;
35
- place-items: top center;
36
- min-width: 320px;
37
- min-height: 100vh;
38
- /* background-color: #ff0000; */
39
- background-color: var(--background-light);
40
-
41
- overflow: auto;
 
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  }
44
 
45
  body::-webkit-scrollbar {
46
- width: 0em;
47
  }
48
 
49
  body::-webkit-scrollbar-thumb {
50
- background-color: #888; /* Color of the thumb */
51
- border-radius: 0.25em; /* Roundness of the thumb */
52
- display: none;
53
  }
54
 
55
  body::-webkit-scrollbar-track {
56
- background-color: #f0f0f0; /* Color of the track */
57
- display: none;
58
  }
59
 
60
  h1 {
@@ -85,18 +102,6 @@ button:focus-visible {
85
  padding: 2em;
86
  }
87
 
88
- #app {
89
- max-width: 1280px;
90
- margin: 0 auto;
91
- padding: 2rem;
92
- text-align: center;
93
- background-color: var(--background-dark);
94
- margin-top: 0px;
95
- border-radius: 8px;
96
- /* give some 3d shade */
97
- box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.75);
98
- }
99
-
100
  @media (prefers-color-scheme: light) {
101
  :root {
102
  color: #213547;
@@ -112,46 +117,46 @@ button:focus-visible {
112
 
113
 
114
  .editorBox {
115
- min-height: 100px;
116
- min-width: 200px;
117
- width: 500px;
118
- height: 145px;
119
- max-width:800px;
120
- overflow: auto;
121
- background-color: rgb(40, 41, 46);
122
- color: rgb(187, 188, 196);
123
- border-radius: 8px;
124
  }
125
  .editorBox::-webkit-scrollbar {
126
- width: 0.5em;
127
  }
128
 
129
  .editorBox::-webkit-scrollbar-thumb {
130
- background-color: #888; /* Color of the thumb */
131
- border-radius: 0.25em; /* Roundness of the thumb */
132
  }
133
 
134
  .editorBox::-webkit-scrollbar-track {
135
- background-color: #f0f0f0; /* Color of the track */
136
  }
137
 
138
  .parsing-error {
139
- color: red;
140
- margin-top: 10px;
141
- text-align: left; /* Set text alignment to left */
142
  }
143
 
144
  .scoreContainer {
145
- border: 0px solid rgb(212, 22, 22);
146
- overflow-x: auto;
147
  }
148
  .scoreContainer::-webkit-scrollbar {
149
- height: 0.5em;
150
  }
151
  .scoreContainer::-webkit-scrollbar-thumb {
152
- background-color: #888; /* Color of the thumb */
153
- border-radius: 0.25em; /* Roundness of the thumb */
154
  }
155
  .scoreContainer::-webkit-scrollbar-track {
156
- background-color: #f0f0f0; /* Color of the track */
157
  }
 
1
  :root {
2
+ font-family: Helvetica, Arial, sans-serif;
3
  line-height: 1.5;
4
  font-weight: 400;
5
 
6
  color-scheme: light dark;
 
7
  background-color: #242424;
8
 
9
  font-synthesis: none;
 
13
 
14
  --primary-color: #3498db;
15
  --background-light: #c6dbfa;
16
+ --background-light2: #dee8f8;
17
  --background-dark: #3c3d3d;
18
  --accent-color: #b9a329;
19
  --text-color-dark: #333333;
20
  --text-color-light: #e0e0e0;
21
+ color: var(--text-color-dark);
22
  }
23
 
24
  a {
 
31
  }
32
 
33
  body {
34
+ /* margin: 100px; */
35
  margin: 0px;
36
+ /* display: flex; */
37
+ /* place-items: top center; */
38
+ /* min-width: 320px; */
39
+ /* min-height: 100vh; */
40
+ /* overflow: auto; */
41
+ /* background-color: var(--background-dark); */
42
+ background-color: rgb(17, 73, 241);
43
+ /* width: 100vw; */
44
+ }
45
 
46
+ #app {
47
+ margin: 0px;
48
+ /* width: 100vw; */
49
+ /* max-width: 1280px; */
50
+ /* margin: 0 auto; */
51
+ /* margin: 100px; */
52
+ /* padding: 2rem; */
53
+ text-align: center;
54
+ /* background-color: var(--background-dark); */
55
+ /* background-color: rgb(172, 89, 89); */
56
+ /* margin-top: 0px; */
57
+ /* border-radius: 8px; */
58
+ /* give some 3d shade */
59
+ /* box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.75); */
60
  }
61
 
62
  body::-webkit-scrollbar {
63
+ width: 0em;
64
  }
65
 
66
  body::-webkit-scrollbar-thumb {
67
+ background-color: #888; /* Color of the thumb */
68
+ border-radius: 0.25em; /* Roundness of the thumb */
69
+ display: none;
70
  }
71
 
72
  body::-webkit-scrollbar-track {
73
+ background-color: #f0f0f0; /* Color of the track */
74
+ display: none;
75
  }
76
 
77
  h1 {
 
102
  padding: 2em;
103
  }
104
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  @media (prefers-color-scheme: light) {
106
  :root {
107
  color: #213547;
 
117
 
118
 
119
  .editorBox {
120
+ min-height: 100px;
121
+ min-width: 200px;
122
+ width: 500px;
123
+ height: 145px;
124
+ max-width:800px;
125
+ overflow: auto;
126
+ background-color: rgb(40, 41, 46);
127
+ color: rgb(187, 188, 196);
128
+ border-radius: 8px;
129
  }
130
  .editorBox::-webkit-scrollbar {
131
+ width: 0.5em;
132
  }
133
 
134
  .editorBox::-webkit-scrollbar-thumb {
135
+ background-color: #888; /* Color of the thumb */
136
+ border-radius: 0.25em; /* Roundness of the thumb */
137
  }
138
 
139
  .editorBox::-webkit-scrollbar-track {
140
+ background-color: #f0f0f0; /* Color of the track */
141
  }
142
 
143
  .parsing-error {
144
+ color: red;
145
+ margin-top: 10px;
146
+ text-align: left; /* Set text alignment to left */
147
  }
148
 
149
  .scoreContainer {
150
+ border: 0px solid rgb(212, 22, 22);
151
+ overflow-x: auto;
152
  }
153
  .scoreContainer::-webkit-scrollbar {
154
+ height: 0.5em;
155
  }
156
  .scoreContainer::-webkit-scrollbar-thumb {
157
+ background-color: #888; /* Color of the thumb */
158
+ border-radius: 0.25em; /* Roundness of the thumb */
159
  }
160
  .scoreContainer::-webkit-scrollbar-track {
161
+ background-color: #f0f0f0; /* Color of the track */
162
  }
frontend/src/views/Analyzer.vue CHANGED
@@ -1,8 +1,10 @@
1
  <script setup>
2
  import { client } from '@gradio/client'
3
  import { onMounted, ref } from 'vue'
 
4
  import VextabWidget from '@/components/VextabWidget.vue'
5
  import MusicXmlWidget from '@/components/MusicXmlWidget.vue'
 
6
  import '@/style.css'
7
 
8
  let app = null
@@ -11,8 +13,9 @@ const currentTab = ref(0)
11
  const currentScoreCode = ref('')
12
  const tabs = [
13
  { name: 'VexTab', component: VextabWidget, id: 'first' },
14
- { name: 'VexTab2', component: VextabWidget, id: 'second' },
15
  { name: 'MusicXml', component: MusicXmlWidget, id: 'second' },
 
 
16
  ]
17
  const tabText_component1 = `
18
  tabstave notation=true key=A time=4/4
@@ -30,8 +33,7 @@ notes :q =|: (5/2.5/3.7/4) :8 7-5h6/3 ^3^ 5h6-7/5 ^3^ :q 7V/4 |
30
  async function connectToServer() {
31
  try {
32
  // app = await client('xribene/guitar-diff')
33
- app = await client("https://xribene-guitar-diff.hf.space/--replicas/qmsjt/");
34
-
35
  }
36
  catch (error) {
37
  console.error('Error fetching data:', error)
@@ -90,6 +92,7 @@ function updateScoreCode(scoreCode) {
90
  <TweakComponent />
91
  </div> -->
92
  <!-- Vextab Widget -->
 
93
  <div v-show="currentTab === 0" class="tab" :class="{ active: currentTab === 0 }">
94
  <VextabWidget
95
  :id="tabs[currentTab].id"
@@ -100,25 +103,36 @@ function updateScoreCode(scoreCode) {
100
  />
101
  </div>
102
 
103
- <div v-show="currentTab === 1" class="tab" :class="{ active: currentTab === 1 }">
104
- <VextabWidget
 
105
  :id="tabs[currentTab].id"
106
- class="tab-vexTab"
107
- :tab-text="tabText_component2"
108
  :is-active="currentTab === 1"
109
  @update-score-code="updateScoreCode"
110
  />
111
  </div>
112
 
113
- <!-- MusicXml Widget -->
114
- <div v-show="currentTab === 2" class="tab" :class="{ active: currentTab === 2 }">
115
- <MusicXmlWidget
116
  :id="tabs[currentTab].id"
117
- class="tab-musicXml"
 
118
  :is-active="currentTab === 2"
119
  @update-score-code="updateScoreCode"
120
  />
121
- </div>
 
 
 
 
 
 
 
 
 
 
 
122
 
123
  <!-- Report Container -->
124
  <div class="debug-background-color report-container">
 
1
  <script setup>
2
  import { client } from '@gradio/client'
3
  import { onMounted, ref } from 'vue'
4
+ import AlphaTabWidget from '../components/AlphaTabWidget.vue'
5
  import VextabWidget from '@/components/VextabWidget.vue'
6
  import MusicXmlWidget from '@/components/MusicXmlWidget.vue'
7
+ import ScoreBaseWidget from '@/components/ScoreWidgetBase.vue'
8
  import '@/style.css'
9
 
10
  let app = null
 
13
  const currentScoreCode = ref('')
14
  const tabs = [
15
  { name: 'VexTab', component: VextabWidget, id: 'first' },
 
16
  { name: 'MusicXml', component: MusicXmlWidget, id: 'second' },
17
+ // { name: 'AlphaTab', component: AlphaTabWidget, id: 'third' },
18
+ // { name: 'Base', component: ScoreBaseWidget, id: 'fourth' },
19
  ]
20
  const tabText_component1 = `
21
  tabstave notation=true key=A time=4/4
 
33
  async function connectToServer() {
34
  try {
35
  // app = await client('xribene/guitar-diff')
36
+ app = await client('https://xribene-guitar-diff.hf.space/--replicas/qmsjt/')
 
37
  }
38
  catch (error) {
39
  console.error('Error fetching data:', error)
 
92
  <TweakComponent />
93
  </div> -->
94
  <!-- Vextab Widget -->
95
+ <!-- <div class="tab-content"> -->
96
  <div v-show="currentTab === 0" class="tab" :class="{ active: currentTab === 0 }">
97
  <VextabWidget
98
  :id="tabs[currentTab].id"
 
103
  />
104
  </div>
105
 
106
+ <!-- MusicXml Widget -->
107
+ <div v-show="currentTab === 1" class="tab" :class="{ active: currentTab === 2 }">
108
+ <MusicXmlWidget
109
  :id="tabs[currentTab].id"
110
+ class="tab-musicXml"
 
111
  :is-active="currentTab === 1"
112
  @update-score-code="updateScoreCode"
113
  />
114
  </div>
115
 
116
+ <!-- <div v-show="currentTab === 2" class="tab" :class="{ active: currentTab === 1 }">
117
+ <AlphaTabWidget
 
118
  :id="tabs[currentTab].id"
119
+ class="tab-vexTab"
120
+ :tab-text="tabText_component2"
121
  :is-active="currentTab === 2"
122
  @update-score-code="updateScoreCode"
123
  />
124
+ </div> -->
125
+
126
+ <!-- <div v-show="currentTab === 3" class="tab" :class="{ active: currentTab === 1 }">
127
+ <ScoreBaseWidget
128
+ :id="tabs[currentTab].id"
129
+ class="tab-vexTab"
130
+ :tab-text="tabText_component2"
131
+ :is-active="currentTab === 3"
132
+ @update-score-code="updateScoreCode"
133
+ />
134
+ </div> -->
135
+ <!-- </div> -->
136
 
137
  <!-- Report Container -->
138
  <div class="debug-background-color report-container">
frontend/uno.config.js CHANGED
@@ -30,6 +30,7 @@ export default defineConfig({
30
  logos: () => import('@iconify/json/json/logos.json').then(i => i.default),
31
  twemoji: () => import('@iconify/json/json/twemoji.json').then(i => i.default),
32
  vscode: () => import('@iconify/json/json/vscode-icons.json').then(i => i.default),
 
33
  },
34
  }),
35
  presetWebFonts({
 
30
  logos: () => import('@iconify/json/json/logos.json').then(i => i.default),
31
  twemoji: () => import('@iconify/json/json/twemoji.json').then(i => i.default),
32
  vscode: () => import('@iconify/json/json/vscode-icons.json').then(i => i.default),
33
+ fa: () => import('@iconify/json/json/fa-solid.json').then(i => i.default),
34
  },
35
  }),
36
  presetWebFonts({
index.html ADDED
@@ -0,0 +1,517 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>AlphaTab Tutorial</title>
6
+ <script src="https://cdn.jsdelivr.net/npm/@coderline/alphatab@latest/dist/alphaTab.js"></script>
7
+ <script src="https://kit.fontawesome.com/b43f0e512e.js"></script>
8
+ <style type="text/css">
9
+ body {
10
+ font-family: Arial, Helvetica, sans-serif;
11
+ font-size: 12px;
12
+ }
13
+
14
+ .at-wrap {
15
+ width: 80vw;
16
+ height: 80vh;
17
+ margin: 0 auto;
18
+ border: 1px solid rgba(0, 0, 0, 0.12);
19
+ display: flex;
20
+ flex-direction: column;
21
+ overflow: hidden;
22
+ position: relative;
23
+ }
24
+
25
+ .at-content {
26
+ position: relative;
27
+ overflow: hidden;
28
+ flex: 1 1 auto;
29
+ }
30
+
31
+ /** Sidebar **/
32
+ .at-sidebar {
33
+ position: absolute;
34
+ top: 0;
35
+ left: 0;
36
+ bottom: 0;
37
+ max-width: 70px;
38
+ width: auto;
39
+ display: flex;
40
+ align-content: stretch;
41
+ z-index: 1001;
42
+ overflow: hidden;
43
+ border-right: 1px solid rgba(0, 0, 0, 0.12);
44
+ background: #f7f7f7;
45
+ }
46
+
47
+ .at-sidebar:hover {
48
+ max-width: 400px;
49
+ transition: max-width 0.2s;
50
+ overflow-y: auto;
51
+ }
52
+
53
+ .at-viewport {
54
+ overflow-y: auto;
55
+ position: absolute;
56
+ top: 0;
57
+ left: 70px;
58
+ right: 0;
59
+ bottom: 0;
60
+ padding-right: 20px;
61
+ }
62
+
63
+ .at-footer {
64
+ flex: 0 0 auto;
65
+ background: #436d9d;
66
+ color: #fff;
67
+ }
68
+
69
+ /** Overlay **/
70
+
71
+ .at-overlay {
72
+ /** Fill Parent */
73
+ position: absolute;
74
+ top: 0;
75
+ left: 0;
76
+ right: 0;
77
+ bottom: 0;
78
+ z-index: 1002;
79
+
80
+ /* Blurry dark shade */
81
+ backdrop-filter: blur(3px);
82
+ background: rgba(0, 0, 0, 0.5);
83
+
84
+ /** center content */
85
+ display: flex;
86
+ justify-content: center;
87
+ align-items: flex-start;
88
+ }
89
+
90
+ .at-overlay-content {
91
+ /* white box with drop-shadow */
92
+ margin-top: 20px;
93
+ background: #fff;
94
+ box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.3);
95
+ padding: 10px;
96
+ }
97
+
98
+ /** Track selector **/
99
+ .at-track {
100
+ display: flex;
101
+ position: relative;
102
+ padding: 5px;
103
+ transition: background 0.2s;
104
+ cursor: pointer;
105
+ }
106
+
107
+ .at-track:hover {
108
+ background: rgba(0, 0, 0, 0.1);
109
+ }
110
+
111
+ .at-track > .at-track-icon,
112
+ .at-track > .at-track-details {
113
+ display: flex;
114
+ flex-direction: column;
115
+ justify-content: center;
116
+ }
117
+
118
+ .at-track > .at-track-icon {
119
+ flex-shrink: 0;
120
+ font-size: 32px;
121
+ opacity: 0.5;
122
+ transition: opacity 0.2s;
123
+ width: 64px;
124
+ height: 64px;
125
+ margin-right: 5px;
126
+ align-items: center;
127
+ }
128
+
129
+ .at-track-name {
130
+ font-weight: bold;
131
+ margin-bottom: 5px;
132
+ }
133
+
134
+ .at-track:hover > .at-track-icon {
135
+ opacity: 0.8;
136
+ }
137
+
138
+ .at-track.active {
139
+ background: rgba(0, 0, 0, 0.03);
140
+ }
141
+
142
+ .at-track.active > .at-track-icon {
143
+ color: #4972a1;
144
+ opacity: 1;
145
+ }
146
+
147
+ .at-track > .at-track-name {
148
+ font-weight: 500;
149
+ }
150
+
151
+ /** Footer **/
152
+ .at-controls {
153
+ flex: 0 0 auto;
154
+ display: flex;
155
+ justify-content: space-between;
156
+ background: #436d9d;
157
+ color: #fff;
158
+ }
159
+
160
+ .at-controls > div {
161
+ display: flex;
162
+ justify-content: flex-start;
163
+ align-content: center;
164
+ align-items: center;
165
+ }
166
+
167
+ .at-controls > div > * {
168
+ display: flex;
169
+ text-align: center;
170
+ align-items: center;
171
+ justify-content: center;
172
+ cursor: pointer;
173
+ padding: 4px;
174
+ margin: 0 3px;
175
+ }
176
+
177
+ .at-controls .btn {
178
+ color: #fff;
179
+ border-radius: 0;
180
+ height: 40px;
181
+ width: 40px;
182
+ height: 40px;
183
+ font-size: 16px;
184
+ }
185
+ .at-controls .btn.disabled {
186
+ cursor: progress;
187
+ opacity: 0.5;
188
+ }
189
+
190
+ .at-controls a.active {
191
+ background: #5588c7;
192
+ text-decoration: none;
193
+ }
194
+
195
+ .at-controls .btn i {
196
+ vertical-align: top;
197
+ }
198
+
199
+ .at-controls select {
200
+ -moz-appearance: none;
201
+ -webkit-appearance: none;
202
+ appearance: none;
203
+ border: none;
204
+ width: 100%;
205
+ height: 40px;
206
+ background: #436d9d;
207
+ padding: 4px 10px;
208
+ color: #fff;
209
+ font-size: 16px;
210
+ text-align-last: center;
211
+ text-align: center;
212
+ -ms-text-align-last: center;
213
+ -moz-text-align-last: center;
214
+ cursor: pointer;
215
+ }
216
+
217
+ .at-song-title {
218
+ font-weight: bold;
219
+ }
220
+
221
+ .at-cursor-bar {
222
+ /* Defines the color of the bar background when a bar is played */
223
+ background: rgba(255, 242, 0, 0.25);
224
+ }
225
+
226
+ .at-selection div {
227
+ /* Defines the color of the selection background */
228
+ background: rgba(64, 64, 255, 0.1);
229
+ }
230
+
231
+ .at-cursor-beat {
232
+ /* Defines the beat cursor */
233
+ background: rgba(64, 64, 255, 0.75);
234
+ width: 3px;
235
+ }
236
+
237
+ .at-highlight * {
238
+ /* Defines the color of the music symbols when they are being played (svg) */
239
+ fill: #0078ff;
240
+ stroke: #0078ff;
241
+ }
242
+ </style>
243
+ </head>
244
+
245
+ <body>
246
+ <div class="at-wrap">
247
+ <div class="at-overlay">
248
+ <div class="at-overlay-content">
249
+ Music sheet is loading
250
+ </div>
251
+ </div>
252
+ <div class="at-content">
253
+ <div class="at-sidebar">
254
+ <div class="at-sidebar-content">
255
+ <div class="at-track-list"></div>
256
+ </div>
257
+ </div>
258
+ <div class="at-viewport">
259
+ <div class="at-main"></div>
260
+ </div>
261
+ </div>
262
+ <div class="at-controls">
263
+ <div class="at-controls-left">
264
+ <a class="btn at-player-stop disabled">
265
+ <i class="fas fa-step-backward"></i>
266
+ </a>
267
+ <a class="btn at-player-play-pause disabled">
268
+ <i class="fas fa-play"></i>
269
+ </a>
270
+ <span class="at-player-progress">0%</span>
271
+ <div class="at-song-info">
272
+ <span class="at-song-title"></span> -
273
+ <span class="at-song-artist"></span>
274
+ </div>
275
+ <div class="at-song-position">00:00 / 00:00</div>
276
+ </div>
277
+ <div class="at-controls-right">
278
+ <a class="btn toggle at-count-in">
279
+ <i class="fas fa-hourglass-half"></i>
280
+ </a>
281
+ <a class="btn at-metronome">
282
+ <i class="fas fa-edit"></i>
283
+ </a>
284
+ <a class="btn at-loop">
285
+ <i class="fas fa-retweet"></i>
286
+ </a>
287
+ <a class="btn at-print">
288
+ <i class="fas fa-print"></i>
289
+ </a>
290
+ <div class="at-zoom">
291
+ <i class="fas fa-search"></i>
292
+ <select>
293
+ <option value="25">25%</option>
294
+ <option value="50">50%</option>
295
+ <option value="75">75%</option>
296
+ <option value="90">90%</option>
297
+ <option value="100" selected>100%</option>
298
+ <option value="110">110%</option>
299
+ <option value="125">125%</option>
300
+ <option value="150">150%</option>
301
+ <option value="200">200%</option>
302
+ </select>
303
+ </div>
304
+ <div class="at-layout">
305
+ <select>
306
+ <option value="horizontal">Horizontal</option>
307
+ <option value="page" selected>Page</option>
308
+ </select>
309
+ </div>
310
+ </div>
311
+ </div>
312
+ </div>
313
+
314
+ <template id="at-track-template">
315
+ <div class="at-track">
316
+ <div class="at-track-icon">
317
+ <i class="fas fa-guitar"></i>
318
+ </div>
319
+ <div class="at-track-details">
320
+ <div class="at-track-name"></div>
321
+ </div>
322
+ </div>
323
+ </template>
324
+
325
+ <script type="text/javascript">
326
+ // load elements
327
+ const wrapper = document.querySelector(".at-wrap");
328
+ const main = wrapper.querySelector(".at-main");
329
+
330
+ // initialize alphatab
331
+ const settings = {
332
+ file: "https://www.alphatab.net/files/canon.gp",
333
+ player: {
334
+ enablePlayer: true,
335
+ soundFont: "https://cdn.jsdelivr.net/npm/@coderline/alphatab@latest/dist/soundfont/sonivox.sf2",
336
+ scrollElement: wrapper.querySelector('.at-viewport')
337
+ },
338
+ };
339
+ const api = new alphaTab.AlphaTabApi(main, settings);
340
+
341
+ // overlay logic
342
+ const overlay = wrapper.querySelector(".at-overlay");
343
+ api.renderStarted.on(() => {
344
+ overlay.style.display = "flex";
345
+ });
346
+ api.renderFinished.on(() => {
347
+ overlay.style.display = "none";
348
+ });
349
+
350
+ // track selector
351
+ function createTrackItem(track) {
352
+ const trackItem = document
353
+ .querySelector("#at-track-template")
354
+ .content.cloneNode(true).firstElementChild;
355
+ trackItem.querySelector(".at-track-name").innerText = track.name;
356
+ trackItem.track = track;
357
+ trackItem.onclick = (e) => {
358
+ e.stopPropagation();
359
+ api.renderTracks([track]);
360
+ };
361
+ return trackItem;
362
+ }
363
+ const trackList = wrapper.querySelector(".at-track-list");
364
+ api.scoreLoaded.on((score) => {
365
+ // clear items
366
+ trackList.innerHTML = "";
367
+ // generate a track item for all tracks of the score
368
+ score.tracks.forEach((track) => {
369
+ trackList.appendChild(createTrackItem(track));
370
+ });
371
+ });
372
+ api.renderStarted.on(() => {
373
+ // collect tracks being rendered
374
+ const tracks = new Map();
375
+ api.tracks.forEach((t) => {
376
+ tracks.set(t.index, t);
377
+ });
378
+ // mark the item as active or not
379
+ const trackItems = trackList.querySelectorAll(".at-track");
380
+ trackItems.forEach((trackItem) => {
381
+ if (tracks.has(trackItem.track.index)) {
382
+ trackItem.classList.add("active");
383
+ } else {
384
+ trackItem.classList.remove("active");
385
+ }
386
+ });
387
+ });
388
+
389
+ /** Controls **/
390
+ api.scoreLoaded.on((score) => {
391
+ wrapper.querySelector(".at-song-title").innerText = score.title;
392
+ wrapper.querySelector(".at-song-artist").innerText = score.artist;
393
+ });
394
+
395
+ const countIn = wrapper.querySelector('.at-controls .at-count-in');
396
+ countIn.onclick = () => {
397
+ countIn.classList.toggle('active');
398
+ if (countIn.classList.contains('active')) {
399
+ api.countInVolume = 1;
400
+ } else {
401
+ api.countInVolume = 0;
402
+ }
403
+ };
404
+
405
+ const metronome = wrapper.querySelector(".at-controls .at-metronome");
406
+ metronome.onclick = () => {
407
+ metronome.classList.toggle("active");
408
+ if (metronome.classList.contains("active")) {
409
+ api.metronomeVolume = 1;
410
+ } else {
411
+ api.metronomeVolume = 0;
412
+ }
413
+ };
414
+
415
+ const loop = wrapper.querySelector(".at-controls .at-loop");
416
+ loop.onclick = () => {
417
+ loop.classList.toggle("active");
418
+ api.isLooping = loop.classList.contains("active");
419
+ };
420
+
421
+ wrapper.querySelector(".at-controls .at-print").onclick = () => {
422
+ api.print();
423
+ };
424
+
425
+ const zoom = wrapper.querySelector(".at-controls .at-zoom select");
426
+ zoom.onchange = () => {
427
+ const zoomLevel = parseInt(zoom.value) / 100;
428
+ api.settings.display.scale = zoomLevel;
429
+ api.updateSettings();
430
+ api.render();
431
+ };
432
+
433
+ const layout = wrapper.querySelector(".at-controls .at-layout select");
434
+ layout.onchange = () => {
435
+ switch (layout.value) {
436
+ case "horizontal":
437
+ api.settings.display.layoutMode = alphaTab.LayoutMode.Horizontal;
438
+ break;
439
+ case "page":
440
+ api.settings.display.layoutMode = alphaTab.LayoutMode.Page;
441
+ break;
442
+ }
443
+ api.updateSettings();
444
+ api.render();
445
+ };
446
+
447
+ // player loading indicator
448
+ const playerIndicator = wrapper.querySelector(
449
+ ".at-controls .at-player-progress"
450
+ );
451
+ api.soundFontLoad.on((e) => {
452
+ const percentage = Math.floor((e.loaded / e.total) * 100);
453
+ playerIndicator.innerText = percentage + "%";
454
+ });
455
+ api.playerReady.on(() => {
456
+ playerIndicator.style.display = "none";
457
+ });
458
+
459
+ // main player controls
460
+ const playPause = wrapper.querySelector(
461
+ ".at-controls .at-player-play-pause"
462
+ );
463
+ const stop = wrapper.querySelector(".at-controls .at-player-stop");
464
+ playPause.onclick = (e) => {
465
+ if (e.target.classList.contains("disabled")) {
466
+ return;
467
+ }
468
+ api.playPause();
469
+ };
470
+ stop.onclick = (e) => {
471
+ if (e.target.classList.contains("disabled")) {
472
+ return;
473
+ }
474
+ api.stop();
475
+ };
476
+ api.playerReady.on(() => {
477
+ playPause.classList.remove("disabled");
478
+ stop.classList.remove("disabled");
479
+ });
480
+ api.playerStateChanged.on((e) => {
481
+ const icon = playPause.querySelector("i.fas");
482
+ if (e.state === alphaTab.synth.PlayerState.Playing) {
483
+ icon.classList.remove("fa-play");
484
+ icon.classList.add("fa-pause");
485
+ } else {
486
+ icon.classList.remove("fa-pause");
487
+ icon.classList.add("fa-play");
488
+ }
489
+ });
490
+
491
+ // song position
492
+ function formatDuration(milliseconds) {
493
+ let seconds = milliseconds / 1000;
494
+ const minutes = (seconds / 60) | 0;
495
+ seconds = (seconds - minutes * 60) | 0;
496
+ return (
497
+ String(minutes).padStart(2, "0") +
498
+ ":" +
499
+ String(seconds).padStart(2, "0")
500
+ );
501
+ }
502
+
503
+ const songPosition = wrapper.querySelector(".at-song-position");
504
+ let previousTime = -1;
505
+ api.playerPositionChanged.on((e) => {
506
+ // reduce number of UI updates to second changes.
507
+ const currentSeconds = (e.currentTime / 1000) | 0;
508
+ if (currentSeconds == previousTime) {
509
+ return;
510
+ }
511
+
512
+ songPosition.innerText =
513
+ formatDuration(e.currentTime) + " / " + formatDuration(e.endTime);
514
+ });
515
+ </script>
516
+ </body>
517
+ </html>