X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F07_audio%2F01_XWebAudio.js;h=f1bc2bd7bc6cddb20e18d7004777f4d8495f559f;hb=475df4df9670f042764a99c80d7716e994d28033;hp=66571d17b738b632f39d1038814b9fd7c56ed517;hpb=2612dc17dae6ba790807049d2587e8a2910007cc;p=pettanr%2FclientJs.git diff --git a/0.6.x/js/07_audio/01_XWebAudio.js b/0.6.x/js/07_audio/01_XWebAudio.js index 66571d1..f1bc2bd 100644 --- a/0.6.x/js/07_audio/01_XWebAudio.js +++ b/0.6.x/js/07_audio/01_XWebAudio.js @@ -1,9 +1,12 @@ var X_Audio_WebAudio_context = !X_UA[ 'iPhone_4s' ] && !X_UA[ 'iPad_2Mini1' ] && !X_UA[ 'iPod_4' ] && + // TODO なんで fennec を禁止? !( X_UA[ 'Gecko' ] && X_UA[ 'Android' ] ) && - ( window.AudioContext || window.webkitAudioContext ), + ( window[ 'AudioContext' ] || window[ 'webkitAudioContext' ] ), X_Audio_BUFFER_LIST = [], - X_Audio_WebAudioWrapper; + X_Audio_WebAudioWrapper, + X_Audio_BufferLoader, + X_Audio_fpsFix; /* * iPhone 4s 以下、iPad2以下、iPad mini 1以下, iPod touch 4G 以下は不可 @@ -13,7 +16,7 @@ if( X_Audio_WebAudio_context ){ X_Audio_WebAudio_context = new X_Audio_WebAudio_context; X_Audio_BufferLoader = X_EventDispatcher[ 'inherits' ]( - 'X.AV.WebAudioBufferLoader', + 'X.WebAudio.BufferLoader', X_Class.POOL_OBJECT, { url : '', @@ -25,10 +28,10 @@ if( X_Audio_WebAudio_context ){ error : 0, webAudioList : null, - Constructor : function( webAudio, url ){ + 'Constructor' : function( webAudio, url ){ this.webAudioList = [ webAudio ]; this.url = url; - this.xhr = X.Net( { 'xhr' : url, 'dataType' : 'arraybuffer' } ) + this.xhr = X[ 'Net' ]( { 'xhr' : url, 'dataType' : 'arraybuffer' } ) [ 'listen' ]( X_EVENT_PROGRESS, this ) [ 'listenOnce' ]( [ X_EVENT_SUCCESS, X_EVENT_COMPLETE ], this ); }, @@ -47,15 +50,13 @@ if( X_Audio_WebAudio_context ){ // iOS 7.1 で decodeAudioData に処理が入った瞬間にスクリーンを長押しする(スクロールを繰り返す)と // decoeAudioData の処理がキャンセルされることがある(エラーやコールバックの発火もなく、ただ処理が消滅する)。 // ただし iOS 8.1.2 では エラーになる - if( X_Audio_WebAudio_context.createBuffer && X_UA[ 'iOS' ] < 8 ){ - this._onDecodeSuccess( X_Audio_WebAudio_context.createBuffer( e.response, false ) ); + if( X_UA[ 'iOS' ] < 8 || !X_Audio_WebAudio_context[ 'decodeAudioData' ] ){ + this._onDecodeSuccess( X_Audio_WebAudio_context[ 'createBuffer' ]( e.response, false ) ); } else - if( X_Audio_WebAudio_context.decodeAudioData ){ - X_Audio_WebAudio_context.decodeAudioData( e.response, - this.onDecodeSuccess = X_Callback_create( this, this._onDecodeSuccess ), - this.onDecodeError = X_Callback_create( this, this._onDecodeError ) ); - } else { - this._onDecodeSuccess( X_Audio_WebAudio_context.createBuffer( e.response, false ) ); + if( X_Audio_WebAudio_context[ 'decodeAudioData' ] ){ + X_Audio_WebAudio_context[ 'decodeAudioData' ]( e.response, + this.onDecodeSuccess = X_Closure_create( this, this._onDecodeSuccess ), + this.onDecodeError = X_Closure_create( this, this._onDecodeError ) ); }; break; @@ -94,9 +95,9 @@ if( X_Audio_WebAudio_context ){ }, _onDecodeComplete : function(){ - X_Callback_correct( this.onDecodeSuccess ); + X_Closure_correct( this.onDecodeSuccess ); delete this.onDecodeSuccess; - X_Callback_correct( this.onDecodeError ); + X_Closure_correct( this.onDecodeError ); delete this.onDecodeError; }, @@ -117,7 +118,7 @@ if( X_Audio_WebAudio_context ){ X_Audio_WebAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ]( - 'X.AV.WebAudioWrapper', + 'X.WebAudio', X_Class.POOL_OBJECT, { @@ -129,15 +130,25 @@ if( X_Audio_WebAudio_context ){ _timerID : 0, _interval : 0, buffer : null, - source : null, + bufferSource : null, gainNode : null, _onended : null, - Constructor : function( target, url, option ){ + 'Constructor' : function( target, url, option ){ var i = 0, l = X_Audio_BUFFER_LIST.length, loader; + /* + * http://qiita.com/sou/items/5688d4e7d3a37b4e2ff1 + * L-01F 等の一部端末で Web Audio API の再生結果に特定条件下でノイズが混ざることがある。 + * 描画レート(描画 FPS)が下がるとノイズが混ざり始め、レートを上げると再生結果が正常になるというもので、オーディオ処理が描画スレッドに巻き込まれているような動作を見せる。 + */ + if( X_UA[ 'Android' ] && X_UA[ 'Chrome' ] && !X_Audio_fpsFix ){ + X_Node_systemNode.create( 'div', { id : 'fps-slowdown-make-sound-noisy' } ); + X_Audio_fpsFix = true; + }; + for( ; i < l; ++i ){ loader = X_Audio_BUFFER_LIST[ i ]; if( loader.url === url ){ @@ -155,7 +166,7 @@ if( X_Audio_WebAudio_context ){ this.setState( option ); - this[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE, X_WebAudio_handleEvent ); + this[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE, this.onKill ); if( loader.buffer || loader.error ){ this._onLoadBufferComplete(); @@ -163,6 +174,20 @@ if( X_Audio_WebAudio_context ){ loader[ 'listenOnce' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete ); }; }, + + onKill : function(){ + this.loader[ 'unlisten' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete ) + .unregister( this ); + + delete this.buffer; + + this.playing && this.actualPause(); + this.bufferSource && this._sourceDispose(); + + this._onended && X_Closure_correct( this._onended ); + + this.gainNode && this.gainNode.disconnect(); + }, _onLoadBufferComplete : function( e ){ var loader = this.loader, buffer = loader.buffer; @@ -188,6 +213,8 @@ if( X_Audio_WebAudio_context ){ this.target[ 'asyncDispatch' ]( X_EVENT_READY ); + console.log( 'WebAudio buffer ready' ); + this.autoplay && X_Timer_once( 16, this, this.play ); }, @@ -205,31 +232,31 @@ if( X_Audio_WebAudio_context ){ console.log( '[WebAudio] play ' + begin + ' -> ' + end ); - if( this.source ) this._sourceDispose(); + if( this.bufferSource ) this._sourceDispose(); if( !this.gainNode ){ - this.gainNode = X_Audio_WebAudio_context.createGain ? X_Audio_WebAudio_context.createGain() : X_Audio_WebAudio_context.createGainNode(); - this.gainNode.connect( X_Audio_WebAudio_context.destination ); + this.gainNode = X_Audio_WebAudio_context[ 'createGain' ] ? X_Audio_WebAudio_context[ 'createGain' ]() : X_Audio_WebAudio_context[ 'createGainNode' ](); + this.gainNode[ 'connect' ]( X_Audio_WebAudio_context[ 'destination' ] ); }; - this.source = X_Audio_WebAudio_context.createBufferSource(); - this.source.buffer = this.buffer; - this.source.connect( this.gainNode ); + this.bufferSource = X_Audio_WebAudio_context[ 'createBufferSource' ](); + this.bufferSource.buffer = this.buffer; + this.bufferSource[ 'connect' ]( this.gainNode ); - this.gainNode.gain.value = this.gain; + this.gainNode[ 'gain' ].value = this.gain; // おかしい、stop 前に外していても呼ばれる、、、@Firefox33.1 // 破棄された X.Callback が呼ばれて、obj.proxy() でエラーになる。Firefox では、onended は使わない - if( false && this.source.onended !== undefined ){ + if( false && this.bufferSource.onended !== undefined ){ //console.log( '> use onended' ); - this.source.onended = this._onended || ( this._onended = X_Callback_create( this, this._onEnded ) ); + this.bufferSource.onended = this._onended || ( this._onended = X_Closure_create( this, this._onEnded ) ); } else { this._timerID && X_Timer_remove( this._timerID ); this._timerID = X_Timer_once( end - begin, this, this._onEnded ); }; - if( this.source.start ){ - this.source.start( 0, begin / 1000, end / 1000 ); + if( this.bufferSource.start ){ + this.bufferSource.start( 0, begin / 1000, end / 1000 ); } else { - this.source.noteGrainOn( 0, begin / 1000, end / 1000 ); + this.bufferSource[ 'noteGrainOn' ]( 0, begin / 1000, end / 1000 ); }; this.playing = true; @@ -240,15 +267,15 @@ if( X_Audio_WebAudio_context ){ }, _sourceDispose : function(){ - this.source.disconnect(); - delete this.source.onended; - delete this.source; + this.bufferSource.disconnect(); + delete this.bufferSource.onended; + delete this.bufferSource; }, _onInterval : function(){ if( !this.playing ){ delete this._interval; - return X_Callback_UN_LISTEN; + return X_CALLBACK_UN_LISTEN; }; this.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING ); }, @@ -273,7 +300,7 @@ if( X_Audio_WebAudio_context ){ }; if( this.autoLoop ){ - if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){ + if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){ this.looped = true; this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED ); this.actualPlay(); @@ -296,11 +323,11 @@ if( X_Audio_WebAudio_context ){ delete this._timerID; delete this.playing; - if( this.source ){ - if( this.source.onended ) delete this.source.onended; + if( this.bufferSource ){ + if( this.bufferSource.onended ) delete this.bufferSource.onended; - this.source.stop ? - this.source.stop( 0 ) : this.source.noteOff( 0 ); + this.bufferSource.stop ? + this.bufferSource.stop( 0 ) : this.bufferSource[ 'noteOff' ]( 0 ); }; }, @@ -313,45 +340,19 @@ if( X_Audio_WebAudio_context ){ this.actualPlay(); } else if( result & 4 ){ - this.gainNode.gain.value = this.gain; + this.gainNode[ 'gain' ].value = this.gain; }; } } ); - function X_WebAudio_handleEvent( e ){ - switch( e.type ){ - - case X_EVENT_KILL_INSTANCE : - this.loader[ 'unlisten' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete ) - .unregister( this ); - - delete this.buffer; - - this.playing && this.actualPause(); - this.source && this._sourceDispose(); - - this._onended && X_Callback_correct( this._onended ); - - this.gainNode && this.gainNode.disconnect(); - break; - }; - }; - - /* - * http://qiita.com/sou/items/5688d4e7d3a37b4e2ff1 - * L-01F 等の一部端末で Web Audio API の再生結果に特定条件下でノイズが混ざることがある。 - * 描画レート(描画 FPS)が下がるとノイズが混ざり始め、レートを上げると再生結果が正常になるというもので、オーディオ処理が描画スレッドに巻き込まれているような動作を見せる。 - */ - if( X_UA[ 'Android' ] && X_UA[ 'Chrome' ] ){ - X_Node_systemNode.create( 'div', { id : 'fps-slowdown-make-sound-noisy' } ); - }; - X_Audio_BACKENDS.push( { backendName : 'Web Audio', + canPlay : {}, // TODO HTMLAudio と同じ + // detect : function( proxy, source, ext ){ proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : X_Audio_codecs[ ext ] } );