Tone.js/Tone/source/oscillator/FMOscillator.ts

237 lines
6.3 KiB
TypeScript
Raw Normal View History

2019-07-16 20:05:48 +00:00
import { Gain } from "../../core/context/Gain";
import { Degrees, Frequency, Seconds, Time } from "../../core/type/Units";
2019-07-16 20:05:48 +00:00
import { optionsFromArguments } from "../../core/util/Defaults";
import { readOnly } from "../../core/util/Interface";
import { Multiply } from "../../signal/Multiply";
import { Signal } from "../../signal/Signal";
import { Source } from "../Source";
import { Oscillator } from "./Oscillator";
import {
FMConstructorOptions, FMOscillatorOptions,
generateWaveform, NonCustomOscillatorType, ToneOscillatorInterface, ToneOscillatorType
} from "./OscillatorInterface";
2019-07-16 20:05:48 +00:00
2019-09-04 22:34:42 +00:00
export { FMOscillatorOptions } from "./OscillatorInterface";
2019-07-16 20:05:48 +00:00
/**
2019-07-16 20:47:07 +00:00
* FMOscillator implements a frequency modulation synthesis
2019-07-17 21:40:47 +00:00
* ```
2019-07-16 20:47:07 +00:00
* +-------------+
* +---------------+ +-------------+ | Carrier Osc |
* | Modulator Osc +>-------> GainNode | | +--->Output
* +---------------+ | +>----> frequency |
* +--> gain | +-------------+
* | +-------------+
* +-----------------+ |
* | modulationIndex +>--+
* +-----------------+
2019-07-17 21:40:47 +00:00
* ```
*
2019-07-16 20:47:07 +00:00
* @example
2020-07-26 20:55:06 +00:00
* return Tone.Offline(() => {
* const fmOsc = new Tone.FMOscillator({
* frequency: 200,
* type: "square",
* modulationType: "triangle",
* harmonicity: 0.2,
* modulationIndex: 3
* }).toDestination().start();
* }, 0.1, 1);
2019-09-16 14:15:23 +00:00
* @category Source
2019-07-16 20:05:48 +00:00
*/
export class FMOscillator extends Source<FMOscillatorOptions> implements ToneOscillatorInterface {
2019-07-16 20:05:48 +00:00
2019-09-04 23:18:44 +00:00
readonly name: string = "FMOscillator";
2019-07-16 20:05:48 +00:00
/**
2019-09-14 20:39:18 +00:00
* The carrier oscillator
2019-07-16 20:05:48 +00:00
*/
private _carrier: Oscillator;
2019-07-16 20:05:48 +00:00
readonly frequency: Signal<"frequency">;
readonly detune: Signal<"cents">;
2019-07-16 20:05:48 +00:00
/**
2019-09-14 20:39:18 +00:00
* The modulating oscillator
2019-07-16 20:05:48 +00:00
*/
private _modulator: Oscillator;
2019-07-16 20:05:48 +00:00
/**
2019-09-14 20:39:18 +00:00
* Harmonicity is the frequency ratio between the carrier and the modulator oscillators.
* A harmonicity of 1 gives both oscillators the same frequency.
* Harmonicity = 2 means a change of an octave.
* @example
* const fmOsc = new Tone.FMOscillator("D2").toDestination().start();
2019-10-25 20:54:33 +00:00
* // pitch the modulator an octave below carrier
* fmOsc.harmonicity.value = 0.5;
2019-07-16 20:05:48 +00:00
*/
readonly harmonicity: Signal<"positive">;
2019-07-16 20:05:48 +00:00
/**
2019-09-14 20:39:18 +00:00
* The modulation index which is in essence the depth or amount of the modulation. In other terms it is the
* ratio of the frequency of the modulating signal (mf) to the amplitude of the
* modulating signal (ma) -- as in ma/mf.
2019-07-16 20:05:48 +00:00
*/
readonly modulationIndex: Signal<"positive">;
2019-07-16 20:05:48 +00:00
/**
2019-09-14 20:39:18 +00:00
* the node where the modulation happens
2019-07-16 20:05:48 +00:00
*/
private _modulationNode: Gain = new Gain({
2019-07-16 20:05:48 +00:00
context: this.context,
gain: 0,
});
2019-08-27 15:47:52 +00:00
/**
* @param frequency The starting frequency of the oscillator.
* @param type The type of the carrier oscillator.
* @param modulationType The type of the modulator oscillator.
*/
2019-07-16 20:05:48 +00:00
constructor(frequency?: Frequency, type?: ToneOscillatorType, modulationType?: ToneOscillatorType);
2019-08-27 15:47:52 +00:00
constructor(options?: Partial<FMConstructorOptions>);
2019-07-16 20:05:48 +00:00
constructor() {
super(optionsFromArguments(FMOscillator.getDefaults(), arguments, ["frequency", "type", "modulationType"]));
const options = optionsFromArguments(FMOscillator.getDefaults(), arguments, ["frequency", "type", "modulationType"]);
this._carrier = new Oscillator({
2019-09-16 03:32:40 +00:00
context: this.context,
detune: options.detune,
frequency: 0,
2019-08-10 15:51:35 +00:00
onstop: () => this.onstop(this),
phase: options.phase,
type: options.type,
} as OscillatorOptions);
this.detune = this._carrier.detune;
this.frequency = new Signal({
context: this.context,
units: "frequency",
value: options.frequency,
});
this._modulator = new Oscillator({
2019-09-16 03:32:40 +00:00
context: this.context,
phase: options.phase,
type: options.modulationType,
} as OscillatorOptions);
this.harmonicity = new Multiply({
context: this.context,
units: "positive",
value: options.harmonicity,
});
this.modulationIndex = new Multiply({
context: this.context,
units: "positive",
value: options.modulationIndex,
});
2019-07-16 20:05:48 +00:00
// connections
this.frequency.connect(this._carrier.frequency);
this.frequency.chain(this.harmonicity, this._modulator.frequency);
this.frequency.chain(this.modulationIndex, this._modulationNode);
this._modulator.connect(this._modulationNode.gain);
this._modulationNode.connect(this._carrier.frequency);
this._carrier.connect(this.output);
this.detune.connect(this._modulator.detune);
readOnly(this, ["modulationIndex", "frequency", "detune", "harmonicity"]);
}
static getDefaults(): FMOscillatorOptions {
return Object.assign(Oscillator.getDefaults(), {
harmonicity: 1,
modulationIndex: 2,
modulationType: "square" as NonCustomOscillatorType,
2019-07-16 20:05:48 +00:00
});
}
/**
2019-09-14 20:39:18 +00:00
* start the oscillator
2019-07-16 20:05:48 +00:00
*/
protected _start(time: Time): void {
this._modulator.start(time);
this._carrier.start(time);
}
/**
2019-09-14 20:39:18 +00:00
* stop the oscillator
2019-07-16 20:05:48 +00:00
*/
protected _stop(time: Time): void {
this._modulator.stop(time);
this._carrier.stop(time);
}
protected _restart(time: Seconds): this {
2019-07-16 20:05:48 +00:00
this._modulator.restart(time);
this._carrier.restart(time);
return this;
}
get type(): ToneOscillatorType {
return this._carrier.type;
}
set type(type: ToneOscillatorType) {
this._carrier.type = type;
}
get baseType(): OscillatorType {
return this._carrier.baseType;
}
set baseType(baseType: OscillatorType) {
this._carrier.baseType = baseType;
}
get partialCount(): number {
return this._carrier.partialCount;
}
set partialCount(partialCount: number) {
this._carrier.partialCount = partialCount;
}
/**
* The type of the modulator oscillator
*/
get modulationType(): ToneOscillatorType {
return this._modulator.type;
}
set modulationType(type: ToneOscillatorType) {
this._modulator.type = type;
}
get phase(): Degrees {
return this._carrier.phase;
}
set phase(phase: Degrees) {
this._carrier.phase = phase;
this._modulator.phase = phase;
}
get partials(): number[] {
return this._carrier.partials;
}
set partials(partials: number[]) {
this._carrier.partials = partials;
}
2019-11-17 18:09:19 +00:00
async asArray(length = 1024): Promise<Float32Array> {
return generateWaveform(this, length);
}
2019-07-16 20:05:48 +00:00
/**
2019-09-14 20:39:18 +00:00
* Clean up.
2019-07-16 20:05:48 +00:00
*/
dispose(): this {
super.dispose();
this.frequency.dispose();
this.harmonicity.dispose();
this._carrier.dispose();
this._modulator.dispose();
this._modulationNode.dispose();
this.modulationIndex.dispose();
return this;
}
}