X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F07_audio%2F03_XSilverlightAudio.js;h=09fa4cd936952e3d8ffdc22e9761e62ec61bad69;hb=HEAD;hp=02c1ec1487e48aabcd71da3c91bc78bbfd603e80;hpb=a09eec2e19c91d656e784e71c73acacb8368f491;p=pettanr%2FclientJs.git diff --git a/0.6.x/js/07_audio/03_XSilverlightAudio.js b/0.6.x/js/07_audio/03_XSilverlightAudio.js index 02c1ec1..09fa4cd 100644 --- a/0.6.x/js/07_audio/03_XSilverlightAudio.js +++ b/0.6.x/js/07_audio/03_XSilverlightAudio.js @@ -12,33 +12,37 @@ * SilverlLight5 ie6&7(ietester,winxp), ie8(winxp) で動作確認。firefox32 では動作しない。(4以下の方がよい?) */ -var X_Audio_SLAudioWrapper, - X_Audio_SLAudio_uid = 0; +var X_SLAudio, + X_SLAudio_uid = 0; -if( X.Pulgin.SilverlightEnabled ){ +if( X_Plugin_SILVER_LIGHT_VERSION ){ + + X_TEMP.slaudioInit = function(){ + // + // http://blog.yuhiisk.com/archive/2014/12/20/dynamic-loading-and-complete-processing-of-script.html + var s; + + if( X_UA[ 'IE' ] < 9 ){ + s = document.createElement( '' ); + } else { + s = document.createElement( 'script' ); + s.id = 'silverlightaudio'; + s.type = 'text/xaml'; + }; + + X_elmHead.appendChild( s ); + s.text = ''; + + delete X_TEMP.slaudioInit; + }; // X.Node.inherits はできない。_rawObject は でなく silverlight - X_Audio_SLAudioWrapper = X.EventDispatcher.inherits( - 'X.AV.SilverlightAudioWrapper', - X.Class.POOL_OBJECT, + X_SLAudio = X_AudioBase[ 'inherits' ]( + 'X.SilverlightAudio', + X_Class.POOL_OBJECT, { - _isSilverlight : true, // for X.EventDispatcher.listen - proxy : null, - - startTime : 0, - endTime : -1, - loopStartTime : -1, - loopEndTime : -1, - seekTime : -1, - duration : 0, - - playing : false, - error : 0, - loop : false, - looped : false, - autoplay : false, - volume : 0.5, - + '_rawType' : X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT, + _onload : '', _callback : null, xnodeObject : null, @@ -49,36 +53,29 @@ if( X.Pulgin.SilverlightEnabled ){ _lastState : '', _interval : 0, // setInterval timer id - Constructor : function( proxy, source, option ){ + 'Constructor' : function( dispatcher, source, option ){ + !X_SLAudio_uid && X_TEMP.slaudioInit(); - if( !X_Audio_SLAudio_uid ){ - // source - // X_Node_systemNode.create( 'script', { type : 'text/xaml', id : 'silverlightaudio' } ) - // .text( ''); - - // html に以下を書いた - // - }; /* * [Silverlight 2]JavaScriptコードからSilverlightのオブジェクトを利用するには?[C#、VB] * http://www.atmarkit.co.jp/fdotnet/dotnettips/902slobjcallfromjs/slobjcallfromjs.html * このページのサンプルは sl5+firefox32 環境で動いている。xaml を js から利用する形ではなく、.xap を sl4 以下で作るのがよさそう. */ + this.dispatcher = dispatcher || this; + this._source = source; + // X.Audio._slOnload_ は不可 + this._onload = 'XAudioSilverlightOnLoad' + ( ++X_SLAudio_uid ); + this._callback = window[ this._onload ] = X_Closure_create( this, this.onSLReady ); // TODO embed - this.proxy = proxy; - this._source = source; - this._onload = 'XAudioSilverlightOnLoad' + ( ++X_Audio_SLAudio_uid ); - this._callback = window[ this._onload ] = X_Callback_create( this, this.onSLReady ); this.xnodeObject = X_Node_body - .create( 'object', { + [ 'create' ]( 'object', { type : 'application/x-silverlight-2', data : 'data:application/x-silverlight-2,', width : 1, height : 1 }) - .html( + [ 'html' ]( '' + // transparent '' + '' + // XAML ID @@ -87,35 +84,34 @@ if( X.Pulgin.SilverlightEnabled ){ //'' + //'' // bond to global ); - X_AudioWrapper_updateStates( this, option ); + this.setState( option ); - this.listenOnce( X.Event.KILL_INSTANCE ); + this[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE ); }, - onSLReady : function( sender ){ - if( !this._onload ) return; - - window[ this._onload ] = null; - delete this._onload; - X_Callback_correct( this._callback ); - delete this._callback; - - sender.children.add( - sender.GetHost(). - content. - CreateFromXaml( - '' + - '' + - '')); - - this._rawObject = sender.findName('media'); // x:Name='media' - - this.listen( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] ); - }, + onSLReady : function( sender ){ + if( !this._onload ) return; + + window[ this._onload ] = null; + delete this._onload; + X_Closure_correct( this._callback ); + delete this._callback; + + sender[ 'children' ][ 'add' ]( + sender[ 'GetHost' ]()[ 'content' ][ 'CreateFromXaml' ]( + '' + + '' + + '')); + + this[ '_rawObject' ] = sender[ 'findName' ]( 'media' ); // x:Name='media' + + this[ 'listen' ]( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] ); + }, handleEvent : function( e ){ var lastState, currentState; + console.log( e.type ); switch( e.type ){ case 'MediaFailed' : @@ -123,29 +119,29 @@ if( X.Pulgin.SilverlightEnabled ){ this.playing = false; this._ended = true; this._paused = false; - this.proxy.dispatch( 'error' ); // open failed + if( this.playing ){ + //X_Timer_once( 16, this, this.actualPlay ); + } else { + this.dispatcher[ 'dispatch' ]( X_EVENT_ERROR ); // open failed + this[ 'kill' ](); + }; break; case 'MediaOpened' : // http://msdn.microsoft.com/ja-jp/library/bb979710(VS.95).aspx - this.duration = this._rawObject.NaturalDuration.Seconds * 1000; - // TODO 'canplaythrough' - this.proxy.asyncDispatch( 'loadstart' ); - this.proxy.asyncDispatch( 'loadedmetadata' ); - this.proxy.asyncDispatch( 'loadeddata' ); - this.proxy.asyncDispatch( 'canplay' ); - this.proxy.asyncDispatch( 'canplaythrough' ); - - this.autoplay && X.Timer.once( 16, this, this.play ); + this.duration = this[ '_rawObject' ][ 'NaturalDuration' ][ 'Seconds' ] * 1000; + this.dispatcher[ 'asyncDispatch' ]( X_EVENT_READY ); break; - case 'MediaEnded' : - this.loop && this.playing && this.play(); + case 'MediaEnded' : + //console.log( ' > ' + this.autoLoop + ' error:' + this.error ); + //this.autoLoop && /* this.playing && */ this.actualPlay(); + this._ended = true; break; case 'CurrentStateChanged' : lastState = this._lastState, - currentState = this._rawObject.CurrentState; + currentState = this[ '_rawObject' ][ 'CurrentState' ]; // ignore consecutive events or 'Closed' == 'Error' if( lastState === currentState @@ -154,15 +150,17 @@ if( X.Pulgin.SilverlightEnabled ){ }; this._lastState = currentState; // update last state + console.log( ' > ' + currentState + ' - ' + this._lastUserAction ); + switch( currentState ){ case 'Buffering' : case 'Opening' : switch( this._lastUserAction ){ case 'play' : - this.proxy.dispatch( 'waiting' ); + this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_WAITING ); break; case 'seek' : - this.proxy.dispatch( 'seeking' ); + this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_SEEKING ); break; case 'pause' : break; @@ -179,23 +177,26 @@ if( X.Pulgin.SilverlightEnabled ){ this.playing = false; this._ended = true; this._paused = false; - this.proxy.dispatch( 'error' ); + this.dispatcher[ 'dispatch' ]( X_EVENT_ERROR ); + this[ 'kill' ](); break; // userAction.pause() -> MediaState('Paused') -> x // userAction.stop() -> MediaState('Paused') -> x // userAction.play() + file end -> MediaState('Paused') -> uueventfire('ended') case 'Paused': - this.playing = false; + + this.playing && X_Timer_once( 16, this, this.actualPlay ); + //this.playing = false; switch( this._lastUserAction ){ case 'play': // play() -> file end -> event('ended') case 'seek': - this.seekTime = 0; - this._ended = true; - this._paused = false; - this.proxy.dispatch( 'ended' ); - this._currentTime( this.startTime ); + //this.seekTime = 0; + this._ended = true; + this._paused = false; + //this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_ENDED ); + //this.setCurrentTime( this.startTime ); break; case 'pause': this._ended = false; @@ -210,197 +211,212 @@ if( X.Pulgin.SilverlightEnabled ){ // media.play -> 'Playing' case 'Playing': this.error = 0; - this.playing = true; + //this.playing = true; this._ended = false; this._paused = false; - this.proxy.dispatch( 'playing' ); + this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING ); break; // stop() case 'Stopped': - this.playing = false; - this._ended = true; - this._paused = false; - this._currentTime( this.startTime ); + this.playing && X_Timer_once( 16, this, this.actualPlay ); + + //this.playing = false; + //this._ended = true; + //this._paused = false; + //this.setCurrentTime( this.startTime ); break; }; break; - case X.Event.KILL_INSTANCE : + case X_EVENT_KILL_INSTANCE : + this.playing && this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_ENDED ); + this.playing && this.actualPause(); + if( this._onload ){ // window への delete に ie5 は対応しないが、そもそも ie5 は Silverlight に非対応 - delete window[ this._onload ]; + window[ this._onload ] = null; delete this._onload; - X_Callback_correct( this._callback ); + X_Closure_correct( this._callback ); }; - this.xnodeObject.destroy(); + this.xnodeObject[ 'kill' ](); break; }; }, - close : function(){ - this.playing && this.pause(); - this.proxy.dispatch( 'ended' ); - this.kill(); - }, - // SilverlightAudio.play - play : function(){ - var begin, end; - + actualPlay : function(){ + var begin, offset, end; + // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気 if( this.error ) return; if( !this.duration ){ - this.autoplay = true; + this._playReserved = true; return; }; this._lastUserAction = 0 <= this.seekTime ? 'seek' : 'play'; - end = X_AudioWrapper_getEndTime( this ); - begin = X_AudioWrapper_getStartTime( this, end, true ); - - console.log( '[SLAudio] play ' + begin + ' -> ' + end ); - - this._rawObject.Volume = this.volume; - this._currentTime( begin ); + end = X_Audio_getEndTime( this ); + begin = X_Audio_getStartTime( this, end, true ) | 0; + + // 1 秒以下は指定できないため四捨五入 + begin = ( begin / 1000 | 0 ) * 1000 + ( 500 < begin % 1000 ? 1000 : 0 ); + + this[ '_rawObject' ][ 'Volume' ] = this.gain; + + this.setCurrentTime( this._beginTime = begin ); + + console.log( '[play] ' + begin + ' -> ' + end ); + + /* + if( offset = begin - this.getActualCurrentTime() ){ + this.setCurrentTime( begin + offset ); + console.log( ' [差補正] ' + offset + ' ct:' + this.getActualCurrentTime() + ' begin:' + begin ); + this._beginTime = begin = this.getActualCurrentTime(); + };*/ - if( !this.playing ){ - this._rawObject.play(); - this.proxy.dispatch( 'play' ); - + if( !this.playing || this._ended ){ + console.log( '[play] play()' + begin + ' -> ' + end ); + this[ '_rawObject' ].play(); this.playing = true; + this._ended = false; }; - this._timerID && X.Timer.remove( this._timerID ); + this._timerID && X_Timer_remove( this._timerID ); - if( end < this.duration ){ - this._timerID = X.Timer.once( end - begin, this, this._onEnded ); - } else { - delete this._timerID; - }; + this._timerID = X_Timer_once( end - begin, this, this._onEnded ); if( !this._interval ){ - this._interval = X.Timer.add( 1000, 0, this, this._onInterval ); + this._interval = X_Timer_add( 1000, 0, this, this._onInterval ); }; }, _onInterval : function(){ if( !this.playing ){ delete this._interval; - return X_Callback_UN_LISTEN; + return X_CALLBACK_UN_LISTEN; }; - this.proxy.dispatch( 'timeupdate' ); + this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING ); }, _onEnded : function(){ - var time; + var time, end; delete this._timerID; if( this.playing ){ + //console.log( '> end ' + X_Audio_getEndTime( this ) + ' current:' + ( this.getActualCurrentTime() ) ); + time = this.getActualCurrentTime(); - time = this._rawObject.Position.Seconds * 1000 - X_AudioWrapper_getEndTime( this ) | 0; - if( time < 0 ){ - console.log( '> onEnd ' + time ); - this._timerID = X.Timer.once( -time, this, this._onEnded ); + if( time < this._beginTime ){ + console.log( '== waiting ' + time + ' < begin:' + this._beginTime ); + this.setCurrentTime( this._beginTime ); + time = this.getActualCurrentTime(); + console.log( ' > ' + time ); + this._ended && this[ '_rawObject' ].play(); + this._ended = false; + this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_WAITING ); + this._timerID = X_Timer_once( X_Audio_getEndTime( this ) - time, this, this._onEnded ); + return; + }; + + time -= X_Audio_getEndTime( this ); + if( time < -50 ){ + console.log( ' > まだ終わらない ' + time ); + this._ended && this[ '_rawObject' ].play(); + this._ended = false; + this._timerID = X_Timer_once( -time, this, this._onEnded ); return; }; - if( this.loop ){ - this.looped = true; - ( this.proxy.dispatch( 'looped' ) & X.Callback.PREVENT_DEFAULT ) || this.play(); + if( this.autoLoop ){ + console.log( '========= loop?' ); + if( !( this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){ + console.log( '========== loopした' ); + this.looped = true; + this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED ); + this.actualPlay(); + }; } else { - this.pause(); - this.proxy.dispatch( 'ended' ); + console.log( '========= pause' ); + this.actualPause(); + this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_ENDED ); }; }; }, // SilverlightAudio.pause - pause : function(){ - if( this.error || !this.playing ) return; + actualPause : function(){ + if( this.error ) return; this._lastUserAction = 'pause'; - this.seekTime = this.state().currentTime; this.playing = false; this._paused = true; this._ended = false; - - this._rawObject.pause(); - this.proxy.dispatch( 'pause' ); + + this[ '_rawObject' ].pause(); + }, + + getActualCurrentTime : function(){ + return this[ '_rawObject' ][ 'Position' ][ 'Seconds' ] * 1000 | 0; }, - // SilverlightAudio.state - state : function( obj ){ // @return Hash: { loop, error, paused, ended, source, duration } - var result, end; + afterUpdateState : function( result ){ + var end, halfway; - if( obj === undefined ){ - return { - startTime : 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, - - currentTime : this.playing ? this._rawObject.Position.Seconds * 1000 : this.seekTime, - - - loop : this.loop, - looped : this.looped, - volume : this.volume, - error : this.error, - playing : this.playing, - duration : this.duration // this._rawObject.NaturalDuration.Seconds; - }; - }; - - result = X_AudioWrapper_updateStates( this, obj ); - - if( result & 2 ){ // seek - this.play(); - } else { - if( result & 1 ){ - end = X_AudioWrapper_getEndTime( this ); - halfway = end < this.duration; - this._timerID && X.Timer.remove( this._timerID ); - - if( halfway ){ - this._timerID = X.Timer.once( end - this._rawObject.Position.Seconds * 1000, this, this._onEnded ); - } else { - delete this._timerID; - }; - - }; - if( result & 4 ){ - this._rawObject.Volume = this.volume; + if( result & 3 ){ // seek + this.actualPlay(); + } else + if( result & 1 ){ + end = X_Audio_getEndTime( this ); + halfway = end < this.duration; + this._timerID && X_Timer_remove( this._timerID ); + + if( halfway ){ + this._timerID = X_Timer_once( end - this.getActualCurrentTime(), this, this._onEnded ); + } else { + delete this._timerID; }; - }; + } else + if( result & 4 ){ + this[ '_rawObject' ][ 'Volume' ] = this.gain; + }; }, // SilverlightAudio.currentTime - _currentTime : function( time ){ // @param Number: time - var position = this._rawObject.Position; // [!] create instance + setCurrentTime : function( time ){ // @param Number: time + var position = this[ '_rawObject' ][ 'Position' ]; // [!] create instance - position.Seconds = time / 1000 | 0; // set current time + position[ 'Seconds' ] = time / 1000 | 0; // set current time - this._rawObject.Position = position; // [!] reattach instance + this[ '_rawObject' ][ 'Position' ] = position; // [!] reattach instance } } ); + /* function slerror(){ alert( 'slerror' ); - }; + }; */ X_Audio_BACKENDS.push( { - backendName : 'Silverlight Audio', + backendID : 8, + + backendName : 'Silverlight', + + canPlay : { + 'mp3' : true, + 'm4a' : true, + 'wma' : true, + 'wav' : true + }, - detect : function( proxy, source, ext ){ - var ok = ext === 'mp3' || ext === 'wma' || ext === 'wav'; - proxy.asyncDispatch( ok ? 'support' : 'nosupport' ); + detect : function( proxy, ext, hash ){ + proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : ext === 'mp3' || ext === 'm4a' || ext === 'wma' || ext === 'wav' } ); }, - klass : X_Audio_SLAudioWrapper + klass : X_SLAudio } );