spessasynth_core 1.1.3 → 1.1.5

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 (189) hide show
  1. package/LICENSE +3 -26
  2. package/README.md +156 -474
  3. package/index.js +74 -8
  4. package/package.json +21 -8
  5. package/src/externals/fflate/LICENSE +21 -0
  6. package/src/externals/fflate/fflate.min.js +1 -0
  7. package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +12 -0
  8. package/src/externals/stbvorbis_sync/LICENSE +202 -0
  9. package/src/externals/stbvorbis_sync/NOTICE +6 -0
  10. package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +1 -0
  11. package/src/midi/README.md +32 -0
  12. package/src/midi/basic_midi.js +567 -0
  13. package/src/midi/midi_builder.js +202 -0
  14. package/src/midi/midi_loader.js +324 -0
  15. package/{spessasynth_core/midi_parser → src/midi}/midi_message.js +58 -35
  16. package/src/midi/midi_sequence.js +224 -0
  17. package/src/midi/midi_tools/get_note_times.js +154 -0
  18. package/src/midi/midi_tools/midi_editor.js +611 -0
  19. package/src/midi/midi_tools/midi_writer.js +99 -0
  20. package/src/midi/midi_tools/rmidi_writer.js +567 -0
  21. package/src/midi/midi_tools/used_keys_loaded.js +238 -0
  22. package/src/midi/xmf_loader.js +454 -0
  23. package/src/sequencer/README.md +5 -0
  24. package/src/sequencer/events.js +81 -0
  25. package/src/sequencer/play.js +349 -0
  26. package/src/sequencer/process_event.js +165 -0
  27. package/{spessasynth_core/sequencer/worklet_sequencer → src/sequencer}/process_tick.js +103 -84
  28. package/src/sequencer/sequencer_engine.js +367 -0
  29. package/src/sequencer/song_control.js +201 -0
  30. package/src/soundfont/README.md +13 -0
  31. package/src/soundfont/basic_soundfont/basic_instrument.js +77 -0
  32. package/src/soundfont/basic_soundfont/basic_preset.js +336 -0
  33. package/src/soundfont/basic_soundfont/basic_sample.js +206 -0
  34. package/src/soundfont/basic_soundfont/basic_soundfont.js +565 -0
  35. package/src/soundfont/basic_soundfont/basic_zone.js +64 -0
  36. package/src/soundfont/basic_soundfont/basic_zones.js +43 -0
  37. package/src/soundfont/basic_soundfont/generator.js +220 -0
  38. package/src/soundfont/basic_soundfont/modulator.js +378 -0
  39. package/src/soundfont/basic_soundfont/riff_chunk.js +149 -0
  40. package/src/soundfont/basic_soundfont/write_dls/art2.js +173 -0
  41. package/src/soundfont/basic_soundfont/write_dls/articulator.js +49 -0
  42. package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +400 -0
  43. package/src/soundfont/basic_soundfont/write_dls/ins.js +103 -0
  44. package/src/soundfont/basic_soundfont/write_dls/lins.js +18 -0
  45. package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +330 -0
  46. package/src/soundfont/basic_soundfont/write_dls/rgn2.js +121 -0
  47. package/src/soundfont/basic_soundfont/write_dls/wave.js +94 -0
  48. package/src/soundfont/basic_soundfont/write_dls/write_dls.js +119 -0
  49. package/src/soundfont/basic_soundfont/write_dls/wsmp.js +78 -0
  50. package/src/soundfont/basic_soundfont/write_dls/wvpl.js +32 -0
  51. package/src/soundfont/basic_soundfont/write_sf2/ibag.js +39 -0
  52. package/src/soundfont/basic_soundfont/write_sf2/igen.js +80 -0
  53. package/src/soundfont/basic_soundfont/write_sf2/imod.js +46 -0
  54. package/src/soundfont/basic_soundfont/write_sf2/inst.js +34 -0
  55. package/src/soundfont/basic_soundfont/write_sf2/pbag.js +39 -0
  56. package/src/soundfont/basic_soundfont/write_sf2/pgen.js +82 -0
  57. package/src/soundfont/basic_soundfont/write_sf2/phdr.js +42 -0
  58. package/src/soundfont/basic_soundfont/write_sf2/pmod.js +46 -0
  59. package/src/soundfont/basic_soundfont/write_sf2/sdta.js +80 -0
  60. package/src/soundfont/basic_soundfont/write_sf2/shdr.js +55 -0
  61. package/src/soundfont/basic_soundfont/write_sf2/write.js +222 -0
  62. package/src/soundfont/dls/articulator_converter.js +396 -0
  63. package/src/soundfont/dls/dls_destinations.js +38 -0
  64. package/src/soundfont/dls/dls_preset.js +44 -0
  65. package/src/soundfont/dls/dls_sample.js +75 -0
  66. package/src/soundfont/dls/dls_soundfont.js +186 -0
  67. package/src/soundfont/dls/dls_sources.js +62 -0
  68. package/src/soundfont/dls/dls_zone.js +95 -0
  69. package/src/soundfont/dls/read_articulation.js +299 -0
  70. package/src/soundfont/dls/read_instrument.js +121 -0
  71. package/src/soundfont/dls/read_instrument_list.js +17 -0
  72. package/src/soundfont/dls/read_lart.js +35 -0
  73. package/src/soundfont/dls/read_region.js +152 -0
  74. package/src/soundfont/dls/read_samples.js +270 -0
  75. package/src/soundfont/load_soundfont.js +21 -0
  76. package/src/soundfont/read_sf2/generators.js +46 -0
  77. package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/instruments.js +20 -14
  78. package/src/soundfont/read_sf2/modulators.js +36 -0
  79. package/src/soundfont/read_sf2/presets.js +80 -0
  80. package/src/soundfont/read_sf2/samples.js +304 -0
  81. package/src/soundfont/read_sf2/soundfont.js +305 -0
  82. package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/zones.js +68 -69
  83. package/src/synthetizer/README.md +7 -0
  84. package/src/synthetizer/audio_engine/README.md +9 -0
  85. package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +266 -0
  86. package/src/synthetizer/audio_engine/engine_components/controller_tables.js +88 -0
  87. package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +150 -0
  88. package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/lfo.js +9 -6
  89. package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +282 -0
  90. package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +467 -0
  91. package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +181 -0
  92. package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/modulator_curves.js +33 -30
  93. package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +221 -0
  94. package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +120 -0
  95. package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/unit_converter.js +11 -4
  96. package/src/synthetizer/audio_engine/engine_components/voice.js +519 -0
  97. package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +401 -0
  98. package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +263 -0
  99. package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +132 -0
  100. package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +48 -0
  101. package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +241 -0
  102. package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +27 -0
  103. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +253 -0
  104. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +66 -0
  105. package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +17 -0
  106. package/src/synthetizer/audio_engine/engine_methods/note_on.js +175 -0
  107. package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +92 -0
  108. package/src/synthetizer/audio_engine/engine_methods/program_change.js +61 -0
  109. package/src/synthetizer/audio_engine/engine_methods/render_voice.js +196 -0
  110. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/clear_sound_font.js +30 -0
  111. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +22 -0
  112. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/reload_sound_font.js +28 -0
  113. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/send_preset_list.js +31 -0
  114. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/set_embedded_sound_font.js +21 -0
  115. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +20 -0
  116. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +55 -0
  117. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +16 -0
  118. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +30 -0
  119. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +63 -0
  120. package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +776 -0
  121. package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +24 -0
  122. package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +33 -0
  123. package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +31 -0
  124. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +15 -0
  125. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +27 -0
  126. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +19 -0
  127. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +27 -0
  128. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +15 -0
  129. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +34 -0
  130. package/src/synthetizer/audio_engine/main_processor.js +804 -0
  131. package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +15 -0
  132. package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +175 -0
  133. package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +116 -0
  134. package/src/synthetizer/synth_constants.js +22 -0
  135. package/{spessasynth_core → src}/utils/README.md +1 -0
  136. package/src/utils/buffer_to_wav.js +185 -0
  137. package/src/utils/byte_functions/big_endian.js +32 -0
  138. package/src/utils/byte_functions/little_endian.js +77 -0
  139. package/src/utils/byte_functions/string.js +107 -0
  140. package/src/utils/byte_functions/variable_length_quantity.js +42 -0
  141. package/src/utils/fill_with_defaults.js +21 -0
  142. package/src/utils/indexed_array.js +52 -0
  143. package/{spessasynth_core → src}/utils/loggin.js +70 -78
  144. package/src/utils/other.js +92 -0
  145. package/src/utils/sysex_detector.js +58 -0
  146. package/src/utils/xg_hacks.js +193 -0
  147. package/.idea/inspectionProfiles/Project_Default.xml +0 -10
  148. package/.idea/jsLibraryMappings.xml +0 -6
  149. package/.idea/modules.xml +0 -8
  150. package/.idea/spessasynth_core.iml +0 -12
  151. package/.idea/vcs.xml +0 -6
  152. package/spessasynth_core/midi_parser/README.md +0 -3
  153. package/spessasynth_core/midi_parser/midi_loader.js +0 -386
  154. package/spessasynth_core/sequencer/sequencer.js +0 -202
  155. package/spessasynth_core/sequencer/worklet_sequencer/play.js +0 -209
  156. package/spessasynth_core/sequencer/worklet_sequencer/process_event.js +0 -120
  157. package/spessasynth_core/sequencer/worklet_sequencer/song_control.js +0 -112
  158. package/spessasynth_core/soundfont/README.md +0 -4
  159. package/spessasynth_core/soundfont/chunk/generators.js +0 -205
  160. package/spessasynth_core/soundfont/chunk/modulators.js +0 -232
  161. package/spessasynth_core/soundfont/chunk/presets.js +0 -264
  162. package/spessasynth_core/soundfont/chunk/riff_chunk.js +0 -46
  163. package/spessasynth_core/soundfont/chunk/samples.js +0 -250
  164. package/spessasynth_core/soundfont/soundfont_parser.js +0 -301
  165. package/spessasynth_core/synthetizer/README.md +0 -6
  166. package/spessasynth_core/synthetizer/synthesizer.js +0 -313
  167. package/spessasynth_core/synthetizer/worklet_system/README.md +0 -3
  168. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/controller_control.js +0 -290
  169. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/data_entry.js +0 -280
  170. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_off.js +0 -102
  171. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_on.js +0 -77
  172. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/program_control.js +0 -140
  173. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/system_exclusive.js +0 -266
  174. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/tuning_control.js +0 -104
  175. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/vibrato_control.js +0 -29
  176. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/voice_control.js +0 -223
  177. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +0 -133
  178. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +0 -73
  179. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +0 -76
  180. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +0 -272
  181. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +0 -83
  182. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +0 -175
  183. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +0 -106
  184. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +0 -285
  185. package/spessasynth_core/utils/buffer_to_wav.js +0 -70
  186. package/spessasynth_core/utils/byte_functions.js +0 -141
  187. package/spessasynth_core/utils/other.js +0 -49
  188. package/spessasynth_core/utils/shiftable_array.js +0 -26
  189. 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 };