3 * original : uupaa-js HTML5Audio.js
\r
4 * https://code.google.com/p/uupaa-js/source/browse/trunk/0.8/src/Audio/HTML5Audio.js?r=568
\r
6 * Windows 版 Safari は QuickTime のインストールが必要
\r
8 * 1. iOS4(iPod 2G) で ended に達すると音が鳴らなくなる fix で解決
\r
9 * 2. iOS6(iPod 4G) で ended に達すると音が鳴らなくなる fix で頻度が改善 emded イベントは発しないので、timeupdate 時に currentTime で判断する
\r
10 * 3. WP7(IS12T) で最後の方にある音が鳴らない? mp3 cbr を使えばいい? 裏に回っても音が鳴り続ける
\r
11 * 4. AOSP 2.x で ended に達すると音が鳴らなくなる -> リロード(audio.src='';audio.src=src;audio.load())でで解決
\r
12 * 5. AOSP 3.x で ended に達すると音が鳴らなくなる -> リロード(audio.src='';audio.sr=src)で解決、但し 2.x 4.x より遅延が大きく 1 秒弱程度ある
\r
13 * 6. AOSP 4.4.2- は ended に達した際に currentTime が変更できなくなり、リロードが必要になる, 4.0, 4.1, 4.2, 4.3 で確認, play() で頻度低下
\r
14 * 7. Android 4.4.4 Chrome WebView は ended に達した際に play() が必要
\r
15 * 8. BlinkOpera32 Win8 は HTMLAudio が壊れている、WebAudio は mp3 がデコードに失敗、ogg が動作
\r
18 * 1. AOSP4.1 iframe 内の Audio は親に focus が移っても再生を継続する
\r
19 * 2. AOSP oggはシークが乱れる m4a, mp3 は優秀
\r
21 * http://unolabo.boo.jp/archives/2011/06/13-iphone%E3%81%AEaudio%E5%91%A8%E3%82%8A%E3%81%AE%E3%83%A1%E3%83%A2.html
\r
22 * 【JS】iPhoneのAudio周りのメモ iOS4.0 と 4.2 の違い
\r
26 * duration が取得できるタイミングが遅くそれまでは infinity(PC Opera12), NaN(WP9), 0(Android 標準ブラウザ ChromeWebView) が入っている
\r
28 * 1. touch が不要の場合、自動で再生を開始して duration を取得するまで再生する
\r
29 * -> 取得後に pause or 通常再生
\r
30 * 2. touch が必要な場合、タッチイベント内の audio.play() で duration 取得
\r
33 * 1. loadeddata 等では duration が infinity で、再生後の durationchange 時に duration が判明する
\r
34 * 2. duration 判明後には currentTime によるシークと、現在時間の取得が可能になる。
\r
35 * 3. Opera12.17 Win32(XP) portable apps は勝手に再生が始まる、、、Win8+Opera では発生しない
\r
36 * -> その際には timeupdate が発行されない、、、 iframe+image+audio で使わないときは破棄する、とか。
\r
37 * -> opera11、10.54 WinXP はまとも、、、 portable が怪しい??
\r
42 // iOS7.1, 8.3 で確認.seeking -> seeked の間の currentTime の値が全くあてにならないので無視する。
\r
43 X_HTMLAudio_seekingFixIOS = 7 <= X_UA[ 'iOS' ],
\r
44 // ended が発生しない timeupdate 内で play() を呼ぶ (未検証) 不具合確認は iOS4,6 iOS7.1,8.3ではpause->ended起きてる 但し iOS7.1 でも 6 と同じ症状になることがある
\r
45 X_HTMLAudio_endedFixIOS = X_UA[ 'iOS' ] < 7,
\r
46 // Android 2.3.5 で ended 時に audio.src='';audio.src=src;audio.load() を実施。 2.3.4 でも問題なし。
\r
47 X_HTMLAudio_endedFixAOSP2 = X_UA[ 'AOSP' ] < 3,
\r
48 // Android 3.1 で ended 時に src='';src=src を実施。
\r
49 X_HTMLAudio_endedFixAOSP3 = !X_HTMLAudio_endedFixAOSP2 && X_UA[ 'AOSP' ] < 4,
\r
50 // ended 時に play() を実施, currentTime が duration に張り付き更新されなければ src='';src=src を実施。
\r
51 X_HTMLAudio_endedFixAOSP4 = 4 <= X_UA[ 'AOSP' ],
\r
52 // ended 時に play() を実施
\r
53 X_HTMLAudio_endedFixCWV = X_UA[ 'ChromeWV' ] || ( X_UA[ 'Blink' ] && X_UA[ 'Android' ] ),
\r
55 // Opera Mobile 12 は 2回目以降の currentTime へのセットで currentTime が更新されなくなるため、タイマーを使用する
\r
56 X_HTMLAudio_currentTimeFix = !!X_UA[ 'OperaMobile' ] || !!X_UA[ 'OperaTablet' ],
\r
57 // Firefox44.0.2 で音声の再生開始に難あり... 49 でも確認, あるいはCGIで動的に生成しているmp3自体に問題があるのかも
\r
58 X_HTMLAudio_playStartFix = X_UA[ 'Windows' ] && 44 <= X_UA[ 'Gecko' ],
\r
60 X_HTMLAudio_volumeFix = X_UA[ 'Chrome' ],
\r
62 * win opera12 volume, mute の変更が2度目以降できない
\r
64 X_HTMLAudio_volumeEnabled = !( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) && !X_UA[ 'Opera' ],
\r
65 // Gecko PC + Android でseek時に再生がしばしば止まる問題の修正、iOS8でも確認
\r
66 X_HTMLAudio_needPlayForSeek = X_UA[ 'iOS' ] || X_UA[ 'Gecko' ],
\r
68 X_HTMLAudio_pauseFix = 12 <= X_UA[ 'Opera' ] && 0 < ' XP XPSP2 2003|XP64'.indexOf( X_UA[ 'Windows' ] ), // XP + Opera12 のみ?
\r
70 X_HTMLAudio_need1stTouch = X_UA[ 'iOS' ] || 4.2 <= X_UA[ 'AOSP' ] || X_UA[ 'ChromeWV' ] || X_UA[ 'WinPhone' ] || ( X_UA[ 'Blink' ] && X_UA[ 'Android' ] ),
\r
72 X_HTMLAudio_playTrigger = ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) ? 'canplay' :
\r
73 X_UA[ 'iOS' ] < 8 ? 'suspend' : // iOS7.x以下
\r
74 X_UA[ 'iOS' ] ? 'loadedmetadata' : // iOS8以上は
\r
75 X_UA[ 'Blink' ] < 32 ? 'stalled' : 'canplaythrough',
\r
77 X_HTMLAudio_durationFix = // iOS8.1(シュミレータでは不要)
\r
78 X_UA[ 'iOS' ] < 8 || X_UA[ 'ChromeWV' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) ||
\r
79 ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] ) || ( X_UA[ 'Blink' ] < 36 && X_UA[ 'Android' ] ),
\r
81 X_HTMLAudio_shortPlayFix = X_UA[ 'AOSP' ],
\r
83 X_HTMLAudio_progressEnabled = !( X_UA[ 'Opera' ] && X_UA[ 'Android' ] ) && !( X_UA[ 'WinPhones' ] && X_UA[ 'IE9' ] ); // Android 4.1.1 でも遭遇
\r
85 if( X_Audio_constructor ){
\r
87 X_HTMLAudio = X_AudioBase[ 'inherits' ](
\r
89 X_Class.POOL_OBJECT,
\r
91 // 1: canplaythrought|timeupdateに達している、またはdurationFixが終了している
\r
99 // 2:touch による play 済
\r
100 _touchState : X_HTMLAudio_need1stTouch ? 1 : 0,
\r
102 _currentFixStart : 0,
\r
103 _currentFixBegin : 0,
\r
105 // 0:durationFix不要 または 完了
\r
106 // 1:durationFix未着手(touchState=1なら play() に入れる)
\r
107 // 2:canplay イベント発生 -> play()
\r
110 // :timeupdate イベントで durationFixは完了
\r
111 _durationFixPhase : X_HTMLAudio_durationFix ? 1 : 0,
\r
112 _lastCurrentTime : 0, // ms
\r
114 _shortPlayFixON : false,
\r
115 _shortPlayFixTime : 0,
\r
117 _endedFixON : false,
\r
119 _seekingFixON : false,
\r
121 'Constructor' : function( dispatcher, source, option ){
\r
124 this.dispatcher = dispatcher || this;
\r
125 this._src = source;
\r
127 if( X_HTMLAudio_shortPlayFix ){
\r
128 this._shortPlayFixON = X_URL_getEXT( source ) === 'm4a';
\r
131 this.setState( option );
\r
133 if( option[ 'useVideo' ] ){
\r
134 raw = document.createElement( 'video' );
\r
135 raw.preload = 'none'; // auto, metadata, none
\r
136 raw.autoplay = false, // no-auto
\r
139 raw.crossorigin = option[ 'crossorigin' ] || ''; //crossorigin: "anonymous", X.URL.isSameDomain() で切り替え
\r
140 raw.style.cssText = 'position:absolute;bottom:0;left:-50px;width:100px;height:100px;opacity:0;';
\r
141 raw.controls = false;
\r
142 raw.WebKitPlaysInline = true;
\r
143 X_elmBody.appendChild( raw );
\r
145 raw = X_TEMP.rawAudio || new X_Audio_constructor( '' );
\r
147 if( X_TEMP.rawAudio ) delete X_TEMP.rawAudio;
\r
150 this[ '_rawObject' ] = raw;
\r
152 this[ 'listen' ]( [
\r
153 X_EVENT_KILL_INSTANCE,
\r
154 X_HTMLAudio_playTrigger,
\r
155 //'loadstart', 'load',
\r
156 'progress', //'error',
\r
157 // 'suspend', 'abort', 'emptied', 'stalled',
\r
158 // 'play', 'pause', 'ratechange', 'volumechange',
\r
160 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough',
\r
161 'playing', 'waiting', 'seeking',
\r
162 'durationchange', 'timeupdate', 'ended' ] );
\r
164 this[ 'listen' ]( [
\r
165 'loadstart', 'load',
\r
166 'progress', 'error',
\r
167 'suspend', 'abort', 'emptied', 'stalled',
\r
168 'play', 'pause', 'seeked', 'ratechange', 'volumechange',
\r
169 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough',
\r
170 'playing', 'waiting', 'seeking',
\r
171 'durationchange', 'timeupdate', 'ended' ], this.onDebug );
\r
173 if( X_HTMLAudio_endedFixAOSP2 || X_HTMLAudio_endedFixAOSP4 ){
\r
174 raw.loop = true; // loop を使えば ended で止まること回避できる 但し ended イベントが起きなくなる
\r
177 if( X_HTMLAudio_need1stTouch ){
\r
180 if( this.autoplay ){
\r
181 raw.preload = 'auto';
\r
182 raw.autoplay = true; // Android 4.0-4.1.x で必要
\r
183 //raw.autobuffer = true;
\r
186 raw.load(); // Android4.1.1 HTL21 では必要!
\r
190 onDebug : function( e ){
\r
191 this.dispatcher[ 'dispatch' ]( {
\r
192 type : X_EVENT_DEBUG,
\r
193 'rawEvent' : e.type,
\r
194 'current' : this[ '_rawObject' ].currentTime,
\r
195 duration : this[ '_rawObject' ].duration } );
\r
198 handleEvent : function( e ){
\r
199 var raw = this[ '_rawObject' ],
\r
200 actualEnded = e.type === 'ended',
\r
201 ended = actualEnded,
\r
204 eventType, duration, end, now;
\r
208 // e.type !== 'timeupdate' && console.log( ' > ' + e.type );
\r
212 case X_EVENT_KILL_INSTANCE :
\r
213 this.playing && this.actualPause();
\r
215 // 【javascript】モバイル向けブラウザでも音を鳴らしたい【WebAudio】
\r
216 // http://ingaouhou.com/archives/3633
\r
217 // ・使い終わったインスタンスはload()しておくとやや安定
\r
221 // removeChild for video
\r
224 //case 'loadstart' : // ブラウザがコンテンツの検索を開始した場合に発生
\r
226 case 'progress' : // ブラウザがコンテンツの取得を実行した場合に発生
\r
227 // console.log( e.loaded + ' ' + e.total * 100 + '%' );
\r
228 // iem9 で常に0 raw.networkState;
\r
229 // opera Android 12 で buffered.end() へのアクセスはエラー try catch も無効、iem9 は常に end(0) = 0
\r
230 if( X_HTMLAudio_progressEnabled && this.duration && this._readyState < 3 ){
\r
231 buf = raw.buffered;
\r
232 for( i = time = 0, l = buf && buf.length; i < l; ++i ){
\r
233 time += buf[ 'end' ]( i ) - buf[ 'start' ]( i );
\r
235 this.dispatcher[ 'dispatch' ]( { type : X_EVENT_PROGRESS, 'percent' : time * 1000 / this.duration * 100 } );
\r
239 case 'loadeddata' : // コンテンツの表示を現在の再生位置で初めて行えるようになった場合に発生
\r
240 case 'canplaythrough' : // 今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生
\r
241 if( !this._endedFixON && !X_HTMLAudio_durationFix && !X_HTMLAudio_need1stTouch ){
\r
242 this._readyState |= 1;
\r
244 case 'canplay' : // 今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生
\r
245 if( this._durationFixPhase === 1 && !X_HTMLAudio_need1stTouch ){ // PC Opera12 用 durationFix
\r
246 this._durationFixPhase = 2;
\r
248 raw.currentTime = 0; // Win8 + Opera12 で必要
\r
250 if( this._endedFixON ){
\r
251 console.log( '▽ onEndedFix の終了 @' + e.type );
\r
252 this._endedFixON = false;
\r
255 case 'loadedmetadata' : // ブラウザがメディアリソースの長さと寸法を判定した場合に発生
\r
256 case 'durationchange' : // duration属性が更新された場合に発生
\r
257 if( !this.duration || this.duration !== raw.duration * 1000 ){ // Blink28 duration が変わる
\r
258 duration = raw.duration;
\r
262 // TODO firefox で 短い音声でtimeupdate, ended が発火しない <- 最後の音に無音部分を追加する
\r
263 case 'timeupdate' : // 通常の再生が行われ現在の再生位置の変化が起こった場合に発生
\r
264 if( this._seekingFixON ){
\r
265 eventType = X_EVENT_MEDIA_SEEKING;
\r
267 if( this._durationFixPhase === 8 ){
\r
268 this._durationFixPhase = 0;
\r
269 this._readyState |= 1;
\r
271 if( this._durationFixPhase === 4 ){ // 1 or 2
\r
272 duration = raw.duration;
\r
273 eventType = X_EVENT_MEDIA_WAITING;
\r
275 if( this._touchState === 3 && !X_HTMLAudio_durationFix ){
\r
276 this._touchState = 0;
\r
277 this._readyState |= 1;
\r
279 if( ( now = this.getActualCurrentTime() ) === this._lastCurrentTime ){
\r
280 eventType = X_EVENT_MEDIA_WAITING;
\r
282 if( X_HTMLAudio_playStartFix && ( now < this._lastCurrentTime ) ){
\r
283 eventType = X_EVENT_MEDIA_WAITING;
\r
284 //console.log( '@Firefox44.0.2 !! ' + now + ' / ' + this._lastCurrentTime );
\r
285 // Firefox44.0.2 で音声の再生開始に難あり...
\r
286 // 20回程度 currentTime の更新に失敗する....
\r
289 if( this.playing ){
\r
290 end = X_Audio_getEndTime( this ) + this._shortPlayFixTime;
\r
292 //console.log( '@ ' + now + ' / ' + this._lastCurrentTime + ' / ' + end );// Firefox44.0.2 で音声の再生開始に難あり...
\r
294 if( ( 0 + end <= 0 + now ) || // 0+ なぜか iem9 で必要,,,
\r
295 ( now < this._lastCurrentTime && now < 2000 ) ){
\r
296 //( ( X_HTMLAudio_endedFixAOSP2 || X_HTMLAudio_endedFixAOSP4 ) && ( now < this._lastCurrentTime && now < 1000 ) ) ){
\r
297 // loop して0付近に戻った場合
\r
298 // iOS8.4 ではこのタイミングで now が last より 0.1秒後退している場合がある
\r
299 // iOS7.1 ではもっと小さい場合がある,,,
\r
300 if( this.autoLoop ){
\r
301 console.log( '☆★☆ 曲の最後に到達 @timeupdate now-end:' + ( now - end ) + ' now:' + now + ' last:' + this._lastCurrentTime );
\r
303 //if( X_HTMLAudio_endedFixIOS ) actualEnded = true;
\r
305 this.actualPause();
\r
306 eventType = X_EVENT_MEDIA_ENDED;
\r
309 eventType = X_EVENT_MEDIA_PLAYING;
\r
311 this._lastCurrentTime = now;
\r
315 //case 'stalled' : // ブラウザがコンテンツの取得を試みたが、データがまだ用意されていない場合に発生
\r
316 // Android2 で ready 扱い?
\r
317 //case 'suspend' : // ブラウザが意図的にコンテンツの取得を現在行っていない場合に発生(ダウンロードは未完了)
\r
319 //case 'emptied' : // 読み込み中に致命的なエラーが発生したか、実行状態ででload()メソッドが実行された場合に発生
\r
320 //case 'abort' : // ダウンロードの完了前にコンテンツの取得を停止した場合に発生(この停止はエラーによるものではない)
\r
323 //case 'error' : // コンテンツの取得実行中にエラーが発生した場合に発生
\r
324 // Opera12 src = '' で error が発生、無視する
\r
325 // eventType = X_EVENT_ERROR;
\r
328 case 'playing' : // 再生が開始された場合に発生
\r
329 if( X_HTMLAudio_volumeFix ){
\r
330 raw.volume = this.gain;
\r
332 //if( X_HTMLAudio_currentTimeFix && !this._currentFixStart ){
\r
333 //this._currentFixStart = X_Timer_now(); // 正確な再生開始時間に補正
\r
335 eventType = !this._durationFixPhase && !this._endedFixON ? X_EVENT_MEDIA_PLAYING : X_EVENT_MEDIA_WAITING;
\r
336 //case 'play' : // 再生が開始された。play()メソッドからの復帰後に発生する場合に発生
\r
337 //case 'pause' : // 再生が一時停止された。pauseメソッドからの復帰後に発生する場合に発生
\r
338 //case 'ratechange' : // defaultPlaybackRate属性とplaybackRate属性のどちらかが更新された場合に発生
\r
339 //case 'volumechange' : // volume属性とmuted属性のどちらかが変化した場合に発生
\r
342 case 'seeking' : // シークがtrueに変化し、イベントを発生させるのに十分な時間がシーク操作にかかっている場合に発生
\r
343 eventType = X_EVENT_MEDIA_SEEKING;
\r
344 if( X_HTMLAudio_seekingFixIOS ) this._seekingFixON = true;
\r
347 if( X_HTMLAudio_seekingFixIOS ) this._seekingFixON = false;
\r
350 case 'waiting' : // 次のフレームが利用不可のため再生を停止したが、そのフレームがやがて利用可能になると想定している場合に発生
\r
351 eventType = X_EVENT_MEDIA_WAITING;
\r
355 // duration は Infinity, NaN, 0 の場合があるため、これを除外する
\r
356 // chrome18 for Android は duration = 100 の間はシークができない? 28 は可能
\r
357 if( 0 < duration && X_Type_isFinite( duration ) && duration !== 100 ){
\r
358 this.duration = duration * 1000;
\r
360 if( this._durationFixPhase === 4 ){
\r
361 console.log( '▼ DurationFix の終了 @' + e.type );
\r
362 this._durationFixPhase = 8;
\r
364 if( this.autoplay || this._playReserved ){
\r
365 console.log( '☆ 再生 <- DurationFix の終了' );
\r
368 if( X_HTMLAudio_pauseFix ){
\r
369 console.log( '☆ PAUSE <- DurationFix の終了' );
\r
370 this.actualPause();
\r
373 if( this._durationFixPhase & 3 ){ // === 1 | 2
\r
374 this._durationFixPhase = 8;
\r
379 if( this._touchState === 1 ){
\r
380 if( e.type === X_HTMLAudio_playTrigger ){
\r
381 this._touchState = 2;
\r
382 this.dispatcher[ 'asyncDispatch' ]( X_EVENT_MEDIA_WAIT_FOR_TOUCH );
\r
386 if( this.autoLoop ){
\r
387 if( !( this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){
\r
388 this.looped = true;
\r
389 this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
\r
390 this.actualPlay( X_HTMLAudio_endedFixCWV && actualEnded, X_HTMLAudio_endedFixAOSP3 && actualEnded );
\r
394 delete this.playing;
\r
395 this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
\r
398 if( this._readyState === 1 && this.duration ){
\r
399 this._readyState |= 2;
\r
400 this.dispatcher[ 'asyncDispatch' ]( X_EVENT_READY );
\r
402 // TODO 勝手に play する環境があるので pause() を実施
\r
404 if( !this.playing && !this.autoplay && !this._playReserved && !X_HTMLAudio_pauseFix ){
\r
405 this.actualPause();
\r
408 console.log( '> Audio Loaded!! ' + e.type + ' d:' + ( this.duration | 0 ) );
\r
411 this.dispatcher[ 'dispatch' ]( eventType );
\r
415 actualPlay : function( forcePlay, forceReload ){
\r
416 var raw = this[ '_rawObject' ],
\r
421 this._playReserved = true;
\r
423 if( X_HTMLAudio_pauseFix ){
\r
424 if( !raw.src ){ // X_HTMLAudio_pauseFix によって src が空になっている
\r
425 console.log( '○ 削除された audio.src の復帰' );
\r
426 raw.src = this._src;
\r
429 if( this._durationFixPhase < 2 ){
\r
434 if( this._touchState === 2 ){
\r
436 var e = X_EventDispatcher_CURRENT_EVENTS[ X_EventDispatcher_CURRENT_EVENTS.length - 1 ];
\r
437 if( !e || !e[ 'pointerType' ] ){
\r
438 alert( 'タッチイベント以外での play! ' + ( e ? e.type : '' ) );
\r
442 this._touchState = 3;
\r
444 if( this._readyState !== 3 && this._durationFixPhase < 2 ){
\r
448 delete this._playReserved;
\r
450 if( this._durationFixPhase & 3 ){ // 1 or 2
\r
451 console.log( '▲ DurationFix の開始' );
\r
452 this._durationFixPhase = 4;
\r
455 end = X_Audio_getEndTime( this );
\r
456 begin = X_Audio_getStartTime( this, end, true );
\r
458 this._lastCurrentTime = begin;
\r
460 if( this._shortPlayFixON ){
\r
461 this._shortPlayFixTime = ( 1000 < end - begin ) ? 200 : 400;
\r
462 if( this.duration < end + this._shortPlayFixTime ){
\r
463 this._shortPlayFixTime = this.duration - end;
\r
467 if( this._endedFixON ){
\r
468 console.log( '☆ audio.play をスキップ ' + begin + ' -> ' + end + ' crt:' + ( raw.currentTime | 0 ) );
\r
470 if( !this.playing ){
\r
471 if( X_HTMLAudio_volumeFix ){
\r
474 raw.volume = X_HTMLAudio_volumeEnabled ? this.gain : 1;
\r
477 this.playing = true;
\r
479 if( X_HTMLAudio_needPlayForSeek || forcePlay ){
\r
483 //http://himaxoff.blog111.fc2.com/blog-entry-97.html
\r
484 //Firefox3.6では一度も play() していない状態で currentTime = 0 を実行するとエラーになる。
\r
485 //また、GoogleChrome7 では currentTime = 0 直後に play() すると、pause()した位置前後の音が混ざることがある。(少なくとも自分の環境では)
\r
487 // iOS で duration が 0 の時に触ると error
\r
489 if( !( this._durationFixPhase % 8 ) && this.duration ) raw.currentTime = this._lastCurrentTime / 1000;
\r
491 console.log( '[HTMLAudio] play ' + begin + ' -> ' + end + ' crt:' + ( raw.currentTime | 0 ) + ' last:' + this._lastCurrentTime );
\r
494 this.playing = false;
\r
495 this._endedFixON = true;
\r
496 raw.src = this._src;
\r
497 console.log( '△ onEndedFix の開始' );
\r
498 this.dispatcher[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );
\r
502 if( X_HTMLAudio_currentTimeFix ){
\r
503 this._currentFixBegin = begin;
\r
504 this._currentFixStart = X_Timer_now();
\r
508 actualPause : function(){
\r
509 var raw = this[ '_rawObject' ];
\r
511 console.log( '[HTMLAudio] pause' );
\r
513 delete this._currentFixStart;
\r
515 !raw.error && raw.pause();
\r
517 if( X_HTMLAudio_pauseFix ){
\r
519 if( X_HTMLAudio_durationFix ){
\r
520 delete this._durationFixPhase;
\r
523 delete this.playing;
\r
526 getActualCurrentTime : function(){
\r
527 return ( X_HTMLAudio_currentTimeFix ?
\r
528 X_Timer_now() - this._currentFixStart + this._currentFixBegin :
\r
529 this._seekingFixON ? this._lastCurrentTime :
\r
530 this[ '_rawObject' ].currentTime * 1000 | 0 );
\r
533 http://www.w3schools.com/tags/av_prop_error.asp
\r
534 1 = MEDIA_ERR_ABORTED - fetching process aborted by user
\r
535 2 = MEDIA_ERR_NETWORK - error occurred when downloading
\r
536 3 = MEDIA_ERR_DECODE - error occurred when decoding
\r
537 4 = MEDIA_ERR_SRC_NOT_SUPPORTED - audio/video not supported
\r
539 getActualError : function(){
\r
540 return this[ '_rawObject' ].error || 0;
\r
543 afterUpdateState : function( result ){
\r
544 if( result & 3 ){ // seek
\r
547 if( ( result & 4 ) && X_HTMLAudio_volumeEnabled ){
\r
548 this[ '_rawObject' ].volume = this.gain;
\r
555 X_HTMLAudio && X_Audio_BACKENDS.push(
\r
559 backendName : 'HTMLAudio',
\r
561 canPlay : X_Audio_codecs,
\r
563 * HTML5 の audio 要素と video 要素でサポートされているメディアフォーマット
\r
564 * https://developer.mozilla.org/ja/docs/Web/HTML/Supported_media_formats
\r
566 * 主要ブラウザのHTML5 audioタグで使えるファイル形式の再生対応状況を調べてみた
\r
567 * http://sothis.blog.so-net.ne.jp/2010-10-27
\r
568 * ダメ元で仕様に含まれていない SHOUTcast もテストしてみました。
\r
570 * IE9 の HTML5 Audio について
\r
571 * http://kentablog.cluscore.com/2011/05/ie9-html5-audio.html
\r
572 * 1.Audioオブジェクトを作ることができないので、Audioタグを使う
\r
573 * 2.クロスドメインアクセスには、「clientaccesspolicy.xml」か「crossdomain.xml」が必要
\r
576 * IE9でHTML5 autio タグが無効になる
\r
577 * http://bbs.wankuma.com/index.cgi?mode=al2&namber=64886&KLOG=109
\r
578 * IEのバージョン9.0.8112.16421では、Audioオブジェクトのnewも対応してました。
\r
579 * createElement等で動的生成すると、よろしくない
\r
581 * media-can-play-wav-audio.html
\r
582 * https://github.com/adobe/webkit/blob/master/LayoutTests/media/media-can-play-wav-audio.html
\r
583 * testExpected("audio.canPlayType('audio/wav; codecs=1')", "probably");
\r
585 * HTML5 audioタグ ブラウザ間の違い
\r
586 * http://wiki.bit-hive.com/tomizoo/pg/HTML5%20audio%A5%BF%A5%B0%20%A5%D6%A5%E9%A5%A6%A5%B6%B4%D6%A4%CE%B0%E3%A4%A4
\r
587 * - volume, muted iPhone(iOS4-6)、Android(2.3.6)では動作せず。
\r
588 * - FireFox3.6, Android 2.3.6については、src変更後、load()を呼び出さないと切り替わらなかった。iPhoneはload()が不要。
\r
590 detect : function( proxy, ext, hash ){
\r
593 proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : X_Audio_codecs[ ext ] } );
\r
596 klass : X_HTMLAudio
\r
604 mp3: !!audioTest.canPlayType('audio/mpeg;').replace(/^no$/, ''),
\r
605 opus: !!audioTest.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ''),
\r
606 ogg: !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''),
\r
607 wav: !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''),
\r
608 aac: !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''),
\r
609 m4a: !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
\r
610 mp4: !!(audioTest.canPlayType('audio/x-mp4;') || audioTest.canPlayType('audio/mp4;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''),
\r
611 weba: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, '')
\r