Create BaseContext + better DummyContext

- this creates a new abstract BaseContext that DummyContext and Context
inherit from
- All methods available to Context are now stubbed by DummyContext
- BaseContext is now used as the type where Context was originally used
This commit is contained in:
Jack Anderson 2019-11-06 14:32:19 -08:00
parent 974ee57353
commit 3354b24527
9 changed files with 303 additions and 24 deletions

View file

@ -1,4 +1,4 @@
import { Context } from "Tone/core";
import { BaseContext } from "Tone/core";
import { Gain } from "../../core/context/Gain";
import { ToneAudioNode, ToneAudioNodeOptions } from "../../core/context/ToneAudioNode";
import { optionsFromArguments } from "../../core/util/Defaults";
@ -61,12 +61,12 @@ export class Solo extends ToneAudioNode<SoloOptions> {
/**
* Hold all of the solo'ed tracks belonging to a specific context
*/
private static _allSolos: Map<Context, Set<Solo>> = new Map();
private static _allSolos: Map<BaseContext, Set<Solo>> = new Map();
/**
* Hold the currently solo'ed instance(s)
*/
private static _soloed: Map<Context, Set<Solo>> = new Map();
private static _soloed: Map<BaseContext, Set<Solo>> = new Map();
/**
* Isolates this instance and mutes all other instances of Solo.

View file

@ -1,29 +1,27 @@
import { version } from "../version";
import { hasAudioContext, theWindow } from "./context/AudioContext";
import { AnyAudioContext, hasAudioContext, theWindow } from "./context/AudioContext";
import { Context } from "./context/Context";
import { DummyContext } from "./context/DummyContext";
import { BaseContext } from "./context/BaseContext";
import { OfflineContext } from "./context/OfflineContext";
import { isAudioContext, isOfflineAudioContext } from "./util/AdvancedTypeCheck";
/**
* This dummy context is used to avoid throwing immediate errors when importing in Node.js
*/
// eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion
const dummyContext: Context = {
destination: {},
transport: {},
} as Context;
const dummyContext = new DummyContext();
/**
* The global audio context which is getable and assignable through
* getContext and setContext
*/
let globalContext: Context = dummyContext;
let globalContext: BaseContext = dummyContext;
/**
* Returns the default system-wide [[Context]]
* @category Core
*/
export function getContext(): Context {
export function getContext(): BaseContext {
if (globalContext === dummyContext && hasAudioContext) {
setContext(new Context());
}
@ -34,7 +32,7 @@ export function getContext(): Context {
* Set the default audio context
* @category Core
*/
export function setContext(context: Context | AudioContext | OfflineAudioContext): void {
export function setContext(context: BaseContext | AnyAudioContext): void {
if (isAudioContext(context)) {
globalContext = new Context(context);
} else if (isOfflineAudioContext(context)) {

View file

@ -1,4 +1,4 @@
import { Context } from "../context/Context";
import { BaseContext } from "../context/BaseContext";
import { TicksClass } from "../type/Ticks";
import { Seconds, Ticks, Time } from "../type/Units";
import { TransportEvent, TransportEventOptions } from "./TransportEvent";
@ -49,7 +49,7 @@ export class TransportRepeatEvent extends TransportEvent {
/**
* The audio context belonging to this event
*/
protected context: Context;
protected context: BaseContext;
/**
* @param transport The transport object which the event belongs to

View file

@ -0,0 +1,103 @@
import { Seconds } from "../type/Units";
import { Emitter } from "../util/Emitter";
import { AnyAudioContext } from "./AudioContext";
type Draw = import("../util/Draw").Draw;
type Destination = import("./Destination").Destination;
type Transport = import("../clock/Transport").Transport;
type BaseAudioContextSubset = import("./Context").BaseAudioContextSubset;
export abstract class BaseContext extends Emitter<"statechange" | "tick"> implements BaseAudioContextSubset {
//---------------------------
// BASE AUDIO CONTEXT METHODS
//---------------------------
abstract createAnalyser(): AnalyserNode
abstract createOscillator(): OscillatorNode
abstract createBufferSource(): AudioBufferSourceNode
abstract createBiquadFilter(): BiquadFilterNode
abstract createBuffer(_numberOfChannels: number, _length: number, _sampleRate: number): AudioBuffer
abstract createChannelMerger(_numberOfInputs?: number | undefined): ChannelMergerNode
abstract createChannelSplitter(_numberOfOutputs?: number | undefined): ChannelSplitterNode
abstract createConstantSource(): ConstantSourceNode
abstract createConvolver(): ConvolverNode
abstract createDelay(_maxDelayTime?: number | undefined): DelayNode
abstract createDynamicsCompressor(): DynamicsCompressorNode
abstract createGain(): GainNode
abstract createIIRFilter(_feedForward: number[] | Float32Array, _feedback: number[] | Float32Array): IIRFilterNode
abstract createPanner(): PannerNode
abstract createPeriodicWave(
_real: number[] | Float32Array,
_imag: number[] | Float32Array,
_constraints?: PeriodicWaveConstraints | undefined,
): PeriodicWave
abstract createStereoPanner(): StereoPannerNode
abstract createWaveShaper(): WaveShaperNode
abstract createMediaStreamSource(_stream: MediaStream): MediaStreamAudioSourceNode
abstract decodeAudioData(_audioData: ArrayBuffer): Promise<AudioBuffer>
//---------------------------
// TONE AUDIO CONTEXT METHODS
//---------------------------
abstract createAudioWorkletNode(
_name: string,
_options?: Partial<AudioWorkletNodeOptions>
): AudioWorkletNode
abstract get rawContext(): AnyAudioContext
abstract async addAudioWorkletModule(_url: string, _name: string): Promise<void>
abstract lookAhead: number;
abstract resume(): Promise<void>
abstract setTimeout(_fn: (...args: any[]) => void, _timeout: Seconds): number
abstract clearTimeout(_id: number): this
abstract setInterval(_fn: (...args: any[]) => void, _interval: Seconds): number
abstract clearInterval(_id: number): this
abstract getConstant(_val: number): AudioBufferSourceNode
abstract get currentTime(): Seconds
abstract get state(): AudioContextState
abstract get sampleRate(): number
abstract get listener(): AudioListener
abstract get transport(): Transport
abstract get draw(): Draw
abstract get destination(): Destination
abstract now(): Seconds
abstract immediate(): Seconds
readonly isOffline: boolean = false;
}

View file

@ -2,12 +2,12 @@ import { Ticker, TickerClockSource } from "../clock/Ticker";
import { Seconds } from "../type/Units";
import { isAudioContext } from "../util/AdvancedTypeCheck";
import { optionsFromArguments } from "../util/Defaults";
import { Emitter } from "../util/Emitter";
import { Omit } from "../util/Interface";
import { Timeline } from "../util/Timeline";
import { isDefined, isString } from "../util/TypeCheck";
import { AnyAudioContext, createAudioContext, createAudioWorkletNode } from "./AudioContext";
import { closeContext, initializeContext } from "./ContextInitialization";
import { BaseContext } from "./BaseContext";
type Transport = import("../clock/Transport").Transport;
type Destination = import("./Destination").Destination;
@ -42,7 +42,7 @@ export interface ContextTimeoutEvent {
* Wrapper around the native AudioContext.
* @category Core
*/
export class Context extends Emitter<"statechange" | "tick"> implements BaseAudioContextSubset {
export class Context extends BaseContext {
readonly name: string = "Context";

View file

@ -0,0 +1,177 @@
import { BaseContext } from "./BaseContext";
import { Seconds } from "../type/Units";
import { AnyAudioContext } from "./AudioContext";
type Draw = import("../util/Draw").Draw;
type Destination = import("./Destination").Destination;
type Transport = import("../clock/Transport").Transport;
export class DummyContext extends BaseContext {
//---------------------------
// BASE AUDIO CONTEXT METHODS
//---------------------------
createAnalyser(): AnalyserNode {
return {} as AnalyserNode;
}
createOscillator(): OscillatorNode {
return {} as OscillatorNode;
}
createBufferSource() {
return {} as AudioBufferSourceNode;
}
createBiquadFilter(): BiquadFilterNode {
return {} as BiquadFilterNode;
}
createBuffer(_numberOfChannels: number, _length: number, _sampleRate: number): AudioBuffer {
return {} as AudioBuffer;
}
createChannelMerger(_numberOfInputs?: number | undefined): ChannelMergerNode {
return {} as ChannelMergerNode;
}
createChannelSplitter(_numberOfOutputs?: number | undefined): ChannelSplitterNode {
return {} as ChannelSplitterNode;
}
createConstantSource(): ConstantSourceNode {
return {} as ConstantSourceNode;
}
createConvolver(): ConvolverNode {
return {} as ConvolverNode;
}
createDelay(_maxDelayTime?: number | undefined): DelayNode {
return {} as DelayNode;
}
createDynamicsCompressor(): DynamicsCompressorNode {
return {} as DynamicsCompressorNode;
}
createGain(): GainNode {
return {} as GainNode;
}
createIIRFilter(_feedForward: number[] | Float32Array, _feedback: number[] | Float32Array): IIRFilterNode {
return {} as IIRFilterNode;
}
createPanner(): PannerNode {
return {} as PannerNode;
}
createPeriodicWave(
_real: number[] | Float32Array,
_imag: number[] | Float32Array,
_constraints?: PeriodicWaveConstraints | undefined,
): PeriodicWave {
return {} as PeriodicWave;
}
createStereoPanner(): StereoPannerNode {
return {} as StereoPannerNode;
}
createWaveShaper(): WaveShaperNode {
return {} as WaveShaperNode;
}
createMediaStreamSource(_stream: MediaStream): MediaStreamAudioSourceNode {
return {} as MediaStreamAudioSourceNode;
}
decodeAudioData(_audioData: ArrayBuffer): Promise<AudioBuffer> {
return Promise.resolve({} as AudioBuffer);
}
//---------------------------
// TONE AUDIO CONTEXT METHODS
//---------------------------
createAudioWorkletNode(
_name: string,
_options?: Partial<AudioWorkletNodeOptions>
): AudioWorkletNode {
return {} as AudioWorkletNode;
}
get rawContext(): AnyAudioContext {
return {} as AnyAudioContext;
}
async addAudioWorkletModule(_url: string, _name: string): Promise<void> {
return Promise.resolve();
}
lookAhead = 0;
resume(): Promise<void> {
return Promise.resolve();
}
setTimeout(_fn: (...args: any[]) => void, _timeout: Seconds): number {
return 0;
}
clearTimeout(_id: number): this {
return this;
}
setInterval(_fn: (...args: any[]) => void, _interval: Seconds): number {
return 0;
}
clearInterval(_id: number): this {
return this;
}
getConstant(_val: number): AudioBufferSourceNode {
return {} as AudioBufferSourceNode;
}
get currentTime(): Seconds {
return 0;
}
get state(): AudioContextState {
return {} as AudioContextState;
}
get sampleRate(): number {
return 0;
}
get listener(): AudioListener {
return {} as AudioListener;
}
get transport(): Transport {
return {} as Transport;
}
get draw(): Draw {
return {} as Draw;
}
set draw(_d) { }
get destination(): Destination {
return {} as Destination;
}
set destination(_d: Destination) { }
now() {
return 0;
}
immediate() {
return 0;
}
readonly isOffline: boolean = false;
}

View file

@ -7,13 +7,13 @@ import { Frequency, Hertz, Seconds, Ticks, Time } from "../type/Units";
import { getDefaultsFromInstance, optionsFromArguments } from "../util/Defaults";
import { RecursivePartial } from "../util/Interface";
import { isArray, isBoolean, isDefined, isNumber, isString, isUndef } from "../util/TypeCheck";
import { Context } from "./Context";
import { BaseContext } from "./BaseContext";
/**
* A unit which process audio
*/
export interface ToneWithContextOptions {
context: Context;
context: BaseContext;
}
/**
@ -24,19 +24,19 @@ export abstract class ToneWithContext<Options extends ToneWithContextOptions> ex
/**
* The context belonging to the node.
*/
readonly context: Context;
readonly context: BaseContext;
/**
* The default context to use if no AudioContext is passed in to the constructor.
* Probably should not be set manually. Used internally.
* @hidden
*/
readonly defaultContext?: Context;
readonly defaultContext?: BaseContext;
/**
* Pass in a constructor as the first argument
*/
constructor(context?: Context)
constructor(context?: BaseContext)
constructor(options?: Partial<ToneWithContextOptions>);
constructor() {
super();

View file

@ -1,4 +1,5 @@
export * from "./context/Context";
export * from "./context/BaseContext";
export * from "./context/Destination";
export * from "./context/Delay";
export * from "./context/Gain";

View file

@ -1,4 +1,4 @@
import { Context } from "../context/Context";
import { BaseContext } from "../context/BaseContext";
import { Tone } from "../Tone";
import { isDefined, isObject, isString, isUndef } from "../util/TypeCheck";
import { BPM, Hertz, MidiNote, Milliseconds, Samples, Seconds, Ticks, Time } from "./Units";
@ -27,7 +27,7 @@ export interface TimeExpression<Type extends number> {
*/
export abstract class TimeBaseClass<Type extends number, Unit extends string> extends Tone {
readonly context: Context;
readonly context: BaseContext;
/**
* The value of the units
@ -55,7 +55,7 @@ export abstract class TimeBaseClass<Type extends number, Unit extends string> ex
* @param value The time value as a number, string or object
* @param units Unit values
*/
constructor(context: Context, value?: TimeValue, units?: Unit) {
constructor(context: BaseContext, value?: TimeValue, units?: Unit) {
super();
this._val = value;