1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
|
---
title: Web ゲーム用のオーディオ
slug: Games/Techniques/Audio_for_Web_Games
tags:
- Audio
- Games
- Web Audio API
- audio sprites
translation_of: Games/Techniques/Audio_for_Web_Games
---
<div>{{GamesSidebar}}</div>
<div>{{IncludeSubnav("/ja/docs/Games")}}</div>
<div class="summary">
<p>オーディオはゲームの重要部分です。つまりこれはフィードバックと雰囲気を与えます。web ベースのオーディオは早くから成熟していますが、まだ多くのブラウザーに案内すべき違いがあります。ゲームエクスペリエンスにとってどのオーディオパーツが重要で、どれがあっても良いけど不要なものかを決めて、それに従って戦略を決めるのが必要となる事がよくあります。この記事では web ゲーム用のオーディオを実装するための詳細なガイドを提供し、現在なるべく広い範囲のプラットフォームで動作するものを観察します。</p>
</div>
<h2 id="モバイルオーディオの注意点">モバイルオーディオの注意点</h2>
<p>これまでウェブオーディオのサポートを提供するのに最も難しいプラットフォームはモバイルプラットフォームでした。不幸にもこれはゲームで遊ぶ人が良くいるプラットフォームです。デスクトップとモバイルのブラウザーにはいくつかの違いがあり、それがブラウザーベンダーに、ゲーム開発者が作業するのが難しくなるウェブオーディオの選択を起こすことがありました。これを見ていきましょう。</p>
<h3 id="自動再生">自動再生</h3>
<p>多くのモバイルブラウザーは、ゲームがオーディオ再生するように要求しても単に無視します。つまりオーディオの再生にはユーザーが開始するイベントが必要です。これはオーディオ再生の構成を考慮する必要があるということです。これは通常、事前にオーディオを読み込んでユーザーが開始するイベントに準備しておくことで守られます。</p>
<p>もっと受け身にオーディオを自動再生するには、例えばゲームがロードするとすぐに始まるバックグラウンドミュージックでは、仕掛けをして *any* ユーザー開始イベントを検知して再生を開始します。その他のゲームで使われる予定のアクティブなサウンドについては、スタートボタンのようなものが押されたらすぐに準備しておくよう考慮します。</p>
<p>このようにオーディオを準備するため、その一部を再生したくなります。つまりこの理由のため、オーディオサンプルの最後に無音期間を入れておくのが便利です。その無音に移動したり、再生や停止することは、JavaScript を使ってファイルの任意の地点を再生できることを意味します。</p>
<div class="note">
<p><strong>注</strong>: もしブラウザーがあなたに音量を変更することを許可するならば、ファイルを音量ゼロで再生することも可能でしょう(後述)。また、オーディオを再生後に即時停止することは、オーディオの小さな断片が再生されないことを保証しない、ということにも注意してください。</p>
</div>
<div class="note">
<p><strong>注</strong>: モバイルのホーム画面にウェブアプリを追加することで特性が変化してしまうかもしれません。今のところ、iOS上の自動再生がこのケースに当てはまるようです。可能であれば、いくつかのデバイスとプラットフォームでコードがどう動くか試すべきです。</p>
</div>
<h3 id="音量">音量</h3>
<p>ボリュームコントロールのプログラムはモバイルブラウザーで無効化されていることがあります。この理由はしばしばユーザーが OS レベルでボリュームをコントロールし、上書きが禁止されているためです。</p>
<h3 id="バッファリングと先読み">バッファリングと先読み</h3>
<p>急騰するモバイルネットワークのデータ使用を軽減するための試みとして、再生開始前のバッファリングができなくなることが恐らくあります。バッファリングとは、ブラウザが前もってメディアのダウンロードをする処理であり、円滑な再生を確実なものにするために度々必要になります。</p>
<p>{{domxref("HTMLMediaElement")}} インターフェイスが備える <a href="/ja/docs/Web/API/HTMLMediaElement#Properties">多くのプロパティ</a> はトラックが再生可能な状態にあるかどうかを決定する助けになります。</p>
<div class="note">
<p><strong>注</strong>: 色々な意味でバッファリングの概念は時代遅れです。バイトレンジリクエストが許容される限り(これが既定の振る舞いです)、先行する内容のダウンロードの必要なしにオーディオの任意の点に飛ぶことができるべきです。しかしながら、先読みは依然として便利です。それなしでは、再生が始められるようになる前に、常にいくらかのクライアント・サーバー間通信が必要になるでしょう。</p>
</div>
<h3 id="並行したオーディオ再生">並行したオーディオ再生</h3>
<p>多くのゲームでは、複数のオーディオを同時に再生することが求められます。例えば、ゲーム内で様々なことが起こるために、バックグラウンドミュージックと効果音を一緒に再生することがあります。この状況は近々、<a href="/ja/docs/Web/API/Web_Audio_API">Web Audio API</a>の採用でもっと上手くいくようになる予定ですが、現状最も広くサポートされている方法 ― 平凡な{{htmlelement("audio")}}要素を使用すること ― は、モバイルデバイス上では不安定な結果になります。</p>
<h3 id="テストとサポート">テストとサポート</h3>
<p>ここでは、どのモバイルプラットフォームが前述した機能をサポートするかを表に示します。</p>
<table class="standard-table">
<caption>web audio 機能のモバイルサポート</caption>
<thead>
<tr>
<th scope="row">モバイルブラウザー</th>
<th scope="col">バージョン</th>
<th scope="col">同時再生</th>
<th scope="col">自動再生</th>
<th scope="col">音量調整</th>
<th scope="col">先読み</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Chrome (Android)</th>
<td>32+</td>
<td>Y</td>
<td>N</td>
<td>N</td>
<td>N</td>
</tr>
<tr>
<th scope="row">Firefox (Android)</th>
<td>26+</td>
<td>Y</td>
<td>Y</td>
<td>N</td>
<td>N</td>
</tr>
<tr>
<th scope="row">Firefox OS</th>
<td>1.2+</td>
<td>Y</td>
<td>Y</td>
<td>Y</td>
<td>Y</td>
</tr>
<tr>
<th scope="row">IE Mobile</th>
<td>11+</td>
<td>Y</td>
<td>Y</td>
<td>N</td>
<td>Y</td>
</tr>
<tr>
<th scope="row">Opera Mobile</th>
<td>11+</td>
<td>N</td>
<td>N</td>
<td>N</td>
<td>N</td>
</tr>
<tr>
<th scope="row">Safari (iOS)</th>
<td>7+</td>
<td>Y/N*</td>
<td>N</td>
<td>N</td>
<td>Y</td>
</tr>
<tr>
<th scope="row">Android Browser</th>
<td>2.3+</td>
<td>N</td>
<td>N</td>
<td>N</td>
<td>N</td>
</tr>
</tbody>
</table>
<div class="note">
<p><strong>* 注</strong>: 同時に全てのオーディオを開始させてみる場合、Safari 7 では再生に問題があります。再生をずらした場合、ある程度は成功するかもしれません。</p>
</div>
<div class="note">
<p><strong>注</strong>: オーディオの同時再生は私たちの<a href="http://jsfiddle.net/dmkyaq0r/">同時オーディオテストの例</a>を使ってテストされますが、そこでは標準のオーディオAPIを使って3つのオーディオの同時再生を試しています。</p>
</div>
<div class="note">
<p><strong>注</strong>: 単純な自動再生機能は私たちの<a href="http://jsfiddle.net/vpdspp2b/">自動再生テストの例</a>でテストされます。</p>
</div>
<div class="note">
<p><strong>注</strong>: 音量の可変性は私たちの<a href="http://jsfiddle.net/7ta12vw4/">音量テストの例</a>でテストされます。</p>
</div>
<h2 id="モバイルの回避処理">モバイルの回避処理</h2>
<p>モバイルブラウザーは問題を抱えていますが、上に詳述した問題については回避する方法があります。</p>
<h3 id="オーディオスプライト">オーディオスプライト</h3>
<p>オーディオスプライトは<a href="/ja/docs/Web/Guide/CSS/CSS_Image_Sprites">CSSスプライト</a>から名前をとったもので、CSSスプライトとは単一グラフィックリソースを連続したスプライトに分解して使うCSSのための視覚的なテクニックです。同じ原理をオーディオに適用することで、読み込みと再生に時間のかかる小さなオーディオファイルの束ではなく、必要とするオーディオすべてを含む大きなオーディオファイル一つを用いることができます。そのファイルから特定の音を再生するには、各オーディオスプライトの既知の開始・停止時間を指定するだけです。</p>
<p>利点は、一つのオーディオを前もって提供しておき、スプライトをすぐ使える状態にできることです。こうすることで、大きな一つのオーディオの再生と即時停止をするだけですみます。また、サーバーリクエスト数を減らすことと、帯域幅を節約することもできます。</p>
<pre class="brush: js notranslate">var myAudio = document.createElement("audio");
myAudio.src = "mysprite.mp3";
myAudio.play();
myAudio.pause();</pre>
<p>停止すべき時間を知るために、現在時間のサンプリングをする必要があるでしょう。もし個々の音声について500ms以上の間隔をあけるなら、<code>timeUpdate</code>イベント(250ms毎に発動します)を使うことで事足りるはずです。ファイルは厳密に必要であるよりわずかに長くなりますが、無音部分はよく圧縮されます。</p>
<p>ここにオーディオスプライトプレイヤーの例があります。 ― まずはHTMLでユーザーインターフェイスを構築しましょう:</p>
<pre class="brush: html notranslate"><audio id="myAudio" src="http://jPlayer.org/tmp/countdown.mp3"></audio>
<button data-start="18" data-stop="19">0</button>
<button data-start="16" data-stop="17">1</button>
<button data-start="14" data-stop="15">2</button>
<button data-start="12" data-stop="13">3</button>
<button data-start="10" data-stop="11">4</button>
<button data-start="8" data-stop="9">5</button>
<button data-start="6" data-stop="7">6</button>
<button data-start="4" data-stop="5">7</button>
<button data-start="2" data-stop="3">8</button>
<button data-start="0" data-stop="1">9</button></pre>
<p>今、開始・停止時間を秒単位で指定したボタンがあります。"countdonw.mp3" というMP3ファイルは2秒ごとに声に出された数字から成り、ここで意図していることは、対応するボタンが押された時にその数が再生されるということです。</p>
<p>このように動作するJavaScriptを追加しましょう:</p>
<pre class="brush: js notranslate">var myAudio = document.getElementById('myAudio');
var buttons = document.getElementsByTagName('button');
var stopTime = 0;
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
myAudio.currentTime = this.getAttribute("data-start");
stopTime = this.getAttribute("data-stop");
myAudio.play();
}, false);
}
myAudio.addEventListener('timeupdate', function() {
if (this.currentTime > stopTime) {
this.pause();
}
}, false);</pre>
<div class="note">
<p><strong>注</strong>: JSFiddle上で<a href="http://jsfiddle.net/59vwaame/">私たちのオーディオスプライトプレイヤーライブ</a>を試すことができます。</p>
</div>
<div class="note">
<p><strong>注</strong>: 上に詳述したように、モバイルでは、スタートボタンが押されるといったようなユーザー開始イベントからコードを発動させる必要があるかもしれません。</p>
</div>
<div class="note">
<p><strong>注</strong>: ビットレートに気をつけてください。低ビットレートのエンコードではファイルサイズは小さくなりますが、シーク精度も低くなります。</p>
</div>
<h2 id="バックグラウンドミュージックBGM">バックグラウンドミュージック(BGM)</h2>
<p>ゲーム内の音楽は感情に訴える強力な効果があります。あなたは様々な楽曲サンプルを組み合わせることができ、さらに、オーディオ要素のボリュームをコントロールすることができることを想定すれば、異なる楽曲をクロスフェードさせることができます。<code><a href="/Apps/Build/Audio_and_video_delivery/HTML5_playbackRate_explained">playbackRate()</a></code>を使うことで、動きにより良く同期させるために、ピッチに影響なく音楽のスピードの調節もできます。</p>
<p>これは標準の{{HTMLElement("audio")}}要素を使って{{domxref("HTMLMediaElement")}}と連携しさえすれば可能となりますが、より発展した<a href="/ja/docs/Web/API/Web_Audio_API">Web Audio API</a>を使えばもっと簡単で柔軟になります。次にこれについて見ていきましょう。</p>
<h2 id="ゲーム用の_Web_オーディオ_API">ゲーム用の Web オーディオ API</h2>
<p>Now that it's supported in all modern browsers except for Opera Mini and Internet Explorer (<a href="https://status.modern.ie/webaudioapi">although Microsoft is now working on it</a>), an acceptable approach for many situations is to use the <a href="/ja/docs/Web/API/Web_Audio_API">Web Audio API</a> (see the <a href="http://caniuse.com/#search=web%20audio%20api">Can I use Web Audio API page</a> for more on browser compatibility). The Web Audio API is an advanced audio JavaScript API that is ideal for game audio. Developers can generate audio and manipulate audio samples as well as positioning sound in 3D game space.</p>
<p>A feasible cross-browser strategy would be to provide basic audio using the standard {{HTMLElement("audio")}} element and, where supported, enhance the experience using the Web Audio API.</p>
<div class="note">
<p><strong>注</strong>: Significantly, iOS Safari now supports the Web Audio API, which means it's now possible to write web-based games with native-quality audio for iOS.</p>
</div>
<p>As the Web Audio API allows precise timing and control of audio playback, we can use it to play samples at specific moments, which is a crucial immersive aspect of gaming. You want those explosions to be <strong>accompanied</strong> by a thundering boom, not <strong>followed</strong> by one, after all.</p>
<h3 id="Web_Audio_API_での_BGM">Web Audio API での BGM</h3>
<p>Although we can use the {{HTMLElement("audio")}} element to deliver linear background music that doesn't change in reaction to the game environment, the Web Audio API is ideal for implementing a more dynamic musical experience. You may want music to change depending on whether you are trying to build suspense or encourage the player in some way. Music is an important part of the gaming experience and depending on the type of game you are making you may wish to invest significant effort into getting it right.</p>
<p>One way you can make your music soundtrack more dynamic is by splitting it up into component loops or tracks. This is often the way that musicians compose music anyway, and the Web Audio API is extremely good at keeping these parts in sync. Once you have the various tracks that make up your piece you can bring tracks in and out as appropriate.</p>
<p>You can also apply filters or effects to music. Is your character in a cave? Increase the echo. Maybe you have underwater scenes, so apply a filter that muffles the sound.</p>
<p>Let's look at some Web Audio API techniques for dynamically adjusting music from its base tracks.</p>
<h3 id="トラックを読み込む">トラックを読み込む</h3>
<p>With the Web Audio API you can load separate tracks and loops individually using <a href="/ja/docs/Web/API/XMLHttpRequest"><code>XMLHttpRequest</code></a>, which means you can load them synchronously or in parallel. Loading synchronously might mean parts of your music are ready earlier and you can start playing them while others load.</p>
<p>Either way you may want to synchronize tracks or loops. The Web Audio API contains the notion of an internal clock that starts ticking the moment you create an audio context. You'll need to take account of the time between creating an audio context and when the first audio track starts playing. Recording this offset and querying the playing track's current time gives you enough information to synchronize separate pieces of audio.</p>
<p>To see this in action, let's lay out some separate tracks:</p>
<pre class="brush: html notranslate"><ul>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-leadguitar.mp3">Lead Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-drums.mp3">Drums</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-bassguitar.mp3">Bass Guitar</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-horns.mp3">Horns</a></li>
<li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-clav.mp3">Clavi</a></li>
</ul></pre>
<p>All of these tracks are the same tempo and are designed to be synchronized with each other.</p>
<pre class="brush: js notranslate">window.AudioContext = window.AudioContext || window.webkitAudioContext;
var offset = 0;
var context = new AudioContext();
function playTrack(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
var audiobuffer;
// Decode asynchronously
request.onload = function() {
if (request.status == 200) {
context.decodeAudioData(request.response, function(buffer) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
console.log('context.currentTime ' + context.currentTime);
if (offset == 0) {
source.start();
offset = context.currentTime;
} else {
source.start(0,context.currentTime - offset);
}
}, function(e) {
console.log('Error decoding audio data:' + e);
});
} else {
console.log('Audio didn\'t load successfully; error code:' + request.statusText);
}
}
request.send();
}
var tracks = document.getElementsByClassName('track');
for (var i = 0, len = tracks.length; i < len; i++) {
tracks[i].addEventListener('click', function(e){
playTrack(this.href);
e.preventDefault();
});
}</pre>
<div class="note">
<p><strong>注</strong>: You can try out our <a href="http://jsfiddle.net/c87z11jj/1/">Web Audio API multitrack demo</a> live on JSFiddle.</p>
</div>
<p>Now let's look over the code. First we set up a new {{domxref("AudioContext")}} and create a function (<code>playTrack()</code>) that loads and starts playing a track.</p>
<p><code>start()</code> (formerly known as <code>noteOn()</code>) will start playing an audio asset. <code>start(</code><code>)</code> asks three (optional) parameters:</p>
<ol>
<li>when: The absolute time to commence playback.</li>
<li>where (offset): The part of the audio to start playing from.</li>
<li>how long: The duration to play for.</li>
</ol>
<p><code>stop()</code> takes one optional parameter — when — which is the delay before stopping.<br>
<br>
If <code>start()</code>'s second parameter — the offset — is zero, we start playing from the start of the given piece of audio, which is what we do in the first instance. We then store the {{domxref("AudioContext.currentTime")}} — the offset of when the first piece began playing, subtract that from any subsequent <code>currentTime</code>s to calculate the actual time, and use that to synchronize our tracks.<br>
<br>
In the context of your game world you may have loops and samples that are played in different circumstances, and it can be useful to be able to synchronize with other tracks for a more seamless experience.</p>
<div class="note">
<p><strong>注</strong>: This example does not wait for the beat to end before introducing the next piece; we could do this if we knew the BPM (Beats Per Minute) of the tracks.</p>
</div>
<p>You may find that the introduction of a new track sounds more natural if it comes in on the beat/bar/phrase or whatever units you want to chunk your background music into.</p>
<p>To do this before playing the track you want to sync, you should calculate how long it is until the start of the next beat/bar etc.</p>
<p>Here's a bit of code that given a tempo (the time in seconds of your beat/bar) will calculate how long to wait until you play the next part — you feed the resulting value to the <code>start()</code> function with the first parameter, which takes the absolute time of when that playback should commence. Note the second parameter (where to start playing from in the new track) is relative:</p>
<pre class="brush: js notranslate">if (offset == 0) {
source.start();
offset = context.currentTime;
} else {
var relativeTime = context.currentTime - offset;
var beats = relativeTime / tempo;
var remainder = beats - Math.floor(beats);
var delay = tempo - (remainder*tempo);
source.start(context.currentTime+delay, relativeTime+delay);
}</pre>
<div class="note">
<p><strong>注</strong>: You can <a href="http://jsfiddle.net/c87z11jj/2/">try our wait calculator code</a> here, on JSFiddle (I've synched to the bar in this case).</p>
</div>
<div class="note">
<p><strong>注</strong>: If the first parameter is 0 or less than the context <code>currentTime</code>, playback will commence immediately.</p>
</div>
<h3 id="場所のオーディオ">場所のオーディオ</h3>
<p>Positional audio can be an important technique in making audio a key part of an immersive gaming experience. The Web Audio API not only enables us to position a number of audio sources in three-dimensional space but can also allow us to apply filters that make that audio appear more realistic.</p>
<p>In short, using the positional capabilities of the Web Audio API we can relate further information about the game world to the player.</p>
<p>We can relate:</p>
<ul>
<li>The position of objects</li>
<li>The direction of objects (movement of position and recreation of the Doppler effect)</li>
<li>The environment (cavernous, underwater, etc.)</li>
</ul>
<p>This is especially useful in a three-dimensional environment rendered using <a href="/ja/docs/Web/WebGL">WebGL</a>, where the Web Audio API makes it possible to tie audio to the objects and viewpoints.</p>
<div class="note">
<p><strong>注</strong>: See <a href="/ja/docs/Web/API/Web_Audio_API/Web_audio_spatialization_basics">Web Audio API Spatialization Basics</a> for more details.</p>
</div>
<h2 id="こちらも確認">こちらも確認</h2>
<ul>
<li><a href="/ja/docs/Web/API/Web_Audio_API">Web Audio API on MDN</a></li>
<li><a href="/ja/docs/Web/HTML/Element/audio"><code><audio></code> on MDN</a></li>
<li><a href="http://www.html5rocks.com/en/tutorials/webaudio/games/">Developing Game Audio with the Web Audio API (HTML5Rocks)</a></li>
<li><a href="http://www.html5rocks.com/en/tutorials/webaudio/positional_audio/">Mixing Positional Audio and WebGL (HTML5Rocks)</a></li>
<li><a href="https://hacks.mozilla.org/2013/10/songs-of-diridum-pushing-the-web-audio-api-to-its-limits/">Songs of Diridum: Pushing the Web Audio API to Its Limits</a></li>
<li><a href="http://pupunzi.open-lab.com/2013/03/13/making-html5-audio-actually-work-on-mobile/">Making HTML5 Audio Actually Work on Mobile</a></li>
<li><a href="http://remysharp.com/2010/12/23/audio-sprites/">Audio Sprites (and fixes for iOS)</a><br>
</li>
</ul>
|