spessasynth_lib 3.24.13 → 3.24.15
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 +9 -6
- 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 +19 -0
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/note_off.js +51 -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,4 +1,4 @@
|
|
|
1
|
-
## This the worklet system synthesis folder.
|
|
1
|
+
## This is the worklet system synthesis folder.
|
|
2
2
|
|
|
3
3
|
The code here is responsible for a single midi channel, synthesizing the sound to it.
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ The code here is responsible for a single midi channel, synthesizing the sound t
|
|
|
6
6
|
- `worklet_utilities` contains the various digital signal processing functions such as the wavetable oscillator, low
|
|
7
7
|
pass filter, etc.
|
|
8
8
|
|
|
9
|
-
For those interested,
|
|
9
|
+
For those interested, `render_voice.js` file contains the actual DSP synthesis code.
|
|
10
10
|
|
|
11
11
|
`minify_processor.js` uses esbuild to minify the processor code. Importing this instead of `worklet_processor.js` is
|
|
12
12
|
recommended.
|
|
@@ -2,54 +2,31 @@ import { DEFAULT_PERCUSSION, DEFAULT_SYNTH_MODE, VOICE_CAP } from "../synthetize
|
|
|
2
2
|
import { WorkletSequencer } from "../../sequencer/worklet_sequencer/worklet_sequencer.js";
|
|
3
3
|
import { SpessaSynthInfo } from "../../utils/loggin.js";
|
|
4
4
|
import { consoleColors } from "../../utils/other.js";
|
|
5
|
-
import {
|
|
5
|
+
import { voiceKilling } from "./worklet_methods/stopping_notes/voice_killing.js";
|
|
6
6
|
import { ALL_CHANNELS_OR_DIFFERENT_ACTION, returnMessageType } from "./message_protocol/worklet_message.js";
|
|
7
7
|
import { stbvorbis } from "../../externals/stbvorbis_sync/stbvorbis_sync.min.js";
|
|
8
8
|
import { VOLUME_ENVELOPE_SMOOTHING_FACTOR } from "./worklet_utilities/volume_envelope.js";
|
|
9
9
|
import { handleMessage } from "./message_protocol/handle_message.js";
|
|
10
10
|
import { callEvent, sendChannelProperties } from "./message_protocol/message_sending.js";
|
|
11
11
|
import { systemExclusive } from "./worklet_methods/system_exclusive.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
channelPressure,
|
|
16
|
-
pitchWheel,
|
|
17
|
-
polyPressure,
|
|
18
|
-
setChannelTuning,
|
|
19
|
-
setChannelTuningSemitones,
|
|
20
|
-
setMasterTuning,
|
|
21
|
-
setModulationDepth,
|
|
22
|
-
setOctaveTuning,
|
|
23
|
-
transposeAllChannels,
|
|
24
|
-
transposeChannel
|
|
25
|
-
} from "./worklet_methods/tuning_control.js";
|
|
26
|
-
import {
|
|
27
|
-
controllerChange,
|
|
28
|
-
muteChannel,
|
|
29
|
-
setMasterGain,
|
|
30
|
-
setMasterPan,
|
|
31
|
-
setMIDIVolume
|
|
32
|
-
} from "./worklet_methods/controller_control.js";
|
|
33
|
-
import { disableAndLockGSNRPN, setVibrato } from "./worklet_methods/vibrato_control.js";
|
|
34
|
-
import { dataEntryCoarse, dataEntryFine } from "./worklet_methods/data_entry.js";
|
|
35
|
-
import { createWorkletChannel } from "./worklet_utilities/worklet_processor_channel.js";
|
|
36
|
-
import { resetAllControllers, resetControllers, resetParameters } from "./worklet_methods/reset_controllers.js";
|
|
37
|
-
import {
|
|
38
|
-
clearSoundFont,
|
|
39
|
-
getPreset,
|
|
40
|
-
programChange,
|
|
41
|
-
reloadSoundFont,
|
|
42
|
-
sendPresetList,
|
|
43
|
-
setDrums,
|
|
44
|
-
setEmbeddedSoundFont,
|
|
45
|
-
setPreset
|
|
46
|
-
} from "./worklet_methods/program_control.js";
|
|
47
|
-
import { applySynthesizerSnapshot, sendSynthesizerSnapshot } from "./snapshot/snapshot.js";
|
|
12
|
+
import { setMasterGain, setMasterPan, setMIDIVolume } from "./worklet_methods/controller_control/master_parameters.js";
|
|
13
|
+
import { resetAllControllers } from "./worklet_methods/controller_control/reset_controllers.js";
|
|
48
14
|
import { WorkletSoundfontManager } from "./worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js";
|
|
49
15
|
import { interpolationTypes } from "./worklet_utilities/wavetable_oscillator.js";
|
|
50
16
|
import { WorkletKeyModifierManager } from "./worklet_methods/worklet_key_modifier.js";
|
|
51
17
|
import { getWorkletVoices } from "./worklet_utilities/worklet_voice.js";
|
|
52
|
-
import { PAN_SMOOTHING_FACTOR
|
|
18
|
+
import { PAN_SMOOTHING_FACTOR } from "./worklet_utilities/stereo_panner.js";
|
|
19
|
+
import { stopAllChannels } from "./worklet_methods/stopping_notes/stop_all_channels.js";
|
|
20
|
+
import { setEmbeddedSoundFont } from "./worklet_methods/soundfont_management/set_embedded_sound_font.js";
|
|
21
|
+
import { reloadSoundFont } from "./worklet_methods/soundfont_management/reload_sound_font.js";
|
|
22
|
+
import { clearSoundFont } from "./worklet_methods/soundfont_management/clear_sound_font.js";
|
|
23
|
+
import { sendPresetList } from "./worklet_methods/soundfont_management/send_preset_list.js";
|
|
24
|
+
import { getPreset } from "./worklet_methods/soundfont_management/get_preset.js";
|
|
25
|
+
import { transposeAllChannels } from "./worklet_methods/tuning_control/transpose_all_channels.js";
|
|
26
|
+
import { setMasterTuning } from "./worklet_methods/tuning_control/set_master_tuning.js";
|
|
27
|
+
import { sendSynthesizerSnapshot } from "./snapshot/send_synthesizer_snapshot.js";
|
|
28
|
+
import { applySynthesizerSnapshot } from "./snapshot/apply_synthesizer_snapshot.js";
|
|
29
|
+
import { createWorkletChannel } from "./worklet_methods/create_worklet_channel.js";
|
|
53
30
|
|
|
54
31
|
|
|
55
32
|
/**
|
|
@@ -334,43 +311,39 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
334
311
|
return;
|
|
335
312
|
}
|
|
336
313
|
let outputIndex;
|
|
337
|
-
let
|
|
338
|
-
let
|
|
339
|
-
let
|
|
340
|
-
let
|
|
314
|
+
let outputL;
|
|
315
|
+
let outputR;
|
|
316
|
+
let reverbL;
|
|
317
|
+
let reverbR;
|
|
318
|
+
let chorusL;
|
|
319
|
+
let chorusR;
|
|
341
320
|
if (this.oneOutputMode)
|
|
342
321
|
{
|
|
343
322
|
// first output only
|
|
344
323
|
const output = outputs[0];
|
|
345
324
|
// reverb and chorus are disabled. 32 output channels: two for each midi channel
|
|
346
325
|
outputIndex = (index % 16) * 2;
|
|
347
|
-
|
|
348
|
-
|
|
326
|
+
outputL = output[outputIndex];
|
|
327
|
+
outputR = output[outputIndex + 1];
|
|
349
328
|
}
|
|
350
329
|
else
|
|
351
330
|
{
|
|
352
331
|
// 2 first outputs are reverb and chorus, others are for channels
|
|
353
332
|
outputIndex = (index % this._outputsAmount) + 2;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
333
|
+
outputL = outputs[outputIndex][0];
|
|
334
|
+
outputR = outputs[outputIndex][1];
|
|
335
|
+
reverbL = outputs[0][0];
|
|
336
|
+
reverbR = outputs[0][1];
|
|
337
|
+
chorusL = outputs[1][0];
|
|
338
|
+
chorusR = outputs[1][1];
|
|
358
339
|
}
|
|
359
340
|
|
|
360
341
|
// for every voice, render it
|
|
361
|
-
channel.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
v,
|
|
367
|
-
outputLeft, outputRight,
|
|
368
|
-
reverbChannels,
|
|
369
|
-
chorusChannels
|
|
370
|
-
);
|
|
371
|
-
|
|
372
|
-
return !v.finished;
|
|
373
|
-
});
|
|
342
|
+
channel.renderAudio(
|
|
343
|
+
outputL, outputR,
|
|
344
|
+
reverbL, reverbR,
|
|
345
|
+
chorusL, chorusR
|
|
346
|
+
);
|
|
374
347
|
|
|
375
348
|
totalCurrentVoices += channel.voices.length;
|
|
376
349
|
});
|
|
@@ -403,12 +376,81 @@ class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
403
376
|
this.soundfontManager.destroyManager();
|
|
404
377
|
delete this.soundfontManager;
|
|
405
378
|
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* @param channel {number}
|
|
382
|
+
* @param controllerNumber {number}
|
|
383
|
+
* @param controllerValue {number}
|
|
384
|
+
* @param force {boolean}
|
|
385
|
+
*/
|
|
386
|
+
controllerChange(channel, controllerNumber, controllerValue, force = false)
|
|
387
|
+
{
|
|
388
|
+
this.workletProcessorChannels[channel].controllerChange(controllerNumber, controllerValue, force);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* @param channel {number}
|
|
393
|
+
* @param midiNote {number}
|
|
394
|
+
* @param velocity {number}
|
|
395
|
+
* @param enableDebug {boolean}
|
|
396
|
+
* @param sendEvent {boolean}
|
|
397
|
+
*/
|
|
398
|
+
noteOn(channel, midiNote, velocity, enableDebug = false, sendEvent = true)
|
|
399
|
+
{
|
|
400
|
+
this.workletProcessorChannels[channel].noteOn(midiNote, velocity, enableDebug, sendEvent);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @param channel {number}
|
|
405
|
+
* @param midiNote {number}
|
|
406
|
+
*/
|
|
407
|
+
noteOff(channel, midiNote)
|
|
408
|
+
{
|
|
409
|
+
this.workletProcessorChannels[channel].noteOff(midiNote);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* @param channel {number}
|
|
414
|
+
* @param midiNote {number}
|
|
415
|
+
* @param pressure {number}
|
|
416
|
+
*/
|
|
417
|
+
polyPressure(channel, midiNote, pressure)
|
|
418
|
+
{
|
|
419
|
+
this.workletProcessorChannels[channel].polyPressure(midiNote, pressure);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* @param channel {number}
|
|
424
|
+
* @param pressure {number}
|
|
425
|
+
*/
|
|
426
|
+
channelPressure(channel, pressure)
|
|
427
|
+
{
|
|
428
|
+
this.workletProcessorChannels[channel].channelPressure(pressure);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* @param channel {number}
|
|
433
|
+
* @param MSB {number}
|
|
434
|
+
* @param LSB {number}
|
|
435
|
+
*/
|
|
436
|
+
pitchWheel(channel, MSB, LSB)
|
|
437
|
+
{
|
|
438
|
+
this.workletProcessorChannels[channel].pitchWheel(MSB, LSB);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* @param channel {number}
|
|
443
|
+
* @param programNumber {number}
|
|
444
|
+
* @param userChange {boolean}
|
|
445
|
+
*/
|
|
446
|
+
programChange(channel, programNumber, userChange = false)
|
|
447
|
+
{
|
|
448
|
+
this.workletProcessorChannels[channel].programChange(programNumber, userChange);
|
|
449
|
+
}
|
|
406
450
|
}
|
|
407
451
|
|
|
408
452
|
// include other methods
|
|
409
453
|
// voice related
|
|
410
|
-
SpessaSynthProcessor.prototype.renderVoice = renderVoice;
|
|
411
|
-
SpessaSynthProcessor.prototype.releaseVoice = releaseVoice;
|
|
412
454
|
SpessaSynthProcessor.prototype.voiceKilling = voiceKilling;
|
|
413
455
|
SpessaSynthProcessor.prototype.getWorkletVoices = getWorkletVoices;
|
|
414
456
|
|
|
@@ -420,30 +462,10 @@ SpessaSynthProcessor.prototype.callEvent = callEvent;
|
|
|
420
462
|
// system-exclusive related
|
|
421
463
|
SpessaSynthProcessor.prototype.systemExclusive = systemExclusive;
|
|
422
464
|
|
|
423
|
-
// note messages related
|
|
424
|
-
SpessaSynthProcessor.prototype.noteOn = noteOn;
|
|
425
|
-
SpessaSynthProcessor.prototype.noteOff = noteOff;
|
|
426
|
-
SpessaSynthProcessor.prototype.polyPressure = polyPressure;
|
|
427
|
-
SpessaSynthProcessor.prototype.killNote = killNote;
|
|
428
|
-
SpessaSynthProcessor.prototype.stopAll = stopAll;
|
|
429
|
-
SpessaSynthProcessor.prototype.stopAllChannels = stopAllChannels;
|
|
430
|
-
SpessaSynthProcessor.prototype.muteChannel = muteChannel;
|
|
431
|
-
|
|
432
|
-
// custom vibrato related
|
|
433
|
-
SpessaSynthProcessor.prototype.setVibrato = setVibrato;
|
|
434
|
-
SpessaSynthProcessor.prototype.disableAndLockGSNRPN = disableAndLockGSNRPN;
|
|
435
|
-
|
|
436
|
-
// data entry related
|
|
437
|
-
SpessaSynthProcessor.prototype.dataEntryCoarse = dataEntryCoarse;
|
|
438
|
-
SpessaSynthProcessor.prototype.dataEntryFine = dataEntryFine;
|
|
439
|
-
|
|
440
465
|
// channel related
|
|
466
|
+
SpessaSynthProcessor.prototype.stopAllChannels = stopAllChannels;
|
|
441
467
|
SpessaSynthProcessor.prototype.createWorkletChannel = createWorkletChannel;
|
|
442
|
-
SpessaSynthProcessor.prototype.controllerChange = controllerChange;
|
|
443
|
-
SpessaSynthProcessor.prototype.channelPressure = channelPressure;
|
|
444
468
|
SpessaSynthProcessor.prototype.resetAllControllers = resetAllControllers;
|
|
445
|
-
SpessaSynthProcessor.prototype.resetControllers = resetControllers;
|
|
446
|
-
SpessaSynthProcessor.prototype.resetParameters = resetParameters;
|
|
447
469
|
|
|
448
470
|
// master parameter related
|
|
449
471
|
SpessaSynthProcessor.prototype.setMasterGain = setMasterGain;
|
|
@@ -452,19 +474,10 @@ SpessaSynthProcessor.prototype.setMIDIVolume = setMIDIVolume;
|
|
|
452
474
|
|
|
453
475
|
// tuning related
|
|
454
476
|
SpessaSynthProcessor.prototype.transposeAllChannels = transposeAllChannels;
|
|
455
|
-
SpessaSynthProcessor.prototype.transposeChannel = transposeChannel;
|
|
456
|
-
SpessaSynthProcessor.prototype.setChannelTuning = setChannelTuning;
|
|
457
|
-
SpessaSynthProcessor.prototype.setChannelTuningSemitones = setChannelTuningSemitones;
|
|
458
477
|
SpessaSynthProcessor.prototype.setMasterTuning = setMasterTuning;
|
|
459
|
-
SpessaSynthProcessor.prototype.setModulationDepth = setModulationDepth;
|
|
460
|
-
SpessaSynthProcessor.prototype.pitchWheel = pitchWheel;
|
|
461
|
-
SpessaSynthProcessor.prototype.setOctaveTuning = setOctaveTuning;
|
|
462
478
|
|
|
463
479
|
// program related
|
|
464
|
-
SpessaSynthProcessor.prototype.programChange = programChange;
|
|
465
480
|
SpessaSynthProcessor.prototype.getPreset = getPreset;
|
|
466
|
-
SpessaSynthProcessor.prototype.setPreset = setPreset;
|
|
467
|
-
SpessaSynthProcessor.prototype.setDrums = setDrums;
|
|
468
481
|
SpessaSynthProcessor.prototype.reloadSoundFont = reloadSoundFont;
|
|
469
482
|
SpessaSynthProcessor.prototype.clearSoundFont = clearSoundFont;
|
|
470
483
|
SpessaSynthProcessor.prototype.setEmbeddedSoundFont = setEmbeddedSoundFont;
|
|
@@ -474,6 +487,4 @@ SpessaSynthProcessor.prototype.sendPresetList = sendPresetList;
|
|
|
474
487
|
SpessaSynthProcessor.prototype.sendSynthesizerSnapshot = sendSynthesizerSnapshot;
|
|
475
488
|
SpessaSynthProcessor.prototype.applySynthesizerSnapshot = applySynthesizerSnapshot;
|
|
476
489
|
|
|
477
|
-
SpessaSynthProcessor.prototype.panVoice = panVoice;
|
|
478
|
-
|
|
479
490
|
export { SpessaSynthProcessor };
|
|
@@ -17,27 +17,32 @@ export function handleMessage(message)
|
|
|
17
17
|
/**
|
|
18
18
|
* @type {WorkletProcessorChannel}
|
|
19
19
|
*/
|
|
20
|
-
let channelObject
|
|
20
|
+
let channelObject;
|
|
21
21
|
if (channel >= 0)
|
|
22
22
|
{
|
|
23
23
|
channelObject = this.workletProcessorChannels[channel];
|
|
24
|
+
if (channelObject === undefined)
|
|
25
|
+
{
|
|
26
|
+
SpessaSynthWarn(`Trying to access channel ${channel} which does not exist... ignoring!`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
24
29
|
}
|
|
25
30
|
switch (message.messageType)
|
|
26
31
|
{
|
|
27
32
|
case workletMessageType.noteOn:
|
|
28
|
-
|
|
33
|
+
channelObject.noteOn(...data);
|
|
29
34
|
break;
|
|
30
35
|
|
|
31
36
|
case workletMessageType.noteOff:
|
|
32
|
-
|
|
37
|
+
channelObject.noteOff(data);
|
|
33
38
|
break;
|
|
34
39
|
|
|
35
40
|
case workletMessageType.pitchWheel:
|
|
36
|
-
|
|
41
|
+
channelObject.pitchWheel(...data);
|
|
37
42
|
break;
|
|
38
43
|
|
|
39
44
|
case workletMessageType.ccChange:
|
|
40
|
-
|
|
45
|
+
channelObject.controllerChange(...data);
|
|
41
46
|
break;
|
|
42
47
|
|
|
43
48
|
case workletMessageType.customcCcChange:
|
|
@@ -46,7 +51,7 @@ export function handleMessage(message)
|
|
|
46
51
|
break;
|
|
47
52
|
|
|
48
53
|
case workletMessageType.killNote:
|
|
49
|
-
|
|
54
|
+
channelObject.killNote(data);
|
|
50
55
|
break;
|
|
51
56
|
|
|
52
57
|
case workletMessageType.programChange:
|
|
@@ -54,11 +59,11 @@ export function handleMessage(message)
|
|
|
54
59
|
break;
|
|
55
60
|
|
|
56
61
|
case workletMessageType.channelPressure:
|
|
57
|
-
|
|
62
|
+
channelObject.channelPressure(data);
|
|
58
63
|
break;
|
|
59
64
|
|
|
60
65
|
case workletMessageType.polyPressure:
|
|
61
|
-
|
|
66
|
+
channelObject.polyPressure(...data);
|
|
62
67
|
break;
|
|
63
68
|
|
|
64
69
|
case workletMessageType.ccReset:
|
|
@@ -68,7 +73,7 @@ export function handleMessage(message)
|
|
|
68
73
|
}
|
|
69
74
|
else
|
|
70
75
|
{
|
|
71
|
-
|
|
76
|
+
channelObject.resetControllers();
|
|
72
77
|
}
|
|
73
78
|
break;
|
|
74
79
|
|
|
@@ -83,21 +88,21 @@ export function handleMessage(message)
|
|
|
83
88
|
{
|
|
84
89
|
if (data.rate === -1)
|
|
85
90
|
{
|
|
86
|
-
|
|
91
|
+
channelObject.disableAndLockGSNRPN();
|
|
87
92
|
}
|
|
88
93
|
else
|
|
89
94
|
{
|
|
90
|
-
|
|
95
|
+
channelObject.setVibrato(data.depth, data.rate, data.delay);
|
|
91
96
|
}
|
|
92
97
|
}
|
|
93
98
|
}
|
|
94
99
|
else if (data.rate === -1)
|
|
95
100
|
{
|
|
96
|
-
|
|
101
|
+
channelObject.disableAndLockGSNRPN();
|
|
97
102
|
}
|
|
98
103
|
else
|
|
99
104
|
{
|
|
100
|
-
|
|
105
|
+
channelObject.setVibrato(data.depth, data.rate, data.delay);
|
|
101
106
|
}
|
|
102
107
|
break;
|
|
103
108
|
|
|
@@ -108,7 +113,7 @@ export function handleMessage(message)
|
|
|
108
113
|
}
|
|
109
114
|
else
|
|
110
115
|
{
|
|
111
|
-
|
|
116
|
+
channelObject.stopAllNotes(data === 1);
|
|
112
117
|
}
|
|
113
118
|
break;
|
|
114
119
|
|
|
@@ -117,7 +122,7 @@ export function handleMessage(message)
|
|
|
117
122
|
break;
|
|
118
123
|
|
|
119
124
|
case workletMessageType.muteChannel:
|
|
120
|
-
|
|
125
|
+
channelObject.muteChannel(data);
|
|
121
126
|
break;
|
|
122
127
|
|
|
123
128
|
case workletMessageType.addNewChannel:
|
|
@@ -155,7 +160,7 @@ export function handleMessage(message)
|
|
|
155
160
|
break;
|
|
156
161
|
|
|
157
162
|
case workletMessageType.setDrums:
|
|
158
|
-
|
|
163
|
+
channelObject.setDrums(data);
|
|
159
164
|
break;
|
|
160
165
|
|
|
161
166
|
case workletMessageType.transpose:
|
|
@@ -165,7 +170,7 @@ export function handleMessage(message)
|
|
|
165
170
|
}
|
|
166
171
|
else
|
|
167
172
|
{
|
|
168
|
-
|
|
173
|
+
channelObject.transposeChannel(data[0], data[1]);
|
|
169
174
|
}
|
|
170
175
|
break;
|
|
171
176
|
|
|
@@ -81,6 +81,7 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
|
|
|
81
81
|
* messageType: workletMessageType,
|
|
82
82
|
* messageData: (
|
|
83
83
|
* number[]
|
|
84
|
+
* |undefined
|
|
84
85
|
* |boolean[]
|
|
85
86
|
* |WorkletVoice[]
|
|
86
87
|
* |number
|
|
@@ -111,7 +112,7 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
|
|
|
111
112
|
* 1 - event call -> {eventName<string>, eventData:<the event's data>}
|
|
112
113
|
* 2 - reported current time -> currentTime<number>
|
|
113
114
|
* 3 - sequencer specific -> [messageType<WorkletSequencerReturnMessageType> messageData<any>] note: refer to sequencer_message.js
|
|
114
|
-
* 4 - synthesizer snapshot -> snapshot<SynthesizerSnapshot> note: refer to
|
|
115
|
+
* 4 - synthesizer snapshot -> snapshot<SynthesizerSnapshot> note: refer to synthesizer_snapshot.js
|
|
115
116
|
* 5 - ready -> (no data)
|
|
116
117
|
* 6 - soundfontError -> errorMessage<string>
|
|
117
118
|
* 7 - idenfity -> version<string>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SpessaSynthInfo } from "../../../utils/loggin.js";
|
|
2
|
+
import { consoleColors } from "../../../utils/other.js";
|
|
3
|
+
import { SynthesizerSnapshot } from "./synthesizer_snapshot.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Applies the snapshot to the synth
|
|
7
|
+
* @param snapshot {SynthesizerSnapshot}
|
|
8
|
+
* @this {SpessaSynthProcessor}
|
|
9
|
+
*/
|
|
10
|
+
export function applySynthesizerSnapshot(snapshot)
|
|
11
|
+
{
|
|
12
|
+
SynthesizerSnapshot.applySnapshot(this, snapshot);
|
|
13
|
+
SpessaSynthInfo("%cFinished applying snapshot!", consoleColors.info);
|
|
14
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a snapshot of a single channel's state in the synthesizer.
|
|
3
|
+
*/
|
|
4
|
+
export class ChannelSnapshot
|
|
5
|
+
{
|
|
6
|
+
/**
|
|
7
|
+
* The channel's MIDI program number.
|
|
8
|
+
* @type {number}
|
|
9
|
+
*/
|
|
10
|
+
program;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The channel's bank number.
|
|
14
|
+
* @type {number}
|
|
15
|
+
*/
|
|
16
|
+
bank;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The name of the patch currently loaded in the channel.
|
|
20
|
+
* @type {string}
|
|
21
|
+
*/
|
|
22
|
+
patchName;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Indicates whether the channel's program change is disabled.
|
|
26
|
+
* @type {boolean}
|
|
27
|
+
*/
|
|
28
|
+
lockPreset;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The array of all MIDI controllers (in 14-bit values) with the modulator sources at the end.
|
|
32
|
+
* @type {Int16Array}
|
|
33
|
+
*/
|
|
34
|
+
midiControllers;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* An array of booleans, indicating if the controller with a current index is locked.
|
|
38
|
+
* @type {boolean[]}
|
|
39
|
+
*/
|
|
40
|
+
lockedControllers;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Array of custom (not SF2) control values such as RPN pitch tuning, transpose, modulation depth, etc.
|
|
44
|
+
* @type {Float32Array}
|
|
45
|
+
*/
|
|
46
|
+
customControllers;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Indicates whether the channel vibrato is locked.
|
|
50
|
+
* @type {boolean}
|
|
51
|
+
*/
|
|
52
|
+
lockVibrato;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The channel's vibrato settings.
|
|
56
|
+
* @type {Object}
|
|
57
|
+
* @property {number} depth - Vibrato depth, in gain.
|
|
58
|
+
* @property {number} delay - Vibrato delay from note on in seconds.
|
|
59
|
+
* @property {number} rate - Vibrato rate in Hz.
|
|
60
|
+
*/
|
|
61
|
+
channelVibrato;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Key shift for the channel.
|
|
65
|
+
* @type {number}
|
|
66
|
+
*/
|
|
67
|
+
channelTransposeKeyShift;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The channel's octave tuning in cents.
|
|
71
|
+
* @type {Int8Array}
|
|
72
|
+
*/
|
|
73
|
+
channelOctaveTuning;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Tuning of individual keys in cents.
|
|
77
|
+
* @type {Int16Array}
|
|
78
|
+
*/
|
|
79
|
+
keyCentTuning;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Indicates whether the channel is muted.
|
|
83
|
+
* @type {boolean}
|
|
84
|
+
*/
|
|
85
|
+
isMuted;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Overrides velocity if greater than 0, otherwise disabled.
|
|
89
|
+
* @type {number}
|
|
90
|
+
*/
|
|
91
|
+
velocityOverride;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Indicates whether the channel is a drum channel.
|
|
95
|
+
* @type {boolean}
|
|
96
|
+
*/
|
|
97
|
+
drumChannel;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Creates a snapshot of a single channel's state in the synthesizer.
|
|
101
|
+
* @param workletProcessor {SpessaSynthProcessor}
|
|
102
|
+
* @param channelNumber {number}
|
|
103
|
+
* @returns {ChannelSnapshot}
|
|
104
|
+
*/
|
|
105
|
+
static getChannelSnapshot(workletProcessor, channelNumber)
|
|
106
|
+
{
|
|
107
|
+
const channelObject = workletProcessor.workletProcessorChannels[channelNumber];
|
|
108
|
+
const channelSnapshot = new ChannelSnapshot();
|
|
109
|
+
// program data
|
|
110
|
+
channelSnapshot.program = channelObject.preset.program;
|
|
111
|
+
channelSnapshot.bank = channelObject.getBankSelect();
|
|
112
|
+
channelSnapshot.lockPreset = channelObject.lockPreset;
|
|
113
|
+
channelSnapshot.patchName = channelObject.preset.presetName;
|
|
114
|
+
|
|
115
|
+
// controller data
|
|
116
|
+
channelSnapshot.midiControllers = channelObject.midiControllers;
|
|
117
|
+
channelSnapshot.lockedControllers = channelObject.lockedControllers;
|
|
118
|
+
channelSnapshot.customControllers = channelObject.customControllers;
|
|
119
|
+
|
|
120
|
+
// vibrato data
|
|
121
|
+
channelSnapshot.channelVibrato = channelObject.channelVibrato;
|
|
122
|
+
channelSnapshot.lockVibrato = channelObject.lockGSNRPNParams;
|
|
123
|
+
|
|
124
|
+
// tuning and transpose data
|
|
125
|
+
channelSnapshot.channelTransposeKeyShift = channelObject.channelTransposeKeyShift;
|
|
126
|
+
channelSnapshot.channelOctaveTuning = channelObject.channelOctaveTuning;
|
|
127
|
+
channelSnapshot.keyCentTuning = channelObject.keyCentTuning;
|
|
128
|
+
|
|
129
|
+
// other data
|
|
130
|
+
channelSnapshot.isMuted = channelObject.isMuted;
|
|
131
|
+
channelSnapshot.velocityOverride = channelObject.velocityOverride;
|
|
132
|
+
channelSnapshot.drumChannel = channelObject.drumChannel;
|
|
133
|
+
return channelSnapshot;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Applies the snapshot to the specified channel.
|
|
138
|
+
* @param workletProcessor {SpessaSynthProcessor}
|
|
139
|
+
* @param channelNumber {number}
|
|
140
|
+
* @param channelSnapshot {ChannelSnapshot}
|
|
141
|
+
*/
|
|
142
|
+
static applyChannelSnapshot(workletProcessor, channelNumber, channelSnapshot)
|
|
143
|
+
{
|
|
144
|
+
const channelObject = workletProcessor.workletProcessorChannels[channelNumber];
|
|
145
|
+
channelObject.muteChannel(channelSnapshot.isMuted);
|
|
146
|
+
channelObject.setDrums(channelSnapshot.drumChannel);
|
|
147
|
+
|
|
148
|
+
// restore controllers
|
|
149
|
+
channelObject.midiControllers = channelSnapshot.midiControllers;
|
|
150
|
+
channelObject.lockedControllers = channelSnapshot.lockedControllers;
|
|
151
|
+
channelObject.customControllers = channelSnapshot.customControllers;
|
|
152
|
+
|
|
153
|
+
// restore vibrato and transpose
|
|
154
|
+
channelObject.channelVibrato = channelSnapshot.channelVibrato;
|
|
155
|
+
channelObject.lockGSNRPNParams = channelSnapshot.lockVibrato;
|
|
156
|
+
channelObject.channelTransposeKeyShift = channelSnapshot.channelTransposeKeyShift;
|
|
157
|
+
channelObject.channelOctaveTuning = channelSnapshot.channelOctaveTuning;
|
|
158
|
+
channelObject.velocityOverride = channelSnapshot.velocityOverride;
|
|
159
|
+
|
|
160
|
+
// restore preset and lock
|
|
161
|
+
channelObject.lockPreset = false;
|
|
162
|
+
channelObject.setBankSelect(channelSnapshot.bank);
|
|
163
|
+
channelObject.programChange(channelSnapshot.program);
|
|
164
|
+
channelObject.lockPreset = channelSnapshot.lockPreset;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { returnMessageType } from "../message_protocol/worklet_message.js";
|
|
2
|
+
import { SynthesizerSnapshot } from "./synthesizer_snapshot.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* sends a snapshot of the current controller values of the synth (used to copy that data to OfflineAudioContext when rendering)
|
|
6
|
+
* @this {SpessaSynthProcessor}
|
|
7
|
+
*/
|
|
8
|
+
export function sendSynthesizerSnapshot()
|
|
9
|
+
{
|
|
10
|
+
this.post({
|
|
11
|
+
messageType: returnMessageType.synthesizerSnapshot,
|
|
12
|
+
messageData: SynthesizerSnapshot.createSynthesizerSnapshot(this)
|
|
13
|
+
});
|
|
14
|
+
}
|