* SilverlLight5 ie6&7(ietester,winxp), ie8(winxp) で動作確認。firefox32 では動作しない。(4以下の方がよい?)\r
*/\r
\r
-var X_Audio_SLAudioWrapper,\r
- X_Audio_SLAudio_uid = 0;\r
+var X_SLAudio,\r
+ X_SLAudio_uid = 0;\r
\r
-if( X.Pulgin.SilverlightEnabled ){\r
+if( X_Pulgin_SILVER_LIGHT_VERSION ){\r
+ \r
+ X_TEMP.slaudioInit = function(){\r
+ //\r
+ // http://blog.yuhiisk.com/archive/2014/12/20/dynamic-loading-and-complete-processing-of-script.html\r
+ var s;\r
+ \r
+ if( X_UA[ 'IE' ] < 9 ){\r
+ s = document.createElement( '<script id="silverlightaudio" type="text/xaml"></script>' );\r
+ } else {\r
+ s = document.createElement( 'script' );\r
+ s.id = 'silverlightaudio';\r
+ s.type = 'text/xaml';\r
+ };\r
+ \r
+ X_elmHead.appendChild( s );\r
+ s.text = '<Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas>';\r
+ \r
+ delete X_TEMP.slaudioInit;\r
+ };\r
\r
// X.Node.inherits はできない。_rawObject は <object> でなく silverlight\r
- X_Audio_SLAudioWrapper = X.EventDispatcher.inherits(\r
- 'X.AV.SilverlightAudioWrapper',\r
- X.Class.POOL_OBJECT,\r
+ X_SLAudio = X_AudioBase[ 'inherits' ](\r
+ 'X.SilverlightAudio',\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
+ '_rawType' : X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT,\r
+\r
_onload : '',\r
_callback : null, \r
xnodeObject : null,\r
_lastState : '',\r
_interval : 0, // setInterval timer id\r
\r
- Constructor : function( proxy, source, option ){\r
+ 'Constructor' : function( disatcher, source, option ){\r
+ !X_SLAudio_uid && X_TEMP.slaudioInit();\r
\r
- if( !X_Audio_SLAudio_uid ){\r
- // source\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
- // html に以下を書いた \r
- // <script id="silverlightaudio" type="text/xaml"><Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas></script>\r
- };\r
/*\r
* [Silverlight 2]JavaScriptコードからSilverlightのオブジェクトを利用するには?[C#、VB]\r
* http://www.atmarkit.co.jp/fdotnet/dotnettips/902slobjcallfromjs/slobjcallfromjs.html\r
* このページのサンプルは sl5+firefox32 環境で動いている。xaml を js から利用する形ではなく、.xap を sl4 以下で作るのがよさそう.\r
*/\r
+ this.disatcher = disatcher || this;\r
+ this._source = source;\r
+ // X.Audio._slOnload_ は不可\r
+ this._onload = 'XAudioSilverlightOnLoad' + ( ++X_SLAudio_uid );\r
+ this._callback = window[ this._onload ] = X_Closure_create( this, this.onSLReady );\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 );\r
this.xnodeObject = X_Node_body\r
- .create( 'object', {\r
+ [ 'create' ]( 'object', {\r
type : 'application/x-silverlight-2',\r
data : 'data:application/x-silverlight-2,',\r
width : 1,\r
height : 1\r
})\r
- .html(\r
+ [ 'html' ](\r
'<param name="background" value="#00000000">' + // transparent\r
'<param name="windowless" value="true">' +\r
'<param name="source" value="#silverlightaudio">' + // XAML ID\r
//'<param value="true" name="autoUpgrade">' +\r
//'<param name="onerror" value="slerror">' // bond to global\r
);\r
- X_AudioWrapper_updateStates( this, option );\r
+ this.setState( option );\r
\r
- this.listenOnce( X.Event.KILL_INSTANCE );\r
+ this[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE );\r
},\r
\r
- onSLReady : function( sender ){\r
- if( !this._onload ) return;\r
- \r
- window[ this._onload ] = null;\r
- delete this._onload;\r
- X_Callback_correct( this._callback );\r
- delete this._callback;\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
- '</Canvas>'));\r
- \r
- this._rawObject = sender.findName('media'); // x:Name='media'\r
- \r
- this.listen( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] );\r
- },\r
+ onSLReady : function( sender ){\r
+ if( !this._onload ) return;\r
+ \r
+ window[ this._onload ] = null;\r
+ delete this._onload;\r
+ X_Closure_correct( this._callback );\r
+ delete this._callback;\r
+ \r
+ sender[ 'children' ][ 'add' ](\r
+ sender[ 'GetHost' ]()[ 'content' ][ 'CreateFromXaml' ](\r
+ '<Canvas xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">' +\r
+ '<MediaElement x:Name="media" Source="' + this._source + '" Volume="' + this.gain + '" AutoPlay="false" />' +\r
+ '</Canvas>'));\r
+ \r
+ this[ '_rawObject' ] = sender[ 'findName' ]( 'media' ); // x:Name='media'\r
+ \r
+ this[ 'listen' ]( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] );\r
+ },\r
\r
handleEvent : function( e ){\r
var lastState, currentState;\r
\r
+ console.log( e.type );\r
switch( e.type ){\r
\r
case 'MediaFailed' :\r
this.playing = false;\r
this._ended = true;\r
this._paused = false;\r
- this.proxy.dispatch( 'error' ); // open failed\r
+ if( this.playing ){\r
+ //X_Timer_once( 16, this, this.actualPlay );\r
+ } else {\r
+ this.disatcher[ 'dispatch' ]( X_EVENT_ERROR ); // open failed\r
+ this[ 'kill' ](); \r
+ };\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 * 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
+ this.duration = this[ '_rawObject' ][ 'NaturalDuration' ][ 'Seconds' ] * 1000;\r
+ this.disatcher[ 'asyncDispatch' ]( X_EVENT_READY );\r
break;\r
\r
- case 'MediaEnded' : \r
- this.loop && this.playing && this.play();\r
+ case 'MediaEnded' :\r
+ //console.log( ' > ' + this.autoLoop + ' error:' + this.error );\r
+ //this.autoLoop && /* this.playing && */ this.actualPlay();\r
+ this._ended = true;\r
break;\r
\r
case 'CurrentStateChanged' :\r
lastState = this._lastState,\r
- currentState = this._rawObject.CurrentState;\r
+ currentState = this[ '_rawObject' ][ 'CurrentState' ];\r
\r
// ignore consecutive events or 'Closed' == 'Error'\r
if( lastState === currentState\r
};\r
this._lastState = currentState; // update last state\r
\r
+ console.log( ' > ' + currentState + ' - ' + this._lastUserAction );\r
+ \r
switch( currentState ){\r
case 'Buffering' :\r
case 'Opening' :\r
switch( this._lastUserAction ){\r
case 'play' :\r
- this.proxy.dispatch( 'waiting' );\r
+ this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
break;\r
case 'seek' :\r
- this.proxy.dispatch( 'seeking' );\r
+ this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_SEEKING );\r
break;\r
case 'pause' :\r
break;\r
this.playing = false;\r
this._ended = true;\r
this._paused = false;\r
- this.proxy.dispatch( 'error' );\r
+ this.disatcher[ 'dispatch' ]( X_EVENT_ERROR );\r
+ this[ 'kill' ]();\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
- this.playing = false;\r
+ \r
+ this.playing && X_Timer_once( 16, this, this.actualPlay );\r
+ //this.playing = false;\r
\r
switch( this._lastUserAction ){\r
case 'play': // play() -> file end -> event('ended')\r
case 'seek':\r
- this.seekTime = 0;\r
- this._ended = true;\r
- this._paused = false;\r
- this.proxy.dispatch( 'ended' );\r
- this._currentTime( this.startTime );\r
+ //this.seekTime = 0;\r
+ this._ended = true;\r
+ this._paused = false;\r
+ //this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
+ //this.setCurrentTime( this.startTime );\r
break;\r
case 'pause':\r
this._ended = false;\r
// media.play -> 'Playing'\r
case 'Playing':\r
this.error = 0;\r
- this.playing = true;\r
+ //this.playing = true;\r
this._ended = false;\r
this._paused = false;\r
- this.proxy.dispatch( 'playing' );\r
+ this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );\r
break;\r
\r
// stop()\r
case 'Stopped':\r
- this.playing = false;\r
- this._ended = true;\r
- this._paused = false;\r
- this._currentTime( this.startTime );\r
+ this.playing && X_Timer_once( 16, this, this.actualPlay );\r
+ \r
+ //this.playing = false;\r
+ //this._ended = true;\r
+ //this._paused = false;\r
+ //this.setCurrentTime( this.startTime );\r
break;\r
};\r
break;\r
\r
- case X.Event.KILL_INSTANCE :\r
+ case X_EVENT_KILL_INSTANCE :\r
+ this.playing && this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
+ this.playing && this.actualPause();\r
+ \r
if( this._onload ){\r
// window への delete に ie5 は対応しないが、そもそも ie5 は Silverlight に非対応\r
- delete window[ this._onload ];\r
+ window[ this._onload ] = null;\r
delete this._onload;\r
- X_Callback_correct( this._callback );\r
+ X_Closure_correct( this._callback );\r
};\r
- this.xnodeObject.destroy();\r
+ this.xnodeObject[ 'kill' ]();\r
break;\r
};\r
},\r
\r
- close : function(){\r
- this.playing && this.pause();\r
- this.proxy.dispatch( 'ended' );\r
- this.kill();\r
- },\r
- \r
// SilverlightAudio.play\r
- play : function(){\r
- var begin, end;\r
- \r
+ actualPlay : function(){\r
+ var begin, offset, end;\r
+\r
// もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
if( this.error ) return;\r
if( !this.duration ){\r
- this.autoplay = true;\r
+ this._playReserved = 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
+ end = X_Audio_getEndTime( this );\r
+ begin = X_Audio_getStartTime( this, end, true ) | 0;\r
+\r
+ // 1 秒以下は指定できないため四捨五入\r
+ begin = ( begin / 1000 | 0 ) * 1000 + ( 500 < begin % 1000 ? 1000 : 0 ); \r
+\r
+ this[ '_rawObject' ][ 'Volume' ] = this.gain;\r
+ \r
+ this.setCurrentTime( this._beginTime = begin );\r
+ \r
+ console.log( '[play] ' + begin + ' -> ' + end );\r
+ \r
+ /*\r
+ if( offset = begin - this.getActualCurrentTime() ){\r
+ this.setCurrentTime( begin + offset );\r
+ console.log( ' [差補正] ' + offset + ' ct:' + this.getActualCurrentTime() + ' begin:' + begin );\r
+ this._beginTime = begin = this.getActualCurrentTime();\r
+ };*/\r
\r
- if( !this.playing ){\r
- this._rawObject.play();\r
- this.proxy.dispatch( 'play' );\r
- \r
+ if( !this.playing || this._ended ){\r
+ console.log( '[play] play()' + begin + ' -> ' + end );\r
+ this[ '_rawObject' ].play();\r
this.playing = true;\r
+ this._ended = false;\r
};\r
\r
- this._timerID && X.Timer.remove( this._timerID );\r
+ this._timerID && X_Timer_remove( this._timerID );\r
\r
- if( end < this.duration ){\r
- this._timerID = X.Timer.once( end - begin, this, this._onEnded );\r
- } else {\r
- delete this._timerID;\r
- };\r
+ this._timerID = X_Timer_once( end - begin, this, this._onEnded );\r
\r
if( !this._interval ){\r
- this._interval = X.Timer.add( 1000, 0, this, this._onInterval );\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
+ return X_CALLBACK_UN_LISTEN;\r
};\r
- this.proxy.dispatch( 'timeupdate' );\r
+ this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );\r
},\r
\r
_onEnded : function(){\r
- var time;\r
+ var time, end;\r
delete this._timerID;\r
\r
if( this.playing ){\r
+ //console.log( '> end ' + X_Audio_getEndTime( this ) + ' current:' + ( this.getActualCurrentTime() ) );\r
+ time = this.getActualCurrentTime();\r
\r
- time = this._rawObject.Position.Seconds * 1000 - X_AudioWrapper_getEndTime( this ) | 0;\r
- if( time < 0 ){\r
- console.log( '> onEnd ' + time );\r
- this._timerID = X.Timer.once( -time, this, this._onEnded );\r
+ if( time < this._beginTime ){\r
+ console.log( '== waiting ' + time + ' < begin:' + this._beginTime );\r
+ this.setCurrentTime( this._beginTime );\r
+ time = this.getActualCurrentTime();\r
+ console.log( ' > ' + time );\r
+ this._ended && this[ '_rawObject' ].play();\r
+ this._ended = false;\r
+ this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
+ this._timerID = X_Timer_once( X_Audio_getEndTime( this ) - time, this, this._onEnded );\r
+ return;\r
+ };\r
+ \r
+ time -= X_Audio_getEndTime( this );\r
+ if( time < -50 ){\r
+ console.log( ' > まだ終わらない ' + time );\r
+ this._ended && this[ '_rawObject' ].play();\r
+ this._ended = false;\r
+ this._timerID = X_Timer_once( -time, this, this._onEnded );\r
return;\r
};\r
\r
- if( this.loop ){\r
- this.looped = true;\r
- ( this.proxy.dispatch( 'looped' ) & X.Callback.PREVENT_DEFAULT ) || this.play();\r
+ if( this.autoLoop ){\r
+ console.log( '========= loop?' );\r
+ if( !( this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){\r
+ console.log( '========== loopした' );\r
+ this.looped = true;\r
+ this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
+ this.actualPlay();\r
+ };\r
} else {\r
- this.pause();\r
- this.proxy.dispatch( 'ended' ); \r
+ console.log( '========= pause' );\r
+ this.actualPause();\r
+ this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
};\r
};\r
},\r
\r
// SilverlightAudio.pause\r
- pause : function(){\r
- if( this.error || !this.playing ) return;\r
+ actualPause : function(){\r
+ if( this.error ) return;\r
\r
this._lastUserAction = 'pause';\r
- this.seekTime = this.state().currentTime;\r
this.playing = false;\r
this._paused = true;\r
this._ended = false;\r
- \r
- this._rawObject.pause();\r
- this.proxy.dispatch( 'pause' );\r
+ \r
+ this[ '_rawObject' ].pause();\r
+ },\r
+\r
+ getActualCurrentTime : function(){\r
+ return this[ '_rawObject' ][ 'Position' ][ 'Seconds' ] * 1000 | 0;\r
},\r
\r
- // SilverlightAudio.state\r
- state : function( obj ){ // @return Hash: { loop, error, paused, ended, source, duration }\r
- var result, end;\r
+ afterUpdateState : function( result ){\r
+ var end, halfway;\r
\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.playing ? this._rawObject.Position.Seconds * 1000 : this.seekTime,\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
- 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
+ if( result & 3 ){ // seek\r
+ this.actualPlay();\r
+ } else\r
+ if( result & 1 ){\r
+ end = X_Audio_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.getActualCurrentTime(), this, this._onEnded );\r
+ } else {\r
+ delete this._timerID;\r
};\r
- };\r
+ } else\r
+ if( result & 4 ){\r
+ this[ '_rawObject' ][ 'Volume' ] = this.gain;\r
+ }; \r
},\r
\r
// SilverlightAudio.currentTime\r
- _currentTime : function( time ){ // @param Number: time\r
- var position = this._rawObject.Position; // [!] create instance\r
+ setCurrentTime : function( time ){ // @param Number: time\r
+ var position = this[ '_rawObject' ][ 'Position' ]; // [!] create instance\r
\r
- position.Seconds = time / 1000 | 0; // set current time\r
+ position[ 'Seconds' ] = time / 1000 | 0; // set current time\r
\r
- this._rawObject.Position = position; // [!] reattach instance\r
+ this[ '_rawObject' ][ 'Position' ] = position; // [!] reattach instance\r
}\r
\r
}\r
);\r
\r
+ /*\r
function slerror(){\r
alert( 'slerror' );\r
- };\r
+ }; */\r
\r
X_Audio_BACKENDS.push( {\r
- backendName : 'Silverlight Audio',\r
+ backendID : 8,\r
+ \r
+ backendName : 'Silverlight',\r
+\r
+ canPlay : {\r
+ 'mp3' : true,\r
+ 'm4a' : true,\r
+ 'wma' : true,\r
+ 'wav' : true\r
+ },\r
\r
detect : function( proxy, source, ext ){\r
- var ok = ext === 'mp3' || ext === 'wma' || ext === 'wav';\r
- proxy.asyncDispatch( ok ? 'support' : 'nosupport' ); \r
+ // TODO source = .mp3#CBR\r
+ // 得意度で返す\r
+ proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : ext === 'mp3' || ext === 'm4a' || ext === 'wma' || ext === 'wav' } ); \r
},\r
\r
- klass : X_Audio_SLAudioWrapper\r
+ klass : X_SLAudio\r
\r
} );\r
\r