spessasynth_core 3.27.8 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -51
- package/dist/index.d.ts +4057 -0
- package/dist/index.js +17178 -0
- package/dist/index.js.map +1 -0
- package/package.json +24 -6
- package/index.js +0 -132
- package/src/externals/README.md +0 -6
- package/src/externals/fflate/LICENSE +0 -21
- package/src/externals/fflate/fflate.min.js +0 -1
- package/src/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +0 -12
- package/src/externals/stbvorbis_sync/LICENSE +0 -202
- package/src/externals/stbvorbis_sync/NOTICE +0 -6
- package/src/externals/stbvorbis_sync/stbvorbis_sync.min.js +0 -1
- package/src/midi/README.md +0 -32
- package/src/midi/basic_midi.js +0 -587
- package/src/midi/midi_builder.js +0 -203
- package/src/midi/midi_loader.js +0 -321
- package/src/midi/midi_message.js +0 -254
- package/src/midi/midi_sequence.js +0 -230
- package/src/midi/midi_tools/get_note_times.js +0 -154
- package/src/midi/midi_tools/midi_editor.js +0 -611
- package/src/midi/midi_tools/midi_writer.js +0 -105
- package/src/midi/midi_tools/rmidi_writer.js +0 -566
- package/src/midi/midi_tools/used_keys_loaded.js +0 -256
- package/src/midi/xmf_loader.js +0 -454
- package/src/sequencer/README.md +0 -9
- package/src/sequencer/events.js +0 -81
- package/src/sequencer/play.js +0 -362
- package/src/sequencer/process_event.js +0 -165
- package/src/sequencer/process_tick.js +0 -104
- package/src/sequencer/sequencer_engine.js +0 -372
- package/src/sequencer/song_control.js +0 -196
- package/src/soundfont/README.md +0 -11
- package/src/soundfont/basic_soundfont/basic_global_zone.js +0 -6
- package/src/soundfont/basic_soundfont/basic_instrument.js +0 -115
- package/src/soundfont/basic_soundfont/basic_instrument_zone.js +0 -45
- package/src/soundfont/basic_soundfont/basic_preset.js +0 -313
- package/src/soundfont/basic_soundfont/basic_preset_zone.js +0 -39
- package/src/soundfont/basic_soundfont/basic_sample.js +0 -477
- package/src/soundfont/basic_soundfont/basic_soundbank.js +0 -740
- package/src/soundfont/basic_soundfont/basic_zone.js +0 -145
- package/src/soundfont/basic_soundfont/generator.js +0 -76
- package/src/soundfont/basic_soundfont/generator_types.js +0 -151
- package/src/soundfont/basic_soundfont/modulator.js +0 -581
- package/src/soundfont/basic_soundfont/riff_chunk.js +0 -195
- package/src/soundfont/basic_soundfont/write_dls/art2.js +0 -174
- package/src/soundfont/basic_soundfont/write_dls/articulator.js +0 -49
- package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +0 -374
- package/src/soundfont/basic_soundfont/write_dls/ins.js +0 -85
- package/src/soundfont/basic_soundfont/write_dls/lins.js +0 -15
- package/src/soundfont/basic_soundfont/write_dls/modulator_converter.js +0 -330
- package/src/soundfont/basic_soundfont/write_dls/rgn2.js +0 -120
- package/src/soundfont/basic_soundfont/write_dls/wave.js +0 -71
- package/src/soundfont/basic_soundfont/write_dls/write_dls.js +0 -124
- package/src/soundfont/basic_soundfont/write_dls/wsmp.js +0 -78
- package/src/soundfont/basic_soundfont/write_dls/wvpl.js +0 -35
- package/src/soundfont/basic_soundfont/write_sf2/ibag.js +0 -60
- package/src/soundfont/basic_soundfont/write_sf2/igen.js +0 -91
- package/src/soundfont/basic_soundfont/write_sf2/imod.js +0 -62
- package/src/soundfont/basic_soundfont/write_sf2/inst.js +0 -42
- package/src/soundfont/basic_soundfont/write_sf2/pbag.js +0 -57
- package/src/soundfont/basic_soundfont/write_sf2/pgen.js +0 -92
- package/src/soundfont/basic_soundfont/write_sf2/phdr.js +0 -61
- package/src/soundfont/basic_soundfont/write_sf2/pmod.js +0 -62
- package/src/soundfont/basic_soundfont/write_sf2/sdta.js +0 -131
- package/src/soundfont/basic_soundfont/write_sf2/shdr.js +0 -77
- package/src/soundfont/basic_soundfont/write_sf2/write.js +0 -287
- package/src/soundfont/dls/articulator_converter.js +0 -402
- package/src/soundfont/dls/dls_destinations.js +0 -38
- package/src/soundfont/dls/dls_instrument.js +0 -20
- package/src/soundfont/dls/dls_preset.js +0 -43
- package/src/soundfont/dls/dls_sample.js +0 -238
- package/src/soundfont/dls/dls_soundfont.js +0 -183
- package/src/soundfont/dls/dls_sources.js +0 -63
- package/src/soundfont/dls/dls_zone.js +0 -89
- package/src/soundfont/dls/read_articulation.js +0 -300
- package/src/soundfont/dls/read_instrument.js +0 -118
- package/src/soundfont/dls/read_instrument_list.js +0 -17
- package/src/soundfont/dls/read_lart.js +0 -35
- package/src/soundfont/dls/read_region.js +0 -157
- package/src/soundfont/dls/read_samples.js +0 -154
- package/src/soundfont/load_soundfont.js +0 -21
- package/src/soundfont/read_sf2/generators.js +0 -43
- package/src/soundfont/read_sf2/instrument_zones.js +0 -75
- package/src/soundfont/read_sf2/instruments.js +0 -71
- package/src/soundfont/read_sf2/modulators.js +0 -25
- package/src/soundfont/read_sf2/preset_zones.js +0 -79
- package/src/soundfont/read_sf2/presets.js +0 -80
- package/src/soundfont/read_sf2/samples.js +0 -317
- package/src/soundfont/read_sf2/soundfont.js +0 -452
- package/src/soundfont/read_sf2/zones.js +0 -28
- package/src/synthetizer/README.md +0 -7
- package/src/synthetizer/audio_engine/README.md +0 -9
- package/src/synthetizer/audio_engine/engine_components/compute_modulator.js +0 -289
- package/src/synthetizer/audio_engine/engine_components/controller_tables.js +0 -90
- package/src/synthetizer/audio_engine/engine_components/dynamic_modulator_system.js +0 -95
- package/src/synthetizer/audio_engine/engine_components/enums.js +0 -18
- package/src/synthetizer/audio_engine/engine_components/key_modifier_manager.js +0 -151
- package/src/synthetizer/audio_engine/engine_components/lfo.js +0 -26
- package/src/synthetizer/audio_engine/engine_components/lowpass_filter.js +0 -282
- package/src/synthetizer/audio_engine/engine_components/midi_audio_channel.js +0 -551
- package/src/synthetizer/audio_engine/engine_components/modulation_envelope.js +0 -181
- package/src/synthetizer/audio_engine/engine_components/modulator_curves.js +0 -89
- package/src/synthetizer/audio_engine/engine_components/soundfont_manager.js +0 -265
- package/src/synthetizer/audio_engine/engine_components/stereo_panner.js +0 -124
- package/src/synthetizer/audio_engine/engine_components/unit_converter.js +0 -73
- package/src/synthetizer/audio_engine/engine_components/voice.js +0 -525
- package/src/synthetizer/audio_engine/engine_components/volume_envelope.js +0 -402
- package/src/synthetizer/audio_engine/engine_components/wavetable_oscillator.js +0 -274
- package/src/synthetizer/audio_engine/engine_methods/controller_control/controller_change.js +0 -159
- package/src/synthetizer/audio_engine/engine_methods/controller_control/master_parameters.js +0 -53
- package/src/synthetizer/audio_engine/engine_methods/controller_control/reset_controllers.js +0 -254
- package/src/synthetizer/audio_engine/engine_methods/create_midi_channel.js +0 -20
- package/src/synthetizer/audio_engine/engine_methods/data_entry/awe32.js +0 -198
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_coarse.js +0 -281
- package/src/synthetizer/audio_engine/engine_methods/data_entry/data_entry_fine.js +0 -109
- package/src/synthetizer/audio_engine/engine_methods/mute_channel.js +0 -17
- package/src/synthetizer/audio_engine/engine_methods/note_on.js +0 -220
- package/src/synthetizer/audio_engine/engine_methods/portamento_time.js +0 -92
- package/src/synthetizer/audio_engine/engine_methods/program_change.js +0 -35
- package/src/synthetizer/audio_engine/engine_methods/render_voice.js +0 -214
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/embedded_sound_bank.js +0 -42
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/get_preset.js +0 -0
- package/src/synthetizer/audio_engine/engine_methods/soundfont_management/update_preset_list.js +0 -19
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/kill_note.js +0 -23
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/note_off.js +0 -56
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_channels.js +0 -16
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/stop_all_notes.js +0 -30
- package/src/synthetizer/audio_engine/engine_methods/stopping_notes/voice_killing.js +0 -63
- package/src/synthetizer/audio_engine/engine_methods/system_exclusive.js +0 -1058
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/channel_pressure.js +0 -23
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/pitch_wheel.js +0 -31
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/poly_pressure.js +0 -29
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_master_tuning.js +0 -15
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_modulation_depth.js +0 -26
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_octave_tuning.js +0 -19
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/set_tuning.js +0 -27
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_all_channels.js +0 -15
- package/src/synthetizer/audio_engine/engine_methods/tuning_control/transpose_channel.js +0 -34
- package/src/synthetizer/audio_engine/main_processor.js +0 -817
- package/src/synthetizer/audio_engine/snapshot/apply_synthesizer_snapshot.js +0 -16
- package/src/synthetizer/audio_engine/snapshot/channel_snapshot.js +0 -175
- package/src/synthetizer/audio_engine/snapshot/synthesizer_snapshot.js +0 -116
- package/src/synthetizer/audio_engine/synth_processor_options.js +0 -18
- package/src/synthetizer/synth_constants.js +0 -26
- package/src/utils/README.md +0 -8
- package/src/utils/buffer_to_wav.js +0 -197
- package/src/utils/byte_functions/big_endian.js +0 -32
- package/src/utils/byte_functions/little_endian.js +0 -77
- package/src/utils/byte_functions/string.js +0 -92
- package/src/utils/byte_functions/variable_length_quantity.js +0 -42
- package/src/utils/fill_with_defaults.js +0 -21
- package/src/utils/indexed_array.js +0 -34
- package/src/utils/loggin.js +0 -71
- package/src/utils/other.js +0 -92
- package/src/utils/sysex_detector.js +0 -58
- package/src/utils/xg_hacks.js +0 -193
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
2
|
-
import { writeStringAsBytes } from "../../../utils/byte_functions/string.js";
|
|
3
|
-
import { writeLittleEndian } from "../../../utils/byte_functions/little_endian.js";
|
|
4
|
-
import { SpessaSynthInfo } from "../../../utils/loggin.js";
|
|
5
|
-
import { consoleColors } from "../../../utils/other.js";
|
|
6
|
-
|
|
7
|
-
/*
|
|
8
|
-
Sdta structure:
|
|
9
|
-
|
|
10
|
-
LIST chunk
|
|
11
|
-
- "sdta" ASCII string
|
|
12
|
-
- smpl chunk
|
|
13
|
-
- - raw data
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
// in bytes, from the start of sdta-LIST to the first actual sample
|
|
17
|
-
const SDTA_TO_DATA_OFFSET =
|
|
18
|
-
4 + // "LIST"
|
|
19
|
-
4 + // sdta size
|
|
20
|
-
4 + // "sdta"
|
|
21
|
-
4 + // "smpl"
|
|
22
|
-
4; // smpl size
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @this {BasicSoundBank}
|
|
26
|
-
* @param smplStartOffsets {number[]}
|
|
27
|
-
* @param smplEndOffsets {number[]}
|
|
28
|
-
* @param compress {boolean}
|
|
29
|
-
* @param decompress {boolean}
|
|
30
|
-
* @param vorbisFunc {SampleEncodingFunction}
|
|
31
|
-
* @param progressFunc {ProgressFunction|undefined}
|
|
32
|
-
* @returns {Uint8Array}
|
|
33
|
-
*/
|
|
34
|
-
export async function getSDTA(smplStartOffsets,
|
|
35
|
-
smplEndOffsets,
|
|
36
|
-
compress,
|
|
37
|
-
decompress,
|
|
38
|
-
vorbisFunc,
|
|
39
|
-
progressFunc
|
|
40
|
-
)
|
|
41
|
-
{
|
|
42
|
-
// write smpl: write int16 data of each sample linearly
|
|
43
|
-
// get size (calling getAudioData twice doesn't matter since it gets cached)
|
|
44
|
-
let writtenCount = 0;
|
|
45
|
-
let smplChunkSize = 0;
|
|
46
|
-
const sampleDatas = [];
|
|
47
|
-
|
|
48
|
-
// linear async is faster here as the writing function usually uses a single wasm instance
|
|
49
|
-
for (const s of this.samples)
|
|
50
|
-
{
|
|
51
|
-
if (compress)
|
|
52
|
-
{
|
|
53
|
-
await s.compressSample(vorbisFunc);
|
|
54
|
-
}
|
|
55
|
-
if (decompress)
|
|
56
|
-
{
|
|
57
|
-
s.setAudioData(s.getAudioData());
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// raw data: either copy s16le or encoded vorbis or encode manually if overridden
|
|
61
|
-
// use set timeout so the thread doesn't die
|
|
62
|
-
const r = s.getRawData(true);
|
|
63
|
-
writtenCount++;
|
|
64
|
-
progressFunc?.(s.sampleName, writtenCount, this.samples.length);
|
|
65
|
-
|
|
66
|
-
SpessaSynthInfo(
|
|
67
|
-
`%cEncoded sample %c${writtenCount}. ${s.sampleName}%c of %c${this.samples.length}%c. Compressed: %c${s.isCompressed}%c.`,
|
|
68
|
-
consoleColors.info,
|
|
69
|
-
consoleColors.recognized,
|
|
70
|
-
consoleColors.info,
|
|
71
|
-
consoleColors.recognized,
|
|
72
|
-
consoleColors.info,
|
|
73
|
-
s.isCompressed ? consoleColors.recognized : consoleColors.unrecognized,
|
|
74
|
-
consoleColors.info
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
/* 6.1 Sample Data Format in the smpl Sub-chunk
|
|
78
|
-
Each sample is followed by a minimum of forty-six zero
|
|
79
|
-
valued sample data points. These zero valued data points are necessary to guarantee that any reasonable upward pitch shift
|
|
80
|
-
using any reasonable interpolator can loop on zero data at the end of the sound.
|
|
81
|
-
This doesn't apply to sf3 tho
|
|
82
|
-
*/
|
|
83
|
-
smplChunkSize += r.length + (s.isCompressed ? 0 : 92);
|
|
84
|
-
sampleDatas.push(r);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (smplChunkSize % 2 !== 0)
|
|
88
|
-
{
|
|
89
|
-
smplChunkSize++;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const sdta = new IndexedByteArray(smplChunkSize + SDTA_TO_DATA_OFFSET);
|
|
93
|
-
|
|
94
|
-
// avoid using writeRIFFChunk for performance
|
|
95
|
-
// sdta chunk
|
|
96
|
-
writeStringAsBytes(sdta, "LIST");
|
|
97
|
-
// "sdta" + full smpl length
|
|
98
|
-
writeLittleEndian(sdta, smplChunkSize + SDTA_TO_DATA_OFFSET - 8, 4);
|
|
99
|
-
writeStringAsBytes(sdta, "sdta");
|
|
100
|
-
writeStringAsBytes(sdta, "smpl");
|
|
101
|
-
writeLittleEndian(sdta, smplChunkSize, 4);
|
|
102
|
-
|
|
103
|
-
let offset = 0;
|
|
104
|
-
// write out
|
|
105
|
-
this.samples.forEach((sample, i) =>
|
|
106
|
-
{
|
|
107
|
-
const data = sampleDatas[i];
|
|
108
|
-
sdta.set(data, offset + SDTA_TO_DATA_OFFSET);
|
|
109
|
-
let startOffset;
|
|
110
|
-
let endOffset;
|
|
111
|
-
if (sample.isCompressed)
|
|
112
|
-
{
|
|
113
|
-
// sf3 offset is in bytes
|
|
114
|
-
startOffset = offset;
|
|
115
|
-
endOffset = startOffset + data.length;
|
|
116
|
-
}
|
|
117
|
-
else
|
|
118
|
-
{
|
|
119
|
-
// sf2 in sample data points
|
|
120
|
-
startOffset = offset / 2; // inclusive
|
|
121
|
-
endOffset = startOffset + data.length / 2; // exclusive
|
|
122
|
-
offset += 92; // 46 sample data points
|
|
123
|
-
}
|
|
124
|
-
offset += data.length;
|
|
125
|
-
smplStartOffsets.push(startOffset);
|
|
126
|
-
|
|
127
|
-
smplEndOffsets.push(endOffset);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
return sdta;
|
|
131
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
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 { writeRIFFChunkRaw } from "../riff_chunk.js";
|
|
5
|
-
import { SF3_BIT_FLIT } from "../../read_sf2/samples.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @this {BasicSoundBank}
|
|
9
|
-
* @param smplStartOffsets {number[]}
|
|
10
|
-
* @param smplEndOffsets {number[]}
|
|
11
|
-
* @returns {ReturnedExtendedSf2Chunks}
|
|
12
|
-
*/
|
|
13
|
-
export function getSHDR(smplStartOffsets, smplEndOffsets)
|
|
14
|
-
{
|
|
15
|
-
const sampleLength = 46;
|
|
16
|
-
const shdrSize = sampleLength * (this.samples.length + 1); // +1 because EOP
|
|
17
|
-
const shdrData = new IndexedByteArray(shdrSize);
|
|
18
|
-
// https://github.com/spessasus/soundfont-proposals/blob/main/extended_limits.md
|
|
19
|
-
const xshdrData = new IndexedByteArray(shdrSize);
|
|
20
|
-
let maxSampleLink = 0;
|
|
21
|
-
this.samples.forEach((sample, index) =>
|
|
22
|
-
{
|
|
23
|
-
// sample name
|
|
24
|
-
writeStringAsBytes(shdrData, sample.sampleName.substring(0, 20), 20);
|
|
25
|
-
writeStringAsBytes(xshdrData, sample.sampleName.substring(20), 20);
|
|
26
|
-
// start offset
|
|
27
|
-
const dwStart = smplStartOffsets[index];
|
|
28
|
-
writeDword(shdrData, dwStart);
|
|
29
|
-
xshdrData.currentIndex += 4;
|
|
30
|
-
// end offset
|
|
31
|
-
const dwEnd = smplEndOffsets[index];
|
|
32
|
-
writeDword(shdrData, dwEnd);
|
|
33
|
-
xshdrData.currentIndex += 4;
|
|
34
|
-
// loop is stored as relative in sample points, change it to absolute sample points here
|
|
35
|
-
let loopStart = sample.sampleLoopStartIndex + dwStart;
|
|
36
|
-
let loopEnd = sample.sampleLoopEndIndex + dwStart;
|
|
37
|
-
if (sample.isCompressed)
|
|
38
|
-
{
|
|
39
|
-
// https://github.com/FluidSynth/fluidsynth/wiki/SoundFont3Format
|
|
40
|
-
loopStart -= dwStart;
|
|
41
|
-
loopEnd -= dwStart;
|
|
42
|
-
}
|
|
43
|
-
writeDword(shdrData, loopStart);
|
|
44
|
-
writeDword(shdrData, loopEnd);
|
|
45
|
-
// sample rate
|
|
46
|
-
writeDword(shdrData, sample.sampleRate);
|
|
47
|
-
// pitch and correction
|
|
48
|
-
shdrData[shdrData.currentIndex++] = sample.samplePitch;
|
|
49
|
-
shdrData[shdrData.currentIndex++] = sample.samplePitchCorrection;
|
|
50
|
-
// skip all those for xshdr
|
|
51
|
-
xshdrData.currentIndex += 14;
|
|
52
|
-
// sample link
|
|
53
|
-
const sampleLinkIndex = this.samples.indexOf(sample.linkedSample);
|
|
54
|
-
writeWord(shdrData, Math.max(0, sampleLinkIndex) & 0xFFFF);
|
|
55
|
-
writeWord(xshdrData, Math.max(0, sampleLinkIndex) >> 16);
|
|
56
|
-
maxSampleLink = Math.max(maxSampleLink, sampleLinkIndex);
|
|
57
|
-
// sample type: add byte if compressed
|
|
58
|
-
let type = sample.sampleType;
|
|
59
|
-
if (sample.isCompressed)
|
|
60
|
-
{
|
|
61
|
-
type |= SF3_BIT_FLIT;
|
|
62
|
-
}
|
|
63
|
-
writeWord(shdrData, type);
|
|
64
|
-
xshdrData.currentIndex += 2;
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// write EOS and zero everything else
|
|
68
|
-
writeStringAsBytes(shdrData, "EOS", sampleLength);
|
|
69
|
-
writeStringAsBytes(xshdrData, "EOS", sampleLength);
|
|
70
|
-
const shdr = writeRIFFChunkRaw("shdr", shdrData);
|
|
71
|
-
const xshdr = writeRIFFChunkRaw("shdr", xshdrData);
|
|
72
|
-
return {
|
|
73
|
-
pdta: shdr,
|
|
74
|
-
xdta: xshdr,
|
|
75
|
-
highestIndex: maxSampleLink
|
|
76
|
-
};
|
|
77
|
-
}
|
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
import { IndexedByteArray } from "../../../utils/indexed_array.js";
|
|
2
|
-
import { writeRIFFChunkParts, writeRIFFChunkRaw } from "../riff_chunk.js";
|
|
3
|
-
import { getStringBytes } from "../../../utils/byte_functions/string.js";
|
|
4
|
-
import { consoleColors } from "../../../utils/other.js";
|
|
5
|
-
import { getIGEN } from "./igen.js";
|
|
6
|
-
import { getSDTA } from "./sdta.js";
|
|
7
|
-
import { getSHDR } from "./shdr.js";
|
|
8
|
-
import { getIMOD } from "./imod.js";
|
|
9
|
-
import { getIBAG } from "./ibag.js";
|
|
10
|
-
import { getINST } from "./inst.js";
|
|
11
|
-
import { getPGEN } from "./pgen.js";
|
|
12
|
-
import { getPMOD } from "./pmod.js";
|
|
13
|
-
import { getPBAG } from "./pbag.js";
|
|
14
|
-
import { getPHDR } from "./phdr.js";
|
|
15
|
-
import { writeLittleEndian, writeWord } from "../../../utils/byte_functions/little_endian.js";
|
|
16
|
-
import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../../../utils/loggin.js";
|
|
17
|
-
import { MOD_BYTE_SIZE } from "../modulator.js";
|
|
18
|
-
import { fillWithDefaults } from "../../../utils/fill_with_defaults.js";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @typedef {function} ProgressFunction
|
|
22
|
-
* @param {string} sampleName - the written sample name.
|
|
23
|
-
* @param {number} sampleIndex - the sample's index.
|
|
24
|
-
* @param {number} sampleCount - the total sample count for progress displaying.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @typedef {Object} SoundFont2WriteOptions
|
|
29
|
-
* @property {boolean} compress - if the soundfont should be compressed with a given function.
|
|
30
|
-
* @property {SampleEncodingFunction} compressionFunction -
|
|
31
|
-
* the encode vorbis function. It can be undefined if not compressed.
|
|
32
|
-
* @property {ProgressFunction} progressFunction - a function to show progress for writing large banks. It can be undefined.
|
|
33
|
-
* @property {boolean} writeDefaultModulators - if the DMOD chunk should be written.
|
|
34
|
-
* Recommended.
|
|
35
|
-
* @property {boolean} writeExtendedLimits - if the xdta chunk should be written to allow virtually infinite parameters.
|
|
36
|
-
* Recommended.
|
|
37
|
-
* @property {boolean} decompress - if an sf3 bank should be decompressed back to sf2. Not recommended.
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @typedef {Object} ReturnedExtendedSf2Chunks
|
|
43
|
-
* @property {IndexedByteArray} pdta - the pdta part of the chunk
|
|
44
|
-
* @property {IndexedByteArray} xdta - the xdta (https://github.com/spessasus/soundfont-proposals/blob/main/extended_limits.md) part of the chunk
|
|
45
|
-
* @property {number} highestIndex - the highest index written (0 if not applicable). Used for determining whether the xdta chunk is necessary.
|
|
46
|
-
*/
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* @type {SoundFont2WriteOptions}
|
|
50
|
-
*/
|
|
51
|
-
const DEFAULT_WRITE_OPTIONS = {
|
|
52
|
-
compress: false,
|
|
53
|
-
compressionQuality: 0.5,
|
|
54
|
-
compressionFunction: undefined,
|
|
55
|
-
progressFunction: undefined,
|
|
56
|
-
writeDefaultModulators: true,
|
|
57
|
-
writeExtendedLimits: true,
|
|
58
|
-
decompress: false
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Write the soundfont as an .sf2 file
|
|
63
|
-
* @this {BasicSoundBank}
|
|
64
|
-
* @param {Partial<SoundFont2WriteOptions>} options
|
|
65
|
-
* @returns {Uint8Array}
|
|
66
|
-
*/
|
|
67
|
-
export async function write(options = DEFAULT_WRITE_OPTIONS)
|
|
68
|
-
{
|
|
69
|
-
options = fillWithDefaults(options, DEFAULT_WRITE_OPTIONS);
|
|
70
|
-
if (options?.compress)
|
|
71
|
-
{
|
|
72
|
-
if (typeof options?.compressionFunction !== "function")
|
|
73
|
-
{
|
|
74
|
-
throw new Error("No compression function supplied but compression enabled.");
|
|
75
|
-
}
|
|
76
|
-
if (options?.decompress)
|
|
77
|
-
{
|
|
78
|
-
throw new Error("Decompressed and compressed at the same time.");
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
SpessaSynthGroupCollapsed(
|
|
82
|
-
"%cSaving soundfont...",
|
|
83
|
-
consoleColors.info
|
|
84
|
-
);
|
|
85
|
-
SpessaSynthInfo(
|
|
86
|
-
`%cCompression: %c${options?.compress || "false"}%c quality: %c${options?.compressionQuality || "none"}`,
|
|
87
|
-
consoleColors.info,
|
|
88
|
-
consoleColors.recognized,
|
|
89
|
-
consoleColors.info,
|
|
90
|
-
consoleColors.recognized
|
|
91
|
-
);
|
|
92
|
-
SpessaSynthInfo(
|
|
93
|
-
"%cWriting INFO...",
|
|
94
|
-
consoleColors.info
|
|
95
|
-
);
|
|
96
|
-
/**
|
|
97
|
-
* Write INFO
|
|
98
|
-
* @type {IndexedByteArray[]}
|
|
99
|
-
*/
|
|
100
|
-
const infoArrays = [];
|
|
101
|
-
this.soundFontInfo["ISFT"] = "SpessaSynth"; // ( ͡° ͜ʖ ͡°)
|
|
102
|
-
if (options?.compress || this.samples.some(s => s.isCompressed))
|
|
103
|
-
{
|
|
104
|
-
this.soundFontInfo["ifil"] = "3.0"; // set version to 3
|
|
105
|
-
}
|
|
106
|
-
if (options?.decompress)
|
|
107
|
-
{
|
|
108
|
-
this.soundFontInfo["ifil"] = "2.4"; // set version to 2.04
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (options?.writeDefaultModulators)
|
|
112
|
-
{
|
|
113
|
-
// trigger the DMOD write
|
|
114
|
-
this.soundFontInfo["DMOD"] = `${this.defaultModulators.length} Modulators`;
|
|
115
|
-
this.customDefaultModulators = true;
|
|
116
|
-
}
|
|
117
|
-
else
|
|
118
|
-
{
|
|
119
|
-
delete this.soundFontInfo["DMOD"];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
for (const [type, data] of Object.entries(this.soundFontInfo))
|
|
123
|
-
{
|
|
124
|
-
if (type === "ifil" || type === "iver")
|
|
125
|
-
{
|
|
126
|
-
const major = parseInt(data.split(".")[0]);
|
|
127
|
-
const minor = parseInt(data.split(".")[1]);
|
|
128
|
-
const ckdata = new IndexedByteArray(4);
|
|
129
|
-
writeWord(ckdata, major);
|
|
130
|
-
writeWord(ckdata, minor);
|
|
131
|
-
infoArrays.push(writeRIFFChunkRaw(type, ckdata));
|
|
132
|
-
}
|
|
133
|
-
else if (type === "DMOD")
|
|
134
|
-
{
|
|
135
|
-
const mods = this.defaultModulators;
|
|
136
|
-
SpessaSynthInfo(
|
|
137
|
-
`%cWriting %c${mods.length}%c default modulators...`,
|
|
138
|
-
consoleColors.info,
|
|
139
|
-
consoleColors.recognized,
|
|
140
|
-
consoleColors.info
|
|
141
|
-
);
|
|
142
|
-
let dmodsize = MOD_BYTE_SIZE + mods.length * MOD_BYTE_SIZE;
|
|
143
|
-
const dmoddata = new IndexedByteArray(dmodsize);
|
|
144
|
-
for (const mod of mods)
|
|
145
|
-
{
|
|
146
|
-
writeWord(dmoddata, mod.getSourceEnum());
|
|
147
|
-
writeWord(dmoddata, mod.modulatorDestination);
|
|
148
|
-
writeWord(dmoddata, mod.transformAmount);
|
|
149
|
-
writeWord(dmoddata, mod.getSecSrcEnum());
|
|
150
|
-
writeWord(dmoddata, mod.transformType);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// terminal modulator, is zero
|
|
154
|
-
writeLittleEndian(dmoddata, 0, MOD_BYTE_SIZE);
|
|
155
|
-
|
|
156
|
-
infoArrays.push(writeRIFFChunkRaw(type, dmoddata));
|
|
157
|
-
}
|
|
158
|
-
else
|
|
159
|
-
{
|
|
160
|
-
infoArrays.push(writeRIFFChunkRaw(
|
|
161
|
-
type,
|
|
162
|
-
getStringBytes(data, true, true) // pad with zero and ensure even length
|
|
163
|
-
));
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
SpessaSynthInfo(
|
|
168
|
-
"%cWriting SDTA...",
|
|
169
|
-
consoleColors.info
|
|
170
|
-
);
|
|
171
|
-
// write sdta
|
|
172
|
-
const smplStartOffsets = [];
|
|
173
|
-
const smplEndOffsets = [];
|
|
174
|
-
const sdtaChunk = await getSDTA.call(
|
|
175
|
-
this,
|
|
176
|
-
smplStartOffsets,
|
|
177
|
-
smplEndOffsets,
|
|
178
|
-
options.compress,
|
|
179
|
-
options.decompress,
|
|
180
|
-
options?.compressionFunction,
|
|
181
|
-
options?.progressFunction
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
SpessaSynthInfo(
|
|
185
|
-
"%cWriting PDTA...",
|
|
186
|
-
consoleColors.info
|
|
187
|
-
);
|
|
188
|
-
// write pdta
|
|
189
|
-
// go in reverse so the indexes are correct
|
|
190
|
-
// instruments
|
|
191
|
-
SpessaSynthInfo(
|
|
192
|
-
"%cWriting SHDR...",
|
|
193
|
-
consoleColors.info
|
|
194
|
-
);
|
|
195
|
-
const shdrChunk = getSHDR.call(this, smplStartOffsets, smplEndOffsets);
|
|
196
|
-
SpessaSynthInfo(
|
|
197
|
-
"%cWriting IGEN...",
|
|
198
|
-
consoleColors.info
|
|
199
|
-
);
|
|
200
|
-
const igenChunk = getIGEN.call(this);
|
|
201
|
-
SpessaSynthInfo(
|
|
202
|
-
"%cWriting IMOD...",
|
|
203
|
-
consoleColors.info
|
|
204
|
-
);
|
|
205
|
-
const imodChunk = getIMOD.call(this);
|
|
206
|
-
SpessaSynthInfo(
|
|
207
|
-
"%cWriting IBAG...",
|
|
208
|
-
consoleColors.info
|
|
209
|
-
);
|
|
210
|
-
const ibagChunk = getIBAG.call(this);
|
|
211
|
-
SpessaSynthInfo(
|
|
212
|
-
"%cWriting INST...",
|
|
213
|
-
consoleColors.info
|
|
214
|
-
);
|
|
215
|
-
const instChunk = getINST.call(this);
|
|
216
|
-
SpessaSynthInfo(
|
|
217
|
-
"%cWriting PGEN...",
|
|
218
|
-
consoleColors.info
|
|
219
|
-
);
|
|
220
|
-
// presets
|
|
221
|
-
const pgenChunk = getPGEN.call(this);
|
|
222
|
-
SpessaSynthInfo(
|
|
223
|
-
"%cWriting PMOD...",
|
|
224
|
-
consoleColors.info
|
|
225
|
-
);
|
|
226
|
-
const pmodChunk = getPMOD.call(this);
|
|
227
|
-
SpessaSynthInfo(
|
|
228
|
-
"%cWriting PBAG...",
|
|
229
|
-
consoleColors.info
|
|
230
|
-
);
|
|
231
|
-
const pbagChunk = getPBAG.call(this);
|
|
232
|
-
SpessaSynthInfo(
|
|
233
|
-
"%cWriting PHDR...",
|
|
234
|
-
consoleColors.info
|
|
235
|
-
);
|
|
236
|
-
const phdrChunk = getPHDR.call(this);
|
|
237
|
-
/**
|
|
238
|
-
* @type {ReturnedExtendedSf2Chunks[]}
|
|
239
|
-
*/
|
|
240
|
-
const chunks = [phdrChunk, pbagChunk, pmodChunk, pgenChunk, instChunk, ibagChunk, imodChunk, igenChunk, shdrChunk];
|
|
241
|
-
// combine in the sfspec order
|
|
242
|
-
const pdtaChunk = writeRIFFChunkParts(
|
|
243
|
-
"pdta",
|
|
244
|
-
chunks.map(c => c.pdta),
|
|
245
|
-
true
|
|
246
|
-
);
|
|
247
|
-
const maxIndex = Math.max(
|
|
248
|
-
...chunks.map(c => c.highestIndex)
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
const writeXdta = options.writeExtendedLimits && (
|
|
252
|
-
maxIndex > 0xFFFF
|
|
253
|
-
|| this.presets.some(p => p.presetName.length > 20)
|
|
254
|
-
|| this.instruments.some(i => i.instrumentName.length > 20)
|
|
255
|
-
|| this.samples.some(s => s.sampleName.length > 20)
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
if (writeXdta)
|
|
259
|
-
{
|
|
260
|
-
SpessaSynthInfo(
|
|
261
|
-
`%cWriting the xdta chunk! Max index: %c${maxIndex}`,
|
|
262
|
-
consoleColors.info,
|
|
263
|
-
consoleColors.value
|
|
264
|
-
);
|
|
265
|
-
// https://github.com/spessasus/soundfont-proposals/blob/main/extended_limits.md
|
|
266
|
-
const xpdtaChunk = writeRIFFChunkParts("xdta", chunks.map(c => c.xdta), true);
|
|
267
|
-
infoArrays.push(xpdtaChunk);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const infoChunk = writeRIFFChunkParts("INFO", infoArrays, true);
|
|
271
|
-
SpessaSynthInfo(
|
|
272
|
-
"%cWriting the output file...",
|
|
273
|
-
consoleColors.info
|
|
274
|
-
);
|
|
275
|
-
// finally, combine everything
|
|
276
|
-
const main = writeRIFFChunkParts(
|
|
277
|
-
"RIFF",
|
|
278
|
-
[getStringBytes("sfbk"), infoChunk, sdtaChunk, pdtaChunk]
|
|
279
|
-
);
|
|
280
|
-
SpessaSynthInfo(
|
|
281
|
-
`%cSaved succesfully! Final file size: %c${main.length}`,
|
|
282
|
-
consoleColors.info,
|
|
283
|
-
consoleColors.recognized
|
|
284
|
-
);
|
|
285
|
-
SpessaSynthGroupEnd();
|
|
286
|
-
return main;
|
|
287
|
-
}
|