allow JSON.stringify(context)

This commit is contained in:
Jack Anderson 2020-12-31 17:53:38 -08:00
parent bfa600399f
commit 16859ff2e2
4 changed files with 227 additions and 113 deletions

View file

@ -8,63 +8,90 @@ type Transport = import("../clock/Transport").Transport;
type Listener = import("./Listener").Listener; type Listener = import("./Listener").Listener;
// these are either not used in Tone.js or deprecated and not implemented. // these are either not used in Tone.js or deprecated and not implemented.
export type ExcludedFromBaseAudioContext = "onstatechange" | "addEventListener" | "removeEventListener" | "listener" | "dispatchEvent" | "audioWorklet" | "destination" | "createScriptProcessor"; export type ExcludedFromBaseAudioContext =
| "onstatechange"
| "addEventListener"
| "removeEventListener"
| "listener"
| "dispatchEvent"
| "audioWorklet"
| "destination"
| "createScriptProcessor";
// the subset of the BaseAudioContext which Tone.Context implements. // the subset of the BaseAudioContext which Tone.Context implements.
export type BaseAudioContextSubset = Omit<BaseAudioContext, ExcludedFromBaseAudioContext>; export type BaseAudioContextSubset = Omit<
BaseAudioContext,
ExcludedFromBaseAudioContext
>;
export type ContextLatencyHint = AudioContextLatencyCategory; export type ContextLatencyHint = AudioContextLatencyCategory;
export abstract class BaseContext extends Emitter<"statechange" | "tick"> implements BaseAudioContextSubset { export abstract class BaseContext
extends Emitter<"statechange" | "tick">
implements BaseAudioContextSubset {
//--------------------------- //---------------------------
// BASE AUDIO CONTEXT METHODS // BASE AUDIO CONTEXT METHODS
//--------------------------- //---------------------------
abstract createAnalyser(): AnalyserNode abstract createAnalyser(): AnalyserNode;
abstract createOscillator(): OscillatorNode abstract createOscillator(): OscillatorNode;
abstract createBufferSource(): AudioBufferSourceNode abstract createBufferSource(): AudioBufferSourceNode;
abstract createBiquadFilter(): BiquadFilterNode abstract createBiquadFilter(): BiquadFilterNode;
abstract createBuffer(_numberOfChannels: number, _length: number, _sampleRate: number): AudioBuffer abstract createBuffer(
_numberOfChannels: number,
_length: number,
_sampleRate: number
): AudioBuffer;
abstract createChannelMerger(_numberOfInputs?: number | undefined): ChannelMergerNode abstract createChannelMerger(
_numberOfInputs?: number | undefined
): ChannelMergerNode;
abstract createChannelSplitter(_numberOfOutputs?: number | undefined): ChannelSplitterNode abstract createChannelSplitter(
_numberOfOutputs?: number | undefined
): ChannelSplitterNode;
abstract createConstantSource(): ConstantSourceNode abstract createConstantSource(): ConstantSourceNode;
abstract createConvolver(): ConvolverNode abstract createConvolver(): ConvolverNode;
abstract createDelay(_maxDelayTime?: number | undefined): DelayNode abstract createDelay(_maxDelayTime?: number | undefined): DelayNode;
abstract createDynamicsCompressor(): DynamicsCompressorNode abstract createDynamicsCompressor(): DynamicsCompressorNode;
abstract createGain(): GainNode abstract createGain(): GainNode;
abstract createIIRFilter(_feedForward: number[] | Float32Array, _feedback: number[] | Float32Array): IIRFilterNode abstract createIIRFilter(
_feedForward: number[] | Float32Array,
_feedback: number[] | Float32Array
): IIRFilterNode;
abstract createPanner(): PannerNode abstract createPanner(): PannerNode;
abstract createPeriodicWave( abstract createPeriodicWave(
_real: number[] | Float32Array, _real: number[] | Float32Array,
_imag: number[] | Float32Array, _imag: number[] | Float32Array,
_constraints?: PeriodicWaveConstraints | undefined, _constraints?: PeriodicWaveConstraints | undefined
): PeriodicWave ): PeriodicWave;
abstract createStereoPanner(): StereoPannerNode abstract createStereoPanner(): StereoPannerNode;
abstract createWaveShaper(): WaveShaperNode abstract createWaveShaper(): WaveShaperNode;
abstract createMediaStreamSource(_stream: MediaStream): MediaStreamAudioSourceNode abstract createMediaStreamSource(
_stream: MediaStream
): MediaStreamAudioSourceNode;
abstract createMediaElementSource(_element: HTMLMediaElement): MediaElementAudioSourceNode abstract createMediaElementSource(
_element: HTMLMediaElement
): MediaElementAudioSourceNode;
abstract createMediaStreamDestination(): MediaStreamAudioDestinationNode abstract createMediaStreamDestination(): MediaStreamAudioDestinationNode;
abstract decodeAudioData(_audioData: ArrayBuffer): Promise<AudioBuffer> abstract decodeAudioData(_audioData: ArrayBuffer): Promise<AudioBuffer>;
//--------------------------- //---------------------------
// TONE AUDIO CONTEXT METHODS // TONE AUDIO CONTEXT METHODS
@ -73,45 +100,56 @@ export abstract class BaseContext extends Emitter<"statechange" | "tick"> implem
abstract createAudioWorkletNode( abstract createAudioWorkletNode(
_name: string, _name: string,
_options?: Partial<AudioWorkletNodeOptions> _options?: Partial<AudioWorkletNodeOptions>
): AudioWorkletNode ): AudioWorkletNode;
abstract get rawContext(): AnyAudioContext abstract get rawContext(): AnyAudioContext;
abstract async addAudioWorkletModule(_url: string, _name: string): Promise<void> abstract async addAudioWorkletModule(
_url: string,
_name: string
): Promise<void>;
abstract lookAhead: number; abstract lookAhead: number;
abstract latencyHint: ContextLatencyHint | Seconds; abstract latencyHint: ContextLatencyHint | Seconds;
abstract resume(): Promise<void> abstract resume(): Promise<void>;
abstract setTimeout(_fn: (...args: any[]) => void, _timeout: Seconds): number abstract setTimeout(
_fn: (...args: any[]) => void,
_timeout: Seconds
): number;
abstract clearTimeout(_id: number): this abstract clearTimeout(_id: number): this;
abstract setInterval(_fn: (...args: any[]) => void, _interval: Seconds): number abstract setInterval(
_fn: (...args: any[]) => void,
_interval: Seconds
): number;
abstract clearInterval(_id: number): this abstract clearInterval(_id: number): this;
abstract getConstant(_val: number): AudioBufferSourceNode abstract getConstant(_val: number): AudioBufferSourceNode;
abstract get currentTime(): Seconds abstract get currentTime(): Seconds;
abstract get state(): AudioContextState abstract get state(): AudioContextState;
abstract get sampleRate(): number abstract get sampleRate(): number;
abstract get listener(): Listener abstract get listener(): Listener;
abstract get transport(): Transport abstract get transport(): Transport;
abstract get draw(): Draw abstract get draw(): Draw;
abstract get destination(): Destination abstract get destination(): Destination;
abstract now(): Seconds abstract now(): Seconds;
abstract immediate(): Seconds abstract immediate(): Seconds;
abstract toJSON(): Record<string, any>;
readonly isOffline: boolean = false; readonly isOffline: boolean = false;
} }

