Skip to content

Commit 9ad519e

Browse files
committed
feat: Get an oscillator wave as an array
1 parent 2935f65 commit 9ad519e

File tree

9 files changed

+79
-10
lines changed

9 files changed

+79
-10
lines changed

Tone/source/oscillator/AMOscillator.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import { Signal } from "../../signal/Signal";
88
import { Source } from "../Source";
99
import { Oscillator } from "./Oscillator";
1010
import { AMConstructorOptions, AMOscillatorOptions,
11-
NonCustomOscillatorType, ToneOscillatorInterface,
11+
generateWaveform, NonCustomOscillatorType,
12+
ToneOscillatorInterface,
1213
ToneOscillatorType } from "./OscillatorInterface";
1314

1415
export { AMOscillatorOptions } from "./OscillatorInterface";
@@ -230,6 +231,10 @@ export class AMOscillator extends Source<AMOscillatorOptions> implements ToneOsc
230231
this._carrier.partials = partials;
231232
}
232233

234+
async asArray(length: number = 1024): Promise<Float32Array> {
235+
return generateWaveform(this, length);
236+
}
237+
233238
/**
234239
* Clean up.
235240
*/

Tone/source/oscillator/FMOscillator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Signal } from "../../signal/Signal";
77
import { Source } from "../Source";
88
import { Oscillator } from "./Oscillator";
99
import { FMConstructorOptions, FMOscillatorOptions,
10-
NonCustomOscillatorType, ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface";
10+
generateWaveform, NonCustomOscillatorType, ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface";
1111

1212
export { FMOscillatorOptions } from "./OscillatorInterface";
1313
/**
@@ -245,6 +245,10 @@ export class FMOscillator extends Source<FMOscillatorOptions> implements ToneOsc
245245
this._carrier.partials = partials;
246246
}
247247

248+
async asArray(length: number = 1024): Promise<Float32Array> {
249+
return generateWaveform(this, length);
250+
}
251+
248252
/**
249253
* Clean up.
250254
*/

Tone/source/oscillator/FatOscillator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Signal } from "../../signal/Signal";
77
import { Source } from "../Source";
88
import { Oscillator } from "./Oscillator";
99
import { FatConstructorOptions, FatOscillatorOptions,
10-
ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface";
10+
generateWaveform, ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface";
1111

1212
export { FatOscillatorOptions } from "./OscillatorInterface";
1313

@@ -273,6 +273,10 @@ export class FatOscillator extends Source<FatOscillatorOptions> implements ToneO
273273
this._type = this._oscillators[0].type;
274274
}
275275

276+
async asArray(length: number = 1024): Promise<Float32Array> {
277+
return generateWaveform(this, length);
278+
}
279+
276280
/**
277281
* Clean up.
278282
*/

Tone/source/oscillator/OmniOscillator.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import { AMOscillator } from "./AMOscillator";
88
import { FatOscillator } from "./FatOscillator";
99
import { FMOscillator } from "./FMOscillator";
1010
import { Oscillator } from "./Oscillator";
11-
import { OmniOscillatorConstructorOptions,
12-
OmniOscillatorOptions, OmniOscillatorType,
13-
ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface";
11+
import { generateWaveform,
12+
OmniOscillatorConstructorOptions, OmniOscillatorOptions,
13+
OmniOscillatorType, ToneOscillatorInterface, ToneOscillatorType } from "./OscillatorInterface";
1414
import { PulseOscillator } from "./PulseOscillator";
1515
import { PWMOscillator } from "./PWMOscillator";
1616

@@ -474,6 +474,10 @@ export class OmniOscillator<OscType extends AnyOscillator>
474474
}
475475
}
476476

