mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-15 00:07:08 +00:00
Merge branch 'dev' of https://github.com/Tonejs/Tone.js into dev
This commit is contained in:
commit
ca47d0f719
23 changed files with 91 additions and 22 deletions
|
@ -9,11 +9,12 @@ import { optionsFromArguments } from "../../core/util/Defaults";
|
|||
export type MidSideMergeOptions = ToneAudioNodeOptions;
|
||||
|
||||
/**
|
||||
* MidSideMerge merges the mid and side signal after they've been separated by [[MidSideMerge]]
|
||||
* MidSideMerge merges the mid and side signal after they've been separated by [[MidSideSplit]]
|
||||
* ```
|
||||
* Mid = (Left+Right)/sqrt(2); // obtain mid-signal from left and right
|
||||
* Side = (Left-Right)/sqrt(2); // obtain side-signal from left and righ
|
||||
* Side = (Left-Right)/sqrt(2); // obtain side-signal from left and right
|
||||
* ```
|
||||
* @category Component
|
||||
*/
|
||||
export class MidSideMerge extends ToneAudioNode<MidSideMergeOptions> {
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ export type MidSideSplitOptions = ToneAudioNodeOptions;
|
|||
* Mid = (Left+Right)/sqrt(2); // obtain mid-signal from left and right
|
||||
* Side = (Left-Right)/sqrt(2); // obtain side-signal from left and right
|
||||
* ```
|
||||
* @category Component
|
||||
*/
|
||||
export class MidSideSplit extends ToneAudioNode<MidSideSplitOptions> {
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ export interface RecorderOptions extends ToneAudioNodeOptions {
|
|||
* anchor.href = url;
|
||||
* anchor.click();
|
||||
* }, 4000);
|
||||
* @category Component
|
||||
*/
|
||||
export class Recorder extends ToneAudioNode<RecorderOptions> {
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface GateOptions extends ToneAudioNodeOptions {
|
|||
* const mic = new Tone.UserMedia().connect(gate);
|
||||
* // the gate will only pass through the incoming
|
||||
* // signal when it's louder than -30db
|
||||
* @category Component
|
||||
*/
|
||||
export class Gate extends ToneAudioNode<GateOptions> {
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface LimiterOptions extends ToneAudioNodeOptions {
|
|||
* const limiter = new Tone.Limiter(-20).toDestination();
|
||||
* const oscillator = new Tone.Oscillator().connect(limiter);
|
||||
* oscillator.start();
|
||||
* @category Component
|
||||
*/
|
||||
export class Limiter extends ToneAudioNode<LimiterOptions> {
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ export interface MidSideCompressorOptions extends ToneAudioNodeOptions {
|
|||
/**
|
||||
* MidSideCompressor applies two different compressors to the [[mid]]
|
||||
* and [[side]] signal components of the input. See [[MidSideSplit]] and [[MidSideMerge]].
|
||||
* @category Component
|
||||
*/
|
||||
export class MidSideCompressor extends ToneAudioNode<MidSideCompressorOptions> {
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ export interface MultibandCompressorOptions extends ToneAudioNodeOptions {
|
|||
* threshold: -12
|
||||
* }
|
||||
* });
|
||||
* @category Component
|
||||
*/
|
||||
export class MultibandCompressor extends ToneAudioNode<MultibandCompressorOptions> {
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ export interface FrequencyEnvelopeOptions extends EnvelopeOptions {
|
|||
* });
|
||||
* freqEnv.connect(oscillator.frequency);
|
||||
* freqEnv.triggerAttack();
|
||||
* @category Component
|
||||
*/
|
||||
export class FrequencyEnvelope extends Envelope {
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ export interface BaseToneOptions { }
|
|||
|
||||
/**
|
||||
* @class Tone is the base class of all other classes.
|
||||
* @category Core
|
||||
* @constructor
|
||||
*/
|
||||
export abstract class Tone {
|
||||
|
|
|
@ -46,6 +46,7 @@ export type AutomationEvent = NormalAutomationEvent | TargetAutomationEvent;
|
|||
* additional unit conversion functionality. It also
|
||||
* serves as a base-class for classes which have a single,
|
||||
* automatable parameter.
|
||||
* @category Core
|
||||
*/
|
||||
export class Param<TypeName extends UnitName = "number">
|
||||
extends ToneWithContext<ParamOptions<TypeName>>
|
||||
|
|
|
@ -29,6 +29,19 @@ describe("Emitter", () => {
|
|||
emitter.dispose();
|
||||
});
|
||||
|
||||
it("can unbind duplicate events", () => {
|
||||
const emitter = new Emitter();
|
||||
const callback = () => {
|
||||
throw new Error("should call this");
|
||||
};
|
||||
emitter.on("something", callback);
|
||||
emitter.on("something", callback);
|
||||
emitter.on("something", callback);
|
||||
emitter.off("something", callback);
|
||||
emitter.emit("something");
|
||||
emitter.dispose();
|
||||
});
|
||||
|
||||
it("'off' does nothing if there is no event scheduled", () => {
|
||||
const emitter = new Emitter();
|
||||
const callback = () => {
|
||||
|
|
|
@ -10,6 +10,7 @@ export interface EmitterEventObject {
|
|||
* the ability to listen for and emit events.
|
||||
* Inspiration and reference from Jerome Etienne's [MicroEvent](https://github.com/jeromeetienne/microevent.js).
|
||||
* MIT (c) 2011 Jerome Etienne.
|
||||
* @category Core
|
||||
*/
|
||||
export class Emitter<EventType extends string = string> extends Tone {
|
||||
|
||||
|
@ -73,7 +74,7 @@ export class Emitter<EventType extends string = string> extends Tone {
|
|||
this._events[event] = [];
|
||||
} else {
|
||||
const eventList = this._events[event];
|
||||
for (let i = 0; i < eventList.length; i++) {
|
||||
for (let i = eventList.length - 1; i >= 0; i--) {
|
||||
if (eventList[i] === callback) {
|
||||
eventList.splice(i, 1);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ export type MidSideEffectOptions = EffectOptions;
|
|||
* Applies a Mid/Side seperation and recombination.
|
||||
* Algorithm found in [kvraudio forums](http://www.kvraudio.com/forum/viewtopic.php?t=212587).
|
||||
* This is a base-class for Mid/Side Effects.
|
||||
* @category Effect
|
||||
*/
|
||||
export abstract class MidSideEffect<Options extends MidSideEffectOptions> extends Effect<Options> {
|
||||
|
||||
|
|
|
@ -11,17 +11,7 @@ export interface StereoFeedbackEffectOptions extends StereoEffectOptions {
|
|||
}
|
||||
|
||||
/**
|
||||
* Just like a stereo feedback effect, but the feedback is routed from left to right
|
||||
* and right to left instead of on the same channel.
|
||||
* ```
|
||||
* +--------------------------------+ feedbackL <-----------------------------------+
|
||||
* | |
|
||||
* +--> +-----> +----> +---+
|
||||
* feedbackMerge +--> split (EFFECT) merge +--> feedbackSplit
|
||||
* +--> +-----> +----> +---+
|
||||
* | |
|
||||
* +--------------------------------+ feedbackR <-----------------------------------+
|
||||
* ```
|
||||
* Base class for stereo feedback effects where the effectReturn is fed back into the same channel.
|
||||
*/
|
||||
export class StereoFeedbackEffect<Options extends StereoFeedbackEffectOptions> extends StereoEffect<Options> {
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ export interface StereoXFeedbackEffectOptions extends StereoFeedbackEffectOption
|
|||
* | |
|
||||
* +--------------------------------+ feedbackR <-------------------------------------+
|
||||
* ```
|
||||
* @category Effect
|
||||
*/
|
||||
export class StereoXFeedbackEffect<Options extends StereoXFeedbackEffectOptions> extends StereoFeedbackEffect<Options> {
|
||||
|
||||
|
|
|
@ -80,14 +80,25 @@ export abstract class Instrument<Options extends InstrumentOptions> extends Tone
|
|||
* Tone.Transport.start();
|
||||
*/
|
||||
sync(): this {
|
||||
if (!this._synced) {
|
||||
this._synced = true;
|
||||
if (this._syncState()) {
|
||||
this._syncMethod("triggerAttack", 1);
|
||||
this._syncMethod("triggerRelease", 0);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* set _sync
|
||||
*/
|
||||
protected _syncState(): boolean {
|
||||
let changed = false;
|
||||
if (!this._synced) {
|
||||
this._synced = true;
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the given method so that it can be synchronized
|
||||
* @param method Which method to wrap and sync
|
||||
|
|
|
@ -103,8 +103,10 @@ export class NoiseSynth extends Instrument<NoiseSynthOptions> {
|
|||
}
|
||||
|
||||
sync(): this {
|
||||
if (this._syncState()) {
|
||||
this._syncMethod("triggerAttack", 0);
|
||||
this._syncMethod("triggerRelease", 0);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -334,8 +334,10 @@ export class PolySynth<Voice extends Monophonic<any> = Synth> extends Instrument
|
|||
}
|
||||
|
||||
sync(): this {
|
||||
if (this._syncState()) {
|
||||
this._syncMethod("triggerAttack", 1);
|
||||
this._syncMethod("triggerRelease", 1);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -254,8 +254,10 @@ export class Sampler extends Instrument<SamplerOptions> {
|
|||
}
|
||||
|
||||
sync(): this {
|
||||
if (this._syncState()) {
|
||||
this._syncMethod("triggerAttack", 1);
|
||||
this._syncMethod("triggerRelease", 1);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ export type GreaterThanOptions = SignalOptions<"number">;
|
|||
* const gt = new Tone.GreaterThan(2).toDestination();
|
||||
* const sig = new Tone.Signal(4).connect(gt);
|
||||
* }, 0.1, 1);
|
||||
* @category Signal
|
||||
*/
|
||||
export class GreaterThan extends Signal<"number"> {
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ export type GreaterThanZeroOptions = SignalOperatorOptions
|
|||
* const sig = new Tone.Signal(0.5).connect(gt0);
|
||||
* sig.setValueAtTime(-1, 0.05);
|
||||
* }, 0.1, 1);
|
||||
* @category Signal
|
||||
*/
|
||||
export class GreaterThanZero extends SignalOperator<GreaterThanZeroOptions> {
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ export interface ScaleExpOptions extends ScaleOptions {
|
|||
* @example
|
||||
* const scaleExp = new Tone.ScaleExp(0, 100, 2);
|
||||
* const signal = new Tone.Signal(0.5).connect(scaleExp);
|
||||
* @category Signal
|
||||
*/
|
||||
export class ScaleExp extends Scale<ScaleExpOptions> {
|
||||
|
||||
|
|
|
@ -5,6 +5,10 @@ import { Offline } from "./Offline";
|
|||
import { OutputAudio } from "./OutputAudio";
|
||||
import { Monophonic } from "Tone/instrument/Monophonic";
|
||||
|
||||
function wait(time) {
|
||||
return new Promise(done => setTimeout(done, time));
|
||||
}
|
||||
|
||||
export function InstrumentTest(Constr, note, constrArg?, optionsIndex?): void {
|
||||
|
||||
context("Instrument Tests", () => {
|
||||
|
@ -167,6 +171,35 @@ export function InstrumentTest(Constr, note, constrArg?, optionsIndex?): void {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
it("can unsync and re-sync triggerAttack to the Transport", () => {
|
||||
return Offline(async ({ transport }) => {
|
||||
const instance = new Constr(constrArg);
|
||||
instance.toDestination();
|
||||
|
||||
instance.sync();
|
||||
if (note) {
|
||||
instance.triggerAttack(note, 0.1);
|
||||
} else {
|
||||
instance.triggerAttack(0.1);
|
||||
}
|
||||
transport.start(0.1);
|
||||
await wait(100);
|
||||
instance.unsync();
|
||||
transport.stop();
|
||||
|
||||
instance.sync();
|
||||
if (note) {
|
||||
instance.triggerAttack(note, 0.1);
|
||||
} else {
|
||||
instance.triggerAttack(0.1);
|
||||
}
|
||||
transport.start(0.1);
|
||||
}, 1).then((buffer) => {
|
||||
expect(buffer.getTimeOfFirstSound()).to.be.within(0.19, 0.25);
|
||||
});
|
||||
});
|
||||
|
||||
it("calling sync and unsync multiple times has no effect", () => {
|
||||
return Offline(({ transport }) => {
|
||||
const instance = new Constr(constrArg);
|
||||
|
|
Loading…
Reference in a new issue