node-web-audio-api
Version:
Web Audio API implementation for Node.js
458 lines (359 loc) • 15.1 kB
JavaScript
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// //
// //
// //
// ██╗ ██╗ █████╗ ██████╗ ███╗ ██╗██╗███╗ ██╗ ██████╗ //
// ██║ ██║██╔══██╗██╔══██╗████╗ ██║██║████╗ ██║██╔════╝ //
// ██║ █╗ ██║███████║██████╔╝██╔██╗ ██║██║██╔██╗ ██║██║ ███╗ //
// ██║███╗██║██╔══██║██╔══██╗██║╚██╗██║██║██║╚██╗██║██║ ██║ //
// ╚███╔███╔╝██║ ██║██║ ██║██║ ╚████║██║██║ ╚████║╚██████╔╝ //
// ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═══╝ ╚═════╝ //
// //
// //
// - This file has been generated --------------------------- //
// //
// //
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
const {
isFunction,
kEnumerableProperty,
kHiddenProperty,
} = require('./lib/utils.js');
const {
kNapiObj,
kPrivateConstructor,
} = require('./lib/symbols.js');
const AudioWorklet = require('./AudioWorklet.js');
module.exports = (jsExport, _nativeBinding) => {
class BaseAudioContext extends EventTarget {
#audioWorklet = null;
#destination = null;
#listener = null;
constructor(options) {
// Make constructor "private"
if (
(typeof options !== 'object') ||
!(kNapiObj in options)
) {
throw new TypeError('Illegal constructor');
}
super();
Object.defineProperty(this, kNapiObj, {
value: options[kNapiObj],
...kHiddenProperty,
});
this.#audioWorklet = new AudioWorklet({
[kPrivateConstructor]: true,
workletId: this[kNapiObj].workletId,
sampleRate: this[kNapiObj].sampleRate,
});
this.#destination = new jsExport.AudioDestinationNode(this, {
[kNapiObj]: this[kNapiObj].destination,
});
}
get audioWorklet() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
return this.#audioWorklet;
}
get destination() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
return this.#destination;
}
get listener() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
if (this.#listener === null) {
this.#listener = new jsExport.AudioListener({
[kNapiObj]: this[kNapiObj].listener,
});
}
return this.#listener;
}
get sampleRate() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
return this[kNapiObj].sampleRate;
}
get currentTime() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
return this[kNapiObj].currentTime;
}
// @todo - implement in upstream crate + pass to AudioWorkletGlobalScope
// get renderQuantumSize() {
// if (!(this instanceof BaseAudioContext)) {
// throw new TypeError("Invalid Invocation: Value of 'this' must be of type 'BaseAudioContext'");
// }
// return this[kNapiObj].renderQuantumSize;
// }
get state() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
return this[kNapiObj].state;
}
get onstatechange() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
return this._statechange || null;
}
set onstatechange(value) {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
if (isFunction(value) || value === null) {
this._statechange = value;
}
}
// This is not exactly what the spec says, but if we reject the promise
// when decodeErrorCallback is present the program will crash in an
// unexpected manner
// cf. https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-decodeaudiodata
async decodeAudioData(arrayBuffer, decodeSuccessCallback = undefined, decodeErrorCallback = undefined) {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
if (arguments.length < 1) {
throw new TypeError(`Failed to execute 'decodeAudioData' on 'BaseAudioContext': 1 argument required, but only ${arguments.length} present`);
}
if (!(arrayBuffer instanceof ArrayBuffer)) {
throw new TypeError('Failed to execute "decodeAudioData": parameter 1 is not of type "ArrayBuffer"');
}
try {
const nativeAudioBuffer = await this[kNapiObj].decodeAudioData(arrayBuffer);
const audioBuffer = new jsExport.AudioBuffer({
[kNapiObj]: nativeAudioBuffer,
});
if (isFunction(decodeSuccessCallback)) {
decodeSuccessCallback(audioBuffer);
} else {
return audioBuffer;
}
} catch (err) {
const error = new DOMException(`Failed to execute 'decodeAudioData': ${err.message}`, 'EncodingError');
if (isFunction(decodeErrorCallback)) {
decodeErrorCallback(error);
} else {
throw error;
}
}
}
createBuffer(numberOfChannels, length, sampleRate) {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
if (arguments.length < 3) {
throw new TypeError(`Failed to execute 'createBuffer' on 'BaseAudioContext': 3 argument required, but only ${arguments.length} present`);
}
const options = {};
if (numberOfChannels !== undefined) {
options.numberOfChannels = numberOfChannels;
}
if (length !== undefined) {
options.length = length;
}
if (sampleRate !== undefined) {
options.sampleRate = sampleRate;
}
return new jsExport.AudioBuffer(options);
}
createPeriodicWave(real, imag) {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
if (arguments.length < 2) {
throw new TypeError(`Failed to execute 'createPeriodicWave' on 'BaseAudioContext': 2 argument required, but only ${arguments.length} present`);
}
const options = {};
if (real !== undefined) {
options.real = real;
}
if (imag !== undefined) {
options.imag = imag;
}
return new jsExport.PeriodicWave(this, options);
}
// --------------------------------------------------------------------
// Factory Methods (use the patched AudioNodes)
// --------------------------------------------------------------------
createScriptProcessor(bufferSize = 0, numberOfInputChannels = 2, numberOfOutputChannels = 2) {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {
bufferSize,
numberOfInputChannels,
numberOfOutputChannels,
};
return new jsExport.ScriptProcessorNode(this, options);
}
createAnalyser() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {};
return new jsExport.AnalyserNode(this, options);
}
createBufferSource() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {};
return new jsExport.AudioBufferSourceNode(this, options);
}
createBiquadFilter() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {};
return new jsExport.BiquadFilterNode(this, options);
}
createChannelMerger(numberOfInputs = 6) {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {
numberOfInputs,
};
return new jsExport.ChannelMergerNode(this, options);
}
createChannelSplitter(numberOfOutputs = 6) {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {
numberOfOutputs,
};
return new jsExport.ChannelSplitterNode(this, options);
}
createConstantSource() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {};
return new jsExport.ConstantSourceNode(this, options);
}
createConvolver() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {};
return new jsExport.ConvolverNode(this, options);
}
createDelay(maxDelayTime = 1.0) {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {
maxDelayTime,
};
return new jsExport.DelayNode(this, options);
}
createDynamicsCompressor() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {};
return new jsExport.DynamicsCompressorNode(this, options);
}
createGain() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {};
return new jsExport.GainNode(this, options);
}
createIIRFilter(feedforward, feedback) {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {
feedforward,
feedback,
};
return new jsExport.IIRFilterNode(this, options);
}
createOscillator() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {};
return new jsExport.OscillatorNode(this, options);
}
createPanner() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {};
return new jsExport.PannerNode(this, options);
}
createStereoPanner() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {};
return new jsExport.StereoPannerNode(this, options);
}
createWaveShaper() {
if (!(this instanceof BaseAudioContext)) {
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
}
const options = {};
return new jsExport.WaveShaperNode(this, options);
}
}
Object.defineProperties(BaseAudioContext, {
length: {
__proto__: null,
writable: false,
enumerable: false,
configurable: true,
value: 0,
},
});
Object.defineProperties(BaseAudioContext.prototype, {
[Symbol.toStringTag]: {
__proto__: null,
writable: false,
enumerable: false,
configurable: true,
value: 'BaseAudioContext',
},
createScriptProcessor: kEnumerableProperty,
createAnalyser: kEnumerableProperty,
createBufferSource: kEnumerableProperty,
createBiquadFilter: kEnumerableProperty,
createChannelMerger: kEnumerableProperty,
createChannelSplitter: kEnumerableProperty,
createConstantSource: kEnumerableProperty,
createConvolver: kEnumerableProperty,
createDelay: kEnumerableProperty,
createDynamicsCompressor: kEnumerableProperty,
createGain: kEnumerableProperty,
createIIRFilter: kEnumerableProperty,
createOscillator: kEnumerableProperty,
createPanner: kEnumerableProperty,
createStereoPanner: kEnumerableProperty,
createWaveShaper: kEnumerableProperty,
listener: kEnumerableProperty,
destination: kEnumerableProperty,
sampleRate: kEnumerableProperty,
currentTime: kEnumerableProperty,
state: kEnumerableProperty,
onstatechange: kEnumerableProperty,
decodeAudioData: kEnumerableProperty,
createBuffer: kEnumerableProperty,
createPeriodicWave: kEnumerableProperty,
});
return BaseAudioContext;
};