spessasynth_lib 3.25.23 → 3.26.1

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 (162) hide show
  1. package/README.md +29 -114
  2. package/external_midi/midi_handler.js +14 -14
  3. package/external_midi/web_midi_link.js +3 -3
  4. package/index.js +33 -33
  5. package/package.json +19 -6
  6. package/{midi → sequencer}/midi_data.js +1 -1
  7. package/sequencer/{worklet_wrapper/sequencer.js → sequencer.js} +13 -12
  8. package/sequencer/{worklet_wrapper/sequencer_message.js → sequencer_message.js} +1 -1
  9. package/synthetizer/README.md +29 -1
  10. package/synthetizer/audio_effects/reverb_as_binary.js +2 -2
  11. package/synthetizer/{worklet_wrapper/key_modifier_manager.js → key_modifier_manager.js} +11 -2
  12. package/synthetizer/{worklet_wrapper/synth_event_handler.js → synth_event_handler.js} +16 -13
  13. package/synthetizer/{worklet_wrapper/synth_soundfont_manager.js → synth_soundfont_manager.js} +8 -5
  14. package/synthetizer/{worklet_wrapper/synthetizer.js → synthetizer.js} +29 -23
  15. package/synthetizer/{audio_engine/message_protocol/worklet_message.js → worklet_message.js} +15 -2
  16. package/synthetizer/{worklet_wrapper/worklet_processor.js → worklet_processor.js} +214 -31
  17. package/synthetizer/worklet_processor.min.js +13 -12
  18. package/synthetizer/worklet_processor.min.js.map +7 -0
  19. package/synthetizer/{worklet_wrapper/worklet_url.js → worklet_url.js} +2 -0
  20. package/utils/buffer_to_wav.js +7 -165
  21. package/utils/other.js +3 -84
  22. package/externals/fflate/LICENSE +0 -21
  23. package/externals/fflate/fflate.min.js +0 -1
  24. package/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +0 -12
  25. package/externals/stbvorbis_sync/LICENSE +0 -202
  26. package/externals/stbvorbis_sync/NOTICE +0 -6
  27. package/externals/stbvorbis_sync/stbvorbis_sync.min.js +0 -1
  28. package/midi/README.md +0 -32
  29. package/midi/basic_midi.js +0 -565
  30. package/midi/midi_builder.js +0 -202
  31. package/midi/midi_loader.js +0 -324
  32. package/midi/midi_message.js +0 -254
  33. package/midi/midi_sequence.js +0 -225
  34. package/midi/midi_tools/get_note_times.js +0 -154
  35. package/midi/midi_tools/midi_editor.js +0 -611
  36. package/midi/midi_tools/midi_writer.js +0 -99
  37. package/midi/midi_tools/rmidi_writer.js +0 -567
  38. package/midi/midi_tools/used_keys_loaded.js +0 -238
  39. package/midi/xmf_loader.js +0 -454
  40. package/sequencer/sequencer_engine/events.js +0 -104
  41. package/sequencer/sequencer_engine/play.js +0 -353
  42. package/sequencer/sequencer_engine/process_event.js +0 -169
  43. package/sequencer/sequencer_engine/process_tick.js +0 -106
  44. package/sequencer/sequencer_engine/sequencer_engine.js +0 -335
  45. package/sequencer/sequencer_engine/song_control.js +0 -229
  46. package/soundfont/README.md +0 -13
  47. package/soundfont/basic_soundfont/basic_instrument.js +0 -77
  48. package/soundfont/basic_soundfont/basic_preset.js +0 -336
  49. package/soundfont/basic_soundfont/basic_sample.js +0 -197
  50. package/soundfont/basic_soundfont/basic_soundfont.js +0 -565
  51. package/soundfont/basic_soundfont/basic_zone.js +0 -64
  52. package/soundfont/basic_soundfont/basic_zones.js +0 -43
  53. package/soundfont/basic_soundfont/generator.js +0 -220
  54. package/soundfont/basic_soundfont/modulator.js +0 -378
  55. package/soundfont/basic_soundfont/riff_chunk.js +0 -149
  56. package/soundfont/basic_soundfont/write_dls/art2.js +0 -173
  57. package/soundfont/basic_soundfont/write_dls/articulator.js +0 -49
  58. package/soundfont/basic_soundfont/write_dls/combine_zones.js +0 -400
  59. package/soundfont/basic_soundfont/write_dls/ins.js +0 -103
  60. package/soundfont/basic_soundfont/write_dls/lins.js +0 -18
  61. package/soundfont/basic_soundfont/write_dls/modulator_converter.js +0 -330
  62. package/soundfont/basic_soundfont/write_dls/rgn2.js +0 -121
  63. package/soundfont/basic_soundfont/write_dls/wave.js +0 -94
  64. package/soundfont/basic_soundfont/write_dls/write_dls.js +0 -119
  65. package/soundfont/basic_soundfont/write_dls/wsmp.js +0 -78
  66. package/soundfont/basic_soundfont/write_dls/wvpl.js +0 -32
  67. package/soundfont/basic_soundfont/write_sf2/ibag.js +0 -39
  68. package/soundfont/basic_soundfont/write_sf2/igen.js +0 -80
  69. package/soundfont/basic_soundfont/write_sf2/imod.js +0 -46
  70. package/soundfont/basic_soundfont/write_sf2/inst.js +0 -34
  71. package/soundfont/basic_soundfont/write_sf2/pbag.js +0 -39
  72. package/soundfont/basic_soundfont/write_sf2/pgen.js +0 -82
  73. package/soundfont/basic_soundfont/write_sf2/phdr.js +0 -42
  74. package/soundfont/basic_soundfont/write_sf2/pmod.js +0 -46
  75. package/soundfont/basic_soundfont/write_sf2/sdta.js +0 -80
  76. package/soundfont/basic_soundfont/write_sf2/shdr.js +0 -55
  77. package/soundfont/basic_soundfont/write_sf2/write.js +0 -222
  78. package/soundfont/dls/articulator_converter.js +0 -396
  79. package/soundfont/dls/dls_destinations.js +0 -38
  80. package/soundfont/dls/dls_preset.js +0 -44
  81. package/soundfont/dls/dls_sample.js +0 -75
  82. package/soundfont/dls/dls_soundfont.js +0 -186
  83. package/soundfont/dls/dls_sources.js +0 -62
  84. package/soundfont/dls/dls_zone.js +0 -95
  85. package/soundfont/dls/read_articulation.js +0 -299
  86. package/soundfont/dls/read_instrument.js +0 -121
  87. package/soundfont/dls/read_instrument_list.js +0 -17
  88. package/soundfont/dls/read_lart.js +0 -35
  89. package/soundfont/dls/read_region.js +0 -152
  90. package/soundfont/dls/read_samples.js +0 -270
  91. package/soundfont/load_soundfont.js +0 -21
  92. package/soundfont/read_sf2/generators.js +0 -46
  93. package/soundfont/read_sf2/instruments.js +0 -66
  94. package/soundfont/read_sf2/modulators.js +0 -36
  95. package/soundfont/read_sf2/presets.js +0 -80
  96. package/soundfont/read_sf2/samples.js +0 -304
  97. package/soundfont/read_sf2/soundfont.js +0 -305
  98. package/soundfont/read_sf2/zones.js +0 -263
  99. package/synthetizer/audio_engine/README.md +0 -25
  100. package/synthetizer/audio_engine/engine_components/compute_modulator.js +0 -266
  101. package/synthetizer/audio_engine/engine_components/controller_tables.js +0 -88
  102. package/synthetizer/audio_engine/engine_components/key_modifier_manager.js +0 -149
  103. package/synthetizer/audio_engine/engine_components/lfo.js +0 -26
  104. package/synthetizer/audio_engine/engine_components/lowpass_filter.js +0 -282
  105. package/synthetizer/audio_engine/engine_components/midi_audio_channel.js +0 -471
  106. package/synthetizer/audio_engine/engine_components/modulation_envelope.js +0 -181
  107. package/synthetizer/audio_engine/engine_components/modulator_curves.js +0 -89
  108. package/synthetizer/audio_engine/engine_components/soundfont_manager.js +0 -228
  109. package/synthetizer/audio_engine/engine_components/stereo_panner.js +0 -120
  110. package/synthetizer/audio_engine/engine_components/unit_converter.js +0 -73
  111. package/synthetizer/audio_engine/engine_components/voice.js +0 -519
  112. package/synthetizer/audio_engine/engine_components/volume_envelope.js +0 -401
  113. package/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +0 -263
  114. package/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +0 -132
  115. package/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +0 -48
  116. package/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -241
  117. package/synthetizer/audio_engine/engine_methods/create_midi_channel.js +0 -27
  118. package/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +0 -253
  119. package/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +0 -66
  120. package/synthetizer/audio_engine/engine_methods/mute_channel.js +0 -17
  121. package/synthetizer/audio_engine/engine_methods/note_on.js +0 -175
  122. package/synthetizer/audio_engine/engine_methods/portamento_time.js +0 -92
  123. package/synthetizer/audio_engine/engine_methods/program_change.js +0 -61
  124. package/synthetizer/audio_engine/engine_methods/render_voice.js +0 -196
  125. package/synthetizer/audio_engine/engine_methods/soundfont_management/clear_sound_font.js +0 -30
  126. package/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +0 -22
  127. package/synthetizer/audio_engine/engine_methods/soundfont_management/reload_sound_font.js +0 -40
  128. package/synthetizer/audio_engine/engine_methods/soundfont_management/send_preset_list.js +0 -34
  129. package/synthetizer/audio_engine/engine_methods/soundfont_management/set_embedded_sound_font.js +0 -21
  130. package/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +0 -20
  131. package/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +0 -55
  132. package/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +0 -16
  133. package/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +0 -30
  134. package/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +0 -63
  135. package/synthetizer/audio_engine/engine_methods/system_exclusive.js +0 -776
  136. package/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +0 -24
  137. package/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +0 -33
  138. package/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +0 -31
  139. package/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +0 -15
  140. package/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +0 -27
  141. package/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +0 -19
  142. package/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +0 -27
  143. package/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +0 -15
  144. package/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +0 -34
  145. package/synthetizer/audio_engine/main_processor.js +0 -765
  146. package/synthetizer/audio_engine/message_protocol/README.md +0 -13
  147. package/synthetizer/audio_engine/message_protocol/message_sending.js +0 -22
  148. package/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +0 -14
  149. package/synthetizer/audio_engine/snapshot/channel_snapshot.js +0 -175
  150. package/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +0 -122
  151. package/synthetizer/synth_constants.js +0 -20
  152. package/utils/README.md +0 -5
  153. package/utils/byte_functions/big_endian.js +0 -32
  154. package/utils/byte_functions/little_endian.js +0 -77
  155. package/utils/byte_functions/string.js +0 -107
  156. package/utils/byte_functions/variable_length_quantity.js +0 -42
  157. package/utils/indexed_array.js +0 -52
  158. package/utils/loggin.js +0 -79
  159. package/utils/sysex_detector.js +0 -58
  160. package/utils/xg_hacks.js +0 -193
  161. /package/sequencer/{worklet_wrapper/default_sequencer_options.js → default_sequencer_options.js} +0 -0
  162. /package/synthetizer/{worklet_wrapper/sfman_message.js → sfman_message.js} +0 -0
@@ -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 };