UNPKG

3.35 kBPlain TextView Raw
1import { Effect, EffectOptions } from "./Effect.js";
2import { Positive } from "../core/type/Units.js";
3import { optionsFromArguments } from "../core/util/Defaults.js";
4import { WaveShaper } from "../signal/WaveShaper.js";
5import { assert } from "../core/util/Debug.js";
6
7export interface ChebyshevOptions extends EffectOptions {
8 order: Positive;
9 oversample: OverSampleType;
10}
11
12/**
13 * Chebyshev is a waveshaper which is good
14 * for making different types of distortion sounds.
15 * Note that odd orders sound very different from even ones,
16 * and order = 1 is no change.
17 * Read more at [music.columbia.edu](http://music.columbia.edu/cmc/musicandcomputers/chapter4/04_06.php).
18 * @example
19 * // create a new cheby
20 * const cheby = new Tone.Chebyshev(50).toDestination();
21 * // create a monosynth connected to our cheby
22 * const synth = new Tone.MonoSynth().connect(cheby);
23 * synth.triggerAttackRelease("C2", 0.4);
24 * @category Effect
25 */
26export class Chebyshev extends Effect<ChebyshevOptions> {
27 readonly name: string = "Chebyshev";
28
29 /**
30 * The private waveshaper node
31 */
32 private _shaper: WaveShaper;
33
34 /**
35 * holds onto the order of the filter
36 */
37 private _order: number;
38
39 /**
40 * @param order The order of the chebyshev polynomial. Normal range between 1-100.
41 */
42 constructor(order?: Positive);
43 constructor(options?: Partial<ChebyshevOptions>);
44 constructor() {
45 const options = optionsFromArguments(
46 Chebyshev.getDefaults(),
47 arguments,
48 ["order"]
49 );
50 super(options);
51
52 this._shaper = new WaveShaper({
53 context: this.context,
54 length: 4096,
55 });
56 this._order = options.order;
57
58 this.connectEffect(this._shaper);
59 this.order = options.order;
60 this.oversample = options.oversample;
61 }
62
63 static getDefaults(): ChebyshevOptions {
64 return Object.assign(Effect.getDefaults(), {
65 order: 1,
66 oversample: "none" as const,
67 });
68 }
69
70 /**
71 * get the coefficient for that degree
72 * @param x the x value
73 * @param degree
74 * @param memo memoize the computed value. this speeds up computation greatly.
75 */
76 private _getCoefficient(
77 x: number,
78 degree: number,
79 memo: Map<number, number>
80 ): number {
81 if (memo.has(degree)) {
82 return memo.get(degree) as number;
83 } else if (degree === 0) {
84 memo.set(degree, 0);
85 } else if (degree === 1) {
86 memo.set(degree, x);
87 } else {
88 memo.set(
89 degree,
90 2 * x * this._getCoefficient(x, degree - 1, memo) -
91 this._getCoefficient(x, degree - 2, memo)
92 );
93 }
94 return memo.get(degree) as number;
95 }
96
97 /**
98 * The order of the Chebyshev polynomial which creates the equation which is applied to the incoming
99 * signal through a Tone.WaveShaper. Must be an integer. The equations are in the form:
100 * ```
101 * order 2: 2x^2 + 1
102 * order 3: 4x^3 + 3x
103 * ```
104 * @min 1
105 * @max 100
106 */
107 get order(): Positive {
108 return this._order;
109 }
110 set order(order) {
111 assert(Number.isInteger(order), "'order' must be an integer");
112 this._order = order;
113 this._shaper.setMap((x) => {
114 return this._getCoefficient(x, order, new Map());
115 });
116 }
117
118 /**
119 * The oversampling of the effect. Can either be "none", "2x" or "4x".
120 */
121 get oversample(): OverSampleType {
122 return this._shaper.oversample;
123 }
124 set oversample(oversampling) {
125 this._shaper.oversample = oversampling;
126 }
127
128 dispose(): this {
129 super.dispose();
130 this._shaper.dispose();
131 return this;
132 }
133}