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