2 var X_Audio_WebAudio_context = window.webkitAudioContext || window.AudioContext,
3 X_Audio_WebAudio_LIVE_LIST = [],
4 X_Audio_WebAudio_POOL_LIST = [],
5 X_Audio_WebAudio, X_Audio_WebAudioWrapper, X_Audio_rawAudio;
7 if( X_Audio_WebAudio_context ){
9 X_Audio_WebAudio_context = new X_Audio_WebAudio_context;
11 function getWebAudioWrapper( proxy ){
12 var i = X_Audio_WebAudio_LIVE_LIST.length;
14 if( X_Audio_WebAudio_LIVE_LIST[ --i ].proxy === proxy ) return X_Audio_WebAudio_LIVE_LIST[ i ];
20 backendName : 'Web Audio',
22 detect : function( proxy, source, ext ){
23 var ok = ext === 'mp3' || ext === 'ogg';
25 proxy.asyncDispatch( ok ? 'support' : 'nosupport' );
28 register : function( proxy, source, option ){
29 X_Audio_WebAudio_LIVE_LIST.push( new X_Audio_WebAudioWrapper( proxy, source, option ) );
32 close : function( proxy ){
33 return getWebAudioWrapper( proxy ).close();
36 play : function( proxy, startTime, endTime, loop, loopStartTime ){
37 return getWebAudioWrapper( proxy ).play( startTime, endTime, loop, loopStartTime );
40 pause : function( proxy ){
41 return getWebAudioWrapper( proxy ).pause();
44 state : function( proxy, obj ){
45 return getWebAudioWrapper( proxy ).state( obj );
49 X_Audio_BACKENDS.push( X_Audio_WebAudio );
51 X_Audio_WebAudioWrapper = X.EventDispatcher.inherits(
52 'X.AV.WebAudioWrapper',
78 onDecodeSuccess : null,
81 Constructor : function( proxy, source, option ){
84 this.xhr = X.Net.xhrGet( source, 'arraybuffer' )
85 .listen( X.Event.PROGRESS, this )
86 .listenOnce( [ X.Event.SUCCESS, X.Event.COMPLETE, X.Event.CANCELED ], this );
87 X_AudioWrapper_updateStates( this, option );
90 handleEvent : function( e ){
92 case X.Event.PROGRESS :
94 this.proxy.dispatch( { type : 'progress', percent : e.percent } ) :
95 this.proxy.dispatch( 'loadstart' );
98 case X.Event.SUCCESS :
99 X_Audio_WebAudio_context.decodeAudioData( e.data,
100 this.callbackDecodeSuccess = X_Callback_create( this, this._onDecodeSuccess ),
101 this.callbackDecodeError = X_Callback_create( this, this._onDecodeError ) );
104 case X.Event.CANCELED :
106 this.proxy.dispatch( 'aborted' );
109 case X.Event.COMPLETE :
111 this.proxy.asyncDispatch( { type : 'error', message : 'xhr error' } );
114 this.xhr.unlisten( [ X.Event.PROGRESS, X.Event.SUCCESS, X.Event.COMPLETE, X.Event.CANCELED ], this );
118 _onDecodeSuccess : function( buffer ){
119 this._onDecodeComplete();
122 this.proxy.asyncDispatch( { type : 'error', message : 'buffer is ' + buffer } );
126 this.buffer = buffer;
127 this.duration = buffer.duration * 1000;
128 this.endTime = this.endTime || this.duration;
130 this.proxy.asyncDispatch( 'loadedmetadata' );
131 this.proxy.asyncDispatch( 'loadeddata' );
132 this.proxy.asyncDispatch( 'canplay' );
133 this.proxy.asyncDispatch( 'canplaythrough' );
136 _onDecodeError : function(){
137 this._onDecodeComplete();
139 this.proxy.asyncDispatch( { type : 'error', message : 'decode error' } );
142 _onDecodeComplete : function(){
143 X_Callback_correct( this.callbackDecodeSuccess );
144 delete this.callbackDecodeSuccess;
145 X_Callback_correct( this.callbackDecodeError );
146 delete this.callbackDecodeError;
152 this.playing && this.pause();
153 this.source && this._sourceDispose();
155 this._onended && X_Callback_correct( this._onended );
157 this.gainNode && this.gainNode.disconnect();
160 _sourceDispose : function(){
161 this.source && this.source.disconnect();
162 delete this.source.onended;
166 play : function( seekTime ){
169 if( !this.buffer ) return this;
171 begin = ( seekTime || seekTime === 0 ) ? seekTime : this.playing ? this.loopStartTime : this.startTime;
173 console.log( '[WebAudio] play' );
175 if( this.source ) this._sourceDispose();
176 if( !this.gainNode ) this.gainNode = X_Audio_WebAudio_context.createGain();
178 this.source = X_Audio_WebAudio_context.createBufferSource();
179 this.source.buffer = this.buffer;
180 this.source.connect( this.gainNode );
182 this.gainNode.connect( X_Audio_WebAudio_context.destination );
183 this.gainNode.gain.value = this.volume;
185 this._timerID && X.Timer.remove( this._timerID );
187 // おかしい、stop 前に外していても呼ばれる、、、@Firefox
188 if( this.source.onended !== undefined ){
189 //console.log( '> use onended' );
190 this.source.onended = this._onended || ( this._onended = X_Callback_create( this, this._onEnded ) );
192 this._timerID = X.Timer.once( this.endTime - begin, this, this._onEnded );
195 if( this.source.start ){
196 this.source.start( 0, begin / 1000, this.endTime / 1000 );
198 this.source.noteGrainOn( 0, begin / 1000, this.endTime / 1000 );
202 this._startTime = begin;
203 this._playTime = X_Audio_WebAudio_context.currentTime * 1000;
204 this._interval = this._interval || X.Timer.add( 1000, 0, this, this._onInterval );
207 _onInterval : function(){
209 delete this._interval;
210 return X_Callback_UN_LISTEN;
212 this.proxy.dispatch( 'timeupdate' );
215 _onEnded : function(){
216 delete this._timerID;
218 console.log( '> onEnd ' + ( this.playing && ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) ) + ' < ' + ( this.endTime - this._startTime ) );
220 if( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime < this.endTime - this._startTime ) return;
226 this.proxy.dispatch( 'ended' );
232 if( !this.playing ) return this;
234 console.log( '[WebAudio] pause' );
236 this._timerID && X.Timer.remove( this._timerID );
237 delete this._timerID;
241 if( this.source.onended ) delete this.source.onended;
244 this.source.stop( 0 ) : this.source.noteOff( 0 );
248 state : function( obj ){
249 var time = this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) : 0,
252 if( obj === undefined ){
254 startTime : this.startTime,
255 endTime : this.endTime,
256 loopStartTime : this.loopStartTime,
257 currentTime : time + this._startTime,
259 volume : this.volume,
261 playing : this.playing,
262 duration : this.duration
266 result = X_AudioWrapper_updateStates( this, obj );
268 if( result & 2 ){ // seek
269 this.play( this.seekTime );
270 delete this.seekTime;
276 this.gainNode.gain.value = this.volume;