ブラウザで音は鳴るのか!?Audio要素とWeb Audioの実用性を問う
ブラウザで音を鳴らす方法は、実に奥が深い。
その昔、ガラケー向けブラウザなんかだとcHTMLなんて規格があって、16和音着メロなんかは余裕で鳴らし放題、なおなつ、環境による音の鳴り方の違いなんてほとんど無かった。デスクトップ向けだと、当時はIEなんかがbgsoundなる特殊な独自実装タグを持っていて、オモチャ感覚で活用されることはあった。けれど、QuickTimeやJavaApplet、Flashとどんどんプラグインへの置換えが進んでいった。
そして今、やはりHTML5の時代。Web標準を使って音を鳴らすのがトレンドみたいになっている。しかしこの方法、以前のようなシンプルさは期待しない方がいい。私も実際にWebの技術で音を鳴らしてみたのだけれど、ずいぶんと厄介な問題にぶつかった。今日はこのあたりのハマった問題なんかも含めて、ブラウザ(Web)で音を鳴らすとはどういうことなのかを語ってみる。
Audio要素の使いドコロ
ブラウザで音を鳴らすにはどうすればいいのか?HTML5の時代に則したやり方としては、2つある。
まず一つ目は「Audio要素」。HTML5が登場後、比較的早期に実装された。使い方は非常にシンプルで、IE8まで実装されていたbgsoundタグのユースケースを継承したような仕様を持つ。以下のように、HTMLドキュメント内にaudioタグを埋め込むだけで利用できる。
<audio> <source src="sample.mp3"> <source src="sample.ogg"> </audio>
sourceタグは、サウンドファイルの置いてある場所を表している。ブラウザによる対応の差を埋めるため、基本は複数のフォーマットを指定する。どんなフォーマットがいいかは、後で説明する。
Audio要素はユースケースとしては2タイプある。Youtubeみたいにオーディオプレイヤーな感じで使うものと、背景サウンドみたいな感じで勝手に鳴らすもの。前者のような、ユーザー側にサウンドを調整して再生させるようなインタフェースを提供する場合、audioタグの書き方はこう変わる。
<audio controls preload> ・・・
controlsというのが、専用のUIを表示するための属性指定になる。preloadは、Webページを表示時直後に読み込みを開始するというものなのだけど、これは開発するアプリの要件次第だと思う。音声の再生程度なら再生ボタンを押した直後に読み込みを開始しても、ユーザはそこまで不快に感じないケースも決して少なくない。
ただし、SEみたいな感じですぐに鳴って欲しいものもある。サーバの負荷が高くなる可能性があるけれど、それでもなおユーザ体験を優先したいケースだ。この場合は、preloadは必須だろう。そもそもブラウザによっては、この属性が無視されたりすることもあるので、あまりシビアなことをやらせるのは考えものなのだけど。
ちなみにChromeだと、こんなUIが表示される。
さて、逆のパターン、今度はプログラマー側で音を制御したい場合について取り上げる。例えば、Webサイトの雰囲気を良くするために小鳥の鳴き声とか川の流れる音なんかを鳴らす場合、以下のような記述になる。
<audio autoplay> ・・・
音を自動的に再生するための属性autoplayを指定している。こういう書き方をすると、UIは表示されないけれど、Webページを表示した際に(勝手に)音が鳴るという状態になる。
ゲームのBGMを鳴らすには若干辛いAudio要素
Audio要素には限界というのがある。Audio要素はシンプルに使わせることをモットーにしている技術だから、あまり複雑なことをするようなユースケースには向かない。その代表的な例としては、ゲーム向けのBGMが挙げられる。
ゲームBGMだと、ループさせる機能なんてのがほぼ必須になる。そして、日本的なゲームというのは、ループの「繋ぎ目」を意識させないようなやり方が求められる。Direct Soundを使ったことがある人ならわかると思うけど、キレイに継ぎ目なくループさせようとすると、WAVやMP3みたいな波形をそのまま符号化しているようなフォーマットのものは、バイナリレベルの操作、つまりは、メモリの操作というのが求められる。
このあたりの、低レイヤーなサウンド制御のユースケースをカバーするために生まれたのが、「Web Audio」。ゲーム制作なんかで求められるような、ニッチなユースケースはそこそこに拾ってくれている。サウンドのループもそうだし、洞窟なんかでやたらリバーブがかかるようなエフェクトとか、そういうのもWeb Audioはちゃんとキャッチアップしてくれている。
ただし、Web Audioを使うにはJavaScriptの知識が必須となる。Audioタグみたいなシンプルなインタフェースは無くて、全てをJavaScript上で専用のオブジェクトを通じて操作することになる。ここまでガッツリと低いレイヤーを操作するのだから、そりゃまぁ多少のコードは書かないと辛いというところはあるだろう。Direct Soundと比べると、若干レイヤーは高くてできる事は狭く、それこそマップドメモリみたいなものを使ってストリーミング再生を細かく制御するようなこととかそういうのはできなけれど(こういうのはやはりC++なんかを使わないと辛い!)、ユーザの耳にどう聞こえるかという点なら、ほぼほぼのユースケースを満たしていると考えていい。
最もシンプルな書き方だとこうなる。
var context = new AudioContext(); var xhr = new XMLHttpRequest(); xhr.open('GET', "sample.mp3"); xhr.responseType = 'arraybuffer'; xhr.onload = function() { if (xhr.status === 200 || xhr.status === 0) { var arrayBuffer = xhr.response; if (arrayBuffer instanceof ArrayBuffer) { context.decodeAudioData(arrayBuffer, function(audioBuffer) { var source = context.createBufferSource(); source.buffer = audioBuffer; var gainNode = context.createGain(); source.connect(gainNode); source.connect(context.destination); gainNode.connect(context.destination); // 再生開始 source.start(0); } } }; xhr.send(null);
ただ、Web Audioはそのレイヤーの低さから、問題も少なくない。
注意点1 : XHRを使っている
先ほどのサンプルを見たらすぐにわかると思うけど、Web AudioはXMLHttpRequestを使う。このあたり、注意が必要だ。例えば、HTML5ハイブリッドアプリケーションだと、XMLHttpRequestは意外にもローカルファイルに対してアクセスするケースで、パフォーマンス問題に発展することがある。また当然だけど、Cross Originの問題だって発生する。
こんなのは断片的な問題だとは思うのだけれど、とにかく、Audio要素とWeb Audioとでは、ファイルを取得する手段が違うということには注意が必要だろう。Web Audioの方が、制約が強いという認識を持てばだいたい正解と言っていいだろう。
注意点2 : ファイルフォーマットのサポート
動画の世界では未だ乱立が酷いファイルフォーマットだけれど、音の方はなんとなく「.ogg」に統一という感じでまとまりつつある。ただ、まだまだAppleなんかが色々問題を抱えてて、実質的な正解は「mp3」という状況。mp3だと、どんなブラウザでもしっかりと動いてくれるという状況みたいだから、ブラウザで音を鳴らす場合、しばらくは「mp3」を選択すべきだろう。
mp3だけでOK、なんてのはプログラマーを結構ハッピーにしてくれる。Audio要素の場合、複数フォーマットを指定してブラウザ側に利用可能なフォーマットを選択させるようなやり方になる。プログラマにとっての負担なんて、些細なものだ。しかし、Web Audioはフォーマットをユーザプログラム側でわざわざ判定してあげなくてはいけない。oggに対応しているのかを判定して、無理ならmp3をロードする、みたいな泥臭い処理を逐一書かなくてはいけないのだ。フォーマットが乱立してしまうと、プログラマの負荷はそれなりに大きいものになる。
Audio要素を使う場合、フォーマットをmp3のみ指定とする場合、sourceタグが不要となる。なので、以下のようにシンプルな記述を行うことができる。
<audio src="sample.mp3"></audio>
私もこの書き方をよく使っている。
注意点3 : ブラウザのサポート状況
Web Audioのサポート状況が微妙だ。FirefoxやChrome、Safariあたりはサポートしているけれど、IEは2015年1月の今の段階において未だにサポートしていない。Androidブラウザもだ。そろそろサポートされていておかしくない時期なのに、なぜか実装されない。これから先、どこかのタイミングでサポートされるのだろうけど、当面の間は厳しそう。
そんな理由もあって、結局のところ現実的な手段としてはAudio要素(IE8以下はbgsound)ということになる。ただ、iOS版Safariについては、地味にautoplayがきかず、必ずユーザ側で再生の操作を行なわなくてはいけなかったりで、機能が全て満足に動くというのも珍しい状況である。mp3もいつサポートが切られるのかわからない。
したがって、現段階で最も安全な手段は「Youtube」ということになる。Youtubeは結構ユースケースが限られるのだけれど、使えるなら出来る限り使った方がいい。どうしてもHTML5的なモダンなWeb標準を使って音を鳴らさなくてはいけない!なんて状況に陥ることがあるとすれば、環境を制限しやすいゲームのHTML5ハイブリッド開発とか、そもそもインターネットとは無縁な環境に置かれているようなガチガチなエンタープライズ向けアプリになると思う。