spessasynth_core 1.1.2 → 1.1.4
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/LICENSE +3 -26
- package/README.md +156 -474
- package/index.js +73 -8
- package/package.json +21 -8
- package/src/externals/fflate/LICENSE +21 -0
- package/src/externals/fflate/fflate.min.js +1 -0
- package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +12 -0
- package/src/externals/stbvorbis_sync/LICENSE +202 -0
- package/src/externals/stbvorbis_sync/NOTICE +6 -0
- package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +1 -0
- package/src/midi/README.md +32 -0
- package/src/midi/basic_midi.js +567 -0
- package/src/midi/midi_builder.js +202 -0
- package/src/midi/midi_loader.js +324 -0
- package/{spessasynth_core/midi_parser → src/midi}/midi_message.js +58 -35
- package/src/midi/midi_sequence.js +224 -0
- package/src/midi/midi_tools/get_note_times.js +154 -0
- package/src/midi/midi_tools/midi_editor.js +611 -0
- package/src/midi/midi_tools/midi_writer.js +99 -0
- package/src/midi/midi_tools/rmidi_writer.js +567 -0
- package/src/midi/midi_tools/used_keys_loaded.js +238 -0
- package/src/midi/xmf_loader.js +454 -0
- package/src/sequencer/README.md +5 -0
- package/src/sequencer/events.js +81 -0
- package/src/sequencer/play.js +349 -0
- package/src/sequencer/process_event.js +165 -0
- package/{spessasynth_core/sequencer/worklet_sequencer → src/sequencer}/process_tick.js +103 -84
- package/src/sequencer/sequencer_engine.js +367 -0
- package/src/sequencer/song_control.js +201 -0
- package/src/soundfont/README.md +13 -0
- package/src/soundfont/basic_soundfont/basic_instrument.js +77 -0
- package/src/soundfont/basic_soundfont/basic_preset.js +336 -0
- package/src/soundfont/basic_soundfont/basic_sample.js +206 -0
- package/src/soundfont/basic_soundfont/basic_soundfont.js +565 -0
- package/src/soundfont/basic_soundfont/basic_zone.js +64 -0
- package/src/soundfont/basic_soundfont/basic_zones.js +43 -0
- package/src/soundfont/basic_soundfont/generator.js +220 -0
- package/src/soundfont/basic_soundfont/modulator.js +378 -0
- package/src/soundfont/basic_soundfont/riff_chunk.js +149 -0
- package/src/soundfont/basic_soundfont/write_dls/art2.js +173 -0
- package/src/soundfont/basic_soundfont/write_dls/articulator.js +49 -0
- package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +400 -0
- package/src/soundfont/basic_soundfont/write_dls/ins.js +103 -0
- package/src/soundfont/basic_soundfont/write_dls/lins.js +18 -0
- package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +330 -0
- package/src/soundfont/basic_soundfont/write_dls/rgn2.js +121 -0
- package/src/soundfont/basic_soundfont/write_dls/wave.js +94 -0
- package/src/soundfont/basic_soundfont/write_dls/write_dls.js +119 -0
- package/src/soundfont/basic_soundfont/write_dls/wsmp.js +78 -0
- package/src/soundfont/basic_soundfont/write_dls/wvpl.js +32 -0
- package/src/soundfont/basic_soundfont/write_sf2/ibag.js +39 -0
- package/src/soundfont/basic_soundfont/write_sf2/igen.js +80 -0
- package/src/soundfont/basic_soundfont/write_sf2/imod.js +46 -0
- package/src/soundfont/basic_soundfont/write_sf2/inst.js +34 -0
- package/src/soundfont/basic_soundfont/write_sf2/pbag.js +39 -0
- package/src/soundfont/basic_soundfont/write_sf2/pgen.js +82 -0
- package/src/soundfont/basic_soundfont/write_sf2/phdr.js +42 -0
- package/src/soundfont/basic_soundfont/write_sf2/pmod.js +46 -0
- package/src/soundfont/basic_soundfont/write_sf2/sdta.js +80 -0
- package/src/soundfont/basic_soundfont/write_sf2/shdr.js +55 -0
- package/src/soundfont/basic_soundfont/write_sf2/write.js +222 -0
- package/src/soundfont/dls/articulator_converter.js +396 -0
- package/src/soundfont/dls/dls_destinations.js +38 -0
- package/src/soundfont/dls/dls_preset.js +44 -0
- package/src/soundfont/dls/dls_sample.js +75 -0
- package/src/soundfont/dls/dls_soundfont.js +186 -0
- package/src/soundfont/dls/dls_sources.js +62 -0
- package/src/soundfont/dls/dls_zone.js +95 -0
- package/src/soundfont/dls/read_articulation.js +299 -0
- package/src/soundfont/dls/read_instrument.js +121 -0
- package/src/soundfont/dls/read_instrument_list.js +17 -0
- package/src/soundfont/dls/read_lart.js +35 -0
- package/src/soundfont/dls/read_region.js +152 -0
- package/src/soundfont/dls/read_samples.js +270 -0
- package/src/soundfont/load_soundfont.js +21 -0
- package/src/soundfont/read_sf2/generators.js +46 -0
- package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/instruments.js +20 -14
- package/src/soundfont/read_sf2/modulators.js +36 -0
- package/src/soundfont/read_sf2/presets.js +80 -0
- package/src/soundfont/read_sf2/samples.js +304 -0
- package/src/soundfont/read_sf2/soundfont.js +305 -0
- package/{spessasynth_core/soundfont/chunk → src/soundfont/read_sf2}/zones.js +68 -69
- package/src/synthetizer/README.md +7 -0
- package/src/synthetizer/audio_engine/README.md +9 -0
- package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +266 -0
- package/src/synthetizer/audio_engine/engine_components/controller_tables.js +88 -0
- package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +150 -0
- package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/lfo.js +9 -6
- package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +282 -0
- package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +467 -0
- package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +181 -0
- package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/modulator_curves.js +33 -30
- package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +221 -0
- package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +120 -0
- package/{spessasynth_core/synthetizer/worklet_system/worklet_utilities → src/synthetizer/audio_engine/engine_components}/unit_converter.js +11 -4
- package/src/synthetizer/audio_engine/engine_components/voice.js +519 -0
- package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +401 -0
- package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +263 -0
- package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +132 -0
- package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +48 -0
- package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +241 -0
- package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +27 -0
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +253 -0
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +66 -0
- package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +17 -0
- package/src/synthetizer/audio_engine/engine_methods/note_on.js +175 -0
- package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +92 -0
- package/src/synthetizer/audio_engine/engine_methods/program_change.js +61 -0
- package/src/synthetizer/audio_engine/engine_methods/render_voice.js +196 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/clear_sound_font.js +30 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +22 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/reload_sound_font.js +28 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/send_preset_list.js +31 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/set_embedded_sound_font.js +21 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +20 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +55 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +16 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +30 -0
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +63 -0
- package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +776 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +24 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +33 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +31 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +15 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +27 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +19 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +27 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +15 -0
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +34 -0
- package/src/synthetizer/audio_engine/main_processor.js +804 -0
- package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +15 -0
- package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +175 -0
- package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +116 -0
- package/src/synthetizer/synth_constants.js +22 -0
- package/{spessasynth_core → src}/utils/README.md +1 -0
- package/src/utils/buffer_to_wav.js +185 -0
- package/src/utils/byte_functions/big_endian.js +32 -0
- package/src/utils/byte_functions/little_endian.js +77 -0
- package/src/utils/byte_functions/string.js +107 -0
- package/src/utils/byte_functions/variable_length_quantity.js +42 -0
- package/src/utils/fill_with_defaults.js +21 -0
- package/src/utils/indexed_array.js +52 -0
- package/{spessasynth_core → src}/utils/loggin.js +70 -78
- package/src/utils/other.js +92 -0
- package/src/utils/sysex_detector.js +58 -0
- package/src/utils/xg_hacks.js +193 -0
- package/.idea/inspectionProfiles/Project_Default.xml +0 -10
- package/.idea/jsLibraryMappings.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/spessasynth_core.iml +0 -12
- package/.idea/vcs.xml +0 -6
- package/spessasynth_core/midi_parser/README.md +0 -3
- package/spessasynth_core/midi_parser/midi_loader.js +0 -386
- package/spessasynth_core/sequencer/sequencer.js +0 -202
- package/spessasynth_core/sequencer/worklet_sequencer/play.js +0 -209
- package/spessasynth_core/sequencer/worklet_sequencer/process_event.js +0 -120
- package/spessasynth_core/sequencer/worklet_sequencer/song_control.js +0 -112
- package/spessasynth_core/soundfont/README.md +0 -4
- package/spessasynth_core/soundfont/chunk/generators.js +0 -205
- package/spessasynth_core/soundfont/chunk/modulators.js +0 -232
- package/spessasynth_core/soundfont/chunk/presets.js +0 -264
- package/spessasynth_core/soundfont/chunk/riff_chunk.js +0 -46
- package/spessasynth_core/soundfont/chunk/samples.js +0 -250
- package/spessasynth_core/soundfont/soundfont_parser.js +0 -301
- package/spessasynth_core/synthetizer/README.md +0 -6
- package/spessasynth_core/synthetizer/synthesizer.js +0 -303
- package/spessasynth_core/synthetizer/worklet_system/README.md +0 -3
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/controller_control.js +0 -290
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/data_entry.js +0 -280
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_off.js +0 -102
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/note_on.js +0 -77
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/program_control.js +0 -140
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/system_exclusive.js +0 -266
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/tuning_control.js +0 -104
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/vibrato_control.js +0 -29
- package/spessasynth_core/synthetizer/worklet_system/worklet_methods/voice_control.js +0 -222
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +0 -95
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +0 -73
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +0 -76
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +0 -194
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +0 -83
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +0 -173
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +0 -106
- package/spessasynth_core/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +0 -313
- package/spessasynth_core/utils/buffer_to_wav.js +0 -70
- package/spessasynth_core/utils/byte_functions.js +0 -141
- package/spessasynth_core/utils/other.js +0 -49
- package/spessasynth_core/utils/shiftable_array.js +0 -26
- package/spessasynth_core/utils/stbvorbis_sync.js +0 -1877
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import {RiffChunk} from "./riff_chunk.js";
|
|
2
|
-
import {PresetZone} from "./zones.js";
|
|
3
|
-
import {readBytesAsString, readBytesAsUintLittleEndian} from "../../utils/byte_functions.js";
|
|
4
|
-
import {Sample} from "./samples.js";
|
|
5
|
-
import { Generator, generatorTypes } from './generators.js'
|
|
6
|
-
import { defaultModulators } from './modulators.js'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* parses soundfont presets, also includes function for getting the generators and samples from midi note and velocity
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
export class Preset {
|
|
13
|
-
/**
|
|
14
|
-
* Creates a preset
|
|
15
|
-
* @param presetChunk {RiffChunk}
|
|
16
|
-
*/
|
|
17
|
-
constructor(presetChunk) {
|
|
18
|
-
this.presetName = readBytesAsString(presetChunk.chunkData, 20)
|
|
19
|
-
.trim()
|
|
20
|
-
.replace(/\d{3}:\d{3}/, ""); // remove those pesky "000:001"
|
|
21
|
-
|
|
22
|
-
this.program = readBytesAsUintLittleEndian(presetChunk.chunkData, 2);
|
|
23
|
-
this.bank = readBytesAsUintLittleEndian(presetChunk.chunkData, 2);
|
|
24
|
-
this.presetZoneStartIndex = readBytesAsUintLittleEndian(presetChunk.chunkData, 2);
|
|
25
|
-
this.presetZonesAmount = 0;
|
|
26
|
-
/**
|
|
27
|
-
* @type {PresetZone[]}
|
|
28
|
-
*/
|
|
29
|
-
this.presetZones = [];
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Stores already found getSamplesAndGenerators for reuse
|
|
33
|
-
* @type {SampleAndGenerators[][][]}
|
|
34
|
-
*/
|
|
35
|
-
this.foundSamplesAndGenerators = [];
|
|
36
|
-
for(let i = 0; i < 128; i++)
|
|
37
|
-
{
|
|
38
|
-
this.foundSamplesAndGenerators[i] = [];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// skip the DWORDs (4bytes times 3)
|
|
42
|
-
readBytesAsUintLittleEndian(presetChunk.chunkData, 12);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Loads all the preset zones, given the amount
|
|
47
|
-
* @param amount {number}
|
|
48
|
-
* @param zones {PresetZone[]}
|
|
49
|
-
*/
|
|
50
|
-
getPresetZones(amount, zones) {
|
|
51
|
-
this.presetZonesAmount = amount;
|
|
52
|
-
for (let i = this.presetZoneStartIndex; i < this.presetZonesAmount + this.presetZoneStartIndex; i++) {
|
|
53
|
-
this.presetZones.push(zones[i]);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Preloads all samples (async)
|
|
59
|
-
*/
|
|
60
|
-
preload(keyMin, keyMax)
|
|
61
|
-
{
|
|
62
|
-
for (let key = keyMin; key < keyMax + 1; key++)
|
|
63
|
-
{
|
|
64
|
-
for (let velocity = 0; velocity < 128; velocity++)
|
|
65
|
-
{
|
|
66
|
-
setTimeout(() => {
|
|
67
|
-
this.getSamplesAndGenerators(key, velocity).forEach(samandgen => {
|
|
68
|
-
if(!samandgen.sample.isSampleLoaded) {
|
|
69
|
-
samandgen.sample.loadBufferData().then();
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* @typedef {{
|
|
79
|
-
* instrumentGenerators: Generator[],
|
|
80
|
-
* presetGenerators: Generator[],
|
|
81
|
-
* modulators: Modulator[],
|
|
82
|
-
* sample: Sample,
|
|
83
|
-
* sampleID: number,
|
|
84
|
-
* }} SampleAndGenerators
|
|
85
|
-
*/
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Returns generatorTranslator and generators for given note
|
|
89
|
-
* @param midiNote {number}
|
|
90
|
-
* @param velocity {number}
|
|
91
|
-
* @returns {SampleAndGenerators[]}
|
|
92
|
-
*/
|
|
93
|
-
getSamplesAndGenerators(midiNote, velocity)
|
|
94
|
-
{
|
|
95
|
-
const memorized = this.foundSamplesAndGenerators[midiNote][velocity];
|
|
96
|
-
if(memorized)
|
|
97
|
-
{
|
|
98
|
-
return memorized;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function isInRange(min, max, number) {
|
|
102
|
-
return number >= min && number <= max;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* @param mod1 {Modulator}
|
|
107
|
-
* @param mod2 {Modulator}
|
|
108
|
-
* @returns {boolean}
|
|
109
|
-
*/
|
|
110
|
-
function identicalMod(mod1, mod2)
|
|
111
|
-
{
|
|
112
|
-
return (mod1.modulatorSource === mod2.modulatorSource)
|
|
113
|
-
&& (mod1.modulatorDestination === mod2.modulatorDestination)
|
|
114
|
-
&& (mod1.modulationSecondarySrc === mod2.modulationSecondarySrc)
|
|
115
|
-
&& (mod1.transformType === mod2.transformType);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* @param main {Generator[]}
|
|
120
|
-
* @param adder {Generator[]}
|
|
121
|
-
*/
|
|
122
|
-
function addUnique(main, adder)
|
|
123
|
-
{
|
|
124
|
-
main.push(...adder.filter(g => !main.find(mg => mg.generatorType === g.generatorType)));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* @param main {Modulator[]}
|
|
129
|
-
* @param adder {Modulator[]}
|
|
130
|
-
*/
|
|
131
|
-
function addUniqueMods(main, adder)
|
|
132
|
-
{
|
|
133
|
-
main.push(...adder.filter(m => !main.find(mm => identicalMod(m, mm))));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* @type {SampleAndGenerators[]}
|
|
138
|
-
*/
|
|
139
|
-
let parsedGeneratorsAndSamples = [];
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* global zone is always first, so it or nothing
|
|
143
|
-
* @type {Generator[]}
|
|
144
|
-
*/
|
|
145
|
-
let globalPresetGenerators = this.presetZones[0].isGlobal ? [...this.presetZones[0].generators] : [];
|
|
146
|
-
|
|
147
|
-
let globalPresetModulators = this.presetZones[0].isGlobal ? [...this.presetZones[0].modulators] : [];
|
|
148
|
-
|
|
149
|
-
// find the preset zones in range
|
|
150
|
-
let presetZonesInRange = this.presetZones.filter(currentZone =>
|
|
151
|
-
(
|
|
152
|
-
isInRange(currentZone.keyRange.min, currentZone.keyRange.max, midiNote)
|
|
153
|
-
&&
|
|
154
|
-
isInRange(currentZone.velRange.min, currentZone.velRange.max, velocity)
|
|
155
|
-
) && !currentZone.isGlobal);
|
|
156
|
-
|
|
157
|
-
presetZonesInRange.forEach(zone =>
|
|
158
|
-
{
|
|
159
|
-
let presetGenerators = zone.generators;
|
|
160
|
-
let presetModulators = zone.modulators;
|
|
161
|
-
/**
|
|
162
|
-
* global zone is always first, so it or nothing
|
|
163
|
-
* @type {Generator[]}
|
|
164
|
-
*/
|
|
165
|
-
let globalInstrumentGenerators = zone.instrument.instrumentZones[0].isGlobal ? [...zone.instrument.instrumentZones[0].generators] : [];
|
|
166
|
-
let globalInstrumentModulators = zone.instrument.instrumentZones[0].isGlobal ? [...zone.instrument.instrumentZones[0].modulators] : [];
|
|
167
|
-
|
|
168
|
-
let instrumentZonesInRange = zone.instrument.instrumentZones
|
|
169
|
-
.filter(currentZone =>
|
|
170
|
-
(
|
|
171
|
-
isInRange(currentZone.keyRange.min,
|
|
172
|
-
currentZone.keyRange.max,
|
|
173
|
-
midiNote)
|
|
174
|
-
&&
|
|
175
|
-
isInRange(currentZone.velRange.min,
|
|
176
|
-
currentZone.velRange.max,
|
|
177
|
-
velocity)
|
|
178
|
-
) && !currentZone.isGlobal
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
instrumentZonesInRange.forEach(instrumentZone =>
|
|
182
|
-
{
|
|
183
|
-
let instrumentGenerators = [...instrumentZone.generators];
|
|
184
|
-
let instrumentModulators = [...instrumentZone.modulators];
|
|
185
|
-
|
|
186
|
-
addUnique(presetGenerators, globalPresetGenerators);
|
|
187
|
-
// add the unique global preset generators (local replace global(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// add the unique global instrument generators (local replace global)
|
|
191
|
-
addUnique(instrumentGenerators, globalInstrumentGenerators);
|
|
192
|
-
|
|
193
|
-
addUniqueMods(presetModulators, globalPresetModulators);
|
|
194
|
-
addUniqueMods(instrumentModulators, globalInstrumentModulators);
|
|
195
|
-
|
|
196
|
-
// default mods
|
|
197
|
-
addUniqueMods(instrumentModulators, defaultModulators);
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* sum preset modulators to instruments (amount) sf spec page 54
|
|
201
|
-
* @type {Modulator[]}
|
|
202
|
-
*/
|
|
203
|
-
const finalModulatorList = [...instrumentModulators];
|
|
204
|
-
for(let i = 0; i < presetModulators.length; i++)
|
|
205
|
-
{
|
|
206
|
-
let mod = presetModulators[i];
|
|
207
|
-
const identicalInstrumentModulator = finalModulatorList.findIndex(m => identicalMod(mod, m));
|
|
208
|
-
if(identicalInstrumentModulator !== -1)
|
|
209
|
-
{
|
|
210
|
-
// sum the amounts (this makes a new modulator because otherwise it would overwrite the one in the soundfont!!!
|
|
211
|
-
finalModulatorList[identicalInstrumentModulator] = finalModulatorList[identicalInstrumentModulator].sumTransform(mod);
|
|
212
|
-
}
|
|
213
|
-
else
|
|
214
|
-
{
|
|
215
|
-
finalModulatorList.push(mod);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
// combine both generators and add to the final result
|
|
221
|
-
parsedGeneratorsAndSamples.push({
|
|
222
|
-
instrumentGenerators: instrumentGenerators,
|
|
223
|
-
presetGenerators: presetGenerators,
|
|
224
|
-
modulators: finalModulatorList,
|
|
225
|
-
sample: instrumentZone.sample,
|
|
226
|
-
sampleID: instrumentZone.generators.find(g => g.generatorType === generatorTypes.sampleID).generatorValue
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
// save and return
|
|
232
|
-
this.foundSamplesAndGenerators[midiNote][velocity] = parsedGeneratorsAndSamples;
|
|
233
|
-
return parsedGeneratorsAndSamples;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Reads the presets
|
|
239
|
-
* @param presetChunk {RiffChunk}
|
|
240
|
-
* @param presetZones {PresetZone[]}
|
|
241
|
-
* @returns {Preset[]}
|
|
242
|
-
*/
|
|
243
|
-
export function readPresets(presetChunk, presetZones)
|
|
244
|
-
{
|
|
245
|
-
/**
|
|
246
|
-
* @type {Preset[]}
|
|
247
|
-
*/
|
|
248
|
-
let presets = [];
|
|
249
|
-
while(presetChunk.chunkData.length > presetChunk.chunkData.currentIndex)
|
|
250
|
-
{
|
|
251
|
-
let preset = new Preset(presetChunk);
|
|
252
|
-
if(presets.length > 0)
|
|
253
|
-
{
|
|
254
|
-
let presetZonesAmount = preset.presetZoneStartIndex - presets[presets.length - 1].presetZoneStartIndex;
|
|
255
|
-
presets[presets.length - 1].getPresetZones(presetZonesAmount, presetZones);
|
|
256
|
-
}
|
|
257
|
-
presets.push(preset);
|
|
258
|
-
}
|
|
259
|
-
// remove EOP
|
|
260
|
-
if (presets.length > 1) {
|
|
261
|
-
presets.pop();
|
|
262
|
-
}
|
|
263
|
-
return presets;
|
|
264
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { ShiftableByteArray } from '../../utils/shiftable_array.js'
|
|
2
|
-
import { readBytesAsString, readBytesAsUintLittleEndian } from '../../utils/byte_functions.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* riff_chunk.js
|
|
6
|
-
* reads a riff chunk and stores it as a class
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export class RiffChunk
|
|
10
|
-
{
|
|
11
|
-
/**
|
|
12
|
-
* Creates a new riff chunk
|
|
13
|
-
* @constructor
|
|
14
|
-
* @param header {string}
|
|
15
|
-
* @param size {number}
|
|
16
|
-
* @param data {ShiftableByteArray}
|
|
17
|
-
*/
|
|
18
|
-
constructor(header, size, data) {
|
|
19
|
-
this.header = header;
|
|
20
|
-
this.size = size;
|
|
21
|
-
this.chunkData = data;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @param dataArray {ShiftableByteArray}
|
|
28
|
-
* @param readData {boolean}
|
|
29
|
-
* @returns {RiffChunk}
|
|
30
|
-
*/
|
|
31
|
-
export function readRIFFChunk(dataArray, readData = true) {
|
|
32
|
-
let header = readBytesAsString(dataArray, 4)
|
|
33
|
-
|
|
34
|
-
let size = readBytesAsUintLittleEndian(dataArray, 4)
|
|
35
|
-
let chunkData = undefined
|
|
36
|
-
if (readData) {
|
|
37
|
-
chunkData = new ShiftableByteArray(size)
|
|
38
|
-
chunkData.set(dataArray.slice(dataArray.currentIndex, dataArray.currentIndex + size))
|
|
39
|
-
dataArray.currentIndex += size
|
|
40
|
-
// for (let i = 0; i < size; i++) {
|
|
41
|
-
// chunkData[i] = readByte(dataArray);
|
|
42
|
-
// }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return new RiffChunk(header, size, chunkData)
|
|
46
|
-
}
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import {RiffChunk} from "./riff_chunk.js";
|
|
2
|
-
import {ShiftableByteArray} from "../../utils/shiftable_array.js";
|
|
3
|
-
import {readByte, readBytesAsUintLittleEndian, readBytesAsString, signedInt8} from "../../utils/byte_functions.js";
|
|
4
|
-
import { stbvorbis } from '../../utils/stbvorbis_sync.js'
|
|
5
|
-
import { SpessaSynthWarn } from '../../utils/loggin.js'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* samples.js
|
|
9
|
-
* purpose: parses soundfont samples, resamples if needed.
|
|
10
|
-
* loads sample data, handles async loading of sf3 compressed samples
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Reads the generatorTranslator from the shdr chunk
|
|
15
|
-
* @param sampleHeadersChunk {RiffChunk}
|
|
16
|
-
* @param smplChunkData {ShiftableByteArray}
|
|
17
|
-
* @returns {Sample[]}
|
|
18
|
-
*/
|
|
19
|
-
export function readSamples(sampleHeadersChunk, smplChunkData)
|
|
20
|
-
{
|
|
21
|
-
/**
|
|
22
|
-
* @type {Sample[]}
|
|
23
|
-
*/
|
|
24
|
-
let samples = [];
|
|
25
|
-
while(sampleHeadersChunk.chunkData.length > sampleHeadersChunk.chunkData.currentIndex)
|
|
26
|
-
{
|
|
27
|
-
const sample = readSample(sampleHeadersChunk.chunkData, smplChunkData);
|
|
28
|
-
samples.push(sample);
|
|
29
|
-
}
|
|
30
|
-
return samples;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Reads it into a sample
|
|
35
|
-
* @param sampleHeaderData {ShiftableByteArray}
|
|
36
|
-
* @param smplArrayData {ShiftableByteArray}
|
|
37
|
-
* @returns {Sample}
|
|
38
|
-
*/
|
|
39
|
-
function readSample(sampleHeaderData, smplArrayData) {
|
|
40
|
-
|
|
41
|
-
// read the sample name
|
|
42
|
-
let sampleName = readBytesAsString(sampleHeaderData, 20);
|
|
43
|
-
|
|
44
|
-
// read the sample start index
|
|
45
|
-
let sampleStartIndex = readBytesAsUintLittleEndian(sampleHeaderData, 4) * 2;
|
|
46
|
-
|
|
47
|
-
// read the sample end index
|
|
48
|
-
let sampleEndIndex = readBytesAsUintLittleEndian(sampleHeaderData, 4) * 2;
|
|
49
|
-
|
|
50
|
-
// read the sample looping start index
|
|
51
|
-
let sampleLoopStartIndex = readBytesAsUintLittleEndian(sampleHeaderData, 4) * 2;
|
|
52
|
-
|
|
53
|
-
// read the sample looping end index
|
|
54
|
-
let sampleLoopEndIndex = readBytesAsUintLittleEndian(sampleHeaderData, 4) * 2;
|
|
55
|
-
|
|
56
|
-
// read the sample rate
|
|
57
|
-
let sampleRate = readBytesAsUintLittleEndian(sampleHeaderData, 4);
|
|
58
|
-
|
|
59
|
-
// read the original sample pitch
|
|
60
|
-
let samplePitch = readByte(sampleHeaderData);
|
|
61
|
-
if(samplePitch === 255)
|
|
62
|
-
{
|
|
63
|
-
// if it's 255, then default to 60
|
|
64
|
-
samplePitch = 60;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// readt the sample pitch correction
|
|
68
|
-
let samplePitchCorrection = signedInt8(readByte(sampleHeaderData));
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// read the link to the other channel
|
|
72
|
-
let sampleLink = readBytesAsUintLittleEndian(sampleHeaderData, 2);
|
|
73
|
-
let sampleType = readBytesAsUintLittleEndian(sampleHeaderData, 2);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return new Sample(sampleName,
|
|
78
|
-
sampleStartIndex,
|
|
79
|
-
sampleEndIndex,
|
|
80
|
-
sampleLoopStartIndex,
|
|
81
|
-
sampleLoopEndIndex,
|
|
82
|
-
sampleRate,
|
|
83
|
-
samplePitch,
|
|
84
|
-
samplePitchCorrection,
|
|
85
|
-
sampleLink,
|
|
86
|
-
sampleType,
|
|
87
|
-
smplArrayData);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export class Sample {
|
|
91
|
-
/**
|
|
92
|
-
* Creates a sample
|
|
93
|
-
* @param sampleName {string}
|
|
94
|
-
* @param sampleStartIndex {number}
|
|
95
|
-
* @param sampleEndIndex {number}
|
|
96
|
-
* @param sampleLoopStartIndex {number}
|
|
97
|
-
* @param sampleLoopEndIndex {number}
|
|
98
|
-
* @param sampleRate {number}
|
|
99
|
-
* @param samplePitch {number}
|
|
100
|
-
* @param samplePitchCorrection {number}
|
|
101
|
-
* @param sampleLink {number}
|
|
102
|
-
* @param sampleType {number}
|
|
103
|
-
* @param smplArr {ShiftableByteArray}
|
|
104
|
-
*/
|
|
105
|
-
constructor(sampleName,
|
|
106
|
-
sampleStartIndex,
|
|
107
|
-
sampleEndIndex,
|
|
108
|
-
sampleLoopStartIndex,
|
|
109
|
-
sampleLoopEndIndex,
|
|
110
|
-
sampleRate,
|
|
111
|
-
samplePitch,
|
|
112
|
-
samplePitchCorrection,
|
|
113
|
-
sampleLink,
|
|
114
|
-
sampleType,
|
|
115
|
-
smplArr) {
|
|
116
|
-
this.sampleName = sampleName
|
|
117
|
-
// in bytes
|
|
118
|
-
this.sampleStartIndex = sampleStartIndex;
|
|
119
|
-
this.sampleEndIndex = sampleEndIndex;
|
|
120
|
-
this.isSampleLoaded = false;
|
|
121
|
-
|
|
122
|
-
this.sampleLoopStartIndex = sampleLoopStartIndex - sampleStartIndex;
|
|
123
|
-
this.sampleLoopEndIndex = sampleLoopEndIndex - sampleStartIndex;
|
|
124
|
-
this.sampleRate = sampleRate;
|
|
125
|
-
this.samplePitch = samplePitch;
|
|
126
|
-
this.samplePitchCorrection = samplePitchCorrection;
|
|
127
|
-
this.sampleLink = sampleLink;
|
|
128
|
-
this.sampleType = sampleType;
|
|
129
|
-
// in bytes
|
|
130
|
-
this.sampleLength = this.sampleEndIndex - this.sampleStartIndex;
|
|
131
|
-
this.indexRatio = 1;
|
|
132
|
-
this.sampleDataArray = smplArr;
|
|
133
|
-
this.sampleData = new Float32Array(0);
|
|
134
|
-
this.sampleLengthSeconds = this.sampleLength / (this.sampleRate * 2);
|
|
135
|
-
this.loopAllowed = this.sampleLoopStartIndex !== this.sampleLoopEndIndex;
|
|
136
|
-
this.isCompressed = (this.sampleType & 0x10) > 0;
|
|
137
|
-
|
|
138
|
-
if (this.sampleLength < 1 || this.sampleName.substring(0, 3).toLowerCase() === "eos") {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if(this.isCompressed)
|
|
143
|
-
{
|
|
144
|
-
// correct loop points
|
|
145
|
-
this.sampleLoopStartIndex += this.sampleStartIndex;
|
|
146
|
-
this.sampleLoopEndIndex += this.sampleStartIndex;
|
|
147
|
-
this.sampleLength = 99999999; // set to 999999 before we decode it
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* @param smplArr {ShiftableByteArray}
|
|
154
|
-
*/
|
|
155
|
-
decodeVorbis(smplArr)
|
|
156
|
-
{
|
|
157
|
-
if (this.sampleLength < 1) {
|
|
158
|
-
// eos, do not do anything
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
// get the compressed byte stream
|
|
162
|
-
const smplStart = smplArr.currentIndex;
|
|
163
|
-
const buff = smplArr.slice(this.sampleStartIndex / 2 + smplStart, this.sampleEndIndex / 2 + smplStart);
|
|
164
|
-
// reset array and being decoding
|
|
165
|
-
this.sampleData = new Float32Array(0);
|
|
166
|
-
/**
|
|
167
|
-
* @type {{data: Float32Array[], error: (string|null), sampleRate: number, eof: boolean}}
|
|
168
|
-
*/
|
|
169
|
-
const vorbis = stbvorbis.decode(buff.buffer);
|
|
170
|
-
this.sampleData = vorbis.data[0];
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* creates a sample sampleData and stores it for reuse
|
|
175
|
-
* @param startAddrOffset {number}
|
|
176
|
-
* @param endAddrOffset {number}
|
|
177
|
-
* @returns {Float32Array} The audioData
|
|
178
|
-
*/
|
|
179
|
-
getAudioData(startAddrOffset = 0, endAddrOffset = 0) {
|
|
180
|
-
if (!this.isSampleLoaded) {
|
|
181
|
-
// start loading data if not loaded
|
|
182
|
-
return this.loadBufferData();
|
|
183
|
-
}
|
|
184
|
-
// if no offset, return saved sampleData
|
|
185
|
-
if (this.sampleData && startAddrOffset === 0 && endAddrOffset === 0) {
|
|
186
|
-
return this.sampleData;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return this.getOffsetData(startAddrOffset, endAddrOffset);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* @param smplArr {ShiftableByteArray}
|
|
194
|
-
* @returns {Float32Array}
|
|
195
|
-
*/
|
|
196
|
-
loadUncompressedData(smplArr)
|
|
197
|
-
{
|
|
198
|
-
if(this.isCompressed)
|
|
199
|
-
{
|
|
200
|
-
SpessaSynthWarn("Trying to load a compressed sample via loadUncompressedData()... aborting!");
|
|
201
|
-
return new Float32Array(0);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// read the sample data
|
|
205
|
-
let audioData = new Float32Array(this.sampleLength / 2 + 1);
|
|
206
|
-
const dataStartIndex = smplArr.currentIndex;
|
|
207
|
-
let convertedSigned16 = new Int16Array(
|
|
208
|
-
smplArr.slice(dataStartIndex + this.sampleStartIndex, dataStartIndex + this.sampleEndIndex)
|
|
209
|
-
.buffer
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
// convert to float
|
|
213
|
-
for(let i = 0; i < convertedSigned16.length; i++)
|
|
214
|
-
{
|
|
215
|
-
audioData[i] = convertedSigned16[i] / 32768;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
this.sampleData = audioData;
|
|
219
|
-
this.isSampleLoaded = true;
|
|
220
|
-
return audioData;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* @returns {Float32Array}
|
|
225
|
-
*/
|
|
226
|
-
loadBufferData() {
|
|
227
|
-
if (this.sampleLength < 1) {
|
|
228
|
-
// eos, do not do anything
|
|
229
|
-
return new Float32Array(1);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if(this.isCompressed)
|
|
233
|
-
{
|
|
234
|
-
this.decodeVorbis(this.sampleDataArray);
|
|
235
|
-
this.isSampleLoaded = true;
|
|
236
|
-
return this.sampleData;
|
|
237
|
-
}
|
|
238
|
-
return this.loadUncompressedData(this.sampleDataArray);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Creates a sample sampleData
|
|
243
|
-
* @param startOffset {number}
|
|
244
|
-
* @param endOffset {number}
|
|
245
|
-
* @returns {Float32Array}
|
|
246
|
-
*/
|
|
247
|
-
getOffsetData(startOffset, endOffset) {
|
|
248
|
-
return this.sampleData.subarray(startOffset, this.sampleData.length - endOffset + 1);
|
|
249
|
-
}
|
|
250
|
-
}
|