Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions examples/loader/polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import path from 'node:path';
import fs from 'node:fs';
import { createRequire } from 'node:module';

// make it sync so that we don't run into weird race conditions in tone.js example
const require = createRequire(import.meta.url);
const examplesDirname = path.join(import.meta.dirname, '..');
const examplesNodeModules = path.join(examplesDirname, 'node_modules');
const nodeModulesExists = fs.existsSync(examplesNodeModules);

if (nodeModulesExists) {
console.log('> loading installed polyfills');
require('node-web-audio-api/polyfill.js');;
} else {
console.log('> loading local polyfills');
require('../../polyfill.js');
}
3 changes: 2 additions & 1 deletion examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"name": "node-web-audio-api-examples",
"type": "module",
"imports": {
"#node-web-audio-api": "./loader/index.js"
"#node-web-audio-api": "./loader/index.js",
"#node-web-audio-api-polyfill": "./loader/polyfill.js"
},
"dependencies": {
"node-web-audio-api": "latest"
Expand Down
27 changes: 27 additions & 0 deletions examples/tone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// polyfill must be loaded first
import '#node-web-audio-api-polyfill';
import { sleep } from '@ircam/sc-utils';
import * as Tone from 'tone';

const audioContext = new window.AudioContext();
Tone.setContext(audioContext);

// Example adapted from https://tonejs.github.io/#scheduling
const synthA = new Tone.FMSynth().toDestination();
const synthB = new Tone.AMSynth().toDestination();
//play a note every quarter-note
new Tone.Loop((time) => {
synthA.triggerAttackRelease('C2', '8n', time);
}, '4n').start(0);
//play another note every off quarter-note, by starting it '8n'
new Tone.Loop((time) => {
synthB.triggerAttackRelease('C4', '8n', time);
}, '4n').start('8n');
// all loops start when the Transport is started
Tone.getTransport().start();
// ramp up to 800 bpm over 10 seconds
Tone.getTransport().bpm.rampTo(800, 10);

await sleep(10);
// don't understand how to properly stop tone.js, so let's be radical...
process.exit(0);
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@
"author": "Benjamin Matuszewski",
"description": "Web Audio API implementation for Node.js",
"exports": {
"default": "./index.js",
"types": "./index.d.ts"
".": {
"default": "./index.js",
"types": "./index.d.ts"
},
"./polyfill.js": {
"default": "./polyfill.js",
"types": "./index.d.ts"
}
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -68,6 +74,7 @@
"mocha": "^11.0.1",
"octokit": "^5.0.5",
"template-literal": "^1.0.4",
"tone": "^15.1.22",
"webidl2": "^24.2.0",
"wpt-runner": "^7.0.0"
},
Expand Down
16 changes: 16 additions & 0 deletions polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import webaudio from 'node-web-audio-api';

Object.assign(globalThis, webaudio);

// Some libraries explicitly rely on window
if (!globalThis.window) {
globalThis.window = {};
}

for (let name in webaudio) {
if (name !== 'mediaDevices') {
globalThis.window[name] = webaudio[name];
}
}

globalThis.navigator.mediaDevices = webaudio.mediaDevices;
84 changes: 84 additions & 0 deletions tests/import-polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { assert } from 'chai';
import '../polyfill.js';

describe('should properly support module syntax', () => {
it(`in globalThis`, () => {
assert.isDefined(OfflineAudioCompletionEvent);
assert.isDefined(AudioProcessingEvent);
assert.isDefined(AudioRenderCapacityEvent);
assert.isDefined(ErrorEvent);
assert.isDefined(AudioBuffer);
assert.isDefined(PeriodicWave);
assert.isDefined(AudioParam);
assert.isDefined(AudioListener);
assert.isDefined(AudioNode);
assert.isDefined(AudioDestinationNode);
assert.isDefined(AudioParamMap);
assert.isDefined(AudioPlaybackStats);
assert.isDefined(AudioRenderCapacity);
assert.isDefined(AudioScheduledSourceNode);
assert.isDefined(AudioWorklet);
assert.isDefined(BaseAudioContext);
assert.isDefined(AudioContext);
assert.isDefined(OfflineAudioContext);
assert.isDefined(AudioWorkletNode);
assert.isDefined(ScriptProcessorNode);
assert.isDefined(AnalyserNode);
assert.isDefined(AudioBufferSourceNode);
assert.isDefined(BiquadFilterNode);
assert.isDefined(ChannelMergerNode);
assert.isDefined(ChannelSplitterNode);
assert.isDefined(ConstantSourceNode);
assert.isDefined(ConvolverNode);
assert.isDefined(DelayNode);
assert.isDefined(DynamicsCompressorNode);
assert.isDefined(GainNode);
assert.isDefined(IIRFilterNode);
assert.isDefined(MediaStreamAudioSourceNode);
assert.isDefined(OscillatorNode);
assert.isDefined(PannerNode);
assert.isDefined(StereoPannerNode);
assert.isDefined(WaveShaperNode);
assert.isDefined(mediaDevices);
});

it(`in window & navigator`, () => {
assert.isDefined(window.OfflineAudioCompletionEvent);
assert.isDefined(window.AudioProcessingEvent);
assert.isDefined(window.AudioRenderCapacityEvent);
assert.isDefined(window.ErrorEvent);
assert.isDefined(window.AudioBuffer);
assert.isDefined(window.PeriodicWave);
assert.isDefined(window.AudioParam);
assert.isDefined(window.AudioListener);
assert.isDefined(window.AudioNode);
assert.isDefined(window.AudioDestinationNode);
assert.isDefined(window.AudioParamMap);
assert.isDefined(window.AudioPlaybackStats);
assert.isDefined(window.AudioRenderCapacity);
assert.isDefined(window.AudioScheduledSourceNode);
assert.isDefined(window.AudioWorklet);
assert.isDefined(window.BaseAudioContext);
assert.isDefined(window.AudioContext);
assert.isDefined(window.OfflineAudioContext);
assert.isDefined(window.ScriptProcessorNode);
assert.isDefined(window.AudioWorkletNode);
assert.isDefined(window.AnalyserNode);
assert.isDefined(window.AudioBufferSourceNode);
assert.isDefined(window.BiquadFilterNode);
assert.isDefined(window.ChannelMergerNode);
assert.isDefined(window.ChannelSplitterNode);
assert.isDefined(window.ConstantSourceNode);
assert.isDefined(window.ConvolverNode);
assert.isDefined(window.DelayNode);
assert.isDefined(window.DynamicsCompressorNode);
assert.isDefined(window.GainNode);
assert.isDefined(window.IIRFilterNode);
assert.isDefined(window.MediaStreamAudioSourceNode);
assert.isDefined(window.OscillatorNode);
assert.isDefined(window.PannerNode);
assert.isDefined(window.StereoPannerNode);
assert.isDefined(window.WaveShaperNode);
assert.isDefined(navigator.mediaDevices);
});
});
Loading