vibe-editor 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/package.json +66 -61
  2. package/src/scripts/js/Core.js +28 -9
  3. package/src/scripts/js/entry.js +6 -5
  4. package/src/scripts/js/gui/Annotations.js +27 -12
  5. package/src/scripts/js/gui/ScoreManipulator.js +6 -4
  6. package/src/scripts/js/gui/Tabbar.js +13 -4
  7. package/src/scripts/js/handlers/AnnotationChangeHandler.js +13 -1
  8. package/src/scripts/js/handlers/InsertModeHandler.js +3 -3
  9. package/src/scripts/js/handlers/PhantomElementHandler.js +3 -10
  10. package/src/scripts/js/handlers/WindowHandler.js +7 -7
  11. package/src/scripts/js/scripts/Core.js +932 -0
  12. package/src/scripts/js/scripts/MusicProcessor.js +810 -0
  13. package/src/scripts/js/scripts/VIBE.js +219 -0
  14. package/src/scripts/js/scripts/datastructures/MeasureMatrix.js +156 -0
  15. package/src/scripts/js/scripts/gui/Annotations.js +549 -0
  16. package/src/scripts/js/scripts/gui/Cursor.js +203 -0
  17. package/src/scripts/js/scripts/gui/PhantomElement.js +132 -0
  18. package/src/scripts/js/scripts/gui/ScoreManipulator.js +188 -0
  19. package/src/scripts/js/scripts/gui/Tabbar.js +705 -0
  20. package/src/scripts/js/{gui/Toolbar copy.js → scripts/gui/Toolbar.js} +15 -11
  21. package/src/scripts/js/scripts/handlers/AnnotationChangeHandler.js +650 -0
  22. package/src/scripts/js/scripts/handlers/ClickModeHandler.js +535 -0
  23. package/src/scripts/js/{gui → scripts/handlers}/CustomAnnotationShapeDrawer.js +34 -17
  24. package/src/scripts/js/{handlers/ModHandler.js → scripts/handlers/CustomToolbarHandler.js} +54 -66
  25. package/src/scripts/js/scripts/handlers/GlobalKeyboardHandler.js +372 -0
  26. package/src/scripts/js/scripts/handlers/Handler.js +2 -0
  27. package/src/scripts/js/{handlers/InsertModeHandler_deprecated.js → scripts/handlers/InsertModeHandler.js} +117 -164
  28. package/src/scripts/js/scripts/handlers/KeyModeHandler.js +405 -0
  29. package/src/scripts/js/scripts/handlers/LabelHandler.js +463 -0
  30. package/src/scripts/js/scripts/handlers/NoteDragHandler.js +97 -0
  31. package/src/scripts/js/scripts/handlers/PhantomElementHandler.js +161 -0
  32. package/src/scripts/js/scripts/handlers/ScoreManipulatorHandler.js +233 -0
  33. package/src/scripts/js/scripts/handlers/SidebarHandler.js +506 -0
  34. package/src/scripts/js/scripts/handlers/TooltipHandler.js +132 -0
  35. package/src/scripts/js/scripts/handlers/WindowHandler.js +278 -0
  36. package/src/scripts/js/scripts/utils/MEIOperations.js +2121 -0
  37. package/src/scripts/js/{utils/Mouse2MEI.js → scripts/utils/Mouse2SVG.js} +225 -57
  38. package/src/scripts/js/scripts/utils/SVGEditor.js +453 -0
  39. package/src/scripts/js/scripts/utils/Types.js +2 -0
  40. package/src/scripts/js/{utils/VerovioWrapper copy.js → scripts/utils/VerovioWrapper.js} +32 -18
  41. package/src/scripts/js/scripts/utils/coordinates.js +78 -0
  42. package/src/scripts/js/utils/Mouse2SVG.js +11 -4
  43. package/src/scripts/js/utils/VerovioWrapper.js +4 -4
  44. package/src/scripts/js/utils/coordinates.js +26 -2
  45. package/src/styles/vibe.css +32 -6
  46. package/src/scripts/js/.DS_Store +0 -0
  47. package/src/scripts/js/MusicPlayer.js +0 -572
  48. package/src/scripts/js/datastructures/ScoreGraph copy.js +0 -432
  49. package/src/scripts/js/gui/CustomAnnotationDrawer.js +0 -114
  50. package/src/scripts/js/handlers/AnnotationDragHandler.js +0 -113
  51. package/src/scripts/js/handlers/AnnotationLineHandler.js +0 -113
  52. package/src/scripts/js/handlers/ArticulationHandler.js +0 -20
  53. package/src/scripts/js/handlers/HarmonyHandler.js +0 -282
  54. package/src/scripts/js/handlers/InsertModeHandler copy.js +0 -423
  55. package/src/scripts/js/handlers/KeyModeHandler copy.js +0 -407
  56. package/src/scripts/js/handlers/KeyModeHandler_deprecated.js +0 -411
  57. package/src/scripts/js/handlers/NoteDragHandler copy.js +0 -148
  58. package/src/scripts/js/handlers/NoteDragHandler_deprecated.js +0 -150
  59. package/src/scripts/js/handlers/SelectionHandler.js +0 -262
  60. package/src/scripts/js/utils/RectWrapper.js +0 -10
  61. package/src/scripts/js/utils/SVGFiller.js +0 -245
  62. package/src/scripts/js/utils/VerovioWrapperLocal.js +0 -156
  63. package/src/scripts/js/utils/firefoxBBoxes.js +0 -143
  64. /package/src/scripts/js/{assets → scripts/assets}/mei_template.js +0 -0
  65. /package/src/scripts/js/{constants.js → scripts/constants.js} +0 -0
  66. /package/src/scripts/js/{datastructures → scripts/datastructures}/ScoreGraph.js +0 -0
  67. /package/src/scripts/js/{datastructures → scripts/datastructures}/ScoreGraph_deprecated.js +0 -0
  68. /package/src/scripts/js/{datastructures → scripts/datastructures}/ScoreNode.js +0 -0
  69. /package/src/scripts/js/{gui → scripts/gui}/HarmonyLabel.js +0 -0
  70. /package/src/scripts/js/{gui → scripts/gui}/Label.js +0 -0
  71. /package/src/scripts/js/{gui → scripts/gui}/TempoLabel.js +0 -0
  72. /package/src/scripts/js/{handlers → scripts/handlers}/DeleteHandler.js +0 -0
  73. /package/src/scripts/js/{utils → scripts/utils}/DOMCreator.js +0 -0
  74. /package/src/scripts/js/{utils → scripts/utils}/MEIConverter.js +0 -0
  75. /package/src/scripts/js/{utils → scripts/utils}/ReactWrapper.js +0 -0
  76. /package/src/scripts/js/{utils → scripts/utils}/convenienceQueries.js +0 -0
  77. /package/src/scripts/js/{utils → scripts/utils}/mappings.js +0 -0
  78. /package/src/scripts/js/{utils → scripts/utils}/random.js +0 -0
