@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.
- package/README.md +252 -76
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +765 -0
- package/dist/index.d.ts +765 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +19 -31
- package/src/Engine.ts +158 -177
- package/src/core/IO/AudioIO.ts +72 -0
- package/src/core/IO/Base.ts +118 -0
- package/src/core/IO/Collection.ts +123 -47
- package/src/core/IO/MidiIO.ts +43 -0
- package/src/core/IO/PolyAudioIO.ts +115 -0
- package/src/core/IO/index.ts +7 -61
- package/src/core/Note/frequencyTable.ts +144 -144
- package/src/core/Note/index.ts +49 -59
- package/src/core/Route.ts +79 -0
- package/src/core/Timing/Scheduler.ts +37 -0
- package/src/core/Timing/Time.ts +103 -0
- package/src/core/Timing/Transport.ts +104 -0
- package/src/core/Timing/index.ts +16 -0
- package/src/core/index.ts +36 -0
- package/src/core/midi/{ComputerKeyboardInput.ts → ComputerKeyboardDevice.ts} +31 -11
- package/src/core/midi/MidiDevice.ts +38 -31
- package/src/core/midi/MidiDeviceManager.ts +54 -55
- package/src/core/midi/MidiEvent.ts +36 -60
- package/src/core/module/Module.ts +233 -0
- package/src/core/module/PolyModule.ts +246 -0
- package/src/core/module/VoiceScheduler.ts +121 -0
- package/src/core/module/index.ts +3 -0
- package/src/core/schema.ts +41 -0
- package/src/index.ts +31 -9
- package/src/modules/BiquadFilter.ts +162 -0
- package/src/modules/Constant.ts +72 -0
- package/src/modules/Envelope.ts +178 -0
- package/src/modules/Filter.ts +109 -104
- package/src/modules/Gain.ts +78 -0
- package/src/modules/Inspector.ts +59 -0
- package/src/modules/Master.ts +18 -21
- package/src/modules/MidiSelector.ts +50 -50
- package/src/modules/Oscillator.ts +202 -148
- package/src/modules/Scale.ts +79 -0
- package/src/modules/StepSequencer.ts +61 -0
- package/src/modules/VirtualMidi.ts +33 -49
- package/src/modules/index.ts +159 -74
- package/src/nodePolyfill.ts +25 -0
- package/src/processors/filter-processor.ts +82 -0
- package/src/processors/index.ts +28 -0
- package/src/processors/scale-processor.ts +81 -0
- package/dist/build/Engine.d.ts +0 -82
- package/dist/build/core/IO/AudioNode.d.ts +0 -35
- package/dist/build/core/IO/Collection.d.ts +0 -13
- package/dist/build/core/IO/ForwardNode/Base.d.ts +0 -18
- package/dist/build/core/IO/ForwardNode/index.d.ts +0 -27
- package/dist/build/core/IO/ForwardNode.d.ts +0 -38
- package/dist/build/core/IO/MidiNode.d.ts +0 -30
- package/dist/build/core/IO/Node.d.ts +0 -40
- package/dist/build/core/IO/index.d.ts +0 -21
- package/dist/build/core/Module/MonoModule.d.ts +0 -67
- package/dist/build/core/Module/PolyModule.d.ts +0 -61
- package/dist/build/core/Module/index.d.ts +0 -6
- package/dist/build/core/Note/frequencyTable.d.ts +0 -4
- package/dist/build/core/Note/index.d.ts +0 -28
- package/dist/build/core/midi/ComputerKeyboardInput.d.ts +0 -11
- package/dist/build/core/midi/MidiDevice.d.ts +0 -29
- package/dist/build/core/midi/MidiDeviceManager.d.ts +0 -14
- package/dist/build/core/midi/MidiEvent.d.ts +0 -21
- package/dist/build/core/midi/index.d.ts +0 -5
- package/dist/build/index.d.ts +0 -9
- package/dist/build/modules/BitCrusher.d.ts +0 -16
- package/dist/build/modules/Delay.d.ts +0 -19
- package/dist/build/modules/Distortion.d.ts +0 -16
- package/dist/build/modules/Effect.d.ts +0 -19
- package/dist/build/modules/Envelope/AmpEnvelope.d.ts +0 -18
- package/dist/build/modules/Envelope/Base.d.ts +0 -66
- package/dist/build/modules/Envelope/FreqEnvelope.d.ts +0 -25
- package/dist/build/modules/Envelope/index.d.ts +0 -3
- package/dist/build/modules/Filter.d.ts +0 -42
- package/dist/build/modules/LFO.d.ts +0 -44
- package/dist/build/modules/Master.d.ts +0 -11
- package/dist/build/modules/MidiSelector.d.ts +0 -21
- package/dist/build/modules/Oscillator.d.ts +0 -55
- package/dist/build/modules/Reverb.d.ts +0 -19
- package/dist/build/modules/Sequencer.d.ts +0 -42
- package/dist/build/modules/VirtualMidi.d.ts +0 -32
- package/dist/build/modules/VoiceScheduler.d.ts +0 -44
- package/dist/build/modules/Volume.d.ts +0 -26
- package/dist/build/modules/index.d.ts +0 -17
- package/dist/build/routes.d.ts +0 -18
- package/dist/build/types.d.ts +0 -5
- package/dist/build/utils.d.ts +0 -1
- package/dist/main.cjs.js +0 -25
- package/dist/main.cjs.js.map +0 -1
- package/dist/main.esm.js +0 -25
- package/dist/main.esm.js.map +0 -1
- package/src/core/IO/AudioNode.ts +0 -82
- package/src/core/IO/ForwardNode/Base.ts +0 -99
- package/src/core/IO/ForwardNode/index.ts +0 -60
- package/src/core/IO/MidiNode.ts +0 -67
- package/src/core/IO/Node.ts +0 -118
- package/src/core/Module/MonoModule.ts +0 -219
- package/src/core/Module/PolyModule.ts +0 -218
- package/src/core/Module/index.ts +0 -15
- package/src/core/midi/index.ts +0 -5
- package/src/modules/BitCrusher.ts +0 -45
- package/src/modules/Delay.ts +0 -53
- package/src/modules/Distortion.ts +0 -45
- package/src/modules/Effect.ts +0 -46
- package/src/modules/Envelope/AmpEnvelope.ts +0 -23
- package/src/modules/Envelope/Base.ts +0 -176
- package/src/modules/Envelope/FreqEnvelope.ts +0 -64
- package/src/modules/Envelope/index.ts +0 -3
- package/src/modules/LFO.ts +0 -149
- package/src/modules/Reverb.ts +0 -53
- package/src/modules/Sequencer.ts +0 -178
- package/src/modules/VoiceScheduler.ts +0 -145
- package/src/modules/Volume.ts +0 -72
- package/src/routes.ts +0 -49
- package/src/types.ts +0 -3
- package/src/utils.ts +0 -18
|
@@ -1,213 +1,267 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
19
|
-
|
|
81
|
+
const DEFAULT_PROPS: IOscillatorProps = {
|
|
82
|
+
wave: OscillatorWave.sine,
|
|
83
|
+
frequency: 440,
|
|
20
84
|
fine: 0,
|
|
21
85
|
coarse: 0,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
volume: 0,
|
|
86
|
+
octave: 0,
|
|
87
|
+
lowGain: false,
|
|
25
88
|
};
|
|
26
89
|
|
|
27
|
-
class MonoOscillator
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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.
|
|
49
|
-
this.start(now());
|
|
114
|
+
this.registerOutputs();
|
|
50
115
|
}
|
|
51
116
|
|
|
52
|
-
|
|
53
|
-
|
|
117
|
+
protected onAfterSetWave(value: OscillatorWave) {
|
|
118
|
+
this.audioNode.type = value;
|
|
54
119
|
}
|
|
55
120
|
|
|
56
|
-
|
|
57
|
-
this.
|
|
58
|
-
this.updateFrequency(time);
|
|
121
|
+
protected onAfterSetFrequency() {
|
|
122
|
+
this.updateFrequency();
|
|
59
123
|
}
|
|
60
124
|
|
|
61
|
-
|
|
62
|
-
this._note = this.getNote(value);
|
|
125
|
+
protected onAfterSetFine() {
|
|
63
126
|
this.updateFrequency();
|
|
64
127
|
}
|
|
65
128
|
|
|
66
|
-
|
|
67
|
-
|
|
129
|
+
protected onAfterSetCoarse() {
|
|
130
|
+
this.updateFrequency();
|
|
68
131
|
}
|
|
69
132
|
|
|
70
|
-
|
|
71
|
-
this.
|
|
72
|
-
this.note = new Note(this.noteName);
|
|
133
|
+
protected onAfterSetOctave() {
|
|
134
|
+
this.updateFrequency();
|
|
73
135
|
}
|
|
74
136
|
|
|
75
|
-
|
|
76
|
-
if (this.
|
|
137
|
+
protected onAfterSetLowGain() {
|
|
138
|
+
if (!this.superInitialized) return;
|
|
77
139
|
|
|
78
|
-
this.
|
|
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
|
-
|
|
89
|
-
|
|
143
|
+
start(time: TTime) {
|
|
144
|
+
if (this.isStated) return;
|
|
90
145
|
|
|
91
|
-
this.
|
|
146
|
+
this.isStated = true;
|
|
147
|
+
this.audioNode.start(nt(time));
|
|
92
148
|
}
|
|
93
149
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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.
|
|
161
|
+
this.isStated = false;
|
|
102
162
|
}
|
|
103
163
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
164
|
+
triggerAttack = (note: Note, triggeredAt: TTime) => {
|
|
165
|
+
super.triggerAttack(note, triggeredAt);
|
|
107
166
|
|
|
108
|
-
|
|
109
|
-
this.
|
|
110
|
-
this.
|
|
111
|
-
}
|
|
167
|
+
this.props = { frequency: note.frequency };
|
|
168
|
+
this.updateFrequency(triggeredAt);
|
|
169
|
+
this.start(triggeredAt);
|
|
170
|
+
};
|
|
112
171
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
172
|
+
triggerRelease(note: Note, triggeredAt: TTime) {
|
|
173
|
+
super.triggerRelease(note, triggeredAt);
|
|
116
174
|
|
|
117
|
-
|
|
118
|
-
|
|
175
|
+
const lastNote = this.activeNotes.length
|
|
176
|
+
? this.activeNotes[this.activeNotes.length - 1]
|
|
177
|
+
: null;
|
|
178
|
+
if (!lastNote) return;
|
|
119
179
|
|
|
120
|
-
this.
|
|
180
|
+
this.props = { frequency: lastNote.frequency };
|
|
181
|
+
this.updateFrequency(triggeredAt);
|
|
121
182
|
}
|
|
122
183
|
|
|
123
|
-
get
|
|
124
|
-
|
|
125
|
-
|
|
184
|
+
private get finalFrequency(): number | undefined {
|
|
185
|
+
const { frequency, coarse, octave, fine } = this.props;
|
|
186
|
+
if (!this.superInitialized) return;
|
|
126
187
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
188
|
+
const transposed =
|
|
189
|
+
frequency * Math.pow(2, coarse / 12 + octave + fine / 12);
|
|
190
|
+
return transposed;
|
|
130
191
|
}
|
|
131
192
|
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
const oscState = this.internalModule.state;
|
|
193
|
+
private updateFrequency(actionAt?: TTime) {
|
|
194
|
+
if (this.finalFrequency === undefined) return;
|
|
136
195
|
|
|
137
|
-
if (
|
|
138
|
-
this.
|
|
196
|
+
if (actionAt) {
|
|
197
|
+
this.audioNode.frequency.setValueAtTime(
|
|
198
|
+
this.finalFrequency,
|
|
199
|
+
nt(actionAt),
|
|
200
|
+
);
|
|
139
201
|
} else {
|
|
140
|
-
this.
|
|
202
|
+
this.audioNode.frequency.value = this.finalFrequency;
|
|
141
203
|
}
|
|
142
204
|
}
|
|
143
205
|
|
|
144
|
-
|
|
145
|
-
this.
|
|
206
|
+
private applyOutputGain() {
|
|
207
|
+
this.audioNode.connect(this.lowOutputGain);
|
|
146
208
|
}
|
|
147
209
|
|
|
148
|
-
|
|
149
|
-
this.
|
|
150
|
-
this.
|
|
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: "
|
|
176
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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.
|
|
202
|
-
this.
|
|
203
|
-
this.registerForwardAudioInput({ name: "fine" });
|
|
248
|
+
this.registerInputs();
|
|
249
|
+
this.registerDefaultIOs("out");
|
|
204
250
|
}
|
|
205
251
|
|
|
206
|
-
start(time:
|
|
207
|
-
this.audioModules.forEach((audioModule) =>
|
|
252
|
+
start(time: TTime) {
|
|
253
|
+
this.audioModules.forEach((audioModule) => {
|
|
254
|
+
audioModule.start(time);
|
|
255
|
+
});
|
|
208
256
|
}
|
|
209
257
|
|
|
210
|
-
stop(time
|
|
211
|
-
this.audioModules.forEach((audioModule) =>
|
|
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
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
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
|
|
6
|
+
export type IVirtualMidi = IModule<ModuleType.VirtualMidi>;
|
|
7
|
+
export type IVirtualMidiProps = {
|
|
8
8
|
activeNotes: string[];
|
|
9
|
-
}
|
|
9
|
+
};
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
activeNotes:
|
|
11
|
+
export const virtualMidiPropSchema: PropSchema<IVirtualMidiProps> = {
|
|
12
|
+
activeNotes: {
|
|
13
|
+
kind: "array",
|
|
14
|
+
label: "Active notes",
|
|
15
|
+
},
|
|
13
16
|
};
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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:
|
|
52
|
-
this.
|
|
53
|
-
|
|
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:
|
|
58
|
-
this.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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",
|