diff --git a/Tone/source/oscillator/AMOscillator.ts b/Tone/source/oscillator/AMOscillator.ts index ba5b324c8..a898c41f5 100644 --- a/Tone/source/oscillator/AMOscillator.ts +++ b/Tone/source/oscillator/AMOscillator.ts @@ -8,7 +8,8 @@ import { Signal } from "../../signal/Signal"; import { Source } from "../Source"; import { Oscillator } from "./Oscillator"; import { AMConstructorOptions, AMOscillatorOptions, - NonCustomOscillatorType, ToneOscillatorInterface, + generateWaveform, NonCustomOscillatorType, + ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface"; export { AMOscillatorOptions } from "./OscillatorInterface"; @@ -230,6 +231,10 @@ export class AMOscillator extends Source implements ToneOsc this._carrier.partials = partials; } + async asArray(length: number = 1024): Promise { + return generateWaveform(this, length); + } + /** * Clean up. */ diff --git a/Tone/source/oscillator/FMOscillator.ts b/Tone/source/oscillator/FMOscillator.ts index 9b3665d1f..3701c2b76 100644 --- a/Tone/source/oscillator/FMOscillator.ts +++ b/Tone/source/oscillator/FMOscillator.ts @@ -7,7 +7,7 @@ import { Signal } from "../../signal/Signal"; import { Source } from "../Source"; import { Oscillator } from "./Oscillator"; import { FMConstructorOptions, FMOscillatorOptions, - NonCustomOscillatorType, ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface"; + generateWaveform, NonCustomOscillatorType, ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface"; export { FMOscillatorOptions } from "./OscillatorInterface"; /** @@ -245,6 +245,10 @@ export class FMOscillator extends Source implements ToneOsc this._carrier.partials = partials; } + async asArray(length: number = 1024): Promise { + return generateWaveform(this, length); + } + /** * Clean up. */ diff --git a/Tone/source/oscillator/FatOscillator.ts b/Tone/source/oscillator/FatOscillator.ts index 57253602d..7d312fd2d 100644 --- a/Tone/source/oscillator/FatOscillator.ts +++ b/Tone/source/oscillator/FatOscillator.ts @@ -7,7 +7,7 @@ import { Signal } from "../../signal/Signal"; import { Source } from "../Source"; import { Oscillator } from "./Oscillator"; import { FatConstructorOptions, FatOscillatorOptions, - ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface"; + generateWaveform, ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface"; export { FatOscillatorOptions } from "./OscillatorInterface"; @@ -273,6 +273,10 @@ export class FatOscillator extends Source implements ToneO this._type = this._oscillators[0].type; } + async asArray(length: number = 1024): Promise { + return generateWaveform(this, length); + } + /** * Clean up. */ diff --git a/Tone/source/oscillator/OmniOscillator.ts b/Tone/source/oscillator/OmniOscillator.ts index f29894d49..942df741d 100644 --- a/Tone/source/oscillator/OmniOscillator.ts +++ b/Tone/source/oscillator/OmniOscillator.ts @@ -8,9 +8,9 @@ import { AMOscillator } from "./AMOscillator"; import { FatOscillator } from "./FatOscillator"; import { FMOscillator } from "./FMOscillator"; import { Oscillator } from "./Oscillator"; -import { OmniOscillatorConstructorOptions, - OmniOscillatorOptions, OmniOscillatorType, - ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface"; +import { generateWaveform, + OmniOscillatorConstructorOptions, OmniOscillatorOptions, + OmniOscillatorType, ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface"; import { PulseOscillator } from "./PulseOscillator"; import { PWMOscillator } from "./PWMOscillator"; @@ -474,6 +474,10 @@ export class OmniOscillator } } + async asArray(length: number = 1024): Promise { + return generateWaveform(this, length); + } + dispose(): this { super.dispose(); this.detune.dispose(); diff --git a/Tone/source/oscillator/Oscillator.ts b/Tone/source/oscillator/Oscillator.ts index 846e73c35..a38e43078 100644 --- a/Tone/source/oscillator/Oscillator.ts +++ b/Tone/source/oscillator/Oscillator.ts @@ -4,10 +4,12 @@ import { noOp, readOnly } from "../../core/util/Interface"; import { isDefined } from "../../core/util/TypeCheck"; import { Signal } from "../../signal/Signal"; import { Source } from "../Source"; -import { ToneOscillatorConstructorOptions, ToneOscillatorInterface, +import { + generateWaveform, + ToneOscillatorConstructorOptions, ToneOscillatorInterface, ToneOscillatorOptions, ToneOscillatorType } from "./OscillatorInterface"; import { ToneOscillatorNode } from "./ToneOscillatorNode"; - +import { OfflineContext } from "../../core/context/OfflineContext"; export { ToneOscillatorOptions, ToneOscillatorType } from "./OscillatorInterface"; /** * Oscillator supports a number of features including @@ -481,6 +483,10 @@ export class Oscillator extends Source implements ToneOsc this.type = this._type; } + async asArray(length: number = 1024): Promise { + return generateWaveform(this, length); + } + dispose(): this { super.dispose(); if (this._oscillator !== null) { diff --git a/Tone/source/oscillator/OscillatorInterface.ts b/Tone/source/oscillator/OscillatorInterface.ts index 41f7d7043..cdc240de3 100644 --- a/Tone/source/oscillator/OscillatorInterface.ts +++ b/Tone/source/oscillator/OscillatorInterface.ts @@ -2,6 +2,7 @@ import { AudioRange, Cents, Degrees, Frequency, Positive } from "../../core/type import { Omit } from "../../core/util/Interface"; import { Signal } from "../../signal/Signal"; import { SourceOptions } from "../Source"; +import { OfflineContext } from "../../core/context/OfflineContext"; /** * The common interface of all Oscillators @@ -14,6 +15,29 @@ export interface ToneOscillatorInterface { phase: Degrees; partials: number[]; partialCount?: number; + /** + * Returns an array of values which represents the waveform. + * @param length The length of the waveform to return + */ + asArray(length: number): Promise; +} + +/** + * Render a segment of the oscillator to an offline context and return the results as an array + */ +export async function generateWaveform(instance: any, length: number): Promise { + const duration = length / instance.context.sampleRate; + const context = new OfflineContext(1, duration, instance.context.sampleRate); + const clone = new instance.constructor(Object.assign(instance.get(), { + // should do 2 iterations + frequency: 2 / duration, + // zero out the detune + detune: 0, + context + })).toDestination(); + clone.start(0); + const buffer = await context.render(); + return buffer.getChannelData(0); } /** diff --git a/Tone/source/oscillator/PWMOscillator.ts b/Tone/source/oscillator/PWMOscillator.ts index a99d4a6a1..ea83e1d45 100644 --- a/Tone/source/oscillator/PWMOscillator.ts +++ b/Tone/source/oscillator/PWMOscillator.ts @@ -5,7 +5,7 @@ import { Multiply } from "../../signal/Multiply"; import { Signal } from "../../signal/Signal"; import { Source } from "../Source"; import { Oscillator } from "./Oscillator"; -import { PWMOscillatorOptions, ToneOscillatorInterface } from "./OscillatorInterface"; +import { generateWaveform, PWMOscillatorOptions, ToneOscillatorInterface } from "./OscillatorInterface"; import { PulseOscillator } from "./PulseOscillator"; export { PWMOscillatorOptions } from "./OscillatorInterface"; @@ -169,6 +169,10 @@ export class PWMOscillator extends Source implements ToneO this._modulator.phase = phase; } + async asArray(length: number = 1024): Promise { + return generateWaveform(this, length); + } + /** * Clean up. */ diff --git a/Tone/source/oscillator/PulseOscillator.ts b/Tone/source/oscillator/PulseOscillator.ts index 069f9c597..465de286e 100644 --- a/Tone/source/oscillator/PulseOscillator.ts +++ b/Tone/source/oscillator/PulseOscillator.ts @@ -6,7 +6,7 @@ import { Signal } from "../../signal/Signal"; import { WaveShaper } from "../../signal/WaveShaper"; import { Source } from "../Source"; import { Oscillator } from "./Oscillator"; -import { PulseOscillatorOptions, ToneOscillatorInterface } from "./OscillatorInterface"; +import { generateWaveform, PulseOscillatorOptions, ToneOscillatorInterface } from "./OscillatorInterface"; export { PulseOscillatorOptions } from "./OscillatorInterface"; @@ -197,6 +197,10 @@ export class PulseOscillator extends Source implements T return 0; } + async asArray(length: number = 1024): Promise { + return generateWaveform(this, length); + } + /** * Clean up method. */ diff --git a/test/helper/OscillatorTests.ts b/test/helper/OscillatorTests.ts index 3ed78ffb3..e9f14fb3c 100644 --- a/test/helper/OscillatorTests.ts +++ b/test/helper/OscillatorTests.ts @@ -60,5 +60,19 @@ export function OscillatorTests(Constr, args?): void { expect(buffer.max()).to.be.at.most(1); }); }); + + it("can generate a waveform", async () => { + const osc = new Constr(); + const waveform = await osc.asArray(); + waveform.forEach((v: number) => expect(v).to.be.within(-1, 1)); + osc.dispose(); + }); + + it("can generate a waveform of a specific length", async () => { + const osc = new Constr(); + const waveform = await osc.asArray(256); + expect(waveform.length).to.equal(256); + osc.dispose(); + }); }); }