mal_standard_channel_map_webaudio = mal_standard_channel_map_flac, // https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions.
/* Make sure all nodes are disconnected and marked for collection. */
if (device.scriptNode !== undefined) {
device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */
device.scriptNode.disconnect();
device.scriptNode = undefined;
}
if (device.streamNode !== undefined) {
device.streamNode.disconnect();
device.streamNode = undefined;
}
/*
Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
to clear the callback before closing.
*/
device.webaudio.close();
device.webaudio = undefined;
/* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */
if (device.intermediaryBuffer !== undefined) {
Module._free(device.intermediaryBuffer);
device.intermediaryBuffer = undefined;
device.intermediaryBufferView = undefined;
device.intermediaryBufferSizeInBytes = undefined;
}
/* Make sure the device is untracked so the slot can be reused later. */
/* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */
pDevice->webaudio.index = EM_ASM_INT({
var channels = $0;
var sampleRate = $1;
var bufferSize = $2; /* In PCM frames. */
var isPlayback = $3;
var pDevice = $4;
if (typeof(mal) === 'undefined') {
return -1; /* Context not initialized. */
}
var device = {};
/* The AudioContext must be created in a suspended state. */
device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate});
device.webaudio.suspend();
/*
We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. Because it's passed between
JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free().
/* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */
var totalFramesProcessed = 0;
while (totalFramesProcessed < e.outputBuffer.length) {
var framesRemaining = e.outputBuffer.length - totalFramesProcessed;
var framesToProcess = framesRemaining;
if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
/* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */
var totalFramesProcessed = 0;
while (totalFramesProcessed < e.inputBuffer.length) {
var framesRemaining = e.inputBuffer.length - totalFramesProcessed;
var framesToProcess = framesRemaining;
if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
/* We need to do the reverse of the playback case. We need to interleave the input data and copy it into the intermediary buffer. Then we send it to the client. */
if (sendSilence) {
device.intermediaryBufferView.fill(0.0);
} else {
for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {