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

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

Working popup editor, saving to localStorage. Tidy up on space-bar for global pause and start play.

File size: 10.7 KB
Line 
1function 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() {
224 console.log("**** bespoke evtListener for mousedown, away to call fnPlayKeyboard()");
225 fnPlayKeyboard({keyCode:_temp}); } })(reverseLookup[n + ',' + i]));
226
227 visualKeyboard[n + ',' + i] = thisKey;
228 visualKeyboard.appendChild(thisKey);
229 iKeys++;
230 }
231 }
232 }
233
234 visualKeyboard.style.width = iWhite * 40 + 'px';
235
236 window.addEventListener(evtListener[1], function() { n = keysPressed.length; while(n--) { fnRemoveKeyBinding({keyCode:keysPressed[n]}); } });
237
238 };
239
240 var midiMiddleC = 60;
241
242 //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});
243
244 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};
245
246 var playedNotePitch;
247 var playedMidiPitch;
248
249 var playedNoteStartTime;
250 var playedNoteStartCurrentTime;
251
252 var playedDuration;
253
254
255 // Creates our audio player
256 var fnPlayNote = function(note, octave) {
257
258 //console.log("**** Note started: " + Date.now()/1000.0)
259
260 playedNoteStartTime = Date.now();
261 playedNoteStartCurrentTime = mediaPlayer.currentTime;
262
263 playedNotePitch = "note = " + note + ", octave = " + octave;
264 playedMidiPitch = midiMiddleC + (12 * (octave-4)) + midiOctaveMap[note];
265
266 inTheGroove();
267 MIDI.noteOn(0, playedMidiPitch, 50);
268
269 //src = __audioSynth.generate(selectSound.value, note, octave, 2);
270 //container = new Audio(src);
271 //container.addEventListener('ended', function() {
272 // container = null;
273 //});
274 //container.addEventListener('loadeddata', function(e) { e.target.play(); });
275 //container.autoplay = false;
276 //container.setAttribute('type', 'audio/wav');
277 /*document.body.appendChild(container);*/
278 //container.load();
279 //return container;
280 return true;
281
282 };
283
284 // Detect keypresses, play notes.
285
286 var fnPlayKeyboard = function(e) {
287 console.log("***### fnPlayKeyboard() e=" + e); //JSON.stringify(e));
288
289 // check to see if any modal dialogs are open
290 var active_modal_dialogs = $('.ui-widget-overlay:visible');
291 if (active_modal_dialogs.length>0) {
292 return false;
293 }
294
295
296 if (e.ctrlKey) {
297 // avoid playing a note if some kind of control-key
298 // combination (such a web page reload) is pressed
299 return false;
300 }
301
302 console.log("**** testing keyboard event key: '" + e.key + "' keycode = " + e.keyCode);
303
304 if (e.keyCode == 32) { // <space-bar>
305 togglePlayPause();
306 //e.preventDefault();
307 return false;
308 }
309
310 if (e.shiftKey) {
311 return fnDrumKeyboard(e);
312 }
313
314
315 var i = keysPressed.length;
316 while(i--) {
317 if(keysPressed[i]==e.keyCode) {
318 return false;
319 }
320 }
321 keysPressed.push(e.keyCode);
322
323 switch(e.keyCode) {
324
325 // left
326 case 37:
327 fnChangeOctave(-1);
328 break;
329
330 // right
331 case 39:
332 fnChangeOctave(1);
333 break;
334
335 // space
336 case 16:
337 break;
338 fnPlaySong([
339 ['E,0', 8],
340 ['D,0', 8],
341 ['C,0', 2],
342 ['C,0', 8],
343 ['D,0', 8],
344 ['C,0', 8],
345 ['E,0', 8],
346 ['D,0', 1],
347 ['C,0', 8],
348 ['D,0', 8],
349 ['E,0', 2],
350 ['A,0', 8],
351 ['G,0', 8],
352 ['E,0', 8],
353 ['C,0', 8],
354 ['D,0', 1],
355 ['A,0', 8],
356 ['B,0', 8],
357 ['C,1', 2],
358 ['B,0', 8],
359 ['C,1', 8],
360 ['D,1', 8],
361 ['C,1', 8],
362 ['A,0', 1],
363 ['G,0', 8],
364 ['A,0', 8],
365 ['B,0', 2],
366 ['C,1', 8],
367 ['B,0', 8],
368 ['A,0', 8],
369 ['G,0', 8],
370 ['A,0', 1]
371 ]);
372 break;
373
374 }
375
376 if(keyboard[e.keyCode]) {
377 if(visualKeyboard[keyboard[e.keyCode]]) {
378 visualKeyboard[keyboard[e.keyCode]].style.backgroundColor = '#ff0000';
379 visualKeyboard[keyboard[e.keyCode]].style.marginTop = '5px';
380 visualKeyboard[keyboard[e.keyCode]].style.boxShadow = 'none';
381 }
382 var arrPlayNote = keyboard[e.keyCode].split(',');
383 var note = arrPlayNote[0];
384 var octaveModifier = arrPlayNote[1]|0;
385 fnPlayNote(note, __octave + octaveModifier);
386 } else {
387 return false;
388 }
389
390 }
391
392 // Remove key bindings once note is done.
393
394 function calcMidiNoteInfo() {
395 if (mediaStartPlayTime != null) {
396 var playedNoteEndTime = Date.now();
397 var relativePlayedNoteStartTime = playedNoteStartTime - mediaStartPlayTime;
398 var relativePlayedNoteEndTime = playedNoteEndTime - mediaStartPlayTime;
399 playedDuration = (relativePlayedNoteEndTime - relativePlayedNoteStartTime);
400
401
402 // console.log("**** " + playedNotePitch + " (midi pitch = " + playedMidiPitch+ ") startTime = ", relativePlayStartTime + ", duration = " + playedDuration);
403
404 mediaPlayedNotes[String(mediaStartPlayTime)].push(
405 { 'midiPitch' : playedMidiPitch,
406 'midiNoteOn' : relativePlayedNoteStartTime,
407 'midiNoteOff' : relativePlayedNoteEndTime,
408 'startCurrentTime' : playedNoteStartCurrentTime,
409 'duration' : playedDuration,
410 'startPercTime': playedNoteStartCurrentTime/mediaPlayer.duration,
411 'humanReadble' : playedNotePitch }
412 );
413
414 }
415 }
416
417 var fnRemoveKeyBinding = function(e) {
418
419 //console.log("**** key up at: " + Date.now())
420 // console.log("*** event e = " + JSON.stringify(e));
421
422
423 var i = keysPressed.length;
424 while(i--) {
425 if(keysPressed[i]==e.keyCode) {
426 if(visualKeyboard[keyboard[e.keyCode]]) {
427
428 delayedMissingTheGroove(500);
429
430 MIDI.noteOff(0, playedMidiPitch, 0);
431 if (mediaPlaybackMode == "record") {
432 calcMidiNoteInfo();
433 }
434
435 visualKeyboard[keyboard[e.keyCode]].style.backgroundColor = '';
436 visualKeyboard[keyboard[e.keyCode]].style.marginTop = '';
437 visualKeyboard[keyboard[e.keyCode]].style.boxShadow = '';
438 }
439 keysPressed.splice(i, 1);
440 }
441 }
442
443 }
444
445 var fnPlaySong = function(arr) {
446
447 if(arr.length>0) {
448
449 var noteLen = 1000*(1/parseInt(arr[0][1]));
450 if(!(arr[0][0] instanceof Array)) {
451 arr[0][0] = [arr[0][0]];
452 }
453 var i = arr[0][0].length;
454 var keys = [];
455 while(i--) {
456 keys.unshift(reverseLookup[arr[0][0][i]]);
457 console.log("**** In fnPlaySong(), away to call fnPlayKeyboard()");
458 fnPlayKeyboard({keyCode:keys[0]});
459 }
460 arr.shift();
461 setTimeout(function(array, val){ return function() { var i = val.length; while(i--) { fnRemoveKeyBinding({keyCode:val[i]}); } fnPlaySong(array); } }(arr, keys), noteLen);
462
463 }
464
465 };
466
467 var fnDrumKeyboard = function(e) {
468 //console.log("*** drum pressed: " + e.key);
469 if (e.key == "C") {
470 $('#drum1').trigger("mousedown");
471 }
472 else if (e.key == "V") {
473 $('#drum2').trigger("mousedown");
474 }
475 else if (e.key == "B") {
476 $('#drum3').trigger("mousedown");
477 }
478 else if (e.key == "N") {
479 $('#drum4').trigger("mousedown");
480 }
481 else if (e.key == "M") {
482 $('#drum5').trigger("mousedown");
483 }
484
485 e.preventDefault();
486 return false;
487 }
488
489
490 // Set up global event listeners
491
492 window.addEventListener('keydown', fnPlayKeyboard);
493 window.addEventListener('keyup', fnRemoveKeyBinding);
494 document.getElementById('-_OCTAVE').addEventListener('click', function() { fnChangeOctave(-1); });
495 document.getElementById('+_OCTAVE').addEventListener('click', function() { fnChangeOctave(1); });
496
497 Object.defineProperty(this, 'draw', {
498 value: fnCreateKeyboard
499 });
500
501}
Note: See TracBrowser for help on using the repository browser.