@@ -0,0 +1,810 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const Tone = require("tone");
4
+ const mappings_1 = require("./utils/mappings");
5
+ const midi_player_js_1 = require("midi-player-js");
6
+ const Soundfont = require("soundfont-player");
7
+ const constants_1 = require("./constants");
8
+ const coordinates = require("./utils/coordinates");
9
+ const cq = require("./utils/convenienceQueries");
10
+ const meiConverter = require("./utils/MEIConverter");
11
+ const buffer_1 = require("buffer");
12
+ const axios_1 = require("axios");
13
+ const currentlyPlayingFlag = "currentlyPlaying";
14
+ const followerRectID = "followerRect";
15
+ const ac = window.AudioContext;
16
+ const synth = new Tone.Synth().toDestination();
17
+ class MusicProcessor {
18
+ constructor(containerId) {
19
+ this.spaceCount = 0;
20
+ this.playBtn = (function playBtn(e) {
21
+ e.preventDefault();
22
+ this.context.resume().then(() => this.playMidi());
23
+ }).bind(this);
24
+ this.rewindBtn = (function rewindBtn(e) {
25
+ e.preventDefault();
26
+ this.rewindMidi();
27
+ }).bind(this);
28
+ this.fetchAudioSeconds = (function fetchAudioSeconds(e) {
29
+ var _a;
30
+ //if ((this.container.querySelector("#recordAlignment") as HTMLInputElement)?.checked || this.audioTimes?.size === 0) return
31
+ if (((_a = this.audioTimes) === null || _a === void 0 ? void 0 : _a.size) === 0)
32
+ return;
33
+ var sec = e.target.currentTime;
34
+ this.highlight(sec, "", "audio");
35
+ this.drawFollowerRect();
36
+ }).bind(this);
37
+ this.resetSpacebarCount = (function resetAudioTimes(e) {
38
+ var input = e.target;
39
+ if (input.checked) {
40
+ this.spaceCount = 0;
41
+ }
42
+ }).bind(this);
43
+ this.preventAudioPause = (function preventAudioPause(e) {
44
+ if (e.code === "Space" && cq.getContainer(this.containerId).querySelector("#recordAlignment").checked) {
45
+ e.target.blur();
46
+ e.preventDefault();
47
+ }
48
+ }).bind(this);
49
+ this.recAlignmentManually = (function recAligmentManually(e) {
50
+ this.recordAlignment(e);
51
+ }).bind(this);
52
+ this.downloadAlignmentHandler = (function downloadAlignmentHandler(e) {
53
+ this.downloadAlignment(e);
54
+ }).bind(this);
55
+ this.handleCountdown = (function handleCountdown(e) {
56
+ var recToggle = this.container.querySelector("#recordAlignment");
57
+ if (!(recToggle === null || recToggle === void 0 ? void 0 : recToggle.checked))
58
+ return;
59
+ var slider = e.target;
60
+ this.countdown(slider);
61
+ }).bind(this);
62
+ this.midiPlayHandler = (function midiPlayHandler(e) {
63
+ if (!this.hasContainerFocus())
64
+ return;
65
+ this.midiPlayFunction(e);
66
+ }).bind(this);
67
+ this.setCurrentHighlightHandler = (function setCurrentNoteHandler(e) {
68
+ this.currentNote = e.target;
69
+ }).bind(this);
70
+ /**
71
+ * Set last clicked element to restartpoint
72
+ */
73
+ this.startPointHandler = (function startPointHandler(e) {
74
+ if (!this.hasContainerFocus())
75
+ return;
76
+ var playingNote = e.target;
77
+ playingNote = cq.getVrvSVG(this.containerId).querySelector("#" + playingNote.closest("[refId]").getAttribute("refId"));
78
+ playingNote = playingNote.closest(".note") || playingNote.closest(".rest") || playingNote.closest(".mRest");
79
+ if (playingNote !== null) {
80
+ this.timemap.forEach(tm => {
81
+ var _a;
82
+ if ((_a = tm.on) === null || _a === void 0 ? void 0 : _a.includes(playingNote.id)) {
83
+ this.restartTime = (tm.tstamp * this.pulse * this.tempo) / 60000;
84
+ }
85
+ });
86
+ }
87
+ }).bind(this);
88
+ /**
89
+ * Adds Class to be highlighted.
90
+ * Dispatches event for every Note which was started most currently
91
+ */
92
+ this.addClass = (function addClass(el, className) {
93
+ return new Promise(resolve => {
94
+ el.classList.add(className);
95
+ el.dispatchEvent(this.noteEvent);
96
+ resolve(true);
97
+ });
98
+ }).bind(this);
99
+ this.setContainerId(containerId);
100
+ this.noteEvent = new Event("currentNote");
101
+ this.playStartEvent = new Event("playStart");
102
+ this.playEndEvent = new Event("playEnd");
103
+ this.restartTime = 0;
104
+ this.setPlayListener();
105
+ if (navigator.userAgent.toLocaleLowerCase().indexOf("firefox") > 0) {
106
+ this.isFirefox = true;
107
+ }
108
+ else {
109
+ this.isFirefox = false;
110
+ }
111
+ this.getMidiInput();
112
+ }
113
+ /**
114
+ * Add Canvas in which all MusicPlayer SVGs are contained
115
+ */
116
+ addCanvas() {
117
+ //this.root = this.interactionOverlay //document.getElementById(c._vrvSVGID_)
118
+ this.rootBBox = this.interactionOverlay.getBoundingClientRect();
119
+ var rootWidth = this.rootBBox.width.toString();
120
+ var rootHeigth = this.rootBBox.height.toString();
121
+ this.interactionOverlay = cq.getInteractOverlay(this.containerId);
122
+ this.canvasMP = this.interactionOverlay.querySelector("#canvasMusicPlayer");
123
+ if (!this.canvasMP) {
124
+ this.canvasMP = document.createElementNS(constants_1.constants._SVGNS_, "svg");
125
+ this.canvasMP.setAttribute("id", "canvasMusicPlayer");
126
+ this.canvasMP.classList.add("canvas");
127
+ //this.canvasMP.setAttribute("viewBox", ["0", "0", rootWidth, rootHeigth].join(" "))
128
+ }
129
+ this.canvasMP.innerHTML = ""; // will delete followerRect if present (usually when score is loaded)
130
+ this.interactionOverlay.insertBefore(this.canvasMP, this.interactionOverlay.firstChild);
131
+ return this;
132
+ }
133
+ /**
134
+ * Initialize Player
135
+ */
136
+ initMidiPlayer() {
137
+ var that = this;
138
+ //@ts-ignore
139
+ this.midiPlayer = new midi_player_js_1.default.Player(function (event) {
140
+ if (event.name === "Set Tempo") {
141
+ that.tempo = event.data;
142
+ //that.pulse = (60000/ (event.data / 2 * 24))/10 //(60000/ (event.data * 24))/10000 //duration is in seconds
143
+ that.pulse = 120; //Math.floor((60 / event.data) * 1000 / 120)
144
+ }
145
+ if (event.name === 'Note on' && event.velocity !== 0) {
146
+ var track = event.track;
147
+ var time = event.tick;
148
+ that.restartTime = event.tick;
149
+ var duration = that.highlight(time, event.noteName, "midi") / 1000; // duratioin must be in seconds
150
+ if (!that.isFirefox) {
151
+ //that.drawFollowerRect()
152
+ }
153
+ if (that.instruments != undefined) {
154
+ var instr = that.instruments[track - 2];
155
+ instr.play(event.noteName, that.context.currentTime, { gain: event.velocity / 100, duration: duration });
156
+ }
157
+ }
158
+ });
159
+ this.midiPlayer.loadArrayBuffer(buffer_1.Buffer.from(this.midi, "base64"));
160
+ //this.mapDurations()
161
+ if (this.instruments == undefined) { // instruments only have to be updated, when new instrument (= track) is added
162
+ this.context = new ac();
163
+ this.instruments = new Array(this.midiPlayer.getEvents().length - 1);
164
+ this.initMidiInstruments();
165
+ }
166
+ }
167
+ /**
168
+ * Find Durations from timemap based on id of note.
169
+ * @param id id of note
170
+ * @returns duration or default 1 when no duration could be computed.
171
+ */
172
+ findDuration(id) {
173
+ var _a, _b;
174
+ var dur;
175
+ var start;
176
+ var end;
177
+ for (let i = 0; i < this.timemap.length; i++) {
178
+ var tm = this.timemap[i];
179
+ if ((_a = tm.on) === null || _a === void 0 ? void 0 : _a.includes(id)) {
180
+ start = tm.tstamp;
181
+ }
182
+ if ((_b = tm.off) === null || _b === void 0 ? void 0 : _b.includes(id)) {
183
+ end = tm.tstamp;
184
+ dur = end - start;
185
+ break;
186
+ }
187
+ }
188
+ return dur || 1;
189
+ }
190
+ /**
191
+ * Stop playing
192
+ *
193
+ **/
194
+ stopMidiInstruments() {
195
+ var _a;
196
+ document.dispatchEvent(this.playEndEvent);
197
+ this.midiPlayer.stop();
198
+ (_a = this.instruments) === null || _a === void 0 ? void 0 : _a.forEach(instr => instr.stop(this.context.currentTime));
199
+ this.midiPlayer = undefined;
200
+ this.stopTimeouts();
201
+ if (this.restartTime === 0) {
202
+ if (document.getElementById(followerRectID) !== null) {
203
+ document.getElementById(followerRectID).remove();
204
+ }
205
+ Array.from(document.getElementsByClassName(currentlyPlayingFlag)).forEach(element => {
206
+ element.classList.remove(currentlyPlayingFlag);
207
+ });
208
+ }
209
+ this.initMidiPlayer();
210
+ }
211
+ rewindMidi() {
212
+ if (this.midiPlayer != undefined) {
213
+ this.restartTime = 0;
214
+ this.stopMidiInstruments();
215
+ }
216
+ }
217
+ /**
218
+ * Initialize Instrument for
219
+ */
220
+ initMidiInstruments() {
221
+ this.setSoundfontsRecursive();
222
+ }
223
+ setSoundfontsRecursive(counter = 0) {
224
+ var i = counter;
225
+ var that = this;
226
+ if (i < this.instruments.length) {
227
+ Soundfont.instrument(this.context, "acoustic_grand_piano").then((instrument) => {
228
+ that.instruments[i] = instrument;
229
+ i += 1;
230
+ that.setSoundfontsRecursive(i);
231
+ });
232
+ }
233
+ }
234
+ playMidi() {
235
+ if (!cq.hasActiveElement(this.containerId))
236
+ return;
237
+ if (this.midiPlayer.isPlaying()) {
238
+ this.stopMidiInstruments();
239
+ }
240
+ else {
241
+ this.midiPlayer.on("endOfFile", () => {
242
+ this.rewindMidi();
243
+ });
244
+ this.midiPlayer.division = 120;
245
+ //this.player.tempo = this.tempo
246
+ this.midiPlayer.tick = this.restartTime || 0;
247
+ console.log("tick", this.midiPlayer.tick);
248
+ this.midiPlayer.skipToTick(this.restartTime);
249
+ this.midiPlayer.play();
250
+ document.dispatchEvent(this.playStartEvent);
251
+ }
252
+ }
253
+ ///// LISTENERS ////
254
+ setListeners() {
255
+ var _a, _b, _c;
256
+ var that = this;
257
+ if (!this.midiTimes) {
258
+ return;
259
+ }
260
+ var it = this.midiTimes.values();
261
+ var result = it.next();
262
+ while (!result.done) {
263
+ var arr = result.value;
264
+ arr.forEach(noteId => {
265
+ var _a;
266
+ const note = cq.getVrvSVG(this.containerId).querySelector(`#${noteId}`);
267
+ if (!note)
268
+ return;
269
+ note.addEventListener("currentNote", this.setCurrentHighlightHandler);
270
+ var id = ((_a = note.querySelector(".notehead")) === null || _a === void 0 ? void 0 : _a.id) || note.id;
271
+ var interactRect = cq.getInteractOverlay(that.containerId).querySelector("#scoreRects g[refId=\"" + id + "\"]");
272
+ interactRect === null || interactRect === void 0 ? void 0 : interactRect.addEventListener("click", this.startPointHandler);
273
+ });
274
+ result = it.next();
275
+ }
276
+ this.container.querySelector("#playBtn").addEventListener("click", this.playBtn);
277
+ this.container.querySelector("#rewindBtn").addEventListener("click", this.rewindBtn);
278
+ this.container.addEventListener("timeupdate", this.fetchAudioSeconds, true);
279
+ //this.container.querySelector("#recordAlignment")?.addEventListener("click", this.resetSpacebarCount)
280
+ (_a = this.container.querySelector("#audioSlider")) === null || _a === void 0 ? void 0 : _a.addEventListener("keydown", this.preventAudioPause);
281
+ (_b = this.container.querySelector("#audioSlider")) === null || _b === void 0 ? void 0 : _b.addEventListener("play", this.handleCountdown);
282
+ (_c = this.container.querySelector("#exportAlignment")) === null || _c === void 0 ? void 0 : _c.addEventListener("click", this.downloadAlignmentHandler);
283
+ //["ended", "pause"].forEach(ev => this.container.querySelector("#audioSlider")?.addEventListener(ev, function () { that.resetListeners() }))
284
+ document.addEventListener("keydown", this.recAlignmentManually);
285
+ }
286
+ removeListeners() {
287
+ var _a, _b, _c;
288
+ var that = this;
289
+ if (this.midiTimes == undefined) {
290
+ return;
291
+ }
292
+ var it = this.midiTimes.values();
293
+ var result = it.next();
294
+ while (!result.done) {
295
+ var arr = result.value;
296
+ arr.forEach(noteId => {
297
+ var _a;
298
+ const note = cq.getVrvSVG(this.containerId).querySelector(`#${noteId}`);
299
+ if (!note)
300
+ return;
301
+ note.removeEventListener("currentNote", this.setCurrentHighlightHandler);
302
+ var id = ((_a = note.querySelector(".notehead")) === null || _a === void 0 ? void 0 : _a.id) || note.id;
303
+ var interactRect = cq.getInteractOverlay(that.containerId).querySelector("#scoreRects g[refId=\"" + id + "\"]");
304
+ interactRect === null || interactRect === void 0 ? void 0 : interactRect.removeEventListener("click", this.startPointHandler);
305
+ });
306
+ result = it.next();
307
+ }
308
+ this.container.removeEventListener("timeupdate", this.fetchAudioSeconds, true);
309
+ (_a = this.container.querySelector("#audioSlider")) === null || _a === void 0 ? void 0 : _a.removeEventListener("keydown", this.preventAudioPause);
310
+ (_b = this.container.querySelector("#audioSlider")) === null || _b === void 0 ? void 0 : _b.removeEventListener("play", this.handleCountdown);
311
+ (_c = this.container.querySelector("#exportAlignment")) === null || _c === void 0 ? void 0 : _c.addEventListener("click", this.downloadAlignmentHandler);
312
+ //["ended", "pause"].forEach(ev => this.container.querySelector("#audioSlider")?.removeEventListener(ev, function () { that.resetListeners() }))
313
+ document.removeEventListener("keydown", this.recAlignmentManually);
314
+ }
315
+ resetListeners() {
316
+ this.removeListeners();
317
+ this.setListeners();
318
+ }
319
+ /**
320
+ * Record an alignment by pressing the space button.
321
+ * For each press the id of the next measure will be saved.
322
+ * @param e
323
+ * @returns
324
+ */
325
+ recordAlignment(e) {
326
+ var _a;
327
+ var audioSlider = this.container.querySelector("#audioSlider");
328
+ var recToggle = this.container.querySelector("#recordAlignment");
329
+ if (audioSlider === null || audioSlider === void 0 ? void 0 : audioSlider.paused)
330
+ return;
331
+ if (!(recToggle === null || recToggle === void 0 ? void 0 : recToggle.checked))
332
+ return;
333
+ if (e.code === "Space") {
334
+ if (this.currentHighlight) {
335
+ this.spaceCount = parseInt(this.currentHighlight.getAttribute("n"));
336
+ (_a = this.audioTimes) === null || _a === void 0 ? void 0 : _a.forEach((v, k) => {
337
+ if (k > audioSlider.currentTime) {
338
+ this.audioTimes.delete(k);
339
+ }
340
+ });
341
+ }
342
+ e.preventDefault();
343
+ if (!this.audioTimes || this.spaceCount === 0) {
344
+ this.audioTimes = new Map();
345
+ }
346
+ this.spaceCount += 1;
347
+ const measure = this.container.querySelector(`#vrvSVG .measure[n='${this.spaceCount}']`);
348
+ this.currentHighlight = measure;
349
+ this.drawFollowerRect();
350
+ this.audioTimes.set(audioSlider.currentTime, [measure.id]);
351
+ console.log(this.audioTimes);
352
+ }
353
+ }
354
+ downloadAlignment(e) {
355
+ const d = new Date();
356
+ const fileName = d.getUTCFullYear()
357
+ + ("0" + d.getDate()).slice(-2)
358
+ + ("0" + d.getMonth()).slice(-2)
359
+ + "_"
360
+ + ("0" + d.getHours()).slice(-2)
361
+ + ("0" + d.getMinutes()).slice(-2)
362
+ + ("0" + d.getSeconds()).slice(-2)
363
+ + "_"
364
+ + "alignment_" + this.containerId + ".json";
365
+ const audioTimesJson = JSON.stringify(Object.fromEntries(this.audioTimes));
366
+ console.log("Audio JSON", audioTimesJson);
367
+ this.download(fileName, audioTimesJson);
368
+ }
369
+ download(file, text) {
370
+ //creating an invisible element
371
+ var element = document.createElement('a');
372
+ element.setAttribute('href', 'data:text/plain;charset=utf-8, '
373
+ + encodeURIComponent(text));
374
+ element.setAttribute('download', file);
375
+ document.body.appendChild(element);
376
+ element.click();
377
+ document.body.removeChild(element);
378
+ }
379
+ /**
380
+ * Set countdown for given audioElement
381
+ * @param audioElement
382
+ */
383
+ countdown(audioElement, seconds = 5) {
384
+ audioElement.pause();
385
+ var that = this;
386
+ that.container.querySelector("#recordDiv label").textContent = seconds.toString();
387
+ seconds--;
388
+ Tone.start();
389
+ const synth = new Tone.Synth().toDestination();
390
+ function cdSound() {
391
+ synth.triggerAttackRelease("C5", "128n", undefined, 0.25);
392
+ }
393
+ cdSound();
394
+ var timer = setInterval(function () {
395
+ var _a;
396
+ if (seconds === 1) {
397
+ synth.triggerAttackRelease("C6", "4n", undefined, 0.25);
398
+ }
399
+ else if (seconds > 0) {
400
+ cdSound();
401
+ }
402
+ that.container.querySelector("#recordDiv label").textContent = seconds.toString();
403
+ seconds--;
404
+ if (seconds < 0) {
405
+ clearInterval(timer);
406
+ (_a = that.container.querySelector("#audioSlider")) === null || _a === void 0 ? void 0 : _a.removeEventListener("play", that.handleCountdown);
407
+ audioElement.play();
408
+ that.container.querySelector("#recordDiv label").textContent = "rec";
409
+ }
410
+ }, 1000);
411
+ }
412
+ /**
413
+ * Separate Listeners to set player options externally
414
+ */
415
+ setPlayListener() {
416
+ document.addEventListener("keydown", this.midiPlayHandler);
417
+ }
418
+ removePlayListener() {
419
+ document.removeEventListener("keydown", this.midiPlayHandler);
420
+ }
421
+ midiPlayFunction(e) {
422
+ if (!this.hasContainerFocus())
423
+ return;
424
+ if (e.code === "Space") {
425
+ e.preventDefault();
426
+ if (e.shiftKey) {
427
+ this.context.resume().then(() => this.playMidi());
428
+ }
429
+ else if (typeof this.midiPlayer != undefined) {
430
+ this.stopMidiInstruments();
431
+ }
432
+ }
433
+ }
434
+ /**
435
+ * Call score tube alignment service and map the cursor to notes.
436
+ * @param file Bufferarray of imported file (Blob)
437
+ */
438
+ align(file) {
439
+ // this listener is not in setListener function since we can get into an infinite loop easily
440
+ ["ended", "pause"].forEach(ev => this.container.querySelector("#audioSlider").addEventListener(ev, function () { that.resetListeners(); }));
441
+ var that = this;
442
+ var fd = new FormData();
443
+ fd.append('mei', new Blob([meiConverter.docToMei(this.currentMEI)]));
444
+ fd.append('audio', file);
445
+ var devUrl = 'http://localhost:8001/align';
446
+ axios_1.default.post(devUrl, fd, {
447
+ headers: {
448
+ 'Content-Type': 'multipart/form-data',
449
+ },
450
+ }).then((response) => {
451
+ var data = response.data;
452
+ that.audioTimes = new Map();
453
+ for (const [key, value] of Object.entries(data)) {
454
+ var val = value;
455
+ if (!that.audioTimes.has(val)) {
456
+ that.audioTimes.set(val, new Array());
457
+ }
458
+ //that.audioTimes.get(val).push(that.container.querySelector("#" + key))
459
+ that.audioTimes.get(val).push(key);
460
+ }
461
+ }).catch((error) => {
462
+ alert(["Aligning Service is not Available:", error, ".", "\nToggle the record button at the audio slider to activate manual measure alignment."].join(" "));
463
+ console.log(["An error occured while aligning the soundfile:", error].join(" "));
464
+ });
465
+ }
466
+ /**
467
+ * Time maps and actual values of audio elements or midi events might have some divergence (by floating number)
468
+ * Finding closest entry is sufficient, but has to be made for all entries, since the miditimes-iterator is not ordered.
469
+ * @param time
470
+ * @returns
471
+ */
472
+ getClosestEntry(time, source) {
473
+ var targetEntry;
474
+ var temp = Infinity;
475
+ var entries;
476
+ var map;
477
+ if (source === "midi") {
478
+ map = this.midiTimes;
479
+ }
480
+ else if (source === "audio") {
481
+ map = this.audioTimes;
482
+ // const audioMap = new Map<number, string[]>()
483
+ // map.forEach((v, k) => {
484
+ // audioMap.set(k, v.map(v => v.id)) // map ids, since highlight function only works with ids, not elements
485
+ // })
486
+ // map = audioMap
487
+ }
488
+ entries = map.entries();
489
+ targetEntry = map.get(time);
490
+ if (targetEntry)
491
+ return targetEntry;
492
+ for (const [key, value] of entries) {
493
+ var diff = Math.abs(time - key);
494
+ // check if diff is in range of next time to be sufficiently percieved as beeing on time
495
+ var diffCondition = diff < temp;
496
+ diffCondition && (diffCondition = source === "audio" ? diff <= 0.2 : true);
497
+ if (diffCondition) {
498
+ targetEntry = value;
499
+ temp = diff;
500
+ }
501
+ }
502
+ return targetEntry;
503
+ }
504
+ getDur(dur, dots, base) {
505
+ var baseDur = base / dur;
506
+ var add = baseDur;
507
+ if (dots > 0) {
508
+ for (var i = 0; i < dots; i++) {
509
+ add = add / 2;
510
+ baseDur += add;
511
+ }
512
+ }
513
+ return baseDur;
514
+ }
515
+ setAudioContext() {
516
+ var that = this;
517
+ return new Promise((resolve, reject) => {
518
+ window.onload = function () {
519
+ resolve();
520
+ };
521
+ });
522
+ }
523
+ setMidi(midi) {
524
+ this.midi = midi;
525
+ return this;
526
+ }
527
+ setTimemap(tm) {
528
+ this.timemap = tm;
529
+ this.midiTimes = new Map();
530
+ this.timemap.forEach(tm => {
531
+ if (tm.on)
532
+ this.midiTimes.set(tm.tstamp, tm.on);
533
+ });
534
+ return this;
535
+ }
536
+ /**
537
+ * Highlight playing Elements
538
+ * @param time Time at which Element is played (in ticks or ms)
539
+ * @param noteName noteName is important to find the correct element for the current time
540
+ */
541
+ highlight(time, noteName, source) {
542
+ noteName = noteName.replace("#", "s").replace("b", "f"); // make sure the notname is compatible with the mei accid names
543
+ noteName = noteName.toLowerCase();
544
+ const timeMS = source === "audio" ? time : (time / (this.pulse)) * (60000 / this.tempo);
545
+ var soundDur;
546
+ var highlightElements = this.getClosestEntry(timeMS, source);
547
+ if (!highlightElements)
548
+ return;
549
+ highlightElements.forEach((id) => {
550
+ if (this.container.querySelector(`#${id}`)) { //.classList.contains("measure")) {
551
+ this.currentHighlight = this.container.querySelector(`#${id}`); //highlightElements[0]
552
+ const meiElement = this.currentMEI.querySelector(`#${id}`);
553
+ const pname = meiElement.getAttribute("pname");
554
+ var accid = meiElement.getAttribute("accid") || meiElement.getAttribute("accid.ges");
555
+ accid = accid === "n" || !accid ? "" : accid;
556
+ const oct = meiElement.getAttribute("oct");
557
+ const meiNoteName = pname + accid + oct;
558
+ const enhUpNoteName = (mappings_1.enharmonicToCross.get(pname + accid) || "") + oct;
559
+ const enhDownNoteName = (mappings_1.enharmonicToB.get(pname + accid) || "") + oct;
560
+ const noteNameVersions = [meiNoteName, enhUpNoteName, enhDownNoteName];
561
+ if (noteNameVersions.includes(noteName)) {
562
+ soundDur = this.findDuration(id);
563
+ }
564
+ }
565
+ });
566
+ this.timeouts = new Array();
567
+ highlightElements.forEach((id) => {
568
+ const el = this.container.querySelector(`#${id}`);
569
+ this.addClass(el, currentlyPlayingFlag).then(() => {
570
+ if (source === "audio") { //we have no proper duration display yet, so no red coloring for notes, just the followerRect for the whole measure
571
+ el.classList.remove(currentlyPlayingFlag);
572
+ return;
573
+ }
574
+ var to = setTimeout(() => { el.classList.remove(currentlyPlayingFlag); }, this.findDuration(id));
575
+ this.timeouts.push(to);
576
+ });
577
+ });
578
+ return soundDur || 500;
579
+ }
580
+ /**
581
+ * Draw follower rectangle over all staves for last sounding element
582
+ */
583
+ drawFollowerRect() {
584
+ // var canvas = document.getElementById(this.containerId).querySelector("#canvasMusicPlayer") //document.getElementById("canvasMusicPlayer")
585
+ // var canvasBBox = canvas.getBoundingClientRect();
586
+ var followerRect;
587
+ if (document.getElementById(followerRectID) !== null) {
588
+ followerRect = document.getElementById(followerRectID);
589
+ }
590
+ else {
591
+ followerRect = document.createElementNS(constants_1.constants._SVGNS_, "rect");
592
+ this.canvasMP.appendChild(followerRect);
593
+ }
594
+ var margin = 5;
595
+ var ptCurrentHighlightElement = coordinates.getDOMMatrixCoordinates(this.currentHighlight, this.canvasMP);
596
+ var parentMeasureRect = this.currentHighlight.closest(".measure").getBoundingClientRect();
597
+ var ptParentMeasure = coordinates.getDOMMatrixCoordinates(parentMeasureRect, this.canvasMP);
598
+ var upperBound = (ptParentMeasure.top - margin);
599
+ var lowerBound = (ptParentMeasure.bottom + margin);
600
+ var leftBound = (ptCurrentHighlightElement.left - margin);
601
+ var rightBound = (ptCurrentHighlightElement.right + margin);
602
+ followerRect.setAttribute("id", followerRectID);
603
+ followerRect.setAttribute("y", upperBound.toString());
604
+ followerRect.setAttribute("x", leftBound.toString());
605
+ followerRect.setAttribute("width", (rightBound - leftBound).toString());
606
+ followerRect.setAttribute("height", (lowerBound - upperBound).toString());
607
+ }
608
+ hasContainerFocus() {
609
+ if (!document.getElementById(this.containerId))
610
+ return false;
611
+ return document.getElementById(this.containerId).classList.contains("activeContainer");
612
+ }
613
+ ///SYNTH////
614
+ generateTone(newNote) {
615
+ if (newNote.rest) {
616
+ return;
617
+ }
618
+ let note = newNote.pname;
619
+ let dur = newNote.dur + "n";
620
+ var accid = typeof newNote.accid === "string" ? newNote.accid.replace("f", "b").replace("s", "#").replace("n", "") : "";
621
+ if (typeof newNote.keysig !== "undefined" && newNote.keysig !== "0") {
622
+ let signMap;
623
+ if (newNote.keysig.charAt(1) === "s") {
624
+ signMap = mappings_1.noteToCross;
625
+ }
626
+ else if (newNote.keysig.charAt(1) === "f") {
627
+ signMap = mappings_1.noteToB;
628
+ }
629
+ let signCount = parseInt(newNote.keysig.charAt(0));
630
+ let submap = new Map();
631
+ let i = 0;
632
+ for (const [key, value] of signMap.entries()) {
633
+ if (i < signCount) {
634
+ submap.set(key, value);
635
+ }
636
+ i += 1;
637
+ }
638
+ if (submap.has(note)) {
639
+ note = submap.get(note);
640
+ note = note.charAt(0).toUpperCase() + note.charAt(1).toUpperCase() + newNote.oct;
641
+ }
642
+ else {
643
+ note = note.toUpperCase() + accid + newNote.oct;
644
+ }
645
+ }
646
+ else {
647
+ note = note.toUpperCase() + accid + newNote.oct;
648
+ }
649
+ if (!note.includes("undefined") && !dur.includes("undefined")) {
650
+ dur = "16n";
651
+ synth.triggerAttackRelease(note, dur, undefined, 0.3);
652
+ Tone.start();
653
+ }
654
+ }
655
+ // UTILS
656
+ setMEI(mei) {
657
+ this.currentMEI = mei;
658
+ return this;
659
+ }
660
+ setMidiTimes(midiTimes) {
661
+ this.midiTimes = midiTimes;
662
+ return this;
663
+ }
664
+ setScoreGraph(scoreGraph) {
665
+ this.scoreGraph = scoreGraph;
666
+ return this;
667
+ }
668
+ stopTimeouts() {
669
+ if (typeof this.timeouts !== "undefined") {
670
+ this.timeouts.forEach(to => {
671
+ clearTimeout(to);
672
+ });
673
+ }
674
+ }
675
+ setContainerId(containerId) {
676
+ this.containerId = containerId;
677
+ this.interactionOverlay = cq.getInteractOverlay(containerId);
678
+ this.vrvSVG = cq.getVrvSVG(containerId);
679
+ this.container = document.getElementById(containerId);
680
+ return this;
681
+ }
682
+ resetMidiInstruments() {
683
+ this.instruments = undefined;
684
+ }
685
+ getRestartTime() {
686
+ return this.restartTime;
687
+ }
688
+ setRestartTimeBySeconds(time) {
689
+ return this.restartTime = time;
690
+ }
691
+ setRestartTimeByElement(el) {
692
+ throw Error("Not yet implemented");
693
+ }
694
+ /**
695
+ * Set audioTimes map for alignment.
696
+ * IDs are set new afterwards to account for different MEI.
697
+ * Assumes that Score and Map have the same number of measures.
698
+ * @param audioTimes
699
+ * @returns
700
+ */
701
+ setAudioTimes(audioTimes) {
702
+ this.audioTimes = audioTimes;
703
+ this.container.querySelectorAll(`#vrvSVG .measure`).forEach((m, i) => {
704
+ const key = Array.from(this.audioTimes.keys())[i];
705
+ this.audioTimes.set(key, [m.id]);
706
+ });
707
+ return this;
708
+ }
709
+ getIsPlaying() {
710
+ var _a;
711
+ return (_a = this.midiPlayer) === null || _a === void 0 ? void 0 : _a.isPlaying();
712
+ }
713
+ update() {
714
+ this.resetMidiInstruments();
715
+ this.resetListeners();
716
+ this.initMidiPlayer();
717
+ return this;
718
+ }
719
+ //// Experimantal
720
+ getMidiInput() {
721
+ var that = this;
722
+ var navigator = require('web-midi-api');
723
+ // consider using var navigator = require('jzz');
724
+ var midi;
725
+ var inputs;
726
+ var outputs;
727
+ function onMIDIFailure(msg) {
728
+ console.log('Failed to get MIDI access - ' + msg);
729
+ //process?.exit(1);
730
+ }
731
+ function onMIDISuccess(midiAccess) {
732
+ midi = midiAccess;
733
+ inputs = midi.inputs;
734
+ outputs = midi.outputs;
735
+ console.log("general midi info:", midi);
736
+ setTimeout(testOutputs, 100);
737
+ }
738
+ function testOutputs() {
739
+ console.log('Testing MIDI-Out ports...');
740
+ outputs.forEach(function (port) {
741
+ console.log('id:', port.id, 'manufacturer:', port.manufacturer, 'name:', port.name, 'version:', port.version);
742
+ port.open();
743
+ port.send([0x90, 60, 0x7f]);
744
+ });
745
+ setTimeout(stopOutputs, 1000);
746
+ }
747
+ function stopOutputs() {
748
+ outputs.forEach(function (port) {
749
+ port.send([0x80, 60, 0]);
750
+ });
751
+ testInputs();
752
+ }
753
+ function onMidiIn(ev) {
754
+ document.dispatchEvent(new CustomEvent("midiin", { detail: ev.data })); // goes to KeyModeHandler since there already most of the logic is implemented
755
+ }
756
+ function testInputs() {
757
+ console.log('Testing MIDI-In ports...');
758
+ inputs.forEach(function (port) {
759
+ console.log('id:', port.id, 'manufacturer:', port.manufacturer, 'name:', port.name, 'version:', port.version);
760
+ port.onmidimessage = onMidiIn;
761
+ });
762
+ //setTimeout(stopInputs, 5000);
763
+ }
764
+ function fillDeviceList(e) {
765
+ var _a;
766
+ var deviceList = (_a = cq.getContainer(that.containerId)) === null || _a === void 0 ? void 0 : _a.querySelector("#midiDeviceSelect");
767
+ if (deviceList === null)
768
+ return;
769
+ var value = e.port.name;
770
+ if (e.port.type === "input" && !e.port.name.includes("EDITOR")) {
771
+ var optionEntry = deviceList.querySelector("option[value='" + value + "']");
772
+ if (optionEntry !== null) {
773
+ optionEntry.remove();
774
+ console.log("Removed MIDI Device", e.port);
775
+ }
776
+ else {
777
+ var option = document.createElement("option");
778
+ option.setAttribute("value", value);
779
+ option.textContent = e.port.manufacturer + " " + e.port.name;
780
+ deviceList.append(option);
781
+ console.log("Added MIDI Device", e.port);
782
+ }
783
+ deviceList.removeEventListener("change", chooseInput);
784
+ deviceList.addEventListener("change", chooseInput);
785
+ }
786
+ }
787
+ function chooseInput(e) {
788
+ var target = e.target;
789
+ console.log(e, target.value);
790
+ inputs.forEach(port => {
791
+ if (target.value === port.name) {
792
+ port.onmidimessage = onMidiIn;
793
+ console.log("Chosen MIDI Device", port);
794
+ }
795
+ else {
796
+ port.close();
797
+ }
798
+ });
799
+ }
800
+ navigator.requestMIDIAccess().then(access => {
801
+ console.log(access);
802
+ access.onstatechange = (e) => {
803
+ fillDeviceList(e);
804
+ };
805
+ onMIDISuccess(access);
806
+ onMIDIFailure(access);
807
+ });
808
+ }
809
+ }
810
+ exports.default = MusicProcessor;