cleaning up circular deps

This commit is contained in:
Yotam Mann 2019-07-11 09:57:06 -04:00
parent c658980915
commit 219462fe9e
24 changed files with 207 additions and 180 deletions

View file

@ -1,6 +1,6 @@
import { Param } from "Tone/core/context/Param";
import { ToneAudioNode, ToneAudioNodeOptions } from "Tone/core/context/ToneAudioNode";
import { Gain } from "../../core/context/Gain";
import { Param } from "../../core/context/Param";
import { ToneAudioNode, ToneAudioNodeOptions } from "../../core/context/ToneAudioNode";
import { optionsFromArguments } from "../../core/util/Defaults";
import { readOnly } from "../../core/util/Interface";

View file

@ -1,114 +1 @@
import { InputNode, OutputNode, ToneAudioNode } from "./context/ToneAudioNode";
import { isArray, isDefined, isNumber } from "./util/TypeCheck";
/**
* connect together all of the arguments in series
* @param nodes
*/
export function connectSeries(...nodes: InputNode[]): void {
const first = nodes.shift();
nodes.reduce((prev, current) => {
if (prev instanceof ToneAudioNode || prev instanceof AudioNode) {
connect(prev, current);
}
return current;
}, first);
}
/**
* Connect two nodes together so that signal flows from the
* first node to the second. Optionally specify the input and output channels.
* @param srcNode The source node
* @param dstNode The destination node
* @param outputNumber The output channel of the srcNode
* @param inputNumber The input channel of the dstNode
*/
export function connect(srcNode: OutputNode, dstNode: InputNode, outputNumber = 0, inputNumber = 0): void {
// resolve the input of the dstNode
while (!(dstNode instanceof AudioNode || dstNode instanceof AudioParam)) {
if (isArray(dstNode.input)) {
this.assert(dstNode.input.length < inputNumber, "the output number is greater than the number of outputs");
dstNode = dstNode.input[inputNumber];
} else if (isDefined(dstNode.input)) {
dstNode = dstNode.input;
}
inputNumber = 0;
}
if (srcNode instanceof ToneAudioNode) {
if (isArray(srcNode.output)) {
this.assert(srcNode.output.length < outputNumber, "the output number is greater than the number of outputs");
srcNode = srcNode.output[outputNumber];
} else if (isDefined(srcNode.output)) {
srcNode = srcNode.output;
}
outputNumber = 0;
}
// make the connection
if (dstNode instanceof AudioParam) {
srcNode.connect(dstNode, outputNumber);
} else {
srcNode.connect(dstNode, outputNumber, inputNumber);
}
}
/**
* Disconnect a node from all nodes or optionally include a destination node and input/output channels.
* @param srcNode The source node
* @param dstNode The destination node
* @param outputNumber The output channel of the srcNode
* @param inputNumber The input channel of the dstNode
*/
export function disconnect(
srcNode: OutputNode,
dstNode?: InputNode,
outputNumber = 0,
inputNumber = 0,
): void {
// resolve the destination node
if (isDefined(dstNode)) {
while (dstNode instanceof ToneAudioNode) {
if (isArray(dstNode.input)) {
if (isNumber(inputNumber)) {
this.assert(dstNode.input.length < inputNumber, "the input number is greater than the number of inputs");
dstNode = dstNode.input[inputNumber];
} else {
// disconnect from all of the nodes
// since we don't know which one was connected
dstNode.input.forEach(dst => {
try {
// catch errors from disconnecting from nodes that are not connected
disconnect(srcNode, dst, outputNumber);
// tslint:disable-next-line: no-empty
} catch (e) { }
});
}
inputNumber = 0;
} else if (dstNode.input) {
dstNode = dstNode.input;
}
}
}
// resolve the src node
while (!(srcNode instanceof AudioNode)) {
if (isArray(srcNode.output)) {
this.assert(srcNode.output.length < outputNumber, "the output number is greater than the number of outputs");
srcNode = srcNode.output[outputNumber];
} else if (isDefined(srcNode.output)) {
srcNode = srcNode.output;
}
outputNumber = 0;
}
if (dstNode instanceof AudioParam) {
srcNode.disconnect(dstNode, outputNumber);
} else if (dstNode instanceof AudioNode) {
srcNode.disconnect(dstNode, outputNumber, inputNumber);
} else {
srcNode.disconnect();
}
}
export { connect, disconnect, connectSeries } from "./context/ToneAudioNode";

