X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F07_audio%2F00_XAudio.js;h=4664aa0b2cc0f9e6f1e700c0b1f217f7376f7950;hb=4e4ab3be10850546063d4a4b93250ed142bb8cd2;hp=acae4369fc7ae69a8b9c76763669835473df5a81;hpb=50462b7b22a3c42bdbf2fb84d782937f817368f4;p=pettanr%2FclientJs.git diff --git a/0.6.x/js/07_audio/00_XAudio.js b/0.6.x/js/07_audio/00_XAudio.js index acae436..4664aa0 100644 --- a/0.6.x/js/07_audio/00_XAudio.js +++ b/0.6.x/js/07_audio/00_XAudio.js @@ -1,7 +1,7 @@  /* WebAudio : 1, - HTML5 : 2, + HTMLAudio : 2, Flash : 3, Silverlight : 4, Unity : 5, @@ -10,88 +10,147 @@ QuickTime : 8, */ -var X_Audio_BACKENDS = [], // Array. - X_Audio_WRAPPER_LIST = []; // Array. +var X_Audio_BACKENDS = []; // Array. -function X_Audio_getAudioWrapper( proxy ){ - var i = X_Audio_WRAPPER_LIST.length; - for( ; i; ){ - if( X_Audio_WRAPPER_LIST[ --i ].proxy === proxy ) return X_Audio_WRAPPER_LIST[ i ]; - }; -}; +X_TEMP.onSystemReady.push( + function(){ + var canPlay = X[ 'Audio' ][ 'canPlay' ] = {}, + i = X_Audio_BACKENDS.length, + be; + for( ; i; ){ + be = X_Audio_BACKENDS[ --i ]; + X_Object_override( canPlay, be.canPlay ); + X[ 'Audio' ][ be.backendName ] = be.backendID; + }; + }); -/* - * X_EVENT_BACKEND_READY - * X_EVENT_BACKEND_NONE +/** + *

複数のオーディオ・バックエンドから、与えられた音声を再生可能なものを見つけ、音声を再生します。 + *

HTMLAudio の動作・機能がブラウザ毎にバラバラなのに業を煮やし、メソッドやイベントは独自に定義しています。 + *

バックエンドの種類

+ *

HTMLAudio, WebAudio, Silverlight, WMP + *

イベント

+ *
+ *
X.Event.BACKEND_READY
音声(src リスト)を再生可能なバックエンドが見つかった。 + *
X.Event.BACKEND_NONE
音声を再生可能なバックエンドが見つからなかった。Audio は kill されます。 + *
X.Event.MEDIA_CAN_TOUCH
モバイル端末の制約で音声の再生またはロードに、タッチを必要とする場合、タッチイベント内で play を呼び出す準備が出来たことを通知する。 + *
X.Event.READY
再生可能、実際の状態は canplay から loadeddata まで様々、、、モバイル端末の場合、タッチして再生が開始された場合に + *
X.Event.ERROR
    + *
  • 1 : ユーザーによってメディアの取得が中断された + *
  • 2 : ネットワークエラー + *
  • 3 : メディアのデコードエラー + *
  • 4 : メディアがサポートされていない + *
+ *
X.Event.MEDIA_PLAYING
再生中に1秒以下のタイミングで発生.currentTime が取れる? + *
X.Event.MEDIA_LOOP
ループ直前に発生、キャンセル可能 + *
X.Event.MEDIA_LOOPED
ループ時に発生 + *
X.Event.MEDIA_ENDED
再生位置の(音声の)最後についた + *
X.Event.MEDIA_PAUSED
ポーズした + *
X.Event.MEDIA_WAITING
再生中に音声が待機状態に。 + *
X.Event.MEDIA_SEEKING
シーク中に音声が待機状態に。 + *
+ *

ソースリストに与える url 文字列

+ *

ハッシュフラグメント以下にデータを書くことで、各オーディオバックエンドが再生可能性の判断にあたって参考にするデータを渡すことができます。 + *

