abcjs 6.1.8 → 6.2.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/RELEASE.md +66 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +4373 -4424
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/index.js +1 -1
- package/package.json +6 -6
- package/plugin.js +1 -1
- package/src/api/abc_timing_callbacks.js +7 -3
- package/src/api/abc_tunebook_svg.js +1 -2
- package/src/data/abc_tune.js +2 -2
- package/src/midi/abc_midi_create.js +8 -2
- package/src/parse/abc_common.js +0 -47
- package/src/parse/abc_parse.js +16 -16
- package/src/parse/abc_parse_book.js +3 -3
- package/src/parse/abc_parse_directive.js +26 -7
- package/src/parse/abc_parse_header.js +11 -9
- package/src/parse/abc_parse_key_voice.js +17 -17
- package/src/parse/abc_parse_music.js +89 -106
- package/src/parse/abc_tokenizer.js +60 -60
- package/src/parse/abc_transpose.js +14 -3
- package/src/parse/tune-builder.js +19 -14
- package/src/synth/abc_midi_flattener.js +25 -9
- package/src/synth/abc_midi_sequencer.js +1 -1
- package/src/synth/create-synth.js +41 -0
- package/src/synth/note-to-midi.js +50 -0
- package/src/tablatures/instruments/guitar/tab-guitar.js +0 -2
- package/src/tablatures/instruments/string-patterns.js +46 -28
- package/src/tablatures/instruments/tab-note.js +26 -103
- package/src/tablatures/instruments/violin/tab-violin.js +0 -2
- package/src/tablatures/tab-absolute-elements.js +9 -31
- package/src/tablatures/tab-renderer.js +2 -2
- package/src/test/abc_parser_lint.js +8 -5
- package/src/write/README.md +31 -0
- package/src/write/creation/abstract-engraver.js +1036 -0
- package/src/write/creation/add-chord.js +102 -0
- package/src/write/{add-text-if.js → creation/add-text-if.js} +6 -6
- package/src/write/{calcHeight.js → creation/calc-height.js} +2 -2
- package/src/write/creation/create-clef.js +72 -0
- package/src/write/creation/create-key-signature.js +31 -0
- package/src/write/creation/create-note-head.js +107 -0
- package/src/write/creation/create-time-signature.js +55 -0
- package/src/write/creation/decoration.js +357 -0
- package/src/write/{abc_absolute_element.js → creation/elements/absolute-element.js} +14 -15
- package/src/write/creation/elements/beam-element.js +113 -0
- package/src/write/{bottom-text.js → creation/elements/bottom-text.js} +14 -15
- package/src/write/{abc_brace_element.js → creation/elements/brace-element.js} +5 -5
- package/src/write/creation/elements/free-text.js +41 -0
- package/src/write/{abc_relative_element.js → creation/elements/relative-element.js} +8 -7
- package/src/write/{separator.js → creation/elements/separator.js} +2 -2
- package/src/write/{abc_staff_group_element.js → creation/elements/staff-group-element.js} +4 -4
- package/src/write/{subtitle.js → creation/elements/subtitle.js} +3 -3
- package/src/write/creation/elements/tempo-element.js +63 -0
- package/src/write/{abc_tie_element.js → creation/elements/tie-element.js} +15 -11
- package/src/write/{top-text.js → creation/elements/top-text.js} +12 -12
- package/src/write/creation/elements/triplet-element.js +28 -0
- package/src/write/{abc_voice_element.js → creation/elements/voice-element.js} +3 -3
- package/src/write/creation/glyphs.js +226 -0
- package/src/write/creation/translate-chord.js +37 -0
- package/src/write/draw/absolute.js +5 -5
- package/src/write/draw/beam.js +8 -8
- package/src/write/draw/brace.js +33 -33
- package/src/write/draw/crescendo.js +4 -4
- package/src/write/draw/debug-box.js +1 -1
- package/src/write/draw/draw.js +7 -7
- package/src/write/draw/dynamics.js +2 -2
- package/src/write/draw/ending.js +6 -6
- package/src/write/draw/glissando.js +17 -17
- package/src/write/draw/group-elements.js +51 -51
- package/src/write/draw/horizontal-line.js +9 -9
- package/src/write/draw/non-music.js +1 -1
- package/src/write/draw/print-line.js +15 -16
- package/src/write/draw/print-stem.js +5 -5
- package/src/write/draw/print-symbol.js +12 -12
- package/src/write/draw/print-vertical-line.js +8 -8
- package/src/write/draw/relative.js +17 -16
- package/src/write/draw/selectables.js +5 -5
- package/src/write/draw/separator.js +4 -4
- package/src/write/draw/set-paper-size.js +2 -2
- package/src/write/draw/sprintf.js +31 -31
- package/src/write/draw/staff-group.js +36 -30
- package/src/write/draw/staff-line.js +2 -2
- package/src/write/draw/staff.js +4 -4
- package/src/write/draw/tab-line.js +26 -26
- package/src/write/draw/tempo.js +30 -30
- package/src/write/draw/text.js +5 -5
- package/src/write/draw/tie.js +18 -18
- package/src/write/draw/triplet.js +6 -6
- package/src/write/draw/voice.js +16 -17
- package/src/write/{abc_engraver_controller.js → engraver-controller.js} +58 -51
- package/src/write/{classes.js → helpers/classes.js} +6 -6
- package/src/write/{get-font-and-attr.js → helpers/get-font-and-attr.js} +9 -7
- package/src/write/{get-text-size.js → helpers/get-text-size.js} +5 -5
- package/src/write/{set-class.js → helpers/set-class.js} +1 -1
- package/src/write/{abc_spacing.js → helpers/spacing.js} +1 -1
- package/src/write/{highlight.js → interactive/highlight.js} +1 -1
- package/src/write/{selection.js → interactive/selection.js} +27 -27
- package/src/write/{unhighlight.js → interactive/unhighlight.js} +1 -1
- package/src/write/layout/beam.js +13 -13
- package/src/write/layout/get-left-edge-of-staff.js +4 -4
- package/src/write/layout/layout.js +74 -74
- package/src/write/layout/{setUpperAndLowerElements.js → set-upper-and-lower-elements.js} +8 -8
- package/src/write/layout/{staffGroup.js → staff-group.js} +32 -32
- package/src/write/layout/triplet.js +4 -4
- package/src/write/layout/{VoiceElements.js → voice-elements.js} +23 -23
- package/src/write/layout/voice.js +6 -6
- package/src/write/{abc_renderer.js → renderer.js} +35 -32
- package/src/write/svg.js +35 -35
- package/test.js +1 -1
- package/types/index.d.ts +99 -22
- package/version.js +1 -1
- package/src/tablatures/instruments/guitar/guitar-fonts.js +0 -19
- package/src/tablatures/instruments/violin/violin-fonts.js +0 -19
- package/src/tablatures/transposer.js +0 -110
- package/src/write/abc_abstract_engraver.js +0 -1026
- package/src/write/abc_beam_element.js +0 -113
- package/src/write/abc_create_clef.js +0 -72
- package/src/write/abc_create_key_signature.js +0 -33
- package/src/write/abc_create_note_head.js +0 -107
- package/src/write/abc_create_time_signature.js +0 -55
- package/src/write/abc_decoration.js +0 -341
- package/src/write/abc_glyphs.js +0 -224
- package/src/write/abc_tempo_element.js +0 -63
- package/src/write/abc_triplet_element.js +0 -28
- package/src/write/add-chord.js +0 -103
- package/src/write/format-jazz-chord.js +0 -15
- package/src/write/free-text.js +0 -41
- /package/src/write/{abc_crescendo_element.js → creation/elements/crescendo-element.js} +0 -0
- /package/src/write/{abc_dynamic_decoration.js → creation/elements/dynamic-decoration.js} +0 -0
- /package/src/write/{abc_ending_element.js → creation/elements/ending-element.js} +0 -0
- /package/src/write/{abc_glissando_element.js → creation/elements/glissando-element.js} +0 -0
- /package/src/write/layout/{getBarYAt.js → get-bar-y-at.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var spacing = require('
|
|
1
|
+
var spacing = require('../helpers/spacing');
|
|
2
2
|
|
|
3
3
|
function setupSelection(engraver, svgs) {
|
|
4
4
|
engraver.rangeHighlight = rangeHighlight;
|
|
@@ -15,9 +15,9 @@ function setupSelection(engraver, svgs) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
for (var i = 0; i < svgs.length; i++) {
|
|
18
|
-
svgs[i].addEventListener('touchstart', mouseDown.bind(engraver));
|
|
19
|
-
svgs[i].addEventListener('touchmove', mouseMove.bind(engraver));
|
|
20
|
-
svgs[i].addEventListener('touchend', mouseUp.bind(engraver));
|
|
18
|
+
svgs[i].addEventListener('touchstart', mouseDown.bind(engraver), { passive: true });
|
|
19
|
+
svgs[i].addEventListener('touchmove', mouseMove.bind(engraver), { passive: true });
|
|
20
|
+
svgs[i].addEventListener('touchend', mouseUp.bind(engraver), { passive: true });
|
|
21
21
|
svgs[i].addEventListener('mousedown', mouseDown.bind(engraver));
|
|
22
22
|
svgs[i].addEventListener('mousemove', mouseMove.bind(engraver));
|
|
23
23
|
svgs[i].addEventListener('mouseup', mouseUp.bind(engraver));
|
|
@@ -56,7 +56,7 @@ function getCoord(ev) {
|
|
|
56
56
|
y = y * scaleY;
|
|
57
57
|
//console.log(x, y)
|
|
58
58
|
|
|
59
|
-
return [x, y+yOffset];
|
|
59
|
+
return [x, y + yOffset];
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function elementFocused(ev) {
|
|
@@ -69,7 +69,7 @@ function elementFocused(ev) {
|
|
|
69
69
|
|
|
70
70
|
function keyboardDown(ev) {
|
|
71
71
|
// Swallow the up and down arrow events - they will be used for dragging with the keyboard
|
|
72
|
-
switch(ev.keyCode) {
|
|
72
|
+
switch (ev.keyCode) {
|
|
73
73
|
case 38:
|
|
74
74
|
case 40:
|
|
75
75
|
ev.preventDefault();
|
|
@@ -80,7 +80,7 @@ function keyboardSelection(ev) {
|
|
|
80
80
|
// "this" is the EngraverController because of the bind(this) when setting the event listener.
|
|
81
81
|
var handled = false;
|
|
82
82
|
var index = ev.target.dataset.index;
|
|
83
|
-
switch(ev.keyCode) {
|
|
83
|
+
switch (ev.keyCode) {
|
|
84
84
|
case 13:
|
|
85
85
|
case 32:
|
|
86
86
|
handled = true;
|
|
@@ -163,7 +163,7 @@ function findElementByCoord(self, x, y) {
|
|
|
163
163
|
// figure out the distance to this element.
|
|
164
164
|
var dx = Math.abs(x - el.dim.left) > Math.abs(x - el.dim.right) ? Math.abs(x - el.dim.right) : Math.abs(x - el.dim.left);
|
|
165
165
|
var dy = Math.abs(y - el.dim.top) > Math.abs(y - el.dim.bottom) ? Math.abs(y - el.dim.bottom) : Math.abs(y - el.dim.top);
|
|
166
|
-
var hypotenuse = Math.sqrt(dx*dx + dy*dy);
|
|
166
|
+
var hypotenuse = Math.sqrt(dx * dx + dy * dy);
|
|
167
167
|
if (hypotenuse < minDistance) {
|
|
168
168
|
minDistance = hypotenuse;
|
|
169
169
|
closestIndex = i;
|
|
@@ -177,20 +177,20 @@ function getBestMatchCoordinates(dim, ev, scale) {
|
|
|
177
177
|
// Different browsers have conflicting meanings for the coordinates that are returned.
|
|
178
178
|
// If the item we want is clicked on directly, then we will just see what is the best match.
|
|
179
179
|
// This seems like less of a hack than browser sniffing.
|
|
180
|
-
if (dim.x <= ev.offsetX && dim.x+dim.width >= ev.offsetX &&
|
|
181
|
-
dim.y <= ev.offsetY && dim.y+dim.height >= ev.offsetY)
|
|
182
|
-
return [
|
|
180
|
+
if (dim.x <= ev.offsetX && dim.x + dim.width >= ev.offsetX &&
|
|
181
|
+
dim.y <= ev.offsetY && dim.y + dim.height >= ev.offsetY)
|
|
182
|
+
return [ev.offsetX, ev.offsetY];
|
|
183
183
|
// Firefox returns a weird value for offset, but layer is correct.
|
|
184
184
|
// Safari and Chrome return the correct value for offset, but layer is multiplied by the scale (that is, if it were rendered with { scale: 2 })
|
|
185
185
|
// For instance (if scale is 2):
|
|
186
186
|
// Firefox: { offsetY: 5, layerY: 335 }
|
|
187
187
|
// Others: {offsetY: 335, layerY: 670} (there could be a little rounding, so the number might not be exactly 2x)
|
|
188
188
|
// So, if layerY/scale is approx. offsetY, then use offsetY, otherwise use layerY
|
|
189
|
-
var epsilon = Math.abs(ev.layerY/scale - ev.offsetY);
|
|
189
|
+
var epsilon = Math.abs(ev.layerY / scale - ev.offsetY);
|
|
190
190
|
if (epsilon < 3)
|
|
191
|
-
return [
|
|
191
|
+
return [ev.offsetX, ev.offsetY];
|
|
192
192
|
else
|
|
193
|
-
return
|
|
193
|
+
return [ev.layerX, ev.layerY];
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
function getTarget(target) {
|
|
@@ -202,7 +202,7 @@ function getTarget(target) {
|
|
|
202
202
|
while (!found) {
|
|
203
203
|
if (!target.parentElement)
|
|
204
204
|
found = true;
|
|
205
|
-
else {
|
|
205
|
+
else {
|
|
206
206
|
target = target.parentElement;
|
|
207
207
|
if (target.tagName === "svg")
|
|
208
208
|
found = true;
|
|
@@ -290,7 +290,7 @@ function mouseMove(ev) {
|
|
|
290
290
|
|
|
291
291
|
var positioning = getMousePosition(this, _ev);
|
|
292
292
|
|
|
293
|
-
var yDist = Math.round((positioning.y - this.dragMouseStart.y)/spacing.STEP);
|
|
293
|
+
var yDist = Math.round((positioning.y - this.dragMouseStart.y) / spacing.STEP);
|
|
294
294
|
if (yDist !== this.dragYStep) {
|
|
295
295
|
this.dragYStep = yDist;
|
|
296
296
|
this.dragTarget.svgEl.setAttribute("transform", "translate(0," + (yDist * spacing.STEP) + ")");
|
|
@@ -329,7 +329,7 @@ function setSelection(dragIndex) {
|
|
|
329
329
|
this.dragTarget = this.selectables[dragIndex];
|
|
330
330
|
this.dragIndex = dragIndex;
|
|
331
331
|
this.dragMechanism = "keyboard";
|
|
332
|
-
mouseUp.bind(this)({target: this.dragTarget.svgEl});
|
|
332
|
+
mouseUp.bind(this)({ target: this.dragTarget.svgEl });
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
335
|
|
|
@@ -368,8 +368,8 @@ function notifySelect(target, dragStep, dragMax, dragIndex, ev) {
|
|
|
368
368
|
analysis.clickedClasses = closest.classList;
|
|
369
369
|
analysis.selectableElement = target.svgEl;
|
|
370
370
|
|
|
371
|
-
for (var i=0; i<this.listeners.length;i++) {
|
|
372
|
-
this.listeners[i](target.absEl.abcelem, target.absEl.tuneNumber, classes.join(' '), analysis, { step: dragStep, max: dragMax, index: dragIndex, setSelection: setSelection.bind(this)}, ev);
|
|
371
|
+
for (var i = 0; i < this.listeners.length; i++) {
|
|
372
|
+
this.listeners[i](target.absEl.abcelem, target.absEl.tuneNumber, classes.join(' '), analysis, { step: dragStep, max: dragMax, index: dragIndex, setSelection: setSelection.bind(this) }, ev);
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
375
|
|
|
@@ -377,32 +377,32 @@ function findNumber(klass, match, target, name) {
|
|
|
377
377
|
if (klass.indexOf(match) === 0) {
|
|
378
378
|
var value = klass.replace(match, '');
|
|
379
379
|
var num = parseInt(value, 10);
|
|
380
|
-
if (''+num === value)
|
|
380
|
+
if ('' + num === value)
|
|
381
381
|
target[name] = num;
|
|
382
382
|
}
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
function clearSelection() {
|
|
386
|
-
for (var i=0;i<this.selected.length;i++) {
|
|
386
|
+
for (var i = 0; i < this.selected.length; i++) {
|
|
387
387
|
this.selected[i].unhighlight(undefined, this.renderer.foregroundColor);
|
|
388
388
|
}
|
|
389
389
|
this.selected = [];
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
function rangeHighlight(start,end) {
|
|
392
|
+
function rangeHighlight(start, end) {
|
|
393
393
|
clearSelection.bind(this)();
|
|
394
|
-
for (var line=0;line<this.staffgroups.length; line++) {
|
|
394
|
+
for (var line = 0; line < this.staffgroups.length; line++) {
|
|
395
395
|
var voices = this.staffgroups[line].voices;
|
|
396
|
-
for (var voice=0;voice<voices.length;voice++) {
|
|
396
|
+
for (var voice = 0; voice < voices.length; voice++) {
|
|
397
397
|
var elems = voices[voice].children;
|
|
398
|
-
for (var elem=0; elem<elems.length; elem++) {
|
|
398
|
+
for (var elem = 0; elem < elems.length; elem++) {
|
|
399
399
|
// Since the user can highlight more than an element, or part of an element, a hit is if any of the endpoints
|
|
400
400
|
// is inside the other range.
|
|
401
401
|
var elStart = elems[elem].abcelem.startChar;
|
|
402
402
|
var elEnd = elems[elem].abcelem.endChar;
|
|
403
|
-
if ((end>elStart && start<elEnd) || ((end===start) && end===elEnd)) {
|
|
403
|
+
if ((end > elStart && start < elEnd) || ((end === start) && end === elEnd)) {
|
|
404
404
|
// if (elems[elem].abcelem.startChar>=start && elems[elem].abcelem.endChar<=end) {
|
|
405
|
-
this.selected[this.selected.length]=elems[elem];
|
|
405
|
+
this.selected[this.selected.length] = elems[elem];
|
|
406
406
|
elems[elem].highlight(undefined, this.selectionColor);
|
|
407
407
|
}
|
|
408
408
|
}
|
package/src/write/layout/beam.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var RelativeElement = require('../
|
|
2
|
-
var spacing = require('../
|
|
3
|
-
var getBarYAt = require('./
|
|
1
|
+
var RelativeElement = require('../creation/elements/relative-element');
|
|
2
|
+
var spacing = require('../helpers/spacing');
|
|
3
|
+
var getBarYAt = require('./get-bar-y-at');
|
|
4
4
|
|
|
5
|
-
var layoutBeam = function(beam) {
|
|
5
|
+
var layoutBeam = function (beam) {
|
|
6
6
|
if (beam.elems.length === 0 || beam.allrests) return;
|
|
7
7
|
|
|
8
8
|
var dy = calcDy(beam.stemsUp, beam.isgrace); // This is the width of the beam line.
|
|
@@ -28,14 +28,14 @@ var layoutBeam = function(beam) {
|
|
|
28
28
|
createStems(beam.elems, beam.stemsUp, beam.beams[0], dy, beam.mainNote);
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
var getDurlog = function(duration) {
|
|
31
|
+
var getDurlog = function (duration) {
|
|
32
32
|
// TODO-PER: This is a hack to prevent a Chrome lockup. Duration should have been defined already,
|
|
33
33
|
// but there's definitely a case where it isn't. [Probably something to do with triplets.]
|
|
34
34
|
if (duration === undefined) {
|
|
35
35
|
return 0;
|
|
36
36
|
}
|
|
37
37
|
// console.log("getDurlog: " + duration);
|
|
38
|
-
return Math.floor(Math.log(duration)/Math.log(2));
|
|
38
|
+
return Math.floor(Math.log(duration) / Math.log(2));
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
//
|
|
@@ -78,7 +78,7 @@ function calcXPos(asc, firstElement, lastElement) {
|
|
|
78
78
|
if (asc) startX += starthead.w - 0.6;
|
|
79
79
|
var endX = endhead.x;
|
|
80
80
|
endX += (asc) ? endhead.w : 0.6;
|
|
81
|
-
return [
|
|
81
|
+
return [startX, endX];
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
function calcYPos(average, numElements, stemHeight, asc, firstAveragePitch, lastAveragePitch, isFlat, minPitch, maxPitch, isGrace) {
|
|
@@ -101,7 +101,7 @@ function calcYPos(average, numElements, stemHeight, asc, firstAveragePitch, last
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
return [
|
|
104
|
+
return [startY, endY];
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function createStems(elems, asc, beam, dy, mainNote) {
|
|
@@ -168,11 +168,11 @@ function createAdditionalBeams(elems, asc, beam, isGrace, dy) {
|
|
|
168
168
|
durlog: durlog, single: true
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
|
-
if (i > 0 && elem.abcelem.beambr && elem.abcelem.beambr <= (index+1)) {
|
|
171
|
+
if (i > 0 && elem.abcelem.beambr && elem.abcelem.beambr <= (index + 1)) {
|
|
172
172
|
if (!auxBeams[index].split)
|
|
173
173
|
auxBeams[index].split = [auxBeams[index].x];
|
|
174
|
-
var xPos = calcXPos(asc, elems[i-1], elem);
|
|
175
|
-
if (auxBeams[index].split[auxBeams[index].split.length-1] >= xPos[0]) {
|
|
174
|
+
var xPos = calcXPos(asc, elems[i - 1], elem);
|
|
175
|
+
if (auxBeams[index].split[auxBeams[index].split.length - 1] >= xPos[0]) {
|
|
176
176
|
// the reduction in beams leaves a note unattached so create a small flag for it.
|
|
177
177
|
xPos[0] += elem.w;
|
|
178
178
|
}
|
|
@@ -195,9 +195,9 @@ function createAdditionalBeams(elems, asc, beam, isGrace, dy) {
|
|
|
195
195
|
var b = { startX: auxBeams[j].x, endX: auxBeamEndX, startY: auxBeams[j].y, endY: auxBeamEndY, dy: dy }
|
|
196
196
|
if (auxBeams[j].split !== undefined) {
|
|
197
197
|
var split = auxBeams[j].split;
|
|
198
|
-
if (b.endX <= split[split.length-1]) {
|
|
198
|
+
if (b.endX <= split[split.length - 1]) {
|
|
199
199
|
// the reduction in beams leaves the last note by itself, so create a little flag for it
|
|
200
|
-
split[split.length-1] -= elem.w;
|
|
200
|
+
split[split.length - 1] -= elem.w;
|
|
201
201
|
}
|
|
202
202
|
split.push(b.endX);
|
|
203
203
|
b.split = auxBeams[j].split;
|
|
@@ -5,10 +5,10 @@ function getLeftEdgeOfStaff(renderer, getTextSize, voices, brace, bracket) {
|
|
|
5
5
|
var voiceheaderw = 0;
|
|
6
6
|
var i;
|
|
7
7
|
var size;
|
|
8
|
-
for (i=0;i<voices.length;i++) {
|
|
9
|
-
if(voices[i].header) {
|
|
8
|
+
for (i = 0; i < voices.length; i++) {
|
|
9
|
+
if (voices[i].header) {
|
|
10
10
|
size = getTextSize.calc(voices[i].header, 'voicefont', '');
|
|
11
|
-
voiceheaderw = Math.max(voiceheaderw,size.width);
|
|
11
|
+
voiceheaderw = Math.max(voiceheaderw, size.width);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
voiceheaderw = addBraceSize(voiceheaderw, brace, getTextSize);
|
|
@@ -32,7 +32,7 @@ function addBraceSize(voiceheaderw, brace, getTextSize) {
|
|
|
32
32
|
for (var i = 0; i < brace.length; i++) {
|
|
33
33
|
if (brace[i].header) {
|
|
34
34
|
var size = getTextSize.calc(brace[i].header, 'voicefont', '');
|
|
35
|
-
voiceheaderw = Math.max(voiceheaderw,size.width);
|
|
35
|
+
voiceheaderw = Math.max(voiceheaderw, size.width);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -1,94 +1,94 @@
|
|
|
1
1
|
var layoutVoice = require('./voice');
|
|
2
|
-
var setUpperAndLowerElements = require('./
|
|
3
|
-
var layoutStaffGroup = require('./
|
|
2
|
+
var setUpperAndLowerElements = require('./set-upper-and-lower-elements');
|
|
3
|
+
var layoutStaffGroup = require('./staff-group');
|
|
4
4
|
var getLeftEdgeOfStaff = require('./get-left-edge-of-staff');
|
|
5
5
|
|
|
6
6
|
var layout = function (renderer, abctune, width, space) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
7
|
+
var i;
|
|
8
|
+
var abcLine;
|
|
9
|
+
// Adjust the x-coordinates to their absolute positions
|
|
10
|
+
var maxWidth = width;
|
|
11
|
+
for (i = 0; i < abctune.lines.length; i++) {
|
|
12
|
+
abcLine = abctune.lines[i];
|
|
13
|
+
if (abcLine.staff) {
|
|
14
|
+
setXSpacing(renderer, width, space, abcLine.staffGroup, abctune.formatting, i === abctune.lines.length - 1, false);
|
|
15
|
+
if (abcLine.staffGroup.w > maxWidth) maxWidth = abcLine.staffGroup.w;
|
|
17
16
|
}
|
|
17
|
+
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
19
|
+
// Layout the beams and add the stems to the beamed notes.
|
|
20
|
+
for (i = 0; i < abctune.lines.length; i++) {
|
|
21
|
+
abcLine = abctune.lines[i];
|
|
22
|
+
if (abcLine.staffGroup && abcLine.staffGroup.voices) {
|
|
23
|
+
for (var j = 0; j < abcLine.staffGroup.voices.length; j++)
|
|
24
|
+
layoutVoice(abcLine.staffGroup.voices[j]);
|
|
25
|
+
setUpperAndLowerElements(renderer, abcLine.staffGroup);
|
|
27
26
|
}
|
|
27
|
+
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
29
|
+
// Set the staff spacing
|
|
30
|
+
// TODO-PER: we should have been able to do this by the time we called setUpperAndLowerElements, but for some reason the "bottom" element seems to be set as a side effect of setting the X spacing.
|
|
31
|
+
for (i = 0; i < abctune.lines.length; i++) {
|
|
32
|
+
abcLine = abctune.lines[i];
|
|
33
|
+
if (abcLine.staffGroup) {
|
|
34
|
+
abcLine.staffGroup.setHeight();
|
|
36
35
|
}
|
|
37
|
-
return maxWidth;
|
|
38
36
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
37
|
+
return maxWidth;
|
|
38
|
+
}
|
|
39
|
+
// Do the x-axis positioning for a single line (a group of related staffs)
|
|
40
|
+
var setXSpacing = function (renderer, width, space, staffGroup, formatting, isLastLine, debug) {
|
|
41
|
+
var leftEdge = getLeftEdgeOfStaff(renderer, staffGroup.getTextSize, staffGroup.voices, staffGroup.brace, staffGroup.bracket);
|
|
42
|
+
var newspace = space;
|
|
43
|
+
for (var it = 0; it < 8; it++) { // TODO-PER: shouldn't need multiple passes, but each pass gets it closer to the right spacing. (Only affects long lines: normal lines break out of this loop quickly.)
|
|
44
|
+
var ret = layoutStaffGroup(newspace, renderer, debug, staffGroup, leftEdge);
|
|
45
|
+
newspace = calcHorizontalSpacing(isLastLine, formatting.stretchlast, width + renderer.padding.left, staffGroup.w, newspace, ret.spacingUnits, ret.minSpace, renderer.padding.left + renderer.padding.right);
|
|
46
|
+
if (debug)
|
|
47
|
+
console.log("setXSpace", it, staffGroup.w, newspace, staffGroup.minspace);
|
|
48
|
+
if (newspace === null) break;
|
|
49
|
+
}
|
|
50
|
+
centerWholeRests(staffGroup.voices);
|
|
51
|
+
};
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
53
|
+
function calcHorizontalSpacing(isLastLine, stretchLast, targetWidth, lineWidth, spacing, spacingUnits, minSpace, padding) {
|
|
54
|
+
if (isLastLine) {
|
|
55
|
+
if (stretchLast === undefined) {
|
|
56
|
+
if (lineWidth / targetWidth < 0.66) return null; // keep this for backward compatibility. The break isn't quite the same for some reason.
|
|
57
|
+
} else {
|
|
58
|
+
// "Stretch the last music line of a tune when it lacks less than the float fraction of the page width."
|
|
59
|
+
var lack = 1 - (lineWidth + padding) / targetWidth;
|
|
60
|
+
var stretch = lack < stretchLast;
|
|
61
|
+
if (!stretch) return null; // don't stretch last line too much
|
|
63
62
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return spacing;
|
|
63
|
+
}
|
|
64
|
+
if (Math.abs(targetWidth - lineWidth) < 2) return null; // if we are already near the target width, we're done.
|
|
65
|
+
var relSpace = spacingUnits * spacing;
|
|
66
|
+
var constSpace = lineWidth - relSpace;
|
|
67
|
+
if (spacingUnits > 0) {
|
|
68
|
+
spacing = (targetWidth - constSpace) / spacingUnits;
|
|
69
|
+
if (spacing * minSpace > 50) {
|
|
70
|
+
spacing = 50 / minSpace;
|
|
73
71
|
}
|
|
74
|
-
return
|
|
72
|
+
return spacing;
|
|
75
73
|
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
77
|
+
function centerWholeRests(voices) {
|
|
78
|
+
// whole rests are a special case: if they are by themselves in a measure, then they should be centered.
|
|
79
|
+
// (If they are not by themselves, that is probably a user error, but we'll just center it between the two items to either side of it.)
|
|
80
|
+
for (var i = 0; i < voices.length; i++) {
|
|
81
|
+
var voice = voices[i];
|
|
82
|
+
// Look through all of the elements except for the first and last. If the whole note appears there then there isn't anything to center it between anyway.
|
|
83
|
+
for (var j = 1; j < voice.children.length - 1; j++) {
|
|
84
|
+
var absElem = voice.children[j];
|
|
85
|
+
if (absElem.abcelem.rest && (absElem.abcelem.rest.type === 'whole' || absElem.abcelem.rest.type === 'multimeasure')) {
|
|
86
|
+
var before = voice.children[j - 1];
|
|
87
|
+
var after = voice.children[j + 1];
|
|
88
|
+
absElem.center(before, after);
|
|
90
89
|
}
|
|
91
90
|
}
|
|
92
91
|
}
|
|
92
|
+
}
|
|
93
93
|
|
|
94
94
|
module.exports = layout;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var spacing = require('../
|
|
1
|
+
var spacing = require('../helpers/spacing');
|
|
2
2
|
|
|
3
|
-
var setUpperAndLowerElements = function(renderer, staffGroup) {
|
|
3
|
+
var setUpperAndLowerElements = function (renderer, staffGroup) {
|
|
4
4
|
// Each staff already has the top and bottom set, now we see if there are elements that are always on top and bottom, and resolve their pitch.
|
|
5
5
|
// Also, get the overall height of all the staves in this group.
|
|
6
6
|
var lastStaffBottom;
|
|
@@ -49,7 +49,7 @@ var setUpperAndLowerElements = function(renderer, staffGroup) {
|
|
|
49
49
|
incTop(staff, positionY, 'tempoHeightAbove');
|
|
50
50
|
|
|
51
51
|
if (staff.specialY.lyricHeightBelow) {
|
|
52
|
-
staff.specialY.lyricHeightBelow += renderer.spacing.vocal/spacing.STEP;
|
|
52
|
+
staff.specialY.lyricHeightBelow += renderer.spacing.vocal / spacing.STEP;
|
|
53
53
|
positionY.lyricHeightBelow = staff.bottom;
|
|
54
54
|
staff.bottom -= (staff.specialY.lyricHeightBelow + margin);
|
|
55
55
|
}
|
|
@@ -82,7 +82,7 @@ var setUpperAndLowerElements = function(renderer, staffGroup) {
|
|
|
82
82
|
if (lastStaffBottom !== undefined) {
|
|
83
83
|
var thisStaffTop = staff.top - 10;
|
|
84
84
|
var forcedSpacingBetween = lastStaffBottom + thisStaffTop;
|
|
85
|
-
var minSpacingInPitches = renderer.spacing.systemStaffSeparation/spacing.STEP;
|
|
85
|
+
var minSpacingInPitches = renderer.spacing.systemStaffSeparation / spacing.STEP;
|
|
86
86
|
var addedSpace = minSpacingInPitches - forcedSpacingBetween;
|
|
87
87
|
if (addedSpace > 0)
|
|
88
88
|
staff.top += addedSpace;
|
|
@@ -195,7 +195,7 @@ function setUpperAndLowerTempoElement(positionY, element) {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
function setUpperAndLowerRelativeElements(positionY, element, renderSpacing) {
|
|
198
|
-
switch(element.type) {
|
|
198
|
+
switch (element.type) {
|
|
199
199
|
case "part":
|
|
200
200
|
element.top = positionY.partHeightAbove + element.height;
|
|
201
201
|
element.bottom = positionY.partHeightAbove;
|
|
@@ -215,9 +215,9 @@ function setUpperAndLowerRelativeElements(positionY, element, renderSpacing) {
|
|
|
215
215
|
element.top = positionY.lyricHeightAbove;
|
|
216
216
|
element.bottom = positionY.lyricHeightAbove;
|
|
217
217
|
} else {
|
|
218
|
-
element.top = positionY.lyricHeightBelow + renderSpacing.vocal/spacing.STEP;
|
|
219
|
-
element.bottom = positionY.lyricHeightBelow + renderSpacing.vocal/spacing.STEP;
|
|
220
|
-
element.pitch -= renderSpacing.vocal/spacing.STEP;
|
|
218
|
+
element.top = positionY.lyricHeightBelow + renderSpacing.vocal / spacing.STEP;
|
|
219
|
+
element.bottom = positionY.lyricHeightBelow + renderSpacing.vocal / spacing.STEP;
|
|
220
|
+
element.pitch -= renderSpacing.vocal / spacing.STEP;
|
|
221
221
|
}
|
|
222
222
|
break;
|
|
223
223
|
case "debug":
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var layoutVoiceElements = require('./
|
|
1
|
+
var layoutVoiceElements = require('./voice-elements');
|
|
2
2
|
|
|
3
3
|
function checkLastBarX(voices) {
|
|
4
4
|
var maxX = 0;
|
|
@@ -19,35 +19,35 @@ function checkLastBarX(voices) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
var layoutStaffGroup = function(spacing, renderer, debug, staffGroup, leftEdge) {
|
|
22
|
+
var layoutStaffGroup = function (spacing, renderer, debug, staffGroup, leftEdge) {
|
|
23
23
|
var epsilon = 0.0000001; // Fudging for inexactness of floating point math.
|
|
24
24
|
var spacingunits = 0; // number of times we will have ended up using the spacing distance (as opposed to fixed width distances)
|
|
25
25
|
var minspace = 1000; // a big number to start off with - used to find out what the smallest space between two notes is -- GD 2014.1.7
|
|
26
26
|
|
|
27
27
|
var x = leftEdge;
|
|
28
|
-
staffGroup.startx=x;
|
|
28
|
+
staffGroup.startx = x;
|
|
29
29
|
var i;
|
|
30
30
|
|
|
31
31
|
var currentduration = 0;
|
|
32
32
|
if (debug) console.log("init layout", spacing);
|
|
33
|
-
for (i=0;i<staffGroup.voices.length;i++) {
|
|
33
|
+
for (i = 0; i < staffGroup.voices.length; i++) {
|
|
34
34
|
layoutVoiceElements.beginLayout(x, staffGroup.voices[i]);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
var spacingunit = 0; // number of spacingunits coming from the previously laid out element to this one
|
|
38
38
|
while (!finished(staffGroup.voices)) {
|
|
39
39
|
// find first duration level to be laid out among candidates across voices
|
|
40
|
-
currentduration= null; // candidate smallest duration level
|
|
41
|
-
for (i=0;i<staffGroup.voices.length;i++) {
|
|
42
|
-
if (!layoutVoiceElements.layoutEnded(staffGroup.voices[i]) && (!currentduration || getDurationIndex(staffGroup.voices[i])<currentduration))
|
|
43
|
-
currentduration=getDurationIndex(staffGroup.voices[i]);
|
|
40
|
+
currentduration = null; // candidate smallest duration level
|
|
41
|
+
for (i = 0; i < staffGroup.voices.length; i++) {
|
|
42
|
+
if (!layoutVoiceElements.layoutEnded(staffGroup.voices[i]) && (!currentduration || getDurationIndex(staffGroup.voices[i]) < currentduration))
|
|
43
|
+
currentduration = getDurationIndex(staffGroup.voices[i]);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
// isolate voices at current duration level
|
|
48
48
|
var currentvoices = [];
|
|
49
49
|
var othervoices = [];
|
|
50
|
-
for (i=0;i<staffGroup.voices.length;i++) {
|
|
50
|
+
for (i = 0; i < staffGroup.voices.length; i++) {
|
|
51
51
|
var durationIndex = getDurationIndex(staffGroup.voices[i]);
|
|
52
52
|
// PER: Because of the inexactness of JS floating point math, we just get close.
|
|
53
53
|
if (durationIndex - currentduration > epsilon) {
|
|
@@ -62,44 +62,44 @@ var layoutStaffGroup = function(spacing, renderer, debug, staffGroup, leftEdge)
|
|
|
62
62
|
// among the current duration level find the one which needs starting furthest right
|
|
63
63
|
spacingunit = 0; // number of spacingunits coming from the previously laid out element to this one
|
|
64
64
|
var spacingduration = 0;
|
|
65
|
-
for (i=0;i<currentvoices.length;i++) {
|
|
65
|
+
for (i = 0; i < currentvoices.length; i++) {
|
|
66
66
|
//console.log("greatest spacing unit", x, layoutVoiceElements.getNextX(currentvoices[i]), layoutVoiceElements.getSpacingUnits(currentvoices[i]), currentvoices[i].spacingduration);
|
|
67
|
-
if (layoutVoiceElements.getNextX(currentvoices[i])>x) {
|
|
68
|
-
x=layoutVoiceElements.getNextX(currentvoices[i]);
|
|
69
|
-
spacingunit=layoutVoiceElements.getSpacingUnits(currentvoices[i]);
|
|
67
|
+
if (layoutVoiceElements.getNextX(currentvoices[i]) > x) {
|
|
68
|
+
x = layoutVoiceElements.getNextX(currentvoices[i]);
|
|
69
|
+
spacingunit = layoutVoiceElements.getSpacingUnits(currentvoices[i]);
|
|
70
70
|
spacingduration = currentvoices[i].spacingduration;
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
spacingunits+=spacingunit;
|
|
74
|
-
minspace = Math.min(minspace,spacingunit);
|
|
75
|
-
if (debug) console.log("currentduration: ",currentduration, spacingunits, minspace);
|
|
73
|
+
spacingunits += spacingunit;
|
|
74
|
+
minspace = Math.min(minspace, spacingunit);
|
|
75
|
+
if (debug) console.log("currentduration: ", currentduration, spacingunits, minspace);
|
|
76
76
|
|
|
77
77
|
var lastTopVoice = undefined;
|
|
78
|
-
for (i=0;i<currentvoices.length;i++) {
|
|
78
|
+
for (i = 0; i < currentvoices.length; i++) {
|
|
79
79
|
var v = currentvoices[i];
|
|
80
80
|
if (v.voicenumber === 0)
|
|
81
81
|
lastTopVoice = i;
|
|
82
82
|
var topVoice = (lastTopVoice !== undefined && currentvoices[lastTopVoice].voicenumber !== v.voicenumber) ? currentvoices[lastTopVoice] : undefined;
|
|
83
83
|
if (!isSameStaff(v, topVoice))
|
|
84
84
|
topVoice = undefined;
|
|
85
|
-
var voicechildx = layoutVoiceElements.layoutOneItem(x,spacing, v, renderer.minPadding, topVoice);
|
|
86
|
-
var dx = voicechildx-x;
|
|
87
|
-
if (dx>0) {
|
|
85
|
+
var voicechildx = layoutVoiceElements.layoutOneItem(x, spacing, v, renderer.minPadding, topVoice);
|
|
86
|
+
var dx = voicechildx - x;
|
|
87
|
+
if (dx > 0) {
|
|
88
88
|
x = voicechildx; //update x
|
|
89
|
-
for (var j=0;j<i;j++) { // shift over all previously laid out elements
|
|
89
|
+
for (var j = 0; j < i; j++) { // shift over all previously laid out elements
|
|
90
90
|
layoutVoiceElements.shiftRight(dx, currentvoices[j]);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// remove the value of already counted spacing units in other voices (e.g. if a voice had planned to use up 5 spacing units but is not in line to be laid out at this duration level - where we've used 2 spacing units - then we must use up 3 spacing units, not 5)
|
|
96
|
-
for (i=0;i<othervoices.length;i++) {
|
|
97
|
-
othervoices[i].spacingduration-=spacingduration;
|
|
98
|
-
layoutVoiceElements.updateNextX(x,spacing, othervoices[i]); // adjust other voices expectations
|
|
96
|
+
for (i = 0; i < othervoices.length; i++) {
|
|
97
|
+
othervoices[i].spacingduration -= spacingduration;
|
|
98
|
+
layoutVoiceElements.updateNextX(x, spacing, othervoices[i]); // adjust other voices expectations
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// update indexes of currently laid out elems
|
|
102
|
-
for (i=0;i<currentvoices.length;i++) {
|
|
102
|
+
for (i = 0; i < currentvoices.length; i++) {
|
|
103
103
|
var voice = currentvoices[i];
|
|
104
104
|
layoutVoiceElements.updateIndices(voice);
|
|
105
105
|
}
|
|
@@ -107,17 +107,17 @@ var layoutStaffGroup = function(spacing, renderer, debug, staffGroup, leftEdge)
|
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
// find the greatest remaining x as a base for the width
|
|
110
|
-
for (i=0;i<staffGroup.voices.length;i++) {
|
|
111
|
-
if (layoutVoiceElements.getNextX(staffGroup.voices[i])>x) {
|
|
112
|
-
x=layoutVoiceElements.getNextX(staffGroup.voices[i]);
|
|
113
|
-
spacingunit=layoutVoiceElements.getSpacingUnits(staffGroup.voices[i]);
|
|
110
|
+
for (i = 0; i < staffGroup.voices.length; i++) {
|
|
111
|
+
if (layoutVoiceElements.getNextX(staffGroup.voices[i]) > x) {
|
|
112
|
+
x = layoutVoiceElements.getNextX(staffGroup.voices[i]);
|
|
113
|
+
spacingunit = layoutVoiceElements.getSpacingUnits(staffGroup.voices[i]);
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
// adjust lastBar when needed (multi staves)
|
|
118
118
|
checkLastBarX(staffGroup.voices);
|
|
119
119
|
//console.log("greatest remaining",spacingunit,x);
|
|
120
|
-
spacingunits+=spacingunit;
|
|
120
|
+
spacingunits += spacingunit;
|
|
121
121
|
staffGroup.setWidth(x);
|
|
122
122
|
|
|
123
123
|
return { spacingUnits: spacingunits, minSpace: minspace };
|
|
@@ -125,14 +125,14 @@ var layoutStaffGroup = function(spacing, renderer, debug, staffGroup, leftEdge)
|
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
function finished(voices) {
|
|
128
|
-
for (var i=0;i<voices.length;i++) {
|
|
128
|
+
for (var i = 0; i < voices.length; i++) {
|
|
129
129
|
if (!layoutVoiceElements.layoutEnded(voices[i])) return false;
|
|
130
130
|
}
|
|
131
131
|
return true;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
function getDurationIndex(element) {
|
|
135
|
-
return element.durationindex - (element.children[element.i] && (element.children[element.i].duration>0)?0:0.0000005); // if the ith element doesn't have a duration (is not a note), its duration index is fractionally before. This enables CLEF KEYSIG TIMESIG PART, etc. to be laid out before we get to the first note of other voices
|
|
135
|
+
return element.durationindex - (element.children[element.i] && (element.children[element.i].duration > 0) ? 0 : 0.0000005); // if the ith element doesn't have a duration (is not a note), its duration index is fractionally before. This enables CLEF KEYSIG TIMESIG PART, etc. to be laid out before we get to the first note of other voices
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
function isSameStaff(voice1, voice2) {
|