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