spessasynth_core 3.26.20 → 3.26.22
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/package.json +5 -2
- package/src/soundfont/basic_soundfont/basic_global_zone.js +1 -11
- package/src/soundfont/basic_soundfont/basic_instrument.js +28 -16
- package/src/soundfont/basic_soundfont/basic_instrument_zone.js +29 -7
- package/src/soundfont/basic_soundfont/basic_preset.js +11 -8
- package/src/soundfont/basic_soundfont/basic_preset_zone.js +27 -6
- package/src/soundfont/basic_soundfont/basic_sample.js +48 -5
- package/src/soundfont/basic_soundfont/basic_soundbank.js +19 -15
- package/src/soundfont/basic_soundfont/basic_zone.js +12 -0
- package/src/soundfont/basic_soundfont/write_dls/combine_zones.js +13 -22
- package/src/soundfont/basic_soundfont/write_dls/ins.js +3 -1
- package/src/soundfont/dls/dls_instrument.js +20 -0
- package/src/soundfont/dls/dls_preset.js +8 -8
- package/src/soundfont/dls/dls_zone.js +4 -7
- package/src/soundfont/dls/read_instrument.js +5 -9
- package/src/soundfont/dls/read_region.js +5 -8
- package/src/soundfont/read_sf2/generators.js +2 -5
- package/src/soundfont/read_sf2/instrument_zones.js +88 -0
- package/src/soundfont/read_sf2/instruments.js +26 -30
- package/src/soundfont/read_sf2/modulators.js +2 -0
- package/src/soundfont/read_sf2/preset_zones.js +92 -0
- package/src/soundfont/read_sf2/presets.js +23 -31
- package/src/soundfont/read_sf2/samples.js +5 -5
- package/src/soundfont/read_sf2/soundfont.js +44 -32
- package/src/utils/buffer_to_wav.js +34 -19
- package/src/soundfont/read_sf2/zones.js +0 -223
|
@@ -13,18 +13,17 @@ import { writeLittleEndian } from "./byte_functions/little_endian.js";
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
*
|
|
16
|
-
* @param audioData {
|
|
16
|
+
* @param audioData {Float32Array[]} channels
|
|
17
|
+
* @param sampleRate {number}
|
|
17
18
|
* @param normalizeAudio {boolean} find the max sample point and set it to 1, and scale others with it
|
|
18
19
|
* @param metadata {WaveMetadata}
|
|
19
20
|
* @param loop {{start: number, end: number}} loop start and end points in seconds. Undefined if no loop
|
|
20
21
|
* @returns {ArrayBuffer}
|
|
21
22
|
*/
|
|
22
|
-
export function audioToWav(audioData, normalizeAudio = true, metadata = {}, loop = undefined)
|
|
23
|
+
export function audioToWav(audioData, sampleRate, normalizeAudio = true, metadata = {}, loop = undefined)
|
|
23
24
|
{
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const length = channel1Data.length;
|
|
27
|
-
const sampleRate = audioData.sampleRate;
|
|
25
|
+
const length = audioData[0].length;
|
|
26
|
+
const numChannels = audioData.length;
|
|
28
27
|
|
|
29
28
|
const bytesPerSample = 2; // 16-bit PCM
|
|
30
29
|
|
|
@@ -100,7 +99,7 @@ export function audioToWav(audioData, normalizeAudio = true, metadata = {}, loop
|
|
|
100
99
|
|
|
101
100
|
// Prepare the header
|
|
102
101
|
const headerSize = 44;
|
|
103
|
-
const dataSize = length *
|
|
102
|
+
const dataSize = length * numChannels * bytesPerSample; // 16-bit per channel
|
|
104
103
|
const fileSize = headerSize + dataSize + infoChunk.length + cueChunk.length - 8; // total file size minus the first 8 bytes
|
|
105
104
|
const header = new Uint8Array(headerSize);
|
|
106
105
|
|
|
@@ -120,20 +119,20 @@ export function audioToWav(audioData, normalizeAudio = true, metadata = {}, loop
|
|
|
120
119
|
// audio format (PCM)
|
|
121
120
|
header.set([1, 0], 20);
|
|
122
121
|
// number of channels (2)
|
|
123
|
-
header.set([
|
|
122
|
+
header.set([numChannels & 255, numChannels >> 8], 22);
|
|
124
123
|
// sample rate
|
|
125
124
|
header.set(
|
|
126
125
|
new Uint8Array([sampleRate & 0xff, (sampleRate >> 8) & 0xff, (sampleRate >> 16) & 0xff, (sampleRate >> 24) & 0xff]),
|
|
127
126
|
24
|
|
128
127
|
);
|
|
129
128
|
// byte rate (sample rate * block align)
|
|
130
|
-
const byteRate = sampleRate *
|
|
129
|
+
const byteRate = sampleRate * numChannels * bytesPerSample; // 16-bit per channel
|
|
131
130
|
header.set(
|
|
132
131
|
new Uint8Array([byteRate & 0xff, (byteRate >> 8) & 0xff, (byteRate >> 16) & 0xff, (byteRate >> 24) & 0xff]),
|
|
133
132
|
28
|
|
134
133
|
);
|
|
135
134
|
// block align (channels * bytes per sample)
|
|
136
|
-
header.set([
|
|
135
|
+
header.set([numChannels * bytesPerSample, 0], 32); // n channels * 16-bit per channel / 8
|
|
137
136
|
// bits per sample
|
|
138
137
|
header.set([16, 0], 34); // 16-bit
|
|
139
138
|
|
|
@@ -154,21 +153,37 @@ export function audioToWav(audioData, normalizeAudio = true, metadata = {}, loop
|
|
|
154
153
|
if (normalizeAudio)
|
|
155
154
|
{
|
|
156
155
|
// find min and max values to prevent clipping when converting to 16 bits
|
|
157
|
-
const
|
|
158
|
-
|
|
156
|
+
const numSamples = audioData[0].length;
|
|
157
|
+
|
|
158
|
+
let maxAbsValue = 0;
|
|
159
|
+
|
|
160
|
+
for (let ch = 0; ch < numChannels; ch++)
|
|
161
|
+
{
|
|
162
|
+
const data = audioData[ch];
|
|
163
|
+
for (let i = 0; i < numSamples; i++)
|
|
164
|
+
{
|
|
165
|
+
const sample = Math.abs(data[i]);
|
|
166
|
+
if (sample > maxAbsValue)
|
|
167
|
+
{
|
|
168
|
+
maxAbsValue = sample;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
159
173
|
multiplier = maxAbsValue > 0 ? (32767 / maxAbsValue) : 1;
|
|
160
174
|
}
|
|
161
175
|
for (let i = 0; i < length; i++)
|
|
162
176
|
{
|
|
163
177
|
// interleave both channels
|
|
164
|
-
|
|
165
|
-
|
|
178
|
+
audioData.forEach(d =>
|
|
179
|
+
{
|
|
180
|
+
const sample = Math.min(32767, Math.max(-32768, d[i] * multiplier));
|
|
181
|
+
// convert to 16-bit
|
|
182
|
+
wavData[offset++] = sample & 0xff;
|
|
183
|
+
wavData[offset++] = (sample >> 8) & 0xff;
|
|
184
|
+
|
|
185
|
+
});
|
|
166
186
|
|
|
167
|
-
// convert to 16-bit
|
|
168
|
-
wavData[offset++] = sample1 & 0xff;
|
|
169
|
-
wavData[offset++] = (sample1 >> 8) & 0xff;
|
|
170
|
-
wavData[offset++] = sample2 & 0xff;
|
|
171
|
-
wavData[offset++] = (sample2 >> 8) & 0xff;
|
|
172
187
|
}
|
|
173
188
|
|
|
174
189
|
if (infoOn)
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import { readLittleEndian } from "../../utils/byte_functions/little_endian.js";
|
|
2
|
-
import { IndexedByteArray } from "../../utils/indexed_array.js";
|
|
3
|
-
import { RiffChunk } from "../basic_soundfont/riff_chunk.js";
|
|
4
|
-
import { BasicPresetZone } from "../basic_soundfont/basic_preset_zone.js";
|
|
5
|
-
import { Generator } from "../basic_soundfont/generator.js";
|
|
6
|
-
import { Modulator } from "../basic_soundfont/modulator.js";
|
|
7
|
-
import { generatorTypes } from "../basic_soundfont/generator_types.js";
|
|
8
|
-
import { BasicInstrumentZone } from "../basic_soundfont/basic_instrument_zone.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* zones.js
|
|
12
|
-
* purpose: reads instrumend and preset zones from soundfont and gets their respective samples and generators and modulators
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
export class InstrumentZone extends BasicInstrumentZone
|
|
16
|
-
{
|
|
17
|
-
/**
|
|
18
|
-
* Creates a zone (instrument)
|
|
19
|
-
* @param dataArray {IndexedByteArray}
|
|
20
|
-
*/
|
|
21
|
-
constructor(dataArray)
|
|
22
|
-
{
|
|
23
|
-
super();
|
|
24
|
-
this.generatorZoneStartIndex = readLittleEndian(dataArray, 2);
|
|
25
|
-
this.modulatorZoneStartIndex = readLittleEndian(dataArray, 2);
|
|
26
|
-
this.modulatorZoneSize = 0;
|
|
27
|
-
this.generatorZoneSize = 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
setZoneSize(modulatorZoneSize, generatorZoneSize)
|
|
31
|
-
{
|
|
32
|
-
this.modulatorZoneSize = modulatorZoneSize;
|
|
33
|
-
this.generatorZoneSize = generatorZoneSize;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* grab the generators
|
|
38
|
-
* @param generators {Generator[]}
|
|
39
|
-
*/
|
|
40
|
-
getGenerators(generators)
|
|
41
|
-
{
|
|
42
|
-
for (let i = this.generatorZoneStartIndex; i < this.generatorZoneStartIndex + this.generatorZoneSize; i++)
|
|
43
|
-
{
|
|
44
|
-
const g = generators[i];
|
|
45
|
-
if (!g)
|
|
46
|
-
{
|
|
47
|
-
throw new Error("Missing generator in instrument zone! The file may corrupted.");
|
|
48
|
-
}
|
|
49
|
-
this.addGenerators(g);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* grab the modulators
|
|
55
|
-
* @param modulators {Modulator[]}
|
|
56
|
-
*/
|
|
57
|
-
getModulators(modulators)
|
|
58
|
-
{
|
|
59
|
-
for (let i = this.modulatorZoneStartIndex; i < this.modulatorZoneStartIndex + this.modulatorZoneSize; i++)
|
|
60
|
-
{
|
|
61
|
-
const m = modulators[i];
|
|
62
|
-
if (!m)
|
|
63
|
-
{
|
|
64
|
-
throw new Error("Missing modulator in instrument zone! The file may corrupted.");
|
|
65
|
-
}
|
|
66
|
-
this.addModulators(m);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Loads the zone's sample
|
|
72
|
-
* @param samples {BasicSample[]}
|
|
73
|
-
*/
|
|
74
|
-
getSample(samples)
|
|
75
|
-
{
|
|
76
|
-
let sampleID = this.generators.find(g => g.generatorType === generatorTypes.sampleID);
|
|
77
|
-
if (sampleID)
|
|
78
|
-
{
|
|
79
|
-
this.setSample(samples[sampleID.generatorValue]);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Reads the given instrument zone read
|
|
86
|
-
* @param zonesChunk {RiffChunk}
|
|
87
|
-
* @param instrumentGenerators {Generator[]}
|
|
88
|
-
* @param instrumentModulators {Modulator[]}
|
|
89
|
-
* @param instrumentSamples {BasicSample[]}
|
|
90
|
-
* @returns {InstrumentZone[]}
|
|
91
|
-
*/
|
|
92
|
-
export function readInstrumentZones(zonesChunk, instrumentGenerators, instrumentModulators, instrumentSamples)
|
|
93
|
-
{
|
|
94
|
-
/**
|
|
95
|
-
* @type {InstrumentZone[]}
|
|
96
|
-
*/
|
|
97
|
-
let zones = [];
|
|
98
|
-
while (zonesChunk.chunkData.length > zonesChunk.chunkData.currentIndex)
|
|
99
|
-
{
|
|
100
|
-
let zone = new InstrumentZone(zonesChunk.chunkData);
|
|
101
|
-
if (zones.length > 0)
|
|
102
|
-
{
|
|
103
|
-
let modulatorZoneSize = zone.modulatorZoneStartIndex - zones[zones.length - 1].modulatorZoneStartIndex;
|
|
104
|
-
let generatorZoneSize = zone.generatorZoneStartIndex - zones[zones.length - 1].generatorZoneStartIndex;
|
|
105
|
-
zones[zones.length - 1].setZoneSize(modulatorZoneSize, generatorZoneSize);
|
|
106
|
-
zones[zones.length - 1].getGenerators(instrumentGenerators);
|
|
107
|
-
zones[zones.length - 1].getModulators(instrumentModulators);
|
|
108
|
-
zones[zones.length - 1].getSample(instrumentSamples);
|
|
109
|
-
}
|
|
110
|
-
zones.push(zone);
|
|
111
|
-
}
|
|
112
|
-
if (zones.length > 1)
|
|
113
|
-
{
|
|
114
|
-
// remove terminal
|
|
115
|
-
zones.pop();
|
|
116
|
-
}
|
|
117
|
-
return zones;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export class PresetZone extends BasicPresetZone
|
|
121
|
-
{
|
|
122
|
-
/**
|
|
123
|
-
* Creates a zone (preset)
|
|
124
|
-
* @param dataArray {IndexedByteArray}
|
|
125
|
-
*/
|
|
126
|
-
constructor(dataArray)
|
|
127
|
-
{
|
|
128
|
-
super();
|
|
129
|
-
this.generatorZoneStartIndex = readLittleEndian(dataArray, 2);
|
|
130
|
-
this.modulatorZoneStartIndex = readLittleEndian(dataArray, 2);
|
|
131
|
-
this.modulatorZoneSize = 0;
|
|
132
|
-
this.generatorZoneSize = 0;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
setZoneSize(modulatorZoneSize, generatorZoneSize)
|
|
136
|
-
{
|
|
137
|
-
this.modulatorZoneSize = modulatorZoneSize;
|
|
138
|
-
this.generatorZoneSize = generatorZoneSize;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* grab the generators
|
|
143
|
-
* @param generators {Generator[]}
|
|
144
|
-
*/
|
|
145
|
-
getGenerators(generators)
|
|
146
|
-
{
|
|
147
|
-
for (let i = this.generatorZoneStartIndex; i < this.generatorZoneStartIndex + this.generatorZoneSize; i++)
|
|
148
|
-
{
|
|
149
|
-
const g = generators[i];
|
|
150
|
-
if (!g)
|
|
151
|
-
{
|
|
152
|
-
throw new Error("Missing generator in preset zone! The file may corrupted.");
|
|
153
|
-
}
|
|
154
|
-
this.addGenerators(g);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* grab the modulators
|
|
160
|
-
* @param modulators {Modulator[]}
|
|
161
|
-
*/
|
|
162
|
-
getModulators(modulators)
|
|
163
|
-
{
|
|
164
|
-
for (let i = this.modulatorZoneStartIndex; i < this.modulatorZoneStartIndex + this.modulatorZoneSize; i++)
|
|
165
|
-
{
|
|
166
|
-
const m = modulators[i];
|
|
167
|
-
if (!m)
|
|
168
|
-
{
|
|
169
|
-
throw new Error("Missing modulator in preset zone! The file may corrupted.");
|
|
170
|
-
}
|
|
171
|
-
this.addModulators(m);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* grab the instrument
|
|
177
|
-
* @param instruments {BasicInstrument[]}
|
|
178
|
-
*/
|
|
179
|
-
getInstrument(instruments)
|
|
180
|
-
{
|
|
181
|
-
let instrumentID = this.generators.find(g => g.generatorType === generatorTypes.instrument);
|
|
182
|
-
if (instrumentID)
|
|
183
|
-
{
|
|
184
|
-
this.setInstrument(instruments[instrumentID.generatorValue]);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Reads the given preset zone read
|
|
191
|
-
* @param zonesChunk {RiffChunk}
|
|
192
|
-
* @param presetGenerators {Generator[]}
|
|
193
|
-
* @param instruments {BasicInstrument[]}
|
|
194
|
-
* @param presetModulators {Modulator[]}
|
|
195
|
-
* @returns {PresetZone[]}
|
|
196
|
-
*/
|
|
197
|
-
export function readPresetZones(zonesChunk, presetGenerators, presetModulators, instruments)
|
|
198
|
-
{
|
|
199
|
-
/**
|
|
200
|
-
* @type {PresetZone[]}
|
|
201
|
-
*/
|
|
202
|
-
let zones = [];
|
|
203
|
-
while (zonesChunk.chunkData.length > zonesChunk.chunkData.currentIndex)
|
|
204
|
-
{
|
|
205
|
-
let zone = new PresetZone(zonesChunk.chunkData);
|
|
206
|
-
if (zones.length > 0)
|
|
207
|
-
{
|
|
208
|
-
let modulatorZoneSize = zone.modulatorZoneStartIndex - zones[zones.length - 1].modulatorZoneStartIndex;
|
|
209
|
-
let generatorZoneSize = zone.generatorZoneStartIndex - zones[zones.length - 1].generatorZoneStartIndex;
|
|
210
|
-
zones[zones.length - 1].setZoneSize(modulatorZoneSize, generatorZoneSize);
|
|
211
|
-
zones[zones.length - 1].getGenerators(presetGenerators);
|
|
212
|
-
zones[zones.length - 1].getModulators(presetModulators);
|
|
213
|
-
zones[zones.length - 1].getInstrument(instruments);
|
|
214
|
-
}
|
|
215
|
-
zones.push(zone);
|
|
216
|
-
}
|
|
217
|
-
if (zones.length > 1)
|
|
218
|
-
{
|
|
219
|
-
// remove terminal
|
|
220
|
-
zones.pop();
|
|
221
|
-
}
|
|
222
|
-
return zones;
|
|
223
|
-
}
|