tone 15.5.1 → 15.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/Tone/core/context/OnRunning.test.ts +63 -0
  2. package/Tone/core/context/OnRunning.ts +26 -0
  3. package/Tone/core/context/ToneWithContext.test.ts +82 -0
  4. package/Tone/core/context/ToneWithContext.ts +24 -0
  5. package/Tone/core/util/Interface.ts +8 -2
  6. package/Tone/core/util/TypeCheck.ts +9 -9
  7. package/Tone/effect/FrequencyShifter.ts +5 -3
  8. package/Tone/effect/LFOStereoEffect.test.ts +85 -0
  9. package/Tone/effect/LFOStereoEffect.ts +136 -0
  10. package/Tone/effect/Phaser.ts +9 -45
  11. package/Tone/effect/PitchShift.ts +8 -5
  12. package/Tone/effect/StereoWidener.ts +12 -2
  13. package/Tone/effect/Tremolo.test.ts +0 -24
  14. package/Tone/effect/Tremolo.ts +13 -80
  15. package/Tone/effect/Vibrato.ts +5 -3
  16. package/Tone/signal/ToneConstantSource.ts +7 -19
  17. package/Tone/signal/WaveShaper.ts +15 -8
  18. package/Tone/source/UserMedia.test.ts +131 -112
  19. package/Tone/source/UserMedia.ts +37 -26
  20. package/Tone/source/oscillator/Oscillator.test.ts +3 -4
  21. package/Tone/version.ts +1 -1
  22. package/build/Tone.js +1 -1
  23. package/build/Tone.js.map +1 -1
  24. package/build/esm/core/context/OnRunning.d.ts +8 -0
  25. package/build/esm/core/context/OnRunning.js +21 -0
  26. package/build/esm/core/context/OnRunning.js.map +1 -0
  27. package/build/esm/core/context/ToneWithContext.d.ts +14 -0
  28. package/build/esm/core/context/ToneWithContext.js +18 -0
  29. package/build/esm/core/context/ToneWithContext.js.map +1 -1
  30. package/build/esm/core/util/Interface.d.ts +2 -2
  31. package/build/esm/core/util/Interface.js.map +1 -1
  32. package/build/esm/core/util/TypeCheck.d.ts +8 -8
  33. package/build/esm/core/util/TypeCheck.js.map +1 -1
  34. package/build/esm/effect/FrequencyShifter.js +5 -3
  35. package/build/esm/effect/FrequencyShifter.js.map +1 -1
  36. package/build/esm/effect/LFOStereoEffect.d.ts +53 -0
  37. package/build/esm/effect/LFOStereoEffect.js +96 -0
  38. package/build/esm/effect/LFOStereoEffect.js.map +1 -0
  39. package/build/esm/effect/Phaser.d.ts +3 -16
  40. package/build/esm/effect/Phaser.js +6 -28
  41. package/build/esm/effect/Phaser.js.map +1 -1
  42. package/build/esm/effect/PitchShift.js +7 -5
  43. package/build/esm/effect/PitchShift.js.map +1 -1
  44. package/build/esm/effect/StereoWidener.d.ts +4 -0
  45. package/build/esm/effect/StereoWidener.js +7 -2
  46. package/build/esm/effect/StereoWidener.js.map +1 -1
  47. package/build/esm/effect/Tremolo.d.ts +4 -32
  48. package/build/esm/effect/Tremolo.js +9 -59
  49. package/build/esm/effect/Tremolo.js.map +1 -1
  50. package/build/esm/effect/Vibrato.js +4 -3
  51. package/build/esm/effect/Vibrato.js.map +1 -1
  52. package/build/esm/signal/ToneConstantSource.d.ts +1 -1
  53. package/build/esm/signal/ToneConstantSource.js +16 -27
  54. package/build/esm/signal/ToneConstantSource.js.map +1 -1
  55. package/build/esm/signal/WaveShaper.d.ts +5 -0
  56. package/build/esm/signal/WaveShaper.js +15 -7
  57. package/build/esm/signal/WaveShaper.js.map +1 -1
  58. package/build/esm/source/UserMedia.d.ts +3 -3
  59. package/build/esm/source/UserMedia.js +32 -25
  60. package/build/esm/source/UserMedia.js.map +1 -1
  61. package/build/esm/version.js +1 -1
  62. package/package.json +1 -1
