abcjs 6.0.0-beta.9 → 6.0.0
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.
- package/.github/workflows/tests.yml +29 -0
- package/CODE_OF_CONDUCT.md +76 -0
- package/CONTRIBUTING.md +1 -0
- package/LICENSE.md +1 -1
- package/README.md +88 -7
- package/RELEASE.md +939 -1
- package/abcjs-audio.css +14 -5
- package/dist/.gitignore +1 -2
- package/dist/abcjs-basic-min.js +3 -0
- package/dist/abcjs-basic-min.js.LICENSE +23 -0
- package/dist/abcjs-basic.js +28231 -0
- package/dist/abcjs-basic.js.map +1 -0
- package/dist/abcjs-plugin-min.js +3 -0
- package/dist/abcjs-plugin-min.js.LICENSE +23 -0
- package/dist/report-basic.html +37 -0
- package/dist/report-before-glyph-compress.html +37 -0
- package/dist/report-brown-ts-target-es5.html +37 -0
- package/dist/report-dev-orig-no-babel.html +37 -0
- package/dist/report-synth.html +37 -0
- package/docker-build.sh +1 -0
- package/glyphs.json +1 -0
- package/index.js +27 -2
- package/{static-wrappers/license.js → license.js} +1 -1
- package/package.json +26 -29
- package/{src/plugin/abc_plugin.js → plugin.js} +31 -19
- package/src/api/abc_animation.js +1 -17
- package/src/api/abc_tablatures.js +144 -0
- package/src/api/abc_timing_callbacks.js +216 -117
- package/src/api/abc_tunebook.js +18 -67
- package/src/api/abc_tunebook_svg.js +38 -46
- package/src/data/abc_tune.js +232 -972
- package/src/data/deline-tune.js +199 -0
- package/src/edit/abc_editarea.js +112 -0
- package/src/edit/abc_editor.js +95 -221
- package/src/midi/abc_midi_create.js +48 -50
- package/src/parse/abc_common.js +0 -14
- package/src/parse/abc_parse.js +167 -1321
- package/src/parse/abc_parse_book.js +62 -0
- package/src/parse/abc_parse_directive.js +164 -41
- package/src/parse/abc_parse_header.js +116 -145
- package/src/parse/abc_parse_key_voice.js +26 -20
- package/src/parse/abc_parse_music.js +1337 -0
- package/src/parse/abc_tokenizer.js +21 -15
- package/src/parse/abc_transpose.js +3 -15
- package/src/parse/tune-builder.js +896 -0
- package/src/parse/wrap_lines.js +205 -453
- package/src/synth/abc_midi_flattener.js +1292 -0
- package/src/{midi → synth}/abc_midi_renderer.js +44 -17
- package/src/synth/abc_midi_sequencer.js +648 -0
- package/src/synth/active-audio-context.js +3 -14
- package/src/synth/cents-to-factor.js +10 -0
- package/src/synth/create-note-map.js +21 -32
- package/src/synth/create-synth-control.js +20 -103
- package/src/synth/create-synth.js +185 -77
- package/src/synth/download-buffer.js +7 -21
- package/src/synth/get-midi-file.js +13 -20
- package/src/synth/images/{loading.svg → loading.svg.js} +4 -0
- package/src/synth/images/loop.svg.js +65 -0
- package/src/synth/images/pause.svg.js +10 -0
- package/src/synth/images/play.svg.js +9 -0
- package/src/synth/images/{reset.svg → reset.svg.js} +5 -1
- package/src/synth/instrument-index-to-name.js +1 -16
- package/src/synth/load-note.js +37 -76
- package/src/synth/pitch-to-note-name.js +0 -15
- package/src/synth/pitches-to-perc.js +64 -0
- package/src/synth/place-note.js +78 -68
- package/src/synth/play-event.js +17 -18
- package/src/synth/register-audio-context.js +11 -23
- package/src/synth/sounds-cache.js +0 -15
- package/src/synth/supports-audio.js +9 -23
- package/src/synth/synth-controller.js +80 -49
- package/src/synth/synth-sequence.js +20 -34
- package/src/tablatures/instruments/guitar/guitar-fonts.js +19 -0
- package/src/tablatures/instruments/guitar/guitar-patterns.js +23 -0
- package/src/tablatures/instruments/guitar/tab-guitar.js +50 -0
- package/src/tablatures/instruments/string-patterns.js +277 -0
- package/src/tablatures/instruments/string-tablature.js +56 -0
- package/src/tablatures/instruments/tab-note.js +282 -0
- package/src/tablatures/instruments/tab-notes.js +41 -0
- package/src/tablatures/instruments/violin/tab-violin.js +47 -0
- package/src/tablatures/instruments/violin/violin-fonts.js +19 -0
- package/src/tablatures/instruments/violin/violin-patterns.js +23 -0
- package/src/tablatures/tab-absolute-elements.js +310 -0
- package/src/tablatures/tab-common.js +29 -0
- package/src/tablatures/tab-renderer.js +243 -0
- package/src/tablatures/transposer.js +110 -0
- package/src/test/abc_midi_lint.js +5 -22
- package/src/test/abc_midi_sequencer_lint.js +11 -14
- package/src/test/abc_parser_lint.js +136 -32
- package/src/test/abc_vertical_lint.js +94 -32
- package/src/test/rendering-lint.js +38 -5
- package/src/write/abc_absolute_element.js +112 -120
- package/src/write/abc_abstract_engraver.js +102 -253
- package/src/write/abc_beam_element.js +30 -290
- package/src/write/abc_brace_element.js +12 -121
- package/src/write/abc_create_clef.js +21 -32
- package/src/write/abc_create_key_signature.js +8 -26
- package/src/write/abc_create_note_head.js +107 -0
- package/src/write/abc_create_time_signature.js +2 -21
- package/src/write/abc_crescendo_element.js +3 -50
- package/src/write/abc_decoration.js +7 -30
- package/src/write/abc_dynamic_decoration.js +3 -37
- package/src/write/abc_ending_element.js +1 -57
- package/src/write/abc_engraver_controller.js +111 -234
- package/src/write/abc_glyphs.js +8 -18
- package/src/write/abc_relative_element.js +57 -97
- package/src/write/abc_renderer.js +10 -832
- package/src/write/abc_spacing.js +0 -15
- package/src/write/abc_staff_group_element.js +14 -349
- package/src/write/abc_tempo_element.js +9 -117
- package/src/write/abc_tie_element.js +5 -68
- package/src/write/abc_triplet_element.js +6 -124
- package/src/write/abc_voice_element.js +7 -222
- package/src/write/add-chord.js +103 -0
- package/src/write/add-text-if.js +33 -0
- package/src/write/bottom-text.js +79 -0
- package/src/write/calcHeight.js +17 -0
- package/src/write/classes.js +100 -0
- package/src/write/draw/absolute.js +68 -0
- package/src/write/draw/beam.js +56 -0
- package/src/write/draw/brace.js +106 -0
- package/src/write/draw/crescendo.js +38 -0
- package/src/write/draw/debug-box.js +8 -0
- package/src/write/draw/draw.js +56 -0
- package/src/write/draw/dynamics.js +20 -0
- package/src/write/draw/ending.js +46 -0
- package/src/write/draw/group-elements.js +66 -0
- package/src/write/draw/horizontal-line.js +25 -0
- package/src/write/draw/non-music.js +50 -0
- package/src/write/draw/print-line.js +24 -0
- package/src/write/draw/print-path.js +7 -0
- package/src/write/draw/print-stem.js +30 -0
- package/src/write/draw/print-symbol.js +59 -0
- package/src/write/draw/print-vertical-line.js +18 -0
- package/src/write/draw/relative.js +77 -0
- package/src/write/draw/round-number.js +5 -0
- package/src/write/draw/selectables.js +59 -0
- package/src/write/draw/separator.js +16 -0
- package/src/write/draw/set-paper-size.js +45 -0
- package/src/write/{sprintf.js → draw/sprintf.js} +0 -0
- package/src/write/draw/staff-group.js +226 -0
- package/src/write/draw/staff-line.js +9 -0
- package/src/write/draw/staff.js +33 -0
- package/src/write/draw/tab-line.js +40 -0
- package/src/write/draw/tempo.js +45 -0
- package/src/write/draw/text.js +71 -0
- package/src/write/draw/tie.js +97 -0
- package/src/write/draw/triplet.js +46 -0
- package/src/write/draw/voice.js +102 -0
- package/src/write/format-jazz-chord.js +15 -0
- package/src/write/free-text.js +41 -0
- package/src/write/get-font-and-attr.js +37 -0
- package/src/write/get-text-size.js +56 -0
- package/src/write/highlight.js +11 -0
- package/src/write/layout/VoiceElements.js +121 -0
- package/src/write/layout/beam.js +213 -0
- package/src/write/layout/get-left-edge-of-staff.js +56 -0
- package/src/write/layout/getBarYAt.js +6 -0
- package/src/write/layout/layout.js +94 -0
- package/src/write/layout/setUpperAndLowerElements.js +232 -0
- package/src/write/layout/staffGroup.js +146 -0
- package/src/write/layout/triplet.js +75 -0
- package/src/write/layout/voice.js +137 -0
- package/src/write/selection.js +188 -70
- package/src/write/separator.js +10 -0
- package/src/write/set-class.js +21 -0
- package/src/write/subtitle.js +12 -0
- package/src/write/svg.js +95 -43
- package/src/write/top-text.js +54 -0
- package/src/write/unhighlight.js +11 -0
- package/temp.txt +17 -0
- package/test.js +27 -64
- package/types/index.d.ts +1095 -0
- package/version.js +1 -1
- package/.babelrc +0 -5
- package/.eslintrc +0 -3
- package/.gitmodules +0 -3
- package/Dockerfile +0 -8
- package/abcjs-midi.css +0 -166
- package/build-utils/loadPresets.js +0 -14
- package/build-utils/presets/webpack.analyze.js +0 -6
- package/build-utils/presets/webpack.optimize.js +0 -30
- package/build-utils/webpack.development.js +0 -14
- package/build-utils/webpack.production.js +0 -35
- package/deploy-docs.sh +0 -25
- package/docker-compose.yml +0 -13
- package/docs/README.md +0 -33
- package/fix-versions.sh +0 -23
- package/midi.js +0 -62
- package/src/api/abc_tunebook_midi.js +0 -116
- package/src/midi/abc_midi_controls.js +0 -701
- package/src/midi/abc_midi_flattener.js +0 -1119
- package/src/midi/abc_midi_js_preparer.js +0 -243
- package/src/midi/abc_midi_sequencer.js +0 -401
- package/src/midi/abc_midi_ui_generator.js +0 -86
- package/src/plugin/abc_plugin_midi.js +0 -220
- package/src/synth/images/loop.svg +0 -61
- package/src/synth/images/pause.svg +0 -6
- package/src/synth/images/play.svg +0 -5
- package/src/transform/abc2abc_write.js +0 -395
- package/static-wrappers/basic.js +0 -2
- package/static-wrappers/midi.js +0 -2
- package/static-wrappers/plugin-midi.js +0 -6
- package/static-wrappers/plugin.js +0 -6
- package/webpack.config.js +0 -29
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
// Copyright (C) 2015-2020 Paul Rosen (paul at paulrosen dot net)
|
|
2
|
-
//
|
|
3
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
4
|
-
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation
|
|
5
|
-
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
|
|
6
|
-
// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
7
|
-
//
|
|
8
|
-
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
9
|
-
//
|
|
10
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
|
11
|
-
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
12
|
-
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
13
|
-
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
14
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
15
|
-
|
|
16
1
|
var TimingCallbacks = function(target, params) {
|
|
17
2
|
var self = this;
|
|
18
3
|
if (!params) params = {};
|
|
@@ -30,124 +15,178 @@ var TimingCallbacks = function(target, params) {
|
|
|
30
15
|
self.joggerTimer = null;
|
|
31
16
|
|
|
32
17
|
self.replaceTarget = function(newTarget) {
|
|
33
|
-
newTarget.setTiming(self.qpm, self.extraMeasuresAtBeginning);
|
|
18
|
+
self.noteTimings = newTarget.setTiming(self.qpm, self.extraMeasuresAtBeginning);
|
|
34
19
|
if (newTarget.noteTimings.length === 0)
|
|
35
20
|
newTarget.setTiming(0,0);
|
|
36
21
|
if (self.lineEndCallback) {
|
|
37
22
|
self.lineEndTimings = getLineEndTimings(newTarget.noteTimings, self.lineEndAnticipation);
|
|
38
23
|
}
|
|
39
|
-
self.
|
|
24
|
+
self.startTime = null;
|
|
25
|
+
self.currentBeat = 0;
|
|
26
|
+
self.currentEvent = 0;
|
|
27
|
+
self.currentLine = 0;
|
|
28
|
+
self.currentTime = 0;
|
|
29
|
+
self.isPaused = false;
|
|
30
|
+
self.isRunning = false;
|
|
31
|
+
self.pausedPercent = null;
|
|
32
|
+
self.justUnpaused = false;
|
|
33
|
+
self.newSeekPercent = 0;
|
|
34
|
+
self.lastTimestamp = 0;
|
|
35
|
+
|
|
36
|
+
if (self.noteTimings.length === 0)
|
|
37
|
+
return;
|
|
38
|
+
// noteTimings contains an array of events sorted by time. Events that happen at the same time are in the same element of the array.
|
|
39
|
+
self.millisecondsPerBeat = 1000 / (self.qpm / 60) / self.beatSubdivisions;
|
|
40
|
+
self.lastMoment = self.noteTimings[self.noteTimings.length-1].milliseconds;
|
|
41
|
+
self.totalBeats = Math.round(self.lastMoment / self.millisecondsPerBeat);
|
|
40
42
|
};
|
|
41
43
|
|
|
42
44
|
self.replaceTarget(target);
|
|
43
|
-
if (self.noteTimings.length === 0)
|
|
44
|
-
return;
|
|
45
|
-
|
|
46
|
-
// noteTimings contains an array of events sorted by time. Events that happen at the same time are in the same element of the array.
|
|
47
|
-
self.noteTimings = target.noteTimings;
|
|
48
|
-
self.millisecondsPerBeat = 1000 / (self.qpm / 60) / self.beatSubdivisions;
|
|
49
|
-
self.lastMoment = self.noteTimings[self.noteTimings.length-1].milliseconds;
|
|
50
|
-
self.totalBeats = Math.round(self.lastMoment / self.millisecondsPerBeat);
|
|
51
|
-
|
|
52
|
-
self.startTime = null;
|
|
53
|
-
self.currentBeat = 0;
|
|
54
|
-
self.currentEvent = 0;
|
|
55
|
-
self.isPaused = false;
|
|
56
|
-
self.isRunning = false;
|
|
57
|
-
self.pausedTime = null;
|
|
58
|
-
self.justUnpaused = false;
|
|
59
|
-
|
|
60
|
-
self.newSeekPercent = 0;
|
|
61
|
-
self.justSeeked = false;
|
|
62
|
-
|
|
63
|
-
function setCurrentLocation(timestamp) {
|
|
64
|
-
// First find the relative amount to move: that is, the difference between the current percentage and the passed in percent.
|
|
65
|
-
var currentPercent = (timestamp - self.startTime) / self.lastMoment;
|
|
66
|
-
var percentDifference = currentPercent - self.newSeekPercent;
|
|
67
|
-
var timeDifference = self.lastMoment * percentDifference;
|
|
68
|
-
self.startTime = self.startTime + timeDifference;
|
|
69
|
-
|
|
70
|
-
var currentTime = timestamp - self.startTime;
|
|
71
|
-
currentTime += 16; // Add a little slop because this function isn't called exactly.
|
|
72
45
|
|
|
73
|
-
var oldBeat = self.currentBeat;
|
|
74
|
-
self.currentBeat = Math.floor(currentTime / self.millisecondsPerBeat);
|
|
75
|
-
if (self.beatCallback && oldBeat !== self.currentBeat) // If the movement caused the beat to change, then immediately report it to the client.
|
|
76
|
-
self.beatCallback(self.currentBeat / self.beatSubdivisions, self.totalBeats / self.beatSubdivisions, self.lastMoment);
|
|
77
|
-
|
|
78
|
-
var lineStart = 0;
|
|
79
|
-
self.currentEvent = 0;
|
|
80
|
-
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < currentTime) {
|
|
81
|
-
self.currentEvent++;
|
|
82
|
-
if (self.lineEndCallback && self.lineEndTimings['e'+self.currentEvent])
|
|
83
|
-
lineStart = self.currentEvent;
|
|
84
|
-
}
|
|
85
|
-
if (self.eventCallback && self.currentEvent > 0 && self.noteTimings[self.currentEvent - 1].type === 'event')
|
|
86
|
-
self.eventCallback(self.noteTimings[self.currentEvent - 1]);
|
|
87
|
-
if (self.lineEndCallback)
|
|
88
|
-
self.lineEndCallback(self.lineEndTimings['e'+lineStart])
|
|
89
|
-
|
|
90
|
-
// console.log("currentPercent="+currentPercent+
|
|
91
|
-
// " newSeekPercent="+self.newSeekPercent+
|
|
92
|
-
// " percentDifference="+percentDifference+
|
|
93
|
-
// " timeDifference=",timeDifference+
|
|
94
|
-
// " currentBeat="+self.currentBeat+
|
|
95
|
-
// " currentEvent="+self.currentEvent);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
self.lastTimestamp = 0;
|
|
99
46
|
self.doTiming = function (timestamp) {
|
|
100
47
|
// This is called 60 times a second, that is, every 16 msecs.
|
|
101
48
|
//console.log("doTiming", timestamp, timestamp-self.lastTimestamp);
|
|
102
49
|
if (self.lastTimestamp === timestamp)
|
|
103
|
-
return; // If there are multiple seeks or other calls, then we can easily get multiple callbacks for the same
|
|
50
|
+
return; // If there are multiple seeks or other calls, then we can easily get multiple callbacks for the same instant.
|
|
104
51
|
self.lastTimestamp = timestamp;
|
|
105
52
|
if (!self.startTime) {
|
|
106
53
|
self.startTime = timestamp;
|
|
107
|
-
} else if (self.justUnpaused) {
|
|
108
|
-
// Add the amount we paused to the start time to get the right place.
|
|
109
|
-
var timePaused = (timestamp - self.pausedTime);
|
|
110
|
-
self.startTime += timePaused;
|
|
111
54
|
}
|
|
112
|
-
self.justUnpaused = false;
|
|
113
55
|
|
|
114
|
-
if (self.
|
|
115
|
-
|
|
116
|
-
self.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
} else if (self.isRunning) {
|
|
121
|
-
var currentTime = timestamp - self.startTime;
|
|
122
|
-
currentTime += 16; // Add a little slop because this function isn't called exactly.
|
|
123
|
-
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < currentTime) {
|
|
124
|
-
if (self.eventCallback && self.noteTimings[self.currentEvent].type === 'event')
|
|
56
|
+
if (!self.isPaused && self.isRunning) {
|
|
57
|
+
self.currentTime = timestamp - self.startTime;
|
|
58
|
+
self.currentTime += 16; // Add a little slop because this function isn't called exactly.
|
|
59
|
+
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < self.currentTime) {
|
|
60
|
+
if (self.eventCallback && self.noteTimings[self.currentEvent].type === 'event') {
|
|
61
|
+
var thisStartTime = self.startTime; // the event callback can call seek and change the position from beneath us.
|
|
125
62
|
self.eventCallback(self.noteTimings[self.currentEvent]);
|
|
63
|
+
if (thisStartTime !== self.startTime) {
|
|
64
|
+
self.currentTime = timestamp - self.startTime;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
126
67
|
self.currentEvent++;
|
|
127
|
-
if (self.lineEndCallback && self.lineEndTimings['e'+self.currentEvent])
|
|
128
|
-
self.lineEndCallback(self.lineEndTimings['e'+self.currentEvent]);
|
|
129
68
|
}
|
|
130
|
-
if (currentTime < self.
|
|
69
|
+
if (self.lineEndCallback && self.lineEndTimings.length > self.currentLine && self.lineEndTimings[self.currentLine].milliseconds < self.currentTime && self.currentEvent < self.noteTimings.length) {
|
|
70
|
+
var leftEvent = self.noteTimings[self.currentEvent].milliseconds === self.currentTime ? self.noteTimings[self.currentEvent] : self.noteTimings[self.currentEvent-1]
|
|
71
|
+
self.lineEndCallback(self.lineEndTimings[self.currentLine], leftEvent, { line: self.currentLine, endTimings: self.lineEndTimings, currentTime: self.currentTime });
|
|
72
|
+
self.currentLine++;
|
|
73
|
+
}
|
|
74
|
+
if (self.currentTime < self.lastMoment) {
|
|
131
75
|
requestAnimationFrame(self.doTiming);
|
|
132
|
-
if (self.currentBeat * self.millisecondsPerBeat < currentTime) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
76
|
+
if (self.currentBeat * self.millisecondsPerBeat < self.currentTime) {
|
|
77
|
+
var ret = self.doBeatCallback(timestamp);
|
|
78
|
+
if (ret !== null)
|
|
79
|
+
self.currentTime = ret;
|
|
136
80
|
}
|
|
137
81
|
} else if (self.currentBeat <= self.totalBeats) {
|
|
138
82
|
// Because of timing issues (for instance, if the browser tab isn't active), the beat callbacks might not have happened when they are supposed to. To keep the client programs from having to deal with that, this will keep calling the loop until all of them have been sent.
|
|
139
83
|
if (self.beatCallback) {
|
|
140
|
-
|
|
141
|
-
|
|
84
|
+
var ret2 = self.doBeatCallback(timestamp);
|
|
85
|
+
if (ret2 !== null)
|
|
86
|
+
self.currentTime = ret2;
|
|
142
87
|
requestAnimationFrame(self.doTiming);
|
|
143
88
|
}
|
|
144
89
|
}
|
|
145
90
|
|
|
146
|
-
if (currentTime >= self.lastMoment
|
|
147
|
-
self.eventCallback
|
|
91
|
+
if (self.currentTime >= self.lastMoment) {
|
|
92
|
+
if (self.eventCallback) {
|
|
93
|
+
// At the end, the event callback can return "continue" to keep from stopping.
|
|
94
|
+
// The event callback can either be a promise or not.
|
|
95
|
+
var promise = self.eventCallback(null);
|
|
96
|
+
self.shouldStop(promise).then(function(shouldStop) {
|
|
97
|
+
if (shouldStop)
|
|
98
|
+
self.stop();
|
|
99
|
+
})
|
|
100
|
+
} else
|
|
101
|
+
self.stop();
|
|
102
|
+
}
|
|
148
103
|
}
|
|
149
104
|
};
|
|
150
105
|
|
|
106
|
+
self.shouldStop = function(promise) {
|
|
107
|
+
// The return of the last event callback can be "continue" or a promise that returns "continue".
|
|
108
|
+
// If it is then don't call stop. Any other value calls stop.
|
|
109
|
+
return new Promise(function (resolve) {
|
|
110
|
+
if (!promise)
|
|
111
|
+
return resolve(true);
|
|
112
|
+
if (promise === "continue")
|
|
113
|
+
return resolve(false);
|
|
114
|
+
if (promise.then) {
|
|
115
|
+
promise.then(function (result) {
|
|
116
|
+
resolve(result !== "continue");
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
self.doBeatCallback = function(timestamp) {
|
|
123
|
+
if (self.beatCallback) {
|
|
124
|
+
var next = self.currentEvent;
|
|
125
|
+
while (next < self.noteTimings.length && self.noteTimings[next].left === null)
|
|
126
|
+
next++;
|
|
127
|
+
var endMs;
|
|
128
|
+
var ev;
|
|
129
|
+
if (next < self.noteTimings.length) {
|
|
130
|
+
endMs = self.noteTimings[next].milliseconds;
|
|
131
|
+
next = self.currentEvent - 1;
|
|
132
|
+
while (next >= 0 && self.noteTimings[next].left === null)
|
|
133
|
+
next--;
|
|
134
|
+
|
|
135
|
+
ev = self.noteTimings[next];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
var position = {};
|
|
139
|
+
var debugInfo = {};
|
|
140
|
+
if (ev) {
|
|
141
|
+
position.top = ev.top;
|
|
142
|
+
position.height = ev.height;
|
|
143
|
+
|
|
144
|
+
// timestamp = the time passed in from the animation timer
|
|
145
|
+
// self.startTime = the time that the tune was started (if there was seeking or pausing, it is adjusted to keep the math the same)
|
|
146
|
+
// ev = the event that is either happening now or has most recently passed.
|
|
147
|
+
// ev.milliseconds = the time that the current event starts (relative to self.startTime)
|
|
148
|
+
// endMs = the time that the next event starts
|
|
149
|
+
// ev.endX = the x coordinate that the next event happens (or the end of the line or repeat measure)
|
|
150
|
+
// ev.left = the x coordinate of the current event
|
|
151
|
+
//
|
|
152
|
+
// The output is the X coordinate of the current cursor location. It is calculated with the ratio of the length of the event and the width of it.
|
|
153
|
+
var offMs = Math.max(0, timestamp-self.startTime-ev.milliseconds); // Offset in time from the last beat
|
|
154
|
+
var gapMs = endMs - ev.milliseconds; // Length of this event in time
|
|
155
|
+
var gapPx = ev.endX - ev.left; // The length in pixels
|
|
156
|
+
var offPx = offMs * gapPx / gapMs;
|
|
157
|
+
position.left = ev.left + offPx;
|
|
158
|
+
debugInfo = {
|
|
159
|
+
timestamp: timestamp,
|
|
160
|
+
startTime: self.startTime,
|
|
161
|
+
ev: ev,
|
|
162
|
+
endMs: endMs,
|
|
163
|
+
offMs: offMs,
|
|
164
|
+
offPs: offPx,
|
|
165
|
+
gapMs: gapMs,
|
|
166
|
+
gapPx: gapPx
|
|
167
|
+
};
|
|
168
|
+
} else {
|
|
169
|
+
debugInfo = {
|
|
170
|
+
timestamp: timestamp,
|
|
171
|
+
startTime: self.startTime,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
var thisStartTime = self.startTime; // the beat callback can call seek and change the position from beneath us.
|
|
176
|
+
self.beatCallback(
|
|
177
|
+
self.currentBeat / self.beatSubdivisions,
|
|
178
|
+
self.totalBeats / self.beatSubdivisions,
|
|
179
|
+
self.lastMoment,
|
|
180
|
+
position,
|
|
181
|
+
debugInfo);
|
|
182
|
+
if (thisStartTime !== self.startTime) {
|
|
183
|
+
return timestamp - self.startTime;
|
|
184
|
+
} else
|
|
185
|
+
self.currentBeat++;
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
};
|
|
189
|
+
|
|
151
190
|
// In general music doesn't need a timer at 60 fps because notes don't happen that fast.
|
|
152
191
|
// For instance, at 120 beats per minute, a sixteenth note takes 125ms. So just as a
|
|
153
192
|
// compromise value between performance and jank this is set about half that.
|
|
@@ -165,46 +204,106 @@ var TimingCallbacks = function(target, params) {
|
|
|
165
204
|
}
|
|
166
205
|
};
|
|
167
206
|
|
|
168
|
-
self.start = function(offsetPercent) {
|
|
207
|
+
self.start = function(offsetPercent, units) {
|
|
169
208
|
self.isRunning = true;
|
|
170
209
|
if (self.isPaused) {
|
|
171
210
|
self.isPaused = false;
|
|
172
|
-
|
|
211
|
+
if (offsetPercent === undefined)
|
|
212
|
+
self.justUnpaused = true;
|
|
173
213
|
}
|
|
174
214
|
if (offsetPercent) {
|
|
175
|
-
self.setProgress(offsetPercent);
|
|
176
|
-
} else {
|
|
177
|
-
|
|
178
|
-
|
|
215
|
+
self.setProgress(offsetPercent, units);
|
|
216
|
+
} else if (offsetPercent === 0) {
|
|
217
|
+
self.reset();
|
|
218
|
+
} else if (self.pausedPercent !== null) {
|
|
219
|
+
var now = performance.now();
|
|
220
|
+
self.currentTime = self.lastMoment * self.pausedPercent;
|
|
221
|
+
self.startTime = now - self.currentTime;
|
|
222
|
+
self.pausedPercent = null;
|
|
223
|
+
self.reportNext = true;
|
|
179
224
|
}
|
|
225
|
+
requestAnimationFrame(self.doTiming);
|
|
226
|
+
self.joggerTimer = setTimeout(self.animationJogger, JOGGING_INTERVAL);
|
|
180
227
|
};
|
|
181
228
|
self.pause = function() {
|
|
182
229
|
self.isPaused = true;
|
|
230
|
+
var now = performance.now();
|
|
231
|
+
self.pausedPercent = (now - self.startTime) / self.lastMoment;
|
|
183
232
|
self.isRunning = false;
|
|
184
233
|
if (self.joggerTimer) {
|
|
185
234
|
clearTimeout(self.joggerTimer);
|
|
186
235
|
self.joggerTimer = null;
|
|
187
236
|
}
|
|
188
237
|
};
|
|
238
|
+
self.currentMillisecond = function() {
|
|
239
|
+
return self.currentTime;
|
|
240
|
+
};
|
|
189
241
|
self.reset = function() {
|
|
190
242
|
self.currentBeat = 0;
|
|
191
243
|
self.currentEvent = 0;
|
|
244
|
+
self.currentLine = 0;
|
|
192
245
|
self.startTime = null;
|
|
193
|
-
self.
|
|
246
|
+
self.pausedPercent = null;
|
|
194
247
|
};
|
|
195
248
|
self.stop = function() {
|
|
196
249
|
self.pause();
|
|
197
250
|
self.reset();
|
|
198
251
|
};
|
|
199
|
-
self.setProgress = function(
|
|
200
|
-
// this is passed a value between 0 and 1.
|
|
252
|
+
self.setProgress = function(position, units) {
|
|
201
253
|
// the effect of this function is to move startTime so that the callbacks happen correctly for the new seek.
|
|
202
|
-
|
|
203
|
-
|
|
254
|
+
var percent;
|
|
255
|
+
switch (units) {
|
|
256
|
+
case "seconds":
|
|
257
|
+
self.currentTime = position * 1000;
|
|
258
|
+
if (self.currentTime < 0) self.currentTime = 0;
|
|
259
|
+
if (self.currentTime > self.lastMoment) self.currentTime = self.lastMoment;
|
|
260
|
+
percent = self.currentTime / self.lastMoment;
|
|
261
|
+
break;
|
|
262
|
+
case "beats":
|
|
263
|
+
self.currentTime = position * self.millisecondsPerBeat * self.beatSubdivisions;
|
|
264
|
+
if (self.currentTime < 0) self.currentTime = 0;
|
|
265
|
+
if (self.currentTime > self.lastMoment) self.currentTime = self.lastMoment;
|
|
266
|
+
percent = self.currentTime / self.lastMoment;
|
|
267
|
+
break;
|
|
268
|
+
default:
|
|
269
|
+
// this is "percent" or any illegal value
|
|
270
|
+
// this is passed a value between 0 and 1.
|
|
271
|
+
percent = position;
|
|
272
|
+
if (percent < 0) percent = 0;
|
|
273
|
+
if (percent > 1) percent = 1;
|
|
274
|
+
self.currentTime = self.lastMoment * percent;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (!self.isRunning)
|
|
279
|
+
self.pausedPercent = percent;
|
|
280
|
+
|
|
281
|
+
var now = performance.now();
|
|
282
|
+
self.startTime = now - self.currentTime;
|
|
283
|
+
|
|
284
|
+
var oldEvent = self.currentEvent;
|
|
285
|
+
self.currentEvent = 0;
|
|
286
|
+
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < self.currentTime) {
|
|
287
|
+
self.currentEvent++;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (self.lineEndCallback) {
|
|
291
|
+
self.currentLine = 0;
|
|
292
|
+
while (self.lineEndTimings.length > self.currentLine && self.lineEndTimings[self.currentLine].milliseconds + self.lineEndAnticipation < self.currentTime) {
|
|
293
|
+
self.currentLine++;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
var oldBeat = self.currentBeat;
|
|
298
|
+
self.currentBeat = Math.floor(self.currentTime / self.millisecondsPerBeat);
|
|
299
|
+
if (self.beatCallback && oldBeat !== self.currentBeat) // If the movement caused the beat to change, then immediately report it to the client.
|
|
300
|
+
self.doBeatCallback(self.startTime+self.currentTime);
|
|
301
|
+
|
|
302
|
+
if (self.eventCallback && self.currentEvent >= 0 && self.noteTimings[self.currentEvent].type === 'event')
|
|
303
|
+
self.eventCallback(self.noteTimings[self.currentEvent]);
|
|
304
|
+
if (self.lineEndCallback)
|
|
305
|
+
self.lineEndCallback(self.lineEndTimings[self.currentLine], self.noteTimings[self.currentEvent], { line: self.currentLine, endTimings: self.lineEndTimings });
|
|
204
306
|
|
|
205
|
-
self.newSeekPercent = percent;
|
|
206
|
-
self.justSeeked = true;
|
|
207
|
-
self.doTiming(performance.now());
|
|
208
307
|
self.joggerTimer = setTimeout(self.animationJogger, JOGGING_INTERVAL);
|
|
209
308
|
};
|
|
210
309
|
};
|
|
@@ -212,12 +311,12 @@ var TimingCallbacks = function(target, params) {
|
|
|
212
311
|
function getLineEndTimings(timings, anticipation) {
|
|
213
312
|
// Returns an array of milliseconds to call the lineEndCallback.
|
|
214
313
|
// This figures out the timing of the beginning of each line and subtracts the anticipation from it.
|
|
215
|
-
var callbackTimes =
|
|
314
|
+
var callbackTimes = [];
|
|
216
315
|
var lastTop = null;
|
|
217
316
|
for (var i = 0; i < timings.length; i++) {
|
|
218
317
|
var timing = timings[i];
|
|
219
|
-
if (timing.top !== lastTop) {
|
|
220
|
-
callbackTimes
|
|
318
|
+
if (timing.type !== 'end' && timing.top !== lastTop) {
|
|
319
|
+
callbackTimes.push({ measureNumber: timing.measureNumber, milliseconds: timing.milliseconds-anticipation, top: timing.top, bottom: timing.top+timing.height });
|
|
221
320
|
lastTop = timing.top;
|
|
222
321
|
}
|
|
223
322
|
}
|
package/src/api/abc_tunebook.js
CHANGED
|
@@ -1,24 +1,9 @@
|
|
|
1
1
|
// abc_tunebook.js: splits a string representing ABC Music Notation into individual tunes.
|
|
2
|
-
// Copyright (C) 2010-2020 Paul Rosen (paul at paulrosen dot net)
|
|
3
|
-
//
|
|
4
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
5
|
-
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation
|
|
6
|
-
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
|
|
7
|
-
// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
8
|
-
//
|
|
9
|
-
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
10
|
-
//
|
|
11
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
|
12
|
-
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
13
|
-
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
14
|
-
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
15
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
16
2
|
|
|
17
|
-
/*global document */
|
|
18
|
-
/*global window, ABCJS, console */
|
|
19
|
-
|
|
20
|
-
var parseCommon = require('../parse/abc_common');
|
|
21
3
|
var Parse = require('../parse/abc_parse');
|
|
4
|
+
var bookParser = require('../parse/abc_parse_book');
|
|
5
|
+
var tablatures = require('./abc_tablatures');
|
|
6
|
+
|
|
22
7
|
|
|
23
8
|
var tunebook = {};
|
|
24
9
|
|
|
@@ -33,52 +18,9 @@ var tunebook = {};
|
|
|
33
18
|
};
|
|
34
19
|
|
|
35
20
|
var TuneBook = tunebook.TuneBook = function(book) {
|
|
36
|
-
var
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
var tunes = book.split("\nX:");
|
|
40
|
-
for (var i = 1; i < tunes.length; i++) // Put back the X: that we lost when splitting the tunes.
|
|
41
|
-
tunes[i] = "X:" + tunes[i];
|
|
42
|
-
// Keep track of the character position each tune starts with.
|
|
43
|
-
var pos = 0;
|
|
44
|
-
This.tunes = [];
|
|
45
|
-
parseCommon.each(tunes, function(tune) {
|
|
46
|
-
This.tunes.push({ abc: tune, startPos: pos});
|
|
47
|
-
pos += tune.length + 1; // We also lost a newline when splitting, so count that.
|
|
48
|
-
});
|
|
49
|
-
if (This.tunes.length > 1 && !parseCommon.startsWith(This.tunes[0].abc, 'X:')) { // If there is only one tune, the X: might be missing, otherwise assume the top of the file is "intertune"
|
|
50
|
-
// There could be file-wide directives in this, if so, we need to insert it into each tune. We can probably get away with
|
|
51
|
-
// just looking for file-wide directives here (before the first tune) and inserting them at the bottom of each tune, since
|
|
52
|
-
// the tune is parsed all at once. The directives will be seen before the engraver begins processing.
|
|
53
|
-
var dir = This.tunes.shift();
|
|
54
|
-
var arrDir = dir.abc.split('\n');
|
|
55
|
-
parseCommon.each(arrDir, function(line) {
|
|
56
|
-
if (parseCommon.startsWith(line, '%%'))
|
|
57
|
-
directives += line + '\n';
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
This.header = directives;
|
|
61
|
-
|
|
62
|
-
// Now, the tune ends at a blank line, so truncate it if needed. There may be "intertune" stuff.
|
|
63
|
-
parseCommon.each(This.tunes, function(tune) {
|
|
64
|
-
var end = tune.abc.indexOf('\n\n');
|
|
65
|
-
if (end > 0)
|
|
66
|
-
tune.abc = tune.abc.substring(0, end);
|
|
67
|
-
tune.pure = tune.abc;
|
|
68
|
-
tune.abc = directives + tune.abc;
|
|
69
|
-
|
|
70
|
-
// for the user's convenience, parse and store the title separately. The title is between the first T: and the next \n
|
|
71
|
-
var title = tune.pure.split("T:");
|
|
72
|
-
if (title.length > 1) {
|
|
73
|
-
title = title[1].split("\n");
|
|
74
|
-
tune.title = title[0].replace(/^\s+|\s+$/g, '');
|
|
75
|
-
} else
|
|
76
|
-
tune.title = "";
|
|
77
|
-
|
|
78
|
-
// for the user's convenience, parse and store the id separately. The id is between the first X: and the next \n
|
|
79
|
-
var id = tune.pure.substring(2, tune.pure.indexOf("\n"));
|
|
80
|
-
tune.id = id.replace(/^\s+|\s+$/g, '');
|
|
81
|
-
});
|
|
21
|
+
var parsed = bookParser(book);
|
|
22
|
+
this.header = parsed.header;
|
|
23
|
+
this.tunes = parsed.tunes;
|
|
82
24
|
};
|
|
83
25
|
|
|
84
26
|
TuneBook.prototype.getTuneById = function(id) {
|
|
@@ -98,7 +40,6 @@ var tunebook = {};
|
|
|
98
40
|
};
|
|
99
41
|
|
|
100
42
|
tunebook.parseOnly = function(abc, params) {
|
|
101
|
-
var tunes = [];
|
|
102
43
|
var numTunes = tunebook.numberOfTunes(abc);
|
|
103
44
|
|
|
104
45
|
// this just needs to be passed in because this tells the engine how many tunes to process.
|
|
@@ -142,16 +83,26 @@ var tunebook = {};
|
|
|
142
83
|
if (currentTune >= 0 && currentTune < book.tunes.length) {
|
|
143
84
|
abcParser.parse(book.tunes[currentTune].abc, params, book.tunes[currentTune].startPos - book.header.length);
|
|
144
85
|
var tune = abcParser.getTune();
|
|
86
|
+
//
|
|
87
|
+
// Init tablatures plugins
|
|
88
|
+
//
|
|
89
|
+
if (params.tablature) {
|
|
90
|
+
tablatures.init();
|
|
91
|
+
tune.tablatures = tablatures.preparePlugins(tune, currentTune, params);
|
|
92
|
+
}
|
|
93
|
+
var warnings = abcParser.getWarnings();
|
|
94
|
+
if (warnings)
|
|
95
|
+
tune.warnings = warnings;
|
|
145
96
|
var override = callback(div, tune, i, book.tunes[currentTune].abc);
|
|
146
97
|
ret.push(override ? override : tune);
|
|
147
98
|
} else {
|
|
148
|
-
if (div
|
|
99
|
+
if (div['innerHTML'])
|
|
149
100
|
div.innerHTML = "";
|
|
150
101
|
}
|
|
151
102
|
}
|
|
152
103
|
currentTune++;
|
|
153
104
|
}
|
|
154
|
-
|
|
105
|
+
return ret;
|
|
155
106
|
};
|
|
156
107
|
|
|
157
108
|
function flattenTune(tuneObj) {
|