View file

@ -5,8 +5,7 @@
* @copyright 2014-2019 Yotam Mann
*/
import { version } from "../version";
import { Context } from "./context/Context";
import { getContext } from "./Global";
// import { getContext } from "./Global";
import "./type/Units";
///////////////////////////////////////////////////////////////////////////
@ -151,11 +150,11 @@ export abstract class Tone {
// STATIC
///////////////////////////////////////////////////////////////////////////
static get context(): Context {
return getContext();
}
// static get context(): import("./context/Context").Context {
// return getContext();
// }
static now(): Seconds {
return Tone.context.now();
}
// static now(): Seconds {
// return Tone.context.now();
// }
}

View file

@ -1,7 +1,7 @@
import { Time, TimeClass } from "Tone/core/type/Time";
import { PlaybackState } from "Tone/core/util/StateTimeline";
import { Signal } from "Tone/signal/Signal";
import { Context } from "../context/Context";
import { Time, TimeClass } from "../../core/type/Time";
import { PlaybackState } from "../../core/util/StateTimeline";
import { Signal } from "../../signal/Signal";
import { onContextInit } from "../context/ContextInitialization";
import { Gain } from "../context/Gain";
import { Param } from "../context/Param";
import { ToneWithContext, ToneWithContextOptions } from "../context/ToneWithContext";
@ -692,6 +692,6 @@ Emitter.mixin(Transport);
// INITIALIZATION
///////////////////////////////////////////////////////////////////////////////
Context.onInit(context => {
onContextInit(context => {
context.transport = new Transport({ context });
});

View file

@ -1,5 +1,6 @@
import { noOp } from "../util/Interface";
import { Transport } from "./Transport";
type Transport = import("../clock/Transport").Transport;
export interface TransportEventOptions {
callback: (time: number) => void;

View file

@ -1,8 +1,9 @@
import { Context } from "../context/Context";
import { Ticks, TicksClass } from "../type/Ticks";
import { Transport } from "./Transport";
import { TransportEvent, TransportEventOptions } from "./TransportEvent";
type Transport = import("../clock/Transport").Transport;
interface TransportRepeatEventOptions extends TransportEventOptions {
interval: Ticks;
duration: Ticks;

View file

@ -2,6 +2,7 @@ import { expect } from "chai";
import { Offline } from "test/helper/Offline";
import { ONLINE_TESTING } from "test/helper/Supports";
import { Transport } from "../clock/Transport";
import { getContext } from "../Global";
import { Tone } from "../Tone";
import { getAudioContext } from "./AudioContext";
import { Context } from "./Context";
@ -29,7 +30,7 @@ describe("Context", () => {
if (ONLINE_TESTING) {
it("clock is running", done => {
const interval = setInterval(() => {
if (Tone.context.currentTime > 0.5) {
if (getContext().currentTime > 0.5) {
clearInterval(interval);
done();
}

View file

@ -1,12 +1,14 @@
import { Ticker, TickerClockSource } from "../clock/Ticker";
import { Transport } from "../clock/Transport";
import { optionsFromArguments } from "../util/Defaults";
import { Emitter } from "../util/Emitter";
import { Omit } from "../util/Interface";
import { Timeline } from "../util/Timeline";
import { isString } from "../util/TypeCheck";
import { getAudioContext } from "./AudioContext";
import { Destination } from "./Destination";
import { initializeContext } from "./ContextInitialization";
type Transport = import("../clock/Transport").Transport;
type Destination = import("./Destination").Destination;
export type ContextLatencyHint = AudioContextLatencyCategory | "fastest";
@ -125,7 +127,7 @@ export class Context extends Emitter<"statechange" | "tick"> implements BaseAudi
initialize(): this {
if (!this._initialized) {
// add any additional modules
Context._notifyNewContext.forEach(cb => cb(this));
initializeContext(this);
this._initialized = true;
}
return this;
@ -429,20 +431,4 @@ export class Context extends Emitter<"statechange" | "tick"> implements BaseAudi
});
return this;
}
///////////////////////////////////////////////////////////////////////////
// INITIALIZING NEW CONTEXT
///////////////////////////////////////////////////////////////////////////
/**
* Array of callbacks to invoke when a new context is created
*/
private static _notifyNewContext: Array<(ctx: Context) => void> = [];
/**
* Used internally to setup a new Context
*/
static onInit(cb: (ctx: Context) => void): void {
Context._notifyNewContext.push(cb);
}
}

View file

@ -0,0 +1,22 @@
///////////////////////////////////////////////////////////////////////////
// INITIALIZING NEW CONTEXT
///////////////////////////////////////////////////////////////////////////
type Context = import("./Context").Context;
/**
* Array of callbacks to invoke when a new context is created
*/
const notifyNewContext: Array<(ctx: Context) => void> = [];
/**
* Used internally to setup a new Context
*/
export function onContextInit(cb: (ctx: Context) => void): void {
notifyNewContext.push(cb);
}
export function initializeContext(ctx: Context): void {
// add any additional modules
notifyNewContext.forEach(cb => cb(ctx));
}

View file

@ -1,7 +1,7 @@
import { Volume } from "Tone/component/channel/Volume";
import { Volume } from "../../component/channel/Volume";
import { connectSeries } from "../Connect";
import { optionsFromArguments } from "../util/Defaults";
import { Context } from "./Context";
import { onContextInit } from "./ContextInitialization";
import { Gain } from "./Gain";
import { Param } from "./Param";
import { ToneAudioNode, ToneAudioNodeOptions } from "./ToneAudioNode";
@ -112,6 +112,6 @@ export class Destination extends ToneAudioNode<DestinationOptions> {
// INITIALIZATION
///////////////////////////////////////////////////////////////////////////
Context.onInit(context => {
onContextInit(context => {
context.destination = new Destination({ context });
});

View file

@ -1,6 +1,5 @@
import { connect, connectSeries, disconnect } from "../Connect";
import { optionsFromArguments } from "../util/Defaults";
import { isArray, isDefined } from "../util/TypeCheck";
import { isArray, isDefined, isNumber } from "../util/TypeCheck";
import { Param } from "./Param";
import { ToneWithContext, ToneWithContextOptions } from "./ToneWithContext";
@ -281,3 +280,119 @@ extends ToneWithContext<Options> {
return this;
}
}
///////////////////////////////////////////////////////////////////////////////
// CONNECTIONS
///////////////////////////////////////////////////////////////////////////////
/**
* connect together all of the arguments in series
* @param nodes
*/
export function connectSeries(...nodes: InputNode[]): void {
const first = nodes.shift();
nodes.reduce((prev, current) => {
if (prev instanceof ToneAudioNode || prev instanceof AudioNode) {
connect(prev, current);
}
return current;
}, first);
}
/**
* Connect two nodes together so that signal flows from the
* first node to the second. Optionally specify the input and output channels.
* @param srcNode The source node
* @param dstNode The destination node
* @param outputNumber The output channel of the srcNode
* @param inputNumber The input channel of the dstNode
*/
export function connect(srcNode: OutputNode, dstNode: InputNode, outputNumber = 0, inputNumber = 0): void {
// resolve the input of the dstNode
while (!(dstNode instanceof AudioNode || dstNode instanceof AudioParam)) {
if (isArray(dstNode.input)) {
this.assert(dstNode.input.length < inputNumber, "the output number is greater than the number of outputs");
dstNode = dstNode.input[inputNumber];
} else if (isDefined(dstNode.input)) {
dstNode = dstNode.input;
}
inputNumber = 0;
}
if (srcNode instanceof ToneAudioNode) {
if (isArray(srcNode.output)) {
this.assert(srcNode.output.length < outputNumber, "the output number is greater than the number of outputs");
srcNode = srcNode.output[outputNumber];
} else if (isDefined(srcNode.output)) {
srcNode = srcNode.output;
}
outputNumber = 0;
}
// make the connection
if (dstNode instanceof AudioParam) {
srcNode.connect(dstNode, outputNumber);
} else {
srcNode.connect(dstNode, outputNumber, inputNumber);
}
}
/**
* Disconnect a node from all nodes or optionally include a destination node and input/output channels.
* @param srcNode The source node
* @param dstNode The destination node
* @param outputNumber The output channel of the srcNode
* @param inputNumber The input channel of the dstNode
*/
export function disconnect(
srcNode: OutputNode,
dstNode?: InputNode,
outputNumber = 0,
inputNumber = 0,
): void {
// resolve the destination node
if (isDefined(dstNode)) {
while (dstNode instanceof ToneAudioNode) {
if (isArray(dstNode.input)) {
if (isNumber(inputNumber)) {
this.assert(dstNode.input.length < inputNumber, "the input number is greater than the number of inputs");
dstNode = dstNode.input[inputNumber];
} else {
// disconnect from all of the nodes
// since we don't know which one was connected
dstNode.input.forEach(dst => {
try {
// catch errors from disconnecting from nodes that are not connected
disconnect(srcNode, dst, outputNumber);
// tslint:disable-next-line: no-empty
} catch (e) { }
});
}
inputNumber = 0;
} else if (dstNode.input) {
dstNode = dstNode.input;
}
}
}
// resolve the src node
while (!(srcNode instanceof AudioNode)) {
if (isArray(srcNode.output)) {
this.assert(srcNode.output.length < outputNumber, "the output number is greater than the number of outputs");
srcNode = srcNode.output[outputNumber];
} else if (isDefined(srcNode.output)) {
srcNode = srcNode.output;
}
outputNumber = 0;
}
if (dstNode instanceof AudioParam) {
srcNode.disconnect(dstNode, outputNumber);
} else if (dstNode instanceof AudioNode) {
srcNode.disconnect(dstNode, outputNumber, inputNumber);
} else {
srcNode.disconnect();
}
}

View file

@ -14,3 +14,6 @@ export { StateTimeline } from "./util/StateTimeline";
export { IntervalTimeline } from "./util/IntervalTimeline";
export { Timeline } from "./util/Timeline";
export { Emitter } from "./util/Emitter";
export { Tone } from "./Tone";
export { Destination } from "./context/Destination";
export { Transport } from "./clock/Transport";

View file

@ -39,7 +39,7 @@ describe("TimeClass", () => {
});
it("with no arguments evaluates to 'now'", () => {
const now = Tone.now();
const now = getContext().now();
expect(Time().valueOf()).to.be.closeTo(now, 0.01);
});
@ -111,7 +111,7 @@ describe("TimeClass", () => {
context("Operators", () => {
it("can add the current time", () => {
const now = Tone.now();
const now = getContext().now();
expect(Time("+4").valueOf()).to.be.closeTo(4 + now, 0.02);
expect(Time("+2n").valueOf()).to.be.closeTo(1 + now, 0.02);
});
@ -141,7 +141,7 @@ describe("TimeClass", () => {
});
it("converts time into samples", () => {
expect(Time(2).toSamples()).to.equal(2 * Tone.context.sampleRate);
expect(Time(2).toSamples()).to.equal(2 * getContext().sampleRate);
});
it("converts time into frequency", () => {

View file

@ -1,6 +1,8 @@
import { BaseToneOptions } from "../Tone";
// import { BaseToneOptions } from "../Tone";
import { isDefined, isObject, isUndef } from "./TypeCheck";
type BaseToneOptions = import("../Tone").BaseToneOptions;
/**
* Recursively merge an object
* @param target the object to merge into

View file

@ -1 +1,2 @@
export * from "./core/index";
export * from "./source/index";

View file

@ -1,5 +1,5 @@
import { ToneAudioBuffer } from "Tone/core/context/ToneAudioBuffer";
import { optionsFromArguments } from "Tone/core/util/Defaults";
import { ToneAudioBuffer } from "../core/context/ToneAudioBuffer";
import { optionsFromArguments } from "../core/util/Defaults";
import { Source, SourceOptions } from "../source/Source";
import { ToneBufferSource } from "./buffer/BufferSource";

View file

@ -1,9 +1,9 @@
import { Volume } from "Tone/component/channel/Volume";
import { ToneAudioNode, ToneAudioNodeOptions } from "Tone/core/context/ToneAudioNode";
import { defaultArg, optionsFromArguments } from "Tone/core/util/Defaults";
import { noOp, readOnly } from "Tone/core/util/Interface";
import { PlaybackState, StateTimeline, StateTimelineEvent } from "Tone/core/util/StateTimeline";
import { isUndef } from "Tone/core/util/TypeCheck";
import { Volume } from "../component/channel/Volume";
import { ToneAudioNode, ToneAudioNodeOptions } from "../core/context/ToneAudioNode";
import { defaultArg, optionsFromArguments } from "../core/util/Defaults";
import { noOp, readOnly } from "../core/util/Interface";
import { PlaybackState, StateTimeline, StateTimelineEvent } from "../core/util/StateTimeline";
import { isUndef } from "../core/util/TypeCheck";
export interface SourceOptions extends ToneAudioNodeOptions {
volume: Decibels;

View file

@ -1,10 +1,9 @@
import { Tone } from "Tone/core/Tone";
import { noOp } from "Tone/core/util/Interface";
import { isDefined, isUndef } from "Tone/core/util/TypeCheck";
import { connect } from "../../core/Connect";
import { Param } from "../../core/context/Param";
import { ToneAudioBuffer } from "../../core/context/ToneAudioBuffer";
import { defaultArg, optionsFromArguments } from "../../core/util/Defaults";
import { noOp } from "../../core/util/Interface";
import { isDefined } from "../../core/util/TypeCheck";
import { OneShotSource, OneShotSourceOptions } from "../OneShotSource";
interface ToneBufferSourceOptions extends OneShotSourceOptions {

4
Tone/source/index.ts Normal file
View file

@ -0,0 +1,4 @@
export { Noise } from "./Noise";
export { Oscillator } from "./oscillator/Oscillator";
export { ToneOscillatorNode } from "./oscillator/OscillatorNode";
export { ToneBufferSource } from "./buffer/BufferSource";

View file

@ -1,5 +1,5 @@
import { optionsFromArguments } from "Tone/core/util/Defaults";
import { readOnly } from "Tone/core/util/Interface";
import { optionsFromArguments } from "../../core/util/Defaults";
import { readOnly } from "../../core/util/Interface";
import { Signal } from "../../signal/Signal";
import { Source, SourceOptions } from "../Source";
import { ToneOscillatorNode } from "./OscillatorNode";

View file

@ -1,7 +1,6 @@
import { connect } from "Tone/core/Connect";
import { Param } from "Tone/core/context/Param";
import { ToneAudioNodeOptions } from "Tone/core/context/ToneAudioNode";
import { optionsFromArguments } from "Tone/core/util/Defaults";
import { connect } from "../../core/Connect";
import { Param } from "../../core/context/Param";
import { optionsFromArguments } from "../../core/util/Defaults";
import { OneShotSource, OneShotSourceOptions } from "../OneShotSource";
interface ToneOscillatorNodeOptions extends OneShotSourceOptions {

8
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "tone",
"version": "14.0.0",
"version": "14.0.0-ts",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -8662,6 +8662,12 @@
}
}
},
"tslint-no-circular-imports": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/tslint-no-circular-imports/-/tslint-no-circular-imports-0.7.0.tgz",
"integrity": "sha512-k3wxpeMC4ef40UbpfBVHEHIzKfNZq5/SCtAO1YjGsaNTklo+K53/TWLrym+poA65RJFDiYgYNWvkeIIkJNA0Vw==",
"dev": true
},
"tslint-no-unused-expression-chai": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/tslint-no-unused-expression-chai/-/tslint-no-unused-expression-chai-0.1.4.tgz",

View file

@ -32,7 +32,7 @@
"tsd": "node ./scripts/generate_docs.js",
"lint": "tslint --project tsconfig.json",
"ts:build": "rm -rf dist && tsc --project tsconfig.build.json",
"watch:old": "npm run increment && npm run collect:deps && webpack -w --env.production --mode=development",
"watch:old": "webpack -w --env.production --mode=development",
"watch": "tsc --watch",
"webpack:build": "rm -rf build && webpack -p --env.production"
},
@ -92,6 +92,7 @@
"ts-loader": "^6.0.4",
"tsd-jsdoc": "^2.1.2",
"tslint": "^5.18.0",
"tslint-no-circular-imports": "^0.7.0",
"tslint-no-unused-expression-chai": "^0.1.4",
"typescript": "^3.3.4000",
"ua-parser-js": "^0.7.20",

View file

@ -1,5 +1,5 @@
{
"extends": "tslint:recommended",
"extends": ["tslint:recommended", "tslint-no-circular-imports"],
"rulesDirectory": [
"tslint-no-unused-expression-chai"
],