abcjs 6.3.0 → 6.4.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/README.md +4 -0
- package/RELEASE.md +44 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +1028 -622
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/index.js +1 -0
- package/package.json +1 -1
- package/src/api/tune-metrics.js +18 -0
- package/src/data/abc_tune.js +13 -2
- package/src/edit/abc_editarea.js +4 -1
- package/src/parse/abc_parse.js +2 -0
- package/src/parse/abc_parse_directive.js +6 -0
- package/src/synth/abc_midi_flattener.js +40 -462
- package/src/synth/abc_midi_sequencer.js +25 -10
- package/src/synth/chord-track.js +562 -0
- package/src/synth/create-note-map.js +2 -1
- package/src/synth/create-synth.js +91 -42
- package/src/test/abc_parser_lint.js +1 -0
- package/src/write/creation/abstract-engraver.js +4 -1
- package/src/write/creation/decoration.js +3 -2
- package/src/write/creation/elements/tie-element.js +23 -0
- package/src/write/draw/draw.js +1 -1
- package/src/write/engraver-controller.js +9 -5
- package/src/write/interactive/create-analysis.js +50 -0
- package/src/write/interactive/find-selectable-element.js +24 -0
- package/src/write/interactive/selection.js +5 -45
- package/src/write/layout/layout-in-grid.js +83 -0
- package/src/write/layout/layout.js +29 -24
- package/src/write/layout/set-upper-and-lower-elements.js +2 -0
- package/src/write/layout/staff-group.js +2 -2
- package/src/write/layout/voice-elements.js +1 -1
- package/src/write/layout/voice.js +1 -1
- package/src/write/renderer.js +3 -0
- package/temp.txt +1 -48
- package/types/index.d.ts +96 -32
- package/version.js +1 -1
- package/abc2xml_239/abc2xml.html +0 -769
- package/abc2xml_239/abc2xml.py +0 -2248
- package/abc2xml_239/abc2xml_changelog.html +0 -124
- package/abc2xml_239/lazy-river.abc +0 -26
- package/abc2xml_239/lazy-river.xml +0 -3698
- package/abc2xml_239/mean-to-me.abc +0 -22
- package/abc2xml_239/mean-to-me.xml +0 -2954
- package/abc2xml_239/pyparsing.py +0 -3672
- package/abc2xml_239/pyparsing.pyc +0 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// It also extracts guitar chords to a separate voice and resolves their rhythm.
|
|
6
6
|
|
|
7
7
|
var flatten;
|
|
8
|
-
var
|
|
8
|
+
var ChordTrack = require("./chord-track");
|
|
9
9
|
var pitchesToPerc = require('./pitches-to-perc');
|
|
10
10
|
|
|
11
11
|
(function() {
|
|
@@ -26,23 +26,11 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
26
26
|
var lastNoteDurationPosition;
|
|
27
27
|
var currentTrackName;
|
|
28
28
|
var lastEventTime;
|
|
29
|
+
var chordTrack;
|
|
29
30
|
|
|
30
31
|
var meter = { num: 4, den: 4 };
|
|
31
|
-
var chordTrack;
|
|
32
|
-
var chordSourceTrack;
|
|
33
|
-
var chordTrackFinished;
|
|
34
|
-
var chordChannel;
|
|
35
|
-
var bassInstrument = 0;
|
|
36
|
-
var chordInstrument = 0;
|
|
37
32
|
var drumInstrument = 128;
|
|
38
|
-
var boomVolume = 64;
|
|
39
|
-
var chickVolume = 48;
|
|
40
|
-
var currentChords;
|
|
41
|
-
var lastChord;
|
|
42
|
-
var chordLastBar;
|
|
43
33
|
var lastBarTime;
|
|
44
|
-
var gChordTacet = false;
|
|
45
|
-
var hasRhythmHead = false;
|
|
46
34
|
var doBeatAccents = true;
|
|
47
35
|
var stressBeat1 = 105;
|
|
48
36
|
var stressBeatDown = 95;
|
|
@@ -83,21 +71,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
83
71
|
lastEventTime = 0;
|
|
84
72
|
percmap = percmap_;
|
|
85
73
|
|
|
86
|
-
// For resolving chords.
|
|
87
74
|
meter = { num: 4, den: 4 };
|
|
88
|
-
chordTrack = [];
|
|
89
|
-
chordSourceTrack = false;
|
|
90
|
-
chordChannel = voices.length; // first free channel for chords
|
|
91
|
-
chordTrackFinished = false;
|
|
92
|
-
currentChords = [];
|
|
93
|
-
bassInstrument = midiOptions.bassprog && midiOptions.bassprog.length === 1 ? midiOptions.bassprog[0] : 0;
|
|
94
|
-
chordInstrument = midiOptions.chordprog && midiOptions.chordprog.length === 1 ? midiOptions.chordprog[0] : 0;
|
|
95
|
-
boomVolume = midiOptions.bassvol && midiOptions.bassvol.length === 1 ? midiOptions.bassvol[0] : 64;
|
|
96
|
-
chickVolume = midiOptions.chordvol && midiOptions.chordvol.length === 1 ? midiOptions.chordvol[0] : 48;
|
|
97
|
-
lastChord = undefined;
|
|
98
|
-
chordLastBar = undefined;
|
|
99
|
-
gChordTacet = options.chordsOff ? true : false;
|
|
100
|
-
hasRhythmHead = false;
|
|
101
75
|
|
|
102
76
|
doBeatAccents = true;
|
|
103
77
|
stressBeat1 = 105;
|
|
@@ -117,16 +91,31 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
117
91
|
if (voices.length > 0 && voices[0].length > 0)
|
|
118
92
|
pickupLength = voices[0][0].pickupLength;
|
|
119
93
|
|
|
94
|
+
// For resolving chords.
|
|
95
|
+
if (options.bassprog !== undefined && !midiOptions.bassprog)
|
|
96
|
+
midiOptions.bassprog = [options.bassprog]
|
|
97
|
+
if (options.bassvol !== undefined && !midiOptions.bassvol)
|
|
98
|
+
midiOptions.bassvol = [options.bassvol]
|
|
99
|
+
if (options.chordprog !== undefined && !midiOptions.chordprog)
|
|
100
|
+
midiOptions.chordprog = [options.chordprog]
|
|
101
|
+
if (options.chordvol !== undefined && !midiOptions.chordvol)
|
|
102
|
+
midiOptions.chordvol = [options.chordvol]
|
|
103
|
+
if (options.gchord !== undefined && !midiOptions.gchord)
|
|
104
|
+
midiOptions.gchord = [options.gchord]
|
|
105
|
+
chordTrack = new ChordTrack(voices.length, options.chordsOff, midiOptions, meter)
|
|
106
|
+
|
|
120
107
|
// First adjust the input to resolve ties, set the starting time for each note, etc. That will make the rest of the logic easier
|
|
121
108
|
preProcess(voices, options);
|
|
122
109
|
|
|
123
110
|
for (var i = 0; i < voices.length; i++) {
|
|
124
111
|
transpose = 0;
|
|
112
|
+
chordTrack.setTranspose(transpose)
|
|
125
113
|
lastNoteDurationPosition = -1;
|
|
126
114
|
var voice = voices[i];
|
|
127
115
|
currentTrack = [{ cmd: 'program', channel: i, instrument: instrument }];
|
|
128
116
|
currentTrackName = undefined;
|
|
129
117
|
lastBarTime = 0;
|
|
118
|
+
chordTrack.setLastBarTime(0)
|
|
130
119
|
var voiceOff = false;
|
|
131
120
|
if (options.voicesOff === true)
|
|
132
121
|
voiceOff = true;
|
|
@@ -139,9 +128,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
139
128
|
currentTrackName = {cmd: 'text', type: "name", text: element.trackName };
|
|
140
129
|
break;
|
|
141
130
|
case "note":
|
|
142
|
-
|
|
143
|
-
if (setChordTrack)
|
|
144
|
-
chordSourceTrack = i;
|
|
131
|
+
writeNote(element, voiceOff);
|
|
145
132
|
break;
|
|
146
133
|
case "key":
|
|
147
134
|
accidentals = setKeySignature(element);
|
|
@@ -150,6 +137,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
150
137
|
if (!startingMeter)
|
|
151
138
|
startingMeter = element;
|
|
152
139
|
meter = element;
|
|
140
|
+
chordTrack.setMeter(meter)
|
|
153
141
|
beatFraction = getBeatFraction(meter);
|
|
154
142
|
alignDrumToMeter();
|
|
155
143
|
break;
|
|
@@ -158,21 +146,21 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
158
146
|
startingTempo = element.qpm;
|
|
159
147
|
else
|
|
160
148
|
tempoChangeFactor = element.qpm ? startingTempo / element.qpm : 1;
|
|
149
|
+
chordTrack.setTempoChangeFactor(tempoChangeFactor)
|
|
161
150
|
break;
|
|
162
151
|
case "transpose":
|
|
163
152
|
transpose = element.transpose;
|
|
153
|
+
chordTrack.setTranspose(transpose)
|
|
164
154
|
break;
|
|
165
155
|
case "bar":
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
currentChords = [];
|
|
169
|
-
}
|
|
156
|
+
chordTrack.barEnd(element)
|
|
157
|
+
|
|
170
158
|
barAccidentals = [];
|
|
171
159
|
if (i === 0) // Only write the drum part on the first voice so that it is not duplicated.
|
|
172
160
|
writeDrum(voices.length+1);
|
|
173
|
-
|
|
174
|
-
chordLastBar = lastChord;
|
|
161
|
+
chordTrack.setRhythmHead(false) // decide whether there are rhythm heads each measure.
|
|
175
162
|
lastBarTime = timeToRealTime(element.time);
|
|
163
|
+
chordTrack.setLastBarTime(lastBarTime)
|
|
176
164
|
break;
|
|
177
165
|
case "bagpipes":
|
|
178
166
|
bagpipes = true;
|
|
@@ -198,9 +186,8 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
198
186
|
drumDefinition = normalizeDrumDefinition(element.params);
|
|
199
187
|
alignDrumToMeter();
|
|
200
188
|
break;
|
|
201
|
-
case "
|
|
202
|
-
|
|
203
|
-
gChordTacet = element.tacet;
|
|
189
|
+
case "gchordOn":
|
|
190
|
+
chordTrack.gChordOn(element)
|
|
204
191
|
break;
|
|
205
192
|
case "beat":
|
|
206
193
|
stressBeat1 = element.beats[0];
|
|
@@ -217,6 +204,13 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
217
204
|
case "beataccents":
|
|
218
205
|
doBeatAccents = element.value;
|
|
219
206
|
break;
|
|
207
|
+
case "gchord":
|
|
208
|
+
case "bassprog":
|
|
209
|
+
case "chordprog":
|
|
210
|
+
case "bassvol":
|
|
211
|
+
case "chordvol":
|
|
212
|
+
chordTrack.paramChange(element)
|
|
213
|
+
break
|
|
220
214
|
default:
|
|
221
215
|
// This should never happen
|
|
222
216
|
console.log("MIDI creation. Unknown el_type: " + element.el_type + "\n");// jshint ignore:line
|
|
@@ -228,8 +222,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
228
222
|
if (currentTrackName)
|
|
229
223
|
currentTrack.unshift(currentTrackName);
|
|
230
224
|
tracks.push(currentTrack);
|
|
231
|
-
|
|
232
|
-
chordTrackFinished = true;
|
|
225
|
+
chordTrack.finish()
|
|
233
226
|
if (drumTrack.length > 0) // Don't do drums on more than one track, so turn off drum after we create it.
|
|
234
227
|
drumTrackFinished = true;
|
|
235
228
|
}
|
|
@@ -237,8 +230,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
237
230
|
if (options.detuneOctave)
|
|
238
231
|
findOctaves(tracks, parseInt(options.detuneOctave, 10));
|
|
239
232
|
|
|
240
|
-
|
|
241
|
-
tracks.push(chordTrack);
|
|
233
|
+
chordTrack.addTrack(tracks)
|
|
242
234
|
if (drumTrack.length > 0)
|
|
243
235
|
tracks.push(drumTrack);
|
|
244
236
|
|
|
@@ -254,15 +246,6 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
254
246
|
}
|
|
255
247
|
}
|
|
256
248
|
|
|
257
|
-
function chordTrackEmpty() {
|
|
258
|
-
var isEmpty = true;
|
|
259
|
-
for (var i = 0; i < chordTrack.length && isEmpty; i++) {
|
|
260
|
-
if (chordTrack[i].cmd === 'note')
|
|
261
|
-
isEmpty = false
|
|
262
|
-
}
|
|
263
|
-
return isEmpty;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
249
|
function timeToRealTime(time) {
|
|
267
250
|
return time/1000000;
|
|
268
251
|
}
|
|
@@ -353,53 +336,6 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
353
336
|
}
|
|
354
337
|
return 0.25;
|
|
355
338
|
}
|
|
356
|
-
//
|
|
357
|
-
// The algorithm for chords is:
|
|
358
|
-
// - The chords are done in a separate track.
|
|
359
|
-
// - If there are notes before the first chord, then put that much silence to start the track.
|
|
360
|
-
// - The pattern of chord expression depends on the meter, and how many chords are in a measure.
|
|
361
|
-
// - There is a possibility that a measure will have an incorrect number of beats, if that is the case, then
|
|
362
|
-
// start the pattern anew on the next measure number.
|
|
363
|
-
// - If a chord root is not A-G, then ignore it as if the chord wasn't there at all.
|
|
364
|
-
// - If a chord modification isn't in our supported list, change it to a major triad.
|
|
365
|
-
//
|
|
366
|
-
// - If there is only one chord in a measure:
|
|
367
|
-
// - If 2/4, play root chord
|
|
368
|
-
// - If cut time, play root(1) chord(3)
|
|
369
|
-
// - If 3/4, play root chord chord
|
|
370
|
-
// - If 4/4 or common time, play root chord fifth chord
|
|
371
|
-
// - If 6/8, play root(1) chord(3) fifth(4) chord(6)
|
|
372
|
-
// - For any other meter, play the full chord on each beat. (TODO-PER: expand this as more support is added.)
|
|
373
|
-
//
|
|
374
|
-
// - If there is a chord specified that is not on a beat, move it earlier to the previous beat, unless there is already a chord on that beat.
|
|
375
|
-
// - Otherwise, move it later, unless there is already a chord on that beat.
|
|
376
|
-
// - Otherwise, ignore it. (TODO-PER: expand this as more support is added.)
|
|
377
|
-
//
|
|
378
|
-
// - If there is a chord on the second beat, play a chord for the first beat instead of a bass note.
|
|
379
|
-
// - Likewise, if there is a chord on the fourth beat of 4/4, play a chord on the third beat instead of a bass note.
|
|
380
|
-
//
|
|
381
|
-
// If there is any note in the melody that has a rhythm head, then assume the melody controls the rhythm, so that is
|
|
382
|
-
// the same as a break.
|
|
383
|
-
var breakSynonyms = [ 'break', '(break)', 'no chord', 'n.c.', 'tacet'];
|
|
384
|
-
|
|
385
|
-
function findChord(elem) {
|
|
386
|
-
if (gChordTacet)
|
|
387
|
-
return 'break';
|
|
388
|
-
|
|
389
|
-
// TODO-PER: Just using the first chord if there are more than one.
|
|
390
|
-
if (chordTrackFinished || !elem.chord || elem.chord.length === 0)
|
|
391
|
-
return null;
|
|
392
|
-
|
|
393
|
-
// Return the first annotation that is a regular chord: that is, it is in the default place or is a recognized "tacet" phrase.
|
|
394
|
-
for (var i = 0; i < elem.chord.length; i++) {
|
|
395
|
-
var ch = elem.chord[i];
|
|
396
|
-
if (ch.position === 'default')
|
|
397
|
-
return ch.name;
|
|
398
|
-
if (breakSynonyms.indexOf(ch.name.toLowerCase()) >= 0)
|
|
399
|
-
return 'break';
|
|
400
|
-
}
|
|
401
|
-
return null;
|
|
402
|
-
}
|
|
403
339
|
|
|
404
340
|
function calcBeat(measureStart, beatLength, currTime) {
|
|
405
341
|
var distanceFromStart = currTime - measureStart;
|
|
@@ -419,7 +355,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
419
355
|
} else if (pickupLength > beat) {
|
|
420
356
|
volume = stressBeatUp;
|
|
421
357
|
} else {
|
|
422
|
-
var barLength = meter.num / meter.den;
|
|
358
|
+
//var barLength = meter.num / meter.den;
|
|
423
359
|
var barBeat = calcBeat(lastBarTime, getBeatFraction(meter), beat);
|
|
424
360
|
if (barBeat === 0)
|
|
425
361
|
volume = stressBeat1;
|
|
@@ -439,28 +375,6 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
439
375
|
return voiceOff ? 0 : volume;
|
|
440
376
|
}
|
|
441
377
|
|
|
442
|
-
function processChord(elem) {
|
|
443
|
-
|
|
444
|
-
var firstChord = false;
|
|
445
|
-
var chord = findChord(elem);
|
|
446
|
-
if (chord) {
|
|
447
|
-
var c = interpretChord(chord);
|
|
448
|
-
// If this isn't a recognized chord, just completely ignore it.
|
|
449
|
-
if (c) {
|
|
450
|
-
// If we ever have a chord in this voice, then we add the chord track.
|
|
451
|
-
// However, if there are chords on more than one voice, then just use the first voice.
|
|
452
|
-
if (chordTrack.length === 0) {
|
|
453
|
-
firstChord = true;
|
|
454
|
-
chordTrack.push({cmd: 'program', channel: chordChannel, instrument: chordInstrument});
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
lastChord = c;
|
|
458
|
-
var barBeat = calcBeat(lastBarTime, getBeatFraction(meter), timeToRealTime(elem.time));
|
|
459
|
-
currentChords.push({chord: lastChord, beat: barBeat, start: timeToRealTime(elem.time)});
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
return firstChord;
|
|
463
|
-
}
|
|
464
378
|
|
|
465
379
|
function findNoteModifications(elem, velocity) {
|
|
466
380
|
var ret = { };
|
|
@@ -552,10 +466,10 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
552
466
|
// If there are guitar chords, then they are put in a separate track, but they have the same format.
|
|
553
467
|
//
|
|
554
468
|
|
|
555
|
-
var trackStartingIndex = currentTrack.length;
|
|
469
|
+
//var trackStartingIndex = currentTrack.length;
|
|
556
470
|
|
|
557
471
|
var velocity = processVolume(timeToRealTime(elem.time), voiceOff);
|
|
558
|
-
|
|
472
|
+
chordTrack.processChord(elem)
|
|
559
473
|
|
|
560
474
|
// if there are grace notes, then also play them.
|
|
561
475
|
// I'm not sure there is an exact rule for the length of the notes. My rule, unless I find
|
|
@@ -611,15 +525,7 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
611
525
|
// TODO-PER: Can also make a different sound on style=x and style=harmonic
|
|
612
526
|
var ePitches = elem.pitches;
|
|
613
527
|
if (elem.style === "rhythm") {
|
|
614
|
-
|
|
615
|
-
if (lastChord && lastChord.chick) {
|
|
616
|
-
ePitches = [];
|
|
617
|
-
for (var i2 = 0; i2 < lastChord.chick.length; i2++) {
|
|
618
|
-
var note2 = parseCommon.clone(elem.pitches[0]);
|
|
619
|
-
note2.actualPitch = lastChord.chick[i2];
|
|
620
|
-
ePitches.push(note2);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
528
|
+
ePitches = chordTrack.setRhythmHead(true, elem)
|
|
623
529
|
}
|
|
624
530
|
|
|
625
531
|
if (elem.elem)
|
|
@@ -674,8 +580,6 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
674
580
|
}
|
|
675
581
|
var realDur = getRealDuration(elem);
|
|
676
582
|
lastEventTime = Math.max(lastEventTime, timeToRealTime(elem.time)+durationRounded(realDur));
|
|
677
|
-
|
|
678
|
-
return setChordTrack;
|
|
679
583
|
}
|
|
680
584
|
function getRealDuration(elem) {
|
|
681
585
|
if (elem.pitches && elem.pitches.length > 0 && elem.pitches[0])
|
|
@@ -810,332 +714,6 @@ var pitchesToPerc = require('./pitches-to-perc');
|
|
|
810
714
|
return pitch;
|
|
811
715
|
}
|
|
812
716
|
|
|
813
|
-
var basses = {
|
|
814
|
-
'A': 33, 'B': 35, 'C': 36, 'D': 38, 'E': 40, 'F': 41, 'G': 43
|
|
815
|
-
};
|
|
816
|
-
function interpretChord(name) {
|
|
817
|
-
// chords have the format:
|
|
818
|
-
// [root][acc][modifier][/][bass][acc]
|
|
819
|
-
// (The chord might be surrounded by parens. Just ignore them.)
|
|
820
|
-
// root must be present and must be from A-G.
|
|
821
|
-
// acc is optional and can be # or b
|
|
822
|
-
// The modifier can be a wide variety of things, like "maj7". As they are discovered, more are supported here.
|
|
823
|
-
// If there is a slash, then there is a bass note, which can be from A-G, with an optional acc.
|
|
824
|
-
// If the root is unrecognized, then "undefined" is returned and there is no chord.
|
|
825
|
-
// If the modifier is unrecognized, a major triad is returned.
|
|
826
|
-
// If the bass notes is unrecognized, it is ignored.
|
|
827
|
-
if (name.length === 0)
|
|
828
|
-
return undefined;
|
|
829
|
-
if (name === 'break')
|
|
830
|
-
return { chick: []};
|
|
831
|
-
var root = name.substring(0,1);
|
|
832
|
-
if (root === '(') {
|
|
833
|
-
name = name.substring(1,name.length-2);
|
|
834
|
-
if (name.length === 0)
|
|
835
|
-
return undefined;
|
|
836
|
-
root = name.substring(0,1);
|
|
837
|
-
}
|
|
838
|
-
var bass = basses[root];
|
|
839
|
-
if (!bass) // If the bass note isn't listed, then this was an unknown root. Only A-G are accepted.
|
|
840
|
-
return undefined;
|
|
841
|
-
// Don't transpose the chords more than an octave.
|
|
842
|
-
var chordTranspose = transpose;
|
|
843
|
-
while (chordTranspose < -8)
|
|
844
|
-
chordTranspose += 12;
|
|
845
|
-
while (chordTranspose > 8)
|
|
846
|
-
chordTranspose -= 12;
|
|
847
|
-
bass += chordTranspose;
|
|
848
|
-
var bass2 = bass - 5; // The alternating bass is a 4th below
|
|
849
|
-
var chick;
|
|
850
|
-
if (name.length === 1)
|
|
851
|
-
chick = chordNotes(bass, '');
|
|
852
|
-
var remaining = name.substring(1);
|
|
853
|
-
var acc = remaining.substring(0,1);
|
|
854
|
-
if (acc === 'b' || acc === '♭') {
|
|
855
|
-
bass--;
|
|
856
|
-
bass2--;
|
|
857
|
-
remaining = remaining.substring(1);
|
|
858
|
-
} else if (acc === '#' || acc === '♯') {
|
|
859
|
-
bass++;
|
|
860
|
-
bass2++;
|
|
861
|
-
remaining = remaining.substring(1);
|
|
862
|
-
}
|
|
863
|
-
var arr = remaining.split('/');
|
|
864
|
-
chick = chordNotes(bass, arr[0]);
|
|
865
|
-
// If the 5th is altered then the bass is altered. Normally the bass is 7 from the root, so adjust if it isn't.
|
|
866
|
-
if (chick.length >= 3) {
|
|
867
|
-
var fifth = chick[2] - chick[0];
|
|
868
|
-
bass2 = bass2 + fifth - 7;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
if (arr.length === 2) {
|
|
872
|
-
var explicitBass = basses[arr[1].substring(0,1)];
|
|
873
|
-
if (explicitBass) {
|
|
874
|
-
var bassAcc = arr[1].substring(1);
|
|
875
|
-
var bassShift = {'#': 1, '♯': 1, 'b': -1, '♭': -1}[bassAcc] || 0;
|
|
876
|
-
bass = basses[arr[1].substring(0,1)] + bassShift + chordTranspose;
|
|
877
|
-
bass2 = bass;
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
return { boom: bass, boom2: bass2, chick: chick };
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
var chordIntervals = {
|
|
884
|
-
// diminished (all flat 5 chords)
|
|
885
|
-
'dim': [ 0, 3, 6 ],
|
|
886
|
-
'°': [ 0, 3, 6 ],
|
|
887
|
-
'˚': [ 0, 3, 6 ],
|
|
888
|
-
|
|
889
|
-
'dim7': [ 0, 3, 6, 9 ],
|
|
890
|
-
'°7': [ 0, 3, 6, 9 ],
|
|
891
|
-
'˚7': [ 0, 3, 6, 9 ],
|
|
892
|
-
|
|
893
|
-
'ø7': [ 0, 3, 6, 10 ],
|
|
894
|
-
'm7(b5)': [ 0, 3, 6, 10 ],
|
|
895
|
-
'm7b5': [ 0, 3, 6, 10 ],
|
|
896
|
-
'm7♭5': [ 0, 3, 6, 10 ],
|
|
897
|
-
'-7(b5)': [ 0, 3, 6, 10 ],
|
|
898
|
-
'-7b5': [ 0, 3, 6, 10 ],
|
|
899
|
-
|
|
900
|
-
'7b5': [ 0, 4, 6, 10 ],
|
|
901
|
-
'7(b5)': [ 0, 4, 6, 10 ],
|
|
902
|
-
'7♭5': [ 0, 4, 6, 10 ],
|
|
903
|
-
|
|
904
|
-
'7(b9,b5)': [ 0, 4, 6, 10, 13 ],
|
|
905
|
-
'7b9,b5': [ 0, 4, 6, 10, 13 ],
|
|
906
|
-
'7(#9,b5)': [ 0, 4, 6, 10, 15 ],
|
|
907
|
-
'7#9b5': [ 0, 4, 6, 10, 15 ],
|
|
908
|
-
'maj7(b5)': [ 0, 4, 6, 11 ],
|
|
909
|
-
'maj7b5': [ 0, 4, 6, 11 ],
|
|
910
|
-
'13(b5)': [ 0, 4, 6, 10, 14, 21 ],
|
|
911
|
-
'13b5': [ 0, 4, 6, 10, 14, 21 ],
|
|
912
|
-
|
|
913
|
-
// minor (all normal 5, minor 3 chords)
|
|
914
|
-
'm': [ 0, 3, 7 ],
|
|
915
|
-
'-': [ 0, 3, 7 ],
|
|
916
|
-
'm6': [ 0, 3, 7, 9 ],
|
|
917
|
-
'-6': [ 0, 3, 7, 9 ],
|
|
918
|
-
'm7': [ 0, 3, 7, 10 ],
|
|
919
|
-
'-7': [ 0, 3, 7, 10 ],
|
|
920
|
-
|
|
921
|
-
'-(b6)': [ 0, 3, 7, 8 ],
|
|
922
|
-
'-b6': [ 0, 3, 7, 8 ],
|
|
923
|
-
'-6/9': [ 0, 3, 7, 9, 14 ],
|
|
924
|
-
'-7(b9)': [ 0, 3, 7, 10, 13 ],
|
|
925
|
-
'-7b9': [ 0, 3, 7, 10, 13 ],
|
|
926
|
-
'-maj7': [ 0, 3, 7, 11 ],
|
|
927
|
-
'-9+7': [ 0, 3, 7, 11, 13 ],
|
|
928
|
-
'-11': [ 0, 3, 7, 11, 14, 17 ],
|
|
929
|
-
'm11': [ 0, 3, 7, 11, 14, 17 ],
|
|
930
|
-
'-maj9': [ 0, 3, 7, 11, 14 ],
|
|
931
|
-
'-∆9': [ 0, 3, 7, 11, 14 ],
|
|
932
|
-
'mM9': [ 0, 3, 7, 11, 14 ],
|
|
933
|
-
|
|
934
|
-
// major (all normal 5, major 3 chords)
|
|
935
|
-
'M': [ 0, 4, 7 ],
|
|
936
|
-
'6': [ 0, 4, 7, 9 ],
|
|
937
|
-
'6/9': [ 0, 4, 7, 9, 14 ],
|
|
938
|
-
'6add9': [ 0, 4, 7, 9, 14 ],
|
|
939
|
-
'69': [ 0, 4, 7, 9, 14 ],
|
|
940
|
-
|
|
941
|
-
'7': [ 0, 4, 7, 10 ],
|
|
942
|
-
'9': [ 0, 4, 7, 10, 14 ],
|
|
943
|
-
'11': [ 0, 7, 10, 14, 17 ],
|
|
944
|
-
'13': [ 0, 4, 7, 10, 14, 21 ],
|
|
945
|
-
'7b9': [ 0, 4, 7, 10, 13 ],
|
|
946
|
-
'7♭9': [ 0, 4, 7, 10, 13 ],
|
|
947
|
-
'7(b9)': [ 0, 4, 7, 10, 13 ],
|
|
948
|
-
'7(#9)': [ 0, 4, 7, 10, 15 ],
|
|
949
|
-
'7#9': [ 0, 4, 7, 10, 15 ],
|
|
950
|
-
'(13)': [ 0, 4, 7, 10, 14, 21 ],
|
|
951
|
-
'7(9,13)': [ 0, 4, 7, 10, 14, 21 ],
|
|
952
|
-
'7(#9,b13)': [ 0, 4, 7, 10, 15, 20 ],
|
|
953
|
-
'7(#11)': [ 0, 4, 7, 10, 14, 18 ],
|
|
954
|
-
'7#11': [ 0, 4, 7, 10, 14, 18 ],
|
|
955
|
-
'7(b13)': [ 0, 4, 7, 10, 20 ],
|
|
956
|
-
'7b13': [ 0, 4, 7, 10, 20 ],
|
|
957
|
-
'9(#11)': [ 0, 4, 7, 10, 14, 18 ],
|
|
958
|
-
'9#11': [ 0, 4, 7, 10, 14, 18 ],
|
|
959
|
-
'13(#11)': [ 0, 4, 7, 10, 18, 21 ],
|
|
960
|
-
'13#11': [ 0, 4, 7, 10, 18, 21 ],
|
|
961
|
-
|
|
962
|
-
'maj7': [ 0, 4, 7, 11 ],
|
|
963
|
-
'∆7': [ 0, 4, 7, 11 ],
|
|
964
|
-
'Δ7': [ 0, 4, 7, 11 ],
|
|
965
|
-
'maj9': [ 0, 4, 7, 11, 14 ],
|
|
966
|
-
'maj7(9)': [ 0, 4, 7, 11, 14 ],
|
|
967
|
-
'maj7(11)': [ 0, 4, 7, 11, 17 ],
|
|
968
|
-
'maj7(#11)': [ 0, 4, 7, 11, 18 ],
|
|
969
|
-
'maj7(13)': [ 0, 4, 7, 14, 21 ],
|
|
970
|
-
'maj7(9,13)': [ 0, 4, 7, 11, 14, 21 ],
|
|
971
|
-
|
|
972
|
-
'7sus4': [ 0, 5, 7, 10 ],
|
|
973
|
-
'm7sus4': [ 0, 3, 7, 10, 17 ],
|
|
974
|
-
'sus4': [ 0, 5, 7 ],
|
|
975
|
-
'sus2': [ 0, 2, 7 ],
|
|
976
|
-
'7sus2': [ 0, 2, 7, 10 ],
|
|
977
|
-
'9sus4': [ 0, 5, 7, 10, 14 ],
|
|
978
|
-
'13sus4': [ 0, 5, 7, 10, 14, 21 ],
|
|
979
|
-
|
|
980
|
-
// augmented (all sharp 5 chords)
|
|
981
|
-
'aug7': [ 0, 4, 8, 10 ],
|
|
982
|
-
'+7': [ 0, 4, 8, 10 ],
|
|
983
|
-
'+': [ 0, 4, 8 ],
|
|
984
|
-
'7#5': [ 0, 4, 8, 10 ],
|
|
985
|
-
'7♯5': [ 0, 4, 8, 10 ],
|
|
986
|
-
'7+5': [ 0, 4, 8, 10 ],
|
|
987
|
-
'9#5': [ 0, 4, 8, 10, 14 ],
|
|
988
|
-
'9♯5': [ 0, 4, 8, 10, 14 ],
|
|
989
|
-
'9+5': [ 0, 4, 8, 10, 14 ],
|
|
990
|
-
'-7(#5)': [ 0, 3, 8, 10 ],
|
|
991
|
-
'-7#5': [ 0, 3, 8, 10 ],
|
|
992
|
-
'7(#5)': [ 0, 4, 8, 10 ],
|
|
993
|
-
'7(b9,#5)': [ 0, 4, 8, 10, 13 ],
|
|
994
|
-
'7b9#5': [ 0, 4, 8, 10, 13 ],
|
|
995
|
-
'maj7(#5)': [ 0, 4, 8, 11 ],
|
|
996
|
-
'maj7#5': [ 0, 4, 8, 11 ],
|
|
997
|
-
'maj7(#5,#11)': [ 0, 4, 8, 11, 18 ],
|
|
998
|
-
'maj7#5#11': [ 0, 4, 8, 11, 18 ],
|
|
999
|
-
'9(#5)': [ 0, 4, 8, 10, 14 ],
|
|
1000
|
-
'13(#5)': [ 0, 4, 8, 10, 14, 21 ],
|
|
1001
|
-
'13#5': [ 0, 4, 8, 10, 14, 21 ]
|
|
1002
|
-
};
|
|
1003
|
-
function chordNotes(bass, modifier) {
|
|
1004
|
-
var intervals = chordIntervals[modifier];
|
|
1005
|
-
if (!intervals) {
|
|
1006
|
-
if (modifier.slice(0,2).toLowerCase() === 'ma' || modifier[0] === 'M')
|
|
1007
|
-
intervals = chordIntervals.M;
|
|
1008
|
-
else if (modifier[0] === 'm' || modifier[0] === '-')
|
|
1009
|
-
intervals = chordIntervals.m;
|
|
1010
|
-
else
|
|
1011
|
-
intervals = chordIntervals.M;
|
|
1012
|
-
}
|
|
1013
|
-
bass += 12; // the chord is an octave above the bass note.
|
|
1014
|
-
var notes = [ ];
|
|
1015
|
-
for (var i = 0; i < intervals.length; i++) {
|
|
1016
|
-
notes.push(bass + intervals[i]);
|
|
1017
|
-
}
|
|
1018
|
-
return notes;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
function writeBoom(boom, beatLength, volume, beat, noteLength) {
|
|
1022
|
-
// undefined means there is a stop time.
|
|
1023
|
-
if (boom !== undefined)
|
|
1024
|
-
chordTrack.push({cmd: 'note', pitch: boom, volume: volume, start: lastBarTime+beat*durationRounded(beatLength), duration: durationRounded(noteLength), gap: 0, instrument: bassInstrument});
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
function writeChick(chick, beatLength, volume, beat, noteLength) {
|
|
1028
|
-
for (var c = 0; c < chick.length; c++)
|
|
1029
|
-
chordTrack.push({cmd: 'note', pitch: chick[c], volume: volume, start: lastBarTime+beat*durationRounded(beatLength), duration: durationRounded(noteLength), gap: 0, instrument: chordInstrument});
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
var rhythmPatterns = { "2/2": [ 'boom', 'chick' ],
|
|
1033
|
-
"2/4": [ 'boom', 'chick' ],
|
|
1034
|
-
"3/4": [ 'boom', 'chick', 'chick' ],
|
|
1035
|
-
"4/4": [ 'boom', 'chick', 'boom2', 'chick' ],
|
|
1036
|
-
"5/4": [ 'boom', 'chick', 'chick', 'boom2', 'chick' ],
|
|
1037
|
-
"6/8": [ 'boom', '', 'chick', 'boom2', '', 'chick' ],
|
|
1038
|
-
"9/8": [ 'boom', '', 'chick', 'boom2', '', 'chick', 'boom2', '', 'chick' ],
|
|
1039
|
-
"12/8": [ 'boom', '', 'chick', 'boom2', '', 'chick', 'boom', '', 'chick', 'boom2', '', 'chick' ],
|
|
1040
|
-
};
|
|
1041
|
-
|
|
1042
|
-
function resolveChords(startTime, endTime) {
|
|
1043
|
-
var num = meter.num;
|
|
1044
|
-
var den = meter.den;
|
|
1045
|
-
var beatLength = 1/den;
|
|
1046
|
-
var noteLength = beatLength/2;
|
|
1047
|
-
var pattern = rhythmPatterns[num+'/'+den];
|
|
1048
|
-
var thisMeasureLength = parseInt(num,10)/parseInt(den,10);
|
|
1049
|
-
var portionOfAMeasure = thisMeasureLength - (endTime-startTime)/tempoChangeFactor;
|
|
1050
|
-
if (Math.abs(portionOfAMeasure) < 0.00001)
|
|
1051
|
-
portionOfAMeasure = false;
|
|
1052
|
-
if (!pattern || portionOfAMeasure) { // If it is an unsupported meter, or this isn't a full bar, just chick on each beat.
|
|
1053
|
-
pattern = [];
|
|
1054
|
-
var beatsPresent = ((endTime-startTime)/tempoChangeFactor) / beatLength;
|
|
1055
|
-
for (var p = 0; p < beatsPresent; p++)
|
|
1056
|
-
pattern.push("chick");
|
|
1057
|
-
}
|
|
1058
|
-
//console.log(startTime, pattern, currentChords, lastChord, portionOfAMeasure)
|
|
1059
|
-
|
|
1060
|
-
if (currentChords.length === 0) { // there wasn't a new chord this measure, so use the last chord declared.
|
|
1061
|
-
currentChords.push({ beat: 0, chord: lastChord});
|
|
1062
|
-
}
|
|
1063
|
-
if (currentChords[0].beat !== 0 && lastChord) { // this is the case where there is a chord declared in the measure, but not on its first beat.
|
|
1064
|
-
if (chordLastBar)
|
|
1065
|
-
currentChords.unshift({ beat: 0, chord: chordLastBar});
|
|
1066
|
-
}
|
|
1067
|
-
if (currentChords.length === 1) {
|
|
1068
|
-
for (var m = currentChords[0].beat; m < pattern.length; m++) {
|
|
1069
|
-
if (!hasRhythmHead) {
|
|
1070
|
-
switch (pattern[m]) {
|
|
1071
|
-
case 'boom':
|
|
1072
|
-
writeBoom(currentChords[0].chord.boom, beatLength, boomVolume, m, noteLength);
|
|
1073
|
-
break;
|
|
1074
|
-
case 'boom2':
|
|
1075
|
-
writeBoom(currentChords[0].chord.boom2, beatLength, boomVolume, m, noteLength);
|
|
1076
|
-
break;
|
|
1077
|
-
case 'chick':
|
|
1078
|
-
writeChick(currentChords[0].chord.chick, beatLength, chickVolume, m, noteLength);
|
|
1079
|
-
break;
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
// If we are here it is because more than one chord was declared in the measure, so we have to sort out what chord goes where.
|
|
1087
|
-
|
|
1088
|
-
// First, normalize the chords on beats.
|
|
1089
|
-
var mult = beatLength === 0.125 ? 3 : 1; // If this is a compound meter then the beats in the currentChords is 1/3 of the true beat
|
|
1090
|
-
var beats = {};
|
|
1091
|
-
for (var i = 0; i < currentChords.length; i++) {
|
|
1092
|
-
var cc = currentChords[i];
|
|
1093
|
-
var b = Math.round(cc.beat*mult);
|
|
1094
|
-
beats[''+b] = cc;
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
// - If there is a chord on the second beat, play a chord for the first beat instead of a bass note.
|
|
1098
|
-
// - Likewise, if there is a chord on the fourth beat of 4/4, play a chord on the third beat instead of a bass note.
|
|
1099
|
-
for (var m2 = 0; m2 < pattern.length; m2++) {
|
|
1100
|
-
var thisChord;
|
|
1101
|
-
if (beats[''+m2])
|
|
1102
|
-
thisChord = beats[''+m2];
|
|
1103
|
-
var lastBoom;
|
|
1104
|
-
if (!hasRhythmHead && thisChord) {
|
|
1105
|
-
switch (pattern[m2]) {
|
|
1106
|
-
case 'boom':
|
|
1107
|
-
if (beats['' + (m2 + 1)]) // If there is not a chord change on the next beat, play a bass note.
|
|
1108
|
-
writeChick(thisChord.chord.chick, beatLength, chickVolume, m2, noteLength);
|
|
1109
|
-
else {
|
|
1110
|
-
writeBoom(thisChord.chord.boom, beatLength, boomVolume, m2, noteLength);
|
|
1111
|
-
lastBoom = thisChord.chord.boom;
|
|
1112
|
-
}
|
|
1113
|
-
break;
|
|
1114
|
-
case 'boom2':
|
|
1115
|
-
if (beats['' + (m2 + 1)])
|
|
1116
|
-
writeChick(thisChord.chord.chick, beatLength, chickVolume, m2, noteLength);
|
|
1117
|
-
else {
|
|
1118
|
-
// If there is the same root as the last chord, use the alternating bass, otherwise play the root.
|
|
1119
|
-
if (lastBoom === thisChord.chord.boom) {
|
|
1120
|
-
writeBoom(thisChord.chord.boom2, beatLength, boomVolume, m2, noteLength);
|
|
1121
|
-
lastBoom = undefined;
|
|
1122
|
-
} else {
|
|
1123
|
-
writeBoom(thisChord.chord.boom, beatLength, boomVolume, m2, noteLength);
|
|
1124
|
-
lastBoom = thisChord.chord.boom;
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
break;
|
|
1128
|
-
case 'chick':
|
|
1129
|
-
writeChick(thisChord.chord.chick, beatLength, chickVolume, m2, noteLength);
|
|
1130
|
-
break;
|
|
1131
|
-
case '':
|
|
1132
|
-
if (beats['' + m2]) // If there is an explicit chord on this beat, play it.
|
|
1133
|
-
writeChick(thisChord.chord.chick, beatLength, chickVolume, m2, noteLength);
|
|
1134
|
-
break;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
717
|
|
|
1140
718
|
function normalizeDrumDefinition(params) {
|
|
1141
719
|
// Be very strict with the drum definition. If anything is not perfect,
|