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,132 @@
1
+ import { midiControllers } from "../../../../midi/midi_message.js";
2
+ import { computeModulators } from "../../engine_components/compute_modulator.js";
3
+ import { channelConfiguration, dataEntryStates } from "../../engine_components/controller_tables.js";
4
+
5
+ /**
6
+ * @param controllerNumber {number}
7
+ * @param controllerValue {number}
8
+ * @param force {boolean}
9
+ * @this {MidiAudioChannel}
10
+ */
11
+ export function controllerChange(controllerNumber, controllerValue, force = false)
12
+ {
13
+ if (controllerNumber > 127)
14
+ {
15
+ // channel configuration. force must be set to true
16
+ if (!force)
17
+ {
18
+ return;
19
+ }
20
+ switch (controllerNumber)
21
+ {
22
+ default:
23
+ return;
24
+
25
+ case channelConfiguration.velocityOverride:
26
+ this.velocityOverride = controllerValue;
27
+ }
28
+ }
29
+
30
+ // lsb controller values: append them as the lower nibble of the 14-bit value
31
+ // excluding bank select and data entry as it's handled separately
32
+ if (
33
+ controllerNumber >= midiControllers.lsbForControl1ModulationWheel
34
+ && controllerNumber <= midiControllers.lsbForControl13EffectControl2
35
+ && controllerNumber !== midiControllers.lsbForControl6DataEntry
36
+ )
37
+ {
38
+ const actualCCNum = controllerNumber - 32;
39
+ if (this.lockedControllers[actualCCNum])
40
+ {
41
+ return;
42
+ }
43
+ // append the lower nibble to the main controller
44
+ this.midiControllers[actualCCNum] = (this.midiControllers[actualCCNum] & 0x3F80) | (controllerValue & 0x7F);
45
+ this.voices.forEach(v => computeModulators(v, this.midiControllers, 1, actualCCNum));
46
+ }
47
+ if (this.lockedControllers[controllerNumber])
48
+ {
49
+ return;
50
+ }
51
+
52
+ // apply the cc to the table
53
+ this.midiControllers[controllerNumber] = controllerValue << 7;
54
+
55
+ // interpret special CCs
56
+ {
57
+ switch (controllerNumber)
58
+ {
59
+ case midiControllers.allNotesOff:
60
+ this.stopAllNotes();
61
+ break;
62
+
63
+ case midiControllers.allSoundOff:
64
+ this.stopAllNotes(true);
65
+ break;
66
+
67
+ // special case: bank select
68
+ case midiControllers.bankSelect:
69
+ this.setBankSelect(controllerValue);
70
+ break;
71
+
72
+ case midiControllers.lsbForControl0BankSelect:
73
+ this.setBankSelect(controllerValue, true);
74
+ break;
75
+
76
+ // check for RPN and NPRN and data entry
77
+ case midiControllers.RPNLsb:
78
+ this.dataEntryState = dataEntryStates.RPFine;
79
+ break;
80
+
81
+ case midiControllers.RPNMsb:
82
+ this.dataEntryState = dataEntryStates.RPCoarse;
83
+ break;
84
+
85
+ case midiControllers.NRPNMsb:
86
+ this.dataEntryState = dataEntryStates.NRPCoarse;
87
+ break;
88
+
89
+ case midiControllers.NRPNLsb:
90
+ this.dataEntryState = dataEntryStates.NRPFine;
91
+ break;
92
+
93
+ case midiControllers.dataEntryMsb:
94
+ this.dataEntryCoarse(controllerValue);
95
+ break;
96
+
97
+ case midiControllers.lsbForControl6DataEntry:
98
+ this.dataEntryFine(controllerValue);
99
+ break;
100
+
101
+ case midiControllers.resetAllControllers:
102
+ this.resetControllersRP15Compliant();
103
+ break;
104
+
105
+ case midiControllers.sustainPedal:
106
+ if (controllerValue >= 64)
107
+ {
108
+ this.holdPedal = true;
109
+ }
110
+ else
111
+ {
112
+ this.holdPedal = false;
113
+ this.sustainedVoices.forEach(v =>
114
+ {
115
+ v.release(this.synth.currentSynthTime);
116
+ });
117
+ this.sustainedVoices = [];
118
+ }
119
+ break;
120
+
121
+ // default: just compute modulators
122
+ default:
123
+ this.voices.forEach(v => computeModulators(v, this.midiControllers, 1, controllerNumber));
124
+ break;
125
+ }
126
+ }
127
+ this.synth.callEvent("controllerchange", {
128
+ channel: this.channelNumber,
129
+ controllerNumber: controllerNumber,
130
+ controllerValue: controllerValue
131
+ });
132
+ }
@@ -0,0 +1,48 @@
1
+ import { SYNTHESIZER_GAIN } from "../../main_processor.js";
2
+
3
+ /**
4
+ * @enum {number}
5
+ */
6
+ export const masterParameterType = {
7
+ mainVolume: 0,
8
+ masterPan: 1,
9
+ voicesCap: 2,
10
+ interpolationType: 3,
11
+ midiSystem: 4
12
+ };
13
+
14
+ /**
15
+ * @this {SpessaSynthProcessor}
16
+ * @param type {masterParameterType}
17
+ * @param value {number|string|interpolationTypes}
18
+ */
19
+ export function setMasterParameter(type, value)
20
+ {
21
+ switch (type)
22
+ {
23
+ case masterParameterType.masterPan:
24
+ let pan = value;
25
+ this.pan = pan;
26
+ // clamp to 0-1 (0 is left)
27
+ pan = (pan / 2) + 0.5;
28
+ this.panLeft = (1 - pan);
29
+ this.panRight = (pan);
30
+ break;
31
+
32
+ case masterParameterType.mainVolume:
33
+ this.masterGain = value * SYNTHESIZER_GAIN;
34
+ this.setMasterParameter(masterParameterType.masterPan, this.pan);
35
+ break;
36
+
37
+ case masterParameterType.voicesCap:
38
+ this.voiceCap = value;
39
+ break;
40
+
41
+ case masterParameterType.interpolationType:
42
+ this.interpolationType = value;
43
+ break;
44
+
45
+ case masterParameterType.midiSystem:
46
+ this.setSystem(value);
47
+ }
48
+ }
@@ -0,0 +1,241 @@
1
+ import { consoleColors } from "../../../../utils/other.js";
2
+ import { SpessaSynthInfo } from "../../../../utils/loggin.js";
3
+ import { modulatorSources } from "../../../../soundfont/basic_soundfont/modulator.js";
4
+ import {
5
+ customControllers,
6
+ customResetArray,
7
+ dataEntryStates,
8
+ NON_CC_INDEX_OFFSET,
9
+ PORTAMENTO_CONTROL_UNSET,
10
+ resetArray
11
+ } from "../../engine_components/controller_tables.js";
12
+ import { midiControllers } from "../../../../midi/midi_message.js";
13
+ import { DEFAULT_PERCUSSION, DEFAULT_SYNTH_MODE } from "../../../synth_constants.js";
14
+ import { getDefaultBank } from "../../../../utils/xg_hacks.js";
15
+
16
+
17
+ /**
18
+ * Full system reset
19
+ * @this {SpessaSynthProcessor}
20
+ * @param log {boolean}
21
+ */
22
+ export function resetAllControllers(log = true)
23
+ {
24
+ if (log)
25
+ {
26
+ SpessaSynthInfo("%cResetting all controllers!", consoleColors.info);
27
+ }
28
+ this.callEvent("allcontrollerreset", undefined);
29
+ this.setSystem(DEFAULT_SYNTH_MODE);
30
+ for (let channelNumber = 0; channelNumber < this.midiAudioChannels.length; channelNumber++)
31
+ {
32
+ this.midiAudioChannels[channelNumber].resetControllers();
33
+
34
+ /**
35
+ * @type {MidiAudioChannel}
36
+ **/
37
+ const ch = this.midiAudioChannels[channelNumber];
38
+
39
+ // if preset is unlocked, switch to non-drums and call event
40
+ if (!ch.lockPreset)
41
+ {
42
+ ch.setBankSelect(getDefaultBank(this.system));
43
+ if (channelNumber % 16 === DEFAULT_PERCUSSION)
44
+ {
45
+ ch.setPreset(this.drumPreset);
46
+ ch.presetUsesOverride = this.defaultDrumsUsesOverride;
47
+ ch.drumChannel = true;
48
+ this.callEvent("drumchange", {
49
+ channel: channelNumber,
50
+ isDrumChannel: true
51
+ });
52
+ }
53
+ else
54
+ {
55
+ ch.drumChannel = false;
56
+ ch.presetUsesOverride = this.defaultDrumsUsesOverride;
57
+ ch.setPreset(this.defaultPreset);
58
+ this.callEvent("drumchange", {
59
+ channel: channelNumber,
60
+ isDrumChannel: false
61
+ });
62
+ }
63
+ }
64
+ else
65
+ {
66
+ this.callEvent("drumchange", {
67
+ channel: channelNumber,
68
+ isDrumChannel: ch.drumChannel
69
+ });
70
+ }
71
+
72
+ const presetBank = ch.preset.bank;
73
+ const sentBank = presetBank === 128 ? 128 : (ch.presetUsesOverride ? presetBank + this.soundfontBankOffset : presetBank);
74
+
75
+ // call program change
76
+ this.callEvent("programchange", {
77
+ channel: channelNumber,
78
+ program: ch.preset.program,
79
+ bank: sentBank
80
+ });
81
+
82
+ for (let ccNum = 0; ccNum < 128; ccNum++)
83
+ {
84
+ if (this.midiAudioChannels[channelNumber].lockedControllers[ccNum])
85
+ {
86
+ // was not reset so restore the value
87
+ this.callEvent("controllerchange", {
88
+ channel: channelNumber,
89
+ controllerNumber: ccNum,
90
+ controllerValue: this.midiAudioChannels[channelNumber].midiControllers[ccNum] >> 7
91
+ });
92
+ }
93
+
94
+ }
95
+
96
+
97
+ // restore pitch wheel
98
+ if (this.midiAudioChannels[channelNumber].lockedControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel] === false)
99
+ {
100
+ const val = this.midiAudioChannels[channelNumber].midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel];
101
+ const msb = val >> 7;
102
+ const lsb = val & 0x7F;
103
+ this.callEvent("pitchwheel", {
104
+ channel: channelNumber,
105
+ MSB: msb,
106
+ LSB: lsb
107
+ });
108
+ }
109
+ }
110
+ this.tunings = [];
111
+ this.tunings = [];
112
+ for (let i = 0; i < 128; i++)
113
+ {
114
+ this.tunings.push([]);
115
+ }
116
+
117
+ this.setMIDIVolume(1);
118
+ }
119
+
120
+ /**
121
+ * Resets all controllers for channel
122
+ * @this {MidiAudioChannel}
123
+ */
124
+ export function resetControllers()
125
+ {
126
+ this.channelOctaveTuning.fill(0);
127
+
128
+ // reset the array
129
+ for (let i = 0; i < resetArray.length; i++)
130
+ {
131
+ if (this.lockedControllers[i])
132
+ {
133
+ continue;
134
+ }
135
+ const resetValue = resetArray[i];
136
+ if (this.midiControllers[i] !== resetValue && i < 127)
137
+ {
138
+ if (i === midiControllers.portamentoControl)
139
+ {
140
+ this.midiControllers[i] = PORTAMENTO_CONTROL_UNSET;
141
+ }
142
+ else
143
+ {
144
+ this.controllerChange(i, resetValue >> 7);
145
+ }
146
+ }
147
+ else
148
+ {
149
+ // out of range, do a regular reset
150
+ this.midiControllers[i] = resetValue;
151
+ }
152
+ }
153
+ this.channelVibrato = { rate: 0, depth: 0, delay: 0 };
154
+ this.holdPedal = false;
155
+ this.randomPan = false;
156
+
157
+ // reset custom controllers
158
+ // special case: transpose does not get affected
159
+ const transpose = this.customControllers[customControllers.channelTransposeFine];
160
+ this.customControllers.set(customResetArray);
161
+ this.setCustomController(customControllers.channelTransposeFine, transpose);
162
+
163
+ this.resetParameters();
164
+
165
+ }
166
+
167
+
168
+ /**
169
+ * @type {Set<midiControllers|number>}
170
+ */
171
+ export const nonResetableCCs = new Set([
172
+ midiControllers.bankSelect,
173
+ midiControllers.lsbForControl0BankSelect,
174
+ midiControllers.mainVolume,
175
+ midiControllers.lsbForControl7MainVolume,
176
+ midiControllers.pan,
177
+ midiControllers.lsbForControl10Pan,
178
+ midiControllers.reverbDepth,
179
+ midiControllers.tremoloDepth,
180
+ midiControllers.chorusDepth,
181
+ midiControllers.detuneDepth,
182
+ midiControllers.phaserDepth,
183
+ midiControllers.soundVariation,
184
+ midiControllers.filterResonance,
185
+ midiControllers.releaseTime,
186
+ midiControllers.attackTime,
187
+ midiControllers.brightness,
188
+ midiControllers.decayTime,
189
+ midiControllers.vibratoRate,
190
+ midiControllers.vibratoDepth,
191
+ midiControllers.vibratoDelay,
192
+ midiControllers.soundController10
193
+ ]);
194
+
195
+ /**
196
+ * Reset all controllers for channel, but RP-15 compliant
197
+ * https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
198
+ * @this {MidiAudioChannel}
199
+ */
200
+ export function resetControllersRP15Compliant()
201
+ {
202
+ // reset tunings
203
+ this.channelOctaveTuning.fill(0);
204
+
205
+ // reset pitch bend
206
+ this.pitchWheel(64, 0);
207
+
208
+ this.channelVibrato = { rate: 0, depth: 0, delay: 0 };
209
+
210
+ for (let i = 0; i < 128; i++)
211
+ {
212
+ const resetValue = resetArray[i];
213
+ if (!nonResetableCCs.has(i) && resetValue !== this.midiControllers[i])
214
+ {
215
+ if (i === midiControllers.portamentoControl)
216
+ {
217
+ this.midiControllers[i] = PORTAMENTO_CONTROL_UNSET;
218
+ }
219
+ else
220
+ {
221
+ this.controllerChange(i, resetValue >> 7);
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ /**
228
+ * @this {MidiAudioChannel}
229
+ */
230
+ export function resetParameters()
231
+ {
232
+ /**
233
+ * reset the state machine to idle
234
+ * @type {string}
235
+ */
236
+ this.dataEntryState = dataEntryStates.Idle;
237
+ SpessaSynthInfo(
238
+ "%cResetting Registered and Non-Registered Parameters!",
239
+ consoleColors.info
240
+ );
241
+ }
@@ -0,0 +1,27 @@
1
+ import { MidiAudioChannel } from "../engine_components/midi_audio_channel.js";
2
+
3
+ import { DEFAULT_PERCUSSION } from "../../synth_constants.js";
4
+
5
+ /**
6
+ * @param sendEvent {boolean}
7
+ * @this {SpessaSynthProcessor}
8
+ */
9
+ export function createMidiChannel(sendEvent = false)
10
+ {
11
+ /**
12
+ * @type {MidiAudioChannel}
13
+ */
14
+ const channel = new MidiAudioChannel(this, this.defaultPreset, this.midiAudioChannels.length);
15
+ this.midiAudioChannels.push(channel);
16
+ channel.resetControllers();
17
+ channel.sendChannelProperty();
18
+ if (sendEvent)
19
+ {
20
+ this.callEvent("newchannel", undefined);
21
+ }
22
+
23
+ if (channel.channelNumber % 16 === DEFAULT_PERCUSSION)
24
+ {
25
+ this.midiAudioChannels[this.midiAudioChannels.length - 1].setDrums(true);
26
+ }
27
+ }
@@ -0,0 +1,253 @@
1
+ import { customControllers, dataEntryStates, NON_CC_INDEX_OFFSET } from "../../engine_components/controller_tables.js";
2
+ import { SpessaSynthInfo, SpessaSynthWarn } from "../../../../utils/loggin.js";
3
+ import { consoleColors } from "../../../../utils/other.js";
4
+ import { midiControllers } from "../../../../midi/midi_message.js";
5
+ import { modulatorSources } from "../../../../soundfont/basic_soundfont/modulator.js";
6
+
7
+
8
+ /**
9
+ * @enum {number}
10
+ */
11
+ const registeredParameterTypes = {
12
+ pitchBendRange: 0x0000,
13
+ fineTuning: 0x0001,
14
+ coarseTuning: 0x0002,
15
+ modulationDepth: 0x0005,
16
+ resetParameters: 0x3FFF
17
+ };
18
+
19
+ /**
20
+ * https://cdn.roland.com/assets/media/pdf/SC-88PRO_OM.pdf
21
+ * http://hummer.stanford.edu/sig/doc/classes/MidiOutput/rpn.html
22
+ * @enum {number}
23
+ */
24
+ const nonRegisteredParameterNumbers = {
25
+ partParameter: 0x01,
26
+
27
+ vibratoRate: 0x08,
28
+ vibratoDepth: 0x09,
29
+ vibratoDelay: 0x0A,
30
+
31
+ EGAttackTime: 0x64,
32
+ EGReleaseTime: 0x66,
33
+
34
+ TVFFilterCutoff: 0x20,
35
+ drumReverb: 0x1D
36
+ };
37
+
38
+
39
+ /**
40
+ * Executes a data entry for an NRP for a sc88pro NRP (because touhou yes) and RPN tuning
41
+ * @param dataValue {number} dataEntryCoarse MSB
42
+ * @this {MidiAudioChannel}
43
+ * @private
44
+ */
45
+ export function dataEntryCoarse(dataValue)
46
+ {
47
+ const addDefaultVibrato = () =>
48
+ {
49
+ if (this.channelVibrato.delay === 0 && this.channelVibrato.rate === 0 && this.channelVibrato.depth === 0)
50
+ {
51
+ this.channelVibrato.depth = 50;
52
+ this.channelVibrato.rate = 8;
53
+ this.channelVibrato.delay = 0.6;
54
+ }
55
+ };
56
+
57
+ const coolInfo = (what, value, type) =>
58
+ {
59
+ if (type.length > 0)
60
+ {
61
+ type = " " + type;
62
+ }
63
+ SpessaSynthInfo(
64
+ `%c${what} for %c${this.channelNumber}%c is now set to %c${value}%c${type}.`,
65
+ consoleColors.info,
66
+ consoleColors.recognized,
67
+ consoleColors.info,
68
+ consoleColors.value,
69
+ consoleColors.info
70
+ );
71
+ };
72
+ switch (this.dataEntryState)
73
+ {
74
+ default:
75
+ case dataEntryStates.Idle:
76
+ break;
77
+
78
+ // process GS NRPNs
79
+ case dataEntryStates.NRPFine:
80
+ if (this.lockGSNRPNParams)
81
+ {
82
+ return;
83
+ }
84
+ /**
85
+ * @type {number}
86
+ */
87
+ const NRPNCoarse = this.midiControllers[midiControllers.NRPNMsb] >> 7;
88
+ /**
89
+ * @type {number}
90
+ */
91
+ const NRPNFine = this.midiControllers[midiControllers.NRPNLsb] >> 7;
92
+ switch (NRPNCoarse)
93
+ {
94
+ default:
95
+ if (dataValue === 64)
96
+ {
97
+ // default value
98
+ return;
99
+ }
100
+ SpessaSynthWarn(
101
+ `%cUnrecognized NRPN for %c${this.channelNumber}%c: %c(0x${NRPNFine.toString(16)
102
+ .toUpperCase()} 0x${NRPNFine.toString(
103
+ 16).toUpperCase()})%c data value: %c${dataValue}`,
104
+ consoleColors.warn,
105
+ consoleColors.recognized,
106
+ consoleColors.warn,
107
+ consoleColors.unrecognized,
108
+ consoleColors.warn,
109
+ consoleColors.value
110
+ );
111
+ break;
112
+
113
+ // part parameters: vibrato, cutoff
114
+ case nonRegisteredParameterNumbers.partParameter:
115
+ switch (NRPNFine)
116
+ {
117
+ default:
118
+ if (dataValue === 64)
119
+ {
120
+ // default value
121
+ return;
122
+ }
123
+ SpessaSynthWarn(
124
+ `%cUnrecognized NRPN for %c${this.channelNumber}%c: %c(0x${NRPNCoarse.toString(16)} 0x${NRPNFine.toString(
125
+ 16)})%c data value: %c${dataValue}`,
126
+ consoleColors.warn,
127
+ consoleColors.recognized,
128
+ consoleColors.warn,
129
+ consoleColors.unrecognized,
130
+ consoleColors.warn,
131
+ consoleColors.value
132
+ );
133
+ break;
134
+
135
+ // vibrato rate
136
+ case nonRegisteredParameterNumbers.vibratoRate:
137
+ if (dataValue === 64)
138
+ {
139
+ return;
140
+ }
141
+ addDefaultVibrato();
142
+ this.channelVibrato.rate = (dataValue / 64) * 8;
143
+ coolInfo("Vibrato rate", `${dataValue} = ${this.channelVibrato.rate}`, "Hz");
144
+ break;
145
+
146
+ // vibrato depth
147
+ case nonRegisteredParameterNumbers.vibratoDepth:
148
+ if (dataValue === 64)
149
+ {
150
+ return;
151
+ }
152
+ addDefaultVibrato();
153
+ this.channelVibrato.depth = dataValue / 2;
154
+ coolInfo("Vibrato depth", `${dataValue} = ${this.channelVibrato.depth}`, "cents of detune");
155
+ break;
156
+
157
+ // vibrato delay
158
+ case nonRegisteredParameterNumbers.vibratoDelay:
159
+ if (dataValue === 64)
160
+ {
161
+ return;
162
+ }
163
+ addDefaultVibrato();
164
+ this.channelVibrato.delay = (dataValue / 64) / 3;
165
+ coolInfo("Vibrato delay", `${dataValue} = ${this.channelVibrato.delay}`, "seconds");
166
+ break;
167
+
168
+ // filter cutoff
169
+ case nonRegisteredParameterNumbers.TVFFilterCutoff:
170
+ // affect the "brightness" controller as we have a default modulator that controls it
171
+ this.controllerChange(midiControllers.brightness, dataValue);
172
+ coolInfo("Filter cutoff", dataValue.toString(), "");
173
+ break;
174
+
175
+ // attack time
176
+ case nonRegisteredParameterNumbers.EGAttackTime:
177
+ // affect the "attack time" controller as we have a default modulator that controls it
178
+ this.controllerChange(midiControllers.attackTime, dataValue);
179
+ coolInfo("EG attack time", dataValue.toString(), "");
180
+ break;
181
+
182
+ // release time
183
+ case nonRegisteredParameterNumbers.EGReleaseTime:
184
+ // affect the "release time" controller as we have a default modulator that controls it
185
+ this.controllerChange(midiControllers.releaseTime, dataValue);
186
+ coolInfo("EG release time", dataValue.toString(), "");
187
+ break;
188
+ }
189
+ break;
190
+
191
+ // drum reverb
192
+ case nonRegisteredParameterNumbers.drumReverb:
193
+ const reverb = dataValue;
194
+ this.controllerChange(midiControllers.reverbDepth, reverb);
195
+ coolInfo("GS Drum reverb", reverb.toString(), "percent");
196
+ break;
197
+ }
198
+ break;
199
+
200
+ case dataEntryStates.RPCoarse:
201
+ case dataEntryStates.RPFine:
202
+ /**
203
+ * @type {number}
204
+ */
205
+ const rpnValue = this.midiControllers[midiControllers.RPNMsb] | (this.midiControllers[midiControllers.RPNLsb] >> 7);
206
+ switch (rpnValue)
207
+ {
208
+ default:
209
+ SpessaSynthWarn(
210
+ `%cUnrecognized RPN for %c${this.channelNumber}%c: %c(0x${rpnValue.toString(16)})%c data value: %c${dataValue}`,
211
+ consoleColors.warn,
212
+ consoleColors.recognized,
213
+ consoleColors.warn,
214
+ consoleColors.unrecognized,
215
+ consoleColors.warn,
216
+ consoleColors.value
217
+ );
218
+ break;
219
+
220
+ // pitch bend range
221
+ case registeredParameterTypes.pitchBendRange:
222
+ this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] = dataValue << 7;
223
+ coolInfo("Pitch bend range", dataValue.toString(), "semitones");
224
+ break;
225
+
226
+ // coarse tuning
227
+ case registeredParameterTypes.coarseTuning:
228
+ // semitones
229
+ const semitones = dataValue - 64;
230
+ this.setCustomController(customControllers.channelTuningSemitones, semitones);
231
+ coolInfo("Coarse tuning", semitones.toString(), "semitones");
232
+ break;
233
+
234
+ // fine-tuning
235
+ case registeredParameterTypes.fineTuning:
236
+ // note: this will not work properly unless the lsb is sent!
237
+ // here we store the raw value to then adjust in fine
238
+ this.setTuning(dataValue - 64, false);
239
+ break;
240
+
241
+ // modulation depth
242
+ case registeredParameterTypes.modulationDepth:
243
+ this.setModulationDepth(dataValue * 100);
244
+ break;
245
+
246
+ case registeredParameterTypes.resetParameters:
247
+ this.resetParameters();
248
+ break;
249
+
250
+ }
251
+
252
+ }
253
+ }