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.21",
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
  }
@@ -58,6 +58,7 @@ export class BasicZone
58
58
  this.generators.unshift(generator);
59
59
  }
60
60
 
61
+ // noinspection JSUnusedGlobalSymbols
61
62
  /**
62
63
  * @param type {generatorTypes}
63
64
  * @param value {number}
@@ -13,18 +13,17 @@ import { writeLittleEndian } from "./byte_functions/little_endian.js";
13
13
 
14
14
  /**
15
15
  *
16
- * @param audioData {{leftChannel: Float32Array, rightChannel: Float32Array, sampleRate: number}}
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 channel1Data = audioData.leftChannel;
25
- const channel2Data = audioData.rightChannel;
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 * 2 * bytesPerSample; // 2 channels, 16-bit per channel
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([2, 0], 22);
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 * 2 * bytesPerSample; // 2 channels, 16-bit per channel
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([4, 0], 32); // 2 channels * 16-bit per channel / 8
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 maxAbsValue = channel1Data.map((v, i) => Math.max(Math.abs(v), Math.abs(channel2Data[i])))
158
- .reduce((a, b) => Math.max(a, b));
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
- const sample1 = Math.min(32767, Math.max(-32768, channel1Data[i] * multiplier));
165
- const sample2 = Math.min(32767, Math.max(-32768, channel2Data[i] * multiplier));
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)