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.
Files changed (61) hide show
  1. package/midi_parser/basic_midi.js +457 -68
  2. package/midi_parser/midi_loader.js +18 -503
  3. package/midi_parser/midi_message.js +18 -5
  4. package/midi_parser/midi_sequence.js +2 -2
  5. package/package.json +1 -1
  6. package/sequencer/worklet_sequencer/process_event.js +1 -6
  7. package/synthetizer/synthetizer.js +13 -7
  8. package/synthetizer/worklet_processor.min.js +12 -12
  9. package/synthetizer/worklet_system/README.md +2 -2
  10. package/synthetizer/worklet_system/main_processor.js +106 -95
  11. package/synthetizer/worklet_system/message_protocol/handle_message.js +22 -17
  12. package/synthetizer/worklet_system/message_protocol/worklet_message.js +2 -1
  13. package/synthetizer/worklet_system/snapshot/apply_synthesizer_snapshot.js +14 -0
  14. package/synthetizer/worklet_system/snapshot/channel_snapshot.js +166 -0
  15. package/synthetizer/worklet_system/snapshot/send_synthesizer_snapshot.js +14 -0
  16. package/synthetizer/worklet_system/snapshot/synthesizer_snapshot.js +121 -0
  17. package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +196 -0
  18. package/synthetizer/worklet_system/worklet_methods/controller_control/master_parameters.js +34 -0
  19. package/synthetizer/worklet_system/worklet_methods/{reset_controllers.js → controller_control/reset_controllers.js} +33 -39
  20. package/synthetizer/worklet_system/worklet_methods/create_worklet_channel.js +26 -0
  21. package/synthetizer/worklet_system/worklet_methods/{data_entry.js → data_entry/data_entry_coarse.js} +38 -105
  22. package/synthetizer/worklet_system/worklet_methods/data_entry/data_entry_fine.js +64 -0
  23. package/synthetizer/worklet_system/worklet_methods/mute_channel.js +17 -0
  24. package/synthetizer/worklet_system/worklet_methods/note_on.js +36 -34
  25. package/synthetizer/worklet_system/worklet_methods/program_change.js +49 -0
  26. package/synthetizer/worklet_system/worklet_methods/{voice_control.js → render_voice.js} +37 -120
  27. package/synthetizer/worklet_system/worklet_methods/soundfont_management/clear_sound_font.js +35 -0
  28. package/synthetizer/worklet_system/worklet_methods/soundfont_management/get_preset.js +20 -0
  29. package/synthetizer/worklet_system/worklet_methods/soundfont_management/reload_sound_font.js +43 -0
  30. package/synthetizer/worklet_system/worklet_methods/soundfont_management/send_preset_list.js +31 -0
  31. package/synthetizer/worklet_system/worklet_methods/soundfont_management/set_embedded_sound_font.js +21 -0
  32. package/synthetizer/worklet_system/worklet_methods/stopping_notes/kill_note.js +20 -0
  33. package/synthetizer/worklet_system/worklet_methods/stopping_notes/note_off.js +55 -0
  34. package/synthetizer/worklet_system/worklet_methods/stopping_notes/stop_all_channels.js +16 -0
  35. package/synthetizer/worklet_system/worklet_methods/stopping_notes/stop_all_notes.js +30 -0
  36. package/synthetizer/worklet_system/worklet_methods/stopping_notes/voice_killing.js +63 -0
  37. package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +31 -30
  38. package/synthetizer/worklet_system/worklet_methods/tuning_control/channel_pressure.js +24 -0
  39. package/synthetizer/worklet_system/worklet_methods/tuning_control/pitch_wheel.js +33 -0
  40. package/synthetizer/worklet_system/worklet_methods/tuning_control/poly_pressure.js +31 -0
  41. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_master_tuning.js +15 -0
  42. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_modulation_depth.js +27 -0
  43. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_octave_tuning.js +15 -0
  44. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning.js +24 -0
  45. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning_semitones.js +19 -0
  46. package/synthetizer/worklet_system/worklet_methods/tuning_control/transpose_all_channels.js +15 -0
  47. package/synthetizer/worklet_system/worklet_methods/tuning_control/transpose_channel.js +31 -0
  48. package/synthetizer/worklet_system/worklet_utilities/controller_tables.js +10 -1
  49. package/synthetizer/worklet_system/worklet_utilities/lfo.js +2 -1
  50. package/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +4 -4
  51. package/synthetizer/worklet_system/worklet_utilities/modulator_curves.js +4 -5
  52. package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +18 -18
  53. package/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +210 -206
  54. package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +354 -108
  55. package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +22 -9
  56. package/synthetizer/worklet_system/snapshot/snapshot.js +0 -311
  57. package/synthetizer/worklet_system/worklet_methods/controller_control.js +0 -260
  58. package/synthetizer/worklet_system/worklet_methods/note_off.js +0 -119
  59. package/synthetizer/worklet_system/worklet_methods/program_control.js +0 -282
  60. package/synthetizer/worklet_system/worklet_methods/tuning_control.js +0 -233
  61. package/synthetizer/worklet_system/worklet_methods/vibrato_control.js +0 -29
@@ -1,32 +1,27 @@
1
- import { consoleColors } from "../../../utils/other.js";
2
- import { midiControllers } from "../../../midi_parser/midi_message.js";
3
- import { SpessaSynthInfo, SpessaSynthWarn } from "../../../utils/loggin.js";
4
- import { modulatorSources } from "../../../soundfont/basic_soundfont/modulator.js";
5
- import { customControllers, dataEntryStates, NON_CC_INDEX_OFFSET } from "../worklet_utilities/controller_tables.js";
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 {SpessaSynthProcessor}
10
+ * @this {WorkletProcessorChannel}
12
11
  * @private