@@ -0,0 +1,63 @@
1
+ import { expect } from "chai";
2
+
3
+ import { Context } from "./Context.js";
4
+ import { OfflineContext } from "./OfflineContext.js";
5
+ import { onContextRunning } from "./OnRunning.js";
6
+
7
+ context("onContextRunning", () => {
8
+ it("callback is invoked immediately when offline context is used", () => {
9
+ const ctx = new OfflineContext(1, 0.1, 44100);
10
+ let wasInvoked = false;
11
+
12
+ onContextRunning(ctx, () => {
13
+ wasInvoked = true;
14
+ });
15
+
16
+ expect(wasInvoked).to.be.true;
17
+ ctx.dispose();
18
+ });
19
+
20
+ it("callback is invoked immediately when context is already running", async () => {
21
+ const ctx = new Context();
22
+ await ctx.resume();
23
+ let wasInvoked = false;
24
+
25
+ onContextRunning(ctx, () => {
26
+ wasInvoked = true;
27
+ });
28
+
29
+ expect(wasInvoked).to.be.true;
30
+ ctx.dispose();
31
+ });
32
+
33
+ it("callback is invoked when the context starts running", async () => {
34
+ const ctx = new Context();
35
+ let wasInvoked = false;
36
+
37
+ onContextRunning(ctx, () => {
38
+ wasInvoked = true;
39
+ });
40
+
41
+ expect(wasInvoked).to.be.false;
42
+
43
+ await ctx.resume();
44
+ expect(wasInvoked).to.be.true;
45
+
46
+ ctx.dispose();
47
+ });
48
+
49
+ it("can remove the callback with the returned function", async () => {
50
+ const ctx = new Context();
51
+ let wasInvoked = false;
52
+ const remove = onContextRunning(ctx, () => {
53
+ wasInvoked = true;
54
+ });
55
+ expect(wasInvoked).to.be.false;
56
+ remove();
57
+
58
+ await ctx.resume();
59
+
60
+ expect(wasInvoked).to.be.false;
61
+ ctx.dispose();
62
+ });
63
+ });
@@ -0,0 +1,26 @@
1
+ import { BaseContext } from "./BaseContext.js";
2
+
3
+ /**
4
+ * Invoked when the context is started. The callback is invoked immediately
5
+ * if the context is already started or when the context is an OfflineContext.
6
+ * Returns a function which can be called to remove the listener.
7
+ * @internal Used to trigger certain events when the context has been started.
8
+ */
9
+ export function onContextRunning(
10
+ context: BaseContext,
11
+ callback: () => void
12
+ ): () => void {
13
+ const listener = (state: AudioContextState) => {
14
+ if (state === "running") {
15
+ callback();
16
+ context.off("statechange", listener);
17
+ }
18
+ };
19
+ if (context.isOffline || context.state === "running") {
20
+ callback();
21
+ return () => {};
22
+ }
23
+
24
+ context.on("statechange", listener);
25
+ return () => context.off("statechange", listener);
26
+ }
@@ -0,0 +1,82 @@
1
+ import { expect } from "chai";
2
+
3
+ import { Offline } from "../../../test/helper/Offline.js";
4
+ import { getContext } from "../Global.js";
5
+ import { Gain } from "./Gain.js";
6
+ import { ToneWithContext } from "./ToneWithContext.js";
7
+
8
+ describe("ToneWithContext", () => {
9
+ context("get", () => {
10
+ it("returns an object with all the default properties", () => {
11
+ const gain = new Gain();
12
+ const values = gain.get();
13
+ expect(values).to.be.an("object");
14
+ expect(values).to.have.property("gain");
15
+ gain.dispose();
16
+ });
17
+
18
+ it("reflects the current value of properties", () => {
19
+ const gain = new Gain(0.3);
20
+ const values = gain.get();
21
+ expect(values.gain).to.be.closeTo(0.3, 0.001);
22
+ gain.dispose();
23
+ });
24
+ });
25
+
26
+ context("set", () => {
27
+ it("sets properties with an object", () => {
28
+ const gain = new Gain();
29
+ gain.set({ gain: 0.5 });
30
+ expect(gain.gain.value).to.be.closeTo(0.5, 0.001);
31
+ gain.dispose();
32
+ });
33
+
34
+ it("returns the instance for chaining", () => {
35
+ const gain = new Gain();
36
+ const returned = gain.set({ gain: 0.5 });
37
+ expect(returned).to.equal(gain);
38
+ gain.dispose();
39
+ });
40
+
41
+ it("does not set a value if it is the same", () => {
42
+ const gain = new Gain(0.7);
43
+ gain.set({ gain: 0.7 });
44
+ expect(gain.gain.value).to.be.closeTo(0.7, 0.001);
45
+ gain.dispose();
46
+ });
47
+ });
48
+
49
+ context("getDefaults", () => {
50
+ it("returns defaults with a context", () => {
51
+ const defaults = ToneWithContext.getDefaults();
52
+ expect(defaults).to.have.property("context");
53
+ expect(defaults.context).to.equal(getContext());
54
+ });
55
+ });
56
+
57
+ context("_onContextRunning", () => {
58
+ it("invokes the callback immediately for an offline context", () => {
59
+ return Offline(() => {
60
+ let callbackInvoked = false;
61
+ const gain = new Gain();
62
+ // Access protected method via subclass trick
63
+ (gain as any)._onContextRunning(() => {
64
+ callbackInvoked = true;
65
+ });
66
+ expect(callbackInvoked).to.equal(true);
67
+ gain.dispose();
68
+ });
69
+ });
70
+
71
+ it("cleans up the listener on dispose", () => {
72
+ return Offline(() => {
73
+ const gain = new Gain();
74
+ (gain as any)._onContextRunning(() => {
75
+ // no-op
76
+ });
77
+ // Should not throw when disposing
78
+ gain.dispose();
79
+ });
80
+ });
81
+ });
82
+ });
@@ -19,6 +19,7 @@ import {
19
19
  isUndef,
20
20
  } from "../util/TypeCheck.js";
