};
};
- testStyle.cssText = 'background:rgba(0,0,0,0.5)';
- X_Node_CSS_Support[ 'rgba' ] = !!testStyle.background;
+ testStyle.cssText = 'background:rgba(0,0,0,0.5);border-color:transparent';
+ X_Node_CSS_Support[ 'rgba' ] = !!testStyle[ 'background' ];
+ X_Node_CSS_Support[ 'transparent' ] = !!testStyle[ 'borderColor' ];
+ // TODO border による三角形の可否
+ // 2:完全、 1:透過に非対応(IE7-) 0:borderの描画が非標準で三角形が作れない
/*
* chrome 1+, ff3.5(1.9.1), ie9+, opera10.5+, Safari3+(522)
* 孤立するとウィンドウ オブジェクトのプロパティが消去される\r
*/\r
\r
+// TODO Node.inherits\r
X.Util.NinjaIframe = X.EventDispatcher.inherits(\r
'NinjaIframe',\r
{\r
{\r
className : 'hidden-iframe',\r
scrolling : 'no',\r
+ allowtransparency : 'no', \r
frameborder : 0,\r
- allowtransparency : 'no',\r
+ tabindex : -1,\r
name : this._name,\r
id : this._name\r
}\r
// こちらに名前をsetしないとtargetが動作しない\r
this._iwin.name = this._name;\r
\r
- this.xnodeIframe.listen( X_UA.IE < 9 ? [ 'readystatechange', 'error' ] : [ 'load', 'error' ], this );\r
+ this.xnodeIframe.listen( [ X_UA.IE < 9 ? 'readystatechange' : 'load', 'error' ], this );\r
\r
if( !( X_UA.IE < 9 ) ){\r
this._html && X_Util_NinjaIframe_writeToIframe( this );\r
if( !this._ready ){\r
this._html && X_Util_NinjaIframe_writeToIframe( this );\r
this._ready = true;\r
- return; \r
+ return;\r
};\r
// onload\r
case 'load' :\r
};\r
\r
/*\r
+ * Opacity不可 NetFront3.4\r
+ */\r
+\r
+/*\r
* original\r
* LICENSE: MIT\r
* AUTHOR: uupaa.js@gmail.com\r
new ActiveXObject( 'Msxml2.XMLHTTP.4.0' ); // バージョン4.0 は bugfix が行われないので、3.0 か 6.0 を指定すべき\r
new ActiveXObject( 'Msxml2.XMLHTTP.5.0' ); // バージョン5.0 は bugfix が行われないので、3.0 か 6.0 を指定すべき\r
\r
+[IE][Javascript][Json] IE+JsonではまったAdd Star\r
+http://d.hatena.ne.jp/khiker/20081026/javascript_json\r
+> AddCharset utf-8 json\r
+> AddType text/javascript json\r
+\r
+JavaScriptでJSONをeval\r
+http://d.hatena.ne.jp/sshi/20060904/p1\r
*/\r
var X_Net_XHR_W3C = ( !X_UA.IE7 || !X_URL_IS_LOCAL ) && window[ 'XMLHttpRequest' ] && new XMLHttpRequest(),\r
X_Net_XHR_X_DOMAIN = window[ 'XDomainRequest' ] && new XDomainRequest(),\r
};\r
\r
if( raw.timeout !== undefined ){\r
- raw.timeout = timeout; //Firefox33 でエラー,,,\r
+ raw.timeout = timeout;\r
} else {\r
this._timerID = X.Timer.once( timeout, this, this.onTimeout );\r
}; \r
break;\r
case 'json' :\r
case 'moz-json' :\r
- data = raw[ 'response' ] || raw[ 'responseText' ]; // とりあえず\r
+ data = raw[ 'response' ] ||\r
+ ( JSON ? JSON.parse( raw[ 'responseText' ] ) : eval( '(' + raw[ 'responseText' ] + ')' ) ); // http://d.hatena.ne.jp/sshi/20060904/p1\r
break;\r
case 'document' :\r
case 'xml' :\r
case 'html' :\r
case 'htm' :\r
+ // svg, vml, xaml\r
data = raw[ 'responseXML' ];\r
break;\r
case 'blob' :\r
X_NET_XHRWrapper.listen( [ 'load', 'readystatechange', 'error', 'timeout' ] ); //, 'abort'\r
};\r
\r
- if( X.Net.XHR.UL_PROGRESS ){\r
- X_NET_XHRWrapper._rawObject.upload.addEventListener( 'progress', X.Net.XHR.xhr.onUploadProgress );\r
+ if( X_NET_XHRWrapper.onUploadProgress ){\r
+ X_NET_XHRWrapper._rawObject.upload.addEventListener( 'progress', X_NET_XHRWrapper.onUploadProgress );\r
};\r
};\r
\r
return X.Callback.UN_LISTEN;
};
-
+// TODO extend NinjaIframe
X_NET_JSONPWrapper = X_Class_override(
new X.EventDispatcher(),
{
}\r
};\r
\r
-var X_Audio_BACKENDS = [];\r
+var X_Audio_BACKENDS = [],\r
+ X_Audio_WRAPPER_LIST = [];\r
\r
/*\r
* TODO preplayerror play してみたら error が出た、backend の変更。\r
};\r
};\r
\r
+function X_AudioProxy_getAudioWrapper( proxy ){\r
+ var i = X_Audio_WRAPPER_LIST.length;\r
+ for( ; i; ){\r
+ if( X_Audio_WRAPPER_LIST[ --i ].proxy === proxy ) return X_Audio_WRAPPER_LIST[ i ];\r
+ };\r
+};\r
\r
var X_AudioProxy = X.EventDispatcher.inherits(\r
'X.AV.AudioProxy',\r
},\r
\r
close : function(){\r
- return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].close( this );\r
+ return this._backend !== -1 && X_AudioProxy_getAudioWrapper( this ).close();\r
},\r
\r
- play : function( startTime, endTime, loop, loopStartTime ){\r
+ play : function( startTime, endTime, loop, loopStartTime, loopEndTime ){\r
var state, duration;\r
- if( startTime ){\r
+ if( 0 <= startTime ){\r
this.state( {\r
+ currentTime : startTime,\r
startTime : startTime,\r
endTime : endTime,\r
loop : loop,\r
- loopStartTime : loopStartTime\r
+ loopStartTime : loopStartTime,\r
+ loopEndTime : loopEndTime\r
} );\r
};\r
- this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].play( this );\r
+ this._backend !== -1 && X_AudioProxy_getAudioWrapper( this ).play();\r
return this;\r
},\r
\r
seek : function( seekTime ){\r
- var state = this.state();\r
- if( state.playing ){\r
- seekTime < state.endTime && this.state( { currentTime : seekTime } );\r
- } else {\r
- this.state( { startTime : seekTime } );\r
+ var state = this.state(),\r
+ end = X_AudioWrapper_getEndTime( X_AudioProxy_getAudioWrapper( this ) );\r
+ if( seekTime < end ){\r
+ this.state( { currentTime : seekTime } );\r
};\r
return this;\r
},\r
\r
pause : function(){\r
- this.state().playing && X_Audio_BACKENDS[ this._backend ].pause( this );\r
+ this.state().playing && X_AudioProxy_getAudioWrapper( this ).pause();\r
return this;\r
},\r
\r
state : function( obj ){\r
- var backend = this._backend !== -1 && X_Audio_BACKENDS[ this._backend ];\r
+ var backend = this._backend !== -1 && X_AudioProxy_getAudioWrapper( this );\r
\r
if( obj === undefined ){\r
return backend ?\r
- backend.state( this ) :\r
+ backend.state() :\r
{\r
startTime : -1,\r
endTime : -1,\r
loopStartTime : -1,\r
+ loopEndTime : -1,\r
currentTime : -1,\r
loop : false,\r
- loaded : false,\r
+ looded : false,\r
error : false,\r
playing : false,\r
\r
duration : 0\r
};\r
};\r
- backend && backend.state( this, obj );\r
+ backend && backend.state( obj );\r
return this;\r
}, \r
\r
loop : function( v ){\r
- var backend = this._backend !== -1 && X_Audio_BACKENDS[ this._backend ];\r
+ var backend = this._backend !== -1 && X_AudioProxy_getAudioWrapper( this );\r
if( v === undefined ){\r
- return backend && backend.state( this ).loop;\r
+ return backend && backend.state().loop;\r
};\r
- backend && backend.state( this, { loop : v } );\r
+ backend && backend.state( { loop : v } );\r
return this;\r
},\r
\r
volume : function( v ){\r
- var backend = this._backend !== -1 && X_Audio_BACKENDS[ this._backend ];\r
+ var backend = this._backend !== -1 && X_AudioProxy_getAudioWrapper( this );\r
if( v === undefined ){\r
- return backend && backend.state( this ).volume;\r
+ return backend && backend.state().volume;\r
};\r
- backend && backend.state( this, { volume : v } );\r
+ backend && backend.state( { volume : v } );\r
return this;\r
},\r
\r
currentTime : function( v ){\r
- var backend = this._backend !== -1 && X_Audio_BACKENDS[ this._backend ];\r
+ var backend = this._backend !== -1 && X_AudioProxy_getAudioWrapper( this );\r
if( v === undefined ){\r
- return backend && backend.state( this ).currentTime;\r
+ return backend && backend.state().currentTime;\r
};\r
- backend && backend.state( this, { currentTime : v } );\r
+ backend && backend.state( { currentTime : v } );\r
return this;\r
},\r
\r
isPlaying : function(){\r
- return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].state( this ).playing;\r
+ return this._backend !== -1 && X_AudioProxy_getAudioWrapper( this ).state().playing;\r
}\r
\r
}\r
this.unlisten( 'nobackend', X_AudioProxy_handleEvent );\r
this.source = e.source;\r
this.backendName = X_Audio_BACKENDS[ this._backend ].backendName;\r
- X_Audio_BACKENDS[ this._backend ].register( this, e.source, e.option );\r
+ X_Audio_WRAPPER_LIST.push( new X_Audio_BACKENDS[ this._backend ].klass( this, e.source, e.option ) );\r
break;\r
\r
case 'nobackend' :\r
audioWrapper.seekTime = v;\r
seek = 2;\r
};\r
- break;\r
- }; \r
+ } else {\r
+ audioWrapper.seekTime = v;\r
+ };\r
} else {\r
continue;\r
};\r
- k = 'startTime';\r
+ break;\r
\r
case 'startTime' :\r
case 'endTime' :\r
case 'loopStartTime' :\r
+ case 'loopEndTime' :\r
v = X_AudioWrapper_timeStringToNumber( v );\r
- if( X.Type.isNumber( v ) && audioWrapper[ k ] !== v ){\r
- audioWrapper[ k ] = v;\r
- // 再生中の endTime, currentTime の変更\r
- if( playing && k === 'endTime' ) end = 1;\r
+ console.log( k + ' ' + v );\r
+ if( v || v === 0 ){\r
+ if( audioWrapper[ k ] !== v ){\r
+ audioWrapper[ k ] = v;\r
+ \r
+ // 再生中の endTime の変更\r
+ if( playing && ( k === 'endTime' || k === 'loopEndTime' ) ) end = 1; \r
+ };\r
+ } else {\r
+ delete audioWrapper[ k ];\r
+ if( playing && ( k === 'endTime' || k === 'loopEndTime' ) ) end = 1;\r
};\r
break;\r
\r
case 'loop' :\r
+ case 'looped' :\r
if( X.Type.isBoolean( v ) && audioWrapper[ k ] !== v ){\r
audioWrapper[ k ] = v;\r
};\r
};\r
};\r
\r
- if( audioWrapper.endTime <= audioWrapper.startTime ||\r
- audioWrapper.endTime <= audioWrapper.loopStartTime ||\r
- audioWrapper.endTime < audioWrapper.seekTime ||\r
- audioWrapper.duration < audioWrapper.endTime\r
+ if( audioWrapper.endTime < audioWrapper.startTime ||\r
+ ( audioWrapper.loopEndTime < 0 ? audioWrapper.endTime : audioWrapper.loopEndTime ) < ( audioWrapper.loopStartTime < 0 ? audioWrapper.startTime : audioWrapper.loopStartTime ) ||\r
+ X_AudioWrapper_getEndTime( audioWrapper ) < audioWrapper.seekTime// ||\r
+ //audioWrapper.duration < audioWrapper.endTime\r
){\r
//alert( 'error @updateStateObject() begin:' + audioWrapper.startTime + ' end:' + audioWrapper.endTime + ' d:' + audioWrapper.duration + ' ls:' + audioWrapper.loopStartTime );\r
return 0;\r
ms = ( h * 3600 + m * 60 + s ) * 1000 + ms;\r
return ms < 0 ? 0 : ms;\r
};\r
+\r
+function X_AudioWrapper_getStartTime( audioWrapper, endTime, delSeekTime ){\r
+ var seek = audioWrapper.seekTime;\r
+ if( delSeekTime ) delete audioWrapper.seekTime;\r
+ \r
+ if( 0 <= seek ){\r
+ if( audioWrapper.duration <= seek || endTime < seek ) return 0;\r
+ return seek;\r
+ };\r
+ \r
+ if( audioWrapper.looped && 0 <= audioWrapper.loopStartTime ){\r
+ if( audioWrapper.duration <= audioWrapper.loopStartTime || endTime < audioWrapper.loopStartTime ) return 0;\r
+ return audioWrapper.loopStartTime;\r
+ };\r
+ \r
+ if( audioWrapper.startTime < 0 || audioWrapper.duration <= audioWrapper.startTime ) return 0;\r
+ return audioWrapper.startTime;\r
+};\r
+\r
+function X_AudioWrapper_getEndTime( audioWrapper ){\r
+ var duration = audioWrapper.duration;\r
+ \r
+ if( audioWrapper.looped && 0 <= audioWrapper.loopEndTime ){\r
+ if( duration <= audioWrapper.loopEndTime ) return duration;\r
+ return audioWrapper.loopEndTime;\r
+ };\r
+ \r
+ if( audioWrapper.endTime < 0 || duration <= audioWrapper.endTime ) return duration;\r
+ return audioWrapper.endTime;\r
+};\r
+\r
-var X_Audio_WebAudio_context = window.webkitAudioContext || window.AudioContext,
- X_Audio_WebAudio_LIVE_LIST = [],
- X_Audio_WebAudio_POOL_LIST = [],
- X_Audio_WebAudio, X_Audio_WebAudioWrapper, X_Audio_rawAudio;
+var X_Audio_WebAudio_context = window.webkitAudioContext || window.AudioContext,
+ X_Audio_WebAudioWrapper;
if( X_Audio_WebAudio_context ){
X_Audio_WebAudio_context = new X_Audio_WebAudio_context;
- function getWebAudioWrapper( proxy ){
- var i = X_Audio_WebAudio_LIVE_LIST.length;
- for( ; i; ){
- if( X_Audio_WebAudio_LIVE_LIST[ --i ].proxy === proxy ) return X_Audio_WebAudio_LIVE_LIST[ i ];
+ function X_Audio_WebAudio_getBuffer( url ){
+ var i = 0, l = X_Audio_WRAPPER_LIST.length;
+ for( i = 0; i < l; ++i ){
+ if( X_Audio_WRAPPER_LIST[ i ].url === url ) return X_Audio_WRAPPER_LIST[ i ];
};
};
- X_Audio_WebAudio =
- {
- backendName : 'Web Audio',
-
- detect : function( proxy, source, ext ){
- var ok = ext === 'mp3' || ext === 'ogg';
-
- proxy.asyncDispatch( ok ? 'support' : 'nosupport' );
- },
-
- register : function( proxy, source, option ){
- X_Audio_WebAudio_LIVE_LIST.push( new X_Audio_WebAudioWrapper( proxy, source, option ) );
- },
-
- close : function( proxy ){
- return getWebAudioWrapper( proxy ).close();
- },
-
- play : function( proxy, startTime, endTime, loop, loopStartTime ){
- return getWebAudioWrapper( proxy ).play( startTime, endTime, loop, loopStartTime );
- },
-
- pause : function( proxy ){
- return getWebAudioWrapper( proxy ).pause();
- },
-
- state : function( proxy, obj ){
- return getWebAudioWrapper( proxy ).state( obj );
- }
- };
-
- X_Audio_BACKENDS.push( X_Audio_WebAudio );
-
X_Audio_WebAudioWrapper = X.EventDispatcher.inherits(
'X.AV.WebAudioWrapper',
X.Class.POOL_OBJECT,
{
+ url : '',
proxy : null,
startTime : 0,
- endTime : 0,
- loopStartTime : 0,
- seekTime : 0,
+ endTime : -1,
+ loopStartTime : -1,
+ loopEndTime : -1,
+ seekTime : -1,
duration : 0,
playing : false,
error : 0,
loop : false,
+ looped : false,
volume : 0.5,
_startTime : 0,
+ _endTime : 0,
_playTime : 0,
_timerID : 0,
_interval : 0,
buffer : null,
+ source : null,
gainNode : null,
_onended : null,
onDecodeSuccess : null,
onDecodeError : null,
- Constructor : function( proxy, source, option ){
+ Constructor : function( proxy, url, option ){
+ var audio = X_Audio_WebAudio_getBuffer( url );
+
+ this.url = url;
this.closed = false;
this.proxy = proxy;
- this.xhr = X.Net.xhrGet( source, 'arraybuffer' )
- .listen( X.Event.PROGRESS, this )
- .listenOnce( [ X.Event.SUCCESS, X.Event.COMPLETE, X.Event.CANCELED ], this );
+
X_AudioWrapper_updateStates( this, option );
+
+ if( audio && audio.buffer ){
+ this._onDecodeSuccess( audio.buffer );
+ } else
+ if( audio ){
+ // TODO 当てにしていたaudioがclose 等した場合
+ 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 );
+ };
},
handleEvent : function( e ){
return;
case X.Event.SUCCESS :
- X_Audio_WebAudio_context.decodeAudioData( e.data,
- this.callbackDecodeSuccess = X_Callback_create( this, this._onDecodeSuccess ),
- this.callbackDecodeError = X_Callback_create( this, this._onDecodeError ) );
+ // TODO 旧api
+ // https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext
+ if( X_Audio_WebAudio_context.decodeAudioData ){
+ X_Audio_WebAudio_context.decodeAudioData( e.data,
+ 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.data, false ) );
+ };
break;
case X.Event.CANCELED :
},
_onDecodeSuccess : function( buffer ){
- this._onDecodeComplete();
+ this.onDecodeSuccess && this._onDecodeComplete();
if ( !buffer ) {
this.proxy.asyncDispatch( { type : 'error', message : 'buffer is ' + buffer } );
this.buffer = buffer;
this.duration = buffer.duration * 1000;
- this.endTime = this.endTime || this.duration;
this.proxy.asyncDispatch( 'loadedmetadata' );
this.proxy.asyncDispatch( 'loadeddata' );
},
_onDecodeComplete : function(){
- X_Callback_correct( this.callbackDecodeSuccess );
- delete this.callbackDecodeSuccess;
- X_Callback_correct( this.callbackDecodeError );
- delete this.callbackDecodeError;
+ X_Callback_correct( this.onDecodeSuccess );
+ delete this.onDecodeSuccess;
+ X_Callback_correct( this.onDecodeError );
+ delete this.onDecodeError;
+ },
+
+ _onBufferReady : function( e ){
+ var audio = X_Audio_WebAudio_getBuffer( this.url );
+ this._onDecodeSuccess( audio.buffer );
},
close : function(){
delete this.buffer;
+ if( this.xhr ) this.xhr.close();
+
+ if( this.onDecodeSuccess ){
+ // 回収はあきらめる、、、
+ };
+
this.playing && this.pause();
this.source && this._sourceDispose();
},
_sourceDispose : function(){
- this.source && this.source.disconnect();
+ this.source.disconnect();
delete this.source.onended;
delete this.source;
},
- play : function( seekTime ){
- var begin;
+ play : function(){
+ var begin, end;
if( !this.buffer ) return this;
- begin = ( seekTime || seekTime === 0 ) ? seekTime : this.playing ? this.loopStartTime : this.startTime;
+ end = X_AudioWrapper_getEndTime( this );
+ begin = X_AudioWrapper_getStartTime( this, end, true );
- console.log( '[WebAudio] play' );
+ console.log( '[WebAudio] play ' + begin + ' -> ' + end );
if( this.source ) this._sourceDispose();
- if( !this.gainNode ) this.gainNode = X_Audio_WebAudio_context.createGain();
-
+ 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.source = X_Audio_WebAudio_context.createBufferSource();
this.source.buffer = this.buffer;
this.source.connect( this.gainNode );
- this.gainNode.connect( X_Audio_WebAudio_context.destination );
this.gainNode.gain.value = this.volume;
this._timerID && X.Timer.remove( this._timerID );
- // おかしい、stop 前に外していても呼ばれる、、、@Firefox
- if( this.source.onended !== undefined ){
+ // おかしい、stop 前に外していても呼ばれる、、、@Firefox33.1
+ // 破棄された X.Callback が呼ばれて、obj._() でエラーになる。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.once( this.endTime - begin, this, this._onEnded );
+ this._timerID = X.Timer.once( end - begin, this, this._onEnded );
};
if( this.source.start ){
- this.source.start( 0, begin / 1000, this.endTime / 1000 );
+ this.source.start( 0, begin / 1000, end / 1000 );
} else {
- this.source.noteGrainOn( 0, begin / 1000, this.endTime / 1000 );
+ 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 );
},
},
_onEnded : function(){
+ var time;
delete this._timerID;
+
if( this.playing ){
- console.log( '> onEnd ' + ( this.playing && ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) ) + ' < ' + ( this.endTime - this._startTime ) );
- // Firefox 用の対策,,,
- if( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime < this.endTime - this._startTime ) return;
+ 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 ) );
+ if( this._onended ){
+ // Firefox 用の対策,,,
+ if( time < 0 ) return;
+ } else {
+ if( time < -16 ){
+ console.log( '> onEnd ' + time );
+ this._timerID = X.Timer.once( -time, this, this._onEnded );
+ return;
+ };
+ };
if( this.loop ){
+ this.looped = true;
this.play();
+ this.proxy.dispatch( 'looped' );
} else {
this.pause();
this.proxy.dispatch( 'ended' );
state : function( obj ){
var time = this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) : 0,
- result, halfway;
+ result;
if( obj === undefined ){
return {
startTime : this.startTime,
- endTime : this.endTime,
- loopStartTime : this.loopStartTime,
- currentTime : time + this._startTime,
+ endTime : this.endTime < 0 ? this.duration : this.endTime,
+ loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,
+ loopEndTime : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,
loop : this.loop,
+ looped : this.looped,
volume : this.volume,
- error : this.error,
playing : this.playing,
- duration : this.duration
- };
+ duration : this.duration,
+
+ currentTime : time + this._startTime,
+ error : this.error
+ };
};
result = X_AudioWrapper_updateStates( this, obj );
if( result & 2 ){ // seek
- this.play( this.seekTime );
- delete this.seekTime;
+ this.play();
} else
if( result & 1 ){
this.play();
}
);
+
+ X_Audio_BACKENDS.push(
+ {
+ backendName : 'Web Audio',
+
+ detect : function( proxy, source, ext ){
+ var ok = ext === 'mp3' || ext === 'ogg';
+
+ proxy.asyncDispatch( ok ? 'support' : 'nosupport' );
+ },
+
+ klass : X_Audio_WebAudioWrapper
+ }
+ );
};
* https://code.google.com/p/uupaa-js/source/browse/trunk/0.8/src/Audio/HTML5Audio.js?r=568\r
*/\r
\r
-var X_Audio_HTML5Audio, X_Audio_HTML5AudioWrapper, X_Audio_rawAudio,\r
- X_Audio_HTML5Audio_LIVE_LIST = [],\r
- X_Audio_HTML5Audio_POOL_LIST = [];\r
+var X_Audio_HTML5AudioWrapper, X_Audio_rawAudio;\r
\r
\r
if( window.HTMLAudioElement ){\r
- function getHTML5AudioWrapper( proxy ){\r
- var i = X_Audio_HTML5Audio_LIVE_LIST.length;\r
- for( ; i; ){\r
- if( X_Audio_HTML5Audio_LIVE_LIST[ --i ].proxy === proxy ) return X_Audio_HTML5Audio_LIVE_LIST[ i ];\r
- };\r
- };\r
- \r
- X_Audio_HTML5Audio = \r
- {\r
- backendName : 'HTML5 Audio',\r
- /*\r
- * HTML5 の audio 要素と video 要素でサポートされているメディアフォーマット\r
- * https://developer.mozilla.org/ja/docs/Web/HTML/Supported_media_formats\r
- * \r
- * 主要ブラウザのHTML5 audioタグで使えるファイル形式の再生対応状況を調べてみた\r
- * http://sothis.blog.so-net.ne.jp/2010-10-27\r
- * ダメ元で仕様に含まれていない SHOUTcast もテストしてみました。\r
- * \r
- * IE9 の HTML5 Audio について\r
- * http://kentablog.cluscore.com/2011/05/ie9-html5-audio.html\r
- * 1.Audioオブジェクトを作ることができないので、Audioタグを使う\r
- * 2.クロスドメインアクセスには、「clientaccesspolicy.xml」か「crossdomain.xml」が必要\r
- * 3.wav が不可\r
- * \r
- * IE9でHTML5 autio タグが無効になる\r
- * http://bbs.wankuma.com/index.cgi?mode=al2&namber=64886&KLOG=109\r
- * IEのバージョン9.0.8112.16421では、Audioオブジェクトのnewも対応してました。\r
- * createElement等で動的生成すると、よろしくない\r
- * \r
- * media-can-play-wav-audio.html\r
- * https://github.com/adobe/webkit/blob/master/LayoutTests/media/media-can-play-wav-audio.html\r
- * testExpected("audio.canPlayType('audio/wav; codecs=1')", "probably");\r
- * \r
- * HTML5 audioタグ ブラウザ間の違い\r
- * http://wiki.bit-hive.com/tomizoo/pg/HTML5%20audio%A5%BF%A5%B0%20%A5%D6%A5%E9%A5%A6%A5%B6%B4%D6%A4%CE%B0%E3%A4%A4\r
- * - volume, muted iPhone(iOS4-6)、Android(2.3.6)では動作せず。\r
- * - FireFox3.6, Android 2.3.6については、src変更後、load()を呼び出さないと切り替わらなかった。iPhoneはload()が不要。\r
- */ \r
- detect : function( proxy, source, ext ){\r
- var ok, mineType = 'audio/' + ext;\r
- switch( ext ){\r
- case 'mp3' :\r
- ok = X_UA.IE || X_UA.Chrome || ( X_UA.Windows && X_UA.Safari );\r
- mineType = 'audio/mpeg';\r
- break;\r
- case 'ogg' :\r
- ok = 15 <= X_UA.Gecko || X_UA.Chrome || X_UA.Opera;\r
- break;\r
- case 'm4a' :\r
- ok = X_UA.IE || X_UA.WebKit;\r
- mineType = 'audio/mp4';\r
- break;\r
- case 'webm' :\r
- ok = 2 <= X_UA.Gecko || 10.6 <= X_UA.Opera; // firefox4+(Gecko2+)\r
- break;\r
- case 'wav' :\r
- ok = X_UA.Gecko || X_UA.Opera || ( X_UA.Windows && X_UA.Safari );\r
- //mineType = 'audio/wav'; // audio/x-wav ?\r
- break;\r
- default :\r
- mineType = '';\r
- };\r
- \r
- if( !ok && mineType ){\r
- if( !X_Audio_rawAudio ) X_Audio_rawAudio = new Audio;\r
- ok = X_Audio_rawAudio.canPlayType( mineType );\r
- };\r
- \r
- proxy.asyncDispatch( ok ? 'support' : 'nosupport' );\r
- },\r
- \r
- register : function( proxy, source, option ){\r
- X_Audio_HTML5Audio_LIVE_LIST.push( new X_Audio_HTML5AudioWrapper( proxy, source, option ) );\r
- },\r
- \r
- close : function( proxy ){\r
- getHTML5AudioWrapper( proxy ).close();\r
- },\r
- \r
- play : function( proxy ){\r
- getHTML5AudioWrapper( proxy ).play();\r
- },\r
- \r
- pause : function( proxy ){\r
- getHTML5AudioWrapper( proxy ).pause();\r
- },\r
- \r
- state : function( proxy, obj ){\r
- return getHTML5AudioWrapper( proxy ).state( obj );\r
- }\r
- };\r
- \r
- X_Audio_BACKENDS.push( X_Audio_HTML5Audio );\r
\r
X_Audio_HTML5AudioWrapper = X.EventDispatcher.inherits(\r
'X.AV.HTML5AudioWrapper',\r
proxy : null,\r
\r
startTime : 0,\r
- endTime : 1 / 0,\r
- loopStartTime : 0,\r
- seekTime : 0,\r
- duration : 1 / 0,\r
+ endTime : -1,\r
+ loopStartTime : -1,\r
+ loopEndTime : -1,\r
+ seekTime : -1,\r
+ duration : 0,\r
\r
playing : false,\r
error : 0, \r
loop : false,\r
+ looped : false,\r
volume : 0.5,\r
\r
_timerID : 0,\r
\r
X_AudioWrapper_updateStates( this, option );\r
\r
+ // TODO use video document.createElement('video')\r
this._rawObject = X_Audio_rawAudio || new Audio( source );// X_Doc_create( 'audio', { src : source } ).appendToRoot();//( X.X_Node_systemNode );\r
\r
this.listen( [\r
'loadeddata', 'waiting', 'playing', 'canplay', 'canplaythrough', 'seeking', 'seeked', 'timeupdate', 'ended',\r
'ratechange', 'durationchange', 'volumechange' ], this.handleEventProxy ); \r
\r
- if( X_Audio_rawAudio ){\r
+ if( X_Audio_rawAudio === this._rawObject ){\r
X_Audio_rawAudio.src = source;\r
X_Audio_rawAudio.load(); // 要る?\r
X_Audio_rawAudio = null;\r
case 'canplay' : // 今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生\r
case 'canplaythrough' : // 今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生\r
this.duration = this._rawObject.duration * 1000;\r
- this.endTime = this.duration < this.endTime ? this.duration : this.endTime;\r
//console.log( this.duration );\r
break;\r
\r
\r
case 'ended' :\r
if( !this._closed && this.loop ){\r
+ this.looped = true;\r
this.play();\r
+ this.proxy.dispatch( 'looped' );\r
} else {\r
this._timerID && X.Timer.remove( this._timerID );\r
delete this._timerID;\r
this._rawObject.load();\r
},\r
\r
- play : function( seekTime ){\r
- var begin, halfway;\r
+ play : function(){\r
+ var begin, end, halfway;\r
\r
// もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
if( this._closed ) return;\r
\r
- begin = ( seekTime || seekTime === 0 ) ? seekTime : this.playing ? this.loopStartTime : this.startTime;\r
- this._rawObject.currentTime = begin / 1000;\r
+ end = X_AudioWrapper_getEndTime( this );\r
+ begin = X_AudioWrapper_getStartTime( this, end, true );\r
+ this._rawObject.currentTime = begin / 1000;\r
+ \r
+ console.log( '[HTMLAudio] play ' + begin + ' -> ' + end );\r
\r
if( !this.playing ){\r
if( X_UA.Chrome ){ // [CHROME][FIX] volume TODO どの version で 修正される?\r
this.playing = true;\r
};\r
\r
- halfway = this.endTime < this.duration;\r
+ halfway = end < this.duration;\r
this._timerID && X.Timer.remove( this._timerID );\r
\r
if( halfway ){\r
- this._timerID = X.Timer.once( this.endTime - begin, this, this._onEnded );\r
+ this._timerID = X.Timer.once( end - begin, this, this._onEnded );\r
} else {\r
delete this._timerID;\r
};\r
- \r
- if( !this._interval ){\r
- this._interval = X.Timer.add( 1000, 0, this, this._onInterval );\r
- };\r
},\r
\r
// [CHROME][FIX] volume\r
_fixForChrome : X_UA.Chrome && function(){\r
!this._closed && ( this._rawObject.volume = this.volume );\r
},\r
-\r
- _onInterval : function(){\r
- if( !this.playing ){\r
- delete this._interval;\r
- return X_Callback_UN_LISTEN;\r
- };\r
- this.proxy.dispatch( 'timeupdate' );\r
- },\r
\r
_onEnded : function(){\r
+ var time;\r
delete this._timerID;\r
+ \r
if( this.playing ){\r
+ \r
+ time = this._rawObject.currentTime * 1000 - X_AudioWrapper_getEndTime( this ) | 0;\r
+ if( time < -16 ){\r
+ console.log( '> onEnd ' + time + ' ' + ( this._rawObject.currentTime * 1000 | 0 ) + '/' + X_AudioWrapper_getEndTime( this ) );\r
+ this._timerID = X.Timer.once( -time, this, this._onEnded );\r
+ return;\r
+ };\r
+ \r
if( this.loop ){\r
+ this.looped = true;\r
this.play();\r
+ this.proxy.dispatch( 'looped' );\r
} else {\r
- console.log( '中断:' + this._rawObject.currentTime + ' ' + this.endTime );\r
this.pause();\r
this.dispatch( 'ended' );\r
};\r
this._timerID && X.Timer.remove( this._timerID );\r
delete this._timerID;\r
\r
- if( this.palying && !this._rawObject.error ){\r
- this._rawObject.pause();\r
+ if( this.playing ){\r
+ !this._rawObject.error && this._rawObject.pause();\r
delete this.playing;\r
};\r
},\r
if( obj === undefined ){\r
return {\r
startTime : this.startTime,\r
- endTime : this.endTime,\r
- loopStartTime : this.loopStartTime,\r
- currentTime : this._rawObject.currentTime * 1000,\r
+ endTime : this.endTime < 0 ? this.duration : this.endTime,\r
+ loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,\r
+ loopEndTime : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,\r
+ \r
loop : this.loop,\r
+ looped : this.looped,\r
volume : this.volume,\r
+ playing : this.playing, // && !this._rawObject.error && !this._rawObject.paused && !this._rawObject.ended, \r
+ duration : this.duration,\r
+ \r
+ currentTime : this._rawObject.currentTime * 1000,\r
/*\r
http://www.w3schools.com/tags/av_prop_error.asp\r
1 = MEDIA_ERR_ABORTED - fetching process aborted by user\r
3 = MEDIA_ERR_DECODE - error occurred when decoding\r
4 = MEDIA_ERR_SRC_NOT_SUPPORTED - audio/video not supported\r
*/\r
- error : this._rawObject.error || 0, // 0, 1 ~ 4\r
- playing : this.palying && !this._rawObject.error && !this._rawObject.paused && !this._rawObject.ended, \r
- duration : this.duration || 0\r
+ error : this._rawObject.error || 0 // 0, 1 ~ 4 \r
}; \r
};\r
\r
result = X_AudioWrapper_updateStates( this, obj );\r
\r
if( result & 2 ){ // seek\r
- this.play( this.seekTime );\r
- delete this.seekTime;\r
+ this.play();\r
} else {\r
if( result & 1 ){\r
- halfway = this.endTime < this.duration;\r
+ end = X_AudioWrapper_getEndTime( this );\r
+ halfway = end < this.duration;\r
this._timerID && X.Timer.remove( this._timerID );\r
\r
if( halfway ){\r
- this._timerID = X.Timer.once( this.endTime - this._rawObject.currentTime * 1000, this, this._onEnded ); \r
+ this._timerID = X.Timer.once( end - this._rawObject.currentTime * 1000, this, this._onEnded ); \r
} else {\r
delete this._timerID;\r
};\r
}\r
);\r
\r
+ X_Audio_BACKENDS.push(\r
+ {\r
+ backendName : 'HTML5 Audio',\r
+ /*\r
+ * HTML5 の audio 要素と video 要素でサポートされているメディアフォーマット\r
+ * https://developer.mozilla.org/ja/docs/Web/HTML/Supported_media_formats\r
+ * \r
+ * 主要ブラウザのHTML5 audioタグで使えるファイル形式の再生対応状況を調べてみた\r
+ * http://sothis.blog.so-net.ne.jp/2010-10-27\r
+ * ダメ元で仕様に含まれていない SHOUTcast もテストしてみました。\r
+ * \r
+ * IE9 の HTML5 Audio について\r
+ * http://kentablog.cluscore.com/2011/05/ie9-html5-audio.html\r
+ * 1.Audioオブジェクトを作ることができないので、Audioタグを使う\r
+ * 2.クロスドメインアクセスには、「clientaccesspolicy.xml」か「crossdomain.xml」が必要\r
+ * 3.wav が不可\r
+ * \r
+ * IE9でHTML5 autio タグが無効になる\r
+ * http://bbs.wankuma.com/index.cgi?mode=al2&namber=64886&KLOG=109\r
+ * IEのバージョン9.0.8112.16421では、Audioオブジェクトのnewも対応してました。\r
+ * createElement等で動的生成すると、よろしくない\r
+ * \r
+ * media-can-play-wav-audio.html\r
+ * https://github.com/adobe/webkit/blob/master/LayoutTests/media/media-can-play-wav-audio.html\r
+ * testExpected("audio.canPlayType('audio/wav; codecs=1')", "probably");\r
+ * \r
+ * HTML5 audioタグ ブラウザ間の違い\r
+ * http://wiki.bit-hive.com/tomizoo/pg/HTML5%20audio%A5%BF%A5%B0%20%A5%D6%A5%E9%A5%A6%A5%B6%B4%D6%A4%CE%B0%E3%A4%A4\r
+ * - volume, muted iPhone(iOS4-6)、Android(2.3.6)では動作せず。\r
+ * - FireFox3.6, Android 2.3.6については、src変更後、load()を呼び出さないと切り替わらなかった。iPhoneはload()が不要。\r
+ */ \r
+ detect : function( proxy, source, ext ){\r
+ var ok, mineType = 'audio/' + ext;\r
+ switch( ext ){\r
+ case 'mp3' :\r
+ ok = X_UA.IE || X_UA.Chrome || ( X_UA.Windows && X_UA.Safari );\r
+ mineType = 'audio/mpeg';\r
+ break;\r
+ case 'ogg' :\r
+ ok = 15 <= X_UA.Gecko || X_UA.Chrome || X_UA.Opera;\r
+ break;\r
+ case 'm4a' :\r
+ ok = X_UA.IE || X_UA.WebKit;\r
+ mineType = 'audio/mp4';\r
+ break;\r
+ case 'webm' :\r
+ ok = 2 <= X_UA.Gecko || 10.6 <= X_UA.Opera; // firefox4+(Gecko2+)\r
+ break;\r
+ case 'wav' :\r
+ ok = X_UA.Gecko || X_UA.Opera || ( X_UA.Windows && X_UA.Safari );\r
+ //mineType = 'audio/wav'; // audio/x-wav ?\r
+ break;\r
+ default :\r
+ mineType = '';\r
+ };\r
+ \r
+ if( !ok && mineType ){\r
+ if( !X_Audio_rawAudio ) X_Audio_rawAudio = new Audio;\r
+ ok = X_Audio_rawAudio.canPlayType( mineType );\r
+ };\r
+ \r
+ proxy.asyncDispatch( ok ? 'support' : 'nosupport' );\r
+ },\r
+ \r
+ klass : X_Audio_HTML5AudioWrapper\r
+ \r
+ } );\r
+\r
+/*\r
+ * \r
+ * howler.js\r
+ * codecs = {\r
+ mp3: !!audioTest.canPlayType('audio/mpeg;').replace(/^no$/, ''),\r
+ opus: !!audioTest.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ''),\r
+ ogg: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''),\r
+ wav: !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''),\r
+ aac: !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''),\r
+ m4a: !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),\r
+ mp4: !!(audioTest.canPlayType('audio/x-mp4;') || audioTest.canPlayType('audio/mp4;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),\r
+ weba: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, '')\r
+ };\r
+ */\r
+ \r
};\r
\r
\r
* SilverlLight5 ie6&7(ietester,winxp), ie8(winxp) で動作確認。firefox32 では動作しない。(4以下の方がよい?)\r
*/\r
\r
-var X_Audio_SLAudio, X_Audio_SLAudioWrapper,\r
- X_Audio_SLAudio_uid = 0,\r
- X_Audio_SLAudio_LIVE_LIST = [],\r
- X_Audio_SLAudio_POOL_LIST = [];\r
+var X_Audio_SLAudioWrapper,\r
+ X_Audio_SLAudio_uid = 0;\r
\r
if( X.Pulgin.SilverlightEnabled ){\r
\r
- function getSLAudioWrapper( proxy ){\r
- var i = X_Audio_SLAudio_LIVE_LIST.length;\r
- for( ; i; ){\r
- if( X_Audio_SLAudio_LIVE_LIST[ --i ].proxy === proxy ) return X_Audio_SLAudio_LIVE_LIST[ i ];\r
- };\r
- };\r
- \r
- X_Audio_SLAudio = \r
- {\r
- backendName : 'Silverlight Audio',\r
-\r
- detect : function( proxy, source, ext ){\r
- var ok = ext === 'mp3' || ext === 'wma';\r
- proxy.asyncDispatch( ok ? 'support' : 'nosupport' ); \r
- },\r
- \r
- register : function( proxy, source, option ){\r
- X_Audio_SLAudio_LIVE_LIST.push( new X_Audio_SLAudioWrapper( proxy, source, option ) );\r
- },\r
- \r
- close : function( proxy ){\r
- return getSLAudioWrapper( proxy ).close();\r
- },\r
- \r
- play : function( proxy ){\r
- return getSLAudioWrapper( proxy ).play();\r
- },\r
- \r
- pause : function( proxy ){\r
- return getSLAudioWrapper( proxy ).pause();\r
- },\r
- \r
- state : function( proxy, obj ){\r
- return getSLAudioWrapper( proxy ).state( obj );\r
- }\r
- };\r
- \r
- X_Audio_BACKENDS.push( X_Audio_SLAudio );\r
- \r
- function slerror(){\r
- alert( 'slerror' );\r
- };\r
- \r
+ // TODO X.Node.inherits\r
X_Audio_SLAudioWrapper = X.EventDispatcher.inherits(\r
'X.AV.SilverlightAudioWrapper',\r
X.Class.POOL_OBJECT,\r
proxy : null,\r
\r
startTime : 0,\r
- endTime : 0,\r
- loopStartTime : 0,\r
- seekTime : 0,\r
+ endTime : -1,\r
+ loopStartTime : -1,\r
+ loopEndTime : -1,\r
+ seekTime : -1,\r
duration : 0,\r
\r
playing : false,\r
error : 0, \r
loop : false,\r
+ looped : false,\r
volume : 0.5,\r
\r
_onload : '',\r
case 'MediaOpened' :\r
// http://msdn.microsoft.com/ja-jp/library/bb979710(VS.95).aspx\r
this.duration = this._rawObject.NaturalDuration.Seconds * 1000;\r
- this.endTime = this.endTime || this.duration;\r
// TODO 'canplaythrough'\r
this.proxy.asyncDispatch( 'loadstart' );\r
this.proxy.asyncDispatch( 'loadedmetadata' );\r
},\r
\r
// SilverlightAudio.play\r
- play : function( seekTime ){\r
- var begin, halfway;\r
+ play : function(){\r
+ var begin, end, halfway;\r
\r
// もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
if( this.error ) return;\r
-\r
- begin = ( seekTime || seekTime === 0 ) ? seekTime : this.playing ? this.loopStartTime : this.startTime;\r
- this._lastUserAction = this.playing ? 'seek' : 'play';\r
\r
- this._currentTime( begin );\r
+ this._lastUserAction = 0 <= this.seekTime ? 'seek' : 'play';\r
+ \r
+ end = X_AudioWrapper_getEndTime( this );\r
+ begin = X_AudioWrapper_getStartTime( this, end, true );\r
+ \r
+ console.log( '[SLAudio] play ' + begin + ' -> ' + end );\r
+ \r
this._rawObject.Volume = this.volume;\r
+ this._currentTime( begin );\r
\r
if( !this.playing ){\r
this._rawObject.play();\r
this.playing = true;\r
};\r
\r
- halfway = this.endTime < this.duration;\r
+ halfway = end < this.duration;\r
this._timerID && X.Timer.remove( this._timerID );\r
\r
if( halfway ){\r
- this._timerID = X.Timer.once( this.endTime - begin, this, this._onEnded );\r
+ this._timerID = X.Timer.once( end - begin, this, this._onEnded );\r
} else {\r
delete this._timerID;\r
};\r
},\r
\r
_onEnded : function(){\r
+ var time;\r
delete this._timerID;\r
+ \r
if( this.playing ){\r
+ \r
+ time = this._rawObject.Position.Seconds * 1000 - X_AudioWrapper_getEndTime( this ) | 0;\r
+ if( time < -16 ){\r
+ console.log( '> onEnd ' + time );\r
+ this._timerID = X.Timer.once( -time, this, this._onEnded );\r
+ return;\r
+ };\r
+ \r
if( this.loop ){\r
+ this.looped = true;\r
this.play();\r
+ this.proxy.dispatch( 'looped' );\r
} else {\r
this.pause();\r
- this.dispatch( 'ended' ); \r
+ this.proxy.dispatch( 'ended' ); \r
};\r
};\r
},\r
\r
// SilverlightAudio.state\r
state : function( obj ){ // @return Hash: { loop, error, paused, ended, source, duration }\r
- var result;\r
+ var result, end;\r
\r
if( obj === undefined ){\r
return {\r
startTime : this.startTime,\r
- endTime : this.endTime,\r
- loopStartTime : this.loopStartTime,\r
+ endTime : this.endTime < 0 ? this.duration : this.endTime,\r
+ loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,\r
+ loopEndTime : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,\r
+ \r
currentTime : this._rawObject.Position.Seconds * 1000,\r
+ \r
+ \r
loop : this.loop,\r
+ looped : this.looped,\r
volume : this.volume,\r
error : this.error,\r
playing : this.playing,\r
- duration : this.duration || 0 // this._rawObject.NaturalDuration.Seconds;\r
+ duration : this.duration // this._rawObject.NaturalDuration.Seconds;\r
};\r
};\r
\r
result = X_AudioWrapper_updateStates( this, obj );\r
\r
if( result & 2 ){ // seek\r
- this.play( this.seekTime );\r
- delete this.seekTime;\r
+ this.play();\r
} else {\r
if( result & 1 ){\r
- halfway = this.endTime < this.duration;\r
+ end = X_AudioWrapper_getEndTime( this );\r
+ halfway = end < this.duration;\r
this._timerID && X.Timer.remove( this._timerID );\r
\r
if( halfway ){\r
- this._timerID = X.Timer.once( this.endTime - this._rawObject.Position.Seconds * 1000, this, this._onEnded );\r
+ this._timerID = X.Timer.once( end - this._rawObject.Position.Seconds * 1000, this, this._onEnded );\r
} else {\r
delete this._timerID;\r
};\r
_currentTime : function( time ){ // @param Number: time\r
var position = this._rawObject.Position; // [!] create instance\r
\r
- position.Seconds = time / 1000; // set current time\r
+ position.Seconds = time / 1000 | 0; // set current time\r
\r
this._rawObject.Position = position; // [!] reattach instance\r
}\r
\r
}\r
);\r
- \r
-};\r
\r
+ function slerror(){\r
+ alert( 'slerror' );\r
+ };\r
+\r
+ X_Audio_BACKENDS.push( {\r
+ backendName : 'Silverlight Audio',\r
\r
+ detect : function( proxy, source, ext ){\r
+ var ok = ext === 'mp3' || ext === 'wma' || ext === 'wav';\r
+ proxy.asyncDispatch( ok ? 'support' : 'nosupport' ); \r
+ },\r
+ \r
+ klass : X_Audio_SLAudioWrapper\r
+ \r
+ } );\r
\r
+};
\ No newline at end of file
--- /dev/null
+\r
+/*\r
+ * http://uupaa.hatenablog.com/entry/2011/12/12/213233\r
+ * Mobile Opera11 は Audio をサポートするがイベントが取れない\r
+ * iframe 内で生成して、Audio Sprite の preset で再生できないか?\r
+ */\r
+var X_Audio_Sprite_shouldUse = window.HTMLAudioElement && ( X_UA.iOS || X_UA.AndroidBrowser || X_UA.OperaMobile || X_UA.OperaTablet ),\r
+ X_Audio_Sprite_needTouchFirst = !!X_UA.iOS,\r
+ X_Audio_Sprite_enableMultiTrack = !X_UA.iOS,\r
+ X_Audio_Sprite_enableVolume = window.HTMLAudioElement && ( !X_UA.iOS && !X_UA.AndroidBrowser && !X_UA.OperaMobile && !X_UA.OperaTablet ),\r
+ X_Audio_Sprite_useVideoForMulti = 4 <= X_UA.AndroidBrowser,\r
+ X_Audio_Sprite_maxTracks = X_UA.iOS < 6 ? 1 : X_Audio_Sprite_useVideoForMulti ? 2 : 9,\r
+ X_Audio_Sprite_lengthSilence = 10000, // 一番最初の無音部分の長さ\r
+ X_Audio_Sprite_lengthDistance = 5000, // 音間の無音の長さ\r
+ X_Audio_Sprite_uid = 0,\r
+ X_Audio_Sprite_members = {},\r
+ X_Audio_Sprite_TEMP = {\r
+ presets : {},\r
+ BGMs : {},\r
+ tracks : [],\r
+ volume : 1,\r
+ bgmTrack : null,\r
+ bgmPosition : 0,\r
+ bgmName : '',\r
+ bgmLooped : false,\r
+ bgmPlaying : false\r
+ },\r
+ X_Audio_Sprite_instance;\r
+\r
+X.Audio.Sprite = {\r
+ \r
+ shouldUse : X_Audio_Sprite_shouldUse,\r
+ \r
+ needTouchFirst : X_Audio_Sprite_needTouchFirst,\r
+ \r
+ enableMultiTrack : X_Audio_Sprite_enableMultiTrack,\r
+ \r
+ create : function( setting ){\r
+ // close()\r
+ if( X_Audio_Sprite_instance ){\r
+ X_Audio_Sprite_instance.close();\r
+ } else {\r
+ X_Audio_Sprite_instance = X_Class_override( new X.EventDispatcher(), X_Audio_Sprite_members );\r
+ };\r
+ X_Audio_Sprite_instance.setup( setting );\r
+ return X_Audio_Sprite_instance;\r
+ \r
+ }\r
+};\r
+\r
+// 再生が終わっているもの、終わりかけのものを探す\r
+function X_Audio_Sprite_getTrackEnded(){\r
+ var tracks = X_Audio_Sprite_TEMP.tracks,\r
+ l = tracks.length,\r
+ i = 0, track, state, last = 1 / 0, _last, index;\r
+ \r
+ for( ; i < l; ++i ){\r
+ track = tracks[ i ];\r
+ state = track.state();\r
+ if( !state.playing ) return track;\r
+ if( track === X_Audio_Sprite_TEMP.bgmTrack ) continue;\r
+ if( state.currentTime <= X_Audio_Sprite_lengthSilence + X_Audio_Sprite_lengthDistance ) return track;\r
+ _last = state.endTime - state.currentTime;\r
+ if( _last < last ){\r
+ last = _last;\r
+ index = i;\r
+ };\r
+ };\r
+ return tracks[ index ];\r
+};\r
+\r
+/*\r
+ * {\r
+ * urls : [ 'xx.ogg', 'xx.mp3' ],\r
+ * numTracks : 3,\r
+ * useVideo : false,\r
+ * volume : 1,\r
+ * BGM_01 : [ '15.00', '45.500', true, '17.666', '50.999' ],\r
+ * BGM_02 : [ '56.00', '1:15.230', true ]\r
+ * }\r
+ */\r
+\r
+X_Audio_Sprite_members = {\r
+ \r
+ setup : function( setting ){\r
+ \r
+ var tracks = X_Audio_Sprite_TEMP.tracks,\r
+ bgms = X_Audio_Sprite_TEMP.BGMs,\r
+ presets = X_Audio_Sprite_TEMP.presets,\r
+ urls = setting[ 'urls' ],\r
+ n = setting[ 'numTracks' ] || 1,\r
+ video = setting[ 'useVideo' ],\r
+ option = {\r
+ volume : setting[ 'volume' ] || 0.5,\r
+ autoplay : false,\r
+ startTime : 0,\r
+ endTime : X_Audio_Sprite_lengthSilence,\r
+ loop : true\r
+ },\r
+ k, i;\r
+ \r
+ n = n <= X_Audio_Sprite_maxTracks ? n : X_Audio_Sprite_maxTracks;\r
+ \r
+ video = video || ( 1 < n && X_Audio_Sprite_useVideoForMulti );\r
+ \r
+ for( k in setting ){\r
+ v = setting[ k ];\r
+ if( X.Type.isArray( v ) && v !== urls){\r
+ v = X.Object.cloneArray( v );\r
+ for( i = v.length; i; ){\r
+ --i;\r
+ if( i !== 2 ) v[ i ] = X_AudioWrapper_timeStringToNumber( v[ i ] );\r
+ }; \r
+ if( v[ 2 ] ) bgms[ k ] = v;\r
+ presets[ k ] = v;\r
+ };\r
+ };\r
+ \r
+ for( i = 0; i < n; ++i ){\r
+ if( i === 1 && X_Audio_Sprite_useVideoForMulti ){\r
+ // use <Video>\r
+ } else {\r
+ tracks.push( X.Audio.create( urls, option ) );\r
+ };\r
+ };\r
+ \r
+ tracks[ n - 1 ].listenOnce( [ 'backendfound', 'nobackend' ], this, X_Audio_Sprite_handleEvent );\r
+ },\r
+ \r
+ close : function(){\r
+ var tracks = X_Audio_Sprite_TEMP.tracks,\r
+ bgms = X_Audio_Sprite_TEMP.BGMs,\r
+ presets = X_Audio_Sprite_TEMP.presets,\r
+ k;\r
+ \r
+ while( tracks.length ){\r
+ tracks.pop().kill();\r
+ };\r
+ \r
+ for( k in bgms ){\r
+ delete bgms[ k ];\r
+ };\r
+ for( k in presets ){\r
+ delete presets[ k ];\r
+ };\r
+ \r
+ X_Audio_Sprite_TEMP.bgmTrack = null;\r
+ X_Audio_Sprite_TEMP.bgmPosition = 0;\r
+ X_Audio_Sprite_TEMP.bgmName = '';\r
+ X_Audio_Sprite_TEMP.bgmLooped = false;\r
+ X_Audio_Sprite_TEMP.bgmPlaying = false;\r
+ },\r
+ \r
+ load : function(){\r
+ tracks[ 0 ].play( 0, X_Audio_Sprite_lengthSilence, true );\r
+ },\r
+ \r
+ /*\r
+ * @return uid Number\r
+ */\r
+ play : function( name ){\r
+ var bgm = X_Audio_Sprite_TEMP.bgmTrack,\r
+ tracks = X_Audio_Sprite_TEMP.tracks,\r
+ bgms = X_Audio_Sprite_TEMP.BGMs,\r
+ presets = X_Audio_Sprite_TEMP.presets,\r
+ preset = presets[ name ],\r
+ i, k;\r
+ \r
+ if( preset ){\r
+ if( bgms[ name ] ){\r
+ if( name !== X_Audio_Sprite_TEMP.bgmName ){\r
+ // bgm変更\r
+ X_Audio_Sprite_TEMP.bgmName = name;\r
+ X_Audio_Sprite_TEMP.bgmPosition = preset[ 0 ];\r
+ X_Audio_Sprite_TEMP.bgmPlaying = true;\r
+ X_Audio_Sprite_TEMP.bgmLooped = false;\r
+ };\r
+ if( bgm ){\r
+ track = bgm;\r
+ } else\r
+ if( 1 < tracks.length ){\r
+ track = X_Audio_Sprite_TEMP.bgmTrack = X_Audio_Sprite_getTrackEnded();\r
+ } else {\r
+ track = X_Audio_Sprite_TEMP.bgmTrack = tracks[ 0 ];\r
+ };\r
+ track\r
+ .state( { looped : X_Audio_Sprite_TEMP.bgmLooped } )\r
+ .play( preset[ 0 ], preset[ 1 ], true, preset[ 3 ], preset[ 4 ] )\r
+ .seek( X_Audio_Sprite_TEMP.bgmPosition )\r
+ .listen( 'looped', this, X_Audio_Sprite_handleEvent );\r
+ } else {\r
+ if( 1 < tracks.length ){\r
+ track = X_Audio_Sprite_getTrackEnded( X_Audio_Sprite_TEMP.bgmPlaying );\r
+ track\r
+ .listen( 'looped', this, X_Audio_Sprite_handleEvent )\r
+ .state( { looped : false } )\r
+ .play( preset[ 0 ], preset[ 1 ], true, 0, X_Audio_Sprite_lengthSilence );\r
+ } else {\r
+ // single track, iOS\r
+ if( bgm ){\r
+ X_Audio_Sprite_TEMP.bgmPosition = bgm.currentTime();\r
+ X_Audio_Sprite_TEMP.bgmTrack = null;\r
+ };\r
+ track = tracks[ 0 ];\r
+ track\r
+ .listen( 'looped', this, X_Audio_Sprite_handleEvent )\r
+ .state( { looped : false } )\r
+ .play( preset[ 0 ], preset[ 1 ], true, 0, X_Audio_Sprite_lengthSilence );\r
+ };\r
+ };\r
+ return tracks.indexOf( track );\r
+ };\r
+ return -1;\r
+ },\r
+ \r
+ pause : function( uid ){\r
+ var track = X_Audio_Sprite_TEMP.tracks[ uid ];\r
+ if( X_Audio_Sprite_TEMP.bgmTrack === track ){\r
+ X_Audio_Sprite_TEMP.bgmPosition = track.currentTime();\r
+ X_Audio_Sprite_TEMP.bgmPlaying = false;\r
+ X_Audio_Sprite_TEMP.bgmTrack = null;\r
+ };\r
+ console.log( 'pause' );\r
+ track && track.play( 0, X_Audio_Sprite_lengthSilence, true, 0, X_Audio_Sprite_lengthSilence ).seek( 0 );\r
+ return this;\r
+ },\r
+ \r
+ seek : function( uid, position ){\r
+ var track = X_Audio_Sprite_TEMP.tracks[ uid ],\r
+ end;\r
+ if( track ){\r
+ delete track.seekTime;\r
+ end = X_AudioWrapper_getEndTime( track );\r
+ position <= end && X_AudioWrapper_getStartTime( track, end ) <= position && track.seek( postion );\r
+ };\r
+ return this;\r
+ },\r
+ \r
+ volume : function( uid, opt_volume ){\r
+ var track, i;\r
+ // TODO uid = 0\r
+ if( uid === 0 ){\r
+ if( opt_volume === undefined ){\r
+ return X_Audio_Sprite_TEMP.volume;\r
+ };\r
+ for( i = X_Audio_Sprite_TEMP.tracks.length; i; ){\r
+ X_Audio_Sprite_TEMP.tracks[ --i ].volume( opt_volume );\r
+ };\r
+ return this;\r
+ };\r
+ track = X_Audio_Sprite_TEMP.tracks[ uid ];\r
+ if( opt_volume === undefined ){\r
+ return track ? track.volume() : -1;\r
+ };\r
+ track && track.volume( opt_volume );\r
+ return this;\r
+ },\r
+ \r
+ state : function( uid, opt_obj ){\r
+ var track = X_Audio_Sprite_TEMP.tracks[ uid ];\r
+ // TODO uid = 0\r
+ if( opt_obj === undefined ){\r
+ return track ? track.state() : { volume : X_Audio_Sprite_TEMP.volume };\r
+ };\r
+ track && track.state( opt_obj );\r
+ return this;\r
+ }\r
+};\r
+\r
+function X_Audio_Sprite_handleEvent( e ){\r
+ switch( e.type ){\r
+ case 'backendfound' :\r
+ this.asyncDispatch( e );\r
+ e.target.unlisten( 'nobackend', this, X_Audio_Sprite_handleEvent );\r
+ break;\r
+\r
+ case 'nobackend' :\r
+ this.asyncDispatch( e );\r
+ e.target.unlisten( 'backendfound', this, X_Audio_Sprite_handleEvent );\r
+ break;\r
+ \r
+ case 'looped' :\r
+ if( e.target === X_Audio_Sprite_TEMP.bgmTrack ){\r
+ X_Audio_Sprite_TEMP.bgmLooped = true;\r
+ } else {\r
+ // single track | iOS\r
+ if( X_Audio_Sprite_TEMP.bgmPlaying && !X_Audio_Sprite_TEMP.bgmTrack ){\r
+ X_Audio_Sprite_TEMP.bgmTrack = e.target;\r
+ this.play( X_Audio_Sprite_TEMP.bgmName );\r
+ };\r
+ };\r
+ break;\r
+ \r
+ case X.Event.KILL_INSTANCE :\r
+ this.close();\r
+ break;\r
+ };\r
+};\r
\r
Constructor : function( layout, args ){\r
\r
- this.SuperConstructor( layout, args );\r
+ this.Super( layout, args );\r
\r
// xnode の追加が可能\r
\r
scrollManager : null,\r
\r
Constructor : function( layout, args ){\r
- this.SuperConstructor( layout, args );\r
+ this.Super( layout, args );\r
this._containerNode = _X_Class_getPrivate( this.containerNode );\r
},\r
\r
_eventBusy : false,
Constructor : function( layout, args ){
- this.SuperConstructor( layout, args );
+ this.Super( layout, args );
if( X_ViewPort_readyState === X.Event.XDOM_READY ){
X.Timer.once( 0, this, this.start );
'js/07_audio/00_XAudio.js',\r
'js/07_audio/01_XWebAudio.js',\r
'js/07_audio/02_XHTMLAudio.js',\r
- 'js/07_audio/03_XSilverlightAudio.js'\r
+ 'js/07_audio/03_XSilverlightAudio.js',\r
+ 'js/07_audio/10_XAudioSprite.js'\r
\r
+ '"></script>'\r
].join( '"></script><script src="' + basePath ));\r