1 | import { optionsFromArguments } from "../core/util/Defaults.js";
|
2 | import { isArray, isFunction } from "../core/util/TypeCheck.js";
|
3 | import { assert } from "../core/util/Debug.js";
|
4 | import { Signal } from "./Signal.js";
|
5 | import { SignalOperator } from "./SignalOperator.js";
|
6 | /**
|
7 | * Wraps the native Web Audio API
|
8 | * [WaveShaperNode](http://webaudio.github.io/web-audio-api/#the-waveshapernode-interface).
|
9 | *
|
10 | * @example
|
11 | * const osc = new Tone.Oscillator().toDestination().start();
|
12 | * // multiply the output of the signal by 2 using the waveshaper's function
|
13 | * const timesTwo = new Tone.WaveShaper((val) => val * 2, 2048).connect(osc.frequency);
|
14 | * const signal = new Tone.Signal(440).connect(timesTwo);
|
15 | * @category Signal
|
16 | */
|
17 | export class WaveShaper extends SignalOperator {
|
18 | constructor() {
|
19 | const options = optionsFromArguments(WaveShaper.getDefaults(), arguments, ["mapping", "length"]);
|
20 | super(options);
|
21 | this.name = "WaveShaper";
|
22 | /**
|
23 | * the waveshaper node
|
24 | */
|
25 | this._shaper = this.context.createWaveShaper();
|
26 | /**
|
27 | * The input to the waveshaper node.
|
28 | */
|
29 | this.input = this._shaper;
|
30 | /**
|
31 | * The output from the waveshaper node
|
32 | */
|
33 | this.output = this._shaper;
|
34 | if (isArray(options.mapping) ||
|
35 | options.mapping instanceof Float32Array) {
|
36 | this.curve = Float32Array.from(options.mapping);
|
37 | }
|
38 | else if (isFunction(options.mapping)) {
|
39 | this.setMap(options.mapping, options.length);
|
40 | }
|
41 | }
|
42 | static getDefaults() {
|
43 | return Object.assign(Signal.getDefaults(), {
|
44 | length: 1024,
|
45 | });
|
46 | }
|
47 | /**
|
48 | * Uses a mapping function to set the value of the curve.
|
49 | * @param mapping The function used to define the values.
|
50 | * The mapping function take two arguments:
|
51 | * the first is the value at the current position
|
52 | * which goes from -1 to 1 over the number of elements
|
53 | * in the curve array. The second argument is the array position.
|
54 | * @example
|
55 | * const shaper = new Tone.WaveShaper();
|
56 | * // map the input signal from [-1, 1] to [0, 10]
|
57 | * shaper.setMap((val, index) => (val + 1) * 5);
|
58 | */
|
59 | setMap(mapping, length = 1024) {
|
60 | const array = new Float32Array(length);
|
61 | for (let i = 0, len = length; i < len; i++) {
|
62 | const normalized = (i / (len - 1)) * 2 - 1;
|
63 | array[i] = mapping(normalized, i);
|
64 | }
|
65 | this.curve = array;
|
66 | return this;
|
67 | }
|
68 | /**
|
69 | * The array to set as the waveshaper curve. For linear curves
|
70 | * array length does not make much difference, but for complex curves
|
71 | * longer arrays will provide smoother interpolation.
|
72 | */
|
73 | get curve() {
|
74 | return this._shaper.curve;
|
75 | }
|
76 | set curve(mapping) {
|
77 | this._shaper.curve = mapping;
|
78 | }
|
79 | /**
|
80 | * Specifies what type of oversampling (if any) should be used when
|
81 | * applying the shaping curve. Can either be "none", "2x" or "4x".
|
82 | */
|
83 | get oversample() {
|
84 | return this._shaper.oversample;
|
85 | }
|
86 | set oversample(oversampling) {
|
87 | const isOverSampleType = ["none", "2x", "4x"].some((str) => str.includes(oversampling));
|
88 | assert(isOverSampleType, "oversampling must be either 'none', '2x', or '4x'");
|
89 | this._shaper.oversample = oversampling;
|
90 | }
|
91 | /**
|
92 | * Clean up.
|
93 | */
|
94 | dispose() {
|
95 | super.dispose();
|
96 | this._shaper.disconnect();
|
97 | return this;
|
98 | }
|
99 | }
|
100 | //# sourceMappingURL=WaveShaper.js.map |
\ | No newline at end of file |