spessasynth_core 3.26.21 → 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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spessasynth_core",
|
|
3
|
-
"version": "3.26.
|
|
3
|
+
"version": "3.26.22",
|
|
4
4
|
"description": "MIDI and SoundFont2/DLS library with no compromises",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -22,9 +22,12 @@
|
|
|
22
22
|
"midi",
|
|
23
23
|
"rmi",
|
|
24
24
|
"midi-player",
|
|
25
|
+
"midi-file",
|
|
26
|
+
"midi-editor",
|
|
25
27
|
"player",
|
|
26
28
|
"soundfont2",
|
|
27
|
-
"soundfont3"
|
|
29
|
+
"soundfont3",
|
|
30
|
+
"audio-to-wav"
|
|
28
31
|
],
|
|
29
32
|
"author": {
|
|
30
33
|
"name": "spessasus",
|
|
@@ -229,6 +229,21 @@ export class BasicSample
|
|
|
229
229
|
*/
|
|
230
230
|
getAudioData()
|
|
231
231
|
{
|
|
232
|
+
if (!this.sampleData)
|
|
233
|
+
{
|
|
234
|
+
throw new Error("Error! Sample data is undefined. Is the method overriden properly?");
|
|
235
|
+
}
|
|
232
236
|
return this.sampleData;
|
|
233
237
|
}
|
|
238
|
+
|
|
239
|
+
// noinspection JSUnusedGlobalSymbols
|
|
240
|
+
/**
|
|
241
|
+
* @param audioData {Float32Array}
|
|
242
|
+
*/
|
|
243
|
+
setAudioData(audioData)
|
|
244
|
+
{
|
|
245
|
+
this.isCompressed = false;
|
|
246
|
+
delete this.compressedData;
|
|
247
|
+
this.sampleData = audioData;
|
|
248
|
+
}
|
|
234
249
|
}
|
|
@@ -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)
|