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

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

Next round of improvements. More open-close bars. Drums with better images. Drums played on mousedown + key mappings. Playbar layout changed. Timing info when playing added.

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