abcjs 6.0.0-beta.33 → 6.0.0-beta.37
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/LICENSE.md +1 -1
- package/README.md +20 -9
- package/RELEASE.md +108 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic-min.js.LICENSE +2 -2
- package/dist/abcjs-basic.js +3493 -841
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/dist/abcjs-plugin-min.js.LICENSE +2 -2
- 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.html → report-dev-orig-no-babel.html} +2 -2
- package/dist/report-synth.html +37 -0
- package/docker-build.sh +1 -0
- package/glyphs.json +1 -0
- package/index.js +23 -1
- package/license.js +1 -1
- package/package.json +9 -9
- package/plugin.js +23 -1
- package/src/api/abc_tablatures.js +144 -0
- package/src/api/abc_timing_callbacks.js +34 -31
- package/src/api/abc_tunebook.js +10 -1
- package/src/api/abc_tunebook_svg.js +23 -27
- package/src/data/abc_tune.js +68 -24
- package/src/edit/abc_editor.js +33 -11
- package/src/midi/abc_midi_create.js +6 -2
- package/src/parse/abc_parse.js +7 -6
- package/src/parse/abc_parse_directive.js +19 -12
- package/src/parse/abc_parse_header.js +12 -12
- package/src/parse/abc_parse_music.js +14 -4
- package/src/parse/tune-builder.js +22 -29
- package/src/synth/abc_midi_sequencer.js +10 -0
- package/src/synth/create-synth.js +56 -13
- package/src/synth/load-note.js +31 -65
- package/src/synth/place-note.js +59 -60
- package/src/synth/register-audio-context.js +4 -1
- package/src/synth/supports-audio.js +9 -8
- 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_parser_lint.js +62 -6
- package/src/write/abc_absolute_element.js +2 -2
- package/src/write/abc_abstract_engraver.js +9 -7
- package/src/write/abc_create_key_signature.js +1 -0
- package/src/write/abc_create_note_head.js +1 -1
- package/src/write/abc_engraver_controller.js +26 -13
- package/src/write/abc_glyphs.js +5 -2
- package/src/write/abc_relative_element.js +11 -3
- package/src/write/abc_renderer.js +5 -1
- package/src/write/add-chord.js +5 -2
- package/src/write/add-text-if.js +33 -0
- package/src/write/bottom-text.js +8 -29
- package/src/write/draw/absolute.js +12 -14
- package/src/write/draw/brace.js +3 -3
- package/src/write/draw/crescendo.js +1 -1
- package/src/write/draw/draw.js +5 -6
- package/src/write/draw/dynamics.js +8 -1
- package/src/write/draw/ending.js +4 -3
- package/src/write/draw/group-elements.js +10 -8
- package/src/write/draw/non-music.js +11 -6
- package/src/write/draw/print-line.js +24 -0
- package/src/write/draw/print-stem.js +12 -11
- package/src/write/draw/print-symbol.js +11 -10
- package/src/write/draw/relative.js +33 -13
- package/src/write/draw/selectables.js +9 -6
- package/src/write/draw/staff-group.js +45 -9
- package/src/write/draw/staff-line.js +3 -17
- package/src/write/draw/staff.js +15 -2
- package/src/write/draw/tab-line.js +40 -0
- package/src/write/draw/tempo.js +7 -7
- package/src/write/draw/text.js +11 -4
- package/src/write/draw/tie.js +2 -2
- package/src/write/draw/triplet.js +3 -3
- package/src/write/draw/voice.js +10 -2
- package/src/write/format-jazz-chord.js +15 -0
- package/src/write/free-text.js +20 -12
- package/src/write/layout/VoiceElements.js +33 -1
- package/src/write/layout/beam.js +2 -0
- package/src/write/layout/staffGroup.js +37 -2
- package/src/write/layout/voice.js +2 -1
- package/src/write/selection.js +15 -5
- package/src/write/separator.js +1 -1
- package/src/write/subtitle.js +3 -3
- package/src/write/svg.js +41 -14
- package/src/write/top-text.js +19 -25
- package/test.js +23 -0
- package/types/index.d.ts +1007 -39
- package/version.js +1 -1
- package/docker-start.sh +0 -1
|
@@ -25,6 +25,7 @@ var TimingCallbacks = function(target, params) {
|
|
|
25
25
|
self.currentBeat = 0;
|
|
26
26
|
self.currentEvent = 0;
|
|
27
27
|
self.currentLine = 0;
|
|
28
|
+
self.currentTime = 0;
|
|
28
29
|
self.isPaused = false;
|
|
29
30
|
self.isRunning = false;
|
|
30
31
|
self.pausedPercent = null;
|
|
@@ -53,41 +54,41 @@ var TimingCallbacks = function(target, params) {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
if (!self.isPaused && self.isRunning) {
|
|
56
|
-
|
|
57
|
-
currentTime += 16; // Add a little slop because this function isn't called exactly.
|
|
58
|
-
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < currentTime) {
|
|
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) {
|
|
59
60
|
if (self.eventCallback && self.noteTimings[self.currentEvent].type === 'event') {
|
|
60
61
|
var thisStartTime = self.startTime; // the event callback can call seek and change the position from beneath us.
|
|
61
62
|
self.eventCallback(self.noteTimings[self.currentEvent]);
|
|
62
63
|
if (thisStartTime !== self.startTime) {
|
|
63
|
-
currentTime = timestamp - self.startTime;
|
|
64
|
+
self.currentTime = timestamp - self.startTime;
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
self.currentEvent++;
|
|
67
68
|
}
|
|
68
|
-
if (self.lineEndCallback && self.lineEndTimings.length > self.currentLine && self.lineEndTimings[self.currentLine].milliseconds < currentTime && self.currentEvent < self.noteTimings.length) {
|
|
69
|
-
var leftEvent = self.noteTimings[self.currentEvent].milliseconds === currentTime ? self.noteTimings[self.currentEvent] : self.noteTimings[self.currentEvent-1]
|
|
70
|
-
self.lineEndCallback(self.lineEndTimings[self.currentLine], leftEvent, { line: self.currentLine, endTimings: self.lineEndTimings, currentTime: currentTime });
|
|
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 });
|
|
71
72
|
self.currentLine++;
|
|
72
73
|
}
|
|
73
|
-
if (currentTime < self.lastMoment) {
|
|
74
|
+
if (self.currentTime < self.lastMoment) {
|
|
74
75
|
requestAnimationFrame(self.doTiming);
|
|
75
|
-
if (self.currentBeat * self.millisecondsPerBeat < currentTime) {
|
|
76
|
+
if (self.currentBeat * self.millisecondsPerBeat < self.currentTime) {
|
|
76
77
|
var ret = self.doBeatCallback(timestamp);
|
|
77
78
|
if (ret !== null)
|
|
78
|
-
currentTime = ret;
|
|
79
|
+
self.currentTime = ret;
|
|
79
80
|
}
|
|
80
81
|
} else if (self.currentBeat <= self.totalBeats) {
|
|
81
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.
|
|
82
83
|
if (self.beatCallback) {
|
|
83
84
|
var ret2 = self.doBeatCallback(timestamp);
|
|
84
85
|
if (ret2 !== null)
|
|
85
|
-
currentTime = ret2;
|
|
86
|
+
self.currentTime = ret2;
|
|
86
87
|
requestAnimationFrame(self.doTiming);
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
if (currentTime >= self.lastMoment) {
|
|
91
|
+
if (self.currentTime >= self.lastMoment) {
|
|
91
92
|
if (self.eventCallback) {
|
|
92
93
|
// At the end, the event callback can return "continue" to keep from stopping.
|
|
93
94
|
// The event callback can either be a promise or not.
|
|
@@ -203,7 +204,7 @@ var TimingCallbacks = function(target, params) {
|
|
|
203
204
|
}
|
|
204
205
|
};
|
|
205
206
|
|
|
206
|
-
self.start = function(offsetPercent) {
|
|
207
|
+
self.start = function(offsetPercent, units) {
|
|
207
208
|
self.isRunning = true;
|
|
208
209
|
if (self.isPaused) {
|
|
209
210
|
self.isPaused = false;
|
|
@@ -211,13 +212,13 @@ var TimingCallbacks = function(target, params) {
|
|
|
211
212
|
self.justUnpaused = true;
|
|
212
213
|
}
|
|
213
214
|
if (offsetPercent) {
|
|
214
|
-
self.setProgress(offsetPercent);
|
|
215
|
+
self.setProgress(offsetPercent, units);
|
|
215
216
|
} else if (offsetPercent === 0) {
|
|
216
217
|
self.reset();
|
|
217
218
|
} else if (self.pausedPercent !== null) {
|
|
218
219
|
var now = performance.now();
|
|
219
|
-
|
|
220
|
-
self.startTime = now - currentTime;
|
|
220
|
+
self.currentTime = self.lastMoment * self.pausedPercent;
|
|
221
|
+
self.startTime = now - self.currentTime;
|
|
221
222
|
self.pausedPercent = null;
|
|
222
223
|
self.reportNext = true;
|
|
223
224
|
}
|
|
@@ -234,6 +235,9 @@ var TimingCallbacks = function(target, params) {
|
|
|
234
235
|
self.joggerTimer = null;
|
|
235
236
|
}
|
|
236
237
|
};
|
|
238
|
+
self.currentMillisecond = function() {
|
|
239
|
+
return self.currentTime;
|
|
240
|
+
};
|
|
237
241
|
self.reset = function() {
|
|
238
242
|
self.currentBeat = 0;
|
|
239
243
|
self.currentEvent = 0;
|
|
@@ -247,20 +251,19 @@ var TimingCallbacks = function(target, params) {
|
|
|
247
251
|
};
|
|
248
252
|
self.setProgress = function(position, units) {
|
|
249
253
|
// the effect of this function is to move startTime so that the callbacks happen correctly for the new seek.
|
|
250
|
-
var currentTime;
|
|
251
254
|
var percent;
|
|
252
255
|
switch (units) {
|
|
253
256
|
case "seconds":
|
|
254
|
-
currentTime = position * 1000;
|
|
255
|
-
if (currentTime < 0) currentTime = 0;
|
|
256
|
-
if (currentTime > self.lastMoment) currentTime = self.lastMoment;
|
|
257
|
-
percent = currentTime / self.lastMoment;
|
|
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;
|
|
258
261
|
break;
|
|
259
262
|
case "beats":
|
|
260
|
-
currentTime = position * self.millisecondsPerBeat * self.beatSubdivisions;
|
|
261
|
-
if (currentTime < 0) currentTime = 0;
|
|
262
|
-
if (currentTime > self.lastMoment) currentTime = self.lastMoment;
|
|
263
|
-
percent = currentTime / self.lastMoment;
|
|
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;
|
|
264
267
|
break;
|
|
265
268
|
default:
|
|
266
269
|
// this is "percent" or any illegal value
|
|
@@ -268,7 +271,7 @@ var TimingCallbacks = function(target, params) {
|
|
|
268
271
|
percent = position;
|
|
269
272
|
if (percent < 0) percent = 0;
|
|
270
273
|
if (percent > 1) percent = 1;
|
|
271
|
-
currentTime = self.lastMoment * percent;
|
|
274
|
+
self.currentTime = self.lastMoment * percent;
|
|
272
275
|
break;
|
|
273
276
|
}
|
|
274
277
|
|
|
@@ -276,25 +279,25 @@ var TimingCallbacks = function(target, params) {
|
|
|
276
279
|
self.pausedPercent = percent;
|
|
277
280
|
|
|
278
281
|
var now = performance.now();
|
|
279
|
-
self.startTime = now - currentTime;
|
|
282
|
+
self.startTime = now - self.currentTime;
|
|
280
283
|
|
|
281
284
|
var oldEvent = self.currentEvent;
|
|
282
285
|
self.currentEvent = 0;
|
|
283
|
-
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < currentTime) {
|
|
286
|
+
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < self.currentTime) {
|
|
284
287
|
self.currentEvent++;
|
|
285
288
|
}
|
|
286
289
|
|
|
287
290
|
if (self.lineEndCallback) {
|
|
288
291
|
self.currentLine = 0;
|
|
289
|
-
while (self.lineEndTimings.length > self.currentLine && self.lineEndTimings[self.currentLine].milliseconds + self.lineEndAnticipation < currentTime) {
|
|
292
|
+
while (self.lineEndTimings.length > self.currentLine && self.lineEndTimings[self.currentLine].milliseconds + self.lineEndAnticipation < self.currentTime) {
|
|
290
293
|
self.currentLine++;
|
|
291
294
|
}
|
|
292
295
|
}
|
|
293
296
|
|
|
294
297
|
var oldBeat = self.currentBeat;
|
|
295
|
-
self.currentBeat = Math.floor(currentTime / self.millisecondsPerBeat);
|
|
298
|
+
self.currentBeat = Math.floor(self.currentTime / self.millisecondsPerBeat);
|
|
296
299
|
if (self.beatCallback && oldBeat !== self.currentBeat) // If the movement caused the beat to change, then immediately report it to the client.
|
|
297
|
-
self.doBeatCallback(self.startTime+currentTime);
|
|
300
|
+
self.doBeatCallback(self.startTime+self.currentTime);
|
|
298
301
|
|
|
299
302
|
if (self.eventCallback && self.currentEvent >= 0 && self.noteTimings[self.currentEvent].type === 'event')
|
|
300
303
|
self.eventCallback(self.noteTimings[self.currentEvent]);
|
package/src/api/abc_tunebook.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
var Parse = require('../parse/abc_parse');
|
|
4
4
|
var bookParser = require('../parse/abc_parse_book');
|
|
5
|
+
var tablatures = require('./abc_tablatures');
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
var tunebook = {};
|
|
7
9
|
|
|
@@ -81,6 +83,13 @@ var tunebook = {};
|
|
|
81
83
|
if (currentTune >= 0 && currentTune < book.tunes.length) {
|
|
82
84
|
abcParser.parse(book.tunes[currentTune].abc, params, book.tunes[currentTune].startPos - book.header.length);
|
|
83
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
|
+
}
|
|
84
93
|
var warnings = abcParser.getWarnings();
|
|
85
94
|
if (warnings)
|
|
86
95
|
tune.warnings = warnings;
|
|
@@ -93,7 +102,7 @@ var tunebook = {};
|
|
|
93
102
|
}
|
|
94
103
|
currentTune++;
|
|
95
104
|
}
|
|
96
|
-
|
|
105
|
+
return ret;
|
|
97
106
|
};
|
|
98
107
|
|
|
99
108
|
function flattenTune(tuneObj) {
|
|
@@ -5,6 +5,8 @@ var EngraverController = require('../write/abc_engraver_controller');
|
|
|
5
5
|
var Parse = require('../parse/abc_parse');
|
|
6
6
|
var wrap = require('../parse/wrap_lines');
|
|
7
7
|
var parseCommon = require("../parse/abc_common");
|
|
8
|
+
// var tablatures = require('./abc_tablatures');
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
var resizeDivs = {};
|
|
10
12
|
function resizeOuter() {
|
|
@@ -26,7 +28,7 @@ try {
|
|
|
26
28
|
// if we aren't in a browser, this code will crash, but it is not needed then either.
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
function renderOne(div, tune, params, tuneNumber) {
|
|
31
|
+
function renderOne(div, tune, params, tuneNumber, lineOffset) {
|
|
30
32
|
if (params.viewportHorizontal) {
|
|
31
33
|
// Create an inner div that holds the music, so that the passed in div will be the viewport.
|
|
32
34
|
div.innerHTML = '<div class="abcjs-inner"></div>';
|
|
@@ -48,7 +50,7 @@ function renderOne(div, tune, params, tuneNumber) {
|
|
|
48
50
|
else
|
|
49
51
|
div.innerHTML = "";
|
|
50
52
|
var engraver_controller = new EngraverController(div, params);
|
|
51
|
-
engraver_controller.engraveABC(tune, tuneNumber);
|
|
53
|
+
engraver_controller.engraveABC(tune, tuneNumber, lineOffset);
|
|
52
54
|
tune.engraver = engraver_controller;
|
|
53
55
|
if (params.viewportVertical || params.viewportHorizontal) {
|
|
54
56
|
// If we added a wrapper around the div, then we need to size the wrapper, too.
|
|
@@ -63,8 +65,6 @@ function renderEachLineSeparately(div, tune, params, tuneNumber) {
|
|
|
63
65
|
obj.formatting = tune.formatting;
|
|
64
66
|
obj.media = tune.media;
|
|
65
67
|
obj.version = tune.version;
|
|
66
|
-
obj.metaText = {};
|
|
67
|
-
obj.lines = [];
|
|
68
68
|
return obj;
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -80,14 +80,7 @@ function renderEachLineSeparately(div, tune, params, tuneNumber) {
|
|
|
80
80
|
|
|
81
81
|
if (i === 0) {
|
|
82
82
|
// These items go on top of the music
|
|
83
|
-
tuneLine.
|
|
84
|
-
tuneLine.metaText.title = tune.metaText.title;
|
|
85
|
-
tuneLine.metaText.header = tune.metaText.header;
|
|
86
|
-
tuneLine.metaText.rhythm = tune.metaText.rhythm;
|
|
87
|
-
tuneLine.metaText.origin = tune.metaText.origin;
|
|
88
|
-
tuneLine.metaText.composer = tune.metaText.composer;
|
|
89
|
-
tuneLine.metaText.author = tune.metaText.author;
|
|
90
|
-
tuneLine.metaText.partOrder = tune.metaText.partOrder;
|
|
83
|
+
tuneLine.copyTopInfo(tune);
|
|
91
84
|
}
|
|
92
85
|
|
|
93
86
|
// push the lines until we get to a music line
|
|
@@ -106,17 +99,7 @@ function renderEachLineSeparately(div, tune, params, tuneNumber) {
|
|
|
106
99
|
|
|
107
100
|
// These items go below the music
|
|
108
101
|
tuneLine = tunes[tunes.length-1];
|
|
109
|
-
tuneLine.
|
|
110
|
-
tuneLine.metaText.book = tune.metaText.book;
|
|
111
|
-
tuneLine.metaText.source = tune.metaText.source;
|
|
112
|
-
tuneLine.metaText.discography = tune.metaText.discography;
|
|
113
|
-
tuneLine.metaText.notes = tune.metaText.notes;
|
|
114
|
-
tuneLine.metaText.transcription = tune.metaText.transcription;
|
|
115
|
-
tuneLine.metaText.history = tune.metaText.history;
|
|
116
|
-
tuneLine.metaText['abc-copyright'] = tune.metaText['abc-copyright'];
|
|
117
|
-
tuneLine.metaText['abc-creator'] = tune.metaText['abc-creator'];
|
|
118
|
-
tuneLine.metaText['abc-edited-by'] = tune.metaText['abc-edited-by'];
|
|
119
|
-
tuneLine.metaText.footer = tune.metaText.footer;
|
|
102
|
+
tuneLine.copyBottomInfo(tune);
|
|
120
103
|
|
|
121
104
|
// Now create sub-divs and render each line. Need to copy the params to change the padding for the interior slices.
|
|
122
105
|
var ep = {};
|
|
@@ -127,7 +110,10 @@ function renderEachLineSeparately(div, tune, params, tuneNumber) {
|
|
|
127
110
|
}
|
|
128
111
|
var origPaddingTop = ep.paddingtop;
|
|
129
112
|
var origPaddingBottom = ep.paddingbottom;
|
|
113
|
+
var currentScrollY = div.parentNode.scrollTop; // If there is scrolling it will be lost during the redraw so remember it.
|
|
114
|
+
var currentScrollX = div.parentNode.scrollLeft;
|
|
130
115
|
div.innerHTML = "";
|
|
116
|
+
var lineCount = 0;
|
|
131
117
|
for (var k = 0; k < tunes.length; k++) {
|
|
132
118
|
var lineEl = document.createElement("div");
|
|
133
119
|
div.appendChild(lineEl);
|
|
@@ -145,9 +131,10 @@ function renderEachLineSeparately(div, tune, params, tuneNumber) {
|
|
|
145
131
|
if (k < tunes.length-1) {
|
|
146
132
|
// If it is not the last line, force stretchlast. If it is, stretchlast might have been set by the input parameters.
|
|
147
133
|
tunes[k].formatting = parseCommon.clone(tunes[k].formatting);
|
|
148
|
-
tunes[k].formatting.stretchlast = true
|
|
134
|
+
tunes[k].formatting.stretchlast = true;
|
|
149
135
|
}
|
|
150
|
-
renderOne(lineEl, tunes[k], ep, tuneNumber);
|
|
136
|
+
renderOne(lineEl, tunes[k], ep, tuneNumber, lineCount);
|
|
137
|
+
lineCount += tunes[k].lines.length;
|
|
151
138
|
if (k === 0)
|
|
152
139
|
tune.engraver = tunes[k].engraver;
|
|
153
140
|
else {
|
|
@@ -157,6 +144,9 @@ function renderEachLineSeparately(div, tune, params, tuneNumber) {
|
|
|
157
144
|
tune.engraver.staffgroups.push(tunes[k].engraver.staffgroups[0]);
|
|
158
145
|
}
|
|
159
146
|
}
|
|
147
|
+
if (currentScrollX || currentScrollY) {
|
|
148
|
+
div.parentNode.scrollTo(currentScrollX, currentScrollY);
|
|
149
|
+
}
|
|
160
150
|
}
|
|
161
151
|
|
|
162
152
|
// A quick way to render a tune from javascript when interactivity is not required.
|
|
@@ -186,6 +176,9 @@ var renderAbc = function(output, abc, parserParams, engraverParams, renderParams
|
|
|
186
176
|
params[key] = parserParams[key];
|
|
187
177
|
}
|
|
188
178
|
}
|
|
179
|
+
if (params.warnings_id && params.tablature) {
|
|
180
|
+
params.tablature.warning_id = params.warnings_id;
|
|
181
|
+
}
|
|
189
182
|
}
|
|
190
183
|
if (engraverParams) {
|
|
191
184
|
for (key in engraverParams) {
|
|
@@ -222,7 +215,7 @@ var renderAbc = function(output, abc, parserParams, engraverParams, renderParams
|
|
|
222
215
|
return tune;
|
|
223
216
|
}
|
|
224
217
|
else if (removeDiv || !params.oneSvgPerLine || tune.lines.length < 2)
|
|
225
|
-
renderOne(div, tune, params, tuneNumber);
|
|
218
|
+
renderOne(div, tune, params, tuneNumber, 0);
|
|
226
219
|
else
|
|
227
220
|
renderEachLineSeparately(div, tune, params, tuneNumber);
|
|
228
221
|
if (removeDiv)
|
|
@@ -242,9 +235,12 @@ function doLineWrapping(div, tune, tuneNumber, abcString, params) {
|
|
|
242
235
|
var abcParser = new Parse();
|
|
243
236
|
abcParser.parse(abcString, ret.revisedParams);
|
|
244
237
|
tune = abcParser.getTune();
|
|
238
|
+
var warnings = abcParser.getWarnings();
|
|
239
|
+
if (warnings)
|
|
240
|
+
tune.warnings = warnings;
|
|
245
241
|
}
|
|
246
242
|
if (!params.oneSvgPerLine || tune.lines.length < 2)
|
|
247
|
-
renderOne(div, tune, ret.revisedParams, tuneNumber);
|
|
243
|
+
renderOne(div, tune, ret.revisedParams, tuneNumber, 0);
|
|
248
244
|
else
|
|
249
245
|
renderEachLineSeparately(div, tune, ret.revisedParams, tuneNumber);
|
|
250
246
|
tune.explanation = ret.explanation;
|
package/src/data/abc_tune.js
CHANGED
|
@@ -12,6 +12,48 @@ var delineTune = require("./deline-tune");
|
|
|
12
12
|
* @alternateClassName ABCJS.Tune
|
|
13
13
|
*/
|
|
14
14
|
var Tune = function() {
|
|
15
|
+
this.reset = function () {
|
|
16
|
+
this.version = "1.1.0";
|
|
17
|
+
this.media = "screen";
|
|
18
|
+
this.metaText = {};
|
|
19
|
+
this.metaTextInfo = {};
|
|
20
|
+
this.formatting = {};
|
|
21
|
+
this.lines = [];
|
|
22
|
+
this.staffNum = 0;
|
|
23
|
+
this.voiceNum = 0;
|
|
24
|
+
this.lineNum = 0;
|
|
25
|
+
this.runningFonts = {};
|
|
26
|
+
delete this.visualTranspose;
|
|
27
|
+
};
|
|
28
|
+
this.reset();
|
|
29
|
+
|
|
30
|
+
function copy(dest, src, prop, attrs) {
|
|
31
|
+
for (var i = 0; i < attrs.length; i++)
|
|
32
|
+
dest[prop][attrs[i]] = src[prop][attrs[i]];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.copyTopInfo = function(src) {
|
|
36
|
+
var attrs = ['tempo', 'title', 'header', 'rhythm', 'origin', 'composer', 'author', 'partOrder'];
|
|
37
|
+
copy(this, src, "metaText", attrs);
|
|
38
|
+
copy(this, src, "metaTextInfo", attrs);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
this.copyBottomInfo = function(src) {
|
|
42
|
+
var attrs = ['unalignedWords',
|
|
43
|
+
'book',
|
|
44
|
+
'source',
|
|
45
|
+
'discography',
|
|
46
|
+
'notes',
|
|
47
|
+
'transcription',
|
|
48
|
+
'history',
|
|
49
|
+
'abc-copyright',
|
|
50
|
+
'abc-creator',
|
|
51
|
+
'abc-edited-by',
|
|
52
|
+
'footer']
|
|
53
|
+
copy(this, src, "metaText", attrs);
|
|
54
|
+
copy(this, src, "metaTextInfo", attrs);
|
|
55
|
+
};
|
|
56
|
+
|
|
15
57
|
// The structure consists of a hash with the following two items:
|
|
16
58
|
// metaText: a hash of {key, value}, where key is one of: title, author, rhythm, source, transcription, unalignedWords, etc...
|
|
17
59
|
// tempo: { noteLength: number (e.g. .125), bpm: number }
|
|
@@ -346,30 +388,32 @@ var Tune = function() {
|
|
|
346
388
|
var tempos = {};
|
|
347
389
|
for (var line = 0; line < this.engraver.staffgroups.length; line++) {
|
|
348
390
|
var group = this.engraver.staffgroups[line];
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
var
|
|
360
|
-
|
|
361
|
-
voicesArr[v]
|
|
362
|
-
|
|
363
|
-
measureNumber[v]
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
391
|
+
if (group && group.staffs && group.staffs.length > 0) {
|
|
392
|
+
var firstStaff = group.staffs[0];
|
|
393
|
+
var middleC = firstStaff.absoluteY;
|
|
394
|
+
var top = middleC - firstStaff.top * spacing.STEP;
|
|
395
|
+
var lastStaff = group.staffs[group.staffs.length - 1];
|
|
396
|
+
middleC = lastStaff.absoluteY;
|
|
397
|
+
var bottom = middleC - lastStaff.bottom * spacing.STEP;
|
|
398
|
+
var height = bottom - top;
|
|
399
|
+
|
|
400
|
+
var voices = group.voices;
|
|
401
|
+
for (var v = 0; v < voices.length; v++) {
|
|
402
|
+
var noteFound = false;
|
|
403
|
+
if (!voicesArr[v])
|
|
404
|
+
voicesArr[v] = [];
|
|
405
|
+
if (measureNumber[v] === undefined)
|
|
406
|
+
measureNumber[v] = 0;
|
|
407
|
+
var elements = voices[v].children;
|
|
408
|
+
for (var elem = 0; elem < elements.length; elem++) {
|
|
409
|
+
if (elements[elem].type === "tempo")
|
|
410
|
+
tempos[measureNumber[v]] = this.getBpm(elements[elem].abcelem);
|
|
411
|
+
voicesArr[v].push({top: top, height: height, line: group.line, measureNumber: measureNumber[v], elem: elements[elem]});
|
|
412
|
+
if (elements[elem].type === 'bar' && noteFound) // Count the measures by counting the bar lines, but skip a bar line that appears at the left of the music, before any notes.
|
|
413
|
+
measureNumber[v]++;
|
|
414
|
+
if (elements[elem].type === 'note' || elements[elem].type === 'rest')
|
|
415
|
+
noteFound = true;
|
|
416
|
+
}
|
|
373
417
|
}
|
|
374
418
|
}
|
|
375
419
|
}
|
package/src/edit/abc_editor.js
CHANGED
|
@@ -82,6 +82,17 @@ function gatherAbcParams(params) {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
+
/*
|
|
86
|
+
if (params.tablature_options) {
|
|
87
|
+
abcjsParams['tablatures'] = params.tablature_options;
|
|
88
|
+
}
|
|
89
|
+
*/
|
|
90
|
+
if (abcjsParams.tablature) {
|
|
91
|
+
if (params.warnings_id) {
|
|
92
|
+
// store for plugin error handling
|
|
93
|
+
abcjsParams.tablature.warnings_id = params.warnings_id;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
85
96
|
return abcjsParams;
|
|
86
97
|
}
|
|
87
98
|
|
|
@@ -123,7 +134,7 @@ var Editor = function(editarea, params) {
|
|
|
123
134
|
el: params.synth.el,
|
|
124
135
|
cursorControl: params.synth.cursorControl,
|
|
125
136
|
options: params.synth.options
|
|
126
|
-
}
|
|
137
|
+
};
|
|
127
138
|
}
|
|
128
139
|
}
|
|
129
140
|
// If the user wants midi, then store the elements that it will be written to. The element could either be passed in as an id,
|
|
@@ -219,16 +230,23 @@ Editor.prototype.redrawMidi = function() {
|
|
|
219
230
|
Editor.prototype.modelChanged = function() {
|
|
220
231
|
if (this.bReentry)
|
|
221
232
|
return; // TODO is this likely? maybe, if we rewrite abc immediately w/ abc2abc
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
233
|
+
this.bReentry = true;
|
|
234
|
+
try {
|
|
235
|
+
this.timerId = null;
|
|
236
|
+
if (this.synth && this.synth.synthControl)
|
|
237
|
+
this.synth.synthControl.disable(true);
|
|
238
|
+
|
|
239
|
+
this.tunes = renderAbc(this.div, this.currentAbc, this.abcjsParams);
|
|
240
|
+
if (this.tunes.length > 0) {
|
|
241
|
+
this.warnings = this.tunes[0].warnings;
|
|
242
|
+
}
|
|
243
|
+
this.redrawMidi();
|
|
244
|
+
} catch(error) {
|
|
245
|
+
console.error("ABCJS error: ", error);
|
|
246
|
+
if (!this.warnings)
|
|
247
|
+
this.warnings = [];
|
|
248
|
+
this.warnings.push(error.message);
|
|
249
|
+
}
|
|
232
250
|
|
|
233
251
|
if (this.warningsdiv) {
|
|
234
252
|
this.warningsdiv.innerHTML = (this.warnings) ? this.warnings.join("<br />") : "No errors";
|
|
@@ -251,6 +269,8 @@ Editor.prototype.paramChanged = function(engraverParams) {
|
|
|
251
269
|
};
|
|
252
270
|
|
|
253
271
|
Editor.prototype.synthParamChanged = function(options) {
|
|
272
|
+
if (!this.synth)
|
|
273
|
+
return;
|
|
254
274
|
this.synth.options = {};
|
|
255
275
|
if (options) {
|
|
256
276
|
for (var key in options) {
|
|
@@ -371,6 +391,8 @@ Editor.prototype.pause = function(shouldPause) {
|
|
|
371
391
|
};
|
|
372
392
|
|
|
373
393
|
Editor.prototype.millisecondsPerMeasure = function() {
|
|
394
|
+
if (!this.synth || !this.synth.synthControl || !this.synth.synthControl.visualObj)
|
|
395
|
+
return 0;
|
|
374
396
|
return this.synth.synthControl.visualObj.millisecondsPerMeasure();
|
|
375
397
|
};
|
|
376
398
|
|
|
@@ -19,6 +19,8 @@ var create;
|
|
|
19
19
|
title = title.substring(0,124) + '...';
|
|
20
20
|
var key = abcTune.getKeySignature();
|
|
21
21
|
var time = abcTune.getMeterFraction();
|
|
22
|
+
var beatsPerSecond = commands.tempo / 60;
|
|
23
|
+
//var beatLength = abcTune.getBeatLength();
|
|
22
24
|
midi.setGlobalInfo(commands.tempo, title, key, time);
|
|
23
25
|
|
|
24
26
|
for (var i = 0; i < commands.tracks.length; i++) {
|
|
@@ -38,9 +40,11 @@ var create;
|
|
|
38
40
|
midi.setInstrument(event.instrument);
|
|
39
41
|
break;
|
|
40
42
|
case 'note':
|
|
43
|
+
var gapLengthInBeats = event.gap * beatsPerSecond;
|
|
41
44
|
var start = event.start;
|
|
42
|
-
|
|
43
|
-
//
|
|
45
|
+
// The staccato and legato are indicated by event.gap.
|
|
46
|
+
// event.gap is in seconds but the durations are in whole notes.
|
|
47
|
+
var end = start + event.duration - gapLengthInBeats;
|
|
44
48
|
if (!notePlacement[start])
|
|
45
49
|
notePlacement[start] = [];
|
|
46
50
|
notePlacement[start].push({ pitch: event.pitch, volume: event.volume, cents: event.cents });
|
package/src/parse/abc_parse.js
CHANGED
|
@@ -24,6 +24,7 @@ var Parse = function() {
|
|
|
24
24
|
lines: tune.lines,
|
|
25
25
|
media: tune.media,
|
|
26
26
|
metaText: tune.metaText,
|
|
27
|
+
metaTextInfo: tune.metaTextInfo,
|
|
27
28
|
version: tune.version,
|
|
28
29
|
|
|
29
30
|
addElementToEvents: tune.addElementToEvents,
|
|
@@ -137,8 +138,10 @@ var Parse = function() {
|
|
|
137
138
|
};
|
|
138
139
|
for (var i = 0; i < this.inTie.length; i++) {
|
|
139
140
|
this.endingHoldOver.inTie.push([]);
|
|
140
|
-
|
|
141
|
-
this.
|
|
141
|
+
if (this.inTie[i]) { // if a voice is suppressed there might be a gap in the array.
|
|
142
|
+
for (var j = 0; j < this.inTie[i].length; j++) {
|
|
143
|
+
this.endingHoldOver.inTie[i].push(this.inTie[i][j]);
|
|
144
|
+
}
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
147
|
for (var key in this.inTieChord) {
|
|
@@ -188,9 +191,7 @@ var Parse = function() {
|
|
|
188
191
|
var bad_char = line.charAt(col_num);
|
|
189
192
|
if (bad_char === ' ')
|
|
190
193
|
bad_char = "SPACE";
|
|
191
|
-
var clean_line = encode(line.substring(
|
|
192
|
-
'<span style="text-decoration:underline;font-size:1.3em;font-weight:bold;">' + bad_char + '</span>' +
|
|
193
|
-
encode(line.substring(col_num+1));
|
|
194
|
+
var clean_line = encode(line.substring(col_num - 64, col_num)) + '<span style="text-decoration:underline;font-size:1.3em;font-weight:bold;">' + bad_char + '</span>' + encode(line.substring(col_num + 1).substring(0,64));
|
|
194
195
|
addWarning("Music Line:" + tokenizer.lineIndex + ":" + (col_num+1) + ': ' + str + ": " + clean_line);
|
|
195
196
|
addWarningObject({message:str, line:line, startChar: multilineVars.iChar + col_num, column: col_num});
|
|
196
197
|
};
|
|
@@ -473,7 +474,7 @@ var Parse = function() {
|
|
|
473
474
|
// switches.transpose: change the key signature, chords, and notes by a number of half-steps.
|
|
474
475
|
if (!switches) switches = {};
|
|
475
476
|
if (!startPos) startPos = 0;
|
|
476
|
-
|
|
477
|
+
tune.reset();
|
|
477
478
|
|
|
478
479
|
// Take care of whatever line endings come our way
|
|
479
480
|
// Tack on newline temporarily to make the last line continuation work
|