spessasynth_core 1.1.2 → 1.1.4

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 +73 -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 -303
  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 -222
  177. package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +0 -95
  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 -194
  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 -173
  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 -313
  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,85 +1,104 @@
1
- /**
2
- * Processes a single tick
3
- * @private
4
- * @this {Sequencer}
5
- */
6
- export function _processTick()
7
- {
8
- let current = this.currentTime;
9
- while(this.playedTime < current)
10
- {
11
- // find next event
12
- let trackIndex = this._findFirstEventIndex();
13
- let event = this.tracks[trackIndex][this.eventIndex[trackIndex]];
14
- this._processEvent(event, trackIndex);
15
-
16
- this.eventIndex[trackIndex]++;
17
-
18
- // find next event
19
- trackIndex = this._findFirstEventIndex();
20
- if(this.tracks[trackIndex].length <= this.eventIndex[trackIndex])
21
- {
22
- // song has ended
23
- if(this.loop)
24
- {
25
- this.setTimeTicks(this.midiData.loop.start);
26
- return;
27
- }
28
- this.eventIndex[trackIndex]--;
29
- this.pause(true);
30
- if(this.songs.length > 1)
31
- {
32
- this.nextSong();
33
- }
34
- return;
35
- }
36
- let eventNext = this.tracks[trackIndex][this.eventIndex[trackIndex]];
37
- this.playedTime += this.oneTickToSeconds * (eventNext.ticks - event.ticks);
38
-
39
- // loop
40
- if((this.midiData.loop.end <= event.ticks) && this.loop)
41
- {
42
- this.setTimeTicks(this.midiData.loop.start);
43
- return;
44
- }
45
- // if song has ended
46
- else if(current >= this.duration)
47
- {
48
- if(this.loop)
49
- {
50
- this.setTimeTicks(this.midiData.loop.start);
51
- return;
52
- }
53
- this.eventIndex[trackIndex]--;
54
- this.pause(true);
55
- if(this.songs.length > 1)
56
- {
57
- this.nextSong();
58
- }
59
- return;
60
- }
61
- }
62
- }
63
-
64
-
65
- /**
66
- * @returns {number} the index of the first to the current played time
67
- * @this {Sequencer}
68
- */
69
- export function _findFirstEventIndex()
70
- {
71
- let index = 0;
72
- let ticks = Infinity;
73
- this.tracks.forEach((track, i) => {
74
- if(this.eventIndex[i] >= track.length)
75
- {
76
- return;
77
- }
78
- if(track[this.eventIndex[i]].ticks < ticks)
79
- {
80
- index = i;
81
- ticks = track[this.eventIndex[i]].ticks;
82
- }
83
- });
84
- return index;
1
+ /**
2
+ * Processes a single tick
3
+ * @this {SpessaSynthSequencer}
4
+ */
5
+ export function processTick()
6
+ {
7
+ if (!this.isActive)
8
+ {
9
+ return;
10
+ }
11
+ let current = this.currentTime;
12
+ while (this.playedTime < current)
13
+ {
14
+ // find the next event
15
+ let trackIndex = this._findFirstEventIndex();
16
+ let event = this.tracks[trackIndex][this.eventIndex[trackIndex]];
17
+ this._processEvent(event, trackIndex);
18
+
19
+ this.eventIndex[trackIndex]++;
20
+
21
+ // find the next event
22
+ trackIndex = this._findFirstEventIndex();
23
+ if (this.tracks[trackIndex].length <= this.eventIndex[trackIndex])
24
+ {
25
+ // the song has ended
26
+ if (this.loop)
27
+ {
28
+ this.setTimeTicks(this.midiData.loop.start);
29
+ return;
30
+ }
31
+ this.eventIndex[trackIndex]--;
32
+ this.pause(true);
33
+ if (this.songs.length > 1)
34
+ {
35
+ this.nextSong();
36
+ }
37
+ return;
38
+ }
39
+ let eventNext = this.tracks[trackIndex][this.eventIndex[trackIndex]];
40
+ this.playedTime += this.oneTickToSeconds * (eventNext.ticks - event.ticks);
41
+
42
+ const canLoop = this.loop && (this.loopCount > 0 || this.loopCount === -1);
43
+
44
+ // if we reached loop.end
45
+ if ((this.midiData.loop.end <= event.ticks) && canLoop)
46
+ {
47
+ // loop
48
+ if (this.loopCount !== Infinity)
49
+ {
50
+ this.loopCount--;
51
+ this?.onLoopCountChange?.(this.loopCount);
52
+ }
53
+ this.setTimeTicks(this.midiData.loop.start);
54
+ return;
55
+ }
56
+ // if the song has ended
57
+ else if (current >= this.duration)
58
+ {
59
+ if (canLoop)
60
+ {
61
+ // loop
62
+ if (this.loopCount !== Infinity)
63
+ {
64
+ this.loopCount--;
65
+ this?.onLoopCountChange?.(this.loopCount);
66
+ }
67
+ this.setTimeTicks(this.midiData.loop.start);
68
+ return;
69
+ }
70
+ // stop the playback
71
+ this.eventIndex[trackIndex]--;
72
+ this.pause(true);
73
+ if (this.songs.length > 1)
74
+ {
75
+ this.nextSong();
76
+ }
77
+ return;
78
+ }
79
+ }
80
+ }
81
+
82
+
83
+ /**
84
+ * @returns {number} the index of the first to the current played time
85
+ * @this {SpessaSynthSequencer}
86
+ */
87
+ export function _findFirstEventIndex()
88
+ {
89
+ let index = 0;
90
+ let ticks = Infinity;
91
+ this.tracks.forEach((track, i) =>
92
+ {
93
+ if (this.eventIndex[i] >= track.length)
94
+ {
95
+ return;
96
+ }
97
+ if (track[this.eventIndex[i]].ticks < ticks)
98
+ {
99
+ index = i;
100
+ ticks = track[this.eventIndex[i]].ticks;
101
+ }
102
+ });
103
+ return index;
85
104
  }
@@ -0,0 +1,367 @@
1
+ import { _addNewMidiPort, _processEvent } from "./process_event.js";
2
+ import { _findFirstEventIndex, processTick } from "./process_tick.js";
3
+ import { assignMIDIPort, loadNewSequence, loadNewSongList, nextSong, previousSong } from "./song_control.js";
4
+ import { _playTo, _recalculateStartTime, play, setTimeTicks } from "./play.js";
5
+ import { messageTypes, midiControllers } from "../midi/midi_message.js";
6
+ import { sendMIDICC, sendMIDIMessage, sendMIDIPitchWheel, sendMIDIProgramChange, sendMIDIReset } from "./events.js";
7
+ import { SpessaSynthWarn } from "../utils/loggin.js";
8
+
9
+ import { MIDI_CHANNEL_COUNT } from "../synthetizer/synth_constants.js";
10
+
11
+ class SpessaSynthSequencer
12
+ {
13
+ /**
14
+ * All the sequencer's songs
15
+ * @type {BasicMIDI[]}
16
+ */
17
+ songs = [];
18
+
19
+ /**
20
+ * Current song index
21
+ * @type {number}
22
+ */
23
+ songIndex = 0;
24
+
25
+ /**
26
+ * shuffled song indexes
27
+ * @type {number[]}
28
+ */
29
+ shuffledSongIndexes = [];
30
+
31
+ /**
32
+ * the synth to use
33
+ * @type {SpessaSynthProcessor}
34
+ */
35
+ synth;
36
+
37
+ /**
38
+ * if the sequencer is active
39
+ * @type {boolean}
40
+ */
41
+ isActive = false;
42
+
43
+ /**
44
+ * If the event should instead be sent back to the main thread instead of synth
45
+ * @type {boolean}
46
+ */
47
+ sendMIDIMessages = false;
48
+
49
+ /**
50
+ * sequencer's loop count
51
+ * @type {number}
52
+ */
53
+ loopCount = Infinity;
54
+
55
+ /**
56
+ * event's number in this.events
57
+ * @type {number[]}
58
+ */
59
+ eventIndex = [];
60
+
61
+ /**
62
+ * tracks the time that has already been played
63
+ * @type {number}
64
+ */
65
+ playedTime = 0;
66
+
67
+ /**
68
+ * The (relative) time when the sequencer was paused. If it's not paused, then it's undefined.
69
+ * @type {number}
70
+ */
71
+ pausedTime = undefined;
72
+
73
+ /**
74
+ * Absolute playback startTime, bases on the synth's time
75
+ * @type {number}
76
+ */
77
+ absoluteStartTime = 0;
78
+ /**
79
+ * Currently playing notes (for pausing and resuming)
80
+ * @type {{
81
+ * midiNote: number,
82
+ * channel: number,
83
+ * velocity: number
84
+ * }[]}
85
+ */
86
+ playingNotes = [];
87
+
88
+ /**
89
+ * controls if the sequencer loops (defaults to true)
90
+ * @type {boolean}
91
+ */
92
+ loop = true;
93
+
94
+ /**
95
+ * controls if the songs are ordered randomly
96
+ * @type {boolean}
97
+ */
98
+ shuffleMode = false;
99
+
100
+ /**
101
+ * the current track data
102
+ * @type {BasicMIDI}
103
+ */
104
+ midiData = undefined;
105
+
106
+ /**
107
+ * midi port number for the corresponding track
108
+ * @type {number[]}
109
+ */
110
+ midiPorts = [];
111
+ midiPortChannelOffset = 0;
112
+ /**
113
+ * stored as:
114
+ * Object<midi port, channel offset>
115
+ * @type {Object<number, number>}
116
+ */
117
+ midiPortChannelOffsets = {};
118
+
119
+ /**
120
+ * @type {boolean}
121
+ */
122
+ skipToFirstNoteOn = true;
123
+
124
+ /**
125
+ * If true, seq will stay paused when seeking or changing the playback rate
126
+ * @type {boolean}
127
+ */
128
+ preservePlaybackState = false;
129
+
130
+ /**
131
+ * Called on a MIDI message if sending MIDI messages is enabled
132
+ * @type {function(message: number[])}
133
+ */
134
+ onMIDIMessage;
135
+
136
+ /**
137
+ * Called when the time changes
138
+ * @type {function(newTime: number)}
139
+ */
140
+ onTimeChange;
141
+
142
+ /**
143
+ * Calls when sequencer stops the playback
144
+ * @type {function(isFinished: boolean)}
145
+ */
146
+ onPlaybackStop;
147
+
148
+ /**
149
+ * Calls after the songs have been processed but before the playback begins
150
+ * @type {function(newSongList: BasicMIDI[])}
151
+ */
152
+ onSongListChange;
153
+
154
+ /**
155
+ * Calls when the song is changed (for example, in a playlist)
156
+ * @type {function(songIndex: number, autoPlay: boolean)}
157
+ */
158
+ onSongChange;
159
+
160
+ /**
161
+ * Calls when a meta-event occurs
162
+ * @type {function(e: MIDIMessage, trackIndex: number)}
163
+ */
164
+ onMetaEvent;
165
+
166
+ /**
167
+ * Calls when the loop count changes (usually decreases)
168
+ * @type {function(count: number)}
169
+ */
170
+ onLoopCountChange;
171
+
172
+ /**
173
+ * @param spessasynthProcessor {SpessaSynthProcessor}
174
+ */
175
+ constructor(spessasynthProcessor)
176
+ {
177
+ this.synth = spessasynthProcessor;
178
+ this.absoluteStartTime = this.synth.currentSynthTime;
179
+ }
180
+
181
+ /**
182
+ * Controls the playback's rate
183
+ * @type {number}
184
+ * @private
185
+ */
186
+ _playbackRate = 1;
187
+
188
+ /**
189
+ * @param value {number}
190
+ */
191
+ set playbackRate(value)
192
+ {
193
+ const time = this.currentTime;
194
+ this._playbackRate = value;
195
+ this.currentTime = time;
196
+ }
197
+
198
+ get currentTime()
199
+ {
200
+ // return the paused time if it's set to something other than undefined
201
+ if (this.pausedTime !== undefined)
202
+ {
203
+ return this.pausedTime;
204
+ }
205
+
206
+ return (this.synth.currentSynthTime - this.absoluteStartTime) * this._playbackRate;
207
+ }
208
+
209
+ set currentTime(time)
210
+ {
211
+ if (time > this.duration || time < 0)
212
+ {
213
+ // time is 0
214
+ if (this.skipToFirstNoteOn)
215
+ {
216
+ this.setTimeTicks(this.midiData.firstNoteOn - 1);
217
+ }
218
+ else
219
+ {
220
+ this.setTimeTicks(0);
221
+ }
222
+ return;
223
+ }
224
+ if (this.skipToFirstNoteOn)
225
+ {
226
+ if (time < this.firstNoteTime)
227
+ {
228
+ this.setTimeTicks(this.midiData.firstNoteOn - 1);
229
+ return;
230
+ }
231
+ }
232
+ this.stop();
233
+ this.playingNotes = [];
234
+ const wasPaused = this.paused && this.preservePlaybackState;
235
+ this.pausedTime = undefined;
236
+ this?.onTimeChange?.(time);
237
+ if (this.midiData.duration === 0)
238
+ {
239
+ SpessaSynthWarn("No duration!");
240
+ this?.onPlaybackStop?.(true);
241
+ return;
242
+ }
243
+ this._playTo(time);
244
+ this._recalculateStartTime(time);
245
+ if (wasPaused)
246
+ {
247
+ this.pause();
248
+ }
249
+ else
250
+ {
251
+ this.play();
252
+ }
253
+ }
254
+
255
+ /**
256
+ * true if paused, false if playing or stopped
257
+ * @returns {boolean}
258
+ */
259
+ get paused()
260
+ {
261
+ return this.pausedTime !== undefined;
262
+ }
263
+
264
+ /**
265
+ * Pauses the playback
266
+ * @param isFinished {boolean}
267
+ */
268
+ pause(isFinished = false)
269
+ {
270
+ if (this.paused)
271
+ {
272
+ SpessaSynthWarn("Already paused");
273
+ return;
274
+ }
275
+ this.pausedTime = this.currentTime;
276
+ this.stop();
277
+ this?.onPlaybackStop?.(isFinished);
278
+ }
279
+
280
+ /**
281
+ * Stops the playback
282
+ */
283
+ stop()
284
+ {
285
+ this.clearProcessHandler();
286
+ // disable sustain
287
+ for (let i = 0; i < 16; i++)
288
+ {
289
+ this.synth.controllerChange(i, midiControllers.sustainPedal, 0);
290
+ }
291
+ this.synth.stopAllChannels();
292
+ if (this.sendMIDIMessages)
293
+ {
294
+ for (let note of this.playingNotes)
295
+ {
296
+ this.sendMIDIMessage([messageTypes.noteOff | (note.channel % 16), note.midiNote]);
297
+ }
298
+ for (let c = 0; c < MIDI_CHANNEL_COUNT; c++)
299
+ {
300
+ this.sendMIDICC(c, midiControllers.allNotesOff, 0);
301
+ }
302
+ }
303
+ }
304
+
305
+ loadCurrentSong(autoPlay = true)
306
+ {
307
+ let index = this.songIndex;
308
+ if (this.shuffleMode)
309
+ {
310
+ index = this.shuffledSongIndexes[this.songIndex];
311
+ }
312
+ this.loadNewSequence(this.songs[index], autoPlay);
313
+ }
314
+
315
+ _resetTimers()
316
+ {
317
+ this.playedTime = 0;
318
+ this.eventIndex = Array(this.tracks.length).fill(0);
319
+ }
320
+
321
+ setProcessHandler()
322
+ {
323
+ this.isActive = true;
324
+ }
325
+
326
+ clearProcessHandler()
327
+ {
328
+ this.isActive = false;
329
+ }
330
+
331
+ shuffleSongIndexes()
332
+ {
333
+ const indexes = this.songs.map((_, i) => i);
334
+ this.shuffledSongIndexes = [];
335
+ while (indexes.length > 0)
336
+ {
337
+ const index = indexes[Math.floor(Math.random() * indexes.length)];
338
+ this.shuffledSongIndexes.push(index);
339
+ indexes.splice(indexes.indexOf(index), 1);
340
+ }
341
+ }
342
+ }
343
+
344
+ // Web MIDI sending
345
+ SpessaSynthSequencer.prototype.sendMIDIMessage = sendMIDIMessage;
346
+ SpessaSynthSequencer.prototype.sendMIDIReset = sendMIDIReset;
347
+ SpessaSynthSequencer.prototype.sendMIDICC = sendMIDICC;
348
+ SpessaSynthSequencer.prototype.sendMIDIProgramChange = sendMIDIProgramChange;
349
+ SpessaSynthSequencer.prototype.sendMIDIPitchWheel = sendMIDIPitchWheel;
350
+ SpessaSynthSequencer.prototype.assignMIDIPort = assignMIDIPort;
351
+
352
+ SpessaSynthSequencer.prototype._processEvent = _processEvent;
353
+ SpessaSynthSequencer.prototype._addNewMidiPort = _addNewMidiPort;
354
+ SpessaSynthSequencer.prototype.processTick = processTick;
355
+ SpessaSynthSequencer.prototype._findFirstEventIndex = _findFirstEventIndex;
356
+
357
+ SpessaSynthSequencer.prototype.loadNewSequence = loadNewSequence;
358
+ SpessaSynthSequencer.prototype.loadNewSongList = loadNewSongList;
359
+ SpessaSynthSequencer.prototype.nextSong = nextSong;
360
+ SpessaSynthSequencer.prototype.previousSong = previousSong;
361
+
362
+ SpessaSynthSequencer.prototype.play = play;
363
+ SpessaSynthSequencer.prototype._playTo = _playTo;
364
+ SpessaSynthSequencer.prototype.setTimeTicks = setTimeTicks;
365
+ SpessaSynthSequencer.prototype._recalculateStartTime = _recalculateStartTime;
366
+
367
+ export { SpessaSynthSequencer };