spessasynth_core 3.27.7 → 4.0.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 (157) hide show
  1. package/README.md +188 -116
  2. package/dist/index.d.ts +4057 -0
  3. package/dist/index.js +17188 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +23 -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 -48
  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 -214
  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 -27
  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 -813
  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,9 +0,0 @@
1
- ## This is the sequencer's folder.
2
-
3
- The code here is responsible for playing back the parsed MIDI sequence with the synthesizer.
4
-
5
- - `sequencer_engine.js` - the core sequencer engine
6
- - `play.js` - handles playback control and timing
7
- - `song_control.js` - manages song state and control
8
- - `process_event.js` - processes MIDI events during playback
9
- - `process_tick.js` - processes a single MIDI tick (think of it like a rendering quantum of the sequencer)
@@ -1,81 +0,0 @@
1
- import { messageTypes, midiControllers } from "../midi/midi_message.js";
2
-
3
- import { MIDI_CHANNEL_COUNT } from "../synthetizer/synth_constants.js";
4
-
5
- /**
6
- * @param message {number[]}
7
- * @this {SpessaSynthSequencer}
8
- */
9
- export function sendMIDIMessage(message)
10
- {
11
- if (!this.sendMIDIMessages)
12
- {
13
- return;
14
- }
15
- this?.onMIDIMessage?.(message);
16
- }
17
-
18
- /**
19
- * @this {SpessaSynthSequencer}
20
- * @param channel {number}
21
- * @param type {number}
22
- * @param value {number}
23
- */
24
- export function sendMIDICC(channel, type, value)
25
- {
26
- channel %= 16;
27
- if (!this.sendMIDIMessages)
28
- {
29
- return;
30
- }
31
- this.sendMIDIMessage([messageTypes.controllerChange | channel, type, value]);
32
- }
33
-
34
- /**
35
- * @this {SpessaSynthSequencer}
36
- * @param channel {number}
37
- * @param program {number}
38
- */
39
- export function sendMIDIProgramChange(channel, program)
40
- {
41
- channel %= 16;
42
- if (!this.sendMIDIMessages)
43
- {
44
- return;
45
- }
46
- this.sendMIDIMessage([messageTypes.programChange | channel, program]);
47
- }
48
-
49
- /**
50
- * Sets the pitch of the given channel
51
- * @this {SpessaSynthSequencer}
52
- * @param channel {number} usually 0-15: the channel to change pitch
53
- * @param MSB {number} SECOND byte of the MIDI pitchWheel message
54
- * @param LSB {number} FIRST byte of the MIDI pitchWheel message
55
- */
56
- export function sendMIDIPitchWheel(channel, MSB, LSB)
57
- {
58
- channel %= 16;
59
- if (!this.sendMIDIMessages)
60
- {
61
- return;
62
- }
63
- this.sendMIDIMessage([messageTypes.pitchBend | channel, LSB, MSB]);
64
- }
65
-
66
- /**
67
- * @this {SpessaSynthSequencer}
68
- */
69
- export function sendMIDIReset()
70
- {
71
- if (!this.sendMIDIMessages)
72
- {
73
- return;
74
- }
75
- this.sendMIDIMessage([messageTypes.reset]);
76
- for (let ch = 0; ch < MIDI_CHANNEL_COUNT; ch++)
77
- {
78
- this.sendMIDIMessage([messageTypes.controllerChange | ch, midiControllers.allSoundOff, 0]);
79
- this.sendMIDIMessage([messageTypes.controllerChange | ch, midiControllers.resetAllControllers, 0]);
80
- }
81
- }
@@ -1,362 +0,0 @@
1
- import { getEvent, messageTypes, midiControllers } from "../midi/midi_message.js";
2
- import { resetArray } from "../synthetizer/audio_engine/engine_components/controller_tables.js";
3
- import { nonResetableCCs } from "../synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js";
4
-
5
-
6
- // an array with preset default values
7
- const defaultControllerArray = resetArray.slice(0, 128);
8
-
9
- /**
10
- * plays from start to the target time, excluding note messages (to get the synth to the correct state)
11
- * @private
12
- * @param time {number} in seconds
13
- * @param ticks {number} optional MIDI ticks, when given is used instead of time
14
- * @returns {boolean} true if the midi file is not finished
15
- * @this {SpessaSynthSequencer}
16
- */
17
- export function _playTo(time, ticks = undefined)
18
- {
19
- this.oneTickToSeconds = 60 / (120 * this.midiData.timeDivision);
20
- // reset
21
- this.synth.resetAllControllers();
22
- this.sendMIDIReset();
23
- this._resetTimers();
24
-
25
- const channelsToSave = this.synth.midiAudioChannels.length;
26
- /**
27
- * save pitch bends here and send them only after
28
- * @type {number[]}
29
- */
30
- const pitchBends = Array(channelsToSave).fill(8192);
31
-
32
- /**
33
- * Save programs here and send them only after
34
- * @type {{program: number, bank: number, actualBank: number}[]}
35
- */
36
- const programs = [];
37
- for (let i = 0; i < channelsToSave; i++)
38
- {
39
- programs.push({
40
- program: -1,
41
- bank: 0,
42
- actualBank: 0
43
- });
44
- }
45
-
46
- const isCCNonSkippable = controllerNumber => (
47
- controllerNumber === midiControllers.dataDecrement ||
48
- controllerNumber === midiControllers.dataIncrement ||
49
- controllerNumber === midiControllers.dataEntryMsb ||
50
- controllerNumber === midiControllers.dataDecrement ||
51
- controllerNumber === midiControllers.lsbForControl6DataEntry ||
52
- controllerNumber === midiControllers.RPNLsb ||
53
- controllerNumber === midiControllers.RPNMsb ||
54
- controllerNumber === midiControllers.NRPNLsb ||
55
- controllerNumber === midiControllers.NRPNMsb ||
56
- controllerNumber === midiControllers.bankSelect ||
57
- controllerNumber === midiControllers.lsbForControl0BankSelect ||
58
- controllerNumber === midiControllers.resetAllControllers
59
- );
60
-
61
- /**
62
- * Save controllers here and send them only after
63
- * @type {number[][]}
64
- */
65
- const savedControllers = [];
66
- for (let i = 0; i < channelsToSave; i++)
67
- {
68
- savedControllers.push(Array.from(defaultControllerArray));
69
- }
70
-
71
- /**
72
- * RP-15 compliant reset
73
- * https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
74
- * @param chan {number}
75
- */
76
- function resetAllControlllers(chan)
77
- {
78
- // reset pitch bend
79
- pitchBends[chan] = 8192;
80
- if (savedControllers?.[chan] === undefined)
81
- {
82
- return;
83
- }
84
- for (let i = 0; i < defaultControllerArray.length; i++)
85
- {
86
- if (!nonResetableCCs.has(i))
87
- {
88
- savedControllers[chan][i] = defaultControllerArray[i];
89
- }
90
- }
91
- }
92
-
93
- while (true)
94
- {
95
- // find the next event
96
- let trackIndex = this._findFirstEventIndex();
97
- let event = this.tracks[trackIndex][this.eventIndex[trackIndex]];
98
- if (ticks !== undefined)
99
- {
100
- if (event.ticks >= ticks)
101
- {
102
- break;
103
- }
104
- }
105
- else
106
- {
107
- if (this.playedTime >= time)
108
- {
109
- break;
110
- }
111
- }
112
-
113
- // skip note ons
114
- const info = getEvent(event.messageStatusByte);
115
- // Keep in mind midi ports to determine the channel!
116
- const channel = info.channel + (this.midiPortChannelOffsets[this.midiPorts[trackIndex]] || 0);
117
- switch (info.status)
118
- {
119
- // skip note messages
120
- case messageTypes.noteOn:
121
- // track portamento control as last note
122
- if (savedControllers[channel] === undefined)
123
- {
124
- savedControllers[channel] = Array.from(defaultControllerArray);
125
- }
126
- savedControllers[channel][midiControllers.portamentoControl] = event.messageData[0];
127
- break;
128
-
129
- case messageTypes.noteOff:
130
- break;
131
-
132
- // skip pitch bend
133
- case messageTypes.pitchBend:
134
- pitchBends[channel] = event.messageData[1] << 7 | event.messageData[0];
135
- break;
136
-
137
- case messageTypes.programChange:
138
- // empty tracks cannot program change
139
- if (this.midiData.isMultiPort && this.midiData.usedChannelsOnTrack[trackIndex].size === 0)
140
- {
141
- break;
142
- }
143
- const p = programs[channel];
144
- p.program = event.messageData[0];
145
- p.actualBank = p.bank;
146
- break;
147
-
148
- case messageTypes.controllerChange:
149
- // empty tracks cannot controller change
150
- if (this.midiData.isMultiPort && this.midiData.usedChannelsOnTrack[trackIndex].size === 0)
151
- {
152
- break;
153
- }
154
- // do not skip data entries
155
- const controllerNumber = event.messageData[0];
156
- if (isCCNonSkippable(controllerNumber))
157
- {
158
- let ccV = event.messageData[1];
159
- if (controllerNumber === midiControllers.bankSelect)
160
- {
161
- // add the bank to be saved
162
- programs[channel].bank = ccV;
163
- break;
164
- }
165
- else if (controllerNumber === midiControllers.resetAllControllers)
166
- {
167
- resetAllControlllers(channel);
168
- }
169
- if (this.sendMIDIMessages)
170
- {
171
- this.sendMIDICC(channel, controllerNumber, ccV);
172
- }
173
- else
174
- {
175
- this.synth.controllerChange(channel, controllerNumber, ccV);
176
- }
177
- }
178
- else
179
- {
180
- if (savedControllers[channel] === undefined)
181
- {
182
- savedControllers[channel] = Array.from(defaultControllerArray);
183
- }
184
- savedControllers[channel][controllerNumber] = event.messageData[1];
185
- }
186
- break;
187
-
188
- default:
189
- this._processEvent(event, trackIndex);
190
- break;
191
- }
192
-
193
- this.eventIndex[trackIndex]++;
194
- // find the next event
195
- trackIndex = this._findFirstEventIndex();
196
- let nextEvent = this.tracks[trackIndex][this.eventIndex[trackIndex]];
197
- if (nextEvent === undefined)
198
- {
199
- this.stop();
200
- return false;
201
- }
202
- this.playedTime += this.oneTickToSeconds * (nextEvent.ticks - event.ticks);
203
- }
204
-
205
- // restoring saved controllers
206
- if (this.sendMIDIMessages)
207
- {
208
- for (let channelNumber = 0; channelNumber < channelsToSave; channelNumber++)
209
- {
210
- // restore pitch bends
211
- if (pitchBends[channelNumber] !== undefined)
212
- {
213
- this.sendMIDIPitchWheel(
214
- channelNumber,
215
- pitchBends[channelNumber] >> 7,
216
- pitchBends[channelNumber] & 0x7F
217
- );
218
- }
219
- if (savedControllers[channelNumber] !== undefined)
220
- {
221
- // every controller that has changed
222
- savedControllers[channelNumber].forEach((value, index) =>
223
- {
224
- if (value !== defaultControllerArray[index] && !isCCNonSkippable(
225
- index))
226
- {
227
- this.sendMIDICC(channelNumber, index, value);
228
- }
229
- });
230
- }
231
- // restore programs
232
- if (programs[channelNumber].program >= 0 && programs[channelNumber].actualBank >= 0)
233
- {
234
- const bank = programs[channelNumber].actualBank;
235
- this.sendMIDICC(channelNumber, midiControllers.bankSelect, bank);
236
- this.sendMIDIProgramChange(channelNumber, programs[channelNumber].program);
237
- }
238
- }
239
- }
240
- else
241
- {
242
- // for all synth channels
243
- for (let channelNumber = 0; channelNumber < channelsToSave; channelNumber++)
244
- {
245
- // restore pitch bends
246
- if (pitchBends[channelNumber] !== undefined)
247
- {
248
- this.synth.pitchWheel(channelNumber, pitchBends[channelNumber] >> 7, pitchBends[channelNumber] & 0x7F);
249
- }
250
- if (savedControllers[channelNumber] !== undefined)
251
- {
252
- // every controller that has changed
253
- savedControllers[channelNumber].forEach((value, index) =>
254
- {
255
- if (value !== defaultControllerArray[index] && !isCCNonSkippable(
256
- index))
257
- {
258
- this.synth.controllerChange(
259
- channelNumber,
260
- index,
261
- value
262
- );
263
- }
264
- });
265
- }
266
- // restore programs
267
- if (programs[channelNumber].actualBank >= 0)
268
- {
269
- const p = programs[channelNumber];
270
- if (p.program !== -1)
271
- {
272
- // a program change has occurred, apply the actual bank when program change was executed
273
- this.synth.controllerChange(channelNumber, midiControllers.bankSelect, p.actualBank);
274
- this.synth.programChange(channelNumber, p.program);
275
- }
276
- else
277
- {
278
- // no program change, apply the current bank select
279
- this.synth.controllerChange(channelNumber, midiControllers.bankSelect, p.bank);
280
- }
281
- }
282
- }
283
- }
284
- return true;
285
- }
286
-
287
- /**
288
- * Starts the playback
289
- * @param resetTime {boolean} If true, time is set to 0 s
290
- * @this {SpessaSynthSequencer}
291
- */
292
- export function play(resetTime = false)
293
- {
294
- if (this.midiData === undefined)
295
- {
296
- return;
297
- }
298
-
299
- // reset the time if necessary
300
- if (resetTime)
301
- {
302
- this.pausedTime = undefined;
303
- this.currentTime = 0;
304
- return;
305
- }
306
-
307
- if (this.currentTime >= this.duration)
308
- {
309
- this.pausedTime = undefined;
310
- this.currentTime = 0;
311
- return;
312
- }
313
-
314
- // unpause if paused
315
- if (this.paused)
316
- {
317
- // adjust the start time
318
- this._recalculateStartTime(this.pausedTime);
319
- this.pausedTime = undefined;
320
- }
321
- if (!this.sendMIDIMessages)
322
- {
323
- this.playingNotes.forEach(n =>
324
- {
325
- this.synth.noteOn(n.channel, n.midiNote, n.velocity);
326
- });
327
- }
328
- this.setProcessHandler();
329
- }
330
-
331
- /**
332
- * @this {SpessaSynthSequencer}
333
- * @param ticks {number}
334
- */
335
- export function setTimeTicks(ticks)
336
- {
337
- if (!this.midiData)
338
- {
339
- return;
340
- }
341
- this.stop();
342
- this.playingNotes = [];
343
- this.pausedTime = undefined;
344
- this?.onTimeChange?.(this.midiData.MIDIticksToSeconds(ticks));
345
- const isNotFinished = this._playTo(0, ticks);
346
- this._recalculateStartTime(this.playedTime);
347
- if (!isNotFinished)
348
- {
349
- return;
350
- }
351
- this.play();
352
- }
353
-
354
- /**
355
- * @param time
356
- * @private
357
- * @this {SpessaSynthSequencer}
358
- */
359
- export function _recalculateStartTime(time)
360
- {
361
- this.absoluteStartTime = this.synth.currentSynthTime - time / this._playbackRate;
362
- }
@@ -1,165 +0,0 @@
1
- import { getEvent, messageTypes } from "../midi/midi_message.js";
2
- import { consoleColors } from "../utils/other.js";
3
- import { SpessaSynthWarn } from "../utils/loggin.js";
4
- import { readBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
5
-
6
- /**
7
- * Processes a single event
8
- * @param event {MIDIMessage}
9
- * @param trackIndex {number}
10
- * @this {SpessaSynthSequencer}
11
- * @private
12
- */
13
- export function _processEvent(event, trackIndex)
14
- {
15
- if (this.sendMIDIMessages)
16
- {
17
- if (event.messageStatusByte >= 0x80)
18
- {
19
- this.sendMIDIMessage([event.messageStatusByte, ...event.messageData]);
20
- return;
21
- }
22
- }
23
- const statusByteData = getEvent(event.messageStatusByte);
24
- const offset = this.midiPortChannelOffsets[this.midiPorts[trackIndex]] || 0;
25
- statusByteData.channel += offset;
26
- // process the event
27
- switch (statusByteData.status)
28
- {
29
- case messageTypes.noteOn:
30
- const velocity = event.messageData[1];
31
- if (velocity > 0)
32
- {
33
- this.synth.noteOn(statusByteData.channel, event.messageData[0], velocity);
34
- this.playingNotes.push({
35
- midiNote: event.messageData[0],
36
- channel: statusByteData.channel,
37
- velocity: velocity
38
- });
39
- }
40
- else
41
- {
42
- this.synth.noteOff(statusByteData.channel, event.messageData[0]);
43
- const toDelete = this.playingNotes.findIndex(n =>
44
- n.midiNote === event.messageData[0] && n.channel === statusByteData.channel);
45
- if (toDelete !== -1)
46
- {
47
- this.playingNotes.splice(toDelete, 1);
48
- }
49
- }
50
- break;
51
-
52
- case messageTypes.noteOff:
53
- this.synth.noteOff(statusByteData.channel, event.messageData[0]);
54
- const toDelete = this.playingNotes.findIndex(n =>
55
- n.midiNote === event.messageData[0] && n.channel === statusByteData.channel);
56
- if (toDelete !== -1)
57
- {
58
- this.playingNotes.splice(toDelete, 1);
59
- }
60
- break;
61
-
62
- case messageTypes.pitchBend:
63
- this.synth.pitchWheel(statusByteData.channel, event.messageData[1], event.messageData[0]);
64
- break;
65
-
66
- case messageTypes.controllerChange:
67
- // empty tracks cannot cc change
68
- if (this.midiData.isMultiPort && this.midiData.usedChannelsOnTrack[trackIndex].size === 0)
69
- {
70
- return;
71
- }
72
- this.synth.controllerChange(statusByteData.channel, event.messageData[0], event.messageData[1]);
73
- break;
74
-
75
- case messageTypes.programChange:
76
- // empty tracks cannot program change
77
- if (this.midiData.isMultiPort && this.midiData.usedChannelsOnTrack[trackIndex].size === 0)
78
- {
79
- return;
80
- }
81
- this.synth.programChange(statusByteData.channel, event.messageData[0]);
82
- break;
83
-
84
- case messageTypes.polyPressure:
85
- this.synth.polyPressure(statusByteData.channel, event.messageData[0], event.messageData[1]);
86
- break;
87
-
88
- case messageTypes.channelPressure:
89
- this.synth.channelPressure(statusByteData.channel, event.messageData[0]);
90
- break;
91
-
92
- case messageTypes.systemExclusive:
93
- this.synth.systemExclusive(event.messageData, offset);
94
- break;
95
-
96
- case messageTypes.setTempo:
97
- event.messageData.currentIndex = 0;
98
- let tempoBPM = 60000000 / readBytesAsUintBigEndian(event.messageData, 3);
99
- this.oneTickToSeconds = 60 / (tempoBPM * this.midiData.timeDivision);
100
- if (this.oneTickToSeconds === 0)
101
- {
102
- this.oneTickToSeconds = 60 / (120 * this.midiData.timeDivision);
103
- SpessaSynthWarn("invalid tempo! falling back to 120 BPM");
104
- tempoBPM = 120;
105
- }
106
- break;
107
-
108
- // recognized but ignored
109
- case messageTypes.timeSignature:
110
- case messageTypes.endOfTrack:
111
- case messageTypes.midiChannelPrefix:
112
- case messageTypes.songPosition:
113
- case messageTypes.activeSensing:
114
- case messageTypes.keySignature:
115
- case messageTypes.sequenceNumber:
116
- case messageTypes.sequenceSpecific:
117
- case messageTypes.text:
118
- case messageTypes.lyric:
119
- case messageTypes.copyright:
120
- case messageTypes.trackName:
121
- case messageTypes.marker:
122
- case messageTypes.cuePoint:
123
- case messageTypes.instrumentName:
124
- case messageTypes.programName:
125
- break;
126
-
127
-
128
- case messageTypes.midiPort:
129
- this.assignMIDIPort(trackIndex, event.messageData[0]);
130
- break;
131
-
132
- case messageTypes.reset:
133
- this.synth.stopAllChannels();
134
- this.synth.resetAllControllers();
135
- break;
136
-
137
- default:
138
- SpessaSynthWarn(
139
- `%cUnrecognized Event: %c${event.messageStatusByte}%c status byte: %c${Object.keys(
140
- messageTypes).find(k => messageTypes[k] === statusByteData.status)}`,
141
- consoleColors.warn,
142
- consoleColors.unrecognized,
143
- consoleColors.warn,
144
- consoleColors.value
145
- );
146
- break;
147
- }
148
- if (statusByteData.status >= 0 && statusByteData.status < 0x80)
149
- {
150
- this?.onMetaEvent?.(event, trackIndex);
151
- }
152
- }
153
-
154
- /**
155
- * Adds 16 channels to the synth
156
- * @this {SpessaSynthSequencer}
157
- * @private
158
- */
159
- export function _addNewMidiPort()
160
- {
161
- for (let i = 0; i < 16; i++)
162
- {
163
- this.synth.createMidiChannel(true);
164
- }
165
- }