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