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
package/synthetizer/worklet_system/worklet_methods/{data_entry.js → data_entry/data_entry_coarse.js}
RENAMED
|
@@ -1,32 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { dataEntryStates, NON_CC_INDEX_OFFSET } from "../../worklet_utilities/controller_tables.js";
|
|
2
|
+
import { SpessaSynthInfo, SpessaSynthWarn } from "../../../../utils/loggin.js";
|
|
3
|
+
import { consoleColors } from "../../../../utils/other.js";
|
|
4
|
+
import { midiControllers } from "../../../../midi_parser/midi_message.js";
|
|
5
|
+
import { modulatorSources } from "../../../../soundfont/basic_soundfont/modulator.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Executes a data entry for an NRP for a sc88pro NRP (because touhou yes) and RPN tuning
|
|
9
|
-
* @param channel {number}
|
|
10
9
|
* @param dataValue {number} dataEntryCoarse MSB
|
|
11
|
-
* @this {
|
|
10
|
+
* @this {WorkletProcessorChannel}
|
|
12
11
|
* @private
|
|
13
12
|
*/
|
|
14
|
-
export function dataEntryCoarse(
|
|
13
|
+
export function dataEntryCoarse(dataValue)
|
|
15
14
|
{
|
|
16
|
-
/**
|
|
17
|
-
* @type {WorkletProcessorChannel}
|
|
18
|
-
*/
|
|
19
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
20
15
|
let addDefaultVibrato = () =>
|
|
21
16
|
{
|
|
22
|
-
if (
|
|
17
|
+
if (this.channelVibrato.delay === 0 && this.channelVibrato.rate === 0 && this.channelVibrato.depth === 0)
|
|
23
18
|
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
this.channelVibrato.depth = 50;
|
|
20
|
+
this.channelVibrato.rate = 8;
|
|
21
|
+
this.channelVibrato.delay = 0.6;
|
|
27
22
|
}
|
|
28
23
|
};
|
|
29
|
-
switch (
|
|
24
|
+
switch (this.dataEntryState)
|
|
30
25
|
{
|
|
31
26
|
default:
|
|
32
27
|
case dataEntryStates.Idle:
|
|
@@ -35,15 +30,15 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
35
30
|
// https://cdn.roland.com/assets/media/pdf/SC-88PRO_OM.pdf
|
|
36
31
|
// http://hummer.stanford.edu/sig/doc/classes/MidiOutput/rpn.html
|
|
37
32
|
case dataEntryStates.NRPFine:
|
|
38
|
-
if (this.system !== "gs")
|
|
33
|
+
if (this.synth.system !== "gs")
|
|
39
34
|
{
|
|
40
35
|
return;
|
|
41
36
|
}
|
|
42
|
-
if (
|
|
37
|
+
if (this.lockGSNRPNParams)
|
|
43
38
|
{
|
|
44
39
|
return;
|
|
45
40
|
}
|
|
46
|
-
switch (
|
|
41
|
+
switch (this.NRPCoarse)
|
|
47
42
|
{
|
|
48
43
|
default:
|
|
49
44
|
if (dataValue === 64)
|
|
@@ -52,8 +47,8 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
52
47
|
return;
|
|
53
48
|
}
|
|
54
49
|
SpessaSynthWarn(
|
|
55
|
-
`%cUnrecognized NRPN for %c${
|
|
56
|
-
.toUpperCase()} 0x${
|
|
50
|
+
`%cUnrecognized NRPN for %c${this.channelNumber}%c: %c(0x${this.NRPCoarse.toString(16)
|
|
51
|
+
.toUpperCase()} 0x${this.NRPFine.toString(
|
|
57
52
|
16).toUpperCase()})%c data value: %c${dataValue}`,
|
|
58
53
|
consoleColors.warn,
|
|
59
54
|
consoleColors.recognized,
|
|
@@ -66,7 +61,7 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
66
61
|
|
|
67
62
|
// part parameters: vibrato, cutoff
|
|
68
63
|
case 0x01:
|
|
69
|
-
switch (
|
|
64
|
+
switch (this.NRPFine)
|
|
70
65
|
{
|
|
71
66
|
default:
|
|
72
67
|
if (dataValue === 64)
|
|
@@ -75,7 +70,7 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
75
70
|
return;
|
|
76
71
|
}
|
|
77
72
|
SpessaSynthWarn(
|
|
78
|
-
`%cUnrecognized NRPN for %c${
|
|
73
|
+
`%cUnrecognized NRPN for %c${this.channelNumber}%c: %c(0x${this.NRPCoarse.toString(16)} 0x${this.NRPFine.toString(
|
|
79
74
|
16)})%c data value: %c${dataValue}`,
|
|
80
75
|
consoleColors.warn,
|
|
81
76
|
consoleColors.recognized,
|
|
@@ -93,9 +88,9 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
93
88
|
return;
|
|
94
89
|
}
|
|
95
90
|
addDefaultVibrato();
|
|
96
|
-
|
|
91
|
+
this.channelVibrato.rate = (dataValue / 64) * 8;
|
|
97
92
|
SpessaSynthInfo(
|
|
98
|
-
`%cVibrato rate for %c${
|
|
93
|
+
`%cVibrato rate for %c${this.channelNumber}%c is now set to %c${dataValue} = ${this.channelVibrato.rate}%cHz.`,
|
|
99
94
|
consoleColors.info,
|
|
100
95
|
consoleColors.recognized,
|
|
101
96
|
consoleColors.info,
|
|
@@ -111,9 +106,9 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
111
106
|
return;
|
|
112
107
|
}
|
|
113
108
|
addDefaultVibrato();
|
|
114
|
-
|
|
109
|
+
this.channelVibrato.depth = dataValue / 2;
|
|
115
110
|
SpessaSynthInfo(
|
|
116
|
-
`%cVibrato depth for %c${
|
|
111
|
+
`%cVibrato depth for %c${this.channelNumber}%c is now set to %c${dataValue} = ${this.channelVibrato.depth}%c cents range of detune.`,
|
|
117
112
|
consoleColors.info,
|
|
118
113
|
consoleColors.recognized,
|
|
119
114
|
consoleColors.info,
|
|
@@ -129,9 +124,9 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
129
124
|
return;
|
|
130
125
|
}
|
|
131
126
|
addDefaultVibrato();
|
|
132
|
-
|
|
127
|
+
this.channelVibrato.delay = (dataValue / 64) / 3;
|
|
133
128
|
SpessaSynthInfo(
|
|
134
|
-
`%cVibrato delay for %c${
|
|
129
|
+
`%cVibrato delay for %c${this.channelNumber}%c is now set to %c${dataValue} = ${this.channelVibrato.delay}%c seconds.`,
|
|
135
130
|
consoleColors.info,
|
|
136
131
|
consoleColors.recognized,
|
|
137
132
|
consoleColors.info,
|
|
@@ -144,9 +139,9 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
144
139
|
case 0x20:
|
|
145
140
|
// affect the "brightness" controller as we have a default modulator that controls it
|
|
146
141
|
const ccValue = dataValue;
|
|
147
|
-
this.controllerChange(
|
|
142
|
+
this.controllerChange(midiControllers.brightness, dataValue);
|
|
148
143
|
SpessaSynthInfo(
|
|
149
|
-
`%cFilter cutoff for %c${
|
|
144
|
+
`%cFilter cutoff for %c${this.channelNumber}%c is now set to %c${ccValue}`,
|
|
150
145
|
consoleColors.info,
|
|
151
146
|
consoleColors.recognized,
|
|
152
147
|
consoleColors.info,
|
|
@@ -158,9 +153,9 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
158
153
|
// drum reverb
|
|
159
154
|
case 0x1D:
|
|
160
155
|
const reverb = dataValue;
|
|
161
|
-
this.controllerChange(
|
|
156
|
+
this.controllerChange(midiControllers.reverbDepth, reverb);
|
|
162
157
|
SpessaSynthInfo(
|
|
163
|
-
`%cGS Drum reverb for %c${
|
|
158
|
+
`%cGS Drum reverb for %c${this.channelNumber}%c: %c${reverb}`,
|
|
164
159
|
consoleColors.info,
|
|
165
160
|
consoleColors.recognized,
|
|
166
161
|
consoleColors.info,
|
|
@@ -172,11 +167,11 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
172
167
|
|
|
173
168
|
case dataEntryStates.RPCoarse:
|
|
174
169
|
case dataEntryStates.RPFine:
|
|
175
|
-
switch (
|
|
170
|
+
switch (this.RPValue)
|
|
176
171
|
{
|
|
177
172
|
default:
|
|
178
173
|
SpessaSynthWarn(
|
|
179
|
-
`%cUnrecognized RPN for %c${
|
|
174
|
+
`%cUnrecognized RPN for %c${this.channelNumber}%c: %c(0x${this.RPValue.toString(16)})%c data value: %c${dataValue}`,
|
|
180
175
|
consoleColors.warn,
|
|
181
176
|
consoleColors.recognized,
|
|
182
177
|
consoleColors.warn,
|
|
@@ -188,9 +183,9 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
188
183
|
|
|
189
184
|
// pitch bend range
|
|
190
185
|
case 0x0000:
|
|
191
|
-
|
|
186
|
+
this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] = dataValue << 7;
|
|
192
187
|
SpessaSynthInfo(
|
|
193
|
-
`%cChannel ${
|
|
188
|
+
`%cChannel ${this.channelNumber} bend range. Semitones: %c${dataValue}`,
|
|
194
189
|
consoleColors.info,
|
|
195
190
|
consoleColors.value
|
|
196
191
|
);
|
|
@@ -199,85 +194,23 @@ export function dataEntryCoarse(channel, dataValue)
|
|
|
199
194
|
// coarse tuning
|
|
200
195
|
case 0x0002:
|
|
201
196
|
// semitones
|
|
202
|
-
this.
|
|
197
|
+
this.setTuningSemitones(dataValue - 64);
|
|
203
198
|
break;
|
|
204
199
|
|
|
205
|
-
// fine
|
|
200
|
+
// fine-tuning
|
|
206
201
|
case 0x0001:
|
|
207
202
|
// note: this will not work properly unless the lsb is sent!
|
|
208
203
|
// here we store the raw value to then adjust in fine
|
|
209
|
-
this.
|
|
204
|
+
this.setTuning(dataValue - 64, false);
|
|
210
205
|
break;
|
|
211
206
|
|
|
212
207
|
// modulation depth
|
|
213
208
|
case 0x0005:
|
|
214
|
-
this.setModulationDepth(
|
|
209
|
+
this.setModulationDepth(dataValue * 100);
|
|
215
210
|
break;
|
|
216
211
|
|
|
217
212
|
case 0x3FFF:
|
|
218
|
-
this.resetParameters(
|
|
219
|
-
break;
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Executes a data entry for an RPN tuning
|
|
228
|
-
* @param channel {number}
|
|
229
|
-
* @param dataValue {number} dataEntry LSB
|
|
230
|
-
* @this {SpessaSynthProcessor}
|
|
231
|
-
* @private
|
|
232
|
-
*/
|
|
233
|
-
export function dataEntryFine(channel, dataValue)
|
|
234
|
-
{
|
|
235
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
236
|
-
switch (channelObject.dataEntryState)
|
|
237
|
-
{
|
|
238
|
-
default:
|
|
239
|
-
break;
|
|
240
|
-
|
|
241
|
-
case dataEntryStates.RPCoarse:
|
|
242
|
-
case dataEntryStates.RPFine:
|
|
243
|
-
switch (channelObject.RPValue)
|
|
244
|
-
{
|
|
245
|
-
default:
|
|
246
|
-
break;
|
|
247
|
-
|
|
248
|
-
// pitch bend range fine tune
|
|
249
|
-
case 0x0000:
|
|
250
|
-
if (dataValue === 0)
|
|
251
|
-
{
|
|
252
|
-
break;
|
|
253
|
-
}
|
|
254
|
-
// 14-bit value, so upper 7 are coarse and lower 7 are fine!
|
|
255
|
-
channelObject.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] |= dataValue;
|
|
256
|
-
const actualTune = (channelObject.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] >> 7) + dataValue / 127;
|
|
257
|
-
SpessaSynthInfo(
|
|
258
|
-
`%cChannel ${channel} bend range. Semitones: %c${actualTune}`,
|
|
259
|
-
consoleColors.info,
|
|
260
|
-
consoleColors.value
|
|
261
|
-
);
|
|
262
|
-
break;
|
|
263
|
-
|
|
264
|
-
// fine tuning
|
|
265
|
-
case 0x0001:
|
|
266
|
-
// grab the data and shift
|
|
267
|
-
const coarse = channelObject.customControllers[customControllers.channelTuning];
|
|
268
|
-
const finalTuning = (coarse << 7) | dataValue;
|
|
269
|
-
this.setChannelTuning(channel, finalTuning * 0.01220703125); // multiply by 8192 / 100 (cent increment)
|
|
270
|
-
break;
|
|
271
|
-
|
|
272
|
-
// modulation depth
|
|
273
|
-
case 0x0005:
|
|
274
|
-
const currentModulationDepthCents = channelObject.customControllers[customControllers.modulationMultiplier] * 50;
|
|
275
|
-
let cents = currentModulationDepthCents + (dataValue / 128) * 100;
|
|
276
|
-
this.setModulationDepth(channel, cents);
|
|
277
|
-
break;
|
|
278
|
-
|
|
279
|
-
case 0x3FFF:
|
|
280
|
-
this.resetParameters(channel);
|
|
213
|
+
this.resetParameters();
|
|
281
214
|
break;
|
|
282
215
|
|
|
283
216
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { consoleColors } from "../../../../utils/other.js";
|
|
2
|
+
import { SpessaSynthInfo } from "../../../../utils/loggin.js";
|
|
3
|
+
import { modulatorSources } from "../../../../soundfont/basic_soundfont/modulator.js";
|
|
4
|
+
import { customControllers, dataEntryStates, NON_CC_INDEX_OFFSET } from "../../worklet_utilities/controller_tables.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Executes a data entry for an RPN tuning
|
|
8
|
+
* @param dataValue {number} dataEntry LSB
|
|
9
|
+
* @this {WorkletProcessorChannel}
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
export function dataEntryFine(dataValue)
|
|
13
|
+
{
|
|
14
|
+
switch (this.dataEntryState)
|
|
15
|
+
{
|
|
16
|
+
default:
|
|
17
|
+
break;
|
|
18
|
+
|
|
19
|
+
case dataEntryStates.RPCoarse:
|
|
20
|
+
case dataEntryStates.RPFine:
|
|
21
|
+
switch (this.RPValue)
|
|
22
|
+
{
|
|
23
|
+
default:
|
|
24
|
+
break;
|
|
25
|
+
|
|
26
|
+
// pitch bend range fine tune
|
|
27
|
+
case 0x0000:
|
|
28
|
+
if (dataValue === 0)
|
|
29
|
+
{
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
// 14-bit value, so upper 7 are coarse and lower 7 are fine!
|
|
33
|
+
this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] |= dataValue;
|
|
34
|
+
const actualTune = (this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] >> 7) + dataValue / 127;
|
|
35
|
+
SpessaSynthInfo(
|
|
36
|
+
`%cChannel ${this.channelNumber} bend range. Semitones: %c${actualTune}`,
|
|
37
|
+
consoleColors.info,
|
|
38
|
+
consoleColors.value
|
|
39
|
+
);
|
|
40
|
+
break;
|
|
41
|
+
|
|
42
|
+
// fine-tuning
|
|
43
|
+
case 0x0001:
|
|
44
|
+
// grab the data and shift
|
|
45
|
+
const coarse = this.customControllers[customControllers.channelTuning];
|
|
46
|
+
const finalTuning = (coarse << 7) | dataValue;
|
|
47
|
+
this.setTuning(finalTuning * 0.01220703125); // multiply by 8192 / 100 (cent increments)
|
|
48
|
+
break;
|
|
49
|
+
|
|
50
|
+
// modulation depth
|
|
51
|
+
case 0x0005:
|
|
52
|
+
const currentModulationDepthCents = this.customControllers[customControllers.modulationMultiplier] * 50;
|
|
53
|
+
let cents = currentModulationDepthCents + (dataValue / 128) * 100;
|
|
54
|
+
this.setModulationDepth(cents);
|
|
55
|
+
break;
|
|
56
|
+
|
|
57
|
+
case 0x3FFF:
|
|
58
|
+
this.resetParameters();
|
|
59
|
+
break;
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param isMuted {boolean}
|
|
3
|
+
* @this {WorkletProcessorChannel}
|
|
4
|
+
*/
|
|
5
|
+
export function muteChannel(isMuted)
|
|
6
|
+
{
|
|
7
|
+
if (isMuted)
|
|
8
|
+
{
|
|
9
|
+
this.stopAllNotes(true);
|
|
10
|
+
}
|
|
11
|
+
this.isMuted = isMuted;
|
|
12
|
+
this.synth.sendChannelProperties();
|
|
13
|
+
this.synth.callEvent("mutechannel", {
|
|
14
|
+
channel: this.channelNumber,
|
|
15
|
+
isMuted: isMuted
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -10,55 +10,53 @@ const EXCLUSIVE_CUTOFF_TIME = -2320;
|
|
|
10
10
|
const EXCLUSIVE_MOD_CUTOFF_TIME = -1130; // less because filter shenanigans
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
* @param channel {number}
|
|
13
|
+
* sends a "MIDI Note on message"
|
|
15
14
|
* @param midiNote {number}
|
|
16
15
|
* @param velocity {number}
|
|
17
16
|
* @param enableDebugging {boolean}
|
|
18
17
|
* @param sendEvent {boolean}
|
|
19
18
|
* @param startTime {number}
|
|
20
|
-
* @this {
|
|
19
|
+
* @this {WorkletProcessorChannel}
|
|
21
20
|
*/
|
|
22
|
-
export function noteOn(
|
|
21
|
+
export function noteOn(midiNote, velocity, enableDebugging = false, sendEvent = true, startTime = currentTime)
|
|
23
22
|
{
|
|
24
23
|
if (velocity < 1)
|
|
25
24
|
{
|
|
26
|
-
this.noteOff(
|
|
25
|
+
this.noteOff(midiNote);
|
|
27
26
|
return;
|
|
28
27
|
}
|
|
29
28
|
velocity = Math.min(127, velocity);
|
|
30
29
|
|
|
31
|
-
const channelObject = this.workletProcessorChannels[channel];
|
|
32
30
|
if (
|
|
33
|
-
(this.highPerformanceMode && this.totalVoicesAmount > 200 && velocity < 40) ||
|
|
34
|
-
(this.highPerformanceMode && velocity < 10) ||
|
|
35
|
-
(
|
|
31
|
+
(this.synth.highPerformanceMode && this.totalVoicesAmount > 200 && velocity < 40) ||
|
|
32
|
+
(this.synth.highPerformanceMode && velocity < 10) ||
|
|
33
|
+
(this.isMuted)
|
|
36
34
|
)
|
|
37
35
|
{
|
|
38
36
|
return;
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
const realKey = midiNote +
|
|
39
|
+
const realKey = midiNote + this.channelTransposeKeyShift;
|
|
42
40
|
let sentMidiNote = realKey;
|
|
43
41
|
|
|
44
42
|
if (realKey > 127 || realKey < 0)
|
|
45
43
|
{
|
|
46
44
|
return;
|
|
47
45
|
}
|
|
48
|
-
const program =
|
|
49
|
-
if (this.tunings[program]?.[realKey]?.midiNote >= 0)
|
|
46
|
+
const program = this.preset.program;
|
|
47
|
+
if (this.synth.tunings[program]?.[realKey]?.midiNote >= 0)
|
|
50
48
|
{
|
|
51
|
-
sentMidiNote = this.tunings[program]?.[realKey].midiNote;
|
|
49
|
+
sentMidiNote = this.synth.tunings[program]?.[realKey].midiNote;
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
// velocity override
|
|
55
|
-
if (
|
|
53
|
+
if (this.velocityOverride > 0)
|
|
56
54
|
{
|
|
57
|
-
velocity =
|
|
55
|
+
velocity = this.velocityOverride;
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
// key velocity override
|
|
61
|
-
const keyVel = this.keyModifierManager.getVelocity(
|
|
59
|
+
const keyVel = this.synth.keyModifierManager.getVelocity(this.channelNumber, realKey);
|
|
62
60
|
if (keyVel > -1)
|
|
63
61
|
{
|
|
64
62
|
velocity = keyVel;
|
|
@@ -67,24 +65,28 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
|
|
|
67
65
|
// portamento
|
|
68
66
|
let portamentoFromKey = -1;
|
|
69
67
|
let portamentoDuration = 0;
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
// note: the 14-bit value needs to go down to 7-bit
|
|
69
|
+
const portamentoTime = this.midiControllers[midiControllers.portamentoTime] >> 7;
|
|
70
|
+
const currentFromKey = this.midiControllers[midiControllers.portamentoControl] >> 7;
|
|
71
|
+
if (
|
|
72
|
+
!this.drumChannel && // no portamento on drum channel
|
|
73
|
+
currentFromKey !== sentMidiNote && // if the same note, there's no portamento
|
|
74
|
+
this.midiControllers[midiControllers.portamentoOnOff] >= 8192 && // (64 << 7)
|
|
75
|
+
portamentoTime > 0 // 0 duration is no portamento
|
|
76
|
+
)
|
|
72
77
|
{
|
|
73
|
-
// note: the 14-bit value needs to go down to 7-bit
|
|
74
|
-
const portamentoTime = channelObject.midiControllers[midiControllers.portamentoTime] >> 7;
|
|
75
78
|
const diff = Math.abs(sentMidiNote - currentFromKey);
|
|
76
79
|
portamentoDuration = portamentoTimeToSeconds(portamentoTime, diff);
|
|
77
80
|
portamentoFromKey = currentFromKey;
|
|
78
81
|
// set portamento control to previous value
|
|
79
|
-
this.controllerChange(
|
|
82
|
+
this.controllerChange(midiControllers.portamentoControl, sentMidiNote);
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
// get voices
|
|
83
|
-
const voices = this.getWorkletVoices(
|
|
84
|
-
|
|
86
|
+
const voices = this.synth.getWorkletVoices(
|
|
87
|
+
this.channelNumber,
|
|
85
88
|
sentMidiNote,
|
|
86
89
|
velocity,
|
|
87
|
-
channelObject,
|
|
88
90
|
startTime,
|
|
89
91
|
realKey,
|
|
90
92
|
enableDebugging
|
|
@@ -92,14 +94,14 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
|
|
|
92
94
|
|
|
93
95
|
// zero means disabled
|
|
94
96
|
let panOverride = 0;
|
|
95
|
-
if (
|
|
97
|
+
if (this.randomPan)
|
|
96
98
|
{
|
|
97
99
|
// the range is -500 to 500
|
|
98
100
|
panOverride = Math.round(Math.random() * 1000 - 500);
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
// add voices
|
|
102
|
-
const channelVoices =
|
|
104
|
+
const channelVoices = this.voices;
|
|
103
105
|
voices.forEach(voice =>
|
|
104
106
|
{
|
|
105
107
|
// apply portamento
|
|
@@ -118,7 +120,7 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
|
|
|
118
120
|
{
|
|
119
121
|
if (v.generators[generatorTypes.exclusiveClass] === exclusive)
|
|
120
122
|
{
|
|
121
|
-
|
|
123
|
+
v.release(MIN_EXCLUSIVE_LENGTH);
|
|
122
124
|
v.modulatedGenerators[generatorTypes.releaseVolEnv] = EXCLUSIVE_CUTOFF_TIME; // make the release nearly instant
|
|
123
125
|
v.modulatedGenerators[generatorTypes.releaseModEnv] = EXCLUSIVE_MOD_CUTOFF_TIME;
|
|
124
126
|
WorkletVolumeEnvelope.recalculate(v);
|
|
@@ -127,7 +129,7 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
|
|
|
127
129
|
});
|
|
128
130
|
}
|
|
129
131
|
// compute all modulators
|
|
130
|
-
computeModulators(voice,
|
|
132
|
+
computeModulators(voice, this.midiControllers);
|
|
131
133
|
// modulate sample offsets (these are not real time)
|
|
132
134
|
const cursorStartOffset = voice.modulatedGenerators[generatorTypes.startAddrsOffset] + voice.modulatedGenerators[generatorTypes.startAddrsCoarseOffset] * 32768;
|
|
133
135
|
const endOffset = voice.modulatedGenerators[generatorTypes.endAddrOffset] + voice.modulatedGenerators[generatorTypes.endAddrsCoarseOffset] * 32768;
|
|
@@ -159,19 +161,19 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
|
|
|
159
161
|
voice.currentPan = Math.max(-500, Math.min(500, voice.modulatedGenerators[generatorTypes.pan])); // -500 to 500
|
|
160
162
|
});
|
|
161
163
|
|
|
162
|
-
this.totalVoicesAmount += voices.length;
|
|
164
|
+
this.synth.totalVoicesAmount += voices.length;
|
|
163
165
|
// cap the voices
|
|
164
|
-
if (this.totalVoicesAmount > this.voiceCap)
|
|
166
|
+
if (this.synth.totalVoicesAmount > this.synth.voiceCap)
|
|
165
167
|
{
|
|
166
|
-
this.voiceKilling(voices.length);
|
|
168
|
+
this.synth.voiceKilling(voices.length);
|
|
167
169
|
}
|
|
168
170
|
channelVoices.push(...voices);
|
|
169
171
|
if (sendEvent)
|
|
170
172
|
{
|
|
171
|
-
this.sendChannelProperties();
|
|
172
|
-
this.callEvent("noteon", {
|
|
173
|
+
this.synth.sendChannelProperties();
|
|
174
|
+
this.synth.callEvent("noteon", {
|
|
173
175
|
midiNote: midiNote,
|
|
174
|
-
channel:
|
|
176
|
+
channel: this.channelNumber,
|
|
175
177
|
velocity: velocity
|
|
176
178
|
});
|
|
177
179
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* executes a program change
|
|
3
|
+
* @param programNumber {number}
|
|
4
|
+
* @param userChange {boolean}
|
|
5
|
+
* @this {WorkletProcessorChannel}
|
|
6
|
+
*/
|
|
7
|
+
export function programChange(programNumber, userChange = false)
|
|
8
|
+
{
|
|
9
|
+
if (this.lockPreset)
|
|
10
|
+
{
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
// always 128 for percussion
|
|
14
|
+
const bank = this.getBankSelect();
|
|
15
|
+
let sentBank;
|
|
16
|
+
let preset;
|
|
17
|
+
|
|
18
|
+
// check if override
|
|
19
|
+
if (this.synth.overrideSoundfont)
|
|
20
|
+
{
|
|
21
|
+
const bankWithOffset = bank === 128 ? 128 : bank - this.synth.soundfontBankOffset;
|
|
22
|
+
const p = this.synth.overrideSoundfont.getPresetNoFallback(bankWithOffset, programNumber);
|
|
23
|
+
if (p)
|
|
24
|
+
{
|
|
25
|
+
sentBank = bank;
|
|
26
|
+
preset = p;
|
|
27
|
+
this.presetUsesOverride = true;
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
{
|
|
31
|
+
preset = this.synth.soundfontManager.getPreset(bank, programNumber);
|
|
32
|
+
sentBank = preset.bank;
|
|
33
|
+
this.presetUsesOverride = false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else
|
|
37
|
+
{
|
|
38
|
+
preset = this.synth.soundfontManager.getPreset(bank, programNumber);
|
|
39
|
+
sentBank = preset.bank;
|
|
40
|
+
this.presetUsesOverride = false;
|
|
41
|
+
}
|
|
42
|
+
this.setPreset(preset);
|
|
43
|
+
this.synth.callEvent("programchange", {
|
|
44
|
+
channel: this.channelNumber,
|
|
45
|
+
program: preset.program,
|
|
46
|
+
bank: sentBank,
|
|
47
|
+
userCalled: userChange
|
|
48
|
+
});
|
|
49
|
+
}
|