OSDN Git Service

Version 0.6.175, fix X.UA & X.WebAudio.
[pettanr/clientJs.git] / 0.6.x / js / 07_audio / 02_XHTMLAudio.js
index 2082a86..a231e67 100644 (file)
@@ -6,39 +6,40 @@
  * Windows 版 Safari は QuickTime のインストールが必要\r
  * \r
  * 1. iOS4(iPod 2G) で ended に達すると音が鳴らなくなる fix で解決\r
- * 2. iOS6(iPod 4G) で ended に達すると音が鳴らなくなる fix で頻度が改善\r
+ * 2. iOS6(iPod 4G) で ended に達すると音が鳴らなくなる fix で頻度が改善 emded イベントは発しないので、timeupdate 時に currentTime で判断する\r
  * 3. WP7(IS12T) で最後の方にある音が鳴らない? mp3 cbr を使えばいい? 裏に回っても音が鳴り続ける\r
- * 4. Android 3.x で ended に達すると音が鳴らなくなる -> リロード(audio.src='';audio.sr=src)で解決\r
+ * 4. Android 3.x で ended に達すると音が鳴らなくなる -> リロード(audio.src='';audio.sr=src)で解決、但し 2.x 4.x より遅延が大きく 1 秒弱程度ある\r
  * 5. Android 2.x で ended に達すると音が鳴らなくなる -> リロード(audio.src='';audio.src=src;audio.load())でで解決\r
- * 6. Android 4.4.2- は ended に達した際に currentTime が変更できなくなり、リロードが必要になる\r
+ * 6. Android 4.4.2- は ended に達した際に currentTime が変更できなくなり、リロードが必要になる, 4.0, 4.1, 4.2, 4.3 で確認\r
+ * 7. Blink5 Opera32 Win8 は HTMLAudio が壊れている、WebAudio は mp3 がデコードに失敗、ogg が動作\r
+ * \r
+ * memo\r
+ * 1. Android4.1 iframe 内の Audio は親に focus が移っても再生を継続する\r
  */\r
 \r
 var X_HTMLAudio_playTrigger =\r
                6 <= X_UA[ 'iOS' ] ? 'loadeddata' :\r
                X_UA[ 'iOS' ] < 5  ? 'stalled' :\r
-               X_UA[ 'iOS' ]     ? 'suspend' :\r
+               X_UA[ 'iOS' ]      ? 'suspend' :\r
                X_UA[ 'Safari' ] < 4 ? 'canplaythrough' :\r
-               X_UA[ 'AndroidChromeBrowser' ] ? 'canplaythrough' :\r
+               X_UA[ 'ChromeWK' ]   ? 'canplaythrough' :\r
                // Android 2.3.5(SBM101SH) では stalled は発生しない,,, ので必ず loadeddata もチェックする\r
-               X_UA[ 'AndroidBrowser' ] ? 'stalled' :\r
+               X_UA[ 'AOSP' ] ? 'stalled' :\r
                X_UA[ 'OperaMobile' ] || X_UA[ 'OperaTablet' ] ? 'loadeddata' :\r
                        'loadeddata', //'canplay',\r
        X_HTMLAudio,\r
-       X_Audio_constructor         = X_UA[ 'Safari' ] < 4 ? function(a){ a = document.createElement( 'audio' ); return a; } : window[ 'Audio' ] || window.HTMLAudioElement,\r
        \r
        // ended が発生しない timeupdate 内で play() を呼ぶ (未検証) 不具合確認は iOS4,6\r
        X_HTMLAudio_endedFixIOS     = X_UA[ 'iOS' ] < 7,\r
        // Android 2.3.5 で ended 時に audio.src='';audio.src=src;audio.load() を実施。 2.3.4 でも問題なし。\r
-       X_HTMLAudio_endedFixAOSP2   = X_UA[ 'AndroidBrowser' ] < 3,\r
+       X_HTMLAudio_endedFixAOSP2   = X_UA[ 'AOSP' ] < 3,\r
        // Android 3.1 で ended 時に src='';src=src を実施。\r
