abcjs 6.2.1 → 6.2.3
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/README.md +3 -0
- package/RELEASE.md +54 -0
- package/abc2xml_239/abc2xml.html +769 -0
- package/abc2xml_239/abc2xml.py +2248 -0
- package/abc2xml_239/abc2xml_changelog.html +124 -0
- package/abc2xml_239/lazy-river.abc +26 -0
- package/abc2xml_239/lazy-river.xml +3698 -0
- package/abc2xml_239/mean-to-me.abc +22 -0
- package/abc2xml_239/mean-to-me.xml +2954 -0
- package/abc2xml_239/pyparsing.py +3672 -0
- package/abc2xml_239/pyparsing.pyc +0 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +205 -91
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/package.json +1 -1
- package/src/api/abc_tablatures.js +5 -0
- package/src/api/abc_tunebook_svg.js +5 -3
- package/src/parse/abc_parse_music.js +18 -53
- package/src/parse/abc_parse_settings.js +165 -0
- package/src/synth/create-synth.js +4 -0
- package/src/synth/place-note.js +6 -0
- package/src/synth/play-event.js +7 -5
- package/src/tablatures/tab-absolute-elements.js +3 -2
- package/src/tablatures/tab-renderer.js +3 -1
- package/src/test/abc_parser_lint.js +12 -12
- package/src/write/creation/abstract-engraver.js +6 -0
- package/src/write/creation/decoration.js +2 -0
- package/src/write/creation/glyphs.js +1 -1
- package/src/write/draw/glissando.js +1 -0
- package/src/write/draw/print-line.js +16 -7
- package/src/write/draw/print-stem.js +16 -8
- package/src/write/draw/set-paper-size.js +1 -1
- package/src/write/draw/tie.js +9 -1
- package/src/write/engraver-controller.js +12 -4
- package/src/write/interactive/selection.js +6 -0
- package/src/write/layout/layout.js +33 -3
- package/src/write/svg.js +10 -0
- package/types/index.d.ts +29 -21
- package/version.js +1 -1
package/package.json
CHANGED
|
@@ -13,6 +13,8 @@ var GuitarTablature = require('../tablatures/instruments/guitar/tab-guitar');
|
|
|
13
13
|
// Existing tab classes
|
|
14
14
|
var pluginTab = {
|
|
15
15
|
'violin': 'ViolinTab',
|
|
16
|
+
'fiddle': 'ViolinTab',
|
|
17
|
+
'mandolin': 'ViolinTab',
|
|
16
18
|
'guitar': 'GuitarTab'
|
|
17
19
|
};
|
|
18
20
|
|
|
@@ -82,6 +84,9 @@ var abcTablatures = {
|
|
|
82
84
|
// plugin.init(tune, tuneNumber, args, ii);
|
|
83
85
|
returned.push(pluginInstance);
|
|
84
86
|
nbPlugins++;
|
|
87
|
+
} else if (instrument === '') {
|
|
88
|
+
// create a placeholder - there is no tab for this staff
|
|
89
|
+
returned.push(null)
|
|
85
90
|
} else {
|
|
86
91
|
// unknown tab plugin
|
|
87
92
|
//this.emit_error('Undefined tablature plugin: ' + tabName)
|
|
@@ -117,12 +117,12 @@ var renderAbc = function(output, abc, parserParams, engraverParams, renderParams
|
|
|
117
117
|
div.setAttribute("style", "visibility: hidden;");
|
|
118
118
|
document.body.appendChild(div);
|
|
119
119
|
}
|
|
120
|
-
if (params.afterParsing)
|
|
121
|
-
params.afterParsing(tune, tuneNumber, abcString);
|
|
122
120
|
if (!removeDiv && params.wrap && params.staffwidth) {
|
|
123
|
-
|
|
121
|
+
tune = doLineWrapping(div, tune, tuneNumber, abcString, params);
|
|
124
122
|
return tune;
|
|
125
123
|
}
|
|
124
|
+
if (params.afterParsing)
|
|
125
|
+
params.afterParsing(tune, tuneNumber, abcString);
|
|
126
126
|
renderOne(div, tune, params, tuneNumber, 0);
|
|
127
127
|
if (removeDiv)
|
|
128
128
|
div.parentNode.removeChild(div);
|
|
@@ -145,6 +145,8 @@ function doLineWrapping(div, tune, tuneNumber, abcString, params) {
|
|
|
145
145
|
if (warnings)
|
|
146
146
|
tune.warnings = warnings;
|
|
147
147
|
}
|
|
148
|
+
if (params.afterParsing)
|
|
149
|
+
params.afterParsing(tune, tuneNumber, abcString);
|
|
148
150
|
renderOne(div, tune, ret.revisedParams, tuneNumber, 0);
|
|
149
151
|
tune.explanation = ret.explanation;
|
|
150
152
|
return tune;
|
|
@@ -9,6 +9,20 @@ var tune;
|
|
|
9
9
|
var tuneBuilder;
|
|
10
10
|
var header;
|
|
11
11
|
|
|
12
|
+
var {
|
|
13
|
+
legalAccents,
|
|
14
|
+
volumeDecorations,
|
|
15
|
+
dynamicDecorations,
|
|
16
|
+
accentPseudonyms,
|
|
17
|
+
accentDynamicPseudonyms,
|
|
18
|
+
nonDecorations,
|
|
19
|
+
durations,
|
|
20
|
+
pitches,
|
|
21
|
+
rests,
|
|
22
|
+
accMap,
|
|
23
|
+
tripletQ
|
|
24
|
+
} = require('./abc_parse_settings')
|
|
25
|
+
|
|
12
26
|
var MusicParser = function(_tokenizer, _warn, _multilineVars, _tune, _tuneBuilder, _header) {
|
|
13
27
|
tokenizer = _tokenizer;
|
|
14
28
|
warn = _warn;
|
|
@@ -76,7 +90,6 @@ var MusicParser = function(_tokenizer, _warn, _multilineVars, _tune, _tuneBuilde
|
|
|
76
90
|
// double-quote: chord symbol
|
|
77
91
|
// less-than, greater-than, slash: duration
|
|
78
92
|
// back-tick, space, tab: space
|
|
79
|
-
var nonDecorations = "ABCDEFGabcdefgxyzZ[]|^_{"; // use this to prescreen so we don't have to look for a decoration at every note.
|
|
80
93
|
|
|
81
94
|
var isInTie = function(multilineVars, overlayLevel, el) {
|
|
82
95
|
if (multilineVars.inTie[overlayLevel] === undefined)
|
|
@@ -540,15 +553,8 @@ MusicParser.prototype.parseMusic = function(line) {
|
|
|
540
553
|
// Create a warning if this is not a displayable duration.
|
|
541
554
|
// The first item on a line is a regular note value, each item after that represents a dot placed after the previous note.
|
|
542
555
|
// Only durations less than a whole note are tested because whole note durations have some tricky rules.
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
0.25, 0.375, 0.4375, 0.46875, 0.484375, 0.4921875,
|
|
546
|
-
0.125, 0.1875, 0.21875, 0.234375, 0.2421875, 0.24609375,
|
|
547
|
-
0.0625, 0.09375, 0.109375, 0.1171875, 0.12109375, 0.123046875,
|
|
548
|
-
0.03125, 0.046875, 0.0546875, 0.05859375, 0.060546875, 0.0615234375,
|
|
549
|
-
0.015625, 0.0234375, 0.02734375, 0.029296875, 0.0302734375, 0.03076171875,
|
|
550
|
-
];
|
|
551
|
-
if (el.duration < 1 && durations.indexOf(el.duration) === -1 && el.duration !== 0) {
|
|
556
|
+
|
|
557
|
+
if (el.duration < 1 && durations.indexOf(el.duration) === -1 && el.duration !== 0) {
|
|
552
558
|
if (!el.rest || el.rest.type !== 'spacer')
|
|
553
559
|
warn("Duration not representable: " + line.substring(startI, i), line, i);
|
|
554
560
|
}
|
|
@@ -717,35 +723,8 @@ function durationOfMeasure(multilineVars) {
|
|
|
717
723
|
return parseInt(meter.value[0].num, 10) / parseInt(meter.value[0].den, 10);
|
|
718
724
|
}
|
|
719
725
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
"fermata", "invertedfermata", "tenuto", "0", "1", "2", "3", "4", "5", "+", "wedge",
|
|
723
|
-
"open", "thumb", "snap", "turn", "roll", "breath", "shortphrase", "mediumphrase", "longphrase",
|
|
724
|
-
"segno", "coda", "D.S.", "D.C.", "fine", "beambr1", "beambr2",
|
|
725
|
-
"slide", "marcato",
|
|
726
|
-
"upbow", "downbow", "/", "//", "///", "////", "trem1", "trem2", "trem3", "trem4",
|
|
727
|
-
"turnx", "invertedturn", "invertedturnx", "trill(", "trill)", "arpeggio", "xstem", "mark", "umarcato",
|
|
728
|
-
"style=normal", "style=harmonic", "style=rhythm", "style=x", "style=triangle", "D.C.alcoda", "D.C.alfine", "D.S.alcoda", "D.S.alfine", "editorial", "courtesy"
|
|
729
|
-
];
|
|
730
|
-
|
|
731
|
-
var volumeDecorations = [
|
|
732
|
-
"p", "pp", "f", "ff", "mf", "mp", "ppp", "pppp", "fff", "ffff", "sfz"
|
|
733
|
-
];
|
|
734
|
-
|
|
735
|
-
var dynamicDecorations = [
|
|
736
|
-
"crescendo(", "crescendo)", "diminuendo(", "diminuendo)", "glissando(", "glissando)"
|
|
737
|
-
];
|
|
738
|
-
|
|
739
|
-
var accentPseudonyms = [
|
|
740
|
-
["<", "accent"], [">", "accent"], ["tr", "trill"],
|
|
741
|
-
["plus", "+"], [ "emphasis", "accent"],
|
|
742
|
-
[ "^", "umarcato" ], [ "marcato", "umarcato" ]
|
|
743
|
-
];
|
|
744
|
-
|
|
745
|
-
var accentDynamicPseudonyms = [
|
|
746
|
-
["<(", "crescendo("], ["<)", "crescendo)"],
|
|
747
|
-
[">(", "diminuendo("], [">)", "diminuendo)"]
|
|
748
|
-
];
|
|
726
|
+
|
|
727
|
+
|
|
749
728
|
|
|
750
729
|
var letter_to_accent = function(line, i) {
|
|
751
730
|
var macro = multilineVars.macros[line[i]];
|
|
@@ -877,17 +856,6 @@ var letter_to_bar = function(line, curr_pos) {
|
|
|
877
856
|
return [ret.len+retRep.len, ret.token, retRep.token];
|
|
878
857
|
};
|
|
879
858
|
|
|
880
|
-
var tripletQ = {
|
|
881
|
-
2: 3,
|
|
882
|
-
3: 2,
|
|
883
|
-
4: 3,
|
|
884
|
-
5: 2, // TODO-PER: not handling 6/8 rhythm yet
|
|
885
|
-
6: 2,
|
|
886
|
-
7: 2, // TODO-PER: not handling 6/8 rhythm yet
|
|
887
|
-
8: 3,
|
|
888
|
-
9: 2 // TODO-PER: not handling 6/8 rhythm yet
|
|
889
|
-
};
|
|
890
|
-
|
|
891
859
|
var letter_to_open_slurs_and_triplets = function(line, i) {
|
|
892
860
|
// consume spaces, and look for all the open parens. If there is a number after the open paren,
|
|
893
861
|
// that is a triplet. Otherwise that is a slur. Collect all the slurs and the first triplet.
|
|
@@ -1050,9 +1018,6 @@ var addEndBeam = function(el) {
|
|
|
1050
1018
|
return el;
|
|
1051
1019
|
};
|
|
1052
1020
|
|
|
1053
|
-
var pitches = {A: 5, B: 6, C: 0, D: 1, E: 2, F: 3, G: 4, a: 12, b: 13, c: 7, d: 8, e: 9, f: 10, g: 11};
|
|
1054
|
-
var rests = {x: 'invisible', X: 'invisible-multimeasure', y: 'spacer', z: 'rest', Z: 'multimeasure' };
|
|
1055
|
-
var accMap = { 'dblflat': '__', 'flat': '_', 'natural': '=', 'sharp': '^', 'dblsharp': '^^', 'quarterflat': '_/', 'quartersharp': '^/'};
|
|
1056
1021
|
var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
|
|
1057
1022
|
//var el = { startChar: index };
|
|
1058
1023
|
var isComplete = function(state) {
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
module.exports.legalAccents = [
|
|
2
|
+
'trill',
|
|
3
|
+
'lowermordent',
|
|
4
|
+
'uppermordent',
|
|
5
|
+
'mordent',
|
|
6
|
+
'pralltriller',
|
|
7
|
+
'accent',
|
|
8
|
+
'fermata',
|
|
9
|
+
'invertedfermata',
|
|
10
|
+
'tenuto',
|
|
11
|
+
'0',
|
|
12
|
+
'1',
|
|
13
|
+
'2',
|
|
14
|
+
'3',
|
|
15
|
+
'4',
|
|
16
|
+
'5',
|
|
17
|
+
'+',
|
|
18
|
+
'wedge',
|
|
19
|
+
'open',
|
|
20
|
+
'thumb',
|
|
21
|
+
'snap',
|
|
22
|
+
'turn',
|
|
23
|
+
'roll',
|
|
24
|
+
'breath',
|
|
25
|
+
'shortphrase',
|
|
26
|
+
'mediumphrase',
|
|
27
|
+
'longphrase',
|
|
28
|
+
'segno',
|
|
29
|
+
'coda',
|
|
30
|
+
'D.S.',
|
|
31
|
+
'D.C.',
|
|
32
|
+
'fine',
|
|
33
|
+
'beambr1',
|
|
34
|
+
'beambr2',
|
|
35
|
+
'slide',
|
|
36
|
+
'marcato',
|
|
37
|
+
'upbow',
|
|
38
|
+
'downbow',
|
|
39
|
+
'/',
|
|
40
|
+
'//',
|
|
41
|
+
'///',
|
|
42
|
+
'////',
|
|
43
|
+
'trem1',
|
|
44
|
+
'trem2',
|
|
45
|
+
'trem3',
|
|
46
|
+
'trem4',
|
|
47
|
+
'turnx',
|
|
48
|
+
'invertedturn',
|
|
49
|
+
'invertedturnx',
|
|
50
|
+
'trill(',
|
|
51
|
+
'trill)',
|
|
52
|
+
'arpeggio',
|
|
53
|
+
'xstem',
|
|
54
|
+
'mark',
|
|
55
|
+
'umarcato',
|
|
56
|
+
'style=normal',
|
|
57
|
+
'style=harmonic',
|
|
58
|
+
'style=rhythm',
|
|
59
|
+
'style=x',
|
|
60
|
+
'style=triangle',
|
|
61
|
+
'D.C.alcoda',
|
|
62
|
+
'D.C.alfine',
|
|
63
|
+
'D.S.alcoda',
|
|
64
|
+
'D.S.alfine',
|
|
65
|
+
'editorial',
|
|
66
|
+
'courtesy'
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
module.exports.volumeDecorations = [
|
|
70
|
+
'p',
|
|
71
|
+
'pp',
|
|
72
|
+
'f',
|
|
73
|
+
'ff',
|
|
74
|
+
'mf',
|
|
75
|
+
'mp',
|
|
76
|
+
'ppp',
|
|
77
|
+
'pppp',
|
|
78
|
+
'fff',
|
|
79
|
+
'ffff',
|
|
80
|
+
'sfz'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
module.exports.dynamicDecorations = [
|
|
84
|
+
'crescendo(',
|
|
85
|
+
'crescendo)',
|
|
86
|
+
'diminuendo(',
|
|
87
|
+
'diminuendo)',
|
|
88
|
+
'glissando(',
|
|
89
|
+
'glissando)',
|
|
90
|
+
'~(',
|
|
91
|
+
'~)'
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
module.exports.accentPseudonyms = [
|
|
95
|
+
['<', 'accent'],
|
|
96
|
+
['>', 'accent'],
|
|
97
|
+
['tr', 'trill'],
|
|
98
|
+
['plus', '+'],
|
|
99
|
+
['emphasis', 'accent'],
|
|
100
|
+
['^', 'umarcato'],
|
|
101
|
+
['marcato', 'umarcato']
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
module.exports.accentDynamicPseudonyms = [
|
|
105
|
+
['<(', 'crescendo('],
|
|
106
|
+
['<)', 'crescendo)'],
|
|
107
|
+
['>(', 'diminuendo('],
|
|
108
|
+
['>)', 'diminuendo)']
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
module.exports.nonDecorations = 'ABCDEFGabcdefgxyzZ[]|^_{'; // use this to prescreen so we don't have to look for a decoration at every note.
|
|
112
|
+
|
|
113
|
+
module.exports.durations = [
|
|
114
|
+
0.5, 0.75, 0.875, 0.9375, 0.96875, 0.984375, 0.25, 0.375, 0.4375, 0.46875,
|
|
115
|
+
0.484375, 0.4921875, 0.125, 0.1875, 0.21875, 0.234375, 0.2421875, 0.24609375,
|
|
116
|
+
0.0625, 0.09375, 0.109375, 0.1171875, 0.12109375, 0.123046875, 0.03125,
|
|
117
|
+
0.046875, 0.0546875, 0.05859375, 0.060546875, 0.0615234375, 0.015625,
|
|
118
|
+
0.0234375, 0.02734375, 0.029296875, 0.0302734375, 0.03076171875
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
module.exports.pitches = {
|
|
122
|
+
A: 5,
|
|
123
|
+
B: 6,
|
|
124
|
+
C: 0,
|
|
125
|
+
D: 1,
|
|
126
|
+
E: 2,
|
|
127
|
+
F: 3,
|
|
128
|
+
G: 4,
|
|
129
|
+
a: 12,
|
|
130
|
+
b: 13,
|
|
131
|
+
c: 7,
|
|
132
|
+
d: 8,
|
|
133
|
+
e: 9,
|
|
134
|
+
f: 10,
|
|
135
|
+
g: 11
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
module.exports.rests = {
|
|
139
|
+
x: 'invisible',
|
|
140
|
+
X: 'invisible-multimeasure',
|
|
141
|
+
y: 'spacer',
|
|
142
|
+
z: 'rest',
|
|
143
|
+
Z: 'multimeasure'
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
module.exports.accMap = {
|
|
147
|
+
dblflat: '__',
|
|
148
|
+
flat: '_',
|
|
149
|
+
natural: '=',
|
|
150
|
+
sharp: '^',
|
|
151
|
+
dblsharp: '^^',
|
|
152
|
+
quarterflat: '_/',
|
|
153
|
+
quartersharp: '^/'
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
module.exports.tripletQ = {
|
|
157
|
+
2: 3,
|
|
158
|
+
3: 2,
|
|
159
|
+
4: 3,
|
|
160
|
+
5: 2, // TODO-PER: not handling 6/8 rhythm yet
|
|
161
|
+
6: 2,
|
|
162
|
+
7: 2, // TODO-PER: not handling 6/8 rhythm yet
|
|
163
|
+
8: 3,
|
|
164
|
+
9: 2 // TODO-PER: not handling 6/8 rhythm yet
|
|
165
|
+
};
|
package/src/synth/place-note.js
CHANGED
|
@@ -17,6 +17,12 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
|
|
|
17
17
|
len = 0.005; // Have some small audible length no matter how short the note is.
|
|
18
18
|
var offlineCtx = new OfflineAC(2,Math.floor((len+fadeTimeSec)*sampleRate),sampleRate);
|
|
19
19
|
var noteName = pitchToNoteName[sound.pitch];
|
|
20
|
+
if (!soundsCache[sound.instrument]) {
|
|
21
|
+
// It shouldn't happen that the entire instrument cache wasn't created, but this has been seen in practice, so guard against it.
|
|
22
|
+
if (debugCallback)
|
|
23
|
+
debugCallback('placeNote skipped (instrument empty): '+sound.instrument+':'+noteName)
|
|
24
|
+
return Promise.resolve();
|
|
25
|
+
}
|
|
20
26
|
var noteBufferPromise = soundsCache[sound.instrument][noteName];
|
|
21
27
|
|
|
22
28
|
if (!noteBufferPromise) {
|
package/src/synth/play-event.js
CHANGED
|
@@ -2,7 +2,7 @@ var SynthSequence = require('./synth-sequence');
|
|
|
2
2
|
var CreateSynth = require('./create-synth');
|
|
3
3
|
var activeAudioContext = require("./active-audio-context");
|
|
4
4
|
|
|
5
|
-
function playEvent(midiPitches, midiGracePitches, millisecondsPerMeasure) {
|
|
5
|
+
function playEvent(midiPitches, midiGracePitches, millisecondsPerMeasure, soundFontUrl, debugCallback) {
|
|
6
6
|
var sequence = new SynthSequence();
|
|
7
7
|
|
|
8
8
|
for (var i = 0; i < midiPitches.length; i++) {
|
|
@@ -21,18 +21,20 @@ function playEvent(midiPitches, midiGracePitches, millisecondsPerMeasure) {
|
|
|
21
21
|
var ac = activeAudioContext();
|
|
22
22
|
if (ac.state === "suspended") {
|
|
23
23
|
return ac.resume().then(function () {
|
|
24
|
-
return doPlay(sequence, millisecondsPerMeasure);
|
|
24
|
+
return doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback);
|
|
25
25
|
});
|
|
26
26
|
} else {
|
|
27
|
-
return doPlay(sequence, millisecondsPerMeasure);
|
|
27
|
+
return doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function doPlay(sequence, millisecondsPerMeasure) {
|
|
31
|
+
function doPlay(sequence, millisecondsPerMeasure, soundFontUrl, debugCallback) {
|
|
32
32
|
var buffer = new CreateSynth();
|
|
33
33
|
return buffer.init({
|
|
34
34
|
sequence: sequence,
|
|
35
|
-
millisecondsPerMeasure: millisecondsPerMeasure
|
|
35
|
+
millisecondsPerMeasure: millisecondsPerMeasure,
|
|
36
|
+
options: { soundFontUrl: soundFontUrl },
|
|
37
|
+
debugCallback: debugCallback,
|
|
36
38
|
}).then(function () {
|
|
37
39
|
return buffer.prime();
|
|
38
40
|
}).then(function () {
|
|
@@ -177,10 +177,11 @@ TabAbsoluteElements.prototype.build = function (plugin,
|
|
|
177
177
|
tabVoice,
|
|
178
178
|
voiceIndex,
|
|
179
179
|
staffIndex,
|
|
180
|
-
keySig
|
|
180
|
+
keySig,
|
|
181
|
+
tabVoiceIndex ) {
|
|
181
182
|
var staffSize = getInitialStaffSize(staffAbsolute);
|
|
182
183
|
var source = staffAbsolute[staffIndex+voiceIndex];
|
|
183
|
-
var dest = staffAbsolute[
|
|
184
|
+
var dest = staffAbsolute[tabVoiceIndex];
|
|
184
185
|
var tabPos = null;
|
|
185
186
|
var defNote = null;
|
|
186
187
|
if (source.children[0].abcelem.el_type != 'clef') {
|
|
@@ -227,15 +227,17 @@ TabRenderer.prototype.doLayout = function () {
|
|
|
227
227
|
this.tabStaff.voices = [];
|
|
228
228
|
for (var ii = 0; ii < nbVoices; ii++) {
|
|
229
229
|
var tabVoice = new VoiceElement(0, 0);
|
|
230
|
+
if (ii > 0) tabVoice.duplicate = true;
|
|
230
231
|
var nameHeight = buildTabName(this, tabVoice) / spacing.STEP;
|
|
231
232
|
nameHeight = Math.max(nameHeight, 1) // If there is no label for the tab line, then there needs to be a little padding
|
|
232
233
|
staffGroup.staffs[this.staffIndex].top += nameHeight;
|
|
233
234
|
staffGroup.height += nameHeight * spacing.STEP;
|
|
234
235
|
tabVoice.staff = staffGroupInfos;
|
|
236
|
+
var tabVoiceIndex = voices.length
|
|
235
237
|
voices.splice(voices.length, 0, tabVoice);
|
|
236
238
|
var keySig = checkVoiceKeySig(voices, ii + this.staffIndex);
|
|
237
239
|
this.tabStaff.voices[ii] = [];
|
|
238
|
-
this.absolutes.build(this.plugin, voices, this.tabStaff.voices[ii], ii , this.staffIndex ,keySig);
|
|
240
|
+
this.absolutes.build(this.plugin, voices, this.tabStaff.voices[ii], ii , this.staffIndex ,keySig, tabVoiceIndex);
|
|
239
241
|
}
|
|
240
242
|
linkStaffAndTabs(staffGroup.staffs); // crossreference tabs and staff
|
|
241
243
|
};
|
|
@@ -51,18 +51,18 @@
|
|
|
51
51
|
var parseCommon = require('../parse/abc_common');
|
|
52
52
|
var JSONSchema = require('./jsonschema-b4');
|
|
53
53
|
|
|
54
|
-
var
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
54
|
+
var { legalAccents } = require('../parse/abc_parse_settings');
|
|
55
|
+
|
|
56
|
+
var ParserLint = function () {
|
|
57
|
+
'use strict';
|
|
58
|
+
var decorationList = {
|
|
59
|
+
type: 'array',
|
|
60
|
+
optional: true,
|
|
61
|
+
items: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
Enum: legalAccents
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
66
|
|
|
67
67
|
var tempoProperties = {
|
|
68
68
|
duration: { type: "array", optional: true, output: "join", requires: [ 'bpm'], items: { type: "number"} },
|
|
@@ -747,6 +747,12 @@ AbstractEngraver.prototype.addNoteToAbcElement = function (abselem, elem, dot, s
|
|
|
747
747
|
else
|
|
748
748
|
p1 += 1;
|
|
749
749
|
}
|
|
750
|
+
if (noteHead && noteHead.c === 'noteheads.triangle.quarter') {
|
|
751
|
+
if (dir === 'down')
|
|
752
|
+
p2 -= 0.7;
|
|
753
|
+
else
|
|
754
|
+
p1 -= 1.2;
|
|
755
|
+
}
|
|
750
756
|
abselem.addRight(new RelativeElement(null, dx, 0, p1, { "type": "stem", "pitch2": p2, linewidth: width, bottom: p1 - 1 }));
|
|
751
757
|
//var RelativeElement = function RelativeElement(c, dx, w, pitch, opt) {
|
|
752
758
|
min = Math.min(p1, p2);
|
|
@@ -313,10 +313,12 @@ Decoration.prototype.dynamicDecoration = function (voice, decoration, abselem, p
|
|
|
313
313
|
crescendo = { start: this.startCrescendoX, stop: abselem };
|
|
314
314
|
this.startCrescendoX = undefined;
|
|
315
315
|
break;
|
|
316
|
+
case '~(':
|
|
316
317
|
case "glissando(":
|
|
317
318
|
this.startGlissandoX = abselem;
|
|
318
319
|
glissando = undefined;
|
|
319
320
|
break;
|
|
321
|
+
case '~)':
|
|
320
322
|
case "glissando)":
|
|
321
323
|
glissando = { start: this.startGlissandoX, stop: abselem };
|
|
322
324
|
this.startGlissandoX = undefined;
|
|
@@ -105,7 +105,7 @@ glyphs['noteheads.slash.quarter'] = { d: [['M', 9, -6], ['l', 0, 4], ['l', -9, 9
|
|
|
105
105
|
|
|
106
106
|
glyphs['noteheads.harmonic.quarter'] = { d: [['M', 3.63, -4.02], ['c', 0.09, -0.06, 0.18, -0.09, 0.24, -0.03], ['c', 0.03, 0.03, 0.87, 0.93, 1.83, 2.01], ['c', 1.50, 1.65, 1.80, 1.98, 1.80, 2.04], ['c', 0.00, 0.06, -0.30, 0.39, -1.80, 2.04], ['c', -0.96, 1.08, -1.80, 1.98, -1.83, 2.01], ['c', -0.06, 0.06, -0.15, 0.03, -0.24, -0.03], ['c', -0.12, -0.09, -3.54, -3.84, -3.60, -3.93], ['c', -0.03, -0.03, -0.03, -0.09, -0.03, -0.15], ['c', 0.03, -0.06, 3.45, -3.84, 3.63, -3.96], ['z']], w: 7.5, h: 8.165 };
|
|
107
107
|
|
|
108
|
-
glyphs['noteheads.triangle.quarter'] = { d: [['M', 0,
|
|
108
|
+
glyphs['noteheads.triangle.quarter'] = { d: [['M', 0, 4], ['l', 9, 0], ['l', -4.5, -9], ['z']], w: 9, h: 9 };
|
|
109
109
|
|
|
110
110
|
var pathClone = function (pathArray) {
|
|
111
111
|
var res = [];
|
|
@@ -11,6 +11,7 @@ function drawGlissando(renderer, params, selectables) {
|
|
|
11
11
|
var rightY = renderer.calcY(params.anchor2.heads[0].pitch)
|
|
12
12
|
var leftX = params.anchor1.x + params.anchor1.w / 2
|
|
13
13
|
var rightX = params.anchor2.x + params.anchor2.w / 2
|
|
14
|
+
|
|
14
15
|
var len = lineLength(leftX, leftY, rightX, rightY)
|
|
15
16
|
var marginLeft = params.anchor1.w / 2 + margin
|
|
16
17
|
var marginRight = params.anchor2.w / 2 + margin
|
|
@@ -7,14 +7,23 @@ function printLine(renderer, x1, x2, y, klass, name, dy) {
|
|
|
7
7
|
x2 = roundNumber(x2);
|
|
8
8
|
var y1 = roundNumber(y - dy);
|
|
9
9
|
var y2 = roundNumber(y + dy);
|
|
10
|
-
// TODO-PER: This fixes a firefox bug where
|
|
11
|
-
if (renderer.firefox112
|
|
12
|
-
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
// TODO-PER: This fixes a firefox bug where it isn't displayed
|
|
11
|
+
if (renderer.firefox112) {
|
|
12
|
+
y += dy / 2; // Because the y coordinate is the edge of where the line goes but the width widens from the middle.
|
|
13
|
+
var attr = {
|
|
14
|
+
x1: x1,
|
|
15
|
+
x2: x2,
|
|
16
|
+
y1: y,
|
|
17
|
+
y2: y,
|
|
18
|
+
stroke: renderer.foregroundColor,
|
|
19
|
+
'stroke-width': Math.abs(dy*2)
|
|
17
20
|
}
|
|
21
|
+
if (klass)
|
|
22
|
+
attr['class'] = klass;
|
|
23
|
+
if (name)
|
|
24
|
+
attr['data-name'] = name;
|
|
25
|
+
|
|
26
|
+
return renderer.paper.lineToBack(attr);
|
|
18
27
|
}
|
|
19
28
|
|
|
20
29
|
var pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", x1, y1, x2, y1,
|
|
@@ -12,15 +12,23 @@ function printStem(renderer, x, dx, y1, y2, klass, name) {
|
|
|
12
12
|
}
|
|
13
13
|
x = roundNumber(x);
|
|
14
14
|
var x2 = roundNumber(x + dx);
|
|
15
|
-
// TODO-PER: This fixes a firefox bug where
|
|
16
|
-
if (renderer.firefox112
|
|
17
|
-
|
|
18
|
-
var
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
// TODO-PER: This fixes a firefox bug where it isn't displayed
|
|
16
|
+
if (renderer.firefox112) {
|
|
17
|
+
x += dx / 2; // Because the x coordinate is the edge of where the line goes but the width widens from the middle.
|
|
18
|
+
var attr = {
|
|
19
|
+
x1: x,
|
|
20
|
+
x2: x,
|
|
21
|
+
y1: y1,
|
|
22
|
+
y2: y2,
|
|
23
|
+
stroke: renderer.foregroundColor,
|
|
24
|
+
'stroke-width': Math.abs(dx)
|
|
23
25
|
}
|
|
26
|
+
if (klass)
|
|
27
|
+
attr['class'] = klass;
|
|
28
|
+
if (name)
|
|
29
|
+
attr['data-name'] = name;
|
|
30
|
+
|
|
31
|
+
return renderer.paper.lineToBack(attr);
|
|
24
32
|
}
|
|
25
33
|
var pathArray = [["M", x, y1], ["L", x, y2], ["L", x2, y2], ["L", x2, y1], ["z"]];
|
|
26
34
|
var attr = { path: "" };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
function setPaperSize(renderer, maxwidth, scale, responsive) {
|
|
2
|
-
var w = (maxwidth + renderer.padding.right) * scale;
|
|
2
|
+
var w = (maxwidth + renderer.padding.left + renderer.padding.right) * scale;
|
|
3
3
|
var h = (renderer.y + renderer.padding.bottom) * scale;
|
|
4
4
|
if (renderer.isPrint)
|
|
5
5
|
h = Math.max(h, 1056); // 11in x 72pt/in x 1.33px/pt
|
package/src/write/draw/tie.js
CHANGED
|
@@ -17,7 +17,15 @@ function drawTie(renderer, params, linestartx, lineendx, selectables) {
|
|
|
17
17
|
klass = "abcjs-hint";
|
|
18
18
|
var fudgeY = params.fixedY ? 1.5 : 0; // TODO-PER: This just compensates for drawArc, which contains too much knowledge of ties and slurs.
|
|
19
19
|
var el = drawArc(renderer, params.startX, params.endX, params.startY + fudgeY, params.endY + fudgeY, params.above, klass, params.isTie, params.dotted);
|
|
20
|
-
|
|
20
|
+
var startChar = -1
|
|
21
|
+
// This gets the start and end points of the contents of the slur. We assume that the parenthesis are just to the outside of that.
|
|
22
|
+
if (params.anchor1 && !params.isTie)
|
|
23
|
+
startChar = params.anchor1.parent.abcelem.startChar - 1
|
|
24
|
+
var endChar = -1
|
|
25
|
+
if (params.anchor2 && !params.isTie)
|
|
26
|
+
endChar = params.anchor2.parent.abcelem.endChar + 1
|
|
27
|
+
|
|
28
|
+
selectables.wrapSvgEl({ el_type: "slur", startChar: startChar, endChar: endChar }, el);
|
|
21
29
|
return [el];
|
|
22
30
|
}
|
|
23
31
|
|
|
@@ -40,6 +40,7 @@ var EngraverController = function (paper, params) {
|
|
|
40
40
|
this.responsive = params.responsive;
|
|
41
41
|
this.space = 3 * spacing.SPACE;
|
|
42
42
|
this.initialClef = params.initialClef;
|
|
43
|
+
this.expandToWidest = !!params.expandToWidest;
|
|
43
44
|
this.scale = params.scale ? parseFloat(params.scale) : 0;
|
|
44
45
|
this.classes = new Classes({ shouldAddClasses: params.add_classes });
|
|
45
46
|
if (!(this.scale > 0.1))
|
|
@@ -242,7 +243,12 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
|
|
|
242
243
|
this.constructTuneElements(abcTune);
|
|
243
244
|
|
|
244
245
|
// Do all the positioning, both horizontally and vertically
|
|
245
|
-
var maxWidth = layout(this.renderer, abcTune, this.width, this.space);
|
|
246
|
+
var maxWidth = layout(this.renderer, abcTune, this.width, this.space, this.expandToWidest);
|
|
247
|
+
|
|
248
|
+
//Set the top text now that we know the width
|
|
249
|
+
if (this.expandToWidest && maxWidth > this.width+1) {
|
|
250
|
+
abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, maxWidth, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.getTextSize);
|
|
251
|
+
}
|
|
246
252
|
|
|
247
253
|
// Deal with tablature for staff
|
|
248
254
|
if (abcTune.tablatures) {
|
|
@@ -256,14 +262,14 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
|
|
|
256
262
|
|
|
257
263
|
if (this.oneSvgPerLine) {
|
|
258
264
|
var div = this.renderer.paper.svg.parentNode
|
|
259
|
-
this.svgs = splitSvgIntoLines(div, abcTune.metaText.title, this.responsive)
|
|
265
|
+
this.svgs = splitSvgIntoLines(this.renderer, div, abcTune.metaText.title, this.responsive)
|
|
260
266
|
} else {
|
|
261
267
|
this.svgs = [this.renderer.paper.svg];
|
|
262
268
|
}
|
|
263
269
|
setupSelection(this, this.svgs);
|
|
264
270
|
};
|
|
265
271
|
|
|
266
|
-
function splitSvgIntoLines(output, title, responsive) {
|
|
272
|
+
function splitSvgIntoLines(renderer, output, title, responsive) {
|
|
267
273
|
// Each line is a top level <g> in the svg. To split it into separate
|
|
268
274
|
// svgs iterate through each of those and put them in a new svg. Since
|
|
269
275
|
// they are placed absolutely, the viewBox needs to be manipulated to
|
|
@@ -297,7 +303,9 @@ function splitSvgIntoLines(output, title, responsive) {
|
|
|
297
303
|
svg.setAttribute("height", height)
|
|
298
304
|
if (responsive === 'resize')
|
|
299
305
|
svg.style.position = ''
|
|
300
|
-
|
|
306
|
+
// TODO-PER: Hack! Not sure why this is needed.
|
|
307
|
+
var viewBoxHeight = renderer.firefox112 ? height+1 : height
|
|
308
|
+
svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + viewBoxHeight)
|
|
301
309
|
svg.appendChild(style.cloneNode(true))
|
|
302
310
|
var titleEl = document.createElement("title")
|
|
303
311
|
titleEl.innerText = fullTitle
|