JavaScriptã§ãªã¢ã«ã¿ã¤ã ã«é³ãé³´ããæ¹æ³ã3ã¤ã»ã©
æè¿ãJavaScriptã§åãMMLã·ã¼ã±ã³ãµã¼ãä½ã£ã¦ãã¾ãã
SiONã®MMLã
ãã®ãããã®MMLã¯çµæ§è´ãã(é©å®ã³ãããã¦ã試ããã ãã)
- http://mmltalks.appspot.com/m/RaKRP
- http://mmltalks.appspot.com/m/DIZpo
- http://mmltalks.appspot.com/m/jVIoz (FMé³æºãæãã)
ã§ã
ã§ã表é¡ã®ä»¶ã§ããJavaScriptã§ãªã¢ã«ã¿ã¤ã ã«é³ãé³´ããã«ã¯ä»¥ä¸ã®3ã¤ã®æ¹æ³ãããã¾ãã
- Audio Data APIã使ã(Firefox 5.0以é)
- Web Audio APIã使ã(Chrome, Safari)
- wavãã¼ã¿ãç´æ¥æ¾ãè¾¼ã(ä¸è¨ã«å ãã¦Operaãåã)
ç°¡åãªãµã³ãã«ãããã¦ãã¾ã â サンプル ソース
*Operaã§ã¯ä½æ
ãåãã¾ããã(MMLã·ã¼ã±ã³ãµã¼ã®æ¹ã¯ä½æ
ãåã)
2011/09/18 追è¨
OSX Lion Chrome 14㧠Web Audio API ãã¯ã©ãã·ã¥ããåé¡ã解決ããã¦ããã¿ããã§ãã
(OSXçGoogle Chrome 14.0.835.163ã§ç¢ºèª)
注æ
ç¾ç¶ãOSX Lion ã® Chrome ã使ã£ã¦ãã人㯠Web Audio API ã使ãã¨ã¿ããã¯ã©ãã·ã¥ãã¾ãã
æºå
ã¨ããããé³´ããé³ãä½ãããã®ã¯ã©ã¹ãæ¬è³ªçãªé¨åã§ã¯ãªãã®ã§ããªãé©å½ãè¦ã¯2ã¤ã®ãµã¤ã³æ³¢ãé©å½ã«ããã¯ã¹ããã¦é³´ãããµã³ãã«ã§ããããé³´ãã®ã¯ããã®ä½ããããºã¤ããã
var samplerate = 48000, channel = 1, stream_length = 4096; var sinwave = function(frequency) { this.phase = 0.0; this.phaseStep = frequency / samplerate; }; sinwave.prototype.next = function() { var retval = Math.sin(2 * Math.PI * this.phase); this.phase += this.phaseStep; return retval; }; var StreamGenerator = function() { this.gen1 = new sinwave(440); this.gen2 = new sinwave(660); this.phase = new sinwave(880); }; StreamGenerator.prototype.next = function() { var stream = []; var i, imax; var g1 = this.gen1, g2 = this.gen2; var v1 = this.phase.next() / 2.0 + 0.5, v2 = 1.0 - v1; for (i = 0, imax = stream_length; i < imax; i++) { stream[i] = (g1.next() * v1 + g2.next() * v2); } return stream; };
Audio Data APIã使ã
対å¿ãã©ã¦ã¶: Firefox 5.0以é
ä»æ§çã«ã¤ãã¦ã¯ãã¡ãâ https://wiki.mozilla.org/Audio_Data_API
ã»ããã¢ãã
Audio ã new ã㦠mozSetup ã§ãã£ãã«æ°ã¨ãµã³ããªã³ã°å¨æ³¢æ°ãæå®ããã
audio = new Audio();
audio.mozSetup(channel, samplerate);
åç
mozWriteAudio ã« Float32Array ãçªã£è¾¼ãã§å¼ã³åºãã
mozWriteAudioã¯ãã³ãããã¯ãªã¡ã½ãããªã®ã§ãã¹ããªã¼ã åçã¿ãããªç¨éã®å ´å㯠setInterval ã§å®æçã«å¼ã³åºãã¦ã¯streamãä¸ãã¦ããã
stream = new Float32Array();
audio.mozWriteAudio(stream);
ã¹ãã¬ãªã®ãã¼ã¿ã¯ãstreamé åã«äº¤äºã«ãã¼ã¿ãå ¥ããã
stream = new Float32Array([L, R, L, R, L, R, .... ]);
ãããã¤ã©ã¤ã©ãã
setIntervalã§å¼ã³åºãã¾ããã®ã¯é¢åãããã®ã§ã©ãã«ããã¦ã»ããã
ç°¡åãªä¾
æºåã§ä½ã£ãã¸ã§ãã¬ã¼ã¿ãã¤ãã£ã¦åçããããã®ã¯ã©ã¹ã
/** * Audio Data APIç¨ã®ãã¬ã¤ã¤ã¼ (Firefox 5.0-) */ var MozPlayer = function() { this.audio = new Audio(); this.audio.mozSetup(channel, samplerate); this.timerId = null; this.isPlaying = false; }; MozPlayer.prototype.play = function(gen) { var self = this; if (this.timerId === null) { this.timerId = setInterval(function() { var s = new Float32Array(gen.next()); self.audio.mozWriteAudio(s); }, stream_length / samplerate * 1000); } this.isPlaying = true; }; MozPlayer.prototype.stop = function() { if (this.timerId !== null) { clearInterval(this.timerId); } this.isPlaying = false; };
Web Audio APIã使ã
対å¿ãã©ã¦ã¶: Chrome, Safari (ãã ãã©ã¡ããOSXçã§ã¯ç¾ç¶ä½¿ããªã)
ä»æ§çã«ã¤ãã¦ã¯ãã¡ãâ https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
Chromeã®å ´åã¯ãã¢ãã¬ã¹ãã¼ã« about:flags ã¨å
¥åããå
ã§ãã¦ã§ããªã¼ãã£ãªãæå¹ã«ããã¨ä½¿ããããã«ãªãã
Safariã®å ´åã¯ãããç¨ã®Safariããã£ã¦ãã¦ã³ãã¼ãã§ãããã ãã©ãä»ã¯ã§ããªãããã®ãã¡ã§ããããã«ãªãã§ãããã
ã»ããã¢ãã
webkitAudioContext ã new ã㦠createJavaScriptNode ãå¼ã³åºãã
ãµã³ããªã³ã°å¨æ³¢æ°ã¯åºå®ã§ãstream_length ã¯ãã¾ã大ããå¤ã¯è¨å®ã§ããªãã¿ããã
(æºåã®ã¨ãã« samplerate = 48000, stream_length = 4096 ã«ãã¦ããã®ã¯ããã«åããã¦ãããã)
context = new webkitAudioContext();
node = context.createJavaScriptNode(stream_length, 1, channel);
console.log(context.sampleRate);
åç
node.connect(context.destination) ãå¼ã¶ã¨ã
åçã®ã¿ã¤ãã³ã°ã§ node.onaudioprocess ãå¼ã³åºãããã®ã§ããã®ä¸ã§ arguments[0].outputBuffer.getChannelData ãã¦
ããã¤ã«ãã¼ã¿ã代å
¥ãããonaudioprocess ã¯å®æçã«å¼ã³åºãããã®ã§æ¥½ãã³ã
node.onaudioprocess = function(event) { var data = event.outputBuffer.getChannelData(0); var s = gen.next(); var i = data.length; while (i--) data[i] = s[i]; }; node.connect(context.destination);
ç°¡åãªä¾
æºåã§ä½ã£ãã¸ã§ãã¬ã¼ã¿ãã¤ãã£ã¦åçããããã®ã¯ã©ã¹ã
/** * Web Audio APIç¨ã®ãã¬ã¤ã¤ã¼ (Chrome, Safari) */ var WebkitPlayer = function() { this.context = new webkitAudioContext(); this.node = this.context.createJavaScriptNode(stream_length, 1, channel); this.isPlaying = false; }; WebkitPlayer.prototype.play = function(gen) { var self = this; this.node.onaudioprocess = function(event) { var data = event.outputBuffer.getChannelData(0); var s = gen.next(); var i = data.length; while (i--) data[i] = s[i]; }; this.node.connect(this.context.destination); this.isPlaying = true; }; WebkitPlayer.prototype.stop = function() { this.node.disconnect(); this.isPlaying = false; };
wavãã¼ã¿ãç´æ¥æ¾ãè¾¼ã
対å¿ãã©ã¦ã¶: Chrome, Opera, Firefox(å¤ãªæãã«ãªã)
詳ããã¯ããã«æ¸ãã¦ããâ http://d.hatena.ne.jp/yanagia/20100323/1269334226
ã»ããã¢ãã
ã¨ãã«ãªã
åç
ãã®ããã http://www.kk.iij4u.or.jp/~kondo/wave/ ãåèã«WAVãã¡ã¤ã«ãããã¨åçãã¤ããã¼ã¿ãä½ãã
wav = btoa(wavheader + bytes); // ãããããå¦ç audio = new Audio("data:audio/wav;base64," + wav); audio.play();
Audio Data APIã¨åããsetIntervalãªãã§å®æçã«å¼ã³åºãã¦ããå¿
è¦ãããã
ã¹ãã¬ãªã®ãã¼ã¿ã¯ãAudio Data APIã¨åãããã«byteså
ã®ãã¼ã¿ã交äºã«ãªãããã«ããã
ãããã¤ã©ã¤ã©ãã
ããã£ã¦ã¹ããªã¼ã åçãããªãã¦ç´°ããwavãã¡ã¤ã«ã大éã«ã¤ãã£ã¦åçãã¦ããã ããªãã§éãããããåéåéã§ãããããªã£ããããããããããå°ãªãããããã«ãã¡ã¤ã«ãã¨ã®åçæéãé·ãããã¨ãã£ãããããå«ãã
ãã¨ãããã¯JavaScriptã®åé¡ãªãã ãã©ããã¨ãã°3ç§ãã¨ã«åçãããã¦
setInterval(func, 3 * 1000);
ã¨ããã¨ãé³ãåºå§ããã¾ã§ã«3ç§ãããã
func(); setInterval(func, 3 * 1000);
ã£ã¦ãã£ã¦ã¿ãã¨ãæåã¨äºåç®ã®åçã®ã¿ã¤ãã³ã°ãããã¦æ°æã¡æªãæãã«ãªãã
ããã¨ãOperaã§Float32Array使ããªãã®ã©ãã«ããªãã¾ããããã
ç°¡åãªä¾
æºåã§ä½ã£ãã¸ã§ãã¬ã¼ã¿ãã¤ãã£ã¦åçããããã®ã¯ã©ã¹ã
/** * Audioã¿ã°ã使ããã¬ã¤ã¤ã¼ (Chrome, Firefox, Opera) */ var HTML5AudioPlayer = function() { this.audio = null; this.timerId = null; this.isPlaying = false; }; HTML5AudioPlayer.prototype.wavheader = function(samples) { var waveBytes = samples * channel * 2, l1 = waveBytes - 8, l2 = l1 - 36, retval = String.fromCharCode( 0x52, 0x49, 0x46, 0x46, // 'RIFF' (l1 >> 0) & 0xFF, (l1 >> 8) & 0xFF, (l1 >> 16) & 0xFF, (l1 >> 24) & 0xFF, 0x57, 0x41, 0x56, 0x45, // 'WAVE' 0x66, 0x6D, 0x74, 0x20, // 'fmt ' 0x10, 0x00, 0x00, 0x00, // byte length 0x01, 0x00, // linear pcm channel, 0x00, // channel (samplerate >> 0) & 0xFF, (samplerate >> 8) & 0xFF, (samplerate >> 16) & 0xFF, (samplerate >> 24) & 0xFF, ((samplerate*channel*2) >> 0) & 0xFF, ((samplerate*channel*2) >> 8) & 0xFF, ((samplerate*channel*2) >> 16) & 0xFF, ((samplerate*channel*2) >> 24) & 0xFF, 0x04, 0x00, // block size 0x10, 0x00, // 16bit 0x64, 0x61, 0x74, 0x61, //'data' (l2 >> 0) & 0xFF, (l2 >> 8) & 0xFF, (l2 >> 16) & 0xFF, (l2 >> 24) & 0xFF); return retval; }; HTML5AudioPlayer.prototype.play = function(gen) { var self = this; var itercount = 20; if (this.timerId === null) { this.timerId = setInterval(function() { var bytes = [], s, x, wav; var i, imax, j, jmax; for (i = 0, imax = itercount; i < imax; i++) { s = gen.next(); for (j = 0, jmax = s.length; j < jmax; j++) { x = (s[j] * 32767.0) >> 0; bytes.push(String.fromCharCode(x & 0xFF, (x >> 8) & 0xFF)); } } wav = btoa(self.wavheader(bytes.length) + bytes.join('')); self.audio = new Audio("data:audio/wav;base64," + wav); self.audio.play(); }, stream_length * itercount / samplerate * 1000); } this.isPlaying = true; }; HTML5AudioPlayer.prototype.stop = function() { if (this.timerId !== null) { clearInterval(this.timerId); } if (this.audio !== null) { this.audio.pause(); this.audio = null; } this.isPlaying = false; };
ã¾ã¨ã
ã¡ãã£ã¨é³ãåºãããã ããªãã ãã©ãããæ¹ãããããããã®ã¯ã ããã®ã§ä¸ã¤ã«ãã¦ã»ããã§ããã
å㯠Web Audio API ã好ã¿ãªãã ãã©ãåè¿°ã®ã¨ãã OSX Lion ã§åããªãã®ã§ãé常ã«æ²ããã
Firefox | Chrome | Opera | Safari | IE9.0 | |
---|---|---|---|---|---|
Audio Data API | 使ãã | 使ããªã | 使ããªã | 使ããªã | 使ããªã |
Web Audio API | 使ããªã | 使ã(ãâã) | 使ããªã | 使ãã | 使ããªã |
HTML5 Audio | 使ãã | 使ãã | 使ãã | 使ããªãï¼ | ç¥ãã |