spessasynth_core 1.1.3 → 1.1.4
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/LICENSE +3 -26
- package/README.md +156 -474
- package/index.js +73 -8
- package/package.json +21 -8
- package/src/externals/fflate/LICENSE +21 -0
- package/src/externals/fflate/fflate.min.js +1 -0
- package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +12 -0
- package/src/externals/stbvorbis_sync/LICENSE +202 -0
- package/src/externals/stbvorbis_sync/NOTICE +6 -0
- package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +1 -0
- package/src/midi/README.md +32 -0
- package/src/midi/basic_midi.js +567 -0
- package/src/midi/midi_builder.js +202 -0
- package/src/midi/midi_loader.js +324 -0
- package/{spessasynth_core/midi_parser → src/midi}/midi_message.js +58 -35
- package/src/midi/midi_sequence.js +224 -0
- package/src/midi/midi_tools/get_note_times.js +154 -0
- package/src/midi/midi_tools/midi_editor.js +611 -0
- package/src/midi/midi_tools/midi_writer.js +99 -0
- package/src/midi/midi_tools/rmidi_writer.js +567 -0
- package/src/midi/midi_tools/used_keys_loaded.js +238 -0
- package/src/midi/xmf_loader.js +454 -0
- package/src/sequencer/README.md +5 -0
- package/src/sequencer/events.js +81 -0
- package/src/sequencer/play.js +349 -0
- package/src/sequencer/process_event.js +165 -0
- package/{spessasynth_core/sequencer/worklet_sequencer → src/sequencer}/process_tick.js +103 -84
- package/src/sequencer/sequencer_engine.js +367 -0
- package/src/sequencer/song_control.js +201 -0
- package/src/soundfont/README.md +13 -0
- package/src/soundfont/basic_soundfont/basic_instrument.js +77 -0
- package/src/soundfont/basic_soundfont/basic_preset.js +336 -0
- package/src/soundfont/basic_soundfont/basic_sample.js +206 -0
- package/src/soundfont/basic_soundfont/basic_soundfont.js +565 -0
- package/src/soundfont/basic_soundfont/basic_zone.js +64 -0
- package/src/soundfont/basic_soundfont/basic_zones.js +43 -0
- package/src/soundfont/basic_soundfont/generator.js +220 -0
- package/src/soundfont/basic_soundfont/modulator.js +378 -0
- package/src/soundfont/basic_soundfont/riff_chunk.js +149 -0
- package/src/soundfont/basic_soundfont/write_dls/art2.js +173 -0
- package/src/soundfont/basic_soundfont/write_dls/articulator.js +49 -0
- package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +400 -0
- package/src/soundfont/basic_soundfont/write_dls/ins.js +103 -0
- package/src/soundfont/basic_soundfont/write_dls/lins.js +18 -0
- package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +330 -0
- package/src/soundfont/basic_soundfont/write_dls/rgn2.js +121 -0
- package/src/soundfont/basic_soundfont/write_dls/wave.js +94 -0
- package/src/soundfont/basic_soundfont/write_dls/write_dls.js +119 -0
- package/src/soundfont/basic_soundfont/write_dls/wsmp.js +78 -0
- package/src/soundfont/basic_soundfont/write_dls/wvpl.js +32 -0
- package/src/soundfont/basic_soundfont/write_sf2/ibag.js +39 -0
- package/src/soundfont/basic_soundfont/write_sf2/igen.js +80 -0
- package/src/soundfont/basic_soundfont/write_sf2/imod.js +46 -0
- package/src/soundfont/basic_soundfont/write_sf2/inst.js +34 -0
- package/src/soundfont/basic_soundfont/write_sf2/pbag.js +39 -0
- package/src/soundfont/basic_soundfont/write_sf2/pgen.js +82 -0
- package/src/soundfont/basic_soundfont/write_sf2/phdr.js +42 -0
- package/src/soundfont/basic_soundfont/write_sf2/pmod.js +46 -0
- package/src/soundfont/basic_soundfont/write_sf2/sdta.js +80 -0
- package/src/soundfont/basic_soundfont/write_sf2/shdr.js +55 -0
- package/src/soundfont/basic_soundfont/write_sf2/write.js +222 -0
- package/src/soundfont/dls/articulator_converter.js +396 -0
- package/src/soundfont/dls/dls_destinations.js +38 -0
- package/src/soundfont/dls/dls_preset.js +44 -0
- package/src/soundfont/dls/dls_sample.js +75 -0
- package/src/soundfont/dls/dls_soundfont.js +186 -0
- package/src/soundfont/dls/dls_sources.js +62 -0
- package/src/soundfont/dls/dls_zone.js +95 -0
- package/src/soundfont/dls/read_articulation.js +299 -0
- package/src/soundfont/dls/read_instrument.js +121 -0
- package/src/soundfont/dls/read_instrument_list.js +17 -0
- package/src/soundfont/dls/read_lart.js +35 -0
- package/src/soundfont/dls/read_region.js +152 -0
- package/src/soundfont/dls/read_samples.js +270 -0
- package/src/soundfont/load_soundfont.js +21 -0
- package/src/soundfont/read_sf2/generators.js +46 -0
- package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/instruments.js +20 -14
- package/src/soundfont/read_sf2/modulators.js +36 -0
- package/src/soundfont/read_sf2/presets.js +80 -0
- package/src/soundfont/read_sf2/samples.js +304 -0
- package/src/soundfont/read_sf2/soundfont.js +305 -0
- package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/zones.js +68 -69
- package/src/synthetizer/README.md +7 -0
- package/src/synthetizer/audio_engine/README.md +9 -0
- package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +266 -0
- package/src/synthetizer/audio_engine/engine_components/controller_tables.js +88 -0
- package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +150 -0
- package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/lfo.js +9 -6
- package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +282 -0
- package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +467 -0
- package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +181 -0
- package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/modulator_curves.js +33 -30
- package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +221 -0
- package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +120 -0
- package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/unit_converter.js +11 -4
- package/src/synthetizer/audio_engine/engine_components/voice.js +519 -0
- package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +401 -0
- package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +263 -0
- package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +132 -0
- package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +48 -0
- package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +241 -0
- package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +27 -0
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +253 -0
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +66 -0
- package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +17 -0
- package/src/synthetizer/audio_engine/engine_methods/note_on.js +175 -0
- package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +92 -0
- package/src/synthetizer/audio_engine/engine_methods/program_change.js +61 -0
- package/src/synthetizer/audio_engine/engine_methods/render_voice.js +196 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/clear_sound_font.js +30 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +22 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/reload_sound_font.js +28 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/send_preset_list.js +31 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/set_embedded_sound_font.js +21 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +20 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +55 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +16 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +30 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +63 -0
- package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +776 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +24 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +33 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +31 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +15 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +27 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +19 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +27 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +15 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +34 -0
- package/src/synthetizer/audio_engine/main_processor.js +804 -0
- package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +15 -0
- package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +175 -0
- package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +116 -0
- package/src/synthetizer/synth_constants.js +22 -0
- package/{spessasynth_core → src}/utils/README.md +1 -0
- package/src/utils/buffer_to_wav.js +185 -0
- package/src/utils/byte_functions/big_endian.js +32 -0
- package/src/utils/byte_functions/little_endian.js +77 -0
- package/src/utils/byte_functions/string.js +107 -0
- package/src/utils/byte_functions/variable_length_quantity.js +42 -0
- package/src/utils/fill_with_defaults.js +21 -0
- package/src/utils/indexed_array.js +52 -0
- package/{spessasynth_core → src}/utils/loggin.js +70 -78
- package/src/utils/other.js +92 -0
- package/src/utils/sysex_detector.js +58 -0
- package/src/utils/xg_hacks.js +193 -0
- package/.idea/inspectionProfiles/Project_Default.xml +0 -10
- package/.idea/jsLibraryMappings.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/spessasynth_core.iml +0 -12
- package/.idea/vcs.xml +0 -6
- package/spessasynth_core/midi_parser/README.md +0 -3
- package/spessasynth_core/midi_parser/midi_loader.js +0 -386
- package/spessasynth_core/sequencer/sequencer.js +0 -202
- package/spessasynth_core/sequencer/worklet_sequencer/play.js +0 -209
- package/spessasynth_core/sequencer/worklet_sequencer/process_event.js +0 -120
- package/spessasynth_core/sequencer/worklet_sequencer/song_control.js +0 -112
- package/spessasynth_core/soundfont/README.md +0 -4
- package/spessasynth_core/soundfont/chunk/generators.js +0 -205
- package/spessasynth_core/soundfont/chunk/modulators.js +0 -232
- package/spessasynth_core/soundfont/chunk/presets.js +0 -264
- package/spessasynth_core/soundfont/chunk/riff_chunk.js +0 -46
- package/spessasynth_core/soundfont/chunk/samples.js +0 -250
- package/spessasynth_core/soundfont/soundfont_parser.js +0 -301
- package/spessasynth_core/synthetizer/README.md +0 -6
- package/spessasynth_core/synthetizer/synthesizer.js +0 -313
- package/spessasynth_core/synthetizer/worklet_system/README.md +0 -3
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/controller_control.js +0 -290
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/data_entry.js +0 -280
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_off.js +0 -102
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_on.js +0 -77
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/program_control.js +0 -140
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/system_exclusive.js +0 -266
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/tuning_control.js +0 -104
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/vibrato_control.js +0 -29
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/voice_control.js +0 -223
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +0 -133
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +0 -73
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +0 -76
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +0 -272
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +0 -83
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +0 -175
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +0 -106
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +0 -285
- package/spessasynth_core/utils/buffer_to_wav.js +0 -70
- package/spessasynth_core/utils/byte_functions.js +0 -141
- package/spessasynth_core/utils/other.js +0 -49
- package/spessasynth_core/utils/shiftable_array.js +0 -26
- package/spessasynth_core/utils/stbvorbis_sync.js +0 -1877
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
import { SpessaSynthInfo } from "../../utils/loggin.js";
|
|
2
|
+
import { consoleColors } from "../../utils/other.js";
|
|
3
|
+
import { voiceKilling } from "./engine_methods/stopping_notes/voice_killing.js";
|
|
4
|
+
import {
|
|
5
|
+
ALL_CHANNELS_OR_DIFFERENT_ACTION,
|
|
6
|
+
DEFAULT_PERCUSSION,
|
|
7
|
+
DEFAULT_SYNTH_MODE,
|
|
8
|
+
VOICE_CAP
|
|
9
|
+
} from "../synth_constants.js";
|
|
10
|
+
import { stbvorbis } from "../../externals/stbvorbis_sync/stbvorbis_sync.min.js";
|
|
11
|
+
import { VOLUME_ENVELOPE_SMOOTHING_FACTOR } from "./engine_components/volume_envelope.js";
|
|
12
|
+
import { systemExclusive } from "./engine_methods/system_exclusive.js";
|
|
13
|
+
import { masterParameterType, setMasterParameter } from "./engine_methods/controller_control/master_parameters.js";
|
|
14
|
+
import { resetAllControllers } from "./engine_methods/controller_control/reset_controllers.js";
|
|
15
|
+
import { WorkletSoundfontManager } from "./engine_components/soundfont_manager.js";
|
|
16
|
+
import { interpolationTypes } from "./engine_components/wavetable_oscillator.js";
|
|
17
|
+
import { KeyModifierManager } from "./engine_components/key_modifier_manager.js";
|
|
18
|
+
import { getVoices } from "./engine_components/voice.js";
|
|
19
|
+
import { PAN_SMOOTHING_FACTOR } from "./engine_components/stereo_panner.js";
|
|
20
|
+
import { stopAllChannels } from "./engine_methods/stopping_notes/stop_all_channels.js";
|
|
21
|
+
import { setEmbeddedSoundFont } from "./engine_methods/soundfont_management/set_embedded_sound_font.js";
|
|
22
|
+
import { reloadSoundFont } from "./engine_methods/soundfont_management/reload_sound_font.js";
|
|
23
|
+
import { clearSoundFont } from "./engine_methods/soundfont_management/clear_sound_font.js";
|
|
24
|
+
import { sendPresetList } from "./engine_methods/soundfont_management/send_preset_list.js";
|
|
25
|
+
import { getPreset } from "./engine_methods/soundfont_management/get_preset.js";
|
|
26
|
+
import { transposeAllChannels } from "./engine_methods/tuning_control/transpose_all_channels.js";
|
|
27
|
+
import { setMasterTuning } from "./engine_methods/tuning_control/set_master_tuning.js";
|
|
28
|
+
import { applySynthesizerSnapshot } from "./snapshot/apply_synthesizer_snapshot.js";
|
|
29
|
+
import { createMidiChannel } from "./engine_methods/create_midi_channel.js";
|
|
30
|
+
import { FILTER_SMOOTHING_FACTOR } from "./engine_components/lowpass_filter.js";
|
|
31
|
+
import { getEvent, messageTypes } from "../../midi/midi_message.js";
|
|
32
|
+
import { IndexedByteArray } from "../../utils/indexed_array.js";
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {"gm"|"gm2"|"gs"|"xg"} SynthSystem
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* main_processor.js
|
|
41
|
+
* purpose: the core synthesis engine
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {Object} NoteOnCallback
|
|
47
|
+
* @property {number} midiNote - The MIDI note number.
|
|
48
|
+
* @property {number} channel - The MIDI channel number.
|
|
49
|
+
* @property {number} velocity - The velocity of the note.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @typedef {Object} NoteOffCallback
|
|
54
|
+
* @property {number} midiNote - The MIDI note number.
|
|
55
|
+
* @property {number} channel - The MIDI channel number.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @typedef {Object} DrumChangeCallback
|
|
60
|
+
* @property {number} channel - The MIDI channel number.
|
|
61
|
+
* @property {boolean} isDrumChannel - Indicates if the channel is a drum channel.
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @typedef {Object} ProgramChangeCallback
|
|
66
|
+
* @property {number} channel - The MIDI channel number.
|
|
67
|
+
* @property {number} program - The program number.
|
|
68
|
+
* @property {number} bank - The bank number.
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @typedef {Object} ControllerChangeCallback
|
|
73
|
+
* @property {number} channel - The MIDI channel number.
|
|
74
|
+
* @property {number} controllerNumber - The controller number.
|
|
75
|
+
* @property {number} controllerValue - The value of the controller.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @typedef {Object} MuteChannelCallback
|
|
80
|
+
* @property {number} channel - The MIDI channel number.
|
|
81
|
+
* @property {boolean} isMuted - Indicates if the channel is muted.
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @typedef {Object} PresetListChangeCallbackSingle
|
|
86
|
+
* @property {string} presetName - The name of the preset.
|
|
87
|
+
* @property {number} bank - The bank number.
|
|
88
|
+
* @property {number} program - The program number.
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @typedef {PresetListChangeCallbackSingle[]} PresetListChangeCallback - A list of preset objects.
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @typedef {Object} SynthDisplayCallback
|
|
97
|
+
* @property {Uint8Array} displayData - The data to display.
|
|
98
|
+
* @property {SynthDisplayType} displayType - The type of display.
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @typedef {Object} PitchWheelCallback
|
|
103
|
+
* @property {number} channel - The MIDI channel number.
|
|
104
|
+
* @property {number} MSB - The most significant byte of the pitch-wheel value.
|
|
105
|
+
* @property {number} LSB - The least significant byte of the pitch-wheel value.
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @typedef {Object} ChannelPressureCallback
|
|
110
|
+
* @property {number} channel - The MIDI channel number.
|
|
111
|
+
* @property {number} pressure - The pressure value.
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @typedef {Error} SoundfontErrorCallback - The error message for soundfont errors.
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @typedef {
|
|
120
|
+
* NoteOnCallback |
|
|
121
|
+
* NoteOffCallback |
|
|
122
|
+
* DrumChangeCallback |
|
|
123
|
+
* ProgramChangeCallback |
|
|
124
|
+
* ControllerChangeCallback |
|
|
125
|
+
* MuteChannelCallback |
|
|
126
|
+
* PresetListChangeCallback |
|
|
127
|
+
* PitchWheelCallback |
|
|
128
|
+
* SoundfontErrorCallback |
|
|
129
|
+
* ChannelPressureCallback |
|
|
130
|
+
* SynthDisplayCallback |
|
|
131
|
+
* undefined
|
|
132
|
+
* } EventCallbackData
|
|
133
|
+
*/
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @typedef {
|
|
137
|
+
* "noteon"|
|
|
138
|
+
* "noteoff"|
|
|
139
|
+
* "pitchwheel"|
|
|
140
|
+
* "controllerchange"|
|
|
141
|
+
* "programchange"|
|
|
142
|
+
* "channelpressure"|
|
|
143
|
+
* "polypressure" |
|
|
144
|
+
* "drumchange"|
|
|
145
|
+
* "stopall"|
|
|
146
|
+
* "newchannel"|
|
|
147
|
+
* "mutechannel"|
|
|
148
|
+
* "presetlistchange"|
|
|
149
|
+
* "allcontrollerreset"|
|
|
150
|
+
* "soundfonterror"|
|
|
151
|
+
* "synthdisplay"} EventTypes
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @typedef {Object} SynthMethodOptions
|
|
157
|
+
* @property {number} time - the audio context time when the event should execute, in seconds.
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
// if the note is released faster than that, it forced to last that long
|
|
161
|
+
// this is used mostly for drum channels, where a lot of midis like to send instant note off after a note on
|
|
162
|
+
export const MIN_NOTE_LENGTH = 0.03;
|
|
163
|
+
// this sounds way nicer for an instant hi-hat cutoff
|
|
164
|
+
export const MIN_EXCLUSIVE_LENGTH = 0.07;
|
|
165
|
+
|
|
166
|
+
export const SYNTHESIZER_GAIN = 1.0;
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
// the core synthesis engine of spessasynth.
|
|
170
|
+
class SpessaSynthProcessor
|
|
171
|
+
{
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Cached voices for all presets for this synthesizer.
|
|
175
|
+
* Nesting goes like this:
|
|
176
|
+
* this.cachedVoices[bankNumber][programNumber][midiNote][velocity] = a list of Voices for that.
|
|
177
|
+
* @type {Voice[][][][][]}
|
|
178
|
+
*/
|
|
179
|
+
cachedVoices = [];
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Synth's device id: -1 means all
|
|
183
|
+
* @type {number}
|
|
184
|
+
*/
|
|
185
|
+
deviceID = ALL_CHANNELS_OR_DIFFERENT_ACTION;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Synth's event queue from the main thread
|
|
189
|
+
* @type {{callback: function(), time: number}[]}
|
|
190
|
+
*/
|
|
191
|
+
eventQueue = [];
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Interpolation type used
|
|
195
|
+
* @type {interpolationTypes}
|
|
196
|
+
*/
|
|
197
|
+
interpolationType = interpolationTypes.fourthOrder;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Global transposition in semitones
|
|
201
|
+
* @type {number}
|
|
202
|
+
*/
|
|
203
|
+
transposition = 0;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* this.tunings[program][key] = tuning
|
|
207
|
+
* @type {MTSProgramTuning[]}
|
|
208
|
+
*/
|
|
209
|
+
tunings = [];
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Bank offset for things like embedded RMIDIS. Added for every program change
|
|
214
|
+
* @type {number}
|
|
215
|
+
*/
|
|
216
|
+
soundfontBankOffset = 0;
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* The volume gain, set by user
|
|
221
|
+
* @type {number}
|
|
222
|
+
*/
|
|
223
|
+
masterGain = SYNTHESIZER_GAIN;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* The volume gain, set by MIDI sysEx
|
|
227
|
+
* @type {number}
|
|
228
|
+
*/
|
|
229
|
+
midiVolume = 1;
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Reverb linear gain
|
|
233
|
+
* @type {number}
|
|
234
|
+
*/
|
|
235
|
+
reverbGain = 1;
|
|
236
|
+
/**
|
|
237
|
+
* Chorus linear gain
|
|
238
|
+
* @type {number}
|
|
239
|
+
*/
|
|
240
|
+
chorusGain = 1;
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Maximum number of voices allowed at once
|
|
244
|
+
* @type {number}
|
|
245
|
+
*/
|
|
246
|
+
voiceCap = VOICE_CAP;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* (-1 to 1)
|
|
250
|
+
* @type {number}
|
|
251
|
+
*/
|
|
252
|
+
pan = 0.0;
|
|
253
|
+
/**
|
|
254
|
+
* the pan of the left channel
|
|
255
|
+
* @type {number}
|
|
256
|
+
*/
|
|
257
|
+
panLeft = 0.5;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* the pan of the right channel
|
|
261
|
+
* @type {number}
|
|
262
|
+
*/
|
|
263
|
+
panRight = 0.5;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* forces note killing instead of releasing
|
|
267
|
+
* @type {boolean}
|
|
268
|
+
*/
|
|
269
|
+
highPerformanceMode = false;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Handlese custom key overrides: velocity and preset
|
|
273
|
+
* @type {KeyModifierManager}
|
|
274
|
+
*/
|
|
275
|
+
keyModifierManager = new KeyModifierManager();
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Overrides the main soundfont (embedded, for example)
|
|
279
|
+
* @type {BasicSoundBank}
|
|
280
|
+
*/
|
|
281
|
+
overrideSoundfont = undefined;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* contains all the channels with their voices on the processor size
|
|
285
|
+
* @type {MidiAudioChannel[]}
|
|
286
|
+
*/
|
|
287
|
+
midiAudioChannels = [];
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Controls the bank selection & SysEx
|
|
291
|
+
* @type {SynthSystem}
|
|
292
|
+
*/
|
|
293
|
+
system = DEFAULT_SYNTH_MODE;
|
|
294
|
+
/**
|
|
295
|
+
* Current total voices amount
|
|
296
|
+
* @type {number}
|
|
297
|
+
*/
|
|
298
|
+
totalVoicesAmount = 0;
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Synth's default (reset) preset
|
|
302
|
+
* @type {BasicPreset}
|
|
303
|
+
*/
|
|
304
|
+
defaultPreset;
|
|
305
|
+
|
|
306
|
+
defaultPresetUsesOverride = false;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Synth's default (reset) drum preset
|
|
310
|
+
* @type {BasicPreset}
|
|
311
|
+
*/
|
|
312
|
+
drumPreset;
|
|
313
|
+
|
|
314
|
+
defaultDrumsUsesOverride = false;
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Controls if the worklet processor is fully initialized
|
|
318
|
+
* @type {Promise<boolean>}
|
|
319
|
+
*/
|
|
320
|
+
processorInitialized = stbvorbis.isInitialized;
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Current audio time
|
|
324
|
+
* @type {number}
|
|
325
|
+
*/
|
|
326
|
+
currentSynthTime = 0;
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* in hertz
|
|
330
|
+
* @type {number}
|
|
331
|
+
*/
|
|
332
|
+
sampleRate;
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* @typedef {Object} CallbacksTypedef
|
|
336
|
+
* @property {function?} ready
|
|
337
|
+
* @property {function(EventTypes, EventCallbackData)?} eventCall
|
|
338
|
+
* @property {function(ChannelProperty, number)?} channelPropertyChange
|
|
339
|
+
* @property {function(masterParameterType, number|string)?} masterParameterChange
|
|
340
|
+
*/
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Creates a new worklet synthesis system. contains all channels
|
|
344
|
+
* @param midiChannels {number}
|
|
345
|
+
* @param soundfont {ArrayBuffer}
|
|
346
|
+
* @param enableEventSystem {boolean}
|
|
347
|
+
* @param callbacks {CallbacksTypedef}
|
|
348
|
+
* @param sampleRate {number}
|
|
349
|
+
* @param initialTime {number}
|
|
350
|
+
* @param effectsEnabled {boolean}
|
|
351
|
+
* @param snapshot {SynthesizerSnapshot}
|
|
352
|
+
*/
|
|
353
|
+
constructor(
|
|
354
|
+
soundfont,
|
|
355
|
+
sampleRate,
|
|
356
|
+
callbacks,
|
|
357
|
+
effectsEnabled = true,
|
|
358
|
+
enableEventSystem = true,
|
|
359
|
+
initialTime = 0,
|
|
360
|
+
midiChannels = 16,
|
|
361
|
+
snapshot = undefined)
|
|
362
|
+
{
|
|
363
|
+
/**
|
|
364
|
+
* Midi output count
|
|
365
|
+
* @type {number}
|
|
366
|
+
*/
|
|
367
|
+
this.midiOutputsCount = midiChannels;
|
|
368
|
+
/**
|
|
369
|
+
* are the chorus and reverb effects enabled?
|
|
370
|
+
* @type {boolean}
|
|
371
|
+
*/
|
|
372
|
+
this.effectsEnabled = effectsEnabled;
|
|
373
|
+
let initialChannelCount = this.midiOutputsCount;
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* @type {CallbacksTypedef}
|
|
377
|
+
*/
|
|
378
|
+
this.callbacks = callbacks;
|
|
379
|
+
|
|
380
|
+
this.currentSynthTime = initialTime;
|
|
381
|
+
this.sampleRate = sampleRate;
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Sample time in seconds
|
|
385
|
+
* @type {number}
|
|
386
|
+
*/
|
|
387
|
+
this.sampleTime = 1 / sampleRate;
|
|
388
|
+
|
|
389
|
+
this.enableEventSystem = enableEventSystem;
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
for (let i = 0; i < 128; i++)
|
|
393
|
+
{
|
|
394
|
+
this.tunings.push([]);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* @type {WorkletSoundfontManager}
|
|
399
|
+
*/
|
|
400
|
+
this.soundfontManager = new WorkletSoundfontManager(
|
|
401
|
+
soundfont
|
|
402
|
+
);
|
|
403
|
+
this.sendPresetList();
|
|
404
|
+
|
|
405
|
+
this.getDefaultPresets();
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
for (let i = 0; i < initialChannelCount; i++)
|
|
409
|
+
{
|
|
410
|
+
this.createWorkletChannel(false);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
this.midiAudioChannels[DEFAULT_PERCUSSION].preset = this.drumPreset;
|
|
414
|
+
this.midiAudioChannels[DEFAULT_PERCUSSION].drumChannel = true;
|
|
415
|
+
|
|
416
|
+
// these smoothing factors were tested on 44,100 Hz, adjust them to target sample rate here
|
|
417
|
+
this.volumeEnvelopeSmoothingFactor = VOLUME_ENVELOPE_SMOOTHING_FACTOR * (44100 / sampleRate);
|
|
418
|
+
this.panSmoothingFactor = PAN_SMOOTHING_FACTOR * (44100 / sampleRate);
|
|
419
|
+
this.filterSmoothingFactor = FILTER_SMOOTHING_FACTOR * (44100 / sampleRate);
|
|
420
|
+
|
|
421
|
+
this._snapshot = snapshot;
|
|
422
|
+
if (this?._snapshot)
|
|
423
|
+
{
|
|
424
|
+
this.applySynthesizerSnapshot(snapshot);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
this.postReady();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* @returns {number}
|
|
432
|
+
*/
|
|
433
|
+
get currentGain()
|
|
434
|
+
{
|
|
435
|
+
return this.masterGain * this.midiVolume;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
getDefaultPresets()
|
|
439
|
+
{
|
|
440
|
+
// override this to XG, to set the default preset to NOT be XG drums!
|
|
441
|
+
const sys = this.system;
|
|
442
|
+
this.system = "xg";
|
|
443
|
+
this.defaultPreset = this.getPreset(0, 0);
|
|
444
|
+
this.defaultPresetUsesOverride = this.overrideSoundfont?.presets?.indexOf(this.defaultPreset) >= 0;
|
|
445
|
+
this.system = sys;
|
|
446
|
+
this.drumPreset = this.getPreset(128, 0);
|
|
447
|
+
this.defaultDrumsUsesOverride = this.overrideSoundfont?.presets?.indexOf(this.drumPreset) >= 0;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* @param value {SynthSystem}
|
|
452
|
+
*/
|
|
453
|
+
setSystem(value)
|
|
454
|
+
{
|
|
455
|
+
this.system = value;
|
|
456
|
+
this?.callbacks?.masterParameterChange?.(masterParameterType.midiSystem, this.system);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* @param bank {number}
|
|
461
|
+
* @param program {number}
|
|
462
|
+
* @param midiNote {number}
|
|
463
|
+
* @param velocity {number}
|
|
464
|
+
* @returns {Voice[]|undefined}
|
|
465
|
+
*/
|
|
466
|
+
getCachedVoice(bank, program, midiNote, velocity)
|
|
467
|
+
{
|
|
468
|
+
return this.cachedVoices?.[bank]?.[program]?.[midiNote]?.[velocity];
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* @param bank {number}
|
|
473
|
+
* @param program {number}
|
|
474
|
+
* @param midiNote {number}
|
|
475
|
+
* @param velocity {number}
|
|
476
|
+
* @param voices {Voice[]}
|
|
477
|
+
*/
|
|
478
|
+
setCachedVoice(bank, program, midiNote, velocity, voices)
|
|
479
|
+
{
|
|
480
|
+
// make sure that it exists
|
|
481
|
+
if (!this.cachedVoices)
|
|
482
|
+
{
|
|
483
|
+
this.cachedVoices = [];
|
|
484
|
+
}
|
|
485
|
+
if (!this.cachedVoices[bank])
|
|
486
|
+
{
|
|
487
|
+
this.cachedVoices[bank] = [];
|
|
488
|
+
}
|
|
489
|
+
if (!this.cachedVoices[bank][program])
|
|
490
|
+
{
|
|
491
|
+
this.cachedVoices[bank][program] = [];
|
|
492
|
+
}
|
|
493
|
+
if (!this.cachedVoices[bank][program][midiNote])
|
|
494
|
+
{
|
|
495
|
+
this.cachedVoices[bank][program][midiNote] = [];
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// cache
|
|
499
|
+
this.cachedVoices[bank][program][midiNote][velocity] = voices;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
postReady()
|
|
503
|
+
{
|
|
504
|
+
// ensure stbvorbis is fully initialized
|
|
505
|
+
this.processorInitialized.then(() =>
|
|
506
|
+
{
|
|
507
|
+
SpessaSynthInfo("%cSpessaSynth is ready!", consoleColors.recognized);
|
|
508
|
+
this?.callbacks?.ready?.();
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// noinspection JSUnusedGlobalSymbols
|
|
513
|
+
/**
|
|
514
|
+
* Renders float32 audio data to stereo outputs; buffer size of 128 is recommended
|
|
515
|
+
* All float arrays must have the same length
|
|
516
|
+
* @param outputs {Float32Array[]} output stereo channels (L, R)
|
|
517
|
+
* @param reverb {Float32Array[]} reverb stereo channels (L, R)
|
|
518
|
+
* @param chorus {Float32Array[]} chorus stereo channels (L, R)
|
|
519
|
+
*/
|
|
520
|
+
renderAudio(outputs, reverb, chorus)
|
|
521
|
+
{
|
|
522
|
+
this.renderAudioSplit(reverb, chorus, Array(16).fill(outputs));
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Renders the float32 audio data of each channel; buffer size of 128 is recommended
|
|
527
|
+
* All float arrays must have the same length
|
|
528
|
+
* @param reverbChannels {Float32Array[]} reverb stereo channels (L, R)
|
|
529
|
+
* @param chorusChannels {Float32Array[]} chorus stereo channels (L, R)
|
|
530
|
+
* @param separateChannels {Float32Array[][]} a total of 16 stereo pairs (L, R) for each MIDI channel
|
|
531
|
+
*/
|
|
532
|
+
renderAudioSplit(reverbChannels, chorusChannels, separateChannels)
|
|
533
|
+
{
|
|
534
|
+
// process event queue
|
|
535
|
+
const time = this.currentSynthTime;
|
|
536
|
+
while (this.eventQueue[0]?.time <= time)
|
|
537
|
+
{
|
|
538
|
+
this.eventQueue.shift().callback();
|
|
539
|
+
}
|
|
540
|
+
const revL = reverbChannels[0];
|
|
541
|
+
const revR = reverbChannels[1];
|
|
542
|
+
const chrL = chorusChannels[0];
|
|
543
|
+
const chrR = chorusChannels[1];
|
|
544
|
+
|
|
545
|
+
// for every channel
|
|
546
|
+
this.totalVoicesAmount = 0;
|
|
547
|
+
this.midiAudioChannels.forEach((channel, index) =>
|
|
548
|
+
{
|
|
549
|
+
if (channel.voices.length < 1 || channel.isMuted)
|
|
550
|
+
{
|
|
551
|
+
// there's nothing to do!
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
let voiceCount = channel.voices.length;
|
|
555
|
+
const ch = index % 16;
|
|
556
|
+
|
|
557
|
+
// render to the appropriate output
|
|
558
|
+
channel.renderAudio(
|
|
559
|
+
separateChannels[ch][0], separateChannels[ch][1],
|
|
560
|
+
revL, revR,
|
|
561
|
+
chrL, chrR
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
this.totalVoicesAmount += channel.voices.length;
|
|
565
|
+
// if voice count changed, update voice amount
|
|
566
|
+
if (channel.voices.length !== voiceCount)
|
|
567
|
+
{
|
|
568
|
+
channel.sendChannelProperty();
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
// advance the time appropriately
|
|
573
|
+
this.currentSynthTime += separateChannels[0][0].length * this.sampleTime;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// noinspection JSUnusedGlobalSymbols
|
|
577
|
+
destroySynthProcessor()
|
|
578
|
+
{
|
|
579
|
+
this.midiAudioChannels.forEach(c =>
|
|
580
|
+
{
|
|
581
|
+
delete c.midiControllers;
|
|
582
|
+
delete c.voices;
|
|
583
|
+
delete c.sustainedVoices;
|
|
584
|
+
delete c.lockedControllers;
|
|
585
|
+
delete c.preset;
|
|
586
|
+
delete c.customControllers;
|
|
587
|
+
});
|
|
588
|
+
delete this.cachedVoices;
|
|
589
|
+
delete this.midiAudioChannels;
|
|
590
|
+
this.soundfontManager.destroyManager();
|
|
591
|
+
delete this.soundfontManager;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* @param channel {number}
|
|
596
|
+
* @param controllerNumber {number}
|
|
597
|
+
* @param controllerValue {number}
|
|
598
|
+
* @param force {boolean}
|
|
599
|
+
*/
|
|
600
|
+
controllerChange(channel, controllerNumber, controllerValue, force = false)
|
|
601
|
+
{
|
|
602
|
+
this.midiAudioChannels[channel].controllerChange(controllerNumber, controllerValue, force);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* @param channel {number}
|
|
607
|
+
* @param midiNote {number}
|
|
608
|
+
* @param velocity {number}
|
|
609
|
+
*/
|
|
610
|
+
noteOn(channel, midiNote, velocity)
|
|
611
|
+
{
|
|
612
|
+
this.midiAudioChannels[channel].noteOn(midiNote, velocity);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* @param channel {number}
|
|
617
|
+
* @param midiNote {number}
|
|
618
|
+
*/
|
|
619
|
+
noteOff(channel, midiNote)
|
|
620
|
+
{
|
|
621
|
+
this.midiAudioChannels[channel].noteOff(midiNote);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* @param channel {number}
|
|
626
|
+
* @param midiNote {number}
|
|
627
|
+
* @param pressure {number}
|
|
628
|
+
*/
|
|
629
|
+
polyPressure(channel, midiNote, pressure)
|
|
630
|
+
{
|
|
631
|
+
this.midiAudioChannels[channel].polyPressure(midiNote, pressure);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* @param channel {number}
|
|
636
|
+
* @param pressure {number}
|
|
637
|
+
*/
|
|
638
|
+
channelPressure(channel, pressure)
|
|
639
|
+
{
|
|
640
|
+
this.midiAudioChannels[channel].channelPressure(pressure);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* @param channel {number}
|
|
645
|
+
* @param MSB {number}
|
|
646
|
+
* @param LSB {number}
|
|
647
|
+
*/
|
|
648
|
+
pitchWheel(channel, MSB, LSB)
|
|
649
|
+
{
|
|
650
|
+
this.midiAudioChannels[channel].pitchWheel(MSB, LSB);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* @param channel {number}
|
|
655
|
+
* @param programNumber {number}
|
|
656
|
+
*/
|
|
657
|
+
programChange(channel, programNumber)
|
|
658
|
+
{
|
|
659
|
+
this.midiAudioChannels[channel].programChange(programNumber);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// noinspection JSUnusedGlobalSymbols
|
|
663
|
+
/**
|
|
664
|
+
* @param message {Uint8Array}
|
|
665
|
+
* @param channelOffset {number}
|
|
666
|
+
* @param force {boolean} cool stuff
|
|
667
|
+
* @param options {SynthMethodOptions}
|
|
668
|
+
*/
|
|
669
|
+
processMessage(message, channelOffset, force, options)
|
|
670
|
+
{
|
|
671
|
+
const call = () =>
|
|
672
|
+
{
|
|
673
|
+
const statusByteData = getEvent(message[0]);
|
|
674
|
+
|
|
675
|
+
const channel = statusByteData.channel + channelOffset;
|
|
676
|
+
// process the event
|
|
677
|
+
switch (statusByteData.status)
|
|
678
|
+
{
|
|
679
|
+
case messageTypes.noteOn:
|
|
680
|
+
const velocity = message[2];
|
|
681
|
+
if (velocity > 0)
|
|
682
|
+
{
|
|
683
|
+
this.noteOn(channel, message[1], velocity);
|
|
684
|
+
}
|
|
685
|
+
else
|
|
686
|
+
{
|
|
687
|
+
this.noteOff(channel, message[1]);
|
|
688
|
+
}
|
|
689
|
+
break;
|
|
690
|
+
|
|
691
|
+
case messageTypes.noteOff:
|
|
692
|
+
if (force)
|
|
693
|
+
{
|
|
694
|
+
this.midiAudioChannels[channel].killNote(message[1]);
|
|
695
|
+
}
|
|
696
|
+
else
|
|
697
|
+
{
|
|
698
|
+
this.noteOff(channel, message[1]);
|
|
699
|
+
}
|
|
700
|
+
break;
|
|
701
|
+
|
|
702
|
+
case messageTypes.pitchBend:
|
|
703
|
+
this.pitchWheel(channel, message[2], message[1]);
|
|
704
|
+
break;
|
|
705
|
+
|
|
706
|
+
case messageTypes.controllerChange:
|
|
707
|
+
this.controllerChange(channel, message[1], message[2], force);
|
|
708
|
+
break;
|
|
709
|
+
|
|
710
|
+
case messageTypes.programChange:
|
|
711
|
+
this.programChange(channel, message[1]);
|
|
712
|
+
break;
|
|
713
|
+
|
|
714
|
+
case messageTypes.polyPressure:
|
|
715
|
+
this.polyPressure(channel, message[0], message[1]);
|
|
716
|
+
break;
|
|
717
|
+
|
|
718
|
+
case messageTypes.channelPressure:
|
|
719
|
+
this.channelPressure(channel, message[1]);
|
|
720
|
+
break;
|
|
721
|
+
|
|
722
|
+
case messageTypes.systemExclusive:
|
|
723
|
+
this.systemExclusive(new IndexedByteArray(message.slice(1)), channelOffset);
|
|
724
|
+
break;
|
|
725
|
+
|
|
726
|
+
case messageTypes.reset:
|
|
727
|
+
this.stopAllChannels(true);
|
|
728
|
+
this.resetAllControllers();
|
|
729
|
+
break;
|
|
730
|
+
|
|
731
|
+
default:
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
const time = options.time;
|
|
737
|
+
if (time > this.currentSynthTime)
|
|
738
|
+
{
|
|
739
|
+
this.eventQueue.push({
|
|
740
|
+
callback: call.bind(this),
|
|
741
|
+
time: time
|
|
742
|
+
});
|
|
743
|
+
this.eventQueue.sort((e1, e2) => e1.time - e2.time);
|
|
744
|
+
}
|
|
745
|
+
else
|
|
746
|
+
{
|
|
747
|
+
call();
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* @param volume {number} 0 to 1
|
|
753
|
+
*/
|
|
754
|
+
setMIDIVolume(volume)
|
|
755
|
+
{
|
|
756
|
+
// GM2 specification, section 4.1: volume is squared.
|
|
757
|
+
// though, according to my own testing, Math.E seems like a better choice
|
|
758
|
+
this.midiVolume = Math.pow(volume, Math.E);
|
|
759
|
+
this.setMasterParameter(masterParameterType.masterPan, this.pan);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Calls synth event from the worklet side
|
|
764
|
+
* @param eventName {EventTypes} the event name
|
|
765
|
+
* @param eventData {EventCallbackData}
|
|
766
|
+
* @this {SpessaSynthProcessor}
|
|
767
|
+
*/
|
|
768
|
+
callEvent(eventName, eventData)
|
|
769
|
+
{
|
|
770
|
+
this?.callbacks?.eventCall?.(eventName, eventData);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// include other methods
|
|
775
|
+
// voice related
|
|
776
|
+
SpessaSynthProcessor.prototype.voiceKilling = voiceKilling;
|
|
777
|
+
SpessaSynthProcessor.prototype.getVoices = getVoices;
|
|
778
|
+
|
|
779
|
+
// system-exclusive related
|
|
780
|
+
SpessaSynthProcessor.prototype.systemExclusive = systemExclusive;
|
|
781
|
+
|
|
782
|
+
// channel related
|
|
783
|
+
SpessaSynthProcessor.prototype.stopAllChannels = stopAllChannels;
|
|
784
|
+
SpessaSynthProcessor.prototype.createWorkletChannel = createMidiChannel;
|
|
785
|
+
SpessaSynthProcessor.prototype.resetAllControllers = resetAllControllers;
|
|
786
|
+
|
|
787
|
+
// master parameter related
|
|
788
|
+
SpessaSynthProcessor.prototype.setMasterParameter = setMasterParameter;
|
|
789
|
+
|
|
790
|
+
// tuning related
|
|
791
|
+
SpessaSynthProcessor.prototype.transposeAllChannels = transposeAllChannels;
|
|
792
|
+
SpessaSynthProcessor.prototype.setMasterTuning = setMasterTuning;
|
|
793
|
+
|
|
794
|
+
// program related
|
|
795
|
+
SpessaSynthProcessor.prototype.getPreset = getPreset;
|
|
796
|
+
SpessaSynthProcessor.prototype.reloadSoundFont = reloadSoundFont;
|
|
797
|
+
SpessaSynthProcessor.prototype.clearSoundFont = clearSoundFont;
|
|
798
|
+
SpessaSynthProcessor.prototype.setEmbeddedSoundFont = setEmbeddedSoundFont;
|
|
799
|
+
SpessaSynthProcessor.prototype.sendPresetList = sendPresetList;
|
|
800
|
+
|
|
801
|
+
// snapshot related
|
|
802
|
+
SpessaSynthProcessor.prototype.applySynthesizerSnapshot = applySynthesizerSnapshot;
|
|
803
|
+
|
|
804
|
+
export { SpessaSynthProcessor };
|