spessasynth_core 3.27.8 → 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.
- package/README.md +85 -51
- package/dist/index.d.ts +4057 -0
- package/dist/index.js +17188 -0
- package/dist/index.js.map +1 -0
- package/package.json +23 -6
- package/index.js +0 -132
- package/src/externals/README.md +0 -6
- package/src/externals/fflate/LICENSE +0 -21
- package/src/externals/fflate/fflate.min.js +0 -1
- package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +0 -12
- package/src/externals/stbvorbis_sync/LICENSE +0 -202
- package/src/externals/stbvorbis_sync/NOTICE +0 -6
- package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +0 -1
- package/src/midi/README.md +0 -32
- package/src/midi/basic_midi.js +0 -587
- package/src/midi/midi_builder.js +0 -203
- package/src/midi/midi_loader.js +0 -321
- package/src/midi/midi_message.js +0 -254
- package/src/midi/midi_sequence.js +0 -230
- package/src/midi/midi_tools/get_note_times.js +0 -154
- package/src/midi/midi_tools/midi_editor.js +0 -611
- package/src/midi/midi_tools/midi_writer.js +0 -105
- package/src/midi/midi_tools/rmidi_writer.js +0 -566
- package/src/midi/midi_tools/used_keys_loaded.js +0 -256
- package/src/midi/xmf_loader.js +0 -454
- package/src/sequencer/README.md +0 -9
- package/src/sequencer/events.js +0 -81
- package/src/sequencer/play.js +0 -362
- package/src/sequencer/process_event.js +0 -165
- package/src/sequencer/process_tick.js +0 -104
- package/src/sequencer/sequencer_engine.js +0 -372
- package/src/sequencer/song_control.js +0 -196
- package/src/soundfont/README.md +0 -11
- package/src/soundfont/basic_soundfont/basic_global_zone.js +0 -6
- package/src/soundfont/basic_soundfont/basic_instrument.js +0 -115
- package/src/soundfont/basic_soundfont/basic_instrument_zone.js +0 -45
- package/src/soundfont/basic_soundfont/basic_preset.js +0 -313
- package/src/soundfont/basic_soundfont/basic_preset_zone.js +0 -39
- package/src/soundfont/basic_soundfont/basic_sample.js +0 -477
- package/src/soundfont/basic_soundfont/basic_soundbank.js +0 -740
- package/src/soundfont/basic_soundfont/basic_zone.js +0 -145
- package/src/soundfont/basic_soundfont/generator.js +0 -76
- package/src/soundfont/basic_soundfont/generator_types.js +0 -151
- package/src/soundfont/basic_soundfont/modulator.js +0 -581
- package/src/soundfont/basic_soundfont/riff_chunk.js +0 -195
- package/src/soundfont/basic_soundfont/write_dls/art2.js +0 -174
- package/src/soundfont/basic_soundfont/write_dls/articulator.js +0 -49
- package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +0 -374
- package/src/soundfont/basic_soundfont/write_dls/ins.js +0 -85
- package/src/soundfont/basic_soundfont/write_dls/lins.js +0 -15
- package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +0 -330
- package/src/soundfont/basic_soundfont/write_dls/rgn2.js +0 -120
- package/src/soundfont/basic_soundfont/write_dls/wave.js +0 -71
- package/src/soundfont/basic_soundfont/write_dls/write_dls.js +0 -124
- package/src/soundfont/basic_soundfont/write_dls/wsmp.js +0 -78
- package/src/soundfont/basic_soundfont/write_dls/wvpl.js +0 -35
- package/src/soundfont/basic_soundfont/write_sf2/ibag.js +0 -60
- package/src/soundfont/basic_soundfont/write_sf2/igen.js +0 -91
- package/src/soundfont/basic_soundfont/write_sf2/imod.js +0 -62
- package/src/soundfont/basic_soundfont/write_sf2/inst.js +0 -42
- package/src/soundfont/basic_soundfont/write_sf2/pbag.js +0 -57
- package/src/soundfont/basic_soundfont/write_sf2/pgen.js +0 -92
- package/src/soundfont/basic_soundfont/write_sf2/phdr.js +0 -61
- package/src/soundfont/basic_soundfont/write_sf2/pmod.js +0 -62
- package/src/soundfont/basic_soundfont/write_sf2/sdta.js +0 -131
- package/src/soundfont/basic_soundfont/write_sf2/shdr.js +0 -77
- package/src/soundfont/basic_soundfont/write_sf2/write.js +0 -287
- package/src/soundfont/dls/articulator_converter.js +0 -402
- package/src/soundfont/dls/dls_destinations.js +0 -38
- package/src/soundfont/dls/dls_instrument.js +0 -20
- package/src/soundfont/dls/dls_preset.js +0 -43
- package/src/soundfont/dls/dls_sample.js +0 -238
- package/src/soundfont/dls/dls_soundfont.js +0 -183
- package/src/soundfont/dls/dls_sources.js +0 -63
- package/src/soundfont/dls/dls_zone.js +0 -89
- package/src/soundfont/dls/read_articulation.js +0 -300
- package/src/soundfont/dls/read_instrument.js +0 -118
- package/src/soundfont/dls/read_instrument_list.js +0 -17
- package/src/soundfont/dls/read_lart.js +0 -35
- package/src/soundfont/dls/read_region.js +0 -157
- package/src/soundfont/dls/read_samples.js +0 -154
- package/src/soundfont/load_soundfont.js +0 -21
- package/src/soundfont/read_sf2/generators.js +0 -43
- package/src/soundfont/read_sf2/instrument_zones.js +0 -75
- package/src/soundfont/read_sf2/instruments.js +0 -71
- package/src/soundfont/read_sf2/modulators.js +0 -25
- package/src/soundfont/read_sf2/preset_zones.js +0 -79
- package/src/soundfont/read_sf2/presets.js +0 -80
- package/src/soundfont/read_sf2/samples.js +0 -317
- package/src/soundfont/read_sf2/soundfont.js +0 -452
- package/src/soundfont/read_sf2/zones.js +0 -28
- package/src/synthetizer/README.md +0 -7
- package/src/synthetizer/audio_engine/README.md +0 -9
- package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +0 -289
- package/src/synthetizer/audio_engine/engine_components/controller_tables.js +0 -90
- package/src/synthetizer/audio_engine/engine_components/dynamic_modulator_system.js +0 -95
- package/src/synthetizer/audio_engine/engine_components/enums.js +0 -18
- package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +0 -151
- package/src/synthetizer/audio_engine/engine_components/lfo.js +0 -26
- package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +0 -282
- package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +0 -551
- package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +0 -181
- package/src/synthetizer/audio_engine/engine_components/modulator_curves.js +0 -89
- package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +0 -265
- package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +0 -124
- package/src/synthetizer/audio_engine/engine_components/unit_converter.js +0 -73
- package/src/synthetizer/audio_engine/engine_components/voice.js +0 -525
- package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +0 -402
- package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +0 -274
- package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +0 -159
- package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +0 -53
- package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -254
- package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +0 -20
- package/src/synthetizer/audio_engine/engine_methods/data_entry/awe32.js +0 -198
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +0 -281
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +0 -109
- package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +0 -17
- package/src/synthetizer/audio_engine/engine_methods/note_on.js +0 -220
- package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +0 -92
- package/src/synthetizer/audio_engine/engine_methods/program_change.js +0 -35
- package/src/synthetizer/audio_engine/engine_methods/render_voice.js +0 -214
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/embedded_sound_bank.js +0 -42
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +0 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/update_preset_list.js +0 -19
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +0 -23
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +0 -56
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +0 -16
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +0 -30
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +0 -63
- package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +0 -1058
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +0 -23
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +0 -31
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +0 -29
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +0 -15
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +0 -26
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +0 -19
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +0 -27
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +0 -15
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +0 -34
- package/src/synthetizer/audio_engine/main_processor.js +0 -817
- package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +0 -16
- package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +0 -175
- package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +0 -116
- package/src/synthetizer/audio_engine/synth_processor_options.js +0 -18
- package/src/synthetizer/synth_constants.js +0 -26
- package/src/utils/README.md +0 -8
- package/src/utils/buffer_to_wav.js +0 -197
- package/src/utils/byte_functions/big_endian.js +0 -32
- package/src/utils/byte_functions/little_endian.js +0 -77
- package/src/utils/byte_functions/string.js +0 -92
- package/src/utils/byte_functions/variable_length_quantity.js +0 -42
- package/src/utils/fill_with_defaults.js +0 -21
- package/src/utils/indexed_array.js +0 -34
- package/src/utils/loggin.js +0 -71
- package/src/utils/other.js +0 -92
- package/src/utils/sysex_detector.js +0 -58
- package/src/utils/xg_hacks.js +0 -193
package/src/sequencer/README.md
DELETED
|
@@ -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)
|
package/src/sequencer/events.js
DELETED
|
@@ -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
|
-
}
|
package/src/sequencer/play.js
DELETED
|
@@ -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
|
-
}
|