abcjs 6.3.0 → 6.4.1

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.
Files changed (46) hide show
  1. package/README.md +4 -0
  2. package/RELEASE.md +52 -0
  3. package/dist/abcjs-basic-min.js +2 -2
  4. package/dist/abcjs-basic.js +1031 -622
  5. package/dist/abcjs-basic.js.map +1 -1
  6. package/dist/abcjs-plugin-min.js +2 -2
  7. package/index.js +1 -0
  8. package/package.json +1 -1
  9. package/src/api/tune-metrics.js +18 -0
  10. package/src/data/abc_tune.js +13 -2
  11. package/src/edit/abc_editarea.js +4 -1
  12. package/src/parse/abc_parse.js +2 -0
  13. package/src/parse/abc_parse_directive.js +6 -0
  14. package/src/synth/abc_midi_flattener.js +40 -462
  15. package/src/synth/abc_midi_sequencer.js +25 -10
  16. package/src/synth/chord-track.js +565 -0
  17. package/src/synth/create-note-map.js +2 -1
  18. package/src/synth/create-synth.js +91 -42
  19. package/src/test/abc_parser_lint.js +1 -0
  20. package/src/write/creation/abstract-engraver.js +4 -1
  21. package/src/write/creation/decoration.js +3 -2
  22. package/src/write/creation/elements/tie-element.js +23 -0
  23. package/src/write/draw/draw.js +1 -1
  24. package/src/write/engraver-controller.js +9 -5
  25. package/src/write/interactive/create-analysis.js +50 -0
  26. package/src/write/interactive/find-selectable-element.js +24 -0
  27. package/src/write/interactive/selection.js +5 -45
  28. package/src/write/layout/layout-in-grid.js +83 -0
  29. package/src/write/layout/layout.js +29 -24
  30. package/src/write/layout/set-upper-and-lower-elements.js +2 -0
  31. package/src/write/layout/staff-group.js +2 -2
  32. package/src/write/layout/voice-elements.js +1 -1
  33. package/src/write/layout/voice.js +1 -1
  34. package/src/write/renderer.js +3 -0
  35. package/types/index.d.ts +96 -32
  36. package/version.js +1 -1
  37. package/abc2xml_239/abc2xml.html +0 -769
  38. package/abc2xml_239/abc2xml.py +0 -2248
  39. package/abc2xml_239/abc2xml_changelog.html +0 -124
  40. package/abc2xml_239/lazy-river.abc +0 -26
  41. package/abc2xml_239/lazy-river.xml +0 -3698
  42. package/abc2xml_239/mean-to-me.abc +0 -22
  43. package/abc2xml_239/mean-to-me.xml +0 -2954
  44. package/abc2xml_239/pyparsing.py +0 -3672
  45. package/abc2xml_239/pyparsing.pyc +0 -0
  46. package/temp.txt +0 -50
@@ -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 parseCommon = require("../parse/abc_common");
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
- var setChordTrack = writeNote(element, voiceOff);
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
- if (chordTrack.length > 0 && (chordSourceTrack === false || i === chordSourceTrack)) {
167
- resolveChords(lastBarTime, timeToRealTime(element.time));
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
- hasRhythmHead = false; // decide whether there are rhythm heads each measure.
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 "gchord":
202
- if (!options.chordsOff)
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
- if (!chordTrackEmpty()) // Don't do chords on more than one track, so turn off chord detection after we create it.
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
- if (!chordTrackEmpty())
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
- var setChordTrack = processChord(elem);
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
- hasRhythmHead = true;
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,