0
# Web Audio API
1
2
The GWT backend provides advanced audio capabilities through Web Audio API integration, offering superior audio performance and features compared to basic HTML5 Audio. This system provides low-latency audio, advanced audio processing, and better mobile device support.
3
4
## Core Web Audio Classes
5
6
### WebAudioAPIManager { .api }
7
8
```java
9
public class WebAudioAPIManager implements LifecycleListener {
10
// Web Audio API availability and setup
11
public static boolean isSupported();
12
public static WebAudioAPIManager getInstance();
13
14
// Audio context management
15
public void createContext();
16
public void resumeContext();
17
public void suspendContext();
18
public boolean isContextRunning();
19
20
// Lifecycle management
21
public void pause();
22
public void resume();
23
public void dispose();
24
25
// Audio graph management
26
public AudioControlGraph createAudioGraph();
27
public void releaseAudioGraph(AudioControlGraph graph);
28
29
// Volume and settings
30
public void setMasterVolume(float volume);
31
public float getMasterVolume();
32
}
33
```
34
35
### WebAudioAPISound { .api }
36
37
```java
38
public class WebAudioAPISound implements Sound {
39
// Sound playback control
40
public long play();
41
public long play(float volume);
42
public long play(float volume, float pitch, float pan);
43
44
// Instance control
45
public void stop();
46
public void stop(long soundId);
47
public void pause();
48
public void pause(long soundId);
49
public void resume();
50
public void resume(long soundId);
51
52
// Sound properties
53
public void setVolume(long soundId, float volume);
54
public void setPitch(long soundId, float pitch);
55
public void setPan(long soundId, float pan, float volume);
56
57
// Looping control
58
public void setLooping(long soundId, boolean looping);
59
60
// Resource management
61
public void dispose();
62
}
63
```
64
65
### WebAudioAPIMusic { .api }
66
67
```java
68
public class WebAudioAPIMusic implements Music {
69
// Playback control
70
public void play();
71
public void pause();
72
public void stop();
73
74
// State queries
75
public boolean isPlaying();
76
public boolean isLooping();
77
78
// Music properties
79
public void setLooping(boolean isLooping);
80
public void setVolume(float volume);
81
public float getVolume();
82
public void setPan(float pan, float volume);
83
84
// Position control
85
public void setPosition(float position);
86
public float getPosition();
87
88
// Event handling
89
public void setOnCompletionListener(OnCompletionListener listener);
90
91
// Resource management
92
public void dispose();
93
}
94
```
95
96
### AudioControlGraph { .api }
97
98
```java
99
public class AudioControlGraph {
100
// Audio node management
101
public void connect(AudioNode source, AudioNode destination);
102
public void disconnect(AudioNode source);
103
public void disconnect(AudioNode source, AudioNode destination);
104
105
// Volume and gain control
106
public void setVolume(float volume);
107
public float getVolume();
108
public void setMasterGain(float gain);
109
110
// Audio effects
111
public void applyLowPassFilter(float frequency);
112
public void applyHighPassFilter(float frequency);
113
public void applyReverb(float roomSize, float damping);
114
public void applyDelay(float delayTime, float feedback);
115
116
// Spatial audio
117
public void setListenerPosition(float x, float y, float z);
118
public void setListenerOrientation(float forwardX, float forwardY, float forwardZ,
119
float upX, float upY, float upZ);
120
public void setSourcePosition(float x, float y, float z);
121
122
// Audio analysis
123
public float[] getFrequencyData();
124
public float[] getTimeData();
125
public float getAverageVolume();
126
127
// Resource management
128
public void dispose();
129
}
130
```
131
132
### AudioControlGraphPool { .api }
133
134
```java
135
public class AudioControlGraphPool extends Pool<AudioControlGraph> {
136
// Object pooling for audio graphs
137
public AudioControlGraphPool(int initialCapacity, int max);
138
139
// Pool management
140
protected AudioControlGraph newObject();
141
protected void reset(AudioControlGraph object);
142
143
// Pool operations
144
public AudioControlGraph obtain();
145
public void free(AudioControlGraph object);
146
public void clear();
147
}
148
```
149
150
## Usage Examples
151
152
### Basic Web Audio Setup
153
154
```java
155
public class WebAudioGame implements ApplicationListener {
156
private WebAudioAPIManager audioManager;
157
private WebAudioAPISound jumpSound;
158
private WebAudioAPIMusic backgroundMusic;
159
160
@Override
161
public void create() {
162
// Check Web Audio API support
163
if (WebAudioAPIManager.isSupported()) {
164
audioManager = WebAudioAPIManager.getInstance();
165
audioManager.createContext();
166
167
// Load audio assets
168
jumpSound = (WebAudioAPISound) Gdx.audio.newSound(Gdx.files.internal("audio/jump.ogg"));
169
backgroundMusic = (WebAudioAPIMusic) Gdx.audio.newMusic(Gdx.files.internal("music/background.ogg"));
170
171
// Configure background music
172
backgroundMusic.setLooping(true);
173
backgroundMusic.setVolume(0.7f);
174
175
System.out.println("Web Audio API initialized successfully");
176
} else {
177
System.out.println("Web Audio API not supported, using fallback");
178
// Fall back to regular HTML5 Audio
179
}
180
}
181
182
@Override
183
public void render() {
184
// Resume audio context on user interaction (required by browsers)
185
if (Gdx.input.justTouched() && audioManager != null) {
186
audioManager.resumeContext();
187
188
if (!backgroundMusic.isPlaying()) {
189
backgroundMusic.play();
190
}
191
}
192
}
193
194
public void playJumpSound() {
195
if (jumpSound != null) {
196
// Play with random pitch variation
197
float pitch = 0.8f + (float)Math.random() * 0.4f; // 0.8 to 1.2
198
jumpSound.play(1.0f, pitch, 0.0f);
199
}
200
}
201
202
@Override
203
public void dispose() {
204
if (jumpSound != null) jumpSound.dispose();
205
if (backgroundMusic != null) backgroundMusic.dispose();
206
if (audioManager != null) audioManager.dispose();
207
}
208
}
209
```
210
211
### Advanced Audio Effects
212
213
```java
214
public class AudioEffectsDemo implements ApplicationListener {
215
private AudioControlGraph audioGraph;
216
private WebAudioAPISound laserSound;
217
private WebAudioAPIMusic ambienceMusic;
218
219
@Override
220
public void create() {
221
if (WebAudioAPIManager.isSupported()) {
222
WebAudioAPIManager manager = WebAudioAPIManager.getInstance();
223
manager.createContext();
224
225
// Create audio processing graph
226
audioGraph = manager.createAudioGraph();
227
228
// Load sounds
229
laserSound = (WebAudioAPISound) Gdx.audio.newSound(Gdx.files.internal("sfx/laser.ogg"));
230
ambienceMusic = (WebAudioAPIMusic) Gdx.audio.newMusic(Gdx.files.internal("music/ambience.ogg"));
231
232
// Apply audio effects
233
setupAudioEffects();
234
}
235
}
236
237
private void setupAudioEffects() {
238
// Apply low-pass filter for underwater effect
239
audioGraph.applyLowPassFilter(800.0f);
240
241
// Add reverb for spacious environment
242
audioGraph.applyReverb(0.8f, 0.2f);
243
244
// Set up spatial audio
245
audioGraph.setListenerPosition(0, 0, 0);
246
audioGraph.setListenerOrientation(0, 0, -1, 0, 1, 0); // Looking forward, up is up
247
}
248
249
public void playLaserAt(float x, float y) {
250
if (laserSound != null) {
251
// Calculate pan based on position
252
float screenWidth = Gdx.graphics.getWidth();
253
float pan = (x - screenWidth / 2) / (screenWidth / 2); // -1 to 1
254
pan = Math.max(-1, Math.min(1, pan)); // Clamp to valid range
255
256
// Play with spatial positioning
257
audioGraph.setSourcePosition(x, y, 0);
258
laserSound.play(1.0f, 1.0f, pan);
259
}
260
}
261
262
public void startUnderwaterMode() {
263
// Apply underwater audio effect
264
audioGraph.applyLowPassFilter(400.0f);
265
audioGraph.setMasterGain(0.6f);
266
267
// Reduce music volume
268
if (ambienceMusic != null) {
269
ambienceMusic.setVolume(0.3f);
270
}
271
}
272
273
public void endUnderwaterMode() {
274
// Remove underwater effect
275
audioGraph.applyLowPassFilter(20000.0f); // Remove filter
276
audioGraph.setMasterGain(1.0f);
277
278
// Restore music volume
279
if (ambienceMusic != null) {
280
ambienceMusic.setVolume(0.8f);
281
}
282
}
283
284
@Override
285
public void dispose() {
286
if (audioGraph != null) audioGraph.dispose();
287
if (laserSound != null) laserSound.dispose();
288
if (ambienceMusic != null) ambienceMusic.dispose();
289
}
290
}
291
```
292
293
### Audio Visualization
294
295
```java
296
public class AudioVisualizerDemo implements ApplicationListener {
297
private AudioControlGraph audioGraph;
298
private WebAudioAPIMusic music;
299
private float[] frequencyData;
300
private float[] timeData;
301
private ShapeRenderer shapeRenderer;
302
303
@Override
304
public void create() {
305
if (WebAudioAPIManager.isSupported()) {
306
WebAudioAPIManager manager = WebAudioAPIManager.getInstance();
307
manager.createContext();
308
309
audioGraph = manager.createAudioGraph();
310
music = (WebAudioAPIMusic) Gdx.audio.newMusic(Gdx.files.internal("music/electronic.ogg"));
311
music.setLooping(true);
312
313
// Initialize visualization data arrays
314
frequencyData = new float[128];
315
timeData = new float[128];
316
317
shapeRenderer = new ShapeRenderer();
318
}
319
}
320
321
@Override
322
public void render() {
323
// Update audio analysis data
324
if (audioGraph != null && music.isPlaying()) {
325
frequencyData = audioGraph.getFrequencyData();
326
timeData = audioGraph.getTimeData();
327
}
328
329
// Clear screen
330
Gdx.gl.glClearColor(0, 0, 0, 1);
331
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
332
333
// Draw frequency spectrum
334
drawFrequencySpectrum();
335
336
// Draw waveform
337
drawWaveform();
338
339
// Start music on user input
340
if (Gdx.input.justTouched() && music != null && !music.isPlaying()) {
341
music.play();
342
}
343
}
344
345
private void drawFrequencySpectrum() {
346
if (frequencyData == null) return;
347
348
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
349
shapeRenderer.setColor(0, 1, 0, 0.8f); // Green bars
350
351
float barWidth = Gdx.graphics.getWidth() / (float)frequencyData.length;
352
353
for (int i = 0; i < frequencyData.length; i++) {
354
float height = frequencyData[i] * Gdx.graphics.getHeight() * 0.4f;
355
float x = i * barWidth;
356
float y = Gdx.graphics.getHeight() * 0.6f;
357
358
shapeRenderer.rect(x, y, barWidth - 1, height);
359
}
360
361
shapeRenderer.end();
362
}
363
364
private void drawWaveform() {
365
if (timeData == null) return;
366
367
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
368
shapeRenderer.setColor(1, 1, 0, 1); // Yellow waveform
369
370
float centerY = Gdx.graphics.getHeight() * 0.3f;
371
float amplitude = 100;
372
float stepX = Gdx.graphics.getWidth() / (float)(timeData.length - 1);
373
374
for (int i = 0; i < timeData.length - 1; i++) {
375
float x1 = i * stepX;
376
float y1 = centerY + timeData[i] * amplitude;
377
float x2 = (i + 1) * stepX;
378
float y2 = centerY + timeData[i + 1] * amplitude;
379
380
shapeRenderer.line(x1, y1, x2, y2);
381
}
382
383
shapeRenderer.end();
384
}
385
386
@Override
387
public void dispose() {
388
if (audioGraph != null) audioGraph.dispose();
389
if (music != null) music.dispose();
390
if (shapeRenderer != null) shapeRenderer.dispose();
391
}
392
}
393
```
394
395
### Dynamic Audio Loading
396
397
```java
398
public class DynamicAudioLoader {
399
private WebAudioAPIManager audioManager;
400
private Map<String, WebAudioAPISound> loadedSounds;
401
private Map<String, WebAudioAPIMusic> loadedMusic;
402
403
public DynamicAudioLoader() {
404
loadedSounds = new HashMap<>();
405
loadedMusic = new HashMap<>();
406
407
if (WebAudioAPIManager.isSupported()) {
408
audioManager = WebAudioAPIManager.getInstance();
409
audioManager.createContext();
410
}
411
}
412
413
public void loadSound(String name, String path, Runnable onComplete) {
414
if (loadedSounds.containsKey(name)) {
415
if (onComplete != null) onComplete.run();
416
return;
417
}
418
419
// Load sound asynchronously
420
Timer.schedule(new Timer.Task() {
421
@Override
422
public void run() {
423
try {
424
WebAudioAPISound sound = (WebAudioAPISound) Gdx.audio.newSound(Gdx.files.internal(path));
425
loadedSounds.put(name, sound);
426
System.out.println("Loaded sound: " + name);
427
if (onComplete != null) onComplete.run();
428
} catch (Exception e) {
429
System.err.println("Failed to load sound: " + name + " - " + e.getMessage());
430
}
431
}
432
}, 0.1f); // Small delay to simulate async loading
433
}
434
435
public void loadMusic(String name, String path, Runnable onComplete) {
436
if (loadedMusic.containsKey(name)) {
437
if (onComplete != null) onComplete.run();
438
return;
439
}
440
441
Timer.schedule(new Timer.Task() {
442
@Override
443
public void run() {
444
try {
445
WebAudioAPIMusic music = (WebAudioAPIMusic) Gdx.audio.newMusic(Gdx.files.internal(path));
446
loadedMusic.put(name, music);
447
System.out.println("Loaded music: " + name);
448
if (onComplete != null) onComplete.run();
449
} catch (Exception e) {
450
System.err.println("Failed to load music: " + name + " - " + e.getMessage());
451
}
452
}
453
}, 0.1f);
454
}
455
456
public WebAudioAPISound getSound(String name) {
457
return loadedSounds.get(name);
458
}
459
460
public WebAudioAPIMusic getMusic(String name) {
461
return loadedMusic.get(name);
462
}
463
464
public void playSound(String name) {
465
WebAudioAPISound sound = getSound(name);
466
if (sound != null) {
467
sound.play();
468
} else {
469
System.err.println("Sound not loaded: " + name);
470
}
471
}
472
473
public void playSound(String name, float volume, float pitch) {
474
WebAudioAPISound sound = getSound(name);
475
if (sound != null) {
476
sound.play(volume, pitch, 0);
477
} else {
478
System.err.println("Sound not loaded: " + name);
479
}
480
}
481
482
public void playMusic(String name) {
483
WebAudioAPIMusic music = getMusic(name);
484
if (music != null) {
485
music.play();
486
} else {
487
System.err.println("Music not loaded: " + name);
488
}
489
}
490
491
public void unloadSound(String name) {
492
WebAudioAPISound sound = loadedSounds.remove(name);
493
if (sound != null) {
494
sound.dispose();
495
System.out.println("Unloaded sound: " + name);
496
}
497
}
498
499
public void unloadMusic(String name) {
500
WebAudioAPIMusic music = loadedMusic.remove(name);
501
if (music != null) {
502
music.dispose();
503
System.out.println("Unloaded music: " + name);
504
}
505
}
506
507
public void dispose() {
508
// Dispose all loaded audio
509
for (WebAudioAPISound sound : loadedSounds.values()) {
510
sound.dispose();
511
}
512
loadedSounds.clear();
513
514
for (WebAudioAPIMusic music : loadedMusic.values()) {
515
music.dispose();
516
}
517
loadedMusic.clear();
518
519
if (audioManager != null) {
520
audioManager.dispose();
521
}
522
}
523
}
524
```
525
526
## Web Audio API Benefits
527
528
### Performance Advantages
529
530
```java
531
// Web Audio API provides several performance benefits over HTML5 Audio:
532
533
public class AudioPerformanceComparison {
534
535
public void demonstrateAdvantages() {
536
if (WebAudioAPIManager.isSupported()) {
537
// ✓ Lower latency audio playback
538
// ✓ Multiple simultaneous audio instances
539
// ✓ Precise timing control
540
// ✓ Audio processing and effects
541
// ✓ Better mobile device support
542
// ✓ No browser audio instance limits
543
544
System.out.println("Using Web Audio API for optimal performance");
545
} else {
546
// Fall back to HTML5 Audio
547
// ✗ Higher latency
548
// ✗ Limited simultaneous sounds
549
// ✗ No advanced audio processing
550
// ✗ Browser-dependent limitations
551
552
System.out.println("Falling back to HTML5 Audio");
553
}
554
}
555
556
public void testAudioLatency() {
557
WebAudioAPISound testSound = (WebAudioAPISound) Gdx.audio.newSound(Gdx.files.internal("test_click.wav"));
558
559
long startTime = System.currentTimeMillis();
560
testSound.play();
561
long endTime = System.currentTimeMillis();
562
563
System.out.println("Audio playback latency: " + (endTime - startTime) + "ms");
564
// Web Audio API typically shows <10ms latency
565
// HTML5 Audio often shows 50-200ms latency
566
}
567
}
568
```
569
570
### Browser Compatibility
571
572
```java
573
// Web Audio API support across browsers
574
public class BrowserCompatibility {
575
576
public static void checkAudioSupport() {
577
if (WebAudioAPIManager.isSupported()) {
578
System.out.println("Web Audio API supported - using advanced audio features");
579
580
// Available in:
581
// - Chrome 14+
582
// - Firefox 25+
583
// - Safari 6+
584
// - Edge 12+
585
// - Mobile Chrome/Safari
586
587
} else {
588
System.out.println("Web Audio API not supported - using HTML5 Audio fallback");
589
590
// Fallback for:
591
// - Internet Explorer
592
// - Very old browser versions
593
// - Browsers with Web Audio disabled
594
}
595
}
596
597
public static void handleAudioContext() {
598
// Modern browsers require user interaction before audio context can start
599
WebAudioAPIManager manager = WebAudioAPIManager.getInstance();
600
601
if (Gdx.input.justTouched()) {
602
// Resume context after user interaction
603
manager.resumeContext();
604
System.out.println("Audio context resumed");
605
}
606
}
607
}