converting Solo to ts

This commit is contained in:
Yotam Mann 2019-09-10 10:07:50 -04:00
parent daf340ba69
commit 3d26c3adba
3 changed files with 284 additions and 158 deletions

View file

@ -1,158 +0,0 @@
import Tone from "../../core/Tone";
import "../core/Gain";
import "../core/AudioNode";
/**
* @class Tone.Solo lets you isolate a specific audio stream. When
* an instance is set to `solo=true`, it will mute all other instances.
* @extends {Tone.AudioNode}
* @example
* var soloA = new Tone.Solo()
* var soloB = new Tone.Solo()
* soloA.solo = true
* //no audio will pass through soloB
*/
Tone.Solo = function(){
var options = Tone.defaults(arguments, ["solo"], Tone.Solo);
Tone.AudioNode.call(this);
/**
* The input and output node
* @type {Tone.Gain}
*/
this.input = this.output = new Tone.Gain();
/**
* A bound _soloed method
* @type {Function}
* @private
*/
this._soloBind = this._soloed.bind(this);
//listen for solo events class-wide.
this.context.on("solo", this._soloBind);
//set initially
this.solo = options.solo;
};
Tone.extend(Tone.Solo, Tone.AudioNode);
/**
* The defaults
* @type {Object}
* @static
*/
Tone.Solo.defaults = {
solo : false,
};
/**
* Isolates this instance and mutes all other instances of Tone.Solo.
* Only one instance can be soloed at a time. A soloed
* instance will report `solo=false` when another instance is soloed.
* @memberOf Tone.Solo#
* @type {Boolean}
* @name solo
*/
Object.defineProperty(Tone.Solo.prototype, "solo", {
get : function(){
return this._isSoloed();
},
set : function(solo){
if (solo){
this._addSolo();
} else {
this._removeSolo();
}
this.context.emit("solo", this);
}
});
/**
* If the current instance is muted, i.e. another instance is soloed
* @memberOf Tone.Solo#
* @type {Boolean}
* @name muted
* @readOnly
*/
Object.defineProperty(Tone.Solo.prototype, "muted", {
get : function(){
return this.input.gain.value === 0;
}
});
/**
* Add this to the soloed array
* @private
*/
Tone.Solo.prototype._addSolo = function(){
if (!Tone.isArray(this.context._currentSolo)){
this.context._currentSolo = [];
}
if (!this._isSoloed()){
this.context._currentSolo.push(this);
}
};
/**
* Remove this from the soloed array
* @private
*/
Tone.Solo.prototype._removeSolo = function(){
if (this._isSoloed()){
var index = this.context._currentSolo.indexOf(this);
this.context._currentSolo.splice(index, 1);
}
};
/**
* @return {Boolean} Is this on the soloed array
* @private
*/
Tone.Solo.prototype._isSoloed = function(){
if (Tone.isArray(this.context._currentSolo)){
return this.context._currentSolo.length !== 0 && this.context._currentSolo.indexOf(this) !== -1;
} else {
return false;
}
};
/**
* @return {Boolean} Returns true if no one is soloed
* @private
*/
Tone.Solo.prototype._noSolos = function(){
return !Tone.isArray(this.context._currentSolo) || this.context._currentSolo.length === 0;
};
/**
* Solo the current instance and unsolo all other instances.
* @param {Tone.Solo} instance The instance which is being soloed/unsoloed.
* @private
*/
Tone.Solo.prototype._soloed = function(){
if (this._isSoloed()){
this.input.gain.value = 1;
} else if (this._noSolos()){
//no one is soloed
this.input.gain.value = 1;
} else {
this.input.gain.value = 0;
}
};
/**
* Clean up
* @return {Tone.Solo} this
*/
Tone.Solo.prototype.dispose = function(){
this.context.off("solo", this._soloBind);
this._removeSolo();
this._soloBind = null;
Tone.AudioNode.prototype.dispose.call(this);
return this;
};
export default Tone.Solo;

View file

