Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libopenal.js
4150 views
1
/**
2
* @license
3
* Copyright 2013 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
7
//'use strict';
8
9
var LibraryOpenAL = {
10
// ************************************************************************
11
// ** INTERNALS
12
// ************************************************************************
13
14
$AL__deps: ['$MainLoop'],
15
$AL: {
16
// ------------------------------------------------------
17
// -- Constants
18
// ------------------------------------------------------
19
20
QUEUE_INTERVAL: 25,
21
QUEUE_LOOKAHEAD: 100.0 / 1000.0,
22
23
DEVICE_NAME: 'Emscripten OpenAL',
24
CAPTURE_DEVICE_NAME: 'Emscripten OpenAL capture',
25
26
ALC_EXTENSIONS: {
27
// TODO: 'ALC_EXT_EFX': true,
28
'ALC_SOFT_pause_device': true,
29
'ALC_SOFT_HRTF': true
30
},
31
AL_EXTENSIONS: {
32
'AL_EXT_float32': true,
33
'AL_SOFT_loop_points': true,
34
'AL_SOFT_source_length': true,
35
'AL_EXT_source_distance_model': true,
36
'AL_SOFT_source_spatialize': true
37
},
38
39
// ------------------------------------------------------
40
// -- ALC Fields
41
// ------------------------------------------------------
42
43
_alcErr: 0,
44
get alcErr() {
45
return this._alcErr;
46
},
47
set alcErr(val) {
48
// Errors should not be overwritten by later errors until they are cleared by a query.
49
if (this._alcErr === {{{ cDefs.ALC_NO_ERROR }}} || val === {{{ cDefs.ALC_NO_ERROR }}}) {
50
this._alcErr = val;
51
}
52
},
53
54
deviceRefCounts: {},
55
alcStringCache: {},
56
paused: false,
57
58
// ------------------------------------------------------
59
// -- AL Fields
60
// ------------------------------------------------------
61
62
stringCache: {},
63
contexts: {},
64
currentCtx: null,
65
buffers: {
66
// The zero buffer is legal to use, so create a placeholder for it
67
'0': {
68
id: 0,
69
refCount: 0,
70
audioBuf: null,
71
frequency: 0,
72
bytesPerSample: 2,
73
channels: 1,
74
length: 0
75
}
76
},
77
paramArray: [], // Used to prevent allocating a new array for each param call
78
79
_nextId: 1,
80
newId: () => AL.freeIds.length > 0 ? AL.freeIds.pop() : AL._nextId++,
81
freeIds: [],
82
83
// ------------------------------------------------------
84
// -- Mixing Logic
85
// ------------------------------------------------------
86
87
scheduleContextAudio: (ctx) => {
88
// If we are animating using the requestAnimationFrame method, then the main loop does not run when in the background.
89
// To give a perfect glitch-free audio stop when switching from foreground to background, we need to avoid updating
90
// audio altogether when in the background, so detect that case and kill audio buffer streaming if so.
91
if (MainLoop.timingMode === {{{ cDefs.EM_TIMING_RAF }}} && document['visibilityState'] != 'visible') {
92
return;
93
}
94
95
for (var i in ctx.sources) {
96
AL.scheduleSourceAudio(ctx.sources[i]);
97
}
98
},
99
100
// This function is the core scheduler that queues web-audio buffers for output.
101
// src.bufQueue represents the abstract OpenAL buffer queue, which is taversed to schedule
102
// corresponding web-audio buffers. These buffers are stored in src.audioQueue, which
103
// represents the queue of buffers scheduled for physical playback. These two queues are
104
// distinct because of the differing semantics of OpenAL and web audio. Some changes
105
// to OpenAL parameters, such as pitch, may require the web audio queue to be flushed and rescheduled.
106
scheduleSourceAudio: (src, lookahead) => {
107
// See comment on scheduleContextAudio above.
108
if (MainLoop.timingMode === {{{ cDefs.EM_TIMING_RAF }}} && document['visibilityState'] != 'visible') {
109
return;
110
}
111
if (src.state !== {{{ cDefs.AL_PLAYING }}}) {
112
return;
113
}
114
115
var currentTime = AL.updateSourceTime(src);
116
117
var startTime = src.bufStartTime;
118
var startOffset = src.bufOffset;
119
var bufCursor = src.bufsProcessed;
120
121
// Advance past any audio that is already scheduled
122
for (var i = 0; i < src.audioQueue.length; i++) {
123
var audioSrc = src.audioQueue[i];
124
startTime = audioSrc._startTime + audioSrc._duration;
125
startOffset = 0.0;
126
bufCursor += audioSrc._skipCount + 1;
127
}
128
129
if (!lookahead) {
130
lookahead = AL.QUEUE_LOOKAHEAD;
131
}
132
var lookaheadTime = currentTime + lookahead;
133
var skipCount = 0;
134
while (startTime < lookaheadTime) {
135
if (bufCursor >= src.bufQueue.length) {
136
if (src.looping) {
137
bufCursor %= src.bufQueue.length;
138
} else {
139
break;
140
}
141
}
142
143
var buf = src.bufQueue[bufCursor % src.bufQueue.length];
144
// If the buffer contains no data, skip it
145
if (buf.length === 0) {
146
skipCount++;
147
// If we've gone through the whole queue and everything is 0 length, just give up
148
if (skipCount === src.bufQueue.length) {
149
break;
150
}
151
} else {
152
var audioSrc = src.context.audioCtx.createBufferSource();
153
audioSrc.buffer = buf.audioBuf;
154
audioSrc.playbackRate.value = src.playbackRate;
155
if (buf.audioBuf._loopStart || buf.audioBuf._loopEnd) {
156
audioSrc.loopStart = buf.audioBuf._loopStart;
157
audioSrc.loopEnd = buf.audioBuf._loopEnd;
158
}
159
160
var duration = 0.0;
161
// If the source is a looping static buffer, use native looping for gapless playback
162
if (src.type === {{{ cDefs.AL_STATIC }}} && src.looping) {
163
duration = Number.POSITIVE_INFINITY;
164
audioSrc.loop = true;
165
if (buf.audioBuf._loopStart) {
166
audioSrc.loopStart = buf.audioBuf._loopStart;
167
}
168
if (buf.audioBuf._loopEnd) {
169
audioSrc.loopEnd = buf.audioBuf._loopEnd;
170
}
171
} else {
172
duration = (buf.audioBuf.duration - startOffset) / src.playbackRate;
173
}
174
175
audioSrc._startOffset = startOffset;
176
audioSrc._duration = duration;
177
audioSrc._skipCount = skipCount;
178
skipCount = 0;
179
180
audioSrc.connect(src.gain);
181
182
if (typeof audioSrc.start != 'undefined') {
183
// Sample the current time as late as possible to mitigate drift
184
startTime = Math.max(startTime, src.context.audioCtx.currentTime);
185
audioSrc.start(startTime, startOffset);
186
} else if (typeof audioSrc.noteOn != 'undefined') {
187
startTime = Math.max(startTime, src.context.audioCtx.currentTime);
188
audioSrc.noteOn(startTime);
189
#if OPENAL_DEBUG
190
if (offset > 0.0) {
191
warnOnce('The current browser does not support AudioBufferSourceNode.start(when, offset); method, so cannot play back audio with an offset '+startOffset+' secs! Audio glitches will occur!');
192
}
193
#endif
194
}
195
#if OPENAL_DEBUG
196
else {
197
warnOnce('Unable to start AudioBufferSourceNode playback! Not supported by the browser?');
198
}
199
200
dbg(`scheduleSourceAudio() queuing buffer ${buf.id} for source ${src.id} at ${startTime} (offset by ${startOffset})`);
201
#endif
202
audioSrc._startTime = startTime;
203
src.audioQueue.push(audioSrc);
204
205
startTime += duration;
206
}
207
208
startOffset = 0.0;
209
bufCursor++;
210
}
211
},
212
213
// Advance the state of a source forward to the current time
214
updateSourceTime: (src) => {
215
var currentTime = src.context.audioCtx.currentTime;
216
if (src.state !== {{{ cDefs.AL_PLAYING }}}) {
217
return currentTime;
218
}
219
220
// if the start time is unset, determine it based on the current offset.
221
// This will be the case when a source is resumed after being paused, and
222
// allows us to pretend that the source actually started playing some time
223
// in the past such that it would just now have reached the stored offset.
224
if (!isFinite(src.bufStartTime)) {
225
src.bufStartTime = currentTime - src.bufOffset / src.playbackRate;
226
src.bufOffset = 0.0;
227
}
228
229
var nextStartTime = 0.0;
230
while (src.audioQueue.length) {
231
var audioSrc = src.audioQueue[0];
232
src.bufsProcessed += audioSrc._skipCount;
233
nextStartTime = audioSrc._startTime + audioSrc._duration; // n.b. audioSrc._duration already factors in playbackRate, so no divide by src.playbackRate on it.
234
235
if (currentTime < nextStartTime) {
236
break;
237
}
238
239
src.audioQueue.shift();
240
src.bufStartTime = nextStartTime;
241
src.bufOffset = 0.0;
242
src.bufsProcessed++;
243
}
244
245
if (src.bufsProcessed >= src.bufQueue.length && !src.looping) {
246
// The source has played its entire queue and is non-looping, so just mark it as stopped.
247
AL.setSourceState(src, {{{ cDefs.AL_STOPPED }}});
248
} else if (src.type === {{{ cDefs.AL_STATIC }}} && src.looping) {
249
// If the source is a looping static buffer, determine the buffer offset based on the loop points
250
var buf = src.bufQueue[0];
251
if (buf.length === 0) {
252
src.bufOffset = 0.0;
253
} else {
254
var delta = (currentTime - src.bufStartTime) * src.playbackRate;
255
var loopStart = buf.audioBuf._loopStart || 0.0;
256
var loopEnd = buf.audioBuf._loopEnd || buf.audioBuf.duration;
257
if (loopEnd <= loopStart) {
258
loopEnd = buf.audioBuf.duration;
259
}
260
261
if (delta < loopEnd) {
262
src.bufOffset = delta;
263
} else {
264
src.bufOffset = loopStart + (delta - loopStart) % (loopEnd - loopStart);
265
}
266
}
267
} else if (src.audioQueue[0]) {
268
// The source is still actively playing, so we just need to calculate where we are in the current buffer
269
// so it can be remembered if the source gets paused.
270
src.bufOffset = (currentTime - src.audioQueue[0]._startTime) * src.playbackRate;
271
} else {
272
// The source hasn't finished yet, but there is no scheduled audio left for it. This can be because
273
// the source has just been started/resumed, or due to an underrun caused by a long blocking operation.
274
// We need to determine what state we would be in by this point in time so that when we next schedule
275
// audio playback, it will be just as if no underrun occurred.
276
277
if (src.type !== {{{ cDefs.AL_STATIC }}} && src.looping) {
278
// if the source is a looping buffer queue, let's first calculate the queue duration, so we can
279
// quickly fast forward past any full loops of the queue and only worry about the remainder.
280
var srcDuration = AL.sourceDuration(src) / src.playbackRate;
281
if (srcDuration > 0.0) {
282
src.bufStartTime += Math.floor((currentTime - src.bufStartTime) / srcDuration) * srcDuration;
283
}
284
}
285
286
// Since we've already skipped any full-queue loops if there were any, we just need to find
287
// out where in the queue the remaining time puts us, which won't require stepping through the
288
// entire queue more than once.
289
for (var i = 0; i < src.bufQueue.length; i++) {
290
if (src.bufsProcessed >= src.bufQueue.length) {
291
if (src.looping) {
292
src.bufsProcessed %= src.bufQueue.length;
293
} else {
294
AL.setSourceState(src, {{{ cDefs.AL_STOPPED }}});
295
break;
296
}
297
}
298
299
var buf = src.bufQueue[src.bufsProcessed];
300
if (buf.length > 0) {
301
nextStartTime = src.bufStartTime + buf.audioBuf.duration / src.playbackRate;
302
303
if (currentTime < nextStartTime) {
304
src.bufOffset = (currentTime - src.bufStartTime) * src.playbackRate;
305
break;
306
}
307
308
src.bufStartTime = nextStartTime;
309
}
310
311
src.bufOffset = 0.0;
312
src.bufsProcessed++;
313
}
314
}
315
316
return currentTime;
317
},
318
319
cancelPendingSourceAudio: (src) => {
320
AL.updateSourceTime(src);
321
322
for (var i = 1; i < src.audioQueue.length; i++) {
323
var audioSrc = src.audioQueue[i];
324
audioSrc.stop();
325
}
326
327
if (src.audioQueue.length > 1) {
328
src.audioQueue.length = 1;
329
}
330
},
331
332
stopSourceAudio: (src) => {
333
for (var i = 0; i < src.audioQueue.length; i++) {
334
src.audioQueue[i].stop();
335
}
336
src.audioQueue.length = 0;
337
},
338
339
setSourceState: (src, state) => {
340
if (state === {{{ cDefs.AL_PLAYING }}}) {
341
if (src.state === {{{ cDefs.AL_PLAYING }}} || src.state == {{{ cDefs.AL_STOPPED }}}) {
342
src.bufsProcessed = 0;
343
src.bufOffset = 0.0;
344
#if OPENAL_DEBUG
345
dbg(`setSourceState() resetting and playing source ${src.id}`);
346
#endif
347
} else {
348
#if OPENAL_DEBUG
349
dbg(`setSourceState() playing source ${src.id} at ${src.bufOffset}`);
350
#endif
351
}
352
353
AL.stopSourceAudio(src);
354
355
src.state = {{{ cDefs.AL_PLAYING }}};
356
src.bufStartTime = Number.NEGATIVE_INFINITY;
357
AL.scheduleSourceAudio(src);
358
} else if (state === {{{ cDefs.AL_PAUSED }}}) {
359
if (src.state === {{{ cDefs.AL_PLAYING }}}) {
360
// Store off the current offset to restore with on resume.
361
AL.updateSourceTime(src);
362
AL.stopSourceAudio(src);
363
364
src.state = {{{ cDefs.AL_PAUSED }}};
365
#if OPENAL_DEBUG
366
dbg(`setSourceState() pausing source ${src.id} at ${src.bufOffset}`);
367
#endif
368
}
369
} else if (state === {{{ cDefs.AL_STOPPED }}}) {
370
if (src.state !== {{{ cDefs.AL_INITIAL }}}) {
371
src.state = {{{ cDefs.AL_STOPPED }}};
372
src.bufsProcessed = src.bufQueue.length;
373
src.bufStartTime = Number.NEGATIVE_INFINITY;
374
src.bufOffset = 0.0;
375
AL.stopSourceAudio(src);
376
#if OPENAL_DEBUG
377
dbg(`setSourceState() stopping source ${src.id}`);
378
#endif
379
}
380
} else if (state === {{{ cDefs.AL_INITIAL }}}) {
381
if (src.state !== {{{ cDefs.AL_INITIAL }}}) {
382
src.state = {{{ cDefs.AL_INITIAL }}};
383
src.bufsProcessed = 0;
384
src.bufStartTime = Number.NEGATIVE_INFINITY;
385
src.bufOffset = 0.0;
386
AL.stopSourceAudio(src);
387
#if OPENAL_DEBUG
388
dbg(`setSourceState() initializing source ${src.id}`);
389
#endif
390
}
391
}
392
},
393
394
initSourcePanner: (src) => {
395
if (src.type === 0x1030 /* AL_UNDETERMINED */) {
396
return;
397
}
398
399
// Find the first non-zero buffer in the queue to determine the proper format
400
var templateBuf = AL.buffers[0];
401
for (var i = 0; i < src.bufQueue.length; i++) {
402
if (src.bufQueue[i].id !== 0) {
403
templateBuf = src.bufQueue[i];
404
break;
405
}
406
}
407
// Create a panner if AL_SOURCE_SPATIALIZE_SOFT is set to true, or alternatively if it's set to auto and the source is mono
408
if (src.spatialize === {{{ cDefs.AL_TRUE }}} || (src.spatialize === 2 /* AL_AUTO_SOFT */ && templateBuf.channels === 1)) {
409
if (src.panner) {
410
return;
411
}
412
src.panner = src.context.audioCtx.createPanner();
413
414
AL.updateSourceGlobal(src);
415
AL.updateSourceSpace(src);
416
417
src.panner.connect(src.context.gain);
418
src.gain.disconnect();
419
src.gain.connect(src.panner);
420
} else {
421
if (!src.panner) {
422
return;
423
}
424
425
src.panner.disconnect();
426
src.gain.disconnect();
427
src.gain.connect(src.context.gain);
428
src.panner = null;
429
}
430
},
431
432
updateContextGlobal: (ctx) => {
433
for (var i in ctx.sources) {
434
AL.updateSourceGlobal(ctx.sources[i]);
435
}
436
},
437
438
updateSourceGlobal: (src) => {
439
var panner = src.panner;
440
if (!panner) {
441
return;
442
}
443
444
panner.refDistance = src.refDistance;
445
panner.maxDistance = src.maxDistance;
446
panner.rolloffFactor = src.rolloffFactor;
447
448
panner.panningModel = src.context.hrtf ? 'HRTF' : 'equalpower';
449
450
// Use the source's distance model if AL_SOURCE_DISTANCE_MODEL is enabled
451
var distanceModel = src.context.sourceDistanceModel ? src.distanceModel : src.context.distanceModel;
452
switch (distanceModel) {
453
case {{{ cDefs.AL_NONE }}}:
454
panner.distanceModel = 'inverse';
455
panner.refDistance = 3.40282e38 /* FLT_MAX */;
456
break;
457
case 0xd001 /* AL_INVERSE_DISTANCE */:
458
case 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */:
459
panner.distanceModel = 'inverse';
460
break;
461
case 0xd003 /* AL_LINEAR_DISTANCE */:
462
case 0xd004 /* AL_LINEAR_DISTANCE_CLAMPED */:
463
panner.distanceModel = 'linear';
464
break;
465
case 0xd005 /* AL_EXPONENT_DISTANCE */:
466
case 0xd006 /* AL_EXPONENT_DISTANCE_CLAMPED */:
467
panner.distanceModel = 'exponential';
468
break;
469
}
470
},
471
472
updateListenerSpace: (ctx) => {
473
var listener = ctx.audioCtx.listener;
474
if (listener.positionX) {
475
listener.positionX.value = ctx.listener.position[0];
476
listener.positionY.value = ctx.listener.position[1];
477
listener.positionZ.value = ctx.listener.position[2];
478
} else {
479
#if OPENAL_DEBUG
480
warnOnce('Listener position attributes are not present, falling back to setPosition()');
481
#endif
482
listener.setPosition(ctx.listener.position[0], ctx.listener.position[1], ctx.listener.position[2]);
483
}
484
if (listener.forwardX) {
485
listener.forwardX.value = ctx.listener.direction[0];
486
listener.forwardY.value = ctx.listener.direction[1];
487
listener.forwardZ.value = ctx.listener.direction[2];
488
listener.upX.value = ctx.listener.up[0];
489
listener.upY.value = ctx.listener.up[1];
490
listener.upZ.value = ctx.listener.up[2];
491
} else {
492
#if OPENAL_DEBUG
493
warnOnce('Listener orientation attributes are not present, falling back to setOrientation()');
494
#endif
495
listener.setOrientation(
496
ctx.listener.direction[0], ctx.listener.direction[1], ctx.listener.direction[2],
497
ctx.listener.up[0], ctx.listener.up[1], ctx.listener.up[2]);
498
}
499
500
// Update sources that are relative to the listener
501
for (var i in ctx.sources) {
502
AL.updateSourceSpace(ctx.sources[i]);
503
}
504
},
505
506
updateSourceSpace: (src) => {
507
if (!src.panner) {
508
return;
509
}
510
var panner = src.panner;
511
512
var posX = src.position[0];
513
var posY = src.position[1];
514
var posZ = src.position[2];
515
var dirX = src.direction[0];
516
var dirY = src.direction[1];
517
var dirZ = src.direction[2];
518
519
var listener = src.context.listener;
520
var lPosX = listener.position[0];
521
var lPosY = listener.position[1];
522
var lPosZ = listener.position[2];
523
524
// WebAudio does spatialization in world-space coordinates, meaning both the buffer sources and
525
// the listener position are in the same absolute coordinate system relative to a fixed origin.
526
// By default, OpenAL works this way as well, but it also provides a "listener relative" mode, where
527
// a buffer source's coordinate are interpreted not in absolute world space, but as being relative
528
// to the listener object itself, so as the listener moves the source appears to move with it
529
// with no update required. Since web audio does not support this mode, we must transform the source
530
// coordinates from listener-relative space to absolute world space.
531
//
532
// We do this via affine transformation matrices applied to the source position and source direction.
533
// A change-of-basis converts from listener-space displacements to world-space displacements,
534
// which must be done for both the source position and direction. Lastly, the source position must be
535
// added to the listener position to get the final source position, since the source position represents
536
// a displacement from the listener.
537
if (src.relative) {
538
// Negate the listener direction since forward is -Z.
539
var lBackX = -listener.direction[0];
540
var lBackY = -listener.direction[1];
541
var lBackZ = -listener.direction[2];
542
var lUpX = listener.up[0];
543
var lUpY = listener.up[1];
544
var lUpZ = listener.up[2];
545
546
var inverseMagnitude = (x, y, z) => {
547
var length = Math.sqrt(x * x + y * y + z * z);
548
549
if (length < Number.EPSILON) {
550
return 0.0;
551
}
552
553
return 1.0 / length;
554
};
555
556
// Normalize the Back vector
557
var invMag = inverseMagnitude(lBackX, lBackY, lBackZ);
558
lBackX *= invMag;
559
lBackY *= invMag;
560
lBackZ *= invMag;
561
562
// ...and the Up vector
563
invMag = inverseMagnitude(lUpX, lUpY, lUpZ);
564
lUpX *= invMag;
565
lUpY *= invMag;
566
lUpZ *= invMag;
567
568
// Calculate the Right vector as the cross product of the Up and Back vectors
569
var lRightX = (lUpY * lBackZ - lUpZ * lBackY);
570
var lRightY = (lUpZ * lBackX - lUpX * lBackZ);
571
var lRightZ = (lUpX * lBackY - lUpY * lBackX);
572
573
// Back and Up might not be exactly perpendicular, so the cross product also needs normalization
574
invMag = inverseMagnitude(lRightX, lRightY, lRightZ);
575
lRightX *= invMag;
576
lRightY *= invMag;
577
lRightZ *= invMag;
578
579
// Recompute Up from the now orthonormal Right and Back vectors so we have a fully orthonormal basis
580
lUpX = (lBackY * lRightZ - lBackZ * lRightY);
581
lUpY = (lBackZ * lRightX - lBackX * lRightZ);
582
lUpZ = (lBackX * lRightY - lBackY * lRightX);
583
584
var oldX = dirX;
585
var oldY = dirY;
586
var oldZ = dirZ;
587
588
// Use our 3 vectors to apply a change-of-basis matrix to the source direction
589
dirX = oldX * lRightX + oldY * lUpX + oldZ * lBackX;
590
dirY = oldX * lRightY + oldY * lUpY + oldZ * lBackY;
591
dirZ = oldX * lRightZ + oldY * lUpZ + oldZ * lBackZ;
592
593
oldX = posX;
594
oldY = posY;
595
oldZ = posZ;
596
597
// ...and to the source position
598
posX = oldX * lRightX + oldY * lUpX + oldZ * lBackX;
599
posY = oldX * lRightY + oldY * lUpY + oldZ * lBackY;
600
posZ = oldX * lRightZ + oldY * lUpZ + oldZ * lBackZ;
601
602
// The change-of-basis corrects the orientation, but the origin is still the listener.
603
// Translate the source position by the listener position to finish.
604
posX += lPosX;
605
posY += lPosY;
606
posZ += lPosZ;
607
}
608
609
if (panner.positionX) {
610
// Assigning to panner.positionX/Y/Z unnecessarily seems to cause performance issues
611
// See https://github.com/emscripten-core/emscripten/issues/15847
612
613
if (posX != panner.positionX.value) panner.positionX.value = posX;
614
if (posY != panner.positionY.value) panner.positionY.value = posY;
615
if (posZ != panner.positionZ.value) panner.positionZ.value = posZ;
616
} else {
617
#if OPENAL_DEBUG
618
warnOnce('Panner position attributes are not present, falling back to setPosition()');
619
#endif
620
panner.setPosition(posX, posY, posZ);
621
}
622
if (panner.orientationX) {
623
// Assigning to panner.orientation/Y/Z unnecessarily seems to cause performance issues
624
// See https://github.com/emscripten-core/emscripten/issues/15847
625
626
if (dirX != panner.orientationX.value) panner.orientationX.value = dirX;
627
if (dirY != panner.orientationY.value) panner.orientationY.value = dirY;
628
if (dirZ != panner.orientationZ.value) panner.orientationZ.value = dirZ;
629
} else {
630
#if OPENAL_DEBUG
631
warnOnce('Panner orientation attributes are not present, falling back to setOrientation()');
632
#endif
633
panner.setOrientation(dirX, dirY, dirZ);
634
}
635
636
var oldShift = src.dopplerShift;
637
var velX = src.velocity[0];
638
var velY = src.velocity[1];
639
var velZ = src.velocity[2];
640
var lVelX = listener.velocity[0];
641
var lVelY = listener.velocity[1];
642
var lVelZ = listener.velocity[2];
643
if (posX === lPosX && posY === lPosY && posZ === lPosZ
644
|| velX === lVelX && velY === lVelY && velZ === lVelZ)
645
{
646
src.dopplerShift = 1.0;
647
} else {
648
// Doppler algorithm from 1.1 spec
649
var speedOfSound = src.context.speedOfSound;
650
var dopplerFactor = src.context.dopplerFactor;
651
652
var slX = lPosX - posX;
653
var slY = lPosY - posY;
654
var slZ = lPosZ - posZ;
655
656
var magSl = Math.sqrt(slX * slX + slY * slY + slZ * slZ);
657
var vls = (slX * lVelX + slY * lVelY + slZ * lVelZ) / magSl;
658
var vss = (slX * velX + slY * velY + slZ * velZ) / magSl;
659
660
vls = Math.min(vls, speedOfSound / dopplerFactor);
661
vss = Math.min(vss, speedOfSound / dopplerFactor);
662
663
src.dopplerShift = (speedOfSound - dopplerFactor * vls) / (speedOfSound - dopplerFactor * vss);
664
}
665
if (src.dopplerShift !== oldShift) {
666
AL.updateSourceRate(src);
667
}
668
},
669
670
updateSourceRate: (src) => {
671
if (src.state === {{{ cDefs.AL_PLAYING }}}) {
672
// clear scheduled buffers
673
AL.cancelPendingSourceAudio(src);
674
675
var audioSrc = src.audioQueue[0];
676
if (!audioSrc) {
677
return; // It is possible that AL.scheduleContextAudio() has not yet fed the next buffer, if so, skip.
678
}
679
680
var duration;
681
if (src.type === {{{ cDefs.AL_STATIC }}} && src.looping) {
682
duration = Number.POSITIVE_INFINITY;
683
} else {
684
// audioSrc._duration is expressed after factoring in playbackRate, so when changing playback rate, need
685
// to recompute/rescale the rate to the new playback speed.
686
duration = (audioSrc.buffer.duration - audioSrc._startOffset) / src.playbackRate;
687
}
688
689
audioSrc._duration = duration;
690
audioSrc.playbackRate.value = src.playbackRate;
691
692
// reschedule buffers with the new playbackRate
693
AL.scheduleSourceAudio(src);
694
}
695
},
696
697
sourceDuration: (src) => {
698
var length = 0.0;
699
for (var i = 0; i < src.bufQueue.length; i++) {
700
var audioBuf = src.bufQueue[i].audioBuf;
701
length += audioBuf ? audioBuf.duration : 0.0;
702
}
703
return length;
704
},
705
706
sourceTell: (src) => {
707
AL.updateSourceTime(src);
708
709
var offset = 0.0;
710
for (var i = 0; i < src.bufsProcessed; i++) {
711
if (src.bufQueue[i].audioBuf) {
712
offset += src.bufQueue[i].audioBuf.duration;
713
}
714
}
715
offset += src.bufOffset;
716
717
return offset;
718
},
719
720
sourceSeek: (src, offset) => {
721
var playing = src.state == {{{ cDefs.AL_PLAYING }}};
722
if (playing) {
723
AL.setSourceState(src, {{{ cDefs.AL_INITIAL }}});
724
}
725
726
if (src.bufQueue[src.bufsProcessed].audioBuf !== null) {
727
src.bufsProcessed = 0;
728
while (offset > src.bufQueue[src.bufsProcessed].audioBuf.duration) {
729
offset -= src.bufQueue[src.bufsProcessed].audioBuf.duration;
730
src.bufsProcessed++;
731
}
732
733
src.bufOffset = offset;
734
}
735
736
if (playing) {
737
AL.setSourceState(src, {{{ cDefs.AL_PLAYING }}});
738
}
739
},
740
741
// ------------------------------------------------------
742
// -- Accessor Helpers
743
// ------------------------------------------------------
744
745
getGlobalParam: (funcname, param) => {
746
if (!AL.currentCtx) {
747
#if OPENAL_DEBUG
748
dbg(`${funcname}() called without a valid context`);
749
#endif
750
return null;
751
}
752
753
switch (param) {
754
case {{{ cDefs.AL_DOPPLER_FACTOR }}}:
755
return AL.currentCtx.dopplerFactor;
756
case {{{ cDefs.AL_SPEED_OF_SOUND }}}:
757
return AL.currentCtx.speedOfSound;
758
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
759
return AL.currentCtx.distanceModel;
760
default:
761
#if OPENAL_DEBUG
762
dbg(`${funcname}() param ${ptrToString(param} is unknown or not implemented`);
763
#endif
764
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
765
return null;
766
}
767
},
768
769
setGlobalParam: (funcname, param, value) => {
770
if (!AL.currentCtx) {
771
#if OPENAL_DEBUG
772
dbg(`${funcname}() called without a valid context`);
773
#endif
774
return;
775
}
776
777
switch (param) {
778
case {{{ cDefs.AL_DOPPLER_FACTOR }}}:
779
if (!Number.isFinite(value) || value < 0.0) { // Strictly negative values are disallowed
780
#if OPENAL_DEBUG
781
dbg(`${funcname}() value ${value} is out of range`);
782
#endif
783
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
784
return;
785
}
786
787
AL.currentCtx.dopplerFactor = value;
788
AL.updateListenerSpace(AL.currentCtx);
789
break;
790
case {{{ cDefs.AL_SPEED_OF_SOUND }}}:
791
if (!Number.isFinite(value) || value <= 0.0) { // Negative or zero values are disallowed
792
#if OPENAL_DEBUG
793
dbg(`${funcname}() value ${value} is out of range`);
794
#endif
795
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
796
return;
797
}
798
799
AL.currentCtx.speedOfSound = value;
800
AL.updateListenerSpace(AL.currentCtx);
801
break;
802
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
803
switch (value) {
804
case {{{ cDefs.AL_NONE }}}:
805
case 0xd001 /* AL_INVERSE_DISTANCE */:
806
case 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */:
807
case 0xd003 /* AL_LINEAR_DISTANCE */:
808
case 0xd004 /* AL_LINEAR_DISTANCE_CLAMPED */:
809
case 0xd005 /* AL_EXPONENT_DISTANCE */:
810
case 0xd006 /* AL_EXPONENT_DISTANCE_CLAMPED */:
811
AL.currentCtx.distanceModel = value;
812
AL.updateContextGlobal(AL.currentCtx);
813
break;
814
default:
815
#if OPENAL_DEBUG
816
dbg(`${funcname}() value ${value} is out of range`);
817
#endif
818
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
819
return;
820
}
821
break;
822
default:
823
#if OPENAL_DEBUG
824
dbg(`${funcname}() param ${ptrToString(param)} is unknown or not implemented`);
825
#endif
826
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
827
return;
828
}
829
},
830
831
getListenerParam: (funcname, param) => {
832
if (!AL.currentCtx) {
833
#if OPENAL_DEBUG
834
dbg(`${funcname}() called without a valid context`);
835
#endif
836
return null;
837
}
838
839
switch (param) {
840
case {{{ cDefs.AL_POSITION }}}:
841
return AL.currentCtx.listener.position;
842
case {{{ cDefs.AL_VELOCITY }}}:
843
return AL.currentCtx.listener.velocity;
844
case {{{ cDefs.AL_ORIENTATION }}}:
845
return AL.currentCtx.listener.direction.concat(AL.currentCtx.listener.up);
846
case {{{ cDefs.AL_GAIN }}}:
847
return AL.currentCtx.gain.gain.value;
848
default:
849
#if OPENAL_DEBUG
850
dbg(`${funcname}() param ${ptrToString(param)} is unknown or not implemented`);
851
#endif
852
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
853
return null;
854
}
855
},
856
857
setListenerParam: (funcname, param, value) => {
858
if (!AL.currentCtx) {
859
#if OPENAL_DEBUG
860
dbg(`${funcname}() called without a valid context`);
861
#endif
862
return;
863
}
864
if (value === null) {
865
#if OPENAL_DEBUG
866
dbg(`${funcname}(): param ${ptrToString(param)} has wrong signature`);
867
#endif
868
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
869
return;
870
}
871
872
var listener = AL.currentCtx.listener;
873
switch (param) {
874
case {{{ cDefs.AL_POSITION }}}:
875
if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) {
876
#if OPENAL_DEBUG
877
dbg(`${funcname}() param AL_POSITION value ${value} is out of range`);
878
#endif
879
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
880
return;
881
}
882
883
listener.position[0] = value[0];
884
listener.position[1] = value[1];
885
listener.position[2] = value[2];
886
AL.updateListenerSpace(AL.currentCtx);
887
break;
888
case {{{ cDefs.AL_VELOCITY }}}:
889
if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) {
890
#if OPENAL_DEBUG
891
dbg(`${funcname}() param AL_VELOCITY value ${value} is out of range`);
892
#endif
893
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
894
return;
895
}
896
897
listener.velocity[0] = value[0];
898
listener.velocity[1] = value[1];
899
listener.velocity[2] = value[2];
900
AL.updateListenerSpace(AL.currentCtx);
901
break;
902
case {{{ cDefs.AL_GAIN }}}:
903
if (!Number.isFinite(value) || value < 0.0) {
904
#if OPENAL_DEBUG
905
dbg(`${funcname}() param AL_GAIN value ${value} is out of range`);
906
#endif
907
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
908
return;
909
}
910
911
AL.currentCtx.gain.gain.value = value;
912
break;
913
case {{{ cDefs.AL_ORIENTATION }}}:
914
if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])
915
|| !Number.isFinite(value[3]) || !Number.isFinite(value[4]) || !Number.isFinite(value[5])
916
) {
917
#if OPENAL_DEBUG
918
dbg(`${funcname}() param AL_ORIENTATION value ${value} is out of range`);
919
#endif
920
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
921
return;
922
}
923
924
listener.direction[0] = value[0];
925
listener.direction[1] = value[1];
926
listener.direction[2] = value[2];
927
listener.up[0] = value[3];
928
listener.up[1] = value[4];
929
listener.up[2] = value[5];
930
AL.updateListenerSpace(AL.currentCtx);
931
break;
932
default:
933
#if OPENAL_DEBUG
934
dbg(`${funcname}() param ${ptrToString(param)} is unknown or not implemented`);
935
#endif
936
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
937
return;
938
}
939
},
940
941
getBufferParam: (funcname, bufferId, param) => {
942
if (!AL.currentCtx) {
943
#if OPENAL_DEBUG
944
dbg(`${funcname}() called without a valid context`);
945
#endif
946
return;
947
}
948
var buf = AL.buffers[bufferId];
949
if (!buf || bufferId === 0) {
950
#if OPENAL_DEBUG
951
dbg(`${funcname}() called with an invalid buffer`);
952
#endif
953
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
954
return;
955
}
956
957
switch (param) {
958
case 0x2001 /* AL_FREQUENCY */:
959
return buf.frequency;
960
case 0x2002 /* AL_BITS */:
961
return buf.bytesPerSample * 8;
962
case 0x2003 /* AL_CHANNELS */:
963
return buf.channels;
964
case 0x2004 /* AL_SIZE */:
965
return buf.length * buf.bytesPerSample * buf.channels;
966
case 0x2015 /* AL_LOOP_POINTS_SOFT */:
967
if (buf.length === 0) {
968
return [0, 0];
969
}
970
return [
971
(buf.audioBuf._loopStart || 0.0) * buf.frequency,
972
(buf.audioBuf._loopEnd || buf.length) * buf.frequency
973
];
974
default:
975
#if OPENAL_DEBUG
976
dbg(`${funcname}() param ${ptrToString(param)} is unknown or not implemented`);
977
#endif
978
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
979
return null;
980
}
981
},
982
983
setBufferParam: (funcname, bufferId, param, value) => {
984
if (!AL.currentCtx) {
985
#if OPENAL_DEBUG
986
dbg(`${funcname}() called without a valid context`);
987
#endif
988
return;
989
}
990
var buf = AL.buffers[bufferId];
991
if (!buf || bufferId === 0) {
992
#if OPENAL_DEBUG
993
dbg(`${funcname}() called with an invalid buffer`);
994
#endif
995
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
996
return;
997
}
998
if (value === null) {
999
#if OPENAL_DEBUG
1000
dbg(`${funcname}(): param ${ptrToString(param)} has wrong signature`);
1001
#endif
1002
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
1003
return;
1004
}
1005
1006
switch (param) {
1007
case 0x2004 /* AL_SIZE */:
1008
if (value !== 0) {
1009
#if OPENAL_DEBUG
1010
dbg(`${funcname}() param AL_SIZE value ${value} is out of range`);
1011
#endif
1012
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1013
return;
1014
}
1015
1016
// Per the spec, setting AL_SIZE to 0 is a legal NOP.
1017
break;
1018
case 0x2015 /* AL_LOOP_POINTS_SOFT */:
1019
if (value[0] < 0 || value[0] > buf.length || value[1] < 0 || value[1] > buf.Length || value[0] >= value[1]) {
1020
#if OPENAL_DEBUG
1021
dbg(`${funcname}() param AL_LOOP_POINTS_SOFT value ${value} is out of range`);
1022
#endif
1023
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1024
return;
1025
}
1026
if (buf.refCount > 0) {
1027
#if OPENAL_DEBUG
1028
dbg(`${funcname}() param AL_LOOP_POINTS_SOFT set on bound buffer`);
1029
#endif
1030
AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}};
1031
return;
1032
}
1033
1034
if (buf.audioBuf) {
1035
buf.audioBuf._loopStart = value[0] / buf.frequency;
1036
buf.audioBuf._loopEnd = value[1] / buf.frequency;
1037
}
1038
break;
1039
default:
1040
#if OPENAL_DEBUG
1041
dbg(`${funcname}() param ${ptrToString(param)}' is unknown or not implemented`);
1042
#endif
1043
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
1044
return;
1045
}
1046
},
1047
1048
getSourceParam: (funcname, sourceId, param) => {
1049
if (!AL.currentCtx) {
1050
#if OPENAL_DEBUG
1051
dbg(`${funcname}() called without a valid context`);
1052
#endif
1053
return null;
1054
}
1055
var src = AL.currentCtx.sources[sourceId];
1056
if (!src) {
1057
#if OPENAL_DEBUG
1058
dbg(`${funcname}() called with an invalid source`);
1059
#endif
1060
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
1061
return null;
1062
}
1063
1064
switch (param) {
1065
case 0x202 /* AL_SOURCE_RELATIVE */:
1066
return src.relative;
1067
case 0x1001 /* AL_CONE_INNER_ANGLE */:
1068
return src.coneInnerAngle;
1069
case 0x1002 /* AL_CONE_OUTER_ANGLE */:
1070
return src.coneOuterAngle;
1071
case 0x1003 /* AL_PITCH */:
1072
return src.pitch;
1073
case {{{ cDefs.AL_POSITION }}}:
1074
return src.position;
1075
case {{{ cDefs.AL_DIRECTION }}}:
1076
return src.direction;
1077
case {{{ cDefs.AL_VELOCITY }}}:
1078
return src.velocity;
1079
case 0x1007 /* AL_LOOPING */:
1080
return src.looping;
1081
case 0x1009 /* AL_BUFFER */:
1082
if (src.type === {{{ cDefs.AL_STATIC }}}) {
1083
return src.bufQueue[0].id;
1084
}
1085
return 0;
1086
case {{{ cDefs.AL_GAIN }}}:
1087
return src.gain.gain.value;
1088
case 0x100D /* AL_MIN_GAIN */:
1089
return src.minGain;
1090
case 0x100E /* AL_MAX_GAIN */:
1091
return src.maxGain;
1092
case 0x1010 /* AL_SOURCE_STATE */:
1093
return src.state;
1094
case 0x1015 /* AL_BUFFERS_QUEUED */:
1095
if (src.bufQueue.length === 1 && src.bufQueue[0].id === 0) {
1096
return 0;
1097
}
1098
return src.bufQueue.length;
1099
case 0x1016 /* AL_BUFFERS_PROCESSED */:
1100
if ((src.bufQueue.length === 1 && src.bufQueue[0].id === 0) || src.looping) {
1101
return 0;
1102
}
1103
return src.bufsProcessed;
1104
case 0x1020 /* AL_REFERENCE_DISTANCE */:
1105
return src.refDistance;
1106
case 0x1021 /* AL_ROLLOFF_FACTOR */:
1107
return src.rolloffFactor;
1108
case 0x1022 /* AL_CONE_OUTER_GAIN */:
1109
return src.coneOuterGain;
1110
case 0x1023 /* AL_MAX_DISTANCE */:
1111
return src.maxDistance;
1112
case 0x1024 /* AL_SEC_OFFSET */:
1113
return AL.sourceTell(src);
1114
case 0x1025 /* AL_SAMPLE_OFFSET */:
1115
var offset = AL.sourceTell(src);
1116
if (offset > 0.0) {
1117
offset *= src.bufQueue[0].frequency;
1118
}
1119
return offset;
1120
case 0x1026 /* AL_BYTE_OFFSET */:
1121
var offset = AL.sourceTell(src);
1122
if (offset > 0.0) {
1123
offset *= src.bufQueue[0].frequency * src.bufQueue[0].bytesPerSample;
1124
}
1125
return offset;
1126
case 0x1027 /* AL_SOURCE_TYPE */:
1127
return src.type;
1128
case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */:
1129
return src.spatialize;
1130
case 0x2009 /* AL_BYTE_LENGTH_SOFT */:
1131
var length = 0;
1132
var bytesPerFrame = 0;
1133
for (var i = 0; i < src.bufQueue.length; i++) {
1134
length += src.bufQueue[i].length;
1135
if (src.bufQueue[i].id !== 0) {
1136
bytesPerFrame = src.bufQueue[i].bytesPerSample * src.bufQueue[i].channels;
1137
}
1138
}
1139
return length * bytesPerFrame;
1140
case 0x200A /* AL_SAMPLE_LENGTH_SOFT */:
1141
var length = 0;
1142
for (var i = 0; i < src.bufQueue.length; i++) {
1143
length += src.bufQueue[i].length;
1144
}
1145
return length;
1146
case 0x200B /* AL_SEC_LENGTH_SOFT */:
1147
return AL.sourceDuration(src);
1148
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
1149
return src.distanceModel;
1150
default:
1151
#if OPENAL_DEBUG
1152
dbg(`${funcname}() param ${ptrToString(param)}' is unknown or not implemented`);
1153
#endif
1154
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
1155
return null;
1156
}
1157
},
1158
1159
setSourceParam: (funcname, sourceId, param, value) => {
1160
if (!AL.currentCtx) {
1161
#if OPENAL_DEBUG
1162
dbg(`${funcname}() called without a valid context`);
1163
#endif
1164
return;
1165
}
1166
var src = AL.currentCtx.sources[sourceId];
1167
if (!src) {
1168
#if OPENAL_DEBUG
1169
dbg('alSourcef() called with an invalid source');
1170
#endif
1171
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
1172
return;
1173
}
1174
if (value === null) {
1175
#if OPENAL_DEBUG
1176
dbg(`${funcname}(): param ${ptrToString(param)}' has wrong signature`);
1177
#endif
1178
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
1179
return;
1180
}
1181
1182
switch (param) {
1183
case 0x202 /* AL_SOURCE_RELATIVE */:
1184
if (value === {{{ cDefs.AL_TRUE }}}) {
1185
src.relative = true;
1186
AL.updateSourceSpace(src);
1187
} else if (value === {{{ cDefs.AL_FALSE }}}) {
1188
src.relative = false;
1189
AL.updateSourceSpace(src);
1190
} else {
1191
#if OPENAL_DEBUG
1192
dbg(`${funcname}() param AL_SOURCE_RELATIVE value ${value} is out of range`);
1193
#endif
1194
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1195
return;
1196
}
1197
break;
1198
case 0x1001 /* AL_CONE_INNER_ANGLE */:
1199
if (!Number.isFinite(value)) {
1200
#if OPENAL_DEBUG
1201
dbg(`${funcname}() param AL_CONE_INNER_ANGLE value ${value} is out of range`);
1202
#endif
1203
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1204
return;
1205
}
1206
1207
src.coneInnerAngle = value;
1208
if (src.panner) {
1209
src.panner.coneInnerAngle = value % 360.0;
1210
}
1211
break;
1212
case 0x1002 /* AL_CONE_OUTER_ANGLE */:
1213
if (!Number.isFinite(value)) {
1214
#if OPENAL_DEBUG
1215
dbg(`${funcname}() param AL_CONE_OUTER_ANGLE value ${value} is out of range`);
1216
#endif
1217
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1218
return;
1219
}
1220
1221
src.coneOuterAngle = value;
1222
if (src.panner) {
1223
src.panner.coneOuterAngle = value % 360.0;
1224
}
1225
break;
1226
case 0x1003 /* AL_PITCH */:
1227
if (!Number.isFinite(value) || value <= 0.0) {
1228
#if OPENAL_DEBUG
1229
dbg(`${funcname}() param AL_PITCH value ${value} is out of range`);
1230
#endif
1231
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1232
return;
1233
}
1234
1235
if (src.pitch === value) {
1236
break;
1237
}
1238
1239
src.pitch = value;
1240
AL.updateSourceRate(src);
1241
break;
1242
case {{{ cDefs.AL_POSITION }}}:
1243
if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) {
1244
#if OPENAL_DEBUG
1245
dbg(`${funcname}() param AL_POSITION value ${value} is out of range`);
1246
#endif
1247
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1248
return;
1249
}
1250
1251
src.position[0] = value[0];
1252
src.position[1] = value[1];
1253
src.position[2] = value[2];
1254
AL.updateSourceSpace(src);
1255
break;
1256
case {{{ cDefs.AL_DIRECTION }}}:
1257
if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) {
1258
#if OPENAL_DEBUG
1259
dbg(`${funcname}() param AL_DIRECTION value ${value} is out of range`);
1260
#endif
1261
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1262
return;
1263
}
1264
1265
src.direction[0] = value[0];
1266
src.direction[1] = value[1];
1267
src.direction[2] = value[2];
1268
AL.updateSourceSpace(src);
1269
break;
1270
case {{{ cDefs.AL_VELOCITY }}}:
1271
if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]) || !Number.isFinite(value[2])) {
1272
#if OPENAL_DEBUG
1273
dbg(`${funcname}() param AL_VELOCITY value ${value} is out of range`);
1274
#endif
1275
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1276
return;
1277
}
1278
1279
src.velocity[0] = value[0];
1280
src.velocity[1] = value[1];
1281
src.velocity[2] = value[2];
1282
AL.updateSourceSpace(src);
1283
break;
1284
case 0x1007 /* AL_LOOPING */:
1285
if (value === {{{ cDefs.AL_TRUE }}}) {
1286
src.looping = true;
1287
AL.updateSourceTime(src);
1288
if (src.type === {{{ cDefs.AL_STATIC }}} && src.audioQueue.length > 0) {
1289
var audioSrc = src.audioQueue[0];
1290
audioSrc.loop = true;
1291
audioSrc._duration = Number.POSITIVE_INFINITY;
1292
}
1293
} else if (value === {{{ cDefs.AL_FALSE }}}) {
1294
src.looping = false;
1295
var currentTime = AL.updateSourceTime(src);
1296
if (src.type === {{{ cDefs.AL_STATIC }}} && src.audioQueue.length > 0) {
1297
var audioSrc = src.audioQueue[0];
1298
audioSrc.loop = false;
1299
audioSrc._duration = src.bufQueue[0].audioBuf.duration / src.playbackRate;
1300
audioSrc._startTime = currentTime - src.bufOffset / src.playbackRate;
1301
}
1302
} else {
1303
#if OPENAL_DEBUG
1304
dbg(`${funcname}() param AL_LOOPING value ${value} is out of range`);
1305
#endif
1306
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1307
return;
1308
}
1309
break;
1310
case 0x1009 /* AL_BUFFER */:
1311
if (src.state === {{{ cDefs.AL_PLAYING }}} || src.state === {{{ cDefs.AL_PAUSED }}}) {
1312
#if OPENAL_DEBUG
1313
dbg(`${funcname}(AL_BUFFER) called while source is playing or paused`);
1314
#endif
1315
AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}};
1316
return;
1317
}
1318
1319
if (value === 0) {
1320
for (var i in src.bufQueue) {
1321
src.bufQueue[i].refCount--;
1322
}
1323
src.bufQueue.length = 1;
1324
src.bufQueue[0] = AL.buffers[0];
1325
1326
src.bufsProcessed = 0;
1327
src.type = 0x1030 /* AL_UNDETERMINED */;
1328
} else {
1329
var buf = AL.buffers[value];
1330
if (!buf) {
1331
#if OPENAL_DEBUG
1332
dbg('alSourcei(AL_BUFFER) called with an invalid buffer');
1333
#endif
1334
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1335
return;
1336
}
1337
1338
for (var i in src.bufQueue) {
1339
src.bufQueue[i].refCount--;
1340
}
1341
src.bufQueue.length = 0;
1342
1343
buf.refCount++;
1344
src.bufQueue = [buf];
1345
src.bufsProcessed = 0;
1346
src.type = {{{ cDefs.AL_STATIC }}};
1347
}
1348
1349
AL.initSourcePanner(src);
1350
AL.scheduleSourceAudio(src);
1351
break;
1352
case {{{ cDefs.AL_GAIN }}}:
1353
if (!Number.isFinite(value) || value < 0.0) {
1354
#if OPENAL_DEBUG
1355
dbg(`${funcname}() param AL_GAIN value ${value} is out of range`);
1356
#endif
1357
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1358
return;
1359
}
1360
src.gain.gain.value = value;
1361
break;
1362
case 0x100D /* AL_MIN_GAIN */:
1363
if (!Number.isFinite(value) || value < 0.0 || value > Math.min(src.maxGain, 1.0)) {
1364
#if OPENAL_DEBUG
1365
dbg(`${funcname}() param AL_MIN_GAIN value ${value} is out of range`);
1366
#endif
1367
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1368
return;
1369
}
1370
#if OPENAL_DEBUG
1371
warnOnce('AL_MIN_GAIN is not currently supported');
1372
#endif
1373
src.minGain = value;
1374
break;
1375
case 0x100E /* AL_MAX_GAIN */:
1376
if (!Number.isFinite(value) || value < Math.max(0.0, src.minGain) || value > 1.0) {
1377
#if OPENAL_DEBUG
1378
dbg(`${funcname}() param AL_MAX_GAIN value ${value} is out of range`);
1379
#endif
1380
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1381
return;
1382
}
1383
#if OPENAL_DEBUG
1384
warnOnce('AL_MAX_GAIN is not currently supported');
1385
#endif
1386
src.maxGain = value;
1387
break;
1388
case 0x1020 /* AL_REFERENCE_DISTANCE */:
1389
if (!Number.isFinite(value) || value < 0.0) {
1390
#if OPENAL_DEBUG
1391
dbg(`${funcname}() param AL_REFERENCE_DISTANCE value ${value} is out of range`);
1392
#endif
1393
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1394
return;
1395
}
1396
src.refDistance = value;
1397
if (src.panner) {
1398
src.panner.refDistance = value;
1399
}
1400
break;
1401
case 0x1021 /* AL_ROLLOFF_FACTOR */:
1402
if (!Number.isFinite(value) || value < 0.0) {
1403
#if OPENAL_DEBUG
1404
dbg(`${funcname}() param AL_ROLLOFF_FACTOR value ${value} is out of range`);
1405
#endif
1406
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1407
return;
1408
}
1409
src.rolloffFactor = value;
1410
if (src.panner) {
1411
src.panner.rolloffFactor = value;
1412
}
1413
break;
1414
case 0x1022 /* AL_CONE_OUTER_GAIN */:
1415
if (!Number.isFinite(value) || value < 0.0 || value > 1.0) {
1416
#if OPENAL_DEBUG
1417
dbg(`${funcname}() param AL_CORE_OUTER_GAIN value ${value} is out of range`);
1418
#endif
1419
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1420
return;
1421
}
1422
src.coneOuterGain = value;
1423
if (src.panner) {
1424
src.panner.coneOuterGain = value;
1425
}
1426
break;
1427
case 0x1023 /* AL_MAX_DISTANCE */:
1428
if (!Number.isFinite(value) || value < 0.0) {
1429
#if OPENAL_DEBUG
1430
dbg(`${funcname}() param AL_MAX_DISTANCE value ${value} is out of range`);
1431
#endif
1432
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1433
return;
1434
}
1435
src.maxDistance = value;
1436
if (src.panner) {
1437
src.panner.maxDistance = value;
1438
}
1439
break;
1440
case 0x1024 /* AL_SEC_OFFSET */:
1441
if (value < 0.0 || value > AL.sourceDuration(src)) {
1442
#if OPENAL_DEBUG
1443
dbg(`${funcname}() param AL_SEC_OFFSET value ${value} is out of range`);
1444
#endif
1445
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1446
return;
1447
}
1448
1449
AL.sourceSeek(src, value);
1450
break;
1451
case 0x1025 /* AL_SAMPLE_OFFSET */:
1452
var srcLen = AL.sourceDuration(src);
1453
if (srcLen > 0.0) {
1454
var frequency;
1455
for (var bufId in src.bufQueue) {
1456
if (bufId) {
1457
frequency = src.bufQueue[bufId].frequency;
1458
break;
1459
}
1460
}
1461
value /= frequency;
1462
}
1463
if (value < 0.0 || value > srcLen) {
1464
#if OPENAL_DEBUG
1465
dbg(`${funcname}() param AL_SAMPLE_OFFSET value ${value} is out of range`);
1466
#endif
1467
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1468
return;
1469
}
1470
1471
AL.sourceSeek(src, value);
1472
break;
1473
case 0x1026 /* AL_BYTE_OFFSET */:
1474
var srcLen = AL.sourceDuration(src);
1475
if (srcLen > 0.0) {
1476
var bytesPerSec;
1477
for (var bufId in src.bufQueue) {
1478
if (bufId) {
1479
var buf = src.bufQueue[bufId];
1480
bytesPerSec = buf.frequency * buf.bytesPerSample * buf.channels;
1481
break;
1482
}
1483
}
1484
value /= bytesPerSec;
1485
}
1486
if (value < 0.0 || value > srcLen) {
1487
#if OPENAL_DEBUG
1488
dbg(`${funcname}() param AL_BYTE_OFFSET value ${value} is out of range`);
1489
#endif
1490
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1491
return;
1492
}
1493
1494
AL.sourceSeek(src, value);
1495
break;
1496
case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */:
1497
if (value !== {{{ cDefs.AL_FALSE }}} && value !== {{{ cDefs.AL_TRUE }}} && value !== 2 /* AL_AUTO_SOFT */) {
1498
#if OPENAL_DEBUG
1499
dbg(`${funcname}() param AL_SOURCE_SPATIALIZE_SOFT value ${value} is out of range`);
1500
#endif
1501
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1502
return;
1503
}
1504
1505
src.spatialize = value;
1506
AL.initSourcePanner(src);
1507
break;
1508
case 0x2009 /* AL_BYTE_LENGTH_SOFT */:
1509
case 0x200A /* AL_SAMPLE_LENGTH_SOFT */:
1510
case 0x200B /* AL_SEC_LENGTH_SOFT */:
1511
#if OPENAL_DEBUG
1512
dbg(`${funcname}() param AL_*_LENGTH_SOFT is read only`);
1513
#endif
1514
AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}};
1515
break;
1516
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
1517
switch (value) {
1518
case {{{ cDefs.AL_NONE }}}:
1519
case 0xd001 /* AL_INVERSE_DISTANCE */:
1520
case 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */:
1521
case 0xd003 /* AL_LINEAR_DISTANCE */:
1522
case 0xd004 /* AL_LINEAR_DISTANCE_CLAMPED */:
1523
case 0xd005 /* AL_EXPONENT_DISTANCE */:
1524
case 0xd006 /* AL_EXPONENT_DISTANCE_CLAMPED */:
1525
src.distanceModel = value;
1526
if (AL.currentCtx.sourceDistanceModel) {
1527
AL.updateContextGlobal(AL.currentCtx);
1528
}
1529
break;
1530
default:
1531
#if OPENAL_DEBUG
1532
dbg(`${funcname}() param AL_DISTANCE_MODEL value ${value} is out of range`);
1533
#endif
1534
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
1535
return;
1536
}
1537
break;
1538
default:
1539
#if OPENAL_DEBUG
1540
dbg(`${funcname}() param ${ptrToString(param)} is unknown or not implemented`);
1541
#endif
1542
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
1543
return;
1544
}
1545
},
1546
1547
// -------------------------------------------------------
1548
// -- Capture
1549
// -------------------------------------------------------
1550
1551
// A map of 'capture device contexts'.
1552
captures: {},
1553
1554
sharedCaptureAudioCtx: null,
1555
1556
// Helper which:
1557
// - Asserts that deviceId is both non-NULL AND a known device ID;
1558
// - Returns a reference to it, or null if not found.
1559
// - Sets alcErr accordingly.
1560
// Treat NULL and <invalid> separately because careless
1561
// people might assume that most alcCapture functions
1562
// accept NULL as a 'use the default' device.
1563
requireValidCaptureDevice: (deviceId, funcname) => {
1564
if (deviceId === 0) {
1565
#if OPENAL_DEBUG
1566
dbg(`${funcname}() on a NULL device is an error`);
1567
#endif
1568
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
1569
return null;
1570
}
1571
var c = AL.captures[deviceId];
1572
if (!c) {
1573
#if OPENAL_DEBUG
1574
dbg(`${funcname}() on an invalid device`);
1575
#endif
1576
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
1577
return null;
1578
}
1579
var err = c.mediaStreamError;
1580
if (err) {
1581
#if OPENAL_DEBUG
1582
switch (err.name) {
1583
case 'PermissionDeniedError':
1584
dbg(`${funcname}() but the user denied access to the device`);
1585
break;
1586
case 'NotFoundError':
1587
dbg(`${funcname}() but no capture device was found`);
1588
break;
1589
default:
1590
dbg(`${funcname}() but a MediaStreamError was encountered: ${err}`);
1591
break;
1592
}
1593
#endif
1594
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
1595
return null;
1596
}
1597
return c;
1598
}
1599
1600
},
1601
1602
// ***************************************************************************
1603
// ** ALC API
1604
// ***************************************************************************
1605
1606
// -------------------------------------------------------
1607
// -- ALC Capture
1608
// -------------------------------------------------------
1609
1610
// bufferSize is actually 'number of sample frames', so was renamed
1611
// bufferFrameCapacity here for clarity.
1612
alcCaptureOpenDevice__deps: ['$autoResumeAudioContext'],
1613
alcCaptureOpenDevice__proxy: 'sync',
1614
alcCaptureOpenDevice: (pDeviceName, requestedSampleRate, format, bufferFrameCapacity) => {
1615
1616
var resolvedDeviceName = AL.CAPTURE_DEVICE_NAME;
1617
1618
// NULL is a valid device name here (resolves to default);
1619
if (pDeviceName !== 0) {
1620
resolvedDeviceName = UTF8ToString(pDeviceName);
1621
if (resolvedDeviceName !== AL.CAPTURE_DEVICE_NAME) {
1622
#if OPENAL_DEBUG
1623
dbg(`alcCaptureOpenDevice() with invalid device name '${resolvedDeviceName}'`);
1624
#endif
1625
// ALC_OUT_OF_MEMORY
1626
// From the programmer's guide, ALC_OUT_OF_MEMORY's meaning is
1627
// overloaded here, to mean:
1628
// 'The specified device is invalid, or can not capture audio.'
1629
// This may be misleading to API users, but well...
1630
AL.alcErr = 0xA005 /* ALC_OUT_OF_MEMORY */;
1631
return 0;
1632
}
1633
}
1634
1635
// Otherwise it's probably okay (though useless) for bufferFrameCapacity to be zero.
1636
if (bufferFrameCapacity < 0) { // ALCsizei is signed int
1637
#if OPENAL_DEBUG
1638
dbg('alcCaptureOpenDevice() with negative bufferSize');
1639
#endif
1640
AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}};
1641
return 0;
1642
}
1643
1644
navigator.getUserMedia = navigator.getUserMedia
1645
|| navigator.webkitGetUserMedia
1646
|| navigator.mozGetUserMedia
1647
|| navigator.msGetUserMedia;
1648
var has_getUserMedia = navigator.getUserMedia
1649
|| (navigator.mediaDevices
1650
&& navigator.mediaDevices.getUserMedia);
1651
1652
if (!has_getUserMedia) {
1653
#if OPENAL_DEBUG
1654
dbg('alcCaptureOpenDevice() cannot capture audio, because your browser lacks a `getUserMedia()` implementation');
1655
#endif
1656
// See previously mentioned rationale for ALC_OUT_OF_MEMORY
1657
AL.alcErr = 0xA005 /* ALC_OUT_OF_MEMORY */;
1658
return 0;
1659
}
1660
1661
var AudioContext = window.AudioContext || window.webkitAudioContext;
1662
1663
if (!AL.sharedCaptureAudioCtx) {
1664
try {
1665
AL.sharedCaptureAudioCtx = new AudioContext();
1666
} catch(e) {
1667
#if OPENAL_DEBUG
1668
dbg(`alcCaptureOpenDevice() could not create the shared capture AudioContext: ${e}`);
1669
#endif
1670
// See previously mentioned rationale for ALC_OUT_OF_MEMORY
1671
AL.alcErr = 0xA005 /* ALC_OUT_OF_MEMORY */;
1672
return 0;
1673
}
1674
}
1675
1676
autoResumeAudioContext(AL.sharedCaptureAudioCtx);
1677
1678
var outputChannelCount;
1679
1680
switch (format) {
1681
case 0x10010: /* AL_FORMAT_MONO_FLOAT32 */
1682
case 0x1101: /* AL_FORMAT_MONO16 */
1683
case 0x1100: /* AL_FORMAT_MONO8 */
1684
outputChannelCount = 1;
1685
break;
1686
case 0x10011: /* AL_FORMAT_STEREO_FLOAT32 */
1687
case 0x1103: /* AL_FORMAT_STEREO16 */
1688
case 0x1102: /* AL_FORMAT_STEREO8 */
1689
outputChannelCount = 2;
1690
break;
1691
default:
1692
#if OPENAL_DEBUG
1693
dbg(`alcCaptureOpenDevice() with unsupported format ${format}`);
1694
#endif
1695
AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}};
1696
return 0;
1697
}
1698
1699
function newF32Array(cap) { return new Float32Array(cap);}
1700
function newI16Array(cap) { return new Int16Array(cap); }
1701
function newU8Array(cap) { return new Uint8Array(cap); }
1702
1703
var requestedSampleType;
1704
var newSampleArray;
1705
1706
switch (format) {
1707
case 0x10010: /* AL_FORMAT_MONO_FLOAT32 */
1708
case 0x10011: /* AL_FORMAT_STEREO_FLOAT32 */
1709
requestedSampleType = 'f32';
1710
newSampleArray = newF32Array;
1711
break;
1712
case 0x1101: /* AL_FORMAT_MONO16 */
1713
case 0x1103: /* AL_FORMAT_STEREO16 */
1714
requestedSampleType = 'i16';
1715
newSampleArray = newI16Array;
1716
break;
1717
case 0x1100: /* AL_FORMAT_MONO8 */
1718
case 0x1102: /* AL_FORMAT_STEREO8 */
1719
requestedSampleType = 'u8';
1720
newSampleArray = newU8Array;
1721
break;
1722
}
1723
1724
var buffers = [];
1725
try {
1726
for (var chan=0; chan < outputChannelCount; ++chan) {
1727
buffers[chan] = newSampleArray(bufferFrameCapacity);
1728
}
1729
} catch(e) {
1730
#if OPENAL_DEBUG
1731
dbg(`alcCaptureOpenDevice() failed to allocate internal buffers (is bufferSize low enough?): ${e}`);
1732
#endif
1733
AL.alcErr = 0xA005 /* ALC_OUT_OF_MEMORY */;
1734
return 0;
1735
}
1736
1737
1738
// What we'll place into the `AL.captures` array in the end,
1739
// declared here for closures to access it
1740
var newCapture = {
1741
audioCtx: AL.sharedCaptureAudioCtx,
1742
deviceName: resolvedDeviceName,
1743
requestedSampleRate,
1744
requestedSampleType,
1745
outputChannelCount,
1746
inputChannelCount: null, // Not known until the getUserMedia() promise resolves
1747
mediaStreamError: null, // Used by other functions to return early and report an error.
1748
mediaStreamSourceNode: null,
1749
mediaStream: null,
1750
// Either one, or none of the below two, is active.
1751
mergerNode: null,
1752
splitterNode: null,
1753
scriptProcessorNode: null,
1754
isCapturing: false,
1755
buffers,
1756
get bufferFrameCapacity() {
1757
return buffers[0].length;
1758
},
1759
capturePlayhead: 0, // current write position, in sample frames
1760
captureReadhead: 0,
1761
capturedFrameCount: 0
1762
};
1763
1764
// Preparing for getUserMedia()
1765
1766
var onError = (mediaStreamError) => {
1767
newCapture.mediaStreamError = mediaStreamError;
1768
#if OPENAL_DEBUG
1769
dbg(`navigator.getUserMedia() errored with: ${mediaStreamError}`);
1770
#endif
1771
};
1772
var onSuccess = (mediaStream) => {
1773
newCapture.mediaStreamSourceNode = newCapture.audioCtx.createMediaStreamSource(mediaStream);
1774
newCapture.mediaStream = mediaStream;
1775
1776
var inputChannelCount = 1;
1777
switch (newCapture.mediaStreamSourceNode.channelCountMode) {
1778
case 'max':
1779
inputChannelCount = outputChannelCount;
1780
break;
1781
case 'clamped-max':
1782
inputChannelCount = Math.min(outputChannelCount, newCapture.mediaStreamSourceNode.channelCount);
1783
break;
1784
case 'explicit':
1785
inputChannelCount = newCapture.mediaStreamSourceNode.channelCount;
1786
break;
1787
}
1788
1789
newCapture.inputChannelCount = inputChannelCount;
1790
1791
#if OPENAL_DEBUG
1792
if (inputChannelCount > 2 || outputChannelCount > 2) {
1793
dbg('The number of input or output channels is too high, capture might not work as expected!');
1794
}
1795
#endif
1796
1797
// Have to pick a size from 256, 512, 1024, 2048, 4096, 8192, 16384.
1798
// One can also set it to zero, which leaves the decision up to the impl.
1799
// An extension could allow specifying this value.
1800
var processorFrameCount = 512;
1801
1802
newCapture.scriptProcessorNode = newCapture.audioCtx.createScriptProcessor(
1803
processorFrameCount, inputChannelCount, outputChannelCount
1804
);
1805
1806
if (inputChannelCount > outputChannelCount) {
1807
newCapture.mergerNode = newCapture.audioCtx.createChannelMerger(inputChannelCount);
1808
newCapture.mediaStreamSourceNode.connect(newCapture.mergerNode);
1809
newCapture.mergerNode.connect(newCapture.scriptProcessorNode);
1810
} else if (inputChannelCount < outputChannelCount) {
1811
newCapture.splitterNode = newCapture.audioCtx.createChannelSplitter(outputChannelCount);
1812
newCapture.mediaStreamSourceNode.connect(newCapture.splitterNode);
1813
newCapture.splitterNode.connect(newCapture.scriptProcessorNode);
1814
} else {
1815
newCapture.mediaStreamSourceNode.connect(newCapture.scriptProcessorNode);
1816
}
1817
1818
newCapture.scriptProcessorNode.connect(newCapture.audioCtx.destination);
1819
1820
newCapture.scriptProcessorNode.onaudioprocess = (audioProcessingEvent) => {
1821
if (!newCapture.isCapturing) {
1822
return;
1823
}
1824
1825
var c = newCapture;
1826
var srcBuf = audioProcessingEvent.inputBuffer;
1827
1828
// Actually just copy srcBuf's channel data into
1829
// c.buffers, optimizing for each case.
1830
switch (format) {
1831
case 0x10010: /* AL_FORMAT_MONO_FLOAT32 */
1832
var channel0 = srcBuf.getChannelData(0);
1833
for (var i = 0 ; i < srcBuf.length; ++i) {
1834
var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity;
1835
c.buffers[0][wi] = channel0[i];
1836
}
1837
break;
1838
case 0x10011: /* AL_FORMAT_STEREO_FLOAT32 */
1839
var channel0 = srcBuf.getChannelData(0);
1840
var channel1 = srcBuf.getChannelData(1);
1841
for (var i = 0 ; i < srcBuf.length; ++i) {
1842
var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity;
1843
c.buffers[0][wi] = channel0[i];
1844
c.buffers[1][wi] = channel1[i];
1845
}
1846
break;
1847
case 0x1101: /* AL_FORMAT_MONO16 */
1848
var channel0 = srcBuf.getChannelData(0);
1849
for (var i = 0 ; i < srcBuf.length; ++i) {
1850
var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity;
1851
c.buffers[0][wi] = channel0[i] * 32767;
1852
}
1853
break;
1854
case 0x1103: /* AL_FORMAT_STEREO16 */
1855
var channel0 = srcBuf.getChannelData(0);
1856
var channel1 = srcBuf.getChannelData(1);
1857
for (var i = 0 ; i < srcBuf.length; ++i) {
1858
var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity;
1859
c.buffers[0][wi] = channel0[i] * 32767;
1860
c.buffers[1][wi] = channel1[i] * 32767;
1861
}
1862
break;
1863
case 0x1100: /* AL_FORMAT_MONO8 */
1864
var channel0 = srcBuf.getChannelData(0);
1865
for (var i = 0 ; i < srcBuf.length; ++i) {
1866
var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity;
1867
c.buffers[0][wi] = (channel0[i] + 1.0) * 127;
1868
}
1869
break;
1870
case 0x1102: /* AL_FORMAT_STEREO8 */
1871
var channel0 = srcBuf.getChannelData(0);
1872
var channel1 = srcBuf.getChannelData(1);
1873
for (var i = 0 ; i < srcBuf.length; ++i) {
1874
var wi = (c.capturePlayhead + i) % c.bufferFrameCapacity;
1875
c.buffers[0][wi] = (channel0[i] + 1.0) * 127;
1876
c.buffers[1][wi] = (channel1[i] + 1.0) * 127;
1877
}
1878
break;
1879
}
1880
1881
c.capturePlayhead += srcBuf.length;
1882
c.capturePlayhead %= c.bufferFrameCapacity;
1883
c.capturedFrameCount += srcBuf.length;
1884
c.capturedFrameCount = Math.min(c.capturedFrameCount, c.bufferFrameCapacity);
1885
};
1886
};
1887
1888
// The latest way to call getUserMedia()
1889
if (navigator.mediaDevices?.getUserMedia) {
1890
navigator.mediaDevices
1891
.getUserMedia({audio: true})
1892
.then(onSuccess)
1893
.catch(onError);
1894
} else { // The usual (now deprecated) way
1895
navigator.getUserMedia({audio: true}, onSuccess, onError);
1896
}
1897
1898
var id = AL.newId();
1899
AL.captures[id] = newCapture;
1900
return id;
1901
},
1902
1903
alcCaptureCloseDevice__proxy: 'sync',
1904
alcCaptureCloseDevice: (deviceId) => {
1905
var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureCloseDevice');
1906
if (!c) return false;
1907
1908
delete AL.captures[deviceId];
1909
AL.freeIds.push(deviceId);
1910
1911
// This clean-up might be unnecessary (paranoid) ?
1912
1913
// May happen if user hasn't decided to grant or deny input
1914
c.mediaStreamSourceNode?.disconnect();
1915
c.mergerNode?.disconnect();
1916
c.splitterNode?.disconnect();
1917
// May happen if user hasn't decided to grant or deny input
1918
c.scriptProcessorNode?.disconnect();
1919
if (c.mediaStream) {
1920
// Disabling the microphone of the browser.
1921
// Without this operation, the red dot on the browser tab page will remain.
1922
c.mediaStream.getTracks().forEach((track) => track.stop());
1923
}
1924
1925
delete c.buffers;
1926
1927
c.capturedFrameCount = 0;
1928
c.isCapturing = false;
1929
1930
return true;
1931
},
1932
1933
alcCaptureStart__proxy: 'sync',
1934
alcCaptureStart: (deviceId) => {
1935
var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureStart');
1936
if (!c) return;
1937
1938
if (c.isCapturing) {
1939
#if OPENAL_DEBUG
1940
dbg('Redundant call to alcCaptureStart()');
1941
#endif
1942
// NOTE: Spec says (emphasis mine):
1943
// The amount of audio samples available after **restarting** a
1944
// stopped capture device is reset to zero.
1945
// So redundant calls to alcCaptureStart() must have no effect.
1946
return;
1947
}
1948
c.isCapturing = true;
1949
c.capturedFrameCount = 0;
1950
c.capturePlayhead = 0;
1951
},
1952
1953
alcCaptureStop__proxy: 'sync',
1954
alcCaptureStop: (deviceId) => {
1955
var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureStop');
1956
if (!c) return;
1957
1958
#if OPENAL_DEBUG
1959
if (!c.isCapturing) {
1960
dbg('Redundant call to alcCaptureStop()');
1961
}
1962
#endif
1963
c.isCapturing = false;
1964
},
1965
1966
// The OpenAL spec hints that implementations are allowed to
1967
// 'defer resampling and other conversions' up until this point.
1968
//
1969
// The last parameter is actually 'number of sample frames', so was
1970
// renamed accordingly here
1971
alcCaptureSamples__proxy: 'sync',
1972
alcCaptureSamples: (deviceId, pFrames, requestedFrameCount) => {
1973
var c = AL.requireValidCaptureDevice(deviceId, 'alcCaptureSamples');
1974
if (!c) return;
1975
1976
// ALCsizei is actually 32-bit signed int, so could be negative
1977
// Also, spec says :
1978
// Requesting more sample frames than are currently available is
1979
// an error.
1980
1981
var dstfreq = c.requestedSampleRate;
1982
var srcfreq = c.audioCtx.sampleRate;
1983
1984
var fratio = srcfreq / dstfreq;
1985
1986
if (requestedFrameCount < 0
1987
|| requestedFrameCount > (c.capturedFrameCount / fratio))
1988
{
1989
#if OPENAL_DEBUG
1990
dbg('alcCaptureSamples() with invalid bufferSize');
1991
#endif
1992
AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}};
1993
return;
1994
}
1995
1996
function setF32Sample(i, sample) {
1997
{{{ makeSetValue('pFrames', '4*i', 'sample', 'float') }}};
1998
}
1999
function setI16Sample(i, sample) {
2000
{{{ makeSetValue('pFrames', '2*i', 'sample', 'i16') }}};
2001
}
2002
function setU8Sample(i, sample) {
2003
{{{ makeSetValue('pFrames', 'i', 'sample', 'i8') }}};
2004
}
2005
2006
var setSample;
2007
2008
switch (c.requestedSampleType) {
2009
case 'f32': setSample = setF32Sample; break;
2010
case 'i16': setSample = setI16Sample; break;
2011
case 'u8' : setSample = setU8Sample ; break;
2012
default:
2013
#if OPENAL_DEBUG
2014
dbg(`Internal error: Unknown sample type '${c.requestedSampleType}'`);
2015
#endif
2016
return;
2017
}
2018
2019
// If fratio is an integer we don't need linear resampling, just skip samples
2020
if (Math.floor(fratio) == fratio) {
2021
for (var i = 0, frame_i = 0; frame_i < requestedFrameCount; ++frame_i) {
2022
for (var chan = 0; chan < c.buffers.length; ++chan, ++i) {
2023
setSample(i, c.buffers[chan][c.captureReadhead]);
2024
}
2025
c.captureReadhead = (fratio + c.captureReadhead) % c.bufferFrameCapacity;
2026
}
2027
} else {
2028
// Perform linear resampling.
2029
2030
// There is room for improvement - right now we're fine with linear resampling.
2031
// We don't use OfflineAudioContexts for this: See the discussion at
2032
// https://github.com/jpernst/emscripten/issues/2#issuecomment-312729735
2033
// if you're curious about why.
2034
for (var i = 0, frame_i = 0; frame_i < requestedFrameCount; ++frame_i) {
2035
var lefti = Math.floor(c.captureReadhead);
2036
var righti = Math.ceil(c.captureReadhead);
2037
var d = c.captureReadhead - lefti;
2038
for (var chan = 0; chan < c.buffers.length; ++chan, ++i) {
2039
var lefts = c.buffers[chan][lefti];
2040
var rights = c.buffers[chan][righti];
2041
setSample(i, (1 - d) * lefts + d * rights);
2042
}
2043
c.captureReadhead = (c.captureReadhead + fratio) % c.bufferFrameCapacity;
2044
}
2045
}
2046
2047
// Spec doesn't say if alcCaptureSamples() must zero the number
2048
// of available captured sample-frames, but not only would it
2049
// be insane not to do, OpenAL-Soft happens to do that as well.
2050
c.capturedFrameCount = 0;
2051
},
2052
2053
2054
// -------------------------------------------------------
2055
// -- ALC Resources
2056
// -------------------------------------------------------
2057
2058
alcOpenDevice__proxy: 'sync',
2059
alcOpenDevice: (pDeviceName) => {
2060
if (pDeviceName) {
2061
var name = UTF8ToString(pDeviceName);
2062
if (name !== AL.DEVICE_NAME) {
2063
return 0;
2064
}
2065
}
2066
2067
if (typeof AudioContext != 'undefined' || typeof webkitAudioContext != 'undefined') {
2068
var deviceId = AL.newId();
2069
AL.deviceRefCounts[deviceId] = 0;
2070
return deviceId;
2071
}
2072
return 0;
2073
},
2074
2075
alcCloseDevice__proxy: 'sync',
2076
alcCloseDevice: (deviceId) => {
2077
if (!(deviceId in AL.deviceRefCounts) || AL.deviceRefCounts[deviceId] > 0) {
2078
return {{{ cDefs.ALC_FALSE }}};
2079
}
2080
2081
delete AL.deviceRefCounts[deviceId];
2082
AL.freeIds.push(deviceId);
2083
return {{{ cDefs.ALC_TRUE }}};
2084
},
2085
2086
alcCreateContext__deps: ['$autoResumeAudioContext'],
2087
alcCreateContext__proxy: 'sync',
2088
alcCreateContext: (deviceId, pAttrList) => {
2089
if (!(deviceId in AL.deviceRefCounts)) {
2090
#if OPENAL_DEBUG
2091
dbg('alcCreateContext() called with an invalid device');
2092
#endif
2093
AL.alcErr = 0xA001; /* ALC_INVALID_DEVICE */
2094
return 0;
2095
}
2096
2097
var options = null;
2098
var attrs = [];
2099
var hrtf = null;
2100
pAttrList >>= 2;
2101
if (pAttrList) {
2102
var attr = 0;
2103
var val = 0;
2104
while (true) {
2105
attr = HEAP32[pAttrList++];
2106
attrs.push(attr);
2107
if (attr === 0) {
2108
break;
2109
}
2110
val = HEAP32[pAttrList++];
2111
attrs.push(val);
2112
2113
switch (attr) {
2114
case 0x1007 /* ALC_FREQUENCY */:
2115
if (!options) {
2116
options = {};
2117
}
2118
2119
options.sampleRate = val;
2120
break;
2121
case 0x1010 /* ALC_MONO_SOURCES */: // fallthrough
2122
case 0x1011 /* ALC_STEREO_SOURCES */:
2123
// Do nothing; these hints are satisfied by default
2124
break
2125
case 0x1992 /* ALC_HRTF_SOFT */:
2126
switch (val) {
2127
case {{{ cDefs.ALC_FALSE }}}:
2128
hrtf = false;
2129
break;
2130
case {{{ cDefs.ALC_TRUE }}}:
2131
hrtf = true;
2132
break;
2133
case 2 /* ALC_DONT_CARE_SOFT */:
2134
break;
2135
default:
2136
#if OPENAL_DEBUG
2137
dbg(`Unsupported ALC_HRTF_SOFT mode ${val}`);
2138
#endif
2139
AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}};
2140
return 0;
2141
}
2142
break;
2143
case 0x1996 /* ALC_HRTF_ID_SOFT */:
2144
if (val !== 0) {
2145
#if OPENAL_DEBUG
2146
dbg(`Invalid ALC_HRTF_ID_SOFT index ${val}`);
2147
#endif
2148
AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}};
2149
return 0;
2150
}
2151
break;
2152
default:
2153
#if OPENAL_DEBUG
2154
dbg(`Unsupported context attribute ${ptrToString(attr)}`);
2155
#endif
2156
AL.alcErr = 0xA004; /* ALC_INVALID_VALUE */
2157
return 0;
2158
}
2159
}
2160
}
2161
2162
var AudioContext = window.AudioContext || window.webkitAudioContext;
2163
var ac = null;
2164
try {
2165
// Only try to pass options if there are any, for compat with browsers that don't support this
2166
if (options) {
2167
ac = new AudioContext(options);
2168
} else {
2169
ac = new AudioContext();
2170
}
2171
} catch (e) {
2172
if (e.name === 'NotSupportedError') {
2173
#if OPENAL_DEBUG
2174
dbg('Invalid or unsupported options');
2175
#endif
2176
AL.alcErr = 0xA004; /* ALC_INVALID_VALUE */
2177
} else {
2178
AL.alcErr = 0xA001; /* ALC_INVALID_DEVICE */
2179
}
2180
2181
return 0;
2182
}
2183
2184
autoResumeAudioContext(ac);
2185
2186
// Old Web Audio API (e.g. Safari 6.0.5) had an inconsistently named createGainNode function.
2187
if (typeof ac.createGain == 'undefined') {
2188
ac.createGain = ac.createGainNode;
2189
}
2190
2191
var gain = ac.createGain();
2192
gain.connect(ac.destination);
2193
var ctx = {
2194
deviceId,
2195
id: AL.newId(),
2196
attrs,
2197
audioCtx: ac,
2198
listener: {
2199
position: [0.0, 0.0, 0.0],
2200
velocity: [0.0, 0.0, 0.0],
2201
direction: [0.0, 0.0, 0.0],
2202
up: [0.0, 0.0, 0.0]
2203
},
2204
sources: [],
2205
interval: setInterval(() => AL.scheduleContextAudio(ctx), AL.QUEUE_INTERVAL),
2206
gain,
2207
distanceModel: 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */,
2208
speedOfSound: 343.3,
2209
dopplerFactor: 1.0,
2210
sourceDistanceModel: false,
2211
hrtf: hrtf || false,
2212
2213
_err: 0,
2214
get err() {
2215
return this._err;
2216
},
2217
set err(val) {
2218
// Errors should not be overwritten by later errors until they are cleared by a query.
2219
if (this._err === {{{ cDefs.AL_NO_ERROR }}} || val === {{{ cDefs.AL_NO_ERROR }}}) {
2220
this._err = val;
2221
}
2222
}
2223
};
2224
AL.deviceRefCounts[deviceId]++;
2225
AL.contexts[ctx.id] = ctx;
2226
2227
if (hrtf !== null) {
2228
// Apply hrtf attrib to all contexts for this device
2229
for (var ctxId in AL.contexts) {
2230
var c = AL.contexts[ctxId];
2231
if (c.deviceId === deviceId) {
2232
c.hrtf = hrtf;
2233
AL.updateContextGlobal(c);
2234
}
2235
}
2236
}
2237
2238
return ctx.id;
2239
},
2240
2241
alcDestroyContext__proxy: 'sync',
2242
alcDestroyContext: (contextId) => {
2243
var ctx = AL.contexts[contextId];
2244
if (AL.currentCtx === ctx) {
2245
#if OPENAL_DEBUG
2246
dbg('alcDestroyContext() called with an invalid context');
2247
#endif
2248
AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */;
2249
return;
2250
}
2251
2252
// Stop playback, etc
2253
if (AL.contexts[contextId].interval) {
2254
clearInterval(AL.contexts[contextId].interval);
2255
}
2256
AL.deviceRefCounts[ctx.deviceId]--;
2257
delete AL.contexts[contextId];
2258
AL.freeIds.push(contextId);
2259
},
2260
2261
// -------------------------------------------------------
2262
// -- ALC State
2263
// -------------------------------------------------------
2264
2265
alcGetError__proxy: 'sync',
2266
alcGetError: (deviceId) => {
2267
var err = AL.alcErr;
2268
AL.alcErr = {{{ cDefs.ALC_NO_ERROR }}};
2269
return err;
2270
},
2271
2272
alcGetCurrentContext__proxy: 'sync',
2273
alcGetCurrentContext: () => {
2274
if (AL.currentCtx !== null) {
2275
return AL.currentCtx.id;
2276
}
2277
return 0;
2278
},
2279
2280
alcMakeContextCurrent__proxy: 'sync',
2281
alcMakeContextCurrent: (contextId) => {
2282
if (contextId === 0) {
2283
AL.currentCtx = null;
2284
} else {
2285
AL.currentCtx = AL.contexts[contextId];
2286
}
2287
return {{{ cDefs.ALC_TRUE }}};
2288
},
2289
2290
alcGetContextsDevice__proxy: 'sync',
2291
alcGetContextsDevice: (contextId) => {
2292
if (contextId in AL.contexts) {
2293
return AL.contexts[contextId].deviceId;
2294
}
2295
return 0;
2296
},
2297
2298
// The spec is vague about what these are actually supposed to do, and NOP is a reasonable implementation
2299
alcProcessContext: (contextId) => {},
2300
alcSuspendContext: (contextId) => {},
2301
2302
alcIsExtensionPresent__proxy: 'sync',
2303
alcIsExtensionPresent: (deviceId, pExtName) => {
2304
var name = UTF8ToString(pExtName);
2305
2306
return AL.ALC_EXTENSIONS[name] ? 1 : 0;
2307
},
2308
2309
alcGetEnumValue__proxy: 'sync',
2310
alcGetEnumValue: (deviceId, pEnumName) => {
2311
// Spec says :
2312
// Using a NULL handle is legal, but only the
2313
// tokens defined by the AL core are guaranteed.
2314
if (deviceId !== 0 && !(deviceId in AL.deviceRefCounts)) {
2315
#if OPENAL_DEBUG
2316
dbg('alcGetEnumValue() called with an invalid device');
2317
#endif
2318
// ALC_INVALID_DEVICE is not listed as a possible error state for
2319
// this function, sadly.
2320
return 0;
2321
} else if (!pEnumName) {
2322
AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}};
2323
return 0;
2324
}
2325
var name = UTF8ToString(pEnumName);
2326
// See alGetEnumValue(), but basically behave the same as OpenAL-Soft
2327
switch (name) {
2328
case 'ALC_NO_ERROR': return 0;
2329
case 'ALC_INVALID_DEVICE': return 0xA001;
2330
case 'ALC_INVALID_CONTEXT': return 0xA002;
2331
case 'ALC_INVALID_ENUM': return 0xA003;
2332
case 'ALC_INVALID_VALUE': return 0xA004;
2333
case 'ALC_OUT_OF_MEMORY': return 0xA005;
2334
case 'ALC_MAJOR_VERSION': return 0x1000;
2335
case 'ALC_MINOR_VERSION': return 0x1001;
2336
case 'ALC_ATTRIBUTES_SIZE': return 0x1002;
2337
case 'ALC_ALL_ATTRIBUTES': return 0x1003;
2338
case 'ALC_DEFAULT_DEVICE_SPECIFIER': return 0x1004;
2339
case 'ALC_DEVICE_SPECIFIER': return 0x1005;
2340
case 'ALC_EXTENSIONS': return 0x1006;
2341
case 'ALC_FREQUENCY': return 0x1007;
2342
case 'ALC_REFRESH': return 0x1008;
2343
case 'ALC_SYNC': return 0x1009;
2344
case 'ALC_MONO_SOURCES': return 0x1010;
2345
case 'ALC_STEREO_SOURCES': return 0x1011;
2346
case 'ALC_CAPTURE_DEVICE_SPECIFIER': return 0x310;
2347
case 'ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER': return 0x311;
2348
case 'ALC_CAPTURE_SAMPLES': return 0x312;
2349
2350
/* Extensions */
2351
case 'ALC_HRTF_SOFT': return 0x1992;
2352
case 'ALC_HRTF_ID_SOFT': return 0x1996;
2353
case 'ALC_DONT_CARE_SOFT': return 0x0002;
2354
case 'ALC_HRTF_STATUS_SOFT': return 0x1993;
2355
case 'ALC_NUM_HRTF_SPECIFIERS_SOFT': return 0x1994;
2356
case 'ALC_HRTF_SPECIFIER_SOFT': return 0x1995;
2357
case 'ALC_HRTF_DISABLED_SOFT': return 0x0000;
2358
case 'ALC_HRTF_ENABLED_SOFT': return 0x0001;
2359
case 'ALC_HRTF_DENIED_SOFT': return 0x0002;
2360
case 'ALC_HRTF_REQUIRED_SOFT': return 0x0003;
2361
case 'ALC_HRTF_HEADPHONES_DETECTED_SOFT': return 0x0004;
2362
case 'ALC_HRTF_UNSUPPORTED_FORMAT_SOFT': return 0x0005;
2363
2364
default:
2365
#if OPENAL_DEBUG
2366
dbg(`No value for `${pEnumName}` is known by alcGetEnumValue()`);
2367
#endif
2368
AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}};
2369
return {{{ cDefs.AL_NONE }}};
2370
}
2371
},
2372
2373
alcGetString__proxy: 'sync',
2374
alcGetString__deps: ['$stringToNewUTF8'],
2375
alcGetString: (deviceId, param) => {
2376
if (AL.alcStringCache[param]) {
2377
return AL.alcStringCache[param];
2378
}
2379
2380
var ret;
2381
switch (param) {
2382
case {{{ cDefs.ALC_NO_ERROR }}}:
2383
ret = 'No Error';
2384
break;
2385
case {{{ cDefs.ALC_INVALID_DEVICE }}}:
2386
ret = 'Invalid Device';
2387
break;
2388
case 0xA002 /* ALC_INVALID_CONTEXT */:
2389
ret = 'Invalid Context';
2390
break;
2391
case {{{ cDefs.ALC_INVALID_ENUM }}}:
2392
ret = 'Invalid Enum';
2393
break;
2394
case {{{ cDefs.ALC_INVALID_VALUE }}}:
2395
ret = 'Invalid Value';
2396
break;
2397
case 0xA005 /* ALC_OUT_OF_MEMORY */:
2398
ret = 'Out of Memory';
2399
break;
2400
case 0x1004 /* ALC_DEFAULT_DEVICE_SPECIFIER */:
2401
if (typeof AudioContext != 'undefined' ||
2402
typeof webkitAudioContext != 'undefined') {
2403
ret = AL.DEVICE_NAME;
2404
} else {
2405
return 0;
2406
}
2407
break;
2408
case 0x1005 /* ALC_DEVICE_SPECIFIER */:
2409
if (typeof AudioContext != 'undefined' ||
2410
typeof webkitAudioContext != 'undefined') {
2411
ret = AL.DEVICE_NAME + '\0';
2412
} else {
2413
ret = '\0';
2414
}
2415
break;
2416
case 0x311 /* ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER */:
2417
ret = AL.CAPTURE_DEVICE_NAME;
2418
break;
2419
case 0x310 /* ALC_CAPTURE_DEVICE_SPECIFIER */:
2420
if (deviceId === 0) {
2421
ret = AL.CAPTURE_DEVICE_NAME + '\0';
2422
} else {
2423
var c = AL.requireValidCaptureDevice(deviceId, 'alcGetString');
2424
if (!c) {
2425
return 0;
2426
}
2427
ret = c.deviceName;
2428
}
2429
break;
2430
case 0x1006 /* ALC_EXTENSIONS */:
2431
if (!deviceId) {
2432
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2433
return 0;
2434
}
2435
2436
ret = Object.keys(AL.ALC_EXTENSIONS).join(' ')
2437
break;
2438
default:
2439
AL.alcErr = {{{ cDefs.ALC_INVALID_ENUM }}};
2440
return 0;
2441
}
2442
2443
ret = stringToNewUTF8(ret);
2444
AL.alcStringCache[param] = ret;
2445
return ret;
2446
},
2447
2448
alcGetIntegerv__proxy: 'sync',
2449
alcGetIntegerv: (deviceId, param, size, pValues) => {
2450
if (size === 0 || !pValues) {
2451
// Ignore the query, per the spec
2452
return;
2453
}
2454
2455
switch (param) {
2456
case 0x1000 /* ALC_MAJOR_VERSION */:
2457
{{{ makeSetValue('pValues', '0', '1', 'i32') }}};
2458
break;
2459
case 0x1001 /* ALC_MINOR_VERSION */:
2460
{{{ makeSetValue('pValues', '0', '1', 'i32') }}};
2461
break;
2462
case 0x1002 /* ALC_ATTRIBUTES_SIZE */:
2463
if (!(deviceId in AL.deviceRefCounts)) {
2464
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2465
return;
2466
}
2467
if (!AL.currentCtx) {
2468
AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */;
2469
return;
2470
}
2471
2472
{{{ makeSetValue('pValues', '0', 'AL.currentCtx.attrs.length', 'i32') }}};
2473
break;
2474
case 0x1003 /* ALC_ALL_ATTRIBUTES */:
2475
if (!(deviceId in AL.deviceRefCounts)) {
2476
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2477
return;
2478
}
2479
if (!AL.currentCtx) {
2480
AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */;
2481
return;
2482
}
2483
2484
for (var i = 0; i < AL.currentCtx.attrs.length; i++) {
2485
{{{ makeSetValue('pValues', 'i*4', 'AL.currentCtx.attrs[i]', 'i32') }}};
2486
}
2487
break;
2488
case 0x1007 /* ALC_FREQUENCY */:
2489
if (!(deviceId in AL.deviceRefCounts)) {
2490
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2491
return;
2492
}
2493
if (!AL.currentCtx) {
2494
AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */;
2495
return;
2496
}
2497
2498
{{{ makeSetValue('pValues', '0', 'AL.currentCtx.audioCtx.sampleRate', 'i32') }}};
2499
break;
2500
case 0x1010 /* ALC_MONO_SOURCES */:
2501
case 0x1011 /* ALC_STEREO_SOURCES */:
2502
if (!(deviceId in AL.deviceRefCounts)) {
2503
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2504
return;
2505
}
2506
if (!AL.currentCtx) {
2507
AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */;
2508
return;
2509
}
2510
2511
{{{ makeSetValue('pValues', '0', '0x7FFFFFFF', 'i32') }}};
2512
break;
2513
case 0x1992 /* ALC_HRTF_SOFT */:
2514
case 0x1993 /* ALC_HRTF_STATUS_SOFT */:
2515
if (!(deviceId in AL.deviceRefCounts)) {
2516
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2517
return;
2518
}
2519
2520
var hrtfStatus = 0 /* ALC_HRTF_DISABLED_SOFT */;
2521
for (var ctxId in AL.contexts) {
2522
var ctx = AL.contexts[ctxId];
2523
if (ctx.deviceId === deviceId) {
2524
hrtfStatus = ctx.hrtf ? 1 /* ALC_HRTF_ENABLED_SOFT */ : 0 /* ALC_HRTF_DISABLED_SOFT */;
2525
}
2526
}
2527
{{{ makeSetValue('pValues', '0', 'hrtfStatus', 'i32') }}};
2528
break;
2529
case 0x1994 /* ALC_NUM_HRTF_SPECIFIERS_SOFT */:
2530
if (!(deviceId in AL.deviceRefCounts)) {
2531
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2532
return;
2533
}
2534
{{{ makeSetValue('pValues', '0', '1', 'i32') }}};
2535
break;
2536
case 0x20003 /* ALC_MAX_AUXILIARY_SENDS */:
2537
if (!(deviceId in AL.deviceRefCounts)) {
2538
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2539
return;
2540
}
2541
if (!AL.currentCtx) {
2542
AL.alcErr = 0xA002 /* ALC_INVALID_CONTEXT */;
2543
return;
2544
}
2545
2546
{{{ makeSetValue('pValues', '0', '1', 'i32') }}};
2547
case 0x312 /* ALC_CAPTURE_SAMPLES */:
2548
var c = AL.requireValidCaptureDevice(deviceId, 'alcGetIntegerv');
2549
if (!c) {
2550
return;
2551
}
2552
var n = c.capturedFrameCount;
2553
var dstfreq = c.requestedSampleRate;
2554
var srcfreq = c.audioCtx.sampleRate;
2555
var nsamples = Math.floor(n * (dstfreq/srcfreq));
2556
{{{ makeSetValue('pValues', '0', 'nsamples', 'i32') }}};
2557
break;
2558
default:
2559
#if OPENAL_DEBUG
2560
dbg(`alcGetIntegerv() with param ${ptrToString(param)} not implemented yet`);
2561
#endif
2562
AL.alcErr = {{{ cDefs.ALC_INVALID_ENUM }}};
2563
return;
2564
}
2565
},
2566
2567
emscripten_alcDevicePauseSOFT__proxy: 'sync',
2568
emscripten_alcDevicePauseSOFT__sig: 'vi',
2569
emscripten_alcDevicePauseSOFT: (deviceId) => {
2570
if (!(deviceId in AL.deviceRefCounts)) {
2571
#if OPENAL_DEBUG
2572
dbg('alcDevicePauseSOFT() called with an invalid device');
2573
#endif
2574
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2575
return;
2576
}
2577
2578
if (AL.paused) {
2579
return;
2580
}
2581
AL.paused = true;
2582
2583
for (var ctxId in AL.contexts) {
2584
var ctx = AL.contexts[ctxId];
2585
if (ctx.deviceId !== deviceId) {
2586
continue;
2587
}
2588
2589
ctx.audioCtx.suspend();
2590
clearInterval(ctx.interval);
2591
ctx.interval = null;
2592
}
2593
},
2594
2595
emscripten_alcDeviceResumeSOFT__proxy: 'sync',
2596
emscripten_alcDeviceResumeSOFT__sig: 'vi',
2597
emscripten_alcDeviceResumeSOFT: (deviceId) => {
2598
if (!(deviceId in AL.deviceRefCounts)) {
2599
#if OPENAL_DEBUG
2600
dbg('alcDeviceResumeSOFT() called with an invalid device');
2601
#endif
2602
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2603
return;
2604
}
2605
2606
if (!AL.paused) {
2607
return;
2608
}
2609
AL.paused = false;
2610
2611
for (var ctxId in AL.contexts) {
2612
var ctx = AL.contexts[ctxId];
2613
if (ctx.deviceId !== deviceId) {
2614
continue;
2615
}
2616
2617
ctx.interval = setInterval(() => AL.scheduleContextAudio(ctx), AL.QUEUE_INTERVAL);
2618
ctx.audioCtx.resume();
2619
}
2620
},
2621
2622
emscripten_alcGetStringiSOFT__proxy: 'sync',
2623
emscripten_alcGetStringiSOFT__sig: 'iiii',
2624
emscripten_alcGetStringiSOFT__deps: ['alcGetString', '$stringToNewUTF8'],
2625
emscripten_alcGetStringiSOFT: (deviceId, param, index) => {
2626
if (!(deviceId in AL.deviceRefCounts)) {
2627
#if OPENAL_DEBUG
2628
dbg('alcGetStringiSOFT() called with an invalid device');
2629
#endif
2630
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2631
return 0;
2632
}
2633
2634
if (AL.alcStringCache[param]) {
2635
return AL.alcStringCache[param];
2636
}
2637
2638
var ret;
2639
switch (param) {
2640
case 0x1995 /* ALC_HRTF_SPECIFIER_SOFT */:
2641
if (index === 0) {
2642
ret = 'Web Audio HRTF';
2643
} else {
2644
#if OPENAL_DEBUG
2645
dbg(`alcGetStringiSOFT() with param ALC_HRTF_SPECIFIER_SOFT index ${index} is out of range`);
2646
#endif
2647
AL.alcErr = {{{ cDefs.ALC_INVALID_VALUE }}};
2648
return 0;
2649
}
2650
break;
2651
default:
2652
if (index !== 0) {
2653
#if OPENAL_DEBUG
2654
dbg(`alcGetStringiSOFT() with param ${ptrToString(param)} not implemented yet`);
2655
#endif
2656
AL.alcErr = {{{ cDefs.ALC_INVALID_ENUM }}};
2657
return 0;
2658
}
2659
return _alcGetString(deviceId, param);
2660
}
2661
2662
ret = stringToNewUTF8(ret);
2663
AL.alcStringCache[param] = ret;
2664
return ret;
2665
},
2666
2667
emscripten_alcResetDeviceSOFT__proxy: 'sync',
2668
emscripten_alcResetDeviceSOFT__sig: 'iii',
2669
emscripten_alcResetDeviceSOFT: (deviceId, pAttrList) => {
2670
if (!(deviceId in AL.deviceRefCounts)) {
2671
#if OPENAL_DEBUG
2672
dbg('alcResetDeviceSOFT() called with an invalid device');
2673
#endif
2674
AL.alcErr = {{{ cDefs.ALC_INVALID_DEVICE }}};
2675
return {{{ cDefs.ALC_FALSE }}};
2676
}
2677
2678
var hrtf = null;
2679
pAttrList >>= 2;
2680
if (pAttrList) {
2681
var attr = 0;
2682
var val = 0;
2683
while (true) {
2684
attr = HEAP32[pAttrList++];
2685
if (attr === 0) {
2686
break;
2687
}
2688
val = HEAP32[pAttrList++];
2689
2690
switch (attr) {
2691
case 0x1992 /* ALC_HRTF_SOFT */:
2692
if (val === {{{ cDefs.ALC_TRUE }}}) {
2693
hrtf = true;
2694
} else if (val === {{{ cDefs.ALC_FALSE }}}) {
2695
hrtf = false;
2696
}
2697
break;
2698
}
2699
}
2700
}
2701
2702
if (hrtf !== null) {
2703
// Apply hrtf attrib to all contexts for this device
2704
for (var ctxId in AL.contexts) {
2705
var ctx = AL.contexts[ctxId];
2706
if (ctx.deviceId === deviceId) {
2707
ctx.hrtf = hrtf;
2708
AL.updateContextGlobal(ctx);
2709
}
2710
}
2711
}
2712
2713
return {{{ cDefs.ALC_TRUE }}};
2714
},
2715
2716
// ***************************************************************************
2717
// ** AL API
2718
// ***************************************************************************
2719
2720
// -------------------------------------------------------
2721
// -- AL Resources
2722
// -------------------------------------------------------
2723
2724
alGenBuffers__proxy: 'sync',
2725
alGenBuffers: (count, pBufferIds) => {
2726
if (!AL.currentCtx) {
2727
#if OPENAL_DEBUG
2728
dbg('alGenBuffers() called without a valid context');
2729
#endif
2730
return;
2731
}
2732
2733
for (var i = 0; i < count; ++i) {
2734
var buf = {
2735
deviceId: AL.currentCtx.deviceId,
2736
id: AL.newId(),
2737
refCount: 0,
2738
audioBuf: null,
2739
frequency: 0,
2740
bytesPerSample: 2,
2741
channels: 1,
2742
length: 0,
2743
};
2744
AL.deviceRefCounts[buf.deviceId]++;
2745
AL.buffers[buf.id] = buf;
2746
{{{ makeSetValue('pBufferIds', 'i*4', 'buf.id', 'i32') }}};
2747
}
2748
},
2749
2750
alDeleteBuffers__proxy: 'sync',
2751
alDeleteBuffers: (count, pBufferIds) => {
2752
if (!AL.currentCtx) {
2753
#if OPENAL_DEBUG
2754
dbg('alDeleteBuffers() called without a valid context');
2755
#endif
2756
return;
2757
}
2758
2759
for (var i = 0; i < count; ++i) {
2760
var bufId = {{{ makeGetValue('pBufferIds', 'i*4', 'i32') }}};
2761
/// Deleting the zero buffer is a legal NOP, so ignore it
2762
if (bufId === 0) {
2763
continue;
2764
}
2765
2766
// Make sure the buffer index is valid.
2767
if (!AL.buffers[bufId]) {
2768
#if OPENAL_DEBUG
2769
dbg('alDeleteBuffers() called with an invalid buffer');
2770
#endif
2771
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
2772
return;
2773
}
2774
2775
// Make sure the buffer is no longer in use.
2776
if (AL.buffers[bufId].refCount) {
2777
#if OPENAL_DEBUG
2778
dbg('alDeleteBuffers() called with a used buffer');
2779
#endif
2780
AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}};
2781
return;
2782
}
2783
}
2784
2785
for (var i = 0; i < count; ++i) {
2786
var bufId = {{{ makeGetValue('pBufferIds', 'i*4', 'i32') }}};
2787
if (bufId === 0) {
2788
continue;
2789
}
2790
2791
AL.deviceRefCounts[AL.buffers[bufId].deviceId]--;
2792
delete AL.buffers[bufId];
2793
AL.freeIds.push(bufId);
2794
}
2795
},
2796
2797
alGenSources__proxy: 'sync',
2798
alGenSources: (count, pSourceIds) => {
2799
if (!AL.currentCtx) {
2800
#if OPENAL_DEBUG
2801
dbg('alGenSources() called without a valid context');
2802
#endif
2803
return;
2804
}
2805
for (var i = 0; i < count; ++i) {
2806
var gain = AL.currentCtx.audioCtx.createGain();
2807
gain.connect(AL.currentCtx.gain);
2808
var src = {
2809
context: AL.currentCtx,
2810
id: AL.newId(),
2811
type: 0x1030 /* AL_UNDETERMINED */,
2812
state: {{{ cDefs.AL_INITIAL }}},
2813
bufQueue: [AL.buffers[0]],
2814
audioQueue: [],
2815
looping: false,
2816
pitch: 1.0,
2817
dopplerShift: 1.0,
2818
gain,
2819
minGain: 0.0,
2820
maxGain: 1.0,
2821
panner: null,
2822
bufsProcessed: 0,
2823
bufStartTime: Number.NEGATIVE_INFINITY,
2824
bufOffset: 0.0,
2825
relative: false,
2826
refDistance: 1.0,
2827
maxDistance: 3.40282e38 /* FLT_MAX */,
2828
rolloffFactor: 1.0,
2829
position: [0.0, 0.0, 0.0],
2830
velocity: [0.0, 0.0, 0.0],
2831
direction: [0.0, 0.0, 0.0],
2832
coneOuterGain: 0.0,
2833
coneInnerAngle: 360.0,
2834
coneOuterAngle: 360.0,
2835
distanceModel: 0xd002 /* AL_INVERSE_DISTANCE_CLAMPED */,
2836
spatialize: 2 /* AL_AUTO_SOFT */,
2837
2838
get playbackRate() {
2839
return this.pitch * this.dopplerShift;
2840
}
2841
};
2842
AL.currentCtx.sources[src.id] = src;
2843
{{{ makeSetValue('pSourceIds', 'i*4', 'src.id', 'i32') }}};
2844
}
2845
},
2846
2847
alDeleteSources__deps: ['alSourcei'],
2848
alDeleteSources__proxy: 'sync',
2849
alDeleteSources: (count, pSourceIds) => {
2850
if (!AL.currentCtx) {
2851
#if OPENAL_DEBUG
2852
dbg('alDeleteSources() called without a valid context');
2853
#endif
2854
return;
2855
}
2856
2857
for (var i = 0; i < count; ++i) {
2858
var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}};
2859
if (!AL.currentCtx.sources[srcId]) {
2860
#if OPENAL_DEBUG
2861
dbg('alDeleteSources() called with an invalid source');
2862
#endif
2863
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
2864
return;
2865
}
2866
}
2867
2868
for (var i = 0; i < count; ++i) {
2869
var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}};
2870
AL.setSourceState(AL.currentCtx.sources[srcId], {{{ cDefs.AL_STOPPED }}});
2871
_alSourcei(srcId, 0x1009 /* AL_BUFFER */, 0);
2872
delete AL.currentCtx.sources[srcId];
2873
AL.freeIds.push(srcId);
2874
}
2875
},
2876
2877
// -------------------------------------------------------
2878
// --- AL Context State
2879
// -------------------------------------------------------
2880
2881
alGetError__proxy: 'sync',
2882
alGetError: () => {
2883
if (!AL.currentCtx) {
2884
return {{{ cDefs.AL_INVALID_OPERATION }}};
2885
}
2886
// Reset error on get.
2887
var err = AL.currentCtx.err;
2888
AL.currentCtx.err = {{{ cDefs.AL_NO_ERROR }}};
2889
return err;
2890
},
2891
2892
alIsExtensionPresent__proxy: 'sync',
2893
alIsExtensionPresent: (pExtName) => {
2894
var name = UTF8ToString(pExtName);
2895
2896
return AL.AL_EXTENSIONS[name] ? 1 : 0;
2897
},
2898
2899
alGetEnumValue__proxy: 'sync',
2900
alGetEnumValue: (pEnumName) => {
2901
if (!AL.currentCtx) {
2902
#if OPENAL_DEBUG
2903
dbg('alGetEnumValue() called without a valid context');
2904
#endif
2905
return 0;
2906
}
2907
2908
if (!pEnumName) {
2909
#if OPENAL_DEBUG
2910
dbg('alGetEnumValue() called with null pointer');
2911
#endif
2912
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
2913
return {{{ cDefs.AL_NONE }}};
2914
}
2915
var name = UTF8ToString(pEnumName);
2916
2917
switch (name) {
2918
// Spec doesn't clearly state that alGetEnumValue() is required to
2919
// support _only_ extension tokens.
2920
// We should probably follow OpenAL-Soft's example and support all
2921
// of the names we know.
2922
// See http://repo.or.cz/openal-soft.git/blob/HEAD:/Alc/ALc.c
2923
case 'AL_BITS': return 0x2002;
2924
case 'AL_BUFFER': return 0x1009;
2925
case 'AL_BUFFERS_PROCESSED': return 0x1016;
2926
case 'AL_BUFFERS_QUEUED': return 0x1015;
2927
case 'AL_BYTE_OFFSET': return 0x1026;
2928
case 'AL_CHANNELS': return 0x2003;
2929
case 'AL_CONE_INNER_ANGLE': return 0x1001;
2930
case 'AL_CONE_OUTER_ANGLE': return 0x1002;
2931
case 'AL_CONE_OUTER_GAIN': return 0x1022;
2932
case 'AL_DIRECTION': return 0x1005;
2933
case 'AL_DISTANCE_MODEL': return 0xD000;
2934
case 'AL_DOPPLER_FACTOR': return 0xC000;
2935
case 'AL_DOPPLER_VELOCITY': return 0xC001;
2936
case 'AL_EXPONENT_DISTANCE': return 0xD005;
2937
case 'AL_EXPONENT_DISTANCE_CLAMPED': return 0xD006;
2938
case 'AL_EXTENSIONS': return 0xB004;
2939
case 'AL_FORMAT_MONO16': return 0x1101;
2940
case 'AL_FORMAT_MONO8': return 0x1100;
2941
case 'AL_FORMAT_STEREO16': return 0x1103;
2942
case 'AL_FORMAT_STEREO8': return 0x1102;
2943
case 'AL_FREQUENCY': return 0x2001;
2944
case 'AL_GAIN': return 0x100A;
2945
case 'AL_INITIAL': return 0x1011;
2946
case 'AL_INVALID': return -1;
2947
case 'AL_ILLEGAL_ENUM': // fallthrough
2948
case 'AL_INVALID_ENUM': return 0xA002;
2949
case 'AL_INVALID_NAME': return 0xA001;
2950
case 'AL_ILLEGAL_COMMAND': // fallthrough
2951
case 'AL_INVALID_OPERATION': return 0xA004;
2952
case 'AL_INVALID_VALUE': return 0xA003;
2953
case 'AL_INVERSE_DISTANCE': return 0xD001;
2954
case 'AL_INVERSE_DISTANCE_CLAMPED': return 0xD002;
2955
case 'AL_LINEAR_DISTANCE': return 0xD003;
2956
case 'AL_LINEAR_DISTANCE_CLAMPED': return 0xD004;
2957
case 'AL_LOOPING': return 0x1007;
2958
case 'AL_MAX_DISTANCE': return 0x1023;
2959
case 'AL_MAX_GAIN': return 0x100E;
2960
case 'AL_MIN_GAIN': return 0x100D;
2961
case 'AL_NONE': return 0;
2962
case 'AL_NO_ERROR': return 0;
2963
case 'AL_ORIENTATION': return 0x100F;
2964
case 'AL_OUT_OF_MEMORY': return 0xA005;
2965
case 'AL_PAUSED': return 0x1013;
2966
case 'AL_PENDING': return 0x2011;
2967
case 'AL_PITCH': return 0x1003;
2968
case 'AL_PLAYING': return 0x1012;
2969
case 'AL_POSITION': return 0x1004;
2970
case 'AL_PROCESSED': return 0x2012;
2971
case 'AL_REFERENCE_DISTANCE': return 0x1020;
2972
case 'AL_RENDERER': return 0xB003;
2973
case 'AL_ROLLOFF_FACTOR': return 0x1021;
2974
case 'AL_SAMPLE_OFFSET': return 0x1025;
2975
case 'AL_SEC_OFFSET': return 0x1024;
2976
case 'AL_SIZE': return 0x2004;
2977
case 'AL_SOURCE_RELATIVE': return 0x202;
2978
case 'AL_SOURCE_STATE': return 0x1010;
2979
case 'AL_SOURCE_TYPE': return 0x1027;
2980
case 'AL_SPEED_OF_SOUND': return 0xC003;
2981
case 'AL_STATIC': return 0x1028;
2982
case 'AL_STOPPED': return 0x1014;
2983
case 'AL_STREAMING': return 0x1029;
2984
case 'AL_UNDETERMINED': return 0x1030;
2985
case 'AL_UNUSED': return 0x2010;
2986
case 'AL_VELOCITY': return 0x1006;
2987
case 'AL_VENDOR': return 0xB001;
2988
case 'AL_VERSION': return 0xB002;
2989
2990
/* Extensions */
2991
case 'AL_AUTO_SOFT': return 0x0002;
2992
case 'AL_SOURCE_DISTANCE_MODEL': return 0x200;
2993
case 'AL_SOURCE_SPATIALIZE_SOFT': return 0x1214;
2994
case 'AL_LOOP_POINTS_SOFT': return 0x2015;
2995
case 'AL_BYTE_LENGTH_SOFT': return 0x2009;
2996
case 'AL_SAMPLE_LENGTH_SOFT': return 0x200A;
2997
case 'AL_SEC_LENGTH_SOFT': return 0x200B;
2998
case 'AL_FORMAT_MONO_FLOAT32': return 0x10010;
2999
case 'AL_FORMAT_STEREO_FLOAT32': return 0x10011;
3000
3001
default:
3002
#if OPENAL_DEBUG
3003
dbg(`No value for `${name}` is known by alGetEnumValue()`);
3004
#endif
3005
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3006
return 0;
3007
}
3008
},
3009
3010
alGetString__proxy: 'sync',
3011
alGetString__deps: ['$stringToNewUTF8'],
3012
alGetString: (param) => {
3013
if (AL.stringCache[param]) {
3014
return AL.stringCache[param];
3015
}
3016
3017
var ret;
3018
switch (param) {
3019
case {{{ cDefs.AL_NO_ERROR }}}:
3020
ret = 'No Error';
3021
break;
3022
case {{{ cDefs.AL_INVALID_NAME }}}:
3023
ret = 'Invalid Name';
3024
break;
3025
case {{{ cDefs.AL_INVALID_ENUM }}}:
3026
ret = 'Invalid Enum';
3027
break;
3028
case {{{ cDefs.AL_INVALID_VALUE }}}:
3029
ret = 'Invalid Value';
3030
break;
3031
case {{{ cDefs.AL_INVALID_OPERATION }}}:
3032
ret = 'Invalid Operation';
3033
break;
3034
case 0xA005 /* AL_OUT_OF_MEMORY */:
3035
ret = 'Out of Memory';
3036
break;
3037
case 0xB001 /* AL_VENDOR */:
3038
ret = 'Emscripten';
3039
break;
3040
case 0xB002 /* AL_VERSION */:
3041
ret = '1.1';
3042
break;
3043
case 0xB003 /* AL_RENDERER */:
3044
ret = 'WebAudio';
3045
break;
3046
case 0xB004 /* AL_EXTENSIONS */:
3047
ret = Object.keys(AL.AL_EXTENSIONS).join(' ');
3048
break;
3049
default:
3050
if (AL.currentCtx) {
3051
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3052
} else {
3053
#if OPENAL_DEBUG
3054
dbg('alGetString() called without a valid context');
3055
#endif
3056
}
3057
return 0;
3058
}
3059
3060
ret = stringToNewUTF8(ret);
3061
AL.stringCache[param] = ret;
3062
return ret;
3063
},
3064
3065
alEnable__proxy: 'sync',
3066
alEnable: (param) => {
3067
if (!AL.currentCtx) {
3068
#if OPENAL_DEBUG
3069
dbg('alEnable() called without a valid context');
3070
#endif
3071
return;
3072
}
3073
switch (param) {
3074
case 0x200 /* AL_SOURCE_DISTANCE_MODEL */:
3075
AL.currentCtx.sourceDistanceModel = true;
3076
AL.updateContextGlobal(AL.currentCtx);
3077
break;
3078
default:
3079
#if OPENAL_DEBUG
3080
dbg(`alEnable() with param ${ptrToString(param)} not implemented yet`);
3081
#endif
3082
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3083
return;
3084
}
3085
},
3086
3087
alDisable__proxy: 'sync',
3088
alDisable: (param) => {
3089
if (!AL.currentCtx) {
3090
#if OPENAL_DEBUG
3091
dbg('alDisable() called without a valid context');
3092
#endif
3093
return;
3094
}
3095
switch (param) {
3096
case 0x200 /* AL_SOURCE_DISTANCE_MODEL */:
3097
AL.currentCtx.sourceDistanceModel = false;
3098
AL.updateContextGlobal(AL.currentCtx);
3099
break;
3100
default:
3101
#if OPENAL_DEBUG
3102
dbg(`alDisable() with param ${ptrToString(param)} not implemented yet`);
3103
#endif
3104
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3105
return;
3106
}
3107
},
3108
3109
alIsEnabled__proxy: 'sync',
3110
alIsEnabled: (param) => {
3111
if (!AL.currentCtx) {
3112
#if OPENAL_DEBUG
3113
dbg('alIsEnabled() called without a valid context');
3114
#endif
3115
return 0;
3116
}
3117
switch (param) {
3118
case 0x200 /* AL_SOURCE_DISTANCE_MODEL */:
3119
return AL.currentCtx.sourceDistanceModel ? {{{ cDefs.AL_FALSE }}} : {{{ cDefs.AL_TRUE }}};
3120
default:
3121
#if OPENAL_DEBUG
3122
dbg(`alIsEnabled() with param ${ptrToString(param)} not implemented yet`);
3123
#endif
3124
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3125
return 0;
3126
}
3127
},
3128
3129
alGetDouble__proxy: 'sync',
3130
alGetDouble: (param) => {
3131
var val = AL.getGlobalParam('alGetDouble', param);
3132
if (val === null) {
3133
return 0.0;
3134
}
3135
3136
switch (param) {
3137
case {{{ cDefs.AL_DOPPLER_FACTOR }}}:
3138
case {{{ cDefs.AL_SPEED_OF_SOUND }}}:
3139
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
3140
return val;
3141
default:
3142
#if OPENAL_DEBUG
3143
dbg(`alGetDouble(): param ${ptrToString(param)} has wrong signature`);
3144
#endif
3145
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3146
return 0.0;
3147
}
3148
},
3149
3150
alGetDoublev__proxy: 'sync',
3151
alGetDoublev: (param, pValues) => {
3152
var val = AL.getGlobalParam('alGetDoublev', param);
3153
// Silently ignore null destinations, as per the spec for global state functions
3154
if (val === null || !pValues) {
3155
return;
3156
}
3157
3158
switch (param) {
3159
case {{{ cDefs.AL_DOPPLER_FACTOR }}}:
3160
case {{{ cDefs.AL_SPEED_OF_SOUND }}}:
3161
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
3162
{{{ makeSetValue('pValues', '0', 'val', 'double') }}};
3163
break;
3164
default:
3165
#if OPENAL_DEBUG
3166
dbg(`alGetDoublev(): param ${ptrToString(param)} has wrong signature`);
3167
#endif
3168
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3169
return;
3170
}
3171
},
3172
3173
alGetFloat__proxy: 'sync',
3174
alGetFloat: (param) => {
3175
var val = AL.getGlobalParam('alGetFloat', param);
3176
if (val === null) {
3177
return 0.0;
3178
}
3179
3180
switch (param) {
3181
case {{{ cDefs.AL_DOPPLER_FACTOR }}}:
3182
case {{{ cDefs.AL_SPEED_OF_SOUND }}}:
3183
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
3184
return val;
3185
default:
3186
#if OPENAL_DEBUG
3187
dbg(`alGetFloat(): param ${ptrToString(param)} has wrong signature`);
3188
#endif
3189
return 0.0;
3190
}
3191
},
3192
3193
alGetFloatv__proxy: 'sync',
3194
alGetFloatv: (param, pValues) => {
3195
var val = AL.getGlobalParam('alGetFloatv', param);
3196
// Silently ignore null destinations, as per the spec for global state functions
3197
if (val === null || !pValues) {
3198
return;
3199
}
3200
3201
switch (param) {
3202
case {{{ cDefs.AL_DOPPLER_FACTOR }}}:
3203
case {{{ cDefs.AL_SPEED_OF_SOUND }}}:
3204
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
3205
{{{ makeSetValue('pValues', '0', 'val', 'float') }}};
3206
break;
3207
default:
3208
#if OPENAL_DEBUG
3209
dbg(`alGetFloatv(): param ${ptrToString(param)} has wrong signature`);
3210
#endif
3211
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3212
return;
3213
}
3214
},
3215
3216
alGetInteger__proxy: 'sync',
3217
alGetInteger: (param) => {
3218
var val = AL.getGlobalParam('alGetInteger', param);
3219
if (val === null) {
3220
return 0;
3221
}
3222
3223
switch (param) {
3224
case {{{ cDefs.AL_DOPPLER_FACTOR }}}:
3225
case {{{ cDefs.AL_SPEED_OF_SOUND }}}:
3226
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
3227
return val;
3228
default:
3229
#if OPENAL_DEBUG
3230
dbg(`alGetInteger(): param ${ptrToString(param)} has wrong signature`);
3231
#endif
3232
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3233
return 0;
3234
}
3235
},
3236
3237
alGetIntegerv__proxy: 'sync',
3238
alGetIntegerv: (param, pValues) => {
3239
var val = AL.getGlobalParam('alGetIntegerv', param);
3240
// Silently ignore null destinations, as per the spec for global state functions
3241
if (val === null || !pValues) {
3242
return;
3243
}
3244
3245
switch (param) {
3246
case {{{ cDefs.AL_DOPPLER_FACTOR }}}:
3247
case {{{ cDefs.AL_SPEED_OF_SOUND }}}:
3248
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
3249
{{{ makeSetValue('pValues', '0', 'val', 'i32') }}};
3250
break;
3251
default:
3252
#if OPENAL_DEBUG
3253
dbg(`alGetIntegerv(): param ${ptrToString(param)} has wrong signature`);
3254
#endif
3255
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3256
return;
3257
}
3258
},
3259
3260
alGetBoolean__proxy: 'sync',
3261
alGetBoolean: (param) => {
3262
var val = AL.getGlobalParam('alGetBoolean', param);
3263
if (val === null) {
3264
return {{{ cDefs.AL_FALSE }}};
3265
}
3266
3267
switch (param) {
3268
case {{{ cDefs.AL_DOPPLER_FACTOR }}}:
3269
case {{{ cDefs.AL_SPEED_OF_SOUND }}}:
3270
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
3271
return val !== 0 ? {{{ cDefs.AL_TRUE }}} : {{{ cDefs.AL_FALSE }}};
3272
default:
3273
#if OPENAL_DEBUG
3274
dbg(`alGetBoolean(): param ${ptrToString(param)} has wrong signature`);
3275
#endif
3276
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3277
return {{{ cDefs.AL_FALSE }}};
3278
}
3279
},
3280
3281
alGetBooleanv__proxy: 'sync',
3282
alGetBooleanv: (param, pValues) => {
3283
var val = AL.getGlobalParam('alGetBooleanv', param);
3284
// Silently ignore null destinations, as per the spec for global state functions
3285
if (val === null || !pValues) {
3286
return;
3287
}
3288
3289
switch (param) {
3290
case {{{ cDefs.AL_DOPPLER_FACTOR }}}:
3291
case {{{ cDefs.AL_SPEED_OF_SOUND }}}:
3292
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
3293
{{{ makeSetValue('pValues', '0', 'val', 'i8') }}};
3294
break;
3295
default:
3296
#if OPENAL_DEBUG
3297
dbg(`alGetBooleanv(): param ${ptrToString(param)} has wrong signature`);
3298
#endif
3299
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3300
return;
3301
}
3302
},
3303
3304
alDistanceModel__proxy: 'sync',
3305
alDistanceModel: (model) => {
3306
AL.setGlobalParam('alDistanceModel', {{{ cDefs.AL_DISTANCE_MODEL }}}, model);
3307
},
3308
3309
alSpeedOfSound__proxy: 'sync',
3310
alSpeedOfSound: (value) => {
3311
AL.setGlobalParam('alSpeedOfSound', {{{ cDefs.AL_SPEED_OF_SOUND }}}, value);
3312
},
3313
3314
alDopplerFactor__proxy: 'sync',
3315
alDopplerFactor: (value) => {
3316
AL.setGlobalParam('alDopplerFactor', {{{ cDefs.AL_DOPPLER_FACTOR }}}, value);
3317
},
3318
3319
// http://openal.996291.n3.nabble.com/alSpeedOfSound-or-alDopperVelocity-tp1960.html
3320
// alDopplerVelocity() sets a multiplier for the speed of sound.
3321
// It's deprecated since it's equivalent to directly calling
3322
// alSpeedOfSound() with an appropriately premultiplied value.
3323
alDopplerVelocity__proxy: 'sync',
3324
alDopplerVelocity: (value) => {
3325
warnOnce('alDopplerVelocity() is deprecated, and only kept for compatibility with OpenAL 1.0. Use alSpeedOfSound() instead.');
3326
if (!AL.currentCtx) {
3327
#if OPENAL_DEBUG
3328
dbg('alDopplerVelocity() called without a valid context');
3329
#endif
3330
return;
3331
}
3332
if (value <= 0) { // Negative or zero values are disallowed
3333
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3334
return;
3335
}
3336
},
3337
3338
// -------------------------------------------------------
3339
// -- AL Listener State
3340
// -------------------------------------------------------
3341
3342
alGetListenerf__proxy: 'sync',
3343
alGetListenerf: (param, pValue) => {
3344
var val = AL.getListenerParam('alGetListenerf', param);
3345
if (val === null) {
3346
return;
3347
}
3348
if (!pValue) {
3349
#if OPENAL_DEBUG
3350
dbg('alGetListenerf() called with a null pointer');
3351
#endif
3352
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3353
return;
3354
}
3355
3356
switch (param) {
3357
case {{{ cDefs.AL_GAIN }}}:
3358
{{{ makeSetValue('pValue', '0', 'val', 'float') }}};
3359
break;
3360
default:
3361
#if OPENAL_DEBUG
3362
dbg(`alGetListenerf(): param ${ptrToString(param)} has wrong signature`);
3363
#endif
3364
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3365
return;
3366
}
3367
},
3368
3369
alGetListener3f__proxy: 'sync',
3370
alGetListener3f: (param, pValue0, pValue1, pValue2) => {
3371
var val = AL.getListenerParam('alGetListener3f', param);
3372
if (val === null) {
3373
return;
3374
}
3375
if (!pValue0 || !pValue1 || !pValue2) {
3376
#if OPENAL_DEBUG
3377
dbg('alGetListener3f() called with a null pointer');
3378
#endif
3379
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3380
return;
3381
}
3382
3383
switch (param) {
3384
case {{{ cDefs.AL_POSITION }}}:
3385
case {{{ cDefs.AL_VELOCITY }}}:
3386
{{{ makeSetValue('pValue0', '0', 'val[0]', 'float') }}};
3387
{{{ makeSetValue('pValue1', '0', 'val[1]', 'float') }}};
3388
{{{ makeSetValue('pValue2', '0', 'val[2]', 'float') }}};
3389
break;
3390
default:
3391
#if OPENAL_DEBUG
3392
dbg(`alGetListener3f(): param ${ptrToString(param)} has wrong signature`);
3393
#endif
3394
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3395
return;
3396
}
3397
},
3398
3399
alGetListenerfv__proxy: 'sync',
3400
alGetListenerfv: (param, pValues) => {
3401
var val = AL.getListenerParam('alGetListenerfv', param);
3402
if (val === null) {
3403
return;
3404
}
3405
if (!pValues) {
3406
#if OPENAL_DEBUG
3407
dbg('alGetListenerfv() called with a null pointer');
3408
#endif
3409
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3410
return;
3411
}
3412
3413
switch (param) {
3414
case {{{ cDefs.AL_POSITION }}}:
3415
case {{{ cDefs.AL_VELOCITY }}}:
3416
{{{ makeSetValue('pValues', '0', 'val[0]', 'float') }}};
3417
{{{ makeSetValue('pValues', '4', 'val[1]', 'float') }}};
3418
{{{ makeSetValue('pValues', '8', 'val[2]', 'float') }}};
3419
break;
3420
case {{{ cDefs.AL_ORIENTATION }}}:
3421
{{{ makeSetValue('pValues', '0', 'val[0]', 'float') }}};
3422
{{{ makeSetValue('pValues', '4', 'val[1]', 'float') }}};
3423
{{{ makeSetValue('pValues', '8', 'val[2]', 'float') }}};
3424
{{{ makeSetValue('pValues', '12', 'val[3]', 'float') }}};
3425
{{{ makeSetValue('pValues', '16', 'val[4]', 'float') }}};
3426
{{{ makeSetValue('pValues', '20', 'val[5]', 'float') }}};
3427
break;
3428
default:
3429
#if OPENAL_DEBUG
3430
dbg(`alGetListenerfv(): param ${ptrToString(param)} has wrong signature`);
3431
#endif
3432
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3433
return;
3434
}
3435
},
3436
3437
alGetListeneri__proxy: 'sync',
3438
alGetListeneri: (param, pValue) => {
3439
var val = AL.getListenerParam('alGetListeneri', param);
3440
if (val === null) {
3441
return;
3442
}
3443
if (!pValue) {
3444
#if OPENAL_DEBUG
3445
dbg('alGetListeneri() called with a null pointer');
3446
#endif
3447
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3448
return;
3449
}
3450
3451
#if OPENAL_DEBUG
3452
dbg(`alGetListeneri(): param ${ptrToString(param)} has wrong signature`);
3453
#endif
3454
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3455
},
3456
3457
alGetListener3i__proxy: 'sync',
3458
alGetListener3i: (param, pValue0, pValue1, pValue2) => {
3459
var val = AL.getListenerParam('alGetListener3i', param);
3460
if (val === null) {
3461
return;
3462
}
3463
if (!pValue0 || !pValue1 || !pValue2) {
3464
#if OPENAL_DEBUG
3465
dbg('alGetListener3i() called with a null pointer');
3466
#endif
3467
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3468
return;
3469
}
3470
3471
switch (param) {
3472
case {{{ cDefs.AL_POSITION }}}:
3473
case {{{ cDefs.AL_VELOCITY }}}:
3474
{{{ makeSetValue('pValue0', '0', 'val[0]', 'i32') }}};
3475
{{{ makeSetValue('pValue1', '0', 'val[1]', 'i32') }}};
3476
{{{ makeSetValue('pValue2', '0', 'val[2]', 'i32') }}};
3477
break;
3478
default:
3479
#if OPENAL_DEBUG
3480
dbg(`alGetListener3i(): param ${ptrToString(param)} has wrong signature`);
3481
#endif
3482
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3483
return;
3484
}
3485
},
3486
3487
alGetListeneriv__proxy: 'sync',
3488
alGetListeneriv: (param, pValues) => {
3489
var val = AL.getListenerParam('alGetListeneriv', param);
3490
if (val === null) {
3491
return;
3492
}
3493
if (!pValues) {
3494
#if OPENAL_DEBUG
3495
dbg('alGetListeneriv() called with a null pointer');
3496
#endif
3497
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3498
return;
3499
}
3500
3501
switch (param) {
3502
case {{{ cDefs.AL_POSITION }}}:
3503
case {{{ cDefs.AL_VELOCITY }}}:
3504
{{{ makeSetValue('pValues', '0', 'val[0]', 'i32') }}};
3505
{{{ makeSetValue('pValues', '4', 'val[1]', 'i32') }}};
3506
{{{ makeSetValue('pValues', '8', 'val[2]', 'i32') }}};
3507
break;
3508
case {{{ cDefs.AL_ORIENTATION }}}:
3509
{{{ makeSetValue('pValues', '0', 'val[0]', 'i32') }}};
3510
{{{ makeSetValue('pValues', '4', 'val[1]', 'i32') }}};
3511
{{{ makeSetValue('pValues', '8', 'val[2]', 'i32') }}};
3512
{{{ makeSetValue('pValues', '12', 'val[3]', 'i32') }}};
3513
{{{ makeSetValue('pValues', '16', 'val[4]', 'i32') }}};
3514
{{{ makeSetValue('pValues', '20', 'val[5]', 'i32') }}};
3515
break;
3516
default:
3517
#if OPENAL_DEBUG
3518
dbg(`alGetListeneriv(): param ${ptrToString(param)} has wrong signature`);
3519
#endif
3520
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3521
return;
3522
}
3523
},
3524
3525
alListenerf__proxy: 'sync',
3526
alListenerf: (param, value) => {
3527
switch (param) {
3528
case {{{ cDefs.AL_GAIN }}}:
3529
AL.setListenerParam('alListenerf', param, value);
3530
break;
3531
default:
3532
AL.setListenerParam('alListenerf', param, null);
3533
break;
3534
}
3535
},
3536
3537
alListener3f__proxy: 'sync',
3538
alListener3f: (param, value0, value1, value2) => {
3539
switch (param) {
3540
case {{{ cDefs.AL_POSITION }}}:
3541
case {{{ cDefs.AL_VELOCITY }}}:
3542
AL.paramArray[0] = value0;
3543
AL.paramArray[1] = value1;
3544
AL.paramArray[2] = value2;
3545
AL.setListenerParam('alListener3f', param, AL.paramArray);
3546
break;
3547
default:
3548
AL.setListenerParam('alListener3f', param, null);
3549
break;
3550
}
3551
},
3552
3553
alListenerfv__proxy: 'sync',
3554
alListenerfv: (param, pValues) => {
3555
if (!AL.currentCtx) {
3556
#if OPENAL_DEBUG
3557
dbg('alListenerfv() called without a valid context');
3558
#endif
3559
return;
3560
}
3561
if (!pValues) {
3562
#if OPENAL_DEBUG
3563
dbg('alListenerfv() called with a null pointer');
3564
#endif
3565
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3566
return;
3567
}
3568
3569
switch (param) {
3570
case {{{ cDefs.AL_POSITION }}}:
3571
case {{{ cDefs.AL_VELOCITY }}}:
3572
AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'float') }}};
3573
AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'float') }}};
3574
AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'float') }}};
3575
AL.setListenerParam('alListenerfv', param, AL.paramArray);
3576
break;
3577
case {{{ cDefs.AL_ORIENTATION }}}:
3578
AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'float') }}};
3579
AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'float') }}};
3580
AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'float') }}};
3581
AL.paramArray[3] = {{{ makeGetValue('pValues', '12', 'float') }}};
3582
AL.paramArray[4] = {{{ makeGetValue('pValues', '16', 'float') }}};
3583
AL.paramArray[5] = {{{ makeGetValue('pValues', '20', 'float') }}};
3584
AL.setListenerParam('alListenerfv', param, AL.paramArray);
3585
break;
3586
default:
3587
AL.setListenerParam('alListenerfv', param, null);
3588
break;
3589
}
3590
},
3591
3592
alListeneri__proxy: 'sync',
3593
alListeneri: (param, value) => {
3594
AL.setListenerParam('alListeneri', param, null);
3595
},
3596
3597
alListener3i__proxy: 'sync',
3598
alListener3i: (param, value0, value1, value2) => {
3599
switch (param) {
3600
case {{{ cDefs.AL_POSITION }}}:
3601
case {{{ cDefs.AL_VELOCITY }}}:
3602
AL.paramArray[0] = value0;
3603
AL.paramArray[1] = value1;
3604
AL.paramArray[2] = value2;
3605
AL.setListenerParam('alListener3i', param, AL.paramArray);
3606
break;
3607
default:
3608
AL.setListenerParam('alListener3i', param, null);
3609
break;
3610
}
3611
},
3612
3613
alListeneriv__proxy: 'sync',
3614
alListeneriv: (param, pValues) => {
3615
if (!AL.currentCtx) {
3616
#if OPENAL_DEBUG
3617
dbg('alListeneriv() called without a valid context');
3618
#endif
3619
return;
3620
}
3621
if (!pValues) {
3622
#if OPENAL_DEBUG
3623
dbg('alListeneriv() called with a null pointer');
3624
#endif
3625
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3626
return;
3627
}
3628
3629
switch (param) {
3630
case {{{ cDefs.AL_POSITION }}}:
3631
case {{{ cDefs.AL_VELOCITY }}}:
3632
AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'i32') }}};
3633
AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'i32') }}};
3634
AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'i32') }}};
3635
AL.setListenerParam('alListeneriv', param, AL.paramArray);
3636
break;
3637
case {{{ cDefs.AL_ORIENTATION }}}:
3638
AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'i32') }}};
3639
AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'i32') }}};
3640
AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'i32') }}};
3641
AL.paramArray[3] = {{{ makeGetValue('pValues', '12', 'i32') }}};
3642
AL.paramArray[4] = {{{ makeGetValue('pValues', '16', 'i32') }}};
3643
AL.paramArray[5] = {{{ makeGetValue('pValues', '20', 'i32') }}};
3644
AL.setListenerParam('alListeneriv', param, AL.paramArray);
3645
break;
3646
default:
3647
AL.setListenerParam('alListeneriv', param, null);
3648
break;
3649
}
3650
},
3651
3652
// -------------------------------------------------------
3653
// -- AL Buffer State
3654
// -------------------------------------------------------
3655
3656
alIsBuffer__proxy: 'sync',
3657
alIsBuffer: (bufferId) => {
3658
if (!AL.currentCtx) {
3659
return false;
3660
}
3661
if (bufferId > AL.buffers.length) {
3662
return false;
3663
}
3664
3665
if (!AL.buffers[bufferId]) {
3666
return false;
3667
}
3668
return true;
3669
},
3670
3671
alBufferData__proxy: 'sync',
3672
alBufferData: (bufferId, format, pData, size, freq) => {
3673
if (!AL.currentCtx) {
3674
#if OPENAL_DEBUG
3675
dbg('alBufferData() called without a valid context');
3676
#endif
3677
return;
3678
}
3679
var buf = AL.buffers[bufferId];
3680
if (!buf) {
3681
#if OPENAL_DEBUG
3682
dbg('alBufferData() called with an invalid buffer');
3683
#endif
3684
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3685
return;
3686
}
3687
if (freq <= 0) {
3688
#if OPENAL_DEBUG
3689
dbg('alBufferData() called with an invalid frequency');
3690
#endif
3691
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3692
return;
3693
}
3694
3695
var audioBuf = null;
3696
try {
3697
switch (format) {
3698
case 0x1100 /* AL_FORMAT_MONO8 */:
3699
if (size > 0) {
3700
audioBuf = AL.currentCtx.audioCtx.createBuffer(1, size, freq);
3701
var channel0 = audioBuf.getChannelData(0);
3702
for (var i = 0; i < size; ++i) {
3703
channel0[i] = HEAPU8[pData++] * 0.0078125 /* 1/128 */ - 1.0;
3704
}
3705
}
3706
buf.bytesPerSample = 1;
3707
buf.channels = 1;
3708
buf.length = size;
3709
break;
3710
case 0x1101 /* AL_FORMAT_MONO16 */:
3711
if (size > 0) {
3712
audioBuf = AL.currentCtx.audioCtx.createBuffer(1, size >> 1, freq);
3713
var channel0 = audioBuf.getChannelData(0);
3714
pData >>= 1;
3715
for (var i = 0; i < size >> 1; ++i) {
3716
channel0[i] = HEAP16[pData++] * 0.000030517578125 /* 1/32768 */;
3717
}
3718
}
3719
buf.bytesPerSample = 2;
3720
buf.channels = 1;
3721
buf.length = size >> 1;
3722
break;
3723
case 0x1102 /* AL_FORMAT_STEREO8 */:
3724
if (size > 0) {
3725
audioBuf = AL.currentCtx.audioCtx.createBuffer(2, size >> 1, freq);
3726
var channel0 = audioBuf.getChannelData(0);
3727
var channel1 = audioBuf.getChannelData(1);
3728
for (var i = 0; i < size >> 1; ++i) {
3729
channel0[i] = HEAPU8[pData++] * 0.0078125 /* 1/128 */ - 1.0;
3730
channel1[i] = HEAPU8[pData++] * 0.0078125 /* 1/128 */ - 1.0;
3731
}
3732
}
3733
buf.bytesPerSample = 1;
3734
buf.channels = 2;
3735
buf.length = size >> 1;
3736
break;
3737
case 0x1103 /* AL_FORMAT_STEREO16 */:
3738
if (size > 0) {
3739
audioBuf = AL.currentCtx.audioCtx.createBuffer(2, size >> 2, freq);
3740
var channel0 = audioBuf.getChannelData(0);
3741
var channel1 = audioBuf.getChannelData(1);
3742
pData >>= 1;
3743
for (var i = 0; i < size >> 2; ++i) {
3744
channel0[i] = HEAP16[pData++] * 0.000030517578125 /* 1/32768 */;
3745
channel1[i] = HEAP16[pData++] * 0.000030517578125 /* 1/32768 */;
3746
}
3747
}
3748
buf.bytesPerSample = 2;
3749
buf.channels = 2;
3750
buf.length = size >> 2;
3751
break;
3752
case 0x10010 /* AL_FORMAT_MONO_FLOAT32 */:
3753
if (size > 0) {
3754
audioBuf = AL.currentCtx.audioCtx.createBuffer(1, size >> 2, freq);
3755
var channel0 = audioBuf.getChannelData(0);
3756
pData >>= 2;
3757
for (var i = 0; i < size >> 2; ++i) {
3758
channel0[i] = HEAPF32[pData++];
3759
}
3760
}
3761
buf.bytesPerSample = 4;
3762
buf.channels = 1;
3763
buf.length = size >> 2;
3764
break;
3765
case 0x10011 /* AL_FORMAT_STEREO_FLOAT32 */:
3766
if (size > 0) {
3767
audioBuf = AL.currentCtx.audioCtx.createBuffer(2, size >> 3, freq);
3768
var channel0 = audioBuf.getChannelData(0);
3769
var channel1 = audioBuf.getChannelData(1);
3770
pData >>= 2;
3771
for (var i = 0; i < size >> 3; ++i) {
3772
channel0[i] = HEAPF32[pData++];
3773
channel1[i] = HEAPF32[pData++];
3774
}
3775
}
3776
buf.bytesPerSample = 4;
3777
buf.channels = 2;
3778
buf.length = size >> 3;
3779
break;
3780
default:
3781
#if OPENAL_DEBUG
3782
dbg(`alBufferData() called with invalid format ${format}`;
3783
#endif
3784
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3785
return;
3786
}
3787
buf.frequency = freq;
3788
buf.audioBuf = audioBuf;
3789
} catch (e) {
3790
#if OPENAL_DEBUG
3791
dbg(`alBufferData() upload failed with an exception ${e}`;
3792
#endif
3793
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3794
return;
3795
}
3796
},
3797
3798
alGetBufferf__proxy: 'sync',
3799
alGetBufferf: (bufferId, param, pValue) => {
3800
var val = AL.getBufferParam('alGetBufferf', bufferId, param);
3801
if (val === null) {
3802
return;
3803
}
3804
if (!pValue) {
3805
#if OPENAL_DEBUG
3806
dbg('alGetBufferf() called with a null pointer');
3807
#endif
3808
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3809
return;
3810
}
3811
3812
#if OPENAL_DEBUG
3813
dbg(`alGetBufferf(): param ${ptrToString(param)} has wrong signature`);
3814
#endif
3815
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3816
},
3817
3818
alGetBuffer3f__proxy: 'sync',
3819
alGetBuffer3f: (bufferId, param, pValue0, pValue1, pValue2) => {
3820
var val = AL.getBufferParam('alGetBuffer3f', bufferId, param);
3821
if (val === null) {
3822
return;
3823
}
3824
if (!pValue0 || !pValue1 || !pValue2) {
3825
#if OPENAL_DEBUG
3826
dbg('alGetBuffer3f() called with a null pointer');
3827
#endif
3828
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3829
return;
3830
}
3831
3832
#if OPENAL_DEBUG
3833
dbg(`alGetBuffer3f(): param ${ptrToString(param)} has wrong signature`);
3834
#endif
3835
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3836
},
3837
3838
alGetBufferfv__proxy: 'sync',
3839
alGetBufferfv: (bufferId, param, pValues) => {
3840
var val = AL.getBufferParam('alGetBufferfv', bufferId, param);
3841
if (val === null) {
3842
return;
3843
}
3844
if (!pValues) {
3845
#if OPENAL_DEBUG
3846
dbg('alGetBufferfv() called with a null pointer');
3847
#endif
3848
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3849
return;
3850
}
3851
3852
#if OPENAL_DEBUG
3853
dbg(`alGetBufferfv(): param ${ptrToString(param)} has wrong signature`);
3854
#endif
3855
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3856
},
3857
3858
alGetBufferi__proxy: 'sync',
3859
alGetBufferi: (bufferId, param, pValue) => {
3860
var val = AL.getBufferParam('alGetBufferi', bufferId, param);
3861
if (val === null) {
3862
return;
3863
}
3864
if (!pValue) {
3865
#if OPENAL_DEBUG
3866
dbg('alGetBufferi() called with a null pointer');
3867
#endif
3868
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3869
return;
3870
}
3871
3872
switch (param) {
3873
case 0x2001 /* AL_FREQUENCY */:
3874
case 0x2002 /* AL_BITS */:
3875
case 0x2003 /* AL_CHANNELS */:
3876
case 0x2004 /* AL_SIZE */:
3877
{{{ makeSetValue('pValue', '0', 'val', 'i32') }}};
3878
break;
3879
default:
3880
#if OPENAL_DEBUG
3881
dbg(`alGetBufferi(): param ${ptrToString(param)} has wrong signature`);
3882
#endif
3883
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3884
return;
3885
}
3886
},
3887
3888
alGetBuffer3i__proxy: 'sync',
3889
alGetBuffer3i: (bufferId, param, pValue0, pValue1, pValue2) => {
3890
var val = AL.getBufferParam('alGetBuffer3i', bufferId, param);
3891
if (val === null) {
3892
return;
3893
}
3894
if (!pValue0 || !pValue1 || !pValue2) {
3895
#if OPENAL_DEBUG
3896
dbg('alGetBuffer3i() called with a null pointer');
3897
#endif
3898
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3899
return;
3900
}
3901
3902
#if OPENAL_DEBUG
3903
dbg(`alGetBuffer3i(): param ${ptrToString(param)} has wrong signature`);
3904
#endif
3905
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3906
},
3907
3908
alGetBufferiv__proxy: 'sync',
3909
alGetBufferiv: (bufferId, param, pValues) => {
3910
var val = AL.getBufferParam('alGetBufferiv', bufferId, param);
3911
if (val === null) {
3912
return;
3913
}
3914
if (!pValues) {
3915
#if OPENAL_DEBUG
3916
dbg('alGetBufferiv() called with a null pointer');
3917
#endif
3918
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3919
return;
3920
}
3921
3922
switch (param) {
3923
case 0x2001 /* AL_FREQUENCY */:
3924
case 0x2002 /* AL_BITS */:
3925
case 0x2003 /* AL_CHANNELS */:
3926
case 0x2004 /* AL_SIZE */:
3927
{{{ makeSetValue('pValues', '0', 'val', 'i32') }}};
3928
break;
3929
case 0x2015 /* AL_LOOP_POINTS_SOFT */:
3930
{{{ makeSetValue('pValues', '0', 'val[0]', 'i32') }}};
3931
{{{ makeSetValue('pValues', '4', 'val[1]', 'i32') }}};
3932
break;
3933
default:
3934
#if OPENAL_DEBUG
3935
dbg(`alGetBufferiv(): param ${ptrToString(param)} has wrong signature`);
3936
#endif
3937
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
3938
return;
3939
}
3940
},
3941
3942
// All of the remaining alBuffer* setters and getters are only of interest
3943
// to extensions which need them. Core OpenAL alone defines no valid
3944
// property for these.
3945
3946
alBufferf__proxy: 'sync',
3947
alBufferf: (bufferId, param, value) => {
3948
AL.setBufferParam('alBufferf', bufferId, param, null);
3949
},
3950
3951
alBuffer3f__proxy: 'sync',
3952
alBuffer3f: (bufferId, param, value0, value1, value2) => {
3953
AL.setBufferParam('alBuffer3f', bufferId, param, null);
3954
},
3955
3956
alBufferfv__proxy: 'sync',
3957
alBufferfv: (bufferId, param, pValues) => {
3958
if (!AL.currentCtx) {
3959
#if OPENAL_DEBUG
3960
dbg('alBufferfv() called without a valid context');
3961
#endif
3962
return;
3963
}
3964
if (!pValues) {
3965
#if OPENAL_DEBUG
3966
dbg('alBufferfv() called with a null pointer');
3967
#endif
3968
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3969
return;
3970
}
3971
3972
AL.setBufferParam('alBufferfv', bufferId, param, null);
3973
},
3974
3975
alBufferi__proxy: 'sync',
3976
alBufferi: (bufferId, param, value) => {
3977
AL.setBufferParam('alBufferi', bufferId, param, null);
3978
},
3979
3980
alBuffer3i__proxy: 'sync',
3981
alBuffer3i: (bufferId, param, value0, value1, value2) => {
3982
AL.setBufferParam('alBuffer3i', bufferId, param, null);
3983
},
3984
3985
alBufferiv__proxy: 'sync',
3986
alBufferiv: (bufferId, param, pValues) => {
3987
if (!AL.currentCtx) {
3988
#if OPENAL_DEBUG
3989
dbg('alBufferiv() called without a valid context');
3990
#endif
3991
return;
3992
}
3993
if (!pValues) {
3994
#if OPENAL_DEBUG
3995
dbg('alBufferiv() called with a null pointer');
3996
#endif
3997
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
3998
return;
3999
}
4000
4001
switch (param) {
4002
case 0x2015 /* AL_LOOP_POINTS_SOFT */:
4003
AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'i32') }}};
4004
AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'i32') }}};
4005
AL.setBufferParam('alBufferiv', bufferId, param, AL.paramArray);
4006
break;
4007
default:
4008
AL.setBufferParam('alBufferiv', bufferId, param, null);
4009
break;
4010
}
4011
},
4012
4013
// -------------------------------------------------------
4014
// -- AL Source State
4015
// -------------------------------------------------------
4016
4017
alIsSource__proxy: 'sync',
4018
alIsSource: (sourceId) => {
4019
if (!AL.currentCtx) {
4020
return false;
4021
}
4022
4023
if (!AL.currentCtx.sources[sourceId]) {
4024
return false;
4025
}
4026
return true;
4027
},
4028
4029
alSourceQueueBuffers__proxy: 'sync',
4030
alSourceQueueBuffers: (sourceId, count, pBufferIds) => {
4031
if (!AL.currentCtx) {
4032
#if OPENAL_DEBUG
4033
dbg('alSourceQueueBuffers() called without a valid context');
4034
#endif
4035
return;
4036
}
4037
var src = AL.currentCtx.sources[sourceId];
4038
if (!src) {
4039
#if OPENAL_DEBUG
4040
dbg('alSourceQueueBuffers() called with an invalid source');
4041
#endif
4042
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
4043
return;
4044
}
4045
if (src.type === {{{ cDefs.AL_STATIC }}}) {
4046
#if OPENAL_DEBUG
4047
dbg('alSourceQueueBuffers() called while a static buffer is bound');
4048
#endif
4049
AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}};
4050
return;
4051
}
4052
4053
if (count === 0) {
4054
return;
4055
}
4056
4057
// Find the first non-zero buffer in the queue to determine the proper format
4058
var templateBuf = AL.buffers[0];
4059
for (var buf of src.bufQueue) {
4060
if (buf.id !== 0) {
4061
templateBuf = buf;
4062
break;
4063
}
4064
}
4065
4066
for (var i = 0; i < count; ++i) {
4067
var bufId = {{{ makeGetValue('pBufferIds', 'i*4', 'i32') }}};
4068
var buf = AL.buffers[bufId];
4069
if (!buf) {
4070
#if OPENAL_DEBUG
4071
dbg('alSourceQueueBuffers() called with an invalid buffer');
4072
#endif
4073
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
4074
return;
4075
}
4076
4077
// Check that the added buffer has the correct format. If the template is the zero buffer, any format is valid.
4078
if (templateBuf.id !== 0 && (
4079
buf.frequency !== templateBuf.frequency
4080
|| buf.bytesPerSample !== templateBuf.bytesPerSample
4081
|| buf.channels !== templateBuf.channels)
4082
) {
4083
#if OPENAL_DEBUG
4084
dbg('alSourceQueueBuffers() called with a buffer of different format');
4085
#endif
4086
AL.currentCtx.err = {{{ cDefs.AL_INVALID_OPERATION }}};
4087
}
4088
}
4089
4090
// If the only buffer in the queue is the zero buffer, clear the queue before we add anything.
4091
if (src.bufQueue.length === 1 && src.bufQueue[0].id === 0) {
4092
src.bufQueue.length = 0;
4093
}
4094
4095
src.type = 0x1029 /* AL_STREAMING */;
4096
for (var i = 0; i < count; ++i) {
4097
var bufId = {{{ makeGetValue('pBufferIds', 'i*4', 'i32') }}};
4098
var buf = AL.buffers[bufId];
4099
buf.refCount++;
4100
src.bufQueue.push(buf);
4101
}
4102
4103
// if the source is looping, cancel the schedule so we can reschedule the loop order
4104
if (src.looping) {
4105
AL.cancelPendingSourceAudio(src);
4106
}
4107
4108
AL.initSourcePanner(src);
4109
AL.scheduleSourceAudio(src);
4110
},
4111
4112
alSourceUnqueueBuffers__proxy: 'sync',
4113
alSourceUnqueueBuffers: (sourceId, count, pBufferIds) => {
4114
if (!AL.currentCtx) {
4115
#if OPENAL_DEBUG
4116
dbg('alSourceUnqueueBuffers() called without a valid context');
4117
#endif
4118
return;
4119
}
4120
var src = AL.currentCtx.sources[sourceId];
4121
if (!src) {
4122
#if OPENAL_DEBUG
4123
dbg('alSourceUnqueueBuffers() called with an invalid source');
4124
#endif
4125
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
4126
return;
4127
}
4128
if (count > (src.bufQueue.length === 1 && src.bufQueue[0].id === 0 ? 0 : src.bufsProcessed)) {
4129
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4130
return;
4131
}
4132
4133
if (count === 0) {
4134
return;
4135
}
4136
4137
for (var i = 0; i < count; i++) {
4138
var buf = src.bufQueue.shift();
4139
buf.refCount--;
4140
// Write the buffers index out to the return list.
4141
{{{ makeSetValue('pBufferIds', 'i*4', 'buf.id', 'i32') }}};
4142
src.bufsProcessed--;
4143
}
4144
4145
/// If the queue is empty, put the zero buffer back in
4146
if (src.bufQueue.length === 0) {
4147
src.bufQueue.push(AL.buffers[0]);
4148
}
4149
4150
AL.initSourcePanner(src);
4151
AL.scheduleSourceAudio(src);
4152
},
4153
4154
alSourcePlay__proxy: 'sync',
4155
alSourcePlay: (sourceId) => {
4156
if (!AL.currentCtx) {
4157
#if OPENAL_DEBUG
4158
dbg('alSourcePlay() called without a valid context');
4159
#endif
4160
return;
4161
}
4162
var src = AL.currentCtx.sources[sourceId];
4163
if (!src) {
4164
#if OPENAL_DEBUG
4165
dbg('alSourcePlay() called with an invalid source');
4166
#endif
4167
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
4168
return;
4169
}
4170
AL.setSourceState(src, {{{ cDefs.AL_PLAYING }}});
4171
},
4172
4173
alSourcePlayv__proxy: 'sync',
4174
alSourcePlayv: (count, pSourceIds) => {
4175
if (!AL.currentCtx) {
4176
#if OPENAL_DEBUG
4177
dbg('alSourcePlayv() called without a valid context');
4178
#endif
4179
return;
4180
}
4181
if (!pSourceIds) {
4182
#if OPENAL_DEBUG
4183
dbg('alSourcePlayv() called with null pointer');
4184
#endif
4185
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4186
}
4187
for (var i = 0; i < count; ++i) {
4188
if (!AL.currentCtx.sources[{{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}]) {
4189
#if OPENAL_DEBUG
4190
dbg('alSourcePlayv() called with an invalid source');
4191
#endif
4192
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
4193
return;
4194
}
4195
}
4196
4197
for (var i = 0; i < count; ++i) {
4198
var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}};
4199
AL.setSourceState(AL.currentCtx.sources[srcId], {{{ cDefs.AL_PLAYING }}});
4200
}
4201
},
4202
4203
alSourceStop__proxy: 'sync',
4204
alSourceStop: (sourceId) => {
4205
if (!AL.currentCtx) {
4206
#if OPENAL_DEBUG
4207
dbg('alSourceStop() called without a valid context');
4208
#endif
4209
return;
4210
}
4211
var src = AL.currentCtx.sources[sourceId];
4212
if (!src) {
4213
#if OPENAL_DEBUG
4214
dbg('alSourceStop() called with an invalid source');
4215
#endif
4216
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
4217
return;
4218
}
4219
AL.setSourceState(src, {{{ cDefs.AL_STOPPED }}});
4220
},
4221
4222
alSourceStopv__proxy: 'sync',
4223
alSourceStopv: (count, pSourceIds) => {
4224
if (!AL.currentCtx) {
4225
#if OPENAL_DEBUG
4226
dbg('alSourceStopv() called without a valid context');
4227
#endif
4228
return;
4229
}
4230
if (!pSourceIds) {
4231
#if OPENAL_DEBUG
4232
dbg('alSourceStopv() called with null pointer');
4233
#endif
4234
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4235
}
4236
for (var i = 0; i < count; ++i) {
4237
if (!AL.currentCtx.sources[{{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}]) {
4238
#if OPENAL_DEBUG
4239
dbg('alSourceStopv() called with an invalid source');
4240
#endif
4241
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
4242
return;
4243
}
4244
}
4245
4246
for (var i = 0; i < count; ++i) {
4247
var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}};
4248
AL.setSourceState(AL.currentCtx.sources[srcId], {{{ cDefs.AL_STOPPED }}});
4249
}
4250
},
4251
4252
alSourceRewind__proxy: 'sync',
4253
alSourceRewind: (sourceId) => {
4254
if (!AL.currentCtx) {
4255
#if OPENAL_DEBUG
4256
dbg('alSourceRewind() called without a valid context');
4257
#endif
4258
return;
4259
}
4260
var src = AL.currentCtx.sources[sourceId];
4261
if (!src) {
4262
#if OPENAL_DEBUG
4263
dbg('alSourceRewind() called with an invalid source');
4264
#endif
4265
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
4266
return;
4267
}
4268
// Stop the source first to clear the source queue
4269
AL.setSourceState(src, {{{ cDefs.AL_STOPPED }}});
4270
// Now set the state of AL_INITIAL according to the specification
4271
AL.setSourceState(src, {{{ cDefs.AL_INITIAL }}});
4272
},
4273
4274
alSourceRewindv__proxy: 'sync',
4275
alSourceRewindv: (count, pSourceIds) => {
4276
if (!AL.currentCtx) {
4277
#if OPENAL_DEBUG
4278
dbg('alSourceRewindv() called without a valid context');
4279
#endif
4280
return;
4281
}
4282
if (!pSourceIds) {
4283
#if OPENAL_DEBUG
4284
dbg('alSourceRewindv() called with null pointer');
4285
#endif
4286
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4287
}
4288
for (var i = 0; i < count; ++i) {
4289
if (!AL.currentCtx.sources[{{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}]) {
4290
#if OPENAL_DEBUG
4291
dbg('alSourceRewindv() called with an invalid source');
4292
#endif
4293
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
4294
return;
4295
}
4296
}
4297
4298
for (var i = 0; i < count; ++i) {
4299
var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}};
4300
AL.setSourceState(AL.currentCtx.sources[srcId], {{{ cDefs.AL_INITIAL }}});
4301
}
4302
},
4303
4304
alSourcePause__proxy: 'sync',
4305
alSourcePause: (sourceId) => {
4306
if (!AL.currentCtx) {
4307
#if OPENAL_DEBUG
4308
dbg('alSourcePause() called without a valid context');
4309
#endif
4310
return;
4311
}
4312
var src = AL.currentCtx.sources[sourceId];
4313
if (!src) {
4314
#if OPENAL_DEBUG
4315
dbg('alSourcePause() called with an invalid source');
4316
#endif
4317
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
4318
return;
4319
}
4320
AL.setSourceState(src, {{{ cDefs.AL_PAUSED }}});
4321
},
4322
4323
alSourcePausev__proxy: 'sync',
4324
alSourcePausev: (count, pSourceIds) => {
4325
if (!AL.currentCtx) {
4326
#if OPENAL_DEBUG
4327
dbg('alSourcePausev() called without a valid context');
4328
#endif
4329
return;
4330
}
4331
if (!pSourceIds) {
4332
#if OPENAL_DEBUG
4333
dbg('alSourcePausev() called with null pointer');
4334
#endif
4335
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4336
}
4337
for (var i = 0; i < count; ++i) {
4338
if (!AL.currentCtx.sources[{{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}}]) {
4339
#if OPENAL_DEBUG
4340
dbg('alSourcePausev() called with an invalid source');
4341
#endif
4342
AL.currentCtx.err = {{{ cDefs.AL_INVALID_NAME }}};
4343
return;
4344
}
4345
}
4346
4347
for (var i = 0; i < count; ++i) {
4348
var srcId = {{{ makeGetValue('pSourceIds', 'i*4', 'i32') }}};
4349
AL.setSourceState(AL.currentCtx.sources[srcId], {{{ cDefs.AL_PAUSED }}});
4350
}
4351
},
4352
4353
alGetSourcef__proxy: 'sync',
4354
alGetSourcef: (sourceId, param, pValue) => {
4355
var val = AL.getSourceParam('alGetSourcef', sourceId, param);
4356
if (val === null) {
4357
return;
4358
}
4359
if (!pValue) {
4360
#if OPENAL_DEBUG
4361
dbg('alGetSourcef() called with a null pointer');
4362
#endif
4363
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4364
return;
4365
}
4366
4367
switch (param) {
4368
case 0x1001 /* AL_CONE_INNER_ANGLE */:
4369
case 0x1002 /* AL_CONE_OUTER_ANGLE */:
4370
case 0x1003 /* AL_PITCH */:
4371
case {{{ cDefs.AL_GAIN }}}:
4372
case 0x100D /* AL_MIN_GAIN */:
4373
case 0x100E /* AL_MAX_GAIN */:
4374
case 0x1020 /* AL_REFERENCE_DISTANCE */:
4375
case 0x1021 /* AL_ROLLOFF_FACTOR */:
4376
case 0x1022 /* AL_CONE_OUTER_GAIN */:
4377
case 0x1023 /* AL_MAX_DISTANCE */:
4378
case 0x1024 /* AL_SEC_OFFSET */:
4379
case 0x1025 /* AL_SAMPLE_OFFSET */:
4380
case 0x1026 /* AL_BYTE_OFFSET */:
4381
case 0x200B /* AL_SEC_LENGTH_SOFT */:
4382
{{{ makeSetValue('pValue', '0', 'val', 'float') }}};
4383
break;
4384
default:
4385
#if OPENAL_DEBUG
4386
dbg(`alGetSourcef(): param ${ptrToString(param)} has wrong signature`);
4387
#endif
4388
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
4389
return;
4390
}
4391
},
4392
4393
alGetSource3f__proxy: 'sync',
4394
alGetSource3f: (sourceId, param, pValue0, pValue1, pValue2) => {
4395
var val = AL.getSourceParam('alGetSource3f', sourceId, param);
4396
if (val === null) {
4397
return;
4398
}
4399
if (!pValue0 || !pValue1 || !pValue2) {
4400
#if OPENAL_DEBUG
4401
dbg('alGetSource3f() called with a null pointer');
4402
#endif
4403
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4404
return;
4405
}
4406
4407
switch (param) {
4408
case {{{ cDefs.AL_POSITION }}}:
4409
case {{{ cDefs.AL_DIRECTION }}}:
4410
case {{{ cDefs.AL_VELOCITY }}}:
4411
{{{ makeSetValue('pValue0', '0', 'val[0]', 'float') }}};
4412
{{{ makeSetValue('pValue1', '0', 'val[1]', 'float') }}};
4413
{{{ makeSetValue('pValue2', '0', 'val[2]', 'float') }}};
4414
break;
4415
default:
4416
#if OPENAL_DEBUG
4417
dbg(`alGetSource3f(): param ${ptrToString(param)} has wrong signature`);
4418
#endif
4419
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
4420
return;
4421
}
4422
},
4423
4424
alGetSourcefv__proxy: 'sync',
4425
alGetSourcefv: (sourceId, param, pValues) => {
4426
var val = AL.getSourceParam('alGetSourcefv', sourceId, param);
4427
if (val === null) {
4428
return;
4429
}
4430
if (!pValues) {
4431
#if OPENAL_DEBUG
4432
dbg('alGetSourcefv() called with a null pointer');
4433
#endif
4434
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4435
return;
4436
}
4437
4438
switch (param) {
4439
case 0x1001 /* AL_CONE_INNER_ANGLE */:
4440
case 0x1002 /* AL_CONE_OUTER_ANGLE */:
4441
case 0x1003 /* AL_PITCH */:
4442
case {{{ cDefs.AL_GAIN }}}:
4443
case 0x100D /* AL_MIN_GAIN */:
4444
case 0x100E /* AL_MAX_GAIN */:
4445
case 0x1020 /* AL_REFERENCE_DISTANCE */:
4446
case 0x1021 /* AL_ROLLOFF_FACTOR */:
4447
case 0x1022 /* AL_CONE_OUTER_GAIN */:
4448
case 0x1023 /* AL_MAX_DISTANCE */:
4449
case 0x1024 /* AL_SEC_OFFSET */:
4450
case 0x1025 /* AL_SAMPLE_OFFSET */:
4451
case 0x1026 /* AL_BYTE_OFFSET */:
4452
case 0x200B /* AL_SEC_LENGTH_SOFT */:
4453
{{{ makeSetValue('pValues', '0', 'val[0]', 'float') }}};
4454
break;
4455
case {{{ cDefs.AL_POSITION }}}:
4456
case {{{ cDefs.AL_DIRECTION }}}:
4457
case {{{ cDefs.AL_VELOCITY }}}:
4458
{{{ makeSetValue('pValues', '0', 'val[0]', 'float') }}};
4459
{{{ makeSetValue('pValues', '4', 'val[1]', 'float') }}};
4460
{{{ makeSetValue('pValues', '8', 'val[2]', 'float') }}};
4461
break;
4462
default:
4463
#if OPENAL_DEBUG
4464
dbg(`alGetSourcefv(): param ${ptrToString(param)} has wrong signature`);
4465
#endif
4466
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
4467
return;
4468
}
4469
},
4470
4471
alGetSourcei__proxy: 'sync',
4472
alGetSourcei: (sourceId, param, pValue) => {
4473
var val = AL.getSourceParam('alGetSourcei', sourceId, param);
4474
if (val === null) {
4475
return;
4476
}
4477
if (!pValue) {
4478
#if OPENAL_DEBUG
4479
dbg('alGetSourcei() called with a null pointer');
4480
#endif
4481
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4482
return;
4483
}
4484
4485
switch (param) {
4486
case 0x202 /* AL_SOURCE_RELATIVE */:
4487
case 0x1001 /* AL_CONE_INNER_ANGLE */:
4488
case 0x1002 /* AL_CONE_OUTER_ANGLE */:
4489
case 0x1007 /* AL_LOOPING */:
4490
case 0x1009 /* AL_BUFFER */:
4491
case 0x1010 /* AL_SOURCE_STATE */:
4492
case 0x1015 /* AL_BUFFERS_QUEUED */:
4493
case 0x1016 /* AL_BUFFERS_PROCESSED */:
4494
case 0x1020 /* AL_REFERENCE_DISTANCE */:
4495
case 0x1021 /* AL_ROLLOFF_FACTOR */:
4496
case 0x1023 /* AL_MAX_DISTANCE */:
4497
case 0x1024 /* AL_SEC_OFFSET */:
4498
case 0x1025 /* AL_SAMPLE_OFFSET */:
4499
case 0x1026 /* AL_BYTE_OFFSET */:
4500
case 0x1027 /* AL_SOURCE_TYPE */:
4501
case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */:
4502
case 0x2009 /* AL_BYTE_LENGTH_SOFT */:
4503
case 0x200A /* AL_SAMPLE_LENGTH_SOFT */:
4504
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
4505
{{{ makeSetValue('pValue', '0', 'val', 'i32') }}};
4506
break;
4507
default:
4508
#if OPENAL_DEBUG
4509
dbg(`alGetSourcei(): param ${ptrToString(param)} has wrong signature`);
4510
#endif
4511
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
4512
return;
4513
}
4514
},
4515
4516
alGetSource3i__proxy: 'sync',
4517
alGetSource3i: (sourceId, param, pValue0, pValue1, pValue2) => {
4518
var val = AL.getSourceParam('alGetSource3i', sourceId, param);
4519
if (val === null) {
4520
return;
4521
}
4522
if (!pValue0 || !pValue1 || !pValue2) {
4523
#if OPENAL_DEBUG
4524
dbg('alGetSource3i() called with a null pointer');
4525
#endif
4526
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4527
return;
4528
}
4529
4530
switch (param) {
4531
case {{{ cDefs.AL_POSITION }}}:
4532
case {{{ cDefs.AL_DIRECTION }}}:
4533
case {{{ cDefs.AL_VELOCITY }}}:
4534
{{{ makeSetValue('pValue0', '0', 'val[0]', 'i32') }}};
4535
{{{ makeSetValue('pValue1', '0', 'val[1]', 'i32') }}};
4536
{{{ makeSetValue('pValue2', '0', 'val[2]', 'i32') }}};
4537
break;
4538
default:
4539
#if OPENAL_DEBUG
4540
dbg(`alGetSource3i(): param ${ptrToString(param)} has wrong signature`);
4541
#endif
4542
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
4543
return;
4544
}
4545
},
4546
4547
alGetSourceiv__proxy: 'sync',
4548
alGetSourceiv: (sourceId, param, pValues) => {
4549
var val = AL.getSourceParam('alGetSourceiv', sourceId, param);
4550
if (val === null) {
4551
return;
4552
}
4553
if (!pValues) {
4554
#if OPENAL_DEBUG
4555
dbg('alGetSourceiv() called with a null pointer');
4556
#endif
4557
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4558
return;
4559
}
4560
4561
switch (param) {
4562
case 0x202 /* AL_SOURCE_RELATIVE */:
4563
case 0x1001 /* AL_CONE_INNER_ANGLE */:
4564
case 0x1002 /* AL_CONE_OUTER_ANGLE */:
4565
case 0x1007 /* AL_LOOPING */:
4566
case 0x1009 /* AL_BUFFER */:
4567
case 0x1010 /* AL_SOURCE_STATE */:
4568
case 0x1015 /* AL_BUFFERS_QUEUED */:
4569
case 0x1016 /* AL_BUFFERS_PROCESSED */:
4570
case 0x1020 /* AL_REFERENCE_DISTANCE */:
4571
case 0x1021 /* AL_ROLLOFF_FACTOR */:
4572
case 0x1023 /* AL_MAX_DISTANCE */:
4573
case 0x1024 /* AL_SEC_OFFSET */:
4574
case 0x1025 /* AL_SAMPLE_OFFSET */:
4575
case 0x1026 /* AL_BYTE_OFFSET */:
4576
case 0x1027 /* AL_SOURCE_TYPE */:
4577
case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */:
4578
case 0x2009 /* AL_BYTE_LENGTH_SOFT */:
4579
case 0x200A /* AL_SAMPLE_LENGTH_SOFT */:
4580
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
4581
{{{ makeSetValue('pValues', '0', 'val', 'i32') }}};
4582
break;
4583
case {{{ cDefs.AL_POSITION }}}:
4584
case {{{ cDefs.AL_DIRECTION }}}:
4585
case {{{ cDefs.AL_VELOCITY }}}:
4586
{{{ makeSetValue('pValues', '0', 'val[0]', 'i32') }}};
4587
{{{ makeSetValue('pValues', '4', 'val[1]', 'i32') }}};
4588
{{{ makeSetValue('pValues', '8', 'val[2]', 'i32') }}};
4589
break;
4590
default:
4591
#if OPENAL_DEBUG
4592
dbg(`alGetSourceiv(): param ${ptrToString(param)} has wrong signature`);
4593
#endif
4594
AL.currentCtx.err = {{{ cDefs.AL_INVALID_ENUM }}};
4595
return;
4596
}
4597
},
4598
4599
alSourcef__proxy: 'sync',
4600
alSourcef: (sourceId, param, value) => {
4601
switch (param) {
4602
case 0x1001 /* AL_CONE_INNER_ANGLE */:
4603
case 0x1002 /* AL_CONE_OUTER_ANGLE */:
4604
case 0x1003 /* AL_PITCH */:
4605
case {{{ cDefs.AL_GAIN }}}:
4606
case 0x100D /* AL_MIN_GAIN */:
4607
case 0x100E /* AL_MAX_GAIN */:
4608
case 0x1020 /* AL_REFERENCE_DISTANCE */:
4609
case 0x1021 /* AL_ROLLOFF_FACTOR */:
4610
case 0x1022 /* AL_CONE_OUTER_GAIN */:
4611
case 0x1023 /* AL_MAX_DISTANCE */:
4612
case 0x1024 /* AL_SEC_OFFSET */:
4613
case 0x1025 /* AL_SAMPLE_OFFSET */:
4614
case 0x1026 /* AL_BYTE_OFFSET */:
4615
case 0x200B /* AL_SEC_LENGTH_SOFT */:
4616
AL.setSourceParam('alSourcef', sourceId, param, value);
4617
break;
4618
default:
4619
AL.setSourceParam('alSourcef', sourceId, param, null);
4620
break;
4621
}
4622
},
4623
4624
alSource3f__proxy: 'sync',
4625
alSource3f: (sourceId, param, value0, value1, value2) => {
4626
switch (param) {
4627
case {{{ cDefs.AL_POSITION }}}:
4628
case {{{ cDefs.AL_DIRECTION }}}:
4629
case {{{ cDefs.AL_VELOCITY }}}:
4630
AL.paramArray[0] = value0;
4631
AL.paramArray[1] = value1;
4632
AL.paramArray[2] = value2;
4633
AL.setSourceParam('alSource3f', sourceId, param, AL.paramArray);
4634
break;
4635
default:
4636
AL.setSourceParam('alSource3f', sourceId, param, null);
4637
break;
4638
}
4639
},
4640
4641
alSourcefv__proxy: 'sync',
4642
alSourcefv: (sourceId, param, pValues) => {
4643
if (!AL.currentCtx) {
4644
#if OPENAL_DEBUG
4645
dbg('alSourcefv() called without a valid context');
4646
#endif
4647
return;
4648
}
4649
if (!pValues) {
4650
#if OPENAL_DEBUG
4651
dbg('alSourcefv() called with a null pointer');
4652
#endif
4653
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4654
return;
4655
}
4656
4657
switch (param) {
4658
case 0x1001 /* AL_CONE_INNER_ANGLE */:
4659
case 0x1002 /* AL_CONE_OUTER_ANGLE */:
4660
case 0x1003 /* AL_PITCH */:
4661
case {{{ cDefs.AL_GAIN }}}:
4662
case 0x100D /* AL_MIN_GAIN */:
4663
case 0x100E /* AL_MAX_GAIN */:
4664
case 0x1020 /* AL_REFERENCE_DISTANCE */:
4665
case 0x1021 /* AL_ROLLOFF_FACTOR */:
4666
case 0x1022 /* AL_CONE_OUTER_GAIN */:
4667
case 0x1023 /* AL_MAX_DISTANCE */:
4668
case 0x1024 /* AL_SEC_OFFSET */:
4669
case 0x1025 /* AL_SAMPLE_OFFSET */:
4670
case 0x1026 /* AL_BYTE_OFFSET */:
4671
case 0x200B /* AL_SEC_LENGTH_SOFT */:
4672
var val = {{{ makeGetValue('pValues', '0', 'float') }}};
4673
AL.setSourceParam('alSourcefv', sourceId, param, val);
4674
break;
4675
case {{{ cDefs.AL_POSITION }}}:
4676
case {{{ cDefs.AL_DIRECTION }}}:
4677
case {{{ cDefs.AL_VELOCITY }}}:
4678
AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'float') }}};
4679
AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'float') }}};
4680
AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'float') }}};
4681
AL.setSourceParam('alSourcefv', sourceId, param, AL.paramArray);
4682
break;
4683
default:
4684
AL.setSourceParam('alSourcefv', sourceId, param, null);
4685
break;
4686
}
4687
},
4688
4689
alSourcei__proxy: 'sync',
4690
alSourcei: (sourceId, param, value) => {
4691
switch (param) {
4692
case 0x202 /* AL_SOURCE_RELATIVE */:
4693
case 0x1001 /* AL_CONE_INNER_ANGLE */:
4694
case 0x1002 /* AL_CONE_OUTER_ANGLE */:
4695
case 0x1007 /* AL_LOOPING */:
4696
case 0x1009 /* AL_BUFFER */:
4697
case 0x1020 /* AL_REFERENCE_DISTANCE */:
4698
case 0x1021 /* AL_ROLLOFF_FACTOR */:
4699
case 0x1023 /* AL_MAX_DISTANCE */:
4700
case 0x1024 /* AL_SEC_OFFSET */:
4701
case 0x1025 /* AL_SAMPLE_OFFSET */:
4702
case 0x1026 /* AL_BYTE_OFFSET */:
4703
case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */:
4704
case 0x2009 /* AL_BYTE_LENGTH_SOFT */:
4705
case 0x200A /* AL_SAMPLE_LENGTH_SOFT */:
4706
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
4707
AL.setSourceParam('alSourcei', sourceId, param, value);
4708
break;
4709
default:
4710
AL.setSourceParam('alSourcei', sourceId, param, null);
4711
break;
4712
}
4713
},
4714
4715
alSource3i__proxy: 'sync',
4716
alSource3i: (sourceId, param, value0, value1, value2) => {
4717
switch (param) {
4718
case {{{ cDefs.AL_POSITION }}}:
4719
case {{{ cDefs.AL_DIRECTION }}}:
4720
case {{{ cDefs.AL_VELOCITY }}}:
4721
AL.paramArray[0] = value0;
4722
AL.paramArray[1] = value1;
4723
AL.paramArray[2] = value2;
4724
AL.setSourceParam('alSource3i', sourceId, param, AL.paramArray);
4725
break;
4726
default:
4727
AL.setSourceParam('alSource3i', sourceId, param, null);
4728
break;
4729
}
4730
},
4731
4732
alSourceiv__proxy: 'sync',
4733
alSourceiv: (sourceId, param, pValues) => {
4734
if (!AL.currentCtx) {
4735
#if OPENAL_DEBUG
4736
dbg('alSourceiv() called without a valid context');
4737
#endif
4738
return;
4739
}
4740
if (!pValues) {
4741
#if OPENAL_DEBUG
4742
dbg('alSourceiv() called with a null pointer');
4743
#endif
4744
AL.currentCtx.err = {{{ cDefs.AL_INVALID_VALUE }}};
4745
return;
4746
}
4747
4748
switch (param) {
4749
case 0x202 /* AL_SOURCE_RELATIVE */:
4750
case 0x1001 /* AL_CONE_INNER_ANGLE */:
4751
case 0x1002 /* AL_CONE_OUTER_ANGLE */:
4752
case 0x1007 /* AL_LOOPING */:
4753
case 0x1009 /* AL_BUFFER */:
4754
case 0x1020 /* AL_REFERENCE_DISTANCE */:
4755
case 0x1021 /* AL_ROLLOFF_FACTOR */:
4756
case 0x1023 /* AL_MAX_DISTANCE */:
4757
case 0x1024 /* AL_SEC_OFFSET */:
4758
case 0x1025 /* AL_SAMPLE_OFFSET */:
4759
case 0x1026 /* AL_BYTE_OFFSET */:
4760
case 0x1214 /* AL_SOURCE_SPATIALIZE_SOFT */:
4761
case 0x2009 /* AL_BYTE_LENGTH_SOFT */:
4762
case 0x200A /* AL_SAMPLE_LENGTH_SOFT */:
4763
case {{{ cDefs.AL_DISTANCE_MODEL }}}:
4764
var val = {{{ makeGetValue('pValues', '0', 'i32') }}};
4765
AL.setSourceParam('alSourceiv', sourceId, param, val);
4766
break;
4767
case {{{ cDefs.AL_POSITION }}}:
4768
case {{{ cDefs.AL_DIRECTION }}}:
4769
case {{{ cDefs.AL_VELOCITY }}}:
4770
AL.paramArray[0] = {{{ makeGetValue('pValues', '0', 'i32') }}};
4771
AL.paramArray[1] = {{{ makeGetValue('pValues', '4', 'i32') }}};
4772
AL.paramArray[2] = {{{ makeGetValue('pValues', '8', 'i32') }}};
4773
AL.setSourceParam('alSourceiv', sourceId, param, AL.paramArray);
4774
break;
4775
default:
4776
AL.setSourceParam('alSourceiv', sourceId, param, null);
4777
break;
4778
}
4779
}
4780
};
4781
4782
autoAddDeps(LibraryOpenAL, '$AL');
4783
addToLibrary(LibraryOpenAL);
4784
4785