spessasynth_core 3.27.7 → 4.0.0

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 (157) hide show
  1. package/README.md +188 -116
  2. package/dist/index.d.ts +4057 -0
  3. package/dist/index.js +17188 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +23 -6
  6. package/index.js +0 -132
  7. package/src/externals/README.md +0 -6
  8. package/src/externals/fflate/LICENSE +0 -21
  9. package/src/externals/fflate/fflate.min.js +0 -1
  10. package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +0 -12
  11. package/src/externals/stbvorbis_sync/LICENSE +0 -202
  12. package/src/externals/stbvorbis_sync/NOTICE +0 -6
  13. package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +0 -1
  14. package/src/midi/README.md +0 -32
  15. package/src/midi/basic_midi.js +0 -587
  16. package/src/midi/midi_builder.js +0 -203
  17. package/src/midi/midi_loader.js +0 -321
  18. package/src/midi/midi_message.js +0 -254
  19. package/src/midi/midi_sequence.js +0 -230
  20. package/src/midi/midi_tools/get_note_times.js +0 -154
  21. package/src/midi/midi_tools/midi_editor.js +0 -611
  22. package/src/midi/midi_tools/midi_writer.js +0 -105
  23. package/src/midi/midi_tools/rmidi_writer.js +0 -566
  24. package/src/midi/midi_tools/used_keys_loaded.js +0 -256
  25. package/src/midi/xmf_loader.js +0 -454
  26. package/src/sequencer/README.md +0 -9
  27. package/src/sequencer/events.js +0 -81
  28. package/src/sequencer/play.js +0 -362
  29. package/src/sequencer/process_event.js +0 -165
  30. package/src/sequencer/process_tick.js +0 -104
  31. package/src/sequencer/sequencer_engine.js +0 -372
  32. package/src/sequencer/song_control.js +0 -196
  33. package/src/soundfont/README.md +0 -11
  34. package/src/soundfont/basic_soundfont/basic_global_zone.js +0 -6
  35. package/src/soundfont/basic_soundfont/basic_instrument.js +0 -115
  36. package/src/soundfont/basic_soundfont/basic_instrument_zone.js +0 -45
  37. package/src/soundfont/basic_soundfont/basic_preset.js +0 -313
  38. package/src/soundfont/basic_soundfont/basic_preset_zone.js +0 -39
  39. package/src/soundfont/basic_soundfont/basic_sample.js +0 -477
  40. package/src/soundfont/basic_soundfont/basic_soundbank.js +0 -740
  41. package/src/soundfont/basic_soundfont/basic_zone.js +0 -145
  42. package/src/soundfont/basic_soundfont/generator.js +0 -76
  43. package/src/soundfont/basic_soundfont/generator_types.js +0 -151
  44. package/src/soundfont/basic_soundfont/modulator.js +0 -581
  45. package/src/soundfont/basic_soundfont/riff_chunk.js +0 -195
  46. package/src/soundfont/basic_soundfont/write_dls/art2.js +0 -174
  47. package/src/soundfont/basic_soundfont/write_dls/articulator.js +0 -49
  48. package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +0 -374
  49. package/src/soundfont/basic_soundfont/write_dls/ins.js +0 -85
  50. package/src/soundfont/basic_soundfont/write_dls/lins.js +0 -15
  51. package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +0 -330
  52. package/src/soundfont/basic_soundfont/write_dls/rgn2.js +0 -120
  53. package/src/soundfont/basic_soundfont/write_dls/wave.js +0 -71
  54. package/src/soundfont/basic_soundfont/write_dls/write_dls.js +0 -124
  55. package/src/soundfont/basic_soundfont/write_dls/wsmp.js +0 -78
  56. package/src/soundfont/basic_soundfont/write_dls/wvpl.js +0 -35
  57. package/src/soundfont/basic_soundfont/write_sf2/ibag.js +0 -60
  58. package/src/soundfont/basic_soundfont/write_sf2/igen.js +0 -91
  59. package/src/soundfont/basic_soundfont/write_sf2/imod.js +0 -62
  60. package/src/soundfont/basic_soundfont/write_sf2/inst.js +0 -42
  61. package/src/soundfont/basic_soundfont/write_sf2/pbag.js +0 -57
  62. package/src/soundfont/basic_soundfont/write_sf2/pgen.js +0 -92
  63. package/src/soundfont/basic_soundfont/write_sf2/phdr.js +0 -61
  64. package/src/soundfont/basic_soundfont/write_sf2/pmod.js +0 -62
  65. package/src/soundfont/basic_soundfont/write_sf2/sdta.js +0 -131
  66. package/src/soundfont/basic_soundfont/write_sf2/shdr.js +0 -77
  67. package/src/soundfont/basic_soundfont/write_sf2/write.js +0 -287
  68. package/src/soundfont/dls/articulator_converter.js +0 -402
  69. package/src/soundfont/dls/dls_destinations.js +0 -38
  70. package/src/soundfont/dls/dls_instrument.js +0 -20
  71. package/src/soundfont/dls/dls_preset.js +0 -43
  72. package/src/soundfont/dls/dls_sample.js +0 -238
  73. package/src/soundfont/dls/dls_soundfont.js +0 -183
  74. package/src/soundfont/dls/dls_sources.js +0 -63
  75. package/src/soundfont/dls/dls_zone.js +0 -89
  76. package/src/soundfont/dls/read_articulation.js +0 -300
  77. package/src/soundfont/dls/read_instrument.js +0 -118
  78. package/src/soundfont/dls/read_instrument_list.js +0 -17
  79. package/src/soundfont/dls/read_lart.js +0 -35
  80. package/src/soundfont/dls/read_region.js +0 -157
  81. package/src/soundfont/dls/read_samples.js +0 -154
  82. package/src/soundfont/load_soundfont.js +0 -21
  83. package/src/soundfont/read_sf2/generators.js +0 -43
  84. package/src/soundfont/read_sf2/instrument_zones.js +0 -75
  85. package/src/soundfont/read_sf2/instruments.js +0 -71
  86. package/src/soundfont/read_sf2/modulators.js +0 -25
  87. package/src/soundfont/read_sf2/preset_zones.js +0 -79
  88. package/src/soundfont/read_sf2/presets.js +0 -80
  89. package/src/soundfont/read_sf2/samples.js +0 -317
  90. package/src/soundfont/read_sf2/soundfont.js +0 -452
  91. package/src/soundfont/read_sf2/zones.js +0 -28
  92. package/src/synthetizer/README.md +0 -7
  93. package/src/synthetizer/audio_engine/README.md +0 -9
  94. package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +0 -289
  95. package/src/synthetizer/audio_engine/engine_components/controller_tables.js +0 -90
  96. package/src/synthetizer/audio_engine/engine_components/dynamic_modulator_system.js +0 -95
  97. package/src/synthetizer/audio_engine/engine_components/enums.js +0 -18
  98. package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +0 -151
  99. package/src/synthetizer/audio_engine/engine_components/lfo.js +0 -26
  100. package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +0 -282
  101. package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +0 -551
  102. package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +0 -181
  103. package/src/synthetizer/audio_engine/engine_components/modulator_curves.js +0 -89
  104. package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +0 -265
  105. package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +0 -124
  106. package/src/synthetizer/audio_engine/engine_components/unit_converter.js +0 -73
  107. package/src/synthetizer/audio_engine/engine_components/voice.js +0 -525
  108. package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +0 -402
  109. package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +0 -274
  110. package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +0 -159
  111. package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +0 -48
  112. package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -254
  113. package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +0 -20
  114. package/src/synthetizer/audio_engine/engine_methods/data_entry/awe32.js +0 -198
  115. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +0 -281
  116. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +0 -109
  117. package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +0 -17
  118. package/src/synthetizer/audio_engine/engine_methods/note_on.js +0 -214
  119. package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +0 -92
  120. package/src/synthetizer/audio_engine/engine_methods/program_change.js +0 -35
  121. package/src/synthetizer/audio_engine/engine_methods/render_voice.js +0 -214
  122. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/embedded_sound_bank.js +0 -42
  123. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +0 -0
  124. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/update_preset_list.js +0 -19
  125. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +0 -23
  126. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +0 -56
  127. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +0 -16
  128. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +0 -30
  129. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +0 -63
  130. package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +0 -1058
  131. package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +0 -23
  132. package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +0 -31
  133. package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +0 -29
  134. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +0 -15
  135. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +0 -27
  136. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +0 -19
  137. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +0 -27
  138. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +0 -15
  139. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +0 -34
  140. package/src/synthetizer/audio_engine/main_processor.js +0 -813
  141. package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +0 -16
  142. package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +0 -175
  143. package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +0 -116
  144. package/src/synthetizer/audio_engine/synth_processor_options.js +0 -18
  145. package/src/synthetizer/synth_constants.js +0 -26
  146. package/src/utils/README.md +0 -8
  147. package/src/utils/buffer_to_wav.js +0 -197
  148. package/src/utils/byte_functions/big_endian.js +0 -32
  149. package/src/utils/byte_functions/little_endian.js +0 -77
  150. package/src/utils/byte_functions/string.js +0 -92
  151. package/src/utils/byte_functions/variable_length_quantity.js +0 -42
  152. package/src/utils/fill_with_defaults.js +0 -21
  153. package/src/utils/indexed_array.js +0 -34
  154. package/src/utils/loggin.js +0 -71
  155. package/src/utils/other.js +0 -92
  156. package/src/utils/sysex_detector.js +0 -58
  157. package/src/utils/xg_hacks.js +0 -193
