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,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
- }
package/midi/midi_data.js DELETED
@@ -1,63 +0,0 @@
1
- import { MIDISequenceData } from "./midi_sequence.js";
2
-
3
- /**
4
- * A simplified version of the MIDI, accessible at all times from the Sequencer.
5
- * Use getMIDI() to get the actual sequence.
6
- * This class contains all properties that MIDI does, except for tracks and the embedded soundfont.
7
- */
8
- export class MIDIData extends MIDISequenceData
9
- {
10
-
11
- /**
12
- * A boolean indicating if the MIDI file contains an embedded soundfont.
13
- * If the embedded soundfont is undefined, this will be false.
14
- * @type {boolean}
15
- */
16
- isEmbedded = false;
17
-
18
- /**
19
- * Constructor that copies data from a BasicMIDI instance.
20
- * @param {BasicMIDI} midi - The BasicMIDI instance to copy data from.
21
- */
22
- constructor(midi)
23
- {
24
- super();
25
- this._copyFromSequence(midi);
26
-
27
- // Set isEmbedded based on the presence of an embeddedSoundFont
28
- this.isEmbedded = midi.embeddedSoundFont !== undefined;
29
- }
30
- }
31
-
32
-
33
- /**
34
- * Temporary MIDI data used when the MIDI is not loaded.
35
- * @type {MIDIData}
36
- */
37
- export const DUMMY_MIDI_DATA = {
38
- duration: 99999,
39
- firstNoteOn: 0,
40
- loop: {
41
- start: 0,
42
- end: 123456
43
- },
44
-
45
- lastVoiceEventTick: 123456,
46
- lyrics: [],
47
- copyright: "",
48
- midiPorts: [],
49
- midiPortChannelOffsets: [],
50
- tracksAmount: 0,
51
- tempoChanges: [{ ticks: 0, tempo: 120 }],
52
- fileName: "NOT_LOADED.mid",
53
- midiName: "Loading...",
54
- rawMidiName: new Uint8Array([76, 111, 97, 100, 105, 110, 103, 46, 46, 46]), // "Loading..."
55
- usedChannelsOnTrack: [],
56
- timeDivision: 0,
57
- keyRange: { min: 0, max: 127 },
58
- isEmbedded: false,
59
- RMIDInfo: {},
60
- bankOffset: 0,
61
- midiNameUsesFileName: false,
62
- format: 0
63
- };
@@ -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 };