1 | import { Signal } from "../signal/Signal.js";
|
2 | import { Multiply } from "../signal/Multiply.js";
|
3 | import { Gain } from "../core/context/Gain.js";
|
4 | import { Envelope } from "../component/envelope/Envelope.js";
|
5 | import { ToneAudioNode, } from "../core/context/ToneAudioNode.js";
|
6 | import { Monophonic } from "./Monophonic.js";
|
7 | import { OmniOscillator } from "../source/oscillator/OmniOscillator.js";
|
8 | import { Source } from "../source/Source.js";
|
9 | import { Synth } from "./Synth.js";
|
10 | import { readOnly } from "../core/util/Interface.js";
|
11 | import { omitFromObject, optionsFromArguments } from "../core/util/Defaults.js";
|
12 |
|
13 |
|
14 |
|
15 | export class ModulationSynth extends Monophonic {
|
16 | constructor() {
|
17 | const options = optionsFromArguments(ModulationSynth.getDefaults(), arguments);
|
18 | super(options);
|
19 | this.name = "ModulationSynth";
|
20 | this._carrier = new Synth({
|
21 | context: this.context,
|
22 | oscillator: options.oscillator,
|
23 | envelope: options.envelope,
|
24 | onsilence: () => this.onsilence(this),
|
25 | volume: -10,
|
26 | });
|
27 | this._modulator = new Synth({
|
28 | context: this.context,
|
29 | oscillator: options.modulation,
|
30 | envelope: options.modulationEnvelope,
|
31 | volume: -10,
|
32 | });
|
33 | this.oscillator = this._carrier.oscillator;
|
34 | this.envelope = this._carrier.envelope;
|
35 | this.modulation = this._modulator.oscillator;
|
36 | this.modulationEnvelope = this._modulator.envelope;
|
37 | this.frequency = new Signal({
|
38 | context: this.context,
|
39 | units: "frequency",
|
40 | });
|
41 | this.detune = new Signal({
|
42 | context: this.context,
|
43 | value: options.detune,
|
44 | units: "cents",
|
45 | });
|
46 | this.harmonicity = new Multiply({
|
47 | context: this.context,
|
48 | value: options.harmonicity,
|
49 | minValue: 0,
|
50 | });
|
51 | this._modulationNode = new Gain({
|
52 | context: this.context,
|
53 | gain: 0,
|
54 | });
|
55 | readOnly(this, [
|
56 | "frequency",
|
57 | "harmonicity",
|
58 | "oscillator",
|
59 | "envelope",
|
60 | "modulation",
|
61 | "modulationEnvelope",
|
62 | "detune",
|
63 | ]);
|
64 | }
|
65 | static getDefaults() {
|
66 | return Object.assign(Monophonic.getDefaults(), {
|
67 | harmonicity: 3,
|
68 | oscillator: Object.assign(omitFromObject(OmniOscillator.getDefaults(), [
|
69 | ...Object.keys(Source.getDefaults()),
|
70 | "frequency",
|
71 | "detune",
|
72 | ]), {
|
73 | type: "sine",
|
74 | }),
|
75 | envelope: Object.assign(omitFromObject(Envelope.getDefaults(), Object.keys(ToneAudioNode.getDefaults())), {
|
76 | attack: 0.01,
|
77 | decay: 0.01,
|
78 | sustain: 1,
|
79 | release: 0.5,
|
80 | }),
|
81 | modulation: Object.assign(omitFromObject(OmniOscillator.getDefaults(), [
|
82 | ...Object.keys(Source.getDefaults()),
|
83 | "frequency",
|
84 | "detune",
|
85 | ]), {
|
86 | type: "square",
|
87 | }),
|
88 | modulationEnvelope: Object.assign(omitFromObject(Envelope.getDefaults(), Object.keys(ToneAudioNode.getDefaults())), {
|
89 | attack: 0.5,
|
90 | decay: 0.0,
|
91 | sustain: 1,
|
92 | release: 0.5,
|
93 | }),
|
94 | });
|
95 | }
|
96 | |
97 |
|
98 |
|
99 | _triggerEnvelopeAttack(time, velocity) {
|
100 |
|
101 | this._carrier._triggerEnvelopeAttack(time, velocity);
|
102 |
|
103 | this._modulator._triggerEnvelopeAttack(time, velocity);
|
104 | }
|
105 | |
106 |
|
107 |
|
108 | _triggerEnvelopeRelease(time) {
|
109 |
|
110 | this._carrier._triggerEnvelopeRelease(time);
|
111 |
|
112 | this._modulator._triggerEnvelopeRelease(time);
|
113 | return this;
|
114 | }
|
115 | getLevelAtTime(time) {
|
116 | time = this.toSeconds(time);
|
117 | return this.envelope.getValueAtTime(time);
|
118 | }
|
119 | dispose() {
|
120 | super.dispose();
|
121 | this._carrier.dispose();
|
122 | this._modulator.dispose();
|
123 | this.frequency.dispose();
|
124 | this.detune.dispose();
|
125 | this.harmonicity.dispose();
|
126 | this._modulationNode.dispose();
|
127 | return this;
|
128 | }
|
129 | }
|
130 |
|
\ | No newline at end of file |