abcjs 6.4.3 → 6.5.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 +58 -1
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +445 -149
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/package.json +1 -1
- package/src/parse/abc_parse_directive.js +36 -4
- package/src/parse/abc_parse_header.js +3 -3
- package/src/parse/abc_parse_key_voice.js +41 -6
- package/src/parse/abc_parse_music.js +10 -4
- package/src/parse/abc_parse_settings.js +1 -0
- package/src/parse/abc_tokenizer.js +11 -3
- package/src/parse/abc_transpose.js +1 -1
- package/src/parse/transpose-chord.js +9 -1
- package/src/parse/tune-builder.js +14 -2
- package/src/str/output.js +4 -4
- package/src/synth/abc_midi_flattener.js +61 -21
- package/src/synth/abc_midi_sequencer.js +26 -6
- package/src/synth/chord-track.js +1 -1
- package/src/synth/create-synth.js +74 -65
- package/src/synth/place-note.js +1 -1
- package/src/test/abc_midi_sequencer_lint.js +1 -1
- package/src/test/abc_parser_lint.js +1 -1
- package/src/write/creation/abstract-engraver.js +11 -7
- package/src/write/creation/decoration.js +2 -0
- package/src/write/creation/elements/absolute-element.js +27 -16
- package/src/write/creation/elements/free-text.js +6 -1
- package/src/write/draw/draw.js +8 -0
- package/src/write/draw/ending.js +12 -3
- package/src/write/draw/text.js +8 -1
- package/src/write/interactive/selection.js +7 -1
- package/src/write/svg.js +23 -2
- package/types/index.d.ts +1 -1
- package/version.js +1 -1
package/package.json
CHANGED
|
@@ -938,14 +938,27 @@ var parseDirective = {};
|
|
|
938
938
|
}
|
|
939
939
|
multilineVars.currBarNumber = tuneBuilder.setBarNumberImmediate(tokens[0].intt);
|
|
940
940
|
break;
|
|
941
|
+
case "keywarn":
|
|
942
|
+
if (tokens.length !== 1 || tokens[0].type !== 'number' || (tokens[0].intt !== 1 && tokens[0].intt !== 0)) {
|
|
943
|
+
return 'Directive ' + cmd + ' requires 0 or 1 as a parameter.';
|
|
944
|
+
}
|
|
945
|
+
multilineVars[cmd] = tokens[0].intt === 1
|
|
946
|
+
break;
|
|
941
947
|
case "begintext":
|
|
942
948
|
var textBlock = '';
|
|
943
949
|
line = tokenizer.nextLine();
|
|
944
950
|
while(line && line.indexOf('%%endtext') !== 0) {
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
951
|
+
// MAE 9 May 2025 - for text blocks with just white space
|
|
952
|
+
if (parseCommon.startsWith(line, "%%")){
|
|
953
|
+
|
|
954
|
+
var theLine = line.substring(2);
|
|
955
|
+
theLine = theLine.trim() + "\n";
|
|
956
|
+
textBlock += theLine;
|
|
957
|
+
|
|
958
|
+
}
|
|
959
|
+
else{
|
|
960
|
+
textBlock += line.trim() + "\n";
|
|
961
|
+
}
|
|
949
962
|
line = tokenizer.nextLine();
|
|
950
963
|
}
|
|
951
964
|
tuneBuilder.addText(textBlock, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+textBlock.length+7});
|
|
@@ -1119,6 +1132,17 @@ var parseDirective = {};
|
|
|
1119
1132
|
}
|
|
1120
1133
|
break;
|
|
1121
1134
|
|
|
1135
|
+
case "maxstaves":
|
|
1136
|
+
var nStaves = tokenizer.getInt(restOfString)
|
|
1137
|
+
if (nStaves.digits === 0)
|
|
1138
|
+
warn("Expected number of staves in maxstaves")
|
|
1139
|
+
else{
|
|
1140
|
+
if (nStaves.value > 0){
|
|
1141
|
+
tune.formatting.maxStaves = nStaves.value;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
break;
|
|
1145
|
+
|
|
1122
1146
|
case "newpage":
|
|
1123
1147
|
var pgNum = tokenizer.getInt(restOfString);
|
|
1124
1148
|
tuneBuilder.addNewPage(pgNum.digits === 0 ? -1 : pgNum.value);
|
|
@@ -1179,6 +1203,14 @@ var parseDirective = {};
|
|
|
1179
1203
|
}
|
|
1180
1204
|
break;
|
|
1181
1205
|
|
|
1206
|
+
case "visualtranspose":
|
|
1207
|
+
var halfSteps = tokenizer.getInt(restOfString)
|
|
1208
|
+
if (halfSteps.digits === 0)
|
|
1209
|
+
warn("Expected number of half steps in visualTranspose")
|
|
1210
|
+
else
|
|
1211
|
+
multilineVars.globalTranspose = halfSteps.value
|
|
1212
|
+
break;
|
|
1213
|
+
|
|
1182
1214
|
case "map":
|
|
1183
1215
|
case "playtempo":
|
|
1184
1216
|
case "auquality":
|
|
@@ -429,9 +429,9 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
|
|
|
429
429
|
return [ line.length ];
|
|
430
430
|
case "K:":
|
|
431
431
|
var result = parseKeyVoice.parseKey(line.substring(i+2), tuneBuilder.hasBeginMusic());
|
|
432
|
-
if (result.foundClef && tuneBuilder.hasBeginMusic())
|
|
432
|
+
if (result.foundClef && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false)
|
|
433
433
|
tuneBuilder.appendStartingElement('clef', multilineVars.iChar + i, multilineVars.iChar + line.length, multilineVars.clef);
|
|
434
|
-
if (result.foundKey && tuneBuilder.hasBeginMusic())
|
|
434
|
+
if (result.foundKey && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false)
|
|
435
435
|
tuneBuilder.appendStartingElement('key', multilineVars.iChar + i, multilineVars.iChar + line.length, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
|
|
436
436
|
return [ line.length ];
|
|
437
437
|
case "P:":
|
|
@@ -504,7 +504,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
|
|
|
504
504
|
// since the key is the last thing that can happen in the header, we can resolve the tempo now
|
|
505
505
|
this.resolveTempo();
|
|
506
506
|
var result = parseKeyVoice.parseKey(line.substring(2), false);
|
|
507
|
-
if (!multilineVars.is_in_header && tuneBuilder.hasBeginMusic()) {
|
|
507
|
+
if (!multilineVars.is_in_header && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false) {
|
|
508
508
|
if (result.foundClef)
|
|
509
509
|
tuneBuilder.appendStartingElement('clef', startChar, endChar, multilineVars.clef);
|
|
510
510
|
if (result.foundKey)
|
|
@@ -292,7 +292,7 @@ var parseKeyVoice = {};
|
|
|
292
292
|
if (isInline)
|
|
293
293
|
multilineVars.globalTransposeOrigKeySig = savedOrigKey
|
|
294
294
|
multilineVars.key.mode = mode;
|
|
295
|
-
if (oldKey) {
|
|
295
|
+
if (oldKey && multilineVars.keywarn !== false) {
|
|
296
296
|
// Add natural in all places that the old key had an accidental.
|
|
297
297
|
var kk;
|
|
298
298
|
for (var k = 0; k < multilineVars.key.accidentals.length; k++) {
|
|
@@ -644,13 +644,48 @@ var parseKeyVoice = {};
|
|
|
644
644
|
case 'tenor,,':
|
|
645
645
|
case 'alto,,':
|
|
646
646
|
case 'none,,':
|
|
647
|
+
// MAE 26 May 2025 Start of additional clefs
|
|
648
|
+
case 'treble+8':
|
|
649
|
+
case 'treble-8':
|
|
650
|
+
case 'treble^8':
|
|
651
|
+
case 'treble_8':
|
|
652
|
+
case 'treble1':
|
|
653
|
+
case 'treble2':
|
|
654
|
+
case 'treble3':
|
|
655
|
+
case 'treble4':
|
|
656
|
+
case 'treble5':
|
|
657
|
+
case 'bass+8':
|
|
658
|
+
case 'bass-8':
|
|
659
|
+
case 'bass^8':
|
|
660
|
+
case 'bass_8':
|
|
661
|
+
case 'bass+16':
|
|
662
|
+
case 'bass-16':
|
|
663
|
+
case 'bass^16':
|
|
664
|
+
case 'bass_16':
|
|
665
|
+
case 'bass1':
|
|
666
|
+
case 'bass2':
|
|
667
|
+
case 'bass3':
|
|
668
|
+
case 'bass4':
|
|
669
|
+
case 'bass5':
|
|
670
|
+
case 'tenor1':
|
|
671
|
+
case 'tenor2':
|
|
672
|
+
case 'tenor3':
|
|
673
|
+
case 'tenor4':
|
|
674
|
+
case 'tenor5':
|
|
675
|
+
case 'alto1':
|
|
676
|
+
case 'alto2':
|
|
677
|
+
case 'alto3':
|
|
678
|
+
case 'alto4':
|
|
679
|
+
case 'alto5':
|
|
680
|
+
case 'alto+8':
|
|
681
|
+
case 'alto-8':
|
|
682
|
+
case 'alto^8':
|
|
683
|
+
case 'alto_8':
|
|
684
|
+
// MAE 26 May 2025 End of additional clefs
|
|
685
|
+
|
|
647
686
|
// TODO-PER: handle the octave indicators on the clef by changing the middle property
|
|
648
687
|
var oct2 = 0;
|
|
649
|
-
|
|
650
|
-
// if (token.token[iii] === ',') oct2 -= 7;
|
|
651
|
-
// else if (token.token[iii] === "'") oct2 += 7;
|
|
652
|
-
// }
|
|
653
|
-
staffInfo.clef = token.token.replace(/[',]/g, ""); //'//comment for emacs formatting of regexp
|
|
688
|
+
staffInfo.clef = token.token.replace(/[',]/g, ""); //'//comment for emacs formatting of regexp
|
|
654
689
|
staffInfo.verticalPos = calcMiddle(staffInfo.clef, oct2);
|
|
655
690
|
multilineVars.voices[id].clef = token.token;
|
|
656
691
|
break;
|
|
@@ -294,8 +294,7 @@ MusicParser.prototype.parseMusic = function(line) {
|
|
|
294
294
|
else if (bar.endEnding)
|
|
295
295
|
multilineVars.barFirstEndingNum = undefined;
|
|
296
296
|
if (bar.type !== 'bar_invisible' && multilineVars.measureNotEmpty) {
|
|
297
|
-
|
|
298
|
-
if (isFirstVoice) {
|
|
297
|
+
if (isFirstVoice()) {
|
|
299
298
|
multilineVars.currBarNumber++;
|
|
300
299
|
if (multilineVars.barNumbers && multilineVars.currBarNumber % multilineVars.barNumbers === 0)
|
|
301
300
|
bar.barNumber = multilineVars.currBarNumber;
|
|
@@ -510,6 +509,8 @@ MusicParser.prototype.parseMusic = function(line) {
|
|
|
510
509
|
if (el.startTie !== undefined) el.pitches[0].startTie = el.startTie;
|
|
511
510
|
} else {
|
|
512
511
|
el.rest = core.rest;
|
|
512
|
+
if (core.rest.type === 'multimeasure' && isFirstVoice())
|
|
513
|
+
multilineVars.currBarNumber += core.rest.text - 1 // The minus one is because the measure with the rest is already counted once normally.
|
|
513
514
|
if (core.endSlur !== undefined) el.endSlur = core.endSlur;
|
|
514
515
|
if (core.endTie !== undefined) el.rest.endTie = core.endTie;
|
|
515
516
|
if (core.startSlur !== undefined) el.startSlur = core.startSlur;
|
|
@@ -834,6 +835,8 @@ var letter_to_accent = function(line, i) {
|
|
|
834
835
|
case 'R':return [1, 'roll'];
|
|
835
836
|
case 'S':return [1, 'segno'];
|
|
836
837
|
case 'T':return [1, 'trill'];
|
|
838
|
+
case 't':return [1, 'trillh'];
|
|
839
|
+
|
|
837
840
|
}
|
|
838
841
|
return [0, 0];
|
|
839
842
|
};
|
|
@@ -1030,8 +1033,7 @@ MusicParser.prototype.startNewLine = function() {
|
|
|
1030
1033
|
params.currentVoiceName = voices[mv]
|
|
1031
1034
|
}
|
|
1032
1035
|
}
|
|
1033
|
-
|
|
1034
|
-
if (multilineVars.barNumbers === 0 && isFirstVoice && multilineVars.currBarNumber !== 1)
|
|
1036
|
+
if (multilineVars.barNumbers === 0 && isFirstVoice() && multilineVars.currBarNumber !== 1)
|
|
1035
1037
|
params.barNumber = multilineVars.currBarNumber;
|
|
1036
1038
|
tuneBuilder.startNewLine(params);
|
|
1037
1039
|
if (multilineVars.key.impliedNaturals)
|
|
@@ -1315,4 +1317,8 @@ var getBrokenRhythm = function(line, index) {
|
|
|
1315
1317
|
return null;
|
|
1316
1318
|
};
|
|
1317
1319
|
|
|
1320
|
+
function isFirstVoice() {
|
|
1321
|
+
return multilineVars.currentVoice === undefined || (multilineVars.currentVoice.staffNum === 0 && multilineVars.currentVoice.index === 0);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1318
1324
|
module.exports = MusicParser;
|
|
@@ -677,9 +677,17 @@ function getTitleNumber(str){
|
|
|
677
677
|
}
|
|
678
678
|
|
|
679
679
|
var thePatterns = [
|
|
680
|
-
{ match: /,\s*
|
|
681
|
-
{ match: /,\s*
|
|
682
|
-
{ match: /,\s*
|
|
680
|
+
{ match: /,\s*The$/, replace: "The " },
|
|
681
|
+
{ match: /,\s*the$/, replace: "the " },
|
|
682
|
+
{ match: /,\s*A$/, replace: "A " },
|
|
683
|
+
{ match: /,\s*a$/, replace: "a " },
|
|
684
|
+
{ match: /,\s*An$/, replace: "An " },
|
|
685
|
+
{ match: /,\s*an$/, replace: "an " },
|
|
686
|
+
{ match: /,\s*Da$/, replace: "Da " },
|
|
687
|
+
{ match: /,\s*La$/, replace: "La " },
|
|
688
|
+
{ match: /,\s*Le$/, replace: "Le " },
|
|
689
|
+
{ match: /,\s*Les$/, replace: "Les " },
|
|
690
|
+
{ match: /,\s*Ye$/, replace: "Ye " },
|
|
683
691
|
]
|
|
684
692
|
|
|
685
693
|
this.theReverser = function (str) {
|
|
@@ -65,7 +65,7 @@ transpose.keySignature = function(multilineVars, keyName, root, acc, localTransp
|
|
|
65
65
|
var newKeyName = (keyName[0] === 'm' ? newKeyMinor[index] : newKey[index]);
|
|
66
66
|
var transposedKey = newKeyName + keyName;
|
|
67
67
|
var newKeySig = keyAccidentals(transposedKey);
|
|
68
|
-
if (newKeySig.length
|
|
68
|
+
if (newKeySig.length === 0 || newKeySig[0].acc === 'flat') // key of C and all keys with flats should have chords with flats
|
|
69
69
|
multilineVars.localTransposePreferFlats = true;
|
|
70
70
|
var distance = transposedKey.charCodeAt(0) - baseKey.charCodeAt(0);
|
|
71
71
|
if (multilineVars.localTranspose > 0) {
|
|
@@ -45,6 +45,14 @@ function transposeChordName(chord, steps, preferFlats, freeGCchord) {
|
|
|
45
45
|
else chord = sharpChords[index]
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
var isDim = extra1 && (extra1.indexOf('dim') >= 0 || extra1.indexOf('°') >= 0)
|
|
49
|
+
console.log(isDim, chord, extra1)
|
|
50
|
+
// We never want A#dim or D#dim
|
|
51
|
+
if (isDim && chord === 'A#') chord = 'Bb'
|
|
52
|
+
if (isDim && chord === 'D#') chord = 'Eb'
|
|
53
|
+
if (isDim && chord === 'A♯') chord = 'B♭'
|
|
54
|
+
if (isDim && chord === 'D♯') chord = 'E♭'
|
|
55
|
+
|
|
48
56
|
if (extra1)
|
|
49
57
|
chord += extra1
|
|
50
58
|
|
|
@@ -77,4 +85,4 @@ function transposeChordName(chord, steps, preferFlats, freeGCchord) {
|
|
|
77
85
|
return chord;
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
module.exports = transposeChordName
|
|
88
|
+
module.exports = transposeChordName
|
|
@@ -9,7 +9,7 @@ var TuneBuilder = function (tune) {
|
|
|
9
9
|
tune.reset();
|
|
10
10
|
|
|
11
11
|
this.setVisualTranspose = function (visualTranspose) {
|
|
12
|
-
if (visualTranspose)
|
|
12
|
+
if (visualTranspose!==undefined)
|
|
13
13
|
tune.visualTranspose = visualTranspose;
|
|
14
14
|
};
|
|
15
15
|
|
|
@@ -382,7 +382,7 @@ var TuneBuilder = function (tune) {
|
|
|
382
382
|
|
|
383
383
|
this.getCurrentVoice = function () {
|
|
384
384
|
//console.log("getCurrentVoice", tune.lineNum)
|
|
385
|
-
var currLine = tune.lines
|
|
385
|
+
var currLine = getPrevMusicLine(tune.lines, tune.lineNum)
|
|
386
386
|
if (!currLine)
|
|
387
387
|
return null;
|
|
388
388
|
var currStaff = currLine.staff[tune.staffNum];
|
|
@@ -803,6 +803,18 @@ function wrapMusicLines(lines, barsperstaff) {
|
|
|
803
803
|
return false;
|
|
804
804
|
}
|
|
805
805
|
|
|
806
|
+
function getPrevMusicLine(lines, currentLine) {
|
|
807
|
+
if (lines.length <= currentLine)
|
|
808
|
+
return null
|
|
809
|
+
// If the current line doesn't have music, search backwards until one is found.
|
|
810
|
+
while (currentLine >= 0) {
|
|
811
|
+
if (lines[currentLine].staff)
|
|
812
|
+
return lines[currentLine];
|
|
813
|
+
currentLine--;
|
|
814
|
+
}
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
|
|
806
818
|
function getNextMusicLine(lines, currentLine) {
|
|
807
819
|
currentLine++;
|
|
808
820
|
while (lines.length > currentLine) {
|
package/src/str/output.js
CHANGED
|
@@ -58,12 +58,12 @@ var strTranspose;
|
|
|
58
58
|
var count = arr[0].length
|
|
59
59
|
for (var i = 1; i < arr.length; i++) {
|
|
60
60
|
var segment = arr[i]
|
|
61
|
-
var match = segment.match(/^( *)([A-G])([#b]?)(\w*)/)
|
|
61
|
+
var match = segment.match(/^( *)([A-G])([#b]?)( ?)(\w*)/)
|
|
62
62
|
if (match) {
|
|
63
63
|
var start = count + 2 + match[1].length // move past the 'K:' and optional white space
|
|
64
|
-
var key = match[2] + match[3] + match[4] // key name, accidental, and mode
|
|
65
|
-
var destinationKey = newKey({ root: match[2], acc: match[3], mode: match[
|
|
66
|
-
var dest = destinationKey.root + destinationKey.acc + destinationKey.mode
|
|
64
|
+
var key = match[2] + match[3] + match[4] + match[5] // key name, accidental, optional space, and mode
|
|
65
|
+
var destinationKey = newKey({ root: match[2], acc: match[3], mode: match[5] }, steps)
|
|
66
|
+
var dest = destinationKey.root + destinationKey.acc + match[4] + destinationKey.mode
|
|
67
67
|
changes.push({ start: start, end: start + key.length, note: dest })
|
|
68
68
|
}
|
|
69
69
|
count += segment.length + 2
|
|
@@ -35,6 +35,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
35
35
|
var stressBeat1 = 105;
|
|
36
36
|
var stressBeatDown = 95;
|
|
37
37
|
var stressBeatUp = 85;
|
|
38
|
+
var volumesPerNotePitch = [[stressBeat1, stressBeatDown, stressBeatUp]];
|
|
38
39
|
var beatFraction = 0.25;
|
|
39
40
|
var nextVolume;
|
|
40
41
|
var nextVolumeDelta;
|
|
@@ -77,6 +78,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
77
78
|
stressBeat1 = 105;
|
|
78
79
|
stressBeatDown = 95;
|
|
79
80
|
stressBeatUp = 85;
|
|
81
|
+
volumesPerNotePitch = [];
|
|
80
82
|
beatFraction = 0.25;
|
|
81
83
|
nextVolume = undefined;
|
|
82
84
|
nextVolumeDelta = undefined;
|
|
@@ -193,6 +195,10 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
193
195
|
stressBeat1 = element.beats[0];
|
|
194
196
|
stressBeatDown = element.beats[1];
|
|
195
197
|
stressBeatUp = element.beats[2];
|
|
198
|
+
if (!element.volumesPerNotePitch)
|
|
199
|
+
volumesPerNotePitch = []
|
|
200
|
+
else
|
|
201
|
+
volumesPerNotePitch = element.volumesPerNotePitch;
|
|
196
202
|
// TODO-PER: also use the last parameter - which changes which beats are strong.
|
|
197
203
|
break;
|
|
198
204
|
case "vol":
|
|
@@ -343,28 +349,35 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
343
349
|
return distanceFromStart / beatLength;
|
|
344
350
|
}
|
|
345
351
|
|
|
346
|
-
function processVolume(beat, voiceOff) {
|
|
352
|
+
function processVolume(beat, voiceOff, pitchIndexOfNote) {
|
|
347
353
|
if (voiceOff)
|
|
348
354
|
return 0;
|
|
349
|
-
|
|
355
|
+
let pitchStressBeat1 = stressBeat1;
|
|
356
|
+
let pitchStressBeatDown = stressBeatDown;
|
|
357
|
+
let pitchStressBeatUp = stressBeatUp;
|
|
358
|
+
if(pitchIndexOfNote !== undefined && volumesPerNotePitch.length >= pitchIndexOfNote+1){
|
|
359
|
+
pitchStressBeat1 = volumesPerNotePitch[pitchIndexOfNote][0];
|
|
360
|
+
pitchStressBeatDown = volumesPerNotePitch[pitchIndexOfNote][1];
|
|
361
|
+
pitchStressBeatUp = volumesPerNotePitch[pitchIndexOfNote][2];
|
|
362
|
+
}
|
|
350
363
|
var volume;
|
|
351
364
|
// MAE 21 Jun 2024 - This previously wasn't allowing zero volume to be applied
|
|
352
|
-
if (nextVolume
|
|
365
|
+
if (nextVolume !== undefined) {
|
|
353
366
|
volume = nextVolume;
|
|
354
367
|
nextVolume = undefined;
|
|
355
368
|
} else if (!doBeatAccents) {
|
|
356
|
-
volume =
|
|
369
|
+
volume = pitchStressBeatDown;
|
|
357
370
|
} else if (pickupLength > beat) {
|
|
358
|
-
volume =
|
|
371
|
+
volume = pitchStressBeatUp;
|
|
359
372
|
} else {
|
|
360
373
|
//var barLength = meter.num / meter.den;
|
|
361
374
|
var barBeat = calcBeat(lastBarTime, getBeatFraction(meter), beat);
|
|
362
375
|
if (barBeat === 0)
|
|
363
|
-
volume =
|
|
376
|
+
volume = pitchStressBeat1;
|
|
364
377
|
else if (parseInt(barBeat,10) === barBeat)
|
|
365
|
-
volume =
|
|
378
|
+
volume = pitchStressBeatDown;
|
|
366
379
|
else
|
|
367
|
-
volume =
|
|
380
|
+
volume = pitchStressBeatUp;
|
|
368
381
|
}
|
|
369
382
|
if (nextVolumeDelta) {
|
|
370
383
|
volume += nextVolumeDelta;
|
|
@@ -393,13 +406,17 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
393
406
|
else if (elem.decoration[d] === 'lowermordent')
|
|
394
407
|
ret.noteModification = "lowermordent";
|
|
395
408
|
else if (elem.decoration[d] === 'uppermordent')
|
|
396
|
-
ret.noteModification = "
|
|
409
|
+
ret.noteModification = "pralltriller";
|
|
397
410
|
else if (elem.decoration[d] === 'mordent')
|
|
398
411
|
ret.noteModification = "mordent";
|
|
399
412
|
else if (elem.decoration[d] === 'turn')
|
|
400
413
|
ret.noteModification = "turn";
|
|
401
414
|
else if (elem.decoration[d] === 'roll')
|
|
402
415
|
ret.noteModification = "roll";
|
|
416
|
+
else if (elem.decoration[d] === 'pralltriller')
|
|
417
|
+
ret.noteModification = "pralltriller";
|
|
418
|
+
else if (elem.decoration[d] === 'trillh')
|
|
419
|
+
ret.noteModification = "trillh";
|
|
403
420
|
}
|
|
404
421
|
}
|
|
405
422
|
return ret;
|
|
@@ -415,39 +432,57 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
415
432
|
|
|
416
433
|
switch (noteModification) {
|
|
417
434
|
case "trill":
|
|
418
|
-
var note =
|
|
435
|
+
var note = 2;
|
|
419
436
|
while (runningDuration > 0) {
|
|
420
437
|
currentTrack.push({ cmd: 'note', pitch: p.pitch+note, volume: p.volume, start: start, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
|
|
421
|
-
note = (note ===
|
|
438
|
+
note = (note === 2) ? 0 : 2;
|
|
422
439
|
runningDuration -= shortestNote;
|
|
423
440
|
start += shortestNote;
|
|
424
441
|
}
|
|
425
442
|
break;
|
|
426
|
-
|
|
443
|
+
case "trillh":
|
|
444
|
+
var note = 1;
|
|
445
|
+
while (runningDuration > 0) {
|
|
446
|
+
currentTrack.push({
|
|
447
|
+
cmd: 'note',
|
|
448
|
+
pitch: p.pitch + note,
|
|
449
|
+
volume: p.volume,
|
|
450
|
+
start: start,
|
|
451
|
+
duration: shortestNote,
|
|
452
|
+
gap: 0,
|
|
453
|
+
instrument: currentInstrument,
|
|
454
|
+
style: 'decoration'
|
|
455
|
+
});
|
|
456
|
+
note = note === 1 ? 0 : 1;
|
|
457
|
+
runningDuration -= shortestNote;
|
|
458
|
+
start += shortestNote;
|
|
459
|
+
}
|
|
460
|
+
break;
|
|
461
|
+
case "pralltriller":
|
|
427
462
|
currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
|
|
428
463
|
runningDuration -= shortestNote;
|
|
429
464
|
start += shortestNote;
|
|
430
|
-
currentTrack.push({ cmd: 'note', pitch: p.pitch+
|
|
465
|
+
currentTrack.push({ cmd: 'note', pitch: p.pitch+2, volume: p.volume, start: start, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
|
|
431
466
|
runningDuration -= shortestNote;
|
|
432
467
|
start += shortestNote;
|
|
433
468
|
currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start, duration: runningDuration, gap: 0, instrument: currentInstrument });
|
|
434
469
|
break;
|
|
470
|
+
case "mordent":
|
|
435
471
|
case "lowermordent":
|
|
436
472
|
currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
|
|
437
473
|
runningDuration -= shortestNote;
|
|
438
474
|
start += shortestNote;
|
|
439
|
-
currentTrack.push({ cmd: 'note', pitch: p.pitch-
|
|
475
|
+
currentTrack.push({ cmd: 'note', pitch: p.pitch-2, volume: p.volume, start: start, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
|
|
440
476
|
runningDuration -= shortestNote;
|
|
441
477
|
start += shortestNote;
|
|
442
478
|
currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start, duration: runningDuration, gap: 0, instrument: currentInstrument });
|
|
443
479
|
break;
|
|
444
480
|
case "turn":
|
|
445
|
-
shortestNote = p.duration /
|
|
446
|
-
currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
|
|
447
|
-
currentTrack.push({ cmd: 'note', pitch: p.pitch
|
|
448
|
-
currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start+shortestNote*2, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
|
|
449
|
-
currentTrack.push({ cmd: 'note', pitch: p.pitch
|
|
450
|
-
currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start+shortestNote*4, duration: shortestNote, gap: 0, instrument: currentInstrument });
|
|
481
|
+
shortestNote = p.duration / 4;
|
|
482
|
+
currentTrack.push({ cmd: 'note', pitch: p.pitch+2, volume: p.volume, start: start, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
|
|
483
|
+
currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start+shortestNote, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
|
|
484
|
+
currentTrack.push({ cmd: 'note', pitch: p.pitch-1, volume: p.volume, start: start+shortestNote*2, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
|
|
485
|
+
currentTrack.push({ cmd: 'note', pitch: p.pitch, volume: p.volume, start: start+shortestNote*3, duration: shortestNote, gap: 0, instrument: currentInstrument, style: 'decoration' });
|
|
451
486
|
break;
|
|
452
487
|
case "roll":
|
|
453
488
|
while (runningDuration > 0) {
|
|
@@ -533,6 +568,11 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
533
568
|
if (elem.elem)
|
|
534
569
|
elem.elem.midiPitches = [];
|
|
535
570
|
for (var i=0; i<ePitches.length; i++) {
|
|
571
|
+
//here we can set the volume for each note in a chord, if specified
|
|
572
|
+
let pitchVelocity = velocity;
|
|
573
|
+
if(!ret.velocity && Array.isArray(elem.decoration) && elem.decoration.length > i){
|
|
574
|
+
pitchVelocity = processVolume(timeToRealTime(elem.time), voiceOff, i)
|
|
575
|
+
}
|
|
536
576
|
var note = ePitches[i];
|
|
537
577
|
if (!note)
|
|
538
578
|
continue;
|
|
@@ -546,7 +586,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
546
586
|
if (name && percmap[name])
|
|
547
587
|
actualPitch = percmap[name].sound;
|
|
548
588
|
}
|
|
549
|
-
var p = { cmd: 'note', pitch: actualPitch, volume:
|
|
589
|
+
var p = { cmd: 'note', pitch: actualPitch, volume: pitchVelocity, start: timeToRealTime(elem.time), duration: durationRounded(note.duration), instrument: currentInstrument, startChar: elem.elem.startChar, endChar: elem.elem.endChar};
|
|
550
590
|
p = adjustForMicroTone(p);
|
|
551
591
|
if (elem.gracenotes) {
|
|
552
592
|
p.duration = p.duration / 2;
|
|
@@ -135,6 +135,7 @@ var parseCommon = require("../parse/abc_common");
|
|
|
135
135
|
|
|
136
136
|
// visit each voice completely in turn
|
|
137
137
|
var voices = [];
|
|
138
|
+
var clefTransposeActive = []
|
|
138
139
|
var inCrescendo = [];
|
|
139
140
|
var inDiminuendo = [];
|
|
140
141
|
var durationCounter = [0];
|
|
@@ -188,12 +189,24 @@ var parseCommon = require("../parse/abc_common");
|
|
|
188
189
|
if (staff.clef && staff.clef.type !== "perc" && staff.clef.transpose) {
|
|
189
190
|
staff.clef.el_type = 'clef';
|
|
190
191
|
voices[voiceNumber].push({ el_type: 'transpose', transpose: staff.clef.transpose });
|
|
192
|
+
clefTransposeActive[voiceNumber] = false
|
|
191
193
|
}
|
|
192
194
|
if (staff.clef && staff.clef.type) {
|
|
193
|
-
if (staff.clef.type.indexOf("-8") >= 0)
|
|
194
|
-
voices[voiceNumber].push({
|
|
195
|
-
|
|
196
|
-
|
|
195
|
+
if (staff.clef.type.indexOf("-8") >= 0) {
|
|
196
|
+
voices[voiceNumber].push({el_type: 'transpose', transpose: -12});
|
|
197
|
+
clefTransposeActive[voiceNumber] = true
|
|
198
|
+
}
|
|
199
|
+
else if (staff.clef.type.indexOf("+8") >= 0) {
|
|
200
|
+
voices[voiceNumber].push({el_type: 'transpose', transpose: 12});
|
|
201
|
+
clefTransposeActive[voiceNumber] = true
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
// if we had a previous treble+8 and now have a regular clef, then cancel the transposition
|
|
205
|
+
if (clefTransposeActive[voiceNumber]) {
|
|
206
|
+
voices[voiceNumber].push({ el_type: 'transpose', transpose: 0 });
|
|
207
|
+
clefTransposeActive[voiceNumber] = false
|
|
208
|
+
}
|
|
209
|
+
}
|
|
197
210
|
}
|
|
198
211
|
|
|
199
212
|
if (abctune.formatting.midi && abctune.formatting.midi.drumoff) {
|
|
@@ -429,7 +442,7 @@ var parseCommon = require("../parse/abc_common");
|
|
|
429
442
|
}
|
|
430
443
|
|
|
431
444
|
function setDynamics(elem) {
|
|
432
|
-
var volumes = {
|
|
445
|
+
var volumes = {//stressBeat1, stressBeatDown, stressBeatUp
|
|
433
446
|
'pppp': [15, 10, 5, 1],
|
|
434
447
|
'ppp': [30, 20, 10, 1],
|
|
435
448
|
'pp': [45, 35, 20, 1],
|
|
@@ -467,7 +480,14 @@ var parseCommon = require("../parse/abc_common");
|
|
|
467
480
|
|
|
468
481
|
if (dynamicType) {
|
|
469
482
|
currentVolume = volumes[dynamicType].slice(0);
|
|
470
|
-
|
|
483
|
+
let volumesPerNotePitch = [currentVolume];
|
|
484
|
+
if(Array.isArray(elem.decoration)){
|
|
485
|
+
volumesPerNotePitch = [];
|
|
486
|
+
elem.decoration.forEach(d=>{
|
|
487
|
+
volumesPerNotePitch.push(volumes[d].slice(0));
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
voices[voiceNumber].push({ el_type: 'beat', beats: currentVolume.slice(0), volumesPerNotePitch: volumesPerNotePitch, });
|
|
471
491
|
inCrescendo[k] = false;
|
|
472
492
|
inDiminuendo[k] = false;
|
|
473
493
|
}
|
package/src/synth/chord-track.js
CHANGED
|
@@ -183,7 +183,7 @@ ChordTrack.prototype.interpretChord = function (name) {
|
|
|
183
183
|
return { chick: [] };
|
|
184
184
|
var root = name.substring(0, 1);
|
|
185
185
|
if (root === '(') {
|
|
186
|
-
name = name.substring(1, name.length -
|
|
186
|
+
name = name.substring(1, name.length - 1);
|
|
187
187
|
if (name.length === 0)
|
|
188
188
|
return undefined;
|
|
189
189
|
root = name.substring(0, 1);
|