@blibliki/engine 0.1.26 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/README.md +252 -76
  2. package/dist/index.cjs +2 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +765 -0
  5. package/dist/index.d.ts +765 -0
  6. package/dist/index.js +2 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +19 -31
  9. package/src/Engine.ts +158 -177
  10. package/src/core/IO/AudioIO.ts +72 -0
  11. package/src/core/IO/Base.ts +118 -0
  12. package/src/core/IO/Collection.ts +123 -47
  13. package/src/core/IO/MidiIO.ts +43 -0
  14. package/src/core/IO/PolyAudioIO.ts +115 -0
  15. package/src/core/IO/index.ts +7 -61
  16. package/src/core/Note/frequencyTable.ts +144 -144
  17. package/src/core/Note/index.ts +49 -59
  18. package/src/core/Route.ts +79 -0
  19. package/src/core/Timing/Scheduler.ts +37 -0
  20. package/src/core/Timing/Time.ts +103 -0
  21. package/src/core/Timing/Transport.ts +104 -0
  22. package/src/core/Timing/index.ts +16 -0
  23. package/src/core/index.ts +36 -0
  24. package/src/core/midi/{ComputerKeyboardInput.ts → ComputerKeyboardDevice.ts} +31 -11
  25. package/src/core/midi/MidiDevice.ts +38 -31
  26. package/src/core/midi/MidiDeviceManager.ts +54 -55
  27. package/src/core/midi/MidiEvent.ts +36 -60
  28. package/src/core/module/Module.ts +233 -0
  29. package/src/core/module/PolyModule.ts +246 -0
  30. package/src/core/module/VoiceScheduler.ts +121 -0
  31. package/src/core/module/index.ts +3 -0
  32. package/src/core/schema.ts +41 -0
  33. package/src/index.ts +31 -9
  34. package/src/modules/BiquadFilter.ts +162 -0
  35. package/src/modules/Constant.ts +72 -0
  36. package/src/modules/Envelope.ts +178 -0
  37. package/src/modules/Filter.ts +109 -104
  38. package/src/modules/Gain.ts +78 -0
  39. package/src/modules/Inspector.ts +59 -0
  40. package/src/modules/Master.ts +18 -21
  41. package/src/modules/MidiSelector.ts +50 -50
  42. package/src/modules/Oscillator.ts +202 -148
  43. package/src/modules/Scale.ts +79 -0
  44. package/src/modules/StepSequencer.ts +61 -0
  45. package/src/modules/VirtualMidi.ts +33 -49
  46. package/src/modules/index.ts +159 -74
  47. package/src/nodePolyfill.ts +25 -0
  48. package/src/processors/filter-processor.ts +82 -0
  49. package/src/processors/index.ts +28 -0
  50. package/src/processors/scale-processor.ts +81 -0
  51. package/dist/build/Engine.d.ts +0 -82
  52. package/dist/build/core/IO/AudioNode.d.ts +0 -35
  53. package/dist/build/core/IO/Collection.d.ts +0 -13
  54. package/dist/build/core/IO/ForwardNode/Base.d.ts +0 -18
  55. package/dist/build/core/IO/ForwardNode/index.d.ts +0 -27
  56. package/dist/build/core/IO/ForwardNode.d.ts +0 -38
  57. package/dist/build/core/IO/MidiNode.d.ts +0 -30
  58. package/dist/build/core/IO/Node.d.ts +0 -40
  59. package/dist/build/core/IO/index.d.ts +0 -21
  60. package/dist/build/core/Module/MonoModule.d.ts +0 -67
  61. package/dist/build/core/Module/PolyModule.d.ts +0 -61
  62. package/dist/build/core/Module/index.d.ts +0 -6
  63. package/dist/build/core/Note/frequencyTable.d.ts +0 -4
  64. package/dist/build/core/Note/index.d.ts +0 -28
  65. package/dist/build/core/midi/ComputerKeyboardInput.d.ts +0 -11
  66. package/dist/build/core/midi/MidiDevice.d.ts +0 -29
  67. package/dist/build/core/midi/MidiDeviceManager.d.ts +0 -14
  68. package/dist/build/core/midi/MidiEvent.d.ts +0 -21
  69. package/dist/build/core/midi/index.d.ts +0 -5
  70. package/dist/build/index.d.ts +0 -9
  71. package/dist/build/modules/BitCrusher.d.ts +0 -16
  72. package/dist/build/modules/Delay.d.ts +0 -19
  73. package/dist/build/modules/Distortion.d.ts +0 -16
  74. package/dist/build/modules/Effect.d.ts +0 -19
  75. package/dist/build/modules/Envelope/AmpEnvelope.d.ts +0 -18
  76. package/dist/build/modules/Envelope/Base.d.ts +0 -66
  77. package/dist/build/modules/Envelope/FreqEnvelope.d.ts +0 -25
  78. package/dist/build/modules/Envelope/index.d.ts +0 -3
  79. package/dist/build/modules/Filter.d.ts +0 -42
  80. package/dist/build/modules/LFO.d.ts +0 -44
  81. package/dist/build/modules/Master.d.ts +0 -11
  82. package/dist/build/modules/MidiSelector.d.ts +0 -21
  83. package/dist/build/modules/Oscillator.d.ts +0 -55
  84. package/dist/build/modules/Reverb.d.ts +0 -19
  85. package/dist/build/modules/Sequencer.d.ts +0 -42
  86. package/dist/build/modules/VirtualMidi.d.ts +0 -32
  87. package/dist/build/modules/VoiceScheduler.d.ts +0 -44
  88. package/dist/build/modules/Volume.d.ts +0 -26
  89. package/dist/build/modules/index.d.ts +0 -17
  90. package/dist/build/routes.d.ts +0 -18
  91. package/dist/build/types.d.ts +0 -5
  92. package/dist/build/utils.d.ts +0 -1
  93. package/dist/main.cjs.js +0 -25
  94. package/dist/main.cjs.js.map +0 -1
  95. package/dist/main.esm.js +0 -25
  96. package/dist/main.esm.js.map +0 -1
  97. package/src/core/IO/AudioNode.ts +0 -82
  98. package/src/core/IO/ForwardNode/Base.ts +0 -99
  99. package/src/core/IO/ForwardNode/index.ts +0 -60
  100. package/src/core/IO/MidiNode.ts +0 -67
  101. package/src/core/IO/Node.ts +0 -118
  102. package/src/core/Module/MonoModule.ts +0 -219
  103. package/src/core/Module/PolyModule.ts +0 -218
  104. package/src/core/Module/index.ts +0 -15
  105. package/src/core/midi/index.ts +0 -5
  106. package/src/modules/BitCrusher.ts +0 -45
  107. package/src/modules/Delay.ts +0 -53
  108. package/src/modules/Distortion.ts +0 -45
  109. package/src/modules/Effect.ts +0 -46
  110. package/src/modules/Envelope/AmpEnvelope.ts +0 -23
  111. package/src/modules/Envelope/Base.ts +0 -176
  112. package/src/modules/Envelope/FreqEnvelope.ts +0 -64
  113. package/src/modules/Envelope/index.ts +0 -3
  114. package/src/modules/LFO.ts +0 -149
  115. package/src/modules/Reverb.ts +0 -53
  116. package/src/modules/Sequencer.ts +0 -178
  117. package/src/modules/VoiceScheduler.ts +0 -145
  118. package/src/modules/Volume.ts +0 -72
  119. package/src/routes.ts +0 -49
  120. package/src/types.ts +0 -3
  121. package/src/utils.ts +0 -18
