Android のスマートフォンで音声を鳴らすときに気をつけることは、ここ十数年で大きく変わりました。昔は機種ごとの違いに振り回されましたが、今はパソコンのブラウザーとほぼ同じ考え方で扱えます。ここでは、今の Android のブラウザーで音声を再生するときの決まりと、形式の選び方をまとめます。
昔は機種ごとにばらばらだった
初期の Android には、OS に付いてくる標準のブラウザーがあり、端末を作る会社ごとに手が加えられていることもありました。そのため、同じ OS の版でも、audio 要素が動かない、特定の形式だけ再生できない、イベントが起きない、といった違いが機種ごとにありました。動作を確かめるには、実際の端末を何台も用意するしかありませんでした。
今の Android では、Chrome のほか、Firefox や各社のブラウザーが使われています。多くは Chrome と同じ Chromium をもとにしていて、更新もアプリとして配られます。音声の扱いは、パソコン向けの同じブラウザーとほとんど変わりません。機種ごとの違いを細かく気にするより、標準の書き方に沿って作り、失敗したときの扱いを用意しておくことが大切です。
なお、アプリの中でページを表示する WebView では、自動再生を許すかどうかをアプリの側の設定 (WebSettings の setMediaPlaybackRequiresUserGesture()) で変えられます。ブラウザーとアプリの中とで動きが違うときは、この設定を疑います。
自動再生には利用者の操作が要る
Android の Chrome などでも、音の出る再生を勝手に始めることはできません。基本は、利用者がページで操作 (タップやクリック、キー入力) をしてから再生する、という決まりです。音を消した動画の自動再生は許されます。
ブラウザーによっては、過去にそのサイトで音声をよく再生していた場合や、ホーム画面に追加したサイトなどで、自動再生を許すことがあります。ただし、条件はブラウザーによって違い、利用者の設定でも変わります。作る側は「操作が無いと鳴らない」ことを前提にし、再生ボタンを用意しておきます。そもそも、ページを開いた途端に音が鳴るのは、電車の中などでは迷惑になりがちです。
const audio = document.querySelector("#talk");
const message = document.querySelector("#audio-message");
document.querySelector("#play-button").addEventListener("click", () => {
audio.play().catch((error) => {
// 再生が許されなかった、形式に対応していない、などの理由で失敗する
message.textContent = "音声を再生できませんでした (" + error.name + ")";
});
});
play() は Promise を返し、再生できなければ失敗します。失敗を捕まえないと、コンソールにエラーが出るだけで、利用者には何も起きていないように見えます。
対応する形式
今の Android のブラウザーは、おおむね次の形式を再生できます。
- MP3 (
audio/mpeg) - AAC を MP4 に入れたもの (拡張子 .m4a、
audio/mp4) - Ogg Vorbis (
audio/ogg) - Opus (Ogg や WebM に入れたもの)
- WAV、FLAC
ただし、iPhone の Safari など、ほかのブラウザーでも再生できるかは別の話です。どの環境でも鳴らしたいなら、MP3 か AAC (.m4a) を用意しておくのが無難です。
再生できるかどうかは、canPlayType() で確かめられます。結果は "probably" (たぶんできる)、"maybe" (できるかもしれない)、"" (できない) の 3 通りで、「できる」と断言はしません。
const probe = document.createElement("audio");
const types = {
"MP3": "audio/mpeg",
"AAC (.m4a)": 'audio/mp4; codecs="mp4a.40.2"',
"Ogg Vorbis": 'audio/ogg; codecs="vorbis"',
"Opus (Ogg)": 'audio/ogg; codecs="opus"',
};
for (const [name, type] of Object.entries(types)) {
// 空文字列なら再生できない
console.log(name, probe.canPlayType(type) || "(非対応)");
}
codecs まで書くと、より確かな答えが返ります。形式名だけだと "maybe" になりやすいです。
audio 要素に複数の source を書く
形式ごとに canPlayType() で調べて切り替える処理を書かなくても、audio 要素の中に source 要素を並べておけば、ブラウザーが上から順に見て、再生できる最初のものを選びます。
<audio controls preload="metadata">
<!-- 上から順に試され、再生できる最初のものが使われる -->
<source src="/audio/talk.opus" type='audio/ogg; codecs="opus"'>
<source src="/audio/talk.m4a" type="audio/mp4">
<source src="/audio/talk.mp3" type="audio/mpeg">
<p><a href="/audio/talk.mp3">音声ファイル (MP3)</a>をダウンロードして聞けます。</p>
</audio>
typeを書いておくと、ブラウザーは再生できない形式のファイルを読みに行かずに済みます- ファイルの大きさが小さくなる形式 (Opus など) を先に、どこでも再生できる形式 (MP3) を最後に並べます
preload="metadata"にすると、最初は長さなどの情報だけを読みます。モバイル回線の通信量を抑えられますaudio要素の中の文章やリンクは、audio要素そのものに対応していないブラウザーでだけ表示されます
まとめ
- 昔の Android は機種ごとに音声の扱いがばらばらだったが、今はパソコンのブラウザーとほぼ同じに扱える
- 音の出る自動再生は、利用者の操作が無いとできないことを前提にし、
play()の失敗を捕まえて再生ボタンを見せる - アプリの中の WebView では、自動再生の扱いがアプリの設定で変わる
- 再生できる形式は
canPlayType()で確かめられる。どこでも鳴らすなら MP3 か AAC を用意する audio要素の中にsourceを並べれば、ブラウザーが再生できるものを選ぶ