@ -0,0 +1,138 @@
import { expect } from "chai";
import { BasicTests } from "test/helper/Basic";
import { ConstantOutput } from "test/helper/ConstantOutput";
import { Signal } from "Tone/signal/Signal";
import { Solo } from "./Solo";
describe("Solo", () => {
BasicTests(Solo);
context("Soloing", () => {
it("can be soloed an unsoloed", () => {
const sol = new Solo();
sol.solo = true;
expect(sol.solo).to.be.true;
sol.solo = false;
expect(sol.solo).to.be.false;
sol.dispose();
});
it("can be passed into the constructor", () => {
const sol = new Solo(true);
expect(sol.solo).to.be.true;
sol.dispose();
});
it("can be passed into the constructor with an object", () => {
const sol = new Solo({ solo : true });
expect(sol.solo).to.be.true;
sol.dispose();
});
it("other instances are unsoloed when one is soloed", () => {
const solA = new Solo();
const solB = new Solo();
solA.solo = true;
solB.solo = false;
expect(solA.solo).to.be.true;
expect(solB.solo).to.be.false;
solB.solo = true;
expect(solA.solo).to.be.true;
expect(solB.solo).to.be.true;
solA.solo = false;
expect(solA.solo).to.be.false;
expect(solB.solo).to.be.true;
solA.dispose();
solB.dispose();
});
it("other instances report themselves as muted", () => {
const solA = new Solo();
const solB = new Solo();
solA.solo = true;
solB.solo = false;
expect(solA.muted).to.be.false;
expect(solB.muted).to.be.true;
solA.dispose();
solB.dispose();
});
it("all instances are unmuted when there is no solo", () => {
const solA = new Solo();
const solB = new Solo();
solA.solo = true;
solB.solo = false;
solA.solo = false;
expect(solA.muted).to.be.false;
expect(solB.muted).to.be.false;
solA.dispose();
solB.dispose();
});
it("a newly created instance will be muted if there is already a soloed instance", () => {
const solA = new Solo(true);
const solB = new Solo();
expect(solA.muted).to.be.false;
expect(solB.muted).to.be.true;
solA.dispose();
solB.dispose();
});
it("passes both signals when nothing is soloed", () => {
return ConstantOutput(() => {
const soloA = new Solo().toDestination();
const soloB = new Solo().toDestination();
new Signal(10).connect(soloA);
new Signal(20).connect(soloB);
}, 30, 0.01);
});
it("passes one signal when it is soloed", () => {
return ConstantOutput(() => {
const soloA = new Solo().toDestination();
const soloB = new Solo().toDestination();
new Signal(10).connect(soloA);
new Signal(20).connect(soloB);
soloA.solo = true;
}, 10, 0.01);
});
it("can solo multiple at once", () => {
return ConstantOutput(() => {
const soloA = new Solo().toDestination();
const soloB = new Solo().toDestination();
new Signal(10).connect(soloA);
new Signal(20).connect(soloB);
soloA.solo = true;
soloB.solo = true;
}, 30, 0.01);
});
it("can unsolo all", () => {
return ConstantOutput(() => {
const soloA = new Solo().toDestination();
const soloB = new Solo().toDestination();
new Signal(10).connect(soloA);
new Signal(20).connect(soloB);
soloA.solo = true;
soloB.solo = true;
soloA.solo = false;
soloB.solo = false;
}, 30, 0.01);
});
it("can solo and unsolo while keeping previous soloed", () => {
return ConstantOutput(() => {
const soloA = new Solo().toDestination();
const soloB = new Solo().toDestination();
new Signal(10).connect(soloA);
new Signal(20).connect(soloB);
soloA.solo = true;
soloB.solo = true;
soloB.solo = false;
}, 10, 0.01);
});
});
});

View file

@ -0,0 +1,146 @@
import { Context } from "Tone/core";
import { Gain } from "../../core/context/Gain";
import { ToneAudioNode, ToneAudioNodeOptions } from "../../core/context/ToneAudioNode";
import { optionsFromArguments } from "../../core/util/Defaults";
export interface SoloOptions extends ToneAudioNodeOptions {
solo: boolean;
}
/**
* Solo lets you isolate a specific audio stream. When an instance is set to `solo=true`,
* it will mute all other instances of Solo.
* @example
* var soloA = new Solo()
* var soloB = new Solo()
* soloA.solo = true
* //no audio will pass through soloB
*/
export class Solo extends ToneAudioNode<SoloOptions> {
readonly name: string = "Solo";
readonly input: Gain;
readonly output: Gain;
/**
* @param solo If the connection should be initially solo'ed.
*/
constructor(solo?: boolean);
// tslint:disable-next-line: unified-signatures
constructor(options?: Partial<SoloOptions>);
constructor() {
super(optionsFromArguments(Solo.getDefaults(), arguments, ["solo"]));
const options = optionsFromArguments(Solo.getDefaults(), arguments, ["solo"]);
this.input = this.output = new Gain({
context: this.context,
});
if (!Solo._allSolos.has(this.context)) {
Solo._allSolos.set(this.context, new Set());
}
(Solo._allSolos.get(this.context) as Set<Solo>).add(this);
// set initially
this.solo = options.solo;
}
static getDefaults(): SoloOptions {
return Object.assign(ToneAudioNode.getDefaults(), {
solo: false,
});
}
/**
* Hold all of the solo'ed tracks belonging to a specific context
*/
private static _allSolos: Map<Context, Set<Solo>> = new Map();
/**
* Hold the currently solo'ed instance(s)
*/
private static _soloed: Map<Context, Set<Solo>> = new Map();
/**
* Isolates this instance and mutes all other instances of Solo.
* Only one instance can be soloed at a time. A soloed
* instance will report `solo=false` when another instance is soloed.
*/
get solo(): boolean {
return this._isSoloed();
}
set solo(solo) {
if (solo) {
this._addSolo();
} else {
this._removeSolo();
}
(Solo._allSolos.get(this.context) as Set<Solo>).forEach(instance => instance._updateSolo());
}
/**
* If the current instance is muted, i.e. another instance is soloed
*/
get muted(): boolean {
return this.input.gain.value === 0;
}
/**
* Add this to the soloed array
*/
private _addSolo(): void {
if (!Solo._soloed.has(this.context)) {
Solo._soloed.set(this.context, new Set());
}
(Solo._soloed.get(this.context) as Set<Solo>).add(this);
}
/**
* Remove this from the soloed array
*/
private _removeSolo(): void {
if (Solo._soloed.has(this.context)) {
(Solo._soloed.get(this.context) as Set<Solo>).delete(this);
}
}
/**
* Is this on the soloed array
*/
private _isSoloed(): boolean {
return Solo._soloed.has(this.context) && (Solo._soloed.get(this.context) as Set<Solo>).has(this);
}
/**
* Returns true if no one is soloed
*/
private _noSolos(): boolean {
// either does not have any soloed added
return !Solo._soloed.has(this.context) ||
// or has a solo set but doesn't include any items
(Solo._soloed.has(this.context) && (Solo._soloed.get(this.context) as Set<Solo>).size === 0);
}
/**
* Solo the current instance and unsolo all other instances.
*/
private _updateSolo(): void {
if (this._isSoloed()) {
this.input.gain.value = 1;
} else if (this._noSolos()) {
// no one is soloed
this.input.gain.value = 1;
} else {
this.input.gain.value = 0;
}
}
dispose(): this {
super.dispose();
(Solo._allSolos.get(this.context) as Set<Solo>).delete(this);
this._removeSolo();
return this;
}
}