diff options
author | Anthony Pesch <anthony@usamp.com> | 2013-05-02 14:05:50 -0700 |
---|---|---|
committer | Anthony Pesch <inolen@gmail.com> | 2013-07-30 17:45:32 -0700 |
commit | c237c138428baacf6a55dd0cf30a5ea0d98bb743 (patch) | |
tree | edfc0aa378e507096a975e9c6ebcec0faac326d1 /src | |
parent | 6fd2313192352995c36d1097e18f591dc65d81d1 (diff) |
First pass at buffer queueing
Diffstat (limited to 'src')
-rw-r--r-- | src/library_openal.js | 572 |
1 files changed, 440 insertions, 132 deletions
diff --git a/src/library_openal.js b/src/library_openal.js index 6a97fce7..c628bfd2 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,131 @@ 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 (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 offset is positive, we need to delay playing the sound that amount. + var when = Math.max(startOffset, 0); + // If the offset is negative, we need to play immediately, offset by its absolute value (print warning?). + 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(when, offset); + + // console.log('playing buffer (' + i + ', ' + entry.buffer.duration.toFixed(4) + ')\t(startTime: ' + startTime.toFixed(4) + ')\t(currentTime: ' + currentTime.toFixed(4) + ')\t(when: ' + when.toFixed(4) + ') ' + ')\t(offset: ' + offset.toFixed(4) + ') '); + } + + startTime = endTime; + } + }, + + setSourceState__deps: ['updateSource', 'stopSourceQueue'], + setSourceState: function (src, state) { + 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; + } 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; + } + _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); + } + } else if (state === 0x1014 /* AL_STOPPED */) { + if (src.state !== 0x1011 /* AL_INITIAL */) { + src.state = 0x1014 /* AL_STOPPED */; + src.buffersPlayed = src.queue.length; + _stopSourceQueue(src); + } + } else if (state == 0x1011 /* AL_INITIAL */) { + if (src.state !== 0x1011 /* AL_INITIAL */) { + src.state = 0x1011 /* AL_INITIAL */; + src.bufferPosition = 0; + src.buffersPlayed = 0; + } + } + }, + + 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 +214,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,189 +233,298 @@ 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) { + var src; 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) { + if (source > AL.currentContext.src.length || + !(src = AL.currentContext.src[source - 1])) { #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 { - AL.currentContext.src[source - 1].buffer = AL.currentContext.buf[value - 1].buf; + 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.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; } }, alSourcef: function(source, param, value) { + var src; if (!AL.currentContext) { #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) { + if (source > AL.currentContext.src.length || + !(src = AL.currentContext.src[source - 1])) { #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) { + var src; 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) { + if (source > AL.currentContext.src.length || + !(src = AL.currentContext.src[source - 1])) { #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: 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) { + var src; 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) { + if (source > AL.currentContext.src.length || + !(src = AL.currentContext.src[source - 1])) { #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: function(source, count, buffers) { + var src; 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) { + if (source > AL.currentContext.src.length || + !(src = AL.currentContext.src[source - 1])) { #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; + src.buffer = null; break; } } + src.buffersPlayed--; } }, @@ -308,21 +534,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 +578,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 +592,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 +625,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 +651,99 @@ var LibraryOpenAL = { } }, - alSourcePlay__deps: ["alSourceStop"], + alSourcePlay__deps: ['setSourceState'], alSourcePlay: function(source) { + var src; 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) { + if (source > AL.currentContext.src.length || + !(src = AL.currentContext.src[source - 1])) { #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) { + var src; 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) { + if (source > AL.currentContext.src.length || + !(src = AL.currentContext.src[source - 1])) { #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) { + var src; 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) { + if (source > AL.currentContext.src.length || + !(src = AL.currentContext.src[source - 1])) { #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: function(source, param, value) { + var src; 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) { + if (source > AL.currentContext.src.length || + !(src = AL.currentContext.src[source - 1])) { #if OPENAL_DEBUG console.error("alGetSourcei called with an invalid source"); #endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } 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 +752,80 @@ 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) { + var src; + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alGetSourcef called without a valid context"); +#endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; + return; + } + if (source > AL.currentContext.src.length || + !(src = AL.currentContext.src[source - 1])) { +#if OPENAL_DEBUG + console.error("alGetSourcef called with an invalid source"); +#endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; + return; + } + switch (param) { + // case 0x1003 /* AL_PITCH */: + // break; + case 0x100A /* AL_GAIN */: + {{{ makeSetValue('value', '0', 'src.gain.gain.value', 'float') }}} + break; + // case 0x100D /* AL_MIN_GAIN */: + // break; + // case 0x100E /* AL_MAX_GAIN */: + // break; + case 0x1023 /* AL_MAX_DISTANCE */: + {{{ makeSetValue('value', '0', 'src.maxDistance', 'float') }}} + break; + case 0x1021 /* AL_ROLLOFF_FACTOR */: + {{{ makeSetValue('value', '0', 'src.rolloffFactor', 'float') }}} + 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 */: + {{{ makeSetValue('value', '0', 'src.refDistance', 'float') }}} + break; + // case 0x1024 /* AL_SEC_OFFSET */: + // break; + // case 0x1025 /* AL_SAMPLE_OFFSET */: + // break; + // case 0x1026 /* AL_BYTE_OFFSET */: + // break; + default: + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; break; } }, alDistanceModel: function(model) { - if (model != 0 /* AL_NONE */) { + if (model !== 0 /* AL_NONE */) { #if OPENAL_DEBUG console.log("Only alDistanceModel(AL_NONE) is currently supported"); #endif @@ -545,6 +837,7 @@ var LibraryOpenAL = { #if OPENAL_DEBUG console.error("alListenerfv called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } switch (param) { @@ -576,6 +869,7 @@ var LibraryOpenAL = { #if OPENAL_DEBUG console.log("alListenerfv with param " + param + " not implemented yet"); #endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; break; } }, @@ -588,13 +882,27 @@ var LibraryOpenAL = { return 0; }, + alGetString: function (param) { + return allocate(intArrayFromString('NA'), 'i8', ALLOC_NORMAL); + }, + alGetProcAddress: function(fname) { return 0; }, + alcGetString: function (param) { + return allocate(intArrayFromString('NA'), 'i8', ALLOC_NORMAL); + }, + alcGetProcAddress: function(device, fname) { return 0; }, + + alDopplerFactor: function(value) { + }, + + alDopplerVelocity: function(value) { + } }; autoAddDeps(LibraryOpenAL, '$AL'); |