spessasynth_core 3.27.8 → 4.0.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 (157) hide show
  1. package/README.md +85 -51
  2. package/dist/index.d.ts +4057 -0
  3. package/dist/index.js +17178 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +24 -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 -53
  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 -220
  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 -26
  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 -817
  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,203 +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
- * @param velocity {number} optional and unsupported by spessasynth
130
- */
131
- addNoteOff(ticks, track, channel, midiNote, velocity = 64)
132
- {
133
- channel %= 16;
134
- midiNote %= 128;
135
- this.addEvent(
136
- ticks,
137
- track,
138
- messageTypes.noteOff | channel,
139
- [midiNote, velocity]
140
- );
141
- }
142
-
143
- /**
144
- * Adds a new Program Change event
145
- * @param ticks {number} the tick time of the event
146
- * @param track {number} the track number to use
147
- * @param channel {number} the channel to use
148
- * @param programNumber {number} the MIDI program to use
149
- */
150
- addProgramChange(ticks, track, channel, programNumber)
151
- {
152
- channel %= 16;
153
- programNumber %= 128;
154
- this.addEvent(
155
- ticks,
156
- track,
157
- messageTypes.programChange | channel,
158
- [programNumber]
159
- );
160
- }
161
-
162
- /**
163
- * Adds a new Controller Change event
164
- * @param ticks {number} the tick time of the event
165
- * @param track {number} the track number to use
166
- * @param channel {number} the channel to use
167
- * @param controllerNumber {number} the MIDI CC to use
168
- * @param controllerValue {number} the new CC value
169
- */
170
- addControllerChange(ticks, track, channel, controllerNumber, controllerValue)
171
- {
172
- channel %= 16;
173
- controllerNumber %= 128;
174
- controllerValue %= 128;
175
- this.addEvent(
176
- ticks,
177
- track,
178
- messageTypes.controllerChange | channel,
179
- [controllerNumber, controllerValue]
180
- );
181
- }
182
-
183
- /**
184
- * Adds a new Pitch Wheel event
185
- * @param ticks {number} the tick time of the event
186
- * @param track {number} the track to use
187
- * @param channel {number} the channel to use
188
- * @param MSB {number} SECOND byte of the MIDI pitchWheel message
189
- * @param LSB {number} FIRST byte of the MIDI pitchWheel message
190
- */
191
- addPitchWheel(ticks, track, channel, MSB, LSB)
192
- {
193
- channel %= 16;
194
- MSB %= 128;
195
- LSB %= 128;
196
- this.addEvent(
197
- ticks,
198
- track,
199
- messageTypes.pitchBend | channel,
200
- [LSB, MSB]
201
- );
202
- }
203
- }
@@ -1,321 +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, 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
- false
105
- ).replaceAll("\n", " ");
106
- }
107
- if (this.RMIDInfo["INAM"])
108
- {
109
- this.rawMidiName = this.RMIDInfo[RMIDINFOChunks.name];
110
- this.midiName = readBytesAsString(
111
- /** @type {IndexedByteArray}*/this.rawMidiName,
112
- this.rawMidiName.length,
113
- false
114
- ).replaceAll("\n", " ");
115
- }
116
- // these can be used interchangeably
117
- if (this.RMIDInfo["IALB"] && !this.RMIDInfo["IPRD"])
118
- {
119
- this.RMIDInfo["IPRD"] = this.RMIDInfo["IALB"];
120
- }
121
- if (this.RMIDInfo["IPRD"] && !this.RMIDInfo["IALB"])
122
- {
123
- this.RMIDInfo["IALB"] = this.RMIDInfo["IPRD"];
124
- }
125
- this.bankOffset = 1; // defaults to 1
126
- if (this.RMIDInfo[RMIDINFOChunks.bankOffset])
127
- {
128
- this.bankOffset = readLittleEndian(this.RMIDInfo[RMIDINFOChunks.bankOffset], 2);
129
- }
130
- }
131
- }
132
- }
133
-
134
- if (this.isDLSRMIDI)
135
- {
136
- // Assume bank offset of 0 by default. If we find any bank selects, then the offset is 1.
137
- this.bankOffset = 0;
138
- }
139
-
140
- // if no embedded bank, assume 0
141
- if (this.embeddedSoundFont === undefined)
142
- {
143
- this.bankOffset = 0;
144
- }
145
- }
146
- else if (initialString === "XMF_")
147
- {
148
- // XMF file
149
- fileByteArray = loadXMF(this, binaryData);
150
- }
151
- else
152
- {
153
- fileByteArray = binaryData;
154
- }
155
- const headerChunk = this._readMIDIChunk(fileByteArray);
156
- if (headerChunk.type !== "MThd")
157
- {
158
- SpessaSynthGroupEnd();
159
- throw new SyntaxError(`Invalid MIDI Header! Expected "MThd", got "${headerChunk.type}"`);
160
- }
161
-
162
- if (headerChunk.size !== 6)
163
- {
164
- SpessaSynthGroupEnd();
165
- throw new RangeError(`Invalid MIDI header chunk size! Expected 6, got ${headerChunk.size}`);
166
- }
167
-
168
- // format
169
- this.format = readBytesAsUintBigEndian(headerChunk.data, 2);
170
- // tracks count
171
- this.tracksAmount = readBytesAsUintBigEndian(headerChunk.data, 2);
172
- // time division
173
- this.timeDivision = readBytesAsUintBigEndian(headerChunk.data, 2);
174
- // read all the tracks
175
- for (let i = 0; i < this.tracksAmount; i++)
176
- {
177
- /**
178
- * @type {MIDIMessage[]}
179
- */
180
- const track = [];
181
- const trackChunk = this._readMIDIChunk(fileByteArray);
182
-
183
- if (trackChunk.type !== "MTrk")
184
- {
185
- SpessaSynthGroupEnd();
186
- throw new SyntaxError(`Invalid track header! Expected "MTrk" got "${trackChunk.type}"`);
187
- }
188
-
189
-
190
- /**
191
- * MIDI running byte
192
- * @type {number}
193
- */
194
- let runningByte = undefined;
195
-
196
- let totalTicks = 0;
197
- // format 2 plays sequentially
198
- if (this.format === 2 && i > 0)
199
- {
200
- totalTicks += this.tracks[i - 1][this.tracks[i - 1].length - 1].ticks;
201
- }
202
- // loop until we reach the end of track
203
- while (trackChunk.data.currentIndex < trackChunk.size)
204
- {
205
- totalTicks += readVariableLengthQuantity(trackChunk.data);
206
-
207
- // check if the status byte is valid (IE. larger than 127)
208
- const statusByteCheck = trackChunk.data[trackChunk.data.currentIndex];
209
-
210
- let statusByte;
211
- // if we have a running byte and the status byte isn't valid
212
- if (runningByte !== undefined && statusByteCheck < 0x80)
213
- {
214
- statusByte = runningByte;
215
- }
216
- else
217
- { // noinspection PointlessBooleanExpressionJS
218
- if (runningByte === undefined && statusByteCheck < 0x80)
219
- {
220
- // if we don't have a running byte and the status byte isn't valid, it's an error.
221
- SpessaSynthGroupEnd();
222
- throw new SyntaxError(`Unexpected byte with no running byte. (${statusByteCheck})`);
223
- }
224
- else
225
- {
226
- // if the status byte is valid, use that
227
- statusByte = trackChunk.data[trackChunk.data.currentIndex++];
228
- }
229
- }
230
- const statusByteChannel = getChannel(statusByte);
231
-
232
- let eventDataLength;
233
-
234
- // determine the message's length;
235
- switch (statusByteChannel)
236
- {
237
- case -1:
238
- // system common/realtime (no length)
239
- eventDataLength = 0;
240
- break;
241
-
242
- case -2:
243
- // meta (the next is the actual status byte)
244
- statusByte = trackChunk.data[trackChunk.data.currentIndex++];
245
- eventDataLength = readVariableLengthQuantity(trackChunk.data);
246
- break;
247
-
248
- case -3:
249
- // sysex
250
- eventDataLength = readVariableLengthQuantity(trackChunk.data);
251
- break;
252
-
253
- default:
254
- // voice message
255
- // gets the midi message length
256
- eventDataLength = dataBytesAmount[statusByte >> 4];
257
- // save the status byte
258
- runningByte = statusByte;
259
- break;
260
- }
261
-
262
- // put the event data into the array
263
- const eventData = new IndexedByteArray(eventDataLength);
264
- eventData.set(trackChunk.data.slice(
265
- trackChunk.data.currentIndex,
266
- trackChunk.data.currentIndex + eventDataLength
267
- ), 0);
268
- const event = new MIDIMessage(totalTicks, statusByte, eventData);
269
- track.push(event);
270
- // advance the track chunk
271
- trackChunk.data.currentIndex += eventDataLength;
272
- }
273
- this.tracks.push(track);
274
-
275
- SpessaSynthInfo(
276
- `%cParsed %c${this.tracks.length}%c / %c${this.tracksAmount}`,
277
- consoleColors.info,
278
- consoleColors.value,
279
- consoleColors.info,
280
- consoleColors.value
281
- );
282
- }
283
-
284
- SpessaSynthInfo(
285
- `%cAll tracks parsed correctly!`,
286
- consoleColors.recognized
287
- );
288
- // parse the events
289
- this._parseInternal();
290
- SpessaSynthGroupEnd();
291
- SpessaSynthInfo(
292
- `%cMIDI file parsed. Total tick time: %c${this.lastVoiceEventTick}%c, total seconds time: %c${this.duration}`,
293
- consoleColors.info,
294
- consoleColors.recognized,
295
- consoleColors.info,
296
- consoleColors.recognized
297
- );
298
- }
299
-
300
- /**
301
- * @param fileByteArray {IndexedByteArray}
302
- * @returns {{type: string, size: number, data: IndexedByteArray}}
303
- * @private
304
- */
305
- _readMIDIChunk(fileByteArray)
306
- {
307
- const chunk = {};
308
- // type
309
- chunk.type = readBytesAsString(fileByteArray, 4);
310
- // size
311
- chunk.size = readBytesAsUintBigEndian(fileByteArray, 4);
312
- // data
313
- chunk.data = new IndexedByteArray(chunk.size);
314
- const dataSlice = fileByteArray.slice(fileByteArray.currentIndex, fileByteArray.currentIndex + chunk.size);
315
- chunk.data.set(dataSlice, 0);
316
- fileByteArray.currentIndex += chunk.size;
317
- return chunk;
318
- }
319
- }
320
-
321
- export { MIDI };