abcjs 6.0.0-beta.32 → 6.0.0-beta.36
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 +13 -7
- package/RELEASE.md +130 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +3763 -825
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +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-dev-orig-no-babel.html +37 -0
- package/dist/report-synth.html +37 -0
- package/docker-build.sh +1 -0
- package/glyphs.json +1 -0
- package/package.json +9 -9
- package/src/api/abc_tablatures.js +144 -0
- package/src/api/abc_timing_callbacks.js +49 -26
- package/src/api/abc_tunebook.js +10 -1
- package/src/api/abc_tunebook_svg.js +16 -22
- package/src/data/abc_tune.js +90 -25
- package/src/data/deline-tune.js +199 -0
- package/src/edit/abc_editor.js +33 -11
- package/src/midi/abc_midi_create.js +6 -2
- package/src/parse/abc_parse.js +10 -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 +15 -5
- package/src/parse/tune-builder.js +23 -30
- package/src/parse/wrap_lines.js +13 -36
- package/src/synth/abc_midi_flattener.js +44 -29
- package/src/synth/abc_midi_sequencer.js +52 -13
- package/src/synth/create-synth.js +22 -7
- 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/synth/synth-controller.js +5 -3
- 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 +22 -9
- 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 +3 -4
- 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/types/index.d.ts +1007 -39
- package/version.js +1 -1
|
@@ -39,6 +39,9 @@ var parseDirective = {};
|
|
|
39
39
|
tune.formatting.footerfont = { face: "\"Times New Roman\"", size: 12, weight: "normal", style: "normal", decoration: "none" };
|
|
40
40
|
tune.formatting.headerfont = { face: "\"Times New Roman\"", size: 12, weight: "normal", style: "normal", decoration: "none" };
|
|
41
41
|
tune.formatting.voicefont = { face: "\"Times New Roman\"", size: 13, weight: "bold", style: "normal", decoration: "none" };
|
|
42
|
+
tune.formatting.tablabelfont = { face: "\"Trebuchet MS\"", size: 16, weight: "normal", style: "normal", decoration: "none" };
|
|
43
|
+
tune.formatting.tabnumberfont = { face: "\"Arial\"", size: 11, weight: "normal", style: "normal", decoration: "none" };
|
|
44
|
+
tune.formatting.tabgracefont = { face: "\"Arial\"", size: 8, weight: "normal", style: "normal", decoration: "none" };
|
|
42
45
|
|
|
43
46
|
// these are the default fonts for these element types. In the printer, these fonts might change as the tune progresses.
|
|
44
47
|
tune.formatting.annotationfont = multilineVars.annotationfont;
|
|
@@ -390,7 +393,7 @@ var parseDirective = {};
|
|
|
390
393
|
var interpretPercMap = function(restOfString) {
|
|
391
394
|
var tokens = restOfString.split(/\s+/); // Allow multiple spaces.
|
|
392
395
|
if (tokens.length !== 2 && tokens.length !== 3)
|
|
393
|
-
return { error: 'Expected parameters "abc-note", "drum-sound", and optionally "note-head"'}
|
|
396
|
+
return { error: 'Expected parameters "abc-note", "drum-sound", and optionally "note-head"'};
|
|
394
397
|
var key = tokens[0];
|
|
395
398
|
// The percussion sound can either be a MIDI number or a drum name. If it is not a number then check for a name.
|
|
396
399
|
var pitch = parseInt(tokens[1], 10);
|
|
@@ -403,7 +406,7 @@ var parseDirective = {};
|
|
|
403
406
|
if (tokens.length === 3)
|
|
404
407
|
value.noteHead = tokens[2];
|
|
405
408
|
return { key: key, value: value };
|
|
406
|
-
}
|
|
409
|
+
};
|
|
407
410
|
|
|
408
411
|
var getRequiredMeasurement = function(cmd, tokens) {
|
|
409
412
|
var points = tokenizer.getMeasurement(tokens);
|
|
@@ -742,6 +745,7 @@ var parseDirective = {};
|
|
|
742
745
|
// titleformat: { type: "string", optional: true },
|
|
743
746
|
case "bagpipes":tune.formatting.bagpipes = true;break;
|
|
744
747
|
case "flatbeams":tune.formatting.flatbeams = true;break;
|
|
748
|
+
case "jazzchords":tune.formatting.jazzchords = true;break;
|
|
745
749
|
case "landscape":multilineVars.landscape = true;break;
|
|
746
750
|
case "papersize":multilineVars.papersize = restOfString;break;
|
|
747
751
|
case "graceslurs":
|
|
@@ -814,7 +818,7 @@ var parseDirective = {};
|
|
|
814
818
|
break;
|
|
815
819
|
case "sep":
|
|
816
820
|
if (tokens.length === 0)
|
|
817
|
-
tuneBuilder.addSeparator(14,14,85); // If no parameters are given, then there is a default size.
|
|
821
|
+
tuneBuilder.addSeparator(14,14,85, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+5}); // If no parameters are given, then there is a default size.
|
|
818
822
|
else {
|
|
819
823
|
var points = tokenizer.getMeasurement(tokens);
|
|
820
824
|
if (points.used === 0)
|
|
@@ -830,7 +834,7 @@ var parseDirective = {};
|
|
|
830
834
|
if (points.used === 0 || tokens.length !== 0)
|
|
831
835
|
return "Directive \"" + cmd + "\" requires 3 numbers: space above, space below, length of line";
|
|
832
836
|
var lenLine = points.value;
|
|
833
|
-
tuneBuilder.addSeparator(spaceAbove, spaceBelow, lenLine);
|
|
837
|
+
tuneBuilder.addSeparator(spaceAbove, spaceBelow, lenLine, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+restOfString.length});
|
|
834
838
|
}
|
|
835
839
|
break;
|
|
836
840
|
case "barsperstaff":
|
|
@@ -874,23 +878,23 @@ var parseDirective = {};
|
|
|
874
878
|
break;
|
|
875
879
|
case "begintext":
|
|
876
880
|
var textBlock = '';
|
|
877
|
-
line = tokenizer.nextLine()
|
|
881
|
+
line = tokenizer.nextLine();
|
|
878
882
|
while(line && line.indexOf('%%endtext') !== 0) {
|
|
879
883
|
if (parseCommon.startsWith(line, "%%"))
|
|
880
884
|
textBlock += line.substring(2) + "\n";
|
|
881
885
|
else
|
|
882
886
|
textBlock += line + "\n";
|
|
883
|
-
line = tokenizer.nextLine()
|
|
887
|
+
line = tokenizer.nextLine();
|
|
884
888
|
}
|
|
885
|
-
tuneBuilder.addText(textBlock);
|
|
889
|
+
tuneBuilder.addText(textBlock, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+textBlock.length+7});
|
|
886
890
|
break;
|
|
887
891
|
case "continueall":
|
|
888
892
|
multilineVars.continueall = true;
|
|
889
893
|
break;
|
|
890
894
|
case "beginps":
|
|
891
|
-
line = tokenizer.nextLine()
|
|
895
|
+
line = tokenizer.nextLine();
|
|
892
896
|
while(line && line.indexOf('%%endps') !== 0) {
|
|
893
|
-
tokenizer.nextLine()
|
|
897
|
+
tokenizer.nextLine();
|
|
894
898
|
}
|
|
895
899
|
warn("Postscript ignored", str, 0);
|
|
896
900
|
break;
|
|
@@ -901,7 +905,7 @@ var parseDirective = {};
|
|
|
901
905
|
break;
|
|
902
906
|
case "text":
|
|
903
907
|
var textstr = tokenizer.translateString(restOfString);
|
|
904
|
-
tuneBuilder.addText(parseDirective.parseFontChangeLine(textstr));
|
|
908
|
+
tuneBuilder.addText(parseDirective.parseFontChangeLine(textstr), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+restOfString.length+7});
|
|
905
909
|
break;
|
|
906
910
|
case "center":
|
|
907
911
|
var centerstr = tokenizer.translateString(restOfString);
|
|
@@ -1067,7 +1071,7 @@ var parseDirective = {};
|
|
|
1067
1071
|
case "-version":
|
|
1068
1072
|
case "-charset":
|
|
1069
1073
|
var subCmd = arr.shift();
|
|
1070
|
-
tuneBuilder.addMetaText(cmd+subCmd, arr.join(' '));
|
|
1074
|
+
tuneBuilder.addMetaText(cmd+subCmd, arr.join(' '), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+restOfString.length+5});
|
|
1071
1075
|
break;
|
|
1072
1076
|
default:
|
|
1073
1077
|
return "Unknown directive: " + cmd+arr[0];
|
|
@@ -1090,7 +1094,7 @@ var parseDirective = {};
|
|
|
1090
1094
|
if (footerArr.length > 3)
|
|
1091
1095
|
warn("Too many tabs in " + cmd + ": " + footerArr.length + " found.", restOfString, 0);
|
|
1092
1096
|
|
|
1093
|
-
tuneBuilder.addMetaTextObj(cmd, footer);
|
|
1097
|
+
tuneBuilder.addMetaTextObj(cmd, footer, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+str.length});
|
|
1094
1098
|
break;
|
|
1095
1099
|
|
|
1096
1100
|
case "midi":
|
|
@@ -1151,6 +1155,9 @@ var parseDirective = {};
|
|
|
1151
1155
|
case "vocalfont":
|
|
1152
1156
|
case "wordsfont":
|
|
1153
1157
|
case "annotationfont":
|
|
1158
|
+
case "tablabelfont":
|
|
1159
|
+
case "tabnumberfont":
|
|
1160
|
+
case "tabgracefont":
|
|
1154
1161
|
getChangingFont(cmd, tokens, value);
|
|
1155
1162
|
break;
|
|
1156
1163
|
case "scale":
|
|
@@ -13,13 +13,13 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
|
|
|
13
13
|
|
|
14
14
|
this.setTitle = function(title) {
|
|
15
15
|
if (multilineVars.hasMainTitle)
|
|
16
|
-
tuneBuilder.addSubtitle(tokenizer.translateString(tokenizer.stripComment(title))); // display secondary title
|
|
16
|
+
tuneBuilder.addSubtitle(tokenizer.translateString(tokenizer.stripComment(title)), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+title.length+2}); // display secondary title
|
|
17
17
|
else
|
|
18
18
|
{
|
|
19
19
|
var titleStr = tokenizer.translateString(tokenizer.theReverser(tokenizer.stripComment(title)));
|
|
20
20
|
if (multilineVars.titlecaps)
|
|
21
21
|
titleStr = titleStr.toUpperCase();
|
|
22
|
-
tuneBuilder.addMetaText("title", titleStr);
|
|
22
|
+
tuneBuilder.addMetaText("title", titleStr, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+title.length+2});
|
|
23
23
|
multilineVars.hasMainTitle = true;
|
|
24
24
|
}
|
|
25
25
|
};
|
|
@@ -235,7 +235,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
|
|
|
235
235
|
prestissimo: 210,
|
|
236
236
|
};
|
|
237
237
|
|
|
238
|
-
this.setTempo = function(line, start, end) {
|
|
238
|
+
this.setTempo = function(line, start, end, iChar) {
|
|
239
239
|
//Q - tempo; can be used to specify the notes per minute, e.g. If
|
|
240
240
|
//the meter denominator is a 4 note then Q:120 or Q:C=120
|
|
241
241
|
//is 120 quarter notes per minute. Similarly Q:C3=40 would be 40
|
|
@@ -255,7 +255,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
|
|
|
255
255
|
|
|
256
256
|
if (tokens.length === 0) throw "Missing parameter in Q: field";
|
|
257
257
|
|
|
258
|
-
var tempo = {};
|
|
258
|
+
var tempo = { startChar: iChar+start-2, endChar: iChar+end };
|
|
259
259
|
var delaySet = true;
|
|
260
260
|
var token = tokens.shift();
|
|
261
261
|
if (token.type === 'quote') {
|
|
@@ -381,7 +381,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
|
|
|
381
381
|
return [ e-i+1+ws ];
|
|
382
382
|
case "[Q:":
|
|
383
383
|
if (e > 0) {
|
|
384
|
-
var tempo = this.setTempo(line, i+3, e);
|
|
384
|
+
var tempo = this.setTempo(line, i+3, e, multilineVars.iChar);
|
|
385
385
|
if (tempo.type === 'delaySet') {
|
|
386
386
|
if (tuneBuilder.hasBeginMusic())
|
|
387
387
|
tuneBuilder.appendElement('tempo', startChar, endChar, this.calcTempo(tempo.tempo));
|
|
@@ -442,7 +442,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
|
|
|
442
442
|
case "Q:":
|
|
443
443
|
var e = line.indexOf('\x12', i+2);
|
|
444
444
|
if (e === -1) e = line.length;
|
|
445
|
-
var tempo = this.setTempo(line, i+2, e);
|
|
445
|
+
var tempo = this.setTempo(line, i+2, e, multilineVars.iChar);
|
|
446
446
|
if (tempo.type === 'delaySet') tuneBuilder.appendElement('tempo', multilineVars.iChar + i, multilineVars.iChar + line.length, this.calcTempo(tempo.tempo));
|
|
447
447
|
else if (tempo.type === 'immediate') tuneBuilder.appendElement('tempo', multilineVars.iChar + i, multilineVars.iChar + line.length, tempo.tempo);
|
|
448
448
|
return [ e, line.charAt(i), parseCommon.strip(line.substring(i+2))];
|
|
@@ -477,9 +477,9 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
|
|
|
477
477
|
var field = metaTextHeaders[line.charAt(0)];
|
|
478
478
|
if (field !== undefined) {
|
|
479
479
|
if (field === 'unalignedWords')
|
|
480
|
-
tuneBuilder.addMetaTextArray(field, parseDirective.parseFontChangeLine(tokenizer.translateString(tokenizer.stripComment(line.substring(2)))));
|
|
480
|
+
tuneBuilder.addMetaTextArray(field, parseDirective.parseFontChangeLine(tokenizer.translateString(tokenizer.stripComment(line.substring(2)))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
|
|
481
481
|
else
|
|
482
|
-
tuneBuilder.addMetaText(field, tokenizer.translateString(tokenizer.stripComment(line.substring(2))));
|
|
482
|
+
tuneBuilder.addMetaText(field, tokenizer.translateString(tokenizer.stripComment(line.substring(2))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
|
|
483
483
|
return {};
|
|
484
484
|
} else {
|
|
485
485
|
var startChar = multilineVars.iChar;
|
|
@@ -487,11 +487,11 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
|
|
|
487
487
|
switch(line.charAt(0))
|
|
488
488
|
{
|
|
489
489
|
case 'H':
|
|
490
|
-
tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line.substring(2))));
|
|
490
|
+
tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line.substring(2))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
|
|
491
491
|
line = tokenizer.peekLine()
|
|
492
492
|
while (line && line.charAt(1) !== ':') {
|
|
493
493
|
tokenizer.nextLine()
|
|
494
|
-
tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line)));
|
|
494
|
+
tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line)), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
|
|
495
495
|
line = tokenizer.peekLine()
|
|
496
496
|
}
|
|
497
497
|
break;
|
|
@@ -516,12 +516,12 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
|
|
|
516
516
|
case 'P':
|
|
517
517
|
// TODO-PER: There is more to do with parts, but the writer doesn't care.
|
|
518
518
|
if (multilineVars.is_in_header)
|
|
519
|
-
tuneBuilder.addMetaText("partOrder", tokenizer.translateString(tokenizer.stripComment(line.substring(2))));
|
|
519
|
+
tuneBuilder.addMetaText("partOrder", tokenizer.translateString(tokenizer.stripComment(line.substring(2))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
|
|
520
520
|
else
|
|
521
521
|
multilineVars.partForNextLine = { title: tokenizer.translateString(tokenizer.stripComment(line.substring(2))), startChar: startChar, endChar: endChar};
|
|
522
522
|
break;
|
|
523
523
|
case 'Q':
|
|
524
|
-
var tempo = this.setTempo(line, 2, line.length);
|
|
524
|
+
var tempo = this.setTempo(line, 2, line.length, multilineVars.iChar);
|
|
525
525
|
if (tempo.type === 'delaySet') multilineVars.tempo = tempo.tempo;
|
|
526
526
|
else if (tempo.type === 'immediate') {
|
|
527
527
|
if (!tune.metaText.tempo)
|
|
@@ -318,6 +318,7 @@ MusicParser.prototype.parseMusic = function(line) {
|
|
|
318
318
|
else {
|
|
319
319
|
el.startTriplet = ret.triplet;
|
|
320
320
|
el.tripletMultiplier = ret.tripletQ / ret.triplet;
|
|
321
|
+
el.tripletR = ret.num_notes;
|
|
321
322
|
tripletNotesLeft = ret.num_notes === undefined ? ret.triplet : ret.num_notes;
|
|
322
323
|
}
|
|
323
324
|
}
|
|
@@ -461,7 +462,7 @@ MusicParser.prototype.parseMusic = function(line) {
|
|
|
461
462
|
}
|
|
462
463
|
|
|
463
464
|
multilineVars.addFormattingOptions(el, tune.formatting, 'note');
|
|
464
|
-
tuneBuilder.appendElement('note', startOfLine+
|
|
465
|
+
tuneBuilder.appendElement('note', startOfLine+startI, startOfLine+i, el);
|
|
465
466
|
multilineVars.measureNotEmpty = true;
|
|
466
467
|
el = {};
|
|
467
468
|
}
|
|
@@ -480,6 +481,7 @@ MusicParser.prototype.parseMusic = function(line) {
|
|
|
480
481
|
// TODO-PER: straighten this out so there is not so much copying: getCoreNote shouldn't change e'
|
|
481
482
|
if (core.accidental !== undefined) el.pitches[0].accidental = core.accidental;
|
|
482
483
|
el.pitches[0].pitch = core.pitch;
|
|
484
|
+
el.pitches[0].name = core.name;
|
|
483
485
|
if (core.midipitch || core.midipitch === 0)
|
|
484
486
|
el.pitches[0].midipitch = core.midipitch;
|
|
485
487
|
if (core.endSlur !== undefined) el.pitches[0].endSlur = core.endSlur;
|
|
@@ -672,12 +674,17 @@ var letter_to_grace = function(line, i) {
|
|
|
672
674
|
|
|
673
675
|
ii = note.endChar;
|
|
674
676
|
delete note.endChar;
|
|
677
|
+
|
|
678
|
+
if (note.end_beam) {
|
|
679
|
+
note.endBeam = true;
|
|
680
|
+
delete note.end_beam;
|
|
681
|
+
}
|
|
675
682
|
}
|
|
676
683
|
else {
|
|
677
684
|
// We shouldn't get anything but notes or a space here, so report an error
|
|
678
685
|
if (gra[1].charAt(ii) === ' ') {
|
|
679
686
|
if (gracenotes.length > 0)
|
|
680
|
-
gracenotes[gracenotes.length-1].
|
|
687
|
+
gracenotes[gracenotes.length-1].endBeam = true;
|
|
681
688
|
} else
|
|
682
689
|
warn("Unknown character '" + gra[1].charAt(ii) + "' while parsing grace note", line, i);
|
|
683
690
|
ii++;
|
|
@@ -1062,6 +1069,7 @@ var addEndBeam = function(el) {
|
|
|
1062
1069
|
|
|
1063
1070
|
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};
|
|
1064
1071
|
var rests = {x: 'invisible', X: 'invisible-multimeasure', y: 'spacer', z: 'rest', Z: 'multimeasure' };
|
|
1072
|
+
var accMap = { 'dblflat': '__', 'flat': '_', 'natural': '=', 'sharp': '^', 'dblsharp': '^^', 'quarterflat': '_/', 'quartersharp': '^/'};
|
|
1065
1073
|
var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
|
|
1066
1074
|
//var el = { startChar: index };
|
|
1067
1075
|
var isComplete = function(state) {
|
|
@@ -1120,6 +1128,9 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
|
|
|
1120
1128
|
case 'g':
|
|
1121
1129
|
if (state === 'startSlur' || state === 'sharp2' || state === 'flat2' || state === 'pitch') {
|
|
1122
1130
|
el.pitch = pitches[line.charAt(index)];
|
|
1131
|
+
el.name = line.charAt(index);
|
|
1132
|
+
if (el.accidental)
|
|
1133
|
+
el.name = accMap[el.accidental] + el.name;
|
|
1123
1134
|
transpose.note(multilineVars, el);
|
|
1124
1135
|
state = 'octave';
|
|
1125
1136
|
// At this point we have a valid note. The rest is optional. Set the duration in case we don't get one below
|
|
@@ -1134,7 +1145,6 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
|
|
|
1134
1145
|
(multilineVars.currentVoice && multilineVars.currentVoice.clef === "perc")) {
|
|
1135
1146
|
var key = line.charAt(index);
|
|
1136
1147
|
if (el.accidental) {
|
|
1137
|
-
var accMap = { 'dblflat': '__', 'flat': '_', 'natural': '=', 'sharp': '^', 'dblsharp': '^^'};
|
|
1138
1148
|
key = accMap[el.accidental] + key;
|
|
1139
1149
|
}
|
|
1140
1150
|
if (tune.formatting && tune.formatting.midi && tune.formatting.midi.drummap)
|
|
@@ -1144,12 +1154,12 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
|
|
|
1144
1154
|
else return null;
|
|
1145
1155
|
break;
|
|
1146
1156
|
case ',':
|
|
1147
|
-
if (state === 'octave') {el.pitch -= 7; }
|
|
1157
|
+
if (state === 'octave') {el.pitch -= 7; el.name += ','; }
|
|
1148
1158
|
else if (isComplete(state)) {el.endChar = index;return el;}
|
|
1149
1159
|
else return null;
|
|
1150
1160
|
break;
|
|
1151
1161
|
case '\'':
|
|
1152
|
-
if (state === 'octave') {el.pitch += 7; }
|
|
1162
|
+
if (state === 'octave') {el.pitch += 7; el.name += "'"; }
|
|
1153
1163
|
else if (isComplete(state)) {el.endChar = index;return el;}
|
|
1154
1164
|
else return null;
|
|
1155
1165
|
break;
|
|
@@ -4,19 +4,6 @@ var parseCommon = require('../parse/abc_common');
|
|
|
4
4
|
var TuneBuilder = function(tune) {
|
|
5
5
|
var self = this;
|
|
6
6
|
|
|
7
|
-
this.reset = function () {
|
|
8
|
-
tune.version = "1.1.0";
|
|
9
|
-
tune.media = "screen";
|
|
10
|
-
tune.metaText = {};
|
|
11
|
-
tune.formatting = {};
|
|
12
|
-
tune.lines = [];
|
|
13
|
-
tune.staffNum = 0;
|
|
14
|
-
tune.voiceNum = 0;
|
|
15
|
-
tune.lineNum = 0;
|
|
16
|
-
tune.runningFonts = {};
|
|
17
|
-
delete tune.visualTranspose;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
7
|
this.setVisualTranspose = function(visualTranspose) {
|
|
21
8
|
if (visualTranspose)
|
|
22
9
|
tune.visualTranspose = visualTranspose;
|
|
@@ -472,7 +459,7 @@ var TuneBuilder = function(tune) {
|
|
|
472
459
|
return currSlur;
|
|
473
460
|
};
|
|
474
461
|
|
|
475
|
-
|
|
462
|
+
tune.reset();
|
|
476
463
|
|
|
477
464
|
this.getLastNote = function() {
|
|
478
465
|
if (tune.lines[tune.lineNum] && tune.lines[tune.lineNum].staff && tune.lines[tune.lineNum].staff[tune.staffNum] &&
|
|
@@ -612,7 +599,7 @@ var TuneBuilder = function(tune) {
|
|
|
612
599
|
// Clone the object because it will be sticking around for the next line and we don't want the extra fields in it.
|
|
613
600
|
var hashParams = parseCommon.clone(hashParams2);
|
|
614
601
|
|
|
615
|
-
if (tune.lines[tune.lineNum].staff) { // be sure that we are on a music type line before doing the following.
|
|
602
|
+
if (tune.lines[tune.lineNum] && tune.lines[tune.lineNum].staff) { // be sure that we are on a music type line before doing the following.
|
|
616
603
|
// If tune is the first item in tune staff, then we might have to initialize the staff, first.
|
|
617
604
|
if (tune.lines[tune.lineNum].staff.length <= tune.staffNum) {
|
|
618
605
|
tune.lines[tune.lineNum].staff[tune.staffNum] = {};
|
|
@@ -665,8 +652,8 @@ var TuneBuilder = function(tune) {
|
|
|
665
652
|
tune.lines.push(hash);
|
|
666
653
|
};
|
|
667
654
|
|
|
668
|
-
this.addSubtitle = function(str) {
|
|
669
|
-
this.pushLine({subtitle: str});
|
|
655
|
+
this.addSubtitle = function(str, info) {
|
|
656
|
+
this.pushLine({subtitle: { text: str, startChar: info.startChar, endChar: info.endChar}});
|
|
670
657
|
};
|
|
671
658
|
|
|
672
659
|
this.addSpacing = function(num) {
|
|
@@ -677,12 +664,12 @@ var TuneBuilder = function(tune) {
|
|
|
677
664
|
this.pushLine({newpage: num});
|
|
678
665
|
};
|
|
679
666
|
|
|
680
|
-
this.addSeparator = function(spaceAbove, spaceBelow, lineLength) {
|
|
681
|
-
this.pushLine({separator: {spaceAbove: Math.round(spaceAbove), spaceBelow: Math.round(spaceBelow), lineLength: Math.round(lineLength)}});
|
|
667
|
+
this.addSeparator = function(spaceAbove, spaceBelow, lineLength, info) {
|
|
668
|
+
this.pushLine({separator: {spaceAbove: Math.round(spaceAbove), spaceBelow: Math.round(spaceBelow), lineLength: Math.round(lineLength), startChar: info.startChar, endChar: info.endChar}});
|
|
682
669
|
};
|
|
683
670
|
|
|
684
|
-
this.addText = function(str) {
|
|
685
|
-
this.pushLine({text: str});
|
|
671
|
+
this.addText = function(str, info) {
|
|
672
|
+
this.pushLine({text: { text: str, startChar: info.startChar, endChar: info.endChar}});
|
|
686
673
|
};
|
|
687
674
|
|
|
688
675
|
this.addCentered = function(str) {
|
|
@@ -699,7 +686,7 @@ var TuneBuilder = function(tune) {
|
|
|
699
686
|
|
|
700
687
|
this.containsNotesStrict = function(voice) {
|
|
701
688
|
for (var i = 0; i < voice.length; i++) {
|
|
702
|
-
if (voice[i].el_type === 'note' && voice[i].rest === undefined)
|
|
689
|
+
if (voice[i].el_type === 'note' && (voice[i].rest === undefined || voice[i].chord !== undefined))
|
|
703
690
|
return true;
|
|
704
691
|
}
|
|
705
692
|
return false;
|
|
@@ -881,23 +868,29 @@ var TuneBuilder = function(tune) {
|
|
|
881
868
|
tune.lineNum = i;
|
|
882
869
|
};
|
|
883
870
|
|
|
884
|
-
this.addMetaText = function(key, value) {
|
|
885
|
-
if (tune.metaText[key] === undefined)
|
|
871
|
+
this.addMetaText = function(key, value, info) {
|
|
872
|
+
if (tune.metaText[key] === undefined) {
|
|
886
873
|
tune.metaText[key] = value;
|
|
887
|
-
|
|
874
|
+
tune.metaTextInfo[key] = info;
|
|
875
|
+
} else {
|
|
888
876
|
tune.metaText[key] += "\n" + value;
|
|
877
|
+
tune.metaTextInfo[key].endChar = info.endChar;
|
|
878
|
+
}
|
|
889
879
|
};
|
|
890
880
|
|
|
891
|
-
this.addMetaTextArray = function(key, value) {
|
|
892
|
-
if (tune.metaText[key] === undefined)
|
|
881
|
+
this.addMetaTextArray = function(key, value, info) {
|
|
882
|
+
if (tune.metaText[key] === undefined) {
|
|
893
883
|
tune.metaText[key] = [value];
|
|
894
|
-
|
|
884
|
+
tune.metaTextInfo[key] = info;
|
|
885
|
+
} else {
|
|
895
886
|
tune.metaText[key].push(value);
|
|
887
|
+
tune.metaTextInfo[key].endChar = info.endChar;
|
|
888
|
+
}
|
|
896
889
|
};
|
|
897
|
-
this.addMetaTextObj = function(key, value) {
|
|
890
|
+
this.addMetaTextObj = function(key, value, info) {
|
|
898
891
|
tune.metaText[key] = value;
|
|
892
|
+
tune.metaTextInfo[key] = info;
|
|
899
893
|
};
|
|
900
|
-
|
|
901
894
|
};
|
|
902
895
|
|
|
903
896
|
module.exports = TuneBuilder;
|
package/src/parse/wrap_lines.js
CHANGED
|
@@ -7,7 +7,7 @@ function wrapLines(tune, lineBreaks) {
|
|
|
7
7
|
// tune.lines contains nested arrays: there is an array of lines (that's the part this function rewrites),
|
|
8
8
|
// there is an array of staffs per line (for instance, piano will have 2, orchestra will have many)
|
|
9
9
|
// there is an array of voices per staff (for instance, 4-part harmony might have bass and tenor on a single staff)
|
|
10
|
-
var lines =
|
|
10
|
+
var lines = tune.deline({lineBreaks: false});
|
|
11
11
|
var linesBreakElements = findLineBreaks(lines, lineBreaks);
|
|
12
12
|
//console.log(JSON.stringify(linesBreakElements))
|
|
13
13
|
tune.lines = addLineBreaks(lines, linesBreakElements);
|
|
@@ -24,6 +24,7 @@ function addLineBreaks(lines, linesBreakElements) {
|
|
|
24
24
|
// If the item doesn't contain "staff" then it is a non music line and should just be copied.
|
|
25
25
|
var outputLines = [];
|
|
26
26
|
var lastKeySig = []; // This is per staff - if the key changed then this will be populated.
|
|
27
|
+
var lastStem = [];
|
|
27
28
|
for (var i = 0; i < linesBreakElements.length; i++) {
|
|
28
29
|
var action = linesBreakElements[i];
|
|
29
30
|
if (lines[action.ogLine].staff) {
|
|
@@ -43,12 +44,15 @@ function addLineBreaks(lines, linesBreakElements) {
|
|
|
43
44
|
}
|
|
44
45
|
if (lastKeySig[action.staff])
|
|
45
46
|
outputLines[action.line].staff[action.staff].key = lastKeySig[action.staff];
|
|
47
|
+
|
|
46
48
|
}
|
|
47
49
|
if (!outputLines[action.line].staff[action.staff].voices[action.voice]) {
|
|
48
50
|
outputLines[action.line].staff[action.staff].voices[action.voice] = [];
|
|
49
51
|
}
|
|
50
52
|
outputLines[action.line].staff[action.staff].voices[action.voice] =
|
|
51
53
|
lines[action.ogLine].staff[action.staff].voices[action.voice].slice(action.start, action.end+1);
|
|
54
|
+
if (lastStem[action.staff*10+action.voice])
|
|
55
|
+
outputLines[action.line].staff[action.staff].voices[action.voice].unshift({el_type: "stem", direction: lastStem[action.staff*10+action.voice].direction})
|
|
52
56
|
var currVoice = outputLines[action.line].staff[action.staff].voices[action.voice];
|
|
53
57
|
for (var kk = currVoice.length-1; kk >= 0; kk--) {
|
|
54
58
|
if (currVoice[kk].el_type === "key") {
|
|
@@ -61,6 +65,14 @@ function addLineBreaks(lines, linesBreakElements) {
|
|
|
61
65
|
break;
|
|
62
66
|
}
|
|
63
67
|
}
|
|
68
|
+
for (kk = currVoice.length-1; kk >= 0; kk--) {
|
|
69
|
+
if (currVoice[kk].el_type === "stem") {
|
|
70
|
+
lastStem[action.staff*10+action.voice] = {
|
|
71
|
+
direction: currVoice[kk].direction,
|
|
72
|
+
};
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
64
76
|
} else {
|
|
65
77
|
outputLines[action.line] = lines[action.ogLine];
|
|
66
78
|
}
|
|
@@ -128,41 +140,6 @@ function findLineBreaks(lines, lineBreakArray) {
|
|
|
128
140
|
return lineBreakIndexes;
|
|
129
141
|
}
|
|
130
142
|
|
|
131
|
-
function removeLineBreaks(lines) {
|
|
132
|
-
// This concatenates all the music lines. If there is a non-music line then it leaves it,
|
|
133
|
-
// so it returns an array of lines where there is no more than one staff line in a row.
|
|
134
|
-
var outputLines = [];
|
|
135
|
-
var startLine = true;
|
|
136
|
-
for (var i = 0; i < lines.length; i++) {
|
|
137
|
-
var line = lines[i];
|
|
138
|
-
if (line.staff) {
|
|
139
|
-
if (startLine) {
|
|
140
|
-
outputLines.push(line);
|
|
141
|
-
startLine = false;
|
|
142
|
-
} else {
|
|
143
|
-
// copy all voices to the previous line
|
|
144
|
-
var output = outputLines[outputLines.length - 1];
|
|
145
|
-
var staffs = line.staff;
|
|
146
|
-
for (var j = 0; j < staffs.length; j++) {
|
|
147
|
-
if (output.staff.length <= j)
|
|
148
|
-
output.staff.push({ voices: []})
|
|
149
|
-
var staff = staffs[j];
|
|
150
|
-
var voices = staff.voices;
|
|
151
|
-
for (var k = 0; k < voices.length; k++) {
|
|
152
|
-
if (output.staff[j].voices.length < k)
|
|
153
|
-
output.staff[j].voices.push([]);
|
|
154
|
-
var voice = voices[k];
|
|
155
|
-
output.staff[j].voices[k] = output.staff[j].voices[k].concat(voice);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
} else {
|
|
160
|
-
startLine = true;
|
|
161
|
-
outputLines.push(line);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return outputLines;
|
|
165
|
-
}
|
|
166
143
|
|
|
167
144
|
function freeFormLineBreaks(widths, lineBreakPoint) {
|
|
168
145
|
var lineBreaks = [];
|
|
@@ -69,7 +69,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
69
69
|
accidentals = [0,0,0,0,0,0,0];
|
|
70
70
|
bagpipes = false;
|
|
71
71
|
tracks = [];
|
|
72
|
-
startingTempo =
|
|
72
|
+
startingTempo = options.qpm;
|
|
73
73
|
startingMeter = undefined;
|
|
74
74
|
tempoChangeFactor = 1;
|
|
75
75
|
instrument = undefined;
|
|
@@ -112,7 +112,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
112
112
|
pickupLength = voices[0][0].pickupLength;
|
|
113
113
|
|
|
114
114
|
// First adjust the input to resolve ties, set the starting time for each note, etc. That will make the rest of the logic easier
|
|
115
|
-
preProcess(voices);
|
|
115
|
+
preProcess(voices, options);
|
|
116
116
|
|
|
117
117
|
for (var i = 0; i < voices.length; i++) {
|
|
118
118
|
transpose = 0;
|
|
@@ -225,35 +225,14 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
225
225
|
if (drumTrack.length > 0) // Don't do drums on more than one track, so turn off drum after we create it.
|
|
226
226
|
drumTrackFinished = true;
|
|
227
227
|
}
|
|
228
|
+
// See if any notes are octaves played at the same time. If so, raise the pitch of the higher one.
|
|
229
|
+
if (options.detuneOctave)
|
|
230
|
+
findOctaves(tracks, parseInt(options.detuneOctave, 10));
|
|
231
|
+
|
|
228
232
|
if (chordTrack.length > 0)
|
|
229
233
|
tracks.push(chordTrack);
|
|
230
234
|
if (drumTrack.length > 0)
|
|
231
235
|
tracks.push(drumTrack);
|
|
232
|
-
// Adjust the tempo according to the meter. The rules are this:
|
|
233
|
-
// 1) If the denominator is 2 or 4, then always make a beat be the denominator.
|
|
234
|
-
//
|
|
235
|
-
// 2) If the denominator is 8 or 16, then:
|
|
236
|
-
// a) If the numerator is divisible by 3, the beat is 3*denominator.
|
|
237
|
-
// b) Otherwise the beat is the denominator.
|
|
238
|
-
//
|
|
239
|
-
// 3) If the denominator is anything else, then don't worry about it because it doesn't make sense. Don't modify it and hope for the best.
|
|
240
|
-
//
|
|
241
|
-
// Right now, the startingTempo is calculated for a quarter note, so modify it if necessary.
|
|
242
|
-
// var num = startingMeter ? parseInt(startingMeter.num, 10) : meter.num;
|
|
243
|
-
// var den = startingMeter ? parseInt(startingMeter.den, 10) : meter.den;
|
|
244
|
-
// if (den === 2)
|
|
245
|
-
// startingTempo *= 2;
|
|
246
|
-
// else if (den === 8) {
|
|
247
|
-
// if (parseInt(num, 10) % 3 === 0)
|
|
248
|
-
// startingTempo *= 3/2;
|
|
249
|
-
// else
|
|
250
|
-
// startingTempo /= 2;
|
|
251
|
-
// } else if (den === 16) {
|
|
252
|
-
// if (num % 3 === 0)
|
|
253
|
-
// startingTempo *= 3/4;
|
|
254
|
-
// else
|
|
255
|
-
// startingTempo /= 4;
|
|
256
|
-
// }
|
|
257
236
|
|
|
258
237
|
return { tempo: startingTempo, instrument: instrument, tracks: tracks, totalDuration: lastEventTime };
|
|
259
238
|
};
|
|
@@ -275,11 +254,11 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
275
254
|
return Math.round(duration*tempoChangeFactor*1000000)/1000000;
|
|
276
255
|
}
|
|
277
256
|
|
|
278
|
-
function preProcess(voices) {
|
|
257
|
+
function preProcess(voices, options) {
|
|
279
258
|
for (var i = 0; i < voices.length; i++) {
|
|
280
259
|
var voice = voices[i];
|
|
281
260
|
var ties = {};
|
|
282
|
-
var startingTempo =
|
|
261
|
+
var startingTempo = options.qpm;
|
|
283
262
|
var timeCounter = 0;
|
|
284
263
|
var tempoMultiplier = 1;
|
|
285
264
|
for (var j = 0; j < voice.length; j++) {
|
|
@@ -1257,6 +1236,42 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
1257
1236
|
start += len;
|
|
1258
1237
|
}
|
|
1259
1238
|
}
|
|
1239
|
+
|
|
1240
|
+
function findOctaves(tracks, detuneCents) {
|
|
1241
|
+
var timing = {};
|
|
1242
|
+
for (var i = 0; i < tracks.length; i++) {
|
|
1243
|
+
for (var j = 0; j < tracks[i].length; j++) {
|
|
1244
|
+
var note = tracks[i][j];
|
|
1245
|
+
if (note.cmd === "note") {
|
|
1246
|
+
if (timing[note.start] === undefined)
|
|
1247
|
+
timing[note.start] = [];
|
|
1248
|
+
timing[note.start].push({track: i, event: j, pitch: note.pitch});
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
var keys = Object.keys(timing);
|
|
1253
|
+
for (i = 0; i < keys.length; i++) {
|
|
1254
|
+
var arr = timing[keys[i]];
|
|
1255
|
+
if (arr.length > 1) {
|
|
1256
|
+
arr = arr.sort(function(a,b) {
|
|
1257
|
+
return a.pitch - b.pitch;
|
|
1258
|
+
});
|
|
1259
|
+
var topEvent = arr[arr.length-1];
|
|
1260
|
+
var topNote = topEvent.pitch % 12;
|
|
1261
|
+
var found = false;
|
|
1262
|
+
for (j = 0; !found && j < arr.length-1; j++) {
|
|
1263
|
+
if (arr[j].pitch % 12 === topNote)
|
|
1264
|
+
found = true;
|
|
1265
|
+
}
|
|
1266
|
+
if (found) {
|
|
1267
|
+
var event = tracks[topEvent.track][topEvent.event];
|
|
1268
|
+
if (!event.cents)
|
|
1269
|
+
event.cents = 0;
|
|
1270
|
+
event.cents += detuneCents;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1260
1275
|
})();
|
|
1261
1276
|
|
|
1262
1277
|
module.exports = flatten;
|