├── .eslintrc.js
├── .gitattributes
├── .github
└── workflows
│ ├── nodejs.yml
│ └── npmpublish.yml
├── .gitignore
├── .idea
├── .gitignore
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── modules.xml
├── prettier.xml
├── vcs.xml
└── web-audio-js.iml
├── .prettierrc
├── .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
├── 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
├── rollup.config.js
├── src
├── __tests__
│ ├── .eslintrc.json
│ ├── api
│ │ ├── AnalyserNode.js
│ │ ├── AudioBuffer.js
│ │ ├── AudioBufferSourceNode.js
│ │ ├── AudioDestinationNode.js
│ │ ├── AudioListener.js
│ │ ├── AudioNode.js
│ │ ├── AudioParam.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
│ │ ├── ScriptProcessorNode.js
│ │ ├── SpatialPannerNode.js
│ │ ├── StereoPannerNode.js
│ │ └── WaveShaperNode.js
│ ├── context
│ │ ├── OfflineAudioContext.js
│ │ ├── RenderingAudioContext.js
│ │ └── StreamAudioContext.js
│ ├── decoder.js
│ ├── encoder.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
│ │ │ ├── DynamicsCompressorNode.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
├── __tests_helpers
│ ├── DynamicsCompressorData.js
│ ├── np.js
│ └── paramTester.js
├── api
│ ├── AnalyserNode.js
│ ├── AudioBuffer.js
│ ├── AudioBufferSourceNode.js
│ ├── AudioDestinationNode.js
│ ├── AudioListener.js
│ ├── AudioNode.js
│ ├── AudioParam.js
│ ├── AudioScheduledSourceNode.js
│ ├── AudioWorkletNode.ts
│ ├── AudioWorkletProcessor.ts
│ ├── 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
│ ├── RawDataAudioContext.ts
│ ├── 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
│ ├── AudioWorklet.ts
│ ├── AudioWorkletNode.ts
│ ├── AudioWorkletPort.ts
│ ├── AudioWorkletProcessor.ts
│ ├── 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
│ │ ├── DynamicsCompressor.js
│ │ ├── DynamicsCompressorKernel.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
│ ├── Encoder.ts
│ ├── EncoderUtils.js
│ ├── FilterUtils.js
│ ├── Format.ts
│ ├── PCMArrayBufferWriter.js
│ ├── PCMBufferWriter.js
│ ├── PCMEncoder.js
│ ├── index.js
│ ├── nmap.ts
│ ├── setImmediate.js
│ └── utils
│ ├── clamp.js
│ ├── defaults.js
│ ├── defineProp.js
│ ├── fill.js
│ ├── fillRange.js
│ ├── flushDenormalFloatToZero.js
│ ├── normalize.js
│ ├── toArrayIfNeeded.js
│ ├── toAudioTime.js
│ ├── toDecibel.js
│ ├── toGain.js
│ ├── toImpl.js
│ ├── toLinear.js
│ ├── toNumber.js
│ ├── toPowerOfTwo.js
│ ├── toValidBitDepth.js
│ ├── toValidBlockSize.js
│ ├── toValidNumberOfChannels.js
│ └── toValidSampleRate.js
├── tsconfig.json
└── yarn.lock
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | 'plugin:@typescript-eslint/recommended',
4 | 'prettier',
5 | 'prettier/@typescript-eslint',
6 | ],
7 | rules: {
8 | '@typescript-eslint/no-inferrable-types': 'off',
9 | '@typescript-eslint/no-use-before-define': 'off',
10 | '@typescript-eslint/no-parameter-properties': 'off',
11 | '@typescript-eslint/explicit-function-return-type': 'off',
12 | '@typescript-eslint/no-var-requires': 'off',
13 | '@typescript-eslint/no-empty-function': 'warn',
14 | '@typescript-eslint/camelcase': 'warn',
15 | '@typescript-eslint/no-this-alias': 'warn',
16 | 'prefer-rest-params': 'warn',
17 | 'prefer-spread': 'warn',
18 | 'prefer-const': 'warn',
19 | 'no-var': 'warn',
20 | },
21 | };
22 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.wav binary
2 | build/*.js -diff
3 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | name: Node CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - '**'
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | strategy:
12 | matrix:
13 | node-version: [12.x, 14.x, 16.x]
14 | env:
15 | CI: true
16 | steps:
17 | - uses: actions/checkout@v1
18 | - name: Use Node.js ${{ matrix.node-version }}
19 | uses: actions/setup-node@v1
20 | with:
21 | node-version: ${{ matrix.node-version }}
22 | - run: yarn install --frozen-lockfile
23 | - run: yarn lint
24 | - run: yarn test-ci
25 | - run: yarn build
26 |
--------------------------------------------------------------------------------
/.github/workflows/npmpublish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to NPM
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | jobs:
9 | publish-npm:
10 | runs-on: ubuntu-latest
11 | env:
12 | CI: true
13 | steps:
14 | - uses: actions/checkout@v1
15 | - uses: actions/setup-node@v1
16 | with:
17 | node-version: 12
18 | registry-url: https://registry.npmjs.org/
19 | - run: yarn install --frozen-lockfile
20 | - run: yarn run lint
21 | - run: yarn run test-ci
22 | - run: yarn run build
23 | - run: npm publish --access public
24 | env:
25 | NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
26 |
--------------------------------------------------------------------------------
/.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 | build/
10 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/prettier.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/web-audio-js.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "singleQuote": true,
4 | "arrowParens": "always"
5 | }
6 |
--------------------------------------------------------------------------------
/.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/descriptinc/web-audio-js/12f6da53aadc6166f2e3179e6b014c9dda859d00/demo/assets/sound/a11wlk01.wav
--------------------------------------------------------------------------------
/demo/assets/sound/amen.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/descriptinc/web-audio-js/12f6da53aadc6166f2e3179e6b014c9dda859d00/demo/assets/sound/amen.wav
--------------------------------------------------------------------------------
/demo/assets/sound/hihat1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/descriptinc/web-audio-js/12f6da53aadc6166f2e3179e6b014c9dda859d00/demo/assets/sound/hihat1.wav
--------------------------------------------------------------------------------
/demo/assets/sound/hihat2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/descriptinc/web-audio-js/12f6da53aadc6166f2e3179e6b014c9dda859d00/demo/assets/sound/hihat2.wav
--------------------------------------------------------------------------------
/demo/assets/sound/kick.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/descriptinc/web-audio-js/12f6da53aadc6166f2e3179e6b014c9dda859d00/demo/assets/sound/kick.wav
--------------------------------------------------------------------------------
/demo/assets/sound/snare.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/descriptinc/web-audio-js/12f6da53aadc6166f2e3179e6b014c9dda859d00/demo/assets/sound/snare.wav
--------------------------------------------------------------------------------
/demo/assets/sound/tom1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/descriptinc/web-audio-js/12f6da53aadc6166f2e3179e6b014c9dda859d00/demo/assets/sound/tom1.wav
--------------------------------------------------------------------------------
/demo/assets/sound/tom2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/descriptinc/web-audio-js/12f6da53aadc6166f2e3179e6b014c9dda859d00/demo/assets/sound/tom2.wav
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | // rollup.config.js
2 | import pkg from './package.json';
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import resolve from '@rollup/plugin-node-resolve';
5 | import typescript from 'rollup-plugin-typescript2';
6 |
7 | export default {
8 | input: 'src',
9 | plugins: [
10 | commonjs(),
11 | resolve({ preferBuiltins: true }),
12 | typescript({ exclude: '**/*.test.ts' }),
13 | ],
14 | external: [
15 | 'assert',
16 | 'events',
17 | 'worker_threads',
18 | ...Object.keys(pkg.dependencies),
19 | ],
20 | output: [
21 | {
22 | sourcemap: true,
23 | sourcemapExcludeSources: true,
24 | file: pkg.main,
25 | format: 'cjs',
26 | },
27 | {
28 | sourcemap: true,
29 | sourcemapExcludeSources: true,
30 | file: pkg.module,
31 | format: 'esm',
32 | },
33 | ],
34 | };
35 |
--------------------------------------------------------------------------------
/src/__tests__/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/__tests__/api/AudioDestinationNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 |
6 | describe('api/AudioDestinationNode', () => {
7 | it('context.destination', () => {
8 | const context = new AudioContext();
9 | const target = context.destination;
10 |
11 | expect(target instanceof api.AudioDestinationNode).toBeTruthy();
12 | });
13 |
14 | describe('attributes', () => {
15 | it('.maxChannelCount', () => {
16 | const context = new AudioContext();
17 | const target = context.destination;
18 | const maxChannelCount = 2;
19 |
20 | target._impl.getMaxChannelCount = jest.fn(() => maxChannelCount);
21 |
22 | expect(target.maxChannelCount).toBe(maxChannelCount);
23 | expect(target._impl.getMaxChannelCount).toHaveBeenCalledTimes(1);
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/src/__tests__/api/AudioListener.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 |
6 | describe('api/AudioListener', () => {
7 | it('context.listener', () => {
8 | const context = new AudioContext();
9 | const target = context.listener;
10 |
11 | expect(target instanceof api.AudioListener).toBeTruthy();
12 | });
13 |
14 | describe('methods', () => {
15 | it('.setPosition(x, y, z)', () => {
16 | const context = new AudioContext();
17 | const target = context.listener;
18 | const x = 0;
19 | const y = 1;
20 | const z = 2;
21 |
22 | target._impl.setPosition = jest.fn();
23 |
24 | target.setPosition(x, y, z);
25 | expect(target._impl.setPosition).toHaveBeenCalledTimes(1);
26 | expect(target._impl.setPosition.mock.calls[0][0]).toBe(x);
27 | expect(target._impl.setPosition.mock.calls[0][1]).toBe(y);
28 | expect(target._impl.setPosition.mock.calls[0][2]).toBe(z);
29 | });
30 |
31 | it('.setOrientation(x, y, z, xUp, yUp, zUp)', () => {
32 | const context = new AudioContext();
33 | const target = context.listener;
34 | const x = 0;
35 | const y = 1;
36 | const z = 2;
37 | const xUp = 3;
38 | const yUp = 4;
39 | const zUp = 5;
40 |
41 | target._impl.setOrientation = jest.fn();
42 |
43 | target.setOrientation(x, y, z, xUp, yUp, zUp);
44 | expect(target._impl.setOrientation).toHaveBeenCalledTimes(1);
45 | expect(target._impl.setOrientation.mock.calls[0][0]).toBe(x);
46 | expect(target._impl.setOrientation.mock.calls[0][1]).toBe(y);
47 | expect(target._impl.setOrientation.mock.calls[0][2]).toBe(z);
48 | expect(target._impl.setOrientation.mock.calls[0][3]).toBe(xUp);
49 | expect(target._impl.setOrientation.mock.calls[0][4]).toBe(yUp);
50 | expect(target._impl.setOrientation.mock.calls[0][5]).toBe(zUp);
51 | });
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/src/__tests__/api/ChannelMergerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 |
6 | describe('api/ChannelMergerNode', () => {
7 | it('context.createChannelMerger(numberOfInputs)', () => {
8 | const context = new AudioContext();
9 | const target = context.createChannelMerger(2);
10 |
11 | expect(target instanceof api.ChannelMergerNode).toBeTruthy();
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/__tests__/api/ChannelSplitterNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 |
6 | describe('api/ChannelSplitterNode', () => {
7 | it('context.createChannelSplitter(numberOfOutputs)', () => {
8 | const context = new AudioContext();
9 | const target = context.createChannelSplitter(2);
10 |
11 | expect(target instanceof api.ChannelSplitterNode).toBeTruthy();
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/__tests__/api/ConstantSourceNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 | import AudioParam from '../../api/AudioParam';
6 |
7 | describe('api/ConstantSourceNode', () => {
8 | it('context.createConstantSource()', () => {
9 | const context = new AudioContext();
10 | const target = context.createConstantSource();
11 |
12 | expect(target instanceof api.ConstantSourceNode).toBeTruthy();
13 | expect(target instanceof api.AudioScheduledSourceNode).toBeTruthy();
14 | });
15 |
16 | describe('attributes', () => {
17 | it('.offset=', () => {
18 | const context = new AudioContext();
19 | const target = context.createConstantSource();
20 |
21 | expect(target.offset instanceof AudioParam).toBeTruthy();
22 | expect(target.offset).toBe(target._impl.$offset);
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/__tests__/api/ConvolverNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 |
6 | describe('api/ConvolverNode', () => {
7 | it('context.createConvolver()', () => {
8 | const context = new AudioContext();
9 | const target = context.createConvolver();
10 |
11 | expect(target instanceof api.ConvolverNode).toBeTruthy();
12 | });
13 |
14 | describe('attributes', () => {
15 | it('.buffer=', () => {
16 | const context = new AudioContext();
17 | const target = context.createConvolver();
18 | const buffer = context.createBuffer(1, 16, 8000);
19 |
20 | target._impl.setBuffer = jest.fn();
21 |
22 | target.buffer = buffer;
23 | expect(target.buffer).toBe(buffer);
24 | expect(target._impl.setBuffer).toHaveBeenCalledTimes(1);
25 | expect(target._impl.setBuffer.mock.calls[0][0]).toBe(buffer);
26 | });
27 |
28 | it('.normalize=', () => {
29 | const context = new AudioContext();
30 | const target = context.createConvolver();
31 | const normalize1 = true;
32 | const normalize2 = false;
33 |
34 | target._impl.getNormalize = jest.fn(() => normalize1);
35 | target._impl.setNormalize = jest.fn();
36 |
37 | expect(target.normalize).toBe(normalize1);
38 | expect(target._impl.getNormalize).toHaveBeenCalledTimes(1);
39 |
40 | target.normalize = normalize2;
41 | expect(target._impl.setNormalize).toHaveBeenCalledTimes(1);
42 | expect(target._impl.setNormalize.mock.calls[0][0]).toBe(normalize2);
43 | });
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/src/__tests__/api/DelayNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 | import AudioParam from '../../api/AudioParam';
6 |
7 | describe('api/DelayNode', () => {
8 | it('context.createDelay(maxDelayTime)', () => {
9 | const context = new AudioContext();
10 | const target = context.createDelay(1);
11 |
12 | expect(target instanceof api.DelayNode).toBeTruthy();
13 | });
14 |
15 | describe('attributes', () => {
16 | it('.delayTime', () => {
17 | const context = new AudioContext();
18 | const target = context.createDelay();
19 |
20 | expect(target.delayTime instanceof AudioParam).toBeTruthy();
21 | expect(target.delayTime).toBe(target._impl.$delayTime);
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/__tests__/api/EventTarget.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioContext from '../../api/BaseAudioContext';
4 |
5 | describe('api/EventTarget', () => {
6 | describe('methods', () => {
7 | it('.addEventListener(type, listener)', () => {
8 | const context = new AudioContext();
9 | const target = context.createOscillator();
10 | const type = 'ended';
11 | const listener = () => {};
12 |
13 | target._impl.addEventListener = jest.fn();
14 |
15 | target.addEventListener(type, listener);
16 | expect(target._impl.addEventListener).toHaveBeenCalledTimes(1);
17 | expect(target._impl.addEventListener.mock.calls[0][0]).toBe(type);
18 | expect(target._impl.addEventListener.mock.calls[0][1]).toBe(listener);
19 | });
20 |
21 | it('.removeEventListener()', () => {
22 | const context = new AudioContext();
23 | const target = context.createOscillator();
24 | const type = 'ended';
25 | const listener = jest.fn();
26 |
27 | target._impl.removeEventListener = jest.fn();
28 |
29 | target.removeEventListener(type, listener);
30 | expect(target._impl.removeEventListener).toHaveBeenCalledTimes(1);
31 | expect(target._impl.removeEventListener.mock.calls[0][0]).toBe(type);
32 | expect(target._impl.removeEventListener.mock.calls[0][1]).toBe(listener);
33 | });
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/__tests__/api/GainNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 | import AudioParam from '../../api/AudioParam';
6 |
7 | describe('api/GainNode', () => {
8 | it('context.createGain()', () => {
9 | const context = new AudioContext();
10 | const target = context.createGain();
11 |
12 | expect(target instanceof api.GainNode).toBeTruthy();
13 | });
14 |
15 | describe('attributes', () => {
16 | it('.gain', () => {
17 | const context = new AudioContext();
18 | const target = context.createGain();
19 |
20 | expect(target.gain instanceof AudioParam).toBeTruthy();
21 | expect(target.gain).toBe(target._impl.$gain);
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/__tests__/api/IIRFilterNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 |
6 | describe('api/IIRFilterNode', () => {
7 | it('context.createIIRFilter(feedforward, feedback)', () => {
8 | const context = new AudioContext();
9 | const feedforward = new Float32Array(4);
10 | const feedback = new Float32Array(4);
11 | const target = context.createIIRFilter(feedforward, feedback);
12 |
13 | expect(target instanceof api.IIRFilterNode).toBeTruthy();
14 | });
15 |
16 | describe('methods', () => {
17 | it('.getFrequencyResponse(frequencyHz, magResponse, phaseResponse)', () => {
18 | const context = new AudioContext();
19 | const feedforward = new Float32Array(4);
20 | const feedback = new Float32Array(4);
21 | const target = context.createIIRFilter(feedforward, feedback);
22 | const frequencyHz = new Float32Array(128);
23 | const magResponse = new Float32Array(128);
24 | const phaseResponse = new Float32Array(128);
25 |
26 | target._impl.getFrequencyResponse = jest.fn();
27 |
28 | target.getFrequencyResponse(frequencyHz, magResponse, phaseResponse);
29 | expect(target._impl.getFrequencyResponse).toHaveBeenCalledTimes(1);
30 | expect(target._impl.getFrequencyResponse.mock.calls[0][0]).toBe(
31 | frequencyHz,
32 | );
33 | expect(target._impl.getFrequencyResponse.mock.calls[0][1]).toBe(
34 | magResponse,
35 | );
36 | expect(target._impl.getFrequencyResponse.mock.calls[0][2]).toBe(
37 | phaseResponse,
38 | );
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/src/__tests__/api/PeriodicWave.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 |
6 | describe('api/PeriodicWave', () => {
7 | it('context.createPeriodicWave(real, imag)', () => {
8 | const context = new AudioContext();
9 | const real = new Float32Array(16);
10 | const imag = new Float32Array(16);
11 | const target = context.createPeriodicWave(real, imag);
12 |
13 | expect(target instanceof api.PeriodicWave).toBeTruthy();
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/src/__tests__/api/ScriptProcessorNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 |
6 | describe('api/ScriptProcessorNode', () => {
7 | it('context.createScriptProcessor(bufferSize, numberOfInputChannels, numberOfOutputChannels)', () => {
8 | const context = new AudioContext();
9 | const target = context.createScriptProcessor(256, 1, 1);
10 |
11 | expect(target instanceof api.ScriptProcessorNode).toBeTruthy();
12 | });
13 |
14 | describe('attributes', () => {
15 | it('.bufferSize', () => {
16 | const context = new AudioContext();
17 | const target = context.createScriptProcessor(256, 1, 1);
18 | const bufferSize = 256;
19 |
20 | target._impl.getBufferSize = jest.fn(() => bufferSize);
21 |
22 | expect(target.bufferSize).toBe(bufferSize);
23 | expect(target._impl.getBufferSize).toHaveBeenCalledTimes(1);
24 | });
25 |
26 | it('.onaudioprocess=', () => {
27 | const context = new AudioContext();
28 | const target = context.createScriptProcessor(256, 1, 1);
29 | const callback1 = jest.fn();
30 | const callback2 = jest.fn();
31 | const callback3 = jest.fn();
32 | const event = { type: 'audioprocess' };
33 |
34 | target.onaudioprocess = callback1;
35 | target.onaudioprocess = callback2;
36 | target.addEventListener('audioprocess', callback3);
37 | target._impl.dispatchEvent(event);
38 |
39 | expect(target.onaudioprocess).toBe(callback2);
40 | expect(callback1).toHaveBeenCalledTimes(0);
41 | expect(callback2).toHaveBeenCalledTimes(1);
42 | expect(callback3).toHaveBeenCalledTimes(1);
43 | expect(callback2.mock.calls[0][0]).toBe(event);
44 | expect(callback3.mock.calls[0][0]).toBe(event);
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/src/__tests__/api/StereoPannerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 | import AudioParam from '../../api/AudioParam';
6 |
7 | describe('api/StereoPannerNode', () => {
8 | it('context.createStereoPanner()', () => {
9 | const context = new AudioContext();
10 | const target = context.createStereoPanner();
11 |
12 | expect(target instanceof api.StereoPannerNode).toBeTruthy();
13 | });
14 |
15 | describe('atrributes', () => {
16 | it('.pan', () => {
17 | const context = new AudioContext();
18 | const target = context.createStereoPanner();
19 |
20 | expect(target.pan instanceof AudioParam).toBeTruthy();
21 | expect(target.pan).toBe(target._impl.$pan);
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/__tests__/api/WaveShaperNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as api from '../../api';
4 | import AudioContext from '../../api/BaseAudioContext';
5 |
6 | describe('api/WaveShaperNode', () => {
7 | it('context.createWaveShaper()', () => {
8 | const context = new AudioContext();
9 | const target = context.createWaveShaper();
10 |
11 | expect(target instanceof api.WaveShaperNode).toBeTruthy();
12 | });
13 |
14 | describe('atrributes', () => {
15 | it('.curve=', () => {
16 | const context = new AudioContext();
17 | const target = context.createWaveShaper();
18 | const curve1 = null;
19 | const curve2 = new Float32Array(128);
20 |
21 | target._impl.getCurve = jest.fn(() => curve1);
22 | target._impl.setCurve = jest.fn();
23 |
24 | expect(target.curve).toBe(curve1);
25 | expect(target._impl.getCurve).toHaveBeenCalledTimes(1);
26 |
27 | target.curve = curve2;
28 | expect(target._impl.setCurve).toHaveBeenCalledTimes(1);
29 | expect(target._impl.setCurve.mock.calls[0][0]).toBe(curve2);
30 | });
31 |
32 | it('.oversample=', () => {
33 | const context = new AudioContext();
34 | const target = context.createWaveShaper();
35 | const oversample1 = 'none';
36 | const oversample2 = '2x';
37 |
38 | target._impl.getOversample = jest.fn(() => oversample1);
39 | target._impl.setOversample = jest.fn();
40 |
41 | expect(target.oversample).toBe(oversample1);
42 | expect(target._impl.getOversample).toHaveBeenCalledTimes(1);
43 |
44 | target.oversample = oversample2;
45 | expect(target._impl.setOversample).toHaveBeenCalledTimes(1);
46 | expect(target._impl.setOversample.mock.calls[0][0]).toBe(oversample2);
47 | });
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/src/__tests__/context/RenderingAudioContext.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import RenderingAudioContext from '../../context/RenderingAudioContext';
4 |
5 | describe('RenderingAudioContext', () => {
6 | it('should return a RenderingAudioContext instance', () => {
7 | const context = new RenderingAudioContext();
8 |
9 | expect(context instanceof RenderingAudioContext).toBeTruthy();
10 | });
11 |
12 | it('should return a RenderingAudioContext instance with options', () => {
13 | const context = new RenderingAudioContext({
14 | sampleRate: 8000,
15 | numberOfChannels: 1,
16 | blockSize: 256,
17 | bitDepth: 8,
18 | });
19 |
20 | expect(context instanceof RenderingAudioContext).toBeTruthy();
21 | expect(context.sampleRate).toBe(8000);
22 | expect(context.numberOfChannels).toBe(1);
23 | expect(context.blockSize).toBe(256);
24 | expect(context.format).toEqual({
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 | expect(context.currentTime).toBe(0);
36 |
37 | context.processTo('00:00:10.000');
38 | expect(context.currentTime | 0).toBe(10);
39 |
40 | context.processTo('00:00:15.000');
41 | expect(context.currentTime | 0).toBe(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 | expect(audioData.numberOfChannels).toBe(2);
52 | expect((audioData.length / audioData.sampleRate) | 0).toBe(10);
53 | expect(audioData.sampleRate).toBe(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 | expect(arrayBuffer instanceof ArrayBuffer).toBeTruthy();
66 | });
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/src/__tests__/impl/ConstantSourceNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioContext from '../../impl/AudioContext';
4 | import ConstantSourceNode from '../../impl/ConstantSourceNode';
5 | import AudioParam from '../../impl/AudioParam';
6 | import AudioNode from '../../impl/AudioNode';
7 |
8 | describe('impl/ConstantSourceNode', () => {
9 | let context;
10 |
11 | beforeEach(() => {
12 | context = new AudioContext({ sampleRate: 8000, blockSize: 32 });
13 | });
14 |
15 | it('constructor', () => {
16 | const node = new ConstantSourceNode(context);
17 |
18 | expect(node instanceof ConstantSourceNode).toBeTruthy();
19 | expect(node instanceof AudioNode).toBeTruthy();
20 | });
21 |
22 | describe('attributes', () => {
23 | it('.numberOfInputs', () => {
24 | const node = new ConstantSourceNode(context);
25 |
26 | expect(node.getNumberOfInputs()).toBe(0);
27 | });
28 |
29 | it('.numberOfOutputs', () => {
30 | const node = new ConstantSourceNode(context);
31 |
32 | expect(node.getNumberOfOutputs()).toBe(1);
33 | });
34 |
35 | it('.offset', () => {
36 | const node = new ConstantSourceNode(context);
37 |
38 | expect(node.getOffset() instanceof AudioParam).toBeTruthy();
39 | expect(node.getOffset().getValue()).toBe(1);
40 | });
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/src/__tests__/impl/PannerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioContext from '../../impl/AudioContext';
4 | import PannerNode from '../../impl/PannerNode';
5 | import BasePannerNode from '../../impl/BasePannerNode';
6 |
7 | describe('impl/PannerNode', () => {
8 | let context;
9 |
10 | beforeEach(() => {
11 | context = new AudioContext({ sampleRate: 8000, blockSize: 32 });
12 | });
13 |
14 | it('constructor', () => {
15 | const node = new PannerNode(context);
16 |
17 | expect(node instanceof PannerNode).toBeTruthy();
18 | expect(node instanceof BasePannerNode).toBeTruthy();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/src/__tests__/impl/PeriodicWave.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioContext from '../../impl/AudioContext';
4 | import PeriodicWave from '../../impl/PeriodicWave';
5 |
6 | const real = new Float32Array([0, 0]);
7 | const imag = new Float32Array([0, 1]);
8 |
9 | describe('impl/PeriodicWave', () => {
10 | let context;
11 |
12 | beforeEach(() => {
13 | context = new AudioContext({ sampleRate: 8000, blockSize: 32 });
14 | });
15 |
16 | it('constructor', () => {
17 | const node = new PeriodicWave(context, { real, imag });
18 |
19 | expect(node instanceof PeriodicWave).toBeTruthy();
20 | });
21 |
22 | describe('attributes', () => {
23 | it('.constraints', () => {
24 | const node = new PeriodicWave(context, { real, imag });
25 |
26 | expect(node.getConstraints()).toBe(false);
27 | });
28 |
29 | it('.real', () => {
30 | const node = new PeriodicWave(context, { real, imag });
31 |
32 | expect(node.getReal()).toBe(real);
33 | });
34 |
35 | it('.imag', () => {
36 | const node = new PeriodicWave(context, { real, imag });
37 |
38 | expect(node.getImag()).toBe(imag);
39 | });
40 | });
41 |
42 | describe('generate basic waveform', () => {
43 | const periodicWave = new PeriodicWave(context, {
44 | real: [0, 0],
45 | imag: [0, 1],
46 | });
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 | expect(periodicWave.getName()).toBe(expected);
58 | });
59 | });
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/src/__tests__/impl/SpatialPannerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioContext from '../../impl/AudioContext';
4 | import SpatialPannerNode from '../../impl/SpatialPannerNode';
5 | import BasePannerNode from '../../impl/BasePannerNode';
6 | import AudioParam from '../../impl/AudioParam';
7 |
8 | describe('impl/SpatialPannerNode', () => {
9 | let context;
10 |
11 | beforeEach(() => {
12 | context = new AudioContext({ sampleRate: 8000, blockSize: 32 });
13 | });
14 |
15 | it('constructor', () => {
16 | const node = new SpatialPannerNode(context);
17 |
18 | expect(node instanceof SpatialPannerNode).toBeTruthy();
19 | expect(node instanceof BasePannerNode).toBeTruthy();
20 | });
21 |
22 | describe('attributes', () => {
23 | it('.positionX', () => {
24 | const node = new SpatialPannerNode(context);
25 |
26 | expect(node.getPositionX() instanceof AudioParam).toBeTruthy();
27 | expect(node.getPositionX().getValue()).toBe(0);
28 | });
29 |
30 | it('.positionY', () => {
31 | const node = new SpatialPannerNode(context);
32 |
33 | expect(node.getPositionY() instanceof AudioParam).toBeTruthy();
34 | expect(node.getPositionY().getValue()).toBe(0);
35 | });
36 |
37 | it('.positionZ', () => {
38 | const node = new SpatialPannerNode(context);
39 |
40 | expect(node.getPositionZ() instanceof AudioParam).toBeTruthy();
41 | expect(node.getPositionZ().getValue()).toBe(0);
42 | });
43 |
44 | it('.orientationX', () => {
45 | const node = new SpatialPannerNode(context);
46 |
47 | expect(node.getOrientationX() instanceof AudioParam).toBeTruthy();
48 | expect(node.getOrientationX().getValue()).toBe(0);
49 | });
50 |
51 | it('.orientationY', () => {
52 | const node = new SpatialPannerNode(context);
53 |
54 | expect(node.getOrientationY() instanceof AudioParam).toBeTruthy();
55 | expect(node.getOrientationY().getValue()).toBe(0);
56 | });
57 |
58 | it('.orientationZ', () => {
59 | const node = new SpatialPannerNode(context);
60 |
61 | expect(node.getOrientationZ() instanceof AudioParam).toBeTruthy();
62 | expect(node.getOrientationZ().getValue()).toBe(0);
63 | });
64 | });
65 | });
66 |
--------------------------------------------------------------------------------
/src/__tests__/impl/StereoPannerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioContext from '../../impl/AudioContext';
4 | import StereoPannerNode from '../../impl/StereoPannerNode';
5 | import BasePannerNode from '../../impl/BasePannerNode';
6 | import AudioParam from '../../impl/AudioParam';
7 |
8 | describe('impl/StereoPannerNode', () => {
9 | let context;
10 |
11 | beforeEach(() => {
12 | context = new AudioContext({ sampleRate: 8000, blockSize: 32 });
13 | });
14 |
15 | it('constructor', () => {
16 | const node = new StereoPannerNode(context);
17 |
18 | expect(node instanceof StereoPannerNode).toBeTruthy();
19 | expect(node instanceof BasePannerNode).toBeTruthy();
20 | });
21 |
22 | describe('attributes', () => {
23 | it('.numberOfInputs', () => {
24 | const node = new StereoPannerNode(context);
25 |
26 | expect(node.getNumberOfInputs()).toBe(1);
27 | });
28 |
29 | it('.numberOfOutputs', () => {
30 | const node = new StereoPannerNode(context);
31 |
32 | expect(node.getNumberOfOutputs()).toBe(1);
33 | });
34 |
35 | it('.pan', () => {
36 | const node = new StereoPannerNode(context);
37 |
38 | expect(node.getPan() instanceof AudioParam).toBeTruthy();
39 | expect(node.getPan().getValue()).toBe(0);
40 | });
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/src/__tests__/impl/core/AudioData.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as np from '../../../__tests_helpers/np';
4 | import AudioData from '../../../impl/core/AudioData';
5 |
6 | describe('impl/core/AudioData', () => {
7 | it('constructor(numberOfChannels, length, sampleRate)', () => {
8 | const data = new AudioData(2, 128, 44100);
9 |
10 | expect(data instanceof AudioData).toBeTruthy();
11 | });
12 |
13 | describe('attributes', () => {
14 | it('.numberOfChannels', () => {
15 | const data = new AudioData(2, 128, 44100);
16 |
17 | expect(data.numberOfChannels).toBe(2);
18 | });
19 |
20 | it('.length', () => {
21 | const data = new AudioData(2, 128, 44100);
22 |
23 | expect(data.length).toBe(128);
24 | });
25 |
26 | it('.sampleRate', () => {
27 | const data = new AudioData(2, 128, 44100);
28 |
29 | expect(data.sampleRate).toBe(44100);
30 | });
31 |
32 | it('.channelData', () => {
33 | const data = new AudioData(2, 128, 44100);
34 |
35 | expect(data.channelData.length).toBe(2);
36 | expect(data.channelData[0]).toEqual(np.zeros(128));
37 | expect(data.channelData[1]).toEqual(np.zeros(128));
38 | });
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/src/__tests__/impl/dsp/AudioContext.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioContext from '../../../impl/AudioContext';
4 |
5 | const contextOpts = { sampleRate: 8000, blockSize: 16 };
6 |
7 | describe('impl/dsp/AudioContext', () => {
8 | let context, destination;
9 |
10 | beforeAll(() => {
11 | context = new AudioContext(contextOpts);
12 | destination = context.getDestination();
13 |
14 | context.resume();
15 | });
16 |
17 | it('1: time advances', () => {
18 | const channelData = [new Float32Array(16), new Float32Array(16)];
19 |
20 | expect(context.getCurrentTime()).toBe(0);
21 | destination.process = jest.fn();
22 |
23 | context.process(channelData, 0);
24 |
25 | expect(destination.process).toHaveBeenCalledTimes(1);
26 | expect(destination.process).toBeCalledWith(channelData, 0);
27 | expect(context.getCurrentTime()).toBe(16 / 8000);
28 | });
29 |
30 | it('2: do post process and reserve pre process (for next process)', () => {
31 | const channelData = [new Float32Array(16), new Float32Array(16)];
32 | const callOrder = [];
33 | const immediateSpy = jest.fn(() => callOrder.push('immediateSpy'));
34 |
35 | expect(context.getCurrentTime()).toBe(16 / 8000);
36 | destination.process = jest.fn(() => {
37 | callOrder.push('destination.process');
38 | context.addPostProcess(immediateSpy);
39 | });
40 |
41 | context.process(channelData, 0);
42 |
43 | expect(destination.process).toHaveBeenCalledTimes(1);
44 | expect(destination.process).toBeCalledWith(channelData, 0);
45 | expect(context.getCurrentTime()).toBe(32 / 8000);
46 | expect(immediateSpy).toHaveBeenCalledTimes(1);
47 | expect(callOrder).toEqual(['destination.process', 'immediateSpy']);
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/src/__tests__/impl/dsp/AudioDestinationNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as np from '../../../__tests_helpers/np';
4 | import AudioContext from '../../../impl/AudioContext';
5 | import AudioDestinationNode from '../../../impl/AudioDestinationNode';
6 | import AudioNode from '../../../impl/AudioNode';
7 |
8 | describe('impl/dsp/AudioDestinationNode', () => {
9 | it('silent', () => {
10 | const channelData = [new Float32Array(16), new Float32Array(16)];
11 | const context = new AudioContext({ sampleRate: 8000, blockSize: 16 });
12 | const node1 = new AudioNode(context, {}, { outputs: [2] });
13 | const node2 = new AudioDestinationNode(context, { numberOfChannels: 2 });
14 | // const outputBus = node2.output.bus;
15 |
16 | node1.outputs[0].bus.zeros();
17 | node1.enableOutputsIfNecessary();
18 | node1.connect(node2);
19 |
20 | node2.process(channelData, 0);
21 |
22 | expect(channelData[0]).toEqual(np.zeros(16));
23 | expect(channelData[1]).toEqual(np.zeros(16));
24 | });
25 |
26 | it('noise', () => {
27 | const channelData = [new Float32Array(16), new Float32Array(16)];
28 | const context = new AudioContext({ sampleRate: 8000, blockSize: 16 });
29 | const node1 = new AudioNode(context, {}, { outputs: [2] });
30 | const node2 = new AudioDestinationNode(context, { numberOfChannels: 2 });
31 | const noise1 = np.random_sample(16);
32 | const noise2 = np.random_sample(16);
33 |
34 | node1.outputs[0].bus.getMutableData()[0].set(noise1);
35 | node1.outputs[0].bus.getMutableData()[1].set(noise2);
36 | node1.enableOutputsIfNecessary();
37 | node1.connect(node2);
38 |
39 | node2.process(channelData, 0);
40 |
41 | expect(channelData[0]).toEqual(noise1);
42 | expect(channelData[1]).toEqual(noise2);
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/src/__tests__/impl/dsp/AudioNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioContext from '../../../impl/AudioContext';
4 | import GainNode from '../../../impl/GainNode';
5 |
6 | const context = new AudioContext({ sampleRate: 8000, blockSize: 16 });
7 |
8 | describe('impl/dsp/AudioNode', () => {
9 | it('propagation', () => {
10 | const node1 = new GainNode(context);
11 | const node2 = new GainNode(context);
12 | const node3 = new GainNode(context);
13 | const param = node1.getGain();
14 |
15 | node1.outputs[0].enable();
16 | node2.outputs[0].enable();
17 | node1.connect(node2);
18 | node2.connect(node3);
19 |
20 | node1.dspProcess = jest.fn();
21 | param.dspProcess = jest.fn();
22 |
23 | node3.processIfNecessary();
24 |
25 | expect(param.dspProcess).toHaveBeenCalledTimes(1);
26 | });
27 |
28 | it('feedback loop', () => {
29 | const node1 = new GainNode(context);
30 | const node2 = new GainNode(context);
31 | const node3 = new GainNode(context);
32 |
33 | node1.outputs[0].enable();
34 | node2.outputs[0].enable();
35 | node1.connect(node2);
36 | node2.connect(node3);
37 | node3.connect(node1);
38 |
39 | node1.dspProcess = jest.fn();
40 |
41 | node3.processIfNecessary();
42 |
43 | expect(node1.dspProcess).toHaveBeenCalledTimes(1);
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/src/__tests__/impl/dsp/ChannelMergerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as np from '../../../__tests_helpers/np';
4 | import AudioContext from '../../../impl/AudioContext';
5 | import ChannelMergerNode from '../../../impl/ChannelMergerNode';
6 | import AudioNode from '../../../impl/AudioNode';
7 |
8 | describe('impl/dsp/ChannelMergerNode', () => {
9 | it('works', () => {
10 | const channelData = [new Float32Array(16), new Float32Array(16)];
11 | const context = new AudioContext({ sampleRate: 8000, blockSize: 16 });
12 | const node1 = new AudioNode(context, {}, { inputs: [1], outputs: [1] });
13 | const node2 = new AudioNode(context, {}, { inputs: [1], outputs: [1] });
14 | const node3 = new ChannelMergerNode(context, { numberOfInputs: 4 });
15 | const noise1 = np.random_sample(16);
16 | const noise2 = np.random_sample(16);
17 |
18 | context.resume();
19 | node1.connect(node3, 0, 0);
20 | node2.connect(node3, 0, 1);
21 | node3.connect(context.getDestination());
22 | node1.enableOutputsIfNecessary();
23 | node2.enableOutputsIfNecessary();
24 | node1.outputs[0].bus.getMutableData()[0].set(noise1);
25 | node2.outputs[0].bus.getMutableData()[0].set(noise2);
26 |
27 | context.process(channelData, 0);
28 |
29 | const actual = node3.outputs[0].bus.getChannelData();
30 | const expected = [noise1, noise2, np.zeros(16), np.zeros(16)];
31 |
32 | expect(actual).toEqual(expected);
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/src/__tests__/impl/dsp/DynamicsCompressorNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioContext from '../../../api/BaseAudioContext';
4 | import DynamicsCompressorNode from '../../../api/DynamicsCompressorNode';
5 | import DynamicsCompressorData from '../../../__tests_helpers/DynamicsCompressorData';
6 |
7 | describe('impl/dsp/DynamicsCompressor', () => {
8 | it('works', () => {
9 | const sampleRate = 44100;
10 | const length = 44100;
11 | const blockSize = 1024;
12 | const context = new AudioContext({
13 | sampleRate,
14 | blockSize,
15 | numberOfChannels: 1,
16 | });
17 | const node = new DynamicsCompressorNode(context);
18 |
19 | // Testing the "Classic Voiceover" preset
20 | // { threshold: -24, ratio: 1.5, attack: 0.15, release: 0.4, knee: 10 }
21 |
22 | node.attack.value = 0.15;
23 | node.ratio.value = 1.5;
24 | node.threshold.value = -24;
25 | node.release.value = 0.4;
26 | node.knee.value = 10;
27 |
28 | const buffer = context.createBuffer(1, length, sampleRate);
29 | const bufSrc = context.createBufferSource();
30 |
31 | const freq = 440;
32 |
33 | function val(t) {
34 | return Math.sin(2 * Math.PI * freq * t);
35 | }
36 |
37 | for (let i = 0; i < length; i++) {
38 | buffer.getChannelData(0)[i] = val(i / sampleRate);
39 | }
40 |
41 | bufSrc.buffer = buffer;
42 |
43 | bufSrc.connect(node);
44 | node.connect(context.destination);
45 | const iterations = Math.ceil(length / blockSize);
46 | const iterLength = iterations * blockSize;
47 | const channelData = [new Float32Array(iterLength)];
48 | bufSrc.start();
49 | context.resume();
50 |
51 | for (let i = 0; i < iterations; i++) {
52 | context._impl.process(channelData, i * blockSize);
53 | }
54 |
55 | const out = channelData[0].slice(0, length);
56 |
57 | expect(out.length).toBe(length);
58 | for (let i = 0; i < out.length; i++) {
59 | const a = out[i];
60 | const b = DynamicsCompressorData[i];
61 | expect(Math.abs(a - b) <= 1e-4).toBeTruthy();
62 | }
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/src/__tests__/impl/dsp/IIRFilterNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioContext from '../../../impl/AudioContext';
4 | import IIRFilterNode from '../../../impl/IIRFilterNode';
5 |
6 | function closeTo(a, b, delta) {
7 | return Math.abs(a - b) <= delta;
8 | }
9 |
10 | describe('impl/dsp/IIRFilterNode', () => {
11 | describe('getFrequencyResponse(frequencyHz, magResponse, phaseResponse)', () => {
12 | it('works', () => {
13 | const context = new AudioContext({ sampleRate: 44100, blockSize: 16 });
14 | const node = new IIRFilterNode(context, {
15 | feedforward: [0.135784, 0.0, -0.135784],
16 | feedback: [1, -1.854196, 0.932108],
17 | });
18 | const frequencyHz = new Float32Array([1000, 2000, 3000]);
19 | const magResponse = new Float32Array(3);
20 | const phaseResponse = new Float32Array(3);
21 |
22 | node.getFrequencyResponse(frequencyHz, magResponse, phaseResponse);
23 |
24 | // computed using Chrome 54
25 | // 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);
26 | const magExpected = new Float32Array([
27 | 0.6521847248077393,
28 | 4,
29 | 1.1262547969818115,
30 | ]);
31 | const phaseExpected = new Float32Array([
32 | 1.4070188999176025,
33 | 0.00000937021650315728,
34 | -1.2853729724884033,
35 | ]);
36 |
37 | expect(
38 | magResponse.every((mag, i) => closeTo(mag, magExpected[i], 1e-6)),
39 | ).toBeTruthy();
40 | expect(
41 | phaseResponse.every((phase, i) =>
42 | closeTo(phase, phaseExpected[i], 1e-6),
43 | ),
44 | ).toBeTruthy();
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/src/__tests__/mocha.opts:
--------------------------------------------------------------------------------
1 | --recursive
2 | --require babel-register
3 |
--------------------------------------------------------------------------------
/src/__tests__/utils/EncoderUtils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as EncoderUtils from '../../utils/EncoderUtils';
4 |
5 | describe('utils/EncoderUtils.encode(encodeFn: function, audioData: AudioData, opts?: object): Promise', () => {
6 | it('should return promise and resolve - from AudioData', () => {
7 | const source = new Uint8Array(128);
8 | const sampleRate = 44100;
9 | const channelData = [new Float32Array(128), new Float32Array(128)];
10 | const audioData = { sampleRate, channelData };
11 | const opts = {};
12 | const encodeFn = jest.fn(() => {
13 | return Promise.resolve(source.buffer);
14 | });
15 |
16 | return EncoderUtils.encode(encodeFn, audioData, opts).then(
17 | (arrayBuffer) => {
18 | expect(encodeFn).toHaveBeenCalledTimes(1);
19 | expect(encodeFn).toBeCalledWith(audioData, opts);
20 | expect(arrayBuffer instanceof ArrayBuffer).toBeTruthy();
21 | },
22 | );
23 | });
24 |
25 | it('should return peomise and resolve - from AudioBuffer', () => {
26 | const source = new Uint8Array(128);
27 | const numberOfChannels = 2;
28 | const sampleRate = 44100;
29 | const channelData = [new Float32Array(128), new Float32Array(128)];
30 | const audioData = {
31 | numberOfChannels,
32 | sampleRate,
33 | getChannelData(ch) {
34 | return channelData[ch];
35 | },
36 | };
37 | const opts = {};
38 | const encodeFn = jest.fn(() => {
39 | return Promise.resolve(source.buffer);
40 | });
41 |
42 | return EncoderUtils.encode(encodeFn, audioData, opts).then(
43 | (arrayBuffer) => {
44 | expect(encodeFn).toHaveBeenCalledTimes(1);
45 |
46 | const _audioData = encodeFn.mock.calls[0][0];
47 |
48 | expect(_audioData.numberOfChannels).toBe(numberOfChannels);
49 | expect(_audioData.length).toBe(128);
50 | expect(_audioData.sampleRate).toBe(44100);
51 | expect(_audioData.channelData[0]).toBe(channelData[0]);
52 | expect(_audioData.channelData[1]).toBe(channelData[1]);
53 |
54 | expect(arrayBuffer instanceof ArrayBuffer).toBeTruthy();
55 | },
56 | );
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/src/__tests__/utils/setImmediate.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import setImmediate from '../../utils/setImmediate';
4 |
5 | describe('utils/setImmediate(fn)', () => {
6 | it('works', (done) => {
7 | setImmediate(done);
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/clamp.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import clamp from '../../../utils/utils/clamp';
4 |
5 | describe('utils/clamp(value, minValue, maxValue)', () => {
6 | it('return clamped value in the range [minValue, maxValue]', () => {
7 | expect(clamp(0, 2, 4)).toBe(2);
8 | expect(clamp(1, 2, 4)).toBe(2);
9 | expect(clamp(2, 2, 4)).toBe(2);
10 | expect(clamp(3, 2, 4)).toBe(3);
11 | expect(clamp(4, 2, 4)).toBe(4);
12 | expect(clamp(5, 2, 4)).toBe(4);
13 | expect(clamp(6, 2, 4)).toBe(4);
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/defaults.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import defaults from '../../../utils/utils/defaults';
4 |
5 | describe('utils/defaults(value, defaultValue)', () => {
6 | it('works', () => {
7 | expect(defaults(0, 1)).toBe(0);
8 | expect(defaults(null, 1)).toBe(null);
9 | expect(defaults(undefined, 1)).toBe(1);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/defineProp.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import defineProp from '../../../utils/utils/defineProp';
4 |
5 | describe('utils/defineProp', () => {
6 | it('define property', () => {
7 | const a = {};
8 |
9 | defineProp(a, 'value', 100);
10 |
11 | expect(a.value).toBe(100);
12 | });
13 |
14 | it('not enumerable', () => {
15 | const a = {};
16 |
17 | defineProp(a, 'value', 100);
18 |
19 | expect(Object.keys(a).length).toBe(0);
20 | });
21 |
22 | it('writable', () => {
23 | const a = {};
24 |
25 | defineProp(a, 'value', 100);
26 |
27 | a.value = 200;
28 | expect(a.value).toBe(200);
29 | });
30 |
31 | it('configurable', () => {
32 | const a = {};
33 |
34 | defineProp(a, 'value', 100);
35 | defineProp(a, 'value', 300);
36 |
37 | expect(a.value).toBe(300);
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/fill.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import fill from '../../../utils/utils/fill';
4 |
5 | describe('utils/fill(list, value)', () => {
6 | it('fill value', () => {
7 | const list = new Float32Array(8);
8 | const actual = fill(list, 1);
9 | const expected = new Float32Array([1, 1, 1, 1, 1, 1, 1, 1]);
10 |
11 | expect(actual).toEqual(expected);
12 | expect(list).toEqual(expected);
13 | });
14 |
15 | it('fill value - polyfill ver', () => {
16 | const list = new Float32Array(8);
17 |
18 | // kill native function
19 | Object.defineProperty(list, 'fill', { value: null });
20 |
21 | const actual = fill(list, 1);
22 | const expected = new Float32Array([1, 1, 1, 1, 1, 1, 1, 1]);
23 |
24 | expect(actual).toEqual(expected);
25 | expect(list).toEqual(expected);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/fillRange.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import fillRange from '../../../utils/utils/fillRange';
4 |
5 | describe('utils/fillRange(list, value, start, end)', () => {
6 | it('fill value', () => {
7 | const list = new Float32Array(8);
8 | const actual = fillRange(list, 1, 2, 6);
9 | const expected = new Float32Array([0, 0, 1, 1, 1, 1, 0, 0]);
10 |
11 | expect(actual).toEqual(expected);
12 | expect(list).toEqual(expected);
13 | });
14 |
15 | it('fill value - polyfill ver', () => {
16 | const list = new Float32Array(8);
17 |
18 | // kill native function
19 | Object.defineProperty(list, 'fill', { value: null });
20 |
21 | const actual = fillRange(list, 1, 2, 6);
22 | const expected = new Float32Array([0, 0, 1, 1, 1, 1, 0, 0]);
23 |
24 | expect(actual).toEqual(expected);
25 | expect(list).toEqual(expected);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/normalize.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import normalize from '../../../utils/utils/normalize';
4 |
5 | describe('utils/normalize(val, min, max)', () => {
6 | it('normalize a value to something between 0 - 1', () => {
7 | expect(normalize(0, -100, 100)).toBe(0.5);
8 | expect(normalize(-100, -100, 100)).toBe(0);
9 | expect(normalize(-200, -100, 100)).toBe(0);
10 | expect(normalize(100, -100, 100)).toBe(1);
11 | expect(normalize(200, -100, 100)).toBe(1);
12 | expect(normalize(50, -100, 100)).toBe(0.75);
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/toArrayIfNeeded.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toArrayIfNeeded from '../../../utils/utils/toArrayIfNeeded';
4 |
5 | describe('utils/toArrayIfNeeded(value)', () => {
6 | it('convert to array if not array', () => {
7 | const value = 1;
8 | const actual = toArrayIfNeeded(value);
9 | const expected = [value];
10 |
11 | expect(actual).toEqual(expected);
12 | });
13 |
14 | it('nothing to do if array', () => {
15 | const value = [1];
16 | const actual = toArrayIfNeeded(value);
17 | const expected = value;
18 |
19 | expect(actual).toBe(expected);
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/toAudioTime.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toAudioTime from '../../../utils/utils/toAudioTime';
4 |
5 | describe('utils/toAudioTime()', () => {
6 | it('return the provided value when provide positive number', () => {
7 | expect(toAudioTime(10)).toBe(10);
8 | });
9 |
10 | it('return 0 when provide a negative number', () => {
11 | expect(toAudioTime(-1)).toBe(0);
12 | });
13 |
14 | it('return 0 when provided infinite number', () => {
15 | expect(toAudioTime(Infinity)).toBe(0);
16 | });
17 |
18 | it("convert to number when provided format of 'ss.SSS'", () => {
19 | expect(toAudioTime('23.456')).toBe(23.456);
20 | });
21 |
22 | it("convert to number when provided format of 'mm:ss.SSS'", () => {
23 | expect(toAudioTime('01:23.456')).toBe(83.456);
24 | });
25 |
26 | it("convert to number when provided format of 'hh:mm:ss.SSS'", () => {
27 | expect(toAudioTime('00:01:23.456')).toBe(83.456);
28 | });
29 |
30 | it('return 0 when provided case', () => {
31 | expect(toAudioTime('UNKNOWN')).toBe(0);
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/toDecibel.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toDecibel from '../../../utils/utils/toDecibel';
4 |
5 | describe('utils/toDecibel(gainValue)', () => {
6 | it('convert gainValue to decibel', () => {
7 | expect(Math.round(toDecibel(3.162))).toBe(10);
8 | expect(Math.round(toDecibel(1.995))).toBe(6);
9 | expect(Math.round(toDecibel(1.413))).toBe(3);
10 | expect(Math.round(toDecibel(1.122))).toBe(1);
11 | expect(Math.round(toDecibel(1.0))).toBe(0);
12 | expect(Math.round(toDecibel(0.891))).toBe(-1);
13 | expect(Math.round(toDecibel(0.708))).toBe(-3);
14 | expect(Math.round(toDecibel(0.501))).toBe(-6);
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/toGain.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toGain from '../../../utils/utils/toGain';
4 |
5 | function closeTo(a, b, delta) {
6 | return Math.abs(a - b) <= delta;
7 | }
8 |
9 | describe('utils/toGain(decibel)', () => {
10 | it('convert decibel to gain', () => {
11 | expect(closeTo(toGain(10), 3.1622776985168457, 1e-6)).toBeTruthy();
12 | expect(closeTo(toGain(6), 1.9952622652053833, 1e-6)).toBeTruthy();
13 | expect(closeTo(toGain(3), 1.4125375747680664, 1e-6)).toBeTruthy();
14 | expect(closeTo(toGain(1), 1.1220184564590454, 1e-6)).toBeTruthy();
15 | expect(toGain(0)).toBe(1);
16 | expect(closeTo(toGain(-1), 0.8912509083747864, 1e-6)).toBeTruthy();
17 | expect(closeTo(toGain(-3), 0.7079457640647888, 1e-6)).toBeTruthy();
18 | expect(closeTo(toGain(-6), 0.5011872053146362, 1e-6)).toBeTruthy();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/toImpl.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toImpl from '../../../utils/utils/toImpl';
4 |
5 | describe('utils/toImpl(value)', () => {
6 | it('convert to impl', () => {
7 | const impl = {};
8 | const value = { _impl: impl };
9 | const actual = toImpl(value);
10 | const expected = impl;
11 |
12 | expect(actual).toBe(expected);
13 | });
14 |
15 | it('nothing to do', () => {
16 | const impl = {};
17 | const actual = toImpl(impl);
18 | const expected = impl;
19 |
20 | expect(actual).toBe(expected);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/toNumber.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toNumber from '../../../utils/utils/toNumber';
4 |
5 | describe('utils/toNumber(value)', () => {
6 | it('convert to number', () => {
7 | expect(toNumber(1)).toBe(1);
8 | expect(toNumber(Infinity)).toBe(Infinity);
9 | expect(toNumber('1')).toBe(1);
10 | expect(toNumber(NaN)).toBe(0);
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/toPowerOfTwo.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toPowerOfTwo from '../../../utils/utils/toPowerOfTwo';
4 |
5 | describe('utils/toPowerOfTwo(value)', () => {
6 | it('convert to 2^n', () => {
7 | expect(toPowerOfTwo(1)).toBe(1);
8 | expect(toPowerOfTwo(2)).toBe(2);
9 | expect(toPowerOfTwo(3)).toBe(4);
10 | expect(toPowerOfTwo(3, Math.floor)).toBe(2);
11 | expect(toPowerOfTwo(3, Math.ceil)).toBe(4);
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/toValidBitDepth.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toValidBitDepth from '../../../utils/utils/toValidBitDepth';
4 |
5 | describe('utils/toValidBitDepth()', () => {
6 | it('return valid bit depth', () => {
7 | expect(toValidBitDepth(8)).toBe(8);
8 | expect(toValidBitDepth(16)).toBe(16);
9 | expect(toValidBitDepth(32)).toBe(32);
10 | });
11 |
12 | it('return the default bit depth 16 when provided an invalid bit depth', () => {
13 | expect(toValidBitDepth(0)).toBe(16);
14 | expect(toValidBitDepth(NaN)).toBe(16);
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/toValidBlockSize.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toValidBlockSize from '../../../utils/utils/toValidBlockSize';
4 |
5 | describe('utils/toValidBlockSize()', () => {
6 | it('return valid block size', () => {
7 | expect(toValidBlockSize(0)).toBe(8);
8 | expect(toValidBlockSize(8)).toBe(8);
9 | expect(toValidBlockSize(128)).toBe(128);
10 | expect(toValidBlockSize(500)).toBe(512);
11 | expect(toValidBlockSize(2000)).toBe(1024);
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/toValidNumberOfChannels.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toValidNumberOfChannels from '../../../utils/utils/toValidNumberOfChannels';
4 |
5 | describe('utils/toValidNumberOfChannels()', () => {
6 | it('return valid number of channels', () => {
7 | expect(toValidNumberOfChannels(0)).toBe(1);
8 | expect(toValidNumberOfChannels(2)).toBe(2);
9 | expect(toValidNumberOfChannels(8)).toBe(8);
10 | expect(toValidNumberOfChannels(2000)).toBe(32);
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/__tests__/utils/utils/toValidSampleRate.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toValidSampleRate from '../../../utils/utils/toValidSampleRate';
4 |
5 | describe('utils/toValidSampleRate()', () => {
6 | it('return valid sampleRate', () => {
7 | expect(toValidSampleRate(0)).toBe(3000);
8 | expect(toValidSampleRate(5512.5)).toBe(5512);
9 | expect(toValidSampleRate(44100)).toBe(44100);
10 | expect(toValidSampleRate(48000)).toBe(48000);
11 | expect(toValidSampleRate(Infinity)).toBe(192000);
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/src/__tests_helpers/np.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function zeros(size, constructor = Float32Array) {
4 | return new constructor(size);
5 | }
6 |
7 | function full(size, value, constructor = Float32Array) {
8 | return new constructor(size).fill(value);
9 | }
10 |
11 | function random_sample(size, constructor = Float32Array) {
12 | return new constructor(size).map(Math.random);
13 | }
14 |
15 | export { zeros, full, random_sample };
16 |
--------------------------------------------------------------------------------
/src/api/AnalyserNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './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 | export default AnalyserNode;
67 |
--------------------------------------------------------------------------------
/src/api/AudioBuffer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import { defineProp } from '../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 | export default AudioBuffer;
41 |
--------------------------------------------------------------------------------
/src/api/AudioBufferSourceNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioScheduledSourceNode from './AudioScheduledSourceNode';
5 | import AudioParam from './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(
13 | context,
14 | this._impl.getPlaybackRate(),
15 | );
16 | this._impl.$detune = new AudioParam(context, this._impl.getDetune());
17 | this._impl.$buffer = null;
18 | this._impl.$onended = null;
19 |
20 | if (opts && opts.buffer) {
21 | this.buffer = opts.buffer;
22 | }
23 | }
24 |
25 | get buffer() {
26 | return this._impl.$buffer;
27 | }
28 |
29 | set buffer(value) {
30 | this._impl.$buffer = value;
31 | this._impl.setBuffer(value);
32 | }
33 |
34 | get playbackRate() {
35 | return this._impl.$playbackRate;
36 | }
37 |
38 | get detune() {
39 | return this._impl.$detune;
40 | }
41 |
42 | get loop() {
43 | return this._impl.getLoop();
44 | }
45 |
46 | set loop(value) {
47 | this._impl.setLoop(value);
48 | }
49 |
50 | get loopStart() {
51 | return this._impl.getLoopStart();
52 | }
53 |
54 | set loopStart(value) {
55 | this._impl.setLoopStart(value);
56 | }
57 |
58 | get loopEnd() {
59 | return this._impl.getLoopEnd();
60 | }
61 |
62 | set loopEnd(value) {
63 | this._impl.setLoopEnd(value);
64 | }
65 |
66 | start(when, offset, duration) {
67 | this._impl.start(when, offset, duration);
68 | }
69 | }
70 |
71 | export default AudioBufferSourceNode;
72 |
--------------------------------------------------------------------------------
/src/api/AudioDestinationNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioNode from './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 | export default AudioDestinationNode;
18 |
--------------------------------------------------------------------------------
/src/api/AudioListener.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { defineProp } from '../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 | export default AudioListener;
20 |
--------------------------------------------------------------------------------
/src/api/AudioNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import EventTarget from './EventTarget';
4 | import { defineProp } from '../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 | export default AudioNode;
65 |
--------------------------------------------------------------------------------
/src/api/AudioParam.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { defineProp } from '../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 automationRate() {
20 | return this._impl.getRate();
21 | }
22 |
23 | get defaultValue() {
24 | return this._impl.getDefaultValue();
25 | }
26 |
27 | setValueAtTime(value, startTime) {
28 | this._impl.setValueAtTime(value, startTime);
29 | return this;
30 | }
31 |
32 | linearRampToValueAtTime(value, endTime) {
33 | this._impl.linearRampToValueAtTime(value, endTime);
34 | return this;
35 | }
36 |
37 | exponentialRampToValueAtTime(value, endTime) {
38 | this._impl.exponentialRampToValueAtTime(value, endTime);
39 | return this;
40 | }
41 |
42 | setTargetAtTime(target, startTime, timeConstant) {
43 | this._impl.setTargetAtTime(target, startTime, timeConstant);
44 | return this;
45 | }
46 |
47 | setValueCurveAtTime(values, startTime, duration) {
48 | this._impl.setValueCurveAtTime(values, startTime, duration);
49 | return this;
50 | }
51 |
52 | cancelScheduledValues(startTime) {
53 | this._impl.cancelScheduledValues(startTime);
54 | return this;
55 | }
56 | }
57 |
58 | export default AudioParam;
59 |
--------------------------------------------------------------------------------
/src/api/AudioScheduledSourceNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioNode from './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 | /**
16 | *
17 | * @param {number} when
18 | * @param {number} [offset]
19 | * @param {number} [duration]
20 | */
21 | start(when, offset, duration) {
22 | this._impl.start(when, offset, duration);
23 | }
24 |
25 | stop(when) {
26 | this._impl.stop(when);
27 | }
28 | }
29 |
30 | export default AudioScheduledSourceNode;
31 |
--------------------------------------------------------------------------------
/src/api/AudioWorkletNode.ts:
--------------------------------------------------------------------------------
1 | import { AudioWorkletNode } from '../impl';
2 |
3 | export default AudioWorkletNode;
4 |
--------------------------------------------------------------------------------
/src/api/AudioWorkletProcessor.ts:
--------------------------------------------------------------------------------
1 | import { AudioWorkletProcessor } from '../impl';
2 |
3 | export default AudioWorkletProcessor;
4 |
--------------------------------------------------------------------------------
/src/api/BiquadFilterNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './AudioNode';
5 | import AudioParam from './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 | export default BiquadFilterNode;
48 |
--------------------------------------------------------------------------------
/src/api/ChannelMergerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './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 | export default ChannelMergerNode;
15 |
--------------------------------------------------------------------------------
/src/api/ChannelSplitterNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './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 | export default ChannelSplitterNode;
15 |
--------------------------------------------------------------------------------
/src/api/ConstantSourceNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioScheduledSourceNode from './AudioScheduledSourceNode';
5 | import AudioParam from './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 | export default ConstantSourceNode;
22 |
--------------------------------------------------------------------------------
/src/api/ConvolverNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './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 | export default ConvolverNode;
37 |
--------------------------------------------------------------------------------
/src/api/DelayNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './AudioNode';
5 | import AudioParam from './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 | export default DelayNode;
21 |
--------------------------------------------------------------------------------
/src/api/DynamicsCompressorNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './AudioNode';
5 | import AudioParam from './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 | export default 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 | export default EventTarget;
14 |
--------------------------------------------------------------------------------
/src/api/GainNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './AudioNode';
5 | import AudioParam from './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 | export default GainNode;
21 |
--------------------------------------------------------------------------------
/src/api/IIRFilterNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './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 | export default IIRFilterNode;
19 |
--------------------------------------------------------------------------------
/src/api/OscillatorNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioScheduledSourceNode from './AudioScheduledSourceNode';
5 | import AudioParam from './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 | export default OscillatorNode;
43 |
--------------------------------------------------------------------------------
/src/api/PannerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './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 | export default PannerNode;
91 |
--------------------------------------------------------------------------------
/src/api/PeriodicWave.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import { defineProp } from '../utils';
5 |
6 | class PeriodicWave {
7 | constructor(context, opts) {
8 | defineProp(this, '_impl', new impl.PeriodicWave(context._impl, opts));
9 | }
10 | }
11 |
12 | export default 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 | import * as impl from '../impl';
4 | import AudioNode from './AudioNode';
5 | import AudioBuffer from './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(
31 | 'audioprocess',
32 | this._impl.$onaudioprocess,
33 | callback,
34 | );
35 | this._impl.$onaudioprocess = callback;
36 | }
37 | }
38 |
39 | export default ScriptProcessorNode;
40 |
--------------------------------------------------------------------------------
/src/api/SpatialListener.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioParam from './AudioParam';
4 | import { defineProp } from '../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 | export default SpatialListener;
60 |
--------------------------------------------------------------------------------
/src/api/StereoPannerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './AudioNode';
5 | import AudioParam from './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 | export default StereoPannerNode;
21 |
--------------------------------------------------------------------------------
/src/api/WaveShaperNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as impl from '../impl';
4 | import AudioNode from './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 | export default WaveShaperNode;
31 |
--------------------------------------------------------------------------------
/src/api/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export { default as AnalyserNode } from './AnalyserNode';
4 | export { default as AudioBuffer } from './AudioBuffer';
5 | export { default as AudioBufferSourceNode } from './AudioBufferSourceNode';
6 | export { default as AudioDestinationNode } from './AudioDestinationNode';
7 | export { default as AudioListener } from './AudioListener';
8 | export { default as AudioNode } from './AudioNode';
9 | export { default as AudioParam } from './AudioParam';
10 | export { default as AudioScheduledSourceNode } from './AudioScheduledSourceNode';
11 | export { default as AudioWorkletNode } from './AudioWorkletNode';
12 | export { default as AudioWorkletProcessor } from './AudioWorkletProcessor';
13 | export { default as BaseAudioContext } from './BaseAudioContext';
14 | export { default as BiquadFilterNode } from './BiquadFilterNode';
15 | export { default as ChannelMergerNode } from './ChannelMergerNode';
16 | export { default as ChannelSplitterNode } from './ChannelSplitterNode';
17 | export { default as ConstantSourceNode } from './ConstantSourceNode';
18 | export { default as ConvolverNode } from './ConvolverNode';
19 | export { default as DelayNode } from './DelayNode';
20 | export { default as DynamicsCompressorNode } from './DynamicsCompressorNode';
21 | export { default as EventTarget } from './EventTarget';
22 | export { default as GainNode } from './GainNode';
23 | export { default as IIRFilterNode } from './IIRFilterNode';
24 | export { default as OscillatorNode } from './OscillatorNode';
25 | export { default as PannerNode } from './PannerNode';
26 | export { default as PeriodicWave } from './PeriodicWave';
27 | export { default as ScriptProcessorNode } from './ScriptProcessorNode';
28 | export { default as SpatialListener } from './SpatialListener';
29 | export { default as SpatialPannerNode } from './SpatialPannerNode';
30 | export { default as StereoPannerNode } from './StereoPannerNode';
31 | export { default as WaveShaperNode } from './WaveShaperNode';
32 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default {
4 | sampleRate: 44100,
5 | numberOfChannels: 2,
6 | blockSize: 128,
7 | bitDepth: 16,
8 | };
9 |
--------------------------------------------------------------------------------
/src/constants/AudioContextState.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const RUNNING = 'running';
4 | export const SUSPENDED = 'suspended';
5 | export const CLOSED = 'closed';
6 |
--------------------------------------------------------------------------------
/src/constants/AudioParamEvent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const SET_VALUE_AT_TIME = 'setValueAtTime';
4 | export const LINEAR_RAMP_TO_VALUE_AT_TIME = 'linearRampToValueAtTime';
5 | export const EXPONENTIAL_RAMP_TO_VALUE_AT_TIME = 'exponentialRampToValueAtTime';
6 | export const SET_TARGET_AT_TIME = 'setTargetAtTime';
7 | export const SET_VALUE_CURVE_AT_TIME = 'setValueCurveAtTime';
8 |
--------------------------------------------------------------------------------
/src/constants/AudioParamRate.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const CONTROL_RATE = 'control';
4 | export const AUDIO_RATE = 'audio';
5 |
--------------------------------------------------------------------------------
/src/constants/BiquadFilterType.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const LOWPASS = 'lowpass';
4 | export const HIGHPASS = 'highpass';
5 | export const BANDPASS = 'bandpass';
6 | export const LOWSHELF = 'lowshelf';
7 | export const HIGHSHELF = 'highshelf';
8 | export const PEAKING = 'peaking';
9 | export const NOTCH = 'notch';
10 | export const ALLPASS = 'allpass';
11 |
--------------------------------------------------------------------------------
/src/constants/ChannelCountMode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const MAX = 'max';
4 | export const CLAMPED_MAX = 'clamped-max';
5 | export const EXPLICIT = 'explicit';
6 |
--------------------------------------------------------------------------------
/src/constants/ChannelInterpretation.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const SPEAKERS = 'speakers';
4 | export const DISCRETE = 'discrete';
5 |
--------------------------------------------------------------------------------
/src/constants/OscillatorType.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const SINE = 'sine';
4 | export const SAWTOOTH = 'sawtooth';
5 | export const TRIANGLE = 'triangle';
6 | export const SQUARE = 'square';
7 | export const CUSTOM = 'custom';
8 |
--------------------------------------------------------------------------------
/src/constants/PlaybackState.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const UNSCHEDULED = 'unscheduled';
4 | export const SCHEDULED = 'scheduled';
5 | export const PLAYING = 'playing';
6 | export const FINISHED = 'finished';
7 |
--------------------------------------------------------------------------------
/src/constants/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const MIN_SAMPLERATE = 3000;
4 | export const MAX_SAMPLERATE = 192000;
5 | export const MIN_NUMBER_OF_CHANNELS = 1;
6 | export const MAX_NUMBER_OF_CHANNELS = 32;
7 | export const MIN_BLOCK_SIZE = 8;
8 | export const MAX_BLOCK_SIZE = 1024;
9 |
--------------------------------------------------------------------------------
/src/context/RawDataAudioContext.ts:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import config from '../config';
4 | import BaseAudioContext from '../api/BaseAudioContext';
5 | import {
6 | toValidBlockSize,
7 | toValidNumberOfChannels,
8 | toValidSampleRate,
9 | } from '../utils';
10 | import AudioContext from '../impl/AudioContext';
11 | import AudioData from '../impl/core/AudioData';
12 | import AudioBuffer from '../api/AudioBuffer';
13 |
14 | export class RawDataAudioContext extends BaseAudioContext {
15 | readonly _impl!: AudioContext;
16 | public readonly blockSize: number;
17 | public readonly numberOfChannels: number;
18 |
19 | constructor({
20 | sampleRate = config.sampleRate,
21 | blockSize = config.blockSize,
22 | numberOfChannels = config.numberOfChannels,
23 | }: {
24 | sampleRate?: number;
25 | blockSize?: number;
26 | numberOfChannels?: number;
27 | } = {}) {
28 | sampleRate = toValidSampleRate(sampleRate);
29 | blockSize = toValidBlockSize(blockSize);
30 | numberOfChannels = toValidNumberOfChannels(numberOfChannels);
31 |
32 | super({ sampleRate, blockSize, numberOfChannels });
33 |
34 | this.blockSize = blockSize;
35 | this.numberOfChannels = numberOfChannels;
36 | }
37 |
38 | suspend() {
39 | return this._impl.suspend();
40 | }
41 |
42 | createAudioBuffer(
43 | length: number,
44 | sampleRate: number,
45 | channels: Float32Array[],
46 | ): AudioBuffer {
47 | return new AudioBuffer(
48 | new AudioData(channels.length, length, sampleRate, channels),
49 | );
50 | }
51 |
52 | process(channelBuffers: Float32Array[], offset: number = 0): void {
53 | this._impl.process(channelBuffers, offset);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/decoder.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import audioType from 'audio-type';
4 | import WavDecoder from 'wav-decoder';
5 | import * as DecoderUtils from './utils/DecoderUtils';
6 | import * as AudioDataUtils from './utils/AudioDataUtils';
7 | import AudioBuffer from './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(
42 | `Decoder does not support the audio format: ${type || 'unknown'}`,
43 | ),
44 | );
45 | }
46 |
47 | return DecoderUtils.decode(decodeFn, audioData, opts).then((audioData) => {
48 | return AudioDataUtils.toAudioBuffer(audioData, AudioBuffer);
49 | });
50 | }
51 |
52 | function toAudioType(audioData) {
53 | if (!(audioData instanceof Uint8Array)) {
54 | audioData = new Uint8Array(audioData, 0, 16);
55 | }
56 | return audioType(audioData) || '';
57 | }
58 |
59 | set('wav', WavDecoder.decode);
60 |
61 | export { get, set, decode };
62 |
--------------------------------------------------------------------------------
/src/encoder.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import WavEncoder from 'wav-encoder';
4 | import * as EncoderUtils from './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 | export { get, set, encode };
48 |
--------------------------------------------------------------------------------
/src/impl/AudioDestinationNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioNode from './AudioNode';
4 | import { toValidNumberOfChannels } from '../utils';
5 |
6 | import { EXPLICIT } from '../constants/ChannelCountMode';
7 |
8 | /**
9 | * @prop {AudioNodeOutput} output
10 | * @prop {AudioBus} outputBus
11 | */
12 | class AudioDestinationNode extends AudioNode {
13 | /**
14 | * @param {AudioContext} context
15 | * @param {object} opts
16 | * @param {number} opts.numberOfChannels
17 | */
18 | constructor(context, opts = {}) {
19 | let numberOfChannels = opts.numberOfChannels;
20 |
21 | numberOfChannels = toValidNumberOfChannels(numberOfChannels);
22 |
23 | super(context, opts, {
24 | inputs: [numberOfChannels],
25 | outputs: [],
26 | channelCount: numberOfChannels,
27 | channelCountMode: EXPLICIT,
28 | allowedMaxChannelCount: numberOfChannels,
29 | });
30 |
31 | this._numberOfChannels = numberOfChannels | 0;
32 | this._destinationChannelData = this.inputs[0].bus.getChannelData();
33 | }
34 |
35 | /**
36 | * @return {number}
37 | */
38 | getMaxChannelCount() {
39 | return this._numberOfChannels;
40 | }
41 |
42 | /**
43 | * @param {Float32Array[]} channelData
44 | * @param {number} offset
45 | */
46 | process(channelData, offset) {
47 | const inputs = this.inputs;
48 | const destinationChannelData = this._destinationChannelData;
49 | const numberOfChannels = channelData.length;
50 |
51 | for (let i = 0, imax = inputs.length; i < imax; i++) {
52 | inputs[i].pull();
53 | }
54 |
55 | for (let ch = 0; ch < numberOfChannels; ch++) {
56 | channelData[ch].set(destinationChannelData[ch], offset);
57 | }
58 | }
59 | }
60 |
61 | export default AudioDestinationNode;
62 |
--------------------------------------------------------------------------------
/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 | export default AudioListener;
36 |
--------------------------------------------------------------------------------
/src/impl/AudioSourceNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import assert from 'assert';
4 | import AudioNode from './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 | export default AudioSourceNode;
28 |
--------------------------------------------------------------------------------
/src/impl/AudioWorklet.ts:
--------------------------------------------------------------------------------
1 | // adapted from https://github.com/GoogleChromeLabs/audioworklet-polyfill/blob/master/src/index.js
2 |
3 | import { BaseAudioContext } from '../api';
4 | import AudioWorkletProcessor from './AudioWorkletProcessor';
5 |
6 | export interface AudioWorkletOptions {
7 | outputChannelCount: number[];
8 | numberOfInputs: number;
9 | numberOfOutputs: number;
10 |
11 | // for web-audio-js only, register via a processor constructor rather than a url
12 | name: string;
13 | processorCtor: typeof AudioWorkletProcessor;
14 | }
15 |
16 | const workletProcessors = new WeakMap<
17 | BaseAudioContext,
18 | Map
19 | >();
20 |
21 | export function getWorkletProcessor(
22 | audioContext: BaseAudioContext,
23 | name: string,
24 | ): typeof AudioWorkletProcessor | undefined {
25 | return workletProcessors.get(audioContext)?.get(name);
26 | }
27 |
28 | function registerProcessor(
29 | audioContext: BaseAudioContext,
30 | name: string,
31 | Processor: typeof AudioWorkletProcessor,
32 | ): void {
33 | let contextProcessors = workletProcessors.get(audioContext);
34 | if (!contextProcessors) {
35 | contextProcessors = new Map();
36 | workletProcessors.set(audioContext, contextProcessors);
37 | }
38 | contextProcessors.set(name, Processor);
39 | }
40 |
41 | export default class AudioWorklet {
42 | constructor(private readonly audioContext: BaseAudioContext) {
43 | // NOP
44 | }
45 |
46 | async addModule(_url: string, options?: AudioWorkletOptions): Promise {
47 | if (options?.processorCtor && options?.name) {
48 | registerProcessor(this.audioContext, options.name, options.processorCtor);
49 | return null;
50 | }
51 |
52 | throw new Error(
53 | 'Cannot load audio worklet via url in web-audio-js. Pass name and processorCtr in options',
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/impl/AudioWorkletPort.ts:
--------------------------------------------------------------------------------
1 | import type { MessagePort } from 'worker_threads';
2 |
3 | let MC: (new () => { port1: MessagePort; port2: MessagePort }) | undefined;
4 |
5 | export function makeMessageChannel() {
6 | if (!MC) {
7 | MC =
8 | // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
9 | // @ts-ignore
10 | typeof MessageChannel === 'function'
11 | ? // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
12 | // @ts-ignore
13 | MessageChannel
14 | : require('worker_threads')?.MessageChannel;
15 | }
16 | if (!MC) {
17 | throw new Error('cannot create message channel');
18 | }
19 | return new MC();
20 | }
21 |
22 | let nextPort: MessagePort | undefined;
23 |
24 | export function setNextPort(port: MessagePort | undefined): void {
25 | nextPort = port;
26 | }
27 |
28 | export function getNextPort(): MessagePort {
29 | if (!nextPort) {
30 | throw new Error('no port available for AudioWorkletProcessor');
31 | }
32 | return nextPort;
33 | }
34 |
--------------------------------------------------------------------------------
/src/impl/AudioWorkletProcessor.ts:
--------------------------------------------------------------------------------
1 | import type { MessagePort } from 'worker_threads';
2 | import { getNextPort } from './AudioWorkletPort';
3 | import { AudioWorkletOptions } from './AudioWorklet';
4 |
5 | export interface AudioParamDescriptor {
6 | name: string;
7 | automationRate?: 'a-rate' | 'k-rate';
8 | minValue?: number;
9 | maxValue?: number;
10 | defaultValue?: number;
11 | }
12 |
13 | export default class AudioWorkletProcessor {
14 | port: MessagePort;
15 |
16 | sampleRate = 0;
17 | currentTime = 0;
18 |
19 | constructor(_options: Partial) {
20 | this.port = getNextPort();
21 | }
22 |
23 | process(
24 | _input: Float32Array[][],
25 | _output: Float32Array[][],
26 | _parameters: Record,
27 | ): void {
28 | // subclass should implement
29 | }
30 |
31 | static parameterDescriptors: AudioParamDescriptor[];
32 | }
33 |
--------------------------------------------------------------------------------
/src/impl/ChannelMergerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioNode from './AudioNode';
4 | import ChannelMergerNodeDSP from './dsp/ChannelMergerNode';
5 | import { defaults, fill, toValidNumberOfChannels } from '../utils';
6 |
7 | import { EXPLICIT } from '../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(
19 | opts.numberOfInputs,
20 | DEFAULT_NUMBER_OF_INPUTS,
21 | );
22 |
23 | numberOfInputs = toValidNumberOfChannels(numberOfInputs);
24 |
25 | super(context, opts, {
26 | inputs: fill(new Array(numberOfInputs), 1),
27 | outputs: [numberOfInputs],
28 | channelCount: 1,
29 | channelCountMode: EXPLICIT,
30 | allowedMaxChannelCount: 1,
31 | allowedChannelCountMode: [EXPLICIT],
32 | });
33 | }
34 |
35 | disableOutputsIfNecessary() {
36 | // disable if all inputs are disabled
37 |
38 | /* istanbul ignore else */
39 | if (this.isEnabled()) {
40 | const inputs = this.inputs;
41 |
42 | for (let i = 0, imax = inputs.length; i < imax; i++) {
43 | if (inputs[i].isEnabled()) {
44 | return;
45 | }
46 | }
47 |
48 | super.disableOutputsIfNecessary();
49 | }
50 | }
51 | }
52 |
53 | Object.assign(ChannelMergerNode.prototype, ChannelMergerNodeDSP);
54 |
55 | export default ChannelMergerNode;
56 |
--------------------------------------------------------------------------------
/src/impl/ChannelSplitterNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioNode from './AudioNode';
4 | import ChannelSplitterNodeDSP from './dsp/ChannelSplitterNode';
5 | import { defaults, fill, toValidNumberOfChannels } from '../utils';
6 |
7 | import { MAX } from '../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(
19 | opts.numberOfOutputs,
20 | DEFAULT_NUMBER_OF_OUTPUTS,
21 | );
22 |
23 | numberOfOutputs = toValidNumberOfChannels(numberOfOutputs);
24 |
25 | super(context, opts, {
26 | inputs: [1],
27 | outputs: fill(new Array(numberOfOutputs), 1),
28 | channelCount: 2,
29 | channelCountMode: MAX,
30 | });
31 | }
32 | }
33 |
34 | Object.assign(ChannelSplitterNode.prototype, ChannelSplitterNodeDSP);
35 |
36 | export default ChannelSplitterNode;
37 |
--------------------------------------------------------------------------------
/src/impl/ConstantSourceNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioScheduledSourceNode from './AudioScheduledSourceNode';
4 | import ConstantSourceNodeDSP from './dsp/ConstantSourceNode';
5 | import { defaults } from '../utils';
6 |
7 | import { MAX } from '../constants/ChannelCountMode';
8 |
9 | import { AUDIO_RATE } from '../constants/AudioParamRate';
10 |
11 | const DEFAULT_OFFSET = 1;
12 |
13 | class ConstantSourceNode extends AudioScheduledSourceNode {
14 | /**
15 | * @param {AudioContext} context
16 | * @param {object} opts
17 | * @param {number} opts.offset
18 | */
19 | constructor(context, opts = {}) {
20 | const offset = defaults(opts.offset, DEFAULT_OFFSET);
21 |
22 | super(context, opts, {
23 | inputs: [1],
24 | outputs: [1],
25 | channelCount: 2,
26 | channelCountMode: MAX,
27 | });
28 |
29 | this._offset = this.addParam(AUDIO_RATE, offset);
30 | }
31 |
32 | /**
33 | * @return {AudioParam}
34 | */
35 | getOffset() {
36 | return this._offset;
37 | }
38 | }
39 |
40 | Object.assign(ConstantSourceNode.prototype, ConstantSourceNodeDSP);
41 |
42 | export default ConstantSourceNode;
43 |
--------------------------------------------------------------------------------
/src/impl/ConvolverNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioNode from './AudioNode';
4 | import AudioBuffer from './AudioBuffer';
5 | import ConvolverNodeDSP from './dsp/ConvolverNode';
6 | import { defaults, toImpl } from '../utils';
7 |
8 | import { CLAMPED_MAX, EXPLICIT } from '../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 | const disableNormalization = defaults(
20 | opts.disableNormalization,
21 | DEFAULT_DISABLE_NORMALIZATION,
22 | );
23 |
24 | super(context, opts, {
25 | inputs: [1],
26 | outputs: [1],
27 | channelCount: 2,
28 | channelCountMode: CLAMPED_MAX,
29 | allowedMaxChannelCount: 2,
30 | allowedChannelCountMode: [CLAMPED_MAX, EXPLICIT],
31 | });
32 |
33 | this._buffer = null;
34 | this._audioData = null;
35 | this._normalize = !disableNormalization;
36 | }
37 |
38 | /**
39 | * @return {AudioBuffer}
40 | */
41 | getBuffer() {
42 | return this._buffer;
43 | }
44 |
45 | /**
46 | * @param {AudioBuffer} value
47 | */
48 | setBuffer(value) {
49 | value = toImpl(value);
50 |
51 | /* istanbul ignore else */
52 | if (value instanceof AudioBuffer) {
53 | this._buffer = value;
54 | this._audioData = this._buffer.audioData;
55 | }
56 | }
57 |
58 | /**
59 | * @return {boolean}
60 | */
61 | getNormalize() {
62 | return this._normalize;
63 | }
64 |
65 | /**
66 | * @param {boolean} value
67 | */
68 | setNormalize(value) {
69 | this._normalize = !!value;
70 | }
71 |
72 | /**
73 | * @param {number} numberOfChannels
74 | */
75 | channelDidUpdate(numberOfChannels) {
76 | numberOfChannels = Math.min(numberOfChannels, 2);
77 |
78 | this.outputs[0].setNumberOfChannels(numberOfChannels);
79 | }
80 | }
81 |
82 | Object.assign(ConvolverNode.prototype, ConvolverNodeDSP);
83 |
84 | export default ConvolverNode;
85 |
--------------------------------------------------------------------------------
/src/impl/DelayNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioNode from './AudioNode';
4 | import DelayNodeDSP from './dsp/DelayNode';
5 | import { defaults, toNumber } from '../utils';
6 |
7 | import { MAX } from '../constants/ChannelCountMode';
8 |
9 | import { AUDIO_RATE } from '../constants/AudioParamRate';
10 |
11 | const DEFAULT_MAX_DELAY_TIME = 1;
12 | const DEFAULT_DELAY_TIME = 0;
13 |
14 | class DelayNode extends AudioNode {
15 | /**
16 | * @param {AudioContext} context
17 | * @param {object} opts
18 | * @param {number} opts.maxDelayTime
19 | * @param {number} opts.delayTime
20 | */
21 | constructor(context, opts = {}) {
22 | let maxDelayTime = defaults(opts.maxDelayTime, DEFAULT_MAX_DELAY_TIME);
23 | let delayTime = defaults(opts.delayTime, DEFAULT_DELAY_TIME);
24 |
25 | maxDelayTime = Math.max(0, toNumber(maxDelayTime));
26 | delayTime = Math.min(delayTime, maxDelayTime);
27 |
28 | super(context, opts, {
29 | inputs: [1],
30 | outputs: [1],
31 | channelCount: 2,
32 | channelCountMode: MAX,
33 | });
34 |
35 | this._maxDelayTime = maxDelayTime;
36 | this._delayTime = this.addParam(AUDIO_RATE, delayTime);
37 |
38 | this.dspInit(this._maxDelayTime);
39 | this.dspUpdateKernel(1);
40 | }
41 |
42 | /**
43 | * @return {number}
44 | */
45 | getDelayTime() {
46 | return this._delayTime;
47 | }
48 |
49 | /**
50 | * @return {number}
51 | */
52 | getMaxDelayTime() {
53 | return this._maxDelayTime;
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 | * @return {number}
66 | */
67 | getTailTime() {
68 | return this._maxDelayTime;
69 | }
70 | }
71 |
72 | Object.assign(DelayNode.prototype, DelayNodeDSP);
73 |
74 | export default DelayNode;
75 |
--------------------------------------------------------------------------------
/src/impl/EventTarget.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import events from '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 | export default EventTarget;
52 |
--------------------------------------------------------------------------------
/src/impl/GainNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioNode from './AudioNode';
4 | import GainNodeDSP from './dsp/GainNode';
5 | import { defaults } from '../utils';
6 |
7 | import { MAX } from '../constants/ChannelCountMode';
8 |
9 | import { AUDIO_RATE } from '../constants/AudioParamRate';
10 |
11 | const DEFAULT_GAIN = 1;
12 |
13 | class GainNode extends AudioNode {
14 | /**
15 | * @param {AudioContext} context
16 | * @param {object} opts
17 | * @param {number} opts.gain
18 | */
19 | constructor(context, opts = {}) {
20 | const gain = defaults(opts.gain, DEFAULT_GAIN);
21 |
22 | super(context, opts, {
23 | inputs: [1],
24 | outputs: [1],
25 | channelCount: 2,
26 | channelCountMode: MAX,
27 | });
28 |
29 | this._gain = this.addParam(AUDIO_RATE, gain);
30 | }
31 |
32 | /**
33 | * @return {AudioParam}
34 | */
35 | getGain() {
36 | return this._gain;
37 | }
38 |
39 | /**
40 | * @param {number} numberOfChannels
41 | */
42 | channelDidUpdate(numberOfChannels) {
43 | this.outputs[0].setNumberOfChannels(numberOfChannels);
44 | }
45 | }
46 |
47 | Object.assign(GainNode.prototype, GainNodeDSP);
48 |
49 | export default GainNode;
50 |
--------------------------------------------------------------------------------
/src/impl/IIRFilterNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioNode from './AudioNode';
4 | import IIRFilterNodeDSP from './dsp/IIRFilterNode';
5 | import { defaults } from '../utils';
6 |
7 | import { MAX } from '../constants/ChannelCountMode';
8 |
9 | class IIRFilterNode extends AudioNode {
10 | /**
11 | * @param {AudioContext} context
12 | * @param {object} opts
13 | * @param {Float32Array} opts.feedforward
14 | * @param {Float32Array} opts.feedback
15 | */
16 | constructor(context, opts = {}) {
17 | const feedforward = defaults(opts.feedforward, [0]);
18 | const feedback = defaults(opts.feedback, [1]);
19 |
20 | super(context, opts, {
21 | inputs: [1],
22 | outputs: [1],
23 | channelCount: 2,
24 | channelCountMode: MAX,
25 | });
26 |
27 | this._feedforward = feedforward;
28 | this._feedback = feedback;
29 |
30 | this.dspInit();
31 | this.dspUpdateKernel(1);
32 | }
33 |
34 | /**
35 | * @param {Float32Array} frequencyHz
36 | * @param {Float32Array} magResponse
37 | * @param {Float32Array} phaseResponse
38 | */
39 | getFrequencyResponse(frequencyHz, magResponse, phaseResponse) {
40 | this.dspGetFrequencyResponse(frequencyHz, magResponse, phaseResponse);
41 | }
42 |
43 | /**
44 | * @return {Float32Array}
45 | */
46 | getFeedforward() {
47 | return this._feedforward;
48 | }
49 |
50 | /**
51 | * @return {Float32Array}
52 | */
53 | getFeedback() {
54 | return this._feedback;
55 | }
56 |
57 | /**
58 | * @param {number} numberOfChannels
59 | */
60 | channelDidUpdate(numberOfChannels) {
61 | this.dspUpdateKernel(numberOfChannels);
62 | this.outputs[0].setNumberOfChannels(numberOfChannels);
63 | }
64 | }
65 |
66 | Object.assign(IIRFilterNode.prototype, IIRFilterNodeDSP);
67 |
68 | export default IIRFilterNode;
69 |
--------------------------------------------------------------------------------
/src/impl/PannerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import BasePannerNode from './BasePannerNode';
4 | import PannerNodeDSP from './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 | export default PannerNode;
48 |
--------------------------------------------------------------------------------
/src/impl/SpatialPannerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import BasePannerNode from './BasePannerNode';
4 | import SpatialPannerNodeDSP from './dsp/SpatialPannerNode';
5 | import { AUDIO_RATE } from '../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 | export default SpatialPannerNode;
68 |
--------------------------------------------------------------------------------
/src/impl/StereoPannerNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import BasePannerNode from './BasePannerNode';
4 | import StereoPannerNodeDSP from './dsp/StereoPannerNode';
5 | import { defaults } from '../utils';
6 |
7 | import { AUDIO_RATE } from '../constants/AudioParamRate';
8 |
9 | const DEFAULT_PAN = 0;
10 |
11 | class StereoPannerNode extends BasePannerNode {
12 | /**
13 | * @param {AudioContext} context
14 | * @param {object} opts
15 | * @param {number} opts.pan
16 | */
17 | constructor(context, opts = {}) {
18 | const pan = defaults(opts.pan, DEFAULT_PAN);
19 |
20 | super(context, opts);
21 |
22 | this._pan = this.addParam(AUDIO_RATE, pan);
23 | }
24 |
25 | /**
26 | * @param {AudioParam}
27 | */
28 | getPan() {
29 | return this._pan;
30 | }
31 | }
32 |
33 | Object.assign(StereoPannerNode.prototype, StereoPannerNodeDSP);
34 |
35 | export default StereoPannerNode;
36 |
--------------------------------------------------------------------------------
/src/impl/WaveShaperNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import AudioNode from './AudioNode';
4 | import WaveShaperNodeDSP from './dsp/WaveShaperNode';
5 | import { defaults } from '../utils';
6 |
7 | import { MAX } from '../constants/ChannelCountMode';
8 |
9 | const OverSampleTypes = ['none', '2x', '4x'];
10 |
11 | const DEFAULT_CURVE = null;
12 | const DEFAULT_OVERSAMPLE = 'none';
13 |
14 | class WaveShaperNode extends AudioNode {
15 | /**
16 | * @param {AudioContext} context
17 | * @param {object} opts
18 | * @param {Float32Arrat} opts.curve
19 | * @param {string} opts.overSample
20 | */
21 | constructor(context, opts = {}) {
22 | const curve = defaults(opts.curve, DEFAULT_CURVE);
23 | const overSample = defaults(opts.overSample, DEFAULT_OVERSAMPLE);
24 |
25 | super(context, opts, {
26 | inputs: [1],
27 | outputs: [1],
28 | channelCount: 2,
29 | channelCountMode: MAX,
30 | });
31 |
32 | this._curve = curve;
33 | this._overSample = overSample;
34 |
35 | this.dspInit();
36 | this.dspUpdateKernel(null, 1);
37 | }
38 |
39 | /**
40 | * @return {Float32Array}
41 | */
42 | getCurve() {
43 | return this._curve;
44 | }
45 |
46 | /**
47 | * @param {Float32Array} value
48 | */
49 | setCurve(value) {
50 | /* istanbul ignore else */
51 | if (value === null || value instanceof Float32Array) {
52 | this._curve = value;
53 | this.dspUpdateKernel(this._curve, this.outputs[0].getNumberOfChannels());
54 | }
55 | }
56 |
57 | /**
58 | * @return {boolean}
59 | */
60 | getOversample() {
61 | return this._overSample;
62 | }
63 |
64 | /**
65 | * @param {boolean} value
66 | */
67 | setOversample(value) {
68 | /* istanbul ignore else */
69 | if (OverSampleTypes.indexOf(value) !== -1) {
70 | this._overSample = value;
71 | }
72 | }
73 |
74 | /**
75 | * @param {number} numberOfChannels
76 | */
77 | channelDidUpdate(numberOfChannels) {
78 | this.dspUpdateKernel(this._curve, numberOfChannels);
79 | this.outputs[0].setNumberOfChannels(numberOfChannels);
80 | }
81 | }
82 |
83 | Object.assign(WaveShaperNode.prototype, WaveShaperNodeDSP);
84 |
85 | export default WaveShaperNode;
86 |
--------------------------------------------------------------------------------
/src/impl/core/AudioData.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { nmap } from '../../utils/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 | * @param {Float32Array[]} [channelData]
20 | */
21 | constructor(numberOfChannels, length, sampleRate, channelData) {
22 | this.numberOfChannels = numberOfChannels | 0;
23 | this.length = length | 0;
24 | this.sampleRate = sampleRate | 0;
25 | this.channelData =
26 | channelData ||
27 | nmap(this.numberOfChannels, () => new Float32Array(this.length));
28 | }
29 | }
30 |
31 | export default AudioData;
32 |
--------------------------------------------------------------------------------
/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 | export default 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 | export default ChannelSplitterNodeDSP;
29 |
--------------------------------------------------------------------------------
/src/impl/dsp/ConstantSourceNode.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { fill } from '../../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 | export default 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 | export default ConvolverNodeDSP;
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 | export default IIRFilterKernel;
53 |
--------------------------------------------------------------------------------
/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 | export default 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 | const 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 | export default 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 | export default SpatialPannerNodeDSP;
13 |
--------------------------------------------------------------------------------
/src/impl/index.js:
--------------------------------------------------------------------------------
1 | export { default as AnalyserNode } from './AnalyserNode';
2 | export { default as AudioBuffer } from './AudioBuffer';
3 | export { default as AudioBufferSourceNode } from './AudioBufferSourceNode';
4 | export { default as AudioContext } from './AudioContext';
5 | export { default as AudioDestinationNode } from './AudioDestinationNode';
6 | export { default as AudioListener } from './AudioListener';
7 | export { default as AudioNode } from './AudioNode';
8 | export { default as AudioParam } from './AudioParam';
9 | export { default as AudioWorklet } from './AudioWorklet';
10 | export { default as AudioWorkletNode } from './AudioWorkletNode';
11 | export { default as AudioWorkletProcessor } from './AudioWorkletProcessor';
12 | export { default as BiquadFilterNode } from './BiquadFilterNode';
13 | export { default as ChannelMergerNode } from './ChannelMergerNode';
14 | export { default as ChannelSplitterNode } from './ChannelSplitterNode';
15 | export { default as ConstantSourceNode } from './ConstantSourceNode';
16 | export { default as ConvolverNode } from './ConvolverNode';
17 | export { default as DelayNode } from './DelayNode';
18 | export { default as DynamicsCompressorNode } from './DynamicsCompressorNode';
19 | export { default as GainNode } from './GainNode';
20 | export { default as IIRFilterNode } from './IIRFilterNode';
21 | export { default as OscillatorNode } from './OscillatorNode';
22 | export { default as PannerNode } from './PannerNode';
23 | export { default as PeriodicWave } from './PeriodicWave';
24 | export { default as ScriptProcessorNode } from './ScriptProcessorNode';
25 | export { default as SpatialPannerNode } from './SpatialPannerNode';
26 | export { default as StereoPannerNode } from './StereoPannerNode';
27 | export { default as WaveShaperNode } from './WaveShaperNode';
28 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export { default as OfflineAudioContext } from './context/OfflineAudioContext';
4 | export { default as StreamAudioContext } from './context/StreamAudioContext';
5 | export { default as RenderingAudioContext } from './context/RenderingAudioContext';
6 | export { default as WebAudioContext } from './context/WebAudioContext';
7 | export { RawDataAudioContext } from './context/RawDataAudioContext';
8 | import * as api from './api';
9 | import * as impl from './impl';
10 | import * as decoder from './decoder';
11 | import * as encoder from './encoder';
12 |
13 | export { api, impl, decoder, encoder };
14 |
--------------------------------------------------------------------------------
/src/utils/DecoderUtils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import { nmap } from '../utils/nmap';
4 | import * as AudioDataUtils from './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 | export { decode, resample };
58 |
--------------------------------------------------------------------------------
/src/utils/Encoder.ts:
--------------------------------------------------------------------------------
1 | export type Encoder = {
2 | encode(
3 | channelData: Float32Array[],
4 | offset?: number,
5 | len?: number,
6 | ): ArrayBuffer;
7 | };
8 |
--------------------------------------------------------------------------------
/src/utils/EncoderUtils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as AudioDataUtils from './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 | export { encode };
18 |
--------------------------------------------------------------------------------
/src/utils/FilterUtils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function getFilterResponse(
4 | b,
5 | a,
6 | frequencyHz,
7 | magResponse,
8 | phaseResponse,
9 | sampleRate,
10 | ) {
11 | for (let i = 0, imax = frequencyHz.length; i < imax; i++) {
12 | const w0 = 2 * Math.PI * (frequencyHz[i] / sampleRate);
13 | const ca = compute(a, Math.cos, w0);
14 | const sa = compute(a, Math.sin, w0);
15 | const cb = compute(b, Math.cos, w0);
16 | const sb = compute(b, Math.sin, w0);
17 |
18 | magResponse[i] = Math.sqrt((cb * cb + sb * sb) / (ca * ca + sa * sa));
19 | phaseResponse[i] = Math.atan2(sa, ca) - Math.atan2(sb, cb);
20 | }
21 | }
22 |
23 | function compute(values, fn, w0) {
24 | let result = 0;
25 |
26 | for (let i = 0, imax = values.length; i < imax; i++) {
27 | result += values[i] * fn(w0 * i);
28 | }
29 |
30 | return result;
31 | }
32 |
33 | export { getFilterResponse };
34 |
--------------------------------------------------------------------------------
/src/utils/Format.ts:
--------------------------------------------------------------------------------
1 | export type Format = {
2 | sampleRate: number;
3 | channels: number;
4 | bitDepth: number;
5 | float: boolean;
6 | };
7 |
--------------------------------------------------------------------------------
/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 | export default 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 | export default PCMBufferWriter;
37 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export { default as clamp } from './utils/clamp';
4 | export { default as defaults } from './utils/defaults';
5 | export { default as defineProp } from './utils/defineProp';
6 | export { default as fill } from './utils/fill';
7 | export { default as fillRange } from './utils/fillRange';
8 | export { default as normalize } from './utils/normalize';
9 | export { default as toArrayIfNeeded } from './utils/toArrayIfNeeded';
10 | export { default as toAudioTime } from './utils/toAudioTime';
11 | export { default as toDecibel } from './utils/toDecibel';
12 | export { default as toLinear } from './utils/toLinear';
13 | export { default as flushDenormalFloatToZero } from './utils/flushDenormalFloatToZero';
14 | export { default as toGain } from './utils/toGain';
15 | export { default as toImpl } from './utils/toImpl';
16 | export { default as toNumber } from './utils/toNumber';
17 | export { default as toPowerOfTwo } from './utils/toPowerOfTwo';
18 | export { default as toValidBitDepth } from './utils/toValidBitDepth';
19 | export { default as toValidBlockSize } from './utils/toValidBlockSize';
20 | export { default as toValidNumberOfChannels } from './utils/toValidNumberOfChannels';
21 | export { default as toValidSampleRate } from './utils/toValidSampleRate';
22 |
--------------------------------------------------------------------------------
/src/utils/nmap.ts:
--------------------------------------------------------------------------------
1 | export function nmap(
2 | n: number,
3 | map: (i: number, i2: number, arr: T[]) => T,
4 | ) {
5 | const result = new Array(n);
6 | for (let i = 0; i < n; i++) {
7 | result[i] = map(i, i, result);
8 | }
9 | return result;
10 | }
11 |
--------------------------------------------------------------------------------
/src/utils/setImmediate.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export default global.setImmediate /* istanbul ignore next */ ||
4 | ((fn) => setTimeout(fn, 0));
5 |
--------------------------------------------------------------------------------
/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 | export default 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 | export default 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, {
10 | value: value,
11 | enumerable: false,
12 | writable: true,
13 | configurable: true,
14 | });
15 | }
16 |
17 | export default defineProp;
18 |
--------------------------------------------------------------------------------
/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 | export default 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 | export default fillRange;
23 |
--------------------------------------------------------------------------------
/src/utils/utils/flushDenormalFloatToZero.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * @param {number} f
5 | * @returns {number}
6 | */
7 | function flushDenormalFloatToZero(f) {
8 | return Math.abs(f) < 1.175494e-38 ? 0.0 : f;
9 | }
10 |
11 | export default flushDenormalFloatToZero;
12 |
--------------------------------------------------------------------------------
/src/utils/utils/normalize.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import clamp from './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 | export default 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 | export default 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 | export default 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 | export default 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 | export default 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 | export default toImpl;
12 |
--------------------------------------------------------------------------------
/src/utils/utils/toLinear.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * @param {*} decibels
5 | * @return {number}
6 | */
7 | function toLinear(decibels) {
8 | return Math.pow(10, 0.05 * decibels);
9 | }
10 |
11 | export default toLinear;
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 | export default 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 | export default toPowerOfTwo;
14 |
--------------------------------------------------------------------------------
/src/utils/utils/toValidBitDepth.js:
--------------------------------------------------------------------------------
1 | 'use strict';
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 | export default toValidBitDepth;
16 |
--------------------------------------------------------------------------------
/src/utils/utils/toValidBlockSize.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import clamp from './clamp';
4 | import toPowerOfTwo from './toPowerOfTwo';
5 | import { MAX_BLOCK_SIZE, MIN_BLOCK_SIZE } from '../../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 | export default toValidBlockSize;
16 |
--------------------------------------------------------------------------------
/src/utils/utils/toValidNumberOfChannels.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toNumber from './toNumber';
4 | import clamp from './clamp';
5 | import { MAX_NUMBER_OF_CHANNELS } from '../../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 | export default toValidNumberOfChannels;
16 |
--------------------------------------------------------------------------------
/src/utils/utils/toValidSampleRate.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import toNumber from './toNumber';
4 | import clamp from './clamp';
5 | import { MAX_SAMPLERATE, MIN_SAMPLERATE } from '../../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 | export default toValidSampleRate;
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "module": "es2015",
5 | "moduleResolution": "node",
6 | "lib": ["es2015"],
7 |
8 | "rootDir": "src",
9 | "outDir": "lib",
10 |
11 | "strict": true,
12 | "alwaysStrict": true,
13 | "strictFunctionTypes": true,
14 | "strictNullChecks": true,
15 | "strictPropertyInitialization": true,
16 | "esModuleInterop": true,
17 |
18 | "allowJs": true,
19 |
20 | "forceConsistentCasingInFileNames": true,
21 | "noImplicitAny": true,
22 | "noImplicitReturns": true,
23 | "noImplicitThis": true,
24 | "noFallthroughCasesInSwitch": true,
25 | "noUnusedLocals": true,
26 | "noUnusedParameters": true,
27 |
28 | "declaration": true,
29 |
30 | "pretty": true
31 | },
32 | "include": ["src/**/*"]
33 | }
34 |
--------------------------------------------------------------------------------