// ------------------------------------------------------------------------ //// ------------------------------------------------------------------------ //// ------------------------------------------------------------------------ //// ------------------------------------------------------------------------ //// ------------------------- wasm-audio-helper.js ------------------------- // /** * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ // Basic byte unit of WASM heap. (16 bit = 2 bytes)const BYTES_PER_UNIT = Uint16Array.BYTES_PER_ELEMENT; // Byte per audio sample. (32 bit float)const BYTES_PER_SAMPLE = Float32Array.BYTES_PER_ELEMENT; // The max audio channel on Chrome is 32.const MAX_CHANNEL_COUNT = 32; // WebAudio's render quantum size.const RENDER_QUANTUM_FRAMES = 128; /** * A WASM HEAP wrapper for AudioBuffer class. This breaks down the AudioBuffer * into an Array of Float32Array for the convinient WASM opearion. * * @class * @dependency Module A WASM module generated by the emscripten glue code. */class HeapAudioBuffer { /** * @constructor * @param {object} wasmModule WASM module generated by Emscripten. * @param {number} length Buffer frame length. * @param {number} channelCount Number of channels. * @param {number=} maxChannelCount Maximum number of channels. */ constructor(wasmModule, length, channelCount, maxChannelCount) { // The |channelCount| must be greater than 0, and less than or equal to // the maximum channel count. this._isInitialized = false; this._module = wasmModule; this._length = length; this._maxChannelCount = maxChannelCount ? Math.min(maxChannelCount, MAX_CHANNEL_COUNT) : channelCount; this._channelCount = channelCount; this._allocateHeap(); this._isInitialized = true; } /** * Allocates memory in the WASM heap and set up Float32Array views for the * channel data. * * @private */ _allocateHeap() { const channelByteSize = this._length * BYTES_PER_SAMPLE; const dataByteSize = this._channelCount * channelByteSize; this._dataPtr = this._module._malloc(dataByteSize); this._channelData = []; for (let i = 0; i < this._channelCount; ++i) { let startByteOffset = this._dataPtr + i * channelByteSize; let endByteOffset = startByteOffset + channelByteSize; // Get the actual array index by dividing the byte offset by 2 bytes. this._channelData[i] = this._module.HEAPF32.subarray(startByteOffset >> BYTES_PER_UNIT, endByteOffset >> BYTES_PER_UNIT); } } /** * Adapt the current channel count to the new input buffer. * * @param {number} newChannelCount The new channel count. */ adaptChannel(newChannelCount) { if (newChannelCount < this._maxChannelCount) { this._channelCount = newChannelCount; } // reform subArrays just in case memory has grown since we last did this const channelByteSize = this._length * BYTES_PER_SAMPLE; for (let i = 0; i < this._channelCount; ++i) { let startByteOffset = this._dataPtr + i * channelByteSize; let endByteOffset = startByteOffset + channelByteSize; // Get the actual array index by dividing the byte offset by 2 bytes. this._channelData[i] = this._module.HEAPF32.subarray(startByteOffset >> BYTES_PER_UNIT, endByteOffset >> BYTES_PER_UNIT); } } /** * Getter for the buffer length in frames. * * @return {?number} Buffer length in frames. */ get length() { return this._isInitialized ? this._length : null; } /** * Getter for the number of channels. * * @return {?number} Buffer length in frames. */ get numberOfChannels() { return this._isInitialized ? this._channelCount : null; } /** * Getter for the maxixmum number of channels allowed for the instance. * * @return {?number} Buffer length in frames. */ get maxChannelCount() { return this._isInitialized ? this._maxChannelCount : null; } /** * Returns a Float32Array object for a given channel index. If the channel * index is undefined, it returns the reference to the entire array of channel * data. * * @param {number|undefined} channelIndex Channel index. * @return {?Array} a channel data array or an * array of channel data. */ getChannelData(channelIndex) { if (channelIndex >= this._channelCount) { return null; } return typeof channelIndex === 'undefined' ? this._channelData : this._channelData[channelIndex]; } /** * Returns the base address of the allocated memory space in the WASM heap. * * @return {number} WASM Heap address. */ getHeapAddress() { return this._dataPtr; } /** * Frees the allocated memory space in the WASM heap. */ free() { this._isInitialized = false; this._module._free(this._dataPtr); this._module._free(this._pointerArrayPtr); this._channelData = null; }} // class HeapAudioBuffer /** * A JS FIFO implementation for the AudioWorklet. 3 assumptions for the * simpler operation: * 1. the push and the pull operation are done by 128 frames. (Web Audio * API's render quantum size in the speficiation) * 2. the channel count of input/output cannot be changed dynamically. * The AudioWorkletNode should be configured with the `.channelCount = k` * (where k is the channel count you want) and * `.channelCountMode = explicit`. * 3. This is for the single-thread operation. (obviously) * * @class */class RingBuffer { /** * @constructor * @param {number} length Buffer length in frames. * @param {number} channelCount Buffer channel count. */ constructor(length, channelCount) { this._readIndex = 0; this._writeIndex = 0; this._framesAvailable = 0; this._channelCount = channelCount; this._length = length; this._channelData = []; for (let i = 0; i < this._channelCount; ++i) { this._channelData[i] = new Float32Array(length); } } /** * Getter for Available frames in buffer. * * @return {number} Available frames in buffer. */ get framesAvailable() { return this._framesAvailable; } /** * Push a sequence of Float32Arrays to buffer. * * @param {array} arraySequence A sequence of Float32Arrays. */ push(arraySequence) { // The channel count of arraySequence and the length of each channel must // match with this buffer obejct. // Transfer data from the |arraySequence| storage to the internal buffer. let sourceLength = arraySequence[0].length; for (let i = 0; i < sourceLength; ++i) { let writeIndex = (this._writeIndex + i) % this._length; for (let channel = 0; channel < this._channelCount; ++channel) { this._channelData[channel][writeIndex] = arraySequence[channel][i]; } } this._writeIndex += sourceLength; if (this._writeIndex >= this._length) { this._writeIndex = 0; } // For excessive frames, the buffer will be overwritten. this._framesAvailable += sourceLength; if (this._framesAvailable > this._length) { this._framesAvailable = this._length; } } /** * Pull data out of buffer and fill a given sequence of Float32Arrays. * * @param {array} arraySequence An array of Float32Arrays. */ pull(arraySequence) { // The channel count of arraySequence and the length of each channel must // match with this buffer obejct. // If the FIFO is completely empty, do nothing. if (this._framesAvailable === 0) { return; } let destinationLength = arraySequence[0].length; // Transfer data from the internal buffer to the |arraySequence| storage. for (let i = 0; i < destinationLength; ++i) { let readIndex = (this._readIndex + i) % this._length; for (let channel = 0; channel < this._channelCount; ++channel) { arraySequence[channel][i] = this._channelData[channel][readIndex]; } } this._readIndex += destinationLength; if (this._readIndex >= this._length) { this._readIndex = 0; } this._framesAvailable -= destinationLength; if (this._framesAvailable < 0) { this._framesAvailable = 0; } }} // class RingBuffer // ------------------------------------------------------------------------ //// ------------------------------------------------------------------------ //// ------------------------------------------------------------------------ //// ------------------------------------------------------------------------ // // ------------------------------------------------------------------------ //// ------------------------------------------------------------------------ //// ------------------------------------------------------------------------ //// ------------------------------------------------------------------------ //// --------------------------- chucknode.js ------------------------------- // var globalInit = false;var globalPromise = undefined;var chucks = {}; var setDataDir, setLogLevel, initChuckInstance, clearChuckInstance, clearGlobals, cleanupChuckInstance, cleanRegisteredChucks, runChuckCode, runChuckCodeWithReplacementDac, runChuckFile, runChuckFileWithReplacementDac, runChuckFileWithArgs, runChuckFileWithArgsWithReplacementDac, replaceChuckCode, replaceChuckCodeWithReplacementDac, replaceChuckFile, replaceChuckFileWithReplacementDac, replaceChuckFileWithArgs, replaceChuckFileWithArgsWithReplacementDac, isShredActive, removeShred, // don't care about these because emscripten has another mechanism // for rerouting print statements // setChoutCallback = // setCherrCallback = // setStdoutCallback = // setStderrCallback = // set/get global int, float, string setChuckInt, getChuckInt, setChuckFloat, getChuckFloat, setChuckString, getChuckString, // global Events signalChuckEvent, broadcastChuckEvent, listenForChuckEventOnce, startListeningForChuckEvent, stopListeningForChuckEvent, // note: array is SAMPLE == Float32 // NOTE ALSO anything using arrays cannot use cwrap; use in similar manner to Module._chuckManualAudioCallback // TODO // getGlobalUGenSamples = Module.cwrap( 'getGlobalUGenSamples', 'number', ['number', 'string', 'array', 'number'] ); // set/get global int arrays // note: anything using arrays cannot use cwrap (since 'array' is an Int64Array) // called manually with heap manipulation below // setGlobalIntArray = Module.cwrap( 'setGlobalIntArray', 'number', ['number', 'string', 'array', 'number'] ); getGlobalIntArray, setGlobalIntArrayValue, getGlobalIntArrayValue, setGlobalAssociativeIntArrayValue, getGlobalAssociativeIntArrayValue, // set/get global float arrays // note: anything using arrays cannot use cwrap. called manually with heap manipulation below // setGlobalFloatArray = Module.cwrap( 'setGlobalFloatArray', 'number', ['number', 'string', 'array', 'number'] ); getGlobalFloatArray, setGlobalFloatArrayValue, getGlobalFloatArrayValue, setGlobalAssociativeFloatArrayValue, getGlobalAssociativeFloatArrayValue; var initGlobals = function( Module ){ // metaproperties (e.g. setDataDir must be called before init_chuck setDataDir = Module.cwrap( 'setDataDir', 'number', ['string'] ); setLogLevel = Module.cwrap( 'setLogLevel', 'number', ['number'] ); // initialize a VM initChuckInstance = Module.cwrap( 'initChuckInstance', 'number', ['number', 'number', 'number', 'number'] ); // cleanup clearChuckInstance = Module.cwrap( 'clearChuckInstance', 'number', ['number'] ); clearGlobals = Module.cwrap( 'clearGlobals', 'number', ['number'] ); cleanupChuckInstance = Module.cwrap( 'cleanupChuckInstance', 'number', ['number'] ); cleanRegisteredChucks = Module.cwrap( 'cleanRegisteredChucks', null ); // running code runChuckCode = Module.cwrap( 'runChuckCode', 'number', ['number', 'string'] ); runChuckCodeWithReplacementDac = Module.cwrap( 'runChuckCodeWithReplacementDac', 'number', ['number', 'string', 'string'] ); runChuckFile = Module.cwrap( 'runChuckFile', 'number', ['number', 'string'] ); runChuckFileWithReplacementDac = Module.cwrap( 'runChuckFileWithReplacementDac', 'number', ['number', 'string', 'string'] ); runChuckFileWithArgs = Module.cwrap( 'runChuckFileWithArgs', 'number', ['number', 'string', 'string'] ); runChuckFileWithArgsWithReplacementDac = Module.cwrap( 'runChuckFileWithArgsWithReplacementDac', 'number', ['number', 'string', 'string', 'string'] ); replaceChuckCode = Module.cwrap( 'replaceChuckCode', 'number', ['number', 'number', 'string'] ); replaceChuckCodeWithReplacementDac = Module.cwrap( 'replaceChuckCodeWithReplacementDac', 'number', ['number', 'number', 'string', 'string'] ); replaceChuckFile = Module.cwrap( 'replaceChuckFile', 'number', ['number', 'number', 'string'] ); replaceChuckFileWithReplacementDac = Module.cwrap( 'replaceChuckFileWithReplacementDac', 'number', ['number', 'number', 'string', 'string'] ); replaceChuckFileWithArgs = Module.cwrap( 'replaceChuckFileWithArgs', 'number', ['number', 'number', 'string', 'string'] ); replaceChuckFileWithArgsWithReplacementDac = Module.cwrap( 'replaceChuckFileWithArgsWithReplacementDac', 'number', ['number', 'number', 'string', 'string', 'string'] ); isShredActive = Module.cwrap( 'isShredActive', 'number', ['number', 'number'] ); removeShred = Module.cwrap( 'removeShred', 'number', ['number', 'number'] ); // don't care about these because emscripten has another mechanism // for rerouting print statements // setChoutCallback = // setCherrCallback = // setStdoutCallback = // setStderrCallback = // set/get global int, float, string setChuckInt = Module.cwrap( 'setChuckInt', 'number', ['number', 'string', 'number'] ); getChuckInt = Module.cwrap( 'getChuckInt', 'number', ['number', 'string'] ); setChuckFloat = Module.cwrap( 'setChuckFloat', 'number', ['number', 'string', 'number'] ); getChuckFloat = Module.cwrap( 'getChuckFloat', 'number', ['number', 'string'] ); setChuckString = Module.cwrap( 'setChuckString', 'number', ['number', 'string', 'string'] ); getChuckString = Module.cwrap( 'getChuckString', 'number', ['number', 'string', 'number'] ); // global Events signalChuckEvent = Module.cwrap( 'signalChuckEvent', 'number', ['number', 'string'] ); broadcastChuckEvent = Module.cwrap( 'broadcastChuckEvent', 'number', ['number', 'string'] ); listenForChuckEventOnce = Module.cwrap( 'listenForChuckEventOnce', 'number', ['number', 'string', 'number'] ); startListeningForChuckEvent = Module.cwrap( 'startListeningForChuckEvent', 'number', ['number', 'string', 'number']); stopListeningForChuckEvent = Module.cwrap( 'stopListeningForChuckEvent', 'number', ['number', 'string', 'number']); // note: array is SAMPLE == Float32 // NOTE ALSO anything using arrays cannot use cwrap; use in similar manner to Module._chuckManualAudioCallback // TODO // getGlobalUGenSamples = Module.cwrap( 'getGlobalUGenSamples', 'number', ['number', 'string', 'array', 'number'] ); // set/get global int arrays // note: anything using arrays cannot use cwrap (since 'array' is an Int64Array) // called manually with heap manipulation below // setGlobalIntArray = Module.cwrap( 'setGlobalIntArray', 'number', ['number', 'string', 'array', 'number'] ); getGlobalIntArray = Module.cwrap( 'getGlobalIntArray', 'number', ['number', 'string', 'number'] ); setGlobalIntArrayValue = Module.cwrap( 'setGlobalIntArrayValue', 'number', ['number', 'string', 'number', 'number'] ); getGlobalIntArrayValue = Module.cwrap( 'getGlobalIntArrayValue', 'number', ['number', 'string', 'number'] ); setGlobalAssociativeIntArrayValue = Module.cwrap( 'setGlobalAssociativeIntArrayValue', 'number', ['number', 'string', 'string', 'number'] ); getGlobalAssociativeIntArrayValue = Module.cwrap( 'getGlobalAssociativeIntArrayValue', 'number', ['number', 'string', 'string'] ); // set/get global float arrays // note: anything using arrays cannot use cwrap. called manually with heap manipulation below // setGlobalFloatArray = Module.cwrap( 'setGlobalFloatArray', 'number', ['number', 'string', 'array', 'number'] ); getGlobalFloatArray = Module.cwrap( 'getGlobalFloatArray', 'number', ['number', 'string', 'number'] ); setGlobalFloatArrayValue = Module.cwrap( 'setGlobalFloatArrayValue', 'number', ['number', 'string', 'number', 'number'] ); getGlobalFloatArrayValue = Module.cwrap( 'getGlobalFloatArrayValue', 'number', ['number', 'string', 'number'] ); setGlobalAssociativeFloatArrayValue = Module.cwrap( 'setGlobalAssociativeFloatArrayValue', 'number', ['number', 'string', 'string', 'number'] ); getGlobalAssociativeFloatArrayValue = Module.cwrap( 'getGlobalAssociativeFloatArrayValue', 'number', ['number', 'string', 'string'] ); // set data dir to "/" for embedded files setDataDir( "/" );} class ChuckNode extends AudioWorkletProcessor{ constructor( options ) { super(); this.srate = options.processorOptions.srate; this.inChannels = options.outputChannelCount[0]; this.outChannels = options.outputChannelCount[0]; this.myID = options.processorOptions.chuckID; chucks[this.myID] = this; // do this in response to an incoming message this.port.onmessage = this.handle_message.bind(this); this.myPointers = {} this.myActiveShreds = []; this.haveInit = false; this.init( options.processorOptions.preloadedFiles, options.processorOptions.wasm ); } init( preloadedFiles, wasm ) { if( !globalInit ) { var PreModule = { wasmBinary: wasm, print: (function( self ) { return function( text ) { console.log( text ); self.port.postMessage( { type: "console print", message: text } ); } })( this ), printErr: (function( self ) { return function( text ) { console.error( text ); self.port.postMessage( { type: "console print", message: text } ); } })( this ), // don't try to decode audio files; I'm really truly trying to copy the binaries only I promise noAudioDecoding: true, // don't try to decode images either, just so it will stop printing warnings it can't do it noImageDecoding: true }; PreModule[ "preRun" ] = [ (function( filesToPreload, Module ) { return function() { for( var i = 0; i < filesToPreload.length; i++ ) { Module["FS_createPreloadedFile"]( "/", filesToPreload[i].filename, filesToPreload[i].data, true, true ); } } })( preloadedFiles, PreModule ) ] Lines 1–500 of 1068. Load more
// ------------------------------------------------------------------------ // // ------------------------------------------------------------------------ // // ------------------------------------------------------------------------ // // ------------------------------------------------------------------------ // // ------------------------- wasm-audio-helper.js ------------------------- // /** * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ // Basic byte unit of WASM heap. (16 bit = 2 bytes) const BYTES_PER_UNIT = Uint16Array.BYTES_PER_ELEMENT; // Byte per audio sample. (32 bit float) const BYTES_PER_SAMPLE = Float32Array.BYTES_PER_ELEMENT; // The max audio channel on Chrome is 32. const MAX_CHANNEL_COUNT = 32; // WebAudio's render quantum size. const RENDER_QUANTUM_FRAMES = 128; /** * A WASM HEAP wrapper for AudioBuffer class. This breaks down the AudioBuffer * into an Array of Float32Array for the convinient WASM opearion. * * @class * @dependency Module A WASM module generated by the emscripten glue code. */ class HeapAudioBuffer { /** * @constructor * @param {object} wasmModule WASM module generated by Emscripten. * @param {number} length Buffer frame length. * @param {number} channelCount Number of channels. * @param {number=} maxChannelCount Maximum number of channels. */ constructor(wasmModule, length, channelCount, maxChannelCount) { // The |channelCount| must be greater than 0, and less than or equal to // the maximum channel count. this._isInitialized = false; this._module = wasmModule; this._length = length; this._maxChannelCount = maxChannelCount ? Math.min(maxChannelCount, MAX_CHANNEL_COUNT) : channelCount; this._channelCount = channelCount; this._allocateHeap(); this._isInitialized = true; } /** * Allocates memory in the WASM heap and set up Float32Array views for the * channel data. * * @private */ _allocateHeap() { const channelByteSize = this._length * BYTES_PER_SAMPLE; const dataByteSize = this._channelCount * channelByteSize; this._dataPtr = this._module._malloc(dataByteSize); this._channelData = []; for (let i = 0; i < this._channelCount; ++i) { let startByteOffset = this._dataPtr + i * channelByteSize; let endByteOffset = startByteOffset + channelByteSize; // Get the actual array index by dividing the byte offset by 2 bytes. this._channelData[i] = this._module.HEAPF32.subarray(startByteOffset >> BYTES_PER_UNIT, endByteOffset >> BYTES_PER_UNIT); } } /** * Adapt the current channel count to the new input buffer. * * @param {number} newChannelCount The new channel count. */ adaptChannel(newChannelCount) { if (newChannelCount < this._maxChannelCount) { this._channelCount = newChannelCount; } // reform subArrays just in case memory has grown since we last did this const channelByteSize = this._length * BYTES_PER_SAMPLE; for (let i = 0; i < this._channelCount; ++i) { let startByteOffset = this._dataPtr + i * channelByteSize; let endByteOffset = startByteOffset + channelByteSize; // Get the actual array index by dividing the byte offset by 2 bytes. this._channelData[i] = this._module.HEAPF32.subarray(startByteOffset >> BYTES_PER_UNIT, endByteOffset >> BYTES_PER_UNIT); } } /** * Getter for the buffer length in frames. * * @return {?number} Buffer length in frames. */ get length() { return this._isInitialized ? this._length : null; } /** * Getter for the number of channels. * * @return {?number} Buffer length in frames. */ get numberOfChannels() { return this._isInitialized ? this._channelCount : null; } /** * Getter for the maxixmum number of channels allowed for the instance. * * @return {?number} Buffer length in frames. */ get maxChannelCount() { return this._isInitialized ? this._maxChannelCount : null; } /** * Returns a Float32Array object for a given channel index. If the channel * index is undefined, it returns the reference to the entire array of channel * data. * * @param {number|undefined} channelIndex Channel index. * @return {?Array} a channel data array or an * array of channel data. */ getChannelData(channelIndex) { if (channelIndex >= this._channelCount) { return null; } return typeof channelIndex === 'undefined' ? this._channelData : this._channelData[channelIndex]; } /** * Returns the base address of the allocated memory space in the WASM heap. * * @return {number} WASM Heap address. */ getHeapAddress() { return this._dataPtr; } /** * Frees the allocated memory space in the WASM heap. */ free() { this._isInitialized = false; this._module._free(this._dataPtr); this._module._free(this._pointerArrayPtr); this._channelData = null; } } // class HeapAudioBuffer /** * A JS FIFO implementation for the AudioWorklet. 3 assumptions for the * simpler operation: * 1. the push and the pull operation are done by 128 frames. (Web Audio * API's render quantum size in the speficiation) * 2. the channel count of input/output cannot be changed dynamically. * The AudioWorkletNode should be configured with the `.channelCount = k` * (where k is the channel count you want) and * `.channelCountMode = explicit`. * 3. This is for the single-thread operation. (obviously) * * @class */ class RingBuffer { /** * @constructor * @param {number} length Buffer length in frames. * @param {number} channelCount Buffer channel count. */ constructor(length, channelCount) { this._readIndex = 0; this._writeIndex = 0; this._framesAvailable = 0; this._channelCount = channelCount; this._length = length; this._channelData = []; for (let i = 0; i < this._channelCount; ++i) { this._channelData[i] = new Float32Array(length); } } /** * Getter for Available frames in buffer. * * @return {number} Available frames in buffer. */ get framesAvailable() { return this._framesAvailable; } /** * Push a sequence of Float32Arrays to buffer. * * @param {array} arraySequence A sequence of Float32Arrays. */ push(arraySequence) { // The channel count of arraySequence and the length of each channel must // match with this buffer obejct. // Transfer data from the |arraySequence| storage to the internal buffer. let sourceLength = arraySequence[0].length; for (let i = 0; i < sourceLength; ++i) { let writeIndex = (this._writeIndex + i) % this._length; for (let channel = 0; channel < this._channelCount; ++channel) { this._channelData[channel][writeIndex] = arraySequence[channel][i]; } } this._writeIndex += sourceLength; if (this._writeIndex >= this._length) { this._writeIndex = 0; } // For excessive frames, the buffer will be overwritten. this._framesAvailable += sourceLength; if (this._framesAvailable > this._length) { this._framesAvailable = this._length; } } /** * Pull data out of buffer and fill a given sequence of Float32Arrays. * * @param {array} arraySequence An array of Float32Arrays. */ pull(arraySequence) { // The channel count of arraySequence and the length of each channel must // match with this buffer obejct. // If the FIFO is completely empty, do nothing. if (this._framesAvailable === 0) { return; } let destinationLength = arraySequence[0].length; // Transfer data from the internal buffer to the |arraySequence| storage. for (let i = 0; i < destinationLength; ++i) { let readIndex = (this._readIndex + i) % this._length; for (let channel = 0; channel < this._channelCount; ++channel) { arraySequence[channel][i] = this._channelData[channel][readIndex]; } } this._readIndex += destinationLength; if (this._readIndex >= this._length) { this._readIndex = 0; } this._framesAvailable -= destinationLength; if (this._framesAvailable < 0) { this._framesAvailable = 0; } } } // class RingBuffer // ------------------------------------------------------------------------ // // ------------------------------------------------------------------------ // // ------------------------------------------------------------------------ // // ------------------------------------------------------------------------ // // ------------------------------------------------------------------------ // // ------------------------------------------------------------------------ // // ------------------------------------------------------------------------ // // ------------------------------------------------------------------------ // // --------------------------- chucknode.js ------------------------------- // var globalInit = false; var globalPromise = undefined; var chucks = {}; var setDataDir, setLogLevel, initChuckInstance, clearChuckInstance, clearGlobals, cleanupChuckInstance, cleanRegisteredChucks, runChuckCode, runChuckCodeWithReplacementDac, runChuckFile, runChuckFileWithReplacementDac, runChuckFileWithArgs, runChuckFileWithArgsWithReplacementDac, replaceChuckCode, replaceChuckCodeWithReplacementDac, replaceChuckFile, replaceChuckFileWithReplacementDac, replaceChuckFileWithArgs, replaceChuckFileWithArgsWithReplacementDac, isShredActive, removeShred, // don't care about these because emscripten has another mechanism // for rerouting print statements // setChoutCallback = // setCherrCallback = // setStdoutCallback = // setStderrCallback = // set/get global int, float, string setChuckInt, getChuckInt, setChuckFloat, getChuckFloat, setChuckString, getChuckString, // global Events signalChuckEvent, broadcastChuckEvent, listenForChuckEventOnce, startListeningForChuckEvent, stopListeningForChuckEvent, // note: array is SAMPLE == Float32 // NOTE ALSO anything using arrays cannot use cwrap; use in similar manner to Module._chuckManualAudioCallback // TODO // getGlobalUGenSamples = Module.cwrap( 'getGlobalUGenSamples', 'number', ['number', 'string', 'array', 'number'] ); // set/get global int arrays // note: anything using arrays cannot use cwrap (since 'array' is an Int64Array) // called manually with heap manipulation below // setGlobalIntArray = Module.cwrap( 'setGlobalIntArray', 'number', ['number', 'string', 'array', 'number'] ); getGlobalIntArray, setGlobalIntArrayValue, getGlobalIntArrayValue, setGlobalAssociativeIntArrayValue, getGlobalAssociativeIntArrayValue, // set/get global float arrays // note: anything using arrays cannot use cwrap. called manually with heap manipulation below // setGlobalFloatArray = Module.cwrap( 'setGlobalFloatArray', 'number', ['number', 'string', 'array', 'number'] ); getGlobalFloatArray, setGlobalFloatArrayValue, getGlobalFloatArrayValue, setGlobalAssociativeFloatArrayValue, getGlobalAssociativeFloatArrayValue; var initGlobals = function( Module ) { // metaproperties (e.g. setDataDir must be called before init_chuck setDataDir = Module.cwrap( 'setDataDir', 'number', ['string'] ); setLogLevel = Module.cwrap( 'setLogLevel', 'number', ['number'] ); // initialize a VM initChuckInstance = Module.cwrap( 'initChuckInstance', 'number', ['number', 'number', 'number', 'number'] ); // cleanup clearChuckInstance = Module.cwrap( 'clearChuckInstance', 'number', ['number'] ); clearGlobals = Module.cwrap( 'clearGlobals', 'number', ['number'] ); cleanupChuckInstance = Module.cwrap( 'cleanupChuckInstance', 'number', ['number'] ); cleanRegisteredChucks = Module.cwrap( 'cleanRegisteredChucks', null ); // running code runChuckCode = Module.cwrap( 'runChuckCode', 'number', ['number', 'string'] ); runChuckCodeWithReplacementDac = Module.cwrap( 'runChuckCodeWithReplacementDac', 'number', ['number', 'string', 'string'] ); runChuckFile = Module.cwrap( 'runChuckFile', 'number', ['number', 'string'] ); runChuckFileWithReplacementDac = Module.cwrap( 'runChuckFileWithReplacementDac', 'number', ['number', 'string', 'string'] ); runChuckFileWithArgs = Module.cwrap( 'runChuckFileWithArgs', 'number', ['number', 'string', 'string'] ); runChuckFileWithArgsWithReplacementDac = Module.cwrap( 'runChuckFileWithArgsWithReplacementDac', 'number', ['number', 'string', 'string', 'string'] ); replaceChuckCode = Module.cwrap( 'replaceChuckCode', 'number', ['number', 'number', 'string'] ); replaceChuckCodeWithReplacementDac = Module.cwrap( 'replaceChuckCodeWithReplacementDac', 'number', ['number', 'number', 'string', 'string'] ); replaceChuckFile = Module.cwrap( 'replaceChuckFile', 'number', ['number', 'number', 'string'] ); replaceChuckFileWithReplacementDac = Module.cwrap( 'replaceChuckFileWithReplacementDac', 'number', ['number', 'number', 'string', 'string'] ); replaceChuckFileWithArgs = Module.cwrap( 'replaceChuckFileWithArgs', 'number', ['number', 'number', 'string', 'string'] ); replaceChuckFileWithArgsWithReplacementDac = Module.cwrap( 'replaceChuckFileWithArgsWithReplacementDac', 'number', ['number', 'number', 'string', 'string', 'string'] ); isShredActive = Module.cwrap( 'isShredActive', 'number', ['number', 'number'] ); removeShred = Module.cwrap( 'removeShred', 'number', ['number', 'number'] ); // don't care about these because emscripten has another mechanism // for rerouting print statements // setChoutCallback = // setCherrCallback = // setStdoutCallback = // setStderrCallback = // set/get global int, float, string setChuckInt = Module.cwrap( 'setChuckInt', 'number', ['number', 'string', 'number'] ); getChuckInt = Module.cwrap( 'getChuckInt', 'number', ['number', 'string'] ); setChuckFloat = Module.cwrap( 'setChuckFloat', 'number', ['number', 'string', 'number'] ); getChuckFloat = Module.cwrap( 'getChuckFloat', 'number', ['number', 'string'] ); setChuckString = Module.cwrap( 'setChuckString', 'number', ['number', 'string', 'string'] ); getChuckString = Module.cwrap( 'getChuckString', 'number', ['number', 'string', 'number'] ); // global Events signalChuckEvent = Module.cwrap( 'signalChuckEvent', 'number', ['number', 'string'] ); broadcastChuckEvent = Module.cwrap( 'broadcastChuckEvent', 'number', ['number', 'string'] ); listenForChuckEventOnce = Module.cwrap( 'listenForChuckEventOnce', 'number', ['number', 'string', 'number'] ); startListeningForChuckEvent = Module.cwrap( 'startListeningForChuckEvent', 'number', ['number', 'string', 'number']); stopListeningForChuckEvent = Module.cwrap( 'stopListeningForChuckEvent', 'number', ['number', 'string', 'number']); // note: array is SAMPLE == Float32 // NOTE ALSO anything using arrays cannot use cwrap; use in similar manner to Module._chuckManualAudioCallback // TODO // getGlobalUGenSamples = Module.cwrap( 'getGlobalUGenSamples', 'number', ['number', 'string', 'array', 'number'] ); // set/get global int arrays // note: anything using arrays cannot use cwrap (since 'array' is an Int64Array) // called manually with heap manipulation below // setGlobalIntArray = Module.cwrap( 'setGlobalIntArray', 'number', ['number', 'string', 'array', 'number'] ); getGlobalIntArray = Module.cwrap( 'getGlobalIntArray', 'number', ['number', 'string', 'number'] ); setGlobalIntArrayValue = Module.cwrap( 'setGlobalIntArrayValue', 'number', ['number', 'string', 'number', 'number'] ); getGlobalIntArrayValue = Module.cwrap( 'getGlobalIntArrayValue', 'number', ['number', 'string', 'number'] ); setGlobalAssociativeIntArrayValue = Module.cwrap( 'setGlobalAssociativeIntArrayValue', 'number', ['number', 'string', 'string', 'number'] ); getGlobalAssociativeIntArrayValue = Module.cwrap( 'getGlobalAssociativeIntArrayValue', 'number', ['number', 'string', 'string'] ); // set/get global float arrays // note: anything using arrays cannot use cwrap. called manually with heap manipulation below // setGlobalFloatArray = Module.cwrap( 'setGlobalFloatArray', 'number', ['number', 'string', 'array', 'number'] ); getGlobalFloatArray = Module.cwrap( 'getGlobalFloatArray', 'number', ['number', 'string', 'number'] ); setGlobalFloatArrayValue = Module.cwrap( 'setGlobalFloatArrayValue', 'number', ['number', 'string', 'number', 'number'] ); getGlobalFloatArrayValue = Module.cwrap( 'getGlobalFloatArrayValue', 'number', ['number', 'string', 'number'] ); setGlobalAssociativeFloatArrayValue = Module.cwrap( 'setGlobalAssociativeFloatArrayValue', 'number', ['number', 'string', 'string', 'number'] ); getGlobalAssociativeFloatArrayValue = Module.cwrap( 'getGlobalAssociativeFloatArrayValue', 'number', ['number', 'string', 'string'] ); // set data dir to "/" for embedded files setDataDir( "/" ); } class ChuckNode extends AudioWorkletProcessor { constructor( options ) { super(); this.srate = options.processorOptions.srate; this.inChannels = options.outputChannelCount[0]; this.outChannels = options.outputChannelCount[0]; this.myID = options.processorOptions.chuckID; chucks[this.myID] = this; // do this in response to an incoming message this.port.onmessage = this.handle_message.bind(this); this.myPointers = {} this.myActiveShreds = []; this.haveInit = false; this.init( options.processorOptions.preloadedFiles, options.processorOptions.wasm ); } init( preloadedFiles, wasm ) { if( !globalInit ) { var PreModule = { wasmBinary: wasm, print: (function( self ) { return function( text ) { console.log( text ); self.port.postMessage( { type: "console print", message: text } ); } })( this ), printErr: (function( self ) { return function( text ) { console.error( text ); self.port.postMessage( { type: "console print", message: text } ); } })( this ), // don't try to decode audio files; I'm really truly trying to copy the binaries only I promise noAudioDecoding: true, // don't try to decode images either, just so it will stop printing warnings it can't do it noImageDecoding: true }; PreModule[ "preRun" ] = [ (function( filesToPreload, Module ) { return function() { for( var i = 0; i < filesToPreload.length; i++ ) { Module["FS_createPreloadedFile"]( "/", filesToPreload[i].filename, filesToPreload[i].data, true, true ); } } })( preloadedFiles, PreModule ) ]