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,282 +0,0 @@
1
- import { returnMessageType } from "../message_protocol/worklet_message.js";
2
- import { SpessaSynthInfo, SpessaSynthWarn } from "../../../utils/loggin.js";
3
- import { consoleColors } from "../../../utils/other.js";
4
- import { loadSoundFont } from "../../../soundfont/load_soundfont.js";
5
- import { getBankSelect } from "../worklet_utilities/worklet_processor_channel.js";
6
-
7
- /**
8
- * executes a program change
9
- * @param channel {number}
10
- * @param programNumber {number}
11
- * @param userChange {boolean}
12
- * @this {SpessaSynthProcessor}
13
- */
14
- export function programChange(channel, programNumber, userChange = false)
15
- {
16
- /**
17
- * @type {WorkletProcessorChannel}
18
- */
19
- const channelObject = this.workletProcessorChannels[channel];
20
- if (channelObject === undefined)
21
- {
22
- SpessaSynthWarn(`Trying to access channel ${channel} which does not exist... ignoring!`);
23
- return;
24
- }
25
- if (channelObject.lockPreset)
26
- {
27
- return;
28
- }
29
- // always 128 for percussion
30
- const bank = getBankSelect(channelObject);
31
- let sentBank;
32
- let preset;
33
-
34
- // check if override
35
- if (this.overrideSoundfont)
36
- {
37
- const bankWithOffset = bank === 128 ? 128 : bank - this.soundfontBankOffset;
38
- const p = this.overrideSoundfont.getPresetNoFallback(bankWithOffset, programNumber);
39
- if (p)
40
- {
41
- sentBank = bank;
42
- preset = p;
43
- channelObject.presetUsesOverride = true;
44
- }
45
- else
46
- {
47
- preset = this.soundfontManager.getPreset(bank, programNumber);
48
- sentBank = preset.bank;
49
- channelObject.presetUsesOverride = false;
50
- }
51
- }
52
- else
53
- {
54
- preset = this.soundfontManager.getPreset(bank, programNumber);
55
- sentBank = preset.bank;
56
- channelObject.presetUsesOverride = false;
57
- }
58
- this.setPreset(channel, preset);
59
- this.callEvent("programchange", {
60
- channel: channel,
61
- program: preset.program,
62
- bank: sentBank,
63
- userCalled: userChange
64
- });
65
- }
66
-
67
- /**
68
- * @this {SpessaSynthProcessor}
69
- * @param program {number}
70
- * @param bank {number}
71
- * @returns {BasicPreset}
72
- */
73
- export function getPreset(bank, program)
74
- {
75
- if (this.overrideSoundfont)
76
- {
77
- // if overriden soundfont
78
- const bankWithOffset = bank === 128 ? 128 : bank - this.soundfontBankOffset;
79
- const preset = this.overrideSoundfont.getPresetNoFallback(bankWithOffset, program);
80
- if (preset)
81
- {
82
- return preset;
83
- }
84
- }
85
- return this.soundfontManager.getPreset(bank, program);
86
- }
87
-
88
-
89
- /**
90
- * @param channel {number}
91
- * @param preset {BasicPreset}
92
- * @this {SpessaSynthProcessor}
93
- */
94
- export function setPreset(channel, preset)
95
- {
96
- if (this.workletProcessorChannels[channel].lockPreset)
97
- {
98
- return;
99
- }
100
- delete this.workletProcessorChannels[channel].preset;
101
- this.workletProcessorChannels[channel].preset = preset;
102
-
103
- // reset cached voices
104
- this.workletProcessorChannels[channel].cachedVoices = [];
105
- for (let i = 0; i < 128; i++)
106
- {
107
- this.workletProcessorChannels[channel].cachedVoices.push([]);
108
- }
109
- }
110
-
111
- /**
112
- * Toggles drums on a given channel
113
- * @param channel {number}
114
- * @param isDrum {boolean}
115
- * @this {SpessaSynthProcessor}
116
- */
117
- export function setDrums(channel, isDrum)
118
- {
119
- const channelObject = this.workletProcessorChannels[channel];
120
- if (channelObject.lockPreset)
121
- {
122
- return;
123
- }
124
- if (channelObject.drumChannel === isDrum)
125
- {
126
- return;
127
- }
128
- if (isDrum)
129
- {
130
- // clear transpose
131
- channelObject.channelTransposeKeyShift = 0;
132
- channelObject.drumChannel = true;
133
- this.setPreset(channel, this.getPreset(getBankSelect(channelObject), channelObject.preset.program));
134
- }
135
- else
136
- {
137
- channelObject.drumChannel = false;
138
- this.setPreset(
139
- channel,
140
- this.getPreset(
141
- getBankSelect(channelObject),
142
- channelObject.preset.program
143
- )
144
- );
145
- }
146
- channelObject.presetUsesOverride = false;
147
- this.callEvent("drumchange", {
148
- channel: channel,
149
- isDrumChannel: channelObject.drumChannel
150
- });
151
- this.sendChannelProperties();
152
- }
153
-
154
- /**
155
- * @this {SpessaSynthProcessor}
156
- */
157
- export function sendPresetList()
158
- {
159
- /**
160
- * @type {{bank: number, presetName: string, program: number}[]}
161
- */
162
- const mainFont = this.soundfontManager.getPresetList();
163
- if (this.overrideSoundfont !== undefined)
164
- {
165
- this.overrideSoundfont.presets.forEach(p =>
166
- {
167
- const bankCheck = p.bank === 128 ? 128 : p.bank + this.soundfontBankOffset;
168
- const exists = mainFont.find(pr => pr.bank === bankCheck && pr.program === p.program);
169
- if (exists !== undefined)
170
- {
171
- exists.presetName = p.presetName;
172
- }
173
- else
174
- {
175
- mainFont.push({
176
- presetName: p.presetName,
177
- bank: bankCheck,
178
- program: p.program
179
- });
180
- }
181
- });
182
- }
183
- this.callEvent("presetlistchange", mainFont);
184
- }
185
-
186
- /**
187
- * @this {SpessaSynthProcessor}
188
- * @param sendPresets {boolean}
189
- * @param clearOverride {boolean}
190
- */
191
- export function clearSoundFont(sendPresets = true, clearOverride = true)
192
- {
193
- this.stopAllChannels(true);
194
- if (clearOverride)
195
- {
196
- delete this.overrideSoundfont;
197
- this.overrideSoundfont = undefined;
198
- }
199
- this.defaultPreset = this.getPreset(0, 0);
200
- this.drumPreset = this.getPreset(128, 0);
201
-
202
- for (let i = 0; i < this.workletProcessorChannels.length; i++)
203
- {
204
- const channelObject = this.workletProcessorChannels[i];
205
- channelObject.cachedVoices = [];
206
- for (let j = 0; j < 128; j++)
207
- {
208
- channelObject.cachedVoices.push([]);
209
- }
210
- if (!clearOverride || (clearOverride && channelObject.presetUsesOverride))
211
- {
212
- channelObject.lockPreset = false;
213
- }
214
- this.programChange(i, channelObject.preset.program);
215
- }
216
- if (sendPresets)
217
- {
218
- this.sendPresetList();
219
- }
220
- }
221
-
222
- /**
223
- * @param buffer {ArrayBuffer}
224
- * @param isOverride {Boolean}
225
- * @this {SpessaSynthProcessor}
226
- */
227
- export function reloadSoundFont(buffer, isOverride = false)
228
- {
229
- this.clearSoundFont(false, isOverride);
230
- try
231
- {
232
- if (isOverride)
233
- {
234
- this.overrideSoundfont = loadSoundFont(buffer);
235
- // assign sample offset
236
- this.overrideSoundfont.setSampleIDOffset(this.soundfontManager.totalSoundfontOffset);
237
- }
238
- else
239
- {
240
- this.soundfontManager.reloadManager(buffer);
241
- }
242
- }
243
- catch (e)
244
- {
245
- this.post({
246
- messageType: returnMessageType.soundfontError,
247
- messageData: e
248
- });
249
- return;
250
- }
251
- this.defaultPreset = this.getPreset(0, 0);
252
- this.drumPreset = this.getPreset(128, 0);
253
- this.workletProcessorChannels.forEach((c, cNum) =>
254
- {
255
- this.programChange(cNum, c.preset.program);
256
- });
257
- this.post({ messageType: returnMessageType.ready, messageData: undefined });
258
- this.sendPresetList();
259
- SpessaSynthInfo("%cSpessaSynth is ready!", consoleColors.recognized);
260
- }
261
-
262
- /**
263
- * Sets the embedded (RMI soundfont)
264
- * @param font {ArrayBuffer}
265
- * @param offset {number}
266
- * @this {SpessaSynthProcessor}
267
- */
268
- export function setEmbeddedSoundFont(font, offset)
269
- {
270
- // set offet
271
- this.soundfontBankOffset = offset;
272
- this.reloadSoundFont(font, true);
273
- // preload all samples
274
- this.overrideSoundfont.samples.forEach(s => s.getAudioData());
275
-
276
- // apply snapshot again if applicable
277
- if (this._snapshot !== undefined)
278
- {
279
- this.applySynthesizerSnapshot(this._snapshot);
280
- this.resetAllControllers();
281
- }
282
- }
@@ -1,233 +0,0 @@
1
- import { consoleColors } from "../../../utils/other.js";
2
- import { computeModulators } from "../worklet_utilities/worklet_modulator.js";
3
- import { SpessaSynthInfo } from "../../../utils/loggin.js";
4
- import { modulatorSources } from "../../../soundfont/basic_soundfont/modulator.js";
5
- import { customControllers, NON_CC_INDEX_OFFSET } from "../worklet_utilities/controller_tables.js";
6
-
7
- /**
8
- * Transposes all channels by given amount of semitones
9
- * @this {SpessaSynthProcessor}
10
- * @param semitones {number} Can be float
11
- * @param force {boolean} defaults to false, if true transposes the channel even if it's a drum channel
12
- */
13
- export function transposeAllChannels(semitones, force = false)
14
- {
15
- this.transposition = 0;
16
- for (let i = 0; i < this.workletProcessorChannels.length; i++)
17
- {
18
- this.transposeChannel(i, semitones, force);
19
- }
20
- this.transposition = semitones;
21
- }
22
-
23
- /**
24
- * Transposes the channel by given amount of semitones
25
- * @this {SpessaSynthProcessor}
26
- * @param channel {number}
27
- * @param semitones {number} Can be float
28
- * @param force {boolean} defaults to false, if true transposes the channel even if it's a drum channel
29
- */
30
- export function transposeChannel(channel, semitones, force = false)
31
- {
32
- const channelObject = this.workletProcessorChannels[channel];
33
- if (!channelObject.drumChannel)
34
- {
35
- semitones += this.transposition;
36
- }
37
- const keyShift = Math.trunc(semitones);
38
- const currentTranspose = channelObject.channelTransposeKeyShift + channelObject.customControllers[customControllers.channelTransposeFine] / 100;
39
- if (
40
- (channelObject.drumChannel && !force)
41
- || semitones === currentTranspose
42
- )
43
- {
44
- return;
45
- }
46
- if (keyShift !== channelObject.channelTransposeKeyShift)
47
- {
48
- this.stopAll(channel, false);
49
- }
50
- // apply transpose
51
- channelObject.channelTransposeKeyShift = keyShift;
52
- channelObject.customControllers[customControllers.channelTransposeFine] = (semitones - keyShift) * 100;
53
- }
54
-
55
- /**
56
- * Sets the channel's tuning
57
- * @this {SpessaSynthProcessor}
58
- * @param channel {number}
59
- * @param cents {number}
60
- * @param log {boolean}
61
- */
62
- export function setChannelTuning(channel, cents, log = true)
63
- {
64
- const channelObject = this.workletProcessorChannels[channel];
65
- cents = Math.round(cents);
66
- channelObject.customControllers[customControllers.channelTuning] = cents;
67
- if (!log)
68
- {
69
- return;
70
- }
71
- SpessaSynthInfo(
72
- `%cChannel ${channel} fine tuning. Cents: %c${cents}`,
73
- consoleColors.info,
74
- consoleColors.value
75
- );
76
- }
77
-
78
- /**
79
- * Sets the channel's tuning in semitones
80
- * @param channel {number}
81
- * @param semitones {number}
82
- * @this {SpessaSynthProcessor}
83
- */
84
- export function setChannelTuningSemitones(channel, semitones)
85
- {
86
- const channelObject = this.workletProcessorChannels[channel];
87
- semitones = Math.round(semitones);
88
- channelObject.customControllers[customControllers.channelTuningSemitones] = semitones;
89
- SpessaSynthInfo(
90
- `%cChannel ${channel} coarse tuning. Semitones: %c${semitones}`,
91
- consoleColors.info,
92
- consoleColors.value
93
- );
94
- }
95
-
96
- /**
97
- * Sets the worklet's master tuning
98
- * @this {SpessaSynthProcessor}
99
- * @param cents {number}
100
- */
101
- export function setMasterTuning(cents)
102
- {
103
- cents = Math.round(cents);
104
- for (let i = 0; i < this.workletProcessorChannels.length; i++)
105
- {
106
- this.workletProcessorChannels[i].customControllers[customControllers.masterTuning] = cents;
107
- }
108
- }
109
-
110
- /**
111
- * @this {SpessaSynthProcessor}
112
- * @param channel {number}
113
- * @param cents {number}
114
- */
115
- export function setModulationDepth(channel, cents)
116
- {
117
- let channelObject = this.workletProcessorChannels[channel];
118
- cents = Math.round(cents);
119
- SpessaSynthInfo(
120
- `%cChannel ${channel} modulation depth. Cents: %c${cents}`,
121
- consoleColors.info,
122
- consoleColors.value
123
- );
124
- /* ==============
125
- IMPORTANT
126
- here we convert cents into a multiplier.
127
- midi spec assumes the default is 50 cents,
128
- but it might be different for the soundfont
129
- so we create a multiplier by divinging cents by 50.
130
- for example, if we want 100 cents, then multiplier will be 2,
131
- which for a preset with depth of 50 will create 100.
132
- ================ */
133
- channelObject.customControllers[customControllers.modulationMultiplier] = cents / 50;
134
- }
135
-
136
- /**
137
- * Sets the pitch of the given channel
138
- * @this {SpessaSynthProcessor}
139
- * @param channel {number} usually 0-15: the channel to change pitch
140
- * @param MSB {number} SECOND byte of the MIDI pitchWheel message
141
- * @param LSB {number} FIRST byte of the MIDI pitchWheel message
142
- */
143
- export function pitchWheel(channel, MSB, LSB)
144
- {
145
- if (this.workletProcessorChannels[channel].lockedControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel])
146
- {
147
- return;
148
- }
149
- const bend = (LSB | (MSB << 7));
150
- this.callEvent("pitchwheel", {
151
- channel: channel,
152
- MSB: MSB,
153
- LSB: LSB
154
- });
155
- this.workletProcessorChannels[channel].midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel] = bend;
156
- this.workletProcessorChannels[channel].voices.forEach(v =>
157
- // compute pitch modulators
158
- computeModulators(
159
- v,
160
- this.workletProcessorChannels[channel].midiControllers,
161
- 0,
162
- modulatorSources.pitchWheel
163
- ));
164
- this.sendChannelProperties();
165
- }
166
-
167
- /**
168
- * Sets the pressure of the given channel
169
- * @this {SpessaSynthProcessor}
170
- * @param channel {number} usually 0-15: the channel to change pitch
171
- * @param pressure {number} the pressure of the channel
172
- */
173
- export function channelPressure(channel, pressure)
174
- {
175
- const channelObject = this.workletProcessorChannels[channel];
176
- channelObject.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.channelPressure] = pressure << 7;
177
- this.workletProcessorChannels[channel].voices.forEach(v =>
178
- computeModulators(
179
- v,
180
- channelObject.midiControllers,
181
- 0,
182
- modulatorSources.channelPressure
183
- ));
184
- this.callEvent("channelpressure", {
185
- channel: channel,
186
- pressure: pressure
187
- });
188
- }
189
-
190
- /**
191
- * Sets the pressure of the given note on a specific channel
192
- * @this {SpessaSynthProcessor}
193
- * @param channel {number} usually 0-15: the channel to change pitch
194
- * @param midiNote {number} 0-127
195
- * @param pressure {number} the pressure of the note
196
- */
197
- export function polyPressure(channel, midiNote, pressure)
198
- {
199
- this.workletProcessorChannels[channel].voices.forEach(v =>
200
- {
201
- if (v.midiNote !== midiNote)
202
- {
203
- return;
204
- }
205
- v.pressure = pressure;
206
- computeModulators(
207
- v,
208
- this.workletProcessorChannels[channel].midiControllers,
209
- 0,
210
- modulatorSources.polyPressure
211
- );
212
- });
213
- this.callEvent("polypressure", {
214
- channel: channel,
215
- midiNote: midiNote,
216
- pressure: pressure
217
- });
218
- }
219
-
220
- /**
221
- * Sets the octave tuning for a given channel
222
- * @this {SpessaSynthProcessor}
223
- * @param channel {number} usually 0-15: the channel to use
224
- * @param tuning {Int8Array} LENGTH of 12!!! relative cent tuning. min -128 max 127.
225
- */
226
- export function setOctaveTuning(channel, tuning)
227
- {
228
- if (tuning.length !== 12)
229
- {
230
- throw new Error("Tuning is not the length of 12.");
231
- }
232
- this.workletProcessorChannels[channel].channelOctaveTuning = tuning;
233
- }
@@ -1,29 +0,0 @@
1
- /**
2
- * @param channel {number}
3
- * @this {SpessaSynthProcessor}
4
- */
5
- export function disableAndLockGSNRPN(channel)
6
- {
7
- this.workletProcessorChannels[channel].lockGSNRPNParams = true;
8
- this.workletProcessorChannels[channel].channelVibrato.rate = 0;
9
- this.workletProcessorChannels[channel].channelVibrato.delay = 0;
10
- this.workletProcessorChannels[channel].channelVibrato.depth = 0;
11
- }
12
-
13
- /**
14
- * @param channel {number}
15
- * @param depth {number}
16
- * @param rate {number}
17
- * @param delay {number}
18
- * @this {SpessaSynthProcessor}
19
- */
20
- export function setVibrato(channel, depth, rate, delay)
21
- {
22
- if (this.workletProcessorChannels[channel].lockGSNRPNParams)
23
- {
24
- return;
25
- }
26
- this.workletProcessorChannels[channel].channelVibrato.rate = rate;
27
- this.workletProcessorChannels[channel].channelVibrato.delay = delay;
28
- this.workletProcessorChannels[channel].channelVibrato.depth = depth;
29
- }