477+
async asArray(length: number = 1024): Promise<Float32Array> {
478+
return generateWaveform(this, length);
479+
}
480+
477481
dispose(): this {
478482
super.dispose();
479483
this.detune.dispose();

Tone/source/oscillator/Oscillator.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { noOp, readOnly } from "../../core/util/Interface";
44
import { isDefined } from "../../core/util/TypeCheck";
55
import { Signal } from "../../signal/Signal";
66
import { Source } from "../Source";
7-
import { ToneOscillatorConstructorOptions, ToneOscillatorInterface,
7+
import {
8+
generateWaveform,
9+
ToneOscillatorConstructorOptions, ToneOscillatorInterface,
810
ToneOscillatorOptions, ToneOscillatorType } from "./OscillatorInterface";
911
import { ToneOscillatorNode } from "./ToneOscillatorNode";
10-
12+
import { OfflineContext } from "../../core/context/OfflineContext";
1113
export { ToneOscillatorOptions, ToneOscillatorType } from "./OscillatorInterface";
1214
/**
1315
* Oscillator supports a number of features including
@@ -481,6 +483,10 @@ export class Oscillator extends Source<ToneOscillatorOptions> implements ToneOsc
481483
this.type = this._type;
482484
}
483485

486+
async asArray(length: number = 1024): Promise<Float32Array> {
487+
return generateWaveform(this, length);
488+
}
489+
484490
dispose(): this {
485491
super.dispose();
486492
if (this._oscillator !== null) {

Tone/source/oscillator/OscillatorInterface.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { AudioRange, Cents, Degrees, Frequency, Positive } from "../../core/type
22
import { Omit } from "../../core/util/Interface";
33
import { Signal } from "../../signal/Signal";
44
import { SourceOptions } from "../Source";
5+
import { OfflineContext } from "../../core/context/OfflineContext";
56

67
/**
78
* The common interface of all Oscillators
@@ -14,6 +15,29 @@ export interface ToneOscillatorInterface {
1415
phase: Degrees;
1516
partials: number[];
1617
partialCount?: number;
18+
/**
19+
* Returns an array of values which represents the waveform.
20+
* @param length The length of the waveform to return
21+
*/
22+
asArray(length: number): Promise<Float32Array>;
23+
}
24+
25+
/**
26+
* Render a segment of the oscillator to an offline context and return the results as an array
27+
*/
28+
export async function generateWaveform(instance: any, length: number): Promise<Float32Array> {
29+
const duration = length / instance.context.sampleRate;
30+
const context = new OfflineContext(1, duration, instance.context.sampleRate);
31+
const clone = new instance.constructor(Object.assign(instance.get(), {
32+
// should do 2 iterations
33+
frequency: 2 / duration,
34+
// zero out the detune
35+
detune: 0,
36+
context
37+
})).toDestination();
38+
clone.start(0);
39+
const buffer = await context.render();
40+
return buffer.getChannelData(0);
1741
}
1842

1943
/**

Tone/source/oscillator/PWMOscillator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Multiply } from "../../signal/Multiply";
55
import { Signal } from "../../signal/Signal";
66
import { Source } from "../Source";
77
import { Oscillator } from "./Oscillator";
8-
import { PWMOscillatorOptions, ToneOscillatorInterface } from "./OscillatorInterface";
8+
import { generateWaveform, PWMOscillatorOptions, ToneOscillatorInterface } from "./OscillatorInterface";
99
import { PulseOscillator } from "./PulseOscillator";
1010

1111
export { PWMOscillatorOptions } from "./OscillatorInterface";
@@ -169,6 +169,10 @@ export class PWMOscillator extends Source<PWMOscillatorOptions> implements ToneO
169169
this._modulator.phase = phase;
170170
}
171171

172+
async asArray(length: number = 1024): Promise<Float32Array> {
173+
return generateWaveform(this, length);
174+
}
175+
172176
/**
173177
* Clean up.
174178
*/

Tone/source/oscillator/PulseOscillator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Signal } from "../../signal/Signal";
66
import { WaveShaper } from "../../signal/WaveShaper";
77
import { Source } from "../Source";
88
import { Oscillator } from "./Oscillator";
9-
import { PulseOscillatorOptions, ToneOscillatorInterface } from "./OscillatorInterface";
9+
import { generateWaveform, PulseOscillatorOptions, ToneOscillatorInterface } from "./OscillatorInterface";
1010

1111
export { PulseOscillatorOptions } from "./OscillatorInterface";
1212

@@ -197,6 +197,10 @@ export class PulseOscillator extends Source<PulseOscillatorOptions> implements T
197197
return 0;
198198
}
199199

200+
async asArray(length: number = 1024): Promise<Float32Array> {
201+
return generateWaveform(this, length);
202+
}
203+
200204
/**
201205
* Clean up method.
202206
*/

test/helper/OscillatorTests.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,19 @@ export function OscillatorTests(Constr, args?): void {
6060
expect(buffer.max()).to.be.at.most(1);
6161
});
6262
});
63+
64+
it("can generate a waveform", async () => {
65+
const osc = new Constr();
66+
const waveform = await osc.asArray();
67+
waveform.forEach((v: number) => expect(v).to.be.within(-1, 1));
68+
osc.dispose();
69+
});
70+
71+
it("can generate a waveform of a specific length", async () => {
72+
const osc = new Constr();
73+
const waveform = await osc.asArray(256);
74+
expect(waveform.length).to.equal(256);
75+
osc.dispose();
76+
});
6377
});
6478
}

0 commit comments

Comments
 (0)