21
21
  import { BaseContext } from "./BaseContext.js";
22
+ import { onContextRunning } from "./OnRunning.js";
22
23
 
23
24
  /**
24
25
  * A unit which process audio
@@ -232,4 +233,27 @@ export abstract class ToneWithContext<
232
233
  });
233
234
  return this;
234
235
  }
236
+
237
+ /**
238
+ * Internal method which removes the onContextRunning callback.
239
+ */
240
+ private _removeOnContextRunning?: () => void;
241
+
242
+ /**
243
+ * Internal method which is called the first time the context is resumed.
244
+ * Useful for setting up AudioNodes which should be running as soon
245
+ * as the context is running, but should not be started until then.
246
+ */
247
+ protected _onContextRunning(callback: () => void): void {
248
+ this._removeOnContextRunning = onContextRunning(this.context, callback);
249
+ }
250
+
251
+ /**
252
+ * Dispose and disconnect
253
+ */
254
+ dispose(): this {
255
+ super.dispose();
256
+ this._removeOnContextRunning?.();
257
+ return this;
258
+ }
235
259
  }
@@ -6,7 +6,10 @@ export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
6
6
  /**
7
7
  * Make the property not writable using `defineProperty`. Internal use only.
8
8
  */
9
- export function readOnly(target: object, property: string | string[]): void {
9
+ export function readOnly<T extends object>(
10
+ target: T,
11
+ property: keyof T | (keyof T)[]
12
+ ): void {
10
13
  if (isArray(property)) {
11
14
  property.forEach((str) => readOnly(target, str));
12
15
  } else {
@@ -20,7 +23,10 @@ export function readOnly(target: object, property: string | string[]): void {
20
23
  /**
21
24
  * Make an attribute writeable. Internal use only.
22
25
  */
23
- export function writable(target: object, property: string | string[]): void {
26
+ export function writable<T extends object>(
27
+ target: T,
28
+ property: keyof T | (keyof T)[]
29
+ ): void {
24
30
  if (isArray(property)) {
25
31
  property.forEach((str) => writable(target, str));
26
32
  } else {
@@ -3,7 +3,7 @@ import { Note } from "../type/Units.js";
3
3
  /**
4
4
  * Test if the arg is undefined
5
5
  */
6
- export function isUndef(arg: any): arg is undefined {
6
+ export function isUndef(arg: unknown): arg is undefined {
7
7
  return arg === undefined;
8
8
  }
9
9
 
@@ -17,45 +17,45 @@ export function isDefined<T>(arg: T | undefined): arg is T {
17
17
  /**
18
18
  * Test if the arg is a function
19
19
  */
20
- export function isFunction(arg: any): arg is (a: any) => any {
20
+ export function isFunction(arg: unknown): arg is (a: any) => any {
21
21
  return typeof arg === "function";
22
22
  }
23
23
 
24
24
  /**
25
25
  * Test if the argument is a number.
26
26
  */
27
- export function isNumber(arg: any): arg is number {
27
+ export function isNumber(arg: unknown): arg is number {
28
28
  return typeof arg === "number";
29
29
  }
30
30
 
31
31
  /**
32
32
  * Test if the given argument is an object literal (i.e. `{}`);
33
33
  */
34
- export function isObject(arg: any): arg is object {
34
+ export function isObject(arg: unknown): arg is object {
35
35
  return (
36
36
  Object.prototype.toString.call(arg) === "[object Object]" &&
37
- arg.constructor === Object
37
+ (arg as object).constructor === Object
38
38
  );
39
39
  }
40
40
 
41
41
  /**
42
42
  * Test if the argument is a boolean.
43
43
  */
44
- export function isBoolean(arg: any): arg is boolean {
44
+ export function isBoolean(arg: unknown): arg is boolean {
45
45
  return typeof arg === "boolean";
46
46
  }
47
47
 
48
48
  /**
49
49
  * Test if the argument is an Array
50
50
  */
51
- export function isArray(arg: any): arg is any[] {
51
+ export function isArray(arg: unknown): arg is any[] {
52
52
  return Array.isArray(arg);
53
53
  }
54
54
 
55
55
  /**
56
56
  * Test if the argument is a string.
57
57
  */
58
- export function isString(arg: any): arg is string {
58
+ export function isString(arg: unknown): arg is string {
59
59
  return typeof arg === "string";
60
60
  }
61
61
 
@@ -63,6 +63,6 @@ export function isString(arg: any): arg is string {
63
63
  * Test if the argument is in the form of a note in scientific pitch notation.
64
64
  * e.g. "C4"
65
65
  */
66
- export function isNote(arg: any): arg is Note {
66
+ export function isNote(arg: unknown): arg is Note {
67
67
  return isString(arg) && /^([a-g]{1}(?:b|#|x|bb)?)(-?[0-9]+)/i.test(arg);
68
68
  }
@@ -131,9 +131,11 @@ export class FrequencyShifter extends Effect<FrequencyShifterOptions> {
131
131
  this._add.connect(this.effectReturn);
132
132
 
133
133
  // start the oscillators at the same time
134
- const now = this.immediate();
135
- this._sine.start(now);
136
- this._cosine.start(now);
134
+ this._onContextRunning(() => {
135
+ const now = this.immediate();
136
+ this._sine.start(now);
137
+ this._cosine.start(now);
138
+ });
137
139
  }
138
140
 
139
141
  static getDefaults(): FrequencyShifterOptions {
@@ -0,0 +1,85 @@
1
+ import { expect } from "chai";
2
+
3
+ import { BasicTests } from "../../test/helper/Basic.js";
4
+ import { Offline } from "../../test/helper/Offline.js";
5
+ import { LFOStereoEffect, LFOStereoEffectOptions } from "./LFOStereoEffect.js";
6
+
7
+ describe("LFOStereoEffect", () => {
8
+ class LFOStereoEffectTest extends LFOStereoEffect<LFOStereoEffectOptions> {
9
+ getLfoState() {
10
+ return this._lfoL.state;
11
+ }
12
+ }
13
+
14
+ BasicTests(LFOStereoEffectTest);
15
+
16
+ context("API", () => {
17
+ it("can pass in options in the constructor", () => {
18
+ const stereoEffect = new LFOStereoEffectTest({
19
+ frequency: 0.2,
20
+ });
21
+ expect(stereoEffect.frequency.value).to.be.closeTo(0.2, 0.01);
22
+ stereoEffect.dispose();
23
+ });
24
+
25
+ it("can be started and stopped", () => {
26
+ const stereoEffect = new LFOStereoEffectTest();
27
+ stereoEffect.start().stop("+0.2");
28
+ stereoEffect.dispose();
29
+ });
30
+
31
+ it("can get/set the options", () => {
32
+ const stereoEffect = new LFOStereoEffectTest();
33
+ stereoEffect.set({
34
+ frequency: 2.4,
35
+ });
36
+ expect(stereoEffect.get().frequency).to.be.closeTo(2.4, 0.01);
37
+ stereoEffect.dispose();
38
+ });
39
+
40
+ it("can set the frequency and depth", () => {
41
+ const stereoEffect = new LFOStereoEffectTest();
42
+ stereoEffect.frequency.value = 0.4;
43
+ expect(stereoEffect.frequency.value).to.be.closeTo(0.4, 0.01);
44
+ stereoEffect.dispose();
45
+ });
46
+
47
+ it("can sync the frequency to the transport", async () => {
48
+ const buffer = await Offline(({ transport }) => {
49
+ const stereoEffect = new LFOStereoEffectTest({
50
+ frequency: 2,
51
+ });
52
+ stereoEffect.sync();
53
+ stereoEffect.frequency.toDestination();
54
+ transport.bpm.setValueAtTime(transport.bpm.value * 2, 0.05);
55
+ }, 0.1);
56
+ expect(buffer.getValueAtTime(0)).to.be.closeTo(2, 0.1);
57
+ expect(buffer.getValueAtTime(0.05)).to.be.closeTo(4, 0.1);
58
+ });
59
+
60
+ it("can unsync the frequency to the transport", async () => {
61
+ const buffer = await Offline(({ transport }) => {
62
+ const stereoEffect = new LFOStereoEffectTest({
63
+ frequency: 2,
64
+ });
65
+ stereoEffect.sync();
66
+ stereoEffect.frequency.toDestination();
67
+ transport.bpm.setValueAtTime(transport.bpm.value * 2, 0.05);
68
+ stereoEffect.unsync();
69
+ }, 0.1);
70
+ expect(buffer.getValueAtTime(0)).to.be.closeTo(2, 0.1);
71
+ expect(buffer.getValueAtTime(0.05)).to.be.closeTo(2, 0.1);
72
+ });
73
+
74
+ it("autostart the LFO", () => {
75
+ const stereoEffect = new LFOStereoEffectTest({
76
+ frequency: 0.2,
77
+ autostart: true,
78
+ });
79
+ expect(stereoEffect.frequency.value).to.be.closeTo(0.2, 0.01);
80
+
81
+ expect(stereoEffect.getLfoState()).to.equal("started");
82
+ stereoEffect.dispose();
83
+ });
84
+ });
85
+ });
@@ -0,0 +1,136 @@
1
+ import { Frequency, Time } from "../core/type/Units.js";
2
+ import { optionsFromArguments } from "../core/util/Defaults.js";
3
+ import { readOnly } from "../core/util/Interface.js";
4
+ import { Signal } from "../signal/Signal.js";
5
+ import { LFO } from "../source/oscillator/LFO.js";
6
+ import { ToneOscillatorType } from "../source/oscillator/OscillatorInterface.js";
7
+ import { StereoEffect, StereoEffectOptions } from "./StereoEffect.js";
8
+
9
+ export interface LFOStereoEffectOptions extends StereoEffectOptions {
10
+ frequency: Frequency;
11
+ autostart: boolean;
12
+ }
13
+
14
+ /**
15
+ * Base class for stereo effects which modulate the incoming signal with an LFO.
16
+ *
17
+ * @category Effect
18
+ */
19
+ export abstract class LFOStereoEffect<
20
+ Options extends LFOStereoEffectOptions,
21
+ > extends StereoEffect<Options> {
22
+ readonly name: string = "LFOStereoEffect";
23
+
24
+ /**
25
+ * The LFO in the left channel
26
+ */
27
+ protected _lfoL: LFO;
28
+
29
+ /**
30
+ * The LFO in the right channel
31
+ */
32
+ protected _lfoR: LFO;
33
+
34
+ /**
35
+ * The frequency of the tremolo.
36
+ */
37
+ readonly frequency: Signal<"frequency">;
38
+
39
+ constructor(options?: Partial<LFOStereoEffectOptions>);
40
+ constructor() {
41
+ const options = optionsFromArguments(
42
+ LFOStereoEffect.getDefaults(),
43
+ arguments
44
+ );
45
+ super(options);
46
+
47
+ this._lfoL = new LFO({
48
+ context: this.context,
49
+ min: 0,
50
+ max: 1,
51
+ });
52
+ this._lfoR = new LFO({
53
+ context: this.context,
54
+ min: 0,
55
+ max: 1,
56
+ });
57
+
58
+ // control the frequency with a single signal
59
+ this.frequency = new Signal({
60
+ context: this.context,
61
+ value: options.frequency,
62
+ units: "frequency",
63
+ });
64
+ this.frequency.fan(this._lfoL.frequency, this._lfoR.frequency);
65
+
66
+ readOnly(this, ["frequency"]);
67
+
68
+ if (options.autostart) {
69
+ this._onContextRunning(() => this.start(this.immediate()));
70
+ }
71
+ }
72
+
73
+ static getDefaults(): LFOStereoEffectOptions {
74
+ return Object.assign(StereoEffect.getDefaults(), {
75
+ frequency: 10,
76
+ autostart: false,
77
+ });
78
+ }
79
+
80
+ /**
81
+ * Start the tremolo.
82
+ */
83
+ start(time?: Time): this {
84
+ this._lfoL.start(time);
85
+ this._lfoR.start(time);
86
+ return this;
87
+ }
88
+
89
+ /**
90
+ * Stop the tremolo.
91
+ */
92
+ stop(time?: Time): this {
93
+ this._lfoL.stop(time);
94
+ this._lfoR.stop(time);
95
+ return this;
96
+ }
97
+
98
+ /**
99
+ * Sync the effect to the transport.
100
+ */
101
+ sync(): this {
102
+ this._lfoL.sync();
103
+ this._lfoR.sync();
104
+ this.context.transport.syncSignal(this.frequency);
105
+ return this;
106
+ }
107
+
108
+ /**
109
+ * Unsync the filter from the transport
110
+ */
111
+ unsync(): this {
112
+ this._lfoL.unsync();
113
+ this._lfoR.unsync();
114
+ this.context.transport.unsyncSignal(this.frequency);
115
+ return this;
116
+ }
117
+
118
+ /**
119
+ * The oscillator type.
120
+ */
121
+ get type(): ToneOscillatorType {
122
+ return this._lfoL.type;
123
+ }
124
+ set type(type) {
125
+ this._lfoL.type = type;
126
+ this._lfoR.type = type;
127
+ }
128
+
129
+ dispose(): this {
130
+ super.dispose();
131
+ this._lfoL.dispose();
132
+ this._lfoR.dispose();
133
+ this.frequency.dispose();
134
+ return this;
135
+ }
136
+ }
@@ -3,10 +3,9 @@ import { optionsFromArguments } from "../core/util/Defaults.js";
3
3
  import { readOnly } from "../core/util/Interface.js";
4
4
  import { Signal } from "../signal/Signal.js";
5
5
  import { LFO } from "../source/oscillator/LFO.js";
6
- import { StereoEffect, StereoEffectOptions } from "./StereoEffect.js";
6
+ import { LFOStereoEffect, LFOStereoEffectOptions } from "./LFOStereoEffect.js";
7
7
 
8
- export interface PhaserOptions extends StereoEffectOptions {
9
- frequency: Frequency;
8
+ export interface PhaserOptions extends LFOStereoEffectOptions {
10
9
  octaves: Positive;
11
10
  stages: Positive;
12
11
  Q: Positive;
@@ -28,19 +27,9 @@ export interface PhaserOptions extends StereoEffectOptions {
28
27
  * synth.triggerAttackRelease("E3", "2n");
29
28
  * @category Effect
30
29
  */
31
- export class Phaser extends StereoEffect<PhaserOptions> {
30
+ export class Phaser extends LFOStereoEffect<PhaserOptions> {
32
31
  readonly name: string = "Phaser";
33
32
 
34
- /**
35
- * the lfo which controls the frequency on the left side
36
- */
37
- private _lfoL: LFO;
38
-
39
- /**
40
- * the lfo which controls the frequency on the right side
41
- */
42
- private _lfoR: LFO;
43
-
44
33
  /**
45
34
  * the base modulation frequency
46
35
  */
@@ -66,11 +55,6 @@ export class Phaser extends StereoEffect<PhaserOptions> {
66
55
  */
67
56
  private _filtersR: BiquadFilterNode[];
68
57
 
69
- /**
70
- * the frequency of the effect
71
- */
72
- readonly frequency: Signal<"frequency">;
73
-
74
58
  /**
75
59
  * @param frequency The speed of the phasing.
76
60
  * @param octaves The octaves of the effect.
@@ -90,19 +74,8 @@ export class Phaser extends StereoEffect<PhaserOptions> {
90
74
  ]);
91
75
  super(options);
92
76
 
93
- this._lfoL = new LFO({
94
- context: this.context,
95
- frequency: options.frequency,
96
- min: 0,
97
- max: 1,
98
- });
99
- this._lfoR = new LFO({
100
- context: this.context,
101
- frequency: options.frequency,
102
- min: 0,
103
- max: 1,
104
- phase: 180,
105
- });
77
+ this._lfoR.phase = 180;
78
+
106
79
  this._baseFrequency = this.toFrequency(options.baseFrequency);
107
80
  this._octaves = options.octaves;
108
81
  this.Q = new Signal({
@@ -113,30 +86,24 @@ export class Phaser extends StereoEffect<PhaserOptions> {
113
86
  this._filtersL = this._makeFilters(options.stages, this._lfoL);
114
87
  this._filtersR = this._makeFilters(options.stages, this._lfoR);
115
88
 
116
- this.frequency = this._lfoL.frequency;
117
- this.frequency.value = options.frequency;
118
-
119
89
  // connect them up
120
90
  this.connectEffectLeft(...this._filtersL);
121
91
  this.connectEffectRight(...this._filtersR);
122
- // control the frequency with one LFO
123
- this._lfoL.frequency.connect(this._lfoR.frequency);
92
+
124
93
  // set the options
125
94
  this.baseFrequency = options.baseFrequency;
126
95
  this.octaves = options.octaves;
127
- // start the lfo
128
- this._lfoL.start();
129
- this._lfoR.start();
130
- readOnly(this, ["frequency", "Q"]);
96
+ readOnly(this, ["Q"]);
131
97
  }
132
98
 
133
99
  static getDefaults(): PhaserOptions {
134
- return Object.assign(StereoEffect.getDefaults(), {
100
+ return Object.assign(LFOStereoEffect.getDefaults(), {
135
101
  frequency: 0.5,
136
102
  octaves: 3,
137
103
  stages: 10,
138
104
  Q: 10,
139
105
  baseFrequency: 350,
106
+ autostart: true,
140
107
  });
141
108
  }
142
109
 
@@ -185,11 +152,8 @@ export class Phaser extends StereoEffect<PhaserOptions> {
185
152
  dispose(): this {
186
153
  super.dispose();
187
154
  this.Q.dispose();
188
- this._lfoL.dispose();
189
- this._lfoR.dispose();
190
155
  this._filtersL.forEach((f) => f.disconnect());
191
156
  this._filtersR.forEach((f) => f.disconnect());
192
- this.frequency.dispose();
193
157
  return this;
194
158
  }
195
159
  }
@@ -147,13 +147,16 @@ export class PitchShift extends FeedbackEffect<PitchShiftOptions> {
147
147
  // route the input
148
148
  this.effectSend.fan(this._delayA, this._delayB);
149
149
  this._crossFade.chain(this._feedbackDelay, this.effectReturn);
150
- // start the LFOs at the same time
151
- const now = this.now();
152
- this._lfoA.start(now);
153
- this._lfoB.start(now);
154
- this._crossFadeLFO.start(now);
155
150
  // set the initial value
156
151
  this.windowSize = this._windowSize;
152
+
153
+ // start the LFOs at the same time
154
+ this._onContextRunning(() => {
155
+ const now = this.immediate();
156
+ this._lfoA.start(now);
157
+ this._lfoB.start(now);
158
+ this._crossFadeLFO.start(now);
159
+ });
157
160
  }
158
161
 
159
162
  static getDefaults(): PitchShiftOptions {