mirror of
https://github.com/Tonejs/Tone.js
synced 2024-12-27 20:13:07 +00:00
allow JSON.stringify(context)
This commit is contained in:
parent
bfa600399f
commit
16859ff2e2
4 changed files with 227 additions and 113 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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", () => {
|
||||||
|
|
|
@ -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 {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue