X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F07_audio%2F03_XSilverlightAudio.js;h=fd9f69493d6a0f262d4a31ce7bddc837ce46b56a;hb=0a4e04fb0af6e1b2e452d1a8c0822e723d32a0ee;hp=2e16da24c21cab1e93331eec662425c8edc7b27a;hpb=78f2b6b1bb07448d1c314775bf28a41b7111e386;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 2e16da2..fd9f694 100644 --- a/0.6.x/js/07_audio/03_XSilverlightAudio.js +++ b/0.6.x/js/07_audio/03_XSilverlightAudio.js @@ -15,29 +15,15 @@ var X_Audio_SLAudioWrapper, X_Audio_SLAudio_uid = 0; -if( X.Pulgin.SilverlightEnabled ){ +if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){ - // TODO X.Node.inherits - X_Audio_SLAudioWrapper = X.EventDispatcher.inherits( + // X.Node.inherits はできない。_rawObject は でなく silverlight + X_Audio_SLAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ]( 'X.AV.SilverlightAudioWrapper', - X.Class.POOL_OBJECT, + 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, - volume : 0.5, - + '_rawType' : X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT, + _onload : '', _callback : null, xnodeObject : null, @@ -48,13 +34,13 @@ if( X.Pulgin.SilverlightEnabled ){ _lastState : '', _interval : 0, // setInterval timer id - Constructor : function( proxy, source, option ){ + Constructor : function( target, source, option ){ + var xnodeScript; if( !X_Audio_SLAudio_uid ){ // source - // X_Node_systemNode.create( 'script', { type : 'text/xaml', id : 'silverlightaudio' } ) - // .text( ''); + //xnodeScript = X_Node_head[ 'create' ]( 'script', { type : 'text/xaml', id : 'silverlightaudio' } ); + //xnodeScript[ '_rawObject' ].innerHTML = ''; // html に以下を書いた // @@ -66,18 +52,19 @@ if( X.Pulgin.SilverlightEnabled ){ */ // TODO embed - this.proxy = proxy; + this.target = target || this; this._source = source; + // X.Audio._slOnload_ は不可 this._onload = 'XAudioSilverlightOnLoad' + ( ++X_Audio_SLAudio_uid ); - this._callback = window[ this._onload ] = X_Callback_create( this, this.onSLReady, [ option.autoplay ] ); + 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 @@ -86,37 +73,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, autoplay ){ - 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' ] ); - - autoplay && X.Timer.once( 100, this, this.play ); - }, + 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' ] ); + }, handleEvent : function( e ){ var lastState, currentState; + console.log( e.type ); switch( e.type ){ case 'MediaFailed' : @@ -124,27 +108,31 @@ 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.target[ '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.duration = this[ '_rawObject' ][ 'NaturalDuration' ][ 'Seconds' ] * 1000; + this.target[ 'asyncDispatch' ]( X_EVENT_READY ); + + this.autoplay && X_Timer_once( 16, this, this.actualPlay ); 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 @@ -153,15 +141,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.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING ); break; case 'seek' : - this.proxy.dispatch( 'seeking' ); + this.target[ 'dispatch' ]( X_EVENT_MEDIA_SEEKING ); break; case 'pause' : break; @@ -172,27 +162,32 @@ if( X.Pulgin.SilverlightEnabled ){ // media.play(file not found) -> 'Closed' // media.load -> 'Error' case 'Error': - case 'Closed': this.error = 4; + case 'Closed': + this.error = this.error || 2; this.playing = false; this._ended = true; this._paused = false; - this.proxy.dispatch( 'error' ); + this.target[ '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._ended = true; - this._paused = false; - this.proxy.dispatch( 'ended' ); - this._currentTime( this.startTime ); + //this.seekTime = 0; + this._ended = true; + this._paused = false; + //this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED ); + //this.setCurrentTime( this.startTime ); break; case 'pause': this._ended = false; @@ -207,75 +202,85 @@ 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.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING ); break; // stop() case 'Stopped': - this.playing = false; + this.playing && X_Timer_once( 16, this, this.actualPlay ); + return; + + //this.playing = false; this._ended = true; this._paused = false; - this._currentTime( this.startTime ); + //this.setCurrentTime( this.startTime ); break; }; break; - case X.Event.KILL_INSTANCE : + case X_EVENT_KILL_INSTANCE : + this.playing && this.target[ '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 ); }; - 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, halfway; - + actualPlay : function(){ + var begin, offset, end; + // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気 if( this.error ) return; + if( !this.duration ){ + this.autoplay = 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 ); + begin = X_AudioWrapper_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 ); - if( !this.playing ){ - this._rawObject.play(); - this.proxy.dispatch( 'play' ); - + 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._ended ){ + console.log( '[play] play()' + begin + ' -> ' + end ); + this[ '_rawObject' ].play(); this.playing = true; + this._ended = false; }; - halfway = end < this.duration; - this._timerID && X.Timer.remove( this._timerID ); + this._timerID && X_Timer_remove( this._timerID ); - if( halfway ){ - 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 ); }; }, @@ -284,98 +289,99 @@ if( X.Pulgin.SilverlightEnabled ){ delete this._interval; return X_Callback_UN_LISTEN; }; - this.proxy.dispatch( 'timeupdate' ); + this.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING ); }, _onEnded : function(){ - var time; + var time, end; delete this._timerID; if( this.playing ){ + //console.log( '> end ' + X_AudioWrapper_getEndTime( this ) + ' current:' + ( this.getActualCurrentTime() ) ); + time = this.getActualCurrentTime(); + + 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.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING ); + this._timerID = X_Timer_once( X_AudioWrapper_getEndTime( this ) - time, this, this._onEnded ); + return; + }; - time = this._rawObject.Position.Seconds * 1000 - X_AudioWrapper_getEndTime( this ) | 0; - if( time < -16 ){ - console.log( '> onEnd ' + time ); - this._timerID = X.Timer.once( -time, this, this._onEnded ); + time -= X_AudioWrapper_getEndTime( this ); + if( time < 0 ){ + 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.play(); - this.proxy.dispatch( 'looped' ); + if( this.autoLoop ){ + console.log( '========= loop?' ); + if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){ + console.log( '========== loopした' ); + this.looped = true; + this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED ); + this.actualPlay(); + }; } else { - this.pause(); - this.proxy.dispatch( 'ended' ); + console.log( '========= pause' ); + this.actualPause(); + this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED ); }; }; }, // SilverlightAudio.pause - pause : function(){ - if( !this.error ){ - this._lastUserAction = 'pause'; - this.playing = false; - this._paused = true; - this._ended = false; - this._rawObject.pause(); - this.proxy.dispatch( 'pause' ); - }; - }, - - // SilverlightAudio.state - state : function( obj ){ // @return Hash: { loop, error, paused, ended, source, duration } - var result, end; + actualPause : function(){ + if( this.error || !this.playing ) return; - 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._rawObject.Position.Seconds * 1000, - - - 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; - }; + this._lastUserAction = 'pause'; + this.seekTime = this.getActualCurrentTime(); + this.playing = false; + this._paused = true; + this._ended = false; + + this[ '_rawObject' ].pause(); + //this.target[ 'dispatch' ]( 'pause' ); + }, + getActualCurrentTime : function(){ + return this[ '_rawObject' ][ 'Position' ][ 'Seconds' ] * 1000 | 0; + }, + + afterUpdateState : function( result ){ + if( result & 3 ){ // seek + this.actualPlay(); + } 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.getActualCurrentTime(), this, this._onEnded ); + } else { + delete this._timerID; }; - if( result & 4 ){ - this._rawObject.Volume = this.volume; - }; - }; + } 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 } } @@ -390,7 +396,7 @@ if( X.Pulgin.SilverlightEnabled ){ detect : function( proxy, source, ext ){ var ok = ext === 'mp3' || ext === 'wma' || ext === 'wav'; - proxy.asyncDispatch( ok ? 'support' : 'nosupport' ); + proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : ok } ); }, klass : X_Audio_SLAudioWrapper