spessasynth_lib 3.24.13 → 3.24.16
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/midi_parser/basic_midi.js +457 -68
- package/midi_parser/midi_loader.js +18 -503
- package/midi_parser/midi_message.js +18 -5
- package/midi_parser/midi_sequence.js +2 -2
- package/package.json +1 -1
- package/sequencer/worklet_sequencer/process_event.js +1 -6
- package/synthetizer/synthetizer.js +13 -7
- package/synthetizer/worklet_processor.min.js +12 -12
- package/synthetizer/worklet_system/README.md +2 -2
- package/synthetizer/worklet_system/main_processor.js +106 -95
- package/synthetizer/worklet_system/message_protocol/handle_message.js +22 -17
- package/synthetizer/worklet_system/message_protocol/worklet_message.js +2 -1
- package/synthetizer/worklet_system/snapshot/apply_synthesizer_snapshot.js +14 -0
- package/synthetizer/worklet_system/snapshot/channel_snapshot.js +166 -0
- package/synthetizer/worklet_system/snapshot/send_synthesizer_snapshot.js +14 -0
- package/synthetizer/worklet_system/snapshot/synthesizer_snapshot.js +121 -0
- package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +196 -0
- package/synthetizer/worklet_system/worklet_methods/controller_control/master_parameters.js +34 -0
- package/synthetizer/worklet_system/worklet_methods/{reset_controllers.js → controller_control/reset_controllers.js} +33 -39
- package/synthetizer/worklet_system/worklet_methods/create_worklet_channel.js +26 -0
- package/synthetizer/worklet_system/worklet_methods/{data_entry.js → data_entry/data_entry_coarse.js} +38 -105
- package/synthetizer/worklet_system/worklet_methods/data_entry/data_entry_fine.js +64 -0
- package/synthetizer/worklet_system/worklet_methods/mute_channel.js +17 -0
- package/synthetizer/worklet_system/worklet_methods/note_on.js +36 -34
- package/synthetizer/worklet_system/worklet_methods/program_change.js +49 -0
- package/synthetizer/worklet_system/worklet_methods/{voice_control.js → render_voice.js} +37 -120
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/clear_sound_font.js +35 -0
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/get_preset.js +20 -0
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/reload_sound_font.js +43 -0
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/send_preset_list.js +31 -0
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/set_embedded_sound_font.js +21 -0
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/kill_note.js +20 -0
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/note_off.js +55 -0
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/stop_all_channels.js +16 -0
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/stop_all_notes.js +30 -0
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/voice_killing.js +63 -0
- package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +31 -30
- package/synthetizer/worklet_system/worklet_methods/tuning_control/channel_pressure.js +24 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/pitch_wheel.js +33 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/poly_pressure.js +31 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_master_tuning.js +15 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_modulation_depth.js +27 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_octave_tuning.js +15 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning.js +24 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning_semitones.js +19 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/transpose_all_channels.js +15 -0
- package/synthetizer/worklet_system/worklet_methods/tuning_control/transpose_channel.js +31 -0
- package/synthetizer/worklet_system/worklet_utilities/controller_tables.js +10 -1
- package/synthetizer/worklet_system/worklet_utilities/lfo.js +2 -1
- package/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +4 -4
- package/synthetizer/worklet_system/worklet_utilities/modulator_curves.js +4 -5
- package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +18 -18
- package/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +210 -206
- package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +354 -108
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +22 -9
- package/synthetizer/worklet_system/snapshot/snapshot.js +0 -311
- package/synthetizer/worklet_system/worklet_methods/controller_control.js +0 -260
- package/synthetizer/worklet_system/worklet_methods/note_off.js +0 -119
- package/synthetizer/worklet_system/worklet_methods/program_control.js +0 -282
- package/synthetizer/worklet_system/worklet_methods/tuning_control.js +0 -233
- package/synthetizer/worklet_system/worklet_methods/vibrato_control.js +0 -29
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import { returnMessageType } from "../message_protocol/worklet_message.js";
|
|
2
|
-
import { SpessaSynthInfo } from "../../../utils/loggin.js";
|
|
3
|
-
import { consoleColors } from "../../../utils/other.js";
|
|
4
|
-
import { getBankSelect, setBankSelect } from "../worklet_utilities/worklet_processor_channel.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Represents a snapshot of a single channel's state in the synthesizer.
|
|
8
|
-
*/
|
|
9
|
-
class ChannelSnapshot
|
|
10
|
-
{
|
|
11
|
-
/**
|
|
12
|
-
* The channel's MIDI program number.
|
|
13
|
-
* @type {number}
|
|
14
|
-
*/
|
|
15
|
-
program;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* The channel's bank number.
|
|
19
|
-
* @type {number}
|
|
20
|
-
*/
|
|
21
|
-
bank;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* The name of the patch currently loaded in the channel.
|
|
25
|
-
* @type {string}
|
|
26
|
-
*/
|
|
27
|
-
patchName;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Indicates whether the channel's program change is disabled.
|
|
31
|
-
* @type {boolean}
|
|
32
|
-
*/
|
|
33
|
-
lockPreset;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* The array of all MIDI controllers (in 14-bit values) with the modulator sources at the end.
|
|
37
|
-
* @type {Int16Array}
|
|
38
|
-
*/
|
|
39
|
-
midiControllers;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* An array of booleans, indicating if the controller with a current index is locked.
|
|
43
|
-
* @type {boolean[]}
|
|
44
|
-
*/
|
|
45
|
-
lockedControllers;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Array of custom (not SF2) control values such as RPN pitch tuning, transpose, modulation depth, etc.
|
|
49
|
-
* @type {Float32Array}
|
|
50
|
-
*/
|
|
51
|
-
customControllers;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Indicates whether the channel vibrato is locked.
|
|
55
|
-
* @type {boolean}
|
|
56
|
-
*/
|
|
57
|
-
lockVibrato;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* The channel's vibrato settings.
|
|
61
|
-
* @type {Object}
|
|
62
|
-
* @property {number} depth - Vibrato depth, in gain.
|
|
63
|
-
* @property {number} delay - Vibrato delay from note on in seconds.
|
|
64
|
-
* @property {number} rate - Vibrato rate in Hz.
|
|
65
|
-
*/
|
|
66
|
-
channelVibrato;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Key shift for the channel.
|
|
70
|
-
* @type {number}
|
|
71
|
-
*/
|
|
72
|
-
channelTransposeKeyShift;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* The channel's octave tuning in cents.
|
|
76
|
-
* @type {Int8Array}
|
|
77
|
-
*/
|
|
78
|
-
channelOctaveTuning;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Tuning of individual keys in cents.
|
|
82
|
-
* @type {Int16Array}
|
|
83
|
-
*/
|
|
84
|
-
keyCentTuning;
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Indicates whether the channel is muted.
|
|
88
|
-
* @type {boolean}
|
|
89
|
-
*/
|
|
90
|
-
isMuted;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Overrides velocity if greater than 0, otherwise disabled.
|
|
94
|
-
* @type {number}
|
|
95
|
-
*/
|
|
96
|
-
velocityOverride;
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Indicates whether the channel is a drum channel.
|
|
100
|
-
* @type {boolean}
|
|
101
|
-
*/
|
|
102
|
-
drumChannel;
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Creates a snapshot of a single channel's state in the synthesizer.
|
|
106
|
-
* @param workletProcessor {SpessaSynthProcessor}
|
|
107
|
-
* @param channelNumber {number}
|
|
108
|
-
* @returns {ChannelSnapshot}
|
|
109
|
-
*/
|
|
110
|
-
static getChannelSnapshot(workletProcessor, channelNumber)
|
|
111
|
-
{
|
|
112
|
-
const channel = workletProcessor.workletProcessorChannels[channelNumber];
|
|
113
|
-
const channelSnapshot = new ChannelSnapshot();
|
|
114
|
-
// program data
|
|
115
|
-
channelSnapshot.program = channel.preset.program;
|
|
116
|
-
channelSnapshot.bank = getBankSelect(channel);
|
|
117
|
-
channelSnapshot.lockPreset = channel.lockPreset;
|
|
118
|
-
channelSnapshot.patchName = channel.preset.presetName;
|
|
119
|
-
|
|
120
|
-
// controller data
|
|
121
|
-
channelSnapshot.midiControllers = channel.midiControllers;
|
|
122
|
-
channelSnapshot.lockedControllers = channel.lockedControllers;
|
|
123
|
-
channelSnapshot.customControllers = channel.customControllers;
|
|
124
|
-
|
|
125
|
-
// vibrato data
|
|
126
|
-
channelSnapshot.channelVibrato = channel.channelVibrato;
|
|
127
|
-
channelSnapshot.lockVibrato = channel.lockGSNRPNParams;
|
|
128
|
-
|
|
129
|
-
// tuning and transpose data
|
|
130
|
-
channelSnapshot.channelTransposeKeyShift = channel.channelTransposeKeyShift;
|
|
131
|
-
channelSnapshot.channelOctaveTuning = channel.channelOctaveTuning;
|
|
132
|
-
channelSnapshot.keyCentTuning = channel.keyCentTuning;
|
|
133
|
-
|
|
134
|
-
// other data
|
|
135
|
-
channelSnapshot.isMuted = channel.isMuted;
|
|
136
|
-
channelSnapshot.velocityOverride = channel.velocityOverride;
|
|
137
|
-
channelSnapshot.drumChannel = channel.drumChannel;
|
|
138
|
-
return channelSnapshot;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Applies the snapshot to the specified channel.
|
|
143
|
-
* @param workletProcessor {SpessaSynthProcessor}
|
|
144
|
-
* @param channelNumber {number}
|
|
145
|
-
* @param channelSnapshot {ChannelSnapshot}
|
|
146
|
-
*/
|
|
147
|
-
static applyChannelSnapshot(workletProcessor, channelNumber, channelSnapshot)
|
|
148
|
-
{
|
|
149
|
-
const channelObject = workletProcessor.workletProcessorChannels[channelNumber];
|
|
150
|
-
workletProcessor.muteChannel(channelNumber, channelSnapshot.isMuted);
|
|
151
|
-
workletProcessor.setDrums(channelNumber, channelSnapshot.drumChannel);
|
|
152
|
-
|
|
153
|
-
// restore controllers
|
|
154
|
-
channelObject.midiControllers = channelSnapshot.midiControllers;
|
|
155
|
-
channelObject.lockedControllers = channelSnapshot.lockedControllers;
|
|
156
|
-
channelObject.customControllers = channelSnapshot.customControllers;
|
|
157
|
-
|
|
158
|
-
// restore vibrato and transpose
|
|
159
|
-
channelObject.channelVibrato = channelSnapshot.channelVibrato;
|
|
160
|
-
channelObject.lockGSNRPNParams = channelSnapshot.lockVibrato;
|
|
161
|
-
channelObject.channelTransposeKeyShift = channelSnapshot.channelTransposeKeyShift;
|
|
162
|
-
channelObject.channelOctaveTuning = channelSnapshot.channelOctaveTuning;
|
|
163
|
-
channelObject.velocityOverride = channelSnapshot.velocityOverride;
|
|
164
|
-
|
|
165
|
-
// restore preset and lock
|
|
166
|
-
channelObject.lockPreset = false;
|
|
167
|
-
setBankSelect(channelObject, channelSnapshot.bank);
|
|
168
|
-
workletProcessor.programChange(channelNumber, channelSnapshot.program);
|
|
169
|
-
channelObject.lockPreset = channelSnapshot.lockPreset;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Represents a snapshot of the synthesizer's state.
|
|
175
|
-
*/
|
|
176
|
-
class SynthesizerSnapshot
|
|
177
|
-
{
|
|
178
|
-
/**
|
|
179
|
-
* The individual channel snapshots.
|
|
180
|
-
* @type {ChannelSnapshot[]}
|
|
181
|
-
*/
|
|
182
|
-
channelSnapshots;
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Key modifiers.
|
|
186
|
-
* @type {KeyModifier[][]}
|
|
187
|
-
*/
|
|
188
|
-
keyMappings;
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Main synth volume (set by MIDI), from 0 to 1.
|
|
192
|
-
* @type {number}
|
|
193
|
-
*/
|
|
194
|
-
mainVolume;
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Master stereo panning, from -1 to 1.
|
|
198
|
-
* @type {number}
|
|
199
|
-
*/
|
|
200
|
-
pan;
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* The synth's interpolation type.
|
|
204
|
-
* @type {interpolationTypes}
|
|
205
|
-
*/
|
|
206
|
-
interpolation;
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* The synth's system. Values can be "gs", "gm", "gm2" or "xg".
|
|
210
|
-
* @type {SynthSystem}
|
|
211
|
-
*/
|
|
212
|
-
system;
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* The current synth transposition in semitones. Can be a float.
|
|
216
|
-
* @type {number}
|
|
217
|
-
*/
|
|
218
|
-
transposition;
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* The effects configuration object.
|
|
222
|
-
* @type {SynthConfig}
|
|
223
|
-
*/
|
|
224
|
-
effectsConfig;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Creates a snapshot of the synthesizer's state.
|
|
229
|
-
* @param workletProcessor {SpessaSynthProcessor}
|
|
230
|
-
* @returns {SynthesizerSnapshot}
|
|
231
|
-
*/
|
|
232
|
-
static createSynthesizerSnapshot(workletProcessor)
|
|
233
|
-
{
|
|
234
|
-
const snapshot = new SynthesizerSnapshot();
|
|
235
|
-
// channel snapshots
|
|
236
|
-
snapshot.channelSnapshots =
|
|
237
|
-
workletProcessor.workletProcessorChannels.map((_, i) =>
|
|
238
|
-
ChannelSnapshot.getChannelSnapshot(workletProcessor, i));
|
|
239
|
-
|
|
240
|
-
// key mappings
|
|
241
|
-
snapshot.keyMappings = workletProcessor.keyModifierManager.getMappings();
|
|
242
|
-
// pan and volume
|
|
243
|
-
snapshot.mainVolume = workletProcessor.midiVolume;
|
|
244
|
-
snapshot.pan = workletProcessor.pan;
|
|
245
|
-
|
|
246
|
-
// others
|
|
247
|
-
snapshot.system = workletProcessor.system;
|
|
248
|
-
snapshot.interpolation = workletProcessor.interpolationType;
|
|
249
|
-
snapshot.transposition = workletProcessor.transposition;
|
|
250
|
-
|
|
251
|
-
// effects config is stored on the main thread, leave it empty
|
|
252
|
-
snapshot.effectsConfig = {};
|
|
253
|
-
return snapshot;
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Applies the snapshot to the synthesizer.
|
|
259
|
-
* @param workletProcessor {SpessaSynthProcessor}
|
|
260
|
-
* @param snapshot {SynthesizerSnapshot}
|
|
261
|
-
*/
|
|
262
|
-
static applySnapshot(workletProcessor, snapshot)
|
|
263
|
-
{
|
|
264
|
-
// restore system
|
|
265
|
-
workletProcessor.system = snapshot.system;
|
|
266
|
-
|
|
267
|
-
// restore pan and volume
|
|
268
|
-
workletProcessor.setMasterGain(snapshot.mainVolume);
|
|
269
|
-
workletProcessor.setMasterPan(snapshot.pan);
|
|
270
|
-
workletProcessor.transposeAllChannels(snapshot.transposition);
|
|
271
|
-
workletProcessor.interpolationType = snapshot.interpolation;
|
|
272
|
-
workletProcessor.keyModifierManager.setMappings(snapshot.keyMappings);
|
|
273
|
-
|
|
274
|
-
// add channels if more needed
|
|
275
|
-
while (workletProcessor.workletProcessorChannels.length < snapshot.channelSnapshots.length)
|
|
276
|
-
{
|
|
277
|
-
workletProcessor.createWorkletChannel();
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// restore channels
|
|
281
|
-
snapshot.channelSnapshots.forEach((channelSnapshot, index) =>
|
|
282
|
-
{
|
|
283
|
-
ChannelSnapshot.applyChannelSnapshot(workletProcessor, index, channelSnapshot);
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
SpessaSynthInfo("%cFinished restoring controllers!", consoleColors.info);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* sends a snapshot of the current controller values of the synth (used to copy that data to OfflineAudioContext when rendering)
|
|
292
|
-
* @this {SpessaSynthProcessor}
|
|
293
|
-
*/
|
|
294
|
-
export function sendSynthesizerSnapshot()
|
|
295
|
-
{
|
|
296
|
-
this.post({
|
|
297
|
-
messageType: returnMessageType.synthesizerSnapshot,
|
|
298
|
-
messageData: SynthesizerSnapshot.createSynthesizerSnapshot(this)
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Applies the snapshot to the synth
|
|
304
|
-
* @param snapshot {SynthesizerSnapshot}
|
|
305
|
-
* @this {SpessaSynthProcessor}
|
|
306
|
-
*/
|
|
307
|
-
export function applySynthesizerSnapshot(snapshot)
|
|
308
|
-
{
|
|
309
|
-
SynthesizerSnapshot.applySnapshot(this, snapshot);
|
|
310
|
-
SpessaSynthInfo("%cFinished applying snapshot!", consoleColors.info);
|
|
311
|
-
}
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import { consoleColors } from "../../../utils/other.js";
|
|
2
|
-
import { midiControllers } from "../../../midi_parser/midi_message.js";
|
|
3
|
-
import { channelConfiguration, getBankSelect, setBankSelect } from "../worklet_utilities/worklet_processor_channel.js";
|
|
4
|
-
import { computeModulators } from "../worklet_utilities/worklet_modulator.js";
|
|
5
|
-
import { SpessaSynthInfo, SpessaSynthWarn } from "../../../utils/loggin.js";
|
|
6
|
-
import { SYNTHESIZER_GAIN } from "../main_processor.js";
|
|
7
|
-
import { DEFAULT_PERCUSSION } from "../../synthetizer.js";
|
|
8
|
-
import { dataEntryStates } from "../worklet_utilities/controller_tables.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @param channel {number}
|
|
12
|
-
* @param controllerNumber {number}
|
|
13
|
-
* @param controllerValue {number}
|
|
14
|
-
* @param force {boolean}
|
|
15
|
-
* @this {SpessaSynthProcessor}
|
|
16
|
-
*/
|
|
17
|
-
export function controllerChange(channel, controllerNumber, controllerValue, force = false)
|
|
18
|
-
{
|
|
19
|
-
/**
|
|
20
|
-
* @type {WorkletProcessorChannel}
|
|
21
|
-
*/
|
|
22
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
23
|
-
if (channelObject === undefined)
|
|
24
|
-
{
|
|
25
|
-
SpessaSynthWarn(`Trying to access channel ${channel} which does not exist... ignoring!`);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
if (controllerNumber > 127)
|
|
29
|
-
{
|
|
30
|
-
// channel configuration. force must be set to true
|
|
31
|
-
if (!force)
|
|
32
|
-
{
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
switch (controllerNumber)
|
|
36
|
-
{
|
|
37
|
-
default:
|
|
38
|
-
return;
|
|
39
|
-
|
|
40
|
-
case channelConfiguration.velocityOverride:
|
|
41
|
-
channelObject.velocityOverride = controllerValue;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// lsb controller values: append them as the lower nibble of the 14-bit value
|
|
46
|
-
// excluding bank select and data entry as it's handled separately
|
|
47
|
-
if (
|
|
48
|
-
controllerNumber >= midiControllers.lsbForControl1ModulationWheel
|
|
49
|
-
&& controllerNumber <= midiControllers.lsbForControl13EffectControl2
|
|
50
|
-
&& controllerNumber !== midiControllers.lsbForControl6DataEntry
|
|
51
|
-
)
|
|
52
|
-
{
|
|
53
|
-
const actualCCNum = controllerNumber - 32;
|
|
54
|
-
if (channelObject.lockedControllers[actualCCNum])
|
|
55
|
-
{
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
// append the lower nibble to the main controller
|
|
59
|
-
channelObject.midiControllers[actualCCNum] = (channelObject.midiControllers[actualCCNum] & 0x3F80) | (controllerValue & 0x7F);
|
|
60
|
-
channelObject.voices.forEach(v => computeModulators(v, channelObject.midiControllers, 1, actualCCNum));
|
|
61
|
-
}
|
|
62
|
-
switch (controllerNumber)
|
|
63
|
-
{
|
|
64
|
-
case midiControllers.allNotesOff:
|
|
65
|
-
this.stopAll(channel);
|
|
66
|
-
break;
|
|
67
|
-
|
|
68
|
-
case midiControllers.allSoundOff:
|
|
69
|
-
this.stopAll(channel, true);
|
|
70
|
-
break;
|
|
71
|
-
|
|
72
|
-
// special case: bank select
|
|
73
|
-
case midiControllers.bankSelect:
|
|
74
|
-
let bankNr = controllerValue;
|
|
75
|
-
if (!force)
|
|
76
|
-
{
|
|
77
|
-
switch (this.system)
|
|
78
|
-
{
|
|
79
|
-
case "gm":
|
|
80
|
-
// gm ignores bank select
|
|
81
|
-
SpessaSynthInfo(
|
|
82
|
-
`%cIgnoring the Bank Select (${controllerValue}), as the synth is in GM mode.`,
|
|
83
|
-
consoleColors.info
|
|
84
|
-
);
|
|
85
|
-
return;
|
|
86
|
-
|
|
87
|
-
case "xg":
|
|
88
|
-
// for xg, if msb is 120, 126 or 127, then it's drums
|
|
89
|
-
if (bankNr === 120 || bankNr === 126 || bankNr === 127)
|
|
90
|
-
{
|
|
91
|
-
this.setDrums(channel, true);
|
|
92
|
-
}
|
|
93
|
-
else
|
|
94
|
-
{
|
|
95
|
-
// drums shall not be disabled on channel 9
|
|
96
|
-
if (channel % 16 !== DEFAULT_PERCUSSION)
|
|
97
|
-
{
|
|
98
|
-
this.setDrums(channel, false);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
break;
|
|
102
|
-
|
|
103
|
-
case "gm2":
|
|
104
|
-
if (bankNr === 120)
|
|
105
|
-
{
|
|
106
|
-
this.setDrums(channel, true);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (channelObject.drumChannel)
|
|
111
|
-
{
|
|
112
|
-
// 128 for percussion channel
|
|
113
|
-
bankNr = 128;
|
|
114
|
-
}
|
|
115
|
-
if (bankNr === 128 && !channelObject.drumChannel)
|
|
116
|
-
{
|
|
117
|
-
// if a channel is not for percussion, default to bank current
|
|
118
|
-
bankNr = getBankSelect(channelObject);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
setBankSelect(channelObject, bankNr);
|
|
123
|
-
break;
|
|
124
|
-
|
|
125
|
-
case midiControllers.lsbForControl0BankSelect:
|
|
126
|
-
if (this.system === "xg")
|
|
127
|
-
{
|
|
128
|
-
if (!channelObject.drumChannel)
|
|
129
|
-
{
|
|
130
|
-
// some soundfonts use 127 as drums and
|
|
131
|
-
// if it's not marked as drums by bank MSB (line 47), then we DO NOT want the drums!
|
|
132
|
-
if (controllerValue !== 127)
|
|
133
|
-
{
|
|
134
|
-
setBankSelect(channelObject, controllerValue);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
else if (this.system === "gm2")
|
|
139
|
-
{
|
|
140
|
-
setBankSelect(channelObject, controllerValue);
|
|
141
|
-
}
|
|
142
|
-
break;
|
|
143
|
-
|
|
144
|
-
// check for RPN and NPRN and data entry
|
|
145
|
-
case midiControllers.RPNLsb:
|
|
146
|
-
channelObject.RPValue = channelObject.RPValue << 7 | controllerValue;
|
|
147
|
-
channelObject.dataEntryState = dataEntryStates.RPFine;
|
|
148
|
-
break;
|
|
149
|
-
|
|
150
|
-
case midiControllers.RPNMsb:
|
|
151
|
-
channelObject.RPValue = controllerValue;
|
|
152
|
-
channelObject.dataEntryState = dataEntryStates.RPCoarse;
|
|
153
|
-
break;
|
|
154
|
-
|
|
155
|
-
case midiControllers.NRPNMsb:
|
|
156
|
-
channelObject.NRPCoarse = controllerValue;
|
|
157
|
-
channelObject.dataEntryState = dataEntryStates.NRPCoarse;
|
|
158
|
-
break;
|
|
159
|
-
|
|
160
|
-
case midiControllers.NRPNLsb:
|
|
161
|
-
channelObject.NRPFine = controllerValue;
|
|
162
|
-
channelObject.dataEntryState = dataEntryStates.NRPFine;
|
|
163
|
-
break;
|
|
164
|
-
|
|
165
|
-
case midiControllers.dataEntryMsb:
|
|
166
|
-
this.dataEntryCoarse(channel, controllerValue);
|
|
167
|
-
break;
|
|
168
|
-
|
|
169
|
-
case midiControllers.lsbForControl6DataEntry:
|
|
170
|
-
this.dataEntryFine(channel, controllerValue);
|
|
171
|
-
break;
|
|
172
|
-
|
|
173
|
-
case midiControllers.resetAllControllers:
|
|
174
|
-
this.resetControllers(channel);
|
|
175
|
-
break;
|
|
176
|
-
|
|
177
|
-
case midiControllers.sustainPedal:
|
|
178
|
-
if (controllerValue >= 64)
|
|
179
|
-
{
|
|
180
|
-
channelObject.holdPedal = true;
|
|
181
|
-
}
|
|
182
|
-
else
|
|
183
|
-
{
|
|
184
|
-
channelObject.holdPedal = false;
|
|
185
|
-
channelObject.sustainedVoices.forEach(v =>
|
|
186
|
-
{
|
|
187
|
-
this.releaseVoice(v);
|
|
188
|
-
});
|
|
189
|
-
channelObject.sustainedVoices = [];
|
|
190
|
-
}
|
|
191
|
-
break;
|
|
192
|
-
|
|
193
|
-
// default: apply the controller to the table
|
|
194
|
-
default:
|
|
195
|
-
if (channelObject.lockedControllers[controllerNumber])
|
|
196
|
-
{
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
channelObject.midiControllers[controllerNumber] = controllerValue << 7;
|
|
200
|
-
channelObject.voices.forEach(v => computeModulators(v, channelObject.midiControllers, 1, controllerNumber));
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
this.callEvent("controllerchange", {
|
|
204
|
-
channel: channel,
|
|
205
|
-
controllerNumber: controllerNumber,
|
|
206
|
-
controllerValue: controllerValue
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* @param volume {number} 0 to 1
|
|
212
|
-
* @this {SpessaSynthProcessor}
|
|
213
|
-
*/
|
|
214
|
-
export function setMIDIVolume(volume)
|
|
215
|
-
{
|
|
216
|
-
this.midiVolume = volume;
|
|
217
|
-
this.setMasterPan(this.pan);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* @param volume {number} 0-1
|
|
222
|
-
* @this {SpessaSynthProcessor}
|
|
223
|
-
*/
|
|
224
|
-
export function setMasterGain(volume)
|
|
225
|
-
{
|
|
226
|
-
this.masterGain = volume * SYNTHESIZER_GAIN;
|
|
227
|
-
this.setMasterPan(this.pan);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* @param pan {number} -1 to one
|
|
232
|
-
* @this {SpessaSynthProcessor}
|
|
233
|
-
*/
|
|
234
|
-
export function setMasterPan(pan)
|
|
235
|
-
{
|
|
236
|
-
this.pan = pan;
|
|
237
|
-
// clamp to 0-1 (0 is left)
|
|
238
|
-
pan = (pan / 2) + 0.5;
|
|
239
|
-
this.panLeft = (1 - pan);
|
|
240
|
-
this.panRight = (pan);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* @param channel {number}
|
|
245
|
-
* @param isMuted {boolean}
|
|
246
|
-
* @this {SpessaSynthProcessor}
|
|
247
|
-
*/
|
|
248
|
-
export function muteChannel(channel, isMuted)
|
|
249
|
-
{
|
|
250
|
-
if (isMuted)
|
|
251
|
-
{
|
|
252
|
-
this.stopAll(channel, true);
|
|
253
|
-
}
|
|
254
|
-
this.workletProcessorChannels[channel].isMuted = isMuted;
|
|
255
|
-
this.sendChannelProperties();
|
|
256
|
-
this.callEvent("mutechannel", {
|
|
257
|
-
channel: channel,
|
|
258
|
-
isMuted: isMuted
|
|
259
|
-
});
|
|
260
|
-
}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { consoleColors } from "../../../utils/other.js";
|
|
2
|
-
import { SpessaSynthInfo, SpessaSynthWarn } from "../../../utils/loggin.js";
|
|
3
|
-
import { generatorTypes } from "../../../soundfont/basic_soundfont/generator.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Release a note
|
|
7
|
-
* @param channel {number}
|
|
8
|
-
* @param midiNote {number}
|
|
9
|
-
* @this {SpessaSynthProcessor}
|
|
10
|
-
*/
|
|
11
|
-
export function noteOff(channel, midiNote)
|
|
12
|
-
{
|
|
13
|
-
if (midiNote > 127 || midiNote < 0)
|
|
14
|
-
{
|
|
15
|
-
SpessaSynthWarn(`Received a noteOn for note`, midiNote, "Ignoring.");
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
let realKey = midiNote + this.workletProcessorChannels[channel].channelTransposeKeyShift;
|
|
20
|
-
|
|
21
|
-
// if high performance mode, kill notes instead of stopping them
|
|
22
|
-
if (this.highPerformanceMode)
|
|
23
|
-
{
|
|
24
|
-
// if the channel is percussion channel, do not kill the notes
|
|
25
|
-
if (!this.workletProcessorChannels[channel].drumChannel)
|
|
26
|
-
{
|
|
27
|
-
this.killNote(channel, realKey);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const channelVoices = this.workletProcessorChannels[channel].voices;
|
|
33
|
-
channelVoices.forEach(v =>
|
|
34
|
-
{
|
|
35
|
-
if (v.realKey !== realKey || v.isInRelease === true)
|
|
36
|
-
{
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
// if hold pedal, move to sustain
|
|
40
|
-
if (this.workletProcessorChannels[channel].holdPedal)
|
|
41
|
-
{
|
|
42
|
-
this.workletProcessorChannels[channel].sustainedVoices.push(v);
|
|
43
|
-
}
|
|
44
|
-
else
|
|
45
|
-
{
|
|
46
|
-
this.releaseVoice(v);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
this.callEvent("noteoff", {
|
|
50
|
-
midiNote: midiNote,
|
|
51
|
-
channel: channel
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Stops a note nearly instantly
|
|
57
|
-
* @param channel {number}
|
|
58
|
-
* @param midiNote {number}
|
|
59
|
-
* @this {SpessaSynthProcessor}
|
|
60
|
-
*/
|
|
61
|
-
export function killNote(channel, midiNote)
|
|
62
|
-
{
|
|
63
|
-
this.workletProcessorChannels[channel].voices.forEach(v =>
|
|
64
|
-
{
|
|
65
|
-
if (v.realKey !== midiNote)
|
|
66
|
-
{
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
v.modulatedGenerators[generatorTypes.releaseVolEnv] = -12000; // set release to be very short
|
|
70
|
-
this.releaseVoice(v);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* stops all notes on a given channel
|
|
76
|
-
* @param channel {number}
|
|
77
|
-
* @param force {boolean}
|
|
78
|
-
* @this {SpessaSynthProcessor}
|
|
79
|
-
*/
|
|
80
|
-
export function stopAll(channel, force = false)
|
|
81
|
-
{
|
|
82
|
-
const channelVoices = this.workletProcessorChannels[channel].voices;
|
|
83
|
-
if (force)
|
|
84
|
-
{
|
|
85
|
-
// force stop all
|
|
86
|
-
channelVoices.length = 0;
|
|
87
|
-
this.workletProcessorChannels[channel].sustainedVoices.length = 0;
|
|
88
|
-
this.sendChannelProperties();
|
|
89
|
-
}
|
|
90
|
-
else
|
|
91
|
-
{
|
|
92
|
-
channelVoices.forEach(v =>
|
|
93
|
-
{
|
|
94
|
-
if (v.isInRelease)
|
|
95
|
-
{
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
this.releaseVoice(v);
|
|
99
|
-
});
|
|
100
|
-
this.workletProcessorChannels[channel].sustainedVoices.forEach(v =>
|
|
101
|
-
{
|
|
102
|
-
this.releaseVoice(v);
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* @this {SpessaSynthProcessor}
|
|
109
|
-
* @param force {boolean}
|
|
110
|
-
*/
|
|
111
|
-
export function stopAllChannels(force = false)
|
|
112
|
-
{
|
|
113
|
-
SpessaSynthInfo("%cStop all received!", consoleColors.info);
|
|
114
|
-
for (let i = 0; i < this.workletProcessorChannels.length; i++)
|
|
115
|
-
{
|
|
116
|
-
this.stopAll(i, force);
|
|
117
|
-
}
|
|
118
|
-
this.callEvent("stopall", undefined);
|
|
119
|
-
}
|