UNPKG

4.09 kBJavaScriptView Raw
1import { PhaseShiftAllpass } from "../component/filter/PhaseShiftAllpass.js";
2import { optionsFromArguments } from "../core/util/Defaults.js";
3import { Effect } from "../effect/Effect.js";
4import { Add } from "../signal/Add.js";
5import { Multiply } from "../signal/Multiply.js";
6import { Negate } from "../signal/Negate.js";
7import { Signal } from "../signal/Signal.js";
8import { Oscillator } from "../source/oscillator/Oscillator.js";
9import { ToneOscillatorNode } from "../source/oscillator/ToneOscillatorNode.js";
10/**
11 * FrequencyShifter can be used to shift all frequencies of a signal by a fixed amount.
12 * The amount can be changed at audio rate and the effect is applied in real time.
13 * The frequency shifting is implemented with a technique called single side band modulation using a ring modulator.
14 * Note: Contrary to pitch shifting, all frequencies are shifted by the same amount,
15 * destroying the harmonic relationship between them. This leads to the classic ring modulator timbre distortion.
16 * The algorithm will produces some aliasing towards the high end, especially if your source material
17 * contains a lot of high frequencies. Unfortunatelly the webaudio API does not support resampling
18 * buffers in real time, so it is not possible to fix it properly. Depending on the use case it might
19 * be an option to low pass filter your input before frequency shifting it to get ride of the aliasing.
20 * You can find a very detailed description of the algorithm here: https://larzeitlin.github.io/RMFS/
21 *
22 * @example
23 * const input = new Tone.Oscillator(230, "sawtooth").start();
24 * const shift = new Tone.FrequencyShifter(42).toDestination();
25 * input.connect(shift);
26 * @category Effect
27 */
28export class FrequencyShifter extends Effect {
29 constructor() {
30 const options = optionsFromArguments(FrequencyShifter.getDefaults(), arguments, ["frequency"]);
31 super(options);
32 this.name = "FrequencyShifter";
33 this.frequency = new Signal({
34 context: this.context,
35 units: "frequency",
36 value: options.frequency,
37 minValue: -this.context.sampleRate / 2,
38 maxValue: this.context.sampleRate / 2,
39 });
40 this._sine = new ToneOscillatorNode({
41 context: this.context,
42 type: "sine",
43 });
44 this._cosine = new Oscillator({
45 context: this.context,
46 phase: -90,
47 type: "sine",
48 });
49 this._sineMultiply = new Multiply({ context: this.context });
50 this._cosineMultiply = new Multiply({ context: this.context });
51 this._negate = new Negate({ context: this.context });
52 this._add = new Add({ context: this.context });
53 this._phaseShifter = new PhaseShiftAllpass({ context: this.context });
54 this.effectSend.connect(this._phaseShifter);
55 // connect the carrier frequency signal to the two oscillators
56 this.frequency.fan(this._sine.frequency, this._cosine.frequency);
57 this._phaseShifter.offset90.connect(this._cosineMultiply);
58 this._cosine.connect(this._cosineMultiply.factor);
59 this._phaseShifter.connect(this._sineMultiply);
60 this._sine.connect(this._sineMultiply.factor);
61 this._sineMultiply.connect(this._negate);
62 this._cosineMultiply.connect(this._add);
63 this._negate.connect(this._add.addend);
64 this._add.connect(this.effectReturn);
65 // start the oscillators at the same time
66 const now = this.immediate();
67 this._sine.start(now);
68 this._cosine.start(now);
69 }
70 static getDefaults() {
71 return Object.assign(Effect.getDefaults(), {
72 frequency: 0,
73 });
74 }
75 dispose() {
76 super.dispose();
77 this.frequency.dispose();
78 this._add.dispose();
79 this._cosine.dispose();
80 this._cosineMultiply.dispose();
81 this._negate.dispose();
82 this._phaseShifter.dispose();
83 this._sine.dispose();
84 this._sineMultiply.dispose();
85 return this;
86 }
87}
88//# sourceMappingURL=FrequencyShifter.js.map
\No newline at end of file