abcjs-rails 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/app/assets/javascripts/abcjs-rails.js +1 -0
- data/app/assets/javascripts/abcjs/api/abc_tunebook.js +158 -0
- data/app/assets/javascripts/abcjs/data/abc_tune.js +686 -0
- data/app/assets/javascripts/abcjs/edit/abc_editor.js +414 -0
- data/app/assets/javascripts/abcjs/midi/abc_midiwriter.js +698 -0
- data/app/assets/javascripts/abcjs/parse/abc_common.js +76 -0
- data/app/assets/javascripts/abcjs/parse/abc_parse.js +1385 -0
- data/app/assets/javascripts/abcjs/parse/abc_parse_directive.js +546 -0
- data/app/assets/javascripts/abcjs/parse/abc_parse_header.js +521 -0
- data/app/assets/javascripts/abcjs/parse/abc_parse_key_voice.js +781 -0
- data/app/assets/javascripts/abcjs/parse/abc_tokenizer.js +751 -0
- data/app/assets/javascripts/abcjs/write/abc_glyphs.js +105 -0
- data/app/assets/javascripts/abcjs/write/abc_graphelements.js +781 -0
- data/app/assets/javascripts/abcjs/write/abc_layout.js +959 -0
- data/app/assets/javascripts/abcjs/write/abc_write.js +487 -0
- data/app/assets/javascripts/abcjs/write/raphael.js +3395 -0
- data/app/assets/javascripts/abcjs/write/sprintf.js +61 -0
- data/lib/abcjs-rails/version.rb +1 -1
- metadata +18 -1
@@ -0,0 +1,414 @@
|
|
1
|
+
// abc_editor.js
|
2
|
+
// window.ABCJS.Editor is the interface class for the area that contains the ABC text. It is responsible for
|
3
|
+
// holding the text of the tune and calling the parser and the rendering engines.
|
4
|
+
//
|
5
|
+
// EditArea is an example of using a textarea as the control that is shown to the user. As long as
|
6
|
+
// the same interface is used, window.ABCJS.Editor can use a different type of object.
|
7
|
+
//
|
8
|
+
// EditArea:
|
9
|
+
// - constructor(textareaid)
|
10
|
+
// This contains the id of a textarea control that will be used.
|
11
|
+
// - addSelectionListener(listener)
|
12
|
+
// A callback class that contains the entry point fireSelectionChanged()
|
13
|
+
// - addChangeListener(listener)
|
14
|
+
// A callback class that contains the entry point fireChanged()
|
15
|
+
// - getSelection()
|
16
|
+
// returns the object { start: , end: } with the current selection in characters
|
17
|
+
// - setSelection(start, end)
|
18
|
+
// start and end are the character positions that should be selected.
|
19
|
+
// - getString()
|
20
|
+
// returns the ABC text that is currently displayed.
|
21
|
+
// - setString(str)
|
22
|
+
// sets the ABC text that is currently displayed, and resets the initialText variable
|
23
|
+
// - getElem()
|
24
|
+
// returns the textarea element
|
25
|
+
// - string initialText
|
26
|
+
// Contains the starting text. This can be compared against the current text to see if anything changed.
|
27
|
+
//
|
28
|
+
|
29
|
+
/*global document, window, clearTimeout, setTimeout */
|
30
|
+
/*global Raphael */
|
31
|
+
|
32
|
+
if (!window.ABCJS)
|
33
|
+
window.ABCJS = {};
|
34
|
+
|
35
|
+
if (!window.ABCJS.edit)
|
36
|
+
window.ABCJS.edit = {};
|
37
|
+
|
38
|
+
window.ABCJS.edit.EditArea = function(textareaid) {
|
39
|
+
this.textarea = document.getElementById(textareaid);
|
40
|
+
this.initialText = this.textarea.value;
|
41
|
+
this.isDragging = false;
|
42
|
+
}
|
43
|
+
|
44
|
+
window.ABCJS.edit.EditArea.prototype.addSelectionListener = function(listener) {
|
45
|
+
this.textarea.onmousemove = function(ev) {
|
46
|
+
if (this.isDragging)
|
47
|
+
listener.fireSelectionChanged();
|
48
|
+
};
|
49
|
+
};
|
50
|
+
|
51
|
+
window.ABCJS.edit.EditArea.prototype.addChangeListener = function(listener) {
|
52
|
+
this.changelistener = listener;
|
53
|
+
this.textarea.onkeyup = function() {
|
54
|
+
listener.fireChanged();
|
55
|
+
};
|
56
|
+
this.textarea.onmousedown = function() {
|
57
|
+
this.isDragging = true;
|
58
|
+
listener.fireSelectionChanged();
|
59
|
+
};
|
60
|
+
this.textarea.onmouseup = function() {
|
61
|
+
this.isDragging = false;
|
62
|
+
listener.fireChanged();
|
63
|
+
};
|
64
|
+
this.textarea.onchange = function() {
|
65
|
+
listener.fireChanged();
|
66
|
+
};
|
67
|
+
};
|
68
|
+
|
69
|
+
//TODO won't work under IE?
|
70
|
+
window.ABCJS.edit.EditArea.prototype.getSelection = function() {
|
71
|
+
return {start: this.textarea.selectionStart, end: this.textarea.selectionEnd};
|
72
|
+
};
|
73
|
+
|
74
|
+
window.ABCJS.edit.EditArea.prototype.setSelection = function(start, end) {
|
75
|
+
if(this.textarea.setSelectionRange)
|
76
|
+
this.textarea.setSelectionRange(start, end);
|
77
|
+
else if(this.textarea.createTextRange) {
|
78
|
+
// For IE8
|
79
|
+
var e = this.textarea.createTextRange();
|
80
|
+
e.collapse(true);
|
81
|
+
e.moveEnd('character', end);
|
82
|
+
e.moveStart('character', start);
|
83
|
+
e.select();
|
84
|
+
}
|
85
|
+
this.textarea.focus();
|
86
|
+
};
|
87
|
+
|
88
|
+
window.ABCJS.edit.EditArea.prototype.getString = function() {
|
89
|
+
return this.textarea.value;
|
90
|
+
};
|
91
|
+
|
92
|
+
window.ABCJS.edit.EditArea.prototype.setString = function(str) {
|
93
|
+
this.textarea.value = str;
|
94
|
+
this.initialText = this.getString();
|
95
|
+
if (this.changelistener) {
|
96
|
+
this.changelistener.fireChanged();
|
97
|
+
}
|
98
|
+
};
|
99
|
+
|
100
|
+
window.ABCJS.edit.EditArea.prototype.getElem = function() {
|
101
|
+
return this.textarea;
|
102
|
+
};
|
103
|
+
|
104
|
+
//
|
105
|
+
// window.ABCJS.Editor:
|
106
|
+
//
|
107
|
+
// constructor(editarea, params)
|
108
|
+
// if editarea is a string, then it is an HTML id of a textarea control.
|
109
|
+
// Otherwise, it should be an instantiation of an object that expresses the EditArea interface.
|
110
|
+
//
|
111
|
+
// params is a hash of:
|
112
|
+
// canvas_id: or paper_id: HTML id to draw in. If not present, then the drawing happens just below the editor.
|
113
|
+
// generate_midi: if present, then midi is generated.
|
114
|
+
// midi_id: if present, the HTML id to place the midi control. Otherwise it is placed in the same div as the paper.
|
115
|
+
// generate_warnings: if present, then parser warnings are displayed on the page.
|
116
|
+
// warnings_id: if present, the HTML id to place the warnings. Otherwise they are placed in the same div as the paper.
|
117
|
+
// onchange: if present, the callback function to call whenever there has been a change.
|
118
|
+
// gui: if present, the paper can send changes back to the editor (presumably because the user changed something directly.)
|
119
|
+
// parser_options: options to send to the parser engine.
|
120
|
+
// midi_options: options to send to the midi engine.
|
121
|
+
// render_options: options to send to the render engine.
|
122
|
+
// indicate_changed: the dirty flag is set if this is true.
|
123
|
+
//
|
124
|
+
// - setReadOnly(bool)
|
125
|
+
// adds or removes the class abc_textarea_readonly, and adds or removes the attribute readonly=yes
|
126
|
+
// - setDirtyStyle(bool)
|
127
|
+
// adds or removes the class abc_textarea_dirty
|
128
|
+
// - renderTune(abc, parserparams, div)
|
129
|
+
// Immediately renders the tune. (Useful for creating the SVG output behind the scenes, if div is hidden)
|
130
|
+
// string abc: the ABC text
|
131
|
+
// parserparams: params to send to the parser
|
132
|
+
// div: the HTML id to render to.
|
133
|
+
// - modelChanged()
|
134
|
+
// Called when the model has been changed to trigger re-rendering
|
135
|
+
// - parseABC()
|
136
|
+
// Called internally by fireChanged()
|
137
|
+
// returns true if there has been a change since last call.
|
138
|
+
// - updateSelection()
|
139
|
+
// Called when the user has changed the selection. This calls the printer to show the selection.
|
140
|
+
// - fireSelectionChanged()
|
141
|
+
// Called by the textarea object when the user has changed the selection.
|
142
|
+
// - paramChanged(printerparams)
|
143
|
+
// Called to signal that the printer params have changed, so re-rendering should occur.
|
144
|
+
// - fireChanged()
|
145
|
+
// Called by the textarea object when the user has changed something.
|
146
|
+
// - setNotDirty()
|
147
|
+
// Called by the client app to reset the dirty flag
|
148
|
+
// - isDirty()
|
149
|
+
// Returns true or false, whether the textarea contains the same text that it started with.
|
150
|
+
// - highlight(abcelem)
|
151
|
+
// Called by the printer to highlight an area.
|
152
|
+
// - pause(bool)
|
153
|
+
// Stops the automatic rendering when the user is typing.
|
154
|
+
//
|
155
|
+
|
156
|
+
window.ABCJS.Editor = function(editarea, params) {
|
157
|
+
if (params.indicate_changed)
|
158
|
+
this.indicate_changed = true;
|
159
|
+
if (typeof editarea === "string") {
|
160
|
+
this.editarea = new window.ABCJS.edit.EditArea(editarea);
|
161
|
+
} else {
|
162
|
+
this.editarea = editarea;
|
163
|
+
}
|
164
|
+
this.editarea.addSelectionListener(this);
|
165
|
+
this.editarea.addChangeListener(this);
|
166
|
+
|
167
|
+
if (params.canvas_id) {
|
168
|
+
this.div = document.getElementById(params.canvas_id);
|
169
|
+
} else if (params.paper_id) {
|
170
|
+
this.div = document.getElementById(params.paper_id);
|
171
|
+
} else {
|
172
|
+
this.div = document.createElement("DIV");
|
173
|
+
this.editarea.getElem().parentNode.insertBefore(this.div, this.editarea.getElem());
|
174
|
+
}
|
175
|
+
|
176
|
+
if (params.generate_midi || params.midi_id) {
|
177
|
+
if (params.midi_id) {
|
178
|
+
this.mididiv = document.getElementById(params.midi_id);
|
179
|
+
} else {
|
180
|
+
this.mididiv = this.div;
|
181
|
+
}
|
182
|
+
}
|
183
|
+
|
184
|
+
if (params.generate_warnings || params.warnings_id) {
|
185
|
+
if (params.warnings_id) {
|
186
|
+
this.warningsdiv = document.getElementById(params.warnings_id);
|
187
|
+
} else {
|
188
|
+
this.warningsdiv = this.div;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
this.parserparams = params.parser_options || {};
|
193
|
+
this.midiparams = params.midi_options || {};
|
194
|
+
this.onchangeCallback = params.onchange;
|
195
|
+
|
196
|
+
this.printerparams = params.render_options || {};
|
197
|
+
|
198
|
+
if (params.gui) {
|
199
|
+
this.target = document.getElementById(editarea);
|
200
|
+
this.printerparams.editable = true;
|
201
|
+
}
|
202
|
+
this.oldt = "";
|
203
|
+
this.bReentry = false;
|
204
|
+
this.parseABC();
|
205
|
+
this.modelChanged();
|
206
|
+
|
207
|
+
this.addClassName = function(element, className) {
|
208
|
+
var hasClassName = function(element, className) {
|
209
|
+
var elementClassName = element.className;
|
210
|
+
return (elementClassName.length > 0 && (elementClassName === className ||
|
211
|
+
new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
|
212
|
+
};
|
213
|
+
|
214
|
+
if (!hasClassName(element, className))
|
215
|
+
element.className += (element.className ? ' ' : '') + className;
|
216
|
+
return element;
|
217
|
+
};
|
218
|
+
|
219
|
+
this.removeClassName = function(element, className) {
|
220
|
+
element.className = window.ABCJS.parse.strip(element.className.replace(
|
221
|
+
new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' '));
|
222
|
+
return element;
|
223
|
+
};
|
224
|
+
|
225
|
+
this.setReadOnly = function(readOnly) {
|
226
|
+
var readonlyClass = 'abc_textarea_readonly';
|
227
|
+
var el = this.editarea.getElem();
|
228
|
+
if (readOnly) {
|
229
|
+
el.setAttribute('readonly', 'yes');
|
230
|
+
this.addClassName(el, readonlyClass);
|
231
|
+
} else {
|
232
|
+
el.removeAttribute('readonly');
|
233
|
+
this.removeClassName(el, readonlyClass);
|
234
|
+
}
|
235
|
+
};
|
236
|
+
}
|
237
|
+
|
238
|
+
window.ABCJS.Editor.prototype.renderTune = function(abc, params, div) {
|
239
|
+
var tunebook = new ABCJS.TuneBook(abc);
|
240
|
+
var abcParser = window.ABCJS.parse.Parse();
|
241
|
+
abcParser.parse(tunebook.tunes[0].abc, params); //TODO handle multiple tunes
|
242
|
+
var tune = abcParser.getTune();
|
243
|
+
var paper = Raphael(div, 800, 400);
|
244
|
+
var printer = new ABCJS.write.Printer(paper, {}); // TODO: handle printer params
|
245
|
+
printer.printABC(tune);
|
246
|
+
};
|
247
|
+
|
248
|
+
window.ABCJS.Editor.prototype.modelChanged = function() {
|
249
|
+
if (this.tunes === undefined) {
|
250
|
+
if (this.mididiv !== undefined && this.mididiv !== this.div)
|
251
|
+
this.mididiv.innerHTML = "";
|
252
|
+
this.div.innerHTML = "";
|
253
|
+
return;
|
254
|
+
}
|
255
|
+
|
256
|
+
if (this.bReentry)
|
257
|
+
return; // TODO is this likely? maybe, if we rewrite abc immediately w/ abc2abc
|
258
|
+
this.bReentry = true;
|
259
|
+
this.timerId = null;
|
260
|
+
this.div.innerHTML = "";
|
261
|
+
var paper = Raphael(this.div, 800, 400);
|
262
|
+
this.printer = new ABCJS.write.Printer(paper, this.printerparams);
|
263
|
+
this.printer.printABC(this.tunes);
|
264
|
+
if (ABCJS.midi.MidiWriter && this.mididiv) {
|
265
|
+
if (this.mididiv !== this.div)
|
266
|
+
this.mididiv.innerHTML = "";
|
267
|
+
var midiwriter = new ABCJS.midi.MidiWriter(this.mididiv,this.midiparams);
|
268
|
+
midiwriter.addListener(this.printer);
|
269
|
+
midiwriter.writeABC(this.tunes[0]); //TODO handle multiple tunes
|
270
|
+
}
|
271
|
+
if (this.warningsdiv) {
|
272
|
+
this.warningsdiv.innerHTML = (this.warnings) ? this.warnings.join("<br />") : "No errors";
|
273
|
+
}
|
274
|
+
if (this.target) {
|
275
|
+
var textprinter = new window.ABCJS.transform.TextPrinter(this.target, true);
|
276
|
+
textprinter.printABC(this.tunes[0]); //TODO handle multiple tunes
|
277
|
+
}
|
278
|
+
this.printer.addSelectListener(this);
|
279
|
+
this.updateSelection();
|
280
|
+
this.bReentry = false;
|
281
|
+
};
|
282
|
+
|
283
|
+
// Call this to reparse in response to the printing parameters changing
|
284
|
+
window.ABCJS.Editor.prototype.paramChanged = function(printerparams) {
|
285
|
+
this.printerparams = printerparams;
|
286
|
+
this.oldt = "";
|
287
|
+
this.fireChanged();
|
288
|
+
};
|
289
|
+
|
290
|
+
// return true if the model has changed
|
291
|
+
window.ABCJS.Editor.prototype.parseABC = function() {
|
292
|
+
var t = this.editarea.getString();
|
293
|
+
if (t===this.oldt) {
|
294
|
+
this.updateSelection();
|
295
|
+
return false;
|
296
|
+
}
|
297
|
+
|
298
|
+
this.oldt = t;
|
299
|
+
if (t === "") {
|
300
|
+
this.tunes = undefined;
|
301
|
+
this.warnings = "";
|
302
|
+
return true;
|
303
|
+
}
|
304
|
+
var tunebook = new ABCJS.TuneBook(t);
|
305
|
+
|
306
|
+
this.tunes = [];
|
307
|
+
this.warnings = [];
|
308
|
+
for (var i=0; i<tunebook.tunes.length; i++) {
|
309
|
+
var abcParser = new window.ABCJS.parse.Parse();
|
310
|
+
abcParser.parse(tunebook.tunes[i].abc, this.parserparams); //TODO handle multiple tunes
|
311
|
+
this.tunes[i] = abcParser.getTune();
|
312
|
+
var warnings = abcParser.getWarnings() || [];
|
313
|
+
for (var j=0; j<warnings.length; j++) {
|
314
|
+
this.warnings.push(warnings[j]);
|
315
|
+
}
|
316
|
+
}
|
317
|
+
return true;
|
318
|
+
};
|
319
|
+
|
320
|
+
window.ABCJS.Editor.prototype.updateSelection = function() {
|
321
|
+
var selection = this.editarea.getSelection();
|
322
|
+
try {
|
323
|
+
this.printer.rangeHighlight(selection.start, selection.end);
|
324
|
+
} catch (e) {} // maybe printer isn't defined yet?
|
325
|
+
};
|
326
|
+
|
327
|
+
window.ABCJS.Editor.prototype.fireSelectionChanged = function() {
|
328
|
+
this.updateSelection();
|
329
|
+
};
|
330
|
+
|
331
|
+
window.ABCJS.Editor.prototype.setDirtyStyle = function(isDirty) {
|
332
|
+
if (this.indicate_changed === undefined)
|
333
|
+
return;
|
334
|
+
var addClassName = function(element, className) {
|
335
|
+
var hasClassName = function(element, className) {
|
336
|
+
var elementClassName = element.className;
|
337
|
+
return (elementClassName.length > 0 && (elementClassName === className ||
|
338
|
+
new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
|
339
|
+
};
|
340
|
+
|
341
|
+
if (!hasClassName(element, className))
|
342
|
+
element.className += (element.className ? ' ' : '') + className;
|
343
|
+
return element;
|
344
|
+
};
|
345
|
+
|
346
|
+
var removeClassName = function(element, className) {
|
347
|
+
element.className = window.ABCJS.parse.strip(element.className.replace(
|
348
|
+
new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' '));
|
349
|
+
return element;
|
350
|
+
};
|
351
|
+
|
352
|
+
var readonlyClass = 'abc_textarea_dirty';
|
353
|
+
var el = this.editarea.getElem();
|
354
|
+
if (isDirty) {
|
355
|
+
addClassName(el, readonlyClass);
|
356
|
+
} else {
|
357
|
+
removeClassName(el, readonlyClass);
|
358
|
+
}
|
359
|
+
};
|
360
|
+
|
361
|
+
// call when abc text is changed and needs re-parsing
|
362
|
+
window.ABCJS.Editor.prototype.fireChanged = function() {
|
363
|
+
if (this.bIsPaused)
|
364
|
+
return;
|
365
|
+
if (this.parseABC()) {
|
366
|
+
var self = this;
|
367
|
+
if (this.timerId) // If the user is still typing, cancel the update
|
368
|
+
clearTimeout(this.timerId);
|
369
|
+
this.timerId = setTimeout(function () {
|
370
|
+
self.modelChanged();
|
371
|
+
}, 300); // Is this a good comprimise between responsiveness and not redrawing too much?
|
372
|
+
var isDirty = this.isDirty();
|
373
|
+
if (this.wasDirty !== isDirty) {
|
374
|
+
this.wasDirty = isDirty;
|
375
|
+
this.setDirtyStyle(isDirty);
|
376
|
+
}
|
377
|
+
if (this.onchangeCallback)
|
378
|
+
this.onchangeCallback(this);
|
379
|
+
}
|
380
|
+
};
|
381
|
+
|
382
|
+
window.ABCJS.Editor.prototype.setNotDirty = function() {
|
383
|
+
this.editarea.initialText = this.editarea.getString();
|
384
|
+
this.wasDirty = false;
|
385
|
+
this.setDirtyStyle(false);
|
386
|
+
};
|
387
|
+
|
388
|
+
window.ABCJS.Editor.prototype.isDirty = function() {
|
389
|
+
if (this.indicate_changed === undefined)
|
390
|
+
return false;
|
391
|
+
return this.editarea.initialText !== this.editarea.getString();
|
392
|
+
};
|
393
|
+
|
394
|
+
window.ABCJS.Editor.prototype.highlight = function(abcelem) {
|
395
|
+
this.editarea.setSelection(abcelem.startChar, abcelem.endChar);
|
396
|
+
};
|
397
|
+
|
398
|
+
window.ABCJS.Editor.prototype.pause = function(shouldPause) {
|
399
|
+
this.bIsPaused = shouldPause;
|
400
|
+
if (!shouldPause)
|
401
|
+
this.updateRendering();
|
402
|
+
};
|
403
|
+
|
404
|
+
window.ABCJS.Editor.prototype.pauseMidi = function(shouldPause) {
|
405
|
+
if (shouldPause && this.mididiv) {
|
406
|
+
this.mididivSave = this.mididiv;
|
407
|
+
this.addClassName(this.mididiv, 'hidden');
|
408
|
+
this.mididiv = null;
|
409
|
+
} else if (!shouldPause && this.mididivSave) {
|
410
|
+
this.mididiv = this.mididivSave;
|
411
|
+
this.removeClassName(this.mididiv, 'hidden');
|
412
|
+
this.mididivSave = null;
|
413
|
+
}
|
414
|
+
};
|
@@ -0,0 +1,698 @@
|
|
1
|
+
/*global window, document, setTimeout */
|
2
|
+
|
3
|
+
if (!window.ABCJS)
|
4
|
+
window.ABCJS = {};
|
5
|
+
|
6
|
+
if (!window.ABCJS.midi)
|
7
|
+
window.ABCJS.midi = {};
|
8
|
+
|
9
|
+
(function() {
|
10
|
+
function setAttributes(elm, attrs){
|
11
|
+
for(var attr in attrs)
|
12
|
+
if (attrs.hasOwnProperty(attr))
|
13
|
+
elm.setAttribute(attr, attrs[attr]);
|
14
|
+
return elm;
|
15
|
+
}
|
16
|
+
|
17
|
+
//TODO-PER: put this back in when the MIDIPlugin works again.
|
18
|
+
//window.oldunload = window.onbeforeunload;
|
19
|
+
//window.onbeforeunload = function() {
|
20
|
+
// if (window.oldunload)
|
21
|
+
// window.oldunload();
|
22
|
+
// if (typeof(MIDIPlugin) !== "undefined" && MIDIPlugin) { // PER: take care of crash in IE 8
|
23
|
+
// MIDIPlugin.closePlugin();
|
24
|
+
// }
|
25
|
+
//};
|
26
|
+
|
27
|
+
|
28
|
+
function MidiProxy(javamidi,qtmidi) {
|
29
|
+
this.javamidi = javamidi;
|
30
|
+
this.qtmidi = qtmidi;
|
31
|
+
}
|
32
|
+
|
33
|
+
MidiProxy.prototype.setTempo = function (qpm) {
|
34
|
+
this.javamidi.setTempo(qpm);
|
35
|
+
this.qtmidi.setTempo(qpm);
|
36
|
+
};
|
37
|
+
|
38
|
+
MidiProxy.prototype.startTrack = function () {
|
39
|
+
this.javamidi.startTrack();
|
40
|
+
this.qtmidi.startTrack();
|
41
|
+
};
|
42
|
+
|
43
|
+
MidiProxy.prototype.endTrack = function () {
|
44
|
+
this.javamidi.endTrack();
|
45
|
+
this.qtmidi.endTrack();
|
46
|
+
};
|
47
|
+
|
48
|
+
MidiProxy.prototype.setInstrument = function (number) {
|
49
|
+
this.javamidi.setInstrument(number);
|
50
|
+
this.qtmidi.setInstrument(number);
|
51
|
+
};
|
52
|
+
|
53
|
+
MidiProxy.prototype.startNote = function (pitch, loudness, abcelem) {
|
54
|
+
this.javamidi.startNote(pitch, loudness, abcelem);
|
55
|
+
this.qtmidi.startNote(pitch, loudness, abcelem);
|
56
|
+
};
|
57
|
+
|
58
|
+
MidiProxy.prototype.endNote = function (pitch, length) {
|
59
|
+
this.javamidi.endNote(pitch, length);
|
60
|
+
this.qtmidi.endNote(pitch, length);
|
61
|
+
};
|
62
|
+
|
63
|
+
MidiProxy.prototype.addRest = function (length) {
|
64
|
+
this.javamidi.addRest(length);
|
65
|
+
this.qtmidi.addRest(length);
|
66
|
+
};
|
67
|
+
|
68
|
+
MidiProxy.prototype.embed = function(parent) {
|
69
|
+
this.javamidi.embed(parent);
|
70
|
+
this.qtmidi.embed(parent,true);
|
71
|
+
};
|
72
|
+
|
73
|
+
function JavaMidi(midiwriter) {
|
74
|
+
this.playlist = []; // contains {time:t,funct:f} pairs
|
75
|
+
this.trackcount = 0;
|
76
|
+
this.timecount = 0;
|
77
|
+
this.tempo = 60;
|
78
|
+
this.midiapi = MIDIPlugin;
|
79
|
+
this.midiwriter = midiwriter;
|
80
|
+
this.noteOnAndChannel = "%90";
|
81
|
+
}
|
82
|
+
|
83
|
+
JavaMidi.prototype.setTempo = function (qpm) {
|
84
|
+
this.tempo = qpm;
|
85
|
+
};
|
86
|
+
|
87
|
+
JavaMidi.prototype.startTrack = function () {
|
88
|
+
this.silencelength = 0;
|
89
|
+
this.trackcount++;
|
90
|
+
this.timecount=0;
|
91
|
+
this.playlistpos=0;
|
92
|
+
this.first=true;
|
93
|
+
if (this.instrument) {
|
94
|
+
this.setInstrument(this.instrument);
|
95
|
+
}
|
96
|
+
if (this.channel) {
|
97
|
+
this.setChannel(this.channel);
|
98
|
+
}
|
99
|
+
};
|
100
|
+
|
101
|
+
JavaMidi.prototype.endTrack = function () {
|
102
|
+
// need to do anything?
|
103
|
+
};
|
104
|
+
|
105
|
+
JavaMidi.prototype.setInstrument = function (number) {
|
106
|
+
this.instrument=number;
|
107
|
+
this.midiapi.setInstrument(number);
|
108
|
+
//TODO push this into the playlist?
|
109
|
+
};
|
110
|
+
|
111
|
+
JavaMidi.prototype.setChannel = function (number) {
|
112
|
+
this.channel=number;
|
113
|
+
this.midiapi.setChannel(number);
|
114
|
+
};
|
115
|
+
|
116
|
+
JavaMidi.prototype.updatePos = function() {
|
117
|
+
while(this.playlist[this.playlistpos] &&
|
118
|
+
this.playlist[this.playlistpos].time<this.timecount) {
|
119
|
+
this.playlistpos++;
|
120
|
+
}
|
121
|
+
};
|
122
|
+
|
123
|
+
JavaMidi.prototype.startNote = function (pitch, loudness, abcelem) {
|
124
|
+
this.timecount+=this.silencelength;
|
125
|
+
this.silencelength = 0;
|
126
|
+
if (this.first) {
|
127
|
+
//nothing special if first?
|
128
|
+
}
|
129
|
+
this.updatePos();
|
130
|
+
var self=this;
|
131
|
+
this.playlist.splice(this.playlistpos,0, {
|
132
|
+
time:this.timecount,
|
133
|
+
funct:function() {
|
134
|
+
self.midiapi.playNote(pitch);
|
135
|
+
self.midiwriter.notifySelect(abcelem);
|
136
|
+
}
|
137
|
+
});
|
138
|
+
};
|
139
|
+
|
140
|
+
JavaMidi.prototype.endNote = function (pitch, length) {
|
141
|
+
this.timecount+=length;
|
142
|
+
this.updatePos();
|
143
|
+
var self=this;
|
144
|
+
this.playlist.splice(this.playlistpos, 0, {
|
145
|
+
time:this.timecount,
|
146
|
+
funct: function() {
|
147
|
+
self.midiapi.stopNote(pitch);
|
148
|
+
}
|
149
|
+
});
|
150
|
+
};
|
151
|
+
|
152
|
+
JavaMidi.prototype.addRest = function (length) {
|
153
|
+
this.silencelength += length;
|
154
|
+
};
|
155
|
+
|
156
|
+
JavaMidi.prototype.embed = function(parent) {
|
157
|
+
|
158
|
+
|
159
|
+
this.playlink = setAttributes(document.createElement('a'), {
|
160
|
+
style: "border:1px solid black; margin:3px;"
|
161
|
+
});
|
162
|
+
this.playlink.innerHTML = "play";
|
163
|
+
var self = this;
|
164
|
+
this.playlink.onmousedown = function() {
|
165
|
+
if (self.playing) {
|
166
|
+
this.innerHTML = "play";
|
167
|
+
self.pausePlay();
|
168
|
+
} else {
|
169
|
+
this.innerHTML = "pause";
|
170
|
+
self.startPlay();
|
171
|
+
}
|
172
|
+
};
|
173
|
+
parent.appendChild(this.playlink);
|
174
|
+
|
175
|
+
var stoplink = setAttributes(document.createElement('a'), {
|
176
|
+
style: "border:1px solid black; margin:3px;"
|
177
|
+
});
|
178
|
+
stoplink.innerHTML = "stop";
|
179
|
+
//var self = this;
|
180
|
+
stoplink.onmousedown = function() {
|
181
|
+
self.stopPlay();
|
182
|
+
};
|
183
|
+
parent.appendChild(stoplink);
|
184
|
+
this.i=0;
|
185
|
+
this.currenttime=0;
|
186
|
+
this.playing = false;
|
187
|
+
};
|
188
|
+
|
189
|
+
JavaMidi.prototype.stopPlay = function() {
|
190
|
+
this.i=0;
|
191
|
+
this.currenttime=0;
|
192
|
+
this.pausePlay();
|
193
|
+
this.playlink.innerHTML = "play";
|
194
|
+
};
|
195
|
+
|
196
|
+
JavaMidi.prototype.startPlay = function() {
|
197
|
+
this.playing = true;
|
198
|
+
var self = this;
|
199
|
+
// repeat every 16th note TODO see the min in the piece
|
200
|
+
this.ticksperinterval = 480/4;
|
201
|
+
this.doPlay();
|
202
|
+
this.playinterval = window.setInterval(function() {self.doPlay(); },
|
203
|
+
(60000/(this.tempo*4)));
|
204
|
+
};
|
205
|
+
|
206
|
+
JavaMidi.prototype.pausePlay = function() {
|
207
|
+
this.playing = false;
|
208
|
+
window.clearInterval(this.playinterval);
|
209
|
+
this.midiapi.stopAllNotes();
|
210
|
+
};
|
211
|
+
|
212
|
+
JavaMidi.prototype.doPlay = function() {
|
213
|
+
while(this.playlist[this.i] &&
|
214
|
+
this.playlist[this.i].time <= this.currenttime) {
|
215
|
+
this.playlist[this.i].funct();
|
216
|
+
this.i++;
|
217
|
+
}
|
218
|
+
if (this.playlist[this.i]) {
|
219
|
+
this.currenttime+=this.ticksperinterval;
|
220
|
+
} else {
|
221
|
+
this.stopPlay();
|
222
|
+
}
|
223
|
+
};
|
224
|
+
|
225
|
+
function Midi() {
|
226
|
+
this.trackstrings="";
|
227
|
+
this.trackcount = 0;
|
228
|
+
this.noteOnAndChannel = "%90";
|
229
|
+
}
|
230
|
+
|
231
|
+
Midi.prototype.setTempo = function (qpm) {
|
232
|
+
if (this.trackcount===0) {
|
233
|
+
this.startTrack();
|
234
|
+
this.track+="%00%FF%51%03"+toHex(Math.round(60000000/qpm),6);
|
235
|
+
this.endTrack();
|
236
|
+
}
|
237
|
+
};
|
238
|
+
|
239
|
+
Midi.prototype.startTrack = function () {
|
240
|
+
this.track = "";
|
241
|
+
this.silencelength = 0;
|
242
|
+
this.trackcount++;
|
243
|
+
this.first=true;
|
244
|
+
if (this.instrument) {
|
245
|
+
this.setInstrument(this.instrument);
|
246
|
+
}
|
247
|
+
};
|
248
|
+
|
249
|
+
Midi.prototype.endTrack = function () {
|
250
|
+
var tracklength = toHex(this.track.length/3+4,8);
|
251
|
+
this.track = "MTrk"+tracklength+ // track header
|
252
|
+
this.track +
|
253
|
+
'%00%FF%2F%00'; // track end
|
254
|
+
this.trackstrings += this.track;
|
255
|
+
};
|
256
|
+
|
257
|
+
Midi.prototype.setInstrument = function (number) {
|
258
|
+
if (this.track)
|
259
|
+
this.track = "%00%C0"+toHex(number,2)+this.track;
|
260
|
+
else
|
261
|
+
this.track = "%00%C0"+toHex(number,2);
|
262
|
+
this.instrument=number;
|
263
|
+
};
|
264
|
+
|
265
|
+
Midi.prototype.setChannel = function (number) {
|
266
|
+
this.channel=number - 1;
|
267
|
+
this.noteOnAndChannel = "%9" + this.channel.toString(16);
|
268
|
+
};
|
269
|
+
|
270
|
+
Midi.prototype.startNote = function (pitch, loudness) {
|
271
|
+
this.track+=toDurationHex(this.silencelength); // only need to shift by amount of silence (if there is any)
|
272
|
+
this.silencelength = 0;
|
273
|
+
if (this.first) {
|
274
|
+
this.first = false;
|
275
|
+
this.track+=this.noteOnAndChannel;
|
276
|
+
}
|
277
|
+
this.track += "%"+pitch.toString(16)+"%"+loudness; //note
|
278
|
+
};
|
279
|
+
|
280
|
+
Midi.prototype.endNote = function (pitch, length) {
|
281
|
+
this.track += toDurationHex(length); //duration
|
282
|
+
this.track += "%"+pitch.toString(16)+"%00";//end note
|
283
|
+
};
|
284
|
+
|
285
|
+
Midi.prototype.addRest = function (length) {
|
286
|
+
this.silencelength += length;
|
287
|
+
};
|
288
|
+
|
289
|
+
Midi.prototype.embed = function(parent, noplayer) {
|
290
|
+
|
291
|
+
var data="data:audio/midi," +
|
292
|
+
"MThd%00%00%00%06%00%01"+toHex(this.trackcount,4)+"%01%e0"+ // header
|
293
|
+
this.trackstrings;
|
294
|
+
|
295
|
+
// var embedContainer = document.createElement("div");
|
296
|
+
// embedContainer.className = "embedContainer";
|
297
|
+
// document.body.appendChild(embedContainer);
|
298
|
+
// embedContainer.innerHTML = '<object id="embed1" classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab"><param name="src" value="' + data + '"></param><param name="Autoplay" value="false"></param><embed name="embed1" src="' + data + '" autostart="false" enablejavascript="true" /></object>';
|
299
|
+
// embed = document["embed1"];
|
300
|
+
|
301
|
+
|
302
|
+
var link = setAttributes(document.createElement('a'), {
|
303
|
+
href: data
|
304
|
+
});
|
305
|
+
link.innerHTML = "download midi";
|
306
|
+
parent.insertBefore(link,parent.firstChild);
|
307
|
+
|
308
|
+
if (noplayer) return;
|
309
|
+
|
310
|
+
var embed = setAttributes(document.createElement('embed'), {
|
311
|
+
src : data,
|
312
|
+
type : 'video/quicktime',
|
313
|
+
controller : 'true',
|
314
|
+
autoplay : 'false',
|
315
|
+
loop : 'false',
|
316
|
+
enablejavascript: 'true',
|
317
|
+
style:'display:block; height: 20px;'
|
318
|
+
});
|
319
|
+
parent.insertBefore(embed,parent.firstChild);
|
320
|
+
};
|
321
|
+
|
322
|
+
// s is assumed to be of even length
|
323
|
+
function encodeHex(s) {
|
324
|
+
var ret = "";
|
325
|
+
for (var i=0; i<s.length; i+=2) {
|
326
|
+
ret += "%";
|
327
|
+
ret += s.substr(i,2);
|
328
|
+
}
|
329
|
+
return ret;
|
330
|
+
}
|
331
|
+
|
332
|
+
function toHex(n, padding) {
|
333
|
+
var s = n.toString(16);
|
334
|
+
while (s.length<padding) {
|
335
|
+
s="0"+s;
|
336
|
+
}
|
337
|
+
return encodeHex(s);
|
338
|
+
}
|
339
|
+
|
340
|
+
function toDurationHex(n) {
|
341
|
+
var res = 0;
|
342
|
+
var a = [];
|
343
|
+
|
344
|
+
// cut up into 7 bit chunks;
|
345
|
+
while (n!==0) {
|
346
|
+
a.push(n & 0x7F);
|
347
|
+
n = n>>7;
|
348
|
+
}
|
349
|
+
|
350
|
+
// join the 7 bit chunks together, all but last chunk get leading 1
|
351
|
+
for (var i=a.length-1;i>=0;i--) {
|
352
|
+
res = res << 8;
|
353
|
+
var bits = a[i];
|
354
|
+
if (i!==0) {
|
355
|
+
bits = bits | 0x80;
|
356
|
+
}
|
357
|
+
res = res | bits;
|
358
|
+
}
|
359
|
+
|
360
|
+
var padding = res.toString(16).length;
|
361
|
+
padding += padding%2;
|
362
|
+
|
363
|
+
return toHex(res, padding);
|
364
|
+
}
|
365
|
+
|
366
|
+
ABCJS.midi.MidiWriter = function(parent, options) {
|
367
|
+
options = options || {};
|
368
|
+
this.parent = parent;
|
369
|
+
this.scale = [0,2,4,5,7,9,11];
|
370
|
+
this.restart = {line:0, staff:0, voice:0, pos:0};
|
371
|
+
this.visited = {};
|
372
|
+
this.multiplier =1;
|
373
|
+
this.next = null;
|
374
|
+
this.qpm = options.qpm || 180;
|
375
|
+
this.program = options.program || 2;
|
376
|
+
this.noteOnAndChannel = "%90";
|
377
|
+
this.javamidi = options.type ==="java" || false;
|
378
|
+
this.listeners = [];
|
379
|
+
this.transpose = 0; // PER
|
380
|
+
if (this.javamidi) {
|
381
|
+
MIDIPlugin = document.MIDIPlugin;
|
382
|
+
setTimeout(function() { // run on next event loop (once MIDIPlugin is loaded)
|
383
|
+
try { // activate MIDIPlugin
|
384
|
+
MIDIPlugin.openPlugin();
|
385
|
+
|
386
|
+
} catch(e) { // plugin not supported (download externals)
|
387
|
+
var a = document.createElement("a");
|
388
|
+
a.href = "http://java.sun.com/products/java-media/sound/soundbanks.html";
|
389
|
+
a.target = "_blank";
|
390
|
+
a.appendChild(document.createTextNode("Download Soundbank"));
|
391
|
+
parent.appendChild(a);
|
392
|
+
}
|
393
|
+
}, 0);
|
394
|
+
}
|
395
|
+
|
396
|
+
};
|
397
|
+
|
398
|
+
ABCJS.midi.MidiWriter.prototype.addListener = function(listener) {
|
399
|
+
this.listeners.push(listener);
|
400
|
+
};
|
401
|
+
|
402
|
+
ABCJS.midi.MidiWriter.prototype.notifySelect = function (abcelem) {
|
403
|
+
for (var i=0; i<this.listeners.length;i++) {
|
404
|
+
this.listeners[i].notifySelect(abcelem.abselem);
|
405
|
+
}
|
406
|
+
};
|
407
|
+
|
408
|
+
ABCJS.midi.MidiWriter.prototype.getMark = function() {
|
409
|
+
return {line:this.line, staff:this.staff,
|
410
|
+
voice:this.voice, pos:this.pos};
|
411
|
+
};
|
412
|
+
|
413
|
+
ABCJS.midi.MidiWriter.prototype.getMarkString = function(mark) {
|
414
|
+
mark = mark || this;
|
415
|
+
return "line"+mark.line+"staff"+mark.staff+
|
416
|
+
"voice"+mark.voice+"pos"+mark.pos;
|
417
|
+
};
|
418
|
+
|
419
|
+
ABCJS.midi.MidiWriter.prototype.goToMark = function(mark) {
|
420
|
+
this.line=mark.line;
|
421
|
+
this.staff=mark.staff;
|
422
|
+
this.voice=mark.voice;
|
423
|
+
this.pos=mark.pos;
|
424
|
+
};
|
425
|
+
|
426
|
+
ABCJS.midi.MidiWriter.prototype.markVisited = function() {
|
427
|
+
this.lastmark = this.getMarkString();
|
428
|
+
this.visited[this.lastmark] = true;
|
429
|
+
};
|
430
|
+
|
431
|
+
ABCJS.midi.MidiWriter.prototype.isVisited = function() {
|
432
|
+
if (this.visited[this.getMarkString()]) return true;
|
433
|
+
return false;
|
434
|
+
};
|
435
|
+
|
436
|
+
ABCJS.midi.MidiWriter.prototype.setJumpMark = function(mark) {
|
437
|
+
this.visited[this.lastmark] = mark;
|
438
|
+
};
|
439
|
+
|
440
|
+
ABCJS.midi.MidiWriter.prototype.getJumpMark = function() {
|
441
|
+
return this.visited[this.getMarkString()];
|
442
|
+
};
|
443
|
+
|
444
|
+
ABCJS.midi.MidiWriter.prototype.getLine = function() {
|
445
|
+
return this.abctune.lines[this.line];
|
446
|
+
};
|
447
|
+
|
448
|
+
ABCJS.midi.MidiWriter.prototype.getStaff = function() {
|
449
|
+
try {
|
450
|
+
return this.getLine().staff[this.staff];
|
451
|
+
} catch (e) {
|
452
|
+
|
453
|
+
}
|
454
|
+
};
|
455
|
+
|
456
|
+
ABCJS.midi.MidiWriter.prototype.getVoice = function() {
|
457
|
+
return this.getStaff().voices[this.voice];
|
458
|
+
};
|
459
|
+
|
460
|
+
ABCJS.midi.MidiWriter.prototype.getElem = function() {
|
461
|
+
return this.getVoice()[this.pos];
|
462
|
+
};
|
463
|
+
|
464
|
+
ABCJS.midi.MidiWriter.prototype.writeABC = function(abctune) {
|
465
|
+
try {
|
466
|
+
this.midi = (this.javamidi) ? new MidiProxy(new JavaMidi(this), new Midi()) : new Midi();
|
467
|
+
this.baraccidentals = [];
|
468
|
+
this.abctune = abctune;
|
469
|
+
this.baseduration = 480*4; // nice and divisible, equals 1 whole note
|
470
|
+
|
471
|
+
// PER: add global transposition.
|
472
|
+
if (abctune.formatting.midi && abctune.formatting.midi.transpose)
|
473
|
+
this.transpose = abctune.formatting.midi.transpose;
|
474
|
+
|
475
|
+
// PER: changed format of the global midi commands from the parser. Using the new definition here.
|
476
|
+
if (abctune.formatting.midi && abctune.formatting.midi.program && abctune.formatting.midi.program.program) {
|
477
|
+
this.midi.setInstrument(abctune.formatting.midi.program.program);
|
478
|
+
} else {
|
479
|
+
this.midi.setInstrument(this.program);
|
480
|
+
}
|
481
|
+
if (abctune.formatting.midi && abctune.formatting.midi.channel) {
|
482
|
+
this.midi.setChannel(abctune.formatting.midi.channel);
|
483
|
+
}
|
484
|
+
|
485
|
+
if (abctune.metaText.tempo) {
|
486
|
+
var duration = 1/4;
|
487
|
+
if (abctune.metaText.tempo.duration) {
|
488
|
+
duration = abctune.metaText.tempo.duration[0];
|
489
|
+
}
|
490
|
+
var bpm = 60;
|
491
|
+
if (abctune.metaText.tempo.bpm) {
|
492
|
+
bpm = abctune.metaText.tempo.bpm;
|
493
|
+
}
|
494
|
+
this.qpm = bpm*duration*4;
|
495
|
+
}
|
496
|
+
this.midi.setTempo(this.qpm);
|
497
|
+
|
498
|
+
// visit each voice completely in turn
|
499
|
+
// "problematic" because it means visiting only one staff+voice for each line each time
|
500
|
+
this.staffcount=1; // we'll know the actual number once we enter the code
|
501
|
+
for(this.staff=0;this.staff<this.staffcount;this.staff++) {
|
502
|
+
this.voicecount=1;
|
503
|
+
for(this.voice=0;this.voice<this.voicecount;this.voice++) {
|
504
|
+
this.midi.startTrack();
|
505
|
+
this.restart = {line:0, staff:this.staff, voice:this.voice, pos:0};
|
506
|
+
this.next= null;
|
507
|
+
for(this.line=0; this.line<abctune.lines.length; this.line++) {
|
508
|
+
var abcline = abctune.lines[this.line];
|
509
|
+
if (this.getLine().staff) {
|
510
|
+
this.writeABCLine();
|
511
|
+
}
|
512
|
+
}
|
513
|
+
this.midi.endTrack();
|
514
|
+
}
|
515
|
+
}
|
516
|
+
|
517
|
+
this.midi.embed(this.parent);
|
518
|
+
} catch (e) {
|
519
|
+
this.parent.innerHTML="Couldn't write midi: "+e;
|
520
|
+
}
|
521
|
+
};
|
522
|
+
|
523
|
+
ABCJS.midi.MidiWriter.prototype.writeABCLine = function() {
|
524
|
+
this.staffcount = this.getLine().staff.length;
|
525
|
+
this.voicecount = this.getStaff().voices.length;
|
526
|
+
this.setKeySignature(this.getStaff().key);
|
527
|
+
this.writeABCVoiceLine();
|
528
|
+
};
|
529
|
+
|
530
|
+
ABCJS.midi.MidiWriter.prototype.writeABCVoiceLine = function () {
|
531
|
+
this.pos=0;
|
532
|
+
while (this.pos<this.getVoice().length) {
|
533
|
+
this.writeABCElement(this.getElem());
|
534
|
+
if (this.next) {
|
535
|
+
this.goToMark(this.next);
|
536
|
+
this.next = null;
|
537
|
+
if (!this.getLine().staff) return;
|
538
|
+
} else {
|
539
|
+
this.pos++;
|
540
|
+
}
|
541
|
+
}
|
542
|
+
};
|
543
|
+
|
544
|
+
ABCJS.midi.MidiWriter.prototype.writeABCElement = function(elem) {
|
545
|
+
var foo;
|
546
|
+
switch (elem.el_type) {
|
547
|
+
case "note":
|
548
|
+
this.writeNote(elem);
|
549
|
+
break;
|
550
|
+
|
551
|
+
case "key":
|
552
|
+
this.setKeySignature(elem);
|
553
|
+
break;
|
554
|
+
case "bar":
|
555
|
+
this.handleBar(elem);
|
556
|
+
break;
|
557
|
+
case "meter":
|
558
|
+
case "clef":
|
559
|
+
break;
|
560
|
+
default:
|
561
|
+
|
562
|
+
}
|
563
|
+
|
564
|
+
};
|
565
|
+
|
566
|
+
|
567
|
+
ABCJS.midi.MidiWriter.prototype.writeNote = function(elem) {
|
568
|
+
|
569
|
+
if (elem.startTriplet) {
|
570
|
+
this.multiplier=2/3;
|
571
|
+
}
|
572
|
+
|
573
|
+
var mididuration = elem.duration*this.baseduration*this.multiplier;
|
574
|
+
if (elem.pitches) {
|
575
|
+
var midipitches = [];
|
576
|
+
for (var i=0; i<elem.pitches.length; i++) {
|
577
|
+
var note = elem.pitches[i];
|
578
|
+
var pitch= note.pitch;
|
579
|
+
if (note.accidental) {
|
580
|
+
switch(note.accidental) { // change that pitch (not other octaves) for the rest of the bar
|
581
|
+
case "sharp":
|
582
|
+
this.baraccidentals[pitch]=1; break;
|
583
|
+
case "flat":
|
584
|
+
this.baraccidentals[pitch]=-1; break;
|
585
|
+
case "natural":
|
586
|
+
this.baraccidentals[pitch]=0; break;
|
587
|
+
case "dblsharp":
|
588
|
+
this.baraccidentals[pitch]=2; break;
|
589
|
+
case "dblflat":
|
590
|
+
this.baraccidentals[pitch]=-2; break;
|
591
|
+
}
|
592
|
+
}
|
593
|
+
|
594
|
+
midipitches[i] = 60 + 12*this.extractOctave(pitch)+this.scale[this.extractNote(pitch)];
|
595
|
+
|
596
|
+
if (this.baraccidentals[pitch]!==undefined) {
|
597
|
+
midipitches[i] += this.baraccidentals[pitch];
|
598
|
+
} else { // use normal accidentals
|
599
|
+
midipitches[i] += this.accidentals[this.extractNote(pitch)];
|
600
|
+
}
|
601
|
+
midipitches[i] += this.transpose; // PER
|
602
|
+
|
603
|
+
this.midi.startNote(midipitches[i], 64, elem);
|
604
|
+
|
605
|
+
if (note.startTie) {
|
606
|
+
this.tieduration=mididuration;
|
607
|
+
}
|
608
|
+
}
|
609
|
+
|
610
|
+
for (i=0; i<elem.pitches.length; i++) {
|
611
|
+
var note = elem.pitches[i];
|
612
|
+
var pitch= note.pitch+this.transpose; // PER
|
613
|
+
if (note.startTie) continue; // don't terminate it
|
614
|
+
if (note.endTie) {
|
615
|
+
this.midi.endNote(midipitches[i],mididuration+this.tieduration);
|
616
|
+
} else {
|
617
|
+
this.midi.endNote(midipitches[i],mididuration);
|
618
|
+
}
|
619
|
+
mididuration = 0; // put these to zero as we've moved forward in the midi
|
620
|
+
this.tieduration=0;
|
621
|
+
}
|
622
|
+
} else if (elem.rest && elem.rest.type !== 'spacer') {
|
623
|
+
this.midi.addRest(mididuration);
|
624
|
+
}
|
625
|
+
|
626
|
+
if (elem.endTriplet) {
|
627
|
+
this.multiplier=1;
|
628
|
+
}
|
629
|
+
|
630
|
+
};
|
631
|
+
|
632
|
+
ABCJS.midi.MidiWriter.prototype.handleBar = function (elem) {
|
633
|
+
this.baraccidentals = [];
|
634
|
+
|
635
|
+
|
636
|
+
var repeat = (elem.type==="bar_right_repeat" || elem.type==="bar_dbl_repeat");
|
637
|
+
var skip = (elem.startEnding)?true:false;
|
638
|
+
var setvisited = (repeat || skip);
|
639
|
+
var setrestart = (elem.type==="bar_left_repeat" || elem.type==="bar_dbl_repeat" || elem.type==="bar_thick_thin" || elem.type==="bar_thin_thick" || elem.type==="bar_thin_thin" || elem.type==="bar_right_repeat");
|
640
|
+
|
641
|
+
var next = null;
|
642
|
+
|
643
|
+
if (this.isVisited()) {
|
644
|
+
next = this.getJumpMark();
|
645
|
+
} else {
|
646
|
+
|
647
|
+
if (skip || repeat) {
|
648
|
+
if (this.visited[this.lastmark] === true) {
|
649
|
+
this.setJumpMark(this.getMark());
|
650
|
+
}
|
651
|
+
}
|
652
|
+
|
653
|
+
if (setvisited) {
|
654
|
+
this.markVisited();
|
655
|
+
}
|
656
|
+
|
657
|
+
if (repeat) {
|
658
|
+
next = this.restart;
|
659
|
+
this.setJumpMark(this.getMark());
|
660
|
+
}
|
661
|
+
}
|
662
|
+
|
663
|
+
if (setrestart) {
|
664
|
+
this.restart = this.getMark();
|
665
|
+
}
|
666
|
+
|
667
|
+
if (next && this.getMarkString(next)!==this.getMarkString()) {
|
668
|
+
this.next = next;
|
669
|
+
}
|
670
|
+
|
671
|
+
};
|
672
|
+
|
673
|
+
ABCJS.midi.MidiWriter.prototype.setKeySignature = function(elem) {
|
674
|
+
this.accidentals = [0,0,0,0,0,0,0];
|
675
|
+
if (this.abctune.formatting.bagpipes) {
|
676
|
+
elem.accidentals=[{acc: 'natural', note: 'g'}, {acc: 'sharp', note: 'f'}, {acc: 'sharp', note: 'c'}];
|
677
|
+
}
|
678
|
+
if (!elem.accidentals) return;
|
679
|
+
window.ABCJS.parse.each(elem.accidentals, function(acc) {
|
680
|
+
var d = (acc.acc === "sharp") ? 1 : (acc.acc === "natural") ?0 : -1;
|
681
|
+
|
682
|
+
var lowercase = acc.note.toLowerCase();
|
683
|
+
var note = this.extractNote(lowercase.charCodeAt(0)-'c'.charCodeAt(0));
|
684
|
+
this.accidentals[note]+=d;
|
685
|
+
}, this);
|
686
|
+
|
687
|
+
};
|
688
|
+
|
689
|
+
ABCJS.midi.MidiWriter.prototype.extractNote = function(pitch) {
|
690
|
+
pitch = pitch%7;
|
691
|
+
if (pitch<0) pitch+=7;
|
692
|
+
return pitch;
|
693
|
+
};
|
694
|
+
|
695
|
+
ABCJS.midi.MidiWriter.prototype.extractOctave = function(pitch) {
|
696
|
+
return Math.floor(pitch/7);
|
697
|
+
};
|
698
|
+
})();
|