View file

@ -12,7 +12,6 @@ import { Draw } from "../util/Draw";
import { connect } from "./ToneAudioNode"; import { connect } from "./ToneAudioNode";
describe("Context", () => { describe("Context", () => {
it("creates and disposes the classes attached to the context", async () => { it("creates and disposes the classes attached to the context", async () => {
const ac = createAudioContext(); const ac = createAudioContext();
const context = new Context(ac); const context = new Context(ac);
@ -32,7 +31,6 @@ describe("Context", () => {
}); });
context("AudioContext", () => { context("AudioContext", () => {
it("extends the AudioContext methods", () => { it("extends the AudioContext methods", () => {
const ctx = new Context(createAudioContext()); const ctx = new Context(createAudioContext());
expect(ctx).to.have.property("createGain"); expect(ctx).to.have.property("createGain");
@ -46,8 +44,15 @@ describe("Context", () => {
return ctx.close(); return ctx.close();
}); });
it("can be stringified", () => {
const ctx = new Context(createAudioContext());
expect(JSON.stringify(ctx)).to.equal("{}");
ctx.dispose();
return ctx.close();
});
if (ONLINE_TESTING) { if (ONLINE_TESTING) {
it("clock is running", done => { it("clock is running", (done) => {
const interval = setInterval(() => { const interval = setInterval(() => {
if (getContext().currentTime > 0.5) { if (getContext().currentTime > 0.5) {
clearInterval(interval); clearInterval(interval);
@ -88,7 +93,6 @@ describe("Context", () => {
}); });
context("state", () => { context("state", () => {
it("can suspend and resume the state", async () => { it("can suspend and resume the state", async () => {
const ac = createAudioContext(); const ac = createAudioContext();
const context = new Context(ac); const context = new Context(ac);
@ -105,14 +109,14 @@ describe("Context", () => {
const ac = createAudioContext(); const ac = createAudioContext();
const context = new Context(ac); const context = new Context(ac);
let triggerChange = false; let triggerChange = false;
context.on("statechange", state => { context.on("statechange", (state) => {
if (!triggerChange) { if (!triggerChange) {
triggerChange = true; triggerChange = true;
expect(state).to.equal("running"); expect(state).to.equal("running");
} }
}); });
await context.resume(); await context.resume();
await new Promise(done => setTimeout(() => done(), 10)); await new Promise((done) => setTimeout(() => done(), 10));
expect(triggerChange).to.equal(true); expect(triggerChange).to.equal(true);
context.dispose(); context.dispose();
return ac.close(); return ac.close();
@ -120,9 +124,7 @@ describe("Context", () => {
}); });
if (ONLINE_TESTING) { if (ONLINE_TESTING) {
context("clockSource", () => { context("clockSource", () => {
let ctx; let ctx;
beforeEach(() => { beforeEach(() => {
ctx = new Context(); ctx = new Context();
@ -138,14 +140,14 @@ describe("Context", () => {
expect(ctx.clockSource).to.equal("worker"); expect(ctx.clockSource).to.equal("worker");
}); });
it("provides callback", done => { it("provides callback", (done) => {
expect(ctx.clockSource).to.equal("worker"); expect(ctx.clockSource).to.equal("worker");
ctx.setTimeout(() => { ctx.setTimeout(() => {
done(); done();
}, 0.1); }, 0.1);
}); });
it("can be set to 'timeout'", done => { it("can be set to 'timeout'", (done) => {
ctx.clockSource = "timeout"; ctx.clockSource = "timeout";
expect(ctx.clockSource).to.equal("timeout"); expect(ctx.clockSource).to.equal("timeout");
ctx.setTimeout(() => { ctx.setTimeout(() => {
@ -153,7 +155,7 @@ describe("Context", () => {
}, 0.1); }, 0.1);
}); });
it("can be set to 'offline'", done => { it("can be set to 'offline'", (done) => {
ctx.clockSource = "offline"; ctx.clockSource = "offline";
expect(ctx.clockSource).to.equal("offline"); expect(ctx.clockSource).to.equal("offline");
// provides no callback // provides no callback
@ -167,9 +169,7 @@ describe("Context", () => {
}); });
} }
context("setTimeout", () => { context("setTimeout", () => {
if (ONLINE_TESTING) { if (ONLINE_TESTING) {
let ctx; let ctx;
beforeEach(() => { beforeEach(() => {
ctx = new Context(); ctx = new Context();
@ -181,19 +181,19 @@ describe("Context", () => {
return ctx.close(); return ctx.close();
}); });
it("can set a timeout", done => { it("can set a timeout", (done) => {
ctx.setTimeout(() => { ctx.setTimeout(() => {
done(); done();
}, 0.1); }, 0.1);
}); });
it("returns an id", () => { it("returns an id", () => {
expect(ctx.setTimeout(() => { }, 0.1)).to.be.a("number"); expect(ctx.setTimeout(() => {}, 0.1)).to.be.a("number");
// try clearing a random ID, shouldn't cause any errors // try clearing a random ID, shouldn't cause any errors
ctx.clearTimeout(-2); ctx.clearTimeout(-2);
}); });
it("timeout is not invoked when cancelled", done => { it("timeout is not invoked when cancelled", (done) => {
const id = ctx.setTimeout(() => { const id = ctx.setTimeout(() => {
throw new Error("shouldn't be invoked"); throw new Error("shouldn't be invoked");
}, 0.01); }, 0.01);
@ -203,7 +203,7 @@ describe("Context", () => {
}, 0.02); }, 0.02);
}); });
it("order is maintained", done => { it("order is maintained", (done) => {
let wasInvoked = false; let wasInvoked = false;
ctx.setTimeout(() => { ctx.setTimeout(() => {
expect(wasInvoked).to.equal(true); expect(wasInvoked).to.equal(true);
@ -216,7 +216,7 @@ describe("Context", () => {
} }
it("is invoked in the offline context", () => { it("is invoked in the offline context", () => {
return Offline(context => { return Offline((context) => {
const transport = new Transport({ context }); const transport = new Transport({ context });
transport.context.setTimeout(() => { transport.context.setTimeout(() => {
expect(transport.now()).to.be.closeTo(0.01, 0.005); expect(transport.now()).to.be.closeTo(0.01, 0.005);
@ -226,9 +226,7 @@ describe("Context", () => {
}); });
context("setInterval", () => { context("setInterval", () => {
if (ONLINE_TESTING) { if (ONLINE_TESTING) {
let ctx; let ctx;
beforeEach(() => { beforeEach(() => {
ctx = new Context(); ctx = new Context();
@ -240,19 +238,19 @@ describe("Context", () => {
return ctx.close(); return ctx.close();
}); });
it("can set an interval", done => { it("can set an interval", (done) => {
ctx.setInterval(() => { ctx.setInterval(() => {
done(); done();
}, 0.1); }, 0.1);
}); });
it("returns an id", () => { it("returns an id", () => {
expect(ctx.setInterval(() => { }, 0.1)).to.be.a("number"); expect(ctx.setInterval(() => {}, 0.1)).to.be.a("number");
// try clearing a random ID, shouldn't cause any errors // try clearing a random ID, shouldn't cause any errors
ctx.clearInterval(-2); ctx.clearInterval(-2);
}); });
it("timeout is not invoked when cancelled", done => { it("timeout is not invoked when cancelled", (done) => {
const id = ctx.setInterval(() => { const id = ctx.setInterval(() => {
throw new Error("shouldn't be invoked"); throw new Error("shouldn't be invoked");
}, 0.01); }, 0.01);
@ -262,7 +260,7 @@ describe("Context", () => {
}, 0.02); }, 0.02);
}); });
it("order is maintained", done => { it("order is maintained", (done) => {
let wasInvoked = false; let wasInvoked = false;
ctx.setInterval(() => { ctx.setInterval(() => {
expect(wasInvoked).to.equal(true); expect(wasInvoked).to.equal(true);
@ -276,7 +274,7 @@ describe("Context", () => {
it("is invoked in the offline context", () => { it("is invoked in the offline context", () => {
let invocationCount = 0; let invocationCount = 0;
return Offline(context => { return Offline((context) => {
context.setInterval(() => { context.setInterval(() => {
invocationCount++; invocationCount++;
}, 0.01); }, 0.01);
@ -287,10 +285,13 @@ describe("Context", () => {
it("is invoked in with the right interval", () => { it("is invoked in with the right interval", () => {
let numberOfInvocations = 0; let numberOfInvocations = 0;
return Offline(context => { return Offline((context) => {
let intervalTime = context.now(); let intervalTime = context.now();
context.setInterval(() => { context.setInterval(() => {
expect(context.now() - intervalTime).to.be.closeTo(0.01, 0.005); expect(context.now() - intervalTime).to.be.closeTo(
0.01,
0.005
);
intervalTime = context.now(); intervalTime = context.now();
numberOfInvocations++; numberOfInvocations++;
}, 0.01); }, 0.01);
@ -301,7 +302,6 @@ describe("Context", () => {
}); });
context("get/set", () => { context("get/set", () => {
let ctx; let ctx;
beforeEach(() => { beforeEach(() => {
ctx = new Context(); ctx = new Context();
@ -324,7 +324,7 @@ describe("Context", () => {
}); });
it("gets a constant signal", () => { it("gets a constant signal", () => {
return ConstantOutput(context => { return ConstantOutput((context) => {
const bufferSrc = context.getConstant(1); const bufferSrc = context.getConstant(1);
connect(bufferSrc, context.destination); connect(bufferSrc, context.destination);
}, 1); }, 1);
@ -335,7 +335,6 @@ describe("Context", () => {
const bufferB = ctx.getConstant(2); const bufferB = ctx.getConstant(2);
expect(bufferA).to.equal(bufferB); expect(bufferA).to.equal(bufferB);
}); });
}); });
context("Methods", () => { context("Methods", () => {

View file

@ -4,7 +4,11 @@ import { isAudioContext } from "../util/AdvancedTypeCheck";
import { optionsFromArguments } from "../util/Defaults"; import { optionsFromArguments } from "../util/Defaults";
import { Timeline } from "../util/Timeline"; import { Timeline } from "../util/Timeline";
import { isDefined, isString } from "../util/TypeCheck"; import { isDefined, isString } from "../util/TypeCheck";
import { AnyAudioContext, createAudioContext, createAudioWorkletNode } from "./AudioContext"; import {
AnyAudioContext,
createAudioContext,
createAudioWorkletNode,
} from "./AudioContext";
import { closeContext, initializeContext } from "./ContextInitialization"; import { closeContext, initializeContext } from "./ContextInitialization";
import { BaseContext, ContextLatencyHint } from "./BaseContext"; import { BaseContext, ContextLatencyHint } from "./BaseContext";
import { assert } from "../util/Debug"; import { assert } from "../util/Debug";
@ -33,7 +37,6 @@ export interface ContextTimeoutEvent {
* @category Core * @category Core
*/ */
export class Context extends BaseContext { export class Context extends BaseContext {
readonly name: string = "Context"; readonly name: string = "Context";
/** /**
@ -107,7 +110,9 @@ export class Context extends BaseContext {
constructor(options?: Partial<ContextOptions>); constructor(options?: Partial<ContextOptions>);
constructor() { constructor() {
super(); super();
const options = optionsFromArguments(Context.getDefaults(), arguments, ["context"]); const options = optionsFromArguments(Context.getDefaults(), arguments, [
"context",
]);
if (options.context) { if (options.context) {
this._context = options.context; this._context = options.context;
@ -117,7 +122,11 @@ export class Context extends BaseContext {
}); });
} }
this._ticker = new Ticker(this.emit.bind(this, "tick"), options.clockSource, options.updateInterval); this._ticker = new Ticker(
this.emit.bind(this, "tick"),
options.clockSource,
options.updateInterval
);
this.on("tick", this._timeoutLoop.bind(this)); this.on("tick", this._timeoutLoop.bind(this));
// fwd events from the context // fwd events from the context
@ -166,13 +175,21 @@ export class Context extends BaseContext {
createBiquadFilter(): BiquadFilterNode { createBiquadFilter(): BiquadFilterNode {
return this._context.createBiquadFilter(); return this._context.createBiquadFilter();
} }
createBuffer(numberOfChannels: number, length: number, sampleRate: number): AudioBuffer { createBuffer(
numberOfChannels: number,
length: number,
sampleRate: number
): AudioBuffer {
return this._context.createBuffer(numberOfChannels, length, sampleRate); return this._context.createBuffer(numberOfChannels, length, sampleRate);
} }
createChannelMerger(numberOfInputs?: number | undefined): ChannelMergerNode { createChannelMerger(
numberOfInputs?: number | undefined
): ChannelMergerNode {
return this._context.createChannelMerger(numberOfInputs); return this._context.createChannelMerger(numberOfInputs);
} }
createChannelSplitter(numberOfOutputs?: number | undefined): ChannelSplitterNode { createChannelSplitter(
numberOfOutputs?: number | undefined
): ChannelSplitterNode {
return this._context.createChannelSplitter(numberOfOutputs); return this._context.createChannelSplitter(numberOfOutputs);
} }
createConstantSource(): ConstantSourceNode { createConstantSource(): ConstantSourceNode {
@ -190,7 +207,10 @@ export class Context extends BaseContext {
createGain(): GainNode { createGain(): GainNode {
return this._context.createGain(); return this._context.createGain();
} }
createIIRFilter(feedForward: number[] | Float32Array, feedback: number[] | Float32Array): IIRFilterNode { createIIRFilter(
feedForward: number[] | Float32Array,
feedback: number[] | Float32Array
): IIRFilterNode {
// @ts-ignore // @ts-ignore
return this._context.createIIRFilter(feedForward, feedback); return this._context.createIIRFilter(feedForward, feedback);
} }
@ -200,7 +220,7 @@ export class Context extends BaseContext {
createPeriodicWave( createPeriodicWave(
real: number[] | Float32Array, real: number[] | Float32Array,
imag: number[] | Float32Array, imag: number[] | Float32Array,
constraints?: PeriodicWaveConstraints | undefined, constraints?: PeriodicWaveConstraints | undefined
): PeriodicWave { ): PeriodicWave {
return this._context.createPeriodicWave(real, imag, constraints); return this._context.createPeriodicWave(real, imag, constraints);
} }
@ -211,17 +231,28 @@ export class Context extends BaseContext {
return this._context.createWaveShaper(); return this._context.createWaveShaper();
} }
createMediaStreamSource(stream: MediaStream): MediaStreamAudioSourceNode { createMediaStreamSource(stream: MediaStream): MediaStreamAudioSourceNode {
assert(isAudioContext(this._context), "Not available if OfflineAudioContext"); assert(
isAudioContext(this._context),
"Not available if OfflineAudioContext"
);
const context = this._context as AudioContext; const context = this._context as AudioContext;
return context.createMediaStreamSource(stream); return context.createMediaStreamSource(stream);
} }
createMediaElementSource(element: HTMLMediaElement): MediaElementAudioSourceNode { createMediaElementSource(
assert(isAudioContext(this._context), "Not available if OfflineAudioContext"); element: HTMLMediaElement
): MediaElementAudioSourceNode {
assert(
isAudioContext(this._context),
"Not available if OfflineAudioContext"
);
const context = this._context as AudioContext; const context = this._context as AudioContext;
return context.createMediaElementSource(element); return context.createMediaElementSource(element);
} }
createMediaStreamDestination(): MediaStreamAudioDestinationNode { createMediaStreamDestination(): MediaStreamAudioDestinationNode {
assert(isAudioContext(this._context), "Not available if OfflineAudioContext"); assert(
isAudioContext(this._context),
"Not available if OfflineAudioContext"
);
const context = this._context as AudioContext; const context = this._context as AudioContext;
return context.createMediaStreamDestination(); return context.createMediaStreamDestination();
} }
@ -256,7 +287,10 @@ export class Context extends BaseContext {
return this._listener; return this._listener;
} }
set listener(l) { set listener(l) {
assert(!this._initialized, "The listener cannot be set after initialization."); assert(
!this._initialized,
"The listener cannot be set after initialization."
);
this._listener = l; this._listener = l;
} }
@ -268,7 +302,10 @@ export class Context extends BaseContext {
return this._transport; return this._transport;
} }
set transport(t: Transport) { set transport(t: Transport) {
assert(!this._initialized, "The transport cannot be set after initialization."); assert(
!this._initialized,
"The transport cannot be set after initialization."
);
this._transport = t; this._transport = t;
} }
@ -292,7 +329,10 @@ export class Context extends BaseContext {
return this._destination; return this._destination;
} }
set destination(d: Destination) { set destination(d: Destination) {
assert(!this._initialized, "The destination cannot be set after initialization."); assert(
!this._initialized,
"The destination cannot be set after initialization."
);
this._destination = d; this._destination = d;
} }
@ -303,7 +343,7 @@ export class Context extends BaseContext {
/** /**
* Maps a module name to promise of the addModule method * Maps a module name to promise of the addModule method
*/ */
private _workletModules: Map<string, Promise<void>> = new Map() private _workletModules: Map<string, Promise<void>> = new Map();
/** /**
* Create an audio worklet node from a name and options. The module * Create an audio worklet node from a name and options. The module
@ -322,9 +362,15 @@ export class Context extends BaseContext {
* @param name The name of the module * @param name The name of the module
*/ */
async addAudioWorkletModule(url: string, name: string): Promise<void> { async addAudioWorkletModule(url: string, name: string): Promise<void> {
assert(isDefined(this.rawContext.audioWorklet), "AudioWorkletNode is only available in a secure context (https or localhost)"); assert(
isDefined(this.rawContext.audioWorklet),
"AudioWorkletNode is only available in a secure context (https or localhost)"
);
if (!this._workletModules.has(name)) { if (!this._workletModules.has(name)) {
this._workletModules.set(name, this.rawContext.audioWorklet.addModule(url)); this._workletModules.set(
name,
this.rawContext.audioWorklet.addModule(url)
);
} }
await this._workletModules.get(name); await this._workletModules.get(name);
} }
@ -334,7 +380,7 @@ export class Context extends BaseContext {
*/ */
protected async workletsAreReady(): Promise<void> { protected async workletsAreReady(): Promise<void> {
const promises: Promise<void>[] = []; const promises: Promise<void>[] = [];
this._workletModules.forEach(promise => promises.push(promise)); this._workletModules.forEach((promise) => promises.push(promise));
await Promise.all(promises); await Promise.all(promises);
} }
@ -465,7 +511,11 @@ export class Context extends BaseContext {
if (this._constants.has(val)) { if (this._constants.has(val)) {
return this._constants.get(val) as AudioBufferSourceNode; return this._constants.get(val) as AudioBufferSourceNode;
} else { } else {
const buffer = this._context.createBuffer(1, 128, this._context.sampleRate); const buffer = this._context.createBuffer(
1,
128,
this._context.sampleRate
);
const arr = buffer.getChannelData(0); const arr = buffer.getChannelData(0);
for (let i = 0; i < arr.length; i++) { for (let i = 0; i < arr.length; i++) {
arr[i] = val; arr[i] = val;
@ -488,7 +538,9 @@ export class Context extends BaseContext {
super.dispose(); super.dispose();
this._ticker.dispose(); this._ticker.dispose();
this._timeouts.dispose(); this._timeouts.dispose();
Object.keys(this._constants).map(val => this._constants[val].disconnect()); Object.keys(this._constants).map((val) =>
this._constants[val].disconnect()
);
return this; return this;
} }
@ -536,7 +588,7 @@ export class Context extends BaseContext {
* @param id The ID returned from setTimeout * @param id The ID returned from setTimeout
*/ */
clearTimeout(id: number): this { clearTimeout(id: number): this {
this._timeouts.forEach(event => { this._timeouts.forEach((event) => {
if (event.id === id) { if (event.id === id) {
this._timeouts.remove(event); this._timeouts.remove(event);
} }
@ -573,4 +625,13 @@ export class Context extends BaseContext {
intervalFn(); intervalFn();
return id; return id;
} }
/*
* This is a placeholder so that JSON.stringify does not throw an error
* This matches what JSON.stringify(audioContext) returns on a native
* audioContext instance.
*/
toJSON() {
return {};
}
} }

View file

@ -8,7 +8,6 @@ type Transport = import("../clock/Transport").Transport;
type Listener = import("./Listener").Listener; type Listener = import("./Listener").Listener;
export class DummyContext extends BaseContext { export class DummyContext extends BaseContext {
//--------------------------- //---------------------------
// BASE AUDIO CONTEXT METHODS // BASE AUDIO CONTEXT METHODS
//--------------------------- //---------------------------
@ -28,15 +27,23 @@ export class DummyContext extends BaseContext {
return {} as BiquadFilterNode; return {} as BiquadFilterNode;
} }
createBuffer(_numberOfChannels: number, _length: number, _sampleRate: number): AudioBuffer { createBuffer(
_numberOfChannels: number,
_length: number,
_sampleRate: number
): AudioBuffer {
return {} as AudioBuffer; return {} as AudioBuffer;
} }
createChannelMerger(_numberOfInputs?: number | undefined): ChannelMergerNode { createChannelMerger(
_numberOfInputs?: number | undefined
): ChannelMergerNode {
return {} as ChannelMergerNode; return {} as ChannelMergerNode;
} }
createChannelSplitter(_numberOfOutputs?: number | undefined): ChannelSplitterNode { createChannelSplitter(
_numberOfOutputs?: number | undefined
): ChannelSplitterNode {
return {} as ChannelSplitterNode; return {} as ChannelSplitterNode;
} }
@ -60,7 +67,10 @@ export class DummyContext extends BaseContext {
return {} as GainNode; return {} as GainNode;
} }
createIIRFilter(_feedForward: number[] | Float32Array, _feedback: number[] | Float32Array): IIRFilterNode { createIIRFilter(
_feedForward: number[] | Float32Array,
_feedback: number[] | Float32Array
): IIRFilterNode {
return {} as IIRFilterNode; return {} as IIRFilterNode;
} }
@ -71,7 +81,7 @@ export class DummyContext extends BaseContext {
createPeriodicWave( createPeriodicWave(
_real: number[] | Float32Array, _real: number[] | Float32Array,
_imag: number[] | Float32Array, _imag: number[] | Float32Array,
_constraints?: PeriodicWaveConstraints | undefined, _constraints?: PeriodicWaveConstraints | undefined
): PeriodicWave { ): PeriodicWave {
return {} as PeriodicWave; return {} as PeriodicWave;
} }
@ -88,7 +98,9 @@ export class DummyContext extends BaseContext {
return {} as MediaStreamAudioSourceNode; return {} as MediaStreamAudioSourceNode;
} }
createMediaElementSource(_element: HTMLMediaElement): MediaElementAudioSourceNode { createMediaElementSource(
_element: HTMLMediaElement
): MediaElementAudioSourceNode {
return {} as MediaElementAudioSourceNode; return {} as MediaElementAudioSourceNode;
} }
@ -170,12 +182,12 @@ export class DummyContext extends BaseContext {
get draw(): Draw { get draw(): Draw {
return {} as Draw; return {} as Draw;
} }
set draw(_d) { } set draw(_d) {}
get destination(): Destination { get destination(): Destination {
return {} as Destination; return {} as Destination;
} }
set destination(_d: Destination) { } set destination(_d: Destination) {}
now() { now() {
return 0; return 0;
@ -185,5 +197,9 @@ export class DummyContext extends BaseContext {
return 0; return 0;
} }
toJSON() {
return {};
}
readonly isOffline: boolean = false; readonly isOffline: boolean = false;
} }