1 | import { FrequencyClass } from "../core/type/Frequency.js";
|
2 | import { Frequency, Positive, Time } from "../core/type/Units.js";
|
3 | import { deepMerge, optionsFromArguments } from "../core/util/Defaults.js";
|
4 | import { readOnly, RecursivePartial } from "../core/util/Interface.js";
|
5 | import { Monophonic } from "./Monophonic.js";
|
6 | import { Synth, SynthOptions } from "./Synth.js";
|
7 | import { range, timeRange } from "../core/util/Decorator.js";
|
8 |
|
9 | export interface MembraneSynthOptions extends SynthOptions {
|
10 | pitchDecay: Time;
|
11 | octaves: Positive;
|
12 | }
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | export class MembraneSynth extends Synth<MembraneSynthOptions> {
|
28 | readonly name: string = "MembraneSynth";
|
29 |
|
30 | |
31 |
|
32 |
|
33 |
|
34 |
|
35 | @range(0)
|
36 | octaves: Positive;
|
37 |
|
38 | |
39 |
|
40 |
|
41 |
|
42 |
|
43 | @timeRange(0)
|
44 | pitchDecay: Time;
|
45 |
|
46 | |
47 |
|
48 |
|
49 | readonly portamento = 0;
|
50 |
|
51 | |
52 |
|
53 |
|
54 | constructor(options?: RecursivePartial<MembraneSynthOptions>);
|
55 | constructor() {
|
56 | const options = optionsFromArguments(
|
57 | MembraneSynth.getDefaults(),
|
58 | arguments
|
59 | );
|
60 | super(options);
|
61 |
|
62 | this.pitchDecay = options.pitchDecay;
|
63 | this.octaves = options.octaves;
|
64 | readOnly(this, ["oscillator", "envelope"]);
|
65 | }
|
66 |
|
67 | static getDefaults(): MembraneSynthOptions {
|
68 | return deepMerge(Monophonic.getDefaults(), Synth.getDefaults(), {
|
69 | envelope: {
|
70 | attack: 0.001,
|
71 | attackCurve: "exponential",
|
72 | decay: 0.4,
|
73 | release: 1.4,
|
74 | sustain: 0.01,
|
75 | },
|
76 | octaves: 10,
|
77 | oscillator: {
|
78 | type: "sine",
|
79 | },
|
80 | pitchDecay: 0.05,
|
81 | });
|
82 | }
|
83 |
|
84 | setNote(note: Frequency | FrequencyClass, time?: Time): this {
|
85 | const seconds = this.toSeconds(time);
|
86 | const hertz = this.toFrequency(
|
87 | note instanceof FrequencyClass ? note.toFrequency() : note
|
88 | );
|
89 | const maxNote = hertz * this.octaves;
|
90 | this.oscillator.frequency.setValueAtTime(maxNote, seconds);
|
91 | this.oscillator.frequency.exponentialRampToValueAtTime(
|
92 | hertz,
|
93 | seconds + this.toSeconds(this.pitchDecay)
|
94 | );
|
95 | return this;
|
96 | }
|
97 |
|
98 | dispose(): this {
|
99 | super.dispose();
|
100 | return this;
|
101 | }
|
102 | }
|