Mobile Safari, Chrome for Android での Web Audio API 覚え書き


## 調査環境

- iOS 8.1 + iPhone 5
- Chrome for Android 38.0 + Xperia A


## iOS タッチ制約

- 初回の start() はユーザのタッチ操作を起点とした実行コンテキストで実行する必要がある
  - AudioContext のインスタンスごとに制約がかかっている
- 実行コンテキスト
 - setTimeout は大丈夫
 - decodeAudioData はアウト。コールバック中で初回の start() を呼び出しても再生されない。

タッチ制約にハマる例

    element.addEventListener('click', function() {
		context.decodeAudioData(buffer, function(buffer) {
			var src = ctx.createBufferSource();
			src.buffer = buffer;
			src.connect(context.destination);
			// タッチ制約により動作しない
			src.start(context.currentTime);
		});
    }, false);

### 回避方法

次のようなコードを仕込んでおくとタッチ制約を回避できる。

	context.createBufferSource().start(0);

## デコード時間

decodeAudioData の所要時間を計測（ms）

### iOS

iOS 8.1 + iPhone 5

- HE-AAC
 - 441, 446, 448
- AAC
 - 250, 252, 251


### Chrome for Android

- HE-AAC
 - 1915, 1902, 1789, 2223, 2059, 
- AAC
 - 2507, 2914, 2698, 2878, 2967,


### DataRate ごとのデコード時間（Chrome）

decodeAudioData のコストが高すぎる為、bitrate ごとの値を取ってみる。
HE-AAC でそれぞれ DataRate を変更し、計測した数値が以下。

- 56kbit/s: 2102, 1941, 1852, 1984, 2088
- 48kbit/s: 1772, 2043, 2048, 2005, 1983
- 32kbit/s: 1806, 1960, 1891, 1922, 1784
- 24kbit/s: 1658, 1829, 1860, 1805, 1739


### createBuffer を使う（iOS）

Safari では ArrayBuffer からオーディオデータへの変換に decodeAudioData に代えて [createBuffer が使える](https://developer.apple.com/library/safari/documentation/audiovideo/conceptual/Using_HTML5_Audio_Video/PlayingandSynthesizingSounds/PlayingandSynthesizingSounds.html)。

createBuffer は何故か変換コストを要しないため iOS ではこちらの方が高速に動作する。
但し標準ではないのでいつ切られてもおかしくなく、decodeAudioData の方が安心して使える。

### CPU 使用率

iOS での AAC、HE-AAC それぞれのデコード所要時間は次の通り（com.apple.WebKit の CPU Usage）

AAC で 21〜25%、
HE-AAC で 35〜40%


### ファイル記録

デコード時間の計測に利用したファイルはそれぞれ次の通り

HE-AAC

		Data format:     2 ch,  16000 Hz, 'aac ' (0x00000000) 0 bits/channel, 0 bytes/packet, 1024 frames/packet, 0 bytes/frame
		Channel layout: Stereo (L R)
		estimated duration: 35.587750 sec
		audio bytes: 251859
		audio packets: 559
		bit rate: 56319 bits per second
		packet size upper bound: 796
		maximum packet size: 796
		audio data file offset: 4096
		optimized
		audio 569404 valid frames + 2112 priming + 900 remainder = 572416
		source bit depth: I16
		format list:
		[ 0] format:	  2 ch,  32000 Hz, 'aach' (0x00000000) 0 bits/channel, 0 bytes/packet, 2048 frames/packet, 0 bytes/frame
		Channel layout: Stereo (L R)
		[ 1] format:	  2 ch,  16000 Hz, 'aac ' (0x00000000) 0 bits/channel, 0 bytes/packet, 1024 frames/packet, 0 bytes/frame
		Channel layout: Stereo (L R)
		info dictionary:
			approximate duration in seconds          35.588


AAC

		Data format:     2 ch,  32000 Hz, 'aac ' (0x00000000) 0 bits/channel, 0 bytes/packet, 1024 frames/packet, 0 bytes/frame
		Channel layout: Stereo (L R)
		estimated duration: 35.587750 sec
		audio bytes: 290577
		audio packets: 1115
		bit rate: 65151 bits per second
		packet size upper bound: 359
		maximum packet size: 359
		audio data file offset: 8192
		optimized
		audio 1138808 valid frames + 2112 priming + 840 remainder = 1141760
		source bit depth: I16
		format list:
		[ 0] format:	  2 ch,  32000 Hz, 'aac ' (0x00000000) 0 bits/channel, 0 bytes/packet, 1024 frames/packet, 0 bytes/frame
		Channel layout: Stereo (L R)
		info dictionary:
			approximate duration in seconds          35.588

## Chrome for Android 一部端末での問題

やはり端末間の差違がある。
音質やノイズの有無も端末によってかなり変わるため注意が必要。


### ノイズが混ざる（処理遅延が生じる）

L-01F 等の一部端末で Web Audio API の再生結果に特定条件下でノイズが混ざることがある。

描画レート（描画 FPS）が下がるとノイズが混ざり始め、レートを上げると再生結果が正常になるというもので、オーディオ処理が描画スレッドに巻き込まれているような動作を見せる。

次のようなコードを入れて無理矢理 FPS を稼ぐことで上記問題は回避できる。
	
    <div id="fps-slowdown-make-sound-noisy"></div>
&nbsp;

    #fps-slowdown-make-sound-noisy {
 	  width:0px;height:0px;
 	  position: absolute;
	  -webkit-animation-name: anim-void;
	  -webkit-animation-duration: 1s;
	  -webkit-animation-iteration-count: infinite;
    }
    @-webkit-keyframes anim-void {
	  from { top: 0px; }
	  to   { top: 1px; }
    }

## API が無い

NEON 命令に対応していない端末では Web Audio API 自体が無効化されている（生えていない）。


## iOS 7.x で decodeAudioData がキャンセルされる

（iOS 8.x では未検証）

iOS 7.1 で decodeAudioData に処理が入った瞬間にスクリーンを長押しする（スクロールを繰り返す）と decoeAudioData の処理がキャンセルされることがある（エラーやコールバックの発火もなく、ただ処理が消滅する）。