spessasynth_core 1.1.3 → 1.1.5

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 (189) hide show
  1. package/LICENSE +3 -26
  2. package/README.md +156 -474
  3. package/index.js +74 -8
  4. package/package.json +21 -8
  5. package/src/externals/fflate/LICENSE +21 -0
  6. package/src/externals/fflate/fflate.min.js +1 -0
  7. package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +12 -0
  8. package/src/externals/stbvorbis_sync/LICENSE +202 -0
  9. package/src/externals/stbvorbis_sync/NOTICE +6 -0
  10. package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +1 -0
  11. package/src/midi/README.md +32 -0
  12. package/src/midi/basic_midi.js +567 -0
  13. package/src/midi/midi_builder.js +202 -0
  14. package/src/midi/midi_loader.js +324 -0
  15. package/{spessasynth_core/midi_parser → src/midi}/midi_message.js +58 -35
  16. package/src/midi/midi_sequence.js +224 -0
  17. package/src/midi/midi_tools/get_note_times.js +154 -0
  18. package/src/midi/midi_tools/midi_editor.js +611 -0
  19. package/src/midi/midi_tools/midi_writer.js +99 -0
  20. package/src/midi/midi_tools/rmidi_writer.js +567 -0
  21. package/src/midi/midi_tools/used_keys_loaded.js +238 -0
  22. package/src/midi/xmf_loader.js +454 -0
  23. package/src/sequencer/README.md +5 -0
  24. package/src/sequencer/events.js +81 -0
  25. package/src/sequencer/play.js +349 -0
  26. package/src/sequencer/process_event.js +165 -0
  27. package/{spessasynth_core/sequencer/worklet_sequencer → src/sequencer}/process_tick.js +103 -84
  28. package/src/sequencer/sequencer_engine.js +367 -0
  29. package/src/sequencer/song_control.js +201 -0
  30. package/src/soundfont/README.md +13 -0
  31. package/src/soundfont/basic_soundfont/basic_instrument.js +77 -0
  32. package/src/soundfont/basic_soundfont/basic_preset.js +336 -0
  33. package/src/soundfont/basic_soundfont/basic_sample.js +206 -0
  34. package/src/soundfont/basic_soundfont/basic_soundfont.js +565 -0
  35. package/src/soundfont/basic_soundfont/basic_zone.js +64 -0
  36. package/src/soundfont/basic_soundfont/basic_zones.js +43 -0
  37. package/src/soundfont/basic_soundfont/generator.js +220 -0
  38. package/src/soundfont/basic_soundfont/modulator.js +378 -0
  39. package/src/soundfont/basic_soundfont/riff_chunk.js +149 -0
  40. package/src/soundfont/basic_soundfont/write_dls/art2.js +173 -0
  41. package/src/soundfont/basic_soundfont/write_dls/articulator.js +49 -0
  42. package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +400 -0
  43. package/src/soundfont/basic_soundfont/write_dls/ins.js +103 -0
  44. package/src/soundfont/basic_soundfont/write_dls/lins.js +18 -0
  45. package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +330 -0
  46. package/src/soundfont/basic_soundfont/write_dls/rgn2.js +121 -0
  47. package/src/soundfont/basic_soundfont/write_dls/wave.js +94 -0
  48. package/src/soundfont/basic_soundfont/write_dls/write_dls.js +119 -0
  49. package/src/soundfont/basic_soundfont/write_dls/wsmp.js +78 -0
  50. package/src/soundfont/basic_soundfont/write_dls/wvpl.js +32 -0
  51. package/src/soundfont/basic_soundfont/write_sf2/ibag.js +39 -0
  52. package/src/soundfont/basic_soundfont/write_sf2/igen.js +80 -0
  53. package/src/soundfont/basic_soundfont/write_sf2/imod.js +46 -0
  54. package/src/soundfont/basic_soundfont/write_sf2/inst.js +34 -0
  55. package/src/soundfont/basic_soundfont/write_sf2/pbag.js +39 -0
  56. package/src/soundfont/basic_soundfont/write_sf2/pgen.js +82 -0
  57. package/src/soundfont/basic_soundfont/write_sf2/phdr.js +42 -0
  58. package/src/soundfont/basic_soundfont/write_sf2/pmod.js +46 -0
  59. package/src/soundfont/basic_soundfont/write_sf2/sdta.js +80 -0
  60. package/src/soundfont/basic_soundfont/write_sf2/shdr.js +55 -0
  61. package/src/soundfont/basic_soundfont/write_sf2/write.js +222 -0
  62. package/src/soundfont/dls/articulator_converter.js +396 -0
  63. package/src/soundfont/dls/dls_destinations.js +38 -0
  64. package/src/soundfont/dls/dls_preset.js +44 -0
  65. package/src/soundfont/dls/dls_sample.js +75 -0
  66. package/src/soundfont/dls/dls_soundfont.js +186 -0
  67. package/src/soundfont/dls/dls_sources.js +62 -0
  68. package/src/soundfont/dls/dls_zone.js +95 -0
  69. package/src/soundfont/dls/read_articulation.js +299 -0
  70. package/src/soundfont/dls/read_instrument.js +121 -0
  71. package/src/soundfont/dls/read_instrument_list.js +17 -0
  72. package/src/soundfont/dls/read_lart.js +35 -0
  73. package/src/soundfont/dls/read_region.js +152 -0
  74. package/src/soundfont/dls/read_samples.js +270 -0
  75. package/src/soundfont/load_soundfont.js +21 -0
  76. package/src/soundfont/read_sf2/generators.js +46 -0
  77. package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/instruments.js +20 -14
  78. package/src/soundfont/read_sf2/modulators.js +36 -0
  79. package/src/soundfont/read_sf2/presets.js +80 -0
  80. package/src/soundfont/read_sf2/samples.js +304 -0
  81. package/src/soundfont/read_sf2/soundfont.js +305 -0
  82. package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/zones.js +68 -69
  83. package/src/synthetizer/README.md +7 -0
  84. package/src/synthetizer/audio_engine/README.md +9 -0
  85. package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +266 -0
  86. package/src/synthetizer/audio_engine/engine_components/controller_tables.js +88 -0
  87. package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +150 -0
  88. package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/lfo.js +9 -6
  89. package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +282 -0
  90. package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +467 -0
  91. package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +181 -0
  92. package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/modulator_curves.js +33 -30
  93. package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +221 -0
  94. package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +120 -0
  95. package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/unit_converter.js +11 -4
  96. package/src/synthetizer/audio_engine/engine_components/voice.js +519 -0
  97. package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +401 -0
  98. package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +263 -0
  99. package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +132 -0
  100. package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +48 -0
  101. package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +241 -0
  102. package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +27 -0
  103. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +253 -0
  104. package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +66 -0
  105. package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +17 -0
  106. package/src/synthetizer/audio_engine/engine_methods/note_on.js +175 -0
  107. package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +92 -0
  108. package/src/synthetizer/audio_engine/engine_methods/program_change.js +61 -0
  109. package/src/synthetizer/audio_engine/engine_methods/render_voice.js +196 -0
  110. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/clear_sound_font.js +30 -0
  111. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +22 -0
  112. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/reload_sound_font.js +28 -0
  113. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/send_preset_list.js +31 -0
  114. package/src/synthetizer/audio_engine/engine_methods/soundfont_management/set_embedded_sound_font.js +21 -0
  115. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +20 -0
  116. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +55 -0
  117. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +16 -0
  118. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +30 -0
  119. package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +63 -0
  120. package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +776 -0
  121. package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +24 -0
  122. package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +33 -0
  123. package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +31 -0
  124. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +15 -0
  125. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +27 -0
  126. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +19 -0
  127. package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +27 -0
  128. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +15 -0
  129. package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +34 -0
  130. package/src/synthetizer/audio_engine/main_processor.js +804 -0
  131. package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +15 -0
  132. package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +175 -0
  133. package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +116 -0
  134. package/src/synthetizer/synth_constants.js +22 -0
  135. package/{spessasynth_core → src}/utils/README.md +1 -0
  136. package/src/utils/buffer_to_wav.js +185 -0
  137. package/src/utils/byte_functions/big_endian.js +32 -0
  138. package/src/utils/byte_functions/little_endian.js +77 -0
  139. package/src/utils/byte_functions/string.js +107 -0
  140. package/src/utils/byte_functions/variable_length_quantity.js +42 -0
  141. package/src/utils/fill_with_defaults.js +21 -0
  142. package/src/utils/indexed_array.js +52 -0
  143. package/{spessasynth_core → src}/utils/loggin.js +70 -78
  144. package/src/utils/other.js +92 -0
  145. package/src/utils/sysex_detector.js +58 -0
  146. package/src/utils/xg_hacks.js +193 -0
  147. package/.idea/inspectionProfiles/Project_Default.xml +0 -10
  148. package/.idea/jsLibraryMappings.xml +0 -6
  149. package/.idea/modules.xml +0 -8
  150. package/.idea/spessasynth_core.iml +0 -12
  151. package/.idea/vcs.xml +0 -6
  152. package/spessasynth_core/midi_parser/README.md +0 -3
  153. package/spessasynth_core/midi_parser/midi_loader.js +0 -386
  154. package/spessasynth_core/sequencer/sequencer.js +0 -202
  155. package/spessasynth_core/sequencer/worklet_sequencer/play.js +0 -209
  156. package/spessasynth_core/sequencer/worklet_sequencer/process_event.js +0 -120
  157. package/spessasynth_core/sequencer/worklet_sequencer/song_control.js +0 -112
  158. package/spessasynth_core/soundfont/README.md +0 -4
  159. package/spessasynth_core/soundfont/chunk/generators.js +0 -205
  160. package/spessasynth_core/soundfont/chunk/modulators.js +0 -232
  161. package/spessasynth_core/soundfont/chunk/presets.js +0 -264
  162. package/spessasynth_core/soundfont/chunk/riff_chunk.js +0 -46
  163. package/spessasynth_core/soundfont/chunk/samples.js +0 -250
  164. package/spessasynth_core/soundfont/soundfont_parser.js +0 -301
  165. package/spessasynth_core/synthetizer/README.md +0 -6
  166. package/spessasynth_core/synthetizer/synthesizer.js +0 -313
  167. package/spessasynth_core/synthetizer/worklet_system/README.md +0 -3
  168. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/controller_control.js +0 -290
  169. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/data_entry.js +0 -280
  170. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_off.js +0 -102
  171. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_on.js +0 -77
  172. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/program_control.js +0 -140
  173. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/system_exclusive.js +0 -266
  174. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/tuning_control.js +0 -104
  175. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/vibrato_control.js +0 -29
  176. package/spessasynth_core/synthetizer/worklet_system/worklet_methods/voice_control.js +0 -223
  177. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +0 -133
  178. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +0 -73
  179. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +0 -76
  180. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +0 -272
  181. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +0 -83
  182. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +0 -175
  183. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +0 -106
  184. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +0 -285
  185. package/spessasynth_core/utils/buffer_to_wav.js +0 -70
  186. package/spessasynth_core/utils/byte_functions.js +0 -141
  187. package/spessasynth_core/utils/other.js +0 -49
  188. package/spessasynth_core/utils/shiftable_array.js +0 -26
  189. package/spessasynth_core/utils/stbvorbis_sync.js +0 -1877
