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
@@ -1,4 +1,4 @@
1
- import { modulatorCurveTypes } from '../../../soundfont/chunk/modulators.js'
1
+ import { modulatorCurveTypes } from "../../../soundfont/basic_soundfont/modulator.js";
2
2
 
3
3
  /**
4
4
  * modulator_curves.js
@@ -8,22 +8,21 @@ import { modulatorCurveTypes } from '../../../soundfont/chunk/modulators.js'
8
8
  // the length of the precomputed curve tables
9
9
  export const MOD_PRECOMPUTED_LENGTH = 16384;
10
10
 
11
- // Precalculate lookup tables for concave and convers
12
- const concave = new Float32Array(MOD_PRECOMPUTED_LENGTH);
13
- const convex = new Float32Array(MOD_PRECOMPUTED_LENGTH);
11
+ // Precalculate lookup tables for concave and convex curves
12
+ const concave = new Float32Array(MOD_PRECOMPUTED_LENGTH + 1);
13
+ const convex = new Float32Array(MOD_PRECOMPUTED_LENGTH + 1);
14
14
  // the equation is taken from FluidSynth as it's the standard for soundFonts
15
- // more precisely, this:
16
- // https://github.com/FluidSynth/fluidsynth/blob/cb8da1e1e2c0a5cff2bab6a419755b598b793384/src/gentables/gen_conv.c#L55
15
+ // more precisely, the gen_conv.c file
17
16
  concave[0] = 0;
18
- concave[MOD_PRECOMPUTED_LENGTH - 1] = 1;
17
+ concave[concave.length - 1] = 1;
19
18
 
20
19
  convex[0] = 0;
21
- convex[MOD_PRECOMPUTED_LENGTH - 1] = 1;
22
- for(let i = 1; i < MOD_PRECOMPUTED_LENGTH - 1; i++)
20
+ convex[convex.length - 1] = 1;
21
+ for (let i = 1; i < MOD_PRECOMPUTED_LENGTH - 1; i++)
23
22
  {
24
- let x = (-200 * 2 / 960) * Math.log(i / (MOD_PRECOMPUTED_LENGTH - 1)) / Math.LN10;
23
+ let x = (-200 * 2 / 960) * Math.log(i / (concave.length - 1)) / Math.LN10;
25
24
  convex[i] = 1 - x;
26
- concave[MOD_PRECOMPUTED_LENGTH - 1 - i] = x;
25
+ concave[concave.length - 1 - i] = x;
27
26
  }
28
27
 
29
28
  /**
@@ -32,52 +31,56 @@ for(let i = 1; i < MOD_PRECOMPUTED_LENGTH - 1; i++)
32
31
  * @param direction {number} 0 or 1
33
32
  * @param curveType {number} see modulatorCurveTypes in modulators.js
34
33
  * @param value {number} the linear value, 0 to 1
35
- * @returns {number} the transformed value, 0 to 1 or -1 to 1
34
+ * @returns {number} the transformed value, 0 to 1, or -1 to 1
36
35
  */