@@ -0,0 +1,162 @@
1
+ import { IAnyAudioContext, Module } from "@/core";
2
+ import { IModuleConstructor } from "@/core/module/Module";
3
+ import { IPolyModuleConstructor, PolyModule } from "@/core/module/PolyModule";
4
+ import { PropSchema } from "@/core/schema";
5
+ import { createModule, ICreateModule, ModuleType } from ".";
6
+ import { MonoGain } from "./Gain";
7
+ import Scale from "./Scale";
8
+
9
+ export type IBiquadFilterProps = {
10
+ cutoff: number;
11
+ envelopeAmount: number;
12
+ type: BiquadFilterType;
13
+ Q: number;
14
+ };
15
+
16
+ const MIN_FREQ = 20;
17
+ const MAX_FREQ = 20000;
18
+
19
+ const DEFAULT_PROPS: IBiquadFilterProps = {
20
+ cutoff: MAX_FREQ,
21
+ envelopeAmount: 0,
22
+ type: "lowpass",
23
+ Q: 1,
24
+ };
25
+
26
+ export const biquadFilterPropSchema: PropSchema<IBiquadFilterProps> = {
27
+ cutoff: {
28
+ kind: "number",
29
+ min: MIN_FREQ,
30
+ max: MAX_FREQ,
31
+ step: 1,
32
+ label: "Cutoff",
33
+ },
34
+ envelopeAmount: {
35
+ kind: "number",
36
+ min: -1,
37
+ max: 1,
38
+ step: 0.01,
39
+ label: "Envelope Amount",
40
+ },
41
+ type: {
42
+ kind: "enum",
43
+ options: ["lowpass", "highpass", "bandpass"] satisfies BiquadFilterType[],
44
+ label: "Type",
45
+ },
46
+ Q: {
47
+ kind: "number",
48
+ min: -100,
49
+ max: 100,
50
+ step: 0.1,
51
+ label: "Q",
52
+ },
53
+ };
54
+
55
+ class MonoBiquadFilter extends Module<ModuleType.BiquadFilter> {
56
+ declare audioNode: BiquadFilterNode;
57
+ private scale: Scale;
58
+ private amount: MonoGain;
59
+
60
+ constructor(
61
+ engineId: string,
62
+ params: ICreateModule<ModuleType.BiquadFilter>,
63
+ ) {
64
+ const props = { ...DEFAULT_PROPS, ...params.props };
65
+
66
+ const audioNodeConstructor = (context: IAnyAudioContext) =>
67
+ new BiquadFilterNode(context, {
68
+ type: props.type,
69
+ frequency: props.cutoff,
70
+ Q: props.Q,
71
+ });
72
+
73
+ super(engineId, {
74
+ ...params,
75
+ props,
76
+ audioNodeConstructor,
77
+ });
78
+
79
+ this.amount = new MonoGain(engineId, {
80
+ name: "amount",
81
+ moduleType: ModuleType.Gain,
82
+ props: { gain: props.envelopeAmount },
83
+ });
84
+
85
+ this.scale = createModule(engineId, {
86
+ name: "scale",
87
+ moduleType: ModuleType.Scale,
88
+ props: { min: MIN_FREQ, max: MAX_FREQ, current: this.props.cutoff },
89
+ }) as Scale;
90
+
91
+ this.amount.plug({ audioModule: this.scale, from: "out", to: "in" });
92
+ this.scale.audioNode.connect(this.audioNode.frequency);
93
+
94
+ this.registerDefaultIOs();
95
+ this.registerInputs();
96
+ }
97
+
98
+ protected onSetType(value: IBiquadFilterProps["type"]) {
99
+ this.audioNode.type = value;
100
+ }
101
+
102
+ protected onSetCutoff(value: IBiquadFilterProps["cutoff"]) {
103
+ if (!this.superInitialized) return;
104
+
105
+ this.scale.props = { current: value };
106
+ }
107
+
108
+ protected onSetQ(value: IBiquadFilterProps["Q"]) {
109
+ this.audioNode.Q.value = value;
110
+ }
111
+
112
+ protected onSetEnvelopeAmount(value: IBiquadFilterProps["envelopeAmount"]) {
113
+ if (!this.superInitialized) return;
114
+
115
+ this.amount.props = { gain: value };
116
+ }
117
+
118
+ private registerInputs() {
119
+ this.registerAudioInput({
120
+ name: "cutoff",
121
+ getAudioNode: () => this.audioNode.frequency,
122
+ });
123
+
124
+ this.registerAudioInput({
125
+ name: "cutoffMod",
126
+ getAudioNode: () => this.amount.audioNode,
127
+ });
128
+
129
+ this.registerAudioInput({
130
+ name: "Q",
131
+ getAudioNode: () => this.audioNode.Q,
132
+ });
133
+ }
134
+ }
135
+
136
+ export default class BiquadFilter extends PolyModule<ModuleType.BiquadFilter> {
137
+ constructor(
138
+ engineId: string,
139
+ params: IPolyModuleConstructor<ModuleType.BiquadFilter>,
140
+ ) {
141
+ const props = { ...DEFAULT_PROPS, ...params.props };
142
+ const monoModuleConstructor = (
143
+ engineId: string,
144
+ params: IModuleConstructor<ModuleType.BiquadFilter>,
145
+ ) => new MonoBiquadFilter(engineId, params);
146
+
147
+ super(engineId, {
148
+ ...params,
149
+ props,
150
+ monoModuleConstructor,
151
+ });
152
+
153
+ this.registerInputs();
154
+ this.registerDefaultIOs();
155
+ }
156
+
157
+ private registerInputs() {
158
+ this.registerAudioInput({ name: "cutoff" });
159
+ this.registerAudioInput({ name: "cutoffMod" });
160
+ this.registerAudioInput({ name: "Q" });
161
+ }
162
+ }
@@ -0,0 +1,72 @@
1
+ import { IAnyAudioContext, IModule, Module } from "@/core";
2
+ import Note from "@/core/Note";
3
+ import { nt, TTime } from "@/core/Timing/Time";
4
+ import { PropSchema } from "@/core/schema";
5
+ import { ICreateModule, ModuleType } from ".";
6
+
7
+ export type IConstant = IModule<ModuleType.Constant>;
8
+ export type IConstantProps = {
9
+ value: number;
10
+ };
11
+
12
+ export const constantPropSchema: PropSchema<IConstantProps> = {
13
+ value: {
14
+ kind: "number",
15
+ min: -Infinity,
16
+ max: Infinity,
17
+ step: 0.01,
18
+ label: "Value",
19
+ },
20
+ };
21
+
22
+ const DEFAULT_PROPS: IConstantProps = { value: 1 };
23
+
24
+ export default class Constant extends Module<ModuleType.Constant> {
25
+ declare audioNode: ConstantSourceNode;
26
+ isStated = false;
27
+
28
+ constructor(engineId: string, params: ICreateModule<ModuleType.Constant>) {
29
+ const props = { ...DEFAULT_PROPS, ...params.props };
30
+ const audioNodeConstructor = (context: IAnyAudioContext) =>
31
+ new ConstantSourceNode(context);
32
+
33
+ super(engineId, {
34
+ ...params,
35
+ props,
36
+ audioNodeConstructor,
37
+ });
38
+
39
+ this.registerDefaultIOs("out");
40
+ }
41
+
42
+ protected onSetValue(value: IConstantProps["value"]) {
43
+ this.audioNode.offset.value = value;
44
+ }
45
+
46
+ start(time: TTime) {
47
+ if (this.isStated) return;
48
+
49
+ this.isStated = true;
50
+ this.audioNode.start(nt(time));
51
+ }
52
+
53
+ stop(time: TTime) {
54
+ this.audioNode.stop(nt(time));
55
+ this.rePlugAll(() => {
56
+ this.audioNode = new ConstantSourceNode(this.context, {
57
+ offset: this.props.value,
58
+ });
59
+ });
60
+
61
+ this.isStated = false;
62
+ }
63
+
64
+ triggerAttack = (note: Note, triggeredAt: TTime) => {
65
+ this.audioNode.offset.setValueAtTime(note.frequency, nt(triggeredAt));
66
+ this.start(triggeredAt);
67
+ };
68
+
69
+ triggerRelease = () => {
70
+ // Do nothing
71
+ };
72
+ }
@@ -0,0 +1,178 @@
1
+ import { createScaleNormalized } from "@blibliki/utils";
2
+ import { IAnyAudioContext, Module } from "@/core";
3
+ import Note from "@/core/Note";
4
+ import { nt, TTime } from "@/core/Timing/Time";
5
+ import { IModuleConstructor } from "@/core/module/Module";
6
+ import { IPolyModuleConstructor, PolyModule } from "@/core/module/PolyModule";
7
+ import { PropSchema } from "@/core/schema";
8
+ import { ICreateModule, ModuleType } from ".";
9
+
10
+ export type IEnvelopeProps = {
11
+ attack: number;
12
+ decay: number;
13
+ sustain: number;
14
+ release: number;
15
+ };
16
+
17
+ const DEFAULT_PROPS: IEnvelopeProps = {
18
+ attack: 0.1,
19
+ decay: 0.2,
20
+ sustain: 0,
21
+ release: 0.3,
22
+ };
23
+
24
+ export const envelopePropSchema: PropSchema<IEnvelopeProps> = {
25
+ attack: {
26
+ kind: "number",
27
+ min: 0.0001,
28
+ max: 1,
29
+ step: 0.01,
30
+ label: "Attack",
31
+ },
32
+ decay: {
33
+ kind: "number",
34
+ min: 0,
35
+ max: 1,
36
+ step: 0.01,
37
+ label: "Decay",
38
+ },
39
+ sustain: {
40
+ kind: "number",
41
+ min: 0,
42
+ max: 1,
43
+ step: 0.01,
44
+ label: "Sustain",
45
+ },
46
+ release: {
47
+ kind: "number",
48
+ min: 0,
49
+ max: 1,
50
+ step: 0.01,
51
+ label: "Release",
52
+ },
53
+ };
54
+
55
+ const scaleToTen = createScaleNormalized({
56
+ min: 0.001,
57
+ max: 10,
58
+ });
59
+
60
+ const scaleToFive = createScaleNormalized({
61
+ min: 0.001,
62
+ max: 5,
63
+ });
64
+
65
+ class MonoEnvelope extends Module<ModuleType.Envelope> {
66
+ declare audioNode: GainNode;
67
+
68
+ constructor(engineId: string, params: ICreateModule<ModuleType.Envelope>) {
69
+ const props = { ...DEFAULT_PROPS, ...params.props };
70
+ const audioNodeConstructor = (context: IAnyAudioContext) => {
71
+ const audioNode = new GainNode(context);
72
+ audioNode.gain.value = 0;
73
+ return audioNode;
74
+ };
75
+
76
+ super(engineId, {
77
+ ...params,
78
+ props,
79
+ audioNodeConstructor,
80
+ });
81
+
82
+ this.registerDefaultIOs();
83
+ }
84
+
85
+ triggerAttack(note: Note, triggeredAt: TTime) {
86
+ super.triggerAttack(note, triggeredAt);
87
+
88
+ const attack = this.scaledAttack();
89
+ const decay = this.scaledDecay();
90
+ const sustain = this.props.sustain;
91
+ const triggeredAtNum = nt(triggeredAt);
92
+
93
+ this.audioNode.gain.cancelAndHoldAtTime(triggeredAtNum);
94
+
95
+ // Always start from a tiny value, can't ramp from 0
96
+ if (this.audioNode.gain.value === 0) {
97
+ this.audioNode.gain.setValueAtTime(0.001, triggeredAtNum);
98
+ }
99
+
100
+ // Attack
101
+ this.audioNode.gain.exponentialRampToValueAtTime(
102
+ 1.0,
103
+ triggeredAtNum + attack,
104
+ );
105
+
106
+ // Decay
107
+ if (sustain > 0) {
108
+ this.audioNode.gain.exponentialRampToValueAtTime(
109
+ sustain,
110
+ triggeredAtNum + attack + decay,
111
+ );
112
+ // Do not set to zero or anything else!
113
+ } else {
114
+ this.audioNode.gain.exponentialRampToValueAtTime(
115
+ 0.001,
116
+ triggeredAtNum + attack + decay,
117
+ );
118
+ }
119
+ }
120
+
121
+ triggerRelease(note: Note, triggeredAt: TTime) {
122
+ super.triggerRelease(note, triggeredAt);
123
+ if (this.activeNotes.length > 0) return;
124
+
125
+ const release = this.scaledRelease();
126
+ const triggeredAtNum = nt(triggeredAt);
127
+
128
+ // Cancel scheduled automations and set gain to the ACTUAL value at this moment
129
+ this.audioNode.gain.cancelAndHoldAtTime(triggeredAtNum);
130
+ const currentGainValue = this.audioNode.gain.value;
131
+
132
+ if (currentGainValue >= 0.0001) {
133
+ // Always set the value at the release time to ensure a smooth ramp from here
134
+ this.audioNode.gain.setValueAtTime(currentGainValue, triggeredAtNum);
135
+ // Exponential ramp to a tiny value
136
+ this.audioNode.gain.exponentialRampToValueAtTime(
137
+ 0.0001,
138
+ triggeredAtNum + release - 0.0001,
139
+ );
140
+ }
141
+
142
+ // Set to zero at the very end
143
+ this.audioNode.gain.setValueAtTime(0, triggeredAtNum + release);
144
+ }
145
+
146
+ private scaledAttack() {
147
+ return scaleToTen(this.props.attack);
148
+ }
149
+
150
+ private scaledDecay() {
151
+ return scaleToFive(this.props.decay);
152
+ }
153
+
154
+ private scaledRelease() {
155
+ return scaleToTen(this.props.release);
156
+ }
157
+ }
158
+
159
+ export default class Envelope extends PolyModule<ModuleType.Envelope> {
160
+ constructor(
161
+ engineId: string,
162
+ params: IPolyModuleConstructor<ModuleType.Envelope>,
163
+ ) {
164
+ const props = { ...DEFAULT_PROPS, ...params.props };
165
+ const monoModuleConstructor = (
166
+ engineId: string,
167
+ params: IModuleConstructor<ModuleType.Envelope>,
168
+ ) => new MonoEnvelope(engineId, params);
169
+
170
+ super(engineId, {
171
+ ...params,
172
+ props,
173
+ monoModuleConstructor,
174
+ });
175
+
176
+ this.registerDefaultIOs();
177
+ }
178
+ }
@@ -1,148 +1,153 @@
1
- import { Filter as InternalFilter, FilterRollOff, Add, Multiply } from "tone";
2
- import Module, { PolyModule } from "../core/Module";
3
-
4
- interface FilterInterface {
1
+ import { IAnyAudioContext, Module } from "@/core";
2
+ import { IModuleConstructor } from "@/core/module/Module";
3
+ import { IPolyModuleConstructor, PolyModule } from "@/core/module/PolyModule";
4
+ import { PropSchema } from "@/core/schema";
5
+ import { CustomWorklet, newAudioWorklet } from "@/processors";
6
+ import { createModule, ICreateModule, ModuleType } from ".";
7
+ import { MonoGain } from "./Gain";
8
+ import Scale from "./Scale";
9
+
10
+ export type IFilterProps = {
5
11
  cutoff: number;
6
- filterType: BiquadFilterType;
7
- resonance: number;
8
- slope: FilterRollOff;
9
12
  envelopeAmount: number;
10
- }
11
-
12
- type FilterProps = Partial<FilterInterface>;
13
+ resonance: number;
14
+ };
13
15
 