13
12
  */
14
- export function dataEntryCoarse(channel, dataValue)
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 (channelObject.channelVibrato.delay === 0 && channelObject.channelVibrato.rate === 0 && channelObject.channelVibrato.depth === 0)
17
+ if (this.channelVibrato.delay === 0 && this.channelVibrato.rate === 0 && this.channelVibrato.depth === 0)
23
18
  {
24
- channelObject.channelVibrato.depth = 50;
25
- channelObject.channelVibrato.rate = 8;
26
- channelObject.channelVibrato.delay = 0.6;
19
+ this.channelVibrato.depth = 50;
20
+ this.channelVibrato.rate = 8;
21
+ this.channelVibrato.delay = 0.6;
27
22
  }
28
23
  };
29
- switch (channelObject.dataEntryState)
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 (channelObject.lockGSNRPNParams)
37
+ if (this.lockGSNRPNParams)
43
38
  {
44
39
  return;
45
40
  }
46
- switch (channelObject.NRPCoarse)
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${channel}%c: %c(0x${channelObject.NRPCoarse.toString(16)
56
- .toUpperCase()} 0x${channelObject.NRPFine.toString(
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 (channelObject.NRPFine)
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${channel}%c: %c(0x${channelObject.NRPCoarse.toString(16)} 0x${channelObject.NRPFine.toString(
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
- channelObject.channelVibrato.rate = (dataValue / 64) * 8;
91
+ this.channelVibrato.rate = (dataValue / 64) * 8;
97
92
  SpessaSynthInfo(
98
- `%cVibrato rate for %c${channel}%c is now set to %c${dataValue} = ${channelObject.channelVibrato.rate}%cHz.`,
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
- channelObject.channelVibrato.depth = dataValue / 2;
109
+ this.channelVibrato.depth = dataValue / 2;
115
110
  SpessaSynthInfo(
116
- `%cVibrato depth for %c${channel}%c is now set to %c${dataValue} = ${channelObject.channelVibrato.depth}%c cents range of detune.`,
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
- channelObject.channelVibrato.delay = (dataValue / 64) / 3;
127
+ this.channelVibrato.delay = (dataValue / 64) / 3;
133
128
  SpessaSynthInfo(
134
- `%cVibrato delay for %c${channel}%c is now set to %c${dataValue} = ${channelObject.channelVibrato.delay}%c seconds.`,
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(channel, midiControllers.brightness, dataValue);
142
+ this.controllerChange(midiControllers.brightness, dataValue);
148
143
  SpessaSynthInfo(
149
- `%cFilter cutoff for %c${channel}%c is now set to %c${ccValue}`,
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(channel, midiControllers.reverbDepth, reverb);
156
+ this.controllerChange(midiControllers.reverbDepth, reverb);
162
157
  SpessaSynthInfo(
163
- `%cGS Drum reverb for %c${channel}%c: %c${reverb}`,
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 (channelObject.RPValue)
170
+ switch (this.RPValue)
176
171
  {
177
172
  default:
178
173
  SpessaSynthWarn(
179
- `%cUnrecognized RPN for %c${channel}%c: %c(0x${channelObject.RPValue.toString(16)})%c data value: %c${dataValue}`,
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
- channelObject.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] = dataValue << 7;
186
+ this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] = dataValue << 7;
192
187
  SpessaSynthInfo(
193
- `%cChannel ${channel} bend range. Semitones: %c${dataValue}`,
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.setChannelTuningSemitones(channel, dataValue - 64);
197
+ this.setTuningSemitones(dataValue - 64);
203
198
  break;
204
199
 
205
- // fine tuning
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.setChannelTuning(channel, (dataValue - 64), false);
204
+ this.setTuning(dataValue - 64, false);
210
205
  break;
211
206
 
212
207
  // modulation depth
213
208
  case 0x0005:
214
- this.setModulationDepth(channel, dataValue * 100);
209
+ this.setModulationDepth(dataValue * 100);
215
210
  break;
216
211
 
217
212
  case 0x3FFF:
218
- this.resetParameters(channel);
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
- * Append the voices
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 {SpessaSynthProcessor}
19
+ * @this {WorkletProcessorChannel}
21
20
  */
22
- export function noteOn(channel, midiNote, velocity, enableDebugging = false, sendEvent = true, startTime = currentTime)
21
+ export function noteOn(midiNote, velocity, enableDebugging = false, sendEvent = true, startTime = currentTime)
23
22
  {
24
23
  if (velocity < 1)
25
24
  {
26
- this.noteOff(channel, midiNote);
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
- (channelObject.isMuted)
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 + channelObject.channelTransposeKeyShift;
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 = channelObject.preset.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 (channelObject.velocityOverride > 0)
53
+ if (this.velocityOverride > 0)
56
54
  {
57
- velocity = channelObject.velocityOverride;
55
+ velocity = this.velocityOverride;
58
56
  }
59
57
 
60
58
  // key velocity override
61
- const keyVel = this.keyModifierManager.getVelocity(channel, realKey);
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
- const currentFromKey = channelObject.midiControllers[midiControllers.portamentoControl] >> 7;
71
- if (!channelObject.drumChannel && currentFromKey !== sentMidiNote && channelObject.midiControllers[midiControllers.portamentoOnOff] >= 8192) // (64 << 7)
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(channel, midiControllers.portamentoControl, sentMidiNote);
82
+ this.controllerChange(midiControllers.portamentoControl, sentMidiNote);
80
83
  }
81
84
 
82
85
  // get voices
83
- const voices = this.getWorkletVoices(
84
- channel,
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 (channelObject.randomPan)
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 = channelObject.voices;
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
- this.releaseVoice(v, MIN_EXCLUSIVE_LENGTH);
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, channelObject.midiControllers);
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: 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
+ }