37
- export function getModulatorCurveValue(direction, curveType, value, polarity) {
36
+ export function getModulatorCurveValue(direction, curveType, value, polarity)
37
+ {
38
38
  // inverse the value if needed
39
- if(direction)
39
+ if (direction)
40
40
  {
41
- value = 1 - value
41
+ value = 1 - value;
42
42
  }
43
- switch (curveType) {
43
+ switch (curveType)
44
+ {
44
45
  case modulatorCurveTypes.linear:
45
- if (polarity) {
46
- // bipolar
46
+ if (polarity)
47
+ {
48
+ // bipolar curve
47
49
  return value * 2 - 1;
48
50
  }
49
51
  return value;
50
-
52
+
51
53
  case modulatorCurveTypes.switch:
52
54
  // switch
53
55
  value = value > 0.5 ? 1 : 0;
54
- if (polarity) {
56
+ if (polarity)
57
+ {
55
58
  // multiply
56
59
  return value * 2 - 1;
57
60
  }
58
61
  return value;
59
-
62
+
60
63
  case modulatorCurveTypes.concave:
61
64
  // look up the value
62
- if(polarity)
65
+ if (polarity)
63
66
  {
64
67
  value = value * 2 - 1;
65
- if(value < 0)
68
+ if (value < 0)
66
69
  {
67
- return 1 - concave[~~(value * -MOD_PRECOMPUTED_LENGTH)] - 1;
70
+ return -concave[~~(value * -MOD_PRECOMPUTED_LENGTH)];
68
71
  }
69
- return concave[~~value * MOD_PRECOMPUTED_LENGTH];
72
+ return concave[~~(value * MOD_PRECOMPUTED_LENGTH)];
70
73
  }
71
- return concave[~~(value * MOD_PRECOMPUTED_LENGTH)]
72
-
74
+ return concave[~~(value * MOD_PRECOMPUTED_LENGTH)];
75
+
73
76
  case modulatorCurveTypes.convex:
74
77
  // look up the value
75
- if(polarity)
78
+ if (polarity)
76
79
  {
77
80
  value = value * 2 - 1;
78
- if(value < 0)
81
+ if (value < 0)
79
82
  {
80
- return 1 - convex[~~(value * -MOD_PRECOMPUTED_LENGTH)] - 1;
83
+ return -convex[~~(value * -MOD_PRECOMPUTED_LENGTH)];
81
84
  }
82
85
  return convex[~~(value * MOD_PRECOMPUTED_LENGTH)];
83
86
  }
@@ -0,0 +1,221 @@
1
+ import { SpessaSynthWarn } from "../../../utils/loggin.js";
2
+ import { loadSoundFont } from "../../../soundfont/load_soundfont.js";
3
+ import { isXGDrums } from "../../../utils/xg_hacks.js";
4
+
5
+ /**
6
+ * @typedef {Object} SoundFontType
7
+ * @property {string} id - unique id for the soundfont
8
+ * @property {BasicSoundBank} soundfont - the soundfont itself
9
+ * @property {number} bankOffset - the soundfont's bank offset
10
+ */
11
+
12
+ export class WorkletSoundfontManager
13
+ {
14
+ /**
15
+ * Creates a new instance of worklet soundfont manager (worklet scope)
16
+ * @param initialSoundFontBuffer {ArrayBuffer} Array buffer of the soundfont. This soudfont always has the id "main"
17
+ */
18
+ constructor(initialSoundFontBuffer)
19
+ {
20
+ this.reloadManager(initialSoundFontBuffer);
21
+ }
22
+
23
+ generatePresetList()
24
+ {
25
+ /**
26
+ * <"bank-program", "presetName">
27
+ * @type {Object<string, string>}
28
+ */
29
+ const presetList = {};
30
+ // gather the presets in reverse and replace if necessary
31
+ for (let i = this.soundfontList.length - 1; i >= 0; i--)
32
+ {
33
+ const font = this.soundfontList[i];
34
+ /**
35
+ * prevent preset names from the same soudfont from being overriden
36
+ * if the soundfont has two presets with matching bank and program
37
+ * @type {Set<string>}
38
+ */
39
+ const presets = new Set();
40
+ for (const p of font.soundfont.presets)
41
+ {
42
+ const presetString = `${p.bank + font.bankOffset}-${p.program}`;
43
+ if (presets.has(presetString))
44
+ {
45
+ continue;
46
+ }
47
+ presets.add(presetString);
48
+ presetList[presetString] = p.presetName;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * @type {{bank: number, presetName: string, program: number}[]}
54
+ */
55
+ this.presetList = [];
56
+ for (const [string, name] of Object.entries(presetList))
57
+ {
58
+ const pb = string.split("-");
59
+ this.presetList.push({
60
+ presetName: name,
61
+ program: parseInt(pb[1]),
62
+ bank: parseInt(pb[0])
63
+ });
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Get the final preset list
69
+ * @returns {{bank: number, presetName: string, program: number}[]}
70
+ */
71
+ getPresetList()
72
+ {
73
+ return this.presetList.slice();
74
+ }
75
+
76
+ /**
77
+ * Clears all soundfonts and adds a new one
78
+ * @param soundFontArrayBuffer {ArrayBuffer}
79
+ */
80
+ reloadManager(soundFontArrayBuffer)
81
+ {
82
+ const font = loadSoundFont(soundFontArrayBuffer);
83
+ /**
84
+ * All the soundfonts, ordered from the most important to the least.
85
+ * @type {SoundFontType[]}
86
+ */
87
+ this.soundfontList = [];
88
+ this.soundfontList.push({
89
+ id: "main",
90
+ bankOffset: 0,
91
+ soundfont: font
92
+ });
93
+ this.generatePresetList();
94
+ }
95
+
96
+ deleteSoundFont(id)
97
+ {
98
+ if (this.soundfontList.length === 0)
99
+ {
100
+ SpessaSynthWarn("1 soundfont left. Aborting!");
101
+ return;
102
+ }
103
+ const index = this.soundfontList.findIndex(s => s.id === id);
104
+ if (index === -1)
105
+ {
106
+ SpessaSynthWarn(`No soundfont with id of "${id}" found. Aborting!`);
107
+ return;
108
+ }
109
+ delete this.soundfontList[index].soundfont.presets;
110
+ delete this.soundfontList[index].soundfont.instruments;
111
+ delete this.soundfontList[index].soundfont.samples;
112
+ this.soundfontList.splice(index, 1);
113
+ this.generatePresetList();
114
+ }
115
+
116
+ /**
117
+ * Adds a new soundfont buffer with a given ID
118
+ * @param buffer {ArrayBuffer}
119
+ * @param id {string}
120
+ * @param bankOffset {number}
121
+ */
122
+ addNewSoundFont(buffer, id, bankOffset)
123
+ {
124
+ if (this.soundfontList.find(s => s.id === id) !== undefined)
125
+ {
126
+ throw new Error("Cannot overwrite the existing soundfont. Use soundfontManager.delete(id) instead.");
127
+ }
128
+ this.soundfontList.push({
129
+ id: id,
130
+ soundfont: loadSoundFont(buffer),
131
+ bankOffset: bankOffset
132
+ });
133
+ this.generatePresetList();
134
+ }
135
+
136
+ /**
137
+ * Rearranges the soundfonts
138
+ * @param newList {string[]} the order of soundfonts, a list of strings, first overwrites second
139
+ */
140
+ rearrangeSoundFonts(newList)
141
+ {
142
+ this.soundfontList.sort((a, b) =>
143
+ newList.indexOf(a.id) - newList.indexOf(b.id)
144
+ );
145
+ this.generatePresetList();
146
+ }
147
+
148
+ /**
149
+ * Gets a given preset from the soundfont stack
150
+ * @param bankNumber {number}
151
+ * @param programNumber {number}
152
+ * @param allowXGDrums {boolean} if true, allows XG drum banks (120, 126 and 127) as drum preset
153
+ * @returns {BasicPreset} the preset
154
+ */
155
+ getPreset(bankNumber, programNumber, allowXGDrums = false)
156
+ {
157
+ if (this.soundfontList.length < 1)
158
+ {
159
+ throw new Error("No soundfonts! This should never happen.");
160
+ }
161
+ for (const sf of this.soundfontList)
162
+ {
163
+ // check for the preset (with given offset)
164
+ const preset = sf.soundfont.getPresetNoFallback(
165
+ bankNumber - sf.bankOffset,
166
+ programNumber,
167
+ allowXGDrums
168
+ );
169
+ if (preset !== undefined)
170
+ {
171
+ return preset;
172
+ }
173
+ // if not found, advance to the next soundfont
174
+ }
175
+ const isDrum = bankNumber === 128 || (allowXGDrums && isXGDrums(bankNumber));
176
+ // if none found, return the first correct preset found
177
+ if (!isDrum)
178
+ {
179
+ for (const sf of this.soundfontList)
180
+ {
181
+ const preset = sf.soundfont.presets.find(p => p.program === programNumber && !p.isDrumPreset(
182
+ allowXGDrums));
183
+ if (preset)
184
+ {
185
+ return preset;
186
+ }
187
+ }
188
+ // if nothing at all, use the first preset
189
+ return this.soundfontList[0].soundfont.presets[0];
190
+ }
191
+ else
192
+ {
193
+ for (const sf of this.soundfontList)
194
+ {
195
+ // check for any drum type (127/128) and matching program
196
+ const p = sf.soundfont.presets.find(p => p.isDrumPreset(allowXGDrums) && p.program === programNumber);
197
+ if (p)
198
+ {
199
+ return p;
200
+ }
201
+ // check for any drum preset
202
+ const preset = sf.soundfont.presets.find(p => p.isDrumPreset(allowXGDrums));
203
+ if (preset)
204
+ {
205
+ return preset;
206
+ }
207
+ }
208
+ // if nothing at all, use the first preset
209
+ return this.soundfontList[0].soundfont.presets[0];
210
+ }
211
+ }
212
+
213
+ destroyManager()
214
+ {
215
+ this.soundfontList.forEach(s =>
216
+ {
217
+ s.soundfont.destroySoundBank();
218
+ });
219
+ delete this.soundfontList;
220
+ }
221
+ }
@@ -0,0 +1,120 @@
1
+ import { generatorTypes } from "../../../soundfont/basic_soundfont/generator.js";
2
+
3
+ /**
4
+ * stereo_panner.js
5
+ * purpose: pans a given voice out to the stereo output and to the effects' outputs
6
+ */
7
+
8
+ export const PAN_SMOOTHING_FACTOR = 0.05;
9
+
10
+ export const WORKLET_SYSTEM_REVERB_DIVIDER = 4600;
11
+ export const WORKLET_SYSTEM_CHORUS_DIVIDER = 2000;
12
+ const HALF_PI = Math.PI / 2;
13
+
14
+ const MIN_PAN = -500;
15
+ const MAX_PAN = 500;
16
+ const PAN_RESOLUTION = MAX_PAN - MIN_PAN;
17
+
18
+ // initialize pan lookup tables
19
+ const panTableLeft = new Float32Array(PAN_RESOLUTION + 1);
20
+ const panTableRight = new Float32Array(PAN_RESOLUTION + 1);
21
+ for (let pan = MIN_PAN; pan <= MAX_PAN; pan++)
22
+ {
23
+ // clamp to 0-1
24
+ const realPan = (pan - MIN_PAN) / PAN_RESOLUTION;
25
+ const tableIndex = pan - MIN_PAN;
26
+ panTableLeft[tableIndex] = Math.cos(HALF_PI * realPan);
27
+ panTableRight[tableIndex] = Math.sin(HALF_PI * realPan);
28
+ }
29
+
30
+ /**
31
+ * Pans the voice to the given output buffers
32
+ * @param voice {Voice} the voice to pan
33
+ * @param inputBuffer {Float32Array} the input buffer in mono
34
+ * @param outputLeft {Float32Array} left output buffer
35
+ * @param outputRight {Float32Array} right output buffer
36
+ * @param reverbLeft {Float32Array} left reverb input
37
+ * @param reverbRight {Float32Array} right reverb input
38
+ * @param chorusLeft {Float32Array} left chorus buffer
39
+ * @param chorusRight {Float32Array} right chorus buffer
40
+ * @this {MidiAudioChannel}
41
+ */
42
+ export function panVoice(voice,
43
+ inputBuffer,
44
+ outputLeft, outputRight,
45
+ reverbLeft, reverbRight,
46
+ chorusLeft, chorusRight)
47
+ {
48
+ if (isNaN(inputBuffer[0]))
49
+ {
50
+ return;
51
+ }
52
+ /**
53
+ * clamp -500 to 500
54
+ * @type {number}
55
+ */
56
+ let pan;
57
+ if (voice.overridePan)
58
+ {
59
+ pan = voice.overridePan;
60
+ }
61
+ else
62
+ {
63
+ // smooth out pan to prevent clicking
64
+ voice.currentPan += (voice.modulatedGenerators[generatorTypes.pan] - voice.currentPan) * this.synth.panSmoothingFactor;
65
+ pan = voice.currentPan;
66
+ }
67
+
68
+ const gain = this.synth.currentGain * voice.gain;
69
+ const index = ~~(pan + 500);
70
+ // get voice's gain levels for each channel
71
+ const gainLeft = panTableLeft[index] * gain * this.synth.panLeft;
72
+ const gainRight = panTableRight[index] * gain * this.synth.panRight;
73
+
74
+ // disable reverb and chorus if necessary
75
+ if (this.synth.effectsEnabled)
76
+ {
77
+ const reverbSend = voice.modulatedGenerators[generatorTypes.reverbEffectsSend];
78
+ if (reverbSend > 0)
79
+ {
80
+ // reverb is mono so we need to multiply by gain
81
+ const reverbGain = this.synth.reverbGain * gain * (reverbSend / WORKLET_SYSTEM_REVERB_DIVIDER);
82
+ for (let i = 0; i < inputBuffer.length; i++)
83
+ {
84
+ reverbLeft[i] += reverbGain * inputBuffer[i];
85
+ }
86
+ // copy as its mono
87
+ reverbRight.set(reverbLeft);
88
+ }
89
+
90
+ const chorusSend = voice.modulatedGenerators[generatorTypes.chorusEffectsSend];
91
+ if (chorusSend > 0)
92
+ {
93
+ // chorus is stereo so we do not need to
94
+ const chorusGain = this.synth.chorusGain * chorusSend / WORKLET_SYSTEM_CHORUS_DIVIDER;
95
+ const chorusLeftGain = gainLeft * chorusGain;
96
+ const chorusRightGain = gainRight * chorusGain;
97
+ for (let i = 0; i < inputBuffer.length; i++)
98
+ {
99
+ chorusLeft[i] += chorusLeftGain * inputBuffer[i];
100
+ chorusRight[i] += chorusRightGain * inputBuffer[i];
101
+ }
102
+ }
103
+ }
104
+
105
+ // mix down the audio data
106
+ if (gainLeft > 0)
107
+ {
108
+ for (let i = 0; i < inputBuffer.length; i++)
109
+ {
110
+ outputLeft[i] += gainLeft * inputBuffer[i];
111
+ }
112
+ }
113
+ if (gainRight > 0)
114
+ {
115
+ for (let i = 0; i < inputBuffer.length; i++)
116
+ {
117
+ outputRight[i] += gainRight * inputBuffer[i];
118
+ }
119
+ }
120
+ }
@@ -8,7 +8,8 @@
8
8
  const MIN_TIMECENT = -15000;
9
9
  const MAX_TIMECENT = 15000;
10
10
  const timecentLookupTable = new Float32Array(MAX_TIMECENT - MIN_TIMECENT + 1);
11
- for (let i = 0; i < timecentLookupTable.length; i++) {
11
+ for (let i = 0; i < timecentLookupTable.length; i++)
12
+ {
12
13
  const timecents = MIN_TIMECENT + i;
13
14
  timecentLookupTable[i] = Math.pow(2, timecents / 1200);
14
15
  }
@@ -20,6 +21,10 @@ for (let i = 0; i < timecentLookupTable.length; i++) {
20
21
  */
21
22
  export function timecentsToSeconds(timecents)
22
23
  {
24
+ if (timecents <= -32767)
25
+ {
26
+ return 0;
27
+ }
23
28
  return timecentLookupTable[timecents - MIN_TIMECENT];
24
29
  }
25
30
 
@@ -27,7 +32,8 @@ export function timecentsToSeconds(timecents)
27
32
  const MIN_ABS_CENT = -20000; // freqVibLfo
28
33
  const MAX_ABS_CENT = 16500; // filterFc
29
34
  const absoluteCentLookupTable = new Float32Array(MAX_ABS_CENT - MIN_ABS_CENT + 1);
30
- for (let i = 0; i < absoluteCentLookupTable.length; i++) {
35
+ for (let i = 0; i < absoluteCentLookupTable.length; i++)
36
+ {
31
37
  const absoluteCents = MIN_ABS_CENT + i;
32
38
  absoluteCentLookupTable[i] = 440 * Math.pow(2, (absoluteCents - 6900) / 1200);
33
39
  }
@@ -39,7 +45,7 @@ for (let i = 0; i < absoluteCentLookupTable.length; i++) {
39
45
  */
40
46
  export function absCentsToHz(cents)
41
47
  {
42
- if(cents < MIN_ABS_CENT || cents > MAX_ABS_CENT)
48
+ if (cents < MIN_ABS_CENT || cents > MAX_ABS_CENT)
43
49
  {
44
50
  return 440 * Math.pow(2, (cents - 6900) / 1200);
45
51
  }
@@ -50,7 +56,8 @@ export function absCentsToHz(cents)
50
56
  const MIN_DECIBELS = -1660;
51
57
  const MAX_DECIBELS = 1600;
52
58
  const decibelLookUpTable = new Float32Array((MAX_DECIBELS - MIN_DECIBELS) * 100 + 1);
53
- for (let i = 0; i < decibelLookUpTable.length; i++) {
59
+ for (let i = 0; i < decibelLookUpTable.length; i++)
60
+ {
54
61
  const decibels = (MIN_DECIBELS * 100 + i) / 100;
55
62
  decibelLookUpTable[i] = Math.pow(10, -decibels / 20);
56
63
  }