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