* 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 = X_Class_override(\r
- new X.EventDispatcher(),\r
- {\r
- backendName : 'Silverlight Audio',\r
-\r
- detect : function( source, ext ){\r
- var ok = ext === 'mp3' || ext === 'wma';\r
- \r
- this.asyncDispatch( ok ? 'support' : 'nosupport' );\r
- \r
- return this;\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(){\r
- return getSLAudioWrapper( this ).close();\r
- },\r
- \r
- play : function( position ){\r
- return getSLAudioWrapper( this ).play( position );\r
- },\r
- \r
- pause : function(){\r
- return getSLAudioWrapper( this ).pause();\r
- },\r
- \r
- stop : function(){\r
- return getSLAudioWrapper( this ).stop();\r
- },\r
- \r
- loop : function( v ){\r
- return getSLAudioWrapper( this ).loop( v );\r
- },\r
- \r
- state : function(){\r
- return getSLAudioWrapper( this ).state();\r
- },\r
- \r
- volume : function( v ){\r
- return getSLAudioWrapper( this ).volume( v );\r
- },\r
- \r
- startTime : function( time ){\r
- return getSLAudioWrapper( this ).startTime( time );\r
- },\r
- \r
- currentTime : function( time ){\r
- return getSLAudioWrapper( this ).currentTime( time );\r
- },\r
- \r
- isPlaying : function(){\r
- return getSLAudioWrapper( this ).isPlaying();\r
- }\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
{\r
_isSilverlight : true, // for X.EventDispatcher.listen\r
proxy : null,\r
+ \r
+ startTime : 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
+ autoplay : false,\r
+ volume : 0.5,\r
+ \r
_onload : '',\r
_callback : null, \r
xnodeObject : null,\r
- \r
- _loop : false,\r
_source : '',\r
- _error : 0,\r
_ended : true,\r
_paused : false,\r
- _volume : 0.5,\r
- _startTime : 0,\r
_lastUserAction : '',\r
_lastState : '',\r
- _duration : 0,\r
_interval : 0, // setInterval timer id\r
\r
Constructor : function( proxy, source, option ){\r
\r
if( !X_Audio_SLAudio_uid ){\r
// source\r
- //X.X_Node_systemNode.create( 'script', { type : 'text/xaml', id : 'silverlightaudio' } )\r
+ // X_Node_systemNode.create( 'script', { type : 'text/xaml', id : 'silverlightaudio' } )\r
// .text( '<Canvas xmlns="http://schemas.microsoft.com/client/2007" ' +\r
// 'xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas>');\r
\r
\r
// TODO embed\r
this.proxy = proxy;\r
+ this._source = source;\r
this._onload = 'XAudioSilverlightOnLoad' + ( ++X_Audio_SLAudio_uid );\r
- this._callback = window[ this._onload ] = X_Callback_create( this, this.onSLReady, [ option.autoplay ] );\r
+ this._callback = window[ this._onload ] = X_Callback_create( this, this.onSLReady );\r
this.xnodeObject = X_Node_body\r
.create( 'object', {\r
type : 'application/x-silverlight-2',\r
'<param name="background" value="#00000000">' + // transparent\r
'<param name="windowless" value="true">' +\r
'<param name="source" value="#silverlightaudio">' + // XAML ID\r
- '<param name="onload" value="' + this._onload + '">'// + // bond to global\r
+ '<param name="onload" value="' + this._onload + '">' // + // bond to global\r
//'<param value="2.0.31005.0" name="minRuntimeVersion">' +\r
//'<param value="true" name="autoUpgrade">' +\r
//'<param name="onerror" value="slerror">' // bond to global\r
);\r
- \r
- this._loop = option.loop;\r
- this._source = source;\r
- if( option.volume ) this._volume = option.volume;\r
- if( option.startTime ) this._startTime = option.startTime;\r
+ X_AudioWrapper_updateStates( this, option );\r
\r
this.listenOnce( X.Event.KILL_INSTANCE );\r
},\r
\r
- onSLReady : function( sender, autoplay ){\r
+ onSLReady : function( sender ){\r
if( !this._onload ) return;\r
\r
window[ this._onload ] = null;\r
X_Callback_correct( this._callback );\r
delete this._callback;\r
\r
- //if( sender.findName('media') ) alert( 'exist' );\r
-\r
sender.children.add(\r
sender.GetHost().\r
content.\r
CreateFromXaml(\r
'<Canvas xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">' +\r
- '<MediaElement x:Name="media" Source="' + this._source + '" Volume="' + this._volume + '" AutoPlay="false" />' +\r
+ '<MediaElement x:Name="media" Source="' + this._source + '" Volume="' + this.volume + '" AutoPlay="false" />' +\r
'</Canvas>'));\r
\r
this._rawObject = sender.findName('media'); // x:Name='media'\r
\r
this.listen( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] );\r
- \r
- autoplay && X.Timer.once( 100, this, this.play );\r
},\r
\r
handleEvent : function( e ){\r
switch( e.type ){\r
\r
case 'MediaFailed' :\r
- this._error = 4;\r
- this._ended = true;\r
+ this.error = 4;\r
+ this.playing = false;\r
+ this._ended = true;\r
this._paused = false;\r
- \r
this.proxy.dispatch( 'error' ); // open failed\r
break;\r
+\r
case 'MediaOpened' :\r
// http://msdn.microsoft.com/ja-jp/library/bb979710(VS.95).aspx\r
- this._duration = this._rawObject.NaturalDuration.Seconds;\r
- // TODO 'canplaythrough'\r
- this.proxy.dispatch( 'canplay' );\r
+ this.duration = this._rawObject.NaturalDuration.Seconds * 1000;\r
+ // TODO 'canplaythrough'\r
+ this.proxy.asyncDispatch( 'loadstart' );\r
+ this.proxy.asyncDispatch( 'loadedmetadata' );\r
+ this.proxy.asyncDispatch( 'loadeddata' );\r
+ this.proxy.asyncDispatch( 'canplay' );\r
+ this.proxy.asyncDispatch( 'canplaythrough' );\r
+ \r
+ this.autoplay && X.Timer.once( 16, this, this.play );\r
break;\r
- case 'MediaEnded' :\r
- this.currentTime(this._startTime);\r
- \r
- if (this._loop) {\r
- this._rawObject.play();\r
- }\r
+\r
+ case 'MediaEnded' : \r
+ this.loop && this.playing && this.play();\r
break;\r
+\r
case 'CurrentStateChanged' :\r
- lastState = this._lastState,\r
+ lastState = this._lastState,\r
currentState = this._rawObject.CurrentState;\r
\r
// ignore consecutive events or 'Closed' == 'Error'\r
};\r
this._lastState = currentState; // update last state\r
\r
- switch (currentState) {\r
- case 'Buffering':\r
- case 'Opening':\r
- break;\r
- \r
+ switch( currentState ){\r
+ case 'Buffering' :\r
+ case 'Opening' :\r
+ switch( this._lastUserAction ){\r
+ case 'play' :\r
+ this.proxy.dispatch( 'waiting' );\r
+ break;\r
+ case 'seek' :\r
+ this.proxy.dispatch( 'seeking' );\r
+ break;\r
+ case 'pause' :\r
+ break;\r
+ };\r
+ break;\r
+\r
// media.play(none supported file) -> 'Error'\r
// media.play(file not found) -> 'Closed'\r
// media.load -> 'Error'\r
case 'Error':\r
case 'Closed':\r
- this._error = 4;\r
- this._ended = true;\r
- this._paused = false;\r
- this.proxy.dispatch( 'error' );\r
- break;\r
- \r
+ this.error = 4;\r
+ this.playing = false;\r
+ this._ended = true;\r
+ this._paused = false;\r
+ this.proxy.dispatch( 'error' );\r
+ break;\r
+\r
// userAction.pause() -> MediaState('Paused') -> x\r
// userAction.stop() -> MediaState('Paused') -> x\r
// userAction.play() + file end -> MediaState('Paused') -> uueventfire('ended')\r
case 'Paused':\r
- switch (this._lastUserAction) {\r
- case 'play': // play() -> file end -> event('ended')\r
- this._ended = true;\r
- this._paused = false;\r
- this.proxy.dispatch( 'ended' );\r
- this.currentTime(this._startTime);\r
- break;\r
- case 'pause':\r
- this._ended = false;\r
- this._paused = true;\r
- break;\r
- case 'stop':\r
- this._ended = true;\r
- this._paused = false;\r
- }\r
- break;\r
- \r
+ this.playing = false;\r
+ \r
+ switch( this._lastUserAction ){\r
+ case 'play': // play() -> file end -> event('ended')\r
+ case 'seek':\r
+ this._ended = true;\r
+ this._paused = false;\r
+ this.proxy.dispatch( 'ended' );\r
+ this._currentTime( this.startTime );\r
+ break;\r
+ case 'pause':\r
+ this._ended = false;\r
+ this._paused = true;\r
+ break;\r
+ case 'stop':\r
+ this._ended = true;\r
+ this._paused = false;\r
+ };\r
+ break;\r
+\r
// media.play -> 'Playing'\r
case 'Playing':\r
- this._error = 0;\r
- this._ended = false;\r
- this._paused = false;\r
- this.proxy.dispatch( 'playing' );\r
- break;\r
- \r
+ this.error = 0;\r
+ this.playing = true;\r
+ this._ended = false;\r
+ this._paused = false;\r
+ this.proxy.dispatch( 'playing' );\r
+ break;\r
+\r
// stop()\r
case 'Stopped':\r
- this._ended = true;\r
- this._paused = false;\r
- this.currentTime(this._startTime);\r
- }\r
+ this.playing = false;\r
+ this._ended = true;\r
+ this._paused = false;\r
+ this._currentTime( this.startTime );\r
+ break;\r
+ };\r
break;\r
- \r
+\r
case X.Event.KILL_INSTANCE :\r
if( this._onload ){\r
- window[ this._onload ] = null;\r
- this._callback.kill();\r
+ // window への delete に ie5 は対応しないが、そもそも ie5 は Silverlight に非対応\r
+ delete window[ this._onload ];\r
+ delete this._onload;\r
+ X_Callback_correct( this._callback );\r
};\r
this.xnodeObject.destroy();\r
break;\r
},\r
\r
close : function(){\r
- if (this._interval) {\r
- X.Timer.remove( this._interval );\r
- delete this._interval;\r
- };\r
+ this.playing && this.pause();\r
this.proxy.dispatch( 'ended' );\r
this.kill();\r
},\r
\r
// SilverlightAudio.play\r
play : function(){\r
- if (!this._error) {\r
- this._lastUserAction = 'play';\r
- if (this._ended) {\r
- this.currentTime(this._startTime);\r
- }\r
- \r
- this._rawObject.play();\r
- this.proxy.dispatch( 'play' );\r
- \r
- if (!this._interval) {\r
- this._interval = X.Timer.add( 1000, 0, this, this._onInterval );\r
- }\r
- }\r
- },\r
- \r
- _onInterval : function(){\r
- this.isPlaying() && this.proxy.dispatch( 'timeupdate' );\r
+ var begin, end, halfway;\r
+ \r
+ // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
+ if( this.error ) return;\r
+ if( !this.duration ){\r
+ this.autoplay = true;\r
+ return;\r
+ };\r
+ \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.proxy.dispatch( 'play' );\r
+ \r
+ this.playing = true;\r
+ };\r
+ \r
+ halfway = end < this.duration;\r
+ this._timerID && X.Timer.remove( this._timerID );\r
+ \r
+ if( halfway ){\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
+ _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.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.proxy.dispatch( 'ended' ); \r
+ };\r
+ };\r
+ },\r
\r
// SilverlightAudio.pause\r
pause : function(){\r
- if (!this._error) {\r
+ if( !this.error ){\r
this._lastUserAction = 'pause';\r
+ this.playing = false;\r
this._paused = true;\r
- this._ended = false;\r
+ this._ended = false;\r
this._rawObject.pause();\r
this.proxy.dispatch( 'pause' );\r
- }\r
- },\r
- \r
- // SilverlightAudio.stop\r
- stop : function(){\r
- if (!this._error) {\r
- this._lastUserAction = 'stop';\r
- this._rawObject.stop();\r
- this._ended = true;\r
- }\r
- },\r
- \r
- // SilverlightAudio.loop\r
- loop : function(value){ // @param Boolean: true is loop\r
- // @return Boolean/void: true is loop\r
- if (value === undefined) {\r
- return this._loop;\r
- }\r
- this._loop = value;\r
- },\r
- \r
- // SilverlightAudio.state\r
- state : function(){ // @return Hash: { loop, error, paused, ended, source, duration }\r
- return {\r
- loop : this._loop,\r
- error : this._error,\r
- paused : this._paused,\r
- ended : this._ended,\r
- source : this._source,\r
- duration : this._duration\r
};\r
},\r
\r
- // SilverlightAudio.volume\r
- volume : function(value){ // @param Number: 0.0 ~ 1.0\r
- // @return Number/void:\r
- if (value === undefined) {\r
- return this._volume;\r
- }\r
- this._volume = value;\r
- this._rawObject.Volume = value;\r
- },\r
- \r
- // SilverlightAudio.startTime\r
- startTime : function(time){ // @param Number: time\r
- // @return Number/void:\r
- if (time === undefined) {\r
- return this._startTime;\r
- }\r
- this._startTime = time;\r
- },\r
- \r
- // SilverlightAudio.currentTime\r
- currentTime : function(time){ // @param Number: time\r
- // @return Number/void:\r
- var position = this._rawObject.Position; // [!] create instance\r
+ // SilverlightAudio.state\r
+ state : function( obj ){ // @return Hash: { loop, error, paused, ended, source, duration }\r
+ var result, end;\r
\r
- if (time === undefined) {\r
- return position.Seconds;\r
+ if( obj === undefined ){\r
+ return {\r
+ startTime : this.startTime,\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 // this._rawObject.NaturalDuration.Seconds;\r
+ };\r
};\r
-\r
- position.Seconds = time; // set current time\r
\r
- this._rawObject.Position = position; // [!] reattach instance\r
+ result = X_AudioWrapper_updateStates( this, obj );\r
+ \r
+ if( result & 2 ){ // seek\r
+ this.play();\r
+ } else {\r
+ if( result & 1 ){\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( end - this._rawObject.Position.Seconds * 1000, this, this._onEnded );\r
+ } else {\r
+ delete this._timerID;\r
+ };\r
+\r
+ };\r
+ if( result & 4 ){\r
+ this._rawObject.Volume = this.volume;\r
+ };\r
+ };\r
},\r
\r
- // SilverlightAudio.isPlaying\r
- isPlaying : function(){ // @return Boolean:\r
- if (!this._error && !this._paused && !this._ended) {\r
- return true;\r
+ // SilverlightAudio.currentTime\r
+ _currentTime : function( time ){ // @param Number: time\r
+ var position = this._rawObject.Position; // [!] create instance\r
+ \r
+ position.Seconds = time / 1000 | 0; // set current time\r
+ \r
+ this._rawObject.Position = position; // [!] reattach instance\r
}\r
- return false;\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