ブラウザ、特にスマホで絶対任意のタイミングで音を鳴らすためにやったことを書きます。
ユースケース
イヤホンで音楽を聴きながら花火を見る実験企画のアプリで、ユーザーそれぞれのデバイスで同時に音を再生しなければならない、という要求がありました。しかもブラウザで。
本題ではない技術仕様は割愛しますが、タイマーを用意して時間になったら音楽を流すという仕組みです。単純にWeb Audio
をつかって音を鳴らせばいいと思っていたのですが、面倒なケースが色々あったので解決策を書き留めます。
事前にボタン等を押させる
音を鳴らすために必要なことです。
Chrome等で実装されている仕様で、避けることはできません。ユーザーの操作なしに1回目の音を鳴らすことはできません。何か操作をさせてから、鳴らすようにしましょう。また、音源を事前ロードすることを忘れずに。
未来時間のstart
を使わない
音を鳴らすために必要なことです。
Web Audio
のAudioBufferSourceNode
を使う時、任意の時間で正確に再生させるためにはstart
を使うことができますが、未来時間を指定すると一部デバイスで再生されない現象がありました。なので頑張ってギリギリ未来時間にならない方法を考えましょう。
wavを使う
音を鳴らすために必要なことです。
AudioContext
のdecodeAudioData
を使う時、mp3ファイルをデコードできないブラウザがありました。独自実装する形でのpolyfillとかもあるかもしれませんが、一番簡単で安全な方法はwavを使うことでした。
残念ながら、ここ2,3年のAndroidデバイスでも標準ブラウザで発生する現象です。各社がカスタマイズしたりすることによって、古いバージョンのchromiumが使われていたりします。
通信量の問題はあるので、そこはサポートしないか、独自の方法を用いるなども選択肢ですね。
直前に別のノイズ音源を鳴らす
鳴らしたい音源が頭から鳴ることを保証するために必要なことです。
iOS
では音を鳴らしてから一定時間が経つと、オーディオ機器との接続がセーブ状態(?)に入ります。その状態から音源を再生すると、最初に「プツッ」と音が入ったり不都合があります。そのため、小さな音のノイズ音源を用意して、直前に再生しておきましょう。無音だと効果がないので注意です。
visibilitychange
をキャッチして、音が鳴るかチェックする
ページを開いてから、音を鳴らすまでに時間が開く時、ユーザーがホーム画面に戻るなどの操作をして、戻ってきた時にも必ず音を鳴らすために必要なことです。
iOS
でページを開いてから、ホーム画面に戻って一定の操作をしてからマルチタスクで戻ってくると、音が再生できない現象がありました。その状態からユーザーの操作なしに復帰することは出来なかったので、ページがアクティブになった時音が再生できるかどうか確認するべきです。
これはWeb Audio
で任意のタイミングで音を再生したい時に重要なのですが、何かを再生するまで音が鳴るかどうか確認する方法がありません。そのため今回は、短いアクティベート音を用意して
https://qiita.com/s9prfe/items/ec199b31009bf90b4e22
この方法で音が鳴るかどうか確認しました。
音が鳴らないことを検知した時、ボタンを押させる
特殊ケースのために必要なことです。
前セクションのケースなど、あらゆる場合で音が鳴らなかった場合等のために、再度アクティブにさせるボタンが必要です。今回はこのように対処しました。
それでも音が鳴らないことを検知した時、ページを更新させる
特殊ケースのために必要なことです。
例えばiOS
で、
- イヤホンを挿したままページを開き、音が鳴っていることを確認する
- ホームボタンを押す
- イヤホンを抜く
- 音が鳴らない
- イヤホンを挿しても鳴らない
- ボタンを押してもならない
...
という現象がありました。この場合に限らず、スマホで音を再生する場合の最終手段としてこれは必要のようです。よってこれも、前セクションと同じように対処しました。
おまけ
-
baseLatency
は未定義の可能性がある
あとがき
カスタマイズされたAndroidデバイスや、OperaNeonなど未だにかなり古いバージョンのChromiumベースなブラウザが存在します。これらまでサポートすると大変ですが、4,5年前のデバイスを使っているユーザーというのは実際ざらにいるでしょうね。手持ちの中にも、そういうデバイスがありました。
テストをする中で手持ちのデバイスにも限界があるので、クラウドでテストできるサービスを探すと、音が鳴るのはLambdaTest
というサービスでした。が、時々音が鳴らないデバイスもあって、アプリのせいなのかサービスのせいなのか切り分けに困ります。結局こういう場合は実機でテストするのがベストです。