HTML5 の能力を解き明かす
本記事は、マイクロソフト本社の IE チームのブログから記事を抜粋し、翻訳したものです。
【元記事】Unlocking the power of HTML5 audio(2011/5/14 1:03 AM)
私たちの生活の中にはさまざまな音があります。Web 開発者は、今日では HTML5 <audio> 要素を使ってアプリケーションに音を埋め込むことができます。制御の柔軟性とプラットフォームとの統合により、単純な効果音から、ゲームのプレイ中に流れる音、洗練されたオーディオ エンジンに至るまで、さまざまなシナリオが可能となります。
このブログ記事では、Web アプリケーションで <audio> タグを使用する際のベスト プラクティスを説明します。また、実際のサイトからの役立つヒントも紹介します。
ページに audio 要素を追加する
最初のステップはページに audio 要素を追加することです。そのためには、マークアップの中で <audio> タグを宣言するか、JavaScript コードで新しい audio 要素のインスタンスを作成するか、またはページにオーディオ ストリームを埋め込みます。
<audio src="audio/sample.mp3" autoplay>
</audio>
var audio = document.createElement("audio");
if (audio != null && audio.canPlayType && audio.canPlayType("audio/mpeg"))
{
audio.src = "audio/sample.mp3";
audio.play();
}
<audio src="data:audio/mpeg,ID3%02%00%00%00%00%..." autoplay>
</audio>
第 1 の方法では、ページの読み込み時にオーディオ コンポーネントを初期化することができます。第 2 の方法では、オーディオ クリップの読み込みがアプリケーション ライフサイクルの特定の時点まで保留されるため、ネットワーク フローをより柔軟に管理することができます。第 3 の方法 (あまり推奨されません) は、オーディオ ファイルをページにデータ URI として埋め込むため、サーバーへの要求の数を減らすことができます。
JavaScript によって生成された audio 要素は、上記のコード スニペットのようにそれが実際に DOM ツリーに追加されなくても再生可能であることに注意してください。しかし、audio 要素をページに追加すれば、既定のコントロール バーを表示することができます。
この記事では触れませんが、複数のオーディオファイル形式をサポートすることが可能です。オーディオ ファイルを自身のサーバー上でホストする場合は、サーバー側で MP3 ファイルの MIME の種類 ("audio/mpeg") を必ず登録してください。たとえば、インターネット インフォメーション サービス (IIS) では次のような設定になります。
再生前にオーディオをプリロードする
audio 要素が用意できたら、最も望ましいプリロード方法を選択します。HTML5 <audio> の仕様では、preload プロパティと、設定可能な次の 3 つの値が定義されています。
"none": メディア リソースをユーザーが必要とすることを作成者は想定していないこと、またはサーバーが不必要なトラフィックを最低限に抑えようとしていることを、ユーザー エージェントに対して示します。
ポッドキャストを掲載するブログのようなシナリオで各記事がオーディオ ファイルを持つ場合は、最初のプリロードに使う帯域幅を少なくできるので、この方法が特に適しています。ユーザーがファイルを再生する (既定のビジュアル コントロールを使う、または JavaScript の load() や play() メソッドが実行される) と、ブラウザーがオーディオ ストリームの取得を開始します。
"metadata": メディア リソースをユーザーが必要とすることを作成者は想定していないが、リソースのメタデータ (寸法や再生時間など) の取得は想定していることを、ユーザー エージェントに対して示します。
オーディオ プレイヤー コントロールを作成するときにオーディオ クリップの基本情報が必要であるが、再生する必要はない場合などに、このオプションが適しています。
"auto": ユーザー エージェントはサーバーにリスクを与えることなくユーザーのニーズを最優先することができることを、ユーザーエージェントに対して示します。この場合は、最も楽観的な判断の結果としてリソース全体がダウンロードされることもあります。ゲームを作成している場合はこの方法が最適となることがあります。この方法を使用すれば、ゲームを実際に開始する前にすべてのオーディオ クリップをプリロードできます。
audio 要素の src プロパティをプログラムで設定している場合、preload プロパティは、特に指定がなければブラウザーによって "auto" に設定されることに注意してください。このため、これ以外の値を必要とする場合は、必ず src を設定するよりも前のコードで設定を行ってください。
これらの 3 つのオプションによるネットワークへの影響は、F12 キーで起動できる開発者ツール ([ネットワーク] タブ) を使用してこのページを実行することによりプレビューできます。デバッグを行う場合は、[常にサーバーから更新する] メニューのチェック ボックスをオンにしてローカル キャッシュを無効化します。これにより、新しい呼び出しをシミュレートできます。
preload=none:
preload=metadata:
preload=auto:
初期化フェーズに関してはこのプロパティが便利ですが、この他にも、実際にブラウザーがオーディオ クリップをダウンロードして再生可能な状態になっているかどうかを調べたい場合もあります。このような情報は、ユーザー エージェントによって呼び出される canplaythrough イベントをリッスンすることによって取得できます。このイベントは、再生が今開始されたとしても、バッファー処理のために停止することなく、現在の再生レートで最後までメディア リソースを提供できるとユーザー エージェントが判断したときに呼び出されます。
var audio = document.createElement("audio");
audio.src = "piano/3C.mp3";
audio.addEventListener('ended', function () {
// 次のループの前に 500 ミリ秒待機する
setTimeout(function () { audio.play(); }, 500);
}, false);
audio.play();
ループ再生
オーディオを使用するシナリオでは、サウンド クリップを繰り返し再生する機能もよく求められます。HTML5 <audio> では "loop" プロパティを使ってこれを実現できます。これを設定するとオーディオ クリップは、ユーザーまたはアプリケーションが pause() オーディオ コントロールを呼び出すまで、繰り返し再生され続けます。
<audio src="audio/sample.mp3" autoplay loop>
</audio>
オーディオ ファイルをループ再生する別の方法としては、オーディオ クリップが終了したときにプログラムによって play() メソッドを呼び出すという方法があります。これを行うと、次のループとの間隔を制御することが可能になります。
var audio = document.createElement("audio");
audio.src = "piano/3C.mp3";
audio.addEventListener('ended', function () {
// 次のループの前に 500 ミリ秒待機する
setTimeout(function () { audio.play(); }, 500);
}, false);
audio.play();
audio 要素の play() 呼び出しは、サウンドが実際に終了する前に実行しても効果がないことに注意してください。現在のサウンドをキャンセルして再開する場合は、currentTime をリセットする必要があります。
var audio = null;
audio = document.createElement("audio");
audio.src = "piano/3C.mp3";
audio.addEventListener('ended', function () {
audio.play();
}, false);
function play() {
audio.play();
}
function restart() {
audio.currentTime = 0;
audio.play();
}
複数の audio タグ
同じオーディオ ファイルをいくつか同時に (つまりサウンドをオーバーラップさせて) 再生することが必要な場合は、同じファイルをポイントする複数の audio タグを作成することでそれを実現できます。また当然ですが、別のオーディオ ファイルを同時に再生する場合も同じ方法が可能です。この記事の最初に説明したとおり、再生する audio 要素はプログラムによって追加することも、マークアップでインスタンス化することも可能です。
次のコード スニペットでは、マークアップを使用して複数の audio ファイルの読み込みと再生を行う例を示しています。オーディオのサンプルはすべて同じ長さであり、実行が終了すると最初に戻ってループ再生します。Internet Explorer 9 で再生すると、各ループが自動的に同期されることがわかります。以下の 5 つのサウンドをまとめて再生すると、前述のデモで使用したオーディオ ファイル (sample.mp3) と同じように聞こえます。
<body>
<audio src="audio/Bass.mp3" autoplay loop>
</audio>
<audio src="audio/Drum.mp3" autoplay loop>
</audio>
<audio src="audio/Crunch.mp3" autoplay loop>
</audio>
<audio src="audio/Guitar.mp3" autoplay loop>
</audio>
<audio src="audio/Pizzicato.mp3" autoplay loop>
</audio>
</body>
この方法はとてもシンプルでわかりやすいものですが、多くの場合、開発者はオーディオ クリップをプログラムで作成する方法を好みます。次のコード スニペットは、3 つのオーディオ クリップをコードによって動的に追加する方法を示しています。これらを一緒に再生すると、C メジャーのコードになります。
AddNote("3C");
AddNote("3E");
AddNote("3G");
function AddNote(name) {
var audio = document.createElement("audio");
audio.src = "piano/" + name + ".mp3";
audio.autoplay = true;
}
このコード パターンはどのブラウザーでも機能し、非常に魅力的なシナリオを実現することができます。
ここで重要な点として、アプリケーションやゲームが複雑化すると、やがてある 2 つの制限に達する可能性があることに注意してください。2 つの制限とは、1 つのページでプリロード可能な audio 要素の数と、同時に再生可能な audio 要素の数です。
これらの数はブラウザーおよび PC の性能に依存します。私の個人的な経験からすると、Internet Explorer 9 は数十個の audio 要素を同時に問題なく処理できます。他のブラウザーについては、同様というわけではありません。複数のファイルをループ再生すると明らかな遅延や劣化が発生する場合があります。
同期の戦略
ネットワークの特性によっては、タグの追加、コンテンツの取得、および再生の準備のときに生じる遅延を常に考慮することが必要な場合があります。特に複数のファイルを処理する場合は、再生の準備にかかる時間がファイルごとに違うことがあります。たとえば次の例は、前の例で使用した 3 つのファイルをローカル ホストから読み込んだ際のものです。
[タイミング] 列に表示されているように、各ファイルが同時に準備完了となるわけではありません。
同期のために最もよく使われる方法は、最初にすべてのファイルをプリロードするという方法です。すべてのファイルの準備ができたら、ループを使って一気にそれらの再生を開始します。
var audios = [];
var loading = 0;
AddNote("2C");
AddNote("2E");
AddNote("2G");
AddNote("3C");
AddNote("3E");
AddNote("3G");
AddNote("4C");
function AddNote(name) {
loading++;
var audio = document.createElement("audio");
audio.loop = true;
audio.addEventListener("canplaythrough", function () {
loading--;
if (loading == 0) // すべてのファイルがプリロードされた場合
StartPlayingAll();
}, false);
audio.src = "piano/" + name + ".mp3";
audios.push(audio);
}
function StartPlayingAll() {
for (var i = 0; i < audios.length; i++)
audios[i].play();
}
ではすべてを合わせてみましょう。次のデモは「Frère Jacques」(「アーユースリーピング」、「かねがなる」としても知られています) のピアノ演奏をシミュレートします。ページは、最初にすべての音符の取得を行います。その間、クライアント上へのプリロードの進行状況を表示します。すべての準備ができたら、曲が始まり、ループ演奏されます。
実際のサイトでのオーディオ
ここまでは、複数のオーディオ ファイルを処理するためによく使われる方法を説明してきました。ここからは、実際に <audio> タグを使用しているいくつかの Web サイトをベスト プラクティスとして紹介します。
Pirates Love Daises: http://www.pirateslovedaisies.com (英語)
別のブログ記事でも紹介しましたが、「Pirates Love Daises」は Grant Skinner 氏によって作成された、HTML5 によるすばらしいゲームです。すばらしいゲームと視覚効果に加え、Grant のチームは洗練されたオーディオ ライブラリも開発し、それを使用してゲームの全編にわたってオーディオを再生しています。メインのロジックは AudioManager クラスにカプセル化されています。前に説明したように、このサイトは、実際にゲームを開始する前にすべてのオーディオ クリップをプリロードし、その進行状況を初期読み込みの画面で表示しています。また、このサイトではオーディオ ファイルのダウンロード中にネットワークのタイムアウトまたはエラーが発生した場合の考慮も行われています。
addAudioChannel:function(b,a,f){
var h=document.createElement("audio");
if(f!=true){
this.currAsset=h;
this.timeoutId=setTimeout($.proxy(this,"handleAudioTimeout"),e.AUDIO_TIMEOUT);
h.addEventListener("canplaythrough",$.proxy(this,"handleAudioComplete"),false);
h.addEventListener("error",$.proxy(this,"handleAudioError"),false)
}
h.setAttribute("id",a);
h.setAttribute("preload","auto");
$("").attr("src",b).appendTo(h);
$("").attr("src",b.split(".mp3")[0]+".ogg").appendTo(h);
document.body.appendChild(h)
}
,handleAudioComplete:function(b){
if(LoadedAssets.getAsset(b.target.id)!=true){
LoadedAssets.addAsset(b.target.id,true);
clearTimeout(this.timeoutId);
this.calculatePercentLoaded(true)
}
}
,handleAudioError:function(b){
trace("Error Loading Audio:",b.target.id);
LoadedAssets.addAsset(b.target.id,true);
clearTimeout(this.timeoutId);
this.calculatePercentLoaded(true)
}
,handleAudioTimeout:function(){
trace("Audio Timed Out:",this.currAsset.id);
LoadedAssets.addAsset(this.currAsset.id,true);
this.calculatePercentLoaded(true)
}
現在、Grant は、開発者が他のアプリケーションでこのサウンド エンジンを使うことができるようにするためのサウンド ライブラリ プロジェクトを実施しています。この成果に期待したいと思います。
Firework (Mike Tompkins 氏作成): www.beautyoftheweb.com/firework (英語)
この Firework のデモは非常におもしろいものです。オーディオ トラックを同時に操作して、各トラックのボリュームを動的に変えることができます。さらに、オーディオのチャンネルを操作すると、入力や設定によってインターフェイスが動的に変化します。
ここでは audio タグが HTML マークアップで宣言されています (単純に、6 つのトラックです)。進行状況はプログラムにより canplaythrough イベントをリッスンすることによって管理されています。すべてのオーディオ ファイルの再生準備が整ったら、ループでリストの処理を行い、再生を開始します。
video.addEventListener('canplaythrough', onCanPlayAudio, false);
for (var i = 0; i < 5; i++) {
var aud = document.getElementById("aud" + i);
targetVolumes.push(0);
aud.volume = 0;
audioTags.push({
"tag": aud,
"ready": false
});
aud.addEventListener('canplaythrough', onCanPlayAudio, false);
}
// オーディオ/ビデオ トラックの設定
document.getElementById("tompkins").src = MediaHelper.GetVideoUrl("Firework_3");
for (var i = 0; i < audioTracks.length; i++) {
document.getElementById("aud" + i).src = MediaHelper.GetAudioUrl(audioTracks[i]);
}
このコードでは、ボリュームを 0 と設定して開始し、再生が可能となったら動的に 1 に上げるという方法が取られています。オーディオ カードとドライバーの性能によってはオーディオが開始される際の最初にノックのような雑音が入る場合がありますが、この小さなトリックによってこれが聞こえるのを防ごうとしています。
BeatKeep: www.beatkeep.net(英語)
最後のシナリオは今回示した実例の中で最も複雑なものです。このページでは、ドラム マシンを使って自分で曲を作り、その複数個のオーディオ クリップをループ再生することができます。このアプリケーションでは、オーディオ チャンネル間の完全な同期と、複数のクリップを読み込むための高速なバッファリング システムが非常に重要です。
このドラム マシンでは、高度なタイマー ロジックと結合モデルを使って、テンポや拍子のすべてを制御することができます。そして最終的に、非常にスムーズなエクスペリエンスが提供されます。
まとめ
Internet Explorer 9 または他のブラウザーを使って、ぜひこの記事で紹介したサンプルとアプリケーションをすべて試してみてください。そしてそのエクスペリエンスがどのようなものであったか、フィードバックをお寄せください。この記事で使用したすべてのサンプル コードはこちら ( 英語) からダウンロードできます。
オーディオとビデオのコントロールに関する詳細な情報は、MIX で行われた 30 分のセッション「 <audio> と <video> を使い始めるために知っておくべきことトップ 5 ( 英語 ) 」をご覧ください。また MSDN の興味深い記事も参照してください。
この記事でオーディオクリップを使用させていただいた DoubleDominant 、およびすばらしい HTML5 エクスペリエンスを作成された Grant Skinner と Archetype に感謝いたします。
—Giorgio Sardo、シニア テクニカル エバンジェリスト、 HTML5 および Internet Explorer