| //"use strict"; |
| |
| var LibraryOpenAL = { |
| $AL__deps: ['$Browser'], |
| $AL: { |
| contexts: [], |
| currentContext: null, |
| }, |
| |
| alcProcessContext: function(context) {}, |
| alcSuspendContext: function(context) {}, |
| |
| alcMakeContextCurrent: function(context) { |
| if (context == 0) { |
| AL.currentContext = null; |
| return 0; |
| } else { |
| AL.currentContext = AL.contexts[context - 1]; |
| return 1; |
| } |
| }, |
| |
| alcGetContextsDevice: function(context) { |
| if (context <= AL.contexts.length && context > 0) { |
| // Returns the only one audio device |
| return 1; |
| } |
| return 0; |
| }, |
| |
| alcGetCurrentContext: function() { |
| for (var i = 0; i < AL.contexts.length; ++i) { |
| if (AL.contexts[i] == AL.currentContext) { |
| return i + 1; |
| } |
| } |
| return 0; |
| }, |
| |
| alcDestroyContext: function(context) { |
| // Stop playback, etc |
| }, |
| |
| alcCloseDevice: function(device) { |
| // Stop playback, etc |
| }, |
| |
| alcOpenDevice: function(deviceName) { |
| if (typeof(AudioContext) == "function" || |
| typeof(webkitAudioContext) == "function") { |
| return 1; // non-null pointer -- we just simulate one device |
| } else { |
| return 0; |
| } |
| }, |
| |
| alcCreateContext: function(device, attrList) { |
| if (device != 1) { |
| return 0; |
| } |
| |
| if (attrList) { |
| #if OPENAL_DEBUG |
| console.log("The attrList argument of alcCreateContext is not supported yet"); |
| #endif |
| return 0; |
| } |
| |
| var ctx; |
| try { |
| ctx = new AudioContext(); |
| } catch (e) { |
| try { |
| ctx = new webkitAudioContext(); |
| } catch (e) {} |
| } |
| |
| if (ctx) { |
| AL.contexts.push({ctx: ctx, err: 0, src: [], buf: []}); |
| return AL.contexts.length; |
| } else { |
| return 0; |
| } |
| }, |
| |
| alGetError: function() { |
| if (!AL.currentContext) { |
| return 0xA004 /* AL_INVALID_OPERATION */; |
| } else { |
| return AL.currentContext.err; |
| } |
| }, |
| |
| alcGetError__deps: ['alGetError'], |
| alcGetError: function(device) { |
| // We have only one audio device, so just return alGetError. |
| return _alGetError(); |
| }, |
| |
| alDeleteSources: function(count, sources) |
| { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alDeleteSources called without a valid context"); |
| #endif |
| return; |
| } |
| for (var i = 0; i < count; ++i) { |
| var sourceIdx = {{{ makeGetValue('sources', 'i*4', 'i32') }}} - 1; |
| delete AL.currentContext.src[sourceIdx]; |
| } |
| }, |
| |
| alGenSources: function(count, sources) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alGenSources called without a valid context"); |
| #endif |
| 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); |
| AL.currentContext.src.push({ |
| loop: false, |
| buffer: null, |
| gain: gain, |
| panner: panner, |
| paused: false, |
| playTime: -1, |
| pausedTime: 0 |
| }); |
| {{{ makeSetValue('sources', 'i*4', 'AL.currentContext.src.length', 'i32') }}}; |
| } |
| }, |
| |
| alSourcei: function(source, param, value) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alSourcei called without a valid context"); |
| #endif |
| return; |
| } |
| if (source > AL.currentContext.src.length) { |
| #if OPENAL_DEBUG |
| console.error("alSourcei called with an invalid source"); |
| #endif |
| return; |
| } |
| switch (param) { |
| case 0x1007 /* AL_LOOPING */: |
| AL.currentContext.src[source - 1].loop = (value != 0 /* AL_FALSE */); |
| break; |
| case 0x1009 /* AL_BUFFER */: |
| if (value == 0) { |
| AL.currentContext.src[source - 1].buffer = null; |
| } else { |
| AL.currentContext.src[source - 1].buffer = AL.currentContext.buf[value - 1].buf; |
| } |
| break; |
| default: |
| #if OPENAL_DEBUG |
| console.log("alSourcei with param " + param + " not implemented yet"); |
| #endif |
| break; |
| } |
| }, |
| |
| alSourcef: function(source, param, value) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alSourcef called without a valid context"); |
| #endif |
| return; |
| } |
| if (source > AL.currentContext.src.length) { |
| #if OPENAL_DEBUG |
| console.error("alSourcef called with an invalid source"); |
| #endif |
| 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"); |
| #endif |
| break; |
| default: |
| #if OPENAL_DEBUG |
| console.log("alSourcef with param " + param + " not implemented yet"); |
| #endif |
| break; |
| } |
| }, |
| |
| alSourcefv: function(source, param, value) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alSourcefv called without a valid context"); |
| #endif |
| return; |
| } |
| if (source > AL.currentContext.src.length) { |
| #if OPENAL_DEBUG |
| console.error("alSourcefv called with an invalid source"); |
| #endif |
| 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') }}} |
| ); |
| break; |
| case 0x1006 /* AL_VELOCITY */: |
| AL.currentContext.src[source - 1].panner.setVelocity( |
| {{{ makeGetValue('value', '0', 'float') }}}, |
| {{{ makeGetValue('value', '4', 'float') }}}, |
| {{{ makeGetValue('value', '8', 'float') }}} |
| ); |
| break; |
| default: |
| #if OPENAL_DEBUG |
| console.log("alSourcefv with param " + param + " not implemented yet"); |
| #endif |
| break; |
| } |
| }, |
| |
| alSourceQueueBuffers: function(source, count, buffers) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alSourceQueueBuffers called without a valid context"); |
| #endif |
| return; |
| } |
| if (source > AL.currentContext.src.length) { |
| #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 |
| return; |
| } |
| for (var i = 0; i < count; ++i) { |
| var buffer = {{{ makeGetValue('buffers', 'i*4', 'i32') }}}; |
| if (buffer > AL.currentContext.buf.length) { |
| #if OPENAL_DEBUG |
| console.error("alSourceQueueBuffers called with an invalid buffer"); |
| #endif |
| return; |
| } |
| AL.currentContext.src[source - 1].buffer = AL.currentContext.buf[buffer - 1].buf; |
| } |
| }, |
| |
| alSourceUnqueueBuffers: function(source, count, buffers) |
| { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alSourceUnqueueBuffers called without a valid context"); |
| #endif |
| return; |
| } |
| if (source > AL.currentContext.src.length) { |
| #if OPENAL_DEBUG |
| console.error("alSourceUnqueueBuffers called with an invalid source"); |
| #endif |
| return; |
| } |
| if (count != 1) { |
| #if OPENAL_DEBUG |
| console.error("Queuing multiple buffers using alSourceUnqueueBuffers is not supported yet"); |
| #endif |
| 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) { |
| {{{ makeSetValue('buffers', 'i*4', 'j+1', 'i32') }}}; |
| AL.currentContext.src[source - 1].buffer = null; |
| break; |
| } |
| } |
| } |
| }, |
| |
| alDeleteBuffers: function(count, buffers) |
| { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alDeleteBuffers called without a valid context"); |
| #endif |
| 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 */; |
| return; |
| } |
| } |
| delete AL.currentContext.buf[bufferIdx]; |
| } |
| } |
| }, |
| |
| alGenBuffers: function(count, buffers) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alGenBuffers called without a valid context"); |
| #endif |
| return; |
| } |
| for (var i = 0; i < count; ++i) { |
| AL.currentContext.buf.push({buf: null}); |
| {{{ makeSetValue('buffers', 'i*4', 'AL.currentContext.buf.length', 'i32') }}}; |
| } |
| }, |
| |
| alBufferData: function(buffer, format, data, size, freq) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alBufferData called without a valid context"); |
| #endif |
| return; |
| } |
| if (buffer > AL.currentContext.buf.length) { |
| #if OPENAL_DEBUG |
| console.error("alBufferData called with an invalid buffer"); |
| #endif |
| return; |
| } |
| var channels, bytes; |
| switch (format) { |
| case 0x1100 /* AL_FORMAT_MONO8 */: |
| bytes = 1; |
| channels = 1; |
| break; |
| case 0x1101 /* AL_FORMAT_MONO16 */: |
| bytes = 2; |
| channels = 1; |
| break; |
| case 0x1102 /* AL_FORMAT_STEREO8 */: |
| bytes = 1; |
| channels = 2; |
| break; |
| case 0x1103 /* AL_FORMAT_STEREO16 */: |
| bytes = 2; |
| channels = 2; |
| break; |
| default: |
| #if OPENAL_DEBUG |
| console.error("alBufferData called with invalid format " + format); |
| #endif |
| return; |
| } |
| AL.currentContext.buf[buffer - 1].buf = AL.currentContext.ctx.createBuffer(channels, size / (bytes * channels), freq); |
| var buf = new Array(channels); |
| for (var i = 0; i < channels; ++i) { |
| buf[i] = AL.currentContext.buf[buffer - 1].buf.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') }}}; |
| buf[j][i] = -1.0 + val * (2/256); |
| break; |
| case 2: |
| var val = {{{ makeGetValue('data', '2*(i*channels+j)', 'i16') }}}; |
| buf[j][i] = val/32768; |
| break; |
| } |
| } |
| } |
| }, |
| |
| alSourcePlay__deps: ["alSourceStop"], |
| alSourcePlay: function(source) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alSourcePlay called without a valid context"); |
| #endif |
| return; |
| } |
| if (source > AL.currentContext.src.length) { |
| #if OPENAL_DEBUG |
| console.error("alSourcePlay called with an invalid source"); |
| #endif |
| 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; |
| }, |
| |
| alSourceStop: function(source) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alSourceStop called without a valid context"); |
| #endif |
| return; |
| } |
| if (source > AL.currentContext.src.length) { |
| #if OPENAL_DEBUG |
| console.error("alSourceStop called with an invalid source"); |
| #endif |
| 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"]; |
| } |
| }, |
| |
| alSourcePause: function(source) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alSourcePause called without a valid context"); |
| #endif |
| return; |
| } |
| if (source > AL.currentContext.src.length) { |
| #if OPENAL_DEBUG |
| console.error("alSourcePause called with an invalid source"); |
| #endif |
| 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; |
| } |
| }, |
| |
| alGetSourcei: function(source, param, value) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alGetSourcei called without a valid context"); |
| #endif |
| return; |
| } |
| if (source > AL.currentContext.src.length) { |
| #if OPENAL_DEBUG |
| console.error("alGetSourcei called with an invalid source"); |
| #endif |
| return; |
| } |
| switch (param) { |
| case 0x202 /* AL_SOURCE_RELATIVE */: |
| // Always return 1 |
| {{{ makeSetValue('value', '0', '1', 'i32') }}}; |
| break; |
| case 0x1009 /* AL_BUFFER */: |
| if (AL.currentContext.src[source - 1].buffer == null) { |
| {{{ makeSetValue('value', '0', '0', 'i32') }}}; |
| } else { |
| var buf = AL.currentContext.src[source - 1].buffer; |
| for (var i = 0; i < AL.currentContext.buf.length; ++i) { |
| if (buf == AL.currentContext.buf[i].buf) { |
| {{{ makeSetValue('value', '0', 'i+1', 'i32') }}}; |
| return; |
| } |
| } |
| {{{ makeSetValue('value', '0', '0', 'i32') }}}; |
| } |
| 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 */; |
| } |
| break; |
| case 0x1015 /* AL_BUFFERS_QUEUED */: |
| if (AL.currentContext.src[source - 1].buffer) { |
| {{{ makeSetValue('value', '0', '1', 'i32') }}} |
| } else { |
| {{{ makeSetValue('value', '0', '0', 'i32') }}} |
| } |
| break; |
| case 0x1016 /* AL_BUFFERS_PROCESSED */: |
| // Always return 1 |
| {{{ makeSetValue('value', '0', '1', 'i32') }}} |
| break; |
| } |
| }, |
| |
| alDistanceModel: function(model) { |
| if (model != 0 /* AL_NONE */) { |
| #if OPENAL_DEBUG |
| console.log("Only alDistanceModel(AL_NONE) is currently supported"); |
| #endif |
| } |
| }, |
| |
| alListenerfv: function(param, values) { |
| if (!AL.currentContext) { |
| #if OPENAL_DEBUG |
| console.error("alListenerfv called without a valid context"); |
| #endif |
| return; |
| } |
| switch (param) { |
| case 0x1004 /* AL_POSITION */: |
| AL.currentContext.ctx.listener.setPosition( |
| {{{ makeGetValue('values', '0', 'float') }}}, |
| {{{ makeGetValue('values', '4', 'float') }}}, |
| {{{ makeGetValue('values', '8', 'float') }}} |
| ); |
| break; |
| case 0x1006 /* AL_VELOCITY */: |
| AL.currentContext.ctx.listener.setVelocity( |
| {{{ makeGetValue('values', '0', 'float') }}}, |
| {{{ makeGetValue('values', '4', 'float') }}}, |
| {{{ makeGetValue('values', '8', 'float') }}} |
| ); |
| break; |
| case 0x100F /* AL_ORIENTATION */: |
| AL.currentContext.ctx.listener.setOrientation( |
| {{{ makeGetValue('values', '0', 'float') }}}, |
| {{{ makeGetValue('values', '4', 'float') }}}, |
| {{{ makeGetValue('values', '8', 'float') }}}, |
| {{{ makeGetValue('values', '12', 'float') }}}, |
| {{{ makeGetValue('values', '16', 'float') }}}, |
| {{{ makeGetValue('values', '20', 'float') }}} |
| ); |
| break; |
| default: |
| #if OPENAL_DEBUG |
| console.log("alListenerfv with param " + param + " not implemented yet"); |
| #endif |
| break; |
| } |
| }, |
| |
| alIsExtensionPresent: function(extName) { |
| return 0; |
| }, |
| |
| alcIsExtensionPresent: function(device, extName) { |
| return 0; |
| }, |
| |
| alGetProcAddress: function(fname) { |
| return 0; |
| }, |
| |
| alcGetProcAddress: function(device, fname) { |
| return 0; |
| }, |
| }; |
| |
| autoAddDeps(LibraryOpenAL, '$AL'); |
| mergeInto(LibraryManager.library, LibraryOpenAL); |
| |