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,202 +0,0 @@
1
- import { BasicMIDI } from "./basic_midi.js";
2
- import { messageTypes, MIDIMessage } from "./midi_message.js";
3
- import { IndexedByteArray } from "../utils/indexed_array.js";
4
- import { SpessaSynthWarn } from "../utils/loggin.js";
5
-
6
- /**
7
- * A class that helps to build a MIDI file from scratch.
8
- */
9
- export class MIDIBuilder extends BasicMIDI
10
- {
11
- /**
12
- * @param name {string} The MIDI's name
13
- * @param timeDivision {number} the file's time division
14
- * @param initialTempo {number} the file's initial tempo
15
- */
16
- constructor(name, timeDivision = 480, initialTempo = 120)
17
- {
18
- super();
19
- this.timeDivision = timeDivision;
20
- this.midiName = name;
21
- this.encoder = new TextEncoder();
22
- this.rawMidiName = this.encoder.encode(name);
23
-
24
- // create the first track with the file name
25
- this.addNewTrack(name);
26
- this.addSetTempo(0, initialTempo);
27
- }
28
-
29
- /**
30
- * Adds a new Set Tempo event
31
- * @param ticks {number} the tick number of the event
32
- * @param tempo {number} the tempo in beats per minute (BPM)
33
- */
34
- addSetTempo(ticks, tempo)
35
- {
36
- const array = new IndexedByteArray(3);
37
-
38
- tempo = 60000000 / tempo;
39
-
40
- // Extract each byte in big-endian order
41
- array[0] = (tempo >> 16) & 0xFF;
42
- array[1] = (tempo >> 8) & 0xFF;
43
- array[2] = tempo & 0xFF;
44
-
45
- this.addEvent(ticks, 0, messageTypes.setTempo, array);
46
- }
47
-
48
- /**
49
- * Adds a new MIDI track
50
- * @param name {string} the new track's name
51
- * @param port {number} the new track's port
52
- */
53
- addNewTrack(name, port = 0)
54
- {
55
- this.tracksAmount++;
56
- if (this.tracksAmount > 1)
57
- {
58
- this.format = 1;
59
- }
60
- this.tracks.push([]);
61
- this.tracks[this.tracksAmount - 1].push(
62
- new MIDIMessage(0, messageTypes.endOfTrack, new IndexedByteArray(0))
63
- );
64
- this.addEvent(0, this.tracksAmount - 1, messageTypes.trackName, this.encoder.encode(name));
65
- this.addEvent(0, this.tracksAmount - 1, messageTypes.midiPort, [port]);
66
- }
67
-
68
- /**
69
- * Adds a new MIDI Event
70
- * @param ticks {number} the tick time of the event
71
- * @param track {number} the track number to use
72
- * @param event {number} the MIDI event number
73
- * @param eventData {Uint8Array|Iterable<number>} the raw event data
74
- */
75
- addEvent(ticks, track, event, eventData)
76
- {
77
- if (!this.tracks[track])
78
- {
79
- throw new Error(`Track ${track} does not exist. Add it via addTrack method.`);
80
- }
81
- if (event === messageTypes.endOfTrack)
82
- {
83
- SpessaSynthWarn(
84
- "The EndOfTrack is added automatically and does not influence the duration. Consider adding a voice event instead.");
85
- return;
86
- }
87
- // remove the end of track
88
- this.tracks[track].pop();
89
- this.tracks[track].push(new MIDIMessage(
90
- ticks,
91
- event,
92
- new IndexedByteArray(eventData)
93
- ));
94
- // add the end of track
95
- this.tracks[track].push(new MIDIMessage(
96
- ticks,
97
- messageTypes.endOfTrack,
98
- new IndexedByteArray(0)
99
- ));
100
- }
101
-
102
- /**
103
- * Adds a new Note On event
104
- * @param ticks {number} the tick time of the event
105
- * @param track {number} the track number to use
106
- * @param channel {number} the channel to use
107
- * @param midiNote {number} the midi note of the keypress
108
- * @param velocity {number} the velocity of the keypress
109
- */
110
- addNoteOn(ticks, track, channel, midiNote, velocity)
111
- {
112
- channel %= 16;
113
- midiNote %= 128;
114
- velocity %= 128;
115
- this.addEvent(
116
- ticks,
117
- track,
118
- messageTypes.noteOn | channel,
119
- [midiNote, velocity]
120
- );
121
- }
122
-
123
- /**
124
- * Adds a new Note Off event
125
- * @param ticks {number} the tick time of the event
126
- * @param track {number} the track number to use
127
- * @param channel {number} the channel to use
128
- * @param midiNote {number} the midi note of the key release
129
- */
130
- addNoteOff(ticks, track, channel, midiNote)
131
- {
132
- channel %= 16;
133
- midiNote %= 128;
134
- this.addEvent(
135
- ticks,
136
- track,
137
- messageTypes.noteOff | channel,
138
- [midiNote, 64]
139
- );
140
- }
141
-
142
- /**
143
- * Adds a new Program Change event
144
- * @param ticks {number} the tick time of the event
145
- * @param track {number} the track number to use
146
- * @param channel {number} the channel to use
147
- * @param programNumber {number} the MIDI program to use
148
- */
149
- addProgramChange(ticks, track, channel, programNumber)
150
- {
151
- channel %= 16;
152
- programNumber %= 128;
153
- this.addEvent(
154
- ticks,
155
- track,
156
- messageTypes.programChange | channel,
157
- [programNumber]
158
- );
159
- }
160
-
161
- /**
162
- * Adds a new Controller Change event
163
- * @param ticks {number} the tick time of the event
164
- * @param track {number} the track number to use
165
- * @param channel {number} the channel to use
166
- * @param controllerNumber {number} the MIDI CC to use
167
- * @param controllerValue {number} the new CC value
168
- */
169
- addControllerChange(ticks, track, channel, controllerNumber, controllerValue)
170
- {
171
- channel %= 16;
172
- controllerNumber %= 128;
173
- controllerValue %= 128;
174
- this.addEvent(
175
- ticks,
176
- track,
177
- messageTypes.controllerChange | channel,
178
- [controllerNumber, controllerValue]
179
- );
180
- }
181
-
182
- /**
183
- * Adds a new Pitch Wheel event
184
- * @param ticks {number} the tick time of the event
185
- * @param track {number} the track to use
186
- * @param channel {number} the channel to use
187
- * @param MSB {number} SECOND byte of the MIDI pitchWheel message
188
- * @param LSB {number} FIRST byte of the MIDI pitchWheel message
189
- */
190
- addPitchWheel(ticks, track, channel, MSB, LSB)
191
- {
192
- channel %= 16;
193
- MSB %= 128;
194
- LSB %= 128;
195
- this.addEvent(
196
- ticks,
197
- track,
198
- messageTypes.pitchBend | channel,
199
- [LSB, MSB]
200
- );
201
- }
202
- }
@@ -1,324 +0,0 @@
1
- import { dataBytesAmount, getChannel, MIDIMessage } from "./midi_message.js";
2
- import { IndexedByteArray } from "../utils/indexed_array.js";
3
- import { consoleColors } from "../utils/other.js";
4
- import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo, SpessaSynthWarn } from "../utils/loggin.js";
5
- import { readRIFFChunk } from "../soundfont/basic_soundfont/riff_chunk.js";
6
- import { readVariableLengthQuantity } from "../utils/byte_functions/variable_length_quantity.js";
7
- import { readBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
8
- import { readBytesAsString } from "../utils/byte_functions/string.js";
9
- import { readLittleEndian } from "../utils/byte_functions/little_endian.js";
10
- import { RMIDINFOChunks } from "./midi_tools/rmidi_writer.js";
11
- import { BasicMIDI } from "./basic_midi.js";
12
- import { loadXMF } from "./xmf_loader.js";
13
-
14
- /**
15
- * midi_loader.js
16
- * purpose:
17
- * parses a midi file for the seqyencer,
18
- * including things like marker or CC 2/4 loop detection, copyright detection, etc.
19
- */
20
-
21
- /**
22
- * The MIDI class is a MIDI file parser that reads a MIDI file and extracts all the necessary information from it.
23
- * Supported formats are .mid and .rmi files.
24
- */
25
- class MIDI extends BasicMIDI
26
- {
27
- /**
28
- * Parses a given midi file
29
- * @param arrayBuffer {ArrayBuffer}
30
- * @param fileName {string} optional, replaces the decoded title if empty
31
- */
32
- constructor(arrayBuffer, fileName = "")
33
- {
34
- super();
35
- SpessaSynthGroupCollapsed(`%cParsing MIDI File...`, consoleColors.info);
36
- this.fileName = fileName;
37
- const binaryData = new IndexedByteArray(arrayBuffer);
38
- let fileByteArray;
39
-
40
- // check for rmid
41
- const initialString = readBytesAsString(binaryData, 4);
42
- binaryData.currentIndex -= 4;
43
- if (initialString === "RIFF")
44
- {
45
- // possibly an RMID file (https://github.com/spessasus/sf2-rmidi-specification#readme)
46
- // skip size
47
- binaryData.currentIndex += 8;
48
- const rmid = readBytesAsString(binaryData, 4, undefined, false);
49
- if (rmid !== "RMID")
50
- {
51
- SpessaSynthGroupEnd();
52
- throw new SyntaxError(`Invalid RMIDI Header! Expected "RMID", got "${rmid}"`);
53
- }
54
- const riff = readRIFFChunk(binaryData);
55
- if (riff.header !== "data")
56
- {
57
- SpessaSynthGroupEnd();
58
- throw new SyntaxError(`Invalid RMIDI Chunk header! Expected "data", got "${rmid}"`);
59
- }
60
- // this is a rmid, load the midi into an array for parsing
61
- fileByteArray = riff.chunkData;
62
-
63
- // keep loading chunks until we get the "SFBK" header
64
- while (binaryData.currentIndex <= binaryData.length)
65
- {
66
- const startIndex = binaryData.currentIndex;
67
- const currentChunk = readRIFFChunk(binaryData, true);
68
- if (currentChunk.header === "RIFF")
69
- {
70
- const type = readBytesAsString(currentChunk.chunkData, 4).toLowerCase();
71
- if (type === "sfbk" || type === "sfpk" || type === "dls ")
72
- {
73
- SpessaSynthInfo("%cFound embedded soundfont!", consoleColors.recognized);
74
- this.embeddedSoundFont = binaryData.slice(startIndex, startIndex + currentChunk.size).buffer;
75
- }
76
- else
77
- {
78
- SpessaSynthWarn(`Unknown RIFF chunk: "${type}"`);
79
- }
80
- if (type === "dls ")
81
- {
82
- // Assume bank offset of 0 by default. If we find any bank selects, then the offset is 1.
83
- this.isDLSRMIDI = true;
84
- }
85
- }
86
- else if (currentChunk.header === "LIST")
87
- {
88
- const type = readBytesAsString(currentChunk.chunkData, 4);
89
- if (type === "INFO")
90
- {
91
- SpessaSynthInfo("%cFound RMIDI INFO chunk!", consoleColors.recognized);
92
- this.RMIDInfo = {};
93
- while (currentChunk.chunkData.currentIndex <= currentChunk.size)
94
- {
95
- const infoChunk = readRIFFChunk(currentChunk.chunkData, true);
96
- this.RMIDInfo[infoChunk.header] = infoChunk.chunkData;
97
- }
98
- if (this.RMIDInfo["ICOP"])
99
- {
100
- // special case, overwrites the copyright components array
101
- this.copyright = readBytesAsString(
102
- this.RMIDInfo["ICOP"],
103
- this.RMIDInfo["ICOP"].length,
104
- undefined,
105
- false
106
- ).replaceAll("\n", " ");
107
- }
108
- if (this.RMIDInfo["INAM"])
109
- {
110
- this.rawMidiName = this.RMIDInfo[RMIDINFOChunks.name];
111
- // noinspection JSCheckFunctionSignatures
112
- this.midiName = readBytesAsString(
113
- this.rawMidiName,
114
- this.rawMidiName.length,
115
- undefined,
116
- false
117
- ).replaceAll("\n", " ");
118
- }
119
- // these can be used interchangeably
120
- if (this.RMIDInfo["IALB"] && !this.RMIDInfo["IPRD"])
121
- {
122
- this.RMIDInfo["IPRD"] = this.RMIDInfo["IALB"];
123
- }
124
- if (this.RMIDInfo["IPRD"] && !this.RMIDInfo["IALB"])
125
- {
126
- this.RMIDInfo["IALB"] = this.RMIDInfo["IPRD"];
127
- }
128
- this.bankOffset = 1; // defaults to 1
129
- if (this.RMIDInfo[RMIDINFOChunks.bankOffset])
130
- {
131
- this.bankOffset = readLittleEndian(this.RMIDInfo[RMIDINFOChunks.bankOffset], 2);
132
- }
133
- }
134
- }
135
- }
136
-
137
- if (this.isDLSRMIDI)
138
- {
139
- // Assume bank offset of 0 by default. If we find any bank selects, then the offset is 1.
140
- this.bankOffset = 0;
141
- }
142
-
143
- // if no embedded bank, assume 0
144
- if (this.embeddedSoundFont === undefined)
145
- {
146
- this.bankOffset = 0;
147
- }
148
- }
149
- else if (initialString === "XMF_")
150
- {
151
- // XMF file
152
- fileByteArray = loadXMF(this, binaryData);
153
- }
154
- else
155
- {
156
- fileByteArray = binaryData;
157
- }
158
- const headerChunk = this._readMIDIChunk(fileByteArray);
159
- if (headerChunk.type !== "MThd")
160
- {
161
- SpessaSynthGroupEnd();
162
- throw new SyntaxError(`Invalid MIDI Header! Expected "MThd", got "${headerChunk.type}"`);
163
- }
164
-
165
- if (headerChunk.size !== 6)
166
- {
167
- SpessaSynthGroupEnd();
168
- throw new RangeError(`Invalid MIDI header chunk size! Expected 6, got ${headerChunk.size}`);
169
- }
170
-
171
- // format
172
- this.format = readBytesAsUintBigEndian(headerChunk.data, 2);
173
- // tracks count
174
- this.tracksAmount = readBytesAsUintBigEndian(headerChunk.data, 2);
175
- // time division
176
- this.timeDivision = readBytesAsUintBigEndian(headerChunk.data, 2);
177
- // read all the tracks
178
- for (let i = 0; i < this.tracksAmount; i++)
179
- {
180
- /**
181
- * @type {MIDIMessage[]}
182
- */
183
- const track = [];
184
- const trackChunk = this._readMIDIChunk(fileByteArray);
185
-
186
- if (trackChunk.type !== "MTrk")
187
- {
188
- SpessaSynthGroupEnd();
189
- throw new SyntaxError(`Invalid track header! Expected "MTrk" got "${trackChunk.type}"`);
190
- }
191
-
192
-
193
- /**
194
- * MIDI running byte
195
- * @type {number}
196
- */
197
- let runningByte = undefined;
198
-
199
- let totalTicks = 0;
200
- // format 2 plays sequentially
201
- if (this.format === 2 && i > 0)
202
- {
203
- totalTicks += this.tracks[i - 1][this.tracks[i - 1].length - 1].ticks;
204
- }
205
- // loop until we reach the end of track
206
- while (trackChunk.data.currentIndex < trackChunk.size)
207
- {
208
- totalTicks += readVariableLengthQuantity(trackChunk.data);
209
-
210
- // check if the status byte is valid (IE. larger than 127)
211
- const statusByteCheck = trackChunk.data[trackChunk.data.currentIndex];
212
-
213
- let statusByte;
214
- // if we have a running byte and the status byte isn't valid
215
- if (runningByte !== undefined && statusByteCheck < 0x80)
216
- {
217
- statusByte = runningByte;
218
- }
219
- else
220
- { // noinspection PointlessBooleanExpressionJS
221
- if (runningByte === undefined && statusByteCheck < 0x80)
222
- {
223
- // if we don't have a running byte and the status byte isn't valid, it's an error.
224
- SpessaSynthGroupEnd();
225
- throw new SyntaxError(`Unexpected byte with no running byte. (${statusByteCheck})`);
226
- }
227
- else
228
- {
229
- // if the status byte is valid, use that
230
- statusByte = trackChunk.data[trackChunk.data.currentIndex++];
231
- }
232
- }
233
- const statusByteChannel = getChannel(statusByte);
234
-
235
- let eventDataLength;
236
-
237
- // determine the message's length;
238
- switch (statusByteChannel)
239
- {
240
- case -1:
241
- // system common/realtime (no length)
242
- eventDataLength = 0;
243
- break;
244
-
245
- case -2:
246
- // meta (the next is the actual status byte)
247
- statusByte = trackChunk.data[trackChunk.data.currentIndex++];
248
- eventDataLength = readVariableLengthQuantity(trackChunk.data);
249
- break;
250
-
251
- case -3:
252
- // sysex
253
- eventDataLength = readVariableLengthQuantity(trackChunk.data);
254
- break;
255
-
256
- default:
257
- // voice message
258
- // gets the midi message length
259
- eventDataLength = dataBytesAmount[statusByte >> 4];
260
- // save the status byte
261
- runningByte = statusByte;
262
- break;
263
- }
264
-
265
- // put the event data into the array
266
- const eventData = new IndexedByteArray(eventDataLength);
267
- eventData.set(trackChunk.data.slice(
268
- trackChunk.data.currentIndex,
269
- trackChunk.data.currentIndex + eventDataLength
270
- ), 0);
271
- const event = new MIDIMessage(totalTicks, statusByte, eventData);
272
- track.push(event);
273
- // advance the track chunk
274
- trackChunk.data.currentIndex += eventDataLength;
275
- }
276
- this.tracks.push(track);
277
-
278
- SpessaSynthInfo(
279
- `%cParsed %c${this.tracks.length}%c / %c${this.tracksAmount}`,
280
- consoleColors.info,
281
- consoleColors.value,
282
- consoleColors.info,
283
- consoleColors.value
284
- );
285
- }
286
-
287
- SpessaSynthInfo(
288
- `%cAll tracks parsed correctly!`,
289
- consoleColors.recognized
290
- );
291
- // parse the events
292
- this._parseInternal();
293
- SpessaSynthGroupEnd();
294
- SpessaSynthInfo(
295
- `%cMIDI file parsed. Total tick time: %c${this.lastVoiceEventTick}%c, total seconds time: %c${this.duration}`,
296
- consoleColors.info,
297
- consoleColors.recognized,
298
- consoleColors.info,
299
- consoleColors.recognized
300
- );
301
- }
302
-
303
- /**
304
- * @param fileByteArray {IndexedByteArray}
305
- * @returns {{type: string, size: number, data: IndexedByteArray}}
306
- * @private
307
- */
308
- _readMIDIChunk(fileByteArray)
309
- {
310
- const chunk = {};
311
- // type
312
- chunk.type = readBytesAsString(fileByteArray, 4);
313
- // size
314
- chunk.size = readBytesAsUintBigEndian(fileByteArray, 4);
315
- // data
316
- chunk.data = new IndexedByteArray(chunk.size);
317
- const dataSlice = fileByteArray.slice(fileByteArray.currentIndex, fileByteArray.currentIndex + chunk.size);
318
- chunk.data.set(dataSlice, 0);
319
- fileByteArray.currentIndex += chunk.size;
320
- return chunk;
321
- }
322
- }
323
-
324
- export { MIDI };