-       X_HTMLAudio_endedFixAOSP3   = !X_HTMLAudio_endedFixAOSP2 && X_UA[ 'AndroidBrowser' ] < 4,\r
+       X_HTMLAudio_endedFixAOSP3   = !X_HTMLAudio_endedFixAOSP2 && X_UA[ 'AOSP' ] < 4,\r
        // ended 時に play() を実施, currentTime が duration に張り付き更新されなければ  src='';src=src を実施。\r
-       X_HTMLAudio_endedFixAOSP4   = !X_UA[ 'AndroidChromeBrowser' ] && 4 <= X_UA[ 'AndroidBrowser' ],\r
+       X_HTMLAudio_endedFixAOSP4   = 4 <= X_UA[ 'AOSP' ],\r
        \r
        // Opera Mobile 12 は 2回目以降の currentTime へのセットで currentTime が更新されなくなるため、タイマーを使用する\r
        X_HTMLAudio_currentTimeFix  = !!X_UA[ 'OperaMobile' ] || !!X_UA[ 'OperaTablet' ],\r
-       // Android1.6+MobileOpera12では無理っぽい、、、\r
-       X_HTMLAudio_badOperaAndroid = X_HTMLAudio_currentTimeFix && X_UA[ 'Android' ] < 2,\r
 \r
        X_HTMLAudio_volumeFix       = X_UA[ 'Chrome' ],\r
        /*\r
@@ -51,7 +52,7 @@ var X_HTMLAudio_playTrigger =
        X_HTMLAudio_pauseFix            = ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] && 0 < ' XP XPSP2 2003|XP64'.indexOf( X_UA[ 'Windows' ] ) ), // XP + Opera12 のみ?\r
        /*\r
         * durationFix\r
-        *  duration が取得できるタイミングが遅くそれまでは infinity(PC Opera12), NaN(WP9), 0(AndroidBrowser Chrome 系) が入っている\r
+        *  duration が取得できるタイミングが遅くそれまでは infinity(PC Opera12), NaN(WP9), 0(AOSP Chrome 系) が入っている\r
         * \r
         *   1. touch が不要の場合、自動で再生を開始して duration を取得するまで再生する\r
         *        -> 取得後に pause or 通常再生\r
@@ -64,71 +65,17 @@ var X_HTMLAudio_playTrigger =
         *        -> その際には timeupdate が発行されない、、、 iframe+image+audio で使わないときは破棄する、とか。\r
         *        -> opera11、10.54 WinXP はまとも、、、 portable が怪しい??\r
         */\r
-       X_HTMLAudio_need1stTouch        = X_UA[ 'iOS' ] || X_UA[ 'AndroidChromeBrowser' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ),   \r
-       X_HTMLAudio_durationFix     = ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] ) || X_UA[ 'AndroidChromeBrowser' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ),\r
+       X_HTMLAudio_need1stTouch        = X_UA[ 'iOS' ] || X_UA[ 'ChromeWK' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ),       \r
+       X_HTMLAudio_durationFix     = ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] ) || X_UA[ 'ChromeWK' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ),\r
        \r
-       X_HTMLAudio_shortPlayFix        = !X_UA[ 'AndroidChromeBrowser' ] && X_UA[ 'AndroidBrowser' ], // Android 4.1.1 でも遭遇(ただしm4a, mp3は優秀, oggはシークが乱れる)\r
+       X_HTMLAudio_shortPlayFix        = X_UA[ 'AOSP' ]; // Android 4.1.1 でも遭遇(ただしm4a, mp3は優秀, oggはシークが乱れる)\r
        \r