14
- const MAX_FREQ = 20000;
16
+ const MIN_FREQ = 20;
17
+ const MAX_FREQ = 22050;
15
18
 
16
- const InitialProps: FilterInterface = {
19
+ const DEFAULT_PROPS: IFilterProps = {
17
20
  cutoff: MAX_FREQ,
18
- resonance: 0,
19
21
  envelopeAmount: 0,
20
- slope: -24,
21
- filterType: "lowpass",
22
+ resonance: 0,
22
23
  };
23
24
 
24
- class MonoFilter extends Module<InternalFilter, FilterInterface> {
25
- private _cutoff: Add;
26
- private _amount: Multiply;
27
-
28
- constructor(params: { id?: string; name: string; props: FilterProps }) {
29
- const { id, name, props } = params;
30
-
31
- super(new InternalFilter({ type: "lowpass" }), {
32
- id,
33
- name,
34
- props: { ...InitialProps, ...props },
35
- });
36
-
37
- this._cutoff = new Add();
38
- this._cutoff.connect(this.internalModule.frequency);
39
-
40
- this._amount = new Multiply();
41
- this._amount.connect(this._cutoff);
42
- this.updateAmountFactor();
25
+ export const filterPropSchema: PropSchema<IFilterProps> = {
26
+ cutoff: {
27
+ kind: "number",
28
+ min: MIN_FREQ,
29
+ max: MAX_FREQ,
30
+ step: 0.0001,
31
+ label: "Cutoff",
32
+ },
33
+ envelopeAmount: {
34
+ kind: "number",
35
+ min: -1,
36
+ max: 1,
37
+ step: 0.01,
38
+ label: "Envelope Amount",
39
+ },
40
+ resonance: {
41
+ kind: "number",
42
+ min: 0,
43
+ max: 4,
44
+ step: 0.01,
45
+ label: "resonance",
46
+ },
47
+ };
43
48
 
44
- this.registerBasicInputs();
45
- this.registerOutputs();
46
- }
49
+ class MonoFilter extends Module<ModuleType.Filter> {
50
+ declare audioNode: AudioWorkletNode;
51
+ private scale: Scale;
52
+ private amount: MonoGain;
47
53
 
48
- get cutoff() {
49
- return this._props["cutoff"];
50
- }
54
+ constructor(engineId: string, params: ICreateModule<ModuleType.Filter>) {
55
+ const props = { ...DEFAULT_PROPS, ...params.props };
51
56
 
52
- set cutoff(value: number) {
53
- if (this._cutoff) {
54
- this._cutoff.addend.value = value;
55
- }
57
+ const audioNodeConstructor = (context: IAnyAudioContext) =>
58
+ newAudioWorklet(context, CustomWorklet.FilterProcessor);
56
59
 
57
- this._props = { ...this.props, cutoff: value };
58
- this.updateAmountFactor();
59
- }
60
+ super(engineId, {
61
+ ...params,
62
+ props,
63
+ audioNodeConstructor,
64
+ });
60
65
 
61
- get filterType() {
62
- return this._props["filterType"];
63
- }
66
+ this.amount = new MonoGain(engineId, {
67
+ name: "amount",
68
+ moduleType: ModuleType.Gain,
69
+ props: { gain: props.envelopeAmount },
70
+ });
64
71
 
65
- set filterType(value: BiquadFilterType) {
66
- this._props = { ...this.props, filterType: value };
67
- this.internalModule.type = value;
68
- }
72
+ this.scale = createModule(engineId, {
73
+ name: "scale",
74
+ moduleType: ModuleType.Scale,
75
+ props: { min: MIN_FREQ, max: MAX_FREQ, current: this.props.cutoff },
76
+ }) as Scale;
69
77
 
70
- get slope() {
71
- return this._props["slope"];
72
- }
78
+ this.amount.plug({ audioModule: this.scale, from: "out", to: "in" });
79
+ this.scale.audioNode.connect(this.cutoff);
73
80
 
74
- set slope(value: FilterRollOff) {
75
- this._props = { ...this.props, slope: value };
76
- this.internalModule.rolloff = value;
81
+ this.registerDefaultIOs();
82
+ this.registerInputs();
77
83
  }
78
84
 
79
- get frequency() {
80
- return this.internalModule.frequency;
85
+ get cutoff() {
86
+ return this.audioNode.parameters.get("cutoff")!;
81
87
  }
82
88
 
83
89
  get resonance() {
84
- return this._props["resonance"];
90
+ return this.audioNode.parameters.get("resonance")!;
85
91
  }
86
92
 
87
- set resonance(value: number) {
88
- this._props = { ...this.props, resonance: value };
93
+ protected onSetCutoff(value: IFilterProps["cutoff"]) {
94
+ if (!this.superInitialized) return;
89
95
 
90
- this.internalModule.Q.value = value;
96
+ this.scale.props = { current: value };
91
97
  }
92
98
 
93
- get envelopeAmount() {
94
- return this._props["envelopeAmount"];
99
+ protected onSetResonance(value: IFilterProps["resonance"]) {
100
+ this.resonance.value = value;
95
101
  }
96
102
 
97
- set envelopeAmount(value: number) {
98
- this._props = { ...this.props, envelopeAmount: value };
99
- this.updateAmountFactor();
100
- }
103
+ protected onSetEnvelopeAmount(value: IFilterProps["envelopeAmount"]) {
104
+ if (!this.superInitialized) return;
101
105
 
102
- private registerOutputs() {
103
- this.registerBasicOutputs();
106
+ this.amount.props = { gain: value };
107
+ }
104
108
 
109
+ private registerInputs() {
105
110
  this.registerAudioInput({
106
111
  name: "cutoff",
107
- internalModule: this._amount,
112
+ getAudioNode: () => this.scale.audioNode,
108
113
  });
109
- }
110
-
111
- private updateAmountFactor() {
112
- if (!this._amount) return;
113
114
 
114
- const value =
115
- this.envelopeAmount > 0
116
- ? this.envelopeAmount * Math.abs(MAX_FREQ - this.cutoff)
117
- : this.envelopeAmount * Math.abs(1 - this.cutoff);
115
+ this.registerAudioInput({
116
+ name: "cutoffMod",
117
+ getAudioNode: () => this.amount.audioNode,
118
+ });
118
119
 
119
- this._amount.factor.value = value;
120
+ this.registerAudioInput({
121
+ name: "Q",
122
+ getAudioNode: () => this.resonance,
123
+ });
120
124
  }
121
125
  }
122
126
 
123
- export default class Filter extends PolyModule<MonoFilter, FilterInterface> {
124
- static moduleName = "Filter";
125
-
126
- constructor(params: {
127
- id?: string;
128
- name: string;
129
- props: Partial<FilterInterface>;
130
- }) {
131
- const { id, name, props } = params;
132
-
133
- super({
134
- id,
135
- name,
136
- child: MonoFilter,
137
- props: { ...InitialProps, ...props },
127
+ export default class Filter extends PolyModule<ModuleType.Filter> {
128
+ constructor(
129
+ engineId: string,
130
+ params: IPolyModuleConstructor<ModuleType.Filter>,
131
+ ) {
132
+ const props = { ...DEFAULT_PROPS, ...params.props };
133
+ const monoModuleConstructor = (
134
+ engineId: string,
135
+ params: IModuleConstructor<ModuleType.Filter>,
136
+ ) => new MonoFilter(engineId, params);
137
+
138
+ super(engineId, {
139
+ ...params,
140
+ props,
141
+ monoModuleConstructor,
138
142
  });
139
143
 
140
- this.registerBasicInputs();
141
- this.registerOutputs();
144
+ this.registerInputs();
145
+ this.registerDefaultIOs();
142
146
  }
143
147
 
144
- private registerOutputs() {
145
- this.registerBasicOutputs();
146
- this.registerForwardAudioInput({ name: "cutoff" });
148
+ private registerInputs() {
149
+ this.registerAudioInput({ name: "cutoff" });
150
+ this.registerAudioInput({ name: "cutoffMod" });
151
+ this.registerAudioInput({ name: "Q" });
147
152
  }
148
153
  }