Tone.js/Tone/component/channel/Solo.ts

152 lines
3.8 KiB
TypeScript
Raw Normal View History

2019-09-10 14:07:50 +00:00
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
2019-10-23 03:39:35 +00:00
* import { Oscillator, Solo } from "tone";
*
2019-10-23 03:04:52 +00:00
* const soloA = new Solo().toDestination();
* const oscA = new Oscillator("C4", "sawtooth").connect(soloA);
* const soloB = new Solo().toDestination();
* const oscB = new Oscillator("E4", "square").connect(soloB);
*
* soloA.solo = true;
* // no audio will pass through soloB
2019-09-16 14:15:23 +00:00
* @category Component
2019-09-10 14:07:50 +00:00
*/
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);
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();
/**
2019-09-14 20:39:18 +00:00
* 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.
2019-09-10 14:07:50 +00:00
*/
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());
}
/**
2019-09-14 20:39:18 +00:00
* If the current instance is muted, i.e. another instance is soloed
2019-09-10 14:07:50 +00:00
*/
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);
}
/**
2019-09-14 20:39:18 +00:00
* Solo the current instance and unsolo all other instances.
2019-09-10 14:07:50 +00:00
*/
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;
}
}