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
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
2
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
3
|
+
import { writeRIFFOddSize } from "../riff_chunk.js";
|
|
4
|
+
|
|
5
|
+
const WSMP_SIZE = 20;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param sample {BasicSample}
|
|
9
|
+
* @param rootKey {number}
|
|
10
|
+
* @param tuning {number}
|
|
11
|
+
* @param attenuationCentibels {number} CENTIBELS, NO CORRECTION
|
|
12
|
+
* @param loopStart {number}
|
|
13
|
+
* @param loopEnd {number}
|
|
14
|
+
* @param loopingMode {number}
|
|
15
|
+
* @returns {IndexedByteArray}
|
|
16
|
+
*/
|
|
17
|
+
export function writeWavesample(
|
|
18
|
+
sample,
|
|
19
|
+
rootKey,
|
|
20
|
+
tuning,
|
|
21
|
+
attenuationCentibels,
|
|
22
|
+
loopStart,
|
|
23
|
+
loopEnd,
|
|
24
|
+
loopingMode)
|
|
25
|
+
{
|
|
26
|
+
let loopCount = loopingMode === 0 ? 0 : 1;
|
|
27
|
+
const wsmpData = new IndexedByteArray(WSMP_SIZE + loopCount * 16);
|
|
28
|
+
writeDword(wsmpData, WSMP_SIZE); // cbSize
|
|
29
|
+
// usUnityNote (apply root pitch here)
|
|
30
|
+
writeWord(wsmpData, rootKey);
|
|
31
|
+
// sFineTune
|
|
32
|
+
writeWord(wsmpData, tuning);
|
|
33
|
+
|
|
34
|
+
// gain correction, use InitialAttenuation, apply attenuation correction
|
|
35
|
+
const attenuationCb = attenuationCentibels * 0.4;
|
|
36
|
+
|
|
37
|
+
// gain correction: Each unit of gain represents 1/655360 dB
|
|
38
|
+
const lGain = Math.floor(attenuationCb * -65536);
|
|
39
|
+
writeDword(wsmpData, lGain);
|
|
40
|
+
// fulOptions: has to be 2, according to all DLS files I have
|
|
41
|
+
writeDword(wsmpData, 2);
|
|
42
|
+
|
|
43
|
+
const loopSize = loopEnd - loopStart;
|
|
44
|
+
let ulLoopType = 0;
|
|
45
|
+
switch (loopingMode)
|
|
46
|
+
{
|
|
47
|
+
default:
|
|
48
|
+
case 0:
|
|
49
|
+
// no loop
|
|
50
|
+
loopCount = 0;
|
|
51
|
+
break;
|
|
52
|
+
|
|
53
|
+
case 1:
|
|
54
|
+
// loop
|
|
55
|
+
ulLoopType = 0;
|
|
56
|
+
loopCount = 1;
|
|
57
|
+
break;
|
|
58
|
+
|
|
59
|
+
case 3:
|
|
60
|
+
// loop and release
|
|
61
|
+
ulLoopType = 1;
|
|
62
|
+
loopCount = 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// cSampleLoops
|
|
66
|
+
writeDword(wsmpData, loopCount);
|
|
67
|
+
if (loopCount === 1)
|
|
68
|
+
{
|
|
69
|
+
writeDword(wsmpData, 16); // cbSize
|
|
70
|
+
writeDword(wsmpData, ulLoopType);
|
|
71
|
+
writeDword(wsmpData, loopStart);
|
|
72
|
+
writeDword(wsmpData, loopSize);
|
|
73
|
+
}
|
|
74
|
+
return writeRIFFOddSize(
|
|
75
|
+
"wsmp",
|
|
76
|
+
wsmpData
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { writeDLSSample } from "./wave.js";
|
|
2
|
+
import { writeRIFFOddSize } from "../riff_chunk.js";
|
|
3
|
+
import { combineArrays } from "../../../utils/indexed_array.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @this {BasicSoundBank}
|
|
7
|
+
* @returns {{data: IndexedByteArray, indexes: number[] }}
|
|
8
|
+
*/
|
|
9
|
+
export function writeWavePool()
|
|
10
|
+
{
|
|
11
|
+
let currentIndex = 0;
|
|
12
|
+
const offsets = [];
|
|
13
|
+
/**
|
|
14
|
+
* @type {IndexedByteArray[]}
|
|
15
|
+
*/
|
|
16
|
+
const samples = this.samples.map(s =>
|
|
17
|
+
{
|
|
18
|
+
const out = writeDLSSample(s);
|
|
19
|
+
offsets.push(currentIndex);
|
|
20
|
+
currentIndex += out.length;
|
|
21
|
+
return out;
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
data: writeRIFFOddSize(
|
|
25
|
+
"wvpl",
|
|
26
|
+
combineArrays(samples),
|
|
27
|
+
false,
|
|
28
|
+
true
|
|
29
|
+
),
|
|
30
|
+
indexes: offsets
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
2
|
+
import { writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
3
|
+
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @this {BasicSoundBank}
|
|
7
|
+
* @returns {IndexedByteArray}
|
|
8
|
+
*/
|
|
9
|
+
export function getIBAG()
|
|
10
|
+
{
|
|
11
|
+
// write all ibag with their start indexes as they were changed in getIGEN() and getIMOD()
|
|
12
|
+
const ibagsize = this.instruments.reduce((sum, i) => i.instrumentZones.length * 4 + sum, 4);
|
|
13
|
+
const ibagdata = new IndexedByteArray(ibagsize);
|
|
14
|
+
let zoneID = 0;
|
|
15
|
+
let generatorIndex = 0;
|
|
16
|
+
let modulatorIndex = 0;
|
|
17
|
+
for (const inst of this.instruments)
|
|
18
|
+
{
|
|
19
|
+
inst.instrumentZoneIndex = zoneID;
|
|
20
|
+
for (const ibag of inst.instrumentZones)
|
|
21
|
+
{
|
|
22
|
+
ibag.zoneID = zoneID;
|
|
23
|
+
writeWord(ibagdata, generatorIndex);
|
|
24
|
+
writeWord(ibagdata, modulatorIndex);
|
|
25
|
+
generatorIndex += ibag.generators.length;
|
|
26
|
+
modulatorIndex += ibag.modulators.length;
|
|
27
|
+
zoneID++;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// write the terminal IBAG
|
|
31
|
+
writeWord(ibagdata, generatorIndex);
|
|
32
|
+
writeWord(ibagdata, modulatorIndex);
|
|
33
|
+
|
|
34
|
+
return writeRIFFChunk(new RiffChunk(
|
|
35
|
+
"ibag",
|
|
36
|
+
ibagdata.length,
|
|
37
|
+
ibagdata
|
|
38
|
+
));
|
|
39
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
2
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
3
|
+
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
4
|
+
|
|
5
|
+
import { Generator, generatorTypes } from "../generator.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @this {BasicSoundBank}
|
|
9
|
+
* @returns {IndexedByteArray}
|
|
10
|
+
*/
|
|
11
|
+
export function getIGEN()
|
|
12
|
+
{
|
|
13
|
+
// go through all instruments -> zones and write generators sequentially (add 4 for terminal)
|
|
14
|
+
let igensize = 4;
|
|
15
|
+
for (const inst of this.instruments)
|
|
16
|
+
{
|
|
17
|
+
igensize += inst.instrumentZones.reduce((sum, z) =>
|
|
18
|
+
{
|
|
19
|
+
// clear sample and range generators before determining the size
|
|
20
|
+
z.generators = z.generators.filter(g =>
|
|
21
|
+
g.generatorType !== generatorTypes.sampleID &&
|
|
22
|
+
g.generatorType !== generatorTypes.keyRange &&
|
|
23
|
+
g.generatorType !== generatorTypes.velRange
|
|
24
|
+
);
|
|
25
|
+
// add sample and ranges if necessary
|
|
26
|
+
// unshift vel then key (to make key first) and the instrument is last
|
|
27
|
+
if (z.velRange.max !== 127 || z.velRange.min !== 0)
|
|
28
|
+
{
|
|
29
|
+
z.generators.unshift(new Generator(
|
|
30
|
+
generatorTypes.velRange,
|
|
31
|
+
z.velRange.max << 8 | Math.max(z.velRange.min, 0),
|
|
32
|
+
false
|
|
33
|
+
));
|
|
34
|
+
}
|
|
35
|
+
if (z.keyRange.max !== 127 || z.keyRange.min !== 0)
|
|
36
|
+
{
|
|
37
|
+
z.generators.unshift(new Generator(
|
|
38
|
+
generatorTypes.keyRange,
|
|
39
|
+
z.keyRange.max << 8 | Math.max(z.keyRange.min, 0),
|
|
40
|
+
false
|
|
41
|
+
));
|
|
42
|
+
}
|
|
43
|
+
if (!z.isGlobal)
|
|
44
|
+
{
|
|
45
|
+
// write sample
|
|
46
|
+
z.generators.push(new Generator(
|
|
47
|
+
generatorTypes.sampleID,
|
|
48
|
+
this.samples.indexOf(z.sample),
|
|
49
|
+
false
|
|
50
|
+
));
|
|
51
|
+
}
|
|
52
|
+
return z.generators.length * 4 + sum;
|
|
53
|
+
}, 0);
|
|
54
|
+
}
|
|
55
|
+
const igendata = new IndexedByteArray(igensize);
|
|
56
|
+
let igenIndex = 0;
|
|
57
|
+
for (const instrument of this.instruments)
|
|
58
|
+
{
|
|
59
|
+
for (const instrumentZone of instrument.instrumentZones)
|
|
60
|
+
{
|
|
61
|
+
// set the start index here
|
|
62
|
+
instrumentZone.generatorZoneStartIndex = igenIndex;
|
|
63
|
+
for (const gen of instrumentZone.generators)
|
|
64
|
+
{
|
|
65
|
+
// name is deceptive, it works on negatives
|
|
66
|
+
writeWord(igendata, gen.generatorType);
|
|
67
|
+
writeWord(igendata, gen.generatorValue);
|
|
68
|
+
igenIndex++;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// terminal generator, is zero
|
|
73
|
+
writeDword(igendata, 0);
|
|
74
|
+
|
|
75
|
+
return writeRIFFChunk(new RiffChunk(
|
|
76
|
+
"igen",
|
|
77
|
+
igendata.length,
|
|
78
|
+
igendata
|
|
79
|
+
));
|
|
80
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
2
|
+
import { writeLittleEndian, writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
3
|
+
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @this {BasicSoundBank}
|
|
7
|
+
* @returns {IndexedByteArray}
|
|
8
|
+
*/
|
|
9
|
+
export function getIMOD()
|
|
10
|
+
{
|
|
11
|
+
// very similar to igen,
|
|
12
|
+
// go through all instruments -> zones and write modulators sequentially
|
|
13
|
+
let imodsize = 10;
|
|
14
|
+
for (const inst of this.instruments)
|
|
15
|
+
{
|
|
16
|
+
imodsize += inst.instrumentZones.reduce((sum, z) => z.modulators.length * 10 + sum, 0);
|
|
17
|
+
}
|
|
18
|
+
const imoddata = new IndexedByteArray(imodsize);
|
|
19
|
+
let imodIndex = 0;
|
|
20
|
+
for (const inst of this.instruments)
|
|
21
|
+
{
|
|
22
|
+
for (const ibag of inst.instrumentZones)
|
|
23
|
+
{
|
|
24
|
+
// set the start index here
|
|
25
|
+
ibag.modulatorZoneStartIndex = imodIndex;
|
|
26
|
+
for (const mod of ibag.modulators)
|
|
27
|
+
{
|
|
28
|
+
writeWord(imoddata, mod.sourceEnum);
|
|
29
|
+
writeWord(imoddata, mod.modulatorDestination);
|
|
30
|
+
writeWord(imoddata, mod.transformAmount);
|
|
31
|
+
writeWord(imoddata, mod.secondarySourceEnum);
|
|
32
|
+
writeWord(imoddata, mod.transformType);
|
|
33
|
+
imodIndex++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// terminal modulator, is zero
|
|
39
|
+
writeLittleEndian(imoddata, 0, 10);
|
|
40
|
+
|
|
41
|
+
return writeRIFFChunk(new RiffChunk(
|
|
42
|
+
"imod",
|
|
43
|
+
imoddata.length,
|
|
44
|
+
imoddata
|
|
45
|
+
));
|
|
46
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
2
|
+
import { writeStringAsBytes } from "../../../utils/byte_functions/string.js";
|
|
3
|
+
import { writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
4
|
+
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @this {BasicSoundBank}
|
|
8
|
+
* @returns {IndexedByteArray}
|
|
9
|
+
*/
|
|
10
|
+
export function getINST()
|
|
11
|
+
{
|
|
12
|
+
const instsize = this.instruments.length * 22 + 22;
|
|
13
|
+
const instdata = new IndexedByteArray(instsize);
|
|
14
|
+
// the instrument start index is adjusted in ibag, write it here
|
|
15
|
+
let instrumentStart = 0;
|
|
16
|
+
let instrumentID = 0;
|
|
17
|
+
for (const inst of this.instruments)
|
|
18
|
+
{
|
|
19
|
+
writeStringAsBytes(instdata, inst.instrumentName, 20);
|
|
20
|
+
writeWord(instdata, instrumentStart);
|
|
21
|
+
instrumentStart += inst.instrumentZones.length;
|
|
22
|
+
inst.instrumentID = instrumentID;
|
|
23
|
+
instrumentID++;
|
|
24
|
+
}
|
|
25
|
+
// write EOI
|
|
26
|
+
writeStringAsBytes(instdata, "EOI", 20);
|
|
27
|
+
writeWord(instdata, instrumentStart);
|
|
28
|
+
|
|
29
|
+
return writeRIFFChunk(new RiffChunk(
|
|
30
|
+
"inst",
|
|
31
|
+
instdata.length,
|
|
32
|
+
instdata
|
|
33
|
+
));
|
|
34
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
2
|
+
import { writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
3
|
+
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @this {BasicSoundBank}
|
|
7
|
+
* @returns {IndexedByteArray}
|
|
8
|
+
*/
|
|
9
|
+
export function getPBAG()
|
|
10
|
+
{
|
|
11
|
+
// write all pbag with their start indexes as they were changed in getPGEN() and getPMOD()
|
|
12
|
+
const pbagsize = this.presets.reduce((sum, i) => i.presetZones.length * 4 + sum, 4);
|
|
13
|
+
const pbagdata = new IndexedByteArray(pbagsize);
|
|
14
|
+
let zoneID = 0;
|
|
15
|
+
let generatorIndex = 0;
|
|
16
|
+
let modulatorIndex = 0;
|
|
17
|
+
for (const preset of this.presets)
|
|
18
|
+
{
|
|
19
|
+
preset.presetZoneStartIndex = zoneID;
|
|
20
|
+
for (const pbag of preset.presetZones)
|
|
21
|
+
{
|
|
22
|
+
pbag.zoneID = zoneID;
|
|
23
|
+
writeWord(pbagdata, generatorIndex);
|
|
24
|
+
writeWord(pbagdata, modulatorIndex);
|
|
25
|
+
generatorIndex += pbag.generators.length;
|
|
26
|
+
modulatorIndex += pbag.modulators.length;
|
|
27
|
+
zoneID++;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// write the terminal PBAG
|
|
31
|
+
writeWord(pbagdata, generatorIndex);
|
|
32
|
+
writeWord(pbagdata, modulatorIndex);
|
|
33
|
+
|
|
34
|
+
return writeRIFFChunk(new RiffChunk(
|
|
35
|
+
"pbag",
|
|
36
|
+
pbagdata.length,
|
|
37
|
+
pbagdata
|
|
38
|
+
));
|
|
39
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
2
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
3
|
+
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
4
|
+
|
|
5
|
+
import { Generator, generatorTypes } from "../generator.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @this {BasicSoundBank}
|
|
9
|
+
* @returns {IndexedByteArray}
|
|
10
|
+
*/
|
|
11
|
+
export function getPGEN()
|
|
12
|
+
{
|
|
13
|
+
// almost identical to igen, except the correct instrument instead of sample gen
|
|
14
|
+
// goes through all preset zones and writes generators sequentially (add 4 for terminal)
|
|
15
|
+
let pgensize = 4;
|
|
16
|
+
for (const preset of this.presets)
|
|
17
|
+
{
|
|
18
|
+
pgensize += preset.presetZones.reduce((size, z) =>
|
|
19
|
+
{
|
|
20
|
+
// clear instrument and range generators before determining the size
|
|
21
|
+
z.generators = z.generators.filter(g =>
|
|
22
|
+
g.generatorType !== generatorTypes.instrument &&
|
|
23
|
+
g.generatorType !== generatorTypes.keyRange &&
|
|
24
|
+
g.generatorType !== generatorTypes.velRange
|
|
25
|
+
);
|
|
26
|
+
// unshift vel then key and instrument is last
|
|
27
|
+
if (z.velRange.max !== 127 || z.velRange.min !== 0)
|
|
28
|
+
{
|
|
29
|
+
z.generators.unshift(new Generator(
|
|
30
|
+
generatorTypes.velRange,
|
|
31
|
+
z.velRange.max << 8 | Math.max(z.velRange.min, 0),
|
|
32
|
+
false
|
|
33
|
+
));
|
|
34
|
+
}
|
|
35
|
+
if (z.keyRange.max !== 127 || z.keyRange.min !== 0)
|
|
36
|
+
{
|
|
37
|
+
z.generators.unshift(new Generator(
|
|
38
|
+
generatorTypes.keyRange,
|
|
39
|
+
z.keyRange.max << 8 | Math.max(z.keyRange.min, 0),
|
|
40
|
+
false
|
|
41
|
+
));
|
|
42
|
+
}
|
|
43
|
+
if (!z.isGlobal)
|
|
44
|
+
{
|
|
45
|
+
// write the instrument
|
|
46
|
+
z.generators.push(new Generator(
|
|
47
|
+
generatorTypes.instrument,
|
|
48
|
+
this.instruments.indexOf(z.instrument),
|
|
49
|
+
false
|
|
50
|
+
));
|
|
51
|
+
}
|
|
52
|
+
return z.generators.length * 4 + size;
|
|
53
|
+
}, 0);
|
|
54
|
+
}
|
|
55
|
+
const pgendata = new IndexedByteArray(pgensize);
|
|
56
|
+
let pgenIndex = 0;
|
|
57
|
+
for (const preset of this.presets)
|
|
58
|
+
{
|
|
59
|
+
for (const presetZone of preset.presetZones)
|
|
60
|
+
{
|
|
61
|
+
// set the start index here
|
|
62
|
+
presetZone.generatorZoneStartIndex = pgenIndex;
|
|
63
|
+
// write generators
|
|
64
|
+
for (const gen of presetZone.generators)
|
|
65
|
+
{
|
|
66
|
+
// name is deceptive, it works on negatives
|
|
67
|
+
writeWord(pgendata, gen.generatorType);
|
|
68
|
+
writeWord(pgendata, gen.generatorValue);
|
|
69
|
+
}
|
|
70
|
+
pgenIndex += presetZone.generators.length;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// terminal generator, is zero
|
|
74
|
+
writeWord(pgendata, 0);
|
|
75
|
+
writeWord(pgendata, 0);
|
|
76
|
+
|
|
77
|
+
return writeRIFFChunk(new RiffChunk(
|
|
78
|
+
"pgen",
|
|
79
|
+
pgendata.length,
|
|
80
|
+
pgendata
|
|
81
|
+
));
|
|
82
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
2
|
+
import { writeStringAsBytes } from "../../../utils/byte_functions/string.js";
|
|
3
|
+
import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
4
|
+
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @this {BasicSoundBank}
|
|
8
|
+
* @returns {IndexedByteArray}
|
|
9
|
+
*/
|
|
10
|
+
export function getPHDR()
|
|
11
|
+
{
|
|
12
|
+
const phdrsize = this.presets.length * 38 + 38;
|
|
13
|
+
const phdrdata = new IndexedByteArray(phdrsize);
|
|
14
|
+
// the preset start is adjusted in pbag, this is only for the terminal preset index
|
|
15
|
+
let presetStart = 0;
|
|
16
|
+
for (const preset of this.presets)
|
|
17
|
+
{
|
|
18
|
+
writeStringAsBytes(phdrdata, preset.presetName, 20);
|
|
19
|
+
writeWord(phdrdata, preset.program);
|
|
20
|
+
writeWord(phdrdata, preset.bank);
|
|
21
|
+
writeWord(phdrdata, presetStart);
|
|
22
|
+
// 3 unused dword, spec says to keep em so we do
|
|
23
|
+
writeDword(phdrdata, preset.library);
|
|
24
|
+
writeDword(phdrdata, preset.genre);
|
|
25
|
+
writeDword(phdrdata, preset.morphology);
|
|
26
|
+
presetStart += preset.presetZones.length;
|
|
27
|
+
}
|
|
28
|
+
// write EOP
|
|
29
|
+
writeStringAsBytes(phdrdata, "EOP", 20);
|
|
30
|
+
writeWord(phdrdata, 0); // program
|
|
31
|
+
writeWord(phdrdata, 0); // bank
|
|
32
|
+
writeWord(phdrdata, presetStart);
|
|
33
|
+
writeDword(phdrdata, 0); // library
|
|
34
|
+
writeDword(phdrdata, 0); // genre
|
|
35
|
+
writeDword(phdrdata, 0); // morphology
|
|
36
|
+
|
|
37
|
+
return writeRIFFChunk(new RiffChunk(
|
|
38
|
+
"phdr",
|
|
39
|
+
phdrdata.length,
|
|
40
|
+
phdrdata
|
|
41
|
+
));
|
|
42
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
2
|
+
import { writeLittleEndian, writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
3
|
+
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @this {BasicSoundBank}
|
|
7
|
+
* @returns {IndexedByteArray}
|
|
8
|
+
*/
|
|
9
|
+
export function getPMOD()
|
|
10
|
+
{
|
|
11
|
+
// very similar to imod,
|
|
12
|
+
// go through all presets -> zones and write modulators sequentially
|
|
13
|
+
let pmodsize = 10;
|
|
14
|
+
for (const preset of this.presets)
|
|
15
|
+
{
|
|
16
|
+
pmodsize += preset.presetZones.reduce((sum, z) => z.modulators.length * 10 + sum, 0);
|
|
17
|
+
}
|
|
18
|
+
const pmoddata = new IndexedByteArray(pmodsize);
|
|
19
|
+
let pmodIndex = 0;
|
|
20
|
+
for (const preset of this.presets)
|
|
21
|
+
{
|
|
22
|
+
for (const pbag of preset.presetZones)
|
|
23
|
+
{
|
|
24
|
+
// set the start index here
|
|
25
|
+
pbag.modulatorZoneStartIndex = pmodIndex;
|
|
26
|
+
for (const mod of pbag.modulators)
|
|
27
|
+
{
|
|
28
|
+
writeWord(pmoddata, mod.sourceEnum);
|
|
29
|
+
writeWord(pmoddata, mod.modulatorDestination);
|
|
30
|
+
writeWord(pmoddata, mod.transformAmount);
|
|
31
|
+
writeWord(pmoddata, mod.secondarySourceEnum);
|
|
32
|
+
writeWord(pmoddata, mod.transformType);
|
|
33
|
+
pmodIndex++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// terminal modulator, is zero
|
|
39
|
+
writeLittleEndian(pmoddata, 0, 10);
|
|
40
|
+
|
|
41
|
+
return writeRIFFChunk(new RiffChunk(
|
|
42
|
+
"pmod",
|
|
43
|
+
pmoddata.length,
|
|
44
|
+
pmoddata
|
|
45
|
+
));
|
|
46
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
2
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
3
|
+
import { SpessaSynthInfo } from "../../../utils/loggin.js";
|
|
4
|
+
import { consoleColors } from "../../../utils/other.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @this {BasicSoundBank}
|
|
8
|
+
* @param smplStartOffsets {number[]}
|
|
9
|
+
* @param smplEndOffsets {number[]}
|
|
10
|
+
* @param compress {boolean}
|
|
11
|
+
* @param quality {number}
|
|
12
|
+
* @param vorbisFunc {EncodeVorbisFunction}
|
|
13
|
+
* @returns {IndexedByteArray}
|
|
14
|
+
*/
|
|
15
|
+
export function getSDTA(smplStartOffsets, smplEndOffsets, compress, quality, vorbisFunc)
|
|
16
|
+
{
|
|
17
|
+
// write smpl: write int16 data of each sample linearly
|
|
18
|
+
// get size (calling getAudioData twice doesn't matter since it gets cached)
|
|
19
|
+
const sampleDatas = this.samples.map((s, i) =>
|
|
20
|
+
{
|
|
21
|
+
if (compress)
|
|
22
|
+
{
|
|
23
|
+
s.compressSample(quality, vorbisFunc);
|
|
24
|
+
}
|
|
25
|
+
const r = s.getRawData();
|
|
26
|
+
SpessaSynthInfo(
|
|
27
|
+
`%cEncoded sample %c${i}. ${s.sampleName}%c of %c${this.samples.length}%c. Compressed: %c${s.isCompressed}%c.`,
|
|
28
|
+
consoleColors.info,
|
|
29
|
+
consoleColors.recognized,
|
|
30
|
+
consoleColors.info,
|
|
31
|
+
consoleColors.recognized,
|
|
32
|
+
consoleColors.info,
|
|
33
|
+
s.isCompressed ? consoleColors.recognized : consoleColors.unrecognized,
|
|
34
|
+
consoleColors.info
|
|
35
|
+
);
|
|
36
|
+
return r;
|
|
37
|
+
});
|
|
38
|
+
const smplSize = this.samples.reduce((total, s, i) =>
|
|
39
|
+
{
|
|
40
|
+
return total + sampleDatas[i].length + 46;
|
|
41
|
+
}, 0);
|
|
42
|
+
const smplData = new IndexedByteArray(smplSize);
|
|
43
|
+
// resample to int16 and write out
|
|
44
|
+
this.samples.forEach((sample, i) =>
|
|
45
|
+
{
|
|
46
|
+
const data = sampleDatas[i];
|
|
47
|
+
let startOffset;
|
|
48
|
+
let endOffset;
|
|
49
|
+
let jump = data.length;
|
|
50
|
+
if (sample.isCompressed)
|
|
51
|
+
{
|
|
52
|
+
// sf3 offset is in bytes
|
|
53
|
+
startOffset = smplData.currentIndex;
|
|
54
|
+
endOffset = startOffset + data.length;
|
|
55
|
+
}
|
|
56
|
+
else
|
|
57
|
+
{
|
|
58
|
+
// sf2 in sample data points
|
|
59
|
+
startOffset = smplData.currentIndex / 2;
|
|
60
|
+
endOffset = startOffset + data.length / 2;
|
|
61
|
+
jump += 46;
|
|
62
|
+
}
|
|
63
|
+
smplStartOffsets.push(startOffset);
|
|
64
|
+
smplData.set(data, smplData.currentIndex);
|
|
65
|
+
smplData.currentIndex += jump;
|
|
66
|
+
smplEndOffsets.push(endOffset);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const smplChunk = writeRIFFChunk(new RiffChunk(
|
|
70
|
+
"smpl",
|
|
71
|
+
smplData.length,
|
|
72
|
+
smplData
|
|
73
|
+
), new IndexedByteArray([115, 100, 116, 97])); // `sdta`
|
|
74
|
+
|
|
75
|
+
return writeRIFFChunk(new RiffChunk(
|
|
76
|
+
"LIST",
|
|
77
|
+
smplChunk.length,
|
|
78
|
+
smplChunk
|
|
79
|
+
));
|
|
80
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
2
|
+
import { writeStringAsBytes } from "../../../utils/byte_functions/string.js";
|
|
3
|
+
import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
4
|
+
import { RiffChunk, writeRIFFChunk } from "../riff_chunk.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @this {BasicSoundBank}
|
|
8
|
+
* @param smplStartOffsets {number[]}
|
|
9
|
+
* @param smplEndOffsets {number[]}
|
|
10
|
+
* @returns {IndexedByteArray}
|
|
11
|
+
*/
|
|
12
|
+
export function getSHDR(smplStartOffsets, smplEndOffsets)
|
|
13
|
+
{
|
|
14
|
+
const sampleLength = 46;
|
|
15
|
+
const shdrData = new IndexedByteArray(sampleLength * (this.samples.length + 1)); // +1 because EOP
|
|
16
|
+
this.samples.forEach((sample, index) =>
|
|
17
|
+
{
|
|
18
|
+
// sample name
|
|
19
|
+
writeStringAsBytes(shdrData, sample.sampleName, 20);
|
|
20
|
+
// start offset
|
|
21
|
+
const dwStart = smplStartOffsets[index];
|
|
22
|
+
writeDword(shdrData, dwStart);
|
|
23
|
+
// end offset
|
|
24
|
+
const dwEnd = smplEndOffsets[index];
|
|
25
|
+
writeDword(shdrData, dwEnd);
|
|
26
|
+
// loop is stored as relative in sample points, change it to absolute sample points here
|
|
27
|
+
let loopStart = sample.sampleLoopStartIndex + dwStart;
|
|
28
|
+
let loopEnd = sample.sampleLoopEndIndex + dwStart;
|
|
29
|
+
if (sample.isCompressed)
|
|
30
|
+
{
|
|
31
|
+
// https://github.com/FluidSynth/fluidsynth/wiki/SoundFont3Format
|
|
32
|
+
loopStart -= dwStart;
|
|
33
|
+
loopEnd -= dwStart;
|
|
34
|
+
}
|
|
35
|
+
writeDword(shdrData, loopStart);
|
|
36
|
+
writeDword(shdrData, loopEnd);
|
|
37
|
+
// sample rate
|
|
38
|
+
writeDword(shdrData, sample.sampleRate);
|
|
39
|
+
// pitch and correction
|
|
40
|
+
shdrData[shdrData.currentIndex++] = sample.samplePitch;
|
|
41
|
+
shdrData[shdrData.currentIndex++] = sample.samplePitchCorrection;
|
|
42
|
+
// sample link
|
|
43
|
+
writeWord(shdrData, sample.sampleLink);
|
|
44
|
+
// sample type: write raw because we simply copy compressed samples
|
|
45
|
+
writeWord(shdrData, sample.sampleType);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// write EOS and zero everything else
|
|
49
|
+
writeStringAsBytes(shdrData, "EOS", sampleLength);
|
|
50
|
+
return writeRIFFChunk(new RiffChunk(
|
|
51
|
+
"shdr",
|
|
52
|
+
shdrData.length,
|
|
53
|
+
shdrData
|
|
54
|
+
));
|
|
55
|
+
}
|