abcjs 6.0.0-beta.7 → 6.0.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.
Files changed (204) hide show
  1. package/.github/workflows/tests.yml +29 -0
  2. package/CODE_OF_CONDUCT.md +76 -0
  3. package/CONTRIBUTING.md +1 -0
  4. package/LICENSE.md +1 -1
  5. package/README.md +92 -3
  6. package/RELEASE.md +957 -1
  7. package/abcjs-audio.css +14 -5
  8. package/dist/.gitignore +1 -2
  9. package/dist/abcjs-basic-min.js +3 -0
  10. package/dist/abcjs-basic-min.js.LICENSE +23 -0
  11. package/dist/abcjs-basic.js +28231 -0
  12. package/dist/abcjs-basic.js.map +1 -0
  13. package/dist/abcjs-plugin-min.js +3 -0
  14. package/dist/abcjs-plugin-min.js.LICENSE +23 -0
  15. package/dist/report-basic.html +37 -0
  16. package/dist/report-before-glyph-compress.html +37 -0
  17. package/dist/report-brown-ts-target-es5.html +37 -0
  18. package/dist/report-dev-orig-no-babel.html +37 -0
  19. package/dist/report-synth.html +37 -0
  20. package/docker-build.sh +1 -0
  21. package/glyphs.json +1 -0
  22. package/index.js +27 -2
  23. package/{static-wrappers/license.js → license.js} +1 -1
  24. package/package.json +26 -29
  25. package/{src/plugin/abc_plugin.js → plugin.js} +31 -19
  26. package/src/api/abc_animation.js +1 -17
  27. package/src/api/abc_tablatures.js +144 -0
  28. package/src/api/abc_timing_callbacks.js +234 -116
  29. package/src/api/abc_tunebook.js +18 -67
  30. package/src/api/abc_tunebook_svg.js +38 -46
  31. package/src/data/abc_tune.js +232 -972
  32. package/src/data/deline-tune.js +199 -0
  33. package/src/edit/abc_editarea.js +112 -0
  34. package/src/edit/abc_editor.js +95 -221
  35. package/src/midi/abc_midi_create.js +48 -50
  36. package/src/parse/abc_common.js +0 -14
  37. package/src/parse/abc_parse.js +167 -1321
  38. package/src/parse/abc_parse_book.js +62 -0
  39. package/src/parse/abc_parse_directive.js +164 -41
  40. package/src/parse/abc_parse_header.js +116 -145
  41. package/src/parse/abc_parse_key_voice.js +26 -20
  42. package/src/parse/abc_parse_music.js +1337 -0
  43. package/src/parse/abc_tokenizer.js +21 -15
  44. package/src/parse/abc_transpose.js +3 -15
  45. package/src/parse/tune-builder.js +896 -0
  46. package/src/parse/wrap_lines.js +205 -453
  47. package/src/synth/abc_midi_flattener.js +1292 -0
  48. package/src/{midi → synth}/abc_midi_renderer.js +44 -17
  49. package/src/synth/abc_midi_sequencer.js +648 -0
  50. package/src/synth/active-audio-context.js +3 -14
  51. package/src/synth/cents-to-factor.js +10 -0
  52. package/src/synth/create-note-map.js +21 -32
  53. package/src/synth/create-synth-control.js +20 -103
  54. package/src/synth/create-synth.js +185 -77
  55. package/src/synth/download-buffer.js +7 -21
  56. package/src/synth/get-midi-file.js +13 -20
  57. package/src/synth/images/{loading.svg → loading.svg.js} +4 -0
  58. package/src/synth/images/loop.svg.js +65 -0
  59. package/src/synth/images/pause.svg.js +10 -0
  60. package/src/synth/images/play.svg.js +9 -0
  61. package/src/synth/images/{reset.svg → reset.svg.js} +5 -1
  62. package/src/synth/instrument-index-to-name.js +1 -16
  63. package/src/synth/load-note.js +37 -76
  64. package/src/synth/pitch-to-note-name.js +0 -15
  65. package/src/synth/pitches-to-perc.js +64 -0
  66. package/src/synth/place-note.js +78 -68
  67. package/src/synth/play-event.js +17 -18
  68. package/src/synth/register-audio-context.js +11 -23
  69. package/src/synth/sounds-cache.js +0 -15
  70. package/src/synth/supports-audio.js +9 -23
  71. package/src/synth/synth-controller.js +80 -49
  72. package/src/synth/synth-sequence.js +20 -34
  73. package/src/tablatures/instruments/guitar/guitar-fonts.js +19 -0
  74. package/src/tablatures/instruments/guitar/guitar-patterns.js +23 -0
  75. package/src/tablatures/instruments/guitar/tab-guitar.js +50 -0
  76. package/src/tablatures/instruments/string-patterns.js +277 -0
  77. package/src/tablatures/instruments/string-tablature.js +56 -0
  78. package/src/tablatures/instruments/tab-note.js +282 -0
  79. package/src/tablatures/instruments/tab-notes.js +41 -0
  80. package/src/tablatures/instruments/violin/tab-violin.js +47 -0
  81. package/src/tablatures/instruments/violin/violin-fonts.js +19 -0
  82. package/src/tablatures/instruments/violin/violin-patterns.js +23 -0
  83. package/src/tablatures/tab-absolute-elements.js +310 -0
  84. package/src/tablatures/tab-common.js +29 -0
  85. package/src/tablatures/tab-renderer.js +243 -0
  86. package/src/tablatures/transposer.js +110 -0
  87. package/src/test/abc_midi_lint.js +5 -22
  88. package/src/test/abc_midi_sequencer_lint.js +11 -14
  89. package/src/test/abc_parser_lint.js +136 -32
  90. package/src/test/abc_vertical_lint.js +94 -32
  91. package/src/test/rendering-lint.js +38 -5
  92. package/src/write/abc_absolute_element.js +112 -120
  93. package/src/write/abc_abstract_engraver.js +102 -253
  94. package/src/write/abc_beam_element.js +30 -290
  95. package/src/write/abc_brace_element.js +12 -121
  96. package/src/write/abc_create_clef.js +21 -32
  97. package/src/write/abc_create_key_signature.js +8 -26
  98. package/src/write/abc_create_note_head.js +107 -0
  99. package/src/write/abc_create_time_signature.js +2 -21
  100. package/src/write/abc_crescendo_element.js +3 -50
  101. package/src/write/abc_decoration.js +7 -30
  102. package/src/write/abc_dynamic_decoration.js +3 -37
  103. package/src/write/abc_ending_element.js +1 -57
  104. package/src/write/abc_engraver_controller.js +111 -234
  105. package/src/write/abc_glyphs.js +9 -19
  106. package/src/write/abc_relative_element.js +57 -97
  107. package/src/write/abc_renderer.js +10 -832
  108. package/src/write/abc_spacing.js +0 -15
  109. package/src/write/abc_staff_group_element.js +14 -349
  110. package/src/write/abc_tempo_element.js +9 -117
  111. package/src/write/abc_tie_element.js +5 -68
  112. package/src/write/abc_triplet_element.js +6 -124
  113. package/src/write/abc_voice_element.js +7 -222
  114. package/src/write/add-chord.js +103 -0
  115. package/src/write/add-text-if.js +33 -0
  116. package/src/write/bottom-text.js +79 -0
  117. package/src/write/calcHeight.js +17 -0
  118. package/src/write/classes.js +100 -0
  119. package/src/write/draw/absolute.js +68 -0
  120. package/src/write/draw/beam.js +56 -0
  121. package/src/write/draw/brace.js +106 -0
  122. package/src/write/draw/crescendo.js +38 -0
  123. package/src/write/draw/debug-box.js +8 -0
  124. package/src/write/draw/draw.js +56 -0
  125. package/src/write/draw/dynamics.js +20 -0
  126. package/src/write/draw/ending.js +46 -0
  127. package/src/write/draw/group-elements.js +66 -0
  128. package/src/write/draw/horizontal-line.js +25 -0
  129. package/src/write/draw/non-music.js +50 -0
  130. package/src/write/draw/print-line.js +24 -0
  131. package/src/write/draw/print-path.js +7 -0
  132. package/src/write/draw/print-stem.js +30 -0
  133. package/src/write/draw/print-symbol.js +59 -0
  134. package/src/write/draw/print-vertical-line.js +18 -0
  135. package/src/write/draw/relative.js +77 -0
  136. package/src/write/draw/round-number.js +5 -0
  137. package/src/write/draw/selectables.js +59 -0
  138. package/src/write/draw/separator.js +16 -0
  139. package/src/write/draw/set-paper-size.js +45 -0
  140. package/src/write/{sprintf.js → draw/sprintf.js} +0 -0
  141. package/src/write/draw/staff-group.js +226 -0
  142. package/src/write/draw/staff-line.js +9 -0
  143. package/src/write/draw/staff.js +33 -0
  144. package/src/write/draw/tab-line.js +40 -0
  145. package/src/write/draw/tempo.js +45 -0
  146. package/src/write/draw/text.js +71 -0
  147. package/src/write/draw/tie.js +97 -0
  148. package/src/write/draw/triplet.js +46 -0
  149. package/src/write/draw/voice.js +102 -0
  150. package/src/write/format-jazz-chord.js +15 -0
  151. package/src/write/free-text.js +41 -0
  152. package/src/write/get-font-and-attr.js +37 -0
  153. package/src/write/get-text-size.js +56 -0
  154. package/src/write/highlight.js +11 -0
  155. package/src/write/layout/VoiceElements.js +121 -0
  156. package/src/write/layout/beam.js +213 -0
  157. package/src/write/layout/get-left-edge-of-staff.js +56 -0
  158. package/src/write/layout/getBarYAt.js +6 -0
  159. package/src/write/layout/layout.js +94 -0
  160. package/src/write/layout/setUpperAndLowerElements.js +232 -0
  161. package/src/write/layout/staffGroup.js +146 -0
  162. package/src/write/layout/triplet.js +75 -0
  163. package/src/write/layout/voice.js +137 -0
  164. package/src/write/selection.js +188 -70
  165. package/src/write/separator.js +10 -0
  166. package/src/write/set-class.js +21 -0
  167. package/src/write/subtitle.js +12 -0
  168. package/src/write/svg.js +95 -43
  169. package/src/write/top-text.js +54 -0
  170. package/src/write/unhighlight.js +11 -0
  171. package/temp.txt +17 -0
  172. package/test.js +27 -64
  173. package/types/index.d.ts +1095 -0
  174. package/version.js +1 -1
  175. package/.babelrc +0 -5
  176. package/.eslintrc +0 -3
  177. package/.gitmodules +0 -3
  178. package/abcjs-midi.css +0 -166
  179. package/build-utils/loadPresets.js +0 -14
  180. package/build-utils/presets/webpack.analyze.js +0 -6
  181. package/build-utils/presets/webpack.optimize.js +0 -30
  182. package/build-utils/webpack.development.js +0 -14
  183. package/build-utils/webpack.production.js +0 -35
  184. package/deploy-docs.sh +0 -25
  185. package/docs/README.md +0 -33
  186. package/fix-versions.sh +0 -23
  187. package/mei.js +0 -43
  188. package/midi.js +0 -62
  189. package/src/api/abc_tunebook_midi.js +0 -116
  190. package/src/midi/abc_midi_controls.js +0 -701
  191. package/src/midi/abc_midi_flattener.js +0 -1119
  192. package/src/midi/abc_midi_js_preparer.js +0 -243
  193. package/src/midi/abc_midi_sequencer.js +0 -401
  194. package/src/midi/abc_midi_ui_generator.js +0 -86
  195. package/src/plugin/abc_plugin_midi.js +0 -220
  196. package/src/synth/images/loop.svg +0 -61
  197. package/src/synth/images/pause.svg +0 -6
  198. package/src/synth/images/play.svg +0 -5
  199. package/src/transform/abc2abc_write.js +0 -395
  200. package/static-wrappers/basic.js +0 -2
  201. package/static-wrappers/midi.js +0 -2
  202. package/static-wrappers/plugin-midi.js +0 -6
  203. package/static-wrappers/plugin.js +0 -6
  204. package/webpack.config.js +0 -29
