@grame/faustwasm 0.1.7 → 0.2.0
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/assets/standalone/faustwasm/index.d.ts +132 -99
- package/assets/standalone/faustwasm/index.js +907 -1230
- package/assets/standalone/faustwasm/index.js.map +4 -4
- package/dist/cjs/index.d.ts +132 -99
- package/dist/cjs/index.js +909 -1212
- package/dist/cjs/index.js.map +4 -4
- package/dist/cjs-bundle/index.d.ts +132 -99
- package/dist/cjs-bundle/index.js +1012 -1317
- package/dist/cjs-bundle/index.js.map +4 -4
- package/dist/esm/index.d.ts +132 -99
- package/dist/esm/index.js +907 -1230
- package/dist/esm/index.js.map +4 -4
- package/dist/esm-bundle/index.d.ts +132 -99
- package/dist/esm-bundle/index.js +1012 -1321
- package/dist/esm-bundle/index.js.map +4 -4
- package/libfaust-wasm/libfaust-wasm.js +1 -1
- package/libfaust-wasm/libfaust-wasm.wasm +0 -0
- package/package.json +15 -15
- package/src/FaustAudioWorkletProcessor.ts +3 -14
- package/src/FaustCmajor.ts +6 -6
- package/src/FaustCompiler.ts +2 -2
- package/src/FaustDspGenerator.ts +109 -75
- package/src/FaustDspInstance.ts +2 -2
- package/src/FaustFFTAudioWorkletProcessor.ts +3 -1
- package/src/FaustWebAudioDsp.ts +224 -449
- package/src/SoundfileReader.ts +117 -0
- package/src/exports.ts +1 -0
- package/src/faust2wasmFiles.js +1 -1
- package/src/types.ts +15 -7
- package/test/faustlive-wasm/faustwasm/index.d.ts +132 -99
- package/test/faustlive-wasm/faustwasm/index.js +907 -1230
- package/test/faustlive-wasm/faustwasm/index.js.map +4 -4
- package/test/soundfile.dsp +15 -0
- package/test/soundfile1.dsp +23 -0
- package/test/web/soundfile.html +69 -0
package/src/FaustWebAudioDsp.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FaustMonoDspInstance, FaustPolyDspInstance, IFaustDspInstance } from "./FaustDspInstance";
|
|
2
|
-
import type { FaustDspMeta, FaustUIDescriptor, FaustUIGroup, FaustUIInputItem, FaustUIItem } from "./types";
|
|
2
|
+
import type { AudioData, FaustDspMeta, FaustUIDescriptor, FaustUIGroup, FaustUIInputItem, FaustUIItem, LooseFaustDspFactory } from "./types";
|
|
3
3
|
|
|
4
4
|
// Public API
|
|
5
5
|
export type OutputParamHandler = (path: string, value: number) => void;
|
|
@@ -10,6 +10,24 @@ export type MetadataHandler = (key: string, value: string) => void;
|
|
|
10
10
|
// Implementation API
|
|
11
11
|
export type UIHandler = (item: FaustUIItem) => void;
|
|
12
12
|
|
|
13
|
+
/** Definition of the AudioBufferItem type */
|
|
14
|
+
export interface AudioBufferItem {
|
|
15
|
+
pathName: string;
|
|
16
|
+
audioBuffer: AudioBuffer;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** Definition of the SoundfileItem type */
|
|
20
|
+
export interface SoundfileItem {
|
|
21
|
+
/** Name of the soundfile */
|
|
22
|
+
name: string;
|
|
23
|
+
/** URL of the soundfile */
|
|
24
|
+
url: string;
|
|
25
|
+
/** Index in the DSP struct */
|
|
26
|
+
index: number;
|
|
27
|
+
/** Base pointer in wasm memory */
|
|
28
|
+
basePtr: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
13
31
|
/**
|
|
14
32
|
* WasmAllocator is a basic memory management class designed to allocate
|
|
15
33
|
* blocks of memory within a WebAssembly.Memory object. It provides a simple
|
|
@@ -19,7 +37,7 @@ export type UIHandler = (item: FaustUIItem) => void;
|
|
|
19
37
|
* always allocating the next block at the end of the last. This approach does not
|
|
20
38
|
* handle freeing of memory or reuse of memory spaces.
|
|
21
39
|
*/
|
|
22
|
-
class WasmAllocator {
|
|
40
|
+
export class WasmAllocator {
|
|
23
41
|
// The WebAssembly.Memory object this allocator will manage.
|
|
24
42
|
private readonly memory: WebAssembly.Memory;
|
|
25
43
|
// The number of bytes currently allocated. This serves as the "pointer" to the
|
|
@@ -110,23 +128,24 @@ class WasmAllocator {
|
|
|
110
128
|
}
|
|
111
129
|
}
|
|
112
130
|
|
|
113
|
-
// Maximum number of soundfile parts.
|
|
114
|
-
const MAX_SOUNDFILE_PARTS = 256;
|
|
115
|
-
|
|
116
|
-
// Maximum number of channels.
|
|
117
|
-
const MAX_CHAN = 64;
|
|
118
|
-
|
|
119
|
-
// Maximum buffer size in frames.
|
|
120
|
-
const BUFFER_SIZE = 1024;
|
|
121
|
-
|
|
122
|
-
// Default sample rate.
|
|
123
|
-
const SAMPLE_RATE = 44100;
|
|
124
|
-
|
|
125
131
|
/**
|
|
126
132
|
* Soundfile class to handle soundfile data in wasm memory.
|
|
127
133
|
*/
|
|
128
|
-
class Soundfile {
|
|
129
|
-
|
|
134
|
+
export class Soundfile {
|
|
135
|
+
/** Maximum number of soundfile parts. */
|
|
136
|
+
static get MAX_SOUNDFILE_PARTS() { return 256; }
|
|
137
|
+
|
|
138
|
+
/** Maximum number of channels. */
|
|
139
|
+
static get MAX_CHAN() { return 64; }
|
|
140
|
+
|
|
141
|
+
/** Maximum buffer size in frames. */
|
|
142
|
+
static get BUFFER_SIZE() { return 1024; }
|
|
143
|
+
|
|
144
|
+
/** Default sample rate. */
|
|
145
|
+
static get SAMPLE_RATE() { return 44100; }
|
|
146
|
+
|
|
147
|
+
/** Pointer to the soundfile structure in wasm memory */
|
|
148
|
+
private readonly fPtr: number;
|
|
130
149
|
private readonly fBuffers: number;
|
|
131
150
|
private readonly fLength: number;
|
|
132
151
|
private readonly fSR: number;
|
|
@@ -155,9 +174,9 @@ class Soundfile {
|
|
|
155
174
|
this.fPtr = allocator.alloc(4 * this.fPtrSize); // 4 fPtrSize: fBuffers, fLength, fSR, fOffset
|
|
156
175
|
|
|
157
176
|
// Use the 4 or 8 bytes size for int. The access are then adapted in copyToOut and emptyFile methods
|
|
158
|
-
this.fLength = allocator.alloc(MAX_SOUNDFILE_PARTS * this.fIntSize);
|
|
159
|
-
this.fSR = allocator.alloc(MAX_SOUNDFILE_PARTS * this.fIntSize);
|
|
160
|
-
this.fOffset = allocator.alloc(MAX_SOUNDFILE_PARTS * this.fIntSize);
|
|
177
|
+
this.fLength = allocator.alloc(Soundfile.MAX_SOUNDFILE_PARTS * this.fIntSize);
|
|
178
|
+
this.fSR = allocator.alloc(Soundfile.MAX_SOUNDFILE_PARTS * this.fIntSize);
|
|
179
|
+
this.fOffset = allocator.alloc(Soundfile.MAX_SOUNDFILE_PARTS * this.fIntSize);
|
|
161
180
|
|
|
162
181
|
this.fBuffers = this.allocBuffers(curChan, length, maxChan);
|
|
163
182
|
|
|
@@ -202,38 +221,37 @@ class Soundfile {
|
|
|
202
221
|
}
|
|
203
222
|
}
|
|
204
223
|
|
|
205
|
-
copyToOut(part: number, maxChannels: number, offset: number,
|
|
206
|
-
|
|
224
|
+
copyToOut(part: number, maxChannels: number, offset: number, audioData: AudioData) {
|
|
207
225
|
// Set the soundfile fields in wasm memory
|
|
208
226
|
if (this.fIntSize === 4) {
|
|
209
227
|
const HEAP32 = this.fAllocator.getInt32Array();
|
|
210
|
-
HEAP32[(this.fLength >> Math.log2(this.fIntSize)) + part] =
|
|
211
|
-
HEAP32[(this.fSR >> Math.log2(this.fIntSize)) + part] =
|
|
228
|
+
HEAP32[(this.fLength >> Math.log2(this.fIntSize)) + part] = audioData.audioBuffer[0].length;
|
|
229
|
+
HEAP32[(this.fSR >> Math.log2(this.fIntSize)) + part] = audioData.sampleRate;
|
|
212
230
|
HEAP32[(this.fOffset >> Math.log2(this.fIntSize)) + part] = offset;
|
|
213
231
|
} else {
|
|
214
232
|
const HEAP64 = this.fAllocator.getInt64Array();
|
|
215
|
-
HEAP64[(this.fLength >> Math.log2(this.fIntSize)) + part] = BigInt(
|
|
216
|
-
HEAP64[(this.fSR >> Math.log2(this.fIntSize)) + part] = BigInt(
|
|
233
|
+
HEAP64[(this.fLength >> Math.log2(this.fIntSize)) + part] = BigInt(audioData.audioBuffer[0].length);
|
|
234
|
+
HEAP64[(this.fSR >> Math.log2(this.fIntSize)) + part] = BigInt(audioData.sampleRate);
|
|
217
235
|
HEAP64[(this.fOffset >> Math.log2(this.fIntSize)) + part] = BigInt(offset);
|
|
218
236
|
}
|
|
219
237
|
|
|
220
|
-
console.log(`copyToOut: part: ${part}, maxChannels: ${maxChannels}, offset: ${offset}, buffer: ${
|
|
238
|
+
console.log(`copyToOut: part: ${part}, maxChannels: ${maxChannels}, offset: ${offset}, buffer: ${audioData}`);
|
|
221
239
|
|
|
222
240
|
//this.displayMemory("IN copyToOut, BEFORE copyToOutReal", true);
|
|
223
241
|
// Copy the soundfile data to the buffer
|
|
224
242
|
if (this.fSampleSize === 8) {
|
|
225
|
-
this.copyToOutReal64(maxChannels, offset,
|
|
243
|
+
this.copyToOutReal64(maxChannels, offset, audioData);
|
|
226
244
|
} else {
|
|
227
|
-
this.copyToOutReal32(maxChannels, offset,
|
|
245
|
+
this.copyToOutReal32(maxChannels, offset, audioData);
|
|
228
246
|
}
|
|
229
247
|
//this.displayMemory("IN copyToOut, AFTER copyToOutReal");
|
|
230
248
|
}
|
|
231
249
|
|
|
232
|
-
copyToOutReal32(maxChannels: number, offset: number,
|
|
250
|
+
copyToOutReal32(maxChannels: number, offset: number, audioData: AudioData) {
|
|
233
251
|
const HEAP32 = this.fAllocator.getInt32Array();
|
|
234
252
|
const HEAPF = this.fAllocator.getFloat32Array();
|
|
235
|
-
for (let chan = 0; chan <
|
|
236
|
-
const input: Float32Array =
|
|
253
|
+
for (let chan = 0; chan < audioData.audioBuffer.length; chan++) {
|
|
254
|
+
const input: Float32Array = audioData.audioBuffer[chan];
|
|
237
255
|
const output: number = HEAP32[(this.fBuffers >> 2) + chan];
|
|
238
256
|
const begin: number = (output + (offset * this.fSampleSize)) >> Math.log2(this.fSampleSize);
|
|
239
257
|
const end: number = (output + (offset + input.length) * this.fSampleSize) >> Math.log2(this.fSampleSize);
|
|
@@ -246,11 +264,11 @@ class Soundfile {
|
|
|
246
264
|
}
|
|
247
265
|
}
|
|
248
266
|
|
|
249
|
-
copyToOutReal64(maxChannels: number, offset: number,
|
|
267
|
+
copyToOutReal64(maxChannels: number, offset: number, audioData: AudioData) {
|
|
250
268
|
const HEAP32 = this.fAllocator.getInt32Array();
|
|
251
269
|
const HEAPF = this.fAllocator.getFloat64Array();
|
|
252
|
-
for (let chan = 0; chan <
|
|
253
|
-
const input: Float32Array =
|
|
270
|
+
for (let chan = 0; chan < audioData.audioBuffer.length; chan++) {
|
|
271
|
+
const input: Float32Array = audioData.audioBuffer[chan];
|
|
254
272
|
const output: number = HEAP32[(this.fBuffers >> 2) + chan];
|
|
255
273
|
const begin: number = (output + (offset * this.fSampleSize)) >> Math.log2(this.fSampleSize);
|
|
256
274
|
const end: number = (output + (offset + input.length) * this.fSampleSize) >> Math.log2(this.fSampleSize);
|
|
@@ -264,22 +282,21 @@ class Soundfile {
|
|
|
264
282
|
}
|
|
265
283
|
|
|
266
284
|
emptyFile(part: number, offset: number): number {
|
|
267
|
-
|
|
268
285
|
// Set the soundfile fields in wasm memory
|
|
269
286
|
if (this.fIntSize === 4) {
|
|
270
287
|
const HEAP32 = this.fAllocator.getInt32Array();
|
|
271
|
-
HEAP32[(this.fLength >> Math.log2(this.fIntSize)) + part] = BUFFER_SIZE;
|
|
272
|
-
HEAP32[(this.fSR >> Math.log2(this.fIntSize)) + part] = SAMPLE_RATE;
|
|
288
|
+
HEAP32[(this.fLength >> Math.log2(this.fIntSize)) + part] = Soundfile.BUFFER_SIZE;
|
|
289
|
+
HEAP32[(this.fSR >> Math.log2(this.fIntSize)) + part] = Soundfile.SAMPLE_RATE;
|
|
273
290
|
HEAP32[(this.fOffset >> Math.log2(this.fIntSize)) + part] = offset;
|
|
274
291
|
} else {
|
|
275
292
|
const HEAP64 = this.fAllocator.getInt64Array();
|
|
276
|
-
HEAP64[(this.fLength >> Math.log2(this.fIntSize)) + part] = BigInt(BUFFER_SIZE);
|
|
277
|
-
HEAP64[(this.fSR >> Math.log2(this.fIntSize)) + part] = BigInt(SAMPLE_RATE);
|
|
293
|
+
HEAP64[(this.fLength >> Math.log2(this.fIntSize)) + part] = BigInt(Soundfile.BUFFER_SIZE);
|
|
294
|
+
HEAP64[(this.fSR >> Math.log2(this.fIntSize)) + part] = BigInt(Soundfile.SAMPLE_RATE);
|
|
278
295
|
HEAP64[(this.fOffset >> Math.log2(this.fIntSize)) + part] = BigInt(offset);
|
|
279
296
|
}
|
|
280
297
|
|
|
281
298
|
// Update and return the new offset
|
|
282
|
-
return offset + BUFFER_SIZE;
|
|
299
|
+
return offset + Soundfile.BUFFER_SIZE;
|
|
283
300
|
}
|
|
284
301
|
|
|
285
302
|
displayMemory(where: string = "", mem: boolean = false) {
|
|
@@ -312,220 +329,6 @@ class Soundfile {
|
|
|
312
329
|
getHEAPFloat64(): Float64Array {
|
|
313
330
|
return this.fAllocator.getFloat64Array();
|
|
314
331
|
}
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Definition of the AudioBufferItem type
|
|
319
|
-
type AudioBufferItem = {
|
|
320
|
-
pathName: string;
|
|
321
|
-
audioBuffer: AudioBuffer;
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* SoundfileReader class to read soundfile data and copy it to the soundfile buffer.
|
|
326
|
-
*/
|
|
327
|
-
class SoundfileReader {
|
|
328
|
-
|
|
329
|
-
private readonly fAllocator: WasmAllocator;
|
|
330
|
-
private readonly fSampleSize: number;
|
|
331
|
-
private readonly fContext;
|
|
332
|
-
private readonly fAudioBuffers: AudioBufferItem[];
|
|
333
|
-
|
|
334
|
-
constructor(allocator: WasmAllocator, context: BaseAudioContext, sampleSize: number) {
|
|
335
|
-
this.fAllocator = allocator;
|
|
336
|
-
this.fSampleSize = sampleSize;
|
|
337
|
-
this.fContext = context;
|
|
338
|
-
this.fAudioBuffers = [];
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Check if the file exists.
|
|
343
|
-
*
|
|
344
|
-
* @param url : the url of the file to check
|
|
345
|
-
* @returns : true if the file exists, otherwise false
|
|
346
|
-
*/
|
|
347
|
-
private async checkFileExists(url: string): Promise<boolean> {
|
|
348
|
-
try {
|
|
349
|
-
console.log(`"checkFileExists" url: ${url}`);
|
|
350
|
-
const response = await fetch(url, { method: 'HEAD' });
|
|
351
|
-
return response.ok; // Will be true if the status code is 200-299
|
|
352
|
-
} catch (error) {
|
|
353
|
-
console.error('Fetch error:', error);
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Check if the file exists in the given directories.
|
|
360
|
-
*
|
|
361
|
-
* @param directories : the list of directories to search for the file
|
|
362
|
-
* @param fileName : the name of the file to search for
|
|
363
|
-
* @returns : the path of the file if found, otherwise an empty string
|
|
364
|
-
*/
|
|
365
|
-
private async checkFile(directories: string[], fileName: string): Promise<string> {
|
|
366
|
-
|
|
367
|
-
if (await this.checkFileExists(fileName)) {
|
|
368
|
-
return fileName;
|
|
369
|
-
} else {
|
|
370
|
-
for (let i = 0; i < directories.length; i++) {
|
|
371
|
-
const pathName = directories[i] + "/" + fileName;
|
|
372
|
-
if (await this.checkFileExists(pathName)) {
|
|
373
|
-
return pathName;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
return "";
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Check if all soundfiles exist and return their real path_name.
|
|
382
|
-
*
|
|
383
|
-
* @param directories : the list of directories to search for the file
|
|
384
|
-
* @param fileNameList : the list of file names to search for
|
|
385
|
-
* @returns : the list of path names of the files if found, otherwise an empty string
|
|
386
|
-
*/
|
|
387
|
-
async checkFiles(directories: string[], fileNameList: string[]): Promise<string[]> {
|
|
388
|
-
const pathNameList: string[] = [];
|
|
389
|
-
for (let i = 0; i < fileNameList.length; i++) {
|
|
390
|
-
const pathName: string = await this.checkFile(directories, fileNameList[i]);
|
|
391
|
-
console.log(`checkFiles pathName: ${pathName}`);
|
|
392
|
-
// If 'pathName' is not found, it is replaced by an identifier for an empty sound (e.g., silence)
|
|
393
|
-
pathNameList.push(pathName === "" ? "__empty_sound__" : pathName);
|
|
394
|
-
}
|
|
395
|
-
return pathNameList;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Get the channels and length values of the given sound resource.
|
|
400
|
-
*
|
|
401
|
-
* @param pathName : the name of the file, or sound resource identified this way
|
|
402
|
-
* @returns channels and length of the soundfile
|
|
403
|
-
*/
|
|
404
|
-
private async getParamsFile(pathName: string): Promise<{ channels: number, length: number }> {
|
|
405
|
-
console.log(`Loading sound file from ${pathName}`);
|
|
406
|
-
|
|
407
|
-
const item = this.fAudioBuffers.find((element: AudioBufferItem) => element.pathName === pathName);
|
|
408
|
-
if (item) {
|
|
409
|
-
console.log(`getItemByPathName FOUND`);
|
|
410
|
-
return { channels: item.audioBuffer.numberOfChannels, length: item.audioBuffer.length };
|
|
411
|
-
} else {
|
|
412
|
-
const response = await fetch(pathName);
|
|
413
|
-
if (!response.ok) {
|
|
414
|
-
console.log(`Failed to load sound file from ${pathName}: ${response.statusText}`);
|
|
415
|
-
return { channels: 1, length: BUFFER_SIZE };
|
|
416
|
-
} else {
|
|
417
|
-
|
|
418
|
-
// Decode the audio data
|
|
419
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
420
|
-
const audioBuffer = await this.fContext.decodeAudioData(arrayBuffer);
|
|
421
|
-
const { numberOfChannels, length } = audioBuffer;
|
|
422
|
-
|
|
423
|
-
// Keep the audio buffer for later use
|
|
424
|
-
this.fAudioBuffers.push({ pathName, audioBuffer });
|
|
425
|
-
|
|
426
|
-
// Ensure the returned object keys match what's being returned
|
|
427
|
-
return { channels: numberOfChannels, length };
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* Read one sound resource and fill the 'soundfile' structure accordingly
|
|
434
|
-
*
|
|
435
|
-
* @param soundfile - the soundfile to be filled
|
|
436
|
-
* @param pathName - the name of the file, or sound resource identified this way
|
|
437
|
-
* @param part - the part number to be filled in the soundfile
|
|
438
|
-
* @param maxChan - the maximum number of mono channels to fill
|
|
439
|
-
*
|
|
440
|
-
* @returns the offset in the soundfile buffer
|
|
441
|
-
*
|
|
442
|
-
*/
|
|
443
|
-
private readFile(soundfile: Soundfile, pathName: string, part: number, offset: number, maxChan: number): number {
|
|
444
|
-
// Read the soundfile
|
|
445
|
-
const item = this.fAudioBuffers.find(entry => entry.pathName === pathName);
|
|
446
|
-
// Copy the soundfile data to the buffer
|
|
447
|
-
if (item) {
|
|
448
|
-
//soundfile.displayMemory("BEFORE copyToOut");
|
|
449
|
-
soundfile.copyToOut(part, maxChan, offset, item.audioBuffer);
|
|
450
|
-
//soundfile.displayMemory("AFTER copyToOut");
|
|
451
|
-
return offset + item.audioBuffer.length;
|
|
452
|
-
} else {
|
|
453
|
-
console.error(`Failed to access sound file from ${pathName}`);
|
|
454
|
-
return offset + BUFFER_SIZE;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* Crate a soundfile, load all parts and copy audio data to the wasm soundfile buffer.
|
|
460
|
-
* @param pathNameList : list of soundfile paths
|
|
461
|
-
* @param maxChan : maximum number of channels
|
|
462
|
-
*/
|
|
463
|
-
async createSoundfile(pathNameList: string[], maxChan: number): Promise<Soundfile | null> {
|
|
464
|
-
try {
|
|
465
|
-
let curChan = 1; // At least one channel
|
|
466
|
-
let totalLength = 0;
|
|
467
|
-
|
|
468
|
-
// Compute total length and channels max of all files
|
|
469
|
-
for (const pathName of pathNameList) {
|
|
470
|
-
let chan: number = 0, len: number = 0;
|
|
471
|
-
if (pathName === "__empty_sound__") {
|
|
472
|
-
length = BUFFER_SIZE;
|
|
473
|
-
chan = 1;
|
|
474
|
-
} else {
|
|
475
|
-
const { channels, length } = await this.getParamsFile(pathName);
|
|
476
|
-
chan = channels;
|
|
477
|
-
len = length;
|
|
478
|
-
}
|
|
479
|
-
curChan = Math.max(curChan, chan);
|
|
480
|
-
totalLength += len;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// Complete with empty parts
|
|
484
|
-
totalLength += (MAX_SOUNDFILE_PARTS - pathNameList.length) * BUFFER_SIZE;
|
|
485
|
-
|
|
486
|
-
// Create the soundfile
|
|
487
|
-
let soundfile = new Soundfile(this.fAllocator, this.fSampleSize, curChan, totalLength, maxChan, pathNameList.length);
|
|
488
|
-
|
|
489
|
-
//soundfile.displayMemory("After soundfile creation");
|
|
490
|
-
// Init offset
|
|
491
|
-
let offset = 0;
|
|
492
|
-
|
|
493
|
-
// Read all files
|
|
494
|
-
for (let part = 0; part < pathNameList.length; part++) {
|
|
495
|
-
if (pathNameList[part] === "__empty_sound__") {
|
|
496
|
-
// Empty sound
|
|
497
|
-
offset = soundfile.emptyFile(part, offset);
|
|
498
|
-
} else {
|
|
499
|
-
// Read the soundfile and update the offset
|
|
500
|
-
offset = await this.readFile(soundfile, pathNameList[part], part, offset, maxChan);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
//soundfile.displayMemory("After reading soundfiles");
|
|
505
|
-
|
|
506
|
-
// Complete with empty parts
|
|
507
|
-
for (let part = pathNameList.length; part < MAX_SOUNDFILE_PARTS; part++) {
|
|
508
|
-
offset = soundfile.emptyFile(part, offset);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
//soundfile.displayMemory("After emptyFile");
|
|
512
|
-
|
|
513
|
-
// Share the same buffers for all other channels so that we have maxChan channels available
|
|
514
|
-
soundfile.shareBuffers(curChan, maxChan);
|
|
515
|
-
|
|
516
|
-
//soundfile.displayMemory("After shareBuffers");
|
|
517
|
-
|
|
518
|
-
return soundfile;
|
|
519
|
-
|
|
520
|
-
} catch (error) {
|
|
521
|
-
console.error("Failed to create soundfile:", error);
|
|
522
|
-
return null;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
getHEAP32(): Int32Array {
|
|
527
|
-
return this.fAllocator.getInt32Array();
|
|
528
|
-
}
|
|
529
332
|
}
|
|
530
333
|
|
|
531
334
|
/**
|
|
@@ -729,36 +532,30 @@ export interface IFaustPolyWebAudioDsp extends IFaustBaseWebAudioDsp {
|
|
|
729
532
|
}
|
|
730
533
|
export interface IFaustPolyWebAudioNode extends IFaustPolyWebAudioDsp, AudioNode { }
|
|
731
534
|
|
|
732
|
-
// Definition of the SoundfileItem type
|
|
733
|
-
type SoundfileItem = {
|
|
734
|
-
name: string; // Name of the soundfile
|
|
735
|
-
url: string; // URL of the soundfile
|
|
736
|
-
index: number; // Index in the DSP struct
|
|
737
|
-
basePtr: number; // Base pointer in wasm memory
|
|
738
|
-
};
|
|
739
|
-
|
|
740
535
|
export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
741
|
-
protected fOutputHandler: OutputParamHandler | null;
|
|
742
|
-
protected fComputeHandler: ComputeHandler | null;
|
|
536
|
+
protected fOutputHandler: OutputParamHandler | null = null;
|
|
537
|
+
protected fComputeHandler: ComputeHandler | null = null;
|
|
743
538
|
|
|
744
539
|
// To handle MIDI events plot
|
|
745
|
-
protected fPlotHandler: PlotHandler | null;
|
|
746
|
-
protected fCachedEvents: { type: string; data: any }[];
|
|
747
|
-
protected fBufferNum
|
|
540
|
+
protected fPlotHandler: PlotHandler | null = null;
|
|
541
|
+
protected fCachedEvents: { type: string; data: any }[] = [];
|
|
542
|
+
protected fBufferNum = 0;
|
|
748
543
|
|
|
749
|
-
protected fInChannels: Float32Array[] | Float64Array[];
|
|
750
|
-
protected fOutChannels: Float32Array[] | Float64Array[];
|
|
544
|
+
protected fInChannels: Float32Array[] | Float64Array[] = [];
|
|
545
|
+
protected fOutChannels: Float32Array[] | Float64Array[] = [];
|
|
751
546
|
|
|
752
|
-
protected fOutputsTimer
|
|
547
|
+
protected fOutputsTimer = 5;
|
|
753
548
|
|
|
754
549
|
// UI items path
|
|
755
|
-
protected fInputsItems: string[];
|
|
756
|
-
protected fOutputsItems: string[];
|
|
757
|
-
protected fDescriptor: FaustUIInputItem[];
|
|
550
|
+
protected fInputsItems: string[] = [];
|
|
551
|
+
protected fOutputsItems: string[] = [];
|
|
552
|
+
protected fDescriptor: FaustUIInputItem[] = [];
|
|
758
553
|
|
|
759
554
|
// Soundfile handling
|
|
760
|
-
protected fSoundfiles: SoundfileItem[];
|
|
761
|
-
protected
|
|
555
|
+
protected fSoundfiles: SoundfileItem[] = [];
|
|
556
|
+
protected fSoundfileBuffers: LooseFaustDspFactory["soundfiles"] = {};
|
|
557
|
+
/** Keep the end of memory offset before soundfiles */
|
|
558
|
+
protected fEndMemory: number;
|
|
762
559
|
|
|
763
560
|
// Buffers in wasm memory
|
|
764
561
|
protected fAudioInputs!: number;
|
|
@@ -769,78 +566,50 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
769
566
|
protected fSampleSize: number;
|
|
770
567
|
|
|
771
568
|
// MIDI handling
|
|
772
|
-
protected fPitchwheelLabel: { path: string; min: number; max: number }[];
|
|
773
|
-
protected fCtrlLabel: { path: string; min: number; max: number }[][];
|
|
774
|
-
protected fPathTable: { [address: string]: number };
|
|
775
|
-
protected fUICallback: UIHandler
|
|
569
|
+
protected fPitchwheelLabel: { path: string; min: number; max: number }[] = [];
|
|
570
|
+
protected fCtrlLabel: { path: string; min: number; max: number }[][] = new Array(128).fill(null).map(() => []);
|
|
571
|
+
protected fPathTable: { [address: string]: number } = {};
|
|
572
|
+
protected fUICallback: UIHandler = (item: FaustUIItem) => {
|
|
573
|
+
if (item.type === "hbargraph" || item.type === "vbargraph") {
|
|
574
|
+
// Keep bargraph adresses
|
|
575
|
+
this.fOutputsItems.push(item.address);
|
|
576
|
+
this.fPathTable[item.address] = item.index;
|
|
577
|
+
} else if (item.type === "vslider" || item.type === "hslider" || item.type === "button" || item.type === "checkbox" || item.type === "nentry") {
|
|
578
|
+
// Keep inputs adresses
|
|
579
|
+
this.fInputsItems.push(item.address);
|
|
580
|
+
this.fPathTable[item.address] = item.index;
|
|
581
|
+
this.fDescriptor.push(item);
|
|
582
|
+
// Parse 'midi' metadata
|
|
583
|
+
if (!item.meta) return;
|
|
584
|
+
item.meta.forEach((meta) => {
|
|
585
|
+
const { midi } = meta;
|
|
586
|
+
if (!midi) return;
|
|
587
|
+
const strMidi = midi.trim();
|
|
588
|
+
if (strMidi === "pitchwheel") {
|
|
589
|
+
this.fPitchwheelLabel.push({ path: item.address, min: item.min as number, max: item.max as number });
|
|
590
|
+
} else {
|
|
591
|
+
const matched = strMidi.match(/^ctrl\s(\d+)/);
|
|
592
|
+
if (!matched) return;
|
|
593
|
+
this.fCtrlLabel[parseInt(matched[1])].push({ path: item.address, min: item.min as number, max: item.max as number });
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
} else if (item.type === "soundfile") {
|
|
597
|
+
this.fSoundfiles.push({ name: item.label, url: item.url, index: item.index, basePtr: -1 });
|
|
598
|
+
}
|
|
599
|
+
};
|
|
776
600
|
|
|
777
601
|
// Audio callback
|
|
778
|
-
protected fProcessing
|
|
779
|
-
protected fDestroyed
|
|
780
|
-
protected fFirstCall
|
|
602
|
+
protected fProcessing = false;
|
|
603
|
+
protected fDestroyed = false;
|
|
604
|
+
protected fFirstCall = true;
|
|
781
605
|
|
|
782
606
|
protected fJSONDsp!: FaustDspMeta;
|
|
783
607
|
|
|
784
|
-
constructor(sampleSize: number, bufferSize: number) {
|
|
785
|
-
this.fOutputHandler = null;
|
|
786
|
-
this.fComputeHandler = null;
|
|
787
|
-
|
|
788
|
-
// To handle MIDI events plot
|
|
789
|
-
this.fCachedEvents = [];
|
|
790
|
-
this.fBufferNum = 0;
|
|
791
|
-
this.fPlotHandler = null;
|
|
792
|
-
|
|
608
|
+
constructor(sampleSize: number, bufferSize: number, soundfiles: LooseFaustDspFactory["soundfiles"]) {
|
|
793
609
|
this.fBufferSize = bufferSize;
|
|
794
|
-
|
|
795
|
-
this.fInChannels = [];
|
|
796
|
-
this.fOutChannels = [];
|
|
797
|
-
|
|
798
610
|
this.fPtrSize = sampleSize; // Done on wast/wasm backend side
|
|
799
611
|
this.fSampleSize = sampleSize;
|
|
800
|
-
|
|
801
|
-
this.fOutputsTimer = 5;
|
|
802
|
-
this.fInputsItems = [];
|
|
803
|
-
this.fOutputsItems = [];
|
|
804
|
-
this.fDescriptor = [];
|
|
805
|
-
|
|
806
|
-
this.fSoundfiles = [];
|
|
807
|
-
|
|
808
|
-
this.fPitchwheelLabel = [];
|
|
809
|
-
this.fCtrlLabel = new Array(128).fill(null).map(() => []);
|
|
810
|
-
this.fPathTable = {};
|
|
811
|
-
|
|
812
|
-
this.fProcessing = false;
|
|
813
|
-
this.fDestroyed = false;
|
|
814
|
-
this.fFirstCall = true;
|
|
815
|
-
|
|
816
|
-
this.fUICallback = (item: FaustUIItem) => {
|
|
817
|
-
if (item.type === "hbargraph" || item.type === "vbargraph") {
|
|
818
|
-
// Keep bargraph adresses
|
|
819
|
-
this.fOutputsItems.push(item.address);
|
|
820
|
-
this.fPathTable[item.address] = item.index;
|
|
821
|
-
} else if (item.type === "vslider" || item.type === "hslider" || item.type === "button" || item.type === "checkbox" || item.type === "nentry") {
|
|
822
|
-
// Keep inputs adresses
|
|
823
|
-
this.fInputsItems.push(item.address);
|
|
824
|
-
this.fPathTable[item.address] = item.index;
|
|
825
|
-
this.fDescriptor.push(item);
|
|
826
|
-
// Parse 'midi' metadata
|
|
827
|
-
if (!item.meta) return;
|
|
828
|
-
item.meta.forEach((meta) => {
|
|
829
|
-
const { midi } = meta;
|
|
830
|
-
if (!midi) return;
|
|
831
|
-
const strMidi = midi.trim();
|
|
832
|
-
if (strMidi === "pitchwheel") {
|
|
833
|
-
this.fPitchwheelLabel.push({ path: item.address, min: item.min as number, max: item.max as number });
|
|
834
|
-
} else {
|
|
835
|
-
const matched = strMidi.match(/^ctrl\s(\d+)/);
|
|
836
|
-
if (!matched) return;
|
|
837
|
-
this.fCtrlLabel[parseInt(matched[1])].push({ path: item.address, min: item.min as number, max: item.max as number });
|
|
838
|
-
}
|
|
839
|
-
});
|
|
840
|
-
} else if (item.type === "soundfile") {
|
|
841
|
-
this.fSoundfiles.push({ name: item.label, url: item.url, index: item.index, basePtr: -1 });
|
|
842
|
-
}
|
|
843
|
-
}
|
|
612
|
+
this.fSoundfileBuffers = soundfiles;
|
|
844
613
|
}
|
|
845
614
|
|
|
846
615
|
// Tools
|
|
@@ -870,20 +639,20 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
870
639
|
}
|
|
871
640
|
}
|
|
872
641
|
|
|
873
|
-
|
|
874
|
-
static
|
|
642
|
+
/** Split the soundfile names and return an array of names */
|
|
643
|
+
static splitSoundfileNames(input: string): string[] {
|
|
875
644
|
// Trim off the curly braces at the start and end, if present
|
|
876
645
|
let trimmed = input.replace(/^\{|\}$/g, '');
|
|
877
646
|
// Split the string into an array of strings and remove first and last characters
|
|
878
647
|
return trimmed.split(";").map(str => str.length <= 2 ? '' : str.substring(1, str.length - 1));
|
|
879
648
|
}
|
|
880
649
|
|
|
881
|
-
|
|
650
|
+
static extractUrlsFromMeta(dspMeta: FaustDspMeta): string[] {
|
|
882
651
|
// Find the entry with the "soundfiles" key
|
|
883
|
-
const soundfilesEntry =
|
|
652
|
+
const soundfilesEntry = dspMeta.meta.find(entry => entry.soundfiles !== undefined);
|
|
884
653
|
// If the entry is found, split the string by semicolon to get the URLs
|
|
885
654
|
if (soundfilesEntry) {
|
|
886
|
-
return soundfilesEntry.soundfiles.split(
|
|
655
|
+
return soundfilesEntry.soundfiles.split(";").filter(url => url !== "");
|
|
887
656
|
} else {
|
|
888
657
|
return [];
|
|
889
658
|
}
|
|
@@ -895,66 +664,102 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
895
664
|
* The DSP struct is located at baseDSP in the wasm memory,
|
|
896
665
|
* either a monophonic DSP, or a voice in a polyphonic context.
|
|
897
666
|
*
|
|
898
|
-
* @param
|
|
667
|
+
* @param allocator : the wasm memory allocator
|
|
899
668
|
* @param baseDSP : the base DSP in the wasm memory
|
|
900
669
|
* @param name : the name of the soundfile
|
|
901
670
|
* @param url : the url of the soundfile
|
|
902
671
|
*/
|
|
903
|
-
private
|
|
904
|
-
|
|
672
|
+
private loadSoundfile(allocator: WasmAllocator, baseDSP: number, name: string, url: string) {
|
|
905
673
|
console.log(`Soundfile ${name} paths: ${url}`);
|
|
906
|
-
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
// Use the cached Soundfile
|
|
922
|
-
if (item.basePtr !== -1) {
|
|
923
|
-
|
|
674
|
+
const soundfileIds = FaustBaseWebAudioDsp.splitSoundfileNames(url);
|
|
675
|
+
const item = this.fSoundfiles.find(item => item.url === url);
|
|
676
|
+
if (!item) throw new Error(`Soundfile with ${url} cannot be found !}`);
|
|
677
|
+
// Use the cached Soundfile
|
|
678
|
+
if (item.basePtr !== -1) {
|
|
679
|
+
// Update HEAP32 after soundfile creation
|
|
680
|
+
const HEAP32 = allocator.getInt32Array();
|
|
681
|
+
// Fill the soundfile structure in wasm memory, soundfiles are at the beginning of the DSP memory
|
|
682
|
+
console.log(`Soundfile CACHE ${url}} : ${name} loaded at ${item.basePtr} in wasm memory with index ${item.index}`);
|
|
683
|
+
// Soundfile is located at 'index' in the DSP struct, to be added with baseDSP in the wasm memory
|
|
684
|
+
HEAP32[(baseDSP + item.index) >> 2] = item.basePtr;
|
|
685
|
+
} else {
|
|
686
|
+
// Create the soundfiles
|
|
687
|
+
const soundfile = this.createSoundfile(allocator, soundfileIds, this.fSoundfileBuffers);
|
|
688
|
+
if (soundfile) {
|
|
924
689
|
// Update HEAP32 after soundfile creation
|
|
925
|
-
const HEAP32 =
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
console.log(`Soundfile
|
|
929
|
-
|
|
690
|
+
const HEAP32 = soundfile.getHEAP32();
|
|
691
|
+
// Get the soundfile pointer in wasm memory
|
|
692
|
+
item.basePtr = soundfile.getPtr();
|
|
693
|
+
console.log(`Soundfile ${name} loaded at ${item.basePtr} in wasm memory with index ${item.index}`);
|
|
930
694
|
// Soundfile is located at 'index' in the DSP struct, to be added with baseDSP in the wasm memory
|
|
931
695
|
HEAP32[(baseDSP + item.index) >> 2] = item.basePtr;
|
|
932
|
-
|
|
933
696
|
} else {
|
|
697
|
+
console.log(`Soundfile ${name} for ${url} cannot be created !}`);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
934
701
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
702
|
+
createSoundfile(allocator: WasmAllocator, soundfileIdList: string[], soundfiles: LooseFaustDspFactory["soundfiles"], maxChan = Soundfile.MAX_CHAN) {
|
|
703
|
+
let curChan = 1; // At least one channel
|
|
704
|
+
let totalLength = 0;
|
|
938
705
|
|
|
939
|
-
|
|
940
|
-
|
|
706
|
+
// Compute total length and channels max of all files
|
|
707
|
+
for (const soundfileId of soundfileIdList) {
|
|
708
|
+
let chan = 0;
|
|
709
|
+
let len = 0;
|
|
710
|
+
const audioData = soundfiles[soundfileId];
|
|
711
|
+
if (audioData) {
|
|
712
|
+
chan = audioData.audioBuffer.length;
|
|
713
|
+
len = audioData.audioBuffer[0].length;
|
|
714
|
+
} else {
|
|
715
|
+
len = Soundfile.BUFFER_SIZE;
|
|
716
|
+
chan = 1;
|
|
717
|
+
}
|
|
718
|
+
curChan = Math.max(curChan, chan);
|
|
719
|
+
totalLength += len;
|
|
720
|
+
}
|
|
941
721
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
722
|
+
// Complete with empty parts
|
|
723
|
+
totalLength += (Soundfile.MAX_SOUNDFILE_PARTS - soundfileIdList.length) * Soundfile.BUFFER_SIZE;
|
|
724
|
+
|
|
725
|
+
// Create the soundfile
|
|
726
|
+
const soundfile = new Soundfile(allocator, this.fSampleSize, curChan, totalLength, maxChan, soundfileIdList.length);
|
|
727
|
+
|
|
728
|
+
//soundfile.displayMemory("After soundfile creation");
|
|
729
|
+
// Init offset
|
|
730
|
+
let offset = 0;
|
|
731
|
+
|
|
732
|
+
// Read all files
|
|
733
|
+
for (let part = 0; part < soundfileIdList.length; part++) {
|
|
734
|
+
const soundfileId = soundfileIdList[part];
|
|
735
|
+
const audioData = soundfiles[soundfileId];
|
|
736
|
+
if (audioData) {
|
|
737
|
+
//soundfile.displayMemory("BEFORE copyToOut");
|
|
738
|
+
soundfile.copyToOut(part, maxChan, offset, audioData);
|
|
739
|
+
//soundfile.displayMemory("AFTER copyToOut");
|
|
740
|
+
offset += audioData.audioBuffer[0].length;
|
|
741
|
+
} else {
|
|
742
|
+
// Empty sound
|
|
743
|
+
offset = soundfile.emptyFile(part, offset);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
945
746
|
|
|
946
|
-
|
|
947
|
-
HEAP32[(baseDSP + item.index) >> 2] = item.basePtr;
|
|
747
|
+
//soundfile.displayMemory("After reading soundfiles");
|
|
948
748
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
}
|
|
953
|
-
} else {
|
|
954
|
-
console.log(`Soundfile with ${url} cannot be found !}`);
|
|
749
|
+
// Complete with empty parts
|
|
750
|
+
for (let part = soundfileIdList.length; part < Soundfile.MAX_SOUNDFILE_PARTS; part++) {
|
|
751
|
+
offset = soundfile.emptyFile(part, offset);
|
|
955
752
|
}
|
|
956
|
-
}
|
|
957
753
|
|
|
754
|
+
//soundfile.displayMemory("After emptyFile");
|
|
755
|
+
|
|
756
|
+
// Share the same buffers for all other channels so that we have maxChan channels available
|
|
757
|
+
soundfile.shareBuffers(curChan, maxChan);
|
|
758
|
+
|
|
759
|
+
//soundfile.displayMemory("After shareBuffers");
|
|
760
|
+
|
|
761
|
+
return soundfile;
|
|
762
|
+
}
|
|
958
763
|
/**
|
|
959
764
|
* Init soundfiles memory.
|
|
960
765
|
*
|
|
@@ -962,10 +767,10 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
962
767
|
* @param sfReader : the soundfile reader
|
|
963
768
|
* @param baseDSP : the DSP struct (either a monophonic DSP of polyphonic voice) base DSP in the wasm memory
|
|
964
769
|
*/
|
|
965
|
-
protected
|
|
770
|
+
protected initSoundfileMemory(allocator: WasmAllocator, baseDSP: number) {
|
|
966
771
|
// Create and fill the soundfile structure
|
|
967
772
|
for (const { name, url } of this.fSoundfiles) {
|
|
968
|
-
|
|
773
|
+
this.loadSoundfile(allocator, baseDSP, name, url);
|
|
969
774
|
};
|
|
970
775
|
}
|
|
971
776
|
|
|
@@ -1078,12 +883,12 @@ export class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1078
883
|
private fInstance: FaustMonoDspInstance;
|
|
1079
884
|
private fDSP!: number;
|
|
1080
885
|
|
|
1081
|
-
constructor(instance: FaustMonoDspInstance, sampleRate: number, sampleSize: number, bufferSize: number) {
|
|
886
|
+
constructor(instance: FaustMonoDspInstance, sampleRate: number, sampleSize: number, bufferSize: number, soundfiles: LooseFaustDspFactory["soundfiles"]) {
|
|
1082
887
|
|
|
1083
|
-
super(sampleSize, bufferSize);
|
|
888
|
+
super(sampleSize, bufferSize, soundfiles);
|
|
1084
889
|
this.fInstance = instance;
|
|
1085
890
|
|
|
1086
|
-
console.log(
|
|
891
|
+
console.log(`sampleSize: ${sampleSize} bufferSize: ${bufferSize}`);
|
|
1087
892
|
|
|
1088
893
|
// Create JSON object
|
|
1089
894
|
this.fJSONDsp = JSON.parse(this.fInstance.json);
|
|
@@ -1096,21 +901,15 @@ export class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1096
901
|
|
|
1097
902
|
// Init DSP
|
|
1098
903
|
this.fInstance.api.init(this.fDSP, sampleRate);
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
async init(context: BaseAudioContext | null): Promise<void> {
|
|
1102
904
|
|
|
1103
905
|
// Init soundfiles memory is needed
|
|
1104
|
-
if (this.fSoundfiles.length > 0
|
|
906
|
+
if (this.fSoundfiles.length > 0) {
|
|
1105
907
|
|
|
1106
908
|
// Create memory allocator for soundfiles in wasm memory, starting at the end of DSP memory
|
|
1107
909
|
const allocator = new WasmAllocator(this.fInstance.memory, this.fEndMemory);
|
|
1108
910
|
|
|
1109
|
-
// Create soundfile reader
|
|
1110
|
-
const sfReader = new SoundfileReader(allocator, context, this.fSampleSize);
|
|
1111
|
-
|
|
1112
911
|
// Init soundfiles memory
|
|
1113
|
-
|
|
912
|
+
this.initSoundfileMemory(allocator, this.fDSP);
|
|
1114
913
|
}
|
|
1115
914
|
}
|
|
1116
915
|
|
|
@@ -1265,48 +1064,32 @@ export class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1265
1064
|
}
|
|
1266
1065
|
|
|
1267
1066
|
export class FaustWebAudioDspVoice {
|
|
1268
|
-
|
|
1269
|
-
static kActiveVoice
|
|
1270
|
-
static kFreeVoice
|
|
1271
|
-
static kReleaseVoice
|
|
1272
|
-
static kLegatoVoice
|
|
1273
|
-
static kNoVoice
|
|
1274
|
-
static VOICE_STOP_LEVEL
|
|
1275
|
-
|
|
1276
|
-
private
|
|
1277
|
-
private
|
|
1278
|
-
private
|
|
1279
|
-
private
|
|
1067
|
+
// Voice state
|
|
1068
|
+
static get kActiveVoice() { return 0; }
|
|
1069
|
+
static get kFreeVoice() { return -1; }
|
|
1070
|
+
static get kReleaseVoice() { return -2; }
|
|
1071
|
+
static get kLegatoVoice() { return -3; }
|
|
1072
|
+
static get kNoVoice() { return -4; }
|
|
1073
|
+
static get VOICE_STOP_LEVEL() { return 0.0005; }
|
|
1074
|
+
|
|
1075
|
+
private fFreqLabel: number[] = [];
|
|
1076
|
+
private fGateLabel: number[] = [];
|
|
1077
|
+
private fGainLabel: number[] = [];
|
|
1078
|
+
private fKeyLabel: number[] = [];
|
|
1079
|
+
private fVelLabel: number[] = [];
|
|
1280
1080
|
private fDSP: number; // Voice DSP location in wasm memory
|
|
1281
1081
|
private fAPI: IFaustDspInstance; // Voice DSP code
|
|
1282
1082
|
// Accessed by PolyDSPImp class
|
|
1283
|
-
fCurNote
|
|
1284
|
-
fNextNote
|
|
1285
|
-
fNextVel
|
|
1286
|
-
fDate
|
|
1287
|
-
fLevel
|
|
1288
|
-
fRelease
|
|
1083
|
+
fCurNote = FaustWebAudioDspVoice.kFreeVoice;
|
|
1084
|
+
fNextNote = -1;
|
|
1085
|
+
fNextVel = -1;
|
|
1086
|
+
fDate = 0;
|
|
1087
|
+
fLevel = 0;
|
|
1088
|
+
fRelease = 0;
|
|
1289
1089
|
|
|
1290
1090
|
constructor($dsp: number, api: IFaustDspInstance, inputItems: string[], pathTable: { [address: string]: number }, sampleRate: number) {
|
|
1291
|
-
// Voice state
|
|
1292
|
-
FaustWebAudioDspVoice.kActiveVoice = 0;
|
|
1293
|
-
FaustWebAudioDspVoice.kFreeVoice = -1;
|
|
1294
|
-
FaustWebAudioDspVoice.kReleaseVoice = -2;
|
|
1295
|
-
FaustWebAudioDspVoice.kLegatoVoice = -3;
|
|
1296
|
-
FaustWebAudioDspVoice.kNoVoice = -4;
|
|
1297
|
-
FaustWebAudioDspVoice.VOICE_STOP_LEVEL = 0.0005;
|
|
1298
|
-
|
|
1299
|
-
this.fCurNote = FaustWebAudioDspVoice.kFreeVoice;
|
|
1300
|
-
this.fNextNote = this.fNextVel = -1;
|
|
1301
|
-
this.fLevel = 0;
|
|
1302
|
-
this.fDate = this.fRelease = 0;
|
|
1303
1091
|
this.fDSP = $dsp;
|
|
1304
1092
|
this.fAPI = api;
|
|
1305
|
-
this.fGateLabel = [];
|
|
1306
|
-
this.fGainLabel = [];
|
|
1307
|
-
this.fFreqLabel = [];
|
|
1308
|
-
this.fKeyLabel = [];
|
|
1309
|
-
this.fVelLabel = [];
|
|
1310
1093
|
this.fAPI.init(this.fDSP, sampleRate);
|
|
1311
1094
|
this.extractPaths(inputItems, pathTable);
|
|
1312
1095
|
}
|
|
@@ -1395,11 +1178,11 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1395
1178
|
private fAudioMixingHalf!: number;
|
|
1396
1179
|
private fVoiceTable: FaustWebAudioDspVoice[];
|
|
1397
1180
|
|
|
1398
|
-
constructor(instance: FaustPolyDspInstance, sampleRate: number, sampleSize: number, bufferSize: number) {
|
|
1399
|
-
super(sampleSize, bufferSize);
|
|
1181
|
+
constructor(instance: FaustPolyDspInstance, sampleRate: number, sampleSize: number, bufferSize: number, soundfiles: LooseFaustDspFactory["soundfiles"]) {
|
|
1182
|
+
super(sampleSize, bufferSize, soundfiles);
|
|
1400
1183
|
this.fInstance = instance;
|
|
1401
1184
|
|
|
1402
|
-
console.log(
|
|
1185
|
+
console.log(`sampleSize: ${sampleSize} bufferSize: ${bufferSize}`);
|
|
1403
1186
|
|
|
1404
1187
|
// Create JSON for voice
|
|
1405
1188
|
this.fJSONDsp = JSON.parse(this.fInstance.voiceJSON);
|
|
@@ -1428,22 +1211,14 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1428
1211
|
|
|
1429
1212
|
// Init effect
|
|
1430
1213
|
if (this.fInstance.effectAPI) this.fInstance.effectAPI.init(this.fEffect, sampleRate);
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
async init(context: BaseAudioContext | null): Promise<void> {
|
|
1434
1214
|
|
|
1435
1215
|
// Init soundfiles memory is needed
|
|
1436
|
-
if (this.fSoundfiles.length > 0
|
|
1437
|
-
|
|
1216
|
+
if (this.fSoundfiles.length > 0) {
|
|
1438
1217
|
// Create memory allocator for soundfiles in wasm memory, starting at the end of DSP memory
|
|
1439
1218
|
const allocator = new WasmAllocator(this.fInstance.memory, this.fEndMemory);
|
|
1440
|
-
|
|
1441
|
-
// Create soundfile reader
|
|
1442
|
-
const sfReader = new SoundfileReader(allocator, context, this.fSampleSize);
|
|
1443
|
-
|
|
1444
1219
|
// Init soundfiles memory for all voices
|
|
1445
1220
|
for (let voice = 0; voice < this.fInstance.voices; voice++) {
|
|
1446
|
-
|
|
1221
|
+
this.initSoundfileMemory(allocator, this.fJSONDsp.size * voice);
|
|
1447
1222
|
}
|
|
1448
1223
|
}
|
|
1449
1224
|
}
|