abcjs-rails 2.3 → 3.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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/abcjs/api/abc_animation.js +166 -4
- data/app/assets/javascripts/abcjs/api/abc_tunebook.js +170 -15
- data/app/assets/javascripts/abcjs/data/abc_tune.js +69 -47
- data/app/assets/javascripts/abcjs/edit/abc_editor.js +78 -37
- data/app/assets/javascripts/abcjs/midi/abc_midi_controls.js +513 -0
- data/app/assets/javascripts/abcjs/midi/abc_midi_create.js +29 -7
- data/app/assets/javascripts/abcjs/midi/abc_midi_flattener.js +21 -18
- data/app/assets/javascripts/abcjs/midi/abc_midi_js_preparer.js +233 -0
- data/app/assets/javascripts/abcjs/midi/abc_midi_renderer.js +3 -229
- data/app/assets/javascripts/abcjs/midi/abc_midi_sequencer.js +11 -3
- data/app/assets/javascripts/abcjs/parse/abc_common.js +24 -0
- data/app/assets/javascripts/abcjs/parse/abc_parse.js +58 -16
- data/app/assets/javascripts/abcjs/parse/abc_parse_header.js +4 -1
- data/app/assets/javascripts/abcjs/parse/abc_parse_key_voice.js +5 -0
- data/app/assets/javascripts/abcjs/write/abc_absolute_element.js +9 -2
- data/app/assets/javascripts/abcjs/write/abc_abstract_engraver.js +71 -19
- data/app/assets/javascripts/abcjs/write/abc_beam_element.js +11 -3
- data/app/assets/javascripts/abcjs/write/abc_brace_element.js +51 -0
- data/app/assets/javascripts/abcjs/write/abc_create_clef.js +3 -3
- data/app/assets/javascripts/abcjs/write/abc_create_key_signature.js +3 -3
- data/app/assets/javascripts/abcjs/write/abc_create_time_signature.js +3 -3
- data/app/assets/javascripts/abcjs/write/abc_crescendo_element.js +4 -1
- data/app/assets/javascripts/abcjs/write/abc_decoration.js +1 -1
- data/app/assets/javascripts/abcjs/write/abc_engraver_controller.js +10 -9
- data/app/assets/javascripts/abcjs/write/abc_glyphs.js +1 -1
- data/app/assets/javascripts/abcjs/write/abc_relative_element.js +1 -1
- data/app/assets/javascripts/abcjs/write/abc_renderer.js +85 -13
- data/app/assets/javascripts/abcjs/write/abc_staff_group_element.js +16 -0
- data/app/assets/javascripts/abcjs/write/abc_tempo_element.js +31 -11
- data/app/assets/javascripts/abcjs/write/abc_tie_element.js +8 -1
- data/lib/abcjs-rails/version.rb +1 -1
- metadata +6 -3
@@ -1,5 +1,5 @@
|
|
1
1
|
// abc_midi_create.js: Turn a linear series of events into a midi file.
|
2
|
-
// Copyright (C) 2010,
|
2
|
+
// Copyright (C) 2010,2016 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
|
3
3
|
//
|
4
4
|
// This program is free software: you can redistribute it and/or modify
|
5
5
|
// it under the terms of the GNU General Public License as published by
|
@@ -26,44 +26,66 @@ if (!window.ABCJS.midi)
|
|
26
26
|
var baseDuration = 480*4; // nice and divisible, equals 1 whole note
|
27
27
|
|
28
28
|
window.ABCJS.midi.create = function(abcTune, options) {
|
29
|
+
if (options === undefined) options = {};
|
29
30
|
var sequence = window.ABCJS.midi.sequence(abcTune, options);
|
30
31
|
var commands = window.ABCJS.midi.flatten(sequence);
|
31
|
-
var midi = window.ABCJS.midi.rendererFactory(
|
32
|
+
var midi = window.ABCJS.midi.rendererFactory();
|
33
|
+
var midiJs = new window.ABCJS.midi.Preparer();
|
32
34
|
|
33
|
-
if (commands.instrument !== undefined)
|
34
|
-
midi.setInstrument(commands.instrument);
|
35
|
-
if (commands.channel !== undefined)
|
36
|
-
midi.setChannel(commands.channel);
|
37
35
|
var title = abcTune.metaText ? abcTune.metaText.title : undefined;
|
38
36
|
if (title && title.length > 128)
|
39
37
|
title = title.substring(0,124) + '...';
|
40
38
|
midi.setGlobalInfo(commands.tempo, title);
|
39
|
+
midiJs.setGlobalInfo(commands.tempo, title);
|
41
40
|
|
41
|
+
if (commands.instrument !== undefined) {
|
42
|
+
midi.setInstrument(commands.instrument);
|
43
|
+
midiJs.setInstrument(commands.instrument);
|
44
|
+
}
|
45
|
+
if (commands.channel !== undefined) {
|
46
|
+
midi.setChannel(commands.channel);
|
47
|
+
midiJs.setChannel(commands.channel);
|
48
|
+
}
|
42
49
|
for (var i = 0; i < commands.tracks.length; i++) {
|
43
50
|
midi.startTrack();
|
51
|
+
midiJs.startTrack();
|
44
52
|
for (var j = 0; j < commands.tracks[i].length; j++) {
|
45
53
|
var event = commands.tracks[i][j];
|
46
54
|
switch (event.cmd) {
|
47
55
|
case 'instrument':
|
48
56
|
midi.setInstrument(event.instrument);
|
57
|
+
midiJs.setInstrument(event.instrument);
|
49
58
|
break;
|
50
59
|
case 'start':
|
51
60
|
midi.startNote(convertPitch(event.pitch), event.volume);
|
61
|
+
midiJs.startNote(convertPitch(event.pitch), event.volume);
|
52
62
|
break;
|
53
63
|
case 'stop':
|
54
64
|
midi.endNote(convertPitch(event.pitch), 0); // TODO-PER: Refactor: the old midi used a duration here.
|
65
|
+
midiJs.endNote(convertPitch(event.pitch));
|
55
66
|
break;
|
56
67
|
case 'move':
|
57
68
|
midi.addRest(event.duration * baseDuration);
|
69
|
+
midiJs.addRest(event.duration * baseDuration);
|
58
70
|
break;
|
59
71
|
default:
|
60
72
|
console.log("MIDI create Unknown: " + event.cmd);
|
61
73
|
}
|
62
74
|
}
|
63
75
|
midi.endTrack();
|
76
|
+
midiJs.endTrack();
|
64
77
|
}
|
65
78
|
|
66
|
-
|
79
|
+
var midiFile = midi.getData();
|
80
|
+
var midiInline = midiJs.getData();
|
81
|
+
if (options.generateInline === undefined) // default is to generate inline controls.
|
82
|
+
options.generateInline = true;
|
83
|
+
if (options.generateInline && options.generateDownload)
|
84
|
+
return { download: midiFile, inline: midiInline };
|
85
|
+
else if (options.generateInline)
|
86
|
+
return midiInline;
|
87
|
+
else
|
88
|
+
return midiFile;
|
67
89
|
};
|
68
90
|
|
69
91
|
function convertPitch(pitch) {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
// abc_midi_create.js: Turn a linear series of events into a series of MIDI commands.
|
2
|
-
// Copyright (C) 2010,
|
2
|
+
// Copyright (C) 2010,2016 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
|
3
3
|
//
|
4
4
|
// This program is free software: you can redistribute it and/or modify
|
5
5
|
// it under the terms of the GNU General Public License as published by
|
@@ -34,7 +34,7 @@ if (!window.ABCJS.midi)
|
|
34
34
|
var multiplier;
|
35
35
|
var tracks;
|
36
36
|
var startingTempo;
|
37
|
-
var
|
37
|
+
var tempoChangeFactor = 1;
|
38
38
|
var instrument;
|
39
39
|
var channel;
|
40
40
|
var currentTrack;
|
@@ -58,7 +58,7 @@ if (!window.ABCJS.midi)
|
|
58
58
|
multiplier = 1;
|
59
59
|
tracks = [];
|
60
60
|
startingTempo = undefined;
|
61
|
-
|
61
|
+
tempoChangeFactor = 1;
|
62
62
|
instrument = undefined;
|
63
63
|
channel = undefined;
|
64
64
|
currentTrack = undefined;
|
@@ -93,7 +93,7 @@ if (!window.ABCJS.midi)
|
|
93
93
|
if (!startingTempo)
|
94
94
|
startingTempo = element.qpm;
|
95
95
|
else
|
96
|
-
|
96
|
+
tempoChangeFactor = element.qpm ? startingTempo / element.qpm : 1;
|
97
97
|
break;
|
98
98
|
case "transpose":
|
99
99
|
transpose = element.transpose;
|
@@ -190,7 +190,7 @@ if (!window.ABCJS.midi)
|
|
190
190
|
// If we ever have a chord in this voice, then we add the chord track.
|
191
191
|
// However, if there are chords on more than one voice, then just use the first voice.
|
192
192
|
if (chordTrack.length === 0) {
|
193
|
-
chordTrack.push({cmd: 'instrument', instrument:
|
193
|
+
chordTrack.push({cmd: 'instrument', instrument: 0});
|
194
194
|
// need to figure out how far in time the chord started: if there are pickup notes before the chords start, we need pauses.
|
195
195
|
var distance = 0;
|
196
196
|
for (var ct = 0; ct < currentTrack.length; ct++) {
|
@@ -198,7 +198,7 @@ if (!window.ABCJS.midi)
|
|
198
198
|
distance += currentTrack[ct].duration;
|
199
199
|
}
|
200
200
|
if (distance > 0)
|
201
|
-
chordTrack.push({cmd: 'move', duration: distance});
|
201
|
+
chordTrack.push({cmd: 'move', duration: distance*tempoChangeFactor });
|
202
202
|
}
|
203
203
|
|
204
204
|
lastChord = c;
|
@@ -253,16 +253,16 @@ if (!window.ABCJS.midi)
|
|
253
253
|
else if (note.endTie)
|
254
254
|
pitchesTied[''+actualPitch] = false;
|
255
255
|
}
|
256
|
-
currentTrack.push({ cmd: 'move', duration: duration-normalBreakBetweenNotes });
|
256
|
+
currentTrack.push({ cmd: 'move', duration: (duration-normalBreakBetweenNotes)*tempoChangeFactor });
|
257
257
|
lastNoteDurationPosition = currentTrack.length-1;
|
258
258
|
|
259
259
|
for (var ii = 0; ii < pitches.length; ii++) {
|
260
260
|
if (!pitchesTied[''+pitches[ii].pitch])
|
261
261
|
currentTrack.push({ cmd: 'stop', pitch: pitches[ii].pitch });
|
262
262
|
}
|
263
|
-
currentTrack.push({ cmd: 'move', duration: normalBreakBetweenNotes });
|
263
|
+
currentTrack.push({ cmd: 'move', duration: normalBreakBetweenNotes*tempoChangeFactor });
|
264
264
|
} else if (elem.rest) {
|
265
|
-
currentTrack.push({ cmd: 'move', duration: duration });
|
265
|
+
currentTrack.push({ cmd: 'move', duration: duration*tempoChangeFactor });
|
266
266
|
}
|
267
267
|
|
268
268
|
if (elem.endTriplet) {
|
@@ -337,7 +337,7 @@ if (!window.ABCJS.midi)
|
|
337
337
|
var gp = adjustPitch(graces[g]);
|
338
338
|
if (gp !== skipNote)
|
339
339
|
currentTrack.push({cmd: 'start', pitch: gp, volume: 64});
|
340
|
-
currentTrack.push({cmd: 'move', duration: graces[g].duration});
|
340
|
+
currentTrack.push({cmd: 'move', duration: graces[g].duration*tempoChangeFactor });
|
341
341
|
if (gp !== skipNote)
|
342
342
|
currentTrack.push({cmd: 'stop', pitch: gp});
|
343
343
|
if (!stealFromCurrent)
|
@@ -404,8 +404,11 @@ if (!window.ABCJS.midi)
|
|
404
404
|
var arr = remaining.split('/');
|
405
405
|
chick = chordNotes(bass, arr[0]);
|
406
406
|
if (arr.length === 2) {
|
407
|
-
|
408
|
-
|
407
|
+
var explicitBass = basses[arr[1]];
|
408
|
+
if (explicitBass) {
|
409
|
+
bass = basses[arr[1]] + transpose;
|
410
|
+
bass2 = bass;
|
411
|
+
}
|
409
412
|
}
|
410
413
|
return { boom: bass, boom2: bass2, chick: chick };
|
411
414
|
}
|
@@ -458,19 +461,19 @@ if (!window.ABCJS.midi)
|
|
458
461
|
// undefined means there is a stop time.
|
459
462
|
if (boom !== undefined)
|
460
463
|
chordTrack.push({cmd: 'start', pitch: boom, volume: 64});
|
461
|
-
chordTrack.push({ cmd: 'move', duration: beatLength/2 });
|
464
|
+
chordTrack.push({ cmd: 'move', duration: (beatLength/2)*tempoChangeFactor });
|
462
465
|
if (boom !== undefined)
|
463
466
|
chordTrack.push({ cmd: 'stop', pitch: boom });
|
464
|
-
chordTrack.push({ cmd: 'move', duration: beatLength/2 });
|
467
|
+
chordTrack.push({ cmd: 'move', duration: (beatLength/2)*tempoChangeFactor });
|
465
468
|
}
|
466
469
|
|
467
470
|
function writeChick(chick, beatLength) {
|
468
471
|
for (var c = 0; c < chick.length; c++)
|
469
472
|
chordTrack.push({cmd: 'start', pitch: chick[c], volume: 48});
|
470
|
-
chordTrack.push({ cmd: 'move', duration: beatLength/2 });
|
473
|
+
chordTrack.push({ cmd: 'move', duration: (beatLength/2)*tempoChangeFactor });
|
471
474
|
for (c = 0; c < chick.length; c++)
|
472
475
|
chordTrack.push({ cmd: 'stop', pitch: chick[c] });
|
473
|
-
chordTrack.push({ cmd: 'move', duration: beatLength/2 });
|
476
|
+
chordTrack.push({ cmd: 'move', duration: (beatLength/2)*tempoChangeFactor });
|
474
477
|
}
|
475
478
|
|
476
479
|
var rhythmPatterns = { "2/2": [ 'boom', 'chick' ],
|
@@ -514,7 +517,7 @@ if (!window.ABCJS.midi)
|
|
514
517
|
writeChick(currentChords[0].chord.chick, beatLength);
|
515
518
|
break;
|
516
519
|
case '':
|
517
|
-
chordTrack.push({ cmd: 'move', duration: beatLength });
|
520
|
+
chordTrack.push({ cmd: 'move', duration: beatLength*tempoChangeFactor });
|
518
521
|
break;
|
519
522
|
}
|
520
523
|
}
|
@@ -557,7 +560,7 @@ if (!window.ABCJS.midi)
|
|
557
560
|
if (beats[''+m2]) // If there is an explicit chord on this beat, play it.
|
558
561
|
writeChick(thisChord.chord.chick, beatLength);
|
559
562
|
else
|
560
|
-
chordTrack.push({cmd: 'move', duration: beatLength});
|
563
|
+
chordTrack.push({cmd: 'move', duration: beatLength*tempoChangeFactor });
|
561
564
|
break;
|
562
565
|
}
|
563
566
|
}
|
@@ -0,0 +1,233 @@
|
|
1
|
+
// abc_midi_js_preparer.js: Create the structure that MIDI.js expects.
|
2
|
+
// Copyright (C) 2010,2016 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
|
3
|
+
//
|
4
|
+
// This program is free software: you can redistribute it and/or modify
|
5
|
+
// it under the terms of the GNU General Public License as published by
|
6
|
+
// the Free Software Foundation, either version 3 of the License, or
|
7
|
+
// (at your option) any later version.
|
8
|
+
//
|
9
|
+
// This program is distributed in the hope that it will be useful,
|
10
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
// GNU General Public License for more details.
|
13
|
+
//
|
14
|
+
// You should have received a copy of the GNU General Public License
|
15
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
if (!window.ABCJS)
|
18
|
+
window.ABCJS = {};
|
19
|
+
|
20
|
+
if (!window.ABCJS.midi)
|
21
|
+
window.ABCJS.midi = {};
|
22
|
+
|
23
|
+
(function() {
|
24
|
+
"use strict";
|
25
|
+
|
26
|
+
window.ABCJS.midi.Preparer = function() {
|
27
|
+
this.tempo = 0;
|
28
|
+
this.timeFactor = 0;
|
29
|
+
this.output = [];
|
30
|
+
this.currentChannel = 0;
|
31
|
+
this.currentInstrument = 0;
|
32
|
+
this.track = 0;
|
33
|
+
this.nextDuration = 0;
|
34
|
+
this.tracks = [ [] ];
|
35
|
+
};
|
36
|
+
|
37
|
+
var Preparer = window.ABCJS.midi.Preparer;
|
38
|
+
|
39
|
+
Preparer.prototype.setInstrument = function(instrument) {
|
40
|
+
// TODO-PER: for the first version, there are no channel or instrument changes.
|
41
|
+
//this.currentInstrument = instrument;
|
42
|
+
|
43
|
+
var ev = [
|
44
|
+
{
|
45
|
+
ticksToEvent: 0,
|
46
|
+
track: this.track,
|
47
|
+
event: {
|
48
|
+
channel: this.currentChannel,
|
49
|
+
deltaTime: 0,
|
50
|
+
programNumber: this.currentInstrument,
|
51
|
+
subtype: 'programChange',
|
52
|
+
type: 'channel'
|
53
|
+
}
|
54
|
+
},
|
55
|
+
this.nextDuration * this.timeFactor
|
56
|
+
];
|
57
|
+
this.tracks[this.track].push(ev);
|
58
|
+
};
|
59
|
+
|
60
|
+
Preparer.prototype.setChannel = function(channel) {
|
61
|
+
// TODO-PER: for the first version, there are no channel or instrument changes.
|
62
|
+
//this.currentChannel = channel;
|
63
|
+
};
|
64
|
+
|
65
|
+
Preparer.prototype.startTrack = function() {
|
66
|
+
this.track++;
|
67
|
+
this.tracks[this.track] = [];
|
68
|
+
this.nextDuration = 0;
|
69
|
+
};
|
70
|
+
|
71
|
+
Preparer.prototype.setGlobalInfo = function(tempo, title) {
|
72
|
+
this.tempo = tempo;
|
73
|
+
var mspb = Math.round((1.0/tempo)*60000000);
|
74
|
+
this.timeFactor = mspb / 480000;
|
75
|
+
var ev = [
|
76
|
+
{
|
77
|
+
ticksToEvent: 0,
|
78
|
+
track: this.track,
|
79
|
+
event: {
|
80
|
+
deltaTime: 0,
|
81
|
+
microsecondsPerBeat: mspb,
|
82
|
+
subtype: 'setTempo',
|
83
|
+
type: 'meta'
|
84
|
+
}
|
85
|
+
},
|
86
|
+
this.nextDuration * this.timeFactor
|
87
|
+
];
|
88
|
+
// this.tracks[this.track].push(ev);
|
89
|
+
|
90
|
+
ev = [
|
91
|
+
{
|
92
|
+
ticksToEvent: 0,
|
93
|
+
track: this.track,
|
94
|
+
event: {
|
95
|
+
deltaTime: 0,
|
96
|
+
subtype: 'trackName',
|
97
|
+
text: title,
|
98
|
+
type: 'meta'
|
99
|
+
}
|
100
|
+
},
|
101
|
+
this.nextDuration * this.timeFactor
|
102
|
+
];
|
103
|
+
// this.tracks[this.track].push(ev);
|
104
|
+
|
105
|
+
ev = [
|
106
|
+
{
|
107
|
+
ticksToEvent: 0,
|
108
|
+
track: this.track,
|
109
|
+
event: {
|
110
|
+
deltaTime: 0,
|
111
|
+
subtype: 'endOfTrack',
|
112
|
+
type: 'meta'
|
113
|
+
}
|
114
|
+
},
|
115
|
+
this.nextDuration * this.timeFactor
|
116
|
+
];
|
117
|
+
// this.tracks[this.track].push(ev);
|
118
|
+
};
|
119
|
+
|
120
|
+
Preparer.prototype.startNote = function(pitch, volume) {
|
121
|
+
var deltaTime = Math.floor(this.nextDuration / 5) * 5;
|
122
|
+
var ev = [
|
123
|
+
{
|
124
|
+
ticksToEvent: deltaTime,
|
125
|
+
track: this.track,
|
126
|
+
event: {
|
127
|
+
deltaTime: deltaTime,
|
128
|
+
channel: this.currentChannel,
|
129
|
+
type: "channel",
|
130
|
+
noteNumber: pitch,
|
131
|
+
velocity: volume,
|
132
|
+
subtype: "noteOn"
|
133
|
+
}
|
134
|
+
},
|
135
|
+
this.nextDuration * this.timeFactor
|
136
|
+
];
|
137
|
+
this.tracks[this.track].push(ev);
|
138
|
+
this.nextDuration = 0;
|
139
|
+
};
|
140
|
+
|
141
|
+
Preparer.prototype.endNote = function(pitch) {
|
142
|
+
var deltaTime = Math.floor(this.nextDuration / 5) * 5;
|
143
|
+
var ev = [
|
144
|
+
{
|
145
|
+
ticksToEvent: deltaTime,
|
146
|
+
track: this.track,
|
147
|
+
event: {
|
148
|
+
deltaTime: deltaTime,
|
149
|
+
channel: this.currentChannel,
|
150
|
+
type: "channel",
|
151
|
+
noteNumber: pitch,
|
152
|
+
velocity: 0,
|
153
|
+
subtype: "noteOff"
|
154
|
+
}
|
155
|
+
},
|
156
|
+
this.nextDuration * this.timeFactor
|
157
|
+
];
|
158
|
+
this.tracks[this.track].push(ev);
|
159
|
+
this.nextDuration = 0;
|
160
|
+
};
|
161
|
+
|
162
|
+
Preparer.prototype.addRest = function(duration) {
|
163
|
+
this.nextDuration += duration;
|
164
|
+
};
|
165
|
+
|
166
|
+
Preparer.prototype.endTrack = function() {
|
167
|
+
var ev = [
|
168
|
+
{
|
169
|
+
ticksToEvent: 0,
|
170
|
+
track: this.track,
|
171
|
+
event: {
|
172
|
+
deltaTime: 0,
|
173
|
+
type: "meta",
|
174
|
+
subtype: "endOfTrack"
|
175
|
+
}
|
176
|
+
},
|
177
|
+
0
|
178
|
+
];
|
179
|
+
// this.tracks[this.track].push(ev);
|
180
|
+
};
|
181
|
+
|
182
|
+
function addAbsoluteTime(tracks) {
|
183
|
+
for (var i = 0; i < tracks.length; i++) {
|
184
|
+
var absTime = 0;
|
185
|
+
for (var j = 0; j < tracks[i].length; j++) {
|
186
|
+
absTime += tracks[i][j][1];
|
187
|
+
tracks[i][j].absTime = absTime;
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
function combineTracks(tracks) {
|
193
|
+
var output = [];
|
194
|
+
for (var i = 0; i < tracks.length; i++) {
|
195
|
+
for (var j = 0; j < tracks[i].length; j++) {
|
196
|
+
output.push(tracks[i][j]);
|
197
|
+
}
|
198
|
+
}
|
199
|
+
return output;
|
200
|
+
}
|
201
|
+
|
202
|
+
function sortTracks(output) {
|
203
|
+
return output.sort(function(a,b) {
|
204
|
+
if (a.absTime > b.absTime) return 1;
|
205
|
+
return -1;
|
206
|
+
});
|
207
|
+
}
|
208
|
+
|
209
|
+
function adjustTime(output) {
|
210
|
+
var lastTime = 0;
|
211
|
+
for (var i = 0; i < output.length; i++) {
|
212
|
+
var thisTime = output[i].absTime;
|
213
|
+
output[i][1] = thisTime - lastTime;
|
214
|
+
lastTime = thisTime;
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
function weaveTracks(tracks) {
|
219
|
+
// Each track has a progression of delta times. To combine them, first assign an absolute time to each event,
|
220
|
+
// then make one large track of all the tracks, sort it by absolute time, then adjust the amount of time each
|
221
|
+
// event causes time to move. That is, the movement was figured out as the distance from the last note in the track,
|
222
|
+
// but now we want the distance from the last note on ANY track.
|
223
|
+
addAbsoluteTime(tracks);
|
224
|
+
var output = combineTracks(tracks);
|
225
|
+
output = sortTracks(output);
|
226
|
+
adjustTime(output);
|
227
|
+
return output;
|
228
|
+
}
|
229
|
+
|
230
|
+
Preparer.prototype.getData = function() {
|
231
|
+
return weaveTracks(this.tracks);
|
232
|
+
};
|
233
|
+
})();
|
@@ -1,5 +1,5 @@
|
|
1
1
|
// abc_midi_renderer.js: Create the actual format for the midi.
|
2
|
-
// Copyright (C) 2010,
|
2
|
+
// Copyright (C) 2010,2016 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
|
3
3
|
//
|
4
4
|
// This program is free software: you can redistribute it and/or modify
|
5
5
|
// it under the terms of the GNU General Public License as published by
|
@@ -29,215 +29,6 @@ if (!window.ABCJS.midi)
|
|
29
29
|
return elm;
|
30
30
|
}
|
31
31
|
|
32
|
-
var MIDIPlugin;
|
33
|
-
//TODO-PER: put this back in when the MIDIPlugin works again.
|
34
|
-
//window.oldunload = window.onbeforeunload;
|
35
|
-
//window.onbeforeunload = function() {
|
36
|
-
// if (window.oldunload)
|
37
|
-
// window.oldunload();
|
38
|
-
// if (typeof(MIDIPlugin) !== "undefined" && MIDIPlugin) { // PER: take care of crash in IE 8
|
39
|
-
// MIDIPlugin.closePlugin();
|
40
|
-
// }
|
41
|
-
//};
|
42
|
-
|
43
|
-
|
44
|
-
function MidiProxy(javamidi, qtmidi) {
|
45
|
-
this.javamidi = javamidi;
|
46
|
-
this.qtmidi = qtmidi;
|
47
|
-
}
|
48
|
-
|
49
|
-
MidiProxy.prototype.setTempo = function(qpm) {
|
50
|
-
this.javamidi.setTempo(qpm);
|
51
|
-
this.qtmidi.setTempo(qpm);
|
52
|
-
};
|
53
|
-
|
54
|
-
MidiProxy.prototype.startTrack = function() {
|
55
|
-
this.javamidi.startTrack();
|
56
|
-
this.qtmidi.startTrack();
|
57
|
-
};
|
58
|
-
|
59
|
-
MidiProxy.prototype.endTrack = function() {
|
60
|
-
this.javamidi.endTrack();
|
61
|
-
this.qtmidi.endTrack();
|
62
|
-
};
|
63
|
-
|
64
|
-
MidiProxy.prototype.setInstrument = function(number) {
|
65
|
-
this.javamidi.setInstrument(number);
|
66
|
-
this.qtmidi.setInstrument(number);
|
67
|
-
};
|
68
|
-
|
69
|
-
MidiProxy.prototype.startNote = function(pitch, loudness, abcelem) {
|
70
|
-
this.javamidi.startNote(pitch, loudness, abcelem);
|
71
|
-
this.qtmidi.startNote(pitch, loudness, abcelem);
|
72
|
-
};
|
73
|
-
|
74
|
-
MidiProxy.prototype.endNote = function(pitch, length) {
|
75
|
-
this.javamidi.endNote(pitch, length);
|
76
|
-
this.qtmidi.endNote(pitch, length);
|
77
|
-
};
|
78
|
-
|
79
|
-
MidiProxy.prototype.addRest = function(length) {
|
80
|
-
this.javamidi.addRest(length);
|
81
|
-
this.qtmidi.addRest(length);
|
82
|
-
};
|
83
|
-
|
84
|
-
MidiProxy.prototype.embed = function(parent) {
|
85
|
-
this.javamidi.embed(parent);
|
86
|
-
this.qtmidi.embed(parent, true);
|
87
|
-
};
|
88
|
-
|
89
|
-
function JavaMidi(midiwriter) {
|
90
|
-
this.playlist = []; // contains {time:t,funct:f} pairs
|
91
|
-
this.trackcount = 0;
|
92
|
-
this.timecount = 0;
|
93
|
-
this.tempo = 60;
|
94
|
-
this.midiapi = MIDIPlugin;
|
95
|
-
this.midiwriter = midiwriter;
|
96
|
-
this.noteOnAndChannel = "%90";
|
97
|
-
}
|
98
|
-
|
99
|
-
JavaMidi.prototype.setTempo = function(qpm) {
|
100
|
-
this.tempo = qpm;
|
101
|
-
};
|
102
|
-
|
103
|
-
JavaMidi.prototype.startTrack = function() {
|
104
|
-
this.silencelength = 0;
|
105
|
-
this.trackcount++;
|
106
|
-
this.timecount = 0;
|
107
|
-
this.playlistpos = 0;
|
108
|
-
this.first = true;
|
109
|
-
if (this.instrument) {
|
110
|
-
this.setInstrument(this.instrument);
|
111
|
-
}
|
112
|
-
if (this.channel) {
|
113
|
-
this.setChannel(this.channel);
|
114
|
-
}
|
115
|
-
};
|
116
|
-
|
117
|
-
JavaMidi.prototype.endTrack = function() {
|
118
|
-
// need to do anything?
|
119
|
-
};
|
120
|
-
|
121
|
-
JavaMidi.prototype.setInstrument = function(number) {
|
122
|
-
this.instrument = number;
|
123
|
-
this.midiapi.setInstrument(number);
|
124
|
-
//TODO push this into the playlist?
|
125
|
-
};
|
126
|
-
|
127
|
-
JavaMidi.prototype.setChannel = function(number) {
|
128
|
-
this.channel = number;
|
129
|
-
this.midiapi.setChannel(number);
|
130
|
-
};
|
131
|
-
|
132
|
-
JavaMidi.prototype.updatePos = function() {
|
133
|
-
while (this.playlist[this.playlistpos] &&
|
134
|
-
this.playlist[this.playlistpos].time < this.timecount) {
|
135
|
-
this.playlistpos++;
|
136
|
-
}
|
137
|
-
};
|
138
|
-
|
139
|
-
JavaMidi.prototype.startNote = function(pitch, loudness, abcelem) {
|
140
|
-
this.timecount += this.silencelength;
|
141
|
-
this.silencelength = 0;
|
142
|
-
if (this.first) {
|
143
|
-
//nothing special if first?
|
144
|
-
}
|
145
|
-
this.updatePos();
|
146
|
-
var self = this;
|
147
|
-
this.playlist.splice(this.playlistpos, 0, {
|
148
|
-
time: this.timecount,
|
149
|
-
funct: function() {
|
150
|
-
self.midiapi.playNote(pitch);
|
151
|
-
self.midiwriter.notifySelect(abcelem);
|
152
|
-
}
|
153
|
-
});
|
154
|
-
};
|
155
|
-
|
156
|
-
JavaMidi.prototype.endNote = function(pitch, length) {
|
157
|
-
this.timecount += length;
|
158
|
-
this.updatePos();
|
159
|
-
var self = this;
|
160
|
-
this.playlist.splice(this.playlistpos, 0, {
|
161
|
-
time: this.timecount,
|
162
|
-
funct: function() {
|
163
|
-
self.midiapi.stopNote(pitch);
|
164
|
-
}
|
165
|
-
});
|
166
|
-
};
|
167
|
-
|
168
|
-
JavaMidi.prototype.addRest = function(length) {
|
169
|
-
this.silencelength += length;
|
170
|
-
};
|
171
|
-
|
172
|
-
JavaMidi.prototype.embed = function(parent) {
|
173
|
-
|
174
|
-
|
175
|
-
this.playlink = setAttributes(document.createElement('a'), {
|
176
|
-
style: "border:1px solid black; margin:3px;"
|
177
|
-
});
|
178
|
-
this.playlink.innerHTML = "play";
|
179
|
-
var self = this;
|
180
|
-
this.playlink.onmousedown = function() {
|
181
|
-
if (self.playing) {
|
182
|
-
this.innerHTML = "play";
|
183
|
-
self.pausePlay();
|
184
|
-
} else {
|
185
|
-
this.innerHTML = "pause";
|
186
|
-
self.startPlay();
|
187
|
-
}
|
188
|
-
};
|
189
|
-
parent.appendChild(this.playlink);
|
190
|
-
|
191
|
-
var stoplink = setAttributes(document.createElement('a'), {
|
192
|
-
style: "border:1px solid black; margin:3px;"
|
193
|
-
});
|
194
|
-
stoplink.innerHTML = "stop";
|
195
|
-
//var self = this;
|
196
|
-
stoplink.onmousedown = function() {
|
197
|
-
self.stopPlay();
|
198
|
-
};
|
199
|
-
parent.appendChild(stoplink);
|
200
|
-
this.i = 0;
|
201
|
-
this.currenttime = 0;
|
202
|
-
this.playing = false;
|
203
|
-
};
|
204
|
-
|
205
|
-
JavaMidi.prototype.stopPlay = function() {
|
206
|
-
this.i = 0;
|
207
|
-
this.currenttime = 0;
|
208
|
-
this.pausePlay();
|
209
|
-
this.playlink.innerHTML = "play";
|
210
|
-
};
|
211
|
-
|
212
|
-
JavaMidi.prototype.startPlay = function() {
|
213
|
-
this.playing = true;
|
214
|
-
var self = this;
|
215
|
-
// repeat every 16th note TODO see the min in the piece
|
216
|
-
this.ticksperinterval = 480 / 4;
|
217
|
-
this.doPlay();
|
218
|
-
this.playinterval = window.setInterval(function() {self.doPlay(); },
|
219
|
-
(60000 / (this.tempo * 4)));
|
220
|
-
};
|
221
|
-
|
222
|
-
JavaMidi.prototype.pausePlay = function() {
|
223
|
-
this.playing = false;
|
224
|
-
window.clearInterval(this.playinterval);
|
225
|
-
this.midiapi.stopAllNotes();
|
226
|
-
};
|
227
|
-
|
228
|
-
JavaMidi.prototype.doPlay = function() {
|
229
|
-
while (this.playlist[this.i] &&
|
230
|
-
this.playlist[this.i].time <= this.currenttime) {
|
231
|
-
this.playlist[this.i].funct();
|
232
|
-
this.i++;
|
233
|
-
}
|
234
|
-
if (this.playlist[this.i]) {
|
235
|
-
this.currenttime += this.ticksperinterval;
|
236
|
-
} else {
|
237
|
-
this.stopPlay();
|
238
|
-
}
|
239
|
-
};
|
240
|
-
|
241
32
|
function Midi() {
|
242
33
|
this.trackstrings = "";
|
243
34
|
this.trackcount = 0;
|
@@ -403,24 +194,7 @@ if (!window.ABCJS.midi)
|
|
403
194
|
return toHex(res, padding);
|
404
195
|
}
|
405
196
|
|
406
|
-
window.ABCJS.midi.rendererFactory = function(
|
407
|
-
return
|
197
|
+
window.ABCJS.midi.rendererFactory = function() {
|
198
|
+
return new Midi();
|
408
199
|
};
|
409
|
-
|
410
|
-
window.ABCJS.midi.initializeJava = function() {
|
411
|
-
MIDIPlugin = document.MIDIPlugin;
|
412
|
-
setTimeout(function() { // run on next event loop (once MIDIPlugin is loaded)
|
413
|
-
try { // activate MIDIPlugin
|
414
|
-
MIDIPlugin.openPlugin();
|
415
|
-
|
416
|
-
} catch(e) { // plugin not supported (download externals)
|
417
|
-
var a = document.createElement("a");
|
418
|
-
a.href = "http://java.sun.com/products/java-media/sound/soundbanks.html";
|
419
|
-
a.target = "_blank";
|
420
|
-
a.appendChild(document.createTextNode("Download Soundbank"));
|
421
|
-
parent.appendChild(a);
|
422
|
-
}
|
423
|
-
}, 0);
|
424
|
-
};
|
425
|
-
|
426
200
|
})();
|