source: main/trunk/model-sites-dev/respooled/collect/popup-video-respooled/js/audiosynth.view.js@ 30093

Last change on this file since 30093 was 30093, checked in by davidb, 9 years ago

Development work done in at JCDL

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