aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/library.js67
-rw-r--r--src/library_jansson.js2
-rw-r--r--src/library_openal.js596
-rw-r--r--src/library_sdl.js168
-rw-r--r--src/postamble.js94
-rw-r--r--src/preamble.js73
-rw-r--r--src/settings.js1655
7 files changed, 1565 insertions, 1090 deletions
diff --git a/src/library.js b/src/library.js
index 28886671..8342decf 100644
--- a/src/library.js
+++ b/src/library.js
@@ -1165,7 +1165,7 @@ LibraryManager.library = {
['i32', 'f_namemax']]),
statvfs__deps: ['$FS', '__statvfs_struct_layout'],
statvfs: function(path, buf) {
- // http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/statvfs.html
// int statvfs(const char *restrict path, struct statvfs *restrict buf);
var offsets = ___statvfs_struct_layout;
// NOTE: None of the constants here are true. We're just returning safe and
@@ -2133,20 +2133,7 @@ LibraryManager.library = {
_exit: function(status) {
// void _exit(int status);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html
-
- function ExitStatus() {
- this.name = "ExitStatus";
- this.message = "Program terminated with exit(" + status + ")";
- this.status = status;
- Module.print('Exit Status: ' + status);
- };
- ExitStatus.prototype = new Error();
- ExitStatus.prototype.constructor = ExitStatus;
-
- exitRuntime();
- ABORT = true;
-
- throw new ExitStatus();
+ Module['exit'](status);
},
fork__deps: ['__setErrNo', '$ERRNO_CODES'],
fork: function() {
@@ -3653,29 +3640,19 @@ LibraryManager.library = {
___setErrNo(ERRNO_CODES.EAGAIN);
return -1;
},
- fscanf__deps: ['$FS', '__setErrNo', '$ERRNO_CODES',
- '_scanString', 'fgetc', 'fseek', 'ftell'],
+ fscanf__deps: ['$FS', '_scanString', 'fgetc', 'ungetc'],
fscanf: function(stream, format, varargs) {
// int fscanf(FILE *restrict stream, const char *restrict format, ... );
// http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html
if (FS.streams[stream]) {
- var i = _ftell(stream), SEEK_SET = 0;
- // if the stream does not support seeking backwards (e.g. stdin), buffer it here
- var buffer = [], bufferIndex = 0;
+ var buffer = [];
var get = function() {
- if (bufferIndex < buffer.length) {
- return buffer[bufferIndex++];
- }
- i++;
- bufferIndex++;
var c = _fgetc(stream);
buffer.push(c);
return c;
};
var unget = function() {
- if (_fseek(stream, --i, SEEK_SET) !== 0) {
- bufferIndex--;
- }
+ _ungetc(buffer.pop(), stream);
};
return __scanString(format, get, unget, varargs);
} else {
@@ -3939,8 +3916,7 @@ LibraryManager.library = {
__cxa_atexit: 'atexit',
abort: function() {
- ABORT = true;
- throw 'abort() at ' + (new Error().stack);
+ Module['abort']();
},
bsearch: function(key, base, num, size, compar) {
@@ -4463,6 +4439,13 @@ LibraryManager.library = {
llvm_memmove_p0i8_p0i8_i32: 'memmove',
llvm_memmove_p0i8_p0i8_i64: 'memmove',
+ bcopy__deps: ['memmove'],
+ bcopy: function(src, dest, num) {
+ // void bcopy(const void *s1, void *s2, size_t n);
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/bcopy.html
+ _memmove(dest, src, num);
+ },
+
memset__inline: function(ptr, value, num, align) {
return makeSetValues(ptr, 0, value, 'null', num, align);
},
@@ -4951,7 +4934,17 @@ LibraryManager.library = {
(chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}});
},
isspace: function(chr) {
- return chr in { 32: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0 };
+ switch(chr) {
+ case 32:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ return true;
+ default:
+ return false;
+ };
},
isblank: function(chr) {
return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}};
@@ -6165,9 +6158,14 @@ LibraryManager.library = {
{{{ makeSetValue('tmPtr', 'offsets.tm_wday', 'date.getUTCDay()', 'i32') }}}
{{{ makeSetValue('tmPtr', 'offsets.tm_gmtoff', '0', 'i32') }}}
{{{ makeSetValue('tmPtr', 'offsets.tm_isdst', '0', 'i32') }}}
-
- var start = new Date(date.getFullYear(), 0, 1);
- var yday = Math.round((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
+ var start = new Date(date); // define date using UTC, start from Jan 01 00:00:00 UTC
+ start.setUTCDate(1);
+ start.setUTCMonth(0);
+ start.setUTCHours(0);
+ start.setUTCMinutes(0);
+ start.setUTCSeconds(0);
+ start.setUTCMilliseconds(0);
+ var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
{{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}}
var timezone = "GMT";
@@ -6178,7 +6176,6 @@ LibraryManager.library = {
return tmPtr;
},
-
timegm__deps: ['mktime'],
timegm: function(tmPtr) {
_tzset();
diff --git a/src/library_jansson.js b/src/library_jansson.js
index 93f239fc..da8c5aa8 100644
--- a/src/library_jansson.js
+++ b/src/library_jansson.js
@@ -79,7 +79,7 @@ var LibraryJansson = {
load: function(string, flags, error) {
// This is potentially a security problem.
// TODO: Make sure everything is properly escaped
- var json_obj = eval('(' + string + ')');
+ var json_obj = JSON.parse(string);
if (json_obj != null) {
// The context is an array storing all child nodes.
diff --git a/src/library_openal.js b/src/library_openal.js
index 6a97fce7..d2516559 100644
--- a/src/library_openal.js
+++ b/src/library_openal.js
@@ -5,6 +5,8 @@ var LibraryOpenAL = {
$AL: {
contexts: [],
currentContext: null,
+ QUEUE_INTERVAL: 25,
+ QUEUE_LOOKAHEAD: 100
},
alcProcessContext: function(context) {},
@@ -39,6 +41,7 @@ var LibraryOpenAL = {
alcDestroyContext: function(context) {
// Stop playback, etc
+ clearInterval(context.interval);
},
alcCloseDevice: function(device) {
@@ -54,6 +57,7 @@ var LibraryOpenAL = {
}
},
+ alcCreateContext__deps: ['updateSources'],
alcCreateContext: function(device, attrList) {
if (device != 1) {
return 0;
@@ -76,18 +80,152 @@ var LibraryOpenAL = {
}
if (ctx) {
- AL.contexts.push({ctx: ctx, err: 0, src: [], buf: []});
+ var context = {
+ ctx: ctx,
+ err: 0,
+ src: [],
+ buf: [],
+ interval: setInterval(function () { _updateSources(context); }, AL.QUEUE_INTERVAL)
+ };
+ AL.contexts.push(context);
return AL.contexts.length;
} else {
return 0;
}
},
+ updateSources__deps: ['updateSource'],
+ updateSources: function (context) {
+ for (var i = 0; i < context.src.length; i++) {
+ _updateSource(context.src[i]);
+ }
+ },
+
+ updateSource__deps: ['setSourceState'],
+ updateSource: function (src) {
+#if OPENAL_DEBUG
+ var idx = AL.currentContext.src.indexOf(src);
+#endif
+ if (src.state !== 0x1012 /* AL_PLAYING */) {
+ return;
+ }
+
+ var currentTime = AL.currentContext.ctx.currentTime;
+ var startTime = src.bufferPosition;
+
+ for (var i = src.buffersPlayed; i < src.queue.length; i++) {
+ var entry = src.queue[i];
+
+ var startOffset = startTime - currentTime;
+ var endTime = startTime + entry.buffer.duration;
+
+ // Clean up old buffers.
+ if (currentTime >= endTime) {
+ // Update our location in the queue.
+ src.bufferPosition = endTime;
+ src.buffersPlayed = i + 1;
+
+ // Stop / restart the source when we hit the end.
+ if (src.buffersPlayed >= src.queue.length) {
+ if (src.loop) {
+ _setSourceState(src, 0x1012 /* AL_PLAYING */);
+ } else {
+ _setSourceState(src, 0x1014 /* AL_STOPPED */);
+ }
+ }
+ }
+ // Process all buffers that'll be played before the next tick.
+ else if (startOffset < (AL.QUEUE_LOOKAHEAD / 1000) && !entry.src) {
+ // If the start offset is negative, we need to offset the actual buffer.
+ var offset = Math.abs(Math.min(startOffset, 0));
+
+ entry.src = AL.currentContext.ctx.createBufferSource();
+ entry.src.buffer = entry.buffer;
+ entry.src.connect(src.gain);
+ entry.src.start(startTime, offset);
+
+#if OPENAL_DEBUG
+ console.log('updateSource queuing buffer ' + i + ' for source ' + idx + ' at ' + startTime + ' (offset by ' + offset + ')');
+#endif
+ }
+
+ startTime = endTime;
+ }
+ },
+
+ setSourceState__deps: ['updateSource', 'stopSourceQueue'],
+ setSourceState: function (src, state) {
+#if OPENAL_DEBUG
+ var idx = AL.currentContext.src.indexOf(src);
+#endif
+ if (state === 0x1012 /* AL_PLAYING */) {
+ if (src.state !== 0x1013 /* AL_PAUSED */) {
+ src.state = 0x1012 /* AL_PLAYING */;
+ // Reset our position.
+ src.bufferPosition = AL.currentContext.ctx.currentTime;
+ src.buffersPlayed = 0;
+#if OPENAL_DEBUG
+ console.log('setSourceState resetting and playing source ' + idx);
+#endif
+ } else {
+ src.state = 0x1012 /* AL_PLAYING */;
+ // Use the current offset from src.bufferPosition to resume at the correct point.
+ src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition;
+#if OPENAL_DEBUG
+ console.log('setSourceState resuming source ' + idx + ' at ' + src.bufferPosition.toFixed(4));
+#endif
+ }
+ _stopSourceQueue(src);
+ _updateSource(src);
+ } else if (state === 0x1013 /* AL_PAUSED */) {
+ if (src.state === 0x1012 /* AL_PLAYING */) {
+ src.state = 0x1013 /* AL_PAUSED */;
+ // Store off the current offset to restore with on resume.
+ src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition;
+ _stopSourceQueue(src);
+#if OPENAL_DEBUG
+ console.log('setSourceState pausing source ' + idx + ' at ' + src.bufferPosition.toFixed(4));
+#endif
+ }
+ } else if (state === 0x1014 /* AL_STOPPED */) {
+ if (src.state !== 0x1011 /* AL_INITIAL */) {
+ src.state = 0x1014 /* AL_STOPPED */;
+ src.buffersPlayed = src.queue.length;
+ _stopSourceQueue(src);
+#if OPENAL_DEBUG
+ console.log('setSourceState stopping source ' + idx);
+#endif
+ }
+ } else if (state == 0x1011 /* AL_INITIAL */) {
+ if (src.state !== 0x1011 /* AL_INITIAL */) {
+ src.state = 0x1011 /* AL_INITIAL */;
+ src.bufferPosition = 0;
+ src.buffersPlayed = 0;
+#if OPENAL_DEBUG
+ console.log('setSourceState initializing source ' + idx);
+#endif
+ }
+ }
+ },
+
+ stopSourceQueue: function (src) {
+ for (var i = 0; i < src.queue.length; i++) {
+ var entry = src.queue[i];
+ if (entry.src) {
+ entry.src.stop(0);
+ entry.src = null;
+ }
+ }
+ },
+
alGetError: function() {
if (!AL.currentContext) {
return 0xA004 /* AL_INVALID_OPERATION */;
} else {
- return AL.currentContext.err;
+ // Reset error on get.
+ var err = AL.currentContext.err;
+ AL.currentContext.err = 0 /* AL_NO_ERROR */;
+ return err;
}
},
@@ -97,12 +235,12 @@ var LibraryOpenAL = {
return _alGetError();
},
- alDeleteSources: function(count, sources)
- {
+ alDeleteSources: function(count, sources) {
if (!AL.currentContext) {
#if OPENAL_DEBUG
console.error("alDeleteSources called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
for (var i = 0; i < count; ++i) {
@@ -116,57 +254,126 @@ var LibraryOpenAL = {
#if OPENAL_DEBUG
console.error("alGenSources called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
for (var i = 0; i < count; ++i) {
var gain = AL.currentContext.ctx.createGain();
- var panner = AL.currentContext.ctx.createPanner();
- panner.panningModel = "equalpower";
- panner.distanceModel = "linear";
- panner.rolloffFactor = 0.3;
- gain.connect(panner);
- panner.connect(AL.currentContext.ctx.destination);
+ gain.connect(AL.currentContext.ctx.destination);
AL.currentContext.src.push({
+ state: 0x1011 /* AL_INITIAL */,
+ queue: [],
loop: false,
- buffer: null,
+ get refDistance() {
+ return this._refDistance || 1;
+ },
+ set refDistance(val) {
+ this._refDistance = val;
+ if (this.panner) this.panner.refDistance = val;
+ },
+ get maxDistance() {
+ return this._maxDistance || 10000;
+ },
+ set maxDistance(val) {
+ this._maxDistance = val;
+ if (this.panner) this.panner.maxDistance = val;
+ },
+ get rolloffFactor() {
+ return this._rolloffFactor || 1;
+ },
+ set rolloffFactor(val) {
+ this._rolloffFactor = val;
+ if (this.panner) this.panner.rolloffFactor = val;
+ },
+ get position() {
+ return this._position || [0, 0, 0];
+ },
+ set position(val) {
+ this._position = val;
+ if (this.panner) this.panner.setPosition(val[0], val[1], val[2]);
+ },
+ get velocity() {
+ return this._velocity || [0, 0, 0];
+ },
+ set velocity(val) {
+ this._velocity = val;
+ if (this.panner) this.panner.setVelocity(val[0], val[1], val[2]);
+ },
gain: gain,
- panner: panner,
- paused: false,
- playTime: -1,
- pausedTime: 0
+ panner: null,
+ buffersPlayed: 0,
+ bufferPosition: 0
});
{{{ makeSetValue('sources', 'i*4', 'AL.currentContext.src.length', 'i32') }}};
}
},
+ alSourcei__deps: ['updateSource'],
alSourcei: function(source, param, value) {
if (!AL.currentContext) {
#if OPENAL_DEBUG
console.error("alSourcei called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
- if (source > AL.currentContext.src.length) {
+ var src = AL.currentContext.src[source - 1];
+ if (!src) {
#if OPENAL_DEBUG
console.error("alSourcei called with an invalid source");
#endif
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
return;
}
switch (param) {
case 0x1007 /* AL_LOOPING */:
- AL.currentContext.src[source - 1].loop = (value != 0 /* AL_FALSE */);
+ src.loop = (value === 1 /* AL_TRUE */);
break;
case 0x1009 /* AL_BUFFER */:
+ var buffer = AL.currentContext.buf[value - 1];
if (value == 0) {
- AL.currentContext.src[source - 1].buffer = null;
+ src.queue = [];
+ } else {
+ src.queue = [{ buffer: buffer }];
+ }
+ _updateSource(src);
+ break;
+ case 0x202 /* AL_SOURCE_RELATIVE */:
+ if (value === 1 /* AL_TRUE */) {
+ if (src.panner) {
+ src.panner = null;
+
+ // Disconnect from the panner.
+ src.gain.disconnect();
+
+ src.gain.connect(AL.currentContext.ctx.destination);
+ }
+ } else if (value === 0 /* AL_FALSE */) {
+ if (!src.panner) {
+ var panner = src.panner = AL.currentContext.ctx.createPanner();
+ panner.panningModel = "equalpower";
+ panner.distanceModel = "linear";
+ panner.refDistance = src.refDistance;
+ panner.maxDistance = src.maxDistance;
+ panner.rolloffFactor = src.rolloffFactor;
+ panner.setPosition(src.position[0], src.position[1], src.position[2]);
+ panner.setVelocity(src.velocity[0], src.velocity[1], src.velocity[2]);
+ panner.connect(AL.currentContext.ctx.destination);
+
+ // Disconnect from the default source.
+ src.gain.disconnect();
+
+ src.gain.connect(panner);
+ }
} else {
- AL.currentContext.src[source - 1].buffer = AL.currentContext.buf[value - 1].buf;
+ AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */;
}
break;
default:
#if OPENAL_DEBUG
console.log("alSourcei with param " + param + " not implemented yet");
#endif
+ AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
break;
}
},
@@ -176,130 +383,168 @@ var LibraryOpenAL = {
#if OPENAL_DEBUG
console.error("alSourcef called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
- if (source > AL.currentContext.src.length) {
+ var src = AL.currentContext.src[source - 1];
+ if (!src) {
#if OPENAL_DEBUG
console.error("alSourcef called with an invalid source");
#endif
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
return;
}
switch (param) {
- case 0x100A /* AL_GAIN */:
- if (AL.currentContext.src[source - 1]) {
- AL.currentContext.src[source - 1].gain.gain.value = value;
- }
- break;
case 0x1003 /* AL_PITCH */:
#if OPENAL_DEBUG
- console.log("alSourcef was called with AL_PITCH, but Web Audio does not support static pitch changes");
+ console.log("alSourcef was called with 0x1003 /* AL_PITCH */, but Web Audio does not support static pitch changes");
#endif
break;
+ case 0x100A /* AL_GAIN */:
+ src.gain.gain.value = value;
+ break;
+ // case 0x100D /* AL_MIN_GAIN */:
+ // break;
+ // case 0x100E /* AL_MAX_GAIN */:
+ // break;
+ case 0x1023 /* AL_MAX_DISTANCE */:
+ src.maxDistance = value;
+ break;
+ case 0x1021 /* AL_ROLLOFF_FACTOR */:
+ src.rolloffFactor = value;
+ break;
+ // case 0x1022 /* AL_CONE_OUTER_GAIN */:
+ // break;
+ // case 0x1001 /* AL_CONE_INNER_ANGLE */:
+ // break;
+ // case 0x1002 /* AL_CONE_OUTER_ANGLE */:
+ // break;
+ case 0x1020 /* AL_REFERENCE_DISTANCE */:
+ src.refDistance = value;
+ break;
default:
#if OPENAL_DEBUG
console.log("alSourcef with param " + param + " not implemented yet");
#endif
+ AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
break;
}
},
- alSourcefv: function(source, param, value) {
+ alSource3f: function(source, param, v1, v2, v3) {
if (!AL.currentContext) {
#if OPENAL_DEBUG
- console.error("alSourcefv called without a valid context");
+ console.error("alSource3f called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
- if (source > AL.currentContext.src.length) {
+ var src = AL.currentContext.src[source - 1];
+ if (!src) {
#if OPENAL_DEBUG
- console.error("alSourcefv called with an invalid source");
+ console.error("alSource3f called with an invalid source");
#endif
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
return;
}
switch (param) {
case 0x1004 /* AL_POSITION */:
- AL.currentContext.src[source - 1].panner.setPosition(
- {{{ makeGetValue('value', '0', 'float') }}},
- {{{ makeGetValue('value', '4', 'float') }}},
- {{{ makeGetValue('value', '8', 'float') }}}
- );
+ src.position = [v1, v2, v3];
break;
case 0x1006 /* AL_VELOCITY */:
- AL.currentContext.src[source - 1].panner.setVelocity(
- {{{ makeGetValue('value', '0', 'float') }}},
- {{{ makeGetValue('value', '4', 'float') }}},
- {{{ makeGetValue('value', '8', 'float') }}}
- );
+ src.velocity = [v1, v2, v3];
break;
default:
#if OPENAL_DEBUG
- console.log("alSourcefv with param " + param + " not implemented yet");
+ console.log("alSource3f with param " + param + " not implemented yet");
#endif
+ AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
break;
}
},
+ alSourcefv__deps: ['alSource3f'],
+ alSourcefv: function(source, param, value) {
+ _alSource3f(source, param,
+ {{{ makeGetValue('value', '0', 'float') }}},
+ {{{ makeGetValue('value', '4', 'float') }}},
+ {{{ makeGetValue('value', '8', 'float') }}});
+ },
+
+ alSourceQueueBuffers__deps: ["updateSource"],
alSourceQueueBuffers: function(source, count, buffers) {
if (!AL.currentContext) {
#if OPENAL_DEBUG
console.error("alSourceQueueBuffers called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
- if (source > AL.currentContext.src.length) {
+ var src = AL.currentContext.src[source - 1];
+ if (!src) {
#if OPENAL_DEBUG
console.error("alSourceQueueBuffers called with an invalid source");
#endif
- return;
- }
- if (count != 1) {
-#if OPENAL_DEBUG
- console.error("Queuing multiple buffers using alSourceQueueBuffers is not supported yet");
-#endif
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
return;
}
for (var i = 0; i < count; ++i) {
- var buffer = {{{ makeGetValue('buffers', 'i*4', 'i32') }}};
- if (buffer > AL.currentContext.buf.length) {
+ var bufferIdx = {{{ makeGetValue('buffers', 'i*4', 'i32') }}};
+ if (bufferIdx > AL.currentContext.buf.length) {
#if OPENAL_DEBUG
console.error("alSourceQueueBuffers called with an invalid buffer");
#endif
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
return;
}
- AL.currentContext.src[source - 1].buffer = AL.currentContext.buf[buffer - 1].buf;
}
+
+ for (var i = 0; i < count; ++i) {
+ var bufferIdx = {{{ makeGetValue('buffers', 'i*4', 'i32') }}};
+ var buffer = AL.currentContext.buf[bufferIdx - 1];
+ src.queue.push({ buffer: buffer, src: null });
+ }
+
+ _updateSource(src);
},
- alSourceUnqueueBuffers: function(source, count, buffers)
- {
+ alSourceUnqueueBuffers__deps: ["updateSource"],
+ alSourceUnqueueBuffers: function(source, count, buffers) {
if (!AL.currentContext) {
#if OPENAL_DEBUG
console.error("alSourceUnqueueBuffers called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
- if (source > AL.currentContext.src.length) {
+ var src = AL.currentContext.src[source - 1];
+ if (!src) {
#if OPENAL_DEBUG
console.error("alSourceUnqueueBuffers called with an invalid source");
#endif
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
return;
}
- if (count != 1) {
-#if OPENAL_DEBUG
- console.error("Queuing multiple buffers using alSourceUnqueueBuffers is not supported yet");
-#endif
+
+ if (count > src.buffersPlayed) {
+ AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */;
return;
}
- for (var i = 0; i < count; ++i) {
- var buffer = AL.currentContext.src[source - 1].buffer;
- for (var j = 0; j < AL.currentContext.buf.length; ++j) {
- if (buffer == AL.currentContext.buf[j].buf) {
+
+ for (var i = 0; i < count; i++) {
+ var entry = src.queue.shift();
+ // Write the buffers index out to the return list.
+ for (var j = 0; j < AL.currentContext.buf.length; j++) {
+ var b = AL.currentContext.buf[j];
+ if (b && b == entry.buffer) {
{{{ makeSetValue('buffers', 'i*4', 'j+1', 'i32') }}};
- AL.currentContext.src[source - 1].buffer = null;
break;
}
}
+ src.buffersPlayed--;
}
+
+ _updateSource(src);
},
alDeleteBuffers: function(count, buffers)
@@ -308,21 +553,43 @@ var LibraryOpenAL = {
#if OPENAL_DEBUG
console.error("alDeleteBuffers called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
+ return;
+ }
+ if (count > AL.currentContext.buf.length) {
+ AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */;
return;
}
+
for (var i = 0; i < count; ++i) {
var bufferIdx = {{{ makeGetValue('buffers', 'i*4', 'i32') }}} - 1;
- if (bufferIdx < AL.currentContext.buf.length && AL.currentContext.buf[bufferIdx]) {
- var buffer = AL.currentContext.buf[bufferIdx].buf;
- for (var j = 0; j < AL.currentContext.src.length; ++j) {
- if (buffer == AL.currentContext.src[j].buffer) {
- AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
+
+ // Make sure the buffer index is valid.
+ if (bufferIdx >= AL.currentContext.buf.length || !AL.currentContext.buf[bufferIdx]) {
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
+ return;
+ }
+
+ // Make sure the buffer is no longer in use.
+ var buffer = AL.currentContext.buf[bufferIdx];
+ for (var j = 0; j < AL.currentContext.src.length; ++j) {
+ var src = AL.currentContext.src[j];
+ if (!src) {
+ continue;
+ }
+ for (var k = 0; k < src.queue.length; k++) {
+ if (buffer === src.queue[k].buffer) {
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
}
- delete AL.currentContext.buf[bufferIdx];
}
}
+
+ for (var i = 0; i < count; ++i) {
+ var bufferIdx = {{{ makeGetValue('buffers', 'i*4', 'i32') }}} - 1;
+ delete AL.currentContext.buf[bufferIdx];
+ }
},
alGenBuffers: function(count, buffers) {
@@ -330,10 +597,11 @@ var LibraryOpenAL = {
#if OPENAL_DEBUG
console.error("alGenBuffers called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
for (var i = 0; i < count; ++i) {
- AL.currentContext.buf.push({buf: null});
+ AL.currentContext.buf.push(null);
{{{ makeSetValue('buffers', 'i*4', 'AL.currentContext.buf.length', 'i32') }}};
}
},
@@ -343,6 +611,7 @@ var LibraryOpenAL = {
#if OPENAL_DEBUG
console.error("alBufferData called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
if (buffer > AL.currentContext.buf.length) {
@@ -375,16 +644,21 @@ var LibraryOpenAL = {
#endif
return;
}
- AL.currentContext.buf[buffer - 1].buf = AL.currentContext.ctx.createBuffer(channels, size / (bytes * channels), freq);
+ try {
+ AL.currentContext.buf[buffer - 1] = AL.currentContext.ctx.createBuffer(channels, size / (bytes * channels), freq);
+ } catch (e) {
+ AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */;
+ return;
+ }
var buf = new Array(channels);
for (var i = 0; i < channels; ++i) {
- buf[i] = AL.currentContext.buf[buffer - 1].buf.getChannelData(i);
+ buf[i] = AL.currentContext.buf[buffer - 1].getChannelData(i);
}
for (var i = 0; i < size / (bytes * channels); ++i) {
for (var j = 0; j < channels; ++j) {
switch (bytes) {
case 1:
- var val = {{{ makeGetValue('data', 'i*channels+j', 'i8') }}};
+ var val = {{{ makeGetValue('data', 'i*channels+j', 'i8') }}} & 0xff; // unsigned
buf[j][i] = -1.0 + val * (2/256);
break;
case 2:
@@ -396,110 +670,105 @@ var LibraryOpenAL = {
}
},
- alSourcePlay__deps: ["alSourceStop"],
+ alSourcePlay__deps: ['setSourceState'],
alSourcePlay: function(source) {
if (!AL.currentContext) {
#if OPENAL_DEBUG
console.error("alSourcePlay called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
- if (source > AL.currentContext.src.length) {
+ var src = AL.currentContext.src[source - 1];
+ if (!src) {
#if OPENAL_DEBUG
console.error("alSourcePlay called with an invalid source");
#endif
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
return;
}
- var offset = 0;
- if ("src" in AL.currentContext.src[source - 1] &&
- AL.currentContext.src[source - 1]["src"].buffer ==
- AL.currentContext.src[source - 1].buffer) {
- if (AL.currentContext.src[source - 1].paused) {
- // So now we have to resume playback, remember the offset here.
- offset = AL.currentContext.src[source - 1].pausedTime -
- AL.currentContext.src[source - 1].playTime;
- } else {
- // If the source is already playing, we need to resume from beginning.
- // We do that by stopping the current source and replaying it.
- _alSourceStop(source);
- }
- }
- var src = AL.currentContext.ctx.createBufferSource();
- src.loop = AL.currentContext.src[source - 1].loop;
- src.buffer = AL.currentContext.src[source - 1].buffer;
- src.connect(AL.currentContext.src[source - 1].gain);
- src.start(0, offset);
- AL.currentContext.src[source - 1].playTime = AL.currentContext.ctx.currentTime;
- AL.currentContext.src[source - 1].paused = false;
- AL.currentContext.src[source - 1]['src'] = src;
+ _setSourceState(src, 0x1012 /* AL_PLAYING */);
},
+ alSourceStop__deps: ['setSourceState'],
alSourceStop: function(source) {
if (!AL.currentContext) {
#if OPENAL_DEBUG
console.error("alSourceStop called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
- if (source > AL.currentContext.src.length) {
+ var src = AL.currentContext.src[source - 1];
+ if (!src) {
#if OPENAL_DEBUG
console.error("alSourceStop called with an invalid source");
#endif
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
return;
}
- if (AL.currentContext.src[source - 1] && "src" in AL.currentContext.src[source - 1]) {
- AL.currentContext.src[source - 1]["src"].stop(0);
- delete AL.currentContext.src[source - 1]["src"];
- }
+ _setSourceState(src, 0x1014 /* AL_STOPPED */);
},
+ alSourcePause__deps: ['setSourceState'],
alSourcePause: function(source) {
if (!AL.currentContext) {
#if OPENAL_DEBUG
console.error("alSourcePause called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
- if (source > AL.currentContext.src.length) {
+ var src = AL.currentContext.src[source - 1];
+ if (!src) {
#if OPENAL_DEBUG
console.error("alSourcePause called with an invalid source");
#endif
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
return;
}
- if ("src" in AL.currentContext.src[source - 1] &&
- !AL.currentContext.src[source - 1].paused) {
- AL.currentContext.src[source - 1].paused = true;
- AL.currentContext.src[source - 1].pausedTime = AL.currentContext.ctx.currentTime;
- AL.currentContext.src[source - 1]["src"].stop(0);
- delete AL.currentContext.src[source - 1].src;
- }
+ _setSourceState(src, 0x1013 /* AL_PAUSED */);
},
+ alGetSourcei__deps: ['updateSource'],
alGetSourcei: function(source, param, value) {
if (!AL.currentContext) {
#if OPENAL_DEBUG
console.error("alGetSourcei called without a valid context");
#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
return;
}
- if (source > AL.currentContext.src.length) {
+ var src = AL.currentContext.src[source - 1];
+ if (!src) {
#if OPENAL_DEBUG
console.error("alGetSourcei called with an invalid source");
#endif
+ AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */;
return;
}
+
+ // Being that we have no way to receive end events from buffer nodes,
+ // we currently proccess and update a source's buffer queue every
+ // ~QUEUE_INTERVAL milliseconds. However, this interval is not precise,
+ // so we also forcefully update the source when alGetSourcei is queried
+ // to aid in the common scenario of application calling alGetSourcei(AL_BUFFERS_PROCESSED)
+ // to recycle buffers.
+ _updateSource(src);
+
switch (param) {
case 0x202 /* AL_SOURCE_RELATIVE */:
- // Always return 1
- {{{ makeSetValue('value', '0', '1', 'i32') }}};
+ {{{ makeSetValue('value', '0', 'src.panner ? 1 : 0', 'i32') }}};
break;
case 0x1009 /* AL_BUFFER */:
- if (AL.currentContext.src[source - 1].buffer == null) {
+ if (!src.queue.length) {
{{{ makeSetValue('value', '0', '0', 'i32') }}};
} else {
- var buf = AL.currentContext.src[source - 1].buffer;
+ // Find the first unprocessed buffer.
+ var buffer = src.queue[src.buffersPlayed].buffer;
+ // Return its index.
for (var i = 0; i < AL.currentContext.buf.length; ++i) {
- if (buf == AL.currentContext.buf[i].buf) {
+ if (buffer == AL.currentContext.buf[i]) {
{{{ makeSetValue('value', '0', 'i+1', 'i32') }}};
return;
}
@@ -508,32 +777,79 @@ var LibraryOpenAL = {
}
break;
case 0x1010 /* AL_SOURCE_STATE */:
- if ("src" in AL.currentContext.src[source - 1]) {
- {{{ makeSetValue('value', '0', '0x1012', 'i32') }}} /* AL_PLAYING */;
- } else if (AL.currentContext.src[source - 1].paused) {
- {{{ makeSetValue('value', '0', '0x1013', 'i32') }}} /* AL_PAUSED */;
- } else if (AL.currentContext.src[source - 1].playTime == -1) {
- {{{ makeSetValue('value', '0', '0x1011', 'i32') }}} /* AL_INITIAL */;
- } else {
- {{{ makeSetValue('value', '0', '0x1014', 'i32') }}} /* AL_STOPPED */;
- }
+ {{{ makeSetValue('value', '0', 'src.state', 'i32') }}};
break;
case 0x1015 /* AL_BUFFERS_QUEUED */:
- if (AL.currentContext.src[source - 1].buffer) {
- {{{ makeSetValue('value', '0', '1', 'i32') }}}
- } else {
+ {{{ makeSetValue('value', '0', 'src.queue.length', 'i32') }}}
+ break;
+ case 0x1016 /* AL_BUFFERS_PROCESSED */:
+ if (src.loop) {
{{{ makeSetValue('value', '0', '0', 'i32') }}}
+ } else {
+ {{{ makeSetValue('value', '0', 'src.buffersPlayed', 'i32') }}}
}
break;
- case 0x1016 /* AL_BUFFERS_PROCESSED */:
- // Always return 1
- {{{ makeSetValue('value', '0', '1', 'i32') }}}
+ default:
+ AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */;
+ break;
+ }
+ },
+
+ alGetSourcef: function(source, param, value) {
+ if (!AL.currentContext) {
+#if OPENAL_DEBUG
+ console.error("alGetSourcef called without a valid context");
+#endif
+ AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */;
+ return;
+ }
+ var