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,587 +0,0 @@
1
- // noinspection JSUnusedGlobalSymbols
2
-
3
- import { MIDISequenceData } from "./midi_sequence.js";
4
- import { getStringBytes, readBytesAsString } from "../utils/byte_functions/string.js";
5
- import { messageTypes, MIDIMessage } from "./midi_message.js";
6
- import { readBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
7
- import { SpessaSynthGroup, SpessaSynthGroupEnd, SpessaSynthInfo } from "../utils/loggin.js";
8
- import { consoleColors, formatTitle, sanitizeKarLyrics } from "../utils/other.js";
9
- import { writeMIDI } from "./midi_tools/midi_writer.js";
10
- import { applySnapshotToMIDI, modifyMIDI } from "./midi_tools/midi_editor.js";
11
- import { writeRMIDI } from "./midi_tools/rmidi_writer.js";
12
- import { getUsedProgramsAndKeys } from "./midi_tools/used_keys_loaded.js";
13
- import { IndexedByteArray } from "../utils/indexed_array.js";
14
- import { getNoteTimes } from "./midi_tools/get_note_times.js";
15
-
16
- /**
17
- * BasicMIDI is the base of a complete MIDI file, used by the sequencer internally.
18
- * BasicMIDI is not available on the main thread, as it contains the actual track data which can be large.
19
- * It can be accessed by calling getMIDI() on the Sequencer.
20
- */
21
- class BasicMIDI extends MIDISequenceData
22
- {
23
-
24
- /**
25
- * The embedded soundfont in the MIDI file, represented as an ArrayBuffer, if available.
26
- * @type {ArrayBuffer|undefined}
27
- */
28
- embeddedSoundFont = undefined;
29
-
30
- /**
31
- * The actual track data of the MIDI file, represented as an array of tracks.
32
- * Tracks are arrays of MIDIMessage objects.
33
- * @type {MIDIMessage[][]}
34
- */
35
- tracks = [];
36
-
37
- /**
38
- * If the MIDI file is a DLS RMIDI file.
39
- * @type {boolean}
40
- */
41
- isDLSRMIDI = false;
42
-
43
- /**
44
- * Copies a MIDI (tracks are shallowly copied!)
45
- * @param mid {BasicMIDI}
46
- * @returns {BasicMIDI}
47
- */
48
- static copyFrom(mid)
49
- {
50
- const m = new BasicMIDI();
51
- m._copyFromSequence(mid);
52
-
53
- m.isDLSRMIDI = mid.isDLSRMIDI;
54
- m.embeddedSoundFont = mid?.embeddedSoundFont ? mid.embeddedSoundFont : undefined; // Shallow copy
55
- m.tracks = mid.tracks.map(track => [...track]); // Shallow copy of each track array
56
- return m;
57
- }
58
-
59
- /**
60
- * Copies a MIDI with deep copy
61
- * @param mid {BasicMIDI}
62
- * @returns {BasicMIDI}
63
- */
64
- static copyFromDeep(mid)
65
- {
66
- const m = new BasicMIDI();
67
- m._copyFromSequence(mid);
68
- m.isDLSRMIDI = mid.isDLSRMIDI;
69
- m.embeddedSoundFont = mid.embeddedSoundFont ? mid.embeddedSoundFont.slice(0) : undefined; // Deep copy
70
- m.tracks = mid.tracks.map(track => track.map(event => new MIDIMessage(
71
- event.ticks,
72
- event.messageStatusByte,
73
- event.messageData
74
- ))); // Deep copy
75
- return m;
76
- }
77
-
78
- /**
79
- * Parses internal MIDI values
80
- * @protected
81
- */
82
- _parseInternal()
83
- {
84
- SpessaSynthGroup(
85
- "%cInterpreting MIDI events...",
86
- consoleColors.info
87
- );
88
- /**
89
- * For karaoke files, text events starting with @T are considered titles,
90
- * usually the first one is the title, and the latter is things such as "sequenced by" etc.
91
- * @type {boolean}
92
- */
93
- let karaokeHasTitle = false;
94
-
95
- this.keyRange = { max: 0, min: 127 };
96
-
97
- /**
98
- * Will be joined with "\n" to form the final string
99
- * @type {string[]}
100
- */
101
- let copyrightComponents = [];
102
- let copyrightDetected = false;
103
- if (typeof this.RMIDInfo["ICOP"] !== "undefined")
104
- {
105
- // if RMIDI has copyright info, don't try to detect one.
106
- copyrightDetected = true;
107
- }
108
-
109
-
110
- let nameDetected = false;
111
- if (typeof this.RMIDInfo["INAM"] !== "undefined")
112
- {
113
- // same as with copyright
114
- nameDetected = true;
115
- }
116
-
117
- // loop tracking
118
- let loopStart = null;
119
- let loopEnd = null;
120
-
121
- for (let i = 0; i < this.tracks.length; i++)
122
- {
123
- /**
124
- * @type {MIDIMessage[]}
125
- */
126
- const track = this.tracks[i];
127
- const usedChannels = new Set();
128
- let trackHasVoiceMessages = false;
129
-
130
- for (const e of track)
131
- {
132
- // check if it's a voice message
133
- if (e.messageStatusByte >= 0x80 && e.messageStatusByte < 0xF0)
134
- {
135
- trackHasVoiceMessages = true;
136
- // voice messages are 7-bit always
137
- for (let j = 0; j < e.messageData.length; j++)
138
- {
139
- e.messageData[j] = Math.min(127, e.messageData[j]);
140
- }
141
- // last voice event tick
142
- if (e.ticks > this.lastVoiceEventTick)
143
- {
144
- this.lastVoiceEventTick = e.ticks;
145
- }
146
-
147
- // interpret the voice message
148
- switch (e.messageStatusByte & 0xF0)
149
- {
150
- // cc change: loop points
151
- case messageTypes.controllerChange:
152
- switch (e.messageData[0])
153
- {
154
- case 2:
155
- case 116:
156
- loopStart = e.ticks;
157
- break;
158
-
159
- case 4:
160
- case 117:
161
- if (loopEnd === null)
162
- {
163
- loopEnd = e.ticks;
164
- }
165
- else
166
- {
167
- // this controller has occurred more than once;
168
- // this means
169
- // that it doesn't indicate the loop
170
- loopEnd = 0;
171
- }
172
- break;
173
-
174
- case 0:
175
- // check RMID
176
- if (this.isDLSRMIDI && e.messageData[1] !== 0 && e.messageData[1] !== 127)
177
- {
178
- SpessaSynthInfo(
179
- "%cDLS RMIDI with offset 1 detected!",
180
- consoleColors.recognized
181
- );
182
- this.bankOffset = 1;
183
- }
184
- }
185
- break;
186
-
187
- // note on: used notes tracking and key range
188
- case messageTypes.noteOn:
189
- usedChannels.add(e.messageStatusByte & 0x0F);
190
- const note = e.messageData[0];
191
- this.keyRange.min = Math.min(this.keyRange.min, note);
192
- this.keyRange.max = Math.max(this.keyRange.max, note);
193
- break;
194
- }
195
- }
196
- e.messageData.currentIndex = 0;
197
- const eventText = readBytesAsString(e.messageData, e.messageData.length);
198
- e.messageData.currentIndex = 0;
199
- // interpret the message
200
- switch (e.messageStatusByte)
201
- {
202
- case messageTypes.setTempo:
203
- // add the tempo change
204
- e.messageData.currentIndex = 0;
205
- this.tempoChanges.push({
206
- ticks: e.ticks,
207
- tempo: 60000000 / readBytesAsUintBigEndian(e.messageData, 3)
208
- });
209
- e.messageData.currentIndex = 0;
210
- break;
211
-
212
- case messageTypes.marker:
213
- // check for loop markers
214
- const text = eventText.trim().toLowerCase();
215
- switch (text)
216
- {
217
- default:
218
- break;
219
-
220
- case "start":
221
- case "loopstart":
222
- loopStart = e.ticks;
223
- break;
224
-
225
- case "loopend":
226
- loopEnd = e.ticks;
227
- }
228
- e.messageData.currentIndex = 0;
229
- break;
230
-
231
- case messageTypes.copyright:
232
- if (!copyrightDetected)
233
- {
234
- e.messageData.currentIndex = 0;
235
- copyrightComponents.push(readBytesAsString(
236
- e.messageData,
237
- e.messageData.length,
238
- false
239
- ));
240
- e.messageData.currentIndex = 0;
241
- }
242
- break;
243
-
244
- case messageTypes.lyric:
245
- // note here: .kar files sometimes just use...
246
- // lyrics instead of text because why not (of course)
247
- // perform the same check for @KMIDI KARAOKE FILE
248
- if (eventText.trim().startsWith("@KMIDI KARAOKE FILE"))
249
- {
250
- this.isKaraokeFile = true;
251
- SpessaSynthInfo("%cKaraoke MIDI detected!", consoleColors.recognized);
252
- }
253
-
254
- if (this.isKaraokeFile)
255
- {
256
- // replace the type of the message with text
257
- e.messageStatusByte = messageTypes.text;
258
- }
259
- else
260
- {
261
- // add lyrics like a regular midi file
262
- this.lyrics.push(e.messageData);
263
- this.lyricsTicks.push(e.ticks);
264
- break;
265
- }
266
-
267
- // kar: treat the same as text
268
- // fallthrough
269
- case messageTypes.text:
270
- // possibly Soft Karaoke MIDI file
271
- // it has a text event at the start of the file
272
- // "@KMIDI KARAOKE FILE"
273
- const checkedText = eventText.trim();
274
- if (checkedText.startsWith("@KMIDI KARAOKE FILE"))
275
- {
276
- this.isKaraokeFile = true;
277
-
278
- SpessaSynthInfo("%cKaraoke MIDI detected!", consoleColors.recognized);
279
- }
280
- else if (this.isKaraokeFile)
281
- {
282
- // check for @T (title)
283
- // or @A because it is a title too sometimes?
284
- // IDK it's strange
285
- if (checkedText.startsWith("@T") || checkedText.startsWith("@A"))
286
- {
287
- if (!karaokeHasTitle)
288
- {
289
- this.midiName = checkedText.substring(2).trim();
290
- karaokeHasTitle = true;
291
- nameDetected = true;
292
- // encode to rawMidiName
293
- this.rawMidiName = getStringBytes(this.midiName);
294
- }
295
- else
296
- {
297
- // append to copyright
298
- copyrightComponents.push(checkedText.substring(2).trim());
299
- }
300
- }
301
- else if (checkedText[0] !== "@")
302
- {
303
- // non @: the lyrics
304
- this.lyrics.push(sanitizeKarLyrics(e.messageData));
305
- this.lyricsTicks.push(e.ticks);
306
- }
307
- }
308
- break;
309
-
310
- case messageTypes.trackName:
311
- break;
312
- }
313
- }
314
- // add used channels
315
- this.usedChannelsOnTrack.push(usedChannels);
316
-
317
- // track name
318
- this.trackNames[i] = "";
319
- const trackName = track.find(e => e.messageStatusByte === messageTypes.trackName);
320
- if (trackName)
321
- {
322
- trackName.messageData.currentIndex = 0;
323
- const name = readBytesAsString(trackName.messageData, trackName.messageData.length);
324
- this.trackNames[i] = name;
325
- // If the track has no voice messages, its "track name" event (if it has any)
326
- // is some metadata.
327
- // Add it to copyright
328
- if (!trackHasVoiceMessages)
329
- {
330
- copyrightComponents.push(name);
331
- }
332
- }
333
- }
334
-
335
- // reverse the tempo changes
336
- this.tempoChanges.reverse();
337
-
338
- SpessaSynthInfo(
339
- `%cCorrecting loops, ports and detecting notes...`,
340
- consoleColors.info
341
- );
342
-
343
- const firstNoteOns = [];
344
- for (const t of this.tracks)
345
- {
346
- const firstNoteOn = t.find(e => (e.messageStatusByte & 0xF0) === messageTypes.noteOn);
347
- if (firstNoteOn)
348
- {
349
- firstNoteOns.push(firstNoteOn.ticks);
350
- }
351
- }
352
- this.firstNoteOn = Math.min(...firstNoteOns);
353
-
354
- SpessaSynthInfo(
355
- `%cFirst note-on detected at: %c${this.firstNoteOn}%c ticks!`,
356
- consoleColors.info,
357
- consoleColors.recognized,
358
- consoleColors.info
359
- );
360
-
361
-
362
- if (loopStart !== null && loopEnd === null)
363
- {
364
- // not a loop
365
- loopStart = this.firstNoteOn;
366
- loopEnd = this.lastVoiceEventTick;
367
- }
368
- else
369
- {
370
- if (loopStart === null)
371
- {
372
- loopStart = this.firstNoteOn;
373
- }
374
-
375
- if (loopEnd === null || loopEnd === 0)
376
- {
377
- loopEnd = this.lastVoiceEventTick;
378
- }
379
- }
380
-
381
- /**
382
- *
383
- * @type {{start: number, end: number}}
384
- */
385
- this.loop = { start: loopStart, end: loopEnd };
386
-
387
- SpessaSynthInfo(
388
- `%cLoop points: start: %c${this.loop.start}%c end: %c${this.loop.end}`,
389
- consoleColors.info,
390
- consoleColors.recognized,
391
- consoleColors.info,
392
- consoleColors.recognized
393
- );
394
-
395
- // determine ports
396
- let portOffset = 0;
397
- this.midiPorts = [];
398
- this.midiPortChannelOffsets = [];
399
- for (let trackNum = 0; trackNum < this.tracks.length; trackNum++)
400
- {
401
- this.midiPorts.push(-1);
402
- if (this.usedChannelsOnTrack[trackNum].size === 0)
403
- {
404
- continue;
405
- }
406
- for (const e of this.tracks[trackNum])
407
- {
408
- if (e.messageStatusByte !== messageTypes.midiPort)
409
- {
410
- continue;
411
- }
412
- const port = e.messageData[0];
413
- this.midiPorts[trackNum] = port;
414
- if (this.midiPortChannelOffsets[port] === undefined)
415
- {
416
- this.midiPortChannelOffsets[port] = portOffset;
417
- portOffset += 16;
418
- }
419
- }
420
- }
421
-
422
- // fix empty port channel offsets (do a copy to turn empty slots into undefined so the map goes over them)
423
- this.midiPortChannelOffsets = [...this.midiPortChannelOffsets].map(o => o ?? 0);
424
-
425
- // fix midi ports:
426
- // midi tracks without ports will have a value of -1
427
- // if all ports have a value of -1, set it to 0,
428
- // otherwise take the first midi port and replace all -1 with it,
429
- // why would we do this?
430
- // some midis (for some reason) specify all channels to port 1 or else,
431
- // but leave the conductor track with no port pref.
432
- // this spessasynth to reserve the first 16 channels for the conductor track
433
- // (which doesn't play anything) and use the additional 16 for the actual ports.
434
- let defaultPort = Infinity;
435
- for (let port of this.midiPorts)
436
- {
437
- if (port !== -1)
438
- {
439
- if (defaultPort > port)
440
- {
441
- defaultPort = port;
442
- }
443
- }
444
- }
445
- if (defaultPort === Infinity)
446
- {
447
- defaultPort = 0;
448
- }
449
- this.midiPorts = this.midiPorts.map(port => port === -1 || port === undefined ? defaultPort : port);
450
- // add fake port if empty
451
- if (this.midiPortChannelOffsets.length === 0)
452
- {
453
- this.midiPortChannelOffsets = [0];
454
- }
455
- if (this.midiPortChannelOffsets.length < 2)
456
- {
457
- SpessaSynthInfo(`%cNo additional MIDI Ports detected.`, consoleColors.info);
458
- }
459
- else
460
- {
461
- this.isMultiPort = true;
462
- SpessaSynthInfo(`%cMIDI Ports detected!`, consoleColors.recognized);
463
- }
464
-
465
- // midi name
466
- if (!nameDetected)
467
- {
468
- if (this.tracks.length > 1)
469
- {
470
- // if more than 1 track and the first track has no notes,
471
- // just find the first trackName in the first track.
472
- if (
473
- this.tracks[0].find(
474
- message => message.messageStatusByte >= messageTypes.noteOn
475
- &&
476
- message.messageStatusByte < messageTypes.polyPressure
477
- ) === undefined
478
- )
479
- {
480
-
481
- let name = this.tracks[0].find(message => message.messageStatusByte === messageTypes.trackName);
482
- if (name)
483
- {
484
- this.rawMidiName = name.messageData;
485
- name.messageData.currentIndex = 0;
486
- this.midiName = readBytesAsString(name.messageData, name.messageData.length, false);
487
- }
488
- }
489
- }
490
- else
491
- {
492
- // if only 1 track, find the first "track name" event
493
- let name = this.tracks[0].find(message => message.messageStatusByte === messageTypes.trackName);
494
- if (name)
495
- {
496
- this.rawMidiName = name.messageData;
497
- name.messageData.currentIndex = 0;
498
- this.midiName = readBytesAsString(name.messageData, name.messageData.length, false);
499
- }
500
- }
501
- }
502
-
503
- if (!copyrightDetected)
504
- {
505
- this.copyright = copyrightComponents
506
- // trim and group newlines into one
507
- .map(c => c.trim().replace(/(\r?\n)+/g, "\n"))
508
- // remove empty strings
509
- .filter(c => c.length > 0)
510
- // join with newlines
511
- .join("\n") || "";
512
- }
513
-
514
- this.midiName = this.midiName.trim();
515
- this.midiNameUsesFileName = false;
516
- // if midiName is "", use the file name
517
- if (this.midiName.length === 0)
518
- {
519
- SpessaSynthInfo(
520
- `%cNo name detected. Using the alt name!`,
521
- consoleColors.info
522
- );
523
- this.midiName = formatTitle(this.fileName);
524
- this.midiNameUsesFileName = true;
525
- // encode it too
526
- this.rawMidiName = new Uint8Array(this.midiName.length);
527
- for (let i = 0; i < this.midiName.length; i++)
528
- {
529
- this.rawMidiName[i] = this.midiName.charCodeAt(i);
530
- }
531
- }
532
- else
533
- {
534
- SpessaSynthInfo(
535
- `%cMIDI Name detected! %c"${this.midiName}"`,
536
- consoleColors.info,
537
- consoleColors.recognized
538
- );
539
- }
540
-
541
- // if the first event is not at 0 ticks, add a track name
542
- // https://github.com/spessasus/SpessaSynth/issues/145
543
- if (!this.tracks.some(t => t[0].ticks === 0))
544
- {
545
- const track = this.tracks[0];
546
- // can copy
547
- track.unshift(new MIDIMessage(
548
- 0,
549
- messageTypes.trackName,
550
- new IndexedByteArray(this.rawMidiName.buffer)
551
- ));
552
- }
553
-
554
-
555
- /**
556
- * The total playback time, in seconds
557
- * @type {number}
558
- */
559
- this.duration = this.MIDIticksToSeconds(this.lastVoiceEventTick);
560
-
561
- SpessaSynthInfo("%cSuccess!", consoleColors.recognized);
562
- SpessaSynthGroupEnd();
563
- }
564
-
565
- /**
566
- * Updates all internal values
567
- */
568
- flush()
569
- {
570
-
571
- for (const t of this.tracks)
572
- {
573
- // sort the track by ticks
574
- t.sort((e1, e2) => e1.ticks - e2.ticks);
575
- }
576
- this._parseInternal();
577
- }
578
- }
579
-
580
- BasicMIDI.prototype.writeMIDI = writeMIDI;
581
- BasicMIDI.prototype.modifyMIDI = modifyMIDI;
582
- BasicMIDI.prototype.applySnapshotToMIDI = applySnapshotToMIDI;
583
- BasicMIDI.prototype.writeRMIDI = writeRMIDI;
584
- BasicMIDI.prototype.getUsedProgramsAndKeys = getUsedProgramsAndKeys;
585
- BasicMIDI.prototype.getNoteTimes = getNoteTimes;
586
-
587
- export { BasicMIDI };