mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-16 08:38:00 +00:00
converting AMOscillator to ts
This commit is contained in:
parent
b8f0d1d576
commit
ab9b363965
2 changed files with 300 additions and 0 deletions
70
Tone/source/oscillator/AMOscillator.test.ts
Normal file
70
Tone/source/oscillator/AMOscillator.test.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { expect } from "chai";
|
||||||
|
import { BasicTests } from "test/helper/Basic";
|
||||||
|
import { CompareToFile } from "test/helper/CompareToFile";
|
||||||
|
import { Offline } from "test/helper/Offline";
|
||||||
|
import { OscillatorTests } from "test/helper/OscillatorTests";
|
||||||
|
import { SourceTests } from "test/helper/SourceTests";
|
||||||
|
import { AMOscillator } from "./AMOscillator";
|
||||||
|
|
||||||
|
describe("AMOscillator", () => {
|
||||||
|
|
||||||
|
// run the common tests
|
||||||
|
BasicTests(AMOscillator);
|
||||||
|
SourceTests(AMOscillator);
|
||||||
|
OscillatorTests(AMOscillator);
|
||||||
|
|
||||||
|
it("matches a file", () => {
|
||||||
|
return CompareToFile(() => {
|
||||||
|
const osc = new AMOscillator().toMaster();
|
||||||
|
osc.start(0.1).stop(0.4);
|
||||||
|
}, "amOscillator.wav", 0.03);
|
||||||
|
});
|
||||||
|
|
||||||
|
context("Amplitude Modulation", () => {
|
||||||
|
|
||||||
|
it("can pass in parameters in the constructor", () => {
|
||||||
|
const amOsc = new AMOscillator({
|
||||||
|
harmonicity : 3,
|
||||||
|
modulationType : "square3",
|
||||||
|
type : "triangle2",
|
||||||
|
});
|
||||||
|
expect(amOsc.type).to.equal("triangle2");
|
||||||
|
expect(amOsc.harmonicity.value).to.be.closeTo(3, 0.001);
|
||||||
|
expect(amOsc.modulationType).to.equal("square3");
|
||||||
|
amOsc.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can set the harmonicity", () => {
|
||||||
|
const amOsc = new AMOscillator();
|
||||||
|
amOsc.harmonicity.value = 0.2;
|
||||||
|
expect(amOsc.harmonicity.value).to.be.closeTo(0.2, 0.001);
|
||||||
|
amOsc.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can set the modulationType", () => {
|
||||||
|
const amOsc = new AMOscillator();
|
||||||
|
amOsc.modulationType = "triangle5";
|
||||||
|
expect(amOsc.modulationType).to.equal("triangle5");
|
||||||
|
amOsc.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can get/set the baseType", () => {
|
||||||
|
const osc = new AMOscillator();
|
||||||
|
osc.type = "sine5";
|
||||||
|
expect(osc.baseType).to.equal("sine");
|
||||||
|
osc.baseType = "triangle";
|
||||||
|
expect(osc.type).to.equal("triangle5");
|
||||||
|
expect(osc.partialCount).to.equal(5);
|
||||||
|
osc.partialCount = 2;
|
||||||
|
expect(osc.type).to.equal("triangle2");
|
||||||
|
osc.baseType = "custom";
|
||||||
|
expect(osc.type).to.equal("custom");
|
||||||
|
osc.partials = [1, 2, 3];
|
||||||
|
expect(osc.baseType).to.equal("custom");
|
||||||
|
expect(osc.partials).to.deep.equal([1, 2, 3]);
|
||||||
|
osc.baseType = "square";
|
||||||
|
expect(osc.type).to.equal("square");
|
||||||
|
osc.dispose();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
230
Tone/source/oscillator/AMOscillator.ts
Normal file
230
Tone/source/oscillator/AMOscillator.ts
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
import { Gain } from "../../core/context/Gain";
|
||||||
|
import { optionsFromArguments } from "../../core/util/Defaults";
|
||||||
|
import { readOnly } from "../../core/util/Interface";
|
||||||
|
import { AudioToGain } from "../../signal/AudioToGain";
|
||||||
|
import { Multiply } from "../../signal/Multiply";
|
||||||
|
import { Signal } from "../../signal/Signal";
|
||||||
|
import { Source } from "../Source";
|
||||||
|
import { Oscillator, OscillatorInterface, ToneOscillatorOptions, ToneOscillatorType } from "./Oscillator";
|
||||||
|
|
||||||
|
interface AMOscillatorOptions extends ToneOscillatorOptions {
|
||||||
|
harmonicity: Positive;
|
||||||
|
modulationType: ToneOscillatorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An amplitude modulated oscillator node. It is implemented with
|
||||||
|
* two oscillators, one which modulators the other's amplitude
|
||||||
|
* through a gain node.
|
||||||
|
*
|
||||||
|
* +-------------+ +----------+
|
||||||
|
* | Carrier Osc +>------> GainNode |
|
||||||
|
* +-------------+ | +--->Output
|
||||||
|
* +---> gain |
|
||||||
|
* +---------------+ | +----------+
|
||||||
|
* | Modulator Osc +>---+
|
||||||
|
* +---------------+
|
||||||
|
*
|
||||||
|
* @param frequency The starting frequency of the oscillator.
|
||||||
|
* @param type The type of the carrier oscillator.
|
||||||
|
* @param modulationType The type of the modulator oscillator.
|
||||||
|
* @example
|
||||||
|
* //a sine oscillator frequency-modulated by a square wave
|
||||||
|
* var fmOsc = new AMOscillator("Ab3", "sine", "square").toMaster().start();
|
||||||
|
*/
|
||||||
|
export class AMOscillator extends Source<AMOscillatorOptions> implements OscillatorInterface {
|
||||||
|
|
||||||
|
readonly name = "AMOscillator";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The carrier oscillator
|
||||||
|
*/
|
||||||
|
private _carrier: Oscillator = new Oscillator({context : this.context });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The oscillator's frequency
|
||||||
|
*/
|
||||||
|
frequency: Signal<"frequency"> = this._carrier.frequency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The detune control signal.
|
||||||
|
*/
|
||||||
|
detune: Signal<"cents"> = this._carrier.detune;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The modulating oscillator
|
||||||
|
*/
|
||||||
|
private _modulator = new Oscillator({ context : this.context });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert the -1,1 output to 0,1
|
||||||
|
*/
|
||||||
|
private _modulationScale = new AudioToGain({ context: this.context });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* //pitch the modulator an octave below carrier
|
||||||
|
* synth.harmonicity.value = 0.5;
|
||||||
|
*/
|
||||||
|
harmonicity = new Multiply({
|
||||||
|
context: this.context,
|
||||||
|
units: "positive",
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the node where the modulation happens
|
||||||
|
*/
|
||||||
|
private _modulationNode = new Gain({
|
||||||
|
context: this.context,
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor(options?: Partial<AMOscillatorOptions>);
|
||||||
|
constructor(frequency?: Frequency, type?: ToneOscillatorType, modulationType?: ToneOscillatorType);
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
super(optionsFromArguments(AMOscillator.getDefaults(), arguments, ["frequency", "type", "modulationType"]));
|
||||||
|
const options = optionsFromArguments(AMOscillator.getDefaults(), arguments, ["frequency", "type", "modulationType"]);
|
||||||
|
|
||||||
|
this._carrier.type = options.type;
|
||||||
|
this._modulator.type = options.modulationType;
|
||||||
|
this.frequency.setValueAtTime(options.frequency, 0);
|
||||||
|
this.detune.setValueAtTime(options.detune, 0);
|
||||||
|
this.harmonicity.setValueAtTime(options.harmonicity, 0);
|
||||||
|
|
||||||
|
// connections
|
||||||
|
this.frequency.chain(this.harmonicity, this._modulator.frequency);
|
||||||
|
this._modulator.chain(this._modulationScale, this._modulationNode.gain);
|
||||||
|
this._carrier.chain(this._modulationNode, this.output);
|
||||||
|
|
||||||
|
this.phase = options.phase;
|
||||||
|
|
||||||
|
readOnly(this, ["frequency", "detune", "harmonicity"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDefaults(): AMOscillatorOptions {
|
||||||
|
return Object.assign(Oscillator.getDefaults(), {
|
||||||
|
harmonicity: 1,
|
||||||
|
modulationType: "square",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start the oscillator
|
||||||
|
*/
|
||||||
|
protected _start(time: Seconds): void {
|
||||||
|
this._modulator.start(time);
|
||||||
|
this._carrier.start(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stop the oscillator
|
||||||
|
*/
|
||||||
|
protected _stop(time: Seconds): void {
|
||||||
|
this._modulator.stop(time);
|
||||||
|
this._carrier.stop(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* restart the oscillator
|
||||||
|
*/
|
||||||
|
restart(time?: Time): this {
|
||||||
|
this._modulator.restart(time);
|
||||||
|
this._carrier.restart(time);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the carrier oscillator
|
||||||
|
*/
|
||||||
|
get type(): ToneOscillatorType {
|
||||||
|
return this._carrier.type;
|
||||||
|
}
|
||||||
|
set type(type: ToneOscillatorType) {
|
||||||
|
this._carrier.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The oscillator type without the partialsCount appended to the end
|
||||||
|
* @example
|
||||||
|
* osc.type = 'sine2'
|
||||||
|
* osc.baseType //'sine'
|
||||||
|
* osc.partialCount = 2
|
||||||
|
*/
|
||||||
|
get baseType(): OscillatorType {
|
||||||
|
return this._carrier.baseType;
|
||||||
|
}
|
||||||
|
set baseType(baseType: OscillatorType) {
|
||||||
|
this._carrier.baseType = baseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 'partialCount' offers an alternative way to set the number of used partials.
|
||||||
|
* When partialCount is 0, the maximum number of partials are used when representing
|
||||||
|
* the waveform using the periodicWave. When 'partials' is set, this value is
|
||||||
|
* not settable, but equals the length of the partials array.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The phase of the oscillator in degrees.
|
||||||
|
*/
|
||||||
|
get phase(): Degrees {
|
||||||
|
return this._carrier.phase;
|
||||||
|
}
|
||||||
|
set phase(phase: Degrees) {
|
||||||
|
this._carrier.phase = phase;
|
||||||
|
this._modulator.phase = phase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The partials of the carrier waveform. A partial represents
|
||||||
|
* the amplitude at a harmonic. The first harmonic is the
|
||||||
|
* fundamental frequency, the second is the octave and so on
|
||||||
|
* following the harmonic series.
|
||||||
|
* Setting this value will automatically set the type to "custom".
|
||||||
|
* The value is an empty array when the type is not "custom".
|
||||||
|
* @example
|
||||||
|
* osc.partials = [1, 0.2, 0.01];
|
||||||
|
*/
|
||||||
|
get partials(): number[] {
|
||||||
|
return this._carrier.partials;
|
||||||
|
}
|
||||||
|
set partials(partials: number[]) {
|
||||||
|
this._carrier.partials = partials;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up.
|
||||||
|
*/
|
||||||
|
dispose(): this {
|
||||||
|
super.dispose();
|
||||||
|
this.frequency.dispose();
|
||||||
|
this.detune.dispose();
|
||||||
|
this.harmonicity.dispose();
|
||||||
|
this._carrier.dispose();
|
||||||
|
this._modulator.dispose();
|
||||||
|
this._modulationNode.dispose();
|
||||||
|
this._modulationScale.dispose();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue