UNPKG

tone

Version:

A Web Audio framework for making interactive music in the browser.

88 lines 4.09 kB
import { PhaseShiftAllpass } from "../component/filter/PhaseShiftAllpass.js"; import { optionsFromArguments } from "../core/util/Defaults.js"; import { Effect } from "../effect/Effect.js"; import { Add } from "../signal/Add.js"; import { Multiply } from "../signal/Multiply.js"; import { Negate } from "../signal/Negate.js"; import { Signal } from "../signal/Signal.js"; import { Oscillator } from "../source/oscillator/Oscillator.js"; import { ToneOscillatorNode } from "../source/oscillator/ToneOscillatorNode.js"; /** * FrequencyShifter can be used to shift all frequencies of a signal by a fixed amount. * The amount can be changed at audio rate and the effect is applied in real time. * The frequency shifting is implemented with a technique called single side band modulation using a ring modulator. * Note: Contrary to pitch shifting, all frequencies are shifted by the same amount, * destroying the harmonic relationship between them. This leads to the classic ring modulator timbre distortion. * The algorithm will produces some aliasing towards the high end, especially if your source material * contains a lot of high frequencies. Unfortunatelly the webaudio API does not support resampling * buffers in real time, so it is not possible to fix it properly. Depending on the use case it might * be an option to low pass filter your input before frequency shifting it to get ride of the aliasing. * You can find a very detailed description of the algorithm here: https://larzeitlin.github.io/RMFS/ * * @example * const input = new Tone.Oscillator(230, "sawtooth").start(); * const shift = new Tone.FrequencyShifter(42).toDestination(); * input.connect(shift); * @category Effect */ export class FrequencyShifter extends Effect { constructor() { const options = optionsFromArguments(FrequencyShifter.getDefaults(), arguments, ["frequency"]); super(options); this.name = "FrequencyShifter"; this.frequency = new Signal({ context: this.context, units: "frequency", value: options.frequency, minValue: -this.context.sampleRate / 2, maxValue: this.context.sampleRate / 2, }); this._sine = new ToneOscillatorNode({ context: this.context, type: "sine", }); this._cosine = new Oscillator({ context: this.context, phase: -90, type: "sine", }); this._sineMultiply = new Multiply({ context: this.context }); this._cosineMultiply = new Multiply({ context: this.context }); this._negate = new Negate({ context: this.context }); this._add = new Add({ context: this.context }); this._phaseShifter = new PhaseShiftAllpass({ context: this.context }); this.effectSend.connect(this._phaseShifter); // connect the carrier frequency signal to the two oscillators this.frequency.fan(this._sine.frequency, this._cosine.frequency); this._phaseShifter.offset90.connect(this._cosineMultiply); this._cosine.connect(this._cosineMultiply.factor); this._phaseShifter.connect(this._sineMultiply); this._sine.connect(this._sineMultiply.factor); this._sineMultiply.connect(this._negate); this._cosineMultiply.connect(this._add); this._negate.connect(this._add.addend); this._add.connect(this.effectReturn); // start the oscillators at the same time const now = this.immediate(); this._sine.start(now); this._cosine.start(now); } static getDefaults() { return Object.assign(Effect.getDefaults(), { frequency: 0, }); } dispose() { super.dispose(); this.frequency.dispose(); this._add.dispose(); this._cosine.dispose(); this._cosineMultiply.dispose(); this._negate.dispose(); this._phaseShifter.dispose(); this._sine.dispose(); this._sineMultiply.dispose(); return this; } } //# sourceMappingURL=FrequencyShifter.js.map