1 | function AudioSynthView() {
|
---|
2 |
|
---|
3 | var isMobile = !!navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i);
|
---|
4 | if(isMobile) { var evtListener = ['touchstart', 'touchend']; } else { var evtListener = ['mousedown', 'mouseup']; }
|
---|
5 |
|
---|
6 | var __audioSynth = new AudioSynth();
|
---|
7 | __audioSynth.setVolume(0.5);
|
---|
8 | var __octave = 4;
|
---|
9 |
|
---|
10 | // Change octave
|
---|
11 | var fnChangeOctave = function(x) {
|
---|
12 |
|
---|
13 | x |= 0;
|
---|
14 |
|
---|
15 | __octave += x;
|
---|
16 |
|
---|
17 | __octave = Math.min(5, Math.max(3, __octave));
|
---|
18 |
|
---|
19 | var octaveName = document.getElementsByName('OCTAVE_LABEL');
|
---|
20 | var i = octaveName.length;
|
---|
21 | while(i--) {
|
---|
22 | var val = parseInt(octaveName[i].getAttribute('value'));
|
---|
23 | octaveName[i].innerHTML = (val + __octave);
|
---|
24 | }
|
---|
25 |
|
---|
26 | document.getElementById('OCTAVE_LOWER').innerHTML = __octave-1;
|
---|
27 | document.getElementById('OCTAVE_UPPER').innerHTML = __octave+1;
|
---|
28 |
|
---|
29 | };
|
---|
30 |
|
---|
31 | // Key bindings, notes to keyCodes.
|
---|
32 | var keyboard = {
|
---|
33 |
|
---|
34 | /* 2 */
|
---|
35 | 50: 'C#,-1',
|
---|
36 |
|
---|
37 | /* 3 */
|
---|
38 | 51: 'D#,-1',
|
---|
39 |
|
---|
40 | /* 5 */
|
---|
41 | 53: 'F#,-1',
|
---|
42 |
|
---|
43 | /* 6 */
|
---|
44 | 54: 'G#,-1',
|
---|
45 |
|
---|
46 | /* 7 */
|
---|
47 | 55: 'A#,-1',
|
---|
48 |
|
---|
49 | /* 9 */
|
---|
50 | 57: 'C#,0',
|
---|
51 |
|
---|
52 | /* 0 */
|
---|
53 | 48: 'D#,0',
|
---|
54 |
|
---|
55 | /* + */
|
---|
56 | 187: 'F#,0',
|
---|
57 | 61: 'F#,0',
|
---|
58 |
|
---|
59 | /* Q */
|
---|
60 | 81: 'C,-1',
|
---|
61 |
|
---|
62 | /* W */
|
---|
63 | 87: 'D,-1',
|
---|
64 |
|
---|
65 | /* E */
|
---|
66 | 69: 'E,-1',
|
---|
67 |
|
---|
68 | /* R */
|
---|
69 | 82: 'F,-1',
|
---|
70 |
|
---|
71 | /* T */
|
---|
72 | 84: 'G,-1',
|
---|
73 |
|
---|
74 | /* Y */
|
---|
75 | 89: 'A,-1',
|
---|
76 |
|
---|
77 | /* U */
|
---|
78 | 85: 'B,-1',
|
---|
79 |
|
---|
80 | /* I */
|
---|
81 | 73: 'C,0',
|
---|
82 |
|
---|
83 | /* O */
|
---|
84 | 79: 'D,0',
|
---|
85 |
|
---|
86 | /* P */
|
---|
87 | 80: 'E,0',
|
---|
88 |
|
---|
89 | /* [ */
|
---|
90 | 219: 'F,0',
|
---|
91 |
|
---|
92 | /* ] */
|
---|
93 | 221: 'G,0',
|
---|
94 |
|
---|
95 | /* A */
|
---|
96 | 65: 'G#,0',
|
---|
97 |
|
---|
98 | /* S */
|
---|
99 | 83: 'A#,0',
|
---|
100 |
|
---|
101 | /* F */
|
---|
102 | 70: 'C#,1',
|
---|
103 |
|
---|
104 | /* G */
|
---|
105 | 71: 'D#,1',
|
---|
106 |
|
---|
107 | /* J */
|
---|
108 | 74: 'F#,1',
|
---|
109 |
|
---|
110 | /* K */
|
---|
111 | 75: 'G#,1',
|
---|
112 |
|
---|
113 | /* L */
|
---|
114 | 76: 'A#,1',
|
---|
115 |
|
---|
116 | /* Z */
|
---|
117 | 90: 'A,0',
|
---|
118 |
|
---|
119 | /* X */
|
---|
120 | 88: 'B,0',
|
---|
121 |
|
---|
122 | /* C */
|
---|
123 | 67: 'C,1',
|
---|
124 |
|
---|
125 | /* V */
|
---|
126 | 86: 'D,1',
|
---|
127 |
|
---|
128 | /* B */
|
---|
129 | 66: 'E,1',
|
---|
130 |
|
---|
131 | /* N */
|
---|
132 | 78: 'F,1',
|
---|
133 |
|
---|
134 | /* M */
|
---|
135 | 77: 'G,1',
|
---|
136 |
|
---|
137 | /* , */
|
---|
138 | 188: 'A,1',
|
---|
139 |
|
---|
140 | /* . */
|
---|
141 | 190: 'B,1'
|
---|
142 |
|
---|
143 | };
|
---|
144 |
|
---|
145 | var reverseLookupText = {};
|
---|
146 | var reverseLookup = {};
|
---|
147 |
|
---|
148 | // Create a reverse lookup table.
|
---|
149 | for(var i in keyboard) {
|
---|
150 |
|
---|
151 | var val;
|
---|
152 |
|
---|
153 | switch(i|0) {
|
---|
154 |
|
---|
155 | case 187:
|
---|
156 | val = 61;
|
---|
157 | break;
|
---|
158 |
|
---|
159 | case 219:
|
---|
160 | val = 91;
|
---|
161 | break;
|
---|
162 |
|
---|
163 | case 221:
|
---|
164 | val = 93;
|
---|
165 | break;
|
---|
166 |
|
---|
167 | case 188:
|
---|
168 | val = 44;
|
---|
169 | break;
|
---|
170 |
|
---|
171 | case 190:
|
---|
172 | val = 46;
|
---|
173 | break;
|
---|
174 |
|
---|
175 | default:
|
---|
176 | val = i;
|
---|
177 | break;
|
---|
178 |
|
---|
179 | }
|
---|
180 |
|
---|
181 | reverseLookupText[keyboard[i]] = val;
|
---|
182 | reverseLookup[keyboard[i]] = i;
|
---|
183 |
|
---|
184 | }
|
---|
185 |
|
---|
186 | // Keys you have pressed down.
|
---|
187 | var keysPressed = [];
|
---|
188 | var visualKeyboard = null;
|
---|
189 | var selectSound = null;
|
---|
190 |
|
---|
191 | var fnCreateKeyboard = function(keyboardElement) {
|
---|
192 | // Generate keyboard
|
---|
193 | // This is our main keyboard element! It's populated dynamically based on what you've set above.
|
---|
194 | visualKeyboard = document.getElementById('keyboard');
|
---|
195 | selectSound = document.getElementById('sound');
|
---|
196 |
|
---|
197 | var iKeys = 0;
|
---|
198 | var iWhite = 0;
|
---|
199 | var notes = __audioSynth._notes;
|
---|
200 |
|
---|
201 | for(var i=-1;i<=1;i++) {
|
---|
202 | for(var n in notes) {
|
---|
203 | if(n[2]!='b') {
|
---|
204 | var thisKey = document.createElement('div');
|
---|
205 | if(n.length>1) {
|
---|
206 | thisKey.className = 'black key';
|
---|
207 | thisKey.style.width = '30px';
|
---|
208 | thisKey.style.height = '120px';
|
---|
209 | thisKey.style.left = (40 * (iWhite - 1)) + 25 + 'px';
|
---|
210 | } else {
|
---|
211 | thisKey.className = 'white key';
|
---|
212 | thisKey.style.width = '40px';
|
---|
213 | thisKey.style.height = '200px';
|
---|
214 | thisKey.style.left = 40 * iWhite + 'px';
|
---|
215 | iWhite++;
|
---|
216 | }
|
---|
217 | var label = document.createElement('div');
|
---|
218 | label.className = 'label';
|
---|
219 | label.innerHTML = '<b>' + String.fromCharCode(reverseLookupText[n + ',' + i]) + '</b>' + '<br /><br />' + n.substr(0,1) +
|
---|
220 | '<span name="OCTAVE_LABEL" value="' + i + '">' + (__octave + parseInt(i)) + '</span>' + (n.substr(1,1)?n.substr(1,1):'');
|
---|
221 | thisKey.appendChild(label);
|
---|
222 | thisKey.setAttribute('ID', 'KEY_' + n + ',' + i);
|
---|
223 | thisKey.addEventListener(evtListener[0], (function(_temp) { return function() { fnPlayKeyboard({keyCode:_temp}); } })(reverseLookup[n + ',' + i]));
|
---|
224 | visualKeyboard[n + ',' + i] = thisKey;
|
---|
225 | visualKeyboard.appendChild(thisKey);
|
---|
226 | iKeys++;
|
---|
227 | }
|
---|
228 | }
|
---|
229 | }
|
---|
230 |
|
---|
231 | visualKeyboard.style.width = iWhite * 40 + 'px';
|
---|
232 |
|
---|
233 | window.addEventListener(evtListener[1], function() { n = keysPressed.length; while(n--) { fnRemoveKeyBinding({keyCode:keysPressed[n]}); } });
|
---|
234 |
|
---|
235 | };
|
---|
236 |
|
---|
237 | var midiMiddleC = 60;
|
---|
238 |
|
---|
239 | //var freqOctaveMap = {'C':261.63,'C#':277.18,'D':293.66,'D#':311.13,'E':329.63,'F':346.23,'F#':369.99,'G':392.00,'G#':415.30,'A':440.00,'A#':466.16,'B':493.88});
|
---|
240 |
|
---|
241 | var midiOctaveMap = {'C':0,'C#':1,'D':2,'D#':3,'E':4,'F':5,'F#':6,'G':7,'G#':8,'A':9,'A#':10,'B':11};
|
---|
242 |
|
---|
243 | var playedNotePitch;
|
---|
244 | var playedMidiPitch;
|
---|
245 |
|
---|
246 | var playedNoteStartTime;
|
---|
247 | var playedNoteStartCurrentTime;
|
---|
248 |
|
---|
249 | var playedDuration;
|
---|
250 |
|
---|
251 |
|
---|
252 | // Creates our audio player
|
---|
253 | var fnPlayNote = function(note, octave) {
|
---|
254 |
|
---|
255 | //console.log("**** Note started: " + Date.now()/1000.0)
|
---|
256 |
|
---|
257 | playedNoteStartTime = Date.now();
|
---|
258 | playedNoteStartCurrentTime = mediaPlayer.currentTime;
|
---|
259 |
|
---|
260 | playedNotePitch = "note = " + note + ", octave = " + octave;
|
---|
261 | playedMidiPitch = midiMiddleC + (12 * (octave-4)) + midiOctaveMap[note];
|
---|
262 |
|
---|
263 | inTheGroove();
|
---|
264 | MIDI.noteOn(0, playedMidiPitch, 50);
|
---|
265 |
|
---|
266 | //src = __audioSynth.generate(selectSound.value, note, octave, 2);
|
---|
267 | //container = new Audio(src);
|
---|
268 | //container.addEventListener('ended', function() {
|
---|
269 | // container = null;
|
---|
270 | //});
|
---|
271 | //container.addEventListener('loadeddata', function(e) { e.target.play(); });
|
---|
272 | //container.autoplay = false;
|
---|
273 | //container.setAttribute('type', 'audio/wav');
|
---|
274 | /*document.body.appendChild(container);*/
|
---|
275 | //container.load();
|
---|
276 | //return container;
|
---|
277 | return true;
|
---|
278 |
|
---|
279 | };
|
---|
280 |
|
---|
281 | // Detect keypresses, play notes.
|
---|
282 |
|
---|
283 | var fnPlayKeyboard = function(e) {
|
---|
284 |
|
---|
285 | if (e.ctrlKey) {
|
---|
286 | // avoid playing a note if some kind of control-key
|
---|
287 | // combination (such a web page reload) is pressed
|
---|
288 | return false;
|
---|
289 | }
|
---|
290 |
|
---|
291 | if (e.key == " ") {
|
---|
292 | togglePlayPause();
|
---|
293 | e.preventDefault();
|
---|
294 | return false;
|
---|
295 | }
|
---|
296 |
|
---|
297 | if (e.shiftKey) {
|
---|
298 | return fnDrumKeyboard(e);
|
---|
299 | }
|
---|
300 |
|
---|
301 |
|
---|
302 | var i = keysPressed.length;
|
---|
303 | while(i--) {
|
---|
304 | if(keysPressed[i]==e.keyCode) {
|
---|
305 | return false;
|
---|
306 | }
|
---|
307 | }
|
---|
308 | keysPressed.push(e.keyCode);
|
---|
309 |
|
---|
310 | switch(e.keyCode) {
|
---|
311 |
|
---|
312 | // left
|
---|
313 | case 37:
|
---|
314 | fnChangeOctave(-1);
|
---|
315 | break;
|
---|
316 |
|
---|
317 | // right
|
---|
318 | case 39:
|
---|
319 | fnChangeOctave(1);
|
---|
320 | break;
|
---|
321 |
|
---|
322 | // space
|
---|
323 | case 16:
|
---|
324 | break;
|
---|
325 | fnPlaySong([
|
---|
326 | ['E,0', 8],
|
---|
327 | ['D,0', 8],
|
---|
328 | ['C,0', 2],
|
---|
329 | ['C,0', 8],
|
---|
330 | ['D,0', 8],
|
---|
331 | ['C,0', 8],
|
---|
332 | ['E,0', 8],
|
---|
333 | ['D,0', 1],
|
---|
334 | ['C,0', 8],
|
---|
335 | ['D,0', 8],
|
---|
336 | ['E,0', 2],
|
---|
337 | ['A,0', 8],
|
---|
338 | ['G,0', 8],
|
---|
339 | ['E,0', 8],
|
---|
340 | ['C,0', 8],
|
---|
341 | ['D,0', 1],
|
---|
342 | ['A,0', 8],
|
---|
343 | ['B,0', 8],
|
---|
344 | ['C,1', 2],
|
---|
345 | ['B,0', 8],
|
---|
346 | ['C,1', 8],
|
---|
347 | ['D,1', 8],
|
---|
348 | ['C,1', 8],
|
---|
349 | ['A,0', 1],
|
---|
350 | ['G,0', 8],
|
---|
351 | ['A,0', 8],
|
---|
352 | ['B,0', 2],
|
---|
353 | ['C,1', 8],
|
---|
354 | ['B,0', 8],
|
---|
355 | ['A,0', 8],
|
---|
356 | ['G,0', 8],
|
---|
357 | ['A,0', 1]
|
---|
358 | ]);
|
---|
359 | break;
|
---|
360 |
|
---|
361 | }
|
---|
362 |
|
---|
363 | if(keyboard[e.keyCode]) {
|
---|
364 | if(visualKeyboard[keyboard[e.keyCode]]) {
|
---|
365 | visualKeyboard[keyboard[e.keyCode]].style.backgroundColor = '#ff0000';
|
---|
366 | visualKeyboard[keyboard[e.keyCode]].style.marginTop = '5px';
|
---|
367 | visualKeyboard[keyboard[e.keyCode]].style.boxShadow = 'none';
|
---|
368 | }
|
---|
369 | var arrPlayNote = keyboard[e.keyCode].split(',');
|
---|
370 | var note = arrPlayNote[0];
|
---|
371 | var octaveModifier = arrPlayNote[1]|0;
|
---|
372 | fnPlayNote(note, __octave + octaveModifier);
|
---|
373 | } else {
|
---|
374 | return false;
|
---|
375 | }
|
---|
376 |
|
---|
377 | }
|
---|
378 |
|
---|
379 | // Remove key bindings once note is done.
|
---|
380 |
|
---|
381 | function calcMidiNoteInfo() {
|
---|
382 | if (mediaStartPlayTime != null) {
|
---|
383 | var playedNoteEndTime = Date.now();
|
---|
384 | var relativePlayedNoteStartTime = playedNoteStartTime - mediaStartPlayTime;
|
---|
385 | var relativePlayedNoteEndTime = playedNoteEndTime - mediaStartPlayTime;
|
---|
386 | playedDuration = (relativePlayedNoteEndTime - relativePlayedNoteStartTime);
|
---|
387 |
|
---|
388 |
|
---|
389 | // console.log("**** " + playedNotePitch + " (midi pitch = " + playedMidiPitch+ ") startTime = ", relativePlayStartTime + ", duration = " + playedDuration);
|
---|
390 |
|
---|
391 | mediaPlayedNotes[String(mediaStartPlayTime)].push(
|
---|
392 | { 'midiPitch' : playedMidiPitch,
|
---|
393 | 'midiNoteOn' : relativePlayedNoteStartTime,
|
---|
394 | 'midiNoteOff' : relativePlayedNoteEndTime,
|
---|
395 | 'startCurrentTime' : playedNoteStartCurrentTime,
|
---|
396 | 'duration ': playedDuration,
|
---|
397 | 'humanReadble': playedNotePitch }
|
---|
398 | );
|
---|
399 |
|
---|
400 | }
|
---|
401 | }
|
---|
402 |
|
---|
403 | var fnRemoveKeyBinding = function(e) {
|
---|
404 |
|
---|
405 | //console.log("**** key up at: " + Date.now())
|
---|
406 | // console.log("*** event e = " + JSON.stringify(e));
|
---|
407 |
|
---|
408 |
|
---|
409 | var i = keysPressed.length;
|
---|
410 | while(i--) {
|
---|
411 | if(keysPressed[i]==e.keyCode) {
|
---|
412 | if(visualKeyboard[keyboard[e.keyCode]]) {
|
---|
413 |
|
---|
414 | delayedMissingTheGroove(500);
|
---|
415 |
|
---|
416 | MIDI.noteOff(0, playedMidiPitch, 0);
|
---|
417 | if (mediaPlaybackMode == "record") {
|
---|
418 | calcMidiNoteInfo();
|
---|
419 | }
|
---|
420 |
|
---|
421 | visualKeyboard[keyboard[e.keyCode]].style.backgroundColor = '';
|
---|
422 | visualKeyboard[keyboard[e.keyCode]].style.marginTop = '';
|
---|
423 | visualKeyboard[keyboard[e.keyCode]].style.boxShadow = '';
|
---|
424 | }
|
---|
425 | keysPressed.splice(i, 1);
|
---|
426 | }
|
---|
427 | }
|
---|
428 |
|
---|
429 | }
|
---|
430 |
|
---|
431 | var fnPlaySong = function(arr) {
|
---|
432 |
|
---|
433 | if(arr.length>0) {
|
---|
434 |
|
---|
435 | var noteLen = 1000*(1/parseInt(arr[0][1]));
|
---|
436 | if(!(arr[0][0] instanceof Array)) {
|
---|
437 | arr[0][0] = [arr[0][0]];
|
---|
438 | }
|
---|
439 | var i = arr[0][0].length;
|
---|
440 | var keys = [];
|
---|
441 | while(i--) {
|
---|
442 | keys.unshift(reverseLookup[arr[0][0][i]]);
|
---|
443 | fnPlayKeyboard({keyCode:keys[0]});
|
---|
444 | }
|
---|
445 | arr.shift();
|
---|
446 | setTimeout(function(array, val){ return function() { var i = val.length; while(i--) { fnRemoveKeyBinding({keyCode:val[i]}); } fnPlaySong(array); } }(arr, keys), noteLen);
|
---|
447 |
|
---|
448 | }
|
---|
449 |
|
---|
450 | };
|
---|
451 |
|
---|
452 | var fnDrumKeyboard = function(e) {
|
---|
453 | //console.log("*** drum pressed: " + e.key);
|
---|
454 | if (e.key == "C") {
|
---|
455 | $('#drum1').trigger("mousedown");
|
---|
456 | }
|
---|
457 | else if (e.key == "V") {
|
---|
458 | $('#drum2').trigger("mousedown");
|
---|
459 | }
|
---|
460 | else if (e.key == "B") {
|
---|
461 | $('#drum3').trigger("mousedown");
|
---|
462 | }
|
---|
463 | else if (e.key == "N") {
|
---|
464 | $('#drum4').trigger("mousedown");
|
---|
465 | }
|
---|
466 | else if (e.key == "M") {
|
---|
467 | $('#drum5').trigger("mousedown");
|
---|
468 | }
|
---|
469 |
|
---|
470 | e.preventDefault();
|
---|
471 | return false;
|
---|
472 | }
|
---|
473 |
|
---|
474 |
|
---|
475 | // Set up global event listeners
|
---|
476 |
|
---|
477 | window.addEventListener('keydown', fnPlayKeyboard);
|
---|
478 | window.addEventListener('keyup', fnRemoveKeyBinding);
|
---|
479 | document.getElementById('-_OCTAVE').addEventListener('click', function() { fnChangeOctave(-1); });
|
---|
480 | document.getElementById('+_OCTAVE').addEventListener('click', function() { fnChangeOctave(1); });
|
---|
481 |
|
---|
482 | Object.defineProperty(this, 'draw', {
|
---|
483 | value: fnCreateKeyboard
|
---|
484 | });
|
---|
485 |
|
---|
486 | }
|
---|