@@ -1,1119 +0,0 @@
1
- // abc_midi_flattener.js: Turn a linear series of events into a series of MIDI commands.
2
- // Copyright (C) 2010-2020 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
3
- //
4
- // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5
- // documentation files (the "Software"), to deal in the Software without restriction, including without limitation
6
- // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
7
- // to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
- //
9
- // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10
- //
11
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
12
- // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
13
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
14
- // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
-
17
- // We input a set of voices, but the notes are still complex. This pass changes the logical definitions
18
- // of the grace notes, decorations, ties, triplets, rests, transpositions, keys, and accidentals into actual note durations.
19
- // It also extracts guitar chords to a separate voice and resolves their rhythm.
20
-
21
- var flatten;
22
-
23
- (function() {
24
- "use strict";
25
-
26
- var barAccidentals;
27
- var accidentals;
28
- var transpose;
29
- var bagpipes;
30
- var multiplier;
31
- var tracks;
32
- var startingTempo;
33
- var startingMeter;
34
- var tempoChangeFactor = 1;
35
- var instrument;
36
- var currentInstrument;
37
- // var channel;
38
- var currentTrack;
39
- var pitchesTied;
40
- var lastNoteDurationPosition;
41
- var currentTrackCounter;
42
- var currentTrackName;
43
-
44
- var meter = { num: 4, den: 4 };
45
- var chordTrack;
46
- var chordTrackFinished;
47
- var chordChannel;
48
- var chordInstrument = 0;
49
- var drumInstrument = 128;
50
- var currentChords;
51
- var lastChord;
52
- var barBeat;
53
- var gChordTacet = false;
54
- var doBeatAccents = true;
55
- var stressBeat1 = 105;
56
- var stressBeatDown = 95;
57
- var stressBeatUp = 85;
58
- var beatFraction = 0.25;
59
- var nextVolume;
60
- var nextVolumeDelta;
61
- var slurCount = 0;
62
-
63
- var drumTrack;
64
- var drumTrackFinished;
65
- var drumDefinition = {};
66
-
67
- var normalBreakBetweenNotes = 1.0/128; // a 128th note of silence between notes for articulation.
68
-
69
- flatten = function(voices, options) {
70
- if (!options) options = {};
71
- barAccidentals = [];
72
- accidentals = [0,0,0,0,0,0,0];
73
- bagpipes = false;
74
- multiplier = 1;
75
- tracks = [];
76
- startingTempo = undefined;
77
- startingMeter = undefined;
78
- tempoChangeFactor = 1;
79
- instrument = undefined;
80
- currentInstrument = undefined;
81
- // channel = undefined;
82
- currentTrack = undefined;
83
- currentTrackCounter = undefined;
84
- currentTrackName = undefined;
85
- pitchesTied = {};
86
-
87
- // For resolving chords.
88
- meter = { num: 4, den: 4 };
89
- chordTrack = [];
90
- chordChannel = voices.length; // first free channel for chords
91
- chordTrackFinished = false;
92
- currentChords = [];
93
- lastChord = undefined;
94
- barBeat = 0;
95
- gChordTacet = options.chordsOff ? true : false;
96
-
97
- doBeatAccents = true;
98
- stressBeat1 = 105;
99
- stressBeatDown = 95;
100
- stressBeatUp = 85;
101
- beatFraction = 0.25;
102
- nextVolume = undefined;
103
- nextVolumeDelta = undefined;
104
- slurCount = 0;
105
-
106
- // For the drum/metronome track.
107
- drumTrack = [];
108
- drumTrackFinished = false;
109
- drumDefinition = {};
110
-
111
- zeroOutMilliseconds(voices);
112
-
113
- for (var i = 0; i < voices.length; i++) {
114
- transpose = 0;
115
- lastNoteDurationPosition = -1;
116
- var voice = voices[i];
117
- currentTrack = [{ cmd: 'program', channel: i, instrument: instrument }];
118
- currentTrackCounter = 0;
119
- currentTrackName = undefined;
120
- pitchesTied = {};
121
- for (var j = 0; j < voice.length; j++) {
122
- var element = voice[j];
123
- switch (element.el_type) {
124
- case "name":
125
- currentTrackName = {cmd: 'text', type: "name", text: element.trackName };
126
- break;
127
- case "note":
128
- writeNote(element, options.voicesOff);
129
- break;
130
- case "key":
131
- accidentals = setKeySignature(element);
132
- break;
133
- case "meter":
134
- if (!startingMeter)
135
- startingMeter = element;
136
- meter = element;
137
- beatFraction = getBeatFraction(meter);
138
- break;
139
- case "tempo":
140
- if (!startingTempo)
141
- startingTempo = element.qpm;
142
- else
143
- tempoChangeFactor = element.qpm ? startingTempo / element.qpm : 1;
144
- break;
145
- case "transpose":
146
- transpose = element.transpose;
147
- break;
148
- case "bar":
149
- if (chordTrack.length > 0 && i === 0) {
150
- resolveChords();
151
- currentChords = [];
152
- }
153
- barBeat = 0;
154
- barAccidentals = [];
155
- if (i === 0) // Only write the drum part on the first voice so that it is not duplicated.
156
- writeDrum(voices.length+1);
157
- break;
158
- case "bagpipes":
159
- bagpipes = true;
160
- break;
161
- case "instrument":
162
- if (instrument === undefined)
163
- instrument = element.program;
164
- currentInstrument = element.program;
165
- if (currentTrack.length > 0 && currentTrack[currentTrack.length-1].cmd === 'program')
166
- currentTrack[currentTrack.length-1].instrument = element.program;
167
- else {
168
- var ii;
169
- for (ii = currentTrack.length-1; ii >= 0 && currentTrack[ii].cmd !== 'program'; ii--)
170
- ;
171
- if (ii < 0 || currentTrack[ii].instrument !== element.program)
172
- currentTrack.push({cmd: 'program', channel: i, instrument: element.program});
173
- }
174
- break;
175
- case "channel":
176
- // if (channel === undefined)
177
- // channel = element.channel;
178
- // currentTrack[0].channel = element.channel;
179
- break;
180
- case "drum":
181
- drumDefinition = normalizeDrumDefinition(element.params);
182
- break;
183
- case "gchord":
184
- if (!options.chordsOff)
185
- gChordTacet = element.tacet;
186
- break;
187
- case "beat":
188
- stressBeat1 = element.beats[0];
189
- stressBeatDown = element.beats[1];
190
- stressBeatUp = element.beats[2];
191
- // TODO-PER: also use the last parameter - which changes which beats are strong.
192
- break;
193
- case "vol":
194
- nextVolume = element.volume;
195
- break;
196
- case "volinc":
197
- nextVolumeDelta = element.volume;
198
- break;
199
- case "beataccents":
200
- doBeatAccents = element.value;
201
- break;
202
- default:
203
- // This should never happen
204
- console.log("MIDI creation. Unknown el_type: " + element.el_type + "\n");// jshint ignore:line
205
- break;
206
- }
207
- }
208
- if (currentTrack[0].instrument === undefined)
209
- currentTrack[0].instrument = instrument ? instrument : 0;
210
- if (currentTrackName)
211
- currentTrack.unshift(currentTrackName);
212
- tracks.push(currentTrack);
213
- if (chordTrack.length > 0) // Don't do chords on more than one track, so turn off chord detection after we create it.
214
- chordTrackFinished = true;
215
- if (drumTrack.length > 0) // Don't do drums on more than one track, so turn off drum after we create it.
216
- drumTrackFinished = true;
217
- }
218
- if (chordTrack.length > 0)
219
- tracks.push(chordTrack);
220
- if (drumTrack.length > 0)
221
- tracks.push(drumTrack);
222
- // Adjust the tempo according to the meter. The rules are this:
223
- // 1) If the denominator is 2 or 4, then always make a beat be the denominator.
224
- //
225
- // 2) If the denominator is 8 or 16, then:
226
- // a) If the numerator is divisible by 3, the beat is 3*denominator.
227
- // b) Otherwise the beat is the denominator.
228
- //
229
- // 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.
230
- //
231
- // Right now, the startingTempo is calculated for a quarter note, so modify it if necessary.
232
- // var num = startingMeter ? parseInt(startingMeter.num, 10) : meter.num;
233
- // var den = startingMeter ? parseInt(startingMeter.den, 10) : meter.den;
234
- // if (den === 2)
235
- // startingTempo *= 2;
236
- // else if (den === 8) {
237
- // if (parseInt(num, 10) % 3 === 0)
238
- // startingTempo *= 3/2;
239
- // else
240
- // startingTempo /= 2;
241
- // } else if (den === 16) {
242
- // if (num % 3 === 0)
243
- // startingTempo *= 3/4;
244
- // else
245
- // startingTempo /= 4;
246
- // }
247
-
248
- return { tempo: startingTempo, instrument: instrument, tracks: tracks, totalDuration: totalDuration(tracks) };
249
- };
250
-
251
- function zeroOutMilliseconds(voices) {
252
- for (var i = 0; i < voices.length; i++) {
253
- var voice = voices[i];
254
- for (var j = 0; j < voice.length; j++) {
255
- var element = voice[j];
256
- delete element.currentTrackMilliseconds;
257
- }
258
- }
259
- }
260
-
261
- function totalDuration(tracks) {
262
- var total = 0;
263
- for (var i = 0; i < tracks.length; i++) {
264
- var track = tracks[i];
265
- var trackTotal = 0;
266
- for (var j = 0; j < track.length; j++) {
267
- var event = track[j];
268
- if (event.duration)
269
- trackTotal += event.duration;
270
- }
271
- total = Math.max(total, trackTotal);
272
- }
273
- return total;
274
- }
275
-
276
- function getBeatFraction(meter) {
277
- switch (meter.den) {
278
- case 2: return 0.5;
279
- case 4: return 0.25;
280
- case 8: return 0.375;
281
- case 16: return 0.125;
282
- }
283
- return 0.25;
284
- }
285
- //
286
- // The algorithm for chords is:
287
- // - The chords are done in a separate track.
288
- // - If there are notes before the first chord, then put that much silence to start the track.
289
- // - The pattern of chord expression depends on the meter, and how many chords are in a measure.
290
- // - There is a possibility that a measure will have an incorrect number of beats, if that is the case, then
291
- // start the pattern anew on the next measure number.
292
- // - If a chord root is not A-G, then ignore it as if the chord wasn't there at all.
293
- // - If a chord modification isn't in our supported list, change it to a major triad.
294
- //
295
- // - If there is only one chord in a measure:
296
- // - If 2/4, play root chord
297
- // - If cut time, play root(1) chord(3)
298
- // - If 3/4, play root chord chord
299
- // - If 4/4 or common time, play root chord fifth chord
300
- // - If 6/8, play root(1) chord(3) fifth(4) chord(6)
301
- // - For any other meter, play the full chord on each beat. (TODO-PER: expand this as more support is added.)
302
- //
303
- // - 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.
304
- // - Otherwise, move it later, unless there is already a chord on that beat.
305
- // - Otherwise, ignore it. (TODO-PER: expand this as more support is added.)
306
- //
307
- // - If there is a chord on the second beat, play a chord for the first beat instead of a bass note.
308
- // - 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.
309
- //
310
- var breakSynonyms = [ 'break', '(break)', 'no chord', 'n.c.', 'tacet'];
311
-
312
- function findChord(elem) {
313
- if (gChordTacet)
314
- return 'break';
315
-
316
- // TODO-PER: Just using the first chord if there are more than one.
317
- if (chordTrackFinished || !elem.chord || elem.chord.length === 0)
318
- return null;
319
-
320
- // Return the first annotation that is a regular chord: that is, it is in the default place or is a recognized "tacet" phrase.
321
- for (var i = 0; i < elem.chord.length; i++) {
322
- var ch = elem.chord[i];
323
- if (ch.position === 'default')
324
- return ch.name;
325
- if (breakSynonyms.indexOf(ch.name.toLowerCase()) >= 0)
326
- return 'break';
327
- }
328
- return null;
329
- }
330
-
331
- function timeFromStart() {
332
- var distance = 0;
333
- for (var ct = 0; ct < currentTrack.length; ct++) {
334
- if (currentTrack[ct].cmd === 'move')
335
- distance += currentTrack[ct].duration;
336
- }
337
- return distance;
338
- }
339
-
340
- function writeNote(elem, voiceOff) {
341
- //
342
- // Create a series of note events to append to the current track.
343
- // The output event is one of: { pitchStart: pitch_in_abc_units, volume: from_1_to_64 }
344
- // { pitchStop: pitch_in_abc_units }
345
- // { moveTime: duration_in_abc_units }
346
- // If there are guitar chords, then they are put in a separate track, but they have the same format.
347
- //
348
-
349
- var volume;
350
- if (nextVolume) {
351
- volume = nextVolume;
352
- nextVolume = undefined;
353
- } else if (!doBeatAccents) {
354
- volume = stressBeatDown;
355
- } else {
356
- if (barBeat === 0)
357
- volume = stressBeat1;
358
- else if (barBeat % beatFraction < 0.001) // A little slop because of JavaScript floating point math.
359
- volume = stressBeatDown;
360
- else
361
- volume = stressBeatUp;
362
- }
363
- if (nextVolumeDelta) {
364
- volume += nextVolumeDelta;
365
- nextVolumeDelta = undefined;
366
- }
367
- if (volume < 0)
368
- volume = 0;
369
- if (volume > 127)
370
- volume = 127;
371
- var velocity = voiceOff ? 0 : volume;
372
- var chord = findChord(elem);
373
- if (chord) {
374
- var c = interpretChord(chord);
375
- // If this isn't a recognized chord, just completely ignore it.
376
- if (c) {
377
- // If we ever have a chord in this voice, then we add the chord track.
378
- // However, if there are chords on more than one voice, then just use the first voice.
379
- if (chordTrack.length === 0) {
380
- chordTrack.push({cmd: 'program', channel: chordChannel, instrument: chordInstrument});
381
- // need to figure out how far in time the chord started: if there are pickup notes before the chords start, we need pauses.
382
- var distance = timeFromStart();
383
- if (distance > 0)
384
- addMove(chordTrack, distance*tempoChangeFactor);
385
- }
386
-
387
- lastChord = c;
388
- currentChords.push({chord: lastChord, beat: barBeat});
389
- }
390
- }
391
-
392
- if (elem.startTriplet) {
393
- multiplier = elem.tripletMultiplier;
394
- }
395
-
396
- var duration = (elem.durationClass ? elem.durationClass : elem.duration) *multiplier;
397
- barBeat += duration;
398
-
399
- // if there are grace notes, then also play them.
400
- // I'm not sure there is an exact rule for the length of the notes. My rule, unless I find
401
- // a better one is: the grace notes cannot take more than 1/2 of the main note's value.
402
- // A grace note (of 1/8 note duration) takes 1/8 of the main note's value.
403
- var graces;
404
- if (elem.gracenotes) {
405
- // There are two cases: if this is bagpipe, the grace notes are played on the beat with the current note.
406
- // Normally, the grace notes would be played before the beat. (If this is the first note in the track, however, then it is played on the current beat.)
407
- // The reason for the exception on the first note is that it would otherwise move the whole track in time and would affect all the other tracks.
408
- var stealFromCurrent = (bagpipes || lastNoteDurationPosition < 0 || currentTrack.length === 0);
409
- var stealFromDuration = stealFromCurrent ? duration : currentTrack[lastNoteDurationPosition].duration;
410
- graces = processGraceNotes(elem.gracenotes, stealFromDuration);
411
- if (!bagpipes) {
412
- duration = writeGraceNotes(graces, stealFromCurrent, duration, null, velocity);
413
- }
414
- }
415
-
416
- // The currentTrackCounter is the number of whole notes from the beginning of the piece.
417
- // The beat fraction is the note that gets a beat (.25 is a quarter note)
418
- // The tempo is in minutes and we want to get to milliseconds.
419
- if (!elem.currentTrackMilliseconds)
420
- elem.currentTrackMilliseconds = [];
421
- elem.currentTrackMilliseconds.push(currentTrackCounter / beatFraction / startingTempo * 60*1000);
422
- if (elem.pitches) {
423
- if (graces && bagpipes) {
424
- // If it is bagpipes, then the graces are played with the note. If the grace has the same pitch as the note, then we just skip it.
425
- duration = writeGraceNotes(graces, true, duration, null, velocity);
426
- }
427
- var pitches = [];
428
- elem.midiPitches = [];
429
- var thisBreakBetweenNotes = normalBreakBetweenNotes;
430
- var noteModification = "none";
431
- if (elem.decoration) {
432
- var isStaccato = false;
433
- var isTenuto = false;
434
- var isAccented = false;
435
- for (var d = 0; d < elem.decoration.length; d++) {
436
- if (elem.decoration[d] === 'staccato')
437
- isStaccato = true;
438
- else if (elem.decoration[d] === 'tenuto')
439
- isTenuto = true;
440
- else if (elem.decoration[d] === 'accent')
441
- isAccented = true;
442
- else if (elem.decoration[d] === 'trill')
443
- noteModification = "trill";
444
- else if (elem.decoration[d] === 'lowermordent')
445
- noteModification = "lowermordent";
446
- else if (elem.decoration[d] === 'uppermordent')
447
- noteModification = "mordent";
448
- else if (elem.decoration[d] === 'mordent')
449
- noteModification = "mordent";
450
- else if (elem.decoration[d] === 'turn')
451
- noteModification = "turn";
452
- else if (elem.decoration[d] === 'roll')
453
- noteModification = "roll";
454
- }
455
- if (isStaccato)
456
- thisBreakBetweenNotes = duration * 3/4;
457
- if (isTenuto)
458
- thisBreakBetweenNotes = 0;
459
- if (isAccented)
460
- velocity = Math.min(127, velocity*1.5);
461
- }
462
- for (var i=0; i<elem.pitches.length; i++) {
463
- var note = elem.pitches[i];
464
- if (note.startSlur)
465
- slurCount += note.startSlur.length;
466
- if (note.endSlur)
467
- slurCount -= note.endSlur.length;
468
- var actualPitch = adjustPitch(note);
469
- pitches.push({ pitch: actualPitch, startTie: note.startTie });
470
- elem.midiPitches.push({ pitch: actualPitch+60, durationInMeasures: duration*tempoChangeFactor, volume: volume, instrument: currentInstrument }); // TODO-PER: why is the internal numbering system offset by 60 from midi? It should probably be the same as midi.
471
-
472
- if (!pitchesTied[''+actualPitch]) // If this is the second note of a tie, we don't start it again.
473
- currentTrack.push({ cmd: 'start', pitch: actualPitch, volume: velocity });
474
- else {
475
- // but we do add the duration to what we call back.
476
- for (var last = currentTrack.length-1; last >= 0; last--) {
477
- if (currentTrack[last].cmd === 'start' && currentTrack[last].pitch === actualPitch && currentTrack[last].elem) {
478
- var pitchArray = currentTrack[last].elem.midiPitches;
479
- for (var last2 = 0; last2 < pitchArray.length; last2++) {
480
- if (pitchArray[last2].pitch-60 === actualPitch) { // TODO-PER: the 60 is to compensate for the midi pitch numbers again.
481
- pitchArray[last2].durationInMeasures += duration * tempoChangeFactor;
482
- }
483
- }
484
- break;
485
- }
486
- }
487
- }
488
-
489
- if (note.startTie) {
490
- pitchesTied['' + actualPitch] = true;
491
- currentTrack[currentTrack.length-1].elem = elem;
492
- } else if (note.endTie)
493
- pitchesTied[''+actualPitch] = false;
494
- }
495
- if (elem.gracenotes) {
496
- for (var j = 0; j < elem.gracenotes.length; j++) {
497
- elem.midiGraceNotePitches = [];
498
- var grace = elem.gracenotes[j];
499
- elem.midiGraceNotePitches.push({ pitch: adjustPitch(grace)+60, durationInMeasures: 0, volume: velocity, instrument: currentInstrument});
500
- }
501
- }
502
- if (slurCount > 0)
503
- thisBreakBetweenNotes = 0;
504
- var soundDuration = duration-thisBreakBetweenNotes;
505
- if (soundDuration < 0) {
506
- soundDuration = 0;
507
- thisBreakBetweenNotes = duration;
508
- }
509
- if (noteModification !== "none") {
510
- var noteTime;
511
- var numNotes;
512
- var relativeNoteList = [];
513
- switch (noteModification) {
514
- case "trill":
515
- // We want an even number of 32nd notes - (the first note has already started) so it starts and stops on the main note
516
- noteTime = 1.0/32;
517
- numNotes = Math.floor(soundDuration/noteTime);
518
- if (numNotes < 1) {
519
- numNotes = 1;
520
- } else if (numNotes % 2 === 1)
521
- numNotes--;
522
- while(numNotes > 0) {
523
- numNotes--;
524
- relativeNoteList.push(numNotes % 2);
525
- }
526
- break;
527
- case "mordent":
528
- noteTime = 1.0/32;
529
- relativeNoteList.push(1);
530
- relativeNoteList.push(0);
531
- break;
532
- case "lowermordent":
533
- noteTime = 1.0/32;
534
- relativeNoteList.push(-1);
535
- relativeNoteList.push(0);
536
- break;
537
- case "turn":
538
- noteTime = soundDuration / 5;
539
- relativeNoteList.push(1);
540
- relativeNoteList.push(0);
541
- relativeNoteList.push(-1);
542
- relativeNoteList.push(0);
543
- break;
544
- case "roll":
545
- noteTime = 1.0/32;
546
- numNotes = Math.floor(soundDuration/noteTime);
547
- if (numNotes < 1) {
548
- numNotes = 1;
549
- }
550
- while (numNotes) {
551
- relativeNoteList.push(0);
552
- numNotes--;
553
- }
554
-
555
- break;
556
- }
557
- var currentlyPlayingNote = [];
558
- var iii;
559
- for (iii = 0; iii < elem.pitches.length; iii++) {
560
- currentlyPlayingNote.push(adjustPitch({ pitch: elem.pitches[iii].pitch}));
561
- }
562
- var remainingTime = soundDuration;
563
- for (var dd = 0; dd < relativeNoteList.length; dd++) {
564
- addMove(currentTrack, (noteTime-0.001)*tempoChangeFactor);
565
- for (iii = 0; iii < elem.pitches.length; iii++) {
566
- currentTrack.push({ cmd: 'stop', pitch: currentlyPlayingNote[iii]});
567
- }
568
- addMove(currentTrack, 0.001*tempoChangeFactor);
569
- for (iii = 0; iii < elem.pitches.length; iii++) {
570
- currentTrack.push({ cmd: 'start', pitch: adjustPitch({ pitch: elem.pitches[iii].pitch+relativeNoteList[dd]}), volume: velocity});
571
- currentlyPlayingNote[iii] = adjustPitch({ pitch: elem.pitches[iii].pitch+relativeNoteList[dd]});
572
- }
573
- remainingTime -= noteTime;
574
- }
575
- addMove(currentTrack, remainingTime*tempoChangeFactor);
576
- } else
577
- addMove(currentTrack, soundDuration*tempoChangeFactor);
578
-
579
- lastNoteDurationPosition = currentTrack.length-1;
580
- currentTrackCounter += soundDuration*tempoChangeFactor;
581
-
582
- for (var ii = 0; ii < pitches.length; ii++) {
583
- if (!pitchesTied[''+pitches[ii].pitch])
584
- currentTrack.push({ cmd: 'stop', pitch: pitches[ii].pitch });
585
- }
586
- addMove(currentTrack, thisBreakBetweenNotes*tempoChangeFactor);
587
- currentTrackCounter += thisBreakBetweenNotes*tempoChangeFactor;
588
- } else if (elem.rest) {
589
- addMove(currentTrack, duration*tempoChangeFactor);
590
- currentTrackCounter += duration*tempoChangeFactor;
591
- }
592
-
593
- if (elem.endTriplet) {
594
- multiplier=1;
595
- }
596
- }
597
-
598
- var scale = [0,2,4,5,7,9,11];
599
- function adjustPitch(note) {
600
- if (note.midipitch)
601
- return note.midipitch - 60;
602
- var pitch = note.pitch;
603
- if (note.accidental) {
604
- switch(note.accidental) { // change that pitch (not other octaves) for the rest of the bar
605
- case "sharp":
606
- barAccidentals[pitch]=1; break;
607
- case "flat":
608
- barAccidentals[pitch]=-1; break;
609
- case "natural":
610
- barAccidentals[pitch]=0; break;
611
- case "dblsharp":
612
- barAccidentals[pitch]=2; break;
613
- case "dblflat":
614
- barAccidentals[pitch]=-2; break;
615
- }
616
- }
617
-
618
- var actualPitch = extractOctave(pitch) *12 + scale[extractNote(pitch)];
619
-
620
- if ( barAccidentals[pitch]!==undefined) {
621
- actualPitch += barAccidentals[pitch];
622
- } else { // use normal accidentals
623
- actualPitch += accidentals[extractNote(pitch)];
624
- }
625
- actualPitch += transpose;
626
- return actualPitch;
627
- }
628
-
629
- function setKeySignature(elem) {
630
- var accidentals = [0,0,0,0,0,0,0];
631
- if (!elem.accidentals) return accidentals;
632
- for (var i = 0; i < elem.accidentals.length; i++) {
633
- var acc = elem.accidentals[i];
634
- var d = (acc.acc === "sharp") ? 1 : (acc.acc === "natural") ?0 : -1;
635
-
636
- var lowercase = acc.note.toLowerCase();
637
- var note = extractNote(lowercase.charCodeAt(0)-'c'.charCodeAt(0));
638
- accidentals[note]+=d;
639
- }
640
- return accidentals;
641
- }
642
-
643
- var graceDivider = 8; // This is the fraction of a note that the grace represents. That is, if this is 2, then a grace note of 1/16 would be a 1/32.
644
- function processGraceNotes(graces, companionDuration) {
645
- var graceDuration = 0;
646
- var ret = [];
647
- var grace;
648
- for (var g = 0; g < graces.length; g++) {
649
- grace = graces[g];
650
- graceDuration += grace.duration;
651
- }
652
- graceDuration = graceDuration / graceDivider;
653
- var multiplier = (graceDuration * 2 > companionDuration) ? companionDuration/(graceDuration * 2) : 1;
654
-
655
- for (g = 0; g < graces.length; g++) {
656
- grace = graces[g];
657
- var pitch = adjustPitch(grace);
658
- ret.push({ pitch: pitch, duration: grace.duration/graceDivider*multiplier });
659
- }
660
- return ret;
661
- }
662
-
663
- function writeGraceNotes(graces, stealFromCurrent, duration, skipNote, velocity) {
664
- for (var g = 0; g < graces.length; g++) {
665
- var gp = graces[g];
666
- if (gp !== skipNote)
667
- currentTrack.push({cmd: 'start', pitch: gp.pitch, volume: velocity});
668
- addMove(currentTrack, graces[g].duration*tempoChangeFactor);
669
- if (gp !== skipNote)
670
- currentTrack.push({cmd: 'stop', pitch: gp.pitch});
671
- if (!stealFromCurrent)
672
- currentTrack[lastNoteDurationPosition].duration -= graces[g].duration;
673
- duration -= graces[g].duration;
674
- }
675
- return duration;
676
- }
677
-
678
- function extractOctave(pitch) {
679
- return Math.floor(pitch/7);
680
- }
681
-
682
- function extractNote(pitch) {
683
- pitch = pitch%7;
684
- if (pitch<0) pitch+=7;
685
- return pitch;
686
- }
687
-
688
- var basses = {
689
- 'A': -27, 'B': -25, 'C': -24, 'D': -22, 'E': -20, 'F': -19, 'G': -17
690
- };
691
- function interpretChord(name) {
692
- // chords have the format:
693
- // [root][acc][modifier][/][bass][acc]
694
- // (The chord might be surrounded by parens. Just ignore them.)
695
- // root must be present and must be from A-G.
696
- // acc is optional and can be # or b
697
- // The modifier can be a wide variety of things, like "maj7". As they are discovered, more are supported here.
698
- // If there is a slash, then there is a bass note, which can be from A-G, with an optional acc.
699
- // If the root is unrecognized, then "undefined" is returned and there is no chord.
700
- // If the modifier is unrecognized, a major triad is returned.
701
- // If the bass notes is unrecognized, it is ignored.
702
- if (name.length === 0)
703
- return undefined;
704
- if (name === 'break')
705
- return { chick: []};
706
- var root = name.substring(0,1);
707
- if (root === '(') {
708
- name = name.substring(1,name.length-2);
709
- if (name.length === 0)
710
- return undefined;
711
- root = name.substring(0,1);
712
- }
713
- var bass = basses[root];
714
- if (!bass) // If the bass note isn't listed, then this was an unknown root. Only A-G are accepted.
715
- return undefined;
716
- bass += transpose;
717
- var bass2 = bass - 5; // The alternating bass is a 4th below
718
- var chick;
719
- if (name.length === 1)
720
- chick = chordNotes(bass, '');
721
- var remaining = name.substring(1);
722
- var acc = remaining.substring(0,1);
723
- if (acc === 'b' || acc === '♭') {
724
- bass--;
725
- bass2--;
726
- remaining = remaining.substring(1);
727
- } else if (acc === '#' || acc === '♯') {
728
- bass++;
729
- bass2++;
730
- remaining = remaining.substring(1);
731
- }
732
- var arr = remaining.split('/');
733
- chick = chordNotes(bass, arr[0]);
734
- if (arr.length === 2) {
735
- var explicitBass = basses[arr[1].substring(0,1)];
736
- if (explicitBass) {
737
- var bassAcc = arr[1].substring(1);
738
- var bassShift = {'#': 1, '♯': 1, 'b': -1, '♭': -1}[bassAcc] || 0;
739
- bass = basses[arr[1].substring(0,1)] + bassShift + transpose;
740
- bass2 = bass;
741
- }
742
- }
743
- return { boom: bass, boom2: bass2, chick: chick };
744
- }
745
-
746
- var chordIntervals = {
747
- // diminished (all flat 5 chords)
748
- 'dim': [ 0, 3, 6 ],
749
- '°': [ 0, 3, 6 ],
750
- '˚': [ 0, 3, 6 ],
751
-
752
- 'dim7': [ 0, 3, 6, 9 ],
753
- '°7': [ 0, 3, 6, 9 ],
754
- '˚7': [ 0, 3, 6, 9 ],
755
-
756
- 'ø7': [ 0, 3, 6, 10 ],
757
- 'm7(b5)': [ 0, 3, 6, 10 ],
758
- 'm7b5': [ 0, 3, 6, 10 ],
759
- 'm7♭5': [ 0, 3, 6, 10 ],
760
- '-7(b5)': [ 0, 3, 6, 10 ],
761
- '-7b5': [ 0, 3, 6, 10 ],
762
-
763
- '7b5': [ 0, 4, 6, 10 ],
764
- '7(b5)': [ 0, 4, 6, 10 ],
765
- '7♭5': [ 0, 4, 6, 10 ],
766
-
767
- '7(b9,b5)': [ 0, 4, 6, 10, 13 ],
768
- '7b9,b5': [ 0, 4, 6, 10, 13 ],
769
- '7(#9,b5)': [ 0, 4, 6, 10, 15 ],
770
- '7#9b5': [ 0, 4, 6, 10, 15 ],
771
- 'maj7(b5)': [ 0, 3, 6, 11 ],
772
- 'maj7b5': [ 0, 3, 6, 11 ],
773
- '13(b5)': [ 0, 4, 6, 10, 14, 18 ],
774
- '13b5': [ 0, 4, 6, 10, 14, 18 ],
775
-
776
- // minor (all normal 5, minor 3 chords)
777
- 'm': [ 0, 3, 7 ],
778
- '-': [ 0, 3, 7 ],
779
- 'm6': [ 0, 3, 7, 9 ],
780
- '-6': [ 0, 3, 7, 9 ],
781
- 'm7': [ 0, 3, 7, 10 ],
782
- '-7': [ 0, 3, 7, 10 ],
783
-
784
- '-(b6)': [ 0, 3, 7, 8 ],
785
- '-b6': [ 0, 3, 7, 8 ],
786
- '-6/9': [ 0, 3, 7, 9, 14 ],
787
- '-7(b9)': [ 0, 3, 7, 10, 13 ],
788
- '-7b9': [ 0, 3, 7, 10, 13 ],
789
- '-maj7': [ 0, 3, 7, 11 ],
790
- '-9+7': [ 0, 3, 7, 11, 13 ],
791
- '-11': [ 0, 3, 7, 11, 14, 16 ],
792
-
793
- // major (all normal 5, major 3 chords)
794
- 'M': [ 0, 4, 7 ],
795
- '6': [ 0, 4, 7, 9 ],
796
- '6/9': [ 0, 4, 7, 9, 14 ],
797
-
798
- '7': [ 0, 4, 7, 10 ],
799
- '9': [ 0, 4, 7, 10, 14 ],
800
- '11': [ 0, 4, 7, 10, 14, 16 ],
801
- '13': [ 0, 4, 7, 10, 14, 18 ],
802
- '7b9': [ 0, 4, 7, 10, 13 ],
803
- '7♭9': [ 0, 4, 7, 10, 13 ],
804
- '7(b9)': [ 0, 4, 7, 10, 13 ],
805
- '7(#9)': [ 0, 4, 7, 10, 15 ],
806
- '7#9': [ 0, 4, 7, 10, 15 ],
807
- '(13)': [ 0, 4, 7, 10, 14, 18 ],
808
- '7(9,13)': [ 0, 4, 7, 10, 14, 18 ],
809
- '7(#9,b13)': [ 0, 4, 7, 10, 15, 17 ],
810
- '7(#11)': [ 0, 4, 7, 10, 14, 17 ],
811
- '7#11': [ 0, 4, 7, 10, 14, 17 ],
812
- '7(b13)': [ 0, 4, 7, 10, 17 ],
813
- '7b13': [ 0, 4, 7, 10, 17 ],
814
- '9(#11)': [ 0, 4, 7, 10, 14, 17 ],
815
- '9#11': [ 0, 4, 7, 10, 14, 17 ],
816
- '13(#11)': [ 0, 4, 7, 10, 15, 18 ],
817
- '13#11': [ 0, 4, 7, 10, 15, 18 ],
818
-
819
- 'maj7': [ 0, 4, 7, 11 ],
820
- '∆7': [ 0, 4, 7, 11 ],
821
- 'Δ7': [ 0, 4, 7, 11 ],
822
- 'maj9': [ 0, 4, 7, 11, 14 ],
823
- 'maj7(9)': [ 0, 4, 7, 11, 14 ],
824
- 'maj7(11)': [ 0, 4, 7, 11, 16 ],
825
- 'maj7(#11)': [ 0, 4, 7, 11, 17 ],
826
- 'maj7(13)': [ 0, 4, 7, 11, 18 ],
827
- 'maj7(9,13)': [ 0, 4, 7, 11, 14, 18 ],
828
-
829
- '7sus4': [ 0, 5, 7, 10 ],
830
- 'm7sus4': [ 0, 5, 7, 10 ],
831
- 'sus4': [ 0, 5, 7 ],
832
- 'sus2': [ 0, 2, 7 ],
833
- '7sus2': [ 0, 2, 7, 10 ],
834
- '9sus4': [ 0, 5, 7, 14 ],
835
- '13sus4': [ 0, 5, 7, 18 ],
836
-
837
- // augmented (all sharp 5 chords)
838
- 'aug7': [ 0, 4, 8, 10 ],
839
- '+7': [ 0, 4, 8, 10 ],
840
- '+': [ 0, 4, 8 ],
841
- '7#5': [ 0, 4, 8, 10 ],
842
- '7♯5': [ 0, 4, 8, 10 ],
843
- '7+5': [ 0, 4, 8, 10 ],
844
- '9#5': [ 0, 4, 8, 10, 14 ],
845
- '9♯5': [ 0, 4, 8, 10, 14 ],
846
- '9+5': [ 0, 4, 8, 10, 14 ],
847
- '-7(#5)': [ 0, 3, 8, 10 ],
848
- '-7#5': [ 0, 3, 8, 10 ],
849
- '7(#5)': [ 0, 4, 8, 10 ],
850
- '7(b9,#5)': [ 0, 4, 8, 10, 13 ],
851
- '7b9#5': [ 0, 4, 8, 10, 13 ],
852
- 'maj7(#5)': [ 0, 4, 8, 11 ],
853
- 'maj7#5': [ 0, 4, 8, 11 ],
854
- 'maj7(#5,#11)': [ 0, 4, 8, 11, 14 ],
855
- 'maj7#5#11': [ 0, 4, 8, 11, 14 ],
856
- '9(#5)': [ 0, 4, 8, 10, 14 ],
857
- '13(#5)': [ 0, 4, 8, 10, 14, 18 ],
858
- '13#5': [ 0, 4, 8, 10, 14, 18 ]
859
- };
860
- function chordNotes(bass, modifier) {
861
- var intervals = chordIntervals[modifier];
862
- if (!intervals) {
863
- if (modifier.charAt(0) === 'm')
864
- intervals = chordIntervals.m;
865
- else
866
- intervals = chordIntervals.M;
867
- }
868
- bass += 12; // the chord is an octave above the bass note.
869
- var notes = [ ];
870
- for (var i = 0; i < intervals.length; i++) {
871
- notes.push(bass + intervals[i]);
872
- }
873
- return notes;
874
- }
875
-
876
- function writeBoom(boom, beatLength) {
877
- // undefined means there is a stop time.
878
- if (boom !== undefined)
879
- chordTrack.push({cmd: 'start', pitch: boom, volume: 64});
880
- addMove(chordTrack, (beatLength/2)*tempoChangeFactor);
881
- if (boom !== undefined)
882
- chordTrack.push({ cmd: 'stop', pitch: boom });
883
- addMove(chordTrack, (beatLength/2)*tempoChangeFactor);
884
- }
885
-
886
- function writeChick(chick, beatLength) {
887
- for (var c = 0; c < chick.length; c++)
888
- chordTrack.push({cmd: 'start', pitch: chick[c], volume: 48});
889
- addMove(chordTrack, (beatLength/2)*tempoChangeFactor);
890
- for (c = 0; c < chick.length; c++)
891
- chordTrack.push({ cmd: 'stop', pitch: chick[c] });
892
- addMove(chordTrack, (beatLength/2)*tempoChangeFactor);
893
- }
894
-
895
- var rhythmPatterns = { "2/2": [ 'boom', 'chick' ],
896
- "2/4": [ 'boom', 'chick' ],
897
- "3/4": [ 'boom', 'chick', 'chick' ],
898
- "4/4": [ 'boom', 'chick', 'boom2', 'chick' ],
899
- "5/4": [ 'boom', 'chick', 'chick', 'boom2', 'chick' ],
900
- "6/8": [ 'boom', '', 'chick', 'boom2', '', 'chick' ],
901
- "9/8": [ 'boom', '', 'chick', 'boom2', '', 'chick', 'boom2', '', 'chick' ],
902
- "12/8": [ 'boom', '', 'chick', 'boom2', '', 'chick', 'boom2', '', 'chick', 'boom2', '', 'chick' ],
903
- };
904
-
905
- function resolveChords() {
906
- var num = meter.num;
907
- var den = meter.den;
908
- var beatLength = 1/den;
909
- var pattern = rhythmPatterns[num+'/'+den];
910
- var thisMeasureLength = parseInt(num,10)/parseInt(den,10);
911
- // See if this is a full measure: unfortunately, with triplets, there isn't an exact match, what with the floating point, so we just see if it is "close".
912
- var portionOfAMeasure = Math.abs(thisMeasureLength - barBeat);
913
- if (!pattern || portionOfAMeasure > 0.0078125) { // If it is an unsupported meter, or this isn't a full bar, just chick on each beat.
914
- pattern = [];
915
- var beatsPresent = barBeat / beatLength;
916
- for (var p = 0; p < beatsPresent; p++)
917
- pattern.push("chick");
918
- }
919
-
920
- if (currentChords.length === 0) { // there wasn't a new chord this measure, so use the last chord declared.
921
- currentChords.push({ beat: 0, chord: lastChord});
922
- }
923
- 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.
924
- currentChords.unshift({ beat: 0, chord: lastChord});
925
- }
926
- if (currentChords.length === 1) {
927
- for (var m = 0; m < pattern.length; m++) {
928
- switch (pattern[m]) {
929
- case 'boom':
930
- writeBoom(currentChords[0].chord.boom, beatLength);
931
- break;
932
- case 'boom2':
933
- writeBoom(currentChords[0].chord.boom2, beatLength);
934
- break;
935
- case 'chick':
936
- writeChick(currentChords[0].chord.chick, beatLength);
937
- break;
938
- case '':
939
- addMove(chordTrack, beatLength*tempoChangeFactor);
940
- break;
941
- }
942
- }
943
- return;
944
- }
945
-
946
- // 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.
947
-
948
- // First, normalize the chords on beats.
949
- var beats = {};
950
- for (var i = 0; i < currentChords.length; i++) {
951
- var cc = currentChords[i];
952
- var beat = Math.floor(cc.beat / beatLength); // now all the beats are integers, there may be
953
- beats[''+beat] = cc;
954
- }
955
-
956
- // - If there is a chord on the second beat, play a chord for the first beat instead of a bass note.
957
- // - 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.
958
- for (var m2 = 0; m2 < pattern.length; m2++) {
959
- var thisChord;
960
- if (beats[''+m2])
961
- thisChord = beats[''+m2];
962
- switch (pattern[m2]) {
963
- case 'boom':
964
- if (beats[''+(m2+1)]) // If there is not a chord change on the next beat, play a bass note.
965
- writeChick(thisChord.chord.chick, beatLength);
966
- else
967
- writeBoom(thisChord.chord.boom, beatLength);
968
- break;
969
- case 'boom2':
970
- if (beats[''+(m2+1)])
971
- writeChick(thisChord.chord.chick, beatLength);
972
- else
973
- writeBoom(thisChord.chord.boom2, beatLength);
974
- break;
975
- case 'chick':
976
- writeChick(thisChord.chord.chick, beatLength);
977
- break;
978
- case '':
979
- if (beats[''+m2]) // If there is an explicit chord on this beat, play it.
980
- writeChick(thisChord.chord.chick, beatLength);
981
- else
982
- addMove(chordTrack, beatLength*tempoChangeFactor);
983
- break;
984
- }
985
- }
986
- }
987
-
988
- function normalizeDrumDefinition(params) {
989
- // Be very strict with the drum definition. If anything is not perfect,
990
- // just turn the drums off.
991
- // Perhaps all of this logic belongs in the parser instead.
992
- if (params.pattern.length === 0 || params.on === false)
993
- return { on: false };
994
-
995
- var str = params.pattern[0];
996
- var events = [];
997
- var event = "";
998
- var totalPlay = 0;
999
- for (var i = 0; i < str.length; i++) {
1000
- if (str[i] === 'd')
1001
- totalPlay++;
1002
- if (str[i] === 'd' || str[i] === 'z') {
1003
- if (event.length !== 0) {
1004
- events.push(event);
1005
- event = str[i];
1006
- } else
1007
- event = event + str[i];
1008
- } else {
1009
- if (event.length === 0) {
1010
- // there was an error: the string should have started with d or z
1011
- return {on: false};
1012
- }
1013
- event = event + str[i];
1014
- }
1015
- }
1016
-
1017
- if (event.length !== 0)
1018
- events.push(event);
1019
-
1020
- // Now the events array should have one item per event.
1021
- // There should be two more params for each event: the volume and the pitch.
1022
- if (params.pattern.length !== totalPlay*2 + 1)
1023
- return { on: false };
1024
-
1025
- var ret = { on: true, bars: params.bars, pattern: []};
1026
- var beatLength = 1/meter.den;
1027
- var playCount = 0;
1028
- for (var j = 0; j < events.length; j++) {
1029
- event = events[j];
1030
- var len = 1;
1031
- var div = false;
1032
- var num = 0;
1033
- for (var k = 1; k < event.length; k++) {
1034
- switch(event[k]) {
1035
- case "/":
1036
- if (num !== 0)
1037
- len *= num;
1038
- num = 0;
1039
- div = true;
1040
- break;
1041
- case "1":
1042
- case "2":
1043
- case "3":
1044
- case "4":
1045
- case "5":
1046
- case "6":
1047
- case "7":
1048
- case "8":
1049
- case "9":
1050
- num = num*10 +event[k];
1051
- break;
1052
- default:
1053
- return { on: false };
1054
- }
1055
- }
1056
- if (div) {
1057
- if (num === 0) num = 2; // a slash by itself is interpreted as "/2"
1058
- len /= num;
1059
- } else if (num)
1060
- len *= num;
1061
- if (event[0] === 'd') {
1062
- ret.pattern.push({ len: len * beatLength, pitch: params.pattern[1 + playCount], velocity: params.pattern[1 + playCount + totalPlay]});
1063
- playCount++;
1064
- } else
1065
- ret.pattern.push({ len: len * beatLength, pitch: null});
1066
- }
1067
- // Now normalize the pattern to cover the correct number of measures. The note lengths passed are relative to each other and need to be scaled to fit a measure.
1068
- var totalTime = 0;
1069
- var measuresPerBeat = meter.num/meter.den;
1070
- for (var ii = 0; ii < ret.pattern.length; ii++)
1071
- totalTime += ret.pattern[ii].len;
1072
- var numBars = params.bars ? params.bars : 1;
1073
- var factor = totalTime / numBars / measuresPerBeat;
1074
- for (ii = 0; ii < ret.pattern.length; ii++)
1075
- ret.pattern[ii].len = ret.pattern[ii].len / factor;
1076
- return ret;
1077
- }
1078
-
1079
- function drumBeat(pitch, soundLength, volume) {
1080
- drumTrack.push({ cmd: 'start', pitch: pitch - 60, volume: volume});
1081
- addMove(drumTrack, soundLength);
1082
- drumTrack.push({ cmd: 'stop', pitch: pitch - 60 });
1083
- }
1084
-
1085
- function writeDrum(channel) {
1086
- if (drumTrack.length === 0 && !drumDefinition.on)
1087
- return;
1088
-
1089
- var measureLen = meter.num/meter.den;
1090
- if (drumTrack.length === 0) {
1091
- drumTrack.push({cmd: 'program', channel: channel, instrument: drumInstrument});
1092
- // need to figure out how far in time the bar started: if there are pickup notes before the chords start, we need pauses.
1093
- var distance = timeFromStart();
1094
- if (distance > 0 && distance < measureLen - 0.01) { // because of floating point, adding the notes might not exactly equal the measure size.
1095
- addMove(drumTrack, distance * tempoChangeFactor);
1096
- return;
1097
- }
1098
- }
1099
-
1100
- if (!drumDefinition.on) {
1101
- // this is the case where there has been a drum track, but it was specifically turned off.
1102
- addMove(drumTrack, measureLen * tempoChangeFactor);
1103
- return;
1104
- }
1105
- for (var i = 0; i < drumDefinition.pattern.length; i++) {
1106
- var len = drumDefinition.pattern[i].len * tempoChangeFactor;
1107
- if (drumDefinition.pattern[i].pitch)
1108
- drumBeat(drumDefinition.pattern[i].pitch, len, drumDefinition.pattern[i].velocity);
1109
- else
1110
- addMove(drumTrack, len);
1111
- }
1112
- }
1113
- function addMove(array, duration) {
1114
- if (duration > 0)
1115
- array.push({ cmd: 'move', duration: duration });
1116
- }
1117
- })();
1118
-
1119
- module.exports = flatten;