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

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

Next round of development. Fixed up clicking on text over drum images. Added guitar chords. Change that might help Safari web audio

File size: 10.1 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() { 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}
Note: See TracBrowser for help on using the repository browser.