@blibliki/engine 0.1.27 → 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 (123) 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 -19484
  7. package/dist/index.js.map +1 -0
  8. package/package.json +16 -27
  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 +203 -158
  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/index.umd.cjs +0 -227
  52. package/dist/src/Engine.d.ts +0 -83
  53. package/dist/src/core/IO/AudioNode.d.ts +0 -36
  54. package/dist/src/core/IO/Collection.d.ts +0 -14
  55. package/dist/src/core/IO/ForwardNode/Base.d.ts +0 -19
  56. package/dist/src/core/IO/ForwardNode/index.d.ts +0 -28
  57. package/dist/src/core/IO/MidiNode.d.ts +0 -31
  58. package/dist/src/core/IO/Node.d.ts +0 -41
  59. package/dist/src/core/IO/index.d.ts +0 -22
  60. package/dist/src/core/Module/MonoModule.d.ts +0 -68
  61. package/dist/src/core/Module/PolyModule.d.ts +0 -62
  62. package/dist/src/core/Module/index.d.ts +0 -7
  63. package/dist/src/core/Note/frequencyTable.d.ts +0 -4
  64. package/dist/src/core/Note/index.d.ts +0 -28
  65. package/dist/src/core/midi/ComputerKeyboardInput.d.ts +0 -12
  66. package/dist/src/core/midi/MidiDevice.d.ts +0 -30
  67. package/dist/src/core/midi/MidiDeviceManager.d.ts +0 -15
  68. package/dist/src/core/midi/MidiEvent.d.ts +0 -22
  69. package/dist/src/core/midi/index.d.ts +0 -5
  70. package/dist/src/index.d.ts +0 -9
  71. package/dist/src/main.d.ts +0 -0
  72. package/dist/src/modules/BitCrusher.d.ts +0 -17
  73. package/dist/src/modules/Delay.d.ts +0 -20
  74. package/dist/src/modules/Distortion.d.ts +0 -17
  75. package/dist/src/modules/Effect.d.ts +0 -20
  76. package/dist/src/modules/Envelope/AmpEnvelope.d.ts +0 -19
  77. package/dist/src/modules/Envelope/Base.d.ts +0 -67
  78. package/dist/src/modules/Envelope/FreqEnvelope.d.ts +0 -26
  79. package/dist/src/modules/Envelope/index.d.ts +0 -3
  80. package/dist/src/modules/Filter.d.ts +0 -43
  81. package/dist/src/modules/LFO.d.ts +0 -45
  82. package/dist/src/modules/Master.d.ts +0 -12
  83. package/dist/src/modules/MidiSelector.d.ts +0 -22
  84. package/dist/src/modules/Oscillator.d.ts +0 -56
  85. package/dist/src/modules/Reverb.d.ts +0 -20
  86. package/dist/src/modules/Sequencer.d.ts +0 -43
  87. package/dist/src/modules/VirtualMidi.d.ts +0 -33
  88. package/dist/src/modules/VoiceScheduler.d.ts +0 -45
  89. package/dist/src/modules/Volume.d.ts +0 -27
  90. package/dist/src/modules/index.d.ts +0 -18
  91. package/dist/src/routes.d.ts +0 -19
  92. package/dist/src/types.d.ts +0 -5
  93. package/dist/src/utils.d.ts +0 -1
  94. package/dist/test/MockingModules.d.ts +0 -22
  95. package/dist/test/Module/Oscillator.test.d.ts +0 -1
  96. package/dist/test/core/IO.test.d.ts +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/main.ts +0 -1
  107. package/src/modules/BitCrusher.ts +0 -45
  108. package/src/modules/Delay.ts +0 -53
  109. package/src/modules/Distortion.ts +0 -45
  110. package/src/modules/Effect.ts +0 -46
  111. package/src/modules/Envelope/AmpEnvelope.ts +0 -23
  112. package/src/modules/Envelope/Base.ts +0 -176
  113. package/src/modules/Envelope/FreqEnvelope.ts +0 -64
  114. package/src/modules/Envelope/index.ts +0 -3
  115. package/src/modules/LFO.ts +0 -149
  116. package/src/modules/Reverb.ts +0 -53
  117. package/src/modules/Sequencer.ts +0 -178
  118. package/src/modules/VoiceScheduler.ts +0 -145
  119. package/src/modules/Volume.ts +0 -72
  120. package/src/routes.ts +0 -49
  121. package/src/types.ts +0 -3
  122. package/src/utils.ts +0 -18
  123. package/src/vite-env.d.ts +0 -1
