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.
- package/README.md +29 -114
- package/index.js +33 -33
- package/package.json +16 -6
- package/external_midi/README.md +0 -4
- package/external_midi/midi_handler.js +0 -130
- package/external_midi/web_midi_link.js +0 -43
- package/externals/fflate/LICENSE +0 -21
- package/externals/fflate/fflate.min.js +0 -1
- package/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +0 -12
- package/externals/stbvorbis_sync/LICENSE +0 -202
- package/externals/stbvorbis_sync/NOTICE +0 -6
- package/externals/stbvorbis_sync/stbvorbis_sync.min.js +0 -1
- package/midi/README.md +0 -32
- package/midi/basic_midi.js +0 -565
- package/midi/midi_builder.js +0 -202
- package/midi/midi_data.js +0 -63
- package/midi/midi_loader.js +0 -324
- package/midi/midi_message.js +0 -254
- package/midi/midi_sequence.js +0 -225
- package/midi/midi_tools/get_note_times.js +0 -154
- package/midi/midi_tools/midi_editor.js +0 -611
- package/midi/midi_tools/midi_writer.js +0 -99
- package/midi/midi_tools/rmidi_writer.js +0 -567
- package/midi/midi_tools/used_keys_loaded.js +0 -238
- package/midi/xmf_loader.js +0 -454
- package/sequencer/README.md +0 -32
- package/sequencer/sequencer_engine/events.js +0 -207
- package/sequencer/sequencer_engine/play.js +0 -353
- package/sequencer/sequencer_engine/process_event.js +0 -169
- package/sequencer/sequencer_engine/process_tick.js +0 -106
- package/sequencer/sequencer_engine/sequencer_engine.js +0 -337
- package/sequencer/sequencer_engine/song_control.js +0 -229
- package/sequencer/worklet_wrapper/default_sequencer_options.js +0 -8
- package/sequencer/worklet_wrapper/sequencer.js +0 -807
- package/sequencer/worklet_wrapper/sequencer_message.js +0 -53
- package/soundfont/README.md +0 -13
- package/soundfont/basic_soundfont/basic_instrument.js +0 -77
- package/soundfont/basic_soundfont/basic_preset.js +0 -336
- package/soundfont/basic_soundfont/basic_sample.js +0 -197
- package/soundfont/basic_soundfont/basic_soundfont.js +0 -565
- package/soundfont/basic_soundfont/basic_zone.js +0 -64
- package/soundfont/basic_soundfont/basic_zones.js +0 -43
- package/soundfont/basic_soundfont/generator.js +0 -220
- package/soundfont/basic_soundfont/modulator.js +0 -378
- package/soundfont/basic_soundfont/riff_chunk.js +0 -149
- package/soundfont/basic_soundfont/write_dls/art2.js +0 -173
- package/soundfont/basic_soundfont/write_dls/articulator.js +0 -49
- package/soundfont/basic_soundfont/write_dls/combine_zones.js +0 -400
- package/soundfont/basic_soundfont/write_dls/ins.js +0 -103
- package/soundfont/basic_soundfont/write_dls/lins.js +0 -18
- package/soundfont/basic_soundfont/write_dls/modulator_converter.js +0 -330
- package/soundfont/basic_soundfont/write_dls/rgn2.js +0 -121
- package/soundfont/basic_soundfont/write_dls/wave.js +0 -94
- package/soundfont/basic_soundfont/write_dls/write_dls.js +0 -119
- package/soundfont/basic_soundfont/write_dls/wsmp.js +0 -78
- package/soundfont/basic_soundfont/write_dls/wvpl.js +0 -32
- package/soundfont/basic_soundfont/write_sf2/ibag.js +0 -39
- package/soundfont/basic_soundfont/write_sf2/igen.js +0 -80
- package/soundfont/basic_soundfont/write_sf2/imod.js +0 -46
- package/soundfont/basic_soundfont/write_sf2/inst.js +0 -34
- package/soundfont/basic_soundfont/write_sf2/pbag.js +0 -39
- package/soundfont/basic_soundfont/write_sf2/pgen.js +0 -82
- package/soundfont/basic_soundfont/write_sf2/phdr.js +0 -42
- package/soundfont/basic_soundfont/write_sf2/pmod.js +0 -46
- package/soundfont/basic_soundfont/write_sf2/sdta.js +0 -80
- package/soundfont/basic_soundfont/write_sf2/shdr.js +0 -55
- package/soundfont/basic_soundfont/write_sf2/write.js +0 -222
- package/soundfont/dls/articulator_converter.js +0 -396
- package/soundfont/dls/dls_destinations.js +0 -38
- package/soundfont/dls/dls_preset.js +0 -44
- package/soundfont/dls/dls_sample.js +0 -75
- package/soundfont/dls/dls_soundfont.js +0 -186
- package/soundfont/dls/dls_sources.js +0 -62
- package/soundfont/dls/dls_zone.js +0 -95
- package/soundfont/dls/read_articulation.js +0 -299
- package/soundfont/dls/read_instrument.js +0 -121
- package/soundfont/dls/read_instrument_list.js +0 -17
- package/soundfont/dls/read_lart.js +0 -35
- package/soundfont/dls/read_region.js +0 -152
- package/soundfont/dls/read_samples.js +0 -270
- package/soundfont/load_soundfont.js +0 -21
- package/soundfont/read_sf2/generators.js +0 -46
- package/soundfont/read_sf2/instruments.js +0 -66
- package/soundfont/read_sf2/modulators.js +0 -36
- package/soundfont/read_sf2/presets.js +0 -80
- package/soundfont/read_sf2/samples.js +0 -304
- package/soundfont/read_sf2/soundfont.js +0 -305
- package/soundfont/read_sf2/zones.js +0 -263
- package/synthetizer/README.md +0 -10
- package/synthetizer/audio_effects/effects_config.js +0 -25
- package/synthetizer/audio_effects/fancy_chorus.js +0 -162
- package/synthetizer/audio_effects/rb_compressed.min.js +0 -1
- package/synthetizer/audio_effects/reverb.js +0 -35
- package/synthetizer/audio_effects/reverb_as_binary.js +0 -18
- package/synthetizer/audio_engine/README.md +0 -25
- package/synthetizer/audio_engine/engine_components/compute_modulator.js +0 -266
- package/synthetizer/audio_engine/engine_components/controller_tables.js +0 -88
- package/synthetizer/audio_engine/engine_components/key_modifier_manager.js +0 -149
- package/synthetizer/audio_engine/engine_components/lfo.js +0 -26
- package/synthetizer/audio_engine/engine_components/lowpass_filter.js +0 -282
- package/synthetizer/audio_engine/engine_components/midi_audio_channel.js +0 -471
- package/synthetizer/audio_engine/engine_components/modulation_envelope.js +0 -181
- package/synthetizer/audio_engine/engine_components/modulator_curves.js +0 -89
- package/synthetizer/audio_engine/engine_components/soundfont_manager/sfman_message.js +0 -9
- package/synthetizer/audio_engine/engine_components/soundfont_manager/soundfont_manager.js +0 -254
- package/synthetizer/audio_engine/engine_components/stereo_panner.js +0 -120
- package/synthetizer/audio_engine/engine_components/unit_converter.js +0 -73
- package/synthetizer/audio_engine/engine_components/voice.js +0 -519
- package/synthetizer/audio_engine/engine_components/volume_envelope.js +0 -401
- package/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +0 -263
- package/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +0 -132
- package/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +0 -48
- package/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -241
- package/synthetizer/audio_engine/engine_methods/create_midi_channel.js +0 -27
- package/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +0 -253
- package/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +0 -66
- package/synthetizer/audio_engine/engine_methods/mute_channel.js +0 -17
- package/synthetizer/audio_engine/engine_methods/note_on.js +0 -174
- package/synthetizer/audio_engine/engine_methods/portamento_time.js +0 -92
- package/synthetizer/audio_engine/engine_methods/program_change.js +0 -61
- package/synthetizer/audio_engine/engine_methods/render_voice.js +0 -196
- package/synthetizer/audio_engine/engine_methods/soundfont_management/clear_sound_font.js +0 -30
- package/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +0 -22
- package/synthetizer/audio_engine/engine_methods/soundfont_management/reload_sound_font.js +0 -40
- package/synthetizer/audio_engine/engine_methods/soundfont_management/send_preset_list.js +0 -34
- package/synthetizer/audio_engine/engine_methods/soundfont_management/set_embedded_sound_font.js +0 -21
- package/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +0 -20
- package/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +0 -55
- package/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +0 -16
- package/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +0 -30
- package/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +0 -63
- package/synthetizer/audio_engine/engine_methods/system_exclusive.js +0 -744
- package/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +0 -24
- package/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +0 -33
- package/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +0 -31
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +0 -15
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +0 -27
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +0 -19
- package/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +0 -27
- package/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +0 -15
- package/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +0 -34
- package/synthetizer/audio_engine/main_processor.js +0 -765
- package/synthetizer/audio_engine/message_protocol/README.md +0 -13
- package/synthetizer/audio_engine/message_protocol/message_sending.js +0 -22
- package/synthetizer/audio_engine/message_protocol/worklet_message.js +0 -107
- package/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +0 -14
- package/synthetizer/audio_engine/snapshot/channel_snapshot.js +0 -175
- package/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +0 -122
- package/synthetizer/synth_constants.js +0 -20
- package/synthetizer/worklet_processor.min.js +0 -21
- package/synthetizer/worklet_wrapper/key_modifier_manager.js +0 -104
- package/synthetizer/worklet_wrapper/synth_event_handler.js +0 -214
- package/synthetizer/worklet_wrapper/synth_soundfont_manager.js +0 -111
- package/synthetizer/worklet_wrapper/synthetizer.js +0 -1027
- package/synthetizer/worklet_wrapper/worklet_processor.js +0 -340
- package/synthetizer/worklet_wrapper/worklet_url.js +0 -16
- package/utils/README.md +0 -5
- package/utils/buffer_to_wav.js +0 -186
- package/utils/byte_functions/big_endian.js +0 -32
- package/utils/byte_functions/little_endian.js +0 -77
- package/utils/byte_functions/string.js +0 -107
- package/utils/byte_functions/variable_length_quantity.js +0 -42
- package/utils/fill_with_defaults.js +0 -21
- package/utils/indexed_array.js +0 -52
- package/utils/loggin.js +0 -79
- package/utils/other.js +0 -92
- package/utils/sysex_detector.js +0 -58
- package/utils/xg_hacks.js +0 -193
package/sequencer/README.md
DELETED
|
@@ -1,32 +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` - the core sequencer engine, currently runs in audio worklet
|
|
6
|
-
|
|
7
|
-
### Message protocol:
|
|
8
|
-
|
|
9
|
-
#### Message structure
|
|
10
|
-
|
|
11
|
-
```js
|
|
12
|
-
const message = {
|
|
13
|
-
messageType: number, // WorkletSequencerMessageType
|
|
14
|
-
messageData: any // any
|
|
15
|
-
}
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
#### To worklet
|
|
19
|
-
|
|
20
|
-
Sequencer uses `Synthetizer`'s `post` method to post a message with `messageData` set to
|
|
21
|
-
`workletMessageType.sequencerSpecific`.
|
|
22
|
-
The `messageData` is set to the sequencer's message.
|
|
23
|
-
|
|
24
|
-
#### From worklet
|
|
25
|
-
|
|
26
|
-
`WorkletSequencer` uses `SpessaSynthProcessor`'s post to send a message with `messageData` set to
|
|
27
|
-
`returnMessageType.sequencerSpecific`.
|
|
28
|
-
The `messageData` is set to the sequencer's return message.
|
|
29
|
-
|
|
30
|
-
### Process tick
|
|
31
|
-
|
|
32
|
-
`processTick` is called every time the `process` method is called via `SpessaSynthProcessor.processTickCallback`.
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ALL_CHANNELS_OR_DIFFERENT_ACTION,
|
|
3
|
-
returnMessageType
|
|
4
|
-
} from "../../synthetizer/audio_engine/message_protocol/worklet_message.js";
|
|
5
|
-
import {
|
|
6
|
-
SongChangeType,
|
|
7
|
-
SpessaSynthSequencerMessageType,
|
|
8
|
-
SpessaSynthSequencerReturnMessageType
|
|
9
|
-
} from "../worklet_wrapper/sequencer_message.js";
|
|
10
|
-
import { messageTypes, midiControllers } from "../../midi/midi_message.js";
|
|
11
|
-
|
|
12
|
-
import { MIDI_CHANNEL_COUNT } from "../../synthetizer/synth_constants.js";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @param messageType {SpessaSynthSequencerMessageType}
|
|
16
|
-
* @param messageData {any}
|
|
17
|
-
* @this {SpessaSynthSequencer}
|
|
18
|
-
*/
|
|
19
|
-
export function processMessage(messageType, messageData)
|
|
20
|
-
{
|
|
21
|
-
switch (messageType)
|
|
22
|
-
{
|
|
23
|
-
default:
|
|
24
|
-
break;
|
|
25
|
-
|
|
26
|
-
case SpessaSynthSequencerMessageType.loadNewSongList:
|
|
27
|
-
this.loadNewSongList(messageData[0], messageData[1]);
|
|
28
|
-
break;
|
|
29
|
-
|
|
30
|
-
case SpessaSynthSequencerMessageType.pause:
|
|
31
|
-
this.pause();
|
|
32
|
-
break;
|
|
33
|
-
|
|
34
|
-
case SpessaSynthSequencerMessageType.play:
|
|
35
|
-
this.play(messageData);
|
|
36
|
-
break;
|
|
37
|
-
|
|
38
|
-
case SpessaSynthSequencerMessageType.stop:
|
|
39
|
-
this.stop();
|
|
40
|
-
break;
|
|
41
|
-
|
|
42
|
-
case SpessaSynthSequencerMessageType.setTime:
|
|
43
|
-
this.currentTime = messageData;
|
|
44
|
-
break;
|
|
45
|
-
|
|
46
|
-
case SpessaSynthSequencerMessageType.changeMIDIMessageSending:
|
|
47
|
-
this.sendMIDIMessages = messageData;
|
|
48
|
-
break;
|
|
49
|
-
|
|
50
|
-
case SpessaSynthSequencerMessageType.setPlaybackRate:
|
|
51
|
-
this.playbackRate = messageData;
|
|
52
|
-
break;
|
|
53
|
-
|
|
54
|
-
case SpessaSynthSequencerMessageType.setLoop:
|
|
55
|
-
const [loop, count] = messageData;
|
|
56
|
-
this.loop = loop;
|
|
57
|
-
if (count === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
58
|
-
{
|
|
59
|
-
this.loopCount = Infinity;
|
|
60
|
-
}
|
|
61
|
-
else
|
|
62
|
-
{
|
|
63
|
-
this.loopCount = count;
|
|
64
|
-
}
|
|
65
|
-
break;
|
|
66
|
-
|
|
67
|
-
case SpessaSynthSequencerMessageType.changeSong:
|
|
68
|
-
switch (messageData[0])
|
|
69
|
-
{
|
|
70
|
-
case SongChangeType.forwards:
|
|
71
|
-
this.nextSong();
|
|
72
|
-
break;
|
|
73
|
-
|
|
74
|
-
case SongChangeType.backwards:
|
|
75
|
-
this.previousSong();
|
|
76
|
-
break;
|
|
77
|
-
|
|
78
|
-
case SongChangeType.shuffleOff:
|
|
79
|
-
this.shuffleMode = false;
|
|
80
|
-
this.songIndex = this.shuffledSongIndexes[this.songIndex];
|
|
81
|
-
break;
|
|
82
|
-
|
|
83
|
-
case SongChangeType.shuffleOn:
|
|
84
|
-
this.shuffleMode = true;
|
|
85
|
-
this.shuffleSongIndexes();
|
|
86
|
-
this.songIndex = 0;
|
|
87
|
-
this.loadCurrentSong();
|
|
88
|
-
break;
|
|
89
|
-
|
|
90
|
-
case SongChangeType.index:
|
|
91
|
-
this.songIndex = messageData[1];
|
|
92
|
-
this.loadCurrentSong();
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
break;
|
|
96
|
-
|
|
97
|
-
case SpessaSynthSequencerMessageType.getMIDI:
|
|
98
|
-
this.post(SpessaSynthSequencerReturnMessageType.getMIDI, this.midiData);
|
|
99
|
-
break;
|
|
100
|
-
|
|
101
|
-
case SpessaSynthSequencerMessageType.setSkipToFirstNote:
|
|
102
|
-
this.skipToFirstNoteOn = messageData;
|
|
103
|
-
break;
|
|
104
|
-
|
|
105
|
-
case SpessaSynthSequencerMessageType.setPreservePlaybackState:
|
|
106
|
-
this.preservePlaybackState = messageData;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
*
|
|
112
|
-
* @param messageType {SpessaSynthSequencerReturnMessageType}
|
|
113
|
-
* @param messageData {any}
|
|
114
|
-
* @this {SpessaSynthSequencer}
|
|
115
|
-
*/
|
|
116
|
-
export function post(messageType, messageData = undefined)
|
|
117
|
-
{
|
|
118
|
-
if (!this.synth.enableEventSystem)
|
|
119
|
-
{
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
this.synth.post({
|
|
123
|
-
messageType: returnMessageType.sequencerSpecific,
|
|
124
|
-
messageData: {
|
|
125
|
-
messageType: messageType,
|
|
126
|
-
messageData: messageData
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* @param message {number[]}
|
|
133
|
-
* @this {SpessaSynthSequencer}
|
|
134
|
-
*/
|
|
135
|
-
export function sendMIDIMessage(message)
|
|
136
|
-
{
|
|
137
|
-
if (!this.sendMIDIMessages)
|
|
138
|
-
{
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
this.post(SpessaSynthSequencerReturnMessageType.midiEvent, message);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* @this {SpessaSynthSequencer}
|
|
146
|
-
* @param channel {number}
|
|
147
|
-
* @param type {number}
|
|
148
|
-
* @param value {number}
|
|
149
|
-
*/
|
|
150
|
-
export function sendMIDICC(channel, type, value)
|
|
151
|
-
{
|
|
152
|
-
channel %= 16;
|
|
153
|
-
if (!this.sendMIDIMessages)
|
|
154
|
-
{
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
this.sendMIDIMessage([messageTypes.controllerChange | channel, type, value]);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* @this {SpessaSynthSequencer}
|
|
162
|
-
* @param channel {number}
|
|
163
|
-
* @param program {number}
|
|
164
|
-
*/
|
|
165
|
-
export function sendMIDIProgramChange(channel, program)
|
|
166
|
-
{
|
|
167
|
-
channel %= 16;
|
|
168
|
-
if (!this.sendMIDIMessages)
|
|
169
|
-
{
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
this.sendMIDIMessage([messageTypes.programChange | channel, program]);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Sets the pitch of the given channel
|
|
177
|
-
* @this {SpessaSynthSequencer}
|
|
178
|
-
* @param channel {number} usually 0-15: the channel to change pitch
|
|
179
|
-
* @param MSB {number} SECOND byte of the MIDI pitchWheel message
|
|
180
|
-
* @param LSB {number} FIRST byte of the MIDI pitchWheel message
|
|
181
|
-
*/
|
|
182
|
-
export function sendMIDIPitchWheel(channel, MSB, LSB)
|
|
183
|
-
{
|
|
184
|
-
channel %= 16;
|
|
185
|
-
if (!this.sendMIDIMessages)
|
|
186
|
-
{
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
this.sendMIDIMessage([messageTypes.pitchBend | channel, LSB, MSB]);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* @this {SpessaSynthSequencer}
|
|
194
|
-
*/
|
|
195
|
-
export function sendMIDIReset()
|
|
196
|
-
{
|
|
197
|
-
if (!this.sendMIDIMessages)
|
|
198
|
-
{
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
this.sendMIDIMessage([messageTypes.reset]);
|
|
202
|
-
for (let ch = 0; ch < MIDI_CHANNEL_COUNT; ch++)
|
|
203
|
-
{
|
|
204
|
-
this.sendMIDIMessage([messageTypes.controllerChange | ch, midiControllers.allSoundOff, 0]);
|
|
205
|
-
this.sendMIDIMessage([messageTypes.controllerChange | ch, midiControllers.resetAllControllers, 0]);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
import { getEvent, messageTypes, midiControllers } from "../../midi/midi_message.js";
|
|
2
|
-
import { SpessaSynthSequencerReturnMessageType } from "../worklet_wrapper/sequencer_message.js";
|
|
3
|
-
import { resetArray } from "../../synthetizer/audio_engine/engine_components/controller_tables.js";
|
|
4
|
-
import { nonResetableCCs } from "../../synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// an array with preset default values
|
|
8
|
-
const defaultControllerArray = resetArray.slice(0, 128);
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* plays from start to the target time, excluding note messages (to get the synth to the correct state)
|
|
12
|
-
* @private
|
|
13
|
-
* @param time {number} in seconds
|
|
14
|
-
* @param ticks {number} optional MIDI ticks, when given is used instead of time
|
|
15
|
-
* @returns {boolean} true if the midi file is not finished
|
|
16
|
-
* @this {SpessaSynthSequencer}
|
|
17
|
-
*/
|
|
18
|
-
export function _playTo(time, ticks = undefined)
|
|
19
|
-
{
|
|
20
|
-
this.oneTickToSeconds = 60 / (120 * this.midiData.timeDivision);
|
|
21
|
-
// reset
|
|
22
|
-
this.synth.resetAllControllers();
|
|
23
|
-
this.sendMIDIReset();
|
|
24
|
-
this._resetTimers();
|
|
25
|
-
|
|
26
|
-
const channelsToSave = this.synth.midiAudioChannels.length;
|
|
27
|
-
/**
|
|
28
|
-
* save pitch bends here and send them only after
|
|
29
|
-
* @type {number[]}
|
|
30
|
-
*/
|
|
31
|
-
const pitchBends = Array(channelsToSave).fill(8192);
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Save programs here and send them only after
|
|
35
|
-
* @type {{program: number, bank: number, actualBank: number}[]}
|
|
36
|
-
*/
|
|
37
|
-
const programs = [];
|
|
38
|
-
for (let i = 0; i < channelsToSave; i++)
|
|
39
|
-
{
|
|
40
|
-
programs.push({
|
|
41
|
-
program: -1,
|
|
42
|
-
bank: 0,
|
|
43
|
-
actualBank: 0
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const isCCNonSkippable = controllerNumber => (
|
|
48
|
-
controllerNumber === midiControllers.dataDecrement ||
|
|
49
|
-
controllerNumber === midiControllers.dataIncrement ||
|
|
50
|
-
controllerNumber === midiControllers.dataEntryMsb ||
|
|
51
|
-
controllerNumber === midiControllers.dataDecrement ||
|
|
52
|
-
controllerNumber === midiControllers.lsbForControl6DataEntry ||
|
|
53
|
-
controllerNumber === midiControllers.RPNLsb ||
|
|
54
|
-
controllerNumber === midiControllers.RPNMsb ||
|
|
55
|
-
controllerNumber === midiControllers.NRPNLsb ||
|
|
56
|
-
controllerNumber === midiControllers.NRPNMsb ||
|
|
57
|
-
controllerNumber === midiControllers.bankSelect ||
|
|
58
|
-
controllerNumber === midiControllers.lsbForControl0BankSelect ||
|
|
59
|
-
controllerNumber === midiControllers.resetAllControllers
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Save controllers here and send them only after
|
|
64
|
-
* @type {number[][]}
|
|
65
|
-
*/
|
|
66
|
-
const savedControllers = [];
|
|
67
|
-
for (let i = 0; i < channelsToSave; i++)
|
|
68
|
-
{
|
|
69
|
-
savedControllers.push(Array.from(defaultControllerArray));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* RP-15 compliant reset
|
|
74
|
-
* https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
|
|
75
|
-
* @param chan {number}
|
|
76
|
-
*/
|
|
77
|
-
function resetAllControlllers(chan)
|
|
78
|
-
{
|
|
79
|
-
// reset pitch bend
|
|
80
|
-
pitchBends[chan] = 8192;
|
|
81
|
-
if (savedControllers?.[chan] === undefined)
|
|
82
|
-
{
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
for (let i = 0; i < defaultControllerArray.length; i++)
|
|
86
|
-
{
|
|
87
|
-
if (!nonResetableCCs.has(i))
|
|
88
|
-
{
|
|
89
|
-
savedControllers[chan][i] = defaultControllerArray[i];
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
while (true)
|
|
95
|
-
{
|
|
96
|
-
// find the next event
|
|
97
|
-
let trackIndex = this._findFirstEventIndex();
|
|
98
|
-
let event = this.tracks[trackIndex][this.eventIndex[trackIndex]];
|
|
99
|
-
if (ticks !== undefined)
|
|
100
|
-
{
|
|
101
|
-
if (event.ticks >= ticks)
|
|
102
|
-
{
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
else
|
|
107
|
-
{
|
|
108
|
-
if (this.playedTime >= time)
|
|
109
|
-
{
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// skip note ons
|
|
115
|
-
const info = getEvent(event.messageStatusByte);
|
|
116
|
-
// Keep in mind midi ports to determine the channel!
|
|
117
|
-
const channel = info.channel + (this.midiPortChannelOffsets[this.midiPorts[trackIndex]] || 0);
|
|
118
|
-
switch (info.status)
|
|
119
|
-
{
|
|
120
|
-
// skip note messages
|
|
121
|
-
case messageTypes.noteOn:
|
|
122
|
-
// track portamento control as last note
|
|
123
|
-
if (savedControllers[channel] === undefined)
|
|
124
|
-
{
|
|
125
|
-
savedControllers[channel] = Array.from(defaultControllerArray);
|
|
126
|
-
}
|
|
127
|
-
savedControllers[channel][midiControllers.portamentoControl] = event.messageData[0];
|
|
128
|
-
break;
|
|
129
|
-
|
|
130
|
-
case messageTypes.noteOff:
|
|
131
|
-
break;
|
|
132
|
-
|
|
133
|
-
// skip pitch bend
|
|
134
|
-
case messageTypes.pitchBend:
|
|
135
|
-
pitchBends[channel] = event.messageData[1] << 7 | event.messageData[0];
|
|
136
|
-
break;
|
|
137
|
-
|
|
138
|
-
case messageTypes.programChange:
|
|
139
|
-
// empty tracks cannot program change
|
|
140
|
-
if (this.midiData.isMultiPort && this.midiData.usedChannelsOnTrack[trackIndex].size === 0)
|
|
141
|
-
{
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
const p = programs[channel];
|
|
145
|
-
p.program = event.messageData[0];
|
|
146
|
-
p.actualBank = p.bank;
|
|
147
|
-
break;
|
|
148
|
-
|
|
149
|
-
case messageTypes.controllerChange:
|
|
150
|
-
// empty tracks cannot controller change
|
|
151
|
-
if (this.midiData.isMultiPort && this.midiData.usedChannelsOnTrack[trackIndex].size === 0)
|
|
152
|
-
{
|
|
153
|
-
break;
|
|
154
|
-
}
|
|
155
|
-
// do not skip data entries
|
|
156
|
-
const controllerNumber = event.messageData[0];
|
|
157
|
-
if (isCCNonSkippable(controllerNumber))
|
|
158
|
-
{
|
|
159
|
-
let ccV = event.messageData[1];
|
|
160
|
-
if (controllerNumber === midiControllers.bankSelect)
|
|
161
|
-
{
|
|
162
|
-
// add the bank to be saved
|
|
163
|
-
programs[channel].bank = ccV;
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
else if (controllerNumber === midiControllers.resetAllControllers)
|
|
167
|
-
{
|
|
168
|
-
resetAllControlllers(channel);
|
|
169
|
-
}
|
|
170
|
-
if (this.sendMIDIMessages)
|
|
171
|
-
{
|
|
172
|
-
this.sendMIDICC(channel, controllerNumber, ccV);
|
|
173
|
-
}
|
|
174
|
-
else
|
|
175
|
-
{
|
|
176
|
-
this.synth.controllerChange(channel, controllerNumber, ccV);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
else
|
|
180
|
-
{
|
|
181
|
-
if (savedControllers[channel] === undefined)
|
|
182
|
-
{
|
|
183
|
-
savedControllers[channel] = Array.from(defaultControllerArray);
|
|
184
|
-
}
|
|
185
|
-
savedControllers[channel][controllerNumber] = event.messageData[1];
|
|
186
|
-
}
|
|
187
|
-
break;
|
|
188
|
-
|
|
189
|
-
default:
|
|
190
|
-
this._processEvent(event, trackIndex);
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
this.eventIndex[trackIndex]++;
|
|
195
|
-
// find the next event
|
|
196
|
-
trackIndex = this._findFirstEventIndex();
|
|
197
|
-
let nextEvent = this.tracks[trackIndex][this.eventIndex[trackIndex]];
|
|
198
|
-
if (nextEvent === undefined)
|
|
199
|
-
{
|
|
200
|
-
this.stop();
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
this.playedTime += this.oneTickToSeconds * (nextEvent.ticks - event.ticks);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// restoring saved controllers
|
|
207
|
-
if (this.sendMIDIMessages)
|
|
208
|
-
{
|
|
209
|
-
for (let channelNumber = 0; channelNumber < channelsToSave; channelNumber++)
|
|
210
|
-
{
|
|
211
|
-
// restore pitch bends
|
|
212
|
-
if (pitchBends[channelNumber] !== undefined)
|
|
213
|
-
{
|
|
214
|
-
this.sendMIDIPitchWheel(
|
|
215
|
-
channelNumber,
|
|
216
|
-
pitchBends[channelNumber] >> 7,
|
|
217
|
-
pitchBends[channelNumber] & 0x7F
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
if (savedControllers[channelNumber] !== undefined)
|
|
221
|
-
{
|
|
222
|
-
// every controller that has changed
|
|
223
|
-
savedControllers[channelNumber].forEach((value, index) =>
|
|
224
|
-
{
|
|
225
|
-
if (value !== defaultControllerArray[index] && !isCCNonSkippable(
|
|
226
|
-
index))
|
|
227
|
-
{
|
|
228
|
-
this.sendMIDICC(channelNumber, index, value);
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
// restore programs
|
|
233
|
-
if (programs[channelNumber].program >= 0 && programs[channelNumber].actualBank >= 0)
|
|
234
|
-
{
|
|
235
|
-
const bank = programs[channelNumber].actualBank;
|
|
236
|
-
this.sendMIDICC(channelNumber, midiControllers.bankSelect, bank);
|
|
237
|
-
this.sendMIDIProgramChange(channelNumber, programs[channelNumber].program);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
else
|
|
242
|
-
{
|
|
243
|
-
// for all synth channels
|
|
244
|
-
for (let channelNumber = 0; channelNumber < channelsToSave; channelNumber++)
|
|
245
|
-
{
|
|
246
|
-
// restore pitch bends
|
|
247
|
-
if (pitchBends[channelNumber] !== undefined)
|
|
248
|
-
{
|
|
249
|
-
this.synth.pitchWheel(channelNumber, pitchBends[channelNumber] >> 7, pitchBends[channelNumber] & 0x7F);
|
|
250
|
-
}
|
|
251
|
-
if (savedControllers[channelNumber] !== undefined)
|
|
252
|
-
{
|
|
253
|
-
// every controller that has changed
|
|
254
|
-
savedControllers[channelNumber].forEach((value, index) =>
|
|
255
|
-
{
|
|
256
|
-
if (value !== defaultControllerArray[index] && !isCCNonSkippable(
|
|
257
|
-
index))
|
|
258
|
-
{
|
|
259
|
-
this.synth.controllerChange(
|
|
260
|
-
channelNumber,
|
|
261
|
-
index,
|
|
262
|
-
value
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
// restore programs
|
|
268
|
-
if (programs[channelNumber].program >= 0 && programs[channelNumber].actualBank >= 0)
|
|
269
|
-
{
|
|
270
|
-
const bank = programs[channelNumber].actualBank;
|
|
271
|
-
this.synth.controllerChange(channelNumber, midiControllers.bankSelect, bank);
|
|
272
|
-
this.synth.programChange(channelNumber, programs[channelNumber].program);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return true;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Starts the playback
|
|
281
|
-
* @param resetTime {boolean} If true, time is set to 0 s
|
|
282
|
-
* @this {SpessaSynthSequencer}
|
|
283
|
-
*/
|
|
284
|
-
export function play(resetTime = false)
|
|
285
|
-
{
|
|
286
|
-
if (this.midiData === undefined)
|
|
287
|
-
{
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// reset the time if necessary
|
|
292
|
-
if (resetTime)
|
|
293
|
-
{
|
|
294
|
-
this.pausedTime = undefined;
|
|
295
|
-
this.currentTime = 0;
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (this.currentTime >= this.duration)
|
|
300
|
-
{
|
|
301
|
-
this.pausedTime = undefined;
|
|
302
|
-
this.currentTime = 0;
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// unpause if paused
|
|
307
|
-
if (this.paused)
|
|
308
|
-
{
|
|
309
|
-
// adjust the start time
|
|
310
|
-
this._recalculateStartTime(this.pausedTime);
|
|
311
|
-
this.pausedTime = undefined;
|
|
312
|
-
}
|
|
313
|
-
if (!this.sendMIDIMessages)
|
|
314
|
-
{
|
|
315
|
-
this.playingNotes.forEach(n =>
|
|
316
|
-
{
|
|
317
|
-
this.synth.noteOn(n.channel, n.midiNote, n.velocity);
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
this.setProcessHandler();
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* @this {SpessaSynthSequencer}
|
|
325
|
-
* @param ticks {number}
|
|
326
|
-
*/
|
|
327
|
-
export function setTimeTicks(ticks)
|
|
328
|
-
{
|
|
329
|
-
this.stop();
|
|
330
|
-
this.playingNotes = [];
|
|
331
|
-
this.pausedTime = undefined;
|
|
332
|
-
this.post(
|
|
333
|
-
SpessaSynthSequencerReturnMessageType.timeChange,
|
|
334
|
-
this.synth.currentSynthTime - this.midiData.MIDIticksToSeconds(ticks)
|
|
335
|
-
);
|
|
336
|
-
const isNotFinished = this._playTo(0, ticks);
|
|
337
|
-
this._recalculateStartTime(this.playedTime);
|
|
338
|
-
if (!isNotFinished)
|
|
339
|
-
{
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
this.play();
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* @param time
|
|
347
|
-
* @private
|
|
348
|
-
* @this {SpessaSynthSequencer}
|
|
349
|
-
*/
|
|
350
|
-
export function _recalculateStartTime(time)
|
|
351
|
-
{
|
|
352
|
-
this.absoluteStartTime = this.synth.currentSynthTime - time / this._playbackRate;
|
|
353
|
-
}
|