spessasynth_core 1.1.2 → 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.
Files changed (189) hide show
  1. package/LICENSE +3 -26
  2. package/README.md +156 -474
  3. package/index.js +73 -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 -303
  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 -222
  177. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +0 -95
  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 -194
  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 -173
  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 -313
  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,299 @@
1
+ import { readLittleEndian } from "../../utils/byte_functions/little_endian.js";
2
+ import { DLSDestinations } from "./dls_destinations.js";
3
+ import { DLS_1_NO_VIBRATO_MOD, DLS_1_NO_VIBRATO_PRESSURE, DLSSources } from "./dls_sources.js";
4
+ import { getSF2ModulatorFromArticulator } from "./articulator_converter.js";
5
+ import { SpessaSynthInfo, SpessaSynthWarn } from "../../utils/loggin.js";
6
+ import { consoleColors } from "../../utils/other.js";
7
+ import { Generator, generatorTypes } from "../basic_soundfont/generator.js";
8
+ import { Modulator } from "../basic_soundfont/modulator.js";
9
+
10
+
11
+ /**
12
+ * Reads the articulator chunk
13
+ * @param chunk {RiffChunk}
14
+ * @param disableVibrato {boolean} it seems that dls 1 does not have vibrato lfo, so we shall disable it
15
+ * @returns {{modulators: Modulator[], generators: Generator[]}}
16
+ */
17
+ export function readArticulation(chunk, disableVibrato)
18
+ {
19
+ const artData = chunk.chunkData;
20
+ /**
21
+ * @type {Generator[]}
22
+ */
23
+ const generators = [];
24
+ /**
25
+ * @type {Modulator[]}
26
+ */
27
+ const modulators = [];
28
+
29
+ // cbSize (ignore)
30
+ readLittleEndian(artData, 4);
31
+ const connectionsAmount = readLittleEndian(artData, 4);
32
+ for (let i = 0; i < connectionsAmount; i++)
33
+ {
34
+ // read the block
35
+ const source = readLittleEndian(artData, 2);
36
+ const control = readLittleEndian(artData, 2);
37
+ const destination = readLittleEndian(artData, 2);
38
+ const transform = readLittleEndian(artData, 2);
39
+ const scale = readLittleEndian(artData, 4) | 0;
40
+ const value = scale >> 16; // convert it to 16 bit as soundfont uses that
41
+
42
+ // modulatorConverterDebug(
43
+ // source,
44
+ // control,
45
+ // destination,
46
+ // value,
47
+ // transform
48
+ // );
49
+
50
+ // interpret this somehow...
51
+ // if source and control are both zero, it's a generator
52
+ if (source === 0 && control === 0 && transform === 0)
53
+ {
54
+ /**
55
+ * @type {Generator}
56
+ */
57
+ let generator;
58
+ switch (destination)
59
+ {
60
+ case DLSDestinations.pan:
61
+ generator = new Generator(generatorTypes.pan, value); // turn percent into tenths of percent
62
+ break;
63
+ case DLSDestinations.gain:
64
+ generator = new Generator(generatorTypes.initialAttenuation, -value * 10 / 0.4); // turn to centibels and apply emu correction
65
+ break;
66
+ case DLSDestinations.filterCutoff:
67
+ generator = new Generator(generatorTypes.initialFilterFc, value);
68
+ break;
69
+ case DLSDestinations.filterQ:
70
+ generator = new Generator(generatorTypes.initialFilterQ, value);
71
+ break;
72
+
73
+ // mod lfo raw values it seems
74
+ case DLSDestinations.modLfoFreq:
75
+ generator = new Generator(generatorTypes.freqModLFO, value);
76
+ break;
77
+ case DLSDestinations.modLfoDelay:
78
+ generator = new Generator(generatorTypes.delayModLFO, value);
79
+ break;
80
+ case DLSDestinations.vibLfoFreq:
81
+ generator = new Generator(generatorTypes.freqVibLFO, value);
82
+ break;
83
+ case DLSDestinations.vibLfoDelay:
84
+ generator = new Generator(generatorTypes.delayVibLFO, value);
85
+ break;
86
+
87
+ // vol. env: all times are timecents like sf2
88
+ case DLSDestinations.volEnvDelay:
89
+ generator = new Generator(generatorTypes.delayVolEnv, value);
90
+ break;
91
+ case DLSDestinations.volEnvAttack:
92
+ generator = new Generator(generatorTypes.attackVolEnv, value);
93
+ break;
94
+ case DLSDestinations.volEnvHold:
95
+ // do not validate because keyNumToSomething
96
+ generator = new Generator(generatorTypes.holdVolEnv, value, false);
97
+ break;
98
+ case DLSDestinations.volEnvDecay:
99
+ // do not validate because keyNumToSomething
100
+ generator = new Generator(generatorTypes.decayVolEnv, value, false);
101
+ break;
102
+ case DLSDestinations.volEnvRelease:
103
+ generator = new Generator(generatorTypes.releaseVolEnv, value);
104
+ break;
105
+ case DLSDestinations.volEnvSustain:
106
+ // gain seems to be (1000 - value) / 10 = sustain dB
107
+ const sustainCb = 1000 - value;
108
+ generator = new Generator(generatorTypes.sustainVolEnv, sustainCb);
109
+ break;
110
+
111
+ // mod env
112
+ case DLSDestinations.modEnvDelay:
113
+ generator = new Generator(generatorTypes.delayModEnv, value);
114
+ break;
115
+ case DLSDestinations.modEnvAttack:
116
+ generator = new Generator(generatorTypes.attackModEnv, value);
117
+ break;
118
+ case DLSDestinations.modEnvHold:
119
+ // do not validate because keyNumToSomething
120
+ generator = new Generator(generatorTypes.holdModEnv, value, false);
121
+ break;
122
+ case DLSDestinations.modEnvDecay:
123
+ // do not validate because keyNumToSomething
124
+ generator = new Generator(generatorTypes.decayModEnv, value, false);
125
+ break;
126
+ case DLSDestinations.modEnvRelease:
127
+ generator = new Generator(generatorTypes.releaseModEnv, value);
128
+ break;
129
+ case DLSDestinations.modEnvSustain:
130
+ // dls uses 1%, desfont uses 0.1%
131
+ const percentageSustain = 1000 - value;
132
+ generator = new Generator(generatorTypes.sustainModEnv, percentageSustain);
133
+ break;
134
+
135
+ case DLSDestinations.reverbSend:
136
+ generator = new Generator(generatorTypes.reverbEffectsSend, value);
137
+ break;
138
+ case DLSDestinations.chorusSend:
139
+ generator = new Generator(generatorTypes.chorusEffectsSend, value);
140
+ break;
141
+ case DLSDestinations.pitch:
142
+ // split it up
143
+ const semi = Math.floor(value / 100);
144
+ const cents = Math.floor(value - semi * 100);
145
+ generator = new Generator(generatorTypes.fineTune, cents);
146
+ generators.push(new Generator(generatorTypes.coarseTune, semi));
147
+ break;
148
+ }
149
+ if (generator)
150
+ {
151
+ generators.push(generator);
152
+ }
153
+ }
154
+ else
155
+ // if not, modulator?
156
+ {
157
+ let isGenerator = true;
158
+
159
+ const applyKeyToCorrection = (value, keyToGen, realGen) =>
160
+ {
161
+ // according to viena and another strange (with modulators) rendition of gm.dls in sf2,
162
+ // it shall be divided by -128
163
+ // and a strange correction needs to be applied to the real value:
164
+ // real + (60 / 128) * scale
165
+ const keyToGenValue = value / -128;
166
+ generators.push(new Generator(keyToGen, keyToGenValue));
167
+ // airfont 340 fix
168
+ if (keyToGenValue <= 120)
169
+ {
170
+ const correction = Math.round((60 / 128) * value);
171
+ generators.forEach(g =>
172
+ {
173
+ if (g.generatorType === realGen)
174
+ {
175
+ g.generatorValue += correction;
176
+ }
177
+ });
178
+ }
179
+ };
180
+
181
+ // a few special cases which are generators:
182
+ if (control === DLSSources.none)
183
+ {
184
+ // mod lfo to pitch
185
+ if (source === DLSSources.modLfo && destination === DLSDestinations.pitch)
186
+ {
187
+ generators.push(new Generator(generatorTypes.modLfoToPitch, value));
188
+ }
189
+ else
190
+ // mod lfo to volume
191
+ if (source === DLSSources.modLfo && destination === DLSDestinations.gain)
192
+ {
193
+ generators.push(new Generator(generatorTypes.modLfoToVolume, value));
194
+ }
195
+ else
196
+ // mod lfo to filter
197
+ if (source === DLSSources.modLfo && destination === DLSDestinations.filterCutoff)
198
+ {
199
+ generators.push(new Generator(generatorTypes.modLfoToFilterFc, value));
200
+ }
201
+ else
202
+ // vib lfo to pitch
203
+ if (source === DLSSources.vibratoLfo && destination === DLSDestinations.pitch)
204
+ {
205
+ generators.push(new Generator(generatorTypes.vibLfoToPitch, value));
206
+ }
207
+ else
208
+ // mod env to pitch
209
+ if (source === DLSSources.modEnv && destination === DLSDestinations.pitch)
210
+ {
211
+ generators.push(new Generator(generatorTypes.modEnvToPitch, value));
212
+ }
213
+ else
214
+ // mod env to filter
215
+ if (source === DLSSources.modEnv && destination === DLSDestinations.filterCutoff)
216
+ {
217
+ generators.push(new Generator(generatorTypes.modEnvToFilterFc, value));
218
+ }
219
+ else
220
+ // scale tuning (key number to pitch)
221
+ if (source === DLSSources.keyNum && destination === DLSDestinations.pitch)
222
+ {
223
+ // this is just a soundfont generator, but the amount must be changed
224
+ // 12,800 means the regular scale (100)
225
+ generators.push(new Generator(generatorTypes.scaleTuning, value / 128));
226
+ }
227
+ else
228
+ // key to vol env hold
229
+ if (source === DLSSources.keyNum && destination === DLSDestinations.volEnvHold)
230
+ {
231
+ applyKeyToCorrection(value, generatorTypes.keyNumToVolEnvHold, generatorTypes.holdVolEnv);
232
+ }
233
+ else
234
+ // key to vol env decay
235
+ if (source === DLSSources.keyNum && destination === DLSDestinations.volEnvDecay)
236
+ {
237
+ applyKeyToCorrection(value, generatorTypes.keyNumToVolEnvDecay, generatorTypes.decayVolEnv);
238
+ }
239
+ else
240
+ // key to mod env hold
241
+ if (source === DLSSources.keyNum && destination === DLSDestinations.modEnvHold)
242
+ {
243
+ applyKeyToCorrection(value, generatorTypes.keyNumToModEnvHold, generatorTypes.holdModEnv);
244
+ }
245
+ else
246
+ // key to mod env decay
247
+ if (source === DLSSources.keyNum && destination === DLSDestinations.modEnvDecay)
248
+ {
249
+ applyKeyToCorrection(value, generatorTypes.keyNumToModEnvDecay, generatorTypes.decayModEnv);
250
+ }
251
+ else
252
+ {
253
+ isGenerator = false;
254
+ }
255
+
256
+ }
257
+ else
258
+ {
259
+ isGenerator = false;
260
+ }
261
+ if (isGenerator === false)
262
+ {
263
+ // UNCOMMENT TO ENABLE DEBUG
264
+ // modulatorConverterDebug(source, control, destination, value, transform)
265
+ // convert it to modulator
266
+ const mod = getSF2ModulatorFromArticulator(
267
+ source,
268
+ control,
269
+ destination,
270
+ transform,
271
+ value
272
+ );
273
+ if (mod)
274
+ {
275
+ // some articulators cannot be turned into modulators, that's why this check is a thing
276
+ modulators.push(mod);
277
+ SpessaSynthInfo("%cSucceeded converting to SF2 Modulator!", consoleColors.recognized);
278
+ }
279
+ else
280
+ {
281
+ SpessaSynthWarn("Failed converting to SF2 Modulator!");
282
+ }
283
+ }
284
+ }
285
+ }
286
+
287
+ // it seems that dls 1 does not have vibrato lfo, so we shall disable it
288
+ if (disableVibrato)
289
+ {
290
+ modulators.push(
291
+ // mod to vib
292
+ Modulator.copy(DLS_1_NO_VIBRATO_MOD),
293
+ // press to vib
294
+ Modulator.copy(DLS_1_NO_VIBRATO_PRESSURE)
295
+ );
296
+ }
297
+
298
+ return { modulators: modulators, generators: generators };
299
+ }
@@ -0,0 +1,121 @@
1
+ import { readBytesAsString } from "../../utils/byte_functions/string.js";
2
+ import { readLittleEndian } from "../../utils/byte_functions/little_endian.js";
3
+ import { DLSPreset } from "./dls_preset.js";
4
+ import { findRIFFListType, readRIFFChunk } from "../basic_soundfont/riff_chunk.js";
5
+ import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd } from "../../utils/loggin.js";
6
+ import { BasicInstrumentZone } from "../basic_soundfont/basic_zones.js";
7
+ import { consoleColors } from "../../utils/other.js";
8
+ import { generatorLimits, generatorTypes } from "../basic_soundfont/generator.js";
9
+ import { Modulator } from "../basic_soundfont/modulator.js";
10
+ import { DEFAULT_DLS_CHORUS, DEFAULT_DLS_REVERB } from "./dls_sources.js";
11
+
12
+ /**
13
+ * @this {DLSSoundFont}
14
+ * @param chunk {RiffChunk}
15
+ */
16
+ export function readDLSInstrument(chunk)
17
+ {
18
+ this.verifyHeader(chunk, "LIST");
19
+ this.verifyText(readBytesAsString(chunk.chunkData, 4), "ins ");
20
+ /**
21
+ * @type {RiffChunk[]}
22
+ */
23
+ const chunks = [];
24
+ while (chunk.chunkData.length > chunk.chunkData.currentIndex)
25
+ {
26
+ chunks.push(readRIFFChunk(chunk.chunkData));
27
+ }
28
+
29
+
30
+ const instrumentHeader = chunks.find(c => c.header === "insh");
31
+ if (!instrumentHeader)
32
+ {
33
+ SpessaSynthGroupEnd();
34
+ throw new Error("No instrument header!");
35
+ }
36
+
37
+ // read instrument header
38
+ const regions = readLittleEndian(instrumentHeader.chunkData, 4);
39
+ const ulBank = readLittleEndian(instrumentHeader.chunkData, 4);
40
+ const ulInstrument = readLittleEndian(instrumentHeader.chunkData, 4);
41
+ const preset = new DLSPreset(this, ulBank, ulInstrument);
42
+
43
+ // read preset name in INFO
44
+ let presetName = "unnamedPreset";
45
+ const infoChunk = findRIFFListType(chunks, "INFO");
46
+ if (infoChunk)
47
+ {
48
+ let info = readRIFFChunk(infoChunk.chunkData);
49
+ while (info.header !== "INAM")
50
+ {
51
+ info = readRIFFChunk(infoChunk.chunkData);
52
+ }
53
+ presetName = readBytesAsString(info.chunkData, info.chunkData.length).trim();
54
+ }
55
+ preset.presetName = presetName;
56
+ preset.DLSInstrument.instrumentName = presetName;
57
+ SpessaSynthGroupCollapsed(
58
+ `%cParsing %c"${presetName}"%c...`,
59
+ consoleColors.info,
60
+ consoleColors.recognized,
61
+ consoleColors.info
62
+ );
63
+
64
+ // list of regions
65
+ const regionListChunk = findRIFFListType(chunks, "lrgn");
66
+ if (!regionListChunk)
67
+ {
68
+ SpessaSynthGroupEnd();
69
+ throw new Error("No region list!");
70
+ }
71
+
72
+ // global articulation: essentially global zone
73
+ const globalZone = new BasicInstrumentZone();
74
+ globalZone.isGlobal = true;
75
+
76
+ // read articulators
77
+ const globalLart = findRIFFListType(chunks, "lart");
78
+ const globalLar2 = findRIFFListType(chunks, "lar2");
79
+ if (globalLar2 !== undefined || globalLart !== undefined)
80
+ {
81
+ this.readLart(globalLart, globalLar2, globalZone);
82
+ }
83
+ // remove generators with default values
84
+ globalZone.generators = globalZone.generators.filter(g => g.generatorValue !== generatorLimits[g.generatorType].def);
85
+ // override reverb and chorus with 1000 instead of 200 (if not override)
86
+ // reverb
87
+ if (globalZone.modulators.find(m => m.modulatorDestination === generatorTypes.reverbEffectsSend) === undefined)
88
+ {
89
+ globalZone.modulators.push(Modulator.copy(DEFAULT_DLS_REVERB));
90
+ }
91
+ // chorus
92
+ if (globalZone.modulators.find(m => m.modulatorDestination === generatorTypes.chorusEffectsSend) === undefined)
93
+ {
94
+ globalZone.modulators.push(Modulator.copy(DEFAULT_DLS_CHORUS));
95
+ }
96
+ preset.DLSInstrument.instrumentZones.push(globalZone);
97
+
98
+ // read regions
99
+ for (let i = 0; i < regions; i++)
100
+ {
101
+ const chunk = readRIFFChunk(regionListChunk.chunkData);
102
+ this.verifyHeader(chunk, "LIST");
103
+ const type = readBytesAsString(chunk.chunkData, 4);
104
+ if (type !== "rgn " && type !== "rgn2")
105
+ {
106
+ SpessaSynthGroupEnd();
107
+ this.parsingError(`Invalid DLS region! Expected "rgn " or "rgn2" got "${type}"`);
108
+ }
109
+
110
+
111
+ const zone = this.readRegion(chunk);
112
+ if (zone)
113
+ {
114
+ preset.DLSInstrument.instrumentZones.push(zone);
115
+ }
116
+ }
117
+
118
+ this.presets.push(preset);
119
+ this.instruments.push(preset.DLSInstrument);
120
+ SpessaSynthGroupEnd();
121
+ }
@@ -0,0 +1,17 @@
1
+ import { readRIFFChunk } from "../basic_soundfont/riff_chunk.js";
2
+ import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd } from "../../utils/loggin.js";
3
+ import { consoleColors } from "../../utils/other.js";
4
+
5
+ /**
6
+ * @this {DLSSoundFont}
7
+ * @param instrumentListChunk {RiffChunk}
8
+ */
9
+ export function readDLSInstrumentList(instrumentListChunk)
10
+ {
11
+ SpessaSynthGroupCollapsed("%cLoading instruments...", consoleColors.info);
12
+ for (let i = 0; i < this.instrumentAmount; i++)
13
+ {
14
+ this.readDLSInstrument(readRIFFChunk(instrumentListChunk.chunkData));
15
+ }
16
+ SpessaSynthGroupEnd();
17
+ }
@@ -0,0 +1,35 @@
1
+ import { readRIFFChunk } from "../basic_soundfont/riff_chunk.js";
2
+ import { readArticulation } from "./read_articulation.js";
3
+
4
+ /**
5
+ * @param lartChunk {RiffChunk|undefined}
6
+ * @param lar2Chunk {RiffChunk|undefined}
7
+ * @param zone {BasicInstrumentZone}
8
+ * @this {DLSSoundFont}
9
+ */
10
+ export function readLart(lartChunk, lar2Chunk, zone)
11
+ {
12
+ if (lartChunk)
13
+ {
14
+ while (lartChunk.chunkData.currentIndex < lartChunk.chunkData.length)
15
+ {
16
+ const art1 = readRIFFChunk(lartChunk.chunkData);
17
+ this.verifyHeader(art1, "art1", "art2");
18
+ const modsAndGens = readArticulation(art1, true);
19
+ zone.generators.push(...modsAndGens.generators);
20
+ zone.modulators.push(...modsAndGens.modulators);
21
+ }
22
+ }
23
+
24
+ if (lar2Chunk)
25
+ {
26
+ while (lar2Chunk.chunkData.currentIndex < lar2Chunk.chunkData.length)
27
+ {
28
+ const art2 = readRIFFChunk(lar2Chunk.chunkData);
29
+ this.verifyHeader(art2, "art2", "art1");
30
+ const modsAndGens = readArticulation(art2, false);
31
+ zone.generators.push(...modsAndGens.generators);
32
+ zone.modulators.push(...modsAndGens.modulators);
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,152 @@
1
+ import { readLittleEndian, signedInt16 } from "../../utils/byte_functions/little_endian.js";
2
+ import { findRIFFListType, readRIFFChunk } from "../basic_soundfont/riff_chunk.js";
3
+ import { DLSZone } from "./dls_zone.js";
4
+ import { Generator, generatorTypes } from "../basic_soundfont/generator.js";
5
+
6
+ /**
7
+ * @this {DLSSoundFont}
8
+ * @param chunk {RiffChunk}
9
+ * @returns {DLSZone}
10
+ */
11
+ export function readRegion(chunk)
12
+ {
13
+ // regions are essentially instrument zones
14
+
15
+ /**
16
+ * read chunks in the region
17
+ * @type {RiffChunk[]}
18
+ */
19
+ const regionChunks = [];
20
+ while (chunk.chunkData.length > chunk.chunkData.currentIndex)
21
+ {
22
+ regionChunks.push(readRIFFChunk(chunk.chunkData));
23
+ }
24
+
25
+ // region header
26
+ const regionHeader = regionChunks.find(c => c.header === "rgnh");
27
+ // key range
28
+ let keyMin = readLittleEndian(regionHeader.chunkData, 2);
29
+ let keyMax = readLittleEndian(regionHeader.chunkData, 2);
30
+ // vel range
31
+ let velMin = readLittleEndian(regionHeader.chunkData, 2);
32
+ let velMax = readLittleEndian(regionHeader.chunkData, 2);
33
+
34
+ // a fix for not cool files
35
+ if (velMin === 0 && velMax === 0)
36
+ {
37
+ velMax = 127;
38
+ velMin = 0;
39
+ }
40
+ // cannot do the same to key zones sadly
41
+
42
+ const zone = new DLSZone(
43
+ { min: keyMin, max: keyMax },
44
+ { min: velMin, max: velMax }
45
+ );
46
+
47
+ // fusOptions: no idea about that one???
48
+ readLittleEndian(regionHeader.chunkData, 2);
49
+
50
+ // keyGroup: essentially exclusive class
51
+ const exclusive = readLittleEndian(regionHeader.chunkData, 2);
52
+ if (exclusive !== 0)
53
+ {
54
+ zone.generators.push(new Generator(generatorTypes.exclusiveClass, exclusive));
55
+ }
56
+
57
+ // lart
58
+ const lart = findRIFFListType(regionChunks, "lart");
59
+ const lar2 = findRIFFListType(regionChunks, "lar2");
60
+ this.readLart(lart, lar2, zone);
61
+
62
+ // wsmp: wave sample chunk
63
+ zone.isGlobal = false;
64
+ const waveSampleChunk = regionChunks.find(c => c.header === "wsmp");
65
+ // cbSize
66
+ readLittleEndian(waveSampleChunk.chunkData, 4);
67
+ let originalKey = readLittleEndian(waveSampleChunk.chunkData, 2);
68
+
69
+ // sFineTune
70
+ let pitchCorrection = signedInt16(
71
+ waveSampleChunk.chunkData[waveSampleChunk.chunkData.currentIndex++],
72
+ waveSampleChunk.chunkData[waveSampleChunk.chunkData.currentIndex++]
73
+ );
74
+
75
+ // gain correction: Each unit of gain represents 1/655360 dB
76
+ // it is set after linking the sample
77
+ const gainCorrection = readLittleEndian(waveSampleChunk.chunkData, 4);
78
+ // convert to signed and turn into attenuation (invert)
79
+ const dbCorrection = (gainCorrection | 0) / -655360;
80
+
81
+ // skip options
82
+ readLittleEndian(waveSampleChunk.chunkData, 4);
83
+
84
+ // read loop count (always one or zero)
85
+ const loopsAmount = readLittleEndian(waveSampleChunk.chunkData, 4);
86
+ let loopingMode;
87
+ const loop = { start: 0, end: 0 };
88
+ if (loopsAmount === 0)
89
+ {
90
+ // no loop
91
+ loopingMode = 0;
92
+ }
93
+ else
94
+ {
95
+ // ignore cbSize
96
+ readLittleEndian(waveSampleChunk.chunkData, 4);
97
+ // loop type: loop normally or loop until release (like soundfont)
98
+ const loopType = readLittleEndian(waveSampleChunk.chunkData, 4); // why is it long?
99
+ if (loopType === 0)
100
+ {
101
+ loopingMode = 1;
102
+ }
103
+ else
104
+ {
105
+ loopingMode = 3;
106
+ }
107
+ loop.start = readLittleEndian(waveSampleChunk.chunkData, 4);
108
+ const loopLength = readLittleEndian(waveSampleChunk.chunkData, 4);
109
+ loop.end = loop.start + loopLength;
110
+ }
111
+
112
+ // wave link
113
+ const waveLinkChunk = regionChunks.find(c => c.header === "wlnk");
114
+ if (waveLinkChunk === undefined)
115
+ {
116
+ // No wave link means no sample. What? Why is it even here then?
117
+ return undefined;
118
+ }
119
+
120
+ // flags
121
+ readLittleEndian(waveLinkChunk.chunkData, 2);
122
+ // phase group
123
+ readLittleEndian(waveLinkChunk.chunkData, 2);
124
+ // channel
125
+ readLittleEndian(waveLinkChunk.chunkData, 4);
126
+ // sampleID
127
+ const sampleID = readLittleEndian(waveLinkChunk.chunkData, 4);
128
+ // noinspection JSValidateTypes
129
+ /**
130
+ * @type {DLSSample}
131
+ */
132
+ const sample = this.samples[sampleID];
133
+ if (sample === undefined)
134
+ {
135
+ throw new Error("Invalid sample ID!");
136
+ }
137
+
138
+ // this correction overrides the sample gain correction
139
+ const actualDbCorrection = dbCorrection || sample.sampleDbAttenuation;
140
+ // convert to centibels
141
+ const attenuation = (actualDbCorrection * 10) / 0.4; // make sure to apply EMU correction
142
+
143
+ zone.setWavesample(
144
+ attenuation, loopingMode,
145
+ loop,
146
+ originalKey,
147
+ sample,
148
+ sampleID,
149
+ pitchCorrection
150
+ );
151
+ return zone;
152
+ }