-       X_Audio_codecs;\r
-\r
-if( X_Audio_constructor && !X_HTMLAudio_badOperaAndroid ){\r
-       //http://himaxoff.blog111.fc2.com/blog-entry-97.html\r
-       //引数なしで new Audio() とすると、Operaでエラーになるそうなので注意。\r
-       X_TEMP.rawAudio = new X_Audio_constructor( '' );\r
        \r
-       // https://html5experts.jp/miyuki-baba/3766/\r
-       // TODO Chrome for Android31 で HE-AAC が低速再生されるバグ\r
-       // TODO Android4 標準ブラウザで ogg のシークが正しくない!\r
-       if( X_TEMP.rawAudio.canPlayType ){\r
-               X_Audio_codecs = {\r
-                 'mp3'  : X_TEMP.rawAudio.canPlayType('audio/mpeg'),\r
-                 'opus' : X_TEMP.rawAudio.canPlayType('audio/ogg; codecs="opus"'),\r
-                 'ogg'  : X_TEMP.rawAudio.canPlayType('audio/ogg; codecs="vorbis"'),\r
-                 'wav'  : X_TEMP.rawAudio.canPlayType('audio/wav; codecs="1"'),\r
-                 'aac'  : X_TEMP.rawAudio.canPlayType('audio/aac'),\r
-                 'm4a'  : X_TEMP.rawAudio.canPlayType('audio/x-m4a') + X_TEMP.rawAudio.canPlayType('audio/m4a') + X_TEMP.rawAudio.canPlayType('audio/aac'),\r
-                 'mp4'  : X_TEMP.rawAudio.canPlayType('audio/x-mp4') + X_TEMP.rawAudio.canPlayType('audio/mp4') + X_TEMP.rawAudio.canPlayType('audio/aac'),\r
-                 'weba' : X_TEMP.rawAudio.canPlayType('audio/webm; codecs="vorbis"')\r
-               };\r
-               (function( X_Audio_codecs, k, v ){\r
-                       for( k in X_Audio_codecs ){\r
-                               //if( X_EMPTY_OBJECT[ k ] ) continue;\r
-                               v = X_Audio_codecs[ k ];\r
-                               v = v && !!( v.split( 'no' ).join( '' ) );\r
-                               if( v ){\r
-                                       console.log( k + ' ' + X_Audio_codecs[ k ] );\r
-                                       X_Audio_codecs[ k ] = true;\r
-                               } else {\r
-                                       delete X_Audio_codecs[ k ];\r
-                               };\r
-                       };\r
-               })( X_Audio_codecs );\r
-       } else {\r
-               // iOS3.2.3\r
-               X_Audio_codecs = {\r
-                 'mp3'  : X_UA[ 'IE' ] || X_UA[ 'Chrome' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ]  ),\r
-                 'ogg'  : 5 <= X_UA[ 'Gecko' ] || X_UA[ 'Chrome' ] || X_UA[ 'Opera' ] ,\r
-                 'wav'  : X_UA[ 'Gecko' ] || X_UA[ 'Opera' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ]  ),\r
-                 'aac'  : X_UA[ 'IE' ] || X_UA[ 'WebKit' ],\r
-                 'm4a'  : X_UA[ 'IE' ] || X_UA[ 'WebKit' ],\r
-                 'mp4'  : X_UA[ 'IE' ] || X_UA[ 'WebKit' ],\r
-                 'weba' : 2 <= X_UA[ 'Gecko' ] || 10.6 <= X_UA[ 'Opera' ] // firefox4+(Gecko2+)\r
-               };\r
-               (function( X_Audio_codecs, k ){\r
-                       for( k in X_Audio_codecs ){\r
-                               //if( X_EMPTY_OBJECT[ k ] ) continue;\r
-                               if( X_Audio_codecs[ k ] ){\r
-                                       console.log( k + ' ' + X_Audio_codecs[ k ] );\r
-                                       X_Audio_codecs[ k ] = true;\r
-                               } else {\r
-                                       delete X_Audio_codecs[ k ];\r
-                               };\r
-                       };\r
-               })( X_Audio_codecs );\r
-       };\r
+\r
+if( X_Audio_constructor ){\r
        \r
-       X_HTMLAudio = X_Audio_AbstractAudioBackend[ 'inherits' ](\r
-               'X.AV.HTML5AudioWrapper',\r
+       X_HTMLAudio = X_AudioBase[ 'inherits' ](\r
+               'X.HTMLAudio',\r
                X_Class.POOL_OBJECT,\r
                {\r
                        _closed               : true,\r
@@ -143,7 +90,6 @@ if( X_Audio_constructor && !X_HTMLAudio_badOperaAndroid ){
                        _durationFixSkip  : X_HTMLAudio_durationFix && !X_HTMLAudio_need1stTouch,                       \r
                        _lastCurrentTime  : 0,\r
 \r
-\r
                        _shortPlayFixON   : false,\r
                        _shortPlayFixTime : 0,\r
                        \r
@@ -195,16 +141,16 @@ if( X_Audio_constructor && !X_HTMLAudio_badOperaAndroid ){
 \r
                                if( X_TEMP.rawAudio ){\r
                                        raw.src = source;\r
-                                       raw.load(); // 要る?\r
-                                       X_TEMP.rawAudio = null;\r
+                                       raw.load(); // AOSP2 で必要\r
+                                       delete X_TEMP.rawAudio;\r
                                };\r
                        },\r
                        \r
                        handleEvent : function( e ){\r
                                var raw    = this[ '_rawObject' ],\r
-                                       _ended = e.type === 'ended',\r
-                                       ended  = _ended,\r
-                                       ready  = e.type === X_HTMLAudio_playTrigger,\r
+                                       actualEnded = e.type === 'ended',\r
+                                       ended       = actualEnded,\r
+                                       ready       = e.type === X_HTMLAudio_playTrigger,\r
                                        eventType, duration, end, now;\r
                                \r
                                // global に公開\r
@@ -231,7 +177,7 @@ if( X_Audio_constructor && !X_HTMLAudio_badOperaAndroid ){
                                        //case 'loadstart' :      //    ブラウザがコンテンツの検索を開始した場合に発生\r
                                                //break;\r
                                        case 'progress' :          //   ブラウザがコンテンツの取得を実行した場合に発生\r
-                                               console.log( e.loaded + ' ' + e.total * 100 + '%' );\r
+                                               // console.log( e.loaded + ' ' + e.total * 100 + '%' );\r
                                                // iem9 で常に0 raw.networkState;\r
                                                // opera Android 12 で buffered.end() へのアクセスはエラー try catch も無効、iem9 は常に end(0) = 0\r
                                                //console.log( 'buffered.end ' + raw.buffered && raw.buffered.end(0) ); \r
@@ -250,9 +196,9 @@ if( X_Audio_constructor && !X_HTMLAudio_badOperaAndroid ){
                                                };\r
                                        case 'canplaythrough' : //      今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生\r
                                                if( this._endedFixON ){\r
+                                                       console.log( '▽ onEndedFix の終了 @' + e.type  );\r
                                                        this._endedFixON = false;\r
                                                        this.actualPlay();\r
-                                                       console.log( '▽ onEndedFix @' + e.type  );\r
                                                };\r
                                        case 'loadedmetadata' : //      ブラウザがメディアリソースの長さと寸法を判定した場合に発生\r
                                        case 'durationchange' : //  duration属性が更新された場合に発生\r
@@ -272,14 +218,14 @@ if( X_Audio_constructor && !X_HTMLAudio_badOperaAndroid ){
                                                        this._lastCurrentTime = raw.currentTime;\r
 \r
                                                        if( this.playing ){\r
-                                                               end = X_AudioWrapper_getEndTime( this ) + this._shortPlayFixTime;\r
+                                                               end = X_Audio_getEndTime( this ) + this._shortPlayFixTime;\r
                                                                now = this.getActualCurrentTime();\r
                                                                //console.log( now + ' / ' + end );\r
                                                                if( 0 + end <= 0 + now ){ // 0+ なぜか iem9 で必要,,,\r
                                                                        if( this.autoLoop ){\r
-                                                                               console.log( '☆★☆ 曲の最後に到達 @timeupdate' );\r
+                                                                               console.log( '☆★☆ 曲の最後に到達 @timeupdate now-end:' + ( now - end ) );\r
                                                                                ended = true;\r
-                                                                               if( X_HTMLAudio_endedFixIOS ) _ended = true;\r
+                                                                               if( X_HTMLAudio_endedFixIOS ) actualEnded = true;\r
                                                                        } else {\r
                                                                                this.actualPause();\r
                                                                                eventType = X_EVENT_MEDIA_ENDED;\r
@@ -362,14 +308,15 @@ if( X_Audio_constructor && !X_HTMLAudio_badOperaAndroid ){
                                                if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){\r
                                                        this.looped = true;\r
                                                        this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
-                                                       ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixIOS ) && _ended && console.log( '☆★☆ 音声の継続用の play() @ended' );\r
-                                                       this.actualPlay( ( X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixIOS ) && _ended, ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP2 ) && _ended );\r
+                                                       ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixIOS ) && actualEnded && console.log( '☆★☆ 音声の継続用の play() @ended' );\r
+                                                       this.actualPlay(\r
+                                                               ( X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixIOS ) && actualEnded,\r
+                                                               ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP2 ) && actualEnded );\r
                                                };\r
-                                               // Android4.1.1 ended 後に曲の再生が継続できない\r
                                        } else {\r
                                                this.seekTime = 0;\r
                                                delete this.playing;\r
-                                               this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );                                                       \r
+                                               this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
                                        };\r
                                } else\r
                                if( this._loaded && this.duration && !this._ready ){\r
@@ -408,8 +355,8 @@ if( X_Audio_constructor && !X_HTMLAudio_badOperaAndroid ){
                                        this._durationFixPhase = 2;\r
                                };\r
 \r
-                               end   = X_AudioWrapper_getEndTime( this );\r
-                               begin = X_AudioWrapper_getStartTime( this, end, true );\r
+                               end   = X_Audio_getEndTime( this );\r
+                               begin = X_Audio_getStartTime( this, end, true );\r
 \r
                                this._lastCurrentTime = begin / 1000;\r
 \r
@@ -445,20 +392,17 @@ if( X_Audio_constructor && !X_HTMLAudio_badOperaAndroid ){
                                        \r
                                        // Android4.0.5 で ended イベント時に currentTime が duration に張り付いたまま変更できない\r
                                        if( forceReload || ( raw.duration && raw.currentTime === raw.duration ) ){\r
-                                               console.log( '△ onEndedFix の開始' );\r
                                                raw.src          = '';\r
                                                raw.src          = this._src;\r
-                                               raw.currentTime  = this._lastCurrentTime;\r
                                                this.playing     = false;\r
                                                this._endedFixON = true;\r
+                                               console.log( '△ onEndedFix の開始' );\r
+                                               raw.currentTime  = this._lastCurrentTime;\r
                                                this.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );                                                                                             \r
                                                X_HTMLAudio_endedFixAOSP2 && raw.load();\r
                                        };                                                      \r
                                };\r
 \r
-                               /* if( X_HTMLAudio_durationFix ){\r
-                                       this._currentFixBegin = begin;\r
-                               } else */\r
                                if( X_HTMLAudio_currentTimeFix ){\r
                                        this._currentFixBegin = begin;\r
                                        this._currentFixStart = X_Timer_now();\r
@@ -512,9 +456,11 @@ if( X_Audio_constructor && !X_HTMLAudio_badOperaAndroid ){
                }\r
        );\r
        \r
-       X_Audio_BACKENDS.push(\r
+       X_HTMLAudio && X_Audio_BACKENDS.push(\r
                {\r
-                       backendName : 'HTML Audio',\r
+                       backendID   : 2,\r
+                       \r
+                       backendName : 'HTMLAudio',\r
                        \r
                        canPlay : X_Audio_codecs,\r
                /*\r