X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F07_audio%2F01_XWebAudio.js;h=8a5e1eda629ba8d52f911d3118b77a59d0518bd8;hb=d836e6243878426d4cfd7a14ceb9b77db9f92b57;hp=abd163bcf86431d80ba4d4c53248fdccb9d069c0;hpb=b8b45541169da1fad8e5454c266f837ea3c52961;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 abd163b..8a5e1ed 100644 --- a/0.6.x/js/07_audio/01_XWebAudio.js +++ b/0.6.x/js/07_audio/01_XWebAudio.js @@ -1,6 +1,6 @@ -var X_Audio_WebAudio_context = !X_UA.iPhone_4s && !X_UA.iPad_2Mini1 && !X_UA.iPod_4 && - !( X_UA.Gecko && X_UA.Android ) && +var X_Audio_WebAudio_context = !X_UA[ 'iPhone_4s' ] && !X_UA[ 'iPad_2Mini1' ] && !X_UA[ 'iPod_4' ] && + !( X_UA[ 'Gecko' ] && X_UA[ 'Android' ] ) && ( window.AudioContext || window.webkitAudioContext ), X_Audio_WebAudioWrapper; @@ -18,9 +18,9 @@ if( X_Audio_WebAudio_context ){ }; }; - X_Audio_WebAudioWrapper = X.EventDispatcher.inherits( + X_Audio_WebAudioWrapper = X_EventDispatcher[ 'inherits' ]( 'X.AV.WebAudioWrapper', - X.Class.POOL_OBJECT, + X_Class.POOL_OBJECT, { url : '', @@ -32,7 +32,7 @@ if( X_Audio_WebAudio_context ){ loopEndTime : -1, seekTime : -1, duration : 0, - + playing : false, error : 0, loop : false, @@ -40,9 +40,9 @@ if( X_Audio_WebAudio_context ){ autoplay : false, volume : 0.5, + _startPos : 0, + _endPosition : 0, _startTime : 0, - _endTime : 0, - _playTime : 0, _timerID : 0, _interval : 0, buffer : null, @@ -57,9 +57,8 @@ if( X_Audio_WebAudio_context ){ Constructor : function( proxy, url, option ){ var audio = X_Audio_WebAudio_getBuffer( url ); - this.url = url; - this.closed = false; this.proxy = proxy; + this.url = url; X_AudioWrapper_updateStates( this, option ); @@ -68,23 +67,23 @@ if( X_Audio_WebAudio_context ){ } else if( audio ){ // TODO 当てにしていたaudioがclose 等した場合 - audio.proxy.listenOnce( 'canplaythrough', this, this._onBufferReady ); + audio.proxy[ 'listenOnce' ]( 'canplaythrough', this, this._onBufferReady ); } else { - this.xhr = X.Net.xhrGet( url, 'arraybuffer' ) - .listen( X.Event.PROGRESS, this ) - .listenOnce( [ X.Event.SUCCESS, X.Event.COMPLETE, X.Event.CANCELED ], this ); + this.xhr = X.Net.xhrGet( url, { type : 'arraybuffer' } ) + [ 'listen' ]( X_EVENT_PROGRESS, this ) + [ 'listenOnce' ]( [ X_EVENT_SUCCESS, X_EVENT_COMPLETE, X_EVENT_CANCELED ], this ); }; }, handleEvent : function( e ){ switch( e.type ){ - case X.Event.PROGRESS : - e.percent ? - this.proxy.dispatch( { type : 'progress', percent : e.percent } ) : - this.proxy.dispatch( 'loadstart' ); + case X_EVENT_PROGRESS : + e[ 'percent' ] ? + this.proxy[ 'dispatch' ]( { type : 'progress', 'percent' : e[ 'percent' ] } ) : + this.proxy[ 'dispatch' ]( 'loadstart' ); return; - case X.Event.SUCCESS : + case X_EVENT_SUCCESS : console.log( 'WebAudio xhr success! ' + !!X_Audio_WebAudio_context.decodeAudioData + ' t:' + typeof e.data ); // TODO 旧api // https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext @@ -92,7 +91,8 @@ if( X_Audio_WebAudio_context ){ // http://qiita.com/sou/items/5688d4e7d3a37b4e2ff1 // iOS 7.1 で decodeAudioData に処理が入った瞬間にスクリーンを長押しする(スクロールを繰り返す)と // decoeAudioData の処理がキャンセルされることがある(エラーやコールバックの発火もなく、ただ処理が消滅する)。 - if( X_Audio_WebAudio_context.createBuffer && X_UA.iOS ){ + // ただし iOS 8.1.2 では エラーになる + if( X_Audio_WebAudio_context.createBuffer && X_UA[ 'iOS' ] < 8 ){ this._onDecodeSuccess( X_Audio_WebAudio_context.createBuffer( e.data, false ) ); } else if( X_Audio_WebAudio_context.decodeAudioData ){ @@ -104,17 +104,17 @@ if( X_Audio_WebAudio_context ){ }; break; - case X.Event.CANCELED : + case X_EVENT_CANCELED : this.error = 1; - this.proxy.dispatch( 'aborted' ); + this.proxy[ 'dispatch' ]( 'aborted' ); break; - case X.Event.COMPLETE : + case X_EVENT_COMPLETE : this.error = 2; - this.proxy.asyncDispatch( { type : 'error', message : 'xhr error' } ); + this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'xhr error' } ); break; }; - this.xhr.unlisten( [ X.Event.PROGRESS, X.Event.SUCCESS, X.Event.COMPLETE, X.Event.CANCELED ], this ); + this.xhr[ 'unlisten' ]( [ X_EVENT_PROGRESS, X_EVENT_SUCCESS, X_EVENT_COMPLETE, X_EVENT_CANCELED ], this ); delete this.xhr; }, @@ -124,19 +124,21 @@ if( X_Audio_WebAudio_context ){ this.onDecodeSuccess && this._onDecodeComplete(); if ( !buffer ) { - this.proxy.asyncDispatch( { type : 'error', message : 'buffer is ' + buffer } ); + this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'buffer is ' + buffer } ); return; }; this.buffer = buffer; this.duration = buffer.duration * 1000; + /* + this.proxy[ 'asyncDispatch' ]( 'loadedmetadata' ); + this.proxy[ 'asyncDispatch' ]( 'loadeddata' ); + this.proxy[ 'asyncDispatch' ]( 'canplay' ); + this.proxy[ 'asyncDispatch' ]( 'canplaythrough' ); + */ + this.proxy[ 'asyncDispatch' ]( X_EVENT_READY ); - this.proxy.asyncDispatch( 'loadedmetadata' ); - this.proxy.asyncDispatch( 'loadeddata' ); - this.proxy.asyncDispatch( 'canplay' ); - this.proxy.asyncDispatch( 'canplaythrough' ); - - this.autoplay && X.Timer.once( 16, this, this.play ); + this.autoplay && X_Timer_once( 16, this, this.play ); console.log( 'WebAudio decoded!' ); }, @@ -145,7 +147,7 @@ if( X_Audio_WebAudio_context ){ console.log( 'WebAudio decode error!' ); this._onDecodeComplete(); this.error = 3; - this.proxy.asyncDispatch( { type : 'error', message : 'decode error' } ); + this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'decode error' } ); }, _onDecodeComplete : function(){ @@ -208,13 +210,13 @@ if( X_Audio_WebAudio_context ){ this.gainNode.gain.value = this.volume; // おかしい、stop 前に外していても呼ばれる、、、@Firefox33.1 - // 破棄された X.Callback が呼ばれて、obj._() でエラーになる。Firefox では、onended は使わない + // 破棄された X.Callback が呼ばれて、obj.proxy() でエラーになる。Firefox では、onended は使わない if( false && this.source.onended !== undefined ){ //console.log( '> use onended' ); this.source.onended = this._onended || ( this._onended = X_Callback_create( this, this._onEnded ) ); } else { - this._timerID && X.Timer.remove( this._timerID ); - this._timerID = X.Timer.once( end - begin, this, this._onEnded ); + this._timerID && X_Timer_remove( this._timerID ); + this._timerID = X_Timer_once( end - begin, this, this._onEnded ); }; if( this.source.start ){ @@ -223,11 +225,11 @@ if( X_Audio_WebAudio_context ){ this.source.noteGrainOn( 0, begin / 1000, end / 1000 ); }; - this.playing = true; - this._startTime = begin; - this._endTime = end; - this._playTime = X_Audio_WebAudio_context.currentTime * 1000; - this._interval = this._interval || X.Timer.add( 1000, 0, this, this._onInterval ); + this.playing = true; + this._startPos = begin; + this._endPosition = end; + this._startTime = X_Audio_WebAudio_context.currentTime * 1000; + this._interval = this._interval || X_Timer_add( 1000, 0, this, this._onInterval ); }, _onInterval : function(){ @@ -235,7 +237,7 @@ if( X_Audio_WebAudio_context ){ delete this._interval; return X_Callback_UN_LISTEN; }; - this.proxy.dispatch( 'timeupdate' ); + this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING ); }, _onEnded : function(){ @@ -243,27 +245,29 @@ if( X_Audio_WebAudio_context ){ delete this._timerID; if( this.playing ){ - time = X_Audio_WebAudio_context.currentTime * 1000 - this._playTime - this._endTime + this._startTime | 0; - //console.log( '> onEnd ' + ( this.playing && ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) ) + ' < ' + ( this._endTime - this._startTime ) ); + time = X_Audio_WebAudio_context.currentTime * 1000 - this._startTime - this._endPosition + this._startPos | 0; + //console.log( '> onEnd ' + ( this.playing && ( X_Audio_WebAudio_context.currentTime * 1000 - this._startTime ) ) + ' < ' + ( this._endPosition - this._startPos ) ); if( this._onended ){ // Firefox 用の対策,,, if( time < 0 ) return; } else { if( time < 0 ){ - console.log( '> onEnd ' + ( -time ) + ' start:' + this._startTime + '-' + this._endTime ); - this._timerID = X.Timer.once( -time, this, this._onEnded ); + console.log( '> onEnd crt:' + ( X_Audio_WebAudio_context.currentTime * 1000 ) + ' startTime:' + this._startTime + + ' from:' + this._startPos + ' to:' + this._endPosition ); + this._timerID = X_Timer_once( -time, this, this._onEnded ); return; }; }; if( this.loop ){ - if( !( this.proxy.dispatch( 'looped' ) & X.Callback.PREVENT_DEFAULT ) ){ + if( !( this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){ this.looped = true; + this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED ); this.play(); - }; + }; } else { this.pause(); - this.proxy.dispatch( 'ended' ); + this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED ); }; }; }, @@ -275,7 +279,7 @@ if( X_Audio_WebAudio_context ){ this.seekTime = this.state().currentTime; - this._timerID && X.Timer.remove( this._timerID ); + this._timerID && X_Timer_remove( this._timerID ); delete this._timerID; delete this.playing; @@ -302,7 +306,7 @@ if( X_Audio_WebAudio_context ){ playing : this.playing, duration : this.duration, - currentTime : this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime + this._startTime | 0 ) : this.seekTime, + currentTime : this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._startTime + this._startPos | 0 ) : this.seekTime, error : this.error }; }; @@ -319,18 +323,26 @@ if( X_Audio_WebAudio_context ){ } ); - + + /* + * 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', // detect : function( proxy, source, ext ){ - proxy.asyncDispatch( X_Audio_codecs[ ext ] ? 'support' : 'nosupport' ); + proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : X_Audio_codecs[ ext ] } ); }, klass : X_Audio_WebAudioWrapper } ); - };