@@ -1,611 +0,0 @@
1
- import { messageTypes, midiControllers, MIDIMessage } from "../midi_message.js";
2
- import { IndexedByteArray } from "../../utils/indexed_array.js";
3
- import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../../utils/loggin.js";
4
- import { consoleColors } from "../../utils/other.js";
5
-
6
- import { customControllers } from "../../synthetizer/audio_engine/engine_components/controller_tables.js";
7
- import { DEFAULT_PERCUSSION } from "../../synthetizer/synth_constants.js";
8
- import { isGM2On, isGMOn, isGSOn, isXGOn } from "../../utils/sysex_detector.js";
9
- import { isSystemXG, isXGDrums, XG_SFX_VOICE } from "../../utils/xg_hacks.js";
10
-
11
- /**
12
- * @param ticks {number}
13
- * @returns {MIDIMessage}
14
- */
15
- export function getGsOn(ticks)
16
- {
17
- return new MIDIMessage(
18
- ticks,
19
- messageTypes.systemExclusive,
20
- new IndexedByteArray([
21
- 0x41, // Roland
22
- 0x10, // Device ID (defaults to 16 on roland)
23
- 0x42, // GS
24
- 0x12, // Command ID (DT1) (whatever that means...)
25
- 0x40, // System parameter - Address
26
- 0x00, // Global parameter - Address
27
- 0x7F, // GS Change - Address
28
- 0x00, // turn on - Data
29
- 0x41, // checksum
30
- 0xF7 // end of exclusive
31
- ])
32
- );
33
- }
34
-
35
- /**
36
- * @param channel {number}
37
- * @param cc {number}
38
- * @param value {number}
39
- * @param ticks {number}
40
- * @returns {MIDIMessage}
41
- */
42
- function getControllerChange(channel, cc, value, ticks)
43
- {
44
- return new MIDIMessage(
45
- ticks,
46
- messageTypes.controllerChange | (channel % 16),
47
- new IndexedByteArray([cc, value])
48
- );
49
- }
50
-
51
- /**
52
- * @param channel {number}
53
- * @param ticks {number}
54
- * @returns {MIDIMessage}
55
- */
56
- function getDrumChange(channel, ticks)
57
- {
58
- const chanAddress = 0x10 | [1, 2, 3, 4, 5, 6, 7, 8, 0, 9, 10, 11, 12, 13, 14, 15][channel % 16];
59
- // excluding manufacturerID DeviceID and ModelID (and F7)
60
- const sysexData = [
61
- 0x41, // Roland
62
- 0x10, // Device ID (defaults to 16 on roland)
63
- 0x42, // GS
64
- 0x12, // Command ID (DT1) (whatever that means...)
65
- 0x40, // System parameter }
66
- chanAddress, // Channel parameter } Address
67
- 0x15, // Drum change }
68
- 0x01 // Is Drums } Data
69
- ];
70
- // calculate checksum
71
- // https://cdn.roland.com/assets/media/pdf/F-20_MIDI_Imple_e01_W.pdf section 4
72
- const sum = 0x40 + chanAddress + 0x15 + 0x01;
73
- const checksum = 128 - (sum % 128);
74
- // add system exclusive to enable drums
75
- return new MIDIMessage(
76
- ticks,
77
- messageTypes.systemExclusive,
78
- new IndexedByteArray([
79
- ...sysexData,
80
- checksum,
81
- 0xF7
82
- ])
83
- );
84
- }
85
-
86
- /**
87
- * @typedef {Object} DesiredProgramChange
88
- * @property {number} channel - The channel number.
89
- * @property {number} program - The program number.
90
- * @property {number} bank - The bank number.
91
- * @property {boolean} isDrum - Indicates if the channel is a drum channel.
92
- * If it is, then the bank number is ignored.
93
- */
94
-
95
- /**
96
- * @typedef {Object} DesiredControllerChange
97
- * @property {number} channel - The channel number.
98
- * @property {number} controllerNumber - The MIDI controller number.
99
- * @property {number} controllerValue - The new controller value.
100
- */
101
-
102
- /**
103
- * @typedef {Object} DesiredChanneltranspose
104
- * @property {number} channel - The channel number.
105
- * @property {number} keyShift - The number of semitones to transpose.
106
- * Note that this can use floating point numbers,
107
- * which will be used to fine-tune the pitch in cents using RPN.
108
- */
109
-
110
-
111
- /**
112
- * Allows easy editing of the file by removing channels, changing programs,
113
- * changing controllers and transposing channels. Note that this modifies the MIDI in-place.
114
- *
115
- * @this {BasicMIDI}
116
- * @param {DesiredProgramChange[]} desiredProgramChanges - The programs to set on given channels.
117
- * @param {DesiredControllerChange[]} desiredControllerChanges - The controllers to set on given channels.
118
- * @param {number[]} desiredChannelsToClear - The channels to remove from the sequence.
119
- * @param {DesiredChanneltranspose[]} desiredChannelsToTranspose - The channels to transpose.
120
- */
121
- export function modifyMIDI(
122
- desiredProgramChanges = [],
123
- desiredControllerChanges = [],
124
- desiredChannelsToClear = [],
125
- desiredChannelsToTranspose = []
126
- )
127
- {
128
- const midi = this;
129
- SpessaSynthGroupCollapsed("%cApplying changes to the MIDI file...", consoleColors.info);
130
-
131
- SpessaSynthInfo("Desired program changes:", desiredProgramChanges);
132
- SpessaSynthInfo("Desired CC changes:", desiredControllerChanges);
133
- SpessaSynthInfo("Desired channels to clear:", desiredChannelsToClear);
134
- SpessaSynthInfo("Desired channels to transpose:", desiredChannelsToTranspose);
135
-
136
- /**
137
- * @type {Set<number>}
138
- */
139
- const channelsToChangeProgram = new Set();
140
- desiredProgramChanges.forEach(c =>
141
- {
142
- channelsToChangeProgram.add(c.channel);
143
- });
144
-
145
-
146
- // go through all events one by one
147
- let system = "gs";
148
- let addedGs = false;
149
- /**
150
- * indexes for tracks
151
- * @type {number[]}
152
- */
153
- const eventIndexes = Array(midi.tracks.length).fill(0);
154
- let remainingTracks = midi.tracks.length;
155
-
156
- function findFirstEventIndex()
157
- {
158
- let index = 0;
159
- let ticks = Infinity;
160
- midi.tracks.forEach((track, i) =>
161
- {
162
- if (eventIndexes[i] >= track.length)
163
- {
164
- return;
165
- }
166
- if (track[eventIndexes[i]].ticks < ticks)
167
- {
168
- index = i;
169
- ticks = track[eventIndexes[i]].ticks;
170
- }
171
- });
172
- return index;
173
- }
174
-
175
- // it copies midiPorts everywhere else, but here 0 works so DO NOT CHANGE!
176
- /**
177
- * midi port number for the corresponding track
178
- * @type {number[]}
179
- */
180
- const midiPorts = midi.midiPorts.slice();
181
- /**
182
- * midi port: channel offset
183
- * @type {Record<number, number>}
184
- */
185
- const midiPortChannelOffsets = {};
186
- let midiPortChannelOffset = 0;
187
-
188
- function assignMIDIPort(trackNum, port)
189
- {
190
- // do not assign ports to empty tracks
191
- if (midi.usedChannelsOnTrack[trackNum].size === 0)
192
- {
193
- return;
194
- }
195
-
196
- // assign new 16 channels if the port is not occupied yet
197
- if (midiPortChannelOffset === 0)
198
- {
199
- midiPortChannelOffset += 16;
200
- midiPortChannelOffsets[port] = 0;
201
- }
202
-
203
- if (midiPortChannelOffsets[port] === undefined)
204
- {
205
- midiPortChannelOffsets[port] = midiPortChannelOffset;
206
- midiPortChannelOffset += 16;
207
- }
208
-
209
- midiPorts[trackNum] = port;
210
- }
211
-
212
- // assign port offsets
213
- midi.midiPorts.forEach((port, trackIndex) =>
214
- {
215
- assignMIDIPort(trackIndex, port);
216
- });
217
-
218
- const channelsAmount = midiPortChannelOffset;
219
- /**
220
- * Tracks if the channel already had its first note on
221
- * @type {boolean[]}
222
- */
223
- const isFirstNoteOn = Array(channelsAmount).fill(true);
224
-
225
- /**
226
- * MIDI key transpose
227
- * @type {number[]}
228
- */
229
- const coarseTranspose = Array(channelsAmount).fill(0);
230
- /**
231
- * RPN fine transpose
232
- * @type {number[]}
233
- */
234
- const fineTranspose = Array(channelsAmount).fill(0);
235
- desiredChannelsToTranspose.forEach(transpose =>
236
- {
237
- const coarse = Math.trunc(transpose.keyShift);
238
- const fine = transpose.keyShift - coarse;
239
- coarseTranspose[transpose.channel] = coarse;
240
- fineTranspose[transpose.channel] = fine;
241
- });
242
-
243
- while (remainingTracks > 0)
244
- {
245
- let trackNum = findFirstEventIndex();
246
- const track = midi.tracks[trackNum];
247
- if (eventIndexes[trackNum] >= track.length)
248
- {
249
- remainingTracks--;
250
- continue;
251
- }
252
- const index = eventIndexes[trackNum]++;
253
- const e = track[index];
254
-
255
- const deleteThisEvent = () =>
256
- {
257
- track.splice(index, 1);
258
- eventIndexes[trackNum]--;
259
- };
260
-
261
- /**
262
- * @param e {MIDIMessage}
263
- * @param offset{number}
264
- */
265
- const addEventBefore = (e, offset = 0) =>
266
- {
267
- track.splice(index + offset, 0, e);
268
- eventIndexes[trackNum]++;
269
- };
270
-
271
-
272
- let portOffset = midiPortChannelOffsets[midiPorts[trackNum]] || 0;
273
- if (e.messageStatusByte === messageTypes.midiPort)
274
- {
275
- assignMIDIPort(trackNum, e.messageData[0]);
276
- continue;
277
- }
278
- // don't clear meta
279
- if (e.messageStatusByte <= messageTypes.sequenceSpecific && e.messageStatusByte >= messageTypes.sequenceNumber)
280
- {
281
- continue;
282
- }
283
- const status = e.messageStatusByte & 0xF0;
284
- const midiChannel = e.messageStatusByte & 0xF;
285
- const channel = midiChannel + portOffset;
286
- // clear channel?
287
- if (desiredChannelsToClear.indexOf(channel) !== -1)
288
- {
289
- deleteThisEvent();
290
- continue;
291
- }
292
- switch (status)
293
- {
294
- case messageTypes.noteOn:
295
- // is it first?
296
- if (isFirstNoteOn[channel])
297
- {
298
- isFirstNoteOn[channel] = false;
299
- // all right, so this is the first note on
300
- // first: controllers
301
- // because FSMP does not like program changes after cc changes in embedded midis
302
- // and since we use splice,
303
- // controllers get added first, then programs before them
304
- // now add controllers
305
- desiredControllerChanges.filter(c => c.channel === channel).forEach(change =>
306
- {
307
- const ccChange = getControllerChange(
308
- midiChannel,
309
- change.controllerNumber,
310
- change.controllerValue,
311
- e.ticks
312
- );
313
- addEventBefore(ccChange);
314
- });
315
- const fineTune = fineTranspose[channel];
316
-
317
- if (fineTune !== 0)
318
- {
319
- // add rpn
320
- // 64 is the center, 96 = 50 cents up
321
- const centsCoarse = (fineTune * 64) + 64;
322
- const rpnCoarse = getControllerChange(midiChannel, midiControllers.RPNMsb, 0, e.ticks);
323
- const rpnFine = getControllerChange(midiChannel, midiControllers.RPNLsb, 1, e.ticks);
324
- const dataEntryCoarse = getControllerChange(
325
- channel,
326
- midiControllers.dataEntryMsb,
327
- centsCoarse,
328
- e.ticks
329
- );
330
- const dataEntryFine = getControllerChange(
331
- midiChannel,
332
- midiControllers.lsbForControl6DataEntry,
333
- 0,
334
- e.ticks
335
- );
336
- addEventBefore(dataEntryFine);
337
- addEventBefore(dataEntryCoarse);
338
- addEventBefore(rpnFine);
339
- addEventBefore(rpnCoarse);
340
-
341
- }
342
-
343
- if (channelsToChangeProgram.has(channel))
344
- {
345
- const change = desiredProgramChanges.find(c => c.channel === channel);
346
- let desiredBank = Math.max(0, Math.min(change.bank, 127));
347
- const desiredProgram = change.program;
348
- SpessaSynthInfo(
349
- `%cSetting %c${change.channel}%c to %c${desiredBank}:${desiredProgram}%c. Track num: %c${trackNum}`,
350
- consoleColors.info,
351
- consoleColors.recognized,
352
- consoleColors.info,
353
- consoleColors.recognized,
354
- consoleColors.info,
355
- consoleColors.recognized
356
- );
357
-
358
- // note: this is in reverse.
359
- // the output event order is: drums -> lsb -> msb -> program change
360
-
361
- // add program change
362
- const programChange = new MIDIMessage(
363
- e.ticks,
364
- messageTypes.programChange | midiChannel,
365
- new IndexedByteArray([
366
- desiredProgram
367
- ])
368
- );
369
- addEventBefore(programChange);
370
-
371
- const addBank = (isLSB, v) =>
372
- {
373
- const bankChange = getControllerChange(
374
- midiChannel,
375
- isLSB ? midiControllers.lsbForControl0BankSelect : midiControllers.bankSelect,
376
- v,
377
- e.ticks
378
- );
379
- addEventBefore(bankChange);
380
- };
381
-
382
- // on xg, add lsb
383
- if (isSystemXG(system))
384
- {
385
- // xg drums: msb can be 120, 126 or 127
386
- if (change.isDrum)
387
- {
388
- SpessaSynthInfo(
389
- `%cAdding XG Drum change on track %c${trackNum}`,
390
- consoleColors.recognized,
391
- consoleColors.value
392
- );
393
- addBank(false, isXGDrums(desiredBank) ? desiredBank : 127);
394
- addBank(true, 0);
395
- }
396
- else
397
- {
398
- // sfx voice is set via MSB
399
- if (desiredBank === XG_SFX_VOICE)
400
- {
401
- addBank(false, XG_SFX_VOICE);
402
- addBank(true, 0);
403
- }
404
- else
405
- {
406
- // add variation as LSB
407
- addBank(false, 0);
408
- addBank(true, desiredBank);
409
- }
410
- }
411
- }
412
- else
413
- {
414
- // add just msb
415
- addBank(false, desiredBank);
416
-
417
- if (change.isDrum && midiChannel !== DEFAULT_PERCUSSION)
418
- {
419
- // add gs drum change
420
- SpessaSynthInfo(
421
- `%cAdding GS Drum change on track %c${trackNum}`,
422
- consoleColors.recognized,
423
- consoleColors.value
424
- );
425
- addEventBefore(getDrumChange(midiChannel, e.ticks));
426
- }
427
- }
428
- }
429
- }
430
- // transpose key (for zero it won't change anyway)
431
- e.messageData[0] += coarseTranspose[channel];
432
- break;
433
-
434
- case messageTypes.noteOff:
435
- e.messageData[0] += coarseTranspose[channel];
436
- break;
437
-
438
- case messageTypes.programChange:
439
- // do we delete it?
440
- if (channelsToChangeProgram.has(channel))
441
- {
442
- // this channel has program change. BEGONE!
443
- deleteThisEvent();
444
- continue;
445
- }
446
- break;
447
-
448
- case messageTypes.controllerChange:
449
- const ccNum = e.messageData[0];
450
- const changes = desiredControllerChanges.find(c => c.channel === channel && ccNum === c.controllerNumber);
451
- if (changes !== undefined)
452
- {
453
- // this controller is locked, BEGONE CHANGE!
454
- deleteThisEvent();
455
- continue;
456
- }
457
- // bank maybe?
458
- if (ccNum === midiControllers.bankSelect || ccNum === midiControllers.lsbForControl0BankSelect)
459
- {
460
- if (channelsToChangeProgram.has(channel))
461
- {
462
- // BEGONE!
463
- deleteThisEvent();
464
- continue;
465
- }
466
- }
467
- break;
468
-
469
- case messageTypes.systemExclusive:
470
- // check for xg on
471
- if (isXGOn(e))
472
- {
473
- SpessaSynthInfo("%cXG system on detected", consoleColors.info);
474
- system = "xg";
475
- addedGs = true; // flag as true so gs won't get added
476
- }
477
- else
478
- // check for xg program change
479
- if (
480
- e.messageData[0] === 0x43 // yamaha
481
- && e.messageData[2] === 0x4C // XG
482
- && e.messageData[3] === 0x08 // part parameter
483
- && e.messageData[5] === 0x03 // program change
484
- )
485
- {
486
- // do we delete it?
487
- if (channelsToChangeProgram.has(e.messageData[4] + portOffset))
488
- {
489
- // this channel has program change. BEGONE!
490
- deleteThisEvent();
491
- }
492
- }
493
- else
494
- // check for GS on
495
- if (isGSOn(e))
496
- {
497
- // that's a GS on, we're done here
498
- addedGs = true;
499
- SpessaSynthInfo(
500
- "%cGS on detected!",
501
- consoleColors.recognized
502
- );
503
- break;
504
- }
505
- else
506
- // check for GM/2 on
507
- if (isGMOn(e) || isGM2On(e))
508
- {
509
- // that's a GM1 system change, remove it!
510
- SpessaSynthInfo(
511
- "%cGM/2 on detected, removing!",
512
- consoleColors.info
513
- );
514
- deleteThisEvent();
515
- addedGs = false;
516
- }
517
- }
518
- }
519
- // check for gs
520
- if (!addedGs && desiredProgramChanges.length > 0)
521
- {
522
- // gs is not on, add it on the first track at index 0 (or 1 if track name is first)
523
- let index = 0;
524
- if (midi.tracks[0][0].messageStatusByte === messageTypes.trackName)
525
- {
526
- index++;
527
- }
528
- midi.tracks[0].splice(index, 0, getGsOn(0));
529
- SpessaSynthInfo("%cGS on not detected. Adding it.", consoleColors.info);
530
- }
531
- this.flush();
532
- SpessaSynthGroupEnd();
533
- }
534
-
535
- /**
536
- * Modifies the sequence according to the locked presets and controllers in the given snapshot
537
- * @this {BasicMIDI}
538
- * @param snapshot {SynthesizerSnapshot}
539
- */
540
- export function applySnapshotToMIDI(snapshot)
541
- {
542
- /**
543
- * @type {{
544
- * channel: number,
545
- * keyShift: number
546
- * }[]}
547
- */
548
- const channelsToTranspose = [];
549
- /**
550
- * @type {number[]}
551
- */
552
- const channelsToClear = [];
553
- /**
554
- * @type {{
555
- * channel: number,
556
- * program: number,
557
- * bank: number,
558
- * isDrum: boolean
559
- * }[]}
560
- */
561
- const programChanges = [];
562
- /**
563
- *
564
- * @type {{
565
- * channel: number,
566
- * controllerNumber: number,
567
- * controllerValue: number
568
- * }[]}
569
- */
570
- const controllerChanges = [];
571
- snapshot.channelSnapshots.forEach((channel, channelNumber) =>
572
- {
573
- if (channel.isMuted)
574
- {
575
- channelsToClear.push(channelNumber);
576
- return;
577
- }
578
- const transposeFloat = channel.channelTransposeKeyShift + channel.customControllers[customControllers.channelTransposeFine] / 100;
579
- if (transposeFloat !== 0)
580
- {
581
- channelsToTranspose.push({
582
- channel: channelNumber,
583
- keyShift: transposeFloat
584
- });
585
- }
586
- if (channel.lockPreset)
587
- {
588
- programChanges.push({
589
- channel: channelNumber,
590
- program: channel.program,
591
- bank: channel.bank,
592
- isDrum: channel.drumChannel
593
- });
594
- }
595
- // check for locked controllers and change them appropriately
596
- channel.lockedControllers.forEach((l, ccNumber) =>
597
- {
598
- if (!l || ccNumber > 127 || ccNumber === midiControllers.bankSelect)
599
- {
600
- return;
601
- }
602
- const targetValue = channel.midiControllers[ccNumber] >> 7; // channel controllers are stored as 14 bit values
603
- controllerChanges.push({
604
- channel: channelNumber,
605
- controllerNumber: ccNumber,
606
- controllerValue: targetValue
607
- });
608
- });
609
- });
610
- this.modifyMIDI(programChanges, controllerChanges, channelsToClear, channelsToTranspose);
611
- }