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
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
export const SongChangeType = {
|
|
2
|
-
backwards: 0, // no additional data
|
|
3
|
-
forwards: 1, // no additional data
|
|
4
|
-
shuffleOn: 2, // no additional data
|
|
5
|
-
shuffleOff: 3, // no additional data
|
|
6
|
-
index: 4 // songIndex<number>
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @enum {number}
|
|
11
|
-
* @property {number} loadNewSongList - 0 -> [...song<MIDI>]
|
|
12
|
-
* @property {number} pause - 1 -> isFinished<boolean>
|
|
13
|
-
* @property {number} stop - 2 -> (no data)
|
|
14
|
-
* @property {number} play - 3 -> resetTime<boolean>
|
|
15
|
-
* @property {number} setTime - 4 -> time<number>
|
|
16
|
-
* @property {number} changeMIDIMessageSending - 5 -> sendMIDIMessages<boolean>
|
|
17
|
-
* @property {number} setPlaybackRate - 6 -> playbackRate<number>
|
|
18
|
-
* @property {number} setLoop - 7 -> [loop<boolean>, count<number>]
|
|
19
|
-
* @property {number} changeSong - 8 -> [changeType<SongChangeType>, data<number>]
|
|
20
|
-
* @property {number} getMIDI - 9 -> (no data)
|
|
21
|
-
* @property {number} setSkipToFirstNote -10 -> skipToFirstNoteOn<boolean>
|
|
22
|
-
* @property {number} setPreservePlaybackState -11 -> preservePlaybackState<boolean>
|
|
23
|
-
*/
|
|
24
|
-
export const SpessaSynthSequencerMessageType = {
|
|
25
|
-
loadNewSongList: 0,
|
|
26
|
-
pause: 1,
|
|
27
|
-
stop: 2,
|
|
28
|
-
play: 3,
|
|
29
|
-
setTime: 4,
|
|
30
|
-
changeMIDIMessageSending: 5,
|
|
31
|
-
setPlaybackRate: 6,
|
|
32
|
-
setLoop: 7,
|
|
33
|
-
changeSong: 8,
|
|
34
|
-
getMIDI: 9,
|
|
35
|
-
setSkipToFirstNote: 10,
|
|
36
|
-
setPreservePlaybackState: 11
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
* @enum {number}
|
|
42
|
-
*/
|
|
43
|
-
export const SpessaSynthSequencerReturnMessageType = {
|
|
44
|
-
midiEvent: 0, // [...midiEventBytes<number>]
|
|
45
|
-
songChange: 1, // [songIndex<number>, isAutoPlayed<boolean>]
|
|
46
|
-
timeChange: 2, // newAbsoluteTime<number>
|
|
47
|
-
pause: 3, // no data
|
|
48
|
-
getMIDI: 4, // midiData<MIDI>
|
|
49
|
-
midiError: 5, // errorMSG<string>
|
|
50
|
-
metaEvent: 6, // [event<MIDIMessage>, trackNum<number>]
|
|
51
|
-
loopCountChange: 7, // newLoopCount<number>
|
|
52
|
-
songListChange: 8 // songListData<MIDIData[]>
|
|
53
|
-
};
|
package/soundfont/README.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
## This is the SoundFont2 parsing library.
|
|
2
|
-
|
|
3
|
-
The code here is responsible for parsing the SoundFont2 file and
|
|
4
|
-
providing an easy way to get the data out.
|
|
5
|
-
Default modulators are also stored here (in `modulators.js`)
|
|
6
|
-
|
|
7
|
-
`basic_soundfont` folder contains the classes that represent the soundfont file.
|
|
8
|
-
|
|
9
|
-
`read_sf2` folder contains the code for reading an `.sf2` file.
|
|
10
|
-
|
|
11
|
-
`write` folder contains the code for writing out an `.sf2` file.
|
|
12
|
-
|
|
13
|
-
`dls` folder contains the code for reading a `.dls` file (and converting in into a soundfont representation).
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
export class BasicInstrument
|
|
2
|
-
{
|
|
3
|
-
/**
|
|
4
|
-
* The instrument's name
|
|
5
|
-
* @type {string}
|
|
6
|
-
*/
|
|
7
|
-
instrumentName = "";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* The instrument's zones
|
|
11
|
-
* @type {BasicInstrumentZone[]}
|
|
12
|
-
*/
|
|
13
|
-
instrumentZones = [];
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Instrument's use count, used for trimming
|
|
17
|
-
* @type {number}
|
|
18
|
-
* @private
|
|
19
|
-
*/
|
|
20
|
-
_useCount = 0;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @returns {number}
|
|
24
|
-
*/
|
|
25
|
-
get useCount()
|
|
26
|
-
{
|
|
27
|
-
return this._useCount;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
addUseCount()
|
|
31
|
-
{
|
|
32
|
-
this._useCount++;
|
|
33
|
-
this.instrumentZones.forEach(z => z.useCount++);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
removeUseCount()
|
|
37
|
-
{
|
|
38
|
-
this._useCount--;
|
|
39
|
-
for (let i = 0; i < this.instrumentZones.length; i++)
|
|
40
|
-
{
|
|
41
|
-
if (this.safeDeleteZone(i))
|
|
42
|
-
{
|
|
43
|
-
i--;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
deleteInstrument()
|
|
49
|
-
{
|
|
50
|
-
this.instrumentZones.forEach(z => z.deleteZone());
|
|
51
|
-
this.instrumentZones.length = 0;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @param index {number}
|
|
56
|
-
* @returns {boolean} is the zone has been deleted
|
|
57
|
-
*/
|
|
58
|
-
safeDeleteZone(index)
|
|
59
|
-
{
|
|
60
|
-
this.instrumentZones[index].useCount--;
|
|
61
|
-
if (this.instrumentZones[index].useCount < 1)
|
|
62
|
-
{
|
|
63
|
-
this.deleteZone(index);
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @param index {number}
|
|
71
|
-
*/
|
|
72
|
-
deleteZone(index)
|
|
73
|
-
{
|
|
74
|
-
this.instrumentZones[index].deleteZone();
|
|
75
|
-
this.instrumentZones.splice(index, 1);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {{
|
|
3
|
-
* instrumentGenerators: Generator[],
|
|
4
|
-
* presetGenerators: Generator[],
|
|
5
|
-
* modulators: Modulator[],
|
|
6
|
-
* sample: BasicSample,
|
|
7
|
-
* sampleID: number,
|
|
8
|
-
* }} SampleAndGenerators
|
|
9
|
-
*/
|
|
10
|
-
import { generatorTypes } from "./generator.js";
|
|
11
|
-
import { Modulator } from "./modulator.js";
|
|
12
|
-
import { isXGDrums } from "../../utils/xg_hacks.js";
|
|
13
|
-
|
|
14
|
-
export class BasicPreset
|
|
15
|
-
{
|
|
16
|
-
/**
|
|
17
|
-
* The parent soundbank instance
|
|
18
|
-
* Currently used for determining default modulators and XG status
|
|
19
|
-
* @type {BasicSoundBank}
|
|
20
|
-
*/
|
|
21
|
-
parentSoundBank;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* The preset's name
|
|
25
|
-
* @type {string}
|
|
26
|
-
*/
|
|
27
|
-
presetName = "";
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The preset's MIDI program number
|
|
31
|
-
* @type {number}
|
|
32
|
-
*/
|
|
33
|
-
program = 0;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* The preset's MIDI bank number
|
|
37
|
-
* @type {number}
|
|
38
|
-
*/
|
|
39
|
-
bank = 0;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The preset's zones
|
|
43
|
-
* @type {BasicPresetZone[]}
|
|
44
|
-
*/
|
|
45
|
-
presetZones = [];
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Stores already found getSamplesAndGenerators for reuse
|
|
49
|
-
* @type {SampleAndGenerators[][][]}
|
|
50
|
-
*/
|
|
51
|
-
foundSamplesAndGenerators = [];
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* unused metadata
|
|
55
|
-
* @type {number}
|
|
56
|
-
*/
|
|
57
|
-
library = 0;
|
|
58
|
-
/**
|
|
59
|
-
* unused metadata
|
|
60
|
-
* @type {number}
|
|
61
|
-
*/
|
|
62
|
-
genre = 0;
|
|
63
|
-
/**
|
|
64
|
-
* unused metadata
|
|
65
|
-
* @type {number}
|
|
66
|
-
*/
|
|
67
|
-
morphology = 0;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Creates a new preset representation
|
|
71
|
-
* @param parentSoundBank {BasicSoundBank}
|
|
72
|
-
*/
|
|
73
|
-
constructor(parentSoundBank)
|
|
74
|
-
{
|
|
75
|
-
this.parentSoundBank = parentSoundBank;
|
|
76
|
-
for (let i = 0; i < 128; i++)
|
|
77
|
-
{
|
|
78
|
-
this.foundSamplesAndGenerators[i] = [];
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* @param allowXG {boolean}
|
|
84
|
-
* @param allowSFX {boolean}
|
|
85
|
-
* @returns {boolean}
|
|
86
|
-
*/
|
|
87
|
-
isDrumPreset(allowXG, allowSFX = false)
|
|
88
|
-
{
|
|
89
|
-
const xg = allowXG && this.parentSoundBank.isXGBank;
|
|
90
|
-
// sfx is not cool
|
|
91
|
-
return this.bank === 128 || (
|
|
92
|
-
xg &&
|
|
93
|
-
(isXGDrums(this.bank) && (this.bank !== 126 || allowSFX))
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
deletePreset()
|
|
98
|
-
{
|
|
99
|
-
this.presetZones.forEach(z => z.deleteZone());
|
|
100
|
-
this.presetZones.length = 0;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* @param index {number}
|
|
105
|
-
*/
|
|
106
|
-
deleteZone(index)
|
|
107
|
-
{
|
|
108
|
-
this.presetZones[index].deleteZone();
|
|
109
|
-
this.presetZones.splice(index, 1);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// noinspection JSUnusedGlobalSymbols
|
|
113
|
-
/**
|
|
114
|
-
* Preloads all samples (async)
|
|
115
|
-
*/
|
|
116
|
-
preload(keyMin, keyMax)
|
|
117
|
-
{
|
|
118
|
-
for (let key = keyMin; key < keyMax + 1; key++)
|
|
119
|
-
{
|
|
120
|
-
for (let velocity = 0; velocity < 128; velocity++)
|
|
121
|
-
{
|
|
122
|
-
this.getSamplesAndGenerators(key, velocity).forEach(samandgen =>
|
|
123
|
-
{
|
|
124
|
-
if (!samandgen.sample.isSampleLoaded)
|
|
125
|
-
{
|
|
126
|
-
samandgen.sample.getAudioData();
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Preloads a specific key/velocity combo
|
|
135
|
-
* @param key {number}
|
|
136
|
-
* @param velocity {number}
|
|
137
|
-
*/
|
|
138
|
-
preloadSpecific(key, velocity)
|
|
139
|
-
{
|
|
140
|
-
this.getSamplesAndGenerators(key, velocity).forEach(samandgen =>
|
|
141
|
-
{
|
|
142
|
-
if (!samandgen.sample.isSampleLoaded)
|
|
143
|
-
{
|
|
144
|
-
samandgen.sample.getAudioData();
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Returns generatorTranslator and generators for given note
|
|
151
|
-
* @param midiNote {number}
|
|
152
|
-
* @param velocity {number}
|
|
153
|
-
* @returns {SampleAndGenerators[]}
|
|
154
|
-
*/
|
|
155
|
-
getSamplesAndGenerators(midiNote, velocity)
|
|
156
|
-
{
|
|
157
|
-
const memorized = this.foundSamplesAndGenerators[midiNote][velocity];
|
|
158
|
-
if (memorized)
|
|
159
|
-
{
|
|
160
|
-
return memorized;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (this.presetZones.length < 1)
|
|
164
|
-
{
|
|
165
|
-
return [];
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* @param range {SoundFontRange}
|
|
170
|
-
* @param number {number}
|
|
171
|
-
* @returns {boolean}
|
|
172
|
-
*/
|
|
173
|
-
function isInRange(range, number)
|
|
174
|
-
{
|
|
175
|
-
return number >= range.min && number <= range.max;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* @param main {Generator[]}
|
|
180
|
-
* @param adder {Generator[]}
|
|
181
|
-
*/
|
|
182
|
-
function addUnique(main, adder)
|
|
183
|
-
{
|
|
184
|
-
main.push(...adder.filter(g => !main.find(mg => mg.generatorType === g.generatorType)));
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* @param main {Modulator[]}
|
|
189
|
-
* @param adder {Modulator[]}
|
|
190
|
-
*/
|
|
191
|
-
function addUniqueMods(main, adder)
|
|
192
|
-
{
|
|
193
|
-
main.push(...adder.filter(m => !main.find(mm => Modulator.isIdentical(m, mm))));
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* @type {SampleAndGenerators[]}
|
|
198
|
-
*/
|
|
199
|
-
let parsedGeneratorsAndSamples = [];
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* global zone is always first, so it or nothing
|
|
203
|
-
* @type {Generator[]}
|
|
204
|
-
*/
|
|
205
|
-
let globalPresetGenerators = this.presetZones[0].isGlobal ? [...this.presetZones[0].generators] : [];
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* @type {Modulator[]}
|
|
209
|
-
*/
|
|
210
|
-
let globalPresetModulators = this.presetZones[0].isGlobal ? [...this.presetZones[0].modulators] : [];
|
|
211
|
-
const globalKeyRange = this.presetZones[0].isGlobal ? this.presetZones[0].keyRange : { min: 0, max: 127 };
|
|
212
|
-
const globalVelRange = this.presetZones[0].isGlobal ? this.presetZones[0].velRange : { min: 0, max: 127 };
|
|
213
|
-
|
|
214
|
-
// find the preset zones in range
|
|
215
|
-
let presetZonesInRange = this.presetZones.filter(currentZone =>
|
|
216
|
-
(
|
|
217
|
-
isInRange(
|
|
218
|
-
currentZone.hasKeyRange ? currentZone.keyRange : globalKeyRange,
|
|
219
|
-
midiNote
|
|
220
|
-
)
|
|
221
|
-
&&
|
|
222
|
-
isInRange(
|
|
223
|
-
currentZone.hasVelRange ? currentZone.velRange : globalVelRange,
|
|
224
|
-
velocity
|
|
225
|
-
)
|
|
226
|
-
) && !currentZone.isGlobal);
|
|
227
|
-
|
|
228
|
-
presetZonesInRange.forEach(zone =>
|
|
229
|
-
{
|
|
230
|
-
// the global zone is already taken into account earlier
|
|
231
|
-
if (zone.instrument.instrumentZones.length < 1)
|
|
232
|
-
{
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
let presetGenerators = zone.generators;
|
|
236
|
-
let presetModulators = zone.modulators;
|
|
237
|
-
const firstZone = zone.instrument.instrumentZones[0];
|
|
238
|
-
/**
|
|
239
|
-
* global zone is always first, so it or nothing
|
|
240
|
-
* @type {Generator[]}
|
|
241
|
-
*/
|
|
242
|
-
let globalInstrumentGenerators = firstZone.isGlobal ? [...firstZone.generators] : [];
|
|
243
|
-
let globalInstrumentModulators = firstZone.isGlobal ? [...firstZone.modulators] : [];
|
|
244
|
-
const globalKeyRange = firstZone.isGlobal ? firstZone.keyRange : { min: 0, max: 127 };
|
|
245
|
-
const globalVelRange = firstZone.isGlobal ? firstZone.velRange : { min: 0, max: 127 };
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
let instrumentZonesInRange = zone.instrument.instrumentZones
|
|
249
|
-
.filter(currentZone =>
|
|
250
|
-
(
|
|
251
|
-
isInRange(
|
|
252
|
-
currentZone.hasKeyRange ? currentZone.keyRange : globalKeyRange,
|
|
253
|
-
midiNote
|
|
254
|
-
)
|
|
255
|
-
&&
|
|
256
|
-
isInRange(
|
|
257
|
-
currentZone.hasVelRange ? currentZone.velRange : globalVelRange,
|
|
258
|
-
velocity
|
|
259
|
-
)
|
|
260
|
-
) && !currentZone.isGlobal
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
instrumentZonesInRange.forEach(instrumentZone =>
|
|
264
|
-
{
|
|
265
|
-
let instrumentGenerators = [...instrumentZone.generators];
|
|
266
|
-
let instrumentModulators = [...instrumentZone.modulators];
|
|
267
|
-
|
|
268
|
-
addUnique(
|
|
269
|
-
presetGenerators,
|
|
270
|
-
globalPresetGenerators
|
|
271
|
-
);
|
|
272
|
-
// add the unique global preset generators (local replace global(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// add the unique global instrument generators (local replace global)
|
|
276
|
-
addUnique(
|
|
277
|
-
instrumentGenerators,
|
|
278
|
-
globalInstrumentGenerators
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
addUniqueMods(
|
|
282
|
-
presetModulators,
|
|
283
|
-
globalPresetModulators
|
|
284
|
-
);
|
|
285
|
-
addUniqueMods(
|
|
286
|
-
instrumentModulators,
|
|
287
|
-
globalInstrumentModulators
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
// default mods
|
|
291
|
-
addUniqueMods(
|
|
292
|
-
instrumentModulators,
|
|
293
|
-
this.parentSoundBank.defaultModulators
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* sum preset modulators to instruments (amount) sf spec page 54
|
|
298
|
-
* @type {Modulator[]}
|
|
299
|
-
*/
|
|
300
|
-
const finalModulatorList = [...instrumentModulators];
|
|
301
|
-
for (let i = 0; i < presetModulators.length; i++)
|
|
302
|
-
{
|
|
303
|
-
let mod = presetModulators[i];
|
|
304
|
-
const identicalInstrumentModulator = finalModulatorList.findIndex(
|
|
305
|
-
m => Modulator.isIdentical(mod, m));
|
|
306
|
-
if (identicalInstrumentModulator !== -1)
|
|
307
|
-
{
|
|
308
|
-
// sum the amounts
|
|
309
|
-
// (this makes a new modulator because otherwise it would overwrite the one in the soundfont!
|
|
310
|
-
finalModulatorList[identicalInstrumentModulator] = finalModulatorList[identicalInstrumentModulator].sumTransform(
|
|
311
|
-
mod);
|
|
312
|
-
}
|
|
313
|
-
else
|
|
314
|
-
{
|
|
315
|
-
finalModulatorList.push(mod);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
// combine both generators and add to the final result
|
|
321
|
-
parsedGeneratorsAndSamples.push({
|
|
322
|
-
instrumentGenerators: instrumentGenerators,
|
|
323
|
-
presetGenerators: presetGenerators,
|
|
324
|
-
modulators: finalModulatorList,
|
|
325
|
-
sample: instrumentZone.sample,
|
|
326
|
-
sampleID: instrumentZone.generators.find(
|
|
327
|
-
g => g.generatorType === generatorTypes.sampleID).generatorValue
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
// save and return
|
|
333
|
-
this.foundSamplesAndGenerators[midiNote][velocity] = parsedGeneratorsAndSamples;
|
|
334
|
-
return parsedGeneratorsAndSamples;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* samples.js
|
|
3
|
-
* purpose: parses soundfont samples, resamples if needed.
|
|
4
|
-
* loads sample data, handles async loading of sf3 compressed samples
|
|
5
|
-
*/
|
|
6
|
-
import { SpessaSynthWarn } from "../../utils/loggin.js";
|
|
7
|
-
|
|
8
|
-
// should be reasonable for most cases
|
|
9
|
-
const RESAMPLE_RATE = 48000;
|
|
10
|
-
|
|
11
|
-
export class BasicSample
|
|
12
|
-
{
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The sample's name
|
|
16
|
-
* @type {string}
|
|
17
|
-
*/
|
|
18
|
-
sampleName;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Sample rate in Hz
|
|
22
|
-
* @type {number}
|
|
23
|
-
*/
|
|
24
|
-
sampleRate;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Original pitch of the sample as a MIDI note number
|
|
28
|
-
* @type {number}
|
|
29
|
-
*/
|
|
30
|
-
samplePitch;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Pitch correction, in cents. Can be negative
|
|
34
|
-
* @type {number}
|
|
35
|
-
*/
|
|
36
|
-
samplePitchCorrection;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Sample link, currently unused here
|
|
40
|
-
* @type {number}
|
|
41
|
-
*/
|
|
42
|
-
sampleLink;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Type of the sample, currently only used for SF3
|
|
46
|
-
* @type {number}
|
|
47
|
-
*/
|
|
48
|
-
sampleType;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Relative to the start of the sample in sample points
|
|
52
|
-
* @type {number}
|
|
53
|
-
*/
|
|
54
|
-
sampleLoopStartIndex;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Relative to the start of the sample in sample points
|
|
58
|
-
* @type {number}
|
|
59
|
-
*/
|
|
60
|
-
sampleLoopEndIndex;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Indicates if the sample is compressed
|
|
64
|
-
* @type {boolean}
|
|
65
|
-
*/
|
|
66
|
-
isCompressed;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* The compressed sample data if it was compressed by spessasynth
|
|
70
|
-
* @type {Uint8Array}
|
|
71
|
-
*/
|
|
72
|
-
compressedData = undefined;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* The sample's use count
|
|
76
|
-
* @type {number}
|
|
77
|
-
*/
|
|
78
|
-
useCount = 0;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* The sample's audio data
|
|
82
|
-
* @type {Float32Array}
|
|
83
|
-
*/
|
|
84
|
-
sampleData = undefined;
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* The basic representation of a soundfont sample
|
|
88
|
-
* @param sampleName {string} The sample's name
|
|
89
|
-
* @param sampleRate {number} The sample's rate in Hz
|
|
90
|
-
* @param samplePitch {number} The sample's pitch as a MIDI note number
|
|
91
|
-
* @param samplePitchCorrection {number} The sample's pitch correction in cents
|
|
92
|
-
* @param sampleLink {number} The sample's link, currently unused
|
|
93
|
-
* @param sampleType {number} The sample's type, an enum
|
|
94
|
-
* @param loopStart {number} The sample's loop start relative to the sample start in sample points
|
|
95
|
-
* @param loopEnd {number} The sample's loop end relative to the sample start in sample points
|
|
96
|
-
*/
|
|
97
|
-
constructor(
|
|
98
|
-
sampleName,
|
|
99
|
-
sampleRate,
|
|
100
|
-
samplePitch,
|
|
101
|
-
samplePitchCorrection,
|
|
102
|
-
sampleLink,
|
|
103
|
-
sampleType,
|
|
104
|
-
loopStart,
|
|
105
|
-
loopEnd
|
|
106
|
-
)
|
|
107
|
-
{
|
|
108
|
-
this.sampleName = sampleName;
|
|
109
|
-
this.sampleRate = sampleRate;
|
|
110
|
-
this.samplePitch = samplePitch;
|
|
111
|
-
this.samplePitchCorrection = samplePitchCorrection;
|
|
112
|
-
this.sampleLink = sampleLink;
|
|
113
|
-
this.sampleType = sampleType;
|
|
114
|
-
this.sampleLoopStartIndex = loopStart;
|
|
115
|
-
this.sampleLoopEndIndex = loopEnd;
|
|
116
|
-
// https://github.com/FluidSynth/fluidsynth/wiki/SoundFont3Format
|
|
117
|
-
this.isCompressed = (sampleType & 0x10) > 0;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* @returns {Uint8Array|IndexedByteArray}
|
|
123
|
-
*/
|
|
124
|
-
getRawData()
|
|
125
|
-
{
|
|
126
|
-
const uint8 = new Uint8Array(this.sampleData.length * 2);
|
|
127
|
-
for (let i = 0; i < this.sampleData.length; i++)
|
|
128
|
-
{
|
|
129
|
-
const sample = Math.floor(this.sampleData[i] * 32768);
|
|
130
|
-
uint8[i * 2] = sample & 0xFF; // lower byte
|
|
131
|
-
uint8[i * 2 + 1] = (sample >> 8) & 0xFF; // upper byte
|
|
132
|
-
}
|
|
133
|
-
return uint8;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
resampleData(newSampleRate)
|
|
137
|
-
{
|
|
138
|
-
let audioData = this.getAudioData();
|
|
139
|
-
const ratio = newSampleRate / this.sampleRate;
|
|
140
|
-
const resampled = new Float32Array(Math.floor(audioData.length * ratio));
|
|
141
|
-
for (let i = 0; i < resampled.length; i++)
|
|
142
|
-
{
|
|
143
|
-
resampled[i] = audioData[Math.floor(i * (1 / ratio))];
|
|
144
|
-
}
|
|
145
|
-
audioData = resampled;
|
|
146
|
-
this.sampleRate = newSampleRate;
|
|
147
|
-
// adjust loop points
|
|
148
|
-
this.sampleLoopStartIndex = Math.floor(this.sampleLoopStartIndex * ratio);
|
|
149
|
-
this.sampleLoopEndIndex = Math.floor(this.sampleLoopEndIndex * ratio);
|
|
150
|
-
this.sampleData = audioData;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* @param quality {number}
|
|
155
|
-
* @param encodeVorbis {EncodeVorbisFunction}
|
|
156
|
-
*/
|
|
157
|
-
compressSample(quality, encodeVorbis)
|
|
158
|
-
{
|
|
159
|
-
// no need to compress
|
|
160
|
-
if (this.isCompressed)
|
|
161
|
-
{
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
// compress, always mono!
|
|
165
|
-
try
|
|
166
|
-
{
|
|
167
|
-
// if the sample rate is too low or too high, resample
|
|
168
|
-
let audioData = this.getAudioData();
|
|
169
|
-
if (this.sampleRate < 8000 || this.sampleRate > 96000)
|
|
170
|
-
{
|
|
171
|
-
this.resampleData(RESAMPLE_RATE);
|
|
172
|
-
audioData = this.getAudioData();
|
|
173
|
-
}
|
|
174
|
-
this.compressedData = encodeVorbis([audioData], 1, this.sampleRate, quality);
|
|
175
|
-
// flag as compressed
|
|
176
|
-
this.sampleType |= 0x10;
|
|
177
|
-
this.isCompressed = true;
|
|
178
|
-
}
|
|
179
|
-
catch (e)
|
|
180
|
-
{
|
|
181
|
-
SpessaSynthWarn(`Failed to compress ${this.sampleName}. Leaving as uncompressed!`);
|
|
182
|
-
this.isCompressed = false;
|
|
183
|
-
this.compressedData = undefined;
|
|
184
|
-
// flag as uncompressed
|
|
185
|
-
this.sampleType &= 0xEF;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* @returns {Float32Array}
|
|
192
|
-
*/
|
|
193
|
-
getAudioData()
|
|
194
|
-
{
|
|
195
|
-
return this.sampleData;
|
|
196
|
-
}
|
|
197
|
-
}
|