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

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

More careful control of keyboard events so text can reach the custom chord part of the system without accidentally playing notes on the piano at the same time

File size: 11.3 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 if (e.shiftKey) {
328 return fnDrumKeyboard(e);
329 }
330
331
332 var i = keysPressed.length;
333 while(i--) {
334 if(keysPressed[i]==e.keyCode) {
335 return false;
336 }
337 }
338 keysPressed.push(e.keyCode);
339
340 switch(e.keyCode) {
341
342 // left
343 case 37:
344 fnChangeOctave(-1);
345 break;
346
347 // right
348 case 39:
349 fnChangeOctave(1);
350 break;
351
352 // space
353 case 16:
354 break;
355 fnPlaySong([
356 ['E,0', 8],
357 ['D,0', 8],
358 ['C,0', 2],
359 ['C,0', 8],
360 ['D,0', 8],
361 ['C,0', 8],
362 ['E,0', 8],
363 ['D,0', 1],
364 ['C,0', 8],
365 ['D,0', 8],
366 ['E,0', 2],
367 ['A,0', 8],
368 ['G,0', 8],
369 ['E,0', 8],
370 ['C,0', 8],
371 ['D,0', 1],
372 ['A,0', 8],
373 ['B,0', 8],
374 ['C,1', 2],
375 ['B,0', 8],
376 ['C,1', 8],
377 ['D,1', 8],
378 ['C,1', 8],
379 ['A,0', 1],
380 ['G,0', 8],
381 ['A,0', 8],
382 ['B,0', 2],
383 ['C,1', 8],
384 ['B,0', 8],
385 ['A,0', 8],
386 ['G,0', 8],
387 ['A,0', 1]
388 ]);
389 break;
390
391 }
392
393 if(keyboard[e.keyCode]) {
394 if(visualKeyboard[keyboard[e.keyCode]]) {
395 visualKeyboard[keyboard[e.keyCode]].style.backgroundColor = '#ff0000';
396 visualKeyboard[keyboard[e.keyCode]].style.marginTop = '5px';
397 visualKeyboard[keyboard[e.keyCode]].style.boxShadow = 'none';
398 }
399 var arrPlayNote = keyboard[e.keyCode].split(',');
400 var note = arrPlayNote[0];
401 var octaveModifier = arrPlayNote[1]|0;
402 fnPlayNote(note, __octave + octaveModifier);
403 } else {
404 return false;
405 }
406
407 }
408
409 // Remove key bindings once note is done.
410
411 function calcMidiNoteInfo() {
412 if (mediaStartPlayTime != null) {
413 var playedNoteEndTime = Date.now();
414 var relativePlayedNoteStartTime = playedNoteStartTime - mediaStartPlayTime;
415 var relativePlayedNoteEndTime = playedNoteEndTime - mediaStartPlayTime;
416 playedDuration = (relativePlayedNoteEndTime - relativePlayedNoteStartTime);
417
418
419 // console.log("**** " + playedNotePitch + " (midi pitch = " + playedMidiPitch+ ") startTime = ", relativePlayStartTime + ", duration = " + playedDuration);
420
421 // ***** xxxx
422 mediaPlayedNotes[String(mediaStartPlayTime)].push(
423 { 'midiPitch' : playedMidiPitch,
424 'midiNoteOn' : relativePlayedNoteStartTime,
425 'midiNoteOff' : relativePlayedNoteEndTime,
426 'startCurrentTime' : playedNoteStartCurrentTime,
427 'duration' : playedDuration,
428 'startPercTime': playedNoteStartCurrentTime/mediaPlayer.duration,
429 'humanReadble' : playedNotePitch }
430 );
431
432 }
433 }
434
435 var fnRemoveKeyBinding = function(e) {
436
437 console.log("**** key up at: " + Date.now())
438 // console.log("*** event e = " + JSON.stringify(e));
439
440
441 var i = keysPressed.length;
442 while(i--) {
443 if(keysPressed[i]==e.keyCode) {
444 if(visualKeyboard[keyboard[e.keyCode]]) {
445
446 delayedMissingTheGroove(500);
447
448 MIDI.noteOff(0, playedMidiPitch, 0);
449 if (mediaPlaybackMode == "record") {
450 calcMidiNoteInfo();
451 }
452
453 visualKeyboard[keyboard[e.keyCode]].style.backgroundColor = '';
454 visualKeyboard[keyboard[e.keyCode]].style.marginTop = '';
455 visualKeyboard[keyboard[e.keyCode]].style.boxShadow = '';
456 }
457 keysPressed.splice(i, 1);
458 }
459 }
460
461 }
462
463 var fnPlaySong = function(arr) {
464
465 if(arr.length>0) {
466
467 var noteLen = 1000*(1/parseInt(arr[0][1]));
468 if(!(arr[0][0] instanceof Array)) {
469 arr[0][0] = [arr[0][0]];
470 }
471 var i = arr[0][0].length;
472 var keys = [];
473 while(i--) {
474 keys.unshift(reverseLookup[arr[0][0][i]]);
475 console.log("**** In fnPlaySong(), away to call fnPlayKeyboard()");
476 fnPlayKeyboard({keyCode:keys[0]});
477 }
478 arr.shift();
479 setTimeout(function(array, val){ return function() { var i = val.length; while(i--) { fnRemoveKeyBinding({keyCode:val[i]}); } fnPlaySong(array); } }(arr, keys), noteLen);
480
481 }
482
483 };
484
485 var fnDrumKeyboard = function(e) {
486 //console.log("*** drum pressed: " + e.key);
487 if (e.key == "C") {
488 $('#drum1').trigger("mousedown");
489 }
490 else if (e.key == "V") {
491 $('#drum2').trigger("mousedown");
492 }
493 else if (e.key == "B") {
494 $('#drum3').trigger("mousedown");
495 }
496 else if (e.key == "N") {
497 $('#drum4').trigger("mousedown");
498 }
499 else if (e.key == "M") {
500 $('#drum5').trigger("mousedown");
501 }
502
503 //e.preventDefault();
504 return true; // let other things outside of this piano/drum chain (such as the customGuitar input) see the event
505 }
506
507
508 // Set up global event listeners
509
510 window.addEventListener('keydown', fnPlayKeyboard);
511 window.addEventListener('keyup', fnRemoveKeyBinding);
512 document.getElementById('-_OCTAVE').addEventListener('click', function() { fnChangeOctave(-1); });
513 document.getElementById('+_OCTAVE').addEventListener('click', function() { fnChangeOctave(1); });
514
515 Object.defineProperty(this, 'draw', {
516 value: fnCreateKeyboard
517 });
518
519}
Note: See TracBrowser for help on using the repository browser.