├── .babelrc ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .nycrc ├── .travis.yml ├── README.md ├── benchmark ├── browser-benchmark │ ├── noop.js │ ├── sched-param.js │ ├── sched-sine.js │ ├── simple-gain.js │ ├── simple-saw.js │ └── simple-sine.js ├── index.html ├── main.js ├── package.json └── suites │ ├── AudioBufferSourceNode-params-fixed-0.js │ ├── AudioBufferSourceNode-params-fixed-1.js │ ├── AudioBufferSourceNode-params-fixed-x.js │ ├── AudioBufferSourceNode-params-sched.js │ ├── BiquadFilterNode-params-fixed-x.js │ ├── BiquadFilterNode-params-sched.js │ ├── BiquadFilterNode-silent-input.js │ ├── DelayNode-params-fixed-x.js │ ├── DelayNode-params-sched.js │ ├── DelayNode-silent-input.js │ ├── GainNode-params-fixed-0.js │ ├── GainNode-params-fixed-1.js │ ├── GainNode-params-fixed-x.js │ ├── GainNode-params-sched.js │ ├── GainNode-silent-input.js │ ├── OfflineAudioContext-noop.js │ ├── OfflineAudioContext-sine.js │ ├── OscillatorNode-sine-params-fixed-x.js │ ├── OscillatorNode-sine-params-sched.js │ ├── OscillatorNode-tri-params-fixed-x.js │ ├── OscillatorNode-tri-params-sched.js │ ├── StereoPannerNode-params-fixed-x.js │ ├── StereoPannerNode-params-sched.js │ ├── StereoPannerNode-silent-input.js │ ├── WaveShaperNode-curve.js │ ├── WaveShaperNode-none-curve.js │ └── WaveShaperNode-slient-input.js ├── build └── web-audio-engine.js ├── demo ├── .gitignore ├── assets │ └── sound │ │ ├── a11wlk01.wav │ │ ├── amen.wav │ │ ├── hihat1.wav │ │ ├── hihat2.wav │ │ ├── kick.wav │ │ ├── snare.wav │ │ ├── tom1.wav │ │ └── tom2.wav ├── demo.js ├── index.html ├── package.json └── sources │ ├── chorus.js │ ├── delay.js │ ├── drum.js │ ├── filter.js │ ├── metronome.js │ ├── pan.js │ ├── sines.js │ ├── test_delay-modulation.js │ ├── test_filter-modulation.js │ ├── test_iir-filter.js │ └── test_loop.js ├── package.json ├── src ├── api │ ├── AnalyserNode.js │ ├── AudioBuffer.js │ ├── AudioBufferSourceNode.js │ ├── AudioDestinationNode.js │ ├── AudioListener.js │ ├── AudioNode.js │ ├── AudioParam.js │ ├── AudioScheduledSourceNode.js │ ├── BaseAudioContext.js │ ├── BiquadFilterNode.js │ ├── ChannelMergerNode.js │ ├── ChannelSplitterNode.js │ ├── ConstantSourceNode.js │ ├── ConvolverNode.js │ ├── DelayNode.js │ ├── DynamicsCompressorNode.js │ ├── EventTarget.js │ ├── GainNode.js │ ├── IIRFilterNode.js │ ├── OscillatorNode.js │ ├── PannerNode.js │ ├── PeriodicWave.js │ ├── README.md │ ├── ScriptProcessorNode.js │ ├── SpatialListener.js │ ├── SpatialPannerNode.js │ ├── StereoPannerNode.js │ ├── WaveShaperNode.js │ └── index.js ├── config.js ├── constants │ ├── AudioContextState.js │ ├── AudioParamEvent.js │ ├── AudioParamRate.js │ ├── BiquadFilterType.js │ ├── ChannelCountMode.js │ ├── ChannelInterpretation.js │ ├── OscillatorType.js │ ├── PlaybackState.js │ └── index.js ├── context │ ├── OfflineAudioContext.js │ ├── RenderingAudioContext.js │ ├── StreamAudioContext.js │ └── WebAudioContext.js ├── decoder.js ├── encoder.js ├── impl │ ├── AnalyserNode.js │ ├── AudioBuffer.js │ ├── AudioBufferSourceNode.js │ ├── AudioContext.js │ ├── AudioDestinationNode.js │ ├── AudioListener.js │ ├── AudioNode.js │ ├── AudioParam.js │ ├── AudioScheduledSourceNode.js │ ├── AudioSourceNode.js │ ├── BasePannerNode.js │ ├── BiquadFilterNode.js │ ├── ChannelMergerNode.js │ ├── ChannelSplitterNode.js │ ├── ConstantSourceNode.js │ ├── ConvolverNode.js │ ├── DelayNode.js │ ├── DynamicsCompressorNode.js │ ├── EventTarget.js │ ├── GainNode.js │ ├── IIRFilterNode.js │ ├── OscillatorNode.js │ ├── PannerNode.js │ ├── PeriodicWave.js │ ├── ScriptProcessorNode.js │ ├── SpatialListener.js │ ├── SpatialPannerNode.js │ ├── StereoPannerNode.js │ ├── WaveShaperNode.js │ ├── core │ │ ├── AudioBus.js │ │ ├── AudioData.js │ │ ├── AudioNodeInput.js │ │ └── AudioNodeOutput.js │ ├── dsp │ │ ├── AnalyserNode.js │ │ ├── AudioBufferSourceNode.js │ │ ├── AudioParam.js │ │ ├── BiquadFilterKernel.js │ │ ├── BiquadFilterNode.js │ │ ├── ChannelMergerNode.js │ │ ├── ChannelSplitterNode.js │ │ ├── ConstantSourceNode.js │ │ ├── ConvolverNode.js │ │ ├── DelayNode.js │ │ ├── DynamicsCompressorNode.js │ │ ├── GainNode.js │ │ ├── IIRFilterKernel.js │ │ ├── IIRFilterNode.js │ │ ├── OscillatorNode.js │ │ ├── PannerNode.js │ │ ├── PeriodicWave.js │ │ ├── ScriptProcessorNode.js │ │ ├── SpatialPannerNode.js │ │ ├── StereoPannerNode.js │ │ └── WaveShaperNode.js │ └── index.js ├── index.js └── utils │ ├── AudioDataUtils.js │ ├── AudioParamUtils.js │ ├── DecoderUtils.js │ ├── EncoderUtils.js │ ├── FilterUtils.js │ ├── PCMArrayBufferWriter.js │ ├── PCMBufferWriter.js │ ├── PCMEncoder.js │ ├── index.js │ ├── setImmediate.js │ └── utils │ ├── clamp.js │ ├── defaults.js │ ├── defineProp.js │ ├── fill.js │ ├── fillRange.js │ ├── normalize.js │ ├── toArrayIfNeeded.js │ ├── toAudioTime.js │ ├── toDecibel.js │ ├── toGain.js │ ├── toImpl.js │ ├── toNumber.js │ ├── toPowerOfTwo.js │ ├── toValidBitDepth.js │ ├── toValidBlockSize.js │ ├── toValidNumberOfChannels.js │ └── toValidSampleRate.js ├── test ├── .eslintrc.json ├── api │ ├── AnalyserNode.js │ ├── AudioBuffer.js │ ├── AudioBufferSourceNode.js │ ├── AudioContext.js │ ├── AudioDestinationNode.js │ ├── AudioListener.js │ ├── AudioNode.js │ ├── AudioParam.js │ ├── BiquadFilterNode.js │ ├── ChannelMergerNode.js │ ├── ChannelSplitterNode.js │ ├── ConstantSourceNode.js │ ├── ConvolverNode.js │ ├── DelayNode.js │ ├── DynamicsCompressorNode.js │ ├── EventTarget.js │ ├── GainNode.js │ ├── IIRFilterNode.js │ ├── OscillatorNode.js │ ├── PannerNode.js │ ├── PeriodicWave.js │ ├── ScriptProcessorNode.js │ ├── SpatialPannerNode.js │ ├── StereoPannerNode.js │ └── WaveShaperNode.js ├── context │ ├── OfflineAudioContext.js │ ├── RenderingAudioContext.js │ └── StreamAudioContext.js ├── decoder.js ├── encoder.js ├── helpers │ ├── np.js │ └── paramTester.js ├── impl │ ├── AnalyserNode.js │ ├── AudioBuffer.js │ ├── AudioBufferSourceNode.js │ ├── AudioContext.js │ ├── AudioDestinationNode.js │ ├── AudioNode.js │ ├── AudioParam.js │ ├── BasePannerNode.js │ ├── BiquadFilterNode.js │ ├── ChannelMergerNode.js │ ├── ChannelSplitterNode.js │ ├── ConstantSourceNode.js │ ├── ConvolverNode.js │ ├── DelayNode.js │ ├── DynamicsCompressorNode.js │ ├── GainNode.js │ ├── IIRFilterNode.js │ ├── OscillatorNode.js │ ├── PannerNode.js │ ├── PeriodicWave.js │ ├── ScriptProcessorNode.js │ ├── SpatialListener.js │ ├── SpatialPannerNode.js │ ├── StereoPannerNode.js │ ├── WaveShaperNode.js │ ├── core │ │ ├── AudioBus.js │ │ ├── AudioBusMixing.js │ │ ├── AudioData.js │ │ ├── AudioNodeChannelPropagation.js │ │ ├── AudioNodeConnection.js │ │ ├── AudioNodeDSP.js │ │ ├── AudioNodeInput.js │ │ └── AudioNodeOutput.js │ └── dsp │ │ ├── AnalyserNode.js │ │ ├── AudioContext.js │ │ ├── AudioDestinationNode.js │ │ ├── AudioNode.js │ │ ├── AudioParam.js │ │ ├── BiquadFilterNode.js │ │ ├── ChannelMergerNode.js │ │ ├── ChannelSplitterNode.js │ │ ├── GainNode.js │ │ ├── IIRFilterNode.js │ │ ├── ScriptProcessorNode.js │ │ ├── StereoPannerNode.js │ │ └── WaveShaperNode.js ├── mocha.opts └── utils │ ├── AudioDataUtils.js │ ├── AudioParamUtils.js │ ├── DecoderUtils.js │ ├── EncoderUtils.js │ ├── PCMArrayBufferWriter.js │ ├── PCMBufferWriter.js │ ├── PCMEncoder.js │ ├── setImmediate.js │ └── utils │ ├── clamp.js │ ├── defaults.js │ ├── defineProp.js │ ├── fill.js │ ├── fillRange.js │ ├── normalize.js │ ├── toArrayIfNeeded.js │ ├── toAudioTime.js │ ├── toDecibel.js │ ├── toGain.js │ ├── toImpl.js │ ├── toNumber.js │ ├── toPowerOfTwo.js │ ├── toValidBitDepth.js │ ├── toValidBlockSize.js │ ├── toValidNumberOfChannels.js │ └── toValidSampleRate.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "env": { 6 | "build": { 7 | "only": [ "src" ], 8 | "plugins": [ "unassert" ] 9 | }, 10 | "development": { 11 | "presets": [ "power-assert" ] 12 | }, 13 | "cover": { 14 | "plugins": [ "istanbul" ] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.wav binary 2 | build/*.js -diff 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | .nyc_output 3 | npm-debug.log 4 | yarn-debug.log 5 | yarn-error.log 6 | node_modules/ 7 | coverage/ 8 | lib/ 9 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "require": [ 3 | "babel-register" 4 | ], 5 | "exclude": [ 6 | "src/impl/dsp/*.js", 7 | "test/**/*.js" 8 | ], 9 | "sourceMap": false, 10 | "instrument": false 11 | } 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "6.0" 5 | cache: 6 | directories: 7 | - node_modules 8 | script: 9 | - npm run travis 10 | -------------------------------------------------------------------------------- /benchmark/browser-benchmark/noop.js: -------------------------------------------------------------------------------- 1 | module.exports = function() {}; 2 | -------------------------------------------------------------------------------- /benchmark/browser-benchmark/sched-param.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context) { 2 | var osc = context.createOscillator(); 3 | 4 | for (var i = 0; i < 1200; i++) { 5 | osc.detune.setValueAtTime(i, i / 1200); 6 | } 7 | 8 | osc.start(0.25); 9 | osc.stop(0.75); 10 | osc.connect(context.destination); 11 | }; 12 | -------------------------------------------------------------------------------- /benchmark/browser-benchmark/sched-sine.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context) { 2 | for (var i = 0; i < 500; i++) { 3 | var osc = context.createOscillator(); 4 | 5 | osc.start(i * 0.01); 6 | osc.stop(i * 0.01 + 0.005); 7 | osc.connect(context.destination); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /benchmark/browser-benchmark/simple-gain.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context) { 2 | var osc = context.createOscillator(); 3 | var amp = context.createGain(); 4 | 5 | osc.start(0); 6 | osc.stop(1); 7 | osc.connect(amp); 8 | 9 | amp.gain.setValueAtTime(1, 0); 10 | amp.gain.linearRampToValueAtTime(0, 1); 11 | amp.connect(context.destination); 12 | }; 13 | -------------------------------------------------------------------------------- /benchmark/browser-benchmark/simple-saw.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context) { 2 | var osc = context.createOscillator(); 3 | 4 | osc.type = "sawtooth"; 5 | osc.start(0.25); 6 | osc.stop(0.75); 7 | osc.connect(context.destination); 8 | }; 9 | -------------------------------------------------------------------------------- /benchmark/browser-benchmark/simple-sine.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context) { 2 | var osc = context.createOscillator(); 3 | 4 | osc.start(0.25); 5 | osc.stop(0.75); 6 | osc.connect(context.destination); 7 | }; 8 | -------------------------------------------------------------------------------- /benchmark/main.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const vm = require("vm"); 5 | const readline = require("readline"); 6 | 7 | const files = fs.readdirSync(`${ __dirname }/suites`) 8 | .filter(filename => /\.js$/.test(filename)) 9 | .sort(); 10 | 11 | function showList() { 12 | files.forEach((filename, index) => { 13 | console.log(`[${ index }] ${ filename.replace(/\.js$/, "") }`) 14 | }); 15 | } 16 | 17 | function chooseBenchmark(callback) { 18 | const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); 19 | 20 | showList(); 21 | 22 | rl.question("choose benchmark? ", (indices) => { 23 | run(indices); 24 | rl.close(); 25 | }); 26 | } 27 | 28 | function run(indices) { 29 | const names = Array.prototype.concat.apply([], indices.split(/\s+/g).map(collectBenchmark)); 30 | 31 | return names.reduce((promise, name) => { 32 | return promise.then(() => { 33 | const func = require(`${ __dirname }/suites/${ name }`); 34 | 35 | console.log("-- " + name + " " + "-".repeat(Math.max(0, 71 - (name.length)))); 36 | 37 | return new Promise((resolve) => { func(resolve); }); 38 | }); 39 | }, Promise.resolve()); 40 | } 41 | 42 | function collectBenchmark(index) { 43 | if (/^\d+$/.test(index)) { 44 | return [ files[index] ]; 45 | } 46 | return files.filter(name => name.indexOf(index) !== -1); 47 | } 48 | 49 | if (process.argv[2]) { 50 | run(process.argv.slice(2).join(" ")); 51 | } else { 52 | chooseBenchmark(); 53 | } 54 | -------------------------------------------------------------------------------- /benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@web-audio-engine/benchnmark", 3 | "description": "benchnmark", 4 | "version": "0.0.0", 5 | "dependencies": { 6 | "benchmark": "^2.1.0" 7 | }, 8 | "license": "MIT", 9 | "main": "main.js", 10 | "private": true 11 | } 12 | -------------------------------------------------------------------------------- /benchmark/suites/AudioBufferSourceNode-params-fixed-0.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const buffer = new wae.impl.AudioBuffer(context, { 18 | numberOfChannels: this.numberOfChannels, length: 1024, sampleRate: context.sampleRate 19 | }); 20 | const bufSrc = new wae.impl.AudioBufferSourceNode(context); 21 | 22 | bufSrc.setBuffer(buffer); 23 | bufSrc.start(0); 24 | bufSrc.getPlaybackRate().value = 0; 25 | }, 26 | fn() { 27 | bufSrc.dspProcess(); 28 | } 29 | }; 30 | } 31 | 32 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 33 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 34 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 35 | 36 | suite.on("cycle", (e) => { 37 | console.log(e.target.toString()); 38 | }); 39 | 40 | suite.on("complete", () => { 41 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 42 | console.log(); 43 | }); 44 | 45 | suite.on("error", (e) => { 46 | console.error(e); 47 | }); 48 | 49 | suite.run(); 50 | } 51 | 52 | module.exports = (done) => { 53 | run(1); 54 | done(); 55 | }; 56 | 57 | if (module.parent === null) { 58 | module.exports(process.exit); 59 | } 60 | -------------------------------------------------------------------------------- /benchmark/suites/AudioBufferSourceNode-params-fixed-1.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const buffer = new wae.impl.AudioBuffer(context, { 18 | numberOfChannels: this.numberOfChannels, length: 1024, sampleRate: context.sampleRate 19 | }); 20 | const bufSrc = new wae.impl.AudioBufferSourceNode(context); 21 | 22 | bufSrc.setBuffer(buffer); 23 | bufSrc.start(0); 24 | bufSrc.getPlaybackRate().value = 1; 25 | }, 26 | fn() { 27 | bufSrc.dspProcess(); 28 | } 29 | }; 30 | } 31 | 32 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 33 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 34 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 35 | 36 | suite.on("cycle", (e) => { 37 | console.log(e.target.toString()); 38 | }); 39 | 40 | suite.on("complete", () => { 41 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 42 | console.log(); 43 | }); 44 | 45 | suite.on("error", (e) => { 46 | console.error(e); 47 | }); 48 | 49 | suite.run(); 50 | } 51 | 52 | module.exports = (done) => { 53 | run(1); 54 | done(); 55 | }; 56 | 57 | if (module.parent === null) { 58 | module.exports(process.exit); 59 | } 60 | -------------------------------------------------------------------------------- /benchmark/suites/AudioBufferSourceNode-params-fixed-x.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const buffer = new wae.impl.AudioBuffer(context, { 18 | numberOfChannels: this.numberOfChannels, length: 1024, sampleRate: context.sampleRate 19 | }); 20 | const bufSrc = new wae.impl.AudioBufferSourceNode(context); 21 | 22 | bufSrc.setBuffer(buffer); 23 | bufSrc.start(0); 24 | bufSrc.getPlaybackRate().value = 0.5; 25 | }, 26 | fn() { 27 | bufSrc.dspProcess(); 28 | } 29 | }; 30 | } 31 | 32 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 33 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 34 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 35 | 36 | suite.on("cycle", (e) => { 37 | console.log(e.target.toString()); 38 | }); 39 | 40 | suite.on("complete", () => { 41 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 42 | console.log(); 43 | }); 44 | 45 | suite.on("error", (e) => { 46 | console.error(e); 47 | }); 48 | 49 | suite.run(); 50 | } 51 | 52 | module.exports = (done) => { 53 | run(1); 54 | done(); 55 | }; 56 | 57 | if (module.parent === null) { 58 | module.exports(process.exit); 59 | } 60 | -------------------------------------------------------------------------------- /benchmark/suites/AudioBufferSourceNode-params-sched.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const buffer = new wae.impl.AudioBuffer(context, { 18 | numberOfChannels: this.numberOfChannels, length: 1024, sampleRate: context.sampleRate 19 | }); 20 | const bufSrc = new wae.impl.AudioBufferSourceNode(context); 21 | 22 | bufSrc.setBuffer(buffer); 23 | bufSrc.start(0); 24 | bufSrc.getPlaybackRate().setValueAtTime(0.5, 0); 25 | bufSrc.getPlaybackRate().linearRampToValueAtTime(1.0, 1); 26 | bufSrc.getPlaybackRate().dspProcess(); 27 | }, 28 | fn() { 29 | bufSrc.dspProcess(); 30 | } 31 | }; 32 | } 33 | 34 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 35 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 36 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 37 | 38 | suite.on("cycle", (e) => { 39 | console.log(e.target.toString()); 40 | }); 41 | 42 | suite.on("complete", () => { 43 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 44 | console.log(); 45 | }); 46 | 47 | suite.on("error", (e) => { 48 | console.error(e); 49 | }); 50 | 51 | suite.run(); 52 | } 53 | 54 | module.exports = (done) => { 55 | run(1); 56 | run(2); 57 | run(4); 58 | done(); 59 | }; 60 | 61 | if (module.parent === null) { 62 | module.exports(process.exit); 63 | } 64 | -------------------------------------------------------------------------------- /benchmark/suites/BiquadFilterNode-params-fixed-x.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const biquad = new wae.impl.BiquadFilterNode(context); 18 | 19 | biquad.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | biquad.inputs[0].bus.getMutableData(); 21 | biquad.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | biquad.getFrequency().value = 350; 24 | }, 25 | fn() { 26 | biquad.dspProcess(); 27 | } 28 | }; 29 | } 30 | 31 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 32 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 33 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 34 | 35 | suite.on("cycle", (e) => { 36 | console.log(e.target.toString()); 37 | }); 38 | 39 | suite.on("complete", () => { 40 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 41 | console.log(); 42 | }); 43 | 44 | suite.on("error", (e) => { 45 | console.error(e); 46 | }); 47 | 48 | suite.run(); 49 | } 50 | 51 | module.exports = (done) => { 52 | run(1); 53 | run(2); 54 | run(4); 55 | done(); 56 | }; 57 | 58 | if (module.parent === null) { 59 | module.exports(process.exit); 60 | } 61 | -------------------------------------------------------------------------------- /benchmark/suites/BiquadFilterNode-params-sched.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const biquad = new wae.impl.BiquadFilterNode(context); 18 | 19 | biquad.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | biquad.inputs[0].bus.getMutableData(); 21 | biquad.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | biquad.getFrequency().setValueAtTime(350.0, 0); 24 | biquad.getFrequency().linearRampToValueAtTime(700.0, 1); 25 | biquad.getFrequency().dspProcess(); 26 | }, 27 | fn() { 28 | biquad.dspProcess(); 29 | } 30 | }; 31 | } 32 | 33 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 34 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 35 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 36 | 37 | suite.on("cycle", (e) => { 38 | console.log(e.target.toString()); 39 | }); 40 | 41 | suite.on("complete", () => { 42 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 43 | console.log(); 44 | }); 45 | 46 | suite.on("error", (e) => { 47 | console.error(e); 48 | }); 49 | 50 | suite.run(); 51 | } 52 | 53 | module.exports = (done) => { 54 | run(1); 55 | run(2); 56 | run(4); 57 | done(); 58 | }; 59 | 60 | if (module.parent === null) { 61 | module.exports(process.exit); 62 | } 63 | -------------------------------------------------------------------------------- /benchmark/suites/BiquadFilterNode-silent-input.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const biquad = new wae.impl.BiquadFilterNode(context); 18 | 19 | biquad.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | biquad.inputs[0].bus.zeros(); 21 | biquad.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | biquad.getFrequency().setValueAtTime(350.0, 0); 24 | biquad.getFrequency().linearRampToValueAtTime(700.0, 1); 25 | biquad.getFrequency().dspProcess(); 26 | }, 27 | fn() { 28 | biquad.dspProcess(); 29 | } 30 | }; 31 | } 32 | 33 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 34 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 35 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 36 | 37 | suite.on("cycle", (e) => { 38 | console.log(e.target.toString()); 39 | }); 40 | 41 | suite.on("complete", () => { 42 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 43 | console.log(); 44 | }); 45 | 46 | suite.on("error", (e) => { 47 | console.error(e); 48 | }); 49 | 50 | suite.run(); 51 | } 52 | 53 | module.exports = (done) => { 54 | run(1); 55 | run(2); 56 | run(4); 57 | done(); 58 | }; 59 | 60 | if (module.parent === null) { 61 | module.exports(process.exit); 62 | } 63 | -------------------------------------------------------------------------------- /benchmark/suites/DelayNode-params-fixed-x.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const delay = new wae.impl.DelayNode(context); 18 | 19 | delay.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | delay.inputs[0].bus.getMutableData(); 21 | delay.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | delay.getDelayTime().value = 8 / context.sampleRate; 24 | }, 25 | fn() { 26 | delay.dspProcess(); 27 | } 28 | }; 29 | } 30 | 31 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 32 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 33 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 34 | 35 | suite.on("cycle", (e) => { 36 | console.log(e.target.toString()); 37 | }); 38 | 39 | suite.on("complete", () => { 40 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 41 | console.log(); 42 | }); 43 | 44 | suite.on("error", (e) => { 45 | console.error(e); 46 | }); 47 | 48 | suite.run(); 49 | } 50 | 51 | module.exports = (done) => { 52 | run(1); 53 | run(2); 54 | run(4); 55 | done(); 56 | }; 57 | 58 | if (module.parent === null) { 59 | module.exports(process.exit); 60 | } 61 | -------------------------------------------------------------------------------- /benchmark/suites/DelayNode-params-sched.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const delay = new wae.impl.DelayNode(context); 18 | 19 | delay.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | delay.inputs[0].bus.getMutableData(); 21 | delay.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | delay.getDelayTime().setValueAtTime(0.0, 0); 24 | delay.getDelayTime().linearRampToValueAtTime(1.0, 1); 25 | delay.getDelayTime().dspProcess(); 26 | }, 27 | fn() { 28 | delay.dspProcess(); 29 | } 30 | }; 31 | } 32 | 33 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 34 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 35 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 36 | 37 | suite.on("cycle", (e) => { 38 | console.log(e.target.toString()); 39 | }); 40 | 41 | suite.on("complete", () => { 42 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 43 | console.log(); 44 | }); 45 | 46 | suite.on("error", (e) => { 47 | console.error(e); 48 | }); 49 | 50 | suite.run(); 51 | } 52 | 53 | module.exports = (done) => { 54 | run(1); 55 | run(2); 56 | run(4); 57 | done(); 58 | }; 59 | 60 | if (module.parent === null) { 61 | module.exports(process.exit); 62 | } 63 | -------------------------------------------------------------------------------- /benchmark/suites/DelayNode-silent-input.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const delay = new wae.impl.DelayNode(context); 18 | 19 | delay.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | delay.inputs[0].bus.zeros(); 21 | delay.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | delay.getDelayTime().setValueAtTime(0.0, 0); 24 | delay.getDelayTime().linearRampToValueAtTime(1.0, 1); 25 | delay.getDelayTime().dspProcess(); 26 | }, 27 | fn() { 28 | delay.dspProcess(); 29 | } 30 | }; 31 | } 32 | 33 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 34 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 35 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 36 | 37 | suite.on("cycle", (e) => { 38 | console.log(e.target.toString()); 39 | }); 40 | 41 | suite.on("complete", () => { 42 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 43 | console.log(); 44 | }); 45 | 46 | suite.on("error", (e) => { 47 | console.error(e); 48 | }); 49 | 50 | suite.run(); 51 | } 52 | 53 | module.exports = (done) => { 54 | run(1); 55 | run(2); 56 | run(4); 57 | done(); 58 | }; 59 | 60 | if (module.parent === null) { 61 | module.exports(process.exit); 62 | } 63 | -------------------------------------------------------------------------------- /benchmark/suites/GainNode-params-fixed-0.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const gain = new wae.impl.GainNode(context); 18 | 19 | gain.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | gain.inputs[0].bus.getMutableData(); 21 | gain.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | gain.getGain().value = 0; 24 | }, 25 | fn() { 26 | gain.dspProcess(); 27 | } 28 | }; 29 | } 30 | 31 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 32 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 33 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 34 | 35 | suite.on("cycle", (e) => { 36 | console.log(e.target.toString()); 37 | }); 38 | 39 | suite.on("complete", () => { 40 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 41 | console.log(); 42 | }); 43 | 44 | suite.on("error", (e) => { 45 | console.error(e); 46 | }); 47 | 48 | suite.run(); 49 | } 50 | 51 | module.exports = (done) => { 52 | run(1); 53 | run(2); 54 | run(4); 55 | done(); 56 | }; 57 | 58 | if (module.parent === null) { 59 | module.exports(process.exit); 60 | } 61 | -------------------------------------------------------------------------------- /benchmark/suites/GainNode-params-fixed-1.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const gain = new wae.impl.GainNode(context); 18 | 19 | gain.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | gain.inputs[0].bus.getMutableData(); 21 | gain.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | gain.getGain().value = 1; 24 | }, 25 | fn() { 26 | gain.dspProcess(); 27 | } 28 | }; 29 | } 30 | 31 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 32 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 33 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 34 | 35 | suite.on("cycle", (e) => { 36 | console.log(e.target.toString()); 37 | }); 38 | 39 | suite.on("complete", () => { 40 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 41 | console.log(); 42 | }); 43 | 44 | suite.on("error", (e) => { 45 | console.error(e); 46 | }); 47 | 48 | suite.run(); 49 | } 50 | 51 | module.exports = (done) => { 52 | run(1); 53 | run(2); 54 | run(4); 55 | done(); 56 | }; 57 | 58 | if (module.parent === null) { 59 | module.exports(process.exit); 60 | } 61 | -------------------------------------------------------------------------------- /benchmark/suites/GainNode-params-fixed-x.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const gain = new wae.impl.GainNode(context); 18 | 19 | gain.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | gain.inputs[0].bus.getMutableData(); 21 | gain.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | gain.getGain().value = 0.5; 24 | }, 25 | fn() { 26 | gain.dspProcess(); 27 | } 28 | }; 29 | } 30 | 31 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 32 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 33 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 34 | 35 | suite.on("cycle", (e) => { 36 | console.log(e.target.toString()); 37 | }); 38 | 39 | suite.on("complete", () => { 40 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 41 | console.log(); 42 | }); 43 | 44 | suite.on("error", (e) => { 45 | console.error(e); 46 | }); 47 | 48 | suite.run(); 49 | } 50 | 51 | module.exports = (done) => { 52 | run(1); 53 | run(2); 54 | run(4); 55 | done(); 56 | }; 57 | 58 | if (module.parent === null) { 59 | module.exports(process.exit); 60 | } 61 | -------------------------------------------------------------------------------- /benchmark/suites/GainNode-params-sched.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const gain = new wae.impl.GainNode(context); 18 | 19 | gain.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | gain.inputs[0].bus.getMutableData(); 21 | gain.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | gain.getGain().setValueAtTime(0.0, 0); 24 | gain.getGain().linearRampToValueAtTime(1.0, 1); 25 | gain.getGain().dspProcess(); 26 | }, 27 | fn() { 28 | gain.dspProcess(); 29 | } 30 | }; 31 | } 32 | 33 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 34 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 35 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 36 | 37 | suite.on("cycle", (e) => { 38 | console.log(e.target.toString()); 39 | }); 40 | 41 | suite.on("complete", () => { 42 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 43 | console.log(); 44 | }); 45 | 46 | suite.on("error", (e) => { 47 | console.error(e); 48 | }); 49 | 50 | suite.run(); 51 | } 52 | 53 | module.exports = (done) => { 54 | run(1); 55 | run(2); 56 | run(4); 57 | done(); 58 | }; 59 | 60 | if (module.parent === null) { 61 | module.exports(process.exit); 62 | } 63 | -------------------------------------------------------------------------------- /benchmark/suites/GainNode-silent-input.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const gain = new wae.impl.GainNode(context); 18 | 19 | gain.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | gain.inputs[0].bus.zeros(); 21 | gain.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | gain.getGain().setValueAtTime(0.0, 0); 24 | gain.getGain().linearRampToValueAtTime(1.0, 1); 25 | gain.getGain().dspProcess(); 26 | }, 27 | fn() { 28 | gain.dspProcess(); 29 | } 30 | }; 31 | } 32 | 33 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 34 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 35 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 36 | 37 | suite.on("cycle", (e) => { 38 | console.log(e.target.toString()); 39 | }); 40 | 41 | suite.on("complete", () => { 42 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 43 | console.log(); 44 | }); 45 | 46 | suite.on("error", (e) => { 47 | console.error(e); 48 | }); 49 | 50 | suite.run(); 51 | } 52 | 53 | module.exports = (done) => { 54 | run(1); 55 | run(2); 56 | run(4); 57 | done(); 58 | }; 59 | 60 | if (module.parent === null) { 61 | module.exports(process.exit); 62 | } 63 | -------------------------------------------------------------------------------- /benchmark/suites/OfflineAudioContext-noop.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(done) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, 14 | defer: true, 15 | setup() { 16 | const wae = this.wae; 17 | }, 18 | fn(deffered) { 19 | const context = new wae.OfflineAudioContext(2, 44100, 44100); 20 | 21 | context.startRendering().then(() => { 22 | deffered.resolve(); 23 | }); 24 | } 25 | }; 26 | } 27 | 28 | suite.add("src", setup(waeSrc)); 29 | suite.add("lib", setup(waeLib)); 30 | suite.add("bld", setup(waeBld)); 31 | 32 | suite.on("cycle", (e) => { 33 | console.log(e.target.toString()); 34 | }); 35 | 36 | suite.on("complete", () => { 37 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 38 | console.log(); 39 | done(); 40 | }); 41 | 42 | suite.run({ async: true }); 43 | } 44 | 45 | module.exports = (done) => { 46 | run(done); 47 | }; 48 | 49 | if (module.parent === null) { 50 | module.exports(process.exit); 51 | } 52 | -------------------------------------------------------------------------------- /benchmark/suites/OfflineAudioContext-sine.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(done) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, 14 | defer: true, 15 | setup() { 16 | const wae = this.wae; 17 | }, 18 | fn(deffered) { 19 | const context = new wae.OfflineAudioContext(2, 44100, 44100); 20 | const osc = context.createOscillator(); 21 | 22 | osc.start(0.25); 23 | osc.stop(0.75); 24 | osc.connect(context.destination); 25 | 26 | context.startRendering().then(() => { 27 | deffered.resolve(); 28 | }); 29 | } 30 | }; 31 | } 32 | 33 | suite.add("src", setup(waeSrc)); 34 | suite.add("lib", setup(waeLib)); 35 | suite.add("bld", setup(waeBld)); 36 | 37 | suite.on("cycle", (e) => { 38 | console.log(e.target.toString()); 39 | }); 40 | 41 | suite.on("complete", () => { 42 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 43 | console.log(); 44 | done(); 45 | }); 46 | 47 | suite.run({ async: true }); 48 | } 49 | 50 | module.exports = (done) => { 51 | run(done); 52 | }; 53 | 54 | if (module.parent === null) { 55 | module.exports(process.exit); 56 | } 57 | -------------------------------------------------------------------------------- /benchmark/suites/OscillatorNode-sine-params-fixed-x.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const osc = new wae.impl.OscillatorNode(context); 18 | 19 | osc.setType("sine"); 20 | osc.start(0); 21 | osc.getFrequency().value = 440; 22 | }, 23 | fn() { 24 | osc.dspProcess(); 25 | } 26 | }; 27 | } 28 | 29 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 30 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 31 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 32 | 33 | suite.on("cycle", (e) => { 34 | console.log(e.target.toString()); 35 | }); 36 | 37 | suite.on("complete", () => { 38 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 39 | console.log(); 40 | }); 41 | 42 | suite.on("error", (e) => { 43 | console.error(e); 44 | }); 45 | 46 | suite.run(); 47 | } 48 | 49 | module.exports = (done) => { 50 | run(1); 51 | done(); 52 | }; 53 | 54 | if (module.parent === null) { 55 | module.exports(process.exit); 56 | } 57 | -------------------------------------------------------------------------------- /benchmark/suites/OscillatorNode-sine-params-sched.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const osc = new wae.impl.OscillatorNode(context); 18 | 19 | osc.setType("triangle"); 20 | osc.start(0); 21 | osc.getFrequency().setValueAtTime(440, 0); 22 | osc.getFrequency().linearRampToValueAtTime(880, 1); 23 | osc.getFrequency().dspProcess(); 24 | }, 25 | fn() { 26 | osc.dspProcess(); 27 | } 28 | }; 29 | } 30 | 31 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 32 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 33 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 34 | 35 | suite.on("cycle", (e) => { 36 | console.log(e.target.toString()); 37 | }); 38 | 39 | suite.on("complete", () => { 40 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 41 | console.log(); 42 | }); 43 | 44 | suite.on("error", (e) => { 45 | console.error(e); 46 | }); 47 | 48 | suite.run(); 49 | } 50 | 51 | module.exports = (done) => { 52 | run(1); 53 | done(); 54 | }; 55 | 56 | if (module.parent === null) { 57 | module.exports(process.exit); 58 | } 59 | -------------------------------------------------------------------------------- /benchmark/suites/OscillatorNode-tri-params-fixed-x.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const osc = new wae.impl.OscillatorNode(context); 18 | 19 | osc.setType("triangle"); 20 | osc.start(0); 21 | osc.getFrequency().value = 440; 22 | }, 23 | fn() { 24 | osc.dspProcess(); 25 | } 26 | }; 27 | } 28 | 29 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 30 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 31 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 32 | 33 | suite.on("cycle", (e) => { 34 | console.log(e.target.toString()); 35 | }); 36 | 37 | suite.on("complete", () => { 38 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 39 | console.log(); 40 | }); 41 | 42 | suite.on("error", (e) => { 43 | console.error(e); 44 | }); 45 | 46 | suite.run(); 47 | } 48 | 49 | module.exports = (done) => { 50 | run(1); 51 | done(); 52 | }; 53 | 54 | if (module.parent === null) { 55 | module.exports(process.exit); 56 | } 57 | -------------------------------------------------------------------------------- /benchmark/suites/OscillatorNode-tri-params-sched.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const osc = new wae.impl.OscillatorNode(context); 18 | 19 | osc.setType("triangle"); 20 | osc.start(0); 21 | osc.getFrequency().setValueAtTime(440, 0); 22 | osc.getFrequency().linearRampToValueAtTime(880, 1); 23 | osc.getFrequency().dspProcess(); 24 | }, 25 | fn() { 26 | osc.dspProcess(); 27 | } 28 | }; 29 | } 30 | 31 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 32 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 33 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 34 | 35 | suite.on("cycle", (e) => { 36 | console.log(e.target.toString()); 37 | }); 38 | 39 | suite.on("complete", () => { 40 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 41 | console.log(); 42 | }); 43 | 44 | suite.on("error", (e) => { 45 | console.error(e); 46 | }); 47 | 48 | suite.run(); 49 | } 50 | 51 | module.exports = (done) => { 52 | run(1); 53 | done(); 54 | }; 55 | 56 | if (module.parent === null) { 57 | module.exports(process.exit); 58 | } 59 | -------------------------------------------------------------------------------- /benchmark/suites/StereoPannerNode-params-fixed-x.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const panner = new wae.impl.StereoPannerNode(context); 18 | 19 | panner.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | panner.inputs[0].bus.getMutableData(); 21 | 22 | panner.getPan().value = 0; 23 | }, 24 | fn() { 25 | panner.dspProcess(); 26 | } 27 | }; 28 | } 29 | 30 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 31 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 32 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 33 | 34 | suite.on("cycle", (e) => { 35 | console.log(e.target.toString()); 36 | }); 37 | 38 | suite.on("complete", () => { 39 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 40 | console.log(); 41 | }); 42 | 43 | suite.on("error", (e) => { 44 | console.error(e); 45 | }); 46 | 47 | suite.run(); 48 | } 49 | 50 | module.exports = (done) => { 51 | run(1); 52 | run(2); 53 | run(4); 54 | done(); 55 | }; 56 | 57 | if (module.parent === null) { 58 | module.exports(process.exit); 59 | } 60 | -------------------------------------------------------------------------------- /benchmark/suites/StereoPannerNode-params-sched.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const panner = new wae.impl.StereoPannerNode(context); 18 | 19 | panner.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | panner.inputs[0].bus.getMutableData(); 21 | 22 | panner.getPan().setValueAtTime(0.0, 0); 23 | panner.getPan().linearRampToValueAtTime(1.0, 1); 24 | panner.getPan().dspProcess(); 25 | }, 26 | fn() { 27 | panner.dspProcess(); 28 | } 29 | }; 30 | } 31 | 32 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 33 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 34 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 35 | 36 | suite.on("cycle", (e) => { 37 | console.log(e.target.toString()); 38 | }); 39 | 40 | suite.on("complete", () => { 41 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 42 | console.log(); 43 | }); 44 | 45 | suite.on("error", (e) => { 46 | console.error(e); 47 | }); 48 | 49 | suite.run(); 50 | } 51 | 52 | module.exports = (done) => { 53 | run(1); 54 | run(2); 55 | run(4); 56 | done(); 57 | }; 58 | 59 | if (module.parent === null) { 60 | module.exports(process.exit); 61 | } 62 | -------------------------------------------------------------------------------- /benchmark/suites/StereoPannerNode-silent-input.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const panner = new wae.impl.StereoPannerNode(context); 18 | 19 | panner.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | panner.inputs[0].bus.zeros(); 21 | 22 | panner.getPan().setValueAtTime(0.0, 0); 23 | panner.getPan().linearRampToValueAtTime(1.0, 1); 24 | panner.getPan().dspProcess(); 25 | }, 26 | fn() { 27 | panner.dspProcess(); 28 | } 29 | }; 30 | } 31 | 32 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 33 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 34 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 35 | 36 | suite.on("cycle", (e) => { 37 | console.log(e.target.toString()); 38 | }); 39 | 40 | suite.on("complete", () => { 41 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 42 | console.log(); 43 | }); 44 | 45 | suite.on("error", (e) => { 46 | console.error(e); 47 | }); 48 | 49 | suite.run(); 50 | } 51 | 52 | module.exports = (done) => { 53 | run(1); 54 | run(2); 55 | run(4); 56 | done(); 57 | }; 58 | 59 | if (module.parent === null) { 60 | module.exports(process.exit); 61 | } 62 | -------------------------------------------------------------------------------- /benchmark/suites/WaveShaperNode-curve.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const shaper = new wae.impl.WaveShaperNode(context); 18 | const curve = new Float32Array(1024).map((_, i) => Math.sin(i / 1024 * Math.PI * 2)); 19 | 20 | shaper.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 21 | shaper.inputs[0].bus.getMutableData(); 22 | shaper.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 23 | 24 | shaper.setCurve(curve); 25 | }, 26 | fn() { 27 | shaper.dspProcess(); 28 | } 29 | }; 30 | } 31 | 32 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 33 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 34 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 35 | 36 | suite.on("cycle", (e) => { 37 | console.log(e.target.toString()); 38 | }); 39 | 40 | suite.on("complete", () => { 41 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 42 | console.log(); 43 | }); 44 | 45 | suite.on("error", (e) => { 46 | console.error(e); 47 | }); 48 | 49 | suite.run(); 50 | } 51 | 52 | module.exports = (done) => { 53 | run(1); 54 | run(2); 55 | run(4); 56 | done(); 57 | }; 58 | 59 | if (module.parent === null) { 60 | module.exports(process.exit); 61 | } 62 | -------------------------------------------------------------------------------- /benchmark/suites/WaveShaperNode-none-curve.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const shaper = new wae.impl.WaveShaperNode(context); 18 | 19 | shaper.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 20 | shaper.inputs[0].bus.getMutableData(); 21 | shaper.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 22 | 23 | shaper.setCurve(null); 24 | }, 25 | fn() { 26 | shaper.dspProcess(); 27 | } 28 | }; 29 | } 30 | 31 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 32 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 33 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 34 | 35 | suite.on("cycle", (e) => { 36 | console.log(e.target.toString()); 37 | }); 38 | 39 | suite.on("complete", () => { 40 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 41 | console.log(); 42 | }); 43 | 44 | suite.on("error", (e) => { 45 | console.error(e); 46 | }); 47 | 48 | suite.run(); 49 | } 50 | 51 | module.exports = (done) => { 52 | run(1); 53 | run(2); 54 | run(4); 55 | done(); 56 | }; 57 | 58 | if (module.parent === null) { 59 | module.exports(process.exit); 60 | } 61 | -------------------------------------------------------------------------------- /benchmark/suites/WaveShaperNode-slient-input.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const benchmark = require("benchmark"); 4 | const waeSrc = require("../../src"); 5 | const waeLib = require("../../lib"); 6 | const waeBld = require("../../build/web-audio-engine"); 7 | 8 | function run(numberOfChannels) { 9 | const suite = new benchmark.Suite(); 10 | 11 | function setup(wae) { 12 | return { 13 | wae, numberOfChannels, 14 | setup() { 15 | const wae = this.wae; 16 | const context = new wae.impl.AudioContext(); 17 | const shaper = new wae.impl.WaveShaperNode(context); 18 | const curve = new Float32Array(1024).map((_, i) => Math.sin(i / 1024 * Math.PI * 2)); 19 | 20 | shaper.inputs[0].bus.setNumberOfChannels(this.numberOfChannels); 21 | shaper.inputs[0].bus.zeros(); 22 | shaper.outputs[0].bus.setNumberOfChannels(this.numberOfChannels); 23 | 24 | shaper.setCurve(curve); 25 | }, 26 | fn() { 27 | shaper.dspProcess(); 28 | } 29 | }; 30 | } 31 | 32 | suite.add(`src ${ numberOfChannels }ch`, setup(waeSrc)); 33 | suite.add(`lib ${ numberOfChannels }ch`, setup(waeLib)); 34 | suite.add(`bld ${ numberOfChannels }ch`, setup(waeBld)); 35 | 36 | suite.on("cycle", (e) => { 37 | console.log(e.target.toString()); 38 | }); 39 | 40 | suite.on("complete", () => { 41 | console.log("* Fastest is " + suite.filter("fastest").map("name")); 42 | console.log(); 43 | }); 44 | 45 | suite.on("error", (e) => { 46 | console.error(e); 47 | }); 48 | 49 | suite.run(); 50 | } 51 | 52 | module.exports = (done) => { 53 | run(1); 54 | run(2); 55 | run(4); 56 | done(); 57 | }; 58 | 59 | if (module.parent === null) { 60 | module.exports(process.exit); 61 | } 62 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | out.wav 2 | -------------------------------------------------------------------------------- /demo/assets/sound/a11wlk01.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohayonao/web-audio-engine/a7d09ffd8aa1ef9837355fd412457c8bab296a21/demo/assets/sound/a11wlk01.wav -------------------------------------------------------------------------------- /demo/assets/sound/amen.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohayonao/web-audio-engine/a7d09ffd8aa1ef9837355fd412457c8bab296a21/demo/assets/sound/amen.wav -------------------------------------------------------------------------------- /demo/assets/sound/hihat1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohayonao/web-audio-engine/a7d09ffd8aa1ef9837355fd412457c8bab296a21/demo/assets/sound/hihat1.wav -------------------------------------------------------------------------------- /demo/assets/sound/hihat2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohayonao/web-audio-engine/a7d09ffd8aa1ef9837355fd412457c8bab296a21/demo/assets/sound/hihat2.wav -------------------------------------------------------------------------------- /demo/assets/sound/kick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohayonao/web-audio-engine/a7d09ffd8aa1ef9837355fd412457c8bab296a21/demo/assets/sound/kick.wav -------------------------------------------------------------------------------- /demo/assets/sound/snare.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohayonao/web-audio-engine/a7d09ffd8aa1ef9837355fd412457c8bab296a21/demo/assets/sound/snare.wav -------------------------------------------------------------------------------- /demo/assets/sound/tom1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohayonao/web-audio-engine/a7d09ffd8aa1ef9837355fd412457c8bab296a21/demo/assets/sound/tom1.wav -------------------------------------------------------------------------------- /demo/assets/sound/tom2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohayonao/web-audio-engine/a7d09ffd8aa1ef9837355fd412457c8bab296a21/demo/assets/sound/tom2.wav -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const wae = require("../"); 5 | const waeCLI = require("wae-cli"); 6 | const WebAudioScheduler = require("web-audio-scheduler"); 7 | const optionator = require("optionator")(require("./package").cliOptions); 8 | 9 | function showHelp() { 10 | console.log(optionator.generateHelp()); 11 | } 12 | 13 | function showList() { 14 | fs.readdir(`${ __dirname }/sources`, (err, files) => { 15 | if (err) { 16 | return; 17 | } 18 | files = files.filter(filename => /\.js$/.test(filename)); 19 | 20 | files.forEach((filename) => { 21 | console.log(filename.replace(/\.js$/, "")); 22 | }); 23 | }); 24 | } 25 | 26 | function opts2argv(argv, opts) { 27 | argv = argv.slice(0, 2); 28 | 29 | Object.keys(opts).forEach((key) => { 30 | if (key !== "_") { 31 | if (typeof opts[key] !== "boolean") { 32 | argv.push("--" + key, "" + opts[key]); 33 | } else if (opts[key]) { 34 | argv.push("--" + key); 35 | } 36 | } 37 | }); 38 | 39 | if (opts._.length) { 40 | argv.push(`${ __dirname }/sources/${ opts._[0] }.js`); 41 | } 42 | 43 | return argv; 44 | } 45 | 46 | function fetchAudioBuffer(context, filename) { 47 | if (Array.isArray(filename)) { 48 | return Promise.all(filename.map(filename => fetchAudioBuffer(context, filename))); 49 | } 50 | 51 | return new Promise((resolve, reject) => { 52 | fs.readFile(`${ __dirname }/assets/sound/${ filename }`, (err, data) => { 53 | context.decodeAudioData(data).then(resolve, reject); 54 | }); 55 | }); 56 | } 57 | 58 | function main(argv) { 59 | const opts = optionator.parse(process.argv); 60 | 61 | if (opts.help) { 62 | return showHelp(); 63 | } 64 | 65 | if (opts.list) { 66 | return showList(); 67 | } 68 | 69 | let sandbox = null; 70 | 71 | const util = { 72 | WebAudioScheduler: WebAudioScheduler, 73 | fetchAudioBuffer(filename) { 74 | return Promise.resolve().then(() => { 75 | return fetchAudioBuffer(sandbox.audioContext, filename); 76 | }); 77 | }, 78 | exit() { sandbox.process.exit(); } 79 | }; 80 | 81 | argv = opts2argv(argv, opts); 82 | sandbox = waeCLI.run(wae, argv, util); 83 | } 84 | 85 | main(process.argv); 86 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@web-audio-engine/demo", 3 | "description": "sound test", 4 | "version": "0.0.0", 5 | "cliOptions": { 6 | "prepend": "Usage: node demo [options] demo-name", 7 | "options": [ 8 | { 9 | "option": "help", 10 | "alias": "h", 11 | "type": "Boolean", 12 | "description": "display help" 13 | }, 14 | { 15 | "option": "list", 16 | "alias": "l", 17 | "type": "Boolean", 18 | "description": "display demo name" 19 | }, 20 | { 21 | "option": "type", 22 | "alias": "t", 23 | "type": "String", 24 | "description": "File type of audio", 25 | "default": "s16" 26 | }, 27 | { 28 | "option": "rate", 29 | "alias": "r", 30 | "type": "Number", 31 | "description": "Sample rate of audio", 32 | "default": "44100" 33 | }, 34 | { 35 | "option": "channels", 36 | "alias": "c", 37 | "type": "Number", 38 | "description": "Number of channels of audio data; e.g. 2 = stereo", 39 | "default": "2" 40 | }, 41 | { 42 | "option": "duration", 43 | "alias": "d", 44 | "type": "Number", 45 | "description": "Number of duration for rendering" 46 | }, 47 | { 48 | "option": "out", 49 | "alias": "o", 50 | "type": "String", 51 | "description": "Write output to " 52 | }, 53 | { 54 | "option": "verbose", 55 | "alias": "V", 56 | "type": "Boolean", 57 | "description": "Run in verbose mode", 58 | "default": "false" 59 | } 60 | ], 61 | "append": "AUDIO FILE FORMATS: s16 s32 u8 raw cd" 62 | }, 63 | "dependencies": { 64 | "optionator": "^0.8.1", 65 | "wae-cli": "^0.6.0", 66 | "web-audio-scheduler": "^1.1.0" 67 | }, 68 | "license": "MIT", 69 | "main": "demo.js", 70 | "private": true 71 | } 72 | -------------------------------------------------------------------------------- /demo/sources/chorus.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context, util) { 2 | return util.fetchAudioBuffer("a11wlk01.wav").then(function(instruments) { 3 | var bufSrc = context.createBufferSource(); 4 | var delay = context.createDelay(); 5 | var lfo1 = context.createOscillator(); 6 | var lfo2 = context.createGain(); 7 | var amp1 = context.createGain(); 8 | var amp2 = context.createGain(); 9 | 10 | bufSrc.buffer = instruments; 11 | bufSrc.loop = true; 12 | bufSrc.start(context.currentTime); 13 | bufSrc.connect(amp1); 14 | bufSrc.connect(delay); 15 | 16 | lfo1.frequency.value = 0.125; 17 | lfo1.start(context.currentTime); 18 | lfo1.connect(lfo2); 19 | 20 | lfo2.gain.value = 0.015; 21 | lfo2.connect(delay.delayTime); 22 | 23 | delay.delayTime.value = 0.03; 24 | delay.connect(amp2); 25 | 26 | amp1.gain.value = 0.6; 27 | amp1.connect(context.destination); 28 | 29 | amp2.gain.value = 0.4; 30 | amp2.connect(context.destination); 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /demo/sources/delay.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context, util) { 2 | var sched = new util.WebAudioScheduler({ context: context, timerAPI: global }); 3 | 4 | function sample(list) { 5 | return list[(Math.random() * list.length)|0]; 6 | } 7 | 8 | function mtof(value) { 9 | return 440 * Math.pow(2, (value - 69) / 12); 10 | } 11 | 12 | function synth(t0, midi, dur) { 13 | var osc = context.createOscillator(); 14 | var amp = context.createGain(); 15 | var delay = context.createDelay(0.1); 16 | var out = context.createGain(); 17 | var t1 = t0 + dur; 18 | 19 | osc.type = "triangle"; 20 | osc.frequency.value = mtof(midi); 21 | osc.start(t0); 22 | osc.stop(t1); 23 | osc.connect(amp); 24 | 25 | amp.gain.setValueAtTime(0.075, t0); 26 | amp.gain.linearRampToValueAtTime(0, t1); 27 | amp.connect(out); 28 | amp.connect(delay); 29 | 30 | delay.delayTime.value = 0.09375; 31 | delay.connect(out); 32 | 33 | return out; 34 | } 35 | 36 | function createFeedbackDelay() { 37 | var input = context.createGain(); 38 | var delay = context.createDelay(); 39 | var feedback = context.createGain(); 40 | var output = context.createGain(); 41 | 42 | input.connect(delay); 43 | input.connect(output); 44 | 45 | delay.delayTime.value = 0.125; 46 | delay.connect(feedback); 47 | 48 | feedback.gain.value = 0.925; 49 | feedback.connect(input); 50 | 51 | return { input: input, output: output, feedback: feedback.gain }; 52 | } 53 | 54 | var efx = createFeedbackDelay(); 55 | 56 | function compose(e) { 57 | var t0 = e.playbackTime; 58 | var midi = sample([ 64, 65, 65, 65, 69, 69, 72, 76 ]) + 12; 59 | var dur = sample([ 0.125, 0.25, 0.25, 0.5 ]); 60 | var feedback = sample([ 0.4, 0.6, 0.8, 0.9, 0.975 ]); 61 | var nextTime = dur * sample([ 0.5, 1, 1, 1.5, 1.5, 2, 2, 4 ]); 62 | 63 | efx.feedback.setTargetAtTime(feedback, t0, 1); 64 | 65 | synth(t0, midi, dur).connect(efx.input); 66 | 67 | sched.insert(t0 + nextTime, compose); 68 | } 69 | 70 | efx.output.connect(context.destination); 71 | 72 | sched.start(compose); 73 | }; 74 | -------------------------------------------------------------------------------- /demo/sources/filter.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context, util) { 2 | var sched = new util.WebAudioScheduler({ context: context, timerAPI: global }); 3 | 4 | function sample(list) { 5 | return list[(Math.random() * list.length)|0]; 6 | } 7 | 8 | function mtof(value) { 9 | return 440 * Math.pow(2, (value - 69) / 12); 10 | } 11 | 12 | function synth(t0, midi, dur) { 13 | var filter = context.createBiquadFilter(); 14 | var gain = context.createGain(); 15 | var t1 = t0 + dur; 16 | var freq = mtof(midi); 17 | 18 | [ 1, 3/2, 15/8 ].forEach(function(ratio, index) { 19 | [ -12, +12 ].forEach(function(detune) { 20 | var osc = context.createOscillator(); 21 | 22 | osc.type = "sawtooth"; 23 | osc.frequency.value = freq * ratio; 24 | osc.detune.value = detune; 25 | osc.start(t0); 26 | osc.stop(t1); 27 | osc.connect(filter); 28 | }); 29 | }); 30 | 31 | var cutoff1 = freq * sample([ 0.25, 0.5, 1, 2, 2, 4, 4, 4, 6, 6, 8 ]); 32 | var cutoff2 = freq * sample([ 0.25, 0.5, 1, 2, 2, 4, 4, 4, 6, 6, 8 ]); 33 | 34 | filter.type = "bandpass"; 35 | filter.frequency.setValueAtTime(cutoff1, t0); 36 | filter.frequency.exponentialRampToValueAtTime(cutoff2, t1); 37 | filter.Q.value = 4; 38 | filter.connect(gain); 39 | 40 | gain.gain.setValueAtTime(0.1, t0); 41 | gain.gain.linearRampToValueAtTime(0, t1); 42 | gain.connect(context.destination); 43 | } 44 | 45 | function compose(e) { 46 | var t0 = e.playbackTime; 47 | var midi = sample([ 60, 60, 62, 64, 64, 67, 69 ]); 48 | var dur = sample([ 2, 4, 8, 16 ]); 49 | var nextTime = dur * sample([ 0.25, 0.25, 0.5, 0.5, 0.5, 0.5, 1, 1.25 ]); 50 | 51 | synth(t0, midi, dur); 52 | 53 | sched.insert(t0 + nextTime, compose); 54 | } 55 | 56 | sched.start(compose); 57 | }; 58 | -------------------------------------------------------------------------------- /demo/sources/metronome.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context, util) { 2 | var sched = new util.WebAudioScheduler({ context: context, timerAPI: global }); 3 | 4 | function metronome(e) { 5 | var t0 = e.playbackTime; 6 | 7 | sched.insert(t0 + 0.000, ticktack, { frequency: 880, amp: 1.0, duration: 1.00 }); 8 | sched.insert(t0 + 0.500, ticktack, { frequency: 440, amp: 0.4, duration: 0.25 }); 9 | sched.insert(t0 + 1.000, ticktack, { frequency: 440, amp: 0.5, duration: 0.25 }); 10 | sched.insert(t0 + 1.500, ticktack, { frequency: 440, amp: 0.4, duration: 0.25 }); 11 | sched.insert(t0 + 2.000, metronome); 12 | } 13 | 14 | function ticktack(e) { 15 | var t0 = e.playbackTime; 16 | var t1 = t0 + e.args.duration; 17 | var osc = context.createOscillator(); 18 | var amp = context.createGain(); 19 | 20 | osc.frequency.value = e.args.frequency; 21 | osc.start(t0); 22 | osc.stop(t1); 23 | osc.connect(amp); 24 | 25 | amp.gain.setValueAtTime(0.5 * e.args.amp, t0); 26 | amp.gain.exponentialRampToValueAtTime(1e-6, t1); 27 | amp.connect(context.destination); 28 | } 29 | 30 | sched.start(metronome); 31 | }; 32 | -------------------------------------------------------------------------------- /demo/sources/pan.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context, util) { 2 | var sched = new util.WebAudioScheduler({ context: context, timerAPI: global }); 3 | 4 | function sample(list) { 5 | return list[(Math.random() * list.length)|0]; 6 | } 7 | 8 | function mtof(value) { 9 | return 440 * Math.pow(2, (value - 69) / 12); 10 | } 11 | 12 | function synth(t0, midi, dur, amp, pos) { 13 | var t1 = t0 + dur; 14 | var osc = context.createOscillator(); 15 | var pan = context.createStereoPanner(); 16 | var gain = context.createGain(); 17 | 18 | osc.frequency.value = mtof(midi); 19 | osc.start(t0); 20 | osc.stop(t1); 21 | osc.connect(pan); 22 | 23 | pan.pan.value = pos; 24 | pan.connect(gain); 25 | 26 | gain.gain.setValueAtTime(amp, t0); 27 | gain.gain.linearRampToValueAtTime(0, t1); 28 | gain.connect(context.destination); 29 | } 30 | 31 | function compose(e) { 32 | var t0 = e.playbackTime; 33 | var midi = 60 + sample([ 1, 2, 2, 2, 3, 4, 4, 5 ]) * 12; 34 | var dur = sample([ 0.125, 0.125, 0.125, 0.250, 0.250, 0.5 ]); 35 | var amp = sample([ 0.05, 0.1, 0.2, 0.4 ]); 36 | var pos = sample([ 0.2, 0.4, 0.6, 0.8, 1.0 ]) * sample([ -1, +1 ]); 37 | var iterations = sample([ 1, 1, 2, 2, 2, 3, 4, 8 ]); 38 | var interval = 0.250 / iterations; 39 | 40 | dur = dur / iterations; 41 | 42 | for (var i = 0; i < iterations; i++) { 43 | synth(t0 + interval * i, midi, dur, amp, pos); 44 | } 45 | 46 | sched.insert(t0 + 0.125, compose); 47 | } 48 | 49 | sched.start(compose); 50 | }; 51 | -------------------------------------------------------------------------------- /demo/sources/sines.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context, util) { 2 | var sched = new util.WebAudioScheduler({ context: context, timerAPI: global }); 3 | 4 | function sample(list) { 5 | return list[(Math.random() * list.length)|0]; 6 | } 7 | 8 | function mtof(value) { 9 | return 440 * Math.pow(2, (value - 69) / 12); 10 | } 11 | 12 | function synth(t0, midi, dur) { 13 | var osc1 = context.createOscillator(); 14 | var osc2 = context.createOscillator(); 15 | var gain = context.createGain(); 16 | var t1 = t0 + dur * 0.25; 17 | var t2 = t1 + dur * 0.75; 18 | 19 | osc1.frequency.value = mtof(midi); 20 | osc1.detune.setValueAtTime(+4, t0); 21 | osc1.detune.linearRampToValueAtTime(+12, t2); 22 | osc1.start(t0); 23 | osc1.stop(t2); 24 | osc1.connect(gain); 25 | 26 | osc2.frequency.value = mtof(midi); 27 | osc2.detune.setValueAtTime(-4, t0); 28 | osc2.detune.linearRampToValueAtTime(-12, t2); 29 | osc2.start(t0); 30 | osc2.stop(t2); 31 | osc2.connect(gain); 32 | 33 | gain.gain.setValueAtTime(0, t0); 34 | gain.gain.linearRampToValueAtTime(0.125, t1); 35 | gain.gain.linearRampToValueAtTime(0, t2); 36 | gain.connect(context.destination); 37 | } 38 | 39 | function compose(e) { 40 | var t0 = e.playbackTime; 41 | var midi = sample([ 72, 72, 74, 76, 76, 79, 81 ]); 42 | var dur = sample([ 2, 2, 4, 4, 4, 4, 8 ]); 43 | var nextTime = dur * sample([ 0.125, 0.125, 0.25, 0.25, 0.25, 0.25, 0.5, 0.75 ]); 44 | 45 | synth(t0, midi, dur); 46 | 47 | sched.insert(t0 + nextTime, compose); 48 | } 49 | 50 | sched.start(compose); 51 | }; 52 | -------------------------------------------------------------------------------- /demo/sources/test_delay-modulation.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context) { 2 | // +---------------+ +---------------+ 3 | // | oscillator1 | | oscillator2 | 4 | // +---------------+ +---------------+ 5 | // | | 6 | // | +---------------+ 7 | // | | gain2 | 8 | // | +---------------+ 9 | // | | 10 | // +---------------+ | 11 | // | delay | | 12 | // | * delayTime <-------------+ 13 | // +---------------+ 14 | // | 15 | // +---------------+ 16 | // | gain1 | 17 | // +---------------+ 18 | var oscillator1 = context.createOscillator(); 19 | var delay = context.createDelay(); 20 | var gain1 = context.createGain(); 21 | var oscillator2 = context.createOscillator(); 22 | var gain2 = context.createGain(); 23 | 24 | oscillator1.type = "sine"; 25 | oscillator1.frequency.value = 880; 26 | oscillator1.start(); 27 | oscillator1.connect(delay); 28 | 29 | delay.delayTime.value = 0.5; 30 | delay.connect(gain1); 31 | 32 | gain1.gain.value = 0.25; 33 | gain1.connect(context.destination); 34 | 35 | oscillator2.type = "sine"; 36 | oscillator2.frequency.value = 20; 37 | oscillator2.start(); 38 | oscillator2.connect(gain2); 39 | 40 | gain2.gain.value = 0.005; 41 | gain2.connect(delay.delayTime); 42 | }; 43 | -------------------------------------------------------------------------------- /demo/sources/test_filter-modulation.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context) { 2 | // +---------------+ +---------------+ 3 | // | oscillator1 | | oscillator2 | 4 | // +---------------+ +---------------+ 5 | // | | 6 | // | +---------------+ 7 | // | | gain2 | 8 | // | +---------------+ 9 | // | | 10 | // +---------------+ | 11 | // | biquadFilter | | 12 | // | * frequency <-------------+ 13 | // +---------------+ 14 | // | 15 | // +---------------+ 16 | // | gain1 | 17 | // +---------------+ 18 | var oscillator1 = context.createOscillator(); 19 | var biquadFilter = context.createBiquadFilter(); 20 | var gain1 = context.createGain(); 21 | var oscillator2 = context.createOscillator(); 22 | var gain2 = context.createGain(); 23 | 24 | oscillator1.type = "sawtooth"; 25 | oscillator1.frequency.value = 880; 26 | oscillator1.start(); 27 | oscillator1.connect(biquadFilter); 28 | 29 | biquadFilter.type = "lowpass"; 30 | biquadFilter.frequency.value = 1000; 31 | biquadFilter.connect(gain1); 32 | 33 | gain1.gain.value = 0.25; 34 | gain1.connect(context.destination); 35 | 36 | oscillator2.type = "sine"; 37 | oscillator2.frequency.value = 4; 38 | oscillator2.start(); 39 | oscillator2.connect(gain2); 40 | 41 | gain2.gain.value = 400; 42 | gain2.connect(biquadFilter.frequency); 43 | }; 44 | -------------------------------------------------------------------------------- /demo/sources/test_iir-filter.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context, util) { 2 | return util.fetchAudioBuffer("amen.wav").then(function(instruments) { 3 | var bufSrc = context.createBufferSource(); 4 | var iir = context.createIIRFilter([ 0.182377, 0, -0.182377 ], [ 1, -1.956784, 0.979231 ]); 5 | 6 | bufSrc.buffer = instruments; 7 | bufSrc.loop = true; 8 | bufSrc.start(context.currentTime); 9 | bufSrc.connect(iir); 10 | 11 | iir.connect(context.destination); 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /demo/sources/test_loop.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context, util) { 2 | return util.fetchAudioBuffer("amen.wav").then(function(instruments) { 3 | var bufSrc = context.createBufferSource(); 4 | 5 | bufSrc.buffer = instruments; 6 | bufSrc.loop = true; 7 | bufSrc.loopStart = 0.2; 8 | bufSrc.loopEnd = 0.4; 9 | bufSrc.start(0, 0); 10 | bufSrc.connect(context.destination); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-audio-engine", 3 | "description": "Pure JS implementation of the Web Audio API", 4 | "version": "0.13.4", 5 | "author": "Nao Yonamine ", 6 | "bugs": { 7 | "url": "https://github.com/mohayonao/web-audio-engine/issues" 8 | }, 9 | "dependencies": { 10 | "audio-type": "^1.0.2", 11 | "biquad-coeffs-webaudio": "^1.2.0", 12 | "fourier-transform": "^1.0.2", 13 | "nmap": "^1.1.0", 14 | "scijs-window-functions": "^2.0.2", 15 | "wav-decoder": "^1.1.0", 16 | "wav-encoder": "^1.1.0" 17 | }, 18 | "devDependencies": { 19 | "babel-cli": "^6.24.1", 20 | "babel-plugin-istanbul": "^4.1.1", 21 | "babel-plugin-unassert": "^2.1.2", 22 | "babel-preset-es2015": "^6.24.1", 23 | "babel-preset-power-assert": "^1.0.0", 24 | "babelify": "^7.3.0", 25 | "browserify": "^14.3.0", 26 | "eslint": "^3.19.0", 27 | "mocha": "^3.3.0", 28 | "npm-run-all": "^4.0.2", 29 | "nyc": "^10.2.0", 30 | "power-assert": "^1.4.2", 31 | "run-with-mocha": "^1.1.0", 32 | "sinon": "^2.1.0" 33 | }, 34 | "engines": { 35 | "node": ">= 4.0.0" 36 | }, 37 | "files": [ 38 | "package.json", 39 | "README.md", 40 | "build", 41 | "lib" 42 | ], 43 | "homepage": "https://github.com/mohayonao/web-audio-engine/", 44 | "keywords": [ 45 | "web audio api" 46 | ], 47 | "license": "MIT", 48 | "main": "lib/index.js", 49 | "repository": { 50 | "type": "git", 51 | "url": "https://github.com/mohayonao/web-audio-engine.git" 52 | }, 53 | "scripts": { 54 | "build": "npm-run-all build:*", 55 | "build:browser": "BABEL_ENV=build browserify src -s WebAudioEngine -o build/web-audio-engine.js -t [ babelify ]", 56 | "build:to5": "BABEL_ENV=build babel --out-dir=lib src", 57 | "clean": "rm -rf lib coverage .nyc_output npm-debug.log", 58 | "cover": "BABEL_ENV=cover nyc --reporter text --reporter html npm test", 59 | "lint": "eslint src test", 60 | "postversion": "git push && git push --tags && npm run clean", 61 | "prepublish": "npm-run-all clean lint test build", 62 | "preversion": "npm-run-all clean lint test", 63 | "test": "mocha", 64 | "travis": "npm-run-all lint test", 65 | "version": "npm run build && git add -A build" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/api/AnalyserNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | 6 | class AnalyserNode extends AudioNode { 7 | constructor(context, opts) { 8 | super(context); 9 | 10 | this._impl = new impl.AnalyserNode(context._impl, opts); 11 | } 12 | 13 | get fftSize() { 14 | return this._impl.getFftSize(); 15 | } 16 | 17 | set fftSize(value) { 18 | this._impl.setFftSize(value); 19 | } 20 | 21 | get frequencyBinCount() { 22 | return this._impl.getFrequencyBinCount(); 23 | } 24 | 25 | get minDecibels() { 26 | return this._impl.getMinDecibels(); 27 | } 28 | 29 | set minDecibels(value) { 30 | this._impl.setMinDecibels(value); 31 | } 32 | 33 | get maxDecibels() { 34 | return this._impl.getMaxDecibels(); 35 | } 36 | 37 | set maxDecibels(value) { 38 | this._impl.setMaxDecibels(value); 39 | } 40 | 41 | get smoothingTimeConstant() { 42 | return this._impl.getSmoothingTimeConstant(); 43 | } 44 | 45 | set smoothingTimeConstant(value) { 46 | this._impl.setSmoothingTimeConstant(value); 47 | } 48 | 49 | getFloatFrequencyData(array) { 50 | this._impl.getFloatFrequencyData(array); 51 | } 52 | 53 | getByteFrequencyData(array) { 54 | this._impl.getByteFrequencyData(array); 55 | } 56 | 57 | getFloatTimeDomainData(array) { 58 | this._impl.getFloatTimeDomainData(array); 59 | } 60 | 61 | getByteTimeDomainData(array) { 62 | this._impl.getByteTimeDomainData(array); 63 | } 64 | } 65 | 66 | module.exports = AnalyserNode; 67 | -------------------------------------------------------------------------------- /src/api/AudioBuffer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const { defineProp } = require("../utils"); 5 | 6 | class AudioBuffer { 7 | constructor(opts) { 8 | defineProp(this, "_impl", new impl.AudioBuffer(opts)); 9 | } 10 | 11 | get sampleRate() { 12 | return this._impl.getSampleRate(); 13 | } 14 | 15 | get length() { 16 | return this._impl.getLength(); 17 | } 18 | 19 | get duration() { 20 | return this._impl.getDuration(); 21 | } 22 | 23 | get numberOfChannels() { 24 | return this._impl.getNumberOfChannels(); 25 | } 26 | 27 | getChannelData(channel) { 28 | return this._impl.getChannelData(channel); 29 | } 30 | 31 | copyFromChannel(destination, channelNumber, startInChannel) { 32 | this._impl.copyFromChannel(destination, channelNumber, startInChannel); 33 | } 34 | 35 | copyToChannel(source, channelNumber, startInChannel) { 36 | this._impl.copyToChannel(source, channelNumber, startInChannel); 37 | } 38 | } 39 | 40 | module.exports = AudioBuffer; 41 | -------------------------------------------------------------------------------- /src/api/AudioBufferSourceNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioScheduledSourceNode = require("./AudioScheduledSourceNode"); 5 | const AudioParam = require("./AudioParam"); 6 | 7 | class AudioBufferSourceNode extends AudioScheduledSourceNode { 8 | constructor(context, opts) { 9 | super(context); 10 | 11 | this._impl = new impl.AudioBufferSourceNode(context._impl, opts); 12 | this._impl.$playbackRate = new AudioParam(context, this._impl.getPlaybackRate()); 13 | this._impl.$detune = new AudioParam(context, this._impl.getDetune()); 14 | this._impl.$buffer = null; 15 | this._impl.$onended = null; 16 | 17 | if (opts && opts.buffer) { 18 | this.buffer = opts.buffer; 19 | } 20 | } 21 | 22 | get buffer() { 23 | return this._impl.$buffer; 24 | } 25 | 26 | set buffer(value) { 27 | this._impl.$buffer = value; 28 | this._impl.setBuffer(value); 29 | } 30 | 31 | get playbackRate() { 32 | return this._impl.$playbackRate; 33 | } 34 | 35 | get detune() { 36 | return this._impl.$detune; 37 | } 38 | 39 | get loop() { 40 | return this._impl.getLoop(); 41 | } 42 | 43 | set loop(value) { 44 | this._impl.setLoop(value); 45 | } 46 | 47 | get loopStart() { 48 | return this._impl.getLoopStart(); 49 | } 50 | 51 | set loopStart(value) { 52 | this._impl.setLoopStart(value); 53 | } 54 | 55 | get loopEnd() { 56 | return this._impl.getLoopEnd(); 57 | } 58 | 59 | set loopEnd(value) { 60 | this._impl.setLoopEnd(value); 61 | } 62 | 63 | start(when, offset, duration) { 64 | this._impl.start(when, offset, duration); 65 | } 66 | } 67 | 68 | module.exports = AudioBufferSourceNode; 69 | -------------------------------------------------------------------------------- /src/api/AudioDestinationNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioNode = require("./AudioNode"); 4 | 5 | class AudioDestinationNode extends AudioNode { 6 | constructor(context, impl) { 7 | super(context); 8 | 9 | this._impl = impl; 10 | } 11 | 12 | get maxChannelCount() { 13 | return this._impl.getMaxChannelCount(); 14 | } 15 | } 16 | 17 | module.exports = AudioDestinationNode; 18 | -------------------------------------------------------------------------------- /src/api/AudioListener.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { defineProp } = require("../utils"); 4 | 5 | class AudioListener { 6 | constructor(context, impl) { 7 | defineProp(this, "_impl", impl); 8 | } 9 | 10 | setPosition(x, y, z) { 11 | this._impl.setPosition(x, y, z); 12 | } 13 | 14 | setOrientation(x, y, z, xUp, yUp, zUp) { 15 | this._impl.setOrientation(x, y, z, xUp, yUp, zUp); 16 | } 17 | } 18 | 19 | module.exports = AudioListener; 20 | -------------------------------------------------------------------------------- /src/api/AudioNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const EventTarget = require("./EventTarget"); 4 | const { defineProp } = require("../utils"); 5 | 6 | class AudioNode extends EventTarget { 7 | constructor(context) { 8 | super(); 9 | 10 | defineProp(this, "_context", context); 11 | defineProp(this, "_impl", null); 12 | } 13 | 14 | get context() { 15 | return this._context; 16 | } 17 | 18 | get numberOfInputs() { 19 | return this._impl.getNumberOfInputs(); 20 | } 21 | 22 | get numberOfOutputs() { 23 | return this._impl.getNumberOfOutputs(); 24 | } 25 | 26 | get channelCount() { 27 | return this._impl.getChannelCount(); 28 | } 29 | 30 | set channelCount(value) { 31 | this._impl.setChannelCount(value); 32 | } 33 | 34 | get channelCountMode() { 35 | return this._impl.getChannelCountMode(); 36 | } 37 | 38 | set channelCountMode(value) { 39 | this._impl.setChannelCountMode(value); 40 | } 41 | 42 | get channelInterpretation() { 43 | return this._impl.getChannelInterpretation(); 44 | } 45 | 46 | set channelInterpretation(value) { 47 | return this._impl.setChannelInterpretation(value); 48 | } 49 | 50 | connect(destination, input, output) { 51 | this._impl.connect(destination._impl, input, output); 52 | 53 | /* istanbul ignore else */ 54 | if (destination instanceof AudioNode) { 55 | return destination; 56 | } 57 | } 58 | 59 | disconnect() { 60 | this._impl.disconnect.apply(this._impl, arguments); 61 | } 62 | } 63 | 64 | module.exports = AudioNode; 65 | -------------------------------------------------------------------------------- /src/api/AudioParam.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { defineProp } = require("../utils"); 4 | 5 | class AudioParam { 6 | constructor(context, impl) { 7 | defineProp(this, "_context", context); 8 | defineProp(this, "_impl", impl); 9 | } 10 | 11 | get value() { 12 | return this._impl.getValue(); 13 | } 14 | 15 | set value(value) { 16 | this._impl.setValue(value); 17 | } 18 | 19 | get defaultValue() { 20 | return this._impl.getDefaultValue(); 21 | } 22 | 23 | setValueAtTime(value, startTime) { 24 | this._impl.setValueAtTime(value, startTime); 25 | return this; 26 | } 27 | 28 | linearRampToValueAtTime(value, endTime) { 29 | this._impl.linearRampToValueAtTime(value, endTime); 30 | return this; 31 | } 32 | 33 | exponentialRampToValueAtTime(value, endTime) { 34 | this._impl.exponentialRampToValueAtTime(value, endTime); 35 | return this; 36 | } 37 | 38 | setTargetAtTime(target, startTime, timeConstant) { 39 | this._impl.setTargetAtTime(target, startTime, timeConstant); 40 | return this; 41 | } 42 | 43 | setValueCurveAtTime(values, startTime, duration) { 44 | this._impl.setValueCurveAtTime(values, startTime, duration); 45 | return this; 46 | } 47 | 48 | cancelScheduledValues(startTime) { 49 | this._impl.cancelScheduledValues(startTime); 50 | return this; 51 | } 52 | } 53 | 54 | module.exports = AudioParam; 55 | -------------------------------------------------------------------------------- /src/api/AudioScheduledSourceNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioNode = require("./AudioNode"); 4 | 5 | class AudioScheduledSourceNode extends AudioNode { 6 | get onended() { 7 | return this._impl.$onended; 8 | } 9 | 10 | set onended(callback) { 11 | this._impl.replaceEventListener("ended", this._impl.$onended, callback); 12 | this._impl.$onended = callback; 13 | } 14 | 15 | start(when) { 16 | this._impl.start(when); 17 | } 18 | 19 | stop(when) { 20 | this._impl.stop(when); 21 | } 22 | } 23 | 24 | module.exports = AudioScheduledSourceNode; 25 | -------------------------------------------------------------------------------- /src/api/BiquadFilterNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | const AudioParam = require("./AudioParam"); 6 | 7 | class BiquadFilterNode extends AudioNode { 8 | constructor(context, opts) { 9 | super(context); 10 | 11 | this._impl = new impl.BiquadFilterNode(context._impl, opts); 12 | this._impl.$frequency = new AudioParam(context, this._impl.getFrequency()); 13 | this._impl.$detune = new AudioParam(context, this._impl.getDetune()); 14 | this._impl.$Q = new AudioParam(context, this._impl.getQ()); 15 | this._impl.$gain = new AudioParam(context, this._impl.getGain()); 16 | } 17 | 18 | get type() { 19 | return this._impl.getType(); 20 | } 21 | 22 | set type(value) { 23 | this._impl.setType(value); 24 | } 25 | 26 | get frequency() { 27 | return this._impl.$frequency; 28 | } 29 | 30 | get detune() { 31 | return this._impl.$detune; 32 | } 33 | 34 | get Q() { 35 | return this._impl.$Q; 36 | } 37 | 38 | get gain() { 39 | return this._impl.$gain; 40 | } 41 | 42 | getFrequencyResponse(frequencyHz, magResponse, phaseResponse) { 43 | this._impl.getFrequencyResponse(frequencyHz, magResponse, phaseResponse); 44 | } 45 | } 46 | 47 | module.exports = BiquadFilterNode; 48 | -------------------------------------------------------------------------------- /src/api/ChannelMergerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | 6 | class ChannelMergerNode extends AudioNode { 7 | constructor(context, opts) { 8 | super(context); 9 | 10 | this._impl = new impl.ChannelMergerNode(context._impl, opts); 11 | } 12 | } 13 | 14 | module.exports = ChannelMergerNode; 15 | -------------------------------------------------------------------------------- /src/api/ChannelSplitterNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | 6 | class ChannelSplitterNode extends AudioNode { 7 | constructor(context, opts) { 8 | super(context); 9 | 10 | this._impl = new impl.ChannelSplitterNode(context._impl, opts); 11 | } 12 | } 13 | 14 | module.exports = ChannelSplitterNode; 15 | -------------------------------------------------------------------------------- /src/api/ConstantSourceNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioScheduledSourceNode = require("./AudioScheduledSourceNode"); 5 | const AudioParam = require("./AudioParam"); 6 | 7 | class ConstantSourceNode extends AudioScheduledSourceNode { 8 | constructor(context, opts) { 9 | super(context); 10 | 11 | this._impl = new impl.ConstantSourceNode(context._impl, opts); 12 | this._impl.$offset = new AudioParam(context, this._impl.getOffset()); 13 | this._impl.$onended = null; 14 | } 15 | 16 | get offset() { 17 | return this._impl.$offset; 18 | } 19 | } 20 | 21 | module.exports = ConstantSourceNode; 22 | -------------------------------------------------------------------------------- /src/api/ConvolverNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | 6 | class ConvolverNode extends AudioNode { 7 | constructor(context, opts) { 8 | super(context); 9 | 10 | this._impl = new impl.ConvolverNode(context._impl, opts); 11 | this._impl.$buffer = null; 12 | 13 | if (opts && opts.buffer) { 14 | this.buffer = opts.buffer; 15 | } 16 | } 17 | 18 | get buffer() { 19 | return this._impl.$buffer; 20 | } 21 | 22 | set buffer(value) { 23 | this._impl.$buffer = value; 24 | this._impl.setBuffer(value); 25 | } 26 | 27 | get normalize() { 28 | return this._impl.getNormalize(); 29 | } 30 | 31 | set normalize(value) { 32 | this._impl.setNormalize(value); 33 | } 34 | } 35 | 36 | module.exports = ConvolverNode; 37 | -------------------------------------------------------------------------------- /src/api/DelayNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | const AudioParam = require("./AudioParam"); 6 | 7 | class DelayNode extends AudioNode { 8 | constructor(context, opts) { 9 | super(context); 10 | 11 | this._impl = new impl.DelayNode(context._impl, opts); 12 | this._impl.$delayTime = new AudioParam(context, this._impl.getDelayTime()); 13 | } 14 | 15 | get delayTime() { 16 | return this._impl.$delayTime; 17 | } 18 | } 19 | 20 | module.exports = DelayNode; 21 | -------------------------------------------------------------------------------- /src/api/DynamicsCompressorNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | const AudioParam = require("./AudioParam"); 6 | 7 | class DynamicsCompressorNode extends AudioNode { 8 | constructor(context, opts) { 9 | super(context); 10 | 11 | this._impl = new impl.DynamicsCompressorNode(context._impl, opts); 12 | this._impl.$threshold = new AudioParam(context, this._impl.getThreshold()); 13 | this._impl.$knee = new AudioParam(context, this._impl.getKnee()); 14 | this._impl.$ratio = new AudioParam(context, this._impl.getRatio()); 15 | this._impl.$attack = new AudioParam(context, this._impl.getAttack()); 16 | this._impl.$release = new AudioParam(context, this._impl.getRelease()); 17 | } 18 | 19 | get threshold() { 20 | return this._impl.$threshold; 21 | } 22 | 23 | get knee() { 24 | return this._impl.$knee; 25 | } 26 | 27 | get ratio() { 28 | return this._impl.$ratio; 29 | } 30 | 31 | get reduction() { 32 | return this._impl.getReduction(); 33 | } 34 | 35 | get attack() { 36 | return this._impl.$attack; 37 | } 38 | 39 | get release() { 40 | return this._impl.$release; 41 | } 42 | } 43 | 44 | module.exports = DynamicsCompressorNode; 45 | -------------------------------------------------------------------------------- /src/api/EventTarget.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class EventTarget { 4 | addEventListener(type, listener) { 5 | this._impl.addEventListener(type, listener); 6 | } 7 | 8 | removeEventListener(type, listener) { 9 | this._impl.removeEventListener(type, listener); 10 | } 11 | } 12 | 13 | module.exports = EventTarget; 14 | -------------------------------------------------------------------------------- /src/api/GainNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | const AudioParam = require("./AudioParam"); 6 | 7 | class GainNode extends AudioNode { 8 | constructor(context, opts) { 9 | super(context); 10 | 11 | this._impl = new impl.GainNode(context._impl, opts); 12 | this._impl.$gain = new AudioParam(context, this._impl.getGain()); 13 | } 14 | 15 | get gain() { 16 | return this._impl.$gain; 17 | } 18 | } 19 | 20 | module.exports = GainNode; 21 | -------------------------------------------------------------------------------- /src/api/IIRFilterNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | 6 | class IIRFilterNode extends AudioNode { 7 | constructor(context, opts) { 8 | super(context); 9 | 10 | this._impl = new impl.IIRFilterNode(context._impl, opts); 11 | } 12 | 13 | getFrequencyResponse(frequencyHz, magResponse, phaseResponse) { 14 | this._impl.getFrequencyResponse(frequencyHz, magResponse, phaseResponse); 15 | } 16 | } 17 | 18 | module.exports = IIRFilterNode; 19 | -------------------------------------------------------------------------------- /src/api/OscillatorNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioScheduledSourceNode = require("./AudioScheduledSourceNode"); 5 | const AudioParam = require("./AudioParam"); 6 | 7 | class OscillatorNode extends AudioScheduledSourceNode { 8 | constructor(context, opts) { 9 | super(context); 10 | 11 | this._impl = new impl.OscillatorNode(context._impl, opts); 12 | this._impl.$frequency = new AudioParam(context, this._impl.getFrequency()); 13 | this._impl.$detune = new AudioParam(context, this._impl.getDetune()); 14 | this._impl.$onended = null; 15 | 16 | if (opts && opts.periodicWave) { 17 | this.setPeriodicWave(opts.periodicWave); 18 | } 19 | } 20 | 21 | get type() { 22 | return this._impl.getType(); 23 | } 24 | 25 | set type(value) { 26 | this._impl.setType(value); 27 | } 28 | 29 | get frequency() { 30 | return this._impl.$frequency; 31 | } 32 | 33 | get detune() { 34 | return this._impl.$detune; 35 | } 36 | 37 | setPeriodicWave(periodicWave) { 38 | this._impl.setPeriodicWave(periodicWave); 39 | } 40 | } 41 | 42 | module.exports = OscillatorNode; 43 | -------------------------------------------------------------------------------- /src/api/PannerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | 6 | class PannerNode extends AudioNode { 7 | constructor(context, opts) { 8 | super(context); 9 | 10 | this._impl = new impl.PannerNode(context._impl, opts); 11 | } 12 | 13 | get panningModel() { 14 | return this._impl.getPanningModel(); 15 | } 16 | 17 | set panningModel(value) { 18 | this._impl.setPanningModel(value); 19 | } 20 | 21 | get distanceModel() { 22 | return this._impl.getDistanceModel(); 23 | } 24 | 25 | set distanceModel(value) { 26 | this._impl.setDistanceModel(value); 27 | } 28 | 29 | get refDistance() { 30 | return this._impl.getRefDistance(); 31 | } 32 | 33 | set refDistance(value) { 34 | this._impl.setRefDistance(value); 35 | } 36 | 37 | get maxDistance() { 38 | return this._impl.getMaxDistance(); 39 | } 40 | 41 | set maxDistance(value) { 42 | this._impl.setMaxDistance(value); 43 | } 44 | 45 | get rolloffFactor() { 46 | return this._impl.getRolloffFactor(); 47 | } 48 | 49 | set rolloffFactor(value) { 50 | this._impl.setRolloffFactor(value); 51 | } 52 | 53 | get coneInnerAngle() { 54 | return this._impl.getConeInnerAngle(); 55 | } 56 | 57 | set coneInnerAngle(value) { 58 | this._impl.setConeInnerAngle(value); 59 | } 60 | 61 | get coneOuterAngle() { 62 | return this._impl.getConeOuterAngle(); 63 | } 64 | 65 | set coneOuterAngle(value) { 66 | this._impl.setConeOuterAngle(value); 67 | } 68 | 69 | get coneOuterGain() { 70 | return this._impl.getConeOuterGain(); 71 | } 72 | 73 | set coneOuterGain(value) { 74 | this._impl.setConeOuterGain(value); 75 | } 76 | 77 | setPosition(x, y, z) { 78 | this._impl.setPosition(x, y, z); 79 | } 80 | 81 | setOrientation(x, y, z) { 82 | this._impl.setOrientation(x, y, z); 83 | } 84 | 85 | setVelocity(x, y, z) { 86 | this._impl.setVelocity(x, y, z); 87 | } 88 | } 89 | 90 | module.exports = PannerNode; 91 | -------------------------------------------------------------------------------- /src/api/PeriodicWave.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const { defineProp } = require("../utils"); 5 | 6 | class PeriodicWave { 7 | constructor(context, opts) { 8 | defineProp(this, "_impl", new impl.PeriodicWave(context._impl, opts)); 9 | } 10 | } 11 | 12 | module.exports = PeriodicWave; 13 | -------------------------------------------------------------------------------- /src/api/README.md: -------------------------------------------------------------------------------- 1 | These files provide public interfaces of the web audio. The implementations are put on [src/impl](../impl). 2 | -------------------------------------------------------------------------------- /src/api/ScriptProcessorNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | const AudioBuffer = require("./AudioBuffer"); 6 | 7 | class ScriptProcessorNode extends AudioNode { 8 | constructor(context, opts) { 9 | super(context); 10 | 11 | this._impl = new impl.ScriptProcessorNode(context._impl, opts); 12 | this._impl.$onaudioprocess = null; 13 | this._impl.setEventItem({ 14 | type: "audioprocess", 15 | playbackTime: 0, 16 | inputBuffer: new AudioBuffer(), 17 | outputBuffer: new AudioBuffer() 18 | }) 19 | } 20 | 21 | get bufferSize() { 22 | return this._impl.getBufferSize(); 23 | } 24 | 25 | get onaudioprocess() { 26 | return this._impl.$onaudioprocess; 27 | } 28 | 29 | set onaudioprocess(callback) { 30 | this._impl.replaceEventListener("audioprocess", this._impl.$onaudioprocess, callback); 31 | this._impl.$onaudioprocess = callback; 32 | } 33 | } 34 | 35 | module.exports = ScriptProcessorNode; 36 | -------------------------------------------------------------------------------- /src/api/SpatialListener.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioParam = require("./AudioParam"); 4 | const { defineProp } = require("../utils"); 5 | 6 | class SpatialListener { 7 | constructor(context, impl) { 8 | defineProp(this, "_context", context); 9 | defineProp(this, "_impl", impl); 10 | 11 | this._impl.$positionX = new AudioParam(context, this._impl.getPositionX()); 12 | this._impl.$positionY = new AudioParam(context, this._impl.getPositionY()); 13 | this._impl.$positionZ = new AudioParam(context, this._impl.getPositionZ()); 14 | this._impl.$forwardX = new AudioParam(context, this._impl.getForwardX()); 15 | this._impl.$forwardY = new AudioParam(context, this._impl.getForwardY()); 16 | this._impl.$forwardZ = new AudioParam(context, this._impl.getForwardZ()); 17 | this._impl.$upX = new AudioParam(context, this._impl.getUpX()); 18 | this._impl.$upY = new AudioParam(context, this._impl.getUpY()); 19 | this._impl.$upZ = new AudioParam(context, this._impl.getUpZ()); 20 | } 21 | 22 | get positionX() { 23 | return this._impl.$positionX; 24 | } 25 | 26 | get positionY() { 27 | return this._impl.$positionY; 28 | } 29 | 30 | get positionZ() { 31 | return this._impl.$positionZ; 32 | } 33 | 34 | get forwardX() { 35 | return this._impl.$forwardX; 36 | } 37 | 38 | get forwardY() { 39 | return this._impl.$forwardY; 40 | } 41 | 42 | get forwardZ() { 43 | return this._impl.$forwardZ; 44 | } 45 | 46 | get upX() { 47 | return this._impl.$upX; 48 | } 49 | 50 | get upY() { 51 | return this._impl.$upY; 52 | } 53 | 54 | get upZ() { 55 | return this._impl.$upZ; 56 | } 57 | } 58 | 59 | module.exports = SpatialListener; 60 | -------------------------------------------------------------------------------- /src/api/StereoPannerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | const AudioParam = require("./AudioParam"); 6 | 7 | class StereoPannerNode extends AudioNode { 8 | constructor(context, opts) { 9 | super(context); 10 | 11 | this._impl = new impl.StereoPannerNode(context._impl, opts); 12 | this._impl.$pan = new AudioParam(context, this._impl.getPan()); 13 | } 14 | 15 | get pan() { 16 | return this._impl.$pan; 17 | } 18 | } 19 | 20 | module.exports = StereoPannerNode; 21 | -------------------------------------------------------------------------------- /src/api/WaveShaperNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const impl = require("../impl"); 4 | const AudioNode = require("./AudioNode"); 5 | 6 | class WaveShaperNode extends AudioNode { 7 | constructor(context, opts) { 8 | super(context); 9 | 10 | this._impl = new impl.WaveShaperNode(context._impl, opts); 11 | } 12 | 13 | get curve() { 14 | return this._impl.getCurve(); 15 | } 16 | 17 | set curve(value) { 18 | this._impl.setCurve(value); 19 | } 20 | 21 | get oversample() { 22 | return this._impl.getOversample(); 23 | } 24 | 25 | set oversample(value) { 26 | this._impl.setOversample(value); 27 | } 28 | } 29 | 30 | module.exports = WaveShaperNode; 31 | -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | AnalyserNode: require("./AnalyserNode"), 5 | AudioBuffer: require("./AudioBuffer"), 6 | AudioBufferSourceNode: require("./AudioBufferSourceNode"), 7 | AudioDestinationNode: require("./AudioDestinationNode"), 8 | AudioListener: require("./AudioListener"), 9 | AudioNode: require("./AudioNode"), 10 | AudioParam: require("./AudioParam"), 11 | AudioScheduledSourceNode: require("./AudioScheduledSourceNode"), 12 | BaseAudioContext: require("./BaseAudioContext"), 13 | BiquadFilterNode: require("./BiquadFilterNode"), 14 | ChannelMergerNode: require("./ChannelMergerNode"), 15 | ChannelSplitterNode: require("./ChannelSplitterNode"), 16 | ConstantSourceNode: require("./ConstantSourceNode"), 17 | ConvolverNode: require("./ConvolverNode"), 18 | DelayNode: require("./DelayNode"), 19 | DynamicsCompressorNode: require("./DynamicsCompressorNode"), 20 | EventTarget: require("./EventTarget"), 21 | GainNode: require("./GainNode"), 22 | IIRFilterNode: require("./IIRFilterNode"), 23 | OscillatorNode: require("./OscillatorNode"), 24 | PannerNode: require("./PannerNode"), 25 | PeriodicWave: require("./PeriodicWave"), 26 | ScriptProcessorNode: require("./ScriptProcessorNode"), 27 | SpatialListener: require("./SpatialListener"), 28 | SpatialPannerNode: require("./SpatialPannerNode"), 29 | StereoPannerNode: require("./StereoPannerNode"), 30 | WaveShaperNode: require("./WaveShaperNode") 31 | }; 32 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | sampleRate: 44100, 5 | numberOfChannels: 2, 6 | blockSize: 128, 7 | bitDepth: 16 8 | }; 9 | -------------------------------------------------------------------------------- /src/constants/AudioContextState.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | RUNNING: "running", 5 | SUSPENDED: "suspended", 6 | CLOSED: "closed", 7 | }; 8 | -------------------------------------------------------------------------------- /src/constants/AudioParamEvent.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | SET_VALUE_AT_TIME: "setValueAtTime", 5 | LINEAR_RAMP_TO_VALUE_AT_TIME: "linearRampToValueAtTime", 6 | EXPONENTIAL_RAMP_TO_VALUE_AT_TIME: "exponentialRampToValueAtTime", 7 | SET_TARGET_AT_TIME: "setTargetAtTime", 8 | SET_VALUE_CURVE_AT_TIME: "setValueCurveAtTime", 9 | }; 10 | -------------------------------------------------------------------------------- /src/constants/AudioParamRate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | CONTROL_RATE: "control", 5 | AUDIO_RATE: "audio", 6 | }; 7 | -------------------------------------------------------------------------------- /src/constants/BiquadFilterType.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | LOWPASS: "lowpass", 5 | HIGHPASS: "highpass", 6 | BANDPASS: "bandpass", 7 | LOWSHELF: "lowshelf", 8 | HIGHSHELF: "highshelf", 9 | PEAKING: "peaking", 10 | NOTCH: "notch", 11 | ALLPASS: "allpass", 12 | }; 13 | -------------------------------------------------------------------------------- /src/constants/ChannelCountMode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | MAX: "max", 5 | CLAMPED_MAX: "clamped-max", 6 | EXPLICIT: "explicit", 7 | }; 8 | -------------------------------------------------------------------------------- /src/constants/ChannelInterpretation.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | SPEAKERS: "speakers", 5 | DISCRETE: "discrete", 6 | }; 7 | -------------------------------------------------------------------------------- /src/constants/OscillatorType.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | SINE: "sine", 5 | SAWTOOTH: "sawtooth", 6 | TRIANGLE: "triangle", 7 | SQUARE: "square", 8 | CUSTOM: "custom", 9 | }; 10 | -------------------------------------------------------------------------------- /src/constants/PlaybackState.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | UNSCHEDULED: "unscheduled", 5 | SCHEDULED: "scheduled", 6 | PLAYING: "playing", 7 | FINISHED: "finished", 8 | }; 9 | -------------------------------------------------------------------------------- /src/constants/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | MIN_SAMPLERATE: 3000, 5 | MAX_SAMPLERATE: 192000, 6 | MIN_NUMBER_OF_CHANNELS: 1, 7 | MAX_NUMBER_OF_CHANNELS: 32, 8 | MIN_BLOCK_SIZE: 8, 9 | MAX_BLOCK_SIZE: 1024, 10 | }; 11 | -------------------------------------------------------------------------------- /src/decoder.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const audioType = require("audio-type"); 4 | const WavDecoder = require("wav-decoder"); 5 | const DecoderUtils = require("./utils/DecoderUtils"); 6 | const AudioDataUtils = require("./utils/AudioDataUtils"); 7 | const AudioBuffer = require("./api/AudioBuffer"); 8 | 9 | const decoders = {}; 10 | 11 | /** 12 | * @param {string} type 13 | * @return {function} 14 | */ 15 | function get(type) { 16 | return decoders[type] || null; 17 | } 18 | 19 | /** 20 | * @param {string} type 21 | * @param {function} fn 22 | */ 23 | function set(type, fn) { 24 | /* istanbul ignore else */ 25 | if (typeof fn === "function") { 26 | decoders[type] = fn; 27 | } 28 | } 29 | 30 | /** 31 | * @param {ArrayBuffer} AudioBuffer 32 | * @param {object} opts 33 | * @return {Promise} 34 | */ 35 | function decode(audioData, opts) { 36 | const type = toAudioType(audioData); 37 | const decodeFn = decoders[type]; 38 | 39 | if (typeof decodeFn !== "function") { 40 | return Promise.reject( 41 | new TypeError(`Decoder does not support the audio format: ${ type || "unknown" }`) 42 | ); 43 | } 44 | 45 | return DecoderUtils.decode(decodeFn, audioData, opts).then((audioData) => { 46 | return AudioDataUtils.toAudioBuffer(audioData, AudioBuffer); 47 | }); 48 | } 49 | 50 | function toAudioType(audioData) { 51 | if (audioData instanceof ArrayBuffer) { 52 | audioData = new Uint8Array(audioData, 0, 16); 53 | } 54 | return audioType(audioData) || ""; 55 | } 56 | 57 | set("wav", WavDecoder.decode); 58 | 59 | module.exports = { get, set, decode }; 60 | -------------------------------------------------------------------------------- /src/encoder.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const WavEncoder = require("wav-encoder"); 4 | const EncoderUtils = require("./utils/EncoderUtils"); 5 | 6 | const encoders = {}; 7 | 8 | /** 9 | * @param {string} type 10 | * @return {function} 11 | */ 12 | function get(type) { 13 | return encoders[type] || null; 14 | } 15 | 16 | /** 17 | * @param {string} type 18 | * @param {function} fn 19 | */ 20 | function set(type, fn) { 21 | /* istanbul ignore else */ 22 | if (typeof fn === "function") { 23 | encoders[type] = fn; 24 | } 25 | } 26 | 27 | /** 28 | * @param {AudioData} audioData 29 | * @param {object} opts 30 | * @return {Promise} 31 | */ 32 | function encode(audioData, /* istanbul ignore next */ opts = {}) { 33 | const type = opts.type || "wav"; 34 | const encodeFn = encoders[type]; 35 | 36 | if (typeof encodeFn !== "function") { 37 | return Promise.reject( 38 | new TypeError(`Encoder does not support the audio format: ${ type }`) 39 | ); 40 | } 41 | 42 | return EncoderUtils.encode(encodeFn, audioData, opts); 43 | } 44 | 45 | set("wav", WavEncoder.encode); 46 | 47 | module.exports = { get, set, encode }; 48 | -------------------------------------------------------------------------------- /src/impl/AudioDestinationNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioNode = require("./AudioNode"); 4 | const { toValidNumberOfChannels } = require("../utils"); 5 | const { EXPLICIT } = require("../constants/ChannelCountMode"); 6 | 7 | /** 8 | * @prop {AudioNodeOutput} output 9 | * @prop {AudioBus} outputBus 10 | */ 11 | class AudioDestinationNode extends AudioNode { 12 | /** 13 | * @param {AudioContext} context 14 | * @param {object} opts 15 | * @param {number} opts.numberOfChannels 16 | */ 17 | constructor(context, opts = {}) { 18 | let numberOfChannels = opts.numberOfChannels; 19 | 20 | numberOfChannels = toValidNumberOfChannels(numberOfChannels); 21 | 22 | super(context, opts, { 23 | inputs: [ numberOfChannels ], 24 | outputs: [], 25 | channelCount: numberOfChannels, 26 | channelCountMode: EXPLICIT, 27 | allowedMaxChannelCount: numberOfChannels 28 | }); 29 | 30 | this._numberOfChannels = numberOfChannels|0; 31 | this._destinationChannelData = this.inputs[0].bus.getChannelData(); 32 | } 33 | 34 | /** 35 | * @return {number} 36 | */ 37 | getMaxChannelCount() { 38 | return this._numberOfChannels; 39 | } 40 | 41 | /** 42 | * @param {Float32Array[]} channelData 43 | * @param {number} offset 44 | */ 45 | process(channelData, offset) { 46 | const inputs = this.inputs; 47 | const destinationChannelData = this._destinationChannelData; 48 | const numberOfChannels = channelData.length; 49 | 50 | for (let i = 0, imax = inputs.length; i < imax; i++) { 51 | inputs[i].pull(); 52 | } 53 | 54 | for (let ch = 0; ch < numberOfChannels; ch++) { 55 | channelData[ch].set(destinationChannelData[ch], offset); 56 | } 57 | } 58 | } 59 | 60 | module.exports = AudioDestinationNode; 61 | -------------------------------------------------------------------------------- /src/impl/AudioListener.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class AudioListener { 4 | /** 5 | * @param {AudioContext} context 6 | */ 7 | constructor(context) { 8 | this.context = context; 9 | } 10 | 11 | /** 12 | * @param {number} x 13 | * @param {number} y 14 | * @param {number} z 15 | */ 16 | /* istanbul ignore next */ 17 | setPosition() { 18 | throw new TypeError("NOT YET IMPLEMENTED"); 19 | } 20 | 21 | /** 22 | * @param {number} x 23 | * @param {number} y 24 | * @param {number} z 25 | * @param {number} xUp 26 | * @param {number} yUp 27 | * @param {number} zUp 28 | */ 29 | /* istanbul ignore next */ 30 | setOrientation() { 31 | throw new TypeError("NOT YET IMPLEMENTED"); 32 | } 33 | } 34 | 35 | module.exports = AudioListener; 36 | -------------------------------------------------------------------------------- /src/impl/AudioScheduledSourceNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioSourceNode = require("./AudioSourceNode"); 4 | const { toNumber } = require("../utils"); 5 | const { UNSCHEDULED, SCHEDULED, PLAYING, FINISHED } = require("../constants/PlaybackState"); 6 | 7 | class AudioScheduledSourceNode extends AudioSourceNode { 8 | /** 9 | * @param {AudioContext} context 10 | */ 11 | constructor(context, opts) { 12 | super(context, opts); 13 | 14 | this._startTime = Infinity; 15 | this._stopTime = Infinity; 16 | this._startFrame = Infinity; 17 | this._stopFrame = Infinity; 18 | } 19 | 20 | /** 21 | * @return {number} 22 | */ 23 | getStartTime() { 24 | if (this._startTime !== Infinity) { 25 | return this._startTime; 26 | } 27 | } 28 | 29 | /** 30 | * @return {number} 31 | */ 32 | getStopTime() { 33 | if (this._stopTime !== Infinity) { 34 | return this._stopTime; 35 | } 36 | } 37 | 38 | /** 39 | * @return {string} 40 | */ 41 | getPlaybackState() { 42 | if (this._startTime === Infinity) { 43 | return UNSCHEDULED; 44 | } 45 | if (this.context.currentTime < this._startTime) { 46 | return SCHEDULED; 47 | } 48 | if (this._stopTime <= this.context.currentTime) { 49 | return FINISHED; 50 | } 51 | return PLAYING; 52 | } 53 | 54 | /** 55 | * @param {number} when 56 | */ 57 | start(when) { 58 | /* istanbul ignore next */ 59 | if (this._startTime !== Infinity) { 60 | return; 61 | } 62 | 63 | when = Math.max(this.context.currentTime, toNumber(when)); 64 | 65 | this._startTime = when; 66 | this._startFrame = Math.round(when * this.sampleRate); 67 | 68 | this.context.sched(when, () => { 69 | this.outputs[0].enable(); 70 | }); 71 | } 72 | 73 | /** 74 | * @param {number} when 75 | */ 76 | stop(when) { 77 | /* istanbul ignore next */ 78 | if (this._stopTime !== Infinity) { 79 | return; 80 | } 81 | 82 | when = Math.max(this.context.currentTime, this._startTime, toNumber(when)); 83 | 84 | this._stopTime = when; 85 | this._stopFrame = Math.round(when * this.sampleRate); 86 | } 87 | } 88 | 89 | module.exports = AudioScheduledSourceNode; 90 | -------------------------------------------------------------------------------- /src/impl/AudioSourceNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const assert = require("assert"); 4 | const AudioNode = require("./AudioNode"); 5 | 6 | /* istanbul ignore next */ 7 | class AudioSourceNode extends AudioNode { 8 | /** 9 | * @param {AudioContext} context 10 | */ 11 | constructor(context, opts) { 12 | super(context, opts, { 13 | inputs: [], 14 | outputs: [ 1 ] 15 | }); 16 | } 17 | 18 | enableOutputsIfNecessary() { 19 | assert(!"SHOULD NOT BE CALLED"); 20 | } 21 | 22 | disableOutputsIfNecessary() { 23 | assert(!"SHOULD NOT BE CALLED"); 24 | } 25 | } 26 | 27 | module.exports = AudioSourceNode; 28 | -------------------------------------------------------------------------------- /src/impl/ChannelMergerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioNode = require("./AudioNode"); 4 | const ChannelMergerNodeDSP = require("./dsp/ChannelMergerNode"); 5 | const { defaults, fill } = require("../utils"); 6 | const { toValidNumberOfChannels } = require("../utils"); 7 | const { EXPLICIT } = require("../constants/ChannelCountMode"); 8 | 9 | const DEFAULT_NUMBER_OF_INPUTS = 6; 10 | 11 | class ChannelMergerNode extends AudioNode { 12 | /** 13 | * @param {AudioContext} context 14 | * @param {object} opts 15 | * @param {number} opts.numberOfInputs 16 | */ 17 | constructor(context, opts = {}) { 18 | let numberOfInputs = defaults(opts.numberOfInputs, DEFAULT_NUMBER_OF_INPUTS); 19 | 20 | numberOfInputs = toValidNumberOfChannels(numberOfInputs); 21 | 22 | super(context, opts, { 23 | inputs: fill(new Array(numberOfInputs), 1), 24 | outputs: [ numberOfInputs ], 25 | channelCount: 1, 26 | channelCountMode: EXPLICIT, 27 | allowedMaxChannelCount: 1, 28 | allowedChannelCountMode: [ EXPLICIT ] 29 | }); 30 | } 31 | 32 | disableOutputsIfNecessary() { 33 | // disable if all inputs are disabled 34 | 35 | /* istanbul ignore else */ 36 | if (this.isEnabled()) { 37 | const inputs = this.inputs; 38 | 39 | for (let i = 0, imax = inputs.length; i < imax; i++) { 40 | if (inputs[i].isEnabled()) { 41 | return; 42 | } 43 | } 44 | 45 | super.disableOutputsIfNecessary(); 46 | } 47 | } 48 | } 49 | 50 | Object.assign(ChannelMergerNode.prototype, ChannelMergerNodeDSP); 51 | 52 | module.exports = ChannelMergerNode; 53 | -------------------------------------------------------------------------------- /src/impl/ChannelSplitterNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioNode = require("./AudioNode"); 4 | const ChannelSplitterNodeDSP = require("./dsp/ChannelSplitterNode"); 5 | const { defaults, fill } = require("../utils"); 6 | const { toValidNumberOfChannels } = require("../utils"); 7 | const { MAX } = require("../constants/ChannelCountMode"); 8 | 9 | const DEFAULT_NUMBER_OF_OUTPUTS = 6; 10 | 11 | class ChannelSplitterNode extends AudioNode { 12 | /** 13 | * @param {AudioContext} context 14 | * @param {object} opts 15 | * @param {number} opts.numberOfOutputs 16 | */ 17 | constructor(context, opts = {}) { 18 | let numberOfOutputs = defaults(opts.numberOfOutputs, DEFAULT_NUMBER_OF_OUTPUTS); 19 | 20 | numberOfOutputs = toValidNumberOfChannels(numberOfOutputs); 21 | 22 | super(context, opts, { 23 | inputs: [ 1 ], 24 | outputs: fill(new Array(numberOfOutputs), 1), 25 | channelCount: 2, 26 | channelCountMode: MAX 27 | }); 28 | } 29 | } 30 | 31 | Object.assign(ChannelSplitterNode.prototype, ChannelSplitterNodeDSP); 32 | 33 | module.exports = ChannelSplitterNode; 34 | -------------------------------------------------------------------------------- /src/impl/ConstantSourceNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioScheduledSourceNode = require("./AudioScheduledSourceNode"); 4 | const ConstantSourceNodeDSP = require("./dsp/ConstantSourceNode"); 5 | const { defaults } = require("../utils"); 6 | const { MAX } = require("../constants/ChannelCountMode"); 7 | const { AUDIO_RATE } = require("../constants/AudioParamRate"); 8 | 9 | const DEFAULT_OFFSET = 1; 10 | 11 | class ConstantSourceNode extends AudioScheduledSourceNode { 12 | /** 13 | * @param {AudioContext} context 14 | * @param {object} opts 15 | * @param {number} opts.offset 16 | */ 17 | constructor(context, opts = {}) { 18 | let offset = defaults(opts.offset, DEFAULT_OFFSET); 19 | 20 | super(context, opts, { 21 | inputs: [ 1 ], 22 | outputs: [ 1 ], 23 | channelCount: 2, 24 | channelCountMode: MAX 25 | }); 26 | 27 | this._offset = this.addParam(AUDIO_RATE, offset); 28 | } 29 | 30 | /** 31 | * @return {AudioParam} 32 | */ 33 | getOffset() { 34 | return this._offset; 35 | } 36 | } 37 | 38 | Object.assign(ConstantSourceNode.prototype, ConstantSourceNodeDSP); 39 | 40 | module.exports = ConstantSourceNode; 41 | -------------------------------------------------------------------------------- /src/impl/ConvolverNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioNode = require("./AudioNode"); 4 | const AudioBuffer = require("./AudioBuffer"); 5 | const ConvolverNodeDSP = require("./dsp/ConvolverNode"); 6 | const { defaults } = require("../utils"); 7 | const { toImpl } = require("../utils"); 8 | const { CLAMPED_MAX, EXPLICIT } = require("../constants/ChannelCountMode"); 9 | 10 | const DEFAULT_DISABLE_NORMALIZATION = false; 11 | 12 | class ConvolverNode extends AudioNode { 13 | /** 14 | * @param {AudioContext} context 15 | * @param {object} opts 16 | * @param {boolean} opts.disableNormalization 17 | */ 18 | constructor(context, opts = {}) { 19 | let disableNormalization = defaults(opts.disableNormalization, DEFAULT_DISABLE_NORMALIZATION); 20 | 21 | super(context, opts, { 22 | inputs: [ 1 ], 23 | outputs: [ 1 ], 24 | channelCount: 2, 25 | channelCountMode: CLAMPED_MAX, 26 | allowedMaxChannelCount: 2, 27 | allowedChannelCountMode: [ CLAMPED_MAX, EXPLICIT ] 28 | }); 29 | 30 | this._buffer = null; 31 | this._audioData = null; 32 | this._normalize = !disableNormalization; 33 | } 34 | 35 | /** 36 | * @return {AudioBuffer} 37 | */ 38 | getBuffer() { 39 | return this._buffer; 40 | } 41 | 42 | /** 43 | * @param {AudioBuffer} value 44 | */ 45 | setBuffer(value) { 46 | value = toImpl(value); 47 | 48 | /* istanbul ignore else */ 49 | if (value instanceof AudioBuffer) { 50 | this._buffer = value; 51 | this._audioData = this._buffer.audioData; 52 | } 53 | } 54 | 55 | /** 56 | * @return {boolean} 57 | */ 58 | getNormalize() { 59 | return this._normalize; 60 | } 61 | 62 | /** 63 | * @param {boolean} value 64 | */ 65 | setNormalize(value) { 66 | this._normalize = !!value; 67 | } 68 | 69 | /** 70 | * @param {number} numberOfChannels 71 | */ 72 | channelDidUpdate(numberOfChannels) { 73 | numberOfChannels = Math.min(numberOfChannels, 2); 74 | 75 | this.outputs[0].setNumberOfChannels(numberOfChannels); 76 | } 77 | } 78 | 79 | Object.assign(ConvolverNode.prototype, ConvolverNodeDSP); 80 | 81 | module.exports = ConvolverNode; 82 | -------------------------------------------------------------------------------- /src/impl/DelayNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioNode = require("./AudioNode"); 4 | const DelayNodeDSP = require("./dsp/DelayNode"); 5 | const { defaults } = require("../utils"); 6 | const { toNumber } = require("../utils"); 7 | const { MAX } = require("../constants/ChannelCountMode"); 8 | const { AUDIO_RATE } = require("../constants/AudioParamRate"); 9 | 10 | const DEFAULT_MAX_DELAY_TIME = 1; 11 | const DEFAULT_DELAY_TIME = 0; 12 | 13 | class DelayNode extends AudioNode { 14 | /** 15 | * @param {AudioContext} context 16 | * @param {object} opts 17 | * @param {number} opts.maxDelayTime 18 | * @param {number} opts.delayTime 19 | */ 20 | constructor(context, opts = {}) { 21 | let maxDelayTime = defaults(opts.maxDelayTime, DEFAULT_MAX_DELAY_TIME); 22 | let delayTime = defaults(opts.delayTime, DEFAULT_DELAY_TIME); 23 | 24 | maxDelayTime = Math.max(0, toNumber(maxDelayTime)); 25 | delayTime = Math.min(delayTime, maxDelayTime); 26 | 27 | super(context, opts, { 28 | inputs: [ 1 ], 29 | outputs: [ 1 ], 30 | channelCount: 2, 31 | channelCountMode: MAX 32 | }); 33 | 34 | this._maxDelayTime = maxDelayTime; 35 | this._delayTime = this.addParam(AUDIO_RATE, delayTime); 36 | 37 | this.dspInit(this._maxDelayTime); 38 | this.dspUpdateKernel(1); 39 | } 40 | 41 | /** 42 | * @return {number} 43 | */ 44 | getDelayTime() { 45 | return this._delayTime; 46 | } 47 | 48 | /** 49 | * @return {number} 50 | */ 51 | getMaxDelayTime() { 52 | return this._maxDelayTime; 53 | } 54 | 55 | /** 56 | * @param {number} numberOfChannels 57 | */ 58 | channelDidUpdate(numberOfChannels) { 59 | this.dspUpdateKernel(numberOfChannels); 60 | this.outputs[0].setNumberOfChannels(numberOfChannels); 61 | } 62 | 63 | /** 64 | * @return {number} 65 | */ 66 | getTailTime() { 67 | return this._maxDelayTime; 68 | } 69 | } 70 | 71 | Object.assign(DelayNode.prototype, DelayNodeDSP); 72 | 73 | module.exports = DelayNode; 74 | -------------------------------------------------------------------------------- /src/impl/EventTarget.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const events = require("events"); 4 | 5 | class EventTarget { 6 | constructor() { 7 | this._emitter = new events.EventEmitter(); 8 | } 9 | 10 | /** 11 | * @param {string} type 12 | * @param {function} listener 13 | */ 14 | addEventListener(type, listener) { 15 | /* istanbul ignore else */ 16 | if (typeof listener === "function") { 17 | this._emitter.addListener(type, listener); 18 | } 19 | } 20 | 21 | /** 22 | * @param {string} type 23 | * @param {function} listener 24 | */ 25 | removeEventListener(type, listener) { 26 | /* istanbul ignore else */ 27 | if (typeof listener === "function") { 28 | this._emitter.removeListener(type, listener); 29 | } 30 | } 31 | 32 | /** 33 | * @param {string} type 34 | * @param {function} oldListener 35 | * @param {function} newListener 36 | */ 37 | replaceEventListener(type, oldListener, newListener) { 38 | this.removeEventListener(type, oldListener); 39 | this.addEventListener(type, newListener); 40 | } 41 | 42 | /** 43 | * @param {object} event 44 | * @param {string} event.type 45 | */ 46 | dispatchEvent(event) { 47 | this._emitter.emit(event.type, event); 48 | } 49 | } 50 | 51 | module.exports = EventTarget; 52 | -------------------------------------------------------------------------------- /src/impl/GainNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioNode = require("./AudioNode"); 4 | const GainNodeDSP = require("./dsp/GainNode"); 5 | const { defaults } = require("../utils"); 6 | const { MAX } = require("../constants/ChannelCountMode"); 7 | const { AUDIO_RATE } = require("../constants/AudioParamRate"); 8 | 9 | const DEFAULT_GAIN = 1; 10 | 11 | class GainNode extends AudioNode { 12 | /** 13 | * @param {AudioContext} context 14 | * @param {object} opts 15 | * @param {number} opts.gain 16 | */ 17 | constructor(context, opts = {}) { 18 | let gain = defaults(opts.gain, DEFAULT_GAIN); 19 | 20 | super(context, opts, { 21 | inputs: [ 1 ], 22 | outputs: [ 1 ], 23 | channelCount: 2, 24 | channelCountMode: MAX 25 | }); 26 | 27 | this._gain = this.addParam(AUDIO_RATE, gain); 28 | } 29 | 30 | /** 31 | * @return {AudioParam} 32 | */ 33 | getGain() { 34 | return this._gain; 35 | } 36 | 37 | /** 38 | * @param {number} numberOfChannels 39 | */ 40 | channelDidUpdate(numberOfChannels) { 41 | this.outputs[0].setNumberOfChannels(numberOfChannels); 42 | } 43 | } 44 | 45 | Object.assign(GainNode.prototype, GainNodeDSP); 46 | 47 | module.exports = GainNode; 48 | -------------------------------------------------------------------------------- /src/impl/IIRFilterNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioNode = require("./AudioNode"); 4 | const IIRFilterNodeDSP = require("./dsp/IIRFilterNode"); 5 | const { defaults } = require("../utils"); 6 | const { MAX } = require("../constants/ChannelCountMode"); 7 | 8 | class IIRFilterNode extends AudioNode { 9 | /** 10 | * @param {AudioContext} context 11 | * @param {object} opts 12 | * @param {Float32Array} opts.feedforward 13 | * @param {Float32Array} opts.feedback 14 | */ 15 | constructor(context, opts = {}) { 16 | let feedforward = defaults(opts.feedforward, [ 0 ]); 17 | let feedback = defaults(opts.feedback, [ 1 ]); 18 | 19 | super(context, opts, { 20 | inputs: [ 1 ], 21 | outputs: [ 1 ], 22 | channelCount: 2, 23 | channelCountMode: MAX 24 | }); 25 | 26 | this._feedforward = feedforward; 27 | this._feedback = feedback; 28 | 29 | this.dspInit(); 30 | this.dspUpdateKernel(1); 31 | } 32 | 33 | /** 34 | * @param {Float32Array} frequencyHz 35 | * @param {Float32Array} magResponse 36 | * @param {Float32Array} phaseResponse 37 | */ 38 | getFrequencyResponse(frequencyHz, magResponse, phaseResponse) { 39 | this.dspGetFrequencyResponse(frequencyHz, magResponse, phaseResponse); 40 | } 41 | 42 | /** 43 | * @return {Float32Array} 44 | */ 45 | getFeedforward() { 46 | return this._feedforward; 47 | } 48 | 49 | /** 50 | * @return {Float32Array} 51 | */ 52 | getFeedback() { 53 | return this._feedback; 54 | } 55 | 56 | /** 57 | * @param {number} numberOfChannels 58 | */ 59 | channelDidUpdate(numberOfChannels) { 60 | this.dspUpdateKernel(numberOfChannels); 61 | this.outputs[0].setNumberOfChannels(numberOfChannels); 62 | } 63 | } 64 | 65 | Object.assign(IIRFilterNode.prototype, IIRFilterNodeDSP); 66 | 67 | module.exports = IIRFilterNode; 68 | -------------------------------------------------------------------------------- /src/impl/PannerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const BasePannerNode = require("./BasePannerNode"); 4 | const PannerNodeDSP = require("./dsp/PannerNode"); 5 | 6 | class PannerNode extends BasePannerNode { 7 | /** 8 | * @param {AudioContext} context 9 | */ 10 | constructor(context, opts) { 11 | super(context, opts); 12 | } 13 | 14 | /** 15 | * @param {number} x 16 | * @param {number} y 17 | * @param {number} z 18 | */ 19 | /* istanbul ignore next */ 20 | setPosition() { 21 | throw new TypeError("NOT YET IMPLEMENTED"); 22 | } 23 | 24 | /** 25 | * @param {number} x 26 | * @param {number} y 27 | * @param {number} z 28 | */ 29 | /* istanbul ignore next */ 30 | setOrientation() { 31 | throw new TypeError("NOT YET IMPLEMENTED"); 32 | } 33 | 34 | /** 35 | * @param {number} x 36 | * @param {number} y 37 | * @param {number} z 38 | */ 39 | /* istanbul ignore next */ 40 | setVelocity() { 41 | throw new TypeError("NOT YET IMPLEMENTED"); 42 | } 43 | } 44 | 45 | Object.assign(PannerNode.prototype, PannerNodeDSP); 46 | 47 | module.exports = PannerNode; 48 | -------------------------------------------------------------------------------- /src/impl/SpatialListener.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioParam = require("./AudioParam"); 4 | const { AUDIO_RATE } = require("../constants/AudioParamRate"); 5 | 6 | class SpatialListener { 7 | /** 8 | * @param {AudioContext} context 9 | */ 10 | constructor(context) { 11 | this.context = context; 12 | this._positionX = new AudioParam(context, { rate: AUDIO_RATE, defaultValue: 0 }); 13 | this._positionY = new AudioParam(context, { rate: AUDIO_RATE, defaultValue: 0 }); 14 | this._positionZ = new AudioParam(context, { rate: AUDIO_RATE, defaultValue: 0 }); 15 | this._forwardX = new AudioParam(context, { rate: AUDIO_RATE, defaultValue: 0 }); 16 | this._forwardY = new AudioParam(context, { rate: AUDIO_RATE, defaultValue: 0 }); 17 | this._forwardZ = new AudioParam(context, { rate: AUDIO_RATE, defaultValue: 0 }); 18 | this._upX = new AudioParam(context, { rate: AUDIO_RATE, defaultValue: 0 }); 19 | this._upY = new AudioParam(context, { rate: AUDIO_RATE, defaultValue: 0 }); 20 | this._upZ = new AudioParam(context, { rate: AUDIO_RATE, defaultValue: 0 }); 21 | } 22 | 23 | /** 24 | * @return {AudioParam} 25 | */ 26 | getPositionX() { 27 | return this._positionX; 28 | } 29 | 30 | /** 31 | * @return {AudioParam} 32 | */ 33 | getPositionY() { 34 | return this._positionY; 35 | } 36 | 37 | /** 38 | * @return {AudioParam} 39 | */ 40 | getPositionZ() { 41 | return this._positionZ; 42 | } 43 | 44 | /** 45 | * @return {AudioParam} 46 | */ 47 | getForwardX() { 48 | return this._forwardX; 49 | } 50 | 51 | /** 52 | * @return {AudioParam} 53 | */ 54 | getForwardY() { 55 | return this._forwardY; 56 | } 57 | 58 | /** 59 | * @return {AudioParam} 60 | */ 61 | getForwardZ() { 62 | return this._forwardZ; 63 | } 64 | 65 | /** 66 | * @return {AudioParam} 67 | */ 68 | getUpX() { 69 | return this._upX; 70 | } 71 | 72 | /** 73 | * @return {AudioParam} 74 | */ 75 | getUpY() { 76 | return this._upY; 77 | } 78 | 79 | /** 80 | * @return {AudioParam} 81 | */ 82 | getUpZ() { 83 | return this._upZ; 84 | } 85 | } 86 | 87 | module.exports = SpatialListener; 88 | -------------------------------------------------------------------------------- /src/impl/SpatialPannerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const BasePannerNode = require("./BasePannerNode"); 4 | const SpatialPannerNodeDSP = require("./dsp/SpatialPannerNode"); 5 | const { AUDIO_RATE } = require("../constants/AudioParamRate"); 6 | 7 | class SpatialPannerNode extends BasePannerNode { 8 | /** 9 | * @param {AudioContext} 10 | */ 11 | constructor(context, opts) { 12 | super(context, opts); 13 | 14 | this._positionX = this.addParam(AUDIO_RATE, 0); 15 | this._positionY = this.addParam(AUDIO_RATE, 0); 16 | this._positionZ = this.addParam(AUDIO_RATE, 0); 17 | this._orientationX = this.addParam(AUDIO_RATE, 0); 18 | this._orientationY = this.addParam(AUDIO_RATE, 0); 19 | this._orientationZ = this.addParam(AUDIO_RATE, 0); 20 | } 21 | 22 | /** 23 | * @param {AudioParam} 24 | */ 25 | getPositionX() { 26 | return this._positionX; 27 | } 28 | 29 | /** 30 | * @param {AudioParam} 31 | */ 32 | getPositionY() { 33 | return this._positionY; 34 | } 35 | 36 | /** 37 | * @param {AudioParam} 38 | */ 39 | getPositionZ() { 40 | return this._positionZ; 41 | } 42 | 43 | /** 44 | * @param {AudioParam} 45 | */ 46 | getOrientationX() { 47 | return this._positionX; 48 | } 49 | 50 | /** 51 | * @param {AudioParam} 52 | */ 53 | getOrientationY() { 54 | return this._positionY; 55 | } 56 | 57 | /** 58 | * @param {AudioParam} 59 | */ 60 | getOrientationZ() { 61 | return this._positionZ; 62 | } 63 | } 64 | 65 | Object.assign(SpatialPannerNode.prototype, SpatialPannerNodeDSP); 66 | 67 | module.exports = SpatialPannerNode; 68 | -------------------------------------------------------------------------------- /src/impl/StereoPannerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const BasePannerNode = require("./BasePannerNode"); 4 | const StereoPannerNodeDSP = require("./dsp/StereoPannerNode"); 5 | const { defaults } = require("../utils"); 6 | const { AUDIO_RATE } = require("../constants/AudioParamRate"); 7 | 8 | const DEFAULT_PAN = 0; 9 | 10 | class StereoPannerNode extends BasePannerNode { 11 | /** 12 | * @param {AudioContext} context 13 | * @param {object} opts 14 | * @param {number} opts.pan 15 | */ 16 | constructor(context, opts = {}) { 17 | let pan = defaults(opts.pan, DEFAULT_PAN); 18 | 19 | super(context, opts); 20 | 21 | this._pan = this.addParam(AUDIO_RATE, pan); 22 | } 23 | 24 | /** 25 | * @param {AudioParam} 26 | */ 27 | getPan() { 28 | return this._pan; 29 | } 30 | } 31 | 32 | Object.assign(StereoPannerNode.prototype, StereoPannerNodeDSP); 33 | 34 | module.exports = StereoPannerNode; 35 | -------------------------------------------------------------------------------- /src/impl/WaveShaperNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioNode = require("./AudioNode"); 4 | const WaveShaperNodeDSP = require("./dsp/WaveShaperNode"); 5 | const { defaults } = require("../utils"); 6 | const { MAX } = require("../constants/ChannelCountMode"); 7 | 8 | const OverSampleTypes = [ "none", "2x", "4x" ]; 9 | 10 | const DEFAULT_CURVE = null; 11 | const DEFAULT_OVERSAMPLE = "none"; 12 | 13 | class WaveShaperNode extends AudioNode { 14 | /** 15 | * @param {AudioContext} context 16 | * @param {object} opts 17 | * @param {Float32Arrat} opts.curve 18 | * @param {string} opts.overSample 19 | */ 20 | constructor(context, opts = {}) { 21 | let curve = defaults(opts.curve, DEFAULT_CURVE); 22 | let overSample = defaults(opts.overSample, DEFAULT_OVERSAMPLE); 23 | 24 | super(context, opts, { 25 | inputs: [ 1 ], 26 | outputs: [ 1 ], 27 | channelCount: 2, 28 | channelCountMode: MAX 29 | }); 30 | 31 | this._curve = curve; 32 | this._overSample = overSample; 33 | 34 | this.dspInit(); 35 | this.dspUpdateKernel(null, 1); 36 | } 37 | 38 | /** 39 | * @return {Float32Array} 40 | */ 41 | getCurve() { 42 | return this._curve; 43 | } 44 | 45 | /** 46 | * @param {Float32Array} value 47 | */ 48 | setCurve(value) { 49 | /* istanbul ignore else */ 50 | if (value === null || value instanceof Float32Array) { 51 | this._curve = value; 52 | this.dspUpdateKernel(this._curve, this.outputs[0].getNumberOfChannels()); 53 | } 54 | } 55 | 56 | /** 57 | * @return {boolean} 58 | */ 59 | getOversample() { 60 | return this._overSample; 61 | } 62 | 63 | /** 64 | * @param {boolean} value 65 | */ 66 | setOversample(value) { 67 | /* istanbul ignore else */ 68 | if (OverSampleTypes.indexOf(value) !== -1) { 69 | this._overSample = value; 70 | } 71 | } 72 | 73 | /** 74 | * @param {number} numberOfChannels 75 | */ 76 | channelDidUpdate(numberOfChannels) { 77 | this.dspUpdateKernel(this._curve, numberOfChannels); 78 | this.outputs[0].setNumberOfChannels(numberOfChannels); 79 | } 80 | } 81 | 82 | Object.assign(WaveShaperNode.prototype, WaveShaperNodeDSP); 83 | 84 | module.exports = WaveShaperNode; 85 | -------------------------------------------------------------------------------- /src/impl/core/AudioData.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const nmap = require("nmap"); 4 | 5 | /** 6 | * AudioData is struct like AudioBuffer. 7 | * This instance has no methods. 8 | * The channel data of this instance are taken via property accessor. 9 | * @prop {number} numberOfChannels 10 | * @prop {number} length 11 | * @prop {number} sampleRate 12 | * @prop {Float32Array[]} channelData 13 | */ 14 | class AudioData { 15 | /** 16 | * @param {number} numberOfChannels 17 | * @param {number} length 18 | * @param {number} sampleRate 19 | */ 20 | constructor(numberOfChannels, length, sampleRate) { 21 | this.numberOfChannels = numberOfChannels|0; 22 | this.length = length|0; 23 | this.sampleRate = sampleRate|0; 24 | this.channelData = nmap(this.numberOfChannels, () => new Float32Array(this.length)); 25 | } 26 | } 27 | 28 | module.exports = AudioData; 29 | -------------------------------------------------------------------------------- /src/impl/dsp/ChannelMergerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const ChannelMergerNodeDSP = { 4 | dspProcess() { 5 | const outputBus = this.outputs[0].bus; 6 | const inputBuses = this.inputs.map(input => input.bus); 7 | const allSilent = inputBuses.every(inputBus => inputBus.isSilent); 8 | 9 | outputBus.zeros(); 10 | 11 | if (!allSilent) { 12 | const outputChannelData = outputBus.getMutableData(); 13 | 14 | for (let i = 0, imax = inputBuses.length; i < imax; i++) { 15 | outputChannelData[i].set(inputBuses[i].getChannelData()[0]); 16 | } 17 | } 18 | } 19 | }; 20 | 21 | module.exports = ChannelMergerNodeDSP; 22 | -------------------------------------------------------------------------------- /src/impl/dsp/ChannelSplitterNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const ChannelSplitterNodeDSP = { 4 | dspProcess() { 5 | const inputBus = this.inputs[0].bus; 6 | const outputs = this.outputs; 7 | 8 | if (inputBus.isSilent) { 9 | for (let i = 0, imax = outputs.length; i < imax; i++) { 10 | outputs[i].bus.zeros(); 11 | } 12 | } else { 13 | const inputChannelData = inputBus.getChannelData(); 14 | 15 | for (let i = 0, imax = outputs.length; i < imax; i++) { 16 | const outputBus = outputs[i].bus; 17 | 18 | if (inputChannelData[i]) { 19 | outputBus.getMutableData()[0].set(inputChannelData[i]); 20 | } else { 21 | outputBus.zeros(); 22 | } 23 | } 24 | } 25 | } 26 | }; 27 | 28 | module.exports = ChannelSplitterNodeDSP; 29 | -------------------------------------------------------------------------------- /src/impl/dsp/ConstantSourceNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { fill } = require("../../utils"); 4 | 5 | const ConstantSourceNode = { 6 | dspInit() {}, 7 | 8 | dspProcess() { 9 | const offsetParam = this._offset; 10 | const outputBus = this.outputs[0].bus; 11 | const outputs = outputBus.getMutableData(); 12 | 13 | if (offsetParam.hasSampleAccurateValues()) { 14 | outputs[0].set(offsetParam.getSampleAccurateValues()); 15 | } else { 16 | fill(outputs[0], offsetParam.getValue()); 17 | } 18 | } 19 | }; 20 | 21 | module.exports = ConstantSourceNode; 22 | -------------------------------------------------------------------------------- /src/impl/dsp/ConvolverNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const ConvolverNodeDSP = { 4 | dspProcess() { 5 | const outputBus = this.outputs[0].bus; 6 | 7 | outputBus.zeros(); 8 | outputBus.sumFrom(this.inputs[0].bus); 9 | } 10 | }; 11 | 12 | module.exports = ConvolverNodeDSP; 13 | -------------------------------------------------------------------------------- /src/impl/dsp/DynamicsCompressorNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const DynamicsCompressorNodeDSP = { 4 | dspProcess() { 5 | const outputBus = this.outputs[0].bus; 6 | 7 | outputBus.zeros(); 8 | outputBus.sumFrom(this.inputs[0].bus); 9 | } 10 | }; 11 | 12 | module.exports = DynamicsCompressorNodeDSP; 13 | -------------------------------------------------------------------------------- /src/impl/dsp/IIRFilterKernel.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class IIRFilterKernel { 4 | constructor(feedforward, feedback) { 5 | this.a = toCoefficient(feedback, feedback[0]); 6 | this.b = toCoefficient(feedforward, feedback[0]); 7 | this.x = new Float32Array(this.b.length); 8 | this.y = new Float32Array(this.a.length); 9 | } 10 | 11 | process(input, output, inNumSamples) { 12 | const a = this.a; 13 | const b = this.b; 14 | const x = this.x; 15 | const y = this.y; 16 | const alen = this.a.length - 1; 17 | const blen = this.b.length; 18 | 19 | for (let i = 0; i < inNumSamples; i++) { 20 | x[blen - 1] = input[i]; 21 | y[alen] = 0; 22 | 23 | for (let j = 0; j < blen; j++) { 24 | y[alen] += b[j] * x[j]; 25 | x[j] = flushDenormalFloatToZero(x[j + 1]); 26 | } 27 | 28 | for (let j = 0; j < alen; j++) { 29 | y[alen] -= a[j] * y[j]; 30 | y[j] = flushDenormalFloatToZero(y[j + 1]); 31 | } 32 | 33 | output[i] = y[alen]; 34 | } 35 | } 36 | } 37 | 38 | function toCoefficient(filter, a0) { 39 | const coeff = new Float32Array(filter.length); 40 | 41 | for (let i = 0, imax = coeff.length; i < imax; i++) { 42 | coeff[i] = filter[imax - i - 1] / a0; 43 | } 44 | 45 | return coeff; 46 | } 47 | 48 | function flushDenormalFloatToZero(f) { 49 | return (Math.abs(f) < 1.175494e-38) ? 0.0 : f; 50 | } 51 | 52 | module.exports = IIRFilterKernel; 53 | -------------------------------------------------------------------------------- /src/impl/dsp/IIRFilterNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const assert = require("assert"); 4 | const IIRFilterKernel = require("./IIRFilterKernel"); 5 | const { getFilterResponse } = require("../../utils/FilterUtils"); 6 | 7 | const IIRFilterNodeDSP = { 8 | dspInit() { 9 | this._kernels = []; 10 | }, 11 | 12 | dspUpdateKernel(numberOfChannels) { 13 | if (numberOfChannels < this._kernels.length) { 14 | this._kernels.splice(numberOfChannels); 15 | } else if (this._kernels.length < numberOfChannels) { 16 | while (numberOfChannels !== this._kernels.length) { 17 | this._kernels.push(new IIRFilterKernel(this._feedforward, this._feedback)); 18 | } 19 | } 20 | 21 | assert(numberOfChannels === this._kernels.length); 22 | 23 | switch (numberOfChannels) { 24 | case 1: 25 | this.dspProcess = this.dspProcess1; 26 | break; 27 | case 2: 28 | this.dspProcess = this.dspProcess2; 29 | break; 30 | default: 31 | this.dspProcess = this.dspProcessN; 32 | break; 33 | } 34 | }, 35 | 36 | dspProcess1() { 37 | const inputs = this.inputs[0].bus.getChannelData(); 38 | const outputs = this.outputs[0].bus.getMutableData(); 39 | const kernels = this._kernels; 40 | 41 | kernels[0].process(inputs[0], outputs[0], this.blockSize); 42 | }, 43 | 44 | dspProcess2() { 45 | const blockSize = this.blockSize; 46 | const inputs = this.inputs[0].bus.getChannelData(); 47 | const outputs = this.outputs[0].bus.getMutableData(); 48 | const kernels = this._kernels; 49 | 50 | kernels[0].process(inputs[0], outputs[0], blockSize); 51 | kernels[1].process(inputs[1], outputs[1], blockSize); 52 | }, 53 | 54 | dspProcessN() { 55 | const blockSize = this.blockSize; 56 | const inputs = this.inputs[0].bus.getChannelData(); 57 | const outputs = this.outputs[0].bus.getMutableData(); 58 | const kernels = this._kernels; 59 | 60 | for (let i = 0, imax = kernels.length; i < imax; i++) { 61 | kernels[i].process(inputs[i], outputs[i], blockSize); 62 | } 63 | }, 64 | 65 | dspGetFrequencyResponse(frequencyHz, magResponse, phaseResponse) { 66 | const b = this._feedforward; 67 | const a = this._feedback; 68 | 69 | getFilterResponse(b, a, frequencyHz, magResponse, phaseResponse, this.sampleRate); 70 | } 71 | }; 72 | 73 | module.exports = IIRFilterNodeDSP; 74 | -------------------------------------------------------------------------------- /src/impl/dsp/PannerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PannerNodeDSP = { 4 | dspProcess() { 5 | const outputBus = this.outputs[0].bus; 6 | 7 | outputBus.zeros(); 8 | outputBus.sumFrom(this.inputs[0].bus); 9 | } 10 | }; 11 | 12 | module.exports = PannerNodeDSP; 13 | -------------------------------------------------------------------------------- /src/impl/dsp/PeriodicWave.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const WAVE_TABLE_LENGTH = 8192; 4 | 5 | const PeriodicWaveDSP = { 6 | dspInit() { 7 | this._waveTable = null; 8 | }, 9 | 10 | dspBuildWaveTable() { 11 | if (this._waveTable !== null) { 12 | return this._waveTable; 13 | } 14 | 15 | const waveTable = new Float32Array(WAVE_TABLE_LENGTH + 1); 16 | const real = this._real; 17 | const imag = this._imag; 18 | 19 | let maxAbsValue = 0; 20 | let periodicWaveLength = Math.min(real.length, 16); 21 | 22 | for (let i = 0; i < WAVE_TABLE_LENGTH; i++) { 23 | const x = (i / WAVE_TABLE_LENGTH) * Math.PI * 2; 24 | 25 | for (let n = 1; n < periodicWaveLength; n++) { 26 | waveTable[i] += real[n] * Math.cos(n * x) + imag[n] * Math.sin(n * x); 27 | } 28 | 29 | maxAbsValue = Math.max(maxAbsValue, Math.abs(waveTable[i])); 30 | } 31 | 32 | if (!this._constants && maxAbsValue !== 1) { 33 | for (let i = 0; i < WAVE_TABLE_LENGTH; i++) { 34 | waveTable[i] *= maxAbsValue; 35 | } 36 | } 37 | waveTable[WAVE_TABLE_LENGTH] = waveTable[0]; 38 | 39 | this._waveTable = waveTable; 40 | 41 | return waveTable; 42 | } 43 | }; 44 | 45 | module.exports = PeriodicWaveDSP; 46 | -------------------------------------------------------------------------------- /src/impl/dsp/SpatialPannerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const SpatialPannerNodeDSP = { 4 | dspProcess() { 5 | const outputBus = this.outputs[0].bus; 6 | 7 | outputBus.zeros(); 8 | outputBus.sumFrom(this.inputs[0].bus); 9 | } 10 | }; 11 | 12 | module.exports = SpatialPannerNodeDSP; 13 | -------------------------------------------------------------------------------- /src/impl/index.js: -------------------------------------------------------------------------------- 1 | "use strict", 2 | 3 | module.exports = { 4 | AnalyserNode: require("./AnalyserNode"), 5 | AudioBuffer: require("./AudioBuffer"), 6 | AudioBufferSourceNode: require("./AudioBufferSourceNode"), 7 | AudioContext: require("./AudioContext"), 8 | AudioDestinationNode: require("./AudioDestinationNode"), 9 | AudioListener: require("./AudioListener"), 10 | AudioNode: require("./AudioNode"), 11 | AudioParam: require("./AudioParam"), 12 | BiquadFilterNode: require("./BiquadFilterNode"), 13 | ChannelMergerNode: require("./ChannelMergerNode"), 14 | ChannelSplitterNode: require("./ChannelSplitterNode"), 15 | ConstantSourceNode: require("./ConstantSourceNode"), 16 | ConvolverNode: require("./ConvolverNode"), 17 | DelayNode: require("./DelayNode"), 18 | DynamicsCompressorNode: require("./DynamicsCompressorNode"), 19 | GainNode: require("./GainNode"), 20 | IIRFilterNode: require("./IIRFilterNode"), 21 | OscillatorNode: require("./OscillatorNode"), 22 | PannerNode: require("./PannerNode"), 23 | PeriodicWave: require("./PeriodicWave"), 24 | ScriptProcessorNode: require("./ScriptProcessorNode"), 25 | SpatialPannerNode: require("./SpatialPannerNode"), 26 | StereoPannerNode: require("./StereoPannerNode"), 27 | WaveShaperNode: require("./WaveShaperNode") 28 | }; 29 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const OfflineAudioContext = require("./context/OfflineAudioContext"); 4 | const StreamAudioContext = require("./context/StreamAudioContext"); 5 | const RenderingAudioContext = require("./context/RenderingAudioContext"); 6 | const WebAudioContext = require("./context/WebAudioContext"); 7 | const api = require("./api"); 8 | const impl = require("./impl"); 9 | const decoder = require("./decoder"); 10 | const encoder = require("./encoder"); 11 | 12 | module.exports = { 13 | OfflineAudioContext, 14 | StreamAudioContext, 15 | RenderingAudioContext, 16 | WebAudioContext, 17 | api, impl, decoder, encoder 18 | }; 19 | -------------------------------------------------------------------------------- /src/utils/DecoderUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const nmap = require("nmap"); 4 | const AudioDataUtils = require("./AudioDataUtils"); 5 | 6 | /** 7 | * @param {function} decodeFn 8 | * @param {ArrayBuffer} audioData 9 | * @param {object} opts 10 | * @return {Promise} 11 | */ 12 | function decode(decodeFn, audioData, /* istanbul ignore next */ opts = {}) { 13 | return new Promise((resolve, reject) => { 14 | return decodeFn(audioData, opts).then((result) => { 15 | if (AudioDataUtils.isAudioData(result)) { 16 | if (typeof opts.sampleRate === "number") { 17 | result = resample(result, opts.sampleRate); 18 | } 19 | return resolve(result); 20 | } 21 | return reject(new TypeError("Failed to decode audio data")); 22 | }, reject); 23 | }); 24 | } 25 | 26 | /** 27 | * @param {AudioData} audioData 28 | * @param {number} sampleRate 29 | */ 30 | function resample(audioData, sampleRate) { 31 | if (audioData.sampleRate === sampleRate) { 32 | return audioData; 33 | } 34 | 35 | const rate = audioData.sampleRate / sampleRate; 36 | const numberOfChannels = audioData.channelData.length; 37 | const length = Math.round(audioData.channelData[0].length / rate); 38 | const channelData = nmap(numberOfChannels, () => new Float32Array(length)); 39 | 40 | for (let i = 0; i < length; i++) { 41 | const ix = i * rate; 42 | const i0 = ix | 0; 43 | const i1 = i0 + 1; 44 | const ia = ix % 1; 45 | 46 | for (let ch = 0; ch < numberOfChannels; ch++) { 47 | const v0 = audioData.channelData[ch][i0]; 48 | const v1 = audioData.channelData[ch][i1]; 49 | 50 | channelData[ch][i] = v0 + ia * (v1 - v0); 51 | } 52 | } 53 | 54 | return { numberOfChannels, length, sampleRate, channelData }; 55 | } 56 | 57 | module.exports = { decode, resample }; 58 | -------------------------------------------------------------------------------- /src/utils/EncoderUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AudioDataUtils = require("./AudioDataUtils"); 4 | 5 | /** 6 | * @param {function} encodeFn 7 | * @param {AudioData} audioData 8 | * @param {object} opts 9 | */ 10 | function encode(encodeFn, audioData, /* istanbul ignore next */ opts = {}) { 11 | if (!AudioDataUtils.isAudioData(audioData)) { 12 | audioData = AudioDataUtils.toAudioData(audioData); 13 | } 14 | return encodeFn(audioData, opts); 15 | } 16 | 17 | module.exports = { encode }; 18 | -------------------------------------------------------------------------------- /src/utils/FilterUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function getFilterResponse(b, a, frequencyHz, magResponse, phaseResponse, sampleRate) { 4 | for (let i = 0, imax = frequencyHz.length; i < imax; i++) { 5 | const w0 = 2 * Math.PI * (frequencyHz[i] / sampleRate); 6 | const ca = compute(a, Math.cos, w0); 7 | const sa = compute(a, Math.sin, w0); 8 | const cb = compute(b, Math.cos, w0); 9 | const sb = compute(b, Math.sin, w0); 10 | 11 | magResponse[i] = Math.sqrt((cb * cb + sb * sb) / (ca * ca + sa * sa)); 12 | phaseResponse[i] = Math.atan2(sa, ca) - Math.atan2(sb, cb); 13 | } 14 | } 15 | 16 | function compute(values, fn, w0) { 17 | let result = 0; 18 | 19 | for (let i = 0, imax = values.length; i < imax; i++) { 20 | result += values[i] * fn(w0 * i); 21 | } 22 | 23 | return result; 24 | } 25 | 26 | module.exports = { getFilterResponse }; 27 | -------------------------------------------------------------------------------- /src/utils/PCMArrayBufferWriter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class PCMArrayBufferWriter { 4 | constructor(buffer) { 5 | this._view = new DataView(buffer); 6 | this._pos = 0; 7 | } 8 | 9 | pcm8(value) { 10 | value = Math.max(-1, Math.min(value, +1)); 11 | value = (value * 0.5 + 0.5) * 128; 12 | this._view.setUint8(this._pos, value|0); 13 | this._pos += 1; 14 | } 15 | 16 | pcm16(value) { 17 | value = Math.max(-1, Math.min(value, +1)); 18 | value = value < 0 ? value * 32768 : value * 32767; 19 | this._view.setInt16(this._pos, value|0, true); 20 | this._pos += 2; 21 | } 22 | 23 | pcm32(value) { 24 | value = Math.max(-1, Math.min(value, +1)); 25 | value = value < 0 ? value * 2147483648 : value * 2147483647; 26 | this._view.setInt32(this._pos, value|0, true); 27 | this._pos += 4; 28 | } 29 | 30 | pcm32f(value) { 31 | this._view.setFloat32(this._pos, value, true); 32 | this._pos += 4; 33 | } 34 | } 35 | 36 | module.exports = PCMArrayBufferWriter; 37 | -------------------------------------------------------------------------------- /src/utils/PCMBufferWriter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class PCMBufferWriter { 4 | constructor(buffer) { 5 | this._buffer = buffer; 6 | this._pos = 0; 7 | } 8 | 9 | pcm8(value) { 10 | value = Math.max(-1, Math.min(value, +1)); 11 | value = (value * 0.5 + 0.5) * 128; 12 | this._buffer.writeUInt8(value|0, this._pos); 13 | this._pos += 1; 14 | } 15 | 16 | pcm16(value) { 17 | value = Math.max(-1, Math.min(value, +1)); 18 | value = value < 0 ? value * 32768 : value * 32767; 19 | this._buffer.writeInt16LE(value|0, this._pos); 20 | this._pos += 2; 21 | } 22 | 23 | pcm32(value) { 24 | value = Math.max(-1, Math.min(value, +1)); 25 | value = value < 0 ? value * 2147483648 : value * 2147483647; 26 | this._buffer.writeInt32LE(value|0, this._pos); 27 | this._pos += 4; 28 | } 29 | 30 | pcm32f(value) { 31 | this._buffer.writeFloatLE(value, this._pos); 32 | this._pos += 4; 33 | } 34 | } 35 | 36 | module.exports = PCMBufferWriter; 37 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports.clamp = require("./utils/clamp"); 4 | module.exports.defaults = require("./utils/defaults"); 5 | module.exports.defineProp = require("./utils/defineProp"); 6 | module.exports.fill = require("./utils/fill"); 7 | module.exports.fillRange = require("./utils/fillRange"); 8 | module.exports.normalize = require("./utils/normalize"); 9 | module.exports.toArrayIfNeeded = require("./utils/toArrayIfNeeded"); 10 | module.exports.toAudioTime = require("./utils/toAudioTime"); 11 | module.exports.toDecibel = require("./utils/toDecibel"); 12 | module.exports.toGain = require("./utils/toGain"); 13 | module.exports.toImpl = require("./utils/toImpl"); 14 | module.exports.toNumber = require("./utils/toNumber"); 15 | module.exports.toPowerOfTwo = require("./utils/toPowerOfTwo"); 16 | module.exports.toValidBitDepth = require("./utils/toValidBitDepth"); 17 | module.exports.toValidBlockSize = require("./utils/toValidBlockSize"); 18 | module.exports.toValidNumberOfChannels = require("./utils/toValidNumberOfChannels"); 19 | module.exports.toValidSampleRate = require("./utils/toValidSampleRate"); 20 | -------------------------------------------------------------------------------- /src/utils/setImmediate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = global.setImmediate /* istanbul ignore next */ || (fn => setTimeout(fn, 0)); 4 | -------------------------------------------------------------------------------- /src/utils/utils/clamp.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {number} value 5 | * @param {number} minValue 6 | * @param {number} maxValue 7 | */ 8 | function clamp(value, minValue, maxValue) { 9 | return Math.max(minValue, Math.min(value, maxValue)); 10 | } 11 | 12 | module.exports = clamp; 13 | -------------------------------------------------------------------------------- /src/utils/utils/defaults.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {*} value 5 | * @param {*) defaultValue 6 | */ 7 | function defaults(value, defaultValue) { 8 | return typeof value !== "undefined" ? value : defaultValue; 9 | } 10 | 11 | module.exports = defaults; 12 | -------------------------------------------------------------------------------- /src/utils/utils/defineProp.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {object} target 5 | * @param {string} name 6 | * @param {*} value 7 | */ 8 | function defineProp(target, name, value) { 9 | Object.defineProperty(target, name, { value: value, enumerable: false, writable: true, configurable: true }); 10 | } 11 | 12 | module.exports = defineProp; 13 | -------------------------------------------------------------------------------- /src/utils/utils/fill.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {number[]} list 5 | * @param {number} value 6 | * @return {number[]} 7 | */ 8 | function fill(list, value) { 9 | if (list.fill) { 10 | return list.fill(value); 11 | } 12 | 13 | for (let i = 0, imax = list.length; i < imax; i++) { 14 | list[i] = value; 15 | } 16 | 17 | return list; 18 | } 19 | 20 | module.exports = fill; 21 | -------------------------------------------------------------------------------- /src/utils/utils/fillRange.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {number[]} list 5 | * @param {number} value 6 | * @param {number} start 7 | * @param {number} end 8 | * @return {number[]} 9 | */ 10 | function fillRange(list, value, start, end) { 11 | if (list.fill) { 12 | return list.fill(value, start, end); 13 | } 14 | 15 | for (let i = start; i < end; i++) { 16 | list[i] = value; 17 | } 18 | 19 | return list; 20 | } 21 | 22 | module.exports = fillRange; 23 | -------------------------------------------------------------------------------- /src/utils/utils/normalize.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const clamp = require("./clamp"); 4 | 5 | /** 6 | * normalize - returns a number between 0 - 1 7 | * @param {number} value 8 | * @param {number} minValue 9 | * @param {number} maxValue 10 | */ 11 | function normalize(value, minValue, maxValue) { 12 | const val = (value - minValue) / (maxValue - minValue); 13 | return clamp(val, 0, 1); 14 | } 15 | 16 | module.exports = normalize; 17 | -------------------------------------------------------------------------------- /src/utils/utils/toArrayIfNeeded.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {*} value 5 | * @return {Array} 6 | */ 7 | function toArrayIfNeeded(value) { 8 | return Array.isArray(value) ? value : [ value ]; 9 | } 10 | 11 | module.exports = toArrayIfNeeded; 12 | -------------------------------------------------------------------------------- /src/utils/utils/toAudioTime.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {number|string} str 5 | * @return {number} 6 | */ 7 | function toAudioTime(str) { 8 | if (Number.isFinite(+str)) { 9 | return Math.max(0, +str); 10 | } 11 | 12 | const matched = ("" + str).match(/^(?:(\d\d+):)?(\d\d?):(\d\d?(?:\.\d+)?)$/); 13 | 14 | if (matched) { 15 | const hours = +matched[1]|0; 16 | const minutes = +matched[2]; 17 | const seconds = +matched[3]; 18 | 19 | return hours * 3600 + minutes * 60 + seconds; 20 | } 21 | 22 | return 0; 23 | } 24 | 25 | module.exports = toAudioTime; 26 | -------------------------------------------------------------------------------- /src/utils/utils/toDecibel.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {*} value 5 | * @return {number} 6 | */ 7 | function toDecibel(value) { 8 | return 20 * Math.log10(value); 9 | } 10 | 11 | module.exports = toDecibel; 12 | -------------------------------------------------------------------------------- /src/utils/utils/toGain.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {*} value 5 | * @return {number} 6 | */ 7 | function toGain(value) { 8 | return Math.sqrt(Math.pow(10, (value / 10))); 9 | } 10 | 11 | module.exports = toGain; 12 | -------------------------------------------------------------------------------- /src/utils/utils/toImpl.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {object} value 5 | * @return {object} 6 | */ 7 | function toImpl(value) { 8 | return value._impl || value; 9 | } 10 | 11 | module.exports = toImpl; 12 | -------------------------------------------------------------------------------- /src/utils/utils/toNumber.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {*} value 5 | * @return {number} 6 | */ 7 | function toNumber(value) { 8 | return +value || 0; 9 | } 10 | 11 | module.exports = toNumber; 12 | -------------------------------------------------------------------------------- /src/utils/utils/toPowerOfTwo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @param {number} value 5 | * @param {function} round 6 | * @return {number} 7 | */ 8 | function toPowerOfTwo(value, round) { 9 | round = round || Math.round; 10 | return 1 << round(Math.log(value) / Math.log(2)); 11 | } 12 | 13 | module.exports = toPowerOfTwo; 14 | -------------------------------------------------------------------------------- /src/utils/utils/toValidBitDepth.js: -------------------------------------------------------------------------------- 1 | "use stirct"; 2 | 3 | /** 4 | * @param {number} value 5 | * @return {number} 6 | */ 7 | function toValidBitDepth(value) { 8 | value = value|0; 9 | if (value === 8 || value === 16 || value === 32) { 10 | return value; 11 | } 12 | return 16; 13 | } 14 | 15 | module.exports = toValidBitDepth; 16 | -------------------------------------------------------------------------------- /src/utils/utils/toValidBlockSize.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const clamp = require("./clamp"); 4 | const toPowerOfTwo = require("./toPowerOfTwo"); 5 | const { MIN_BLOCK_SIZE, MAX_BLOCK_SIZE } = require("../../constants"); 6 | 7 | /** 8 | * @param {number} value 9 | * @return {number} 10 | */ 11 | function toValidBlockSize(value) { 12 | return clamp(toPowerOfTwo(value), MIN_BLOCK_SIZE, MAX_BLOCK_SIZE); 13 | } 14 | 15 | module.exports = toValidBlockSize; 16 | -------------------------------------------------------------------------------- /src/utils/utils/toValidNumberOfChannels.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const toNumber = require("./toNumber"); 4 | const clamp = require("./clamp"); 5 | const { MAX_NUMBER_OF_CHANNELS } = require("../../constants"); 6 | 7 | /** 8 | * @param {number} value 9 | * @return {number} 10 | */ 11 | function toValidNumberOfChannels(value) { 12 | return clamp(toNumber(value), 1, MAX_NUMBER_OF_CHANNELS)|0; 13 | } 14 | 15 | module.exports = toValidNumberOfChannels; 16 | -------------------------------------------------------------------------------- /src/utils/utils/toValidSampleRate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const toNumber = require("./toNumber"); 4 | const clamp = require("./clamp"); 5 | const { MIN_SAMPLERATE, MAX_SAMPLERATE } = require("../../constants"); 6 | 7 | /** 8 | * @param {number} value 9 | * @return {number} 10 | */ 11 | function toValidSampleRate(value) { 12 | return clamp(toNumber(value), MIN_SAMPLERATE, MAX_SAMPLERATE)|0; 13 | } 14 | 15 | module.exports = toValidSampleRate; 16 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/api/AudioDestinationNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const api = require("../../src/api"); 8 | const AudioContext = require("../../src/api/BaseAudioContext"); 9 | 10 | describe("api/AudioDestinationNode", () => { 11 | it("context.destination", () => { 12 | const context = new AudioContext(); 13 | const target = context.destination; 14 | 15 | assert(target instanceof api.AudioDestinationNode); 16 | }); 17 | 18 | describe("attributes", () => { 19 | it(".maxChannelCount", () => { 20 | const context = new AudioContext(); 21 | const target = context.destination; 22 | const maxChannelCount = 2; 23 | 24 | target._impl.getMaxChannelCount = sinon.spy(() => maxChannelCount); 25 | 26 | assert(target.maxChannelCount === maxChannelCount); 27 | assert(target._impl.getMaxChannelCount.callCount === 1); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/api/AudioListener.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const api = require("../../src/api"); 8 | const AudioContext = require("../../src/api/BaseAudioContext"); 9 | 10 | describe("api/AudioListener", () => { 11 | it("context.listener", () => { 12 | const context = new AudioContext(); 13 | const target = context.listener; 14 | 15 | assert(target instanceof api.AudioListener); 16 | }); 17 | 18 | describe("methods", () => { 19 | it(".setPosition(x, y, z)", () => { 20 | const context = new AudioContext(); 21 | const target = context.listener; 22 | const x = 0; 23 | const y = 1; 24 | const z = 2; 25 | 26 | target._impl.setPosition = sinon.spy(); 27 | 28 | target.setPosition(x, y, z); 29 | assert(target._impl.setPosition.callCount === 1); 30 | assert(target._impl.setPosition.args[0][0] === x); 31 | assert(target._impl.setPosition.args[0][1] === y); 32 | assert(target._impl.setPosition.args[0][2] === z); 33 | }); 34 | 35 | it(".setOrientation(x, y, z, xUp, yUp, zUp)", () => { 36 | const context = new AudioContext(); 37 | const target = context.listener; 38 | const x = 0; 39 | const y = 1; 40 | const z = 2; 41 | const xUp = 3; 42 | const yUp = 4; 43 | const zUp = 5; 44 | 45 | target._impl.setOrientation = sinon.spy(); 46 | 47 | target.setOrientation(x, y, z, xUp, yUp, zUp); 48 | assert(target._impl.setOrientation.callCount === 1); 49 | assert(target._impl.setOrientation.args[0][0] === x); 50 | assert(target._impl.setOrientation.args[0][1] === y); 51 | assert(target._impl.setOrientation.args[0][2] === z); 52 | assert(target._impl.setOrientation.args[0][3] === xUp); 53 | assert(target._impl.setOrientation.args[0][4] === yUp); 54 | assert(target._impl.setOrientation.args[0][5] === zUp); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/api/ChannelMergerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const api = require("../../src/api"); 7 | const AudioContext = require("../../src/api/BaseAudioContext"); 8 | 9 | describe("api/ChannelMergerNode", () => { 10 | it("context.createChannelMerger(numberOfInputs)", () => { 11 | const context = new AudioContext(); 12 | const target = context.createChannelMerger(2); 13 | 14 | assert(target instanceof api.ChannelMergerNode); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/api/ChannelSplitterNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const api = require("../../src/api"); 7 | const AudioContext = require("../../src/api/BaseAudioContext"); 8 | 9 | describe("api/ChannelSplitterNode", () => { 10 | it("context.createChannelSplitter(numberOfOutputs)", () => { 11 | const context = new AudioContext(); 12 | const target = context.createChannelSplitter(2); 13 | 14 | assert(target instanceof api.ChannelSplitterNode); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/api/ConstantSourceNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const api = require("../../src/api"); 7 | const AudioContext = require("../../src/api/BaseAudioContext"); 8 | const AudioParam = require("../../src/api/AudioParam"); 9 | 10 | describe("api/ConstantSourceNode", () => { 11 | it("context.createConstantSource()", () => { 12 | const context = new AudioContext(); 13 | const target = context.createConstantSource(); 14 | 15 | assert(target instanceof api.ConstantSourceNode); 16 | assert(target instanceof api.AudioScheduledSourceNode); 17 | }); 18 | 19 | describe("attributes", () => { 20 | it(".offset=", () => { 21 | const context = new AudioContext(); 22 | const target = context.createConstantSource(); 23 | 24 | assert(target.offset instanceof AudioParam); 25 | assert(target.offset === target._impl.$offset); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/api/ConvolverNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const api = require("../../src/api"); 8 | const AudioContext = require("../../src/api/BaseAudioContext"); 9 | 10 | describe("api/ConvolverNode", () => { 11 | it("context.createConvolver()", () => { 12 | const context = new AudioContext(); 13 | const target = context.createConvolver(); 14 | 15 | assert(target instanceof api.ConvolverNode); 16 | }); 17 | 18 | describe("attributes", () => { 19 | it(".buffer=", () => { 20 | const context = new AudioContext(); 21 | const target = context.createConvolver(); 22 | const buffer = context.createBuffer(1, 16, 8000); 23 | 24 | target._impl.setBuffer = sinon.spy(); 25 | 26 | target.buffer = buffer; 27 | assert(target.buffer === buffer); 28 | assert(target._impl.setBuffer.callCount === 1); 29 | assert(target._impl.setBuffer.args[0][0] === buffer); 30 | }); 31 | 32 | it(".normalize=", () => { 33 | const context = new AudioContext(); 34 | const target = context.createConvolver(); 35 | const normalize1 = true; 36 | const normalize2 = false; 37 | 38 | target._impl.getNormalize = sinon.spy(() => normalize1); 39 | target._impl.setNormalize = sinon.spy(); 40 | 41 | assert(target.normalize === normalize1); 42 | assert(target._impl.getNormalize.callCount === 1); 43 | 44 | target.normalize = normalize2; 45 | assert(target._impl.setNormalize.callCount === 1); 46 | assert(target._impl.setNormalize.args[0][0] === normalize2); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/api/DelayNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const api = require("../../src/api"); 7 | const AudioContext = require("../../src/api/BaseAudioContext"); 8 | const AudioParam = require("../../src/api/AudioParam"); 9 | 10 | describe("api/DelayNode", () => { 11 | it("context.createDelay(maxDelayTime)", () => { 12 | const context = new AudioContext(); 13 | const target = context.createDelay(1); 14 | 15 | assert(target instanceof api.DelayNode); 16 | }); 17 | 18 | describe("attributes", () => { 19 | it(".delayTime", () => { 20 | const context = new AudioContext(); 21 | const target = context.createDelay(); 22 | 23 | assert(target.delayTime instanceof AudioParam); 24 | assert(target.delayTime === target._impl.$delayTime); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/api/DynamicsCompressorNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const api = require("../../src/api"); 8 | const AudioContext = require("../../src/api/BaseAudioContext"); 9 | const AudioParam = require("../../src/api/AudioParam"); 10 | 11 | describe("api/DynamicsCompressorNode", () => { 12 | it("context.createDynamicsCompressor()", () => { 13 | const context = new AudioContext(); 14 | const target = context.createDynamicsCompressor(); 15 | 16 | assert(target instanceof api.DynamicsCompressorNode); 17 | }); 18 | 19 | describe("attributes", () => { 20 | it(".threshold", () => { 21 | const context = new AudioContext(); 22 | const target = context.createDynamicsCompressor(); 23 | 24 | assert(target.threshold instanceof AudioParam); 25 | assert(target.threshold === target._impl.$threshold); 26 | }); 27 | 28 | it(".knee", () => { 29 | const context = new AudioContext(); 30 | const target = context.createDynamicsCompressor(); 31 | 32 | assert(target.knee instanceof AudioParam); 33 | assert(target.knee === target._impl.$knee); 34 | }); 35 | 36 | it(".ratio", () => { 37 | const context = new AudioContext(); 38 | const target = context.createDynamicsCompressor(); 39 | 40 | assert(target.ratio instanceof AudioParam); 41 | assert(target.ratio === target._impl.$ratio); 42 | }); 43 | 44 | it(".reduction", () => { 45 | const context = new AudioContext(); 46 | const target = context.createDynamicsCompressor(); 47 | const reduction = 0; 48 | 49 | target._impl.getReduction = sinon.spy(() => reduction); 50 | 51 | assert(target.reduction === reduction); 52 | assert(target._impl.getReduction.callCount === 1); 53 | }); 54 | 55 | it(".attack", () => { 56 | const context = new AudioContext(); 57 | const target = context.createDynamicsCompressor(); 58 | 59 | assert(target.attack instanceof AudioParam); 60 | assert(target.attack === target._impl.$attack); 61 | }); 62 | 63 | it(".release", () => { 64 | const context = new AudioContext(); 65 | const target = context.createDynamicsCompressor(); 66 | 67 | assert(target.release instanceof AudioParam); 68 | assert(target.release === target._impl.$release); 69 | }); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /test/api/EventTarget.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const AudioContext = require("../../src/api/BaseAudioContext"); 8 | 9 | describe("api/EventTarget", () => { 10 | describe("methods", () => { 11 | it(".addEventListener(type, listener)", () => { 12 | const context = new AudioContext(); 13 | const target = context.createOscillator(); 14 | const type = "ended"; 15 | const listener = () => {}; 16 | 17 | target._impl.addEventListener = sinon.spy(); 18 | 19 | target.addEventListener(type, listener); 20 | assert(target._impl.addEventListener.callCount === 1); 21 | assert(target._impl.addEventListener.args[0][0] === type); 22 | assert(target._impl.addEventListener.args[0][1] === listener); 23 | }); 24 | 25 | it(".removeEventListener()", () => { 26 | const context = new AudioContext(); 27 | const target = context.createOscillator(); 28 | const type = "ended"; 29 | const listener = sinon.spy(); 30 | 31 | target._impl.removeEventListener = sinon.spy(); 32 | 33 | target.removeEventListener(type, listener); 34 | assert(target._impl.removeEventListener.callCount === 1); 35 | assert(target._impl.removeEventListener.args[0][0] === type); 36 | assert(target._impl.removeEventListener.args[0][1] === listener); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/api/GainNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const api = require("../../src/api"); 7 | const AudioContext = require("../../src/api/BaseAudioContext"); 8 | const AudioParam = require("../../src/api/AudioParam"); 9 | 10 | describe("api/GainNode", () => { 11 | it("context.createGain()", () => { 12 | const context = new AudioContext(); 13 | const target = context.createGain(); 14 | 15 | assert(target instanceof api.GainNode); 16 | }); 17 | 18 | describe("attributes", () => { 19 | it(".gain", () => { 20 | const context = new AudioContext(); 21 | const target = context.createGain(); 22 | 23 | assert(target.gain instanceof AudioParam); 24 | assert(target.gain === target._impl.$gain); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/api/IIRFilterNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const api = require("../../src/api"); 8 | const AudioContext = require("../../src/api/BaseAudioContext"); 9 | 10 | describe("api/IIRFilterNode", () => { 11 | it("context.createIIRFilter(feedforward, feedback)", () => { 12 | const context = new AudioContext(); 13 | const feedforward = new Float32Array(4); 14 | const feedback = new Float32Array(4); 15 | const target = context.createIIRFilter(feedforward, feedback); 16 | 17 | assert(target instanceof api.IIRFilterNode); 18 | }); 19 | 20 | describe("methods", () => { 21 | it(".getFrequencyResponse(frequencyHz, magResponse, phaseResponse)", () => { 22 | const context = new AudioContext(); 23 | const feedforward = new Float32Array(4); 24 | const feedback = new Float32Array(4); 25 | const target = context.createIIRFilter(feedforward, feedback); 26 | const frequencyHz = new Float32Array(128); 27 | const magResponse = new Float32Array(128); 28 | const phaseResponse = new Float32Array(128); 29 | 30 | target._impl.getFrequencyResponse = sinon.spy(); 31 | 32 | target.getFrequencyResponse(frequencyHz, magResponse, phaseResponse); 33 | assert(target._impl.getFrequencyResponse.callCount === 1); 34 | assert(target._impl.getFrequencyResponse.args[0][0] === frequencyHz); 35 | assert(target._impl.getFrequencyResponse.args[0][1] === magResponse); 36 | assert(target._impl.getFrequencyResponse.args[0][2] === phaseResponse); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/api/PeriodicWave.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const api = require("../../src/api"); 7 | const AudioContext = require("../../src/api/BaseAudioContext"); 8 | 9 | describe("api/PeriodicWave", () => { 10 | it("context.createPeriodicWave(real, imag)", () => { 11 | const context = new AudioContext(); 12 | const real = new Float32Array(16); 13 | const imag = new Float32Array(16); 14 | const target = context.createPeriodicWave(real, imag); 15 | 16 | assert(target instanceof api.PeriodicWave); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/api/ScriptProcessorNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const api = require("../../src/api"); 8 | const AudioContext = require("../../src/api/BaseAudioContext"); 9 | 10 | describe("api/ScriptProcessorNode", () => { 11 | it("context.createScriptProcessor(bufferSize, numberOfInputChannels, numberOfOutputChannels)", () => { 12 | const context = new AudioContext(); 13 | const target = context.createScriptProcessor(256, 1, 1); 14 | 15 | assert(target instanceof api.ScriptProcessorNode); 16 | }); 17 | 18 | describe("attributes", () => { 19 | it(".bufferSize", () => { 20 | const context = new AudioContext(); 21 | const target = context.createScriptProcessor(256, 1, 1); 22 | const bufferSize = 256; 23 | 24 | target._impl.getBufferSize = sinon.spy(() => bufferSize); 25 | 26 | assert(target.bufferSize === bufferSize); 27 | assert(target._impl.getBufferSize.callCount === 1); 28 | }); 29 | 30 | it(".onaudioprocess=", () => { 31 | const context = new AudioContext(); 32 | const target = context.createScriptProcessor(256, 1, 1); 33 | const callback1 = sinon.spy(); 34 | const callback2 = sinon.spy(); 35 | const callback3 = sinon.spy(); 36 | const event = { type: "audioprocess" }; 37 | 38 | target.onaudioprocess = callback1; 39 | target.onaudioprocess = callback2; 40 | target.addEventListener("audioprocess", callback3); 41 | target._impl.dispatchEvent(event); 42 | 43 | assert(target.onaudioprocess === callback2); 44 | assert(callback1.callCount === 0); 45 | assert(callback2.callCount === 1); 46 | assert(callback3.callCount === 1); 47 | assert(callback2.args[0][0] === event); 48 | assert(callback3.args[0][0] === event); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/api/StereoPannerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const api = require("../../src/api"); 7 | const AudioContext = require("../../src/api/BaseAudioContext"); 8 | const AudioParam = require("../../src/api/AudioParam"); 9 | 10 | describe("api/StereoPannerNode", () => { 11 | it("context.createStereoPanner()", () => { 12 | const context = new AudioContext(); 13 | const target = context.createStereoPanner(); 14 | 15 | assert(target instanceof api.StereoPannerNode); 16 | }); 17 | 18 | describe("atrributes", () => { 19 | it(".pan", () => { 20 | const context = new AudioContext(); 21 | const target = context.createStereoPanner(); 22 | 23 | assert(target.pan instanceof AudioParam); 24 | assert(target.pan === target._impl.$pan); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/api/WaveShaperNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const api = require("../../src/api"); 8 | const AudioContext = require("../../src/api/BaseAudioContext"); 9 | 10 | describe("api/WaveShaperNode", () => { 11 | it("context.createWaveShaper()", () => { 12 | const context = new AudioContext(); 13 | const target = context.createWaveShaper(); 14 | 15 | assert(target instanceof api.WaveShaperNode); 16 | }); 17 | 18 | describe("atrributes", () => { 19 | it(".curve=", () => { 20 | const context = new AudioContext(); 21 | const target = context.createWaveShaper(); 22 | const curve1 = null; 23 | const curve2 = new Float32Array(128); 24 | 25 | target._impl.getCurve = sinon.spy(() => curve1); 26 | target._impl.setCurve = sinon.spy(); 27 | 28 | assert(target.curve === curve1); 29 | assert(target._impl.getCurve.callCount === 1); 30 | 31 | target.curve = curve2; 32 | assert(target._impl.setCurve.callCount === 1); 33 | assert(target._impl.setCurve.args[0][0] === curve2); 34 | }); 35 | 36 | it(".oversample=", () => { 37 | const context = new AudioContext(); 38 | const target = context.createWaveShaper(); 39 | const oversample1 = "none"; 40 | const oversample2 = "2x"; 41 | 42 | target._impl.getOversample = sinon.spy(() => oversample1); 43 | target._impl.setOversample = sinon.spy(); 44 | 45 | assert(target.oversample === oversample1); 46 | assert(target._impl.getOversample.callCount === 1); 47 | 48 | target.oversample = oversample2; 49 | assert(target._impl.setOversample.callCount === 1); 50 | assert(target._impl.setOversample.args[0][0] === oversample2); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /test/context/RenderingAudioContext.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const RenderingAudioContext = require("../../src/context/RenderingAudioContext"); 7 | 8 | describe("RenderingAudioContext", () => { 9 | it("should return a RenderingAudioContext instance", () => { 10 | const context = new RenderingAudioContext(); 11 | 12 | assert(context instanceof RenderingAudioContext); 13 | }); 14 | 15 | it("should return a RenderingAudioContext instance with options", () => { 16 | const context = new RenderingAudioContext({ 17 | sampleRate: 8000, numberOfChannels: 1, blockSize: 256, bitDepth: 8 18 | }); 19 | 20 | assert(context instanceof RenderingAudioContext); 21 | assert(context.sampleRate === 8000); 22 | assert(context.numberOfChannels === 1); 23 | assert(context.blockSize === 256); 24 | assert.deepEqual(context.format, { 25 | sampleRate: 8000, 26 | channels: 1, 27 | bitDepth: 8, 28 | float: false 29 | }); 30 | }); 31 | 32 | it("should advance current time when rendered", () => { 33 | const context = new RenderingAudioContext(); 34 | 35 | assert(context.currentTime === 0); 36 | 37 | context.processTo("00:00:10.000"); 38 | assert(context.currentTime|0 === 10); 39 | 40 | context.processTo("00:00:15.000"); 41 | assert(context.currentTime|0 === 15); 42 | }); 43 | 44 | it("should export AudioData", () => { 45 | const context = new RenderingAudioContext(); 46 | 47 | context.processTo("00:00:10.000"); 48 | 49 | const audioData = context.exportAsAudioData(); 50 | 51 | assert(audioData.numberOfChannels === 2); 52 | assert((audioData.length / audioData.sampleRate)|0 === 10); 53 | assert(audioData.sampleRate === 44100); 54 | }); 55 | 56 | it("should encode AudioData", () => { 57 | const context = new RenderingAudioContext(); 58 | 59 | const audioData = { 60 | sampleRate: 44100, 61 | channelData: [ new Float32Array(16) ] 62 | }; 63 | 64 | return context.encodeAudioData(audioData).then((arrayBuffer) => { 65 | assert(arrayBuffer instanceof ArrayBuffer); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/context/StreamAudioContext.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const events = require("events"); 8 | const StreamAudioContext = require("../../src/context/StreamAudioContext"); 9 | 10 | describe("StreamAudioContext", () => { 11 | describe("constructor", () => { 12 | it("works", () => { 13 | const context = new StreamAudioContext(); 14 | 15 | assert(context instanceof StreamAudioContext); 16 | assert.doesNotThrow(() => { context._stream.write(); }); 17 | }); 18 | 19 | it("with options", () => { 20 | const context = new StreamAudioContext({ sampleRate: 8000, numberOfChannels: 1, blockSize: 16, bitDepth: 8 }); 21 | 22 | assert(context.numberOfChannels === 1); 23 | assert(context.blockSize === 16); 24 | assert.deepEqual(context.format, { sampleRate: 8000, channels: 1, bitDepth: 8, float: false }); 25 | }); 26 | }); 27 | 28 | describe("rendering", () => { 29 | it("basic", (done) => { 30 | const context = new StreamAudioContext(); 31 | const streamOut = new events.EventEmitter(); 32 | const write1 = sinon.spy((buffer) => { 33 | assert(buffer instanceof Buffer); 34 | if (write1.callCount >= 75) { 35 | done(); 36 | return false; 37 | } 38 | return true; 39 | }); 40 | 41 | streamOut.write = write1; 42 | 43 | context.pipe(streamOut); 44 | context.resume(); 45 | }); 46 | 47 | it("suspend", (done) => { 48 | const context = new StreamAudioContext(); 49 | const streamOut = new events.EventEmitter(); 50 | const write1 = sinon.spy((buffer) => { 51 | assert(buffer instanceof Buffer); 52 | if (write1.callCount === 10) { 53 | context.suspend().then(() => { 54 | streamOut.write = write2; 55 | context.resume(); 56 | }); 57 | } 58 | return true; 59 | }); 60 | const write2 = sinon.spy((buffer) => { 61 | assert(buffer instanceof Buffer); 62 | assert(write1.callCount === 10); 63 | if (write2.callCount >= 10) { 64 | context.close().then(done); 65 | } 66 | return true; 67 | }); 68 | 69 | streamOut.write = write1; 70 | 71 | context.pipe(streamOut); 72 | context.resume(); 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/helpers/np.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function zeros(size) { 4 | return new Float32Array(size); 5 | } 6 | 7 | function full(size, value) { 8 | return new Float32Array(size).fill(value); 9 | } 10 | 11 | function random_sample(size) { 12 | return new Float32Array(size).map(Math.random); 13 | } 14 | 15 | module.exports = { zeros, full, random_sample }; 16 | -------------------------------------------------------------------------------- /test/impl/ConstantSourceNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const AudioContext = require("../../src/impl/AudioContext"); 7 | const ConstantSourceNode = require("../../src/impl/ConstantSourceNode"); 8 | const AudioParam = require("../../src/impl/AudioParam"); 9 | const AudioNode = require("../../src/impl/AudioNode"); 10 | 11 | describe("impl/ConstantSourceNode", () => { 12 | let context; 13 | 14 | beforeEach(() => { 15 | context = new AudioContext({ sampleRate: 8000, blockSize: 32 }); 16 | }); 17 | 18 | it("constructor", () => { 19 | const node = new ConstantSourceNode(context); 20 | 21 | assert(node instanceof ConstantSourceNode); 22 | assert(node instanceof AudioNode); 23 | }); 24 | 25 | describe("attributes", () => { 26 | it(".numberOfInputs", () => { 27 | const node = new ConstantSourceNode(context); 28 | 29 | assert(node.getNumberOfInputs() === 0); 30 | }); 31 | 32 | it(".numberOfOutputs", () => { 33 | const node = new ConstantSourceNode(context); 34 | 35 | assert(node.getNumberOfOutputs() === 1); 36 | }); 37 | 38 | it(".offset", () => { 39 | const node = new ConstantSourceNode(context); 40 | 41 | assert(node.getOffset() instanceof AudioParam); 42 | assert(node.getOffset().getValue() === 1); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/impl/PannerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const AudioContext = require("../../src/impl/AudioContext"); 7 | const PannerNode = require("../../src/impl/PannerNode"); 8 | const BasePannerNode = require("../../src/impl/BasePannerNode"); 9 | 10 | describe("impl/PannerNode", () => { 11 | let context; 12 | 13 | beforeEach(() => { 14 | context = new AudioContext({ sampleRate: 8000, blockSize: 32 }); 15 | }); 16 | 17 | it("constructor", () => { 18 | const node = new PannerNode(context); 19 | 20 | assert(node instanceof PannerNode); 21 | assert(node instanceof BasePannerNode); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/impl/PeriodicWave.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const AudioContext = require("../../src/impl/AudioContext"); 7 | const PeriodicWave = require("../../src/impl/PeriodicWave"); 8 | 9 | const real = new Float32Array([ 0, 0 ]); 10 | const imag = new Float32Array([ 0, 1 ]); 11 | 12 | describe("impl/PeriodicWave", () => { 13 | let context; 14 | 15 | beforeEach(() => { 16 | context = new AudioContext({ sampleRate: 8000, blockSize: 32 }); 17 | }); 18 | 19 | it("constructor", () => { 20 | const node = new PeriodicWave(context, { real, imag }); 21 | 22 | assert(node instanceof PeriodicWave); 23 | }); 24 | 25 | describe("attributes", () => { 26 | it(".constraints", () => { 27 | const node = new PeriodicWave(context, { real, imag }); 28 | 29 | assert(node.getConstraints() === false); 30 | }); 31 | 32 | it(".real", () => { 33 | const node = new PeriodicWave(context, { real, imag }); 34 | 35 | assert(node.getReal() === real); 36 | }); 37 | 38 | it(".imag", () => { 39 | const node = new PeriodicWave(context, { real, imag }); 40 | 41 | assert(node.getImag() === imag); 42 | }); 43 | }); 44 | 45 | describe("generate basic waveform", () => { 46 | const periodicWave = new PeriodicWave(context, { real: [ 0, 0 ], imag: [ 0, 1 ] }); 47 | 48 | [ 49 | { type: "sine", expected: "sine" }, 50 | { type: "sawtooth", expected: "sawtooth" }, 51 | { type: "triangle", expected: "triangle" }, 52 | { type: "square", expected: "square" }, 53 | { type: "unknown", expected: "custom" } 54 | ].forEach(({ type, expected }) => { 55 | it(type, () => { 56 | periodicWave.generateBasicWaveform(type); 57 | assert(periodicWave.getName() === expected); 58 | }); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/impl/SpatialPannerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const AudioContext = require("../../src/impl/AudioContext"); 7 | const SpatialPannerNode = require("../../src/impl/SpatialPannerNode"); 8 | const BasePannerNode = require("../../src/impl/BasePannerNode"); 9 | const AudioParam = require("../../src/impl/AudioParam"); 10 | 11 | describe("impl/SpatialPannerNode", () => { 12 | let context; 13 | 14 | beforeEach(() => { 15 | context = new AudioContext({ sampleRate: 8000, blockSize: 32 }); 16 | }); 17 | 18 | it("constructor", () => { 19 | const node = new SpatialPannerNode(context); 20 | 21 | assert(node instanceof SpatialPannerNode); 22 | assert(node instanceof BasePannerNode); 23 | }); 24 | 25 | describe("attributes", () => { 26 | it(".positionX", () => { 27 | const node = new SpatialPannerNode(context); 28 | 29 | assert(node.getPositionX() instanceof AudioParam); 30 | assert(node.getPositionX().getValue() === 0); 31 | }); 32 | 33 | it(".positionY", () => { 34 | const node = new SpatialPannerNode(context); 35 | 36 | assert(node.getPositionY() instanceof AudioParam); 37 | assert(node.getPositionY().getValue() === 0); 38 | }); 39 | 40 | it(".positionZ", () => { 41 | const node = new SpatialPannerNode(context); 42 | 43 | assert(node.getPositionZ() instanceof AudioParam); 44 | assert(node.getPositionZ().getValue() === 0); 45 | }); 46 | 47 | it(".orientationX", () => { 48 | const node = new SpatialPannerNode(context); 49 | 50 | assert(node.getOrientationX() instanceof AudioParam); 51 | assert(node.getOrientationX().getValue() === 0); 52 | }); 53 | 54 | it(".orientationY", () => { 55 | const node = new SpatialPannerNode(context); 56 | 57 | assert(node.getOrientationY() instanceof AudioParam); 58 | assert(node.getOrientationY().getValue() === 0); 59 | }); 60 | 61 | it(".orientationZ", () => { 62 | const node = new SpatialPannerNode(context); 63 | 64 | assert(node.getOrientationZ() instanceof AudioParam); 65 | assert(node.getOrientationZ().getValue() === 0); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/impl/StereoPannerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const AudioContext = require("../../src/impl/AudioContext"); 7 | const StereoPannerNode = require("../../src/impl/StereoPannerNode"); 8 | const BasePannerNode = require("../../src/impl/BasePannerNode"); 9 | const AudioParam = require("../../src/impl/AudioParam"); 10 | 11 | describe("impl/StereoPannerNode", () => { 12 | let context; 13 | 14 | beforeEach(() => { 15 | context = new AudioContext({ sampleRate: 8000, blockSize: 32 }); 16 | }); 17 | 18 | it("constructor", () => { 19 | const node = new StereoPannerNode(context); 20 | 21 | assert(node instanceof StereoPannerNode); 22 | assert(node instanceof BasePannerNode); 23 | }); 24 | 25 | describe("attributes", () => { 26 | it(".numberOfInputs", () => { 27 | const node = new StereoPannerNode(context); 28 | 29 | assert(node.getNumberOfInputs() === 1); 30 | }); 31 | 32 | it(".numberOfOutputs", () => { 33 | const node = new StereoPannerNode(context); 34 | 35 | assert(node.getNumberOfOutputs() === 1); 36 | }); 37 | 38 | it(".pan", () => { 39 | const node = new StereoPannerNode(context); 40 | 41 | assert(node.getPan() instanceof AudioParam); 42 | assert(node.getPan().getValue() === 0); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/impl/core/AudioData.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const np = require("../../helpers/np"); 7 | const AudioData = require("../../../src/impl/core/AudioData"); 8 | 9 | describe("impl/core/AudioData", () => { 10 | it("constructor(numberOfChannels, length, sampleRate)", () => { 11 | const data = new AudioData(2, 128, 44100); 12 | 13 | assert(data instanceof AudioData); 14 | }); 15 | 16 | describe("attributes", () => { 17 | it(".numberOfChannels", () => { 18 | const data = new AudioData(2, 128, 44100); 19 | 20 | assert(data.numberOfChannels === 2); 21 | }); 22 | 23 | it(".length", () => { 24 | const data = new AudioData(2, 128, 44100); 25 | 26 | assert(data.length === 128); 27 | }); 28 | 29 | it(".sampleRate", () => { 30 | const data = new AudioData(2, 128, 44100); 31 | 32 | assert(data.sampleRate === 44100); 33 | }); 34 | 35 | it(".channelData", () => { 36 | const data = new AudioData(2, 128, 44100); 37 | 38 | assert(data.channelData.length === 2); 39 | assert.deepEqual(data.channelData[0], np.zeros(128)); 40 | assert.deepEqual(data.channelData[1], np.zeros(128)); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/impl/dsp/AudioContext.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const AudioContext = require("../../../src/impl/AudioContext"); 8 | 9 | const contextOpts = { sampleRate: 8000, blockSize: 16 }; 10 | 11 | describe("impl/dsp/AudioContext", () => { 12 | let context, destination; 13 | 14 | before(() => { 15 | context = new AudioContext(contextOpts); 16 | destination = context.getDestination(); 17 | 18 | context.resume(); 19 | }); 20 | 21 | it("1: time advances", () => { 22 | const channelData = [new Float32Array(16), new Float32Array(16) ]; 23 | 24 | assert(context.getCurrentTime() === 0); 25 | destination.process = sinon.spy(); 26 | 27 | context.process(channelData, 0); 28 | 29 | assert(destination.process.callCount === 1); 30 | assert(destination.process.calledWith(channelData, 0)); 31 | assert(context.getCurrentTime() === 16 / 8000); 32 | }); 33 | 34 | it("2: do post process and reserve pre process (for next process)", () => { 35 | const channelData = [ new Float32Array(16), new Float32Array(16) ]; 36 | const immediateSpy = sinon.spy(); 37 | 38 | assert(context.getCurrentTime() === 16 / 8000); 39 | destination.process = sinon.spy(() => { 40 | context.addPostProcess(immediateSpy); 41 | }); 42 | 43 | context.process(channelData, 0); 44 | 45 | assert(destination.process.callCount === 1); 46 | assert(destination.process.calledWith(channelData, 0)); 47 | assert(context.getCurrentTime() === 32 / 8000); 48 | assert(immediateSpy.callCount === 1); 49 | assert(immediateSpy.calledAfter(destination.process)); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/impl/dsp/AudioDestinationNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const np = require("../../helpers/np"); 7 | const AudioContext = require("../../../src/impl/AudioContext"); 8 | const AudioDestinationNode = require("../../../src/impl/AudioDestinationNode"); 9 | const AudioNode = require("../../../src/impl/AudioNode"); 10 | 11 | describe("impl/dsp/AudioDestinationNode", () => { 12 | it("silent", () => { 13 | const channelData = [ new Float32Array(16), new Float32Array(16) ]; 14 | const context = new AudioContext({ sampleRate: 8000, blockSize: 16 }); 15 | const node1 = new AudioNode(context, {}, { outputs: [ 2 ] }); 16 | const node2 = new AudioDestinationNode(context, { numberOfChannels: 2 }); 17 | // const outputBus = node2.output.bus; 18 | 19 | node1.outputs[0].bus.zeros(); 20 | node1.enableOutputsIfNecessary(); 21 | node1.connect(node2); 22 | 23 | node2.process(channelData, 0); 24 | 25 | assert.deepEqual(channelData[0], np.zeros(16)); 26 | assert.deepEqual(channelData[1], np.zeros(16)); 27 | }); 28 | 29 | it("noise", () => { 30 | const channelData = [ new Float32Array(16), new Float32Array(16) ]; 31 | const context = new AudioContext({ sampleRate: 8000, blockSize: 16 }); 32 | const node1 = new AudioNode(context, {}, { outputs: [ 2 ] }); 33 | const node2 = new AudioDestinationNode(context, { numberOfChannels: 2 }); 34 | const noise1 = np.random_sample(16); 35 | const noise2 = np.random_sample(16); 36 | 37 | node1.outputs[0].bus.getMutableData()[0].set(noise1); 38 | node1.outputs[0].bus.getMutableData()[1].set(noise2); 39 | node1.enableOutputsIfNecessary(); 40 | node1.connect(node2); 41 | 42 | node2.process(channelData, 0); 43 | 44 | assert.deepEqual(channelData[0], noise1); 45 | assert.deepEqual(channelData[1], noise2); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/impl/dsp/AudioNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const AudioContext = require("../../../src/impl/AudioContext"); 8 | const GainNode = require("../../../src/impl/GainNode"); 9 | 10 | const context = new AudioContext({ sampleRate: 8000, blockSize: 16 }); 11 | 12 | describe("impl/dsp/AudioNode", () => { 13 | it("propagation", () => { 14 | const node1 = new GainNode(context); 15 | const node2 = new GainNode(context); 16 | const node3 = new GainNode(context); 17 | const param = node1.getGain(); 18 | 19 | node1.outputs[0].enable(); 20 | node2.outputs[0].enable(); 21 | node1.connect(node2); 22 | node2.connect(node3); 23 | 24 | node1.dspProcess = sinon.spy(); 25 | param.dspProcess = sinon.spy(); 26 | 27 | node3.processIfNecessary(); 28 | 29 | assert(param.dspProcess.callCount === 1); 30 | }); 31 | 32 | it("feedback loop", () => { 33 | const node1 = new GainNode(context); 34 | const node2 = new GainNode(context); 35 | const node3 = new GainNode(context); 36 | 37 | node1.outputs[0].enable(); 38 | node2.outputs[0].enable(); 39 | node1.connect(node2); 40 | node2.connect(node3); 41 | node3.connect(node1); 42 | 43 | node1.dspProcess = sinon.spy(); 44 | 45 | node3.processIfNecessary(); 46 | 47 | assert(node1.dspProcess.callCount === 1); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/impl/dsp/ChannelMergerNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const np = require("../../helpers/np"); 7 | const AudioContext = require("../../../src/impl/AudioContext"); 8 | const ChannelMergerNode = require("../../../src/impl/ChannelMergerNode"); 9 | const AudioNode = require("../../../src/impl/AudioNode"); 10 | 11 | describe("impl/dsp/ChannelMergerNode", () => { 12 | it("works", () => { 13 | const channelData = [ new Float32Array(16), new Float32Array(16) ]; 14 | const context = new AudioContext({ sampleRate: 8000, blockSize: 16 }); 15 | const node1 = new AudioNode(context, {}, { inputs: [ 1 ], outputs: [ 1 ] }); 16 | const node2 = new AudioNode(context, {}, { inputs: [ 1 ], outputs: [ 1 ] }); 17 | const node3 = new ChannelMergerNode(context, { numberOfInputs: 4 }); 18 | const noise1 = np.random_sample(16); 19 | const noise2 = np.random_sample(16); 20 | 21 | context.resume(); 22 | node1.connect(node3, 0, 0); 23 | node2.connect(node3, 0, 1); 24 | node3.connect(context.getDestination()); 25 | node1.enableOutputsIfNecessary(); 26 | node2.enableOutputsIfNecessary(); 27 | node1.outputs[0].bus.getMutableData()[0].set(noise1); 28 | node2.outputs[0].bus.getMutableData()[0].set(noise2); 29 | 30 | context.process(channelData, 0); 31 | 32 | const actual = node3.outputs[0].bus.getChannelData(); 33 | const expected = [ noise1, noise2, np.zeros(16), np.zeros(16) ]; 34 | 35 | assert.deepEqual(actual, expected); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/impl/dsp/IIRFilterNode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const AudioContext = require("../../../src/impl/AudioContext"); 7 | const IIRFilterNode = require("../../../src/impl/IIRFilterNode"); 8 | 9 | function closeTo(a, b, delta) { 10 | return Math.abs(a - b) <= delta; 11 | } 12 | 13 | describe("impl/dsp/IIRFilterNode", () => { 14 | describe("getFrequencyResponse(frequencyHz, magResponse, phaseResponse)", () => { 15 | it("works", () => { 16 | const context = new AudioContext({ sampleRate: 44100, blockSize: 16 }); 17 | const node = new IIRFilterNode(context, { 18 | feedforward: [ 0.135784, 0.000000, -0.135784 ], 19 | feedback: [ 1, -1.854196, 0.932108 ], 20 | }); 21 | const frequencyHz = new Float32Array([ 1000, 2000, 3000 ]); 22 | const magResponse = new Float32Array(3); 23 | const phaseResponse = new Float32Array(3); 24 | 25 | node.getFrequencyResponse(frequencyHz, magResponse, phaseResponse); 26 | 27 | // computed using Chrome 54 28 | // a=a||new AudioContext(); b=a.createIIRFilter([ 0.135784, 0.000000, -0.135784 ], [ 1, -1.854196, 0.932108 ]); c=new Float32Array([ 1000, 2000, 3000 ]); d=new Float32Array(3); e=new Float32Array(3); b.getFrequencyResponse(c, d, e); console.log(d); console.log(e); 29 | const magExpected = new Float32Array([ 0.6521847248077393, 4, 1.1262547969818115 ]); 30 | const phaseExpected = new Float32Array([ 1.4070188999176025, 0.00000937021650315728, -1.2853729724884033 ]); 31 | 32 | assert(magResponse.every((mag, i) => closeTo(mag, magExpected[i], 1e-6))) 33 | assert(phaseResponse.every((phase, i) => closeTo(phase, phaseExpected[i], 1e-6))) 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --recursive 2 | --require babel-register 3 | -------------------------------------------------------------------------------- /test/utils/EncoderUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const sinon = require("sinon"); 7 | const EncoderUtils = require("../../src/utils/EncoderUtils"); 8 | 9 | describe("utils/EncoderUtils.encode(encodeFn: function, audioData: AudioData, opts?: object): Promise", () => { 10 | it("should return promise and resolve - from AudioData", () => { 11 | const source = new Uint8Array(128); 12 | const sampleRate = 44100; 13 | const channelData = [ new Float32Array(128), new Float32Array(128) ]; 14 | const audioData = { sampleRate, channelData }; 15 | const opts = {}; 16 | const encodeFn = sinon.spy(() => { 17 | return Promise.resolve(source.buffer); 18 | }); 19 | 20 | return EncoderUtils.encode(encodeFn, audioData, opts).then((arrayBuffer) => { 21 | assert(encodeFn.callCount === 1); 22 | assert(encodeFn.calledWith(audioData, opts)); 23 | assert(arrayBuffer instanceof ArrayBuffer); 24 | }); 25 | }); 26 | 27 | it("should return peomise and resolve - from AudioBuffer", () => { 28 | const source = new Uint8Array(128); 29 | const numberOfChannels = 2; 30 | const sampleRate = 44100; 31 | const channelData = [ new Float32Array(128), new Float32Array(128) ]; 32 | const audioData = { numberOfChannels, sampleRate, getChannelData(ch) { return channelData[ch]; } }; 33 | const opts = {}; 34 | const encodeFn = sinon.spy(() => { 35 | return Promise.resolve(source.buffer); 36 | }); 37 | 38 | return EncoderUtils.encode(encodeFn, audioData, opts).then((arrayBuffer) => { 39 | assert(encodeFn.callCount === 1); 40 | 41 | const _audioData = encodeFn.args[0][0]; 42 | 43 | assert(_audioData.numberOfChannels === numberOfChannels); 44 | assert(_audioData.length === 128); 45 | assert(_audioData.sampleRate === 44100); 46 | assert(_audioData.channelData[0] === channelData[0]); 47 | assert(_audioData.channelData[1] === channelData[1]); 48 | 49 | assert(arrayBuffer instanceof ArrayBuffer); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/utils/setImmediate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const setImmediate = require("../../src/utils/setImmediate"); 6 | 7 | describe("utils/setImmediate(fn)", () => { 8 | it("works", (done) => { 9 | setImmediate(done); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/utils/utils/clamp.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const clamp = require("../../../src/utils/utils/clamp"); 7 | 8 | describe("utils/clamp(value, minValue, maxValue)", () => { 9 | it("return clamped value in the range [minValue, maxValue]", () => { 10 | assert(clamp(0, 2, 4) === 2); 11 | assert(clamp(1, 2, 4) === 2); 12 | assert(clamp(2, 2, 4) === 2); 13 | assert(clamp(3, 2, 4) === 3); 14 | assert(clamp(4, 2, 4) === 4); 15 | assert(clamp(5, 2, 4) === 4); 16 | assert(clamp(6, 2, 4) === 4); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/utils/utils/defaults.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const defaults = require("../../../src/utils/utils/defaults"); 7 | 8 | describe("utils/defaults(value, defaultValue)", () => { 9 | it("works", () => { 10 | assert(defaults(0, 1) === 0); 11 | assert(defaults(null, 1) === null); 12 | assert(defaults(undefined, 1) === 1); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/utils/utils/defineProp.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const defineProp = require("../../../src/utils/utils/defineProp"); 7 | 8 | describe("utils/defineProp", () => { 9 | it("define property", () => { 10 | const a = {}; 11 | 12 | defineProp(a, "value", 100); 13 | 14 | assert(a.value === 100); 15 | }); 16 | 17 | it("not enumerable", () => { 18 | const a = {}; 19 | 20 | defineProp(a, "value", 100); 21 | 22 | assert(Object.keys(a).length === 0); 23 | }); 24 | 25 | it("writable", () => { 26 | const a = {}; 27 | 28 | defineProp(a, "value", 100); 29 | 30 | a.value = 200; 31 | assert(a.value === 200); 32 | }); 33 | 34 | it("configurable", () => { 35 | const a = {}; 36 | 37 | defineProp(a, "value", 100); 38 | defineProp(a, "value", 300); 39 | 40 | assert(a.value === 300); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/utils/utils/fill.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const fill = require("../../../src/utils/utils/fill"); 7 | 8 | describe("utils/fill(list, value)", () => { 9 | it("fill value", () => { 10 | const list = new Float32Array(8); 11 | const actual = fill(list, 1); 12 | const expected = new Float32Array([ 1, 1, 1, 1, 1, 1, 1, 1 ]); 13 | 14 | assert.deepEqual(actual, expected); 15 | assert.deepEqual(list, expected); 16 | }); 17 | 18 | it("fill value - polyfill ver", () => { 19 | const list = new Float32Array(8); 20 | 21 | // kill native function 22 | Object.defineProperty(list, "fill", { value: null }); 23 | 24 | const actual = fill(list, 1); 25 | const expected = new Float32Array([ 1, 1, 1, 1, 1, 1, 1, 1 ]); 26 | 27 | assert.deepEqual(actual, expected); 28 | assert.deepEqual(list, expected); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/utils/utils/fillRange.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const fillRange = require("../../../src/utils/utils/fillRange"); 7 | 8 | describe("utils/fillRange(list, value, start, end)", () => { 9 | it("fill value", () => { 10 | const list = new Float32Array(8); 11 | const actual = fillRange(list, 1, 2, 6); 12 | const expected = new Float32Array([ 0, 0, 1, 1, 1, 1, 0, 0 ]); 13 | 14 | assert.deepEqual(actual, expected); 15 | assert.deepEqual(list, expected); 16 | }); 17 | 18 | it("fill value - polyfill ver", () => { 19 | const list = new Float32Array(8); 20 | 21 | // kill native function 22 | Object.defineProperty(list, "fill", { value: null }); 23 | 24 | const actual = fillRange(list, 1, 2, 6); 25 | const expected = new Float32Array([ 0, 0, 1, 1, 1, 1, 0, 0 ]); 26 | 27 | assert.deepEqual(actual, expected); 28 | assert.deepEqual(list, expected); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/utils/utils/normalize.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const normalize = require("../../../src/utils/utils/normalize"); 7 | 8 | describe("utils/normalize(val, min, max)", () => { 9 | it("normalize a value to something between 0 - 1", () => { 10 | assert(normalize(0, -100, 100) === 0.5); 11 | assert(normalize(-100, -100, 100) === 0); 12 | assert(normalize(-200, -100, 100) === 0); 13 | assert(normalize(100, -100, 100) === 1); 14 | assert(normalize(200, -100, 100) === 1); 15 | assert(normalize(50, -100, 100) === 0.75); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/utils/utils/toArrayIfNeeded.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const toArrayIfNeeded = require("../../../src/utils/utils/toArrayIfNeeded"); 7 | 8 | describe("utils/toArrayIfNeeded(value)", () => { 9 | it("convert to array if not array", () => { 10 | const value = 1; 11 | const actual = toArrayIfNeeded(value); 12 | const expected = [ value ]; 13 | 14 | assert.deepEqual(actual, expected); 15 | }); 16 | 17 | it("nothing to do if array", () => { 18 | const value = [ 1 ]; 19 | const actual = toArrayIfNeeded(value); 20 | const expected = value; 21 | 22 | assert(actual === expected); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/utils/utils/toAudioTime.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const toAudioTime = require("../../../src/utils/utils/toAudioTime"); 7 | 8 | describe("utils/toAudioTime()", () => { 9 | it("return the provided value when provide positive number", () => { 10 | assert(toAudioTime(10) === 10); 11 | }); 12 | 13 | it("return 0 when provide a negative number", () => { 14 | assert(toAudioTime(-1) === 0); 15 | }); 16 | 17 | it("return 0 when provided infinite number", () => { 18 | assert(toAudioTime(Infinity) === 0); 19 | }); 20 | 21 | it("convert to number when provided format of 'ss.SSS'", () => { 22 | assert(toAudioTime("23.456") === 23.456); 23 | }); 24 | 25 | it("convert to number when provided format of 'mm:ss.SSS'", () => { 26 | assert(toAudioTime("01:23.456") === 83.456); 27 | }); 28 | 29 | it("convert to number when provided format of 'hh:mm:ss.SSS'", () => { 30 | assert(toAudioTime("00:01:23.456") === 83.456); 31 | }); 32 | 33 | it("return 0 when provided case", () => { 34 | assert(toAudioTime("UNKNOWN") === 0); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/utils/utils/toDecibel.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const toDecibel = require("../../../src/utils/utils/toDecibel"); 7 | 8 | describe("utils/toDecibel(gainValue)", () => { 9 | it("convert gainValue to decibel", () => { 10 | assert(Math.round(toDecibel(3.162)) === 10); 11 | assert(Math.round(toDecibel(1.995)) === 6); 12 | assert(Math.round(toDecibel(1.413)) === 3); 13 | assert(Math.round(toDecibel(1.122)) === 1); 14 | assert(Math.round(toDecibel(1.000)) === 0); 15 | assert(Math.round(toDecibel(0.891)) === -1); 16 | assert(Math.round(toDecibel(0.708)) === -3); 17 | assert(Math.round(toDecibel(0.501)) === -6); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/utils/utils/toGain.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const toGain = require("../../../src/utils/utils/toGain"); 7 | 8 | function closeTo(a, b, delta) { 9 | return Math.abs(a - b) <= delta; 10 | } 11 | 12 | describe("utils/toGain(decibel)", () => { 13 | it("convert decibel to gain", () => { 14 | assert(closeTo(toGain(10), 3.1622776985168457, 1e-6)); 15 | assert(closeTo(toGain( 6), 1.9952622652053833, 1e-6)); 16 | assert(closeTo(toGain( 3), 1.4125375747680664, 1e-6)); 17 | assert(closeTo(toGain( 1), 1.1220184564590454, 1e-6)); 18 | assert(toGain(0) === 1); 19 | assert(closeTo(toGain(-1), 0.8912509083747864, 1e-6)); 20 | assert(closeTo(toGain(-3), 0.7079457640647888, 1e-6)); 21 | assert(closeTo(toGain(-6), 0.5011872053146362, 1e-6)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/utils/utils/toImpl.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const toImpl = require("../../../src/utils/utils/toImpl"); 7 | 8 | describe("utils/toImpl(value)", () => { 9 | it("convert to impl", () => { 10 | const impl = {}; 11 | const value = { _impl: impl }; 12 | const actual = toImpl(value); 13 | const expected = impl; 14 | 15 | assert(actual === expected); 16 | }); 17 | 18 | it("nothing to do", () => { 19 | const impl = {}; 20 | const actual = toImpl(impl); 21 | const expected = impl; 22 | 23 | assert(actual === expected); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/utils/utils/toNumber.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const toNumber = require("../../../src/utils/utils/toNumber"); 7 | 8 | describe("utils/toNumber(value)", () => { 9 | it("convert to number", () => { 10 | assert(toNumber(1) === 1); 11 | assert(toNumber(Infinity) === Infinity); 12 | assert(toNumber("1") === 1); 13 | assert(toNumber(NaN) === 0); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/utils/utils/toPowerOfTwo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const toPowerOfTwo = require("../../../src/utils/utils/toPowerOfTwo"); 7 | 8 | describe("utils/toPowerOfTwo(value)", () => { 9 | it("convert to 2^n", () => { 10 | assert(toPowerOfTwo(1) === 1); 11 | assert(toPowerOfTwo(2) === 2); 12 | assert(toPowerOfTwo(3) === 4); 13 | assert(toPowerOfTwo(3, Math.floor) === 2); 14 | assert(toPowerOfTwo(3, Math.ceil) === 4); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/utils/utils/toValidBitDepth.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const toValidBitDepth = require("../../../src/utils/utils/toValidBitDepth"); 7 | 8 | describe("utils/toValidBitDepth()", () => { 9 | it("return valid bit depth", () => { 10 | assert(toValidBitDepth(8) === 8); 11 | assert(toValidBitDepth(16) === 16); 12 | assert(toValidBitDepth(32) === 32); 13 | }); 14 | 15 | it("return the default bit depth 16 when provided an invalid bit depth", () => { 16 | assert(toValidBitDepth(0) === 16); 17 | assert(toValidBitDepth(NaN) === 16); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/utils/utils/toValidBlockSize.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const toValidBlockSize = require("../../../src/utils/utils/toValidBlockSize"); 7 | 8 | describe("utils/toValidBlockSize()", () => { 9 | it("return valid block size", () => { 10 | assert(toValidBlockSize(0) === 8); 11 | assert(toValidBlockSize(8) === 8); 12 | assert(toValidBlockSize(128) === 128); 13 | assert(toValidBlockSize(500) === 512); 14 | assert(toValidBlockSize(2000) === 1024); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/utils/utils/toValidNumberOfChannels.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const toValidNumberOfChannels = require("../../../src/utils/utils/toValidNumberOfChannels"); 7 | 8 | describe("utils/toValidNumberOfChannels()", () => { 9 | it("return valid number of channels", () => { 10 | assert(toValidNumberOfChannels(0) === 1); 11 | assert(toValidNumberOfChannels(2) === 2); 12 | assert(toValidNumberOfChannels(8) === 8); 13 | assert(toValidNumberOfChannels(2000) === 32); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/utils/utils/toValidSampleRate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | require("run-with-mocha"); 4 | 5 | const assert = require("assert"); 6 | const toValidSampleRate = require("../../../src/utils/utils/toValidSampleRate"); 7 | 8 | describe("utils/toValidSampleRate()", () => { 9 | it("return valid sampleRate", () => { 10 | assert(toValidSampleRate(0) === 3000); 11 | assert(toValidSampleRate(5512.5) === 5512); 12 | assert(toValidSampleRate(44100) === 44100); 13 | assert(toValidSampleRate(48000) === 48000); 14 | assert(toValidSampleRate(Infinity) === 192000); 15 | }); 16 | }); 17 | --------------------------------------------------------------------------------