@@ -1,21 +1,38 @@
1
- import {ShiftableByteArray} from "../utils/shiftable_array.js";
1
+ import { IndexedByteArray } from "../utils/indexed_array.js";
2
2
 
3
3
  /**
4
4
  * midi_message.js
5
5
  * purpose: contains enums for midi events and controllers and functions to parse them
6
6
  */
7
7
 
8
- export class MidiMessage
8
+ export class MIDIMessage
9
9
  {
10
+ /**
11
+ * Absolute number of MIDI ticks from the start of the track.
12
+ * @type {number}
13
+ */
14
+ ticks;
15
+
16
+ /**
17
+ * The MIDI message status byte. Note that for meta events, it is the second byte. (not 0xFF)
18
+ * @type {number}
19
+ */
20
+ messageStatusByte;
21
+
22
+ /**
23
+ * Message's binary data
24
+ * @type {IndexedByteArray}
25
+ */
26
+ messageData;
27
+
10
28
  /**
11
29
  * @param ticks {number}
12
30
  * @param byte {number} the message status byte
13
- * @param data {ShiftableByteArray}
31
+ * @param data {IndexedByteArray}
14
32
  */
15
- constructor(ticks, byte, data) {
16
- // absolute ticks from the start
33
+ constructor(ticks, byte, data)
34
+ {
17
35
  this.ticks = ticks;
18
- // message status byte (for meta it's the second byte)
19
36
  this.messageStatusByte = byte;
20
37
  this.messageData = data;
21
38
  }
@@ -26,13 +43,15 @@ export class MidiMessage
26
43
  * @param statusByte
27
44
  * @returns {number} channel is -1 for system messages -2 for meta and -3 for sysex
28
45
  */
29
- export function getChannel(statusByte) {
46
+ export function getChannel(statusByte)
47
+ {
30
48
  const eventType = statusByte & 0xF0;
31
49
  const channel = statusByte & 0x0F;
32
-
50
+
33
51
  let resultChannel = channel;
34
-
35
- switch (eventType) {
52
+
53
+ switch (eventType)
54
+ {
36
55
  // midi (and meta and sysex headers)
37
56
  case 0x80:
38
57
  case 0x90:
@@ -42,13 +61,14 @@ export function getChannel(statusByte) {
42
61
  case 0xD0:
43
62
  case 0xE0:
44
63
  break;
45
-
64
+
46
65
  case 0xF0:
47
- switch (channel) {
66
+ switch (channel)
67
+ {
48
68
  case 0x0:
49
69
  resultChannel = -3;
50
70
  break;
51
-
71
+
52
72
  case 0x1:
53
73
  case 0x2:
54
74
  case 0x3:
@@ -65,17 +85,17 @@ export function getChannel(statusByte) {
65
85
  case 0xE:
66
86
  resultChannel = -1;
67
87
  break;
68
-
88
+
69
89
  case 0xF:
70
90
  resultChannel = -2;
71
91
  break;
72
92
  }
73
93
  break;
74
-
94
+
75
95
  default:
76
96
  resultChannel = -1;
77
97
  }
78
-
98
+
79
99
  return resultChannel;
80
100
  }
81
101
 
@@ -83,10 +103,10 @@ export function getChannel(statusByte) {
83
103
  export const messageTypes = {
84
104
  noteOff: 0x80,
85
105
  noteOn: 0x90,
86
- noteAftertouch: 0xA0,
106
+ polyPressure: 0xA0,
87
107
  controllerChange: 0xB0,
88
108
  programChange: 0xC0,
89
- channelAftertouch: 0xD0,
109
+ channelPressure: 0xD0,
90
110
  pitchBend: 0xE0,
91
111
  systemExclusive: 0xF0,
92
112
  timecode: 0xF1,
@@ -107,6 +127,7 @@ export const messageTypes = {
107
127
  lyric: 0x05,
108
128
  marker: 0x06,
109
129
  cuePoint: 0x07,
130
+ programName: 0x08,
110
131
  midiChannelPrefix: 0x20,
111
132
  midiPort: 0x21,
112
133
  endOfTrack: 0x2F,
@@ -123,18 +144,20 @@ export const messageTypes = {
123
144
  * @param statusByte {number} the status byte
124
145
  * @returns {{channel: number, status: number}} channel will be -1 for sysex and meta
125
146
  */
126
- export function getEvent(statusByte) {
147
+ export function getEvent(statusByte)
148
+ {
127
149
  const status = statusByte & 0xF0;
128
150
  const channel = statusByte & 0x0F;
129
-
151
+
130
152
  let eventChannel = -1;
131
153
  let eventStatus = statusByte;
132
-
133
- if (status >= 0x80 && status <= 0xE0) {
154
+
155
+ if (status >= 0x80 && status <= 0xE0)
156
+ {
134
157
  eventChannel = channel;
135
158
  eventStatus = status;
136
159
  }
137
-
160
+
138
161
  return {
139
162
  status: eventStatus,
140
163
  channel: eventChannel
@@ -143,7 +166,7 @@ export function getEvent(statusByte) {
143
166
 
144
167
 
145
168
  /**
146
- * @type {{timbreHarmonicContent: number, omniModeOn: number, polyModeOn: number, localControlOnOff: number, NRPNLsb: number, allNotesOff: number, footController: number, effects2Depth: number, lsbForControl7MainVolume: number, expressionController: number, monoModeOn: number, balance: number, effectControl1: number, effectControl2: number, modulationWheel: number, lsbForControl1ModulationWheel: number, allSoundOff: number, pan: number, effects1Depth: number, effects3Depth: number, attackTime: number, dataEntryMsb: number, portamentoControl: number, sostenutoPedal: number, lsbForControl5PortamentoTime: number, RPNLsb: number, bankSelect: number, portamentoTime: number, mainVolume: number, hold2Pedal: number, releaseTime: number, dataDecrement: number, NRPNMsb: number, legatoFootswitch: number, sustainPedal: number, portamentoOnOff: number, lsbForControl0BankSelect: number, lsbForControl13EffectControl2: number, effects5Depth: number, generalPurposeController2: number, lsbForControl6DataEntry: number, resetAllControllers: number, generalPurposeController3: number, generalPurposeController4: number, generalPurposeController5: number, softPedal: number, generalPurposeController6: number, lsbForControl4FootController: number, lsbForControl12EffectControl1: number, generalPurposeController7: number, generalPurposeController8: number, effects4Depth: number, lsbForControl8Balance: number, soundController9: number, soundVariation: number, soundController8: number, soundController7: number, soundController6: number, soundController10: number, dataIncrement: number, generalPurposeController1: number, lsbForControl2BreathController: number, lsbForControl11ExpressionController: number, brightness: number, lsbForControl10Pan: number, RPNMsb: number, breathController: number, omniModeOff: number}}
169
+ * @enum {number}
147
170
  */
148
171
  export const midiControllers = {
149
172
  bankSelect: 0,
@@ -181,25 +204,25 @@ export const midiControllers = {
181
204
  legatoFootswitch: 68,
182
205
  hold2Pedal: 69,
183
206
  soundVariation: 70,
184
- timbreHarmonicContent: 71,
207
+ filterResonance: 71,
185
208
  releaseTime: 72,
186
209
  attackTime: 73,
187
210
  brightness: 74,
188
- soundController6: 75,
189
- soundController7: 76,
190
- soundController8: 77,
191
- soundController9: 78,
211
+ decayTime: 75,
212
+ vibratoRate: 76,
213
+ vibratoDepth: 77,
214
+ vibratoDelay: 78,
192
215
  soundController10: 79,
193
216
  generalPurposeController5: 80,
194
217
  generalPurposeController6: 81,
195
218
  generalPurposeController7: 82,
196
219
  generalPurposeController8: 83,
197
220
  portamentoControl: 84,
198
- effects1Depth: 91,
199
- effects2Depth: 92,
200
- effects3Depth: 93,
201
- effects4Depth: 94,
202
- effects5Depth: 95,
221
+ reverbDepth: 91,
222
+ tremoloDepth: 92,
223
+ chorusDepth: 93,
224
+ detuneDepth: 94,
225
+ phaserDepth: 95,
203
226
  dataIncrement: 96,
204
227
  dataDecrement: 97,
205
228
  NRPNLsb: 98,
@@ -226,6 +249,6 @@ export const dataBytesAmount = {
226
249
  0xA: 2, // note at
227
250
  0xB: 2, // cc change
228
251
  0xC: 1, // pg change
229
- 0xD: 1, // channel aftertouch
252
+ 0xD: 1, // channel after touch
230
253
  0xE: 2 // pitch wheel
231
254
  };
@@ -0,0 +1,224 @@
1
+ /**
2
+ * This is the base type for MIDI files. It contains all the "metadata" and information.
3
+ * It extends to:
4
+ * - BasicMIDI, which contains the actual track data of the MIDI file. Essentially the MIDI file itself.
5
+ * - MIDIData, which contains all properties that MIDI does, except for tracks and the embedded soundfont.
6
+ * MIDIData is the "shell" of the file which is available on the main thread at all times, containing the metadata.
7
+ */
8
+ class MIDISequenceData
9
+ {
10
+ /**
11
+ * The time division of the sequence, representing the number of ticks per beat.
12
+ * @type {number}
13
+ */
14
+ timeDivision = 0;
15
+
16
+ /**
17
+ * The duration of the sequence, in seconds.
18
+ * @type {number}
19
+ */
20
+ duration = 0;
21
+
22
+ /**
23
+ * The tempo changes in the sequence, ordered from the last change to the first.
24
+ * Each change is represented by an object with a tick position and a tempo value in beats per minute.
25
+ * @type {{ticks: number, tempo: number}[]}
26
+ */
27
+ tempoChanges = [{ ticks: 0, tempo: 120 }];
28
+
29
+ /**
30
+ * A string containing the copyright information for the MIDI sequence if detected.
31
+ * @type {string}
32
+ */
33
+ copyright = "";
34
+
35
+ /**
36
+ * The number of tracks in the MIDI sequence.
37
+ * @type {number}
38
+ */
39
+ tracksAmount = 0;
40
+
41
+ /**
42
+ * The track names in the MIDI file, an empty string if not set.
43
+ * @type {string[]}
44
+ */
45
+ trackNames = [];
46
+
47
+ /**
48
+ * An array containing the lyrics of the sequence, stored as binary chunks (Uint8Array).
49
+ * @type {Uint8Array[]}
50
+ */
51
+ lyrics = [];
52
+
53
+ /**
54
+ * An array of tick positions where lyrics events occur in the sequence.
55
+ * @type {number[]}
56
+ */
57
+ lyricsTicks = [];
58
+
59
+ /**
60
+ * The tick position of the first note-on event in the MIDI sequence.
61
+ * @type {number}
62
+ */
63
+ firstNoteOn = 0;
64
+
65
+ /**
66
+ * The MIDI key range used in the sequence, represented by a minimum and maximum note value.
67
+ * @type {{min: number, max: number}}
68
+ */
69
+ keyRange = { min: 0, max: 127 };
70
+
71
+ /**
72
+ * The tick position of the last voice event (such as note-on, note-off, or control change) in the sequence.
73
+ * @type {number}
74
+ */
75
+ lastVoiceEventTick = 0;
76
+
77
+ /**
78
+ * An array of MIDI port numbers used by each track in the sequence.
79
+ * @type {number[]}
80
+ */
81
+ midiPorts = [0];
82
+
83
+ /**
84
+ * An array of channel offsets for each MIDI port, using the SpessaSynth method.
85
+ * @type {number[]}
86
+ */
87
+ midiPortChannelOffsets = [0];
88
+
89
+ /**
90
+ * A list of sets, where each set contains the MIDI channels used by each track in the sequence.
91
+ * @type {Set<number>[]}
92
+ */
93
+ usedChannelsOnTrack = [];
94
+
95
+ /**
96
+ * The loop points (in ticks) of the sequence, including both start and end points.
97
+ * @type {{start: number, end: number}}
98
+ */
99
+ loop = { start: 0, end: 0 };
100
+
101
+ /**
102
+ * The name of the MIDI sequence.
103
+ * @type {string}
104
+ */
105
+ midiName = "";
106
+
107
+ /**
108
+ * A boolean indicating if the sequence's name is the same as the file name.
109
+ * @type {boolean}
110
+ */
111
+ midiNameUsesFileName = false;
112
+
113
+ /**
114
+ * The file name of the MIDI sequence, if provided during parsing.
115
+ * @type {string}
116
+ */
117
+ fileName = "";
118
+
119
+ /**
120
+ * The raw, encoded MIDI name, represented as a Uint8Array.
121
+ * Useful when the MIDI file uses a different code page.
122
+ * @type {Uint8Array}
123
+ */
124
+ rawMidiName;
125
+
126
+ /**
127
+ * The format of the MIDI file, which can be 0, 1, or 2, indicating the type of the MIDI file.
128
+ * @type {number}
129
+ */
130
+ format = 0;
131
+
132
+ /**
133
+ * The RMID (Resource-Interchangeable MIDI) info data, if the file is RMID formatted.
134
+ * Otherwise, this field is undefined.
135
+ * Chunk type (e.g. "INAM"): Chunk data as a binary array.
136
+ * @type {Object<string, IndexedByteArray>}
137
+ */
138
+ RMIDInfo = {};
139
+
140
+ /**
141
+ * The bank offset used for RMID files.
142
+ * @type {number}
143
+ */
144
+ bankOffset = 0;
145
+
146
+ /**
147
+ * If the MIDI file is a Soft Karaoke file (.kar), this flag is set to true.
148
+ * https://www.mixagesoftware.com/en/midikit/help/HTML/karaoke_formats.html
149
+ * @type {boolean}
150
+ */
151
+ isKaraokeFile = false;
152
+
153
+ /**
154
+ * Indicates if this file is a Multi-Port MIDI file.
155
+ * @type {boolean}
156
+ */
157
+ isMultiPort = false;
158
+
159
+ /**
160
+ * Converts ticks to time in seconds
161
+ * @param ticks {number} time in MIDI ticks
162
+ * @returns {number} time in seconds
163
+ */
164
+ MIDIticksToSeconds(ticks)
165
+ {
166
+ let totalSeconds = 0;
167
+
168
+ while (ticks > 0)
169
+ {
170
+ // tempo changes are reversed, so the first element is the last tempo change
171
+ // and the last element is the first tempo change
172
+ // (always at tick 0 and tempo 120)
173
+ // find the last tempo change that has occurred
174
+ let tempo = this.tempoChanges.find(v => v.ticks < ticks);
175
+
176
+ // calculate the difference and tempo time
177
+ let timeSinceLastTempo = ticks - tempo.ticks;
178
+ totalSeconds += (timeSinceLastTempo * 60) / (tempo.tempo * this.timeDivision);
179
+ ticks -= timeSinceLastTempo;
180
+ }
181
+
182
+ return totalSeconds;
183
+ }
184
+
185
+ /**
186
+ * INTERNAL USE ONLY!
187
+ * @param sequence {MIDISequenceData}
188
+ * @protected
189
+ */
190
+ _copyFromSequence(sequence)
191
+ {
192
+ // properties can be assigned
193
+ this.midiName = sequence.midiName;
194
+ this.midiNameUsesFileName = sequence.midiNameUsesFileName;
195
+ this.fileName = sequence.fileName;
196
+ this.timeDivision = sequence.timeDivision;
197
+ this.duration = sequence.duration;
198
+ this.copyright = sequence.copyright;
199
+ this.tracksAmount = sequence.tracksAmount;
200
+ this.firstNoteOn = sequence.firstNoteOn;
201
+ this.lastVoiceEventTick = sequence.lastVoiceEventTick;
202
+ this.format = sequence.format;
203
+ this.bankOffset = sequence.bankOffset;
204
+ this.isKaraokeFile = sequence.isKaraokeFile;
205
+ this.isMultiPort = sequence.isMultiPort;
206
+
207
+ // copying arrays
208
+ this.tempoChanges = [...sequence.tempoChanges];
209
+ this.lyrics = sequence.lyrics.map(arr => new Uint8Array(arr));
210
+ this.lyricsTicks = [...sequence.lyricsTicks];
211
+ this.midiPorts = [...sequence.midiPorts];
212
+ this.trackNames = [...sequence.trackNames];
213
+ this.midiPortChannelOffsets = [...sequence.midiPortChannelOffsets];
214
+ this.usedChannelsOnTrack = sequence.usedChannelsOnTrack.map(set => new Set(set));
215
+ this.rawMidiName = sequence.rawMidiName ? new Uint8Array(sequence.rawMidiName) : undefined;
216
+
217
+ // copying objects
218
+ this.loop = { ...sequence.loop };
219
+ this.keyRange = { ...sequence.keyRange };
220
+ this.RMIDInfo = { ...sequence.RMIDInfo };
221
+ }
222
+ }
223
+
224
+ export { MIDISequenceData };
@@ -0,0 +1,154 @@
1
+ import { IndexedByteArray } from "../../utils/indexed_array.js";
2
+ import { readBytesAsUintBigEndian } from "../../utils/byte_functions/big_endian.js";
3
+ import { DEFAULT_PERCUSSION } from "../../synthetizer/synth_constants.js";
4
+
5
+ /**
6
+ * Calculates all note times in seconds.
7
+ * @this {BasicMIDI}
8
+ * @param minDrumLength {number} the shortest a drum note (channel 10) can be, in seconds.
9
+ * @returns {{
10
+ * midiNote: number,
11
+ * start: number,
12
+ * length: number,
13
+ * velocity: number,
14
+ * }[][]} an array of 16 channels, each channel containing its notes,
15
+ * with their key number, velocity, absolute start time and length in seconds.
16
+ */
17
+ export function getNoteTimes(minDrumLength = 0)
18
+ {
19
+
20
+ /**
21
+ * gets tempo from the midi message
22
+ * @param event {MIDIMessage}
23
+ * @return {number} the tempo in bpm
24
+ */
25
+ function getTempo(event)
26
+ {
27
+ // simulate IndexedByteArray
28
+ event.messageData = new IndexedByteArray(event.messageData.buffer);
29
+ event.messageData.currentIndex = 0;
30
+ return 60000000 / readBytesAsUintBigEndian(event.messageData, 3);
31
+ }
32
+
33
+ /**
34
+ * an array of 16 arrays (channels)
35
+ * @type {{
36
+ * midiNote: number,
37
+ * start: number,
38
+ * length: number,
39
+ * velocity: number,
40
+ * }[][]}
41
+ */
42
+ const noteTimes = [];
43
+ // flatten and sort by ticks
44
+ const trackData = this.tracks;
45
+ let events = trackData.flat();
46
+ events.sort((e1, e2) => e1.ticks - e2.ticks);
47
+
48
+ for (let i = 0; i < 16; i++)
49
+ {
50
+ noteTimes.push([]);
51
+ }
52
+ let elapsedTime = 0;
53
+ let oneTickToSeconds = 60 / (120 * this.timeDivision);
54
+ let eventIndex = 0;
55
+ let unfinished = 0;
56
+ /**
57
+ * @type {{
58
+ * midiNote: number,
59
+ * start: number,
60
+ * length: number,
61
+ * velocity: number,
62
+ * }[][]}
63
+ */
64
+ const unfinishedNotes = [];
65
+ for (let i = 0; i < 16; i++)
66
+ {
67
+ unfinishedNotes.push([]);
68
+ }
69
+ const noteOff = (midiNote, channel) =>
70
+ {
71
+ const noteIndex = unfinishedNotes[channel].findIndex(n => n.midiNote === midiNote);
72
+ const note = unfinishedNotes[channel][noteIndex];
73
+ if (note)
74
+ {
75
+ const time = elapsedTime - note.start;
76
+ note.length = time;
77
+ if (channel === DEFAULT_PERCUSSION)
78
+ {
79
+ note.length = time < minDrumLength ? minDrumLength : time;
80
+ }
81
+ // delete from unfinished
82
+ unfinishedNotes[channel].splice(noteIndex, 1);
83
+ }
84
+ unfinished--;
85
+ };
86
+ while (eventIndex < events.length)
87
+ {
88
+ const event = events[eventIndex];
89
+
90
+ const status = event.messageStatusByte >> 4;
91
+ const channel = event.messageStatusByte & 0x0F;
92
+
93
+ // note off
94
+ if (status === 0x8)
95
+ {
96
+ noteOff(event.messageData[0], channel);
97
+ }
98
+ // note on
99
+ else if (status === 0x9)
100
+ {
101
+ if (event.messageData[1] === 0)
102
+ {
103
+ // never mind, its note off
104
+ noteOff(event.messageData[0], channel);
105
+ }
106
+ else
107
+ {
108
+ // stop previous
109
+ noteOff(event.messageData[0], channel);
110
+ const noteTime = {
111
+ midiNote: event.messageData[0],
112
+ start: elapsedTime,
113
+ length: -1,
114
+ velocity: event.messageData[1] / 127
115
+ };
116
+ noteTimes[channel].push(noteTime);
117
+ unfinishedNotes[channel].push(noteTime);
118
+ unfinished++;
119
+
120
+ }
121
+ }
122
+ // set tempo
123
+ else if (event.messageStatusByte === 0x51)
124
+ {
125
+ oneTickToSeconds = 60 / (getTempo(event) * this.timeDivision);
126
+ }
127
+
128
+ if (++eventIndex >= events.length)
129
+ {
130
+ break;
131
+ }
132
+
133
+ elapsedTime += oneTickToSeconds * (events[eventIndex].ticks - event.ticks);
134
+ }
135
+
136
+ // finish the unfinished notes
137
+ if (unfinished > 0)
138
+ {
139
+ // for every channel, for every note that is unfinished (has -1 length)
140
+ unfinishedNotes.forEach((channelNotes, channel) =>
141
+ {
142
+ channelNotes.forEach(note =>
143
+ {
144
+ const time = elapsedTime - note.start;
145
+ note.length = time;
146
+ if (channel === DEFAULT_PERCUSSION)
147
+ {
148
+ note.length = time < minDrumLength ? minDrumLength : time;
149
+ }
150
+ });
151
+ });
152
+ }
153
+ return noteTimes;
154
+ }