音が鳴るようになったら、今度は遅れた——効果音の遅延と重なりを直した話
前回、音が鳴らない問題の地図を書きました。結論は「効果音も全部WAVファイルにして、BGMと同じAudio要素で再生する」でした。これで鳴らない問題は消えました。
そして今度は、遅れました。
「若干遅れる」から始まる犯人探し
報告は「SEが若干遅れる」というものでした。厄介な種類の報告です。鳴らないなら原因は切り分けられますが、遅れるは「気のせいかもしれない」の隣にあります。まず、遅れているのが本当かを数字にするところから始めました。
最初に疑ったのは音源です。書き出しに無音が入っていれば、コードがどれだけ速くても遅れて聞こえます。ありがちな話なので、共通効果音のWAV22本を全部デコードして、先頭から「ピークの2%を超える最初のサンプル」までの時間を測りました。
結果は0.1〜0.3ms。全ファイル、ほぼ完璧に切り詰められていました。音源は無実です。ここで疑いが再生方法に移りました。
犯人は「巻き戻して鳴らす」だった
当時の実装は素直なものでした。音1種類につきAudio要素を1個持っておき、鳴らすときは再生位置を先頭に戻してplay()する。よく見る書き方です。
ところがこの「先頭に戻す」がシーク扱いになり、実際に音が進み始めるまで待たされます。同じ端末・同じブラウザで測った数字がこれです。
| 再生方法 | 音が出始めるまで |
|---|---|
| Audio要素を巻き戻して再生(当時) | 初回 102.6ms、以降 8.7〜14.2ms |
| Web Audio(デコード済みを再生) | 0.1〜0.3ms |
ハードウェア由来の出力遅延はどちらにも等しく乗るので、差分がまるごと上乗せぶんです。しかもこれはPCのChromeでの数字で、モバイルSafariのAudio要素はさらに悪化します。「若干遅れる」の正体でした。
もうひとつ、報告されていなかった不具合
調べている途中で、誰も報告していない問題が見つかりました。音1種類につき要素が1個しかないということは、同じ音が連続すると前の音が途中で切れるということです。
実際に測ると、320msある撃破音が68msまで進んだところで2発目に巻き戻され、1発目が消えていました。敵が同時に倒れる場面で、鳴るべき音が鳴っていなかったわけです。遅延より、こちらのほうが体験としては損をしていたかもしれません。
合成に戻ったわけではない
直し方は、WAVを起動時に一度デコードしてメモリに持ち、鳴らすたびに使い捨ての発音体を作って再生する方式です。これで遅延は0.1〜0.3msになり、発音体が毎回別なので音は自然に重なります。
ここで自分に念を押したのは、これは前回やめた「WebAudio合成」への逆戻りではないということです。音源は同じWAVのままで、音色は1ミリも変わりません。変えたのは再生経路だけです。前回WebAudioで鳴らなかった原因は合成そのものではなく、オーディオの文脈が停止したまま復帰していなかったことでした。初回タップで復帰させ、それでも駄目な環境ではAudio要素に自動で退避する作りにしてあります。
「一度失敗した技術」に戻るときは、失敗の原因を名指しできるかどうかが分かれ目だと思います。名指しできないうちに戻ると、同じ穴に落ちます。
おまけ: 効果音だと思っていたものが曲だった
同じ流れで「結果画面でBGMと効果音が重なって濁る」という報告も来ました。原因を探す前に、まず結果音の長さを測りました。
| ファイル | 長さ |
|---|---|
| クリア時のジングル | 6.0秒 |
| 勝利時のジングル | 9.7秒 |
| 失敗時のジングル | 22.8秒 |
22.8秒は効果音ではありません。曲です。これがループBGMと同時に鳴っていれば、濁って当然でした。あるゲームではBGM開始とジングル開始を同じ行で並べて呼んでいて、別のゲームでは「2.2秒後にBGMを重ねる」という固定待ちを入れていました。9.7秒のジングルに対して2.2秒なので、7.5秒ぶん重なっていた計算になります。
直し方は単純で、ジングルが鳴り終わってからBGMを始めるようにしただけです。ただし固定の待ち時間は使いませんでした。音源を差し替えた瞬間にまたズレるからです。「終わったら次を鳴らす」を仕組みとして持たせておけば、長さが変わっても勝手に正しくなります。
教訓
今回いちばん効いたのは、体感の報告を最初に数字に変換したことでした。「遅れる」は主観ですが、9〜100msは主観ではありません。数字にした瞬間に、直すべき場所とそうでない場所(音源)がはっきり分かれました。
そして「効果音」という名前を疑わなかったせいで、22.8秒の曲を効果音として扱い続けていました。ファイル名やフォルダ名は、作った本人の思い込みをそのまま固定します。ときどき中身を測ってみるといいです。うちは測って驚きました。