@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
@@ -1,213 +1,267 @@
1
- import { Multiply, now, Oscillator as Osc, ToneOscillatorType } from "tone";
2
- import Engine from "../Engine";
3
-
4
- import Note from "../core/Note";
5
- import Module, { PolyModule, Startable } from "../core/Module";
6
-
7
- export type TWave = "sine" | "triangle" | "square" | "sawtooth";
1
+ import { dbToGain } from "@blibliki/utils";
2
+ import { IAnyAudioContext, IModule, 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
+ const LOW_GAIN = -18;
11
+
12
+ export type IOscillator = IModule<ModuleType.Oscillator>;
13
+
14
+ export enum OscillatorWave {
15
+ sine = "sine",
16
+ triangle = "triangle",
17
+ square = "square",
18
+ sawtooth = "sawtooth",
19
+ }
8
20
 
9
- export interface OscillatorInterface {
10
- noteName: string;
21
+ /**
22
+ * Props for the Oscillator module.
23
+ *
24
+ * @property wave - Waveform shape of the oscillator.
25
+ * One of: "sine", "square", "sawtooth", "triangle", or "custom".
26
+ * @property frequency - Base frequency in Hz (e.g. 440 for A4).
27
+ * @property fine - Fine tuning factor in the range [-1, 1], where ±1 represents ±1 semitone.
28
+ * @property coarse - Coarse tuning factor in the range [-1, 1], scaled to ±12 semitones.
29
+ * @property octave - Octave transposition value (e.g. +1 for one octave up, -2 for two octaves down).
30
+ * @property lowGain - Whether to gain reduction (-18dB). When false, oscillator runs at full gain.
31
+ */
32
+ export type IOscillatorProps = {
33
+ wave: OscillatorWave;
34
+ frequency: number;
11
35
  fine: number;
12
36
  coarse: number;
13
- wave: TWave;
14
- volume: number;
15
- range: number;
16
- }
37
+ octave: number;
38
+ lowGain: boolean;
39
+ };
40
+
41
+ export const oscillatorPropSchema: PropSchema<IOscillatorProps> = {
42
+ wave: {
43
+ kind: "enum",
44
+ options: Object.values(OscillatorWave),
45
+ label: "Waveform",
46
+ },
47
+ frequency: {
48
+ kind: "number",
49
+ min: 0,
50
+ max: 25000,
51
+ step: 1,
52
+ label: "Frequency",
53
+ },
54
+ fine: {
55
+ kind: "number",
56
+ min: -1,
57
+ max: 1,
58
+ step: 0.01,
59
+ label: "Fine",
60
+ },
61
+ coarse: {
62
+ kind: "number",
63
+ min: -12,
64
+ max: 12,
65
+ step: 1,
66
+ label: "Coarse",
67
+ },
68
+ octave: {
69
+ kind: "number",
70
+ min: -4,
71
+ max: 4,
72
+ step: 1,
73
+ label: "Octave",
74
+ },
75
+ lowGain: {
76
+ kind: "boolean",
77
+ label: `Use ${LOW_GAIN}db Gain`,
78
+ },
79
+ };
17
80
 
18
- const InitialProps: OscillatorInterface = {
19
- noteName: "C3",
81
+ const DEFAULT_PROPS: IOscillatorProps = {
82
+ wave: OscillatorWave.sine,
83
+ frequency: 440,
20
84
  fine: 0,
21
85
  coarse: 0,
22
- wave: "sine",
23
- range: 0,
24
- volume: 0,
86
+ octave: 0,
87
+ lowGain: false,
25
88
  };
26
89
 
27
- class MonoOscillator
28
- extends Module<Osc, OscillatorInterface>
29
- implements Startable
30
- {
31
- private _note: Note;
32
- private _fineSignal: Multiply;
33
-
34
- constructor(params: {
35
- id?: string;
36
- name: string;
37
- props: Partial<OscillatorInterface>;
38
- }) {
39
- const { id, name, props } = params;
40
-
41
- super(new Osc(), {
42
- id,
43
- name,
44
- props: { ...InitialProps, ...props },
90
+ export class MonoOscillator extends Module<ModuleType.Oscillator> {
91
+ declare audioNode: OscillatorNode;
92
+ isStated = false;
93
+ lowOutputGain: GainNode;
94
+ detuneGain!: GainNode;
95
+
96
+ constructor(engineId: string, params: ICreateModule<ModuleType.Oscillator>) {
97
+ const props = { ...DEFAULT_PROPS, ...params.props };
98
+ const audioNodeConstructor = (context: IAnyAudioContext) =>
99
+ new OscillatorNode(context);
100
+
101
+ super(engineId, {
102
+ ...params,
103
+ props,
104
+ audioNodeConstructor,
105
+ });
106
+
107
+ this.lowOutputGain = new GainNode(this.context, {
108
+ gain: dbToGain(LOW_GAIN),
45
109
  });
46
110
 
111
+ this.applyOutputGain();
112
+ this.initializeGainDetune();
47
113
  this.registerInputs();
48
- this.registerBasicOutputs();
49
- this.start(now());
114
+ this.registerOutputs();
50
115
  }
51
116
 
52
- get note(): Note {
53
- return this._note;
117
+ protected onAfterSetWave(value: OscillatorWave) {
118
+ this.audioNode.type = value;
54
119
  }
55
120
 
56
- setNoteAt(value: Note | string, time: number) {
57
- this._note = this.getNote(value);
58
- this.updateFrequency(time);
121
+ protected onAfterSetFrequency() {
122
+ this.updateFrequency();
59
123
  }
60
124
 
61
- set note(value: Note | string) {
62
- this._note = this.getNote(value);
125
+ protected onAfterSetFine() {
63
126
  this.updateFrequency();
64
127
  }
65
128
 
66
- get noteName() {
67
- return this._props["noteName"];
129
+ protected onAfterSetCoarse() {
130
+ this.updateFrequency();
68
131
  }
69
132
 
70
- set noteName(value: string) {
71
- this._props = { ...this.props, noteName: value };
72
- this.note = new Note(this.noteName);
133
+ protected onAfterSetOctave() {
134
+ this.updateFrequency();
73
135
  }
74
136
 
75
- get fineSingal() {
76
- if (this._fineSignal) return this._fineSignal;
137
+ protected onAfterSetLowGain() {
138
+ if (!this.superInitialized) return;
77
139
 
78
- this._fineSignal = new Multiply(100);
79
- this._fineSignal.connect(this.internalModule.detune);
80
-
81
- return this._fineSignal;
82
- }
83
-
84
- get fine() {
85
- return this._props["fine"];
140
+ this.rePlugAll();
86
141
  }
87
142
 
88
- set fine(value: number) {
89
- this._props = { ...this.props, fine: value };
143
+ start(time: TTime) {
144
+ if (this.isStated) return;
90
145
 
91
- this.fineSingal.value = this.fine;
146
+ this.isStated = true;
147
+ this.audioNode.start(nt(time));
92
148
  }
93
149
 
94
- get coarse() {
95
- return this._props["coarse"];
96
- }
97
-
98
- set coarse(value: number) {
99
- this._props = { ...this.props, coarse: Math.floor(value) };
150
+ stop(time: TTime) {
151
+ this.audioNode.stop(nt(time));
152
+ this.rePlugAll(() => {
153
+ this.audioNode = new OscillatorNode(this.context, {
154
+ type: this.props.wave,
155
+ frequency: this.finalFrequency,
156
+ });
157
+ this.applyOutputGain();
158
+ this.detuneGain.connect(this.audioNode.detune);
159
+ });
100
160
 
101
- this.updateFrequency();
161
+ this.isStated = false;
102
162
  }
103
163
 
104
- get wave() {
105
- return this._props["wave"];
106
- }
164
+ triggerAttack = (note: Note, triggeredAt: TTime) => {
165
+ super.triggerAttack(note, triggeredAt);
107
166
 
108
- set wave(value: TWave) {
109
- this._props = { ...this.props, wave: value };
110
- this.internalModule.type = this.wave as ToneOscillatorType;
111
- }
167
+ this.props = { frequency: note.frequency };
168
+ this.updateFrequency(triggeredAt);
169
+ this.start(triggeredAt);
170
+ };
112
171
 
113
- get volume() {
114
- return this._props["volume"];
115
- }
172
+ triggerRelease(note: Note, triggeredAt: TTime) {
173
+ super.triggerRelease(note, triggeredAt);
116
174
 
117
- set volume(value: number) {
118
- this._props = { ...this.props, volume: value };
175
+ const lastNote = this.activeNotes.length
176
+ ? this.activeNotes[this.activeNotes.length - 1]
177
+ : null;
178
+ if (!lastNote) return;
119
179
 
120
- this.internalModule.volume.value = this.volume;
180
+ this.props = { frequency: lastNote.frequency };
181
+ this.updateFrequency(triggeredAt);
121
182
  }
122
183
 
123
- get range() {
124
- return this._props["range"];
125
- }
184
+ private get finalFrequency(): number | undefined {
185
+ const { frequency, coarse, octave, fine } = this.props;
186
+ if (!this.superInitialized) return;
126
187
 
127
- set range(value: number) {
128
- this._props = { ...this.props, range: value };
129
- this.updateFrequency();
188
+ const transposed =
189
+ frequency * Math.pow(2, coarse / 12 + octave + fine / 12);
190
+ return transposed;
130
191
  }
131
192
 
132
- start(time: number) {
133
- if (!Engine.isStarted) return;
134
-
135
- const oscState = this.internalModule.state;
193
+ private updateFrequency(actionAt?: TTime) {
194
+ if (this.finalFrequency === undefined) return;
136
195
 
137
- if (oscState === "started") {
138
- this.internalModule.restart(time);
196
+ if (actionAt) {
197
+ this.audioNode.frequency.setValueAtTime(
198
+ this.finalFrequency,
199
+ nt(actionAt),
200
+ );
139
201
  } else {
140
- this.internalModule.start(time);
202
+ this.audioNode.frequency.value = this.finalFrequency;
141
203
  }
142
204
  }
143
205
 
144
- stop(time?: number) {
145
- this.internalModule.stop(time);
206
+ private applyOutputGain() {
207
+ this.audioNode.connect(this.lowOutputGain);
146
208
  }
147
209
 
148
- triggerAttack = (note: Note, triggeredAt: number) => {
149
- this.setNoteAt(note, triggeredAt);
150
- this.start(triggeredAt);
151
- };
152
-
153
- triggerRelease = () => {
154
- // Do nothing
155
- };
156
-
157
- private updateFrequency(time?: number) {
158
- if (!this.note) return;
159
-
160
- const freq = this.note.adjustFrequency(this.range, this.coarse);
161
-
162
- if (time) {
163
- this.internalModule.frequency.linearRampToValueAtTime(freq, time);
164
- } else {
165
- this.internalModule.frequency.value = freq;
166
- }
167
- }
168
-
169
- private getNote(note: Note | string): Note {
170
- return note instanceof Note ? note : new Note(note);
210
+ private initializeGainDetune() {
211
+ this.detuneGain = new GainNode(this.context, { gain: 100 });
212
+ this.detuneGain.connect(this.audioNode.detune);
171
213
  }
172
214
 
173
215
  private registerInputs() {
174
216
  this.registerAudioInput({
175
- name: "fine",
176
- internalModule: this.fineSingal,
217
+ name: "detune",
218
+ getAudioNode: () => this.detuneGain,
219
+ });
220
+ }
221
+
222
+ private registerOutputs() {
223
+ this.registerAudioOutput({
224
+ name: "out",
225
+ getAudioNode: () =>
226
+ this.props.lowGain ? this.lowOutputGain : this.audioNode,
177
227
  });
178
228
  }
179
229
  }
180
230
 
181
- export default class Oscillator extends PolyModule<
182
- MonoOscillator,
183
- OscillatorInterface
184
- > {
185
- static moduleName = "Oscillator";
186
-
187
- constructor(params: {
188
- id?: string;
189
- name: string;
190
- props: Partial<OscillatorInterface>;
191
- }) {
192
- const { id, name, props } = params;
193
-
194
- super({
195
- id,
196
- name,
197
- child: MonoOscillator,
198
- props: { ...InitialProps, ...props },
231
+ export default class Oscillator extends PolyModule<ModuleType.Oscillator> {
232
+ constructor(
233
+ engineId: string,
234
+ params: IPolyModuleConstructor<ModuleType.Oscillator>,
235
+ ) {
236
+ const props = { ...DEFAULT_PROPS, ...params.props };
237
+ const monoModuleConstructor = (
238
+ engineId: string,
239
+ params: IModuleConstructor<ModuleType.Oscillator>,
240
+ ) => new MonoOscillator(engineId, params);
241
+
242
+ super(engineId, {
243
+ ...params,
244
+ props,
245
+ monoModuleConstructor,
199
246
  });
200
247
 
201
- this.registerBasicOutputs();
202
- this.registerMidiIn();
203
- this.registerForwardAudioInput({ name: "fine" });
248
+ this.registerInputs();
249
+ this.registerDefaultIOs("out");
204
250
  }
205
251
 
206
- start(time: number) {
207
- this.audioModules.forEach((audioModule) => audioModule.start(time));
252
+ start(time: TTime) {
253
+ this.audioModules.forEach((audioModule) => {
254
+ audioModule.start(time);
255
+ });
208
256
  }
209
257
 
210
- stop(time?: number) {
211
- this.audioModules.forEach((audioModule) => audioModule.stop(time));
258
+ stop(time: TTime) {
259
+ this.audioModules.forEach((audioModule) => {
260
+ audioModule.stop(time);
261
+ });
262
+ }
263
+
264
+ private registerInputs() {
265
+ this.registerAudioInput({ name: "detune" });
212
266
  }
213
267
  }
@@ -0,0 +1,79 @@
1
+ import { IAnyAudioContext, IModule, Module } from "@/core";
2
+ import { PropSchema } from "@/core/schema";
3
+ import { CustomWorklet, newAudioWorklet } from "@/processors";
4
+ import { ICreateModule, ModuleType } from ".";
5
+
6
+ export type IScale = IModule<ModuleType.Scale>;
7
+ export type IScaleProps = {
8
+ min: number;
9
+ max: number;
10
+ current: number;
11
+ };
12
+
13
+ export const scalePropSchema: PropSchema<IScaleProps> = {
14
+ min: {
15
+ kind: "number",
16
+ min: -Infinity,
17
+ max: Infinity,
18
+ step: 0.01,
19
+ label: "Min",
20
+ },
21
+ max: {
22
+ kind: "number",
23
+ min: -Infinity,
24
+ max: Infinity,
25
+ step: 0.01,
26
+ label: "Max",
27
+ },
28
+ current: {
29
+ kind: "number",
30
+ min: -Infinity,
31
+ max: Infinity,
32
+ step: 0.01,
33
+ label: "Current",
34
+ },
35
+ };
36
+
37
+ const DEFAULT_PROPS: IScaleProps = { min: 0, max: 1, current: 0.5 };
38
+
39
+ export default class Scale extends Module<ModuleType.Scale> {
40
+ declare audioNode: AudioWorkletNode;
41
+
42
+ constructor(engineId: string, params: ICreateModule<ModuleType.Scale>) {
43
+ const props = { ...DEFAULT_PROPS, ...params.props };
44
+ const audioNodeConstructor = (context: IAnyAudioContext) =>
45
+ newAudioWorklet(context, CustomWorklet.ScaleProcessor);
46
+
47
+ super(engineId, {
48
+ ...params,
49
+ props,
50
+ audioNodeConstructor,
51
+ });
52
+
53
+ this.registerDefaultIOs();
54
+ }
55
+
56
+ get current() {
57
+ return this.audioNode.parameters.get("current")!;
58
+ }
59
+
60
+ get min() {
61
+ return this.audioNode.parameters.get("min")!;
62
+ }
63
+
64
+ get max() {
65
+ return this.audioNode.parameters.get("max")!;
66
+ }
67
+
68
+ protected onSetMin(value: number) {
69
+ this.min.value = value;
70
+ }
71
+
72
+ protected onSetMax(value: number) {
73
+ this.max.value = value;
74
+ }
75
+
76
+ protected onSetCurrent(value: number) {
77
+ this.current.value = value;
78
+ }
79
+ }
@@ -0,0 +1,61 @@
1
+ import { INote, PropSchema, Module, IModule, MidiOutput } from "@/core";
2
+ import { BarsBeatsSixteenths } from "@/core/Timing/Time";
3
+ import { ICreateModule, ModuleType } from ".";
4
+
5
+ export type IStepSequencer = IModule<ModuleType.StepSequencer>;
6
+
7
+ export type ISequence = {
8
+ active: boolean;
9
+ time: BarsBeatsSixteenths;
10
+ duration: string;
11
+ notes: INote[];
12
+ };
13
+
14
+ export type IStepSequencerProps = {
15
+ bars: number;
16
+ steps: number;
17
+ sequences: ISequence[][];
18
+ };
19
+
20
+ export const stepSequencerPropSchema: PropSchema<
21
+ Omit<IStepSequencerProps, "sequences">
22
+ > = {
23
+ steps: {
24
+ kind: "number",
25
+ min: 1,
26
+ max: 16,
27
+ step: 1,
28
+ label: "Steps",
29
+ },
30
+ bars: {
31
+ kind: "number",
32
+ min: 1,
33
+ max: 16,
34
+ step: 1,
35
+ label: "Steps",
36
+ },
37
+ };
38
+
39
+ const DEFAULT_PROPS: IStepSequencerProps = {
40
+ sequences: [],
41
+ steps: 16,
42
+ bars: 1,
43
+ };
44
+
45
+ // Not implemented yet, just the data modeling
46
+ export default class StepSequencer extends Module<ModuleType.StepSequencer> {
47
+ declare audioNode: undefined;
48
+ midiOutput!: MidiOutput;
49
+
50
+ constructor(
51
+ engineId: string,
52
+ params: ICreateModule<ModuleType.StepSequencer>,
53
+ ) {
54
+ const props = { ...DEFAULT_PROPS, ...params.props };
55
+
56
+ super(engineId, {
57
+ ...params,
58
+ props,
59
+ });
60
+ }
61
+ }
@@ -1,74 +1,58 @@
1
- import Engine from "../Engine";
2
- import { MidiEvent } from "../core/midi";
3
- import Note from "../core/Note";
4
- import Module, { DummnyInternalModule } from "../core/Module";
5
- import { MidiOutput } from "../core/IO";
1
+ import { PropSchema, Module, IModule, MidiOutput, Note } from "@/core";
2
+ import { TTime } from "@/core/Timing/Time";
3
+ import MidiEvent from "@/core/midi/MidiEvent";
4
+ import { ICreateModule, ModuleType } from ".";
6
5
 
7
- export interface VirtualMidiInterface {
6
+ export type IVirtualMidi = IModule<ModuleType.VirtualMidi>;
7
+ export type IVirtualMidiProps = {
8
8
  activeNotes: string[];
9
- }
9
+ };
10
10
 
11
- const InitialProps: VirtualMidiInterface = {
12
- activeNotes: [],
11
+ export const virtualMidiPropSchema: PropSchema<IVirtualMidiProps> = {
12
+ activeNotes: {
13
+ kind: "array",
14
+ label: "Active notes",
15
+ },
13
16
  };
14
17
 
15
- export default class VirtualMidi extends Module<
16
- DummnyInternalModule,
17
- VirtualMidiInterface
18
- > {
19
- static moduleName = "VirtualMidi";
20
- midiOutput: MidiOutput;
18
+ const DEFAULT_PROPS: IVirtualMidiProps = { activeNotes: [] };
19
+
20
+ export default class VirtualMidi extends Module<ModuleType.VirtualMidi> {
21
+ declare audioNode: undefined;
22
+ midiOutput!: MidiOutput;
21
23
 
22
- constructor(params: {
23
- id?: string;
24
- name: string;
25
- props: VirtualMidiInterface;
26
- }) {
27
- const { id, name, props } = params;
28
- super(new DummnyInternalModule(), {
29
- id,
30
- name,
31
- props: { ...InitialProps, ...props },
24
+ constructor(engineId: string, params: ICreateModule<ModuleType.VirtualMidi>) {
25
+ const props = { ...DEFAULT_PROPS, ...params.props };
26
+
27
+ super(engineId, {
28
+ ...params,
29
+ props,
32
30
  });
33
31
 
34
32
  this.registerInputs();
35
33
  this.registerOutputs();
36
34
  }
37
35
 
38
- get activeNotes() {
39
- return this._props["activeNotes"];
40
- }
41
-
42
- // can't set externaly
43
- set activeNotes(value: string[]) {
44
- this._props = { ...this.props, activeNotes: value };
45
- }
46
-
47
36
  sendMidi(midiEvent: MidiEvent) {
48
37
  this.midiOutput.onMidiEvent(midiEvent);
49
38
  }
50
39
 
51
- triggerAttack = (note: Note, triggerAttack: number) => {
52
- this.activeNotes = [...this.activeNotes, note.fullName];
53
- Engine._triggerPropsUpdate(this.id, this.props);
40
+ triggerAttack = (note: Note, triggerAttack: TTime) => {
41
+ this.props = { activeNotes: [...this.props.activeNotes, note.fullName] };
42
+ this.triggerPropsUpdate();
54
43
  this.sendMidi(MidiEvent.fromNote(note, true, triggerAttack));
55
44
  };
56
45
 
57
- triggerRelease = (note: Note, triggerAttack: number) => {
58
- this.activeNotes = this.activeNotes.filter(
59
- (name) => name !== note.fullName
60
- );
61
- Engine._triggerPropsUpdate(this.id, this.props);
46
+ triggerRelease = (note: Note, triggerAttack: TTime) => {
47
+ this.props = {
48
+ activeNotes: this.props.activeNotes.filter(
49
+ (name) => name !== note.fullName,
50
+ ),
51
+ };
52
+ this.triggerPropsUpdate();
62
53
  this.sendMidi(MidiEvent.fromNote(note, false, triggerAttack));
63
54
  };
64
55
 
65
- serialize() {
66
- return {
67
- ...super.serialize(),
68
- activeNotes: this.activeNotes,
69
- };
70
- }
71
-
72
56
  private registerInputs() {
73
57
  this.registerMidiInput({
74
58
  name: "midi in",