+ *
CBR=1
audio が固定ビットレートであることを示す。Android 用 Opera12- は可変ビットレートの mp3 を正しくシークできない。 + * [ 'snd.mp3', 'snd.mp3#CBR=1' ] と指定すると、Android 用 Opera12- では CBR な mp3 が、他の環境ではよりファイルサイズの小さい VBR な mp3 が使用される。(未実装) + *
ext=mp3
パスに拡張子が含まれない場合、または上書き指定したい場合に指定する * - * X_EVENT_READY 再生可能、実際の状態は canplay から loadeddata まで様々、、、 - * X_EVENT_ERROR - * 1 : ユーザーによってメディアの取得が中断された - * 2 : ネットワークエラー - * 3 : メディアのデコードエラー - * 4 : メディアがサポートされていない - * - * X_EVENT_MEDIA_PLAYING 再生中に1秒以下のタイミングで発生.currentTime が取れる? - * X_EVENT_MEDIA_LOOP ループ直前に発生、キャンセル可能 - * X_EVENT_MEDIA_LOOPED ループ時に発生 - * X_EVENT_MEDIA_ENDED 再生位置の(音声の)最後についた - * X_EVENT_MEDIA_PAUSED ポーズした - * X_EVENT_MEDIA_WAITING 再生中に音声が待機状態に。間もなく X_EVENT_MEDIA_PLAYING に移行。 - * X_EVENT_MEDIA_SEEKING シーク中に音声が待機状態に。間もなく X_EVENT_MEDIA_PLAYING に移行。 + * @alias X.Audio + * @class 各種オーディオ機能をラップしインターフェイスを共通化する。 + * @constructs Audio + * @extends {EventDispatcher} + * @param {array|string} sourceList + * @param {object=} opt_option + * @example // + * var audio = X.Audio( [ 'etc/special.mp3', 'etc/special.ogg', 'etc/special.wav' ] ).listenOnce( X.Event.READY, onReady ); */ - X[ 'Audio' ] = X_EventDispatcher[ 'inherits' ]( 'X.Audio', - X_Class.POOL_OBJECT, + X_Class.NONE, { + /** + * 音声の url。X.Event.BACKEND_READY で設定される。 + * @alias Audio.prototype.source + * @type {string} + */ 'source' : '', - 'backendName' : '', - _backend : -1, + /** + * 音声再生バックエンドの名前。X.Event.BACKEND_READY で設定される。 + * @alias Audio.prototype.backendName + * @type {string} + */ + 'backendName' : '', + 'Constructor' : function( sourceList, opt_option ){ X_Audio_startDetectionBackend( X_Audio_BACKENDS[ 0 ], this, - X_Type_isArray( sourceList ) ? X_Object_cloneArray( sourceList ) : [ sourceList ], + X_Type_isArray( sourceList ) ? X_Array_copy( sourceList ) : [ sourceList ], opt_option || {} ); this[ 'listenOnce' ]( [ X_EVENT_BACKEND_READY, X_EVENT_BACKEND_NONE, X_EVENT_KILL_INSTANCE ], X_Audio_handleEvent ); + X_ViewPort[ 'listenOnce' ]( X_EVENT_UNLOAD, this, X_AudioSprite_handleEvent ); }, + /** + * 再生。開始位置・終了位置、ループの有無、ループ以降の開始位置、ループ以降の終了位置 + * @alias Audio.prototype.play + * @param {number=} startTime 開始時間を ms で + * @param {number=} endTime 終了時間を ms で + * @param {boolean=} loop endTimeに達した際に曲をループさせるか + * @param {number=} loopStartTime ループ以後の開始時間を ms で + * @param {number=} loopEndTime ループ以後の終了時間を ms で + * @return {Audio} メソッドチェーン + */ 'play' : function( startTime, endTime, loop, loopStartTime, loopEndTime ){ - var state, duration; - if( 0 <= startTime ){ - this[ 'state' ]( { - currentTime : startTime, - startTime : startTime, - endTime : endTime, - loop : loop, - loopStartTime : loopStartTime, - loopEndTime : loopEndTime - } ); - }; - this._backend !== -1 && X_Audio_getAudioWrapper( this ).play(); + var pair = X_Pair_get( this ); + pair && pair.play( startTime, endTime, loop, loopStartTime, loopEndTime ); return this; }, - + /** + * シーク、再生中で無い場合は次回再生開始位置の指定のみ + * @alias Audio.prototype.seek + * @param {number} seekTime シーク位置を ms で + * @return {Audio} メソッドチェーン + */ 'seek' : function( seekTime ){ - var state = this[ 'state' ](), - end = X_AudioWrapper_getEndTime( X_Audio_getAudioWrapper( this ) ); - if( seekTime < end ){ - this[ 'state' ]( { currentTime : seekTime } ); - }; + var pair = X_Pair_get( this ); + pair && pair.seek( seekTime ); return this; }, - + /** + * ポーズ + * @alias Audio.prototype.pause + * @return {Audio} メソッドチェーン + */ 'pause' : function(){ - this[ 'state' ]().playing && X_Audio_getAudioWrapper( this ).pause(); + var pair = X_Pair_get( this ); + pair && pair.pause(); return this; }, - + /** + * 状態の getter と setter + * @alias Audio.prototype.state + * @param {object=} obj setter の場合、上書きする値を格納したobject + * @return {Audio|object} + * @example +audio.setState( + { + 'startTime' : 0, + 'endTime' : 80000, + 'loopStartTime' : 120000, + 'loopEndTime' : 200000, + 'currentTime' : 0, + 'loop' : true, + 'looded' : false, + 'volume' : 1, + 'autoplay' : true +}); + */ 'state' : function( obj ){ - var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this ); - + var pair = X_Pair_get( this ); if( obj === undefined ){ - return backend ? - backend.state() : + return pair ? pair.getState() : { 'startTime' : -1, 'endTime' : -1, @@ -100,68 +159,95 @@ X[ 'Audio' ] = X_EventDispatcher[ 'inherits' ]( 'currentTime' : -1, 'loop' : false, 'looded' : false, - 'error' : false, + 'error' : 0, + 'autoplay' : false, 'playing' : false, - - 'source' : this[ 'source' ] || '', - 'duration' : 0 + 'source' : this[ 'source' ], + 'duration' : 0, + 'volume' : 0.5 }; }; - backend && backend.state( obj ); + pair && pair.setState( obj ); return this; }, - + /** + * ループの setter + * @alias Audio.prototype.loop + * @param {boolean} v + * @return {Audio} + */ 'loop' : function( v ){ - var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this ); - if( v === undefined ){ - return backend && backend.state().loop; - }; - backend && backend.state( { loop : v } ); + var pair = X_Pair_get( this ); + pair && pair.loop( v ); return this; }, - + /** + * ボリュームの setter 実装不十分! + * @alias Audio.prototype.volume + * @param {number} v 0~1 + * @return {Audio} + */ 'volume' : function( v ){ - var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this ); - if( v === undefined ){ - return backend && backend.state().volume; - }; - backend && backend.state( { volume : v } ); + var pair = X_Pair_get( this ); + pair && pair.volume( v ); return this; }, - + /** + * 再生位置のsetter。 + * @alias Audio.prototype.currentTime + * @param {number} v msで + * @return {Audio} + */ 'currentTime' : function( v ){ - var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this ); - if( v === undefined ){ - return backend && backend.state().currentTime; - }; - backend && backend.state( { currentTime : v } ); + var pair = X_Pair_get( this ); + pair && pair.currentTime( v ); return this; }, - + /** + * 再生中か? + * @alias Audio.prototype.isPlaying + * @return {boolean} + */ 'isPlaying' : function(){ - return this._backend !== -1 && X_Audio_getAudioWrapper( this ).state().playing; + var pair = X_Pair_get( this ); + return pair && pair.playing; } } ); function X_Audio_handleEvent( e ){ + var backend, src, pair; + switch( e.type ){ case X_EVENT_BACKEND_READY : + backend = X_Audio_BACKENDS[ e[ 'backendID' ] ]; + this[ 'unlisten' ]( X_EVENT_BACKEND_NONE, X_Audio_handleEvent ); - this[ 'source' ] = e.source; - this[ 'backendName' ] = X_Audio_BACKENDS[ this._backend ].backendName; - X_Audio_WRAPPER_LIST.push( - new X_Audio_BACKENDS[ this._backend ] - .klass( this, e.source, e.option ) ); + this[ 'source' ] = e[ 'source' ]; + this[ 'backendName' ] = backend.backendName; + + X_Pair_create( this, backend.klass( this, e[ 'source' ], e[ 'option' ] ) ); + this[ 'listenOnce' ]( X_EVENT_READY, X_Audio_handleEvent ); + break; + + case X_EVENT_READY : + pair = X_Pair_get( this ); + ( pair.autoplay || pair._playReserved ) && pair.actualPlay(); + delete pair._playReserved; break; case X_EVENT_BACKEND_NONE : + case X_EVENT_UNLOAD : this[ 'kill' ](); break; case X_EVENT_KILL_INSTANCE : - this._backend !== -1 && X_Audio_getAudioWrapper( this ).close(); + X_ViewPort[ 'unlisten' ]( X_EVENT_UNLOAD, this, X_AudioSprite_handleEvent ); + if( backend = X_Pair_get( this ) ){ + backend[ 'kill' ](); + X_Pair_release( this, backend ); + }; break; }; }; @@ -171,134 +257,282 @@ function X_Audio_handleEvent( e ){ * TODO preplayerror play してみたら error が出た、backend の変更。 */ -function X_Audio_startDetectionBackend( backend, proxy, sourceList, option ){ - var source = sourceList[ 0 ] || '', - ext = X_URL_getEXT( source ), +function X_Audio_startDetectionBackend( backend, xaudio, sourceList, option ){ + var source = sourceList[ 0 ] || '', + hash = X_URL_paramToObj( X_URL_getHash( source ) ), + ext = hash[ 'ext' ] || X_URL_getEXT( source ), sup; if( source && backend ){ - sup = [ proxy, sourceList, option, source, ext ]; + sup = [ xaudio, sourceList, option, source, ext ]; sup[ 5 ] = sup; - proxy[ 'listenOnce' ]( X_EVENT_COMPLETE, backend, X_Audio_onEndedDetection, sup ); - backend.detect( proxy, source, ext ); + xaudio[ 'listenOnce' ]( X_EVENT_COMPLETE, backend, X_Audio_onEndedDetection, sup ); + backend.detect( xaudio, ext, hash ); } else { - proxy[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE ); + xaudio[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE ); }; }; -function X_Audio_onEndedDetection( e, proxy, sourceList, option, source, ext, sup ){ - var i = X_Audio_BACKENDS.indexOf( this ), backend; +function X_Audio_onEndedDetection( e, xaudio, sourceList, option, source, ext, sup ){ + var i = X_Audio_BACKENDS.indexOf( this ), _e, hash, backend; if( e.canPlay ){ - proxy._backend = i; - proxy[ 'asyncDispatch' ]( { + _e = { type : X_EVENT_BACKEND_READY, 'option' : option, 'source' : source, - 'backendName' : this[ 'backendName' ] - } ); + 'backendName' : this.backendName, + 'backendID' : i + }; + // WebAudio + if( this.backendID === 1 ) _e[ 'needTouchForPlay' ] = X_WebAudio_need1stTouch; + // HTMLAudio + if( this.backendID === 2 ) _e[ 'needTouchForLoad' ] = X_HTMLAudio_need1stTouch; + + xaudio[ 'asyncDispatch' ]( _e ); } else { - console.log( 'No ' + source + ' ' + this[ 'backendName' ] ); + console.log( 'No ' + source + ' ' + this.backendName ); if( sup[ 3 ] = source = sourceList[ sourceList.indexOf( source ) + 1 ] ){ - sup[ 4 ] = ext = X_URL_getEXT( source ); - proxy[ 'listenOnce' ]( X_EVENT_COMPLETE, this, X_Audio_onEndedDetection, sup ); - this.detect( proxy, source, ext ); + hash = X_URL_paramToObj( X_URL_getHash( source ) ); + sup[ 4 ] = ext = hash[ 'ext' ] || X_URL_getEXT( source ); + xaudio[ 'listenOnce' ]( X_EVENT_COMPLETE, this, X_Audio_onEndedDetection, sup ); + this.detect( xaudio, ext, hash ); } else if( backend = X_Audio_BACKENDS[ i + 1 ] ){ - X_Audio_startDetectionBackend( backend, proxy, sourceList, option ); + X_Audio_startDetectionBackend( backend, xaudio, sourceList, option ); } else { - proxy[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE ); + xaudio[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE ); }; }; }; -function X_AudioWrapper_updateStates( audioWrapper, obj ){ - var playing = audioWrapper.playing, - k, v, - end = 0, seek = 0, volume = 0; - - for( k in obj ){ - v = obj[ k ]; - switch( k ){ - case 'currentTime' : - v = X_AudioWrapper_timeStringToNumber( v ); - if( X_Type_isNumber( v ) ){ - if( playing ){ - if( audioWrapper.state().currentTime !== v ){ - audioWrapper.seekTime = v; - seek = 2; +var X_AudioBase = X_EventDispatcher[ 'inherits' ]( + 'X.AudioBase', + X_Class.ABSTRACT, + { + disatcher : null, + + startTime : 0, // state_startTime + endTime : -1, // state_startTime + loopStartTime : -1, + loopEndTime : -1, + seekTime : -1, + duration : 0, // + + playing : false, + error : 0, // + autoLoop : false, + looped : false, + autoplay : false,// + gain : 0.5, + + _playReserved : false, + + play : function( startTime, endTime, loop, loopStartTime, loopEndTime ){ + if( 0 <= startTime ){ + this.setState( { + 'currentTime' : startTime, + 'startTime' : startTime, + 'endTime' : endTime, + 'loop' : loop, + 'looped' : false, + 'loopStartTime' : loopStartTime, + 'loopEndTime' : loopEndTime + } ); + }; + // canPlay() : autoplay = true + this.actualPlay(); + }, + + seek : function( seekTime ){ + if( seekTime < X_Audio_getEndTime( this ) ){ + this.setState( { 'currentTime' : seekTime } ); + }; + }, + + pause : function(){ + this.seekTime = this.getActualCurrentTime(); + this.playing && this.actualPause(); + // delete this.autoplay + // delete this.playing + }, + + loop : function( v ){ + if( v === undefined ){ + return this.autoLoop; + }; + this.setState( { 'loop' : v } ); + }, + + volume : function( v ){ + if( v === undefined ){ + return this.gain; + }; + this.setState( { 'volume' : v } ); + }, + + currentTime : function( v ){ + if( v === undefined ){ + return this.playing ? this.getActualCurrentTime() : this.seekTime; + }; + this.setState( { 'currentTime' : v } ); + }, + + getState : function(){ + + 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, + 'loop' : this.autoLoop, + 'looped' : this.looped, + 'volume' : this.gain, + 'playing' : this.playing, + 'duration' : this.duration, + 'autoplay' : this.autoplay, + + 'currentTime' : this.playing ? this.getActualCurrentTime() : this.seekTime, + 'error' : this.getActualError ? this.getActualError() : this.error + }; + }, + + setState : function( obj ){ + var playing = this.playing, + k, v, + end = 0, seek = 0, volume = 0; + + for( k in obj ){ + v = obj[ k ]; + switch( k ){ + case 'currentTime' : + v = X_Audio_timeStringToNumber( v ); + if( X_Type_isNumber( v ) ){ + if( playing ){ + if( this.getActualCurrentTime() !== v ){ + seek = 2; + this.seekTime = v; + }; + } else { + this.seekTime = v; + }; + } else { + continue; }; - } else { - audioWrapper.seekTime = v; - }; - } else { - continue; - }; - break; + break; + + case 'startTime' : + v = X_Audio_timeStringToNumber( v ); + if( v || v === 0 ){ + if( this.startTime !== v ){ + this.startTime = v; + }; + } else { + delete this.startTime; + }; + break; - case 'startTime' : - case 'endTime' : - case 'loopStartTime' : - case 'loopEndTime' : - v = X_AudioWrapper_timeStringToNumber( v ); - console.log( k + ' ' + v ); - if( v || v === 0 ){ - if( audioWrapper[ k ] !== v ){ - audioWrapper[ k ] = v; + case 'endTime' : + v = X_Audio_timeStringToNumber( v ); + if( v || v === 0 ){ + if( this.endTime !== v ){ + this.endTime = v; + if( playing ) end = 1; + }; + } else { + delete this.endTime; + if( playing ) end = 1; + }; + break; - // 再生中の endTime の変更 - if( playing && ( k === 'endTime' || k === 'loopEndTime' ) ) end = 1; - }; - } else { - delete audioWrapper[ k ]; - if( playing && ( k === 'endTime' || k === 'loopEndTime' ) ) end = 1; - }; - break; - - case 'looped' : - if( playing ) seek = 2; - case 'loop' : - case 'autoplay' : - if( X_Type_isBoolean( v ) && audioWrapper[ k ] !== v ){ - audioWrapper[ k ] = v; + case 'loopStartTime' : + v = X_Audio_timeStringToNumber( v ); + if( v || v === 0 ){ + if( this.loopStartTime !== v ){ + this.loopStartTime = v; + }; + } else { + delete this.loopStartTime; + }; + break; + + case 'loopEndTime' : + v = X_Audio_timeStringToNumber( v ); + if( v || v === 0 ){ + if( this.loopEndTime !== v ){ + this.loopEndTime = v; + if( playing ) end = 1; + }; + } else { + delete this.loopEndTime; + if( playing ) end = 1; + }; + break; + + case 'looped' : + if( X_Type_isBoolean( v ) && this.looped !== v ){ + this.looped = v; + if( playing ) seek = 2; + }; + break; + + case 'loop' : + if( X_Type_isBoolean( v ) && this.autoLoop !== v ){ + this.autoLoop = v; + }; + break; + + case 'autoplay' : + if( X_Type_isBoolean( v ) && this.autoplay !== v ){ + this.autoplay = v; + }; + break; + + case 'volume' : + if( X_Type_isNumber( v ) ){ + v = v < 0 ? 0 : 1 < v ? 1 : v; + if( this.gain !== v ){ + this.gain = v; + // if playing -> update + if( playing ) volume = 4; + }; + }; + break; + case 'useVideo' : + break; + default : + alert( 'bad arg! ' + k ); }; - break; + }; + + if( this.endTime < this.startTime || + ( this.loopEndTime < 0 ? this.endTime : this.loopEndTime ) < ( this.loopStartTime < 0 ? this.startTime : this.loopStartTime ) || + X_Audio_getEndTime( this ) < this.seekTime// || + //this.duration < this.endTime + ){ + console.log( 'setState 0:' + this.startTime + ' -> ' + this.endTime + ' looped:' + this.looped + ' 1:' + this.loopStartTime + ' -> ' + this.loopEndTime ); + return; + }; + + v = end + seek + volume; + return v && this.playing && this.afterUpdateState( v ); + } + + } +); - case 'volume' : - if( X_Type_isNumber( v ) ){ - v = v < 0 ? 0 : 1 < v ? 1 : v; - if( audioWrapper[ k ] !== v ){ - audioWrapper[ k ] = v; - // if playing -> update - if( playing ) volume = 4; - }; - }; - break; - }; - }; - - if( audioWrapper.endTime < audioWrapper.startTime || - ( audioWrapper.loopEndTime < 0 ? audioWrapper.endTime : audioWrapper.loopEndTime ) < ( audioWrapper.loopStartTime < 0 ? audioWrapper.startTime : audioWrapper.loopStartTime ) || - X_AudioWrapper_getEndTime( audioWrapper ) < audioWrapper.seekTime// || - //audioWrapper.duration < audioWrapper.endTime - ){ - console.log( 'error @updateStateObject() begin:' + audioWrapper.startTime + ' end:' + audioWrapper.endTime + ' d:' + audioWrapper.duration + ' ls:' + audioWrapper.loopStartTime ); - return 0; - }; - - return end + seek + volume; -}; -function X_AudioWrapper_timeStringToNumber( time ){ +function X_Audio_timeStringToNumber( time ){ var ary, ms, s = 0, m = 0, h = 0; + if( X_Type_isNumber( time ) ) return time; if( !X_Type_isString( time ) || !time.length ) return; ary = time.split( '.' ); - ms = parseInt( ( ary[ 1 ] + '000' ).substr( 0, 3 ) ) || 0; + ms = parseFloat( ( ary[ 1 ] + '000' ).substr( 0, 3 ) ) || 0; ary = ary[ 0 ].split( ':' ); if( 3 < ary.length ) return; @@ -307,17 +541,17 @@ function X_AudioWrapper_timeStringToNumber( time ){ case 0 : break; case 1 : - s = parseInt( ary[ 0 ] ) || 0; + s = parseFloat( ary[ 0 ] ) || 0; break; case 2 : - m = parseInt( ary[ 0 ] ) || 0; - s = parseInt( ary[ 1 ] ) || 0; + m = parseFloat( ary[ 0 ] ) || 0; + s = parseFloat( ary[ 1 ] ) || 0; if( 60 <= s ) alert( 'invalid time string ' + time ); break; case 3 : - h = parseInt( ary[ 0 ] ) || 0; - m = parseInt( ary[ 1 ] ) || 0; - s = parseInt( ary[ 2 ] ) || 0; + h = parseFloat( ary[ 0 ] ) || 0; + m = parseFloat( ary[ 1 ] ) || 0; + s = parseFloat( ary[ 2 ] ) || 0; if( 60 <= s ) alert( 'invalid time string ' + time ); if( 60 <= m ) alert( 'invalid time string ' + time ); break; @@ -328,33 +562,34 @@ function X_AudioWrapper_timeStringToNumber( time ){ return ms < 0 ? 0 : ms; }; -function X_AudioWrapper_getStartTime( audioWrapper, endTime, delSeekTime ){ - var seek = audioWrapper.seekTime; - if( delSeekTime ) delete audioWrapper.seekTime; +function X_Audio_getStartTime( audioBase, endTime, delSeekTime ){ + var seek = audioBase.seekTime; + + if( delSeekTime ) delete audioBase.seekTime; if( 0 <= seek ){ - if( audioWrapper.duration <= seek || endTime < seek ) return 0; + if( audioBase.duration <= seek || endTime < seek ) return 0; return seek; }; - if( audioWrapper.looped && 0 <= audioWrapper.loopStartTime ){ - if( audioWrapper.duration <= audioWrapper.loopStartTime || endTime < audioWrapper.loopStartTime ) return 0; - return audioWrapper.loopStartTime; + if( audioBase.looped && 0 <= audioBase.loopStartTime ){ + if( audioBase.duration <= audioBase.loopStartTime || endTime < audioBase.loopStartTime ) return 0; + return audioBase.loopStartTime; }; - if( audioWrapper.startTime < 0 || audioWrapper.duration <= audioWrapper.startTime ) return 0; - return audioWrapper.startTime; + if( audioBase.startTime < 0 || audioBase.duration <= audioBase.startTime ) return 0; + return audioBase.startTime; }; -function X_AudioWrapper_getEndTime( audioWrapper ){ - var duration = audioWrapper.duration; +function X_Audio_getEndTime( audioBase ){ + var duration = audioBase.duration; - if( audioWrapper.looped && 0 <= audioWrapper.loopEndTime ){ - if( duration <= audioWrapper.loopEndTime ) return duration; - return audioWrapper.loopEndTime; + if( audioBase.looped && 0 <= audioBase.loopEndTime ){ + if( duration <= audioBase.loopEndTime ) return duration; + return audioBase.loopEndTime; }; - if( audioWrapper.endTime < 0 || duration <= audioWrapper.endTime ) return duration; - return audioWrapper.endTime; + if( audioBase.endTime < 0 || duration <= audioBase.endTime ) return duration; + return audioBase.endTime; };