@@ -1,222 +1,267 @@
1
- import {
2
- Multiply,
3
- now,
4
- Oscillator as Osc,
5
- Signal,
6
- ToneOscillatorType,
7
- } from "tone";
8
- import Engine from "../Engine";
9
-
10
- import Note from "../core/Note";
11
- import Module, { PolyModule, Startable } from "../core/Module";
12
-
13
- export type TWave = "sine" | "triangle" | "square" | "sawtooth";
14
-
15
- export interface OscillatorInterface {
16
- noteName: string;
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
+ }
20
+
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;
17
35
  fine: number;
18
36
  coarse: number;
19
- wave: TWave;
20
- volume: number;
21
- range: number;
22
- }
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
+ };
23
80
 
24
- const InitialProps: OscillatorInterface = {
25
- noteName: "C3",
81
+ const DEFAULT_PROPS: IOscillatorProps = {
82
+ wave: OscillatorWave.sine,
83
+ frequency: 440,
26
84
  fine: 0,
27
85
  coarse: 0,
28
- wave: "sine",
29
- range: 0,
30
- volume: 0,
86
+ octave: 0,
87
+ lowGain: false,
31
88
  };
32
89
 
33
- export class MonoOscillator
34
- extends Module<Osc, OscillatorInterface>
35
- implements Startable
36
- {
37
- private _note: Note;
38
- private _fineMulti: Multiply;
39
- private _fineSignal: Signal;
40
-
41
- constructor(params: {
42
- id?: string;
43
- name: string;
44
- props: Partial<OscillatorInterface>;
45
- }) {
46
- const { id, name, props } = params;
47
-
48
- super(new Osc(), {
49
- id,
50
- name,
51
- 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,
52
105
  });
53
106
 
107
+ this.lowOutputGain = new GainNode(this.context, {
108
+ gain: dbToGain(LOW_GAIN),
109
+ });
110
+
111
+ this.applyOutputGain();
112
+ this.initializeGainDetune();
54
113
  this.registerInputs();
55
- this.registerBasicOutputs();
56
- this.start(now());
114
+ this.registerOutputs();
57
115
  }
58
116
 
59
- get note(): Note {
60
- return this._note;
117
+ protected onAfterSetWave(value: OscillatorWave) {
118
+ this.audioNode.type = value;
61
119
  }
62
120
 
63
- setNoteAt(value: Note | string, time: number) {
64
- this._note = this.getNote(value);
65
- this.updateFrequency(time);
121
+ protected onAfterSetFrequency() {
122
+ this.updateFrequency();
66
123
  }
67
124
 
68
- set note(value: Note | string) {
69
- this._note = this.getNote(value);
125
+ protected onAfterSetFine() {
70
126
  this.updateFrequency();
71
127
  }
72
128
 
73
- get noteName() {
74
- return this._props["noteName"];
129
+ protected onAfterSetCoarse() {
130
+ this.updateFrequency();
75
131
  }
76
132
 
77
- set noteName(value: string) {
78
- this._props = { ...this.props, noteName: value };
79
- this.note = new Note(this.noteName);
133
+ protected onAfterSetOctave() {
134
+ this.updateFrequency();
80
135
  }
81
136
 
82
- get fineSingal() {
83
- if (this._fineSignal) return this._fineSignal;
84
-
85
- this._fineSignal = new Signal();
86
- this._fineMulti = new Multiply(100);
87
- this._fineSignal.connect(this._fineMulti);
88
- this._fineMulti.connect(this.internalModule.detune);
137
+ protected onAfterSetLowGain() {
138
+ if (!this.superInitialized) return;
89
139
 
90
- return this._fineSignal;
140
+ this.rePlugAll();
91
141
  }
92
142
 
93
- get fine() {
94
- return this._props["fine"];
95
- }
96
-
97
- set fine(value: number) {
98
- this._props = { ...this.props, fine: value };
99
-
100
- this.fineSingal.value = this.fine;
101
- }
143
+ start(time: TTime) {
144
+ if (this.isStated) return;
102
145
 
103
- get coarse() {
104
- return this._props["coarse"];
146
+ this.isStated = true;
147
+ this.audioNode.start(nt(time));
105
148
  }
106
149
 
107
- set coarse(value: number) {
108
- 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
+ });
109
160
 
110
- this.updateFrequency();
161
+ this.isStated = false;
111
162
  }
112
163
 
113
- get wave() {
114
- return this._props["wave"];
115
- }
164
+ triggerAttack = (note: Note, triggeredAt: TTime) => {
165
+ super.triggerAttack(note, triggeredAt);
116
166
 
117
- set wave(value: TWave) {
118
- this._props = { ...this.props, wave: value };
119
- this.internalModule.type = this.wave as ToneOscillatorType;
120
- }
167
+ this.props = { frequency: note.frequency };
168
+ this.updateFrequency(triggeredAt);
169
+ this.start(triggeredAt);
170
+ };
121
171
 
122
- get volume() {
123
- return this._props["volume"];
124
- }
172
+ triggerRelease(note: Note, triggeredAt: TTime) {
173
+ super.triggerRelease(note, triggeredAt);
125
174
 
126
- set volume(value: number) {
127
- 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;
128
179
 
129
- this.internalModule.volume.value = this.volume;
180
+ this.props = { frequency: lastNote.frequency };
181
+ this.updateFrequency(triggeredAt);
130
182
  }
131
183
 
132
- get range() {
133
- return this._props["range"];
134
- }
184
+ private get finalFrequency(): number | undefined {
185
+ const { frequency, coarse, octave, fine } = this.props;
186
+ if (!this.superInitialized) return;
135
187
 
136
- set range(value: number) {
137
- this._props = { ...this.props, range: value };
138
- this.updateFrequency();
188
+ const transposed =
189
+ frequency * Math.pow(2, coarse / 12 + octave + fine / 12);
190
+ return transposed;
139
191
  }
140
192
 
141
- start(time: number) {
142
- if (!Engine.isStarted) return;
143
-
144
- const oscState = this.internalModule.state;
193
+ private updateFrequency(actionAt?: TTime) {
194
+ if (this.finalFrequency === undefined) return;
145
195
 
146
- if (oscState === "started") {
147
- this.internalModule.restart(time);
196
+ if (actionAt) {
197
+ this.audioNode.frequency.setValueAtTime(
198
+ this.finalFrequency,
199
+ nt(actionAt),
200
+ );
148
201
  } else {
149
- this.internalModule.start(time);
202
+ this.audioNode.frequency.value = this.finalFrequency;
150
203
  }
151
204
  }
152
205
 
153
- stop(time?: number) {
154
- this.internalModule.stop(time);
206
+ private applyOutputGain() {
207
+ this.audioNode.connect(this.lowOutputGain);
155
208
  }
156
209
 
157
- triggerAttack = (note: Note, triggeredAt: number) => {
158
- this.setNoteAt(note, triggeredAt);
159
- this.start(triggeredAt);
160
- };
161
-
162
- triggerRelease = () => {
163
- // Do nothing
164
- };
165
-
166
- private updateFrequency(time?: number) {
167
- if (!this.note) return;
168
-
169
- const freq = this.note.adjustFrequency(this.range, this.coarse);
170
-
171
- if (time) {
172
- this.internalModule.frequency.linearRampToValueAtTime(freq, time);
173
- } else {
174
- this.internalModule.frequency.value = freq;
175
- }
176
- }
177
-
178
- private getNote(note: Note | string): Note {
179
- 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);
180
213
  }
181
214
 
182
215
  private registerInputs() {
183
216
  this.registerAudioInput({
184
- name: "fine",
185
- 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,
186
227
  });
187
228
  }
188
229
  }
189
230
 
190
- export default class Oscillator extends PolyModule<
191
- MonoOscillator,
192
- OscillatorInterface
193
- > {
194
- static moduleName = "Oscillator";
195
-
196
- constructor(params: {
197
- id?: string;
198
- name: string;
199
- props: Partial<OscillatorInterface>;
200
- }) {
201
- const { id, name, props } = params;
202
-
203
- super({
204
- id,
205
- name,
206
- child: MonoOscillator,
207
- 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,
208
246
  });
209
247
 
210
- this.registerBasicOutputs();
211
- this.registerMidiIn();
212
- this.registerForwardAudioInput({ name: "fine" });
248
+ this.registerInputs();
249
+ this.registerDefaultIOs("out");
213
250
  }
214
251
 
215
- start(time: number) {
216
- this.audioModules.forEach((audioModule) => audioModule.start(time));
252
+ start(time: TTime) {
253
+ this.audioModules.forEach((audioModule) => {
254
+ audioModule.start(time);
255
+ });
217
256
  }
218
257
 
219
- stop(time?: number) {
220
- 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" });
221
266
  }
222
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",