spessasynth_core 3.26.30 → 3.26.32
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 +0 -1
- package/package.json +1 -1
- package/src/midi/midi_tools/rmidi_writer.js +34 -35
- package/src/soundfont/basic_soundfont/basic_preset.js +1 -4
- package/src/soundfont/basic_soundfont/basic_sample.js +13 -3
- package/src/soundfont/basic_soundfont/basic_soundbank.js +9 -1
- package/src/soundfont/basic_soundfont/riff_chunk.js +85 -42
- package/src/soundfont/basic_soundfont/write_dls/art2.js +4 -4
- package/src/soundfont/basic_soundfont/write_dls/ins.js +14 -18
- package/src/soundfont/basic_soundfont/write_dls/lins.js +3 -6
- package/src/soundfont/basic_soundfont/write_dls/rgn2.js +8 -9
- package/src/soundfont/basic_soundfont/write_dls/wave.js +12 -35
- package/src/soundfont/basic_soundfont/write_dls/write_dls.js +18 -29
- package/src/soundfont/basic_soundfont/write_dls/wsmp.js +2 -2
- package/src/soundfont/basic_soundfont/write_dls/wvpl.js +3 -5
- package/src/soundfont/basic_soundfont/write_sf2/ibag.js +3 -11
- package/src/soundfont/basic_soundfont/write_sf2/igen.js +3 -11
- package/src/soundfont/basic_soundfont/write_sf2/imod.js +3 -11
- package/src/soundfont/basic_soundfont/write_sf2/inst.js +3 -12
- package/src/soundfont/basic_soundfont/write_sf2/pbag.js +3 -11
- package/src/soundfont/basic_soundfont/write_sf2/pgen.js +4 -11
- package/src/soundfont/basic_soundfont/write_sf2/phdr.js +3 -11
- package/src/soundfont/basic_soundfont/write_sf2/pmod.js +3 -11
- package/src/soundfont/basic_soundfont/write_sf2/sdta.js +56 -26
- package/src/soundfont/basic_soundfont/write_sf2/shdr.js +3 -11
- package/src/soundfont/basic_soundfont/write_sf2/write.js +23 -52
- package/src/soundfont/dls/dls_soundfont.js +1 -3
- package/src/soundfont/dls/read_instrument.js +7 -3
- package/src/soundfont/dls/read_region.js +11 -3
- package/src/soundfont/read_sf2/samples.js +10 -4
- package/src/soundfont/read_sf2/soundfont.js +1 -1
- package/src/utils/buffer_to_wav.js +12 -15
- package/src/utils/byte_functions/string.js +7 -2
- package/src/utils/indexed_array.js +0 -18
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
4
|
import { consoleColors } from "../../../utils/other.js";
|
|
5
5
|
import { getIGEN } from "./igen.js";
|
|
6
6
|
import { getSDTA } from "./sdta.js";
|
|
@@ -109,11 +109,7 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
|
|
|
109
109
|
const ckdata = new IndexedByteArray(4);
|
|
110
110
|
writeWord(ckdata, major);
|
|
111
111
|
writeWord(ckdata, minor);
|
|
112
|
-
infoArrays.push(
|
|
113
|
-
type,
|
|
114
|
-
4,
|
|
115
|
-
ckdata
|
|
116
|
-
)));
|
|
112
|
+
infoArrays.push(writeRIFFChunkRaw(type, ckdata));
|
|
117
113
|
}
|
|
118
114
|
else if (type === "DMOD")
|
|
119
115
|
{
|
|
@@ -139,22 +135,14 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
|
|
|
139
135
|
// terminal modulator, is zero
|
|
140
136
|
writeLittleEndian(dmoddata, 0, MOD_BYTE_SIZE);
|
|
141
137
|
|
|
142
|
-
infoArrays.push(
|
|
143
|
-
type,
|
|
144
|
-
dmoddata.length,
|
|
145
|
-
dmoddata
|
|
146
|
-
)));
|
|
138
|
+
infoArrays.push(writeRIFFChunkRaw(type, dmoddata));
|
|
147
139
|
}
|
|
148
140
|
else
|
|
149
141
|
{
|
|
150
|
-
|
|
151
|
-
const arr = new IndexedByteArray(data.length + 1);
|
|
152
|
-
writeStringAsBytes(arr, data);
|
|
153
|
-
infoArrays.push(writeRIFFChunk(new RiffChunk(
|
|
142
|
+
infoArrays.push(writeRIFFChunkRaw(
|
|
154
143
|
type,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
)));
|
|
144
|
+
getStringBytes(data, true, true) // pad with zero and ensure even length
|
|
145
|
+
));
|
|
158
146
|
}
|
|
159
147
|
}
|
|
160
148
|
|
|
@@ -206,6 +194,10 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
|
|
|
206
194
|
consoleColors.info
|
|
207
195
|
);
|
|
208
196
|
const instChunk = getINST.call(this);
|
|
197
|
+
SpessaSynthInfo(
|
|
198
|
+
"%cWriting PGEN...",
|
|
199
|
+
consoleColors.info
|
|
200
|
+
);
|
|
209
201
|
// presets
|
|
210
202
|
const pgenChunk = getPGEN.call(this);
|
|
211
203
|
SpessaSynthInfo(
|
|
@@ -228,21 +220,17 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
|
|
|
228
220
|
*/
|
|
229
221
|
const chunks = [phdrChunk, pbagChunk, pmodChunk, pgenChunk, instChunk, ibagChunk, imodChunk, igenChunk, shdrChunk];
|
|
230
222
|
// combine in the sfspec order
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
"LIST",
|
|
237
|
-
pdtaData.length,
|
|
238
|
-
pdtaData
|
|
239
|
-
));
|
|
223
|
+
const pdtaChunk = writeRIFFChunkParts(
|
|
224
|
+
"pdta",
|
|
225
|
+
chunks.map(c => c.pdta),
|
|
226
|
+
true
|
|
227
|
+
);
|
|
240
228
|
const maxIndex = Math.max(
|
|
241
229
|
...chunks.map(c => c.highestIndex)
|
|
242
230
|
);
|
|
243
231
|
|
|
244
232
|
const writeXdta = options.writeExtendedLimits && (
|
|
245
|
-
maxIndex >
|
|
233
|
+
maxIndex > 0xFFFF
|
|
246
234
|
|| this.presets.some(p => p.presetName.length > 20)
|
|
247
235
|
|| this.instruments.some(i => i.instrumentName.length > 20)
|
|
248
236
|
|| this.samples.some(s => s.sampleName.length > 20)
|
|
@@ -256,37 +244,20 @@ export function write(options = DEFAULT_WRITE_OPTIONS)
|
|
|
256
244
|
consoleColors.value
|
|
257
245
|
);
|
|
258
246
|
// https://github.com/spessasus/soundfont-proposals/blob/main/extended_limits.md
|
|
259
|
-
const
|
|
260
|
-
new IndexedByteArray([120, 100, 116, 97]), // "xdta"
|
|
261
|
-
...chunks.map(c => c.xdta)
|
|
262
|
-
]);
|
|
263
|
-
|
|
264
|
-
const xpdtaChunk = writeRIFFChunk(new RiffChunk(
|
|
265
|
-
"LIST",
|
|
266
|
-
xpdtaData.length,
|
|
267
|
-
xpdtaData
|
|
268
|
-
));
|
|
247
|
+
const xpdtaChunk = writeRIFFChunkParts("xdta", chunks.map(c => c.xdta), true);
|
|
269
248
|
infoArrays.push(xpdtaChunk);
|
|
270
249
|
}
|
|
271
250
|
|
|
272
|
-
const infoChunk =
|
|
251
|
+
const infoChunk = writeRIFFChunkParts("INFO", infoArrays, true);
|
|
273
252
|
SpessaSynthInfo(
|
|
274
253
|
"%cWriting the output file...",
|
|
275
254
|
consoleColors.info
|
|
276
255
|
);
|
|
277
256
|
// finally, combine everything
|
|
278
|
-
const
|
|
279
|
-
new IndexedByteArray([115, 102, 98, 107]), // "sfbk"
|
|
280
|
-
infoChunk,
|
|
281
|
-
sdtaChunk,
|
|
282
|
-
pdtaChunk
|
|
283
|
-
]);
|
|
284
|
-
|
|
285
|
-
const main = writeRIFFChunk(new RiffChunk(
|
|
257
|
+
const main = writeRIFFChunkParts(
|
|
286
258
|
"RIFF",
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
));
|
|
259
|
+
[getStringBytes("sfbk"), infoChunk, sdtaChunk, pdtaChunk]
|
|
260
|
+
);
|
|
290
261
|
SpessaSynthInfo(
|
|
291
262
|
`%cSaved succesfully! Final file size: %c${main.length}`,
|
|
292
263
|
consoleColors.info,
|
|
@@ -8,7 +8,6 @@ import { readLittleEndian } from "../../utils/byte_functions/little_endian.js";
|
|
|
8
8
|
import { readDLSInstrumentList } from "./read_instrument_list.js";
|
|
9
9
|
import { readDLSInstrument } from "./read_instrument.js";
|
|
10
10
|
import { readLart } from "./read_lart.js";
|
|
11
|
-
import { readRegion } from "./read_region.js";
|
|
12
11
|
import { readDLSSamples } from "./read_samples.js";
|
|
13
12
|
|
|
14
13
|
class DLSSoundFont extends BasicSoundBank
|
|
@@ -45,7 +44,7 @@ class DLSSoundFont extends BasicSoundBank
|
|
|
45
44
|
|
|
46
45
|
// mandatory
|
|
47
46
|
this.soundFontInfo["ifil"] = "2.1"; // always for dls
|
|
48
|
-
this.soundFontInfo["isng"] = "
|
|
47
|
+
this.soundFontInfo["isng"] = "E-mu 10K2";
|
|
49
48
|
|
|
50
49
|
// set some defaults
|
|
51
50
|
this.soundFontInfo["INAM"] = "Unnamed DLS";
|
|
@@ -178,7 +177,6 @@ class DLSSoundFont extends BasicSoundBank
|
|
|
178
177
|
|
|
179
178
|
DLSSoundFont.prototype.readDLSInstrumentList = readDLSInstrumentList;
|
|
180
179
|
DLSSoundFont.prototype.readDLSInstrument = readDLSInstrument;
|
|
181
|
-
DLSSoundFont.prototype.readRegion = readRegion;
|
|
182
180
|
DLSSoundFont.prototype.readLart = readLart;
|
|
183
181
|
DLSSoundFont.prototype.readDLSSamples = readDLSSamples;
|
|
184
182
|
|
|
@@ -7,6 +7,7 @@ import { consoleColors } from "../../utils/other.js";
|
|
|
7
7
|
import { Modulator } from "../basic_soundfont/modulator.js";
|
|
8
8
|
import { DEFAULT_DLS_CHORUS, DEFAULT_DLS_REVERB } from "./dls_sources.js";
|
|
9
9
|
import { generatorLimits, generatorTypes } from "../basic_soundfont/generator_types.js";
|
|
10
|
+
import { readRegion } from "./read_region.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* @this {DLSSoundFont}
|
|
@@ -40,7 +41,7 @@ export function readDLSInstrument(chunk)
|
|
|
40
41
|
const preset = new DLSPreset(this, ulBank, ulInstrument);
|
|
41
42
|
|
|
42
43
|
// read preset name in INFO
|
|
43
|
-
let presetName =
|
|
44
|
+
let presetName = ``;
|
|
44
45
|
const infoChunk = findRIFFListType(chunks, "INFO");
|
|
45
46
|
if (infoChunk)
|
|
46
47
|
{
|
|
@@ -51,6 +52,10 @@ export function readDLSInstrument(chunk)
|
|
|
51
52
|
}
|
|
52
53
|
presetName = readBytesAsString(info.chunkData, info.chunkData.length).trim();
|
|
53
54
|
}
|
|
55
|
+
if (presetName.length < 1)
|
|
56
|
+
{
|
|
57
|
+
presetName = `unnamed ${(ulBank >> 8) & 127}:${ulInstrument & 127}`;
|
|
58
|
+
}
|
|
54
59
|
preset.presetName = presetName;
|
|
55
60
|
preset.dlsInstrument.instrumentName = presetName;
|
|
56
61
|
SpessaSynthGroupCollapsed(
|
|
@@ -105,8 +110,7 @@ export function readDLSInstrument(chunk)
|
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
|
|
108
|
-
|
|
109
|
-
this.readRegion(chunk, zone);
|
|
113
|
+
readRegion.call(this, chunk, preset.dlsInstrument);
|
|
110
114
|
}
|
|
111
115
|
this.addPresets(preset);
|
|
112
116
|
this.addInstruments(preset.dlsInstrument);
|
|
@@ -2,13 +2,14 @@ import { readLittleEndian, signedInt16 } from "../../utils/byte_functions/little
|
|
|
2
2
|
import { findRIFFListType, readRIFFChunk } from "../basic_soundfont/riff_chunk.js";
|
|
3
3
|
import { Generator } from "../basic_soundfont/generator.js";
|
|
4
4
|
import { generatorTypes } from "../basic_soundfont/generator_types.js";
|
|
5
|
+
import { SpessaSynthWarn } from "../../utils/loggin.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @this {DLSSoundFont}
|
|
8
9
|
* @param chunk {RiffChunk}
|
|
9
|
-
* @param
|
|
10
|
+
* @param instrument {DLSInstrument}
|
|
10
11
|
*/
|
|
11
|
-
export function readRegion(chunk,
|
|
12
|
+
export function readRegion(chunk, instrument)
|
|
12
13
|
{
|
|
13
14
|
// regions are essentially instrument zones
|
|
14
15
|
|
|
@@ -24,6 +25,12 @@ export function readRegion(chunk, zone)
|
|
|
24
25
|
|
|
25
26
|
// region header
|
|
26
27
|
const regionHeader = regionChunks.find(c => c.header === "rgnh");
|
|
28
|
+
|
|
29
|
+
if (!regionHeader)
|
|
30
|
+
{
|
|
31
|
+
SpessaSynthWarn("Invalid DLS region: missing 'rgnh' chunk! Discarding...");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
27
34
|
// key range
|
|
28
35
|
let keyMin = readLittleEndian(regionHeader.chunkData, 2);
|
|
29
36
|
let keyMax = readLittleEndian(regionHeader.chunkData, 2);
|
|
@@ -38,7 +45,8 @@ export function readRegion(chunk, zone)
|
|
|
38
45
|
velMin = 0;
|
|
39
46
|
}
|
|
40
47
|
// cannot do the same to key zones sadly
|
|
41
|
-
|
|
48
|
+
// create zone
|
|
49
|
+
const zone = instrument.createZone();
|
|
42
50
|
// apply ranges
|
|
43
51
|
zone.keyRange = { min: keyMin, max: keyMax };
|
|
44
52
|
zone.velRange = { min: velMin, max: velMax };
|
|
@@ -2,9 +2,10 @@ import { RiffChunk } from "../basic_soundfont/riff_chunk.js";
|
|
|
2
2
|
import { IndexedByteArray } from "../../utils/indexed_array.js";
|
|
3
3
|
import { readLittleEndian, signedInt8 } from "../../utils/byte_functions/little_endian.js";
|
|
4
4
|
import { stbvorbis } from "../../externals/stbvorbis_sync/stbvorbis_sync.min.js";
|
|
5
|
-
import { SpessaSynthWarn } from "../../utils/loggin.js";
|
|
5
|
+
import { SpessaSynthInfo, SpessaSynthWarn } from "../../utils/loggin.js";
|
|
6
6
|
import { readBytesAsString } from "../../utils/byte_functions/string.js";
|
|
7
7
|
import { BasicSample, sampleTypes } from "../basic_soundfont/basic_sample.js";
|
|
8
|
+
import { consoleColors } from "../../utils/other.js";
|
|
8
9
|
|
|
9
10
|
export const SF3_BIT_FLIT = 0x10;
|
|
10
11
|
|
|
@@ -139,7 +140,8 @@ export class SoundFontSample extends BasicSample
|
|
|
139
140
|
const linkedSample = samplesArray[this.linkedSampleIndex];
|
|
140
141
|
if (!linkedSample)
|
|
141
142
|
{
|
|
142
|
-
|
|
143
|
+
// log as info because it's common and not really dangerous
|
|
144
|
+
SpessaSynthInfo(`%cInvalid linked sample for ${this.sampleName}. Setting to mono.`, consoleColors.warn);
|
|
143
145
|
this.setSampleType(sampleTypes.monoSample);
|
|
144
146
|
}
|
|
145
147
|
else
|
|
@@ -259,9 +261,13 @@ export class SoundFontSample extends BasicSample
|
|
|
259
261
|
}
|
|
260
262
|
else
|
|
261
263
|
{
|
|
262
|
-
if (this.compressedData
|
|
264
|
+
if (this.compressedData)
|
|
263
265
|
{
|
|
264
|
-
|
|
266
|
+
if (allowVorbis)
|
|
267
|
+
{
|
|
268
|
+
return this.compressedData;
|
|
269
|
+
}
|
|
270
|
+
return this.encodeS16LE();
|
|
265
271
|
}
|
|
266
272
|
return this.sf2FileArrayHandle.slice(this.s16leStart, this.s16leEnd);
|
|
267
273
|
}
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* @property {string|undefined} genre - the song's genre
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { IndexedByteArray } from "./indexed_array.js";
|
|
10
|
+
import { writeStringAsBytes } from "./byte_functions/string.js";
|
|
11
|
+
import { writeRIFFChunkParts, writeRIFFChunkRaw } from "../soundfont/basic_soundfont/riff_chunk.js";
|
|
12
12
|
import { writeLittleEndian } from "./byte_functions/little_endian.js";
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -35,34 +35,33 @@ export function audioToWav(audioData, sampleRate, normalizeAudio = true, metadat
|
|
|
35
35
|
{
|
|
36
36
|
const encoder = new TextEncoder();
|
|
37
37
|
const infoChunks = [
|
|
38
|
-
|
|
39
|
-
writeRIFFOddSize("ICMT", encoder.encode("Created with SpessaSynth"), true)
|
|
38
|
+
writeRIFFChunkRaw("ICMT", encoder.encode("Created with SpessaSynth"), true)
|
|
40
39
|
];
|
|
41
40
|
if (metadata.artist)
|
|
42
41
|
{
|
|
43
42
|
infoChunks.push(
|
|
44
|
-
|
|
43
|
+
writeRIFFChunkRaw("IART", encoder.encode(metadata.artist), true)
|
|
45
44
|
);
|
|
46
45
|
}
|
|
47
46
|
if (metadata.album)
|
|
48
47
|
{
|
|
49
48
|
infoChunks.push(
|
|
50
|
-
|
|
49
|
+
writeRIFFChunkRaw("IPRD", encoder.encode(metadata.album), true)
|
|
51
50
|
);
|
|
52
51
|
}
|
|
53
52
|
if (metadata.genre)
|
|
54
53
|
{
|
|
55
54
|
infoChunks.push(
|
|
56
|
-
|
|
55
|
+
writeRIFFChunkRaw("IGNR", encoder.encode(metadata.genre), true)
|
|
57
56
|
);
|
|
58
57
|
}
|
|
59
58
|
if (metadata.title)
|
|
60
59
|
{
|
|
61
60
|
infoChunks.push(
|
|
62
|
-
|
|
61
|
+
writeRIFFChunkRaw("INAM", encoder.encode(metadata.title), true)
|
|
63
62
|
);
|
|
64
63
|
}
|
|
65
|
-
infoChunk =
|
|
64
|
+
infoChunk = writeRIFFChunkParts("INFO", infoChunks, true);
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
// prepare CUE chunk
|
|
@@ -89,12 +88,10 @@ export function audioToWav(audioData, sampleRate, normalizeAudio = true, metadat
|
|
|
89
88
|
writeLittleEndian(cueEnd, 0, 4); // BlockStart, always 0
|
|
90
89
|
writeLittleEndian(cueEnd, loopEndSamples, 4); // sampleOffset
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
new IndexedByteArray([2, 0, 0, 0]), // cue points count
|
|
91
|
+
cueChunk = writeRIFFChunkParts("cue ", [
|
|
92
|
+
new IndexedByteArray([2, 0, 0, 0]), // cue points count
|
|
94
93
|
cueStart,
|
|
95
|
-
cueEnd
|
|
96
|
-
]);
|
|
97
|
-
cueChunk = writeRIFFOddSize("cue ", out);
|
|
94
|
+
cueEnd]);
|
|
98
95
|
}
|
|
99
96
|
|
|
100
97
|
// Prepare the header
|
|
@@ -52,14 +52,19 @@ export function readBytesAsString(dataArray, bytes, encoding = undefined, trimEn
|
|
|
52
52
|
/**
|
|
53
53
|
* @param string {string}
|
|
54
54
|
* @param addZero {boolean} adds a zero terminator at the end
|
|
55
|
+
* @param ensureEven {boolean} ensures even byte count
|
|
55
56
|
* @returns {IndexedByteArray}
|
|
56
57
|
*/
|
|
57
|
-
export function getStringBytes(string, addZero = false)
|
|
58
|
+
export function getStringBytes(string, addZero = false, ensureEven = false)
|
|
58
59
|
{
|
|
59
60
|
let len = string.length;
|
|
60
61
|
if (addZero)
|
|
61
62
|
{
|
|
62
|
-
len
|
|
63
|
+
len++;
|
|
64
|
+
}
|
|
65
|
+
if (ensureEven && len % 2 !== 0)
|
|
66
|
+
{
|
|
67
|
+
len++;
|
|
63
68
|
}
|
|
64
69
|
const arr = new IndexedByteArray(len);
|
|
65
70
|
writeStringAsBytes(arr, string);
|
|
@@ -31,22 +31,4 @@ export class IndexedByteArray extends Uint8Array
|
|
|
31
31
|
a.currentIndex = 0;
|
|
32
32
|
return a;
|
|
33
33
|
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @param arrs {(IndexedByteArray|Uint8Array)[]}
|
|
39
|
-
* @returns {IndexedByteArray|Uint8Array}
|
|
40
|
-
*/
|
|
41
|
-
export function combineArrays(arrs)
|
|
42
|
-
{
|
|
43
|
-
const length = arrs.reduce((sum, current) => sum + current.length, 0);
|
|
44
|
-
const newArr = new IndexedByteArray(length);
|
|
45
|
-
let offset = 0;
|
|
46
|
-
for (const arr of arrs)
|
|
47
|
-
{
|
|
48
|
-
newArr.set(arr, offset);
|
|
49
|
-
offset += arr.length;
|
|
50
|
-
}
|
|
51
|
-
return newArr;
|
|
52
34
|
}
|