UNPKG

4.15 kBPlain TextView Raw
1import { AmplitudeEnvelope } from "../component/envelope/AmplitudeEnvelope.js";
2import { NormalRange, Time } from "../core/type/Units.js";
3import { omitFromObject, optionsFromArguments } from "../core/util/Defaults.js";
4import { RecursivePartial } from "../core/util/Interface.js";
5import { Noise, NoiseOptions } from "../source/Noise.js";
6import { Instrument, InstrumentOptions } from "./Instrument.js";
7import {
8 ToneAudioNode,
9 ToneAudioNodeOptions,
10} from "../core/context/ToneAudioNode.js";
11import { Envelope, EnvelopeOptions } from "../component/envelope/Envelope.js";
12import { Source } from "../source/Source.js";
13
14export interface NoiseSynthOptions extends InstrumentOptions {
15 envelope: Omit<EnvelopeOptions, keyof ToneAudioNodeOptions>;
16 noise: Omit<NoiseOptions, keyof ToneAudioNodeOptions>;
17}
18
19/**
20 * Tone.NoiseSynth is composed of {@link Noise} through an {@link AmplitudeEnvelope}.
21 * ```
22 * +-------+ +-------------------+
23 * | Noise +>--> AmplitudeEnvelope +>--> Output
24 * +-------+ +-------------------+
25 * ```
26 * @example
27 * const noiseSynth = new Tone.NoiseSynth().toDestination();
28 * noiseSynth.triggerAttackRelease("8n", 0.05);
29 * @category Instrument
30 */
31export class NoiseSynth extends Instrument<NoiseSynthOptions> {
32 readonly name = "NoiseSynth";
33
34 /**
35 * The noise source.
36 */
37 readonly noise: Noise;
38
39 /**
40 * The amplitude envelope.
41 */
42 readonly envelope: AmplitudeEnvelope;
43
44 constructor(options?: RecursivePartial<NoiseSynthOptions>);
45 constructor() {
46 const options = optionsFromArguments(
47 NoiseSynth.getDefaults(),
48 arguments
49 );
50 super(options);
51
52 this.noise = new Noise(
53 Object.assign(
54 {
55 context: this.context,
56 },
57 options.noise
58 )
59 );
60
61 this.envelope = new AmplitudeEnvelope(
62 Object.assign(
63 {
64 context: this.context,
65 },
66 options.envelope
67 )
68 );
69
70 // connect the noise to the output
71 this.noise.chain(this.envelope, this.output);
72 }
73
74 static getDefaults(): NoiseSynthOptions {
75 return Object.assign(Instrument.getDefaults(), {
76 envelope: Object.assign(
77 omitFromObject(
78 Envelope.getDefaults(),
79 Object.keys(ToneAudioNode.getDefaults())
80 ),
81 {
82 decay: 0.1,
83 sustain: 0.0,
84 }
85 ),
86 noise: Object.assign(
87 omitFromObject(
88 Noise.getDefaults(),
89 Object.keys(Source.getDefaults())
90 ),
91 {
92 type: "white",
93 }
94 ),
95 });
96 }
97
98 /**
99 * Start the attack portion of the envelopes. Unlike other
100 * instruments, Tone.NoiseSynth doesn't have a note.
101 * @example
102 * const noiseSynth = new Tone.NoiseSynth().toDestination();
103 * noiseSynth.triggerAttack();
104 */
105 triggerAttack(time?: Time, velocity: NormalRange = 1): this {
106 time = this.toSeconds(time);
107 // the envelopes
108 this.envelope.triggerAttack(time, velocity);
109 // start the noise
110 this.noise.start(time);
111 if (this.envelope.sustain === 0) {
112 this.noise.stop(
113 time +
114 this.toSeconds(this.envelope.attack) +
115 this.toSeconds(this.envelope.decay)
116 );
117 }
118 return this;
119 }
120
121 /**
122 * Start the release portion of the envelopes.
123 */
124 triggerRelease(time?: Time): this {
125 time = this.toSeconds(time);
126 this.envelope.triggerRelease(time);
127 this.noise.stop(time + this.toSeconds(this.envelope.release));
128 return this;
129 }
130
131 sync(): this {
132 if (this._syncState()) {
133 this._syncMethod("triggerAttack", 0);
134 this._syncMethod("triggerRelease", 0);
135 }
136 return this;
137 }
138
139 /**
140 * Trigger the attack and then the release after the duration.
141 * @param duration The amount of time to hold the note for
142 * @param time The time the note should start
143 * @param velocity The volume of the note (0-1)
144 * @example
145 * const noiseSynth = new Tone.NoiseSynth().toDestination();
146 * // hold the note for 0.5 seconds
147 * noiseSynth.triggerAttackRelease(0.5);
148 */
149 triggerAttackRelease(
150 duration: Time,
151 time?: Time,
152 velocity: NormalRange = 1
153 ): this {
154 time = this.toSeconds(time);
155 duration = this.toSeconds(duration);
156 this.triggerAttack(time, velocity);
157 this.triggerRelease(time + duration);
158 return this;
159 }
160
161 dispose(): this {
162 super.dispose();
163 this.noise.dispose();
164 this.envelope.dispose();
165 return this;
166 }
167}