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.
- package/.github/workflows/tests.yml +29 -0
- package/CODE_OF_CONDUCT.md +76 -0
- package/CONTRIBUTING.md +1 -0
- package/LICENSE.md +1 -1
- package/README.md +92 -3
- package/RELEASE.md +957 -1
- package/abcjs-audio.css +14 -5
- package/dist/.gitignore +1 -2
- package/dist/abcjs-basic-min.js +3 -0
- package/dist/abcjs-basic-min.js.LICENSE +23 -0
- package/dist/abcjs-basic.js +28231 -0
- package/dist/abcjs-basic.js.map +1 -0
- package/dist/abcjs-plugin-min.js +3 -0
- package/dist/abcjs-plugin-min.js.LICENSE +23 -0
- package/dist/report-basic.html +37 -0
- package/dist/report-before-glyph-compress.html +37 -0
- package/dist/report-brown-ts-target-es5.html +37 -0
- package/dist/report-dev-orig-no-babel.html +37 -0
- package/dist/report-synth.html +37 -0
- package/docker-build.sh +1 -0
- package/glyphs.json +1 -0
- package/index.js +27 -2
- package/{static-wrappers/license.js → license.js} +1 -1
- package/package.json +26 -29
- package/{src/plugin/abc_plugin.js → plugin.js} +31 -19
- package/src/api/abc_animation.js +1 -17
- package/src/api/abc_tablatures.js +144 -0
- package/src/api/abc_timing_callbacks.js +234 -116
- package/src/api/abc_tunebook.js +18 -67
- package/src/api/abc_tunebook_svg.js +38 -46
- package/src/data/abc_tune.js +232 -972
- package/src/data/deline-tune.js +199 -0
- package/src/edit/abc_editarea.js +112 -0
- package/src/edit/abc_editor.js +95 -221
- package/src/midi/abc_midi_create.js +48 -50
- package/src/parse/abc_common.js +0 -14
- package/src/parse/abc_parse.js +167 -1321
- package/src/parse/abc_parse_book.js +62 -0
- package/src/parse/abc_parse_directive.js +164 -41
- package/src/parse/abc_parse_header.js +116 -145
- package/src/parse/abc_parse_key_voice.js +26 -20
- package/src/parse/abc_parse_music.js +1337 -0
- package/src/parse/abc_tokenizer.js +21 -15
- package/src/parse/abc_transpose.js +3 -15
- package/src/parse/tune-builder.js +896 -0
- package/src/parse/wrap_lines.js +205 -453
- package/src/synth/abc_midi_flattener.js +1292 -0
- package/src/{midi → synth}/abc_midi_renderer.js +44 -17
- package/src/synth/abc_midi_sequencer.js +648 -0
- package/src/synth/active-audio-context.js +3 -14
- package/src/synth/cents-to-factor.js +10 -0
- package/src/synth/create-note-map.js +21 -32
- package/src/synth/create-synth-control.js +20 -103
- package/src/synth/create-synth.js +185 -77
- package/src/synth/download-buffer.js +7 -21
- package/src/synth/get-midi-file.js +13 -20
- package/src/synth/images/{loading.svg → loading.svg.js} +4 -0
- package/src/synth/images/loop.svg.js +65 -0
- package/src/synth/images/pause.svg.js +10 -0
- package/src/synth/images/play.svg.js +9 -0
- package/src/synth/images/{reset.svg → reset.svg.js} +5 -1
- package/src/synth/instrument-index-to-name.js +1 -16
- package/src/synth/load-note.js +37 -76
- package/src/synth/pitch-to-note-name.js +0 -15
- package/src/synth/pitches-to-perc.js +64 -0
- package/src/synth/place-note.js +78 -68
- package/src/synth/play-event.js +17 -18
- package/src/synth/register-audio-context.js +11 -23
- package/src/synth/sounds-cache.js +0 -15
- package/src/synth/supports-audio.js +9 -23
- package/src/synth/synth-controller.js +80 -49
- package/src/synth/synth-sequence.js +20 -34
- package/src/tablatures/instruments/guitar/guitar-fonts.js +19 -0
- package/src/tablatures/instruments/guitar/guitar-patterns.js +23 -0
- package/src/tablatures/instruments/guitar/tab-guitar.js +50 -0
- package/src/tablatures/instruments/string-patterns.js +277 -0
- package/src/tablatures/instruments/string-tablature.js +56 -0
- package/src/tablatures/instruments/tab-note.js +282 -0
- package/src/tablatures/instruments/tab-notes.js +41 -0
- package/src/tablatures/instruments/violin/tab-violin.js +47 -0
- package/src/tablatures/instruments/violin/violin-fonts.js +19 -0
- package/src/tablatures/instruments/violin/violin-patterns.js +23 -0
- package/src/tablatures/tab-absolute-elements.js +310 -0
- package/src/tablatures/tab-common.js +29 -0
- package/src/tablatures/tab-renderer.js +243 -0
- package/src/tablatures/transposer.js +110 -0
- package/src/test/abc_midi_lint.js +5 -22
- package/src/test/abc_midi_sequencer_lint.js +11 -14
- package/src/test/abc_parser_lint.js +136 -32
- package/src/test/abc_vertical_lint.js +94 -32
- package/src/test/rendering-lint.js +38 -5
- package/src/write/abc_absolute_element.js +112 -120
- package/src/write/abc_abstract_engraver.js +102 -253
- package/src/write/abc_beam_element.js +30 -290
- package/src/write/abc_brace_element.js +12 -121
- package/src/write/abc_create_clef.js +21 -32
- package/src/write/abc_create_key_signature.js +8 -26
- package/src/write/abc_create_note_head.js +107 -0
- package/src/write/abc_create_time_signature.js +2 -21
- package/src/write/abc_crescendo_element.js +3 -50
- package/src/write/abc_decoration.js +7 -30
- package/src/write/abc_dynamic_decoration.js +3 -37
- package/src/write/abc_ending_element.js +1 -57
- package/src/write/abc_engraver_controller.js +111 -234
- package/src/write/abc_glyphs.js +9 -19
- package/src/write/abc_relative_element.js +57 -97
- package/src/write/abc_renderer.js +10 -832
- package/src/write/abc_spacing.js +0 -15
- package/src/write/abc_staff_group_element.js +14 -349
- package/src/write/abc_tempo_element.js +9 -117
- package/src/write/abc_tie_element.js +5 -68
- package/src/write/abc_triplet_element.js +6 -124
- package/src/write/abc_voice_element.js +7 -222
- package/src/write/add-chord.js +103 -0
- package/src/write/add-text-if.js +33 -0
- package/src/write/bottom-text.js +79 -0
- package/src/write/calcHeight.js +17 -0
- package/src/write/classes.js +100 -0
- package/src/write/draw/absolute.js +68 -0
- package/src/write/draw/beam.js +56 -0
- package/src/write/draw/brace.js +106 -0
- package/src/write/draw/crescendo.js +38 -0
- package/src/write/draw/debug-box.js +8 -0
- package/src/write/draw/draw.js +56 -0
- package/src/write/draw/dynamics.js +20 -0
- package/src/write/draw/ending.js +46 -0
- package/src/write/draw/group-elements.js +66 -0
- package/src/write/draw/horizontal-line.js +25 -0
- package/src/write/draw/non-music.js +50 -0
- package/src/write/draw/print-line.js +24 -0
- package/src/write/draw/print-path.js +7 -0
- package/src/write/draw/print-stem.js +30 -0
- package/src/write/draw/print-symbol.js +59 -0
- package/src/write/draw/print-vertical-line.js +18 -0
- package/src/write/draw/relative.js +77 -0
- package/src/write/draw/round-number.js +5 -0
- package/src/write/draw/selectables.js +59 -0
- package/src/write/draw/separator.js +16 -0
- package/src/write/draw/set-paper-size.js +45 -0
- package/src/write/{sprintf.js → draw/sprintf.js} +0 -0
- package/src/write/draw/staff-group.js +226 -0
- package/src/write/draw/staff-line.js +9 -0
- package/src/write/draw/staff.js +33 -0
- package/src/write/draw/tab-line.js +40 -0
- package/src/write/draw/tempo.js +45 -0
- package/src/write/draw/text.js +71 -0
- package/src/write/draw/tie.js +97 -0
- package/src/write/draw/triplet.js +46 -0
- package/src/write/draw/voice.js +102 -0
- package/src/write/format-jazz-chord.js +15 -0
- package/src/write/free-text.js +41 -0
- package/src/write/get-font-and-attr.js +37 -0
- package/src/write/get-text-size.js +56 -0
- package/src/write/highlight.js +11 -0
- package/src/write/layout/VoiceElements.js +121 -0
- package/src/write/layout/beam.js +213 -0
- package/src/write/layout/get-left-edge-of-staff.js +56 -0
- package/src/write/layout/getBarYAt.js +6 -0
- package/src/write/layout/layout.js +94 -0
- package/src/write/layout/setUpperAndLowerElements.js +232 -0
- package/src/write/layout/staffGroup.js +146 -0
- package/src/write/layout/triplet.js +75 -0
- package/src/write/layout/voice.js +137 -0
- package/src/write/selection.js +188 -70
- package/src/write/separator.js +10 -0
- package/src/write/set-class.js +21 -0
- package/src/write/subtitle.js +12 -0
- package/src/write/svg.js +95 -43
- package/src/write/top-text.js +54 -0
- package/src/write/unhighlight.js +11 -0
- package/temp.txt +17 -0
- package/test.js +27 -64
- package/types/index.d.ts +1095 -0
- package/version.js +1 -1
- package/.babelrc +0 -5
- package/.eslintrc +0 -3
- package/.gitmodules +0 -3
- package/abcjs-midi.css +0 -166
- package/build-utils/loadPresets.js +0 -14
- package/build-utils/presets/webpack.analyze.js +0 -6
- package/build-utils/presets/webpack.optimize.js +0 -30
- package/build-utils/webpack.development.js +0 -14
- package/build-utils/webpack.production.js +0 -35
- package/deploy-docs.sh +0 -25
- package/docs/README.md +0 -33
- package/fix-versions.sh +0 -23
- package/mei.js +0 -43
- package/midi.js +0 -62
- package/src/api/abc_tunebook_midi.js +0 -116
- package/src/midi/abc_midi_controls.js +0 -701
- package/src/midi/abc_midi_flattener.js +0 -1119
- package/src/midi/abc_midi_js_preparer.js +0 -243
- package/src/midi/abc_midi_sequencer.js +0 -401
- package/src/midi/abc_midi_ui_generator.js +0 -86
- package/src/plugin/abc_plugin_midi.js +0 -220
- package/src/synth/images/loop.svg +0 -61
- package/src/synth/images/pause.svg +0 -6
- package/src/synth/images/play.svg +0 -5
- package/src/transform/abc2abc_write.js +0 -395
- package/static-wrappers/basic.js +0 -2
- package/static-wrappers/midi.js +0 -2
- package/static-wrappers/plugin-midi.js +0 -6
- package/static-wrappers/plugin.js +0 -6
- 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;
|