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 ),
+ // Firefox40.0.5 + Windows8 で音声が鳴らない
+ !( X_UA[ 'Gecko' ] === 40 && X_UA[ 'Windows' ] ) &&
+ ( window[ 'AudioContext' ] || window[ 'webkitAudioContext' ] ),
X_Audio_BUFFER_LIST = [],
X_Audio_WebAudioWrapper,
- X_Audio_BufferLoader;
+ X_Audio_BufferLoader,
+ X_Audio_fpsFix;
/*
* iPhone 4s 以下、iPad2以下、iPad mini 1以下, iPod touch 4G 以下は不可
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 : '',
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 );
},
// 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;
},
_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;
},
X_Audio_WebAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ](
- 'X.AV.WebAudioWrapper',
+ 'X.WebAudio',
X_Class.POOL_OBJECT,
{
_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 ){
delete this.buffer;
- this.playing && this.actualPause();
- this.source && this._sourceDispose();
+ this.playing && this.actualPause();
+ this.bufferSource && this._sourceDispose();
- this._onended && X_Callback_correct( this._onended );
+ this._onended && X_Closure_correct( this._onended );
- this.gainNode && this.gainNode.disconnect();
+ this.gainNode && this.gainNode.disconnect();
},
_onLoadBufferComplete : function( e ){
var loader = this.loader,
this.target[ 'asyncDispatch' ]( X_EVENT_READY );
+ console.log( 'WebAudio buffer ready' );
+
this.autoplay && X_Timer_once( 16, this, this.play );
},
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;
},
_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 );
},
if( time < 0 ) return;
} else {
if( time < 0 ){
- console.log( '> onEnd crt:' + ( X_Audio_WebAudio_context.currentTime * 1000 ) + ' startTime:' + this._startTime +
- ' from:' + this._startPos + ' to:' + this._endPosition );
+ //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.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();
},
actualPause : function(){
- if( !this.playing ) return this;
+ //if( !this.playing ) return this;
console.log( '[WebAudio] pause' );
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 );
};
},
this.actualPlay();
} else
if( result & 4 ){
- this.gainNode.gain.value = this.gain;
+ this.gainNode[ 'gain' ].value = this.gain;
};
}
}
);
- /*
- * 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',