@grame/faustwasm 0.12.2 → 0.13.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/.prettierrc +5 -0
- package/README.md +21 -3
- package/assets/standalone/faustwasm/index.d.ts +38 -16
- package/assets/standalone/faustwasm/index.js +1574 -404
- package/assets/standalone/faustwasm/index.js.map +2 -2
- package/dist/cjs/index.d.ts +38 -16
- package/dist/cjs/index.js +1573 -404
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs-bundle/index.d.ts +38 -16
- package/dist/cjs-bundle/index.js +1576 -406
- package/dist/cjs-bundle/index.js.map +2 -2
- package/dist/esm/index.d.ts +38 -16
- package/dist/esm/index.js +1574 -404
- package/dist/esm/index.js.map +2 -2
- package/dist/esm-bundle/index.d.ts +38 -16
- package/dist/esm-bundle/index.js +1576 -406
- package/dist/esm-bundle/index.js.map +2 -2
- package/eslint.config.js +11 -0
- package/package.json +57 -51
- package/sound.cpp +163 -0
- package/sound.dsp +4 -0
- package/sound.json +1 -0
- package/sound.wasm +0 -0
- package/src/FaustAudioWorkletCommunicator.ts +150 -143
- package/src/FaustAudioWorkletNode.ts +173 -75
- package/src/FaustAudioWorkletProcessor.ts +218 -90
- package/src/FaustCmajor.ts +9 -3
- package/src/FaustCompiler.ts +112 -35
- package/src/FaustDspGenerator.ts +561 -117
- package/src/FaustDspInstance.ts +58 -24
- package/src/FaustFFTAudioWorkletProcessor.ts +822 -612
- package/src/FaustOfflineProcessor.ts +187 -56
- package/src/FaustScriptProcessorNode.ts +156 -49
- package/src/FaustSensors.ts +426 -96
- package/src/FaustSvgDiagrams.ts +18 -5
- package/src/FaustWasmInstantiator.ts +224 -70
- package/src/FaustWebAudioDsp.ts +1060 -349
- package/src/LibFaust.ts +13 -4
- package/src/SoundfileReader.ts +175 -131
- package/src/WavDecoder.ts +42 -20
- package/src/WavEncoder.ts +35 -21
- package/src/copyWebStandaloneAssets.d.ts +20 -5
- package/src/copyWebStandaloneAssets.js +203 -75
- package/src/exports-bundle.ts +5 -5
- package/src/exports.ts +24 -24
- package/src/faust2CmajorFiles.d.ts +7 -3
- package/src/faust2cmajorFiles.js +10 -8
- package/src/faust2svgFiles.d.ts +7 -3
- package/src/faust2svgFiles.js +9 -7
- package/src/faust2wasmFiles.d.ts +10 -5
- package/src/faust2wasmFiles.js +34 -17
- package/src/faust2wavFiles.d.ts +12 -3
- package/src/faust2wavFiles.js +25 -12
- package/src/index-bundle-iife.ts +2 -2
- package/src/index-bundle.ts +1 -1
- package/src/index.ts +1 -1
- package/src/instantiateFaustModule.ts +43 -36
- package/src/instantiateFaustModuleFromFile.ts +30 -14
- package/src/types.ts +101 -25
- package/test/faustlive-wasm/faustwasm/index.d.ts +38 -16
- package/test/faustlive-wasm/faustwasm/index.js +1574 -404
- package/test/faustlive-wasm/faustwasm/index.js.map +2 -2
- package/test/web/create-faust-node.html +53 -0
- package/test/web/create-faust-node.js +186 -0
- package/test/organ/create-node.js +0 -252
- package/test/organ/dsp-meta.json +0 -131
- package/test/organ/dsp-module.wasm +0 -0
- package/test/organ/faust-pwa.js +0 -344
- package/test/organ/faust-ui/index.css +0 -442
- package/test/organ/faust-ui/index.css.map +0 -1
- package/test/organ/faust-ui/index.d.ts +0 -1
- package/test/organ/faust-ui/index.js +0 -3756
- package/test/organ/faust-ui/index.js.map +0 -1
- package/test/organ/faustwasm/index.d.ts +0 -1705
- package/test/organ/faustwasm/index.js +0 -4773
- package/test/organ/faustwasm/index.js.map +0 -7
- package/test/organ/icon.png +0 -0
- package/test/organ/index.html +0 -76
- package/test/organ/index.js +0 -69
- package/test/organ/manifest.json +0 -17
- package/test/organ/service-worker.js +0 -165
package/src/FaustWebAudioDsp.ts
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type {
|
|
2
|
+
FaustMonoDspInstance,
|
|
3
|
+
FaustPolyDspInstance,
|
|
4
|
+
IFaustDspInstance
|
|
5
|
+
} from './FaustDspInstance';
|
|
6
|
+
import type {
|
|
7
|
+
AudioData,
|
|
8
|
+
FaustDspMeta,
|
|
9
|
+
FaustUIDescriptor,
|
|
10
|
+
FaustUIGroup,
|
|
11
|
+
FaustUIInputItem,
|
|
12
|
+
FaustUIItem,
|
|
13
|
+
LooseFaustDspFactory
|
|
14
|
+
} from './types';
|
|
15
|
+
import FaustSensors, {
|
|
16
|
+
Axis,
|
|
17
|
+
Curve,
|
|
18
|
+
UpdatableValueConverter
|
|
19
|
+
} from './FaustSensors';
|
|
4
20
|
|
|
5
21
|
// Public API
|
|
6
22
|
export type OutputParamHandler = (path: string, value: number) => void;
|
|
7
23
|
export type ComputeHandler = (buffer_size: number) => void;
|
|
8
|
-
export type PlotHandler = (
|
|
24
|
+
export type PlotHandler = (
|
|
25
|
+
plotted: Float32Array[] | Float64Array[],
|
|
26
|
+
index: number,
|
|
27
|
+
events?: { type: string; data: any }[]
|
|
28
|
+
) => void;
|
|
9
29
|
export type MetadataHandler = (key: string, value: string) => void;
|
|
10
30
|
|
|
11
31
|
// Implementation API
|
|
@@ -25,7 +45,7 @@ export type SensorEventHandlers = {
|
|
|
25
45
|
export interface AudioBufferItem {
|
|
26
46
|
pathName: string;
|
|
27
47
|
audioBuffer: AudioBuffer;
|
|
28
|
-
}
|
|
48
|
+
}
|
|
29
49
|
|
|
30
50
|
/** Definition of the SoundfileItem type */
|
|
31
51
|
export interface SoundfileItem {
|
|
@@ -37,13 +57,13 @@ export interface SoundfileItem {
|
|
|
37
57
|
index: number;
|
|
38
58
|
/** Base pointer in wasm memory */
|
|
39
59
|
basePtr: number;
|
|
40
|
-
}
|
|
60
|
+
}
|
|
41
61
|
|
|
42
62
|
/**
|
|
43
63
|
* WasmAllocator is a basic memory management class designed to allocate
|
|
44
64
|
* blocks of memory within a WebAssembly.Memory object. It provides a simple
|
|
45
65
|
* alloc method to allocate a contiguous block of memory of a specified size.
|
|
46
|
-
*
|
|
66
|
+
*
|
|
47
67
|
* The allocator operates by keeping a linear progression through the memory,
|
|
48
68
|
* always allocating the next block at the end of the last. This approach does not
|
|
49
69
|
* handle freeing of memory or reuse of memory spaces.
|
|
@@ -65,7 +85,7 @@ export class WasmAllocator {
|
|
|
65
85
|
* Allocates a block of memory of the specified size, returning the pointer to the
|
|
66
86
|
* beginning of the block. The block is allocated at the current offset and the
|
|
67
87
|
* offset is incremented by the size of the block.
|
|
68
|
-
*
|
|
88
|
+
*
|
|
69
89
|
* @param sizeInBytes The size of the block to allocate in bytes.
|
|
70
90
|
* @returns The offset (pointer) to the beginning of the allocated block.
|
|
71
91
|
*/
|
|
@@ -81,7 +101,9 @@ export class WasmAllocator {
|
|
|
81
101
|
if (newOffset > totalMemoryBytes) {
|
|
82
102
|
// Calculate the number of WebAssembly pages needed to fit the new allocation.
|
|
83
103
|
// WebAssembly memory pages are 64KiB each.
|
|
84
|
-
const neededPages = Math.ceil(
|
|
104
|
+
const neededPages = Math.ceil(
|
|
105
|
+
(newOffset - totalMemoryBytes) / 65536
|
|
106
|
+
);
|
|
85
107
|
// Grow the memory by the required number of pages.
|
|
86
108
|
console.log(`GROW: ${neededPages} pages`);
|
|
87
109
|
this.memory.grow(neededPages);
|
|
@@ -95,7 +117,7 @@ export class WasmAllocator {
|
|
|
95
117
|
|
|
96
118
|
/**
|
|
97
119
|
* Returns the underlying buffer object.
|
|
98
|
-
*
|
|
120
|
+
*
|
|
99
121
|
* @returns The buffer object.
|
|
100
122
|
*/
|
|
101
123
|
getBuffer(): ArrayBuffer {
|
|
@@ -104,7 +126,7 @@ export class WasmAllocator {
|
|
|
104
126
|
|
|
105
127
|
/**
|
|
106
128
|
* Returns the Int32 view of the underlying buffer object.
|
|
107
|
-
*
|
|
129
|
+
*
|
|
108
130
|
* @returns The view of the memory buffer as Int32Array.
|
|
109
131
|
*/
|
|
110
132
|
getInt32Array(): Int32Array {
|
|
@@ -113,7 +135,7 @@ export class WasmAllocator {
|
|
|
113
135
|
|
|
114
136
|
/**
|
|
115
137
|
* Returns the Int64 view of the underlying buffer object.
|
|
116
|
-
*
|
|
138
|
+
*
|
|
117
139
|
* @returns The view of the memory buffer as BigInt64Array.
|
|
118
140
|
*/
|
|
119
141
|
getInt64Array(): BigInt64Array {
|
|
@@ -122,7 +144,7 @@ export class WasmAllocator {
|
|
|
122
144
|
|
|
123
145
|
/**
|
|
124
146
|
* Returns the Float32 view of the underlying buffer object.
|
|
125
|
-
*
|
|
147
|
+
*
|
|
126
148
|
* @returns The view of the memory buffer as Float32Array.
|
|
127
149
|
*/
|
|
128
150
|
getFloat32Array(): Float32Array {
|
|
@@ -131,7 +153,7 @@ export class WasmAllocator {
|
|
|
131
153
|
|
|
132
154
|
/**
|
|
133
155
|
* Returns the Float64 view of the underlying buffer object..
|
|
134
|
-
*
|
|
156
|
+
*
|
|
135
157
|
* @returns The view of the memory buffer as Float64Array.
|
|
136
158
|
*/
|
|
137
159
|
getFloat64Array(): Float64Array {
|
|
@@ -144,16 +166,24 @@ export class WasmAllocator {
|
|
|
144
166
|
*/
|
|
145
167
|
export class Soundfile {
|
|
146
168
|
/** Maximum number of soundfile parts. */
|
|
147
|
-
static get MAX_SOUNDFILE_PARTS() {
|
|
169
|
+
static get MAX_SOUNDFILE_PARTS() {
|
|
170
|
+
return 256;
|
|
171
|
+
}
|
|
148
172
|
|
|
149
173
|
/** Maximum number of channels. */
|
|
150
|
-
static get MAX_CHAN() {
|
|
174
|
+
static get MAX_CHAN() {
|
|
175
|
+
return 64;
|
|
176
|
+
}
|
|
151
177
|
|
|
152
178
|
/** Maximum buffer size in frames. */
|
|
153
|
-
static get BUFFER_SIZE() {
|
|
179
|
+
static get BUFFER_SIZE() {
|
|
180
|
+
return 1024;
|
|
181
|
+
}
|
|
154
182
|
|
|
155
183
|
/** Default sample rate. */
|
|
156
|
-
static get SAMPLE_RATE() {
|
|
184
|
+
static get SAMPLE_RATE() {
|
|
185
|
+
return 44100;
|
|
186
|
+
}
|
|
157
187
|
|
|
158
188
|
/** Pointer to the soundfile structure in wasm memory */
|
|
159
189
|
private readonly fPtr: number;
|
|
@@ -166,8 +196,14 @@ export class Soundfile {
|
|
|
166
196
|
private readonly fIntSize: number;
|
|
167
197
|
private readonly fAllocator: WasmAllocator;
|
|
168
198
|
|
|
169
|
-
constructor(
|
|
170
|
-
|
|
199
|
+
constructor(
|
|
200
|
+
allocator: WasmAllocator,
|
|
201
|
+
sampleSize: number,
|
|
202
|
+
curChan: number,
|
|
203
|
+
length: number,
|
|
204
|
+
maxChan: number,
|
|
205
|
+
totalParts: number
|
|
206
|
+
) {
|
|
171
207
|
this.fSampleSize = sampleSize;
|
|
172
208
|
|
|
173
209
|
// To be coherent with the code generated by the wast/wasm backends:
|
|
@@ -175,19 +211,27 @@ export class Soundfile {
|
|
|
175
211
|
// - that uses 8 bytes for int when double is used (to simplify the code generation)
|
|
176
212
|
this.fIntSize = this.fSampleSize;
|
|
177
213
|
|
|
178
|
-
this.fPtrSize = 4;
|
|
214
|
+
this.fPtrSize = 4; // Not related to float/double choice, so always 4
|
|
179
215
|
|
|
180
216
|
this.fAllocator = allocator;
|
|
181
217
|
|
|
182
|
-
console.log(
|
|
218
|
+
console.log(
|
|
219
|
+
`Soundfile constructor: curChan: ${curChan}, length: ${length}, maxChan: ${maxChan}, totalParts: ${totalParts}`
|
|
220
|
+
);
|
|
183
221
|
|
|
184
222
|
// Allocate wasm memory for the soundfile structure
|
|
185
223
|
this.fPtr = allocator.alloc(4 * this.fPtrSize); // 4 fPtrSize: fBuffers, fLength, fSR, fOffset
|
|
186
224
|
|
|
187
225
|
// Use the 4 or 8 bytes size for int. The access are then adapted in copyToOut and emptyFile methods
|
|
188
|
-
this.fLength = allocator.alloc(
|
|
189
|
-
|
|
190
|
-
|
|
226
|
+
this.fLength = allocator.alloc(
|
|
227
|
+
Soundfile.MAX_SOUNDFILE_PARTS * this.fIntSize
|
|
228
|
+
);
|
|
229
|
+
this.fSR = allocator.alloc(
|
|
230
|
+
Soundfile.MAX_SOUNDFILE_PARTS * this.fIntSize
|
|
231
|
+
);
|
|
232
|
+
this.fOffset = allocator.alloc(
|
|
233
|
+
Soundfile.MAX_SOUNDFILE_PARTS * this.fIntSize
|
|
234
|
+
);
|
|
191
235
|
|
|
192
236
|
this.fBuffers = this.allocBuffers(curChan, length, maxChan);
|
|
193
237
|
|
|
@@ -197,8 +241,8 @@ export class Soundfile {
|
|
|
197
241
|
const HEAP32 = this.fAllocator.getInt32Array();
|
|
198
242
|
HEAP32[this.fPtr >> 2] = this.fBuffers;
|
|
199
243
|
HEAP32[(this.fPtr + this.fPtrSize) >> 2] = this.fLength;
|
|
200
|
-
HEAP32[(this.fPtr +
|
|
201
|
-
HEAP32[(this.fPtr +
|
|
244
|
+
HEAP32[(this.fPtr + 2 * this.fPtrSize) >> 2] = this.fSR;
|
|
245
|
+
HEAP32[(this.fPtr + 3 * this.fPtrSize) >> 2] = this.fOffset;
|
|
202
246
|
|
|
203
247
|
for (let chan = 0; chan < curChan; chan++) {
|
|
204
248
|
const buffer: number = HEAP32[(this.fBuffers >> 2) + chan];
|
|
@@ -208,13 +252,19 @@ export class Soundfile {
|
|
|
208
252
|
//this.displayMemory("Allocated soundfile structure 2");
|
|
209
253
|
}
|
|
210
254
|
|
|
211
|
-
private allocBuffers(
|
|
255
|
+
private allocBuffers(
|
|
256
|
+
curChan: number,
|
|
257
|
+
length: number,
|
|
258
|
+
maxChan: number
|
|
259
|
+
): number {
|
|
212
260
|
const buffers = this.fAllocator.alloc(maxChan * this.fPtrSize);
|
|
213
261
|
|
|
214
262
|
console.log(`allocBuffers buffers: ${buffers}`);
|
|
215
263
|
|
|
216
264
|
for (let chan = 0; chan < curChan; chan++) {
|
|
217
|
-
const buffer: number = this.fAllocator.alloc(
|
|
265
|
+
const buffer: number = this.fAllocator.alloc(
|
|
266
|
+
length * this.fSampleSize
|
|
267
|
+
);
|
|
218
268
|
// HEAP32 is the Int32Array view of the memory buffer which can change after grow in `alloc` method
|
|
219
269
|
// so we need to recompute the buffer address
|
|
220
270
|
const HEAP32 = this.fAllocator.getInt32Array();
|
|
@@ -228,25 +278,40 @@ export class Soundfile {
|
|
|
228
278
|
// Share the same buffers for all other channels so that we have maxChan channels available
|
|
229
279
|
const HEAP32 = this.fAllocator.getInt32Array();
|
|
230
280
|
for (let chan = curChan; chan < maxChan; chan++) {
|
|
231
|
-
HEAP32[(this.fBuffers >> 2) + chan] =
|
|
281
|
+
HEAP32[(this.fBuffers >> 2) + chan] =
|
|
282
|
+
HEAP32[(this.fBuffers >> 2) + (chan % curChan)];
|
|
232
283
|
}
|
|
233
284
|
}
|
|
234
285
|
|
|
235
|
-
copyToOut(
|
|
286
|
+
copyToOut(
|
|
287
|
+
part: number,
|
|
288
|
+
maxChannels: number,
|
|
289
|
+
offset: number,
|
|
290
|
+
audioData: AudioData
|
|
291
|
+
) {
|
|
236
292
|
// Set the soundfile fields in wasm memory
|
|
237
293
|
if (this.fIntSize === 4) {
|
|
238
294
|
const HEAP32 = this.fAllocator.getInt32Array();
|
|
239
|
-
HEAP32[(this.fLength >> Math.log2(this.fIntSize)) + part] =
|
|
240
|
-
|
|
295
|
+
HEAP32[(this.fLength >> Math.log2(this.fIntSize)) + part] =
|
|
296
|
+
audioData.audioBuffer[0].length;
|
|
297
|
+
HEAP32[(this.fSR >> Math.log2(this.fIntSize)) + part] =
|
|
298
|
+
audioData.sampleRate;
|
|
241
299
|
HEAP32[(this.fOffset >> Math.log2(this.fIntSize)) + part] = offset;
|
|
242
300
|
} else {
|
|
243
301
|
const HEAP64 = this.fAllocator.getInt64Array();
|
|
244
|
-
HEAP64[(this.fLength >> Math.log2(this.fIntSize)) + part] = BigInt(
|
|
245
|
-
|
|
246
|
-
|
|
302
|
+
HEAP64[(this.fLength >> Math.log2(this.fIntSize)) + part] = BigInt(
|
|
303
|
+
audioData.audioBuffer[0].length
|
|
304
|
+
);
|
|
305
|
+
HEAP64[(this.fSR >> Math.log2(this.fIntSize)) + part] = BigInt(
|
|
306
|
+
audioData.sampleRate
|
|
307
|
+
);
|
|
308
|
+
HEAP64[(this.fOffset >> Math.log2(this.fIntSize)) + part] =
|
|
309
|
+
BigInt(offset);
|
|
247
310
|
}
|
|
248
311
|
|
|
249
|
-
console.log(
|
|
312
|
+
console.log(
|
|
313
|
+
`copyToOut: part: ${part}, maxChannels: ${maxChannels}, offset: ${offset}, buffer: ${audioData}`
|
|
314
|
+
);
|
|
250
315
|
|
|
251
316
|
//this.displayMemory("IN copyToOut, BEFORE copyToOutReal", true);
|
|
252
317
|
// Copy the soundfile data to the buffer
|
|
@@ -264,11 +329,21 @@ export class Soundfile {
|
|
|
264
329
|
for (let chan = 0; chan < audioData.audioBuffer.length; chan++) {
|
|
265
330
|
const input: Float32Array = audioData.audioBuffer[chan];
|
|
266
331
|
const output: number = HEAP32[(this.fBuffers >> 2) + chan];
|
|
267
|
-
const begin: number =
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
(output + (offset + input.length) * this.fSampleSize) >>
|
|
332
|
+
const begin: number =
|
|
333
|
+
(output + offset * this.fSampleSize) >>
|
|
334
|
+
Math.log2(this.fSampleSize);
|
|
335
|
+
const end: number =
|
|
336
|
+
(output + (offset + input.length) * this.fSampleSize) >>
|
|
337
|
+
Math.log2(this.fSampleSize);
|
|
338
|
+
console.log(
|
|
339
|
+
`copyToOutReal32 begin: ${begin}, end: ${end}, delta: ${end - begin}`
|
|
340
|
+
);
|
|
341
|
+
const outputReal: Float32Array = HEAPF.subarray(
|
|
342
|
+
(output + offset * this.fSampleSize) >>
|
|
343
|
+
Math.log2(this.fSampleSize),
|
|
344
|
+
(output + (offset + input.length) * this.fSampleSize) >>
|
|
345
|
+
Math.log2(this.fSampleSize)
|
|
346
|
+
);
|
|
272
347
|
for (let sample = 0; sample < input.length; sample++) {
|
|
273
348
|
outputReal[sample] = input[sample];
|
|
274
349
|
}
|
|
@@ -281,11 +356,21 @@ export class Soundfile {
|
|
|
281
356
|
for (let chan = 0; chan < audioData.audioBuffer.length; chan++) {
|
|
282
357
|
const input: Float32Array = audioData.audioBuffer[chan];
|
|
283
358
|
const output: number = HEAP32[(this.fBuffers >> 2) + chan];
|
|
284
|
-
const begin: number =
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
(output + (offset + input.length) * this.fSampleSize) >>
|
|
359
|
+
const begin: number =
|
|
360
|
+
(output + offset * this.fSampleSize) >>
|
|
361
|
+
Math.log2(this.fSampleSize);
|
|
362
|
+
const end: number =
|
|
363
|
+
(output + (offset + input.length) * this.fSampleSize) >>
|
|
364
|
+
Math.log2(this.fSampleSize);
|
|
365
|
+
console.log(
|
|
366
|
+
`copyToOutReal64 begin: ${begin}, end: ${end}, delta: ${end - begin}`
|
|
367
|
+
);
|
|
368
|
+
const outputReal: Float64Array = HEAPF.subarray(
|
|
369
|
+
(output + offset * this.fSampleSize) >>
|
|
370
|
+
Math.log2(this.fSampleSize),
|
|
371
|
+
(output + (offset + input.length) * this.fSampleSize) >>
|
|
372
|
+
Math.log2(this.fSampleSize)
|
|
373
|
+
);
|
|
289
374
|
for (let sample = 0; sample < input.length; sample++) {
|
|
290
375
|
outputReal[sample] = input[sample];
|
|
291
376
|
}
|
|
@@ -296,22 +381,29 @@ export class Soundfile {
|
|
|
296
381
|
// Set the soundfile fields in wasm memory
|
|
297
382
|
if (this.fIntSize === 4) {
|
|
298
383
|
const HEAP32 = this.fAllocator.getInt32Array();
|
|
299
|
-
HEAP32[(this.fLength >> Math.log2(this.fIntSize)) + part] =
|
|
300
|
-
|
|
384
|
+
HEAP32[(this.fLength >> Math.log2(this.fIntSize)) + part] =
|
|
385
|
+
Soundfile.BUFFER_SIZE;
|
|
386
|
+
HEAP32[(this.fSR >> Math.log2(this.fIntSize)) + part] =
|
|
387
|
+
Soundfile.SAMPLE_RATE;
|
|
301
388
|
HEAP32[(this.fOffset >> Math.log2(this.fIntSize)) + part] = offset;
|
|
302
389
|
} else {
|
|
303
390
|
const HEAP64 = this.fAllocator.getInt64Array();
|
|
304
|
-
HEAP64[(this.fLength >> Math.log2(this.fIntSize)) + part] = BigInt(
|
|
305
|
-
|
|
306
|
-
|
|
391
|
+
HEAP64[(this.fLength >> Math.log2(this.fIntSize)) + part] = BigInt(
|
|
392
|
+
Soundfile.BUFFER_SIZE
|
|
393
|
+
);
|
|
394
|
+
HEAP64[(this.fSR >> Math.log2(this.fIntSize)) + part] = BigInt(
|
|
395
|
+
Soundfile.SAMPLE_RATE
|
|
396
|
+
);
|
|
397
|
+
HEAP64[(this.fOffset >> Math.log2(this.fIntSize)) + part] =
|
|
398
|
+
BigInt(offset);
|
|
307
399
|
}
|
|
308
400
|
|
|
309
401
|
// Update and return the new offset
|
|
310
402
|
return offset + Soundfile.BUFFER_SIZE;
|
|
311
403
|
}
|
|
312
404
|
|
|
313
|
-
displayMemory(where: string =
|
|
314
|
-
console.log(
|
|
405
|
+
displayMemory(where: string = '', mem: boolean = false) {
|
|
406
|
+
console.log('Soundfile memory: ' + where);
|
|
315
407
|
console.log(`fPtr: ${this.fPtr}`);
|
|
316
408
|
console.log(`fBuffers: ${this.fBuffers}`);
|
|
317
409
|
console.log(`fLength: ${this.fLength}`);
|
|
@@ -320,9 +412,15 @@ export class Soundfile {
|
|
|
320
412
|
const HEAP32 = this.fAllocator.getInt32Array();
|
|
321
413
|
if (mem) console.log(`HEAP32: ${HEAP32}`);
|
|
322
414
|
console.log(`HEAP32[this.fPtr >> 2]: ${HEAP32[this.fPtr >> 2]}`);
|
|
323
|
-
console.log(
|
|
324
|
-
|
|
325
|
-
|
|
415
|
+
console.log(
|
|
416
|
+
`HEAP32[(this.fPtr + ptrSize) >> 2]: ${HEAP32[(this.fPtr + this.fPtrSize) >> 2]}`
|
|
417
|
+
);
|
|
418
|
+
console.log(
|
|
419
|
+
`HEAP32[(this.fPtr + 2 * ptrSize) >> 2]: ${HEAP32[(this.fPtr + 2 * this.fPtrSize) >> 2]}`
|
|
420
|
+
);
|
|
421
|
+
console.log(
|
|
422
|
+
`HEAP32[(this.fPtr + 3 * ptrSize) >> 2]: ${HEAP32[(this.fPtr + 3 * this.fPtrSize) >> 2]}`
|
|
423
|
+
);
|
|
326
424
|
}
|
|
327
425
|
|
|
328
426
|
// Return the pointer to the soundfile structure in wasm memory
|
|
@@ -345,7 +443,7 @@ export class Soundfile {
|
|
|
345
443
|
/**
|
|
346
444
|
* DSP implementation that mimic the C++ 'dsp' class:
|
|
347
445
|
* - adding MIDI control: metadata are decoded and incoming MIDI messages will control the associated controllers
|
|
348
|
-
* - an output handler can be set to treat produced output controllers (like 'bargraph')
|
|
446
|
+
* - an output handler can be set to treat produced output controllers (like 'bargraph')
|
|
349
447
|
* - regular controllers are handled using setParamValue/getParamValue and getParams methods
|
|
350
448
|
*/
|
|
351
449
|
export interface IFaustBaseWebAudioDsp {
|
|
@@ -365,7 +463,7 @@ export interface IFaustBaseWebAudioDsp {
|
|
|
365
463
|
|
|
366
464
|
/**
|
|
367
465
|
* Call the output parameter handler with a path and value.
|
|
368
|
-
*
|
|
466
|
+
*
|
|
369
467
|
* @param path - the path to the wanted parameter (retrieved using 'getParams' method)
|
|
370
468
|
* @param value - the float value for the wanted control
|
|
371
469
|
*/
|
|
@@ -455,17 +553,17 @@ export interface IFaustBaseWebAudioDsp {
|
|
|
455
553
|
|
|
456
554
|
/**
|
|
457
555
|
* Handle MIDI keyOn messages.
|
|
458
|
-
* @param channel
|
|
459
|
-
* @param pitch
|
|
460
|
-
* @param velocity
|
|
556
|
+
* @param channel
|
|
557
|
+
* @param pitch
|
|
558
|
+
* @param velocity
|
|
461
559
|
*/
|
|
462
560
|
keyOn(channel: number, pitch: number, velocity: number): void;
|
|
463
561
|
|
|
464
562
|
/**
|
|
465
563
|
* Handle MIDI keyOn messages.
|
|
466
|
-
* @param channel
|
|
467
|
-
* @param pitch
|
|
468
|
-
* @param velocity
|
|
564
|
+
* @param channel
|
|
565
|
+
* @param pitch
|
|
566
|
+
* @param velocity
|
|
469
567
|
*/
|
|
470
568
|
keyOff(channel: number, pitch: number, velocity: number): void;
|
|
471
569
|
|
|
@@ -508,10 +606,10 @@ export interface IFaustBaseWebAudioDsp {
|
|
|
508
606
|
getUI(): FaustUIDescriptor;
|
|
509
607
|
|
|
510
608
|
/**
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
609
|
+
* Get DSP UI items description.
|
|
610
|
+
*
|
|
611
|
+
* @return the DSP UI items description
|
|
612
|
+
*/
|
|
515
613
|
getDescriptors(): FaustUIInputItem[];
|
|
516
614
|
|
|
517
615
|
/**
|
|
@@ -522,8 +620,8 @@ export interface IFaustBaseWebAudioDsp {
|
|
|
522
620
|
getJSON(): string;
|
|
523
621
|
|
|
524
622
|
/**
|
|
525
|
-
|
|
526
|
-
|
|
623
|
+
* Start accelerometer and gyroscope handlers.
|
|
624
|
+
*/
|
|
527
625
|
startSensors(): void;
|
|
528
626
|
|
|
529
627
|
/**
|
|
@@ -534,24 +632,31 @@ export interface IFaustBaseWebAudioDsp {
|
|
|
534
632
|
/** Indicating if the DSP handles the accelerometer */
|
|
535
633
|
readonly hasAccInput: boolean;
|
|
536
634
|
|
|
537
|
-
/**
|
|
635
|
+
/**
|
|
538
636
|
* Accelerometer handling.
|
|
539
637
|
* accelerationIncludingGravity: DeviceMotionEvent["accelerationIncludingGravity"]
|
|
540
638
|
* invert: boolean
|
|
541
639
|
*/
|
|
542
|
-
propagateAcc(
|
|
640
|
+
propagateAcc(
|
|
641
|
+
accelerationIncludingGravity: NonNullable<
|
|
642
|
+
DeviceMotionEvent['accelerationIncludingGravity']
|
|
643
|
+
>,
|
|
644
|
+
invert: boolean
|
|
645
|
+
): void;
|
|
543
646
|
|
|
544
647
|
/** Indicating if the DSP handles the gyroscope */
|
|
545
648
|
readonly hasGyrInput: boolean;
|
|
546
649
|
|
|
547
|
-
/**
|
|
650
|
+
/**
|
|
548
651
|
* Gyroscope handling.
|
|
549
652
|
* event: Pick<DeviceOrientationEvent, "alpha" | "beta" | "gamma">
|
|
550
653
|
*/
|
|
551
|
-
propagateGyr(
|
|
654
|
+
propagateGyr(
|
|
655
|
+
event: Pick<DeviceOrientationEvent, 'alpha' | 'beta' | 'gamma'>
|
|
656
|
+
): void;
|
|
552
657
|
|
|
553
658
|
/**
|
|
554
|
-
* Start the DSP audio processing.
|
|
659
|
+
* Start the DSP audio processing.
|
|
555
660
|
*/
|
|
556
661
|
start(): void;
|
|
557
662
|
|
|
@@ -566,8 +671,10 @@ export interface IFaustBaseWebAudioDsp {
|
|
|
566
671
|
destroy(): void;
|
|
567
672
|
}
|
|
568
673
|
|
|
569
|
-
export
|
|
570
|
-
export interface IFaustMonoWebAudioNode
|
|
674
|
+
export type IFaustMonoWebAudioDsp = IFaustBaseWebAudioDsp;
|
|
675
|
+
export interface IFaustMonoWebAudioNode
|
|
676
|
+
extends IFaustMonoWebAudioDsp,
|
|
677
|
+
AudioNode {}
|
|
571
678
|
|
|
572
679
|
export interface IFaustPolyWebAudioDsp extends IFaustBaseWebAudioDsp {
|
|
573
680
|
/**
|
|
@@ -595,7 +702,9 @@ export interface IFaustPolyWebAudioDsp extends IFaustBaseWebAudioDsp {
|
|
|
595
702
|
*/
|
|
596
703
|
allNotesOff(hard: boolean): void;
|
|
597
704
|
}
|
|
598
|
-
export interface IFaustPolyWebAudioNode
|
|
705
|
+
export interface IFaustPolyWebAudioNode
|
|
706
|
+
extends IFaustPolyWebAudioDsp,
|
|
707
|
+
AudioNode {}
|
|
599
708
|
|
|
600
709
|
export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
601
710
|
protected fOutputHandler: OutputParamHandler | null = null;
|
|
@@ -618,7 +727,7 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
618
727
|
|
|
619
728
|
// Soundfile handling
|
|
620
729
|
protected fSoundfiles: SoundfileItem[] = [];
|
|
621
|
-
protected fSoundfileBuffers: LooseFaustDspFactory[
|
|
730
|
+
protected fSoundfileBuffers: LooseFaustDspFactory['soundfiles'] = {};
|
|
622
731
|
/** Keep the end of memory offset before soundfiles */
|
|
623
732
|
protected fEndMemory: number;
|
|
624
733
|
|
|
@@ -635,20 +744,51 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
635
744
|
protected fSampleSize: number;
|
|
636
745
|
|
|
637
746
|
// MIDI handling
|
|
638
|
-
protected fPitchwheelLabel: {
|
|
639
|
-
|
|
747
|
+
protected fPitchwheelLabel: {
|
|
748
|
+
path: string;
|
|
749
|
+
chan: number;
|
|
750
|
+
min: number;
|
|
751
|
+
max: number;
|
|
752
|
+
}[] = [];
|
|
753
|
+
protected fCtrlLabel: {
|
|
754
|
+
path: string;
|
|
755
|
+
chan: number;
|
|
756
|
+
min: number;
|
|
757
|
+
max: number;
|
|
758
|
+
}[][] = new Array(128).fill(null).map(() => []);
|
|
640
759
|
// array of MIDI key handlers; array index is the MIDI note number
|
|
641
|
-
protected fMidiKeyLabel: {
|
|
642
|
-
|
|
643
|
-
|
|
760
|
+
protected fMidiKeyLabel: {
|
|
761
|
+
path: string;
|
|
762
|
+
chan: number;
|
|
763
|
+
min: number;
|
|
764
|
+
max: number;
|
|
765
|
+
}[][] = new Array(128).fill(null).map(() => []);
|
|
766
|
+
protected fMidiKeyOnLabel: {
|
|
767
|
+
path: string;
|
|
768
|
+
chan: number;
|
|
769
|
+
min: number;
|
|
770
|
+
max: number;
|
|
771
|
+
}[][] = new Array(128).fill(null).map(() => []);
|
|
772
|
+
protected fMidiKeyOffLabel: {
|
|
773
|
+
path: string;
|
|
774
|
+
chan: number;
|
|
775
|
+
min: number;
|
|
776
|
+
max: number;
|
|
777
|
+
}[][] = new Array(128).fill(null).map(() => []);
|
|
644
778
|
|
|
645
779
|
protected fPathTable: { [address: string]: number } = {};
|
|
646
780
|
protected fUICallback: UIHandler = (item: FaustUIItem) => {
|
|
647
|
-
if (item.type ===
|
|
781
|
+
if (item.type === 'hbargraph' || item.type === 'vbargraph') {
|
|
648
782
|
// Keep bargraph adresses
|
|
649
783
|
this.fOutputsItems.push(item.address);
|
|
650
784
|
this.fPathTable[item.address] = item.index;
|
|
651
|
-
} else if (
|
|
785
|
+
} else if (
|
|
786
|
+
item.type === 'vslider' ||
|
|
787
|
+
item.type === 'hslider' ||
|
|
788
|
+
item.type === 'button' ||
|
|
789
|
+
item.type === 'checkbox' ||
|
|
790
|
+
item.type === 'nentry'
|
|
791
|
+
) {
|
|
652
792
|
// Keep inputs adresses
|
|
653
793
|
this.fInputsItems.push(item.address);
|
|
654
794
|
this.fPathTable[item.address] = item.index;
|
|
@@ -659,14 +799,24 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
659
799
|
// Parse 'midi' metadata
|
|
660
800
|
if (midi) {
|
|
661
801
|
const strMidi = midi.trim();
|
|
662
|
-
if (strMidi ===
|
|
802
|
+
if (strMidi === 'pitchwheel') {
|
|
663
803
|
const matched = strMidi.match(/^pitchwheel\s(\d+)/);
|
|
664
804
|
// "pitchwheel chan"
|
|
665
805
|
if (matched) {
|
|
666
|
-
this.fPitchwheelLabel.push({
|
|
806
|
+
this.fPitchwheelLabel.push({
|
|
807
|
+
path: item.address,
|
|
808
|
+
chan: parseInt(matched[1]),
|
|
809
|
+
min: item.min as number,
|
|
810
|
+
max: item.max as number
|
|
811
|
+
});
|
|
667
812
|
// "pitchwheel"
|
|
668
813
|
} else {
|
|
669
|
-
this.fPitchwheelLabel.push({
|
|
814
|
+
this.fPitchwheelLabel.push({
|
|
815
|
+
path: item.address,
|
|
816
|
+
chan: 0,
|
|
817
|
+
min: item.min as number,
|
|
818
|
+
max: item.max as number
|
|
819
|
+
});
|
|
670
820
|
}
|
|
671
821
|
} else {
|
|
672
822
|
// "ctrl num chan"
|
|
@@ -674,44 +824,106 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
674
824
|
// "ctrl num"
|
|
675
825
|
const matched1 = strMidi.match(/^ctrl\s(\d+)/);
|
|
676
826
|
// match `key <note>[ <channel>]`
|
|
677
|
-
const matchedKey = strMidi.match(
|
|
827
|
+
const matchedKey = strMidi.match(
|
|
828
|
+
/^key\s+(\d+)(?:\s+(\d+))?$/
|
|
829
|
+
);
|
|
678
830
|
//match `keyon <note>[ <channel>]`
|
|
679
|
-
const matchedKeyOn = strMidi.match(
|
|
831
|
+
const matchedKeyOn = strMidi.match(
|
|
832
|
+
/^keyon\s+(\d+)(?:\s+(\d+))?$/
|
|
833
|
+
);
|
|
680
834
|
//match `keyoff <note>[ <channel>]`
|
|
681
|
-
const matchedKeyOff = strMidi.match(
|
|
835
|
+
const matchedKeyOff = strMidi.match(
|
|
836
|
+
/^keyoff\s+(\d+)(?:\s+(\d+))?$/
|
|
837
|
+
);
|
|
682
838
|
if (matched2) {
|
|
683
|
-
this.fCtrlLabel[parseInt(matched2[1])].push({
|
|
839
|
+
this.fCtrlLabel[parseInt(matched2[1])].push({
|
|
840
|
+
path: item.address,
|
|
841
|
+
chan: parseInt(matched2[2]),
|
|
842
|
+
min: item.min as number,
|
|
843
|
+
max: item.max as number
|
|
844
|
+
});
|
|
684
845
|
} else if (matched1) {
|
|
685
|
-
this.fCtrlLabel[parseInt(matched1[1])].push({
|
|
846
|
+
this.fCtrlLabel[parseInt(matched1[1])].push({
|
|
847
|
+
path: item.address,
|
|
848
|
+
chan: 0,
|
|
849
|
+
min: item.min as number,
|
|
850
|
+
max: item.max as number
|
|
851
|
+
});
|
|
686
852
|
} else if (matchedKey) {
|
|
687
853
|
const note = parseInt(matchedKey[1]);
|
|
688
|
-
const channel = matchedKey[2]
|
|
689
|
-
|
|
854
|
+
const channel = matchedKey[2]
|
|
855
|
+
? parseInt(matchedKey[2])
|
|
856
|
+
: 0;
|
|
857
|
+
this.fMidiKeyLabel[note].push({
|
|
858
|
+
path: item.address,
|
|
859
|
+
chan: channel,
|
|
860
|
+
min: (item.min as number) ?? 0,
|
|
861
|
+
max: (item.max as number) ?? 1
|
|
862
|
+
});
|
|
690
863
|
} else if (matchedKeyOn) {
|
|
691
864
|
const note = parseInt(matchedKeyOn[1]);
|
|
692
|
-
const channel = matchedKeyOn[2]
|
|
693
|
-
|
|
865
|
+
const channel = matchedKeyOn[2]
|
|
866
|
+
? parseInt(matchedKeyOn[2])
|
|
867
|
+
: 0;
|
|
868
|
+
this.fMidiKeyOnLabel[note].push({
|
|
869
|
+
path: item.address,
|
|
870
|
+
chan: channel,
|
|
871
|
+
min: (item.min as number) ?? 0,
|
|
872
|
+
max: (item.max as number) ?? 1
|
|
873
|
+
});
|
|
694
874
|
} else if (matchedKeyOff) {
|
|
695
875
|
const note = parseInt(matchedKeyOff[1]);
|
|
696
|
-
const channel = matchedKeyOff[2]
|
|
697
|
-
|
|
876
|
+
const channel = matchedKeyOff[2]
|
|
877
|
+
? parseInt(matchedKeyOff[2])
|
|
878
|
+
: 0;
|
|
879
|
+
this.fMidiKeyOffLabel[note].push({
|
|
880
|
+
path: item.address,
|
|
881
|
+
chan: channel,
|
|
882
|
+
min: (item.min as number) ?? 0,
|
|
883
|
+
max: (item.max as number) ?? 1
|
|
884
|
+
});
|
|
698
885
|
}
|
|
699
886
|
}
|
|
700
887
|
}
|
|
701
888
|
|
|
702
889
|
// Parse 'acc' metadata
|
|
703
890
|
if (acc) {
|
|
704
|
-
const numAcc: number[] = acc.trim().split(
|
|
705
|
-
this.setupAccHandler(
|
|
891
|
+
const numAcc: number[] = acc.trim().split(' ').map(Number);
|
|
892
|
+
this.setupAccHandler(
|
|
893
|
+
item.address,
|
|
894
|
+
FaustSensors.convertToAxis(numAcc[0]),
|
|
895
|
+
FaustSensors.convertToCurve(numAcc[1]),
|
|
896
|
+
numAcc[2],
|
|
897
|
+
numAcc[3],
|
|
898
|
+
numAcc[4],
|
|
899
|
+
item.min as number,
|
|
900
|
+
item.init as number,
|
|
901
|
+
item.max as number
|
|
902
|
+
);
|
|
706
903
|
}
|
|
707
904
|
// Parse 'gyr' metadata
|
|
708
905
|
if (gyr) {
|
|
709
|
-
const numAcc: number[] = gyr.trim().split(
|
|
710
|
-
this.setupGyrHandler(
|
|
906
|
+
const numAcc: number[] = gyr.trim().split(' ').map(Number);
|
|
907
|
+
this.setupGyrHandler(
|
|
908
|
+
item.address,
|
|
909
|
+
FaustSensors.convertToAxis(numAcc[0]),
|
|
910
|
+
FaustSensors.convertToCurve(numAcc[1]),
|
|
911
|
+
numAcc[2],
|
|
912
|
+
numAcc[3],
|
|
913
|
+
numAcc[4],
|
|
914
|
+
item.min as number,
|
|
915
|
+
item.init as number,
|
|
916
|
+
item.max as number
|
|
917
|
+
);
|
|
711
918
|
}
|
|
712
919
|
});
|
|
713
|
-
} else if (item.type ===
|
|
714
|
-
this.fSoundfiles.push({
|
|
920
|
+
} else if (item.type === 'soundfile') {
|
|
921
|
+
this.fSoundfiles.push({
|
|
922
|
+
name: item.label,
|
|
923
|
+
url: item.url,
|
|
924
|
+
index: item.index,
|
|
925
|
+
basePtr: -1
|
|
926
|
+
});
|
|
715
927
|
}
|
|
716
928
|
};
|
|
717
929
|
|
|
@@ -722,7 +934,11 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
722
934
|
|
|
723
935
|
protected fJSONDsp!: FaustDspMeta;
|
|
724
936
|
|
|
725
|
-
constructor(
|
|
937
|
+
constructor(
|
|
938
|
+
sampleSize: number,
|
|
939
|
+
bufferSize: number,
|
|
940
|
+
soundfiles: LooseFaustDspFactory['soundfiles']
|
|
941
|
+
) {
|
|
726
942
|
this.fBufferSize = bufferSize;
|
|
727
943
|
this.fPtrSize = sampleSize; // Done on wast/wasm backend side
|
|
728
944
|
this.fSampleSize = sampleSize;
|
|
@@ -732,26 +948,45 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
732
948
|
}
|
|
733
949
|
|
|
734
950
|
// Tools
|
|
735
|
-
static remap(
|
|
736
|
-
|
|
951
|
+
static remap(
|
|
952
|
+
v: number,
|
|
953
|
+
mn0: number,
|
|
954
|
+
mx0: number,
|
|
955
|
+
mn1: number,
|
|
956
|
+
mx1: number
|
|
957
|
+
) {
|
|
958
|
+
return ((v - mn0) / (mx0 - mn0)) * (mx1 - mn1) + mn1;
|
|
737
959
|
}
|
|
738
960
|
|
|
739
961
|
// JSON parsing functions
|
|
740
|
-
static parseUI(
|
|
741
|
-
ui
|
|
962
|
+
static parseUI(
|
|
963
|
+
ui: FaustUIDescriptor,
|
|
964
|
+
callback: (item: FaustUIItem) => any
|
|
965
|
+
) {
|
|
966
|
+
ui.forEach((group) => this.parseGroup(group, callback));
|
|
742
967
|
}
|
|
743
968
|
|
|
744
|
-
static parseGroup(
|
|
969
|
+
static parseGroup(
|
|
970
|
+
group: FaustUIGroup,
|
|
971
|
+
callback: (item: FaustUIItem) => any
|
|
972
|
+
) {
|
|
745
973
|
if (group.items) {
|
|
746
974
|
this.parseItems(group.items, callback);
|
|
747
975
|
}
|
|
748
976
|
}
|
|
749
|
-
static parseItems(
|
|
750
|
-
items
|
|
977
|
+
static parseItems(
|
|
978
|
+
items: FaustUIItem[],
|
|
979
|
+
callback: (item: FaustUIItem) => any
|
|
980
|
+
) {
|
|
981
|
+
items.forEach((item) => this.parseItem(item, callback));
|
|
751
982
|
}
|
|
752
983
|
|
|
753
984
|
static parseItem(item: FaustUIItem, callback: (item: FaustUIItem) => any) {
|
|
754
|
-
if (
|
|
985
|
+
if (
|
|
986
|
+
item.type === 'vgroup' ||
|
|
987
|
+
item.type === 'hgroup' ||
|
|
988
|
+
item.type === 'tgroup'
|
|
989
|
+
) {
|
|
755
990
|
this.parseItems(item.items, callback);
|
|
756
991
|
} else {
|
|
757
992
|
callback(item);
|
|
@@ -761,82 +996,145 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
761
996
|
/** Split the soundfile names and return an array of names */
|
|
762
997
|
static splitSoundfileNames(input: string): string[] {
|
|
763
998
|
// Trim off the curly braces at the start and end, if present
|
|
764
|
-
|
|
999
|
+
const trimmed = input.replace(/^\{|\}$/g, '');
|
|
765
1000
|
// Split the string into an array of strings and remove first and last characters
|
|
766
|
-
return trimmed
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1001
|
+
return trimmed
|
|
1002
|
+
.split(';')
|
|
1003
|
+
.map((str) =>
|
|
1004
|
+
str.length <= 2 ? '' : str.substring(1, str.length - 1)
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
get hasAccInput() {
|
|
1009
|
+
return this.fAcc.x.length + this.fAcc.y.length + this.fAcc.z.length > 0;
|
|
1010
|
+
}
|
|
1011
|
+
propagateAcc(
|
|
1012
|
+
accelerationIncludingGravity: NonNullable<
|
|
1013
|
+
DeviceMotionEvent['accelerationIncludingGravity']
|
|
1014
|
+
>,
|
|
1015
|
+
invert: boolean = false
|
|
1016
|
+
) {
|
|
772
1017
|
// Get accelerometervalues
|
|
773
1018
|
const { x, y, z } = accelerationIncludingGravity;
|
|
774
1019
|
|
|
775
1020
|
if (invert) {
|
|
776
1021
|
// Call the accelerometer handlers
|
|
777
|
-
if (x !== null) this.fAcc.x.forEach(handler => handler(-x));
|
|
778
|
-
if (y !== null) this.fAcc.y.forEach(handler => handler(-y));
|
|
779
|
-
if (z !== null) this.fAcc.z.forEach(handler => handler(-z));
|
|
1022
|
+
if (x !== null) this.fAcc.x.forEach((handler) => handler(-x));
|
|
1023
|
+
if (y !== null) this.fAcc.y.forEach((handler) => handler(-y));
|
|
1024
|
+
if (z !== null) this.fAcc.z.forEach((handler) => handler(-z));
|
|
780
1025
|
} else {
|
|
781
1026
|
// Call the accelerometer handlers
|
|
782
|
-
if (x !== null) this.fAcc.x.forEach(handler => handler(x));
|
|
783
|
-
if (y !== null) this.fAcc.y.forEach(handler => handler(y));
|
|
784
|
-
if (z !== null) this.fAcc.z.forEach(handler => handler(z));
|
|
1027
|
+
if (x !== null) this.fAcc.x.forEach((handler) => handler(x));
|
|
1028
|
+
if (y !== null) this.fAcc.y.forEach((handler) => handler(y));
|
|
1029
|
+
if (z !== null) this.fAcc.z.forEach((handler) => handler(z));
|
|
785
1030
|
}
|
|
786
1031
|
}
|
|
787
1032
|
|
|
788
|
-
get hasGyrInput() {
|
|
789
|
-
|
|
790
|
-
|
|
1033
|
+
get hasGyrInput() {
|
|
1034
|
+
return this.fGyr.x.length + this.fGyr.y.length + this.fGyr.z.length > 0;
|
|
1035
|
+
}
|
|
1036
|
+
propagateGyr(
|
|
1037
|
+
event: Pick<DeviceOrientationEvent, 'alpha' | 'beta' | 'gamma'>
|
|
1038
|
+
) {
|
|
791
1039
|
// Get gyroscope values
|
|
792
1040
|
const { alpha, beta, gamma } = event;
|
|
793
1041
|
|
|
794
1042
|
// Call the gyroscope handlers
|
|
795
|
-
if (alpha !== null) this.fGyr.x.forEach(handler => handler(alpha));
|
|
796
|
-
if (beta !== null) this.fGyr.y.forEach(handler => handler(beta));
|
|
797
|
-
if (gamma !== null) this.fGyr.z.forEach(handler => handler(gamma));
|
|
1043
|
+
if (alpha !== null) this.fGyr.x.forEach((handler) => handler(alpha));
|
|
1044
|
+
if (beta !== null) this.fGyr.y.forEach((handler) => handler(beta));
|
|
1045
|
+
if (gamma !== null) this.fGyr.z.forEach((handler) => handler(gamma));
|
|
798
1046
|
}
|
|
799
1047
|
|
|
800
1048
|
/** Build the accelerometer handler */
|
|
801
|
-
private setupAccHandler(
|
|
802
|
-
|
|
803
|
-
|
|
1049
|
+
private setupAccHandler(
|
|
1050
|
+
path: string,
|
|
1051
|
+
axis: Axis,
|
|
1052
|
+
curve: Curve,
|
|
1053
|
+
amin: number,
|
|
1054
|
+
amid: number,
|
|
1055
|
+
amax: number,
|
|
1056
|
+
min: number,
|
|
1057
|
+
init: number,
|
|
1058
|
+
max: number
|
|
1059
|
+
) {
|
|
1060
|
+
const handler: UpdatableValueConverter = FaustSensors.buildHandler(
|
|
1061
|
+
curve,
|
|
1062
|
+
amin,
|
|
1063
|
+
amid,
|
|
1064
|
+
amax,
|
|
1065
|
+
min,
|
|
1066
|
+
init,
|
|
1067
|
+
max
|
|
1068
|
+
);
|
|
804
1069
|
switch (axis) {
|
|
805
1070
|
case Axis.x:
|
|
806
|
-
this.fAcc.x.push((val) =>
|
|
1071
|
+
this.fAcc.x.push((val) =>
|
|
1072
|
+
this.setParamValue(path, handler.uiToFaust(val))
|
|
1073
|
+
);
|
|
807
1074
|
break;
|
|
808
1075
|
case Axis.y:
|
|
809
|
-
this.fAcc.y.push((val) =>
|
|
1076
|
+
this.fAcc.y.push((val) =>
|
|
1077
|
+
this.setParamValue(path, handler.uiToFaust(val))
|
|
1078
|
+
);
|
|
810
1079
|
break;
|
|
811
1080
|
case Axis.z:
|
|
812
|
-
this.fAcc.z.push((val) =>
|
|
1081
|
+
this.fAcc.z.push((val) =>
|
|
1082
|
+
this.setParamValue(path, handler.uiToFaust(val))
|
|
1083
|
+
);
|
|
813
1084
|
break;
|
|
814
1085
|
}
|
|
815
1086
|
}
|
|
816
1087
|
|
|
817
1088
|
/** Build the gyroscope handler */
|
|
818
|
-
private setupGyrHandler(
|
|
819
|
-
|
|
820
|
-
|
|
1089
|
+
private setupGyrHandler(
|
|
1090
|
+
path: string,
|
|
1091
|
+
axis: Axis,
|
|
1092
|
+
curve: Curve,
|
|
1093
|
+
amin: number,
|
|
1094
|
+
amid: number,
|
|
1095
|
+
amax: number,
|
|
1096
|
+
min: number,
|
|
1097
|
+
init: number,
|
|
1098
|
+
max: number
|
|
1099
|
+
) {
|
|
1100
|
+
const handler: UpdatableValueConverter = FaustSensors.buildHandler(
|
|
1101
|
+
curve,
|
|
1102
|
+
amin,
|
|
1103
|
+
amid,
|
|
1104
|
+
amax,
|
|
1105
|
+
min,
|
|
1106
|
+
init,
|
|
1107
|
+
max
|
|
1108
|
+
);
|
|
821
1109
|
switch (axis) {
|
|
822
1110
|
case Axis.x:
|
|
823
|
-
this.fGyr.x.push((val) =>
|
|
1111
|
+
this.fGyr.x.push((val) =>
|
|
1112
|
+
this.setParamValue(path, handler.uiToFaust(val))
|
|
1113
|
+
);
|
|
824
1114
|
break;
|
|
825
1115
|
case Axis.y:
|
|
826
|
-
this.fGyr.y.push((val) =>
|
|
1116
|
+
this.fGyr.y.push((val) =>
|
|
1117
|
+
this.setParamValue(path, handler.uiToFaust(val))
|
|
1118
|
+
);
|
|
827
1119
|
break;
|
|
828
1120
|
case Axis.z:
|
|
829
|
-
this.fGyr.z.push((val) =>
|
|
1121
|
+
this.fGyr.z.push((val) =>
|
|
1122
|
+
this.setParamValue(path, handler.uiToFaust(val))
|
|
1123
|
+
);
|
|
830
1124
|
break;
|
|
831
1125
|
}
|
|
832
1126
|
}
|
|
833
1127
|
|
|
834
1128
|
static extractUrlsFromMeta(dspMeta: FaustDspMeta): string[] {
|
|
835
1129
|
// Find the entry with the "soundfiles" key
|
|
836
|
-
const soundfilesEntry = dspMeta.meta.find(
|
|
1130
|
+
const soundfilesEntry = dspMeta.meta.find(
|
|
1131
|
+
(entry) => entry.soundfiles !== undefined
|
|
1132
|
+
);
|
|
837
1133
|
// If the entry is found, split the string by semicolon to get the URLs
|
|
838
1134
|
if (soundfilesEntry) {
|
|
839
|
-
return soundfilesEntry.soundfiles
|
|
1135
|
+
return soundfilesEntry.soundfiles
|
|
1136
|
+
.split(';')
|
|
1137
|
+
.filter((url) => url !== '');
|
|
840
1138
|
} else {
|
|
841
1139
|
return [];
|
|
842
1140
|
}
|
|
@@ -845,45 +1143,65 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
845
1143
|
/**
|
|
846
1144
|
* Load a soundfile possibly containing several parts in the DSP struct.
|
|
847
1145
|
* Soundfile pointers are located at 'index' offset, to be read in the JSON file.
|
|
848
|
-
* The DSP struct is located at baseDSP in the wasm memory,
|
|
1146
|
+
* The DSP struct is located at baseDSP in the wasm memory,
|
|
849
1147
|
* either a monophonic DSP, or a voice in a polyphonic context.
|
|
850
|
-
*
|
|
1148
|
+
*
|
|
851
1149
|
* @param allocator : the wasm memory allocator
|
|
852
1150
|
* @param baseDSP : the base DSP in the wasm memory
|
|
853
1151
|
* @param name : the name of the soundfile
|
|
854
1152
|
* @param url : the url of the soundfile
|
|
855
1153
|
*/
|
|
856
|
-
private loadSoundfile(
|
|
1154
|
+
private loadSoundfile(
|
|
1155
|
+
allocator: WasmAllocator,
|
|
1156
|
+
baseDSP: number,
|
|
1157
|
+
name: string,
|
|
1158
|
+
url: string
|
|
1159
|
+
) {
|
|
857
1160
|
console.log(`Soundfile ${name} paths: ${url}`);
|
|
858
1161
|
const soundfileIds = FaustBaseWebAudioDsp.splitSoundfileNames(url);
|
|
859
|
-
const item = this.fSoundfiles.find(item => item.url === url);
|
|
1162
|
+
const item = this.fSoundfiles.find((item) => item.url === url);
|
|
860
1163
|
if (!item) throw new Error(`Soundfile with ${url} cannot be found !}`);
|
|
861
1164
|
// Use the cached Soundfile
|
|
862
1165
|
if (item.basePtr !== -1) {
|
|
863
1166
|
// Update HEAP32 after soundfile creation
|
|
864
1167
|
const HEAP32 = allocator.getInt32Array();
|
|
865
1168
|
// Fill the soundfile structure in wasm memory, soundfiles are at the beginning of the DSP memory
|
|
866
|
-
console.log(
|
|
1169
|
+
console.log(
|
|
1170
|
+
`Soundfile CACHE ${url}} : ${name} loaded at ${item.basePtr} in wasm memory with index ${item.index}`
|
|
1171
|
+
);
|
|
867
1172
|
// Soundfile is located at 'index' in the DSP struct, to be added with baseDSP in the wasm memory
|
|
868
1173
|
HEAP32[(baseDSP + item.index) >> 2] = item.basePtr;
|
|
869
1174
|
} else {
|
|
870
1175
|
// Create the soundfiles
|
|
871
|
-
const soundfile = this.createSoundfile(
|
|
1176
|
+
const soundfile = this.createSoundfile(
|
|
1177
|
+
allocator,
|
|
1178
|
+
soundfileIds,
|
|
1179
|
+
this.fSoundfileBuffers
|
|
1180
|
+
);
|
|
872
1181
|
if (soundfile) {
|
|
873
1182
|
// Update HEAP32 after soundfile creation
|
|
874
1183
|
const HEAP32 = soundfile.getHEAP32();
|
|
875
1184
|
// Get the soundfile pointer in wasm memory
|
|
876
1185
|
item.basePtr = soundfile.getPtr();
|
|
877
|
-
console.log(
|
|
1186
|
+
console.log(
|
|
1187
|
+
`Soundfile ${name} loaded at ${item.basePtr} in wasm memory with index ${item.index}`
|
|
1188
|
+
);
|
|
878
1189
|
// Soundfile is located at 'index' in the DSP struct, to be added with baseDSP in the wasm memory
|
|
879
1190
|
HEAP32[(baseDSP + item.index) >> 2] = item.basePtr;
|
|
880
1191
|
} else {
|
|
881
|
-
console.log(
|
|
1192
|
+
console.log(
|
|
1193
|
+
`Soundfile ${name} for ${url} cannot be created !}`
|
|
1194
|
+
);
|
|
882
1195
|
}
|
|
883
1196
|
}
|
|
884
1197
|
}
|
|
885
1198
|
|
|
886
|
-
createSoundfile(
|
|
1199
|
+
createSoundfile(
|
|
1200
|
+
allocator: WasmAllocator,
|
|
1201
|
+
soundfileIdList: string[],
|
|
1202
|
+
soundfiles: LooseFaustDspFactory['soundfiles'],
|
|
1203
|
+
maxChan = Soundfile.MAX_CHAN
|
|
1204
|
+
) {
|
|
887
1205
|
let curChan = 1; // At least one channel
|
|
888
1206
|
let totalLength = 0;
|
|
889
1207
|
|
|
@@ -904,10 +1222,19 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
904
1222
|
}
|
|
905
1223
|
|
|
906
1224
|
// Complete with empty parts
|
|
907
|
-
totalLength +=
|
|
1225
|
+
totalLength +=
|
|
1226
|
+
(Soundfile.MAX_SOUNDFILE_PARTS - soundfileIdList.length) *
|
|
1227
|
+
Soundfile.BUFFER_SIZE;
|
|
908
1228
|
|
|
909
1229
|
// Create the soundfile
|
|
910
|
-
const soundfile = new Soundfile(
|
|
1230
|
+
const soundfile = new Soundfile(
|
|
1231
|
+
allocator,
|
|
1232
|
+
this.fSampleSize,
|
|
1233
|
+
curChan,
|
|
1234
|
+
totalLength,
|
|
1235
|
+
maxChan,
|
|
1236
|
+
soundfileIdList.length
|
|
1237
|
+
);
|
|
911
1238
|
|
|
912
1239
|
//soundfile.displayMemory("After soundfile creation");
|
|
913
1240
|
// Init offset
|
|
@@ -931,7 +1258,11 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
931
1258
|
//soundfile.displayMemory("After reading soundfiles");
|
|
932
1259
|
|
|
933
1260
|
// Complete with empty parts
|
|
934
|
-
for (
|
|
1261
|
+
for (
|
|
1262
|
+
let part = soundfileIdList.length;
|
|
1263
|
+
part < Soundfile.MAX_SOUNDFILE_PARTS;
|
|
1264
|
+
part++
|
|
1265
|
+
) {
|
|
935
1266
|
offset = soundfile.emptyFile(part, offset);
|
|
936
1267
|
}
|
|
937
1268
|
|
|
@@ -944,30 +1275,38 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
944
1275
|
|
|
945
1276
|
return soundfile;
|
|
946
1277
|
}
|
|
947
|
-
/**
|
|
1278
|
+
/**
|
|
948
1279
|
* Init soundfiles memory.
|
|
949
|
-
*
|
|
1280
|
+
*
|
|
950
1281
|
* @param allocator : the wasm memory allocator
|
|
951
1282
|
* @param baseDSP : the DSP struct (either a monophonic DSP of polyphonic voice) base DSP in the wasm memory
|
|
952
|
-
|
|
1283
|
+
*/
|
|
953
1284
|
protected initSoundfileMemory(allocator: WasmAllocator, baseDSP: number) {
|
|
954
1285
|
// Create and fill the soundfile structure
|
|
955
1286
|
for (const { name, url } of this.fSoundfiles) {
|
|
956
1287
|
this.loadSoundfile(allocator, baseDSP, name, url);
|
|
957
|
-
}
|
|
1288
|
+
}
|
|
958
1289
|
}
|
|
959
1290
|
|
|
960
1291
|
protected updateOutputs() {
|
|
961
|
-
if (
|
|
1292
|
+
if (
|
|
1293
|
+
this.fOutputsItems.length > 0 &&
|
|
1294
|
+
this.fOutputHandler &&
|
|
1295
|
+
this.fOutputsTimer-- === 0
|
|
1296
|
+
) {
|
|
962
1297
|
this.fOutputsTimer = 5;
|
|
963
|
-
this.fOutputsItems.forEach(item =>
|
|
1298
|
+
this.fOutputsItems.forEach((item) =>
|
|
1299
|
+
this.fOutputHandler?.(item, this.getParamValue(item))
|
|
1300
|
+
);
|
|
964
1301
|
}
|
|
965
1302
|
}
|
|
966
1303
|
|
|
967
1304
|
// Public API
|
|
968
1305
|
metadata(handler: MetadataHandler) {
|
|
969
1306
|
if (this.fJSONDsp.meta) {
|
|
970
|
-
this.fJSONDsp.meta.forEach(
|
|
1307
|
+
this.fJSONDsp.meta.forEach((meta) =>
|
|
1308
|
+
handler(Object.keys(meta)[0], meta[Object.keys(meta)[0]])
|
|
1309
|
+
);
|
|
971
1310
|
}
|
|
972
1311
|
}
|
|
973
1312
|
|
|
@@ -985,7 +1324,7 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
985
1324
|
if (this.fOutputHandler) {
|
|
986
1325
|
this.fOutputHandler(path, value);
|
|
987
1326
|
} else {
|
|
988
|
-
console.warn(
|
|
1327
|
+
console.warn('No OutputParamHandler set for this Faust node.');
|
|
989
1328
|
}
|
|
990
1329
|
}
|
|
991
1330
|
|
|
@@ -1011,18 +1350,16 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
1011
1350
|
}
|
|
1012
1351
|
|
|
1013
1352
|
midiMessage(data: number[] | Uint8Array) {
|
|
1014
|
-
if (this.fPlotHandler) this.fCachedEvents.push({ data, type:
|
|
1353
|
+
if (this.fPlotHandler) this.fCachedEvents.push({ data, type: 'midi' });
|
|
1015
1354
|
const cmd = data[0] >> 4;
|
|
1016
1355
|
const channel = data[0] & 0xf;
|
|
1017
1356
|
const data1 = data[1];
|
|
1018
1357
|
const data2 = data[2];
|
|
1019
1358
|
if (cmd === 11) return this.ctrlChange(channel, data1, data2);
|
|
1020
|
-
if (cmd === 14) return this.pitchWheel(channel,
|
|
1359
|
+
if (cmd === 14) return this.pitchWheel(channel, data2 * 128.0 + data1);
|
|
1021
1360
|
if (cmd === 9) {
|
|
1022
|
-
if (data2 > 0)
|
|
1023
|
-
|
|
1024
|
-
else
|
|
1025
|
-
return this.keyOff(channel, data1, data2);
|
|
1361
|
+
if (data2 > 0) return this.keyOn(channel, data1, data2);
|
|
1362
|
+
else return this.keyOff(channel, data1, data2);
|
|
1026
1363
|
}
|
|
1027
1364
|
if (cmd === 8) {
|
|
1028
1365
|
return this.keyOff(channel, data1, data2);
|
|
@@ -1030,47 +1367,99 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
1030
1367
|
}
|
|
1031
1368
|
|
|
1032
1369
|
ctrlChange(channel: number, ctrl: number, value: number) {
|
|
1033
|
-
if (this.fPlotHandler)
|
|
1370
|
+
if (this.fPlotHandler)
|
|
1371
|
+
this.fCachedEvents.push({
|
|
1372
|
+
type: 'ctrlChange',
|
|
1373
|
+
data: [channel, ctrl, value]
|
|
1374
|
+
});
|
|
1034
1375
|
if (this.fCtrlLabel[ctrl].length) {
|
|
1035
1376
|
this.fCtrlLabel[ctrl].forEach((ctrl) => {
|
|
1036
1377
|
const { path, chan } = ctrl;
|
|
1037
1378
|
if (chan === 0 || channel === chan - 1) {
|
|
1038
|
-
this.setParamValue(
|
|
1379
|
+
this.setParamValue(
|
|
1380
|
+
path,
|
|
1381
|
+
FaustBaseWebAudioDsp.remap(
|
|
1382
|
+
value,
|
|
1383
|
+
0,
|
|
1384
|
+
127,
|
|
1385
|
+
ctrl.min,
|
|
1386
|
+
ctrl.max
|
|
1387
|
+
)
|
|
1388
|
+
);
|
|
1039
1389
|
// Typically used to reflect parameter change on GUI
|
|
1040
|
-
if (this.fOutputHandler)
|
|
1390
|
+
if (this.fOutputHandler)
|
|
1391
|
+
this.fOutputHandler(path, this.getParamValue(path));
|
|
1041
1392
|
}
|
|
1042
1393
|
});
|
|
1043
1394
|
}
|
|
1044
1395
|
}
|
|
1045
1396
|
|
|
1046
1397
|
keyOn(channel: number, pitch: number, velocity: number) {
|
|
1047
|
-
if (this.fPlotHandler)
|
|
1398
|
+
if (this.fPlotHandler)
|
|
1399
|
+
this.fCachedEvents.push({
|
|
1400
|
+
type: 'keyOn',
|
|
1401
|
+
data: [channel, pitch, velocity]
|
|
1402
|
+
});
|
|
1048
1403
|
this.fMidiKeyOnLabel[pitch].forEach((key) => {
|
|
1049
1404
|
const { path, chan } = key;
|
|
1050
1405
|
if (chan === 0 || channel === chan - 1) {
|
|
1051
|
-
this.setParamValue(
|
|
1406
|
+
this.setParamValue(
|
|
1407
|
+
path,
|
|
1408
|
+
FaustBaseWebAudioDsp.remap(
|
|
1409
|
+
velocity,
|
|
1410
|
+
0,
|
|
1411
|
+
127,
|
|
1412
|
+
key.min,
|
|
1413
|
+
key.max
|
|
1414
|
+
)
|
|
1415
|
+
);
|
|
1052
1416
|
// Typically used to reflect parameter change on GUI
|
|
1053
|
-
if (this.fOutputHandler)
|
|
1417
|
+
if (this.fOutputHandler)
|
|
1418
|
+
this.fOutputHandler(path, this.getParamValue(path));
|
|
1054
1419
|
}
|
|
1055
1420
|
});
|
|
1056
1421
|
this.fMidiKeyLabel[pitch].forEach((key) => {
|
|
1057
1422
|
const { path, chan } = key;
|
|
1058
1423
|
if (chan === 0 || channel === chan - 1) {
|
|
1059
|
-
this.setParamValue(
|
|
1424
|
+
this.setParamValue(
|
|
1425
|
+
path,
|
|
1426
|
+
FaustBaseWebAudioDsp.remap(
|
|
1427
|
+
velocity,
|
|
1428
|
+
0,
|
|
1429
|
+
127,
|
|
1430
|
+
key.min,
|
|
1431
|
+
key.max
|
|
1432
|
+
)
|
|
1433
|
+
);
|
|
1060
1434
|
// Typically used to reflect parameter change on GUI
|
|
1061
|
-
if (this.fOutputHandler)
|
|
1435
|
+
if (this.fOutputHandler)
|
|
1436
|
+
this.fOutputHandler(path, this.getParamValue(path));
|
|
1062
1437
|
}
|
|
1063
1438
|
});
|
|
1064
1439
|
}
|
|
1065
1440
|
|
|
1066
1441
|
keyOff(channel: number, pitch: number, velocity: number) {
|
|
1067
|
-
if (this.fPlotHandler)
|
|
1442
|
+
if (this.fPlotHandler)
|
|
1443
|
+
this.fCachedEvents.push({
|
|
1444
|
+
type: 'keyOff',
|
|
1445
|
+
data: [channel, pitch, velocity]
|
|
1446
|
+
});
|
|
1068
1447
|
this.fMidiKeyOffLabel[pitch].forEach((key) => {
|
|
1069
1448
|
const { path, chan } = key;
|
|
1070
1449
|
if (chan === 0 || channel === chan - 1) {
|
|
1071
|
-
this.setParamValue(
|
|
1450
|
+
this.setParamValue(
|
|
1451
|
+
path,
|
|
1452
|
+
FaustBaseWebAudioDsp.remap(
|
|
1453
|
+
velocity,
|
|
1454
|
+
0,
|
|
1455
|
+
127,
|
|
1456
|
+
key.min,
|
|
1457
|
+
key.max
|
|
1458
|
+
)
|
|
1459
|
+
);
|
|
1072
1460
|
// Typically used to reflect parameter change on GUI
|
|
1073
|
-
if (this.fOutputHandler)
|
|
1461
|
+
if (this.fOutputHandler)
|
|
1462
|
+
this.fOutputHandler(path, this.getParamValue(path));
|
|
1074
1463
|
}
|
|
1075
1464
|
});
|
|
1076
1465
|
this.fMidiKeyLabel[pitch].forEach((key) => {
|
|
@@ -1078,33 +1467,56 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
1078
1467
|
if (chan === 0 || channel === chan - 1) {
|
|
1079
1468
|
this.setParamValue(path, 0);
|
|
1080
1469
|
// Typically used to reflect parameter change on GUI
|
|
1081
|
-
if (this.fOutputHandler)
|
|
1470
|
+
if (this.fOutputHandler)
|
|
1471
|
+
this.fOutputHandler(path, this.getParamValue(path));
|
|
1082
1472
|
}
|
|
1083
1473
|
});
|
|
1084
1474
|
}
|
|
1085
1475
|
|
|
1086
1476
|
pitchWheel(channel: number, wheel: number) {
|
|
1087
|
-
if (this.fPlotHandler)
|
|
1477
|
+
if (this.fPlotHandler)
|
|
1478
|
+
this.fCachedEvents.push({
|
|
1479
|
+
type: 'pitchWheel',
|
|
1480
|
+
data: [channel, wheel]
|
|
1481
|
+
});
|
|
1088
1482
|
this.fPitchwheelLabel.forEach((pw) => {
|
|
1089
1483
|
const { path, chan } = pw;
|
|
1090
1484
|
if (chan === 0 || channel === chan - 1) {
|
|
1091
|
-
this.setParamValue(
|
|
1485
|
+
this.setParamValue(
|
|
1486
|
+
path,
|
|
1487
|
+
FaustBaseWebAudioDsp.remap(wheel, 0, 16383, pw.min, pw.max)
|
|
1488
|
+
);
|
|
1092
1489
|
// Typically used to reflect parameter change on GUI
|
|
1093
|
-
if (this.fOutputHandler)
|
|
1490
|
+
if (this.fOutputHandler)
|
|
1491
|
+
this.fOutputHandler(path, this.getParamValue(path));
|
|
1094
1492
|
}
|
|
1095
1493
|
});
|
|
1096
1494
|
}
|
|
1097
1495
|
|
|
1098
|
-
setParamValue(path: string, value: number) {
|
|
1099
|
-
getParamValue(path: string) {
|
|
1496
|
+
setParamValue(path: string, value: number) {}
|
|
1497
|
+
getParamValue(path: string) {
|
|
1498
|
+
return 0;
|
|
1499
|
+
}
|
|
1100
1500
|
|
|
1101
|
-
getParams() {
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1501
|
+
getParams() {
|
|
1502
|
+
return this.fInputsItems;
|
|
1503
|
+
}
|
|
1504
|
+
getMeta() {
|
|
1505
|
+
return this.fJSONDsp;
|
|
1506
|
+
}
|
|
1507
|
+
getJSON() {
|
|
1508
|
+
return JSON.stringify(this.getMeta());
|
|
1509
|
+
}
|
|
1510
|
+
getUI() {
|
|
1511
|
+
return this.fJSONDsp.ui;
|
|
1512
|
+
}
|
|
1513
|
+
getDescriptors() {
|
|
1514
|
+
return this.fDescriptor;
|
|
1515
|
+
}
|
|
1106
1516
|
|
|
1107
|
-
hasSoundfiles() {
|
|
1517
|
+
hasSoundfiles() {
|
|
1518
|
+
return this.fSoundfiles.length > 0;
|
|
1519
|
+
}
|
|
1108
1520
|
|
|
1109
1521
|
startSensors(): void {
|
|
1110
1522
|
this.startSensors();
|
|
@@ -1128,16 +1540,22 @@ export class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
|
|
|
1128
1540
|
this.fComputeHandler = null;
|
|
1129
1541
|
this.fPlotHandler = null;
|
|
1130
1542
|
}
|
|
1131
|
-
|
|
1132
1543
|
}
|
|
1133
1544
|
|
|
1134
|
-
export class FaustMonoWebAudioDsp
|
|
1135
|
-
|
|
1545
|
+
export class FaustMonoWebAudioDsp
|
|
1546
|
+
extends FaustBaseWebAudioDsp
|
|
1547
|
+
implements IFaustMonoWebAudioDsp
|
|
1548
|
+
{
|
|
1136
1549
|
private fInstance: FaustMonoDspInstance;
|
|
1137
1550
|
private fDSP!: number;
|
|
1138
1551
|
|
|
1139
|
-
constructor(
|
|
1140
|
-
|
|
1552
|
+
constructor(
|
|
1553
|
+
instance: FaustMonoDspInstance,
|
|
1554
|
+
sampleRate: number,
|
|
1555
|
+
sampleSize: number,
|
|
1556
|
+
bufferSize: number,
|
|
1557
|
+
soundfiles: LooseFaustDspFactory['soundfiles']
|
|
1558
|
+
) {
|
|
1141
1559
|
super(sampleSize, bufferSize, soundfiles);
|
|
1142
1560
|
this.fInstance = instance;
|
|
1143
1561
|
|
|
@@ -1157,9 +1575,11 @@ export class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1157
1575
|
|
|
1158
1576
|
// Init soundfiles memory is needed
|
|
1159
1577
|
if (this.fSoundfiles.length > 0) {
|
|
1160
|
-
|
|
1161
1578
|
// Create memory allocator for soundfiles in wasm memory, starting at the end of DSP memory
|
|
1162
|
-
const allocator = new WasmAllocator(
|
|
1579
|
+
const allocator = new WasmAllocator(
|
|
1580
|
+
this.fInstance.memory,
|
|
1581
|
+
this.fEndMemory
|
|
1582
|
+
);
|
|
1163
1583
|
|
|
1164
1584
|
// Init soundfiles memory
|
|
1165
1585
|
this.initSoundfileMemory(allocator, this.fDSP);
|
|
@@ -1167,7 +1587,6 @@ export class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1167
1587
|
}
|
|
1168
1588
|
|
|
1169
1589
|
private initMemory(): number {
|
|
1170
|
-
|
|
1171
1590
|
// Start of DSP memory: Mono DSP is placed first with index 0
|
|
1172
1591
|
this.fDSP = 0;
|
|
1173
1592
|
|
|
@@ -1176,37 +1595,62 @@ export class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1176
1595
|
|
|
1177
1596
|
// Setup audio pointers offset
|
|
1178
1597
|
this.fAudioInputs = $audio;
|
|
1179
|
-
this.fAudioOutputs =
|
|
1598
|
+
this.fAudioOutputs =
|
|
1599
|
+
this.fAudioInputs + this.getNumInputs() * this.fPtrSize;
|
|
1180
1600
|
|
|
1181
1601
|
// Prepare wasm memory layout
|
|
1182
|
-
const $audioInputs =
|
|
1183
|
-
|
|
1602
|
+
const $audioInputs =
|
|
1603
|
+
this.fAudioOutputs + this.getNumOutputs() * this.fPtrSize;
|
|
1604
|
+
const $audioOutputs =
|
|
1605
|
+
$audioInputs +
|
|
1606
|
+
this.getNumInputs() * this.fBufferSize * this.fSampleSize;
|
|
1184
1607
|
// Compute memory end in bytes
|
|
1185
|
-
const endMemory =
|
|
1608
|
+
const endMemory =
|
|
1609
|
+
$audioOutputs +
|
|
1610
|
+
this.getNumOutputs() * this.fBufferSize * this.fSampleSize;
|
|
1186
1611
|
|
|
1187
1612
|
// Setup Int32 and Real views of the memory
|
|
1188
1613
|
const HEAP = this.fInstance.memory.buffer;
|
|
1189
1614
|
const HEAP32 = new Int32Array(HEAP);
|
|
1190
|
-
const HEAPF =
|
|
1615
|
+
const HEAPF =
|
|
1616
|
+
this.fSampleSize === 4
|
|
1617
|
+
? new Float32Array(HEAP)
|
|
1618
|
+
: new Float64Array(HEAP);
|
|
1191
1619
|
|
|
1192
1620
|
if (this.getNumInputs() > 0) {
|
|
1193
1621
|
for (let chan = 0; chan < this.getNumInputs(); chan++) {
|
|
1194
|
-
HEAP32[(this.fAudioInputs >> 2) + chan] =
|
|
1622
|
+
HEAP32[(this.fAudioInputs >> 2) + chan] =
|
|
1623
|
+
$audioInputs + this.fBufferSize * this.fSampleSize * chan;
|
|
1195
1624
|
}
|
|
1196
1625
|
// Prepare Ins buffer tables
|
|
1197
|
-
const dspInChans = HEAP32.subarray(
|
|
1626
|
+
const dspInChans = HEAP32.subarray(
|
|
1627
|
+
this.fAudioInputs >> 2,
|
|
1628
|
+
(this.fAudioInputs + this.getNumInputs() * this.fPtrSize) >> 2
|
|
1629
|
+
);
|
|
1198
1630
|
for (let chan = 0; chan < this.getNumInputs(); chan++) {
|
|
1199
|
-
this.fInChannels[chan] = HEAPF.subarray(
|
|
1631
|
+
this.fInChannels[chan] = HEAPF.subarray(
|
|
1632
|
+
dspInChans[chan] >> Math.log2(this.fSampleSize),
|
|
1633
|
+
(dspInChans[chan] + this.fBufferSize * this.fSampleSize) >>
|
|
1634
|
+
Math.log2(this.fSampleSize)
|
|
1635
|
+
);
|
|
1200
1636
|
}
|
|
1201
1637
|
}
|
|
1202
1638
|
if (this.getNumOutputs() > 0) {
|
|
1203
1639
|
for (let chan = 0; chan < this.getNumOutputs(); chan++) {
|
|
1204
|
-
HEAP32[(this.fAudioOutputs >> 2) + chan] =
|
|
1640
|
+
HEAP32[(this.fAudioOutputs >> 2) + chan] =
|
|
1641
|
+
$audioOutputs + this.fBufferSize * this.fSampleSize * chan;
|
|
1205
1642
|
}
|
|
1206
1643
|
// Prepare Out buffer tables
|
|
1207
|
-
const dspOutChans = HEAP32.subarray(
|
|
1644
|
+
const dspOutChans = HEAP32.subarray(
|
|
1645
|
+
this.fAudioOutputs >> 2,
|
|
1646
|
+
(this.fAudioOutputs + this.getNumOutputs() * this.fPtrSize) >> 2
|
|
1647
|
+
);
|
|
1208
1648
|
for (let chan = 0; chan < this.getNumOutputs(); chan++) {
|
|
1209
|
-
this.fOutChannels[chan] = HEAPF.subarray(
|
|
1649
|
+
this.fOutChannels[chan] = HEAPF.subarray(
|
|
1650
|
+
dspOutChans[chan] >> Math.log2(this.fSampleSize),
|
|
1651
|
+
(dspOutChans[chan] + this.fBufferSize * this.fSampleSize) >>
|
|
1652
|
+
Math.log2(this.fSampleSize)
|
|
1653
|
+
);
|
|
1210
1654
|
}
|
|
1211
1655
|
}
|
|
1212
1656
|
|
|
@@ -1223,8 +1667,14 @@ export class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1223
1667
|
}
|
|
1224
1668
|
|
|
1225
1669
|
// Public API
|
|
1226
|
-
compute(
|
|
1227
|
-
|
|
1670
|
+
compute(
|
|
1671
|
+
input:
|
|
1672
|
+
| Float32Array[]
|
|
1673
|
+
| ((input: Float32Array[] | Float64Array[]) => any),
|
|
1674
|
+
output:
|
|
1675
|
+
| Float32Array[]
|
|
1676
|
+
| ((output: Float32Array[] | Float64Array[]) => any)
|
|
1677
|
+
) {
|
|
1228
1678
|
// Check DSP state
|
|
1229
1679
|
if (this.fDestroyed) return false;
|
|
1230
1680
|
|
|
@@ -1237,25 +1687,36 @@ export class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1237
1687
|
this.fFirstCall = false;
|
|
1238
1688
|
}
|
|
1239
1689
|
|
|
1240
|
-
if (typeof input ===
|
|
1690
|
+
if (typeof input === 'function') {
|
|
1241
1691
|
// Call input callback to avoid array copy
|
|
1242
1692
|
input(this.fInChannels);
|
|
1243
1693
|
} else {
|
|
1244
1694
|
// Check inputs
|
|
1245
|
-
if (
|
|
1695
|
+
if (
|
|
1696
|
+
this.getNumInputs() > 0 &&
|
|
1697
|
+
(!input || !input[0] || input[0].length === 0)
|
|
1698
|
+
) {
|
|
1246
1699
|
// console.log("Process input error");
|
|
1247
1700
|
return true;
|
|
1248
1701
|
}
|
|
1249
1702
|
|
|
1250
1703
|
// Check outputs
|
|
1251
|
-
if (
|
|
1704
|
+
if (
|
|
1705
|
+
this.getNumOutputs() > 0 &&
|
|
1706
|
+
typeof output !== 'function' &&
|
|
1707
|
+
(!output || !output[0] || output[0].length === 0)
|
|
1708
|
+
) {
|
|
1252
1709
|
// console.log("Process output error");
|
|
1253
1710
|
return true;
|
|
1254
1711
|
}
|
|
1255
1712
|
|
|
1256
1713
|
// Copy inputs
|
|
1257
1714
|
if (input !== undefined) {
|
|
1258
|
-
for (
|
|
1715
|
+
for (
|
|
1716
|
+
let chan = 0;
|
|
1717
|
+
chan < Math.min(this.getNumInputs(), input.length);
|
|
1718
|
+
chan++
|
|
1719
|
+
) {
|
|
1259
1720
|
const dspInput = this.fInChannels[chan];
|
|
1260
1721
|
dspInput.set(input[chan]);
|
|
1261
1722
|
}
|
|
@@ -1265,18 +1726,27 @@ export class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1265
1726
|
if (this.fComputeHandler) this.fComputeHandler(this.fBufferSize);
|
|
1266
1727
|
|
|
1267
1728
|
// Compute
|
|
1268
|
-
this.fInstance.api.compute(
|
|
1729
|
+
this.fInstance.api.compute(
|
|
1730
|
+
this.fDSP,
|
|
1731
|
+
this.fBufferSize,
|
|
1732
|
+
this.fAudioInputs,
|
|
1733
|
+
this.fAudioOutputs
|
|
1734
|
+
);
|
|
1269
1735
|
|
|
1270
1736
|
// Update bargraph
|
|
1271
1737
|
this.updateOutputs();
|
|
1272
1738
|
|
|
1273
1739
|
let forPlot = this.fOutChannels;
|
|
1274
|
-
if (typeof output ===
|
|
1740
|
+
if (typeof output === 'function') {
|
|
1275
1741
|
// Call output callback to avoid array copy
|
|
1276
1742
|
output(this.fOutChannels);
|
|
1277
1743
|
} else {
|
|
1278
1744
|
// Copy outputs
|
|
1279
|
-
for (
|
|
1745
|
+
for (
|
|
1746
|
+
let chan = 0;
|
|
1747
|
+
chan < Math.min(this.getNumOutputs(), output.length);
|
|
1748
|
+
chan++
|
|
1749
|
+
) {
|
|
1280
1750
|
const dspOutput = this.fOutChannels[chan];
|
|
1281
1751
|
output[chan].set(dspOutput);
|
|
1282
1752
|
// console.log("chan: " + chan + " output: " + dspOutput[0]);
|
|
@@ -1284,16 +1754,22 @@ export class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1284
1754
|
forPlot = output;
|
|
1285
1755
|
}
|
|
1286
1756
|
|
|
1287
|
-
// PlotHandler handling
|
|
1757
|
+
// PlotHandler handling
|
|
1288
1758
|
if (this.fPlotHandler) {
|
|
1289
|
-
this.fPlotHandler(
|
|
1759
|
+
this.fPlotHandler(
|
|
1760
|
+
forPlot,
|
|
1761
|
+
this.fBufferNum++,
|
|
1762
|
+
this.fCachedEvents.length ? this.fCachedEvents : undefined
|
|
1763
|
+
);
|
|
1290
1764
|
this.fCachedEvents = [];
|
|
1291
1765
|
}
|
|
1292
1766
|
|
|
1293
1767
|
return true;
|
|
1294
1768
|
}
|
|
1295
1769
|
|
|
1296
|
-
metadata(handler: MetadataHandler) {
|
|
1770
|
+
metadata(handler: MetadataHandler) {
|
|
1771
|
+
super.metadata(handler);
|
|
1772
|
+
}
|
|
1297
1773
|
|
|
1298
1774
|
getNumInputs() {
|
|
1299
1775
|
return this.fInstance.api.getNumInputs(this.fDSP);
|
|
@@ -1303,34 +1779,62 @@ export class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1303
1779
|
}
|
|
1304
1780
|
|
|
1305
1781
|
setParamValue(path: string, value: number) {
|
|
1306
|
-
if (this.fPlotHandler)
|
|
1307
|
-
|
|
1782
|
+
if (this.fPlotHandler)
|
|
1783
|
+
this.fCachedEvents.push({ type: 'param', data: { path, value } });
|
|
1784
|
+
this.fInstance.api.setParamValue(
|
|
1785
|
+
this.fDSP,
|
|
1786
|
+
this.fPathTable[path],
|
|
1787
|
+
value
|
|
1788
|
+
);
|
|
1308
1789
|
}
|
|
1309
1790
|
getParamValue(path: string) {
|
|
1310
|
-
return this.fInstance.api.getParamValue(
|
|
1791
|
+
return this.fInstance.api.getParamValue(
|
|
1792
|
+
this.fDSP,
|
|
1793
|
+
this.fPathTable[path]
|
|
1794
|
+
);
|
|
1311
1795
|
}
|
|
1312
1796
|
|
|
1313
|
-
getMeta() {
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1797
|
+
getMeta() {
|
|
1798
|
+
return this.fJSONDsp;
|
|
1799
|
+
}
|
|
1800
|
+
getJSON() {
|
|
1801
|
+
return this.fInstance.json;
|
|
1802
|
+
}
|
|
1803
|
+
getDescriptors() {
|
|
1804
|
+
return this.fDescriptor;
|
|
1805
|
+
}
|
|
1806
|
+
getUI() {
|
|
1807
|
+
return this.fJSONDsp.ui;
|
|
1808
|
+
}
|
|
1317
1809
|
}
|
|
1318
1810
|
|
|
1319
1811
|
export class FaustWebAudioDspVoice {
|
|
1320
1812
|
// Voice state
|
|
1321
|
-
static get kActiveVoice() {
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
static get
|
|
1325
|
-
|
|
1326
|
-
|
|
1813
|
+
static get kActiveVoice() {
|
|
1814
|
+
return 0;
|
|
1815
|
+
}
|
|
1816
|
+
static get kFreeVoice() {
|
|
1817
|
+
return -1;
|
|
1818
|
+
}
|
|
1819
|
+
static get kReleaseVoice() {
|
|
1820
|
+
return -2;
|
|
1821
|
+
}
|
|
1822
|
+
static get kLegatoVoice() {
|
|
1823
|
+
return -3;
|
|
1824
|
+
}
|
|
1825
|
+
static get kNoVoice() {
|
|
1826
|
+
return -4;
|
|
1827
|
+
}
|
|
1828
|
+
static get VOICE_STOP_LEVEL() {
|
|
1829
|
+
return 0.00003162;
|
|
1830
|
+
} // -90 db
|
|
1327
1831
|
|
|
1328
1832
|
private fFreqLabel: number[] = [];
|
|
1329
1833
|
private fGateLabel: number[] = [];
|
|
1330
1834
|
private fGainLabel: number[] = [];
|
|
1331
1835
|
private fKeyLabel: number[] = [];
|
|
1332
1836
|
private fVelLabel: number[] = [];
|
|
1333
|
-
private fDSP: number;
|
|
1837
|
+
private fDSP: number; // Voice DSP location in wasm memory
|
|
1334
1838
|
private fAPI: IFaustDspInstance; // Voice DSP code
|
|
1335
1839
|
// Accessed by PolyDSPImp class
|
|
1336
1840
|
fCurNote = FaustWebAudioDspVoice.kFreeVoice;
|
|
@@ -1339,28 +1843,41 @@ export class FaustWebAudioDspVoice {
|
|
|
1339
1843
|
fDate = 0;
|
|
1340
1844
|
fLevel = 0;
|
|
1341
1845
|
|
|
1342
|
-
constructor(
|
|
1846
|
+
constructor(
|
|
1847
|
+
$dsp: number,
|
|
1848
|
+
api: IFaustDspInstance,
|
|
1849
|
+
inputItems: string[],
|
|
1850
|
+
pathTable: { [address: string]: number },
|
|
1851
|
+
sampleRate: number
|
|
1852
|
+
) {
|
|
1343
1853
|
this.fDSP = $dsp;
|
|
1344
1854
|
this.fAPI = api;
|
|
1345
1855
|
this.fAPI.init(this.fDSP, sampleRate);
|
|
1346
1856
|
this.extractPaths(inputItems, pathTable);
|
|
1347
1857
|
}
|
|
1348
1858
|
|
|
1349
|
-
static midiToFreq(note: number) {
|
|
1859
|
+
static midiToFreq(note: number) {
|
|
1860
|
+
return 440.0 * 2 ** ((note - 69) / 12);
|
|
1861
|
+
}
|
|
1350
1862
|
|
|
1351
|
-
static normalizeVelocity(velocity: number) {
|
|
1863
|
+
static normalizeVelocity(velocity: number) {
|
|
1864
|
+
return velocity / 127.0;
|
|
1865
|
+
}
|
|
1352
1866
|
|
|
1353
|
-
private extractPaths(
|
|
1867
|
+
private extractPaths(
|
|
1868
|
+
inputItems: string[],
|
|
1869
|
+
pathTable: { [address: string]: number }
|
|
1870
|
+
) {
|
|
1354
1871
|
inputItems.forEach((item) => {
|
|
1355
|
-
if (item.endsWith(
|
|
1872
|
+
if (item.endsWith('/gate')) {
|
|
1356
1873
|
this.fGateLabel.push(pathTable[item]);
|
|
1357
|
-
} else if (item.endsWith(
|
|
1874
|
+
} else if (item.endsWith('/freq')) {
|
|
1358
1875
|
this.fFreqLabel.push(pathTable[item]);
|
|
1359
|
-
} else if (item.endsWith(
|
|
1876
|
+
} else if (item.endsWith('/key')) {
|
|
1360
1877
|
this.fKeyLabel.push(pathTable[item]);
|
|
1361
|
-
} else if (item.endsWith(
|
|
1878
|
+
} else if (item.endsWith('/gain')) {
|
|
1362
1879
|
this.fGainLabel.push(pathTable[item]);
|
|
1363
|
-
} else if (item.endsWith(
|
|
1880
|
+
} else if (item.endsWith('/vel') && item.endsWith('/velocity')) {
|
|
1364
1881
|
this.fVelLabel.push(pathTable[item]);
|
|
1365
1882
|
}
|
|
1366
1883
|
});
|
|
@@ -1372,18 +1889,38 @@ export class FaustWebAudioDspVoice {
|
|
|
1372
1889
|
this.fNextNote = pitch;
|
|
1373
1890
|
this.fNextVel = velocity;
|
|
1374
1891
|
} else {
|
|
1375
|
-
this.fFreqLabel.forEach(index =>
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1892
|
+
this.fFreqLabel.forEach((index) =>
|
|
1893
|
+
this.fAPI.setParamValue(
|
|
1894
|
+
this.fDSP,
|
|
1895
|
+
index,
|
|
1896
|
+
FaustWebAudioDspVoice.midiToFreq(pitch)
|
|
1897
|
+
)
|
|
1898
|
+
);
|
|
1899
|
+
this.fGateLabel.forEach((index) =>
|
|
1900
|
+
this.fAPI.setParamValue(this.fDSP, index, 1)
|
|
1901
|
+
);
|
|
1902
|
+
this.fGainLabel.forEach((index) =>
|
|
1903
|
+
this.fAPI.setParamValue(
|
|
1904
|
+
this.fDSP,
|
|
1905
|
+
index,
|
|
1906
|
+
FaustWebAudioDspVoice.normalizeVelocity(velocity)
|
|
1907
|
+
)
|
|
1908
|
+
);
|
|
1909
|
+
this.fKeyLabel.forEach((index) =>
|
|
1910
|
+
this.fAPI.setParamValue(this.fDSP, index, pitch)
|
|
1911
|
+
);
|
|
1912
|
+
this.fVelLabel.forEach((index) =>
|
|
1913
|
+
this.fAPI.setParamValue(this.fDSP, index, velocity)
|
|
1914
|
+
);
|
|
1380
1915
|
// Keep pitch
|
|
1381
1916
|
this.fCurNote = pitch;
|
|
1382
1917
|
}
|
|
1383
1918
|
}
|
|
1384
1919
|
|
|
1385
1920
|
keyOff(hard: boolean = false) {
|
|
1386
|
-
this.fGateLabel.forEach(index =>
|
|
1921
|
+
this.fGateLabel.forEach((index) =>
|
|
1922
|
+
this.fAPI.setParamValue(this.fDSP, index, 0)
|
|
1923
|
+
);
|
|
1387
1924
|
if (hard) {
|
|
1388
1925
|
this.fCurNote = FaustWebAudioDspVoice.kFreeVoice;
|
|
1389
1926
|
} else {
|
|
@@ -1391,12 +1928,18 @@ export class FaustWebAudioDspVoice {
|
|
|
1391
1928
|
}
|
|
1392
1929
|
}
|
|
1393
1930
|
|
|
1394
|
-
computeLegato(
|
|
1395
|
-
|
|
1396
|
-
|
|
1931
|
+
computeLegato(
|
|
1932
|
+
bufferSize: number,
|
|
1933
|
+
$inputs: number,
|
|
1934
|
+
$outputZero: number,
|
|
1935
|
+
$outputsHalf: number
|
|
1936
|
+
) {
|
|
1937
|
+
const size = bufferSize / 2;
|
|
1397
1938
|
|
|
1398
1939
|
// Reset envelops
|
|
1399
|
-
this.fGateLabel.forEach(index =>
|
|
1940
|
+
this.fGateLabel.forEach((index) =>
|
|
1941
|
+
this.fAPI.setParamValue(this.fDSP, index, 0)
|
|
1942
|
+
);
|
|
1400
1943
|
|
|
1401
1944
|
// Compute current voice on half buffer
|
|
1402
1945
|
this.fAPI.compute(this.fDSP, size, $inputs, $outputZero);
|
|
@@ -1420,8 +1963,10 @@ export class FaustWebAudioDspVoice {
|
|
|
1420
1963
|
}
|
|
1421
1964
|
}
|
|
1422
1965
|
|
|
1423
|
-
export class FaustPolyWebAudioDsp
|
|
1424
|
-
|
|
1966
|
+
export class FaustPolyWebAudioDsp
|
|
1967
|
+
extends FaustBaseWebAudioDsp
|
|
1968
|
+
implements IFaustPolyWebAudioDsp
|
|
1969
|
+
{
|
|
1425
1970
|
private fInstance: FaustPolyDspInstance;
|
|
1426
1971
|
private fEffect!: number;
|
|
1427
1972
|
private fJSONEffect: FaustDspMeta | null;
|
|
@@ -1429,7 +1974,13 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1429
1974
|
private fAudioMixingHalf!: number;
|
|
1430
1975
|
private fVoiceTable: FaustWebAudioDspVoice[];
|
|
1431
1976
|
|
|
1432
|
-
constructor(
|
|
1977
|
+
constructor(
|
|
1978
|
+
instance: FaustPolyDspInstance,
|
|
1979
|
+
sampleRate: number,
|
|
1980
|
+
sampleSize: number,
|
|
1981
|
+
bufferSize: number,
|
|
1982
|
+
soundfiles: LooseFaustDspFactory['soundfiles']
|
|
1983
|
+
) {
|
|
1433
1984
|
super(sampleSize, bufferSize, soundfiles);
|
|
1434
1985
|
this.fInstance = instance;
|
|
1435
1986
|
|
|
@@ -1439,11 +1990,15 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1439
1990
|
this.fJSONDsp = JSON.parse(this.fInstance.voiceJSON);
|
|
1440
1991
|
|
|
1441
1992
|
// Create JSON for effect
|
|
1442
|
-
this.fJSONEffect =
|
|
1993
|
+
this.fJSONEffect =
|
|
1994
|
+
this.fInstance.effectAPI && this.fInstance.effectJSON
|
|
1995
|
+
? JSON.parse(this.fInstance.effectJSON)
|
|
1996
|
+
: null;
|
|
1443
1997
|
|
|
1444
1998
|
// Setup GUI
|
|
1445
1999
|
FaustBaseWebAudioDsp.parseUI(this.fJSONDsp.ui, this.fUICallback);
|
|
1446
|
-
if (this.fJSONEffect)
|
|
2000
|
+
if (this.fJSONEffect)
|
|
2001
|
+
FaustBaseWebAudioDsp.parseUI(this.fJSONEffect.ui, this.fUICallback);
|
|
1447
2002
|
|
|
1448
2003
|
// Setup wasm memory
|
|
1449
2004
|
this.fEndMemory = this.initMemory();
|
|
@@ -1451,22 +2006,28 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1451
2006
|
// Init DSP voices
|
|
1452
2007
|
this.fVoiceTable = [];
|
|
1453
2008
|
for (let voice = 0; voice < this.fInstance.voices; voice++) {
|
|
1454
|
-
this.fVoiceTable.push(
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
2009
|
+
this.fVoiceTable.push(
|
|
2010
|
+
new FaustWebAudioDspVoice(
|
|
2011
|
+
this.fJSONDsp.size * voice,
|
|
2012
|
+
this.fInstance.voiceAPI,
|
|
2013
|
+
this.fInputsItems,
|
|
2014
|
+
this.fPathTable,
|
|
2015
|
+
sampleRate
|
|
2016
|
+
)
|
|
2017
|
+
);
|
|
1461
2018
|
}
|
|
1462
2019
|
|
|
1463
2020
|
// Init effect
|
|
1464
|
-
if (this.fInstance.effectAPI)
|
|
2021
|
+
if (this.fInstance.effectAPI)
|
|
2022
|
+
this.fInstance.effectAPI.init(this.fEffect, sampleRate);
|
|
1465
2023
|
|
|
1466
2024
|
// Init soundfiles memory is needed
|
|
1467
2025
|
if (this.fSoundfiles.length > 0) {
|
|
1468
2026
|
// Create memory allocator for soundfiles in wasm memory, starting at the end of DSP memory
|
|
1469
|
-
const allocator = new WasmAllocator(
|
|
2027
|
+
const allocator = new WasmAllocator(
|
|
2028
|
+
this.fInstance.memory,
|
|
2029
|
+
this.fEndMemory
|
|
2030
|
+
);
|
|
1470
2031
|
// Init soundfiles memory for all voices
|
|
1471
2032
|
for (let voice = 0; voice < this.fInstance.voices; voice++) {
|
|
1472
2033
|
this.initSoundfileMemory(allocator, this.fJSONDsp.size * voice);
|
|
@@ -1475,52 +2036,85 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1475
2036
|
}
|
|
1476
2037
|
|
|
1477
2038
|
private initMemory() {
|
|
1478
|
-
|
|
1479
2039
|
// Effet start at the end of all DSP voices
|
|
1480
2040
|
this.fEffect = this.fJSONDsp.size * this.fInstance.voices;
|
|
1481
2041
|
|
|
1482
2042
|
// Audio buffer start at the end of effect
|
|
1483
|
-
const $audio =
|
|
2043
|
+
const $audio =
|
|
2044
|
+
this.fEffect + (this.fJSONEffect ? this.fJSONEffect.size : 0);
|
|
1484
2045
|
|
|
1485
2046
|
// Setup audio pointers offset
|
|
1486
2047
|
this.fAudioInputs = $audio;
|
|
1487
|
-
this.fAudioOutputs =
|
|
1488
|
-
|
|
1489
|
-
this.
|
|
2048
|
+
this.fAudioOutputs =
|
|
2049
|
+
this.fAudioInputs + this.getNumInputs() * this.fPtrSize;
|
|
2050
|
+
this.fAudioMixing =
|
|
2051
|
+
this.fAudioOutputs + this.getNumOutputs() * this.fPtrSize;
|
|
2052
|
+
this.fAudioMixingHalf =
|
|
2053
|
+
this.fAudioMixing + this.getNumOutputs() * this.fPtrSize;
|
|
1490
2054
|
|
|
1491
2055
|
// Prepare wasm memory layout
|
|
1492
|
-
const $audioInputs =
|
|
1493
|
-
|
|
1494
|
-
const $
|
|
2056
|
+
const $audioInputs =
|
|
2057
|
+
this.fAudioMixingHalf + this.getNumOutputs() * this.fPtrSize;
|
|
2058
|
+
const $audioOutputs =
|
|
2059
|
+
$audioInputs +
|
|
2060
|
+
this.getNumInputs() * this.fBufferSize * this.fSampleSize;
|
|
2061
|
+
const $audioMixing =
|
|
2062
|
+
$audioOutputs +
|
|
2063
|
+
this.getNumOutputs() * this.fBufferSize * this.fSampleSize;
|
|
1495
2064
|
|
|
1496
2065
|
// Compute memory end in bytes
|
|
1497
|
-
const endMemory =
|
|
2066
|
+
const endMemory =
|
|
2067
|
+
$audioMixing +
|
|
2068
|
+
this.getNumOutputs() * this.fBufferSize * this.fSampleSize;
|
|
1498
2069
|
|
|
1499
2070
|
// Setup Int32 and Real views of the memory
|
|
1500
2071
|
const HEAP = this.fInstance.memory.buffer;
|
|
1501
2072
|
const HEAP32 = new Int32Array(HEAP);
|
|
1502
|
-
const HEAPF =
|
|
2073
|
+
const HEAPF =
|
|
2074
|
+
this.fSampleSize === 4
|
|
2075
|
+
? new Float32Array(HEAP)
|
|
2076
|
+
: new Float64Array(HEAP);
|
|
1503
2077
|
|
|
1504
2078
|
if (this.getNumInputs() > 0) {
|
|
1505
2079
|
for (let chan = 0; chan < this.getNumInputs(); chan++) {
|
|
1506
|
-
HEAP32[(this.fAudioInputs >> 2) + chan] =
|
|
2080
|
+
HEAP32[(this.fAudioInputs >> 2) + chan] =
|
|
2081
|
+
$audioInputs + this.fBufferSize * this.fSampleSize * chan;
|
|
1507
2082
|
}
|
|
1508
2083
|
// Prepare Ins buffer tables
|
|
1509
|
-
const dspInChans = HEAP32.subarray(
|
|
2084
|
+
const dspInChans = HEAP32.subarray(
|
|
2085
|
+
this.fAudioInputs >> 2,
|
|
2086
|
+
(this.fAudioInputs + this.getNumInputs() * this.fPtrSize) >> 2
|
|
2087
|
+
);
|
|
1510
2088
|
for (let chan = 0; chan < this.getNumInputs(); chan++) {
|
|
1511
|
-
this.fInChannels[chan] = HEAPF.subarray(
|
|
2089
|
+
this.fInChannels[chan] = HEAPF.subarray(
|
|
2090
|
+
dspInChans[chan] >> Math.log2(this.fSampleSize),
|
|
2091
|
+
(dspInChans[chan] + this.fBufferSize * this.fSampleSize) >>
|
|
2092
|
+
Math.log2(this.fSampleSize)
|
|
2093
|
+
);
|
|
1512
2094
|
}
|
|
1513
2095
|
}
|
|
1514
2096
|
if (this.getNumOutputs() > 0) {
|
|
1515
2097
|
for (let chan = 0; chan < this.getNumOutputs(); chan++) {
|
|
1516
|
-
HEAP32[(this.fAudioOutputs >> 2) + chan] =
|
|
1517
|
-
|
|
1518
|
-
HEAP32[(this.
|
|
2098
|
+
HEAP32[(this.fAudioOutputs >> 2) + chan] =
|
|
2099
|
+
$audioOutputs + this.fBufferSize * this.fSampleSize * chan;
|
|
2100
|
+
HEAP32[(this.fAudioMixing >> 2) + chan] =
|
|
2101
|
+
$audioMixing + this.fBufferSize * this.fSampleSize * chan;
|
|
2102
|
+
HEAP32[(this.fAudioMixingHalf >> 2) + chan] =
|
|
2103
|
+
$audioMixing +
|
|
2104
|
+
this.fBufferSize * this.fSampleSize * chan +
|
|
2105
|
+
(this.fBufferSize / 2) * this.fSampleSize;
|
|
1519
2106
|
}
|
|
1520
2107
|
// Prepare Out buffer tables
|
|
1521
|
-
const dspOutChans = HEAP32.subarray(
|
|
2108
|
+
const dspOutChans = HEAP32.subarray(
|
|
2109
|
+
this.fAudioOutputs >> 2,
|
|
2110
|
+
(this.fAudioOutputs + this.getNumOutputs() * this.fPtrSize) >> 2
|
|
2111
|
+
);
|
|
1522
2112
|
for (let chan = 0; chan < this.getNumOutputs(); chan++) {
|
|
1523
|
-
this.fOutChannels[chan] = HEAPF.subarray(
|
|
2113
|
+
this.fOutChannels[chan] = HEAPF.subarray(
|
|
2114
|
+
dspOutChans[chan] >> Math.log2(this.fSampleSize),
|
|
2115
|
+
(dspOutChans[chan] + this.fBufferSize * this.fSampleSize) >>
|
|
2116
|
+
Math.log2(this.fSampleSize)
|
|
2117
|
+
);
|
|
1524
2118
|
}
|
|
1525
2119
|
}
|
|
1526
2120
|
|
|
@@ -1548,10 +2142,14 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1548
2142
|
let oldestDatePlaying = Number.MAX_VALUE;
|
|
1549
2143
|
|
|
1550
2144
|
for (let i = 0; i < this.fInstance.voices; i++) {
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
if (
|
|
2145
|
+
const curNote = this.fVoiceTable[i].fCurNote;
|
|
2146
|
+
const nextNote = this.fVoiceTable[i].fNextNote;
|
|
2147
|
+
|
|
2148
|
+
if (
|
|
2149
|
+
curNote === pitch ||
|
|
2150
|
+
(curNote === FaustWebAudioDspVoice.kLegatoVoice &&
|
|
2151
|
+
nextNote === pitch)
|
|
2152
|
+
) {
|
|
1555
2153
|
// Keeps oldest playing voice
|
|
1556
2154
|
if (this.fVoiceTable[i].fDate < oldestDatePlaying) {
|
|
1557
2155
|
oldestDatePlaying = this.fVoiceTable[i].fDate;
|
|
@@ -1564,8 +2162,14 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1564
2162
|
|
|
1565
2163
|
private getFreeVoice() {
|
|
1566
2164
|
for (let voice = 0; voice < this.fInstance.voices; voice++) {
|
|
1567
|
-
if (
|
|
1568
|
-
|
|
2165
|
+
if (
|
|
2166
|
+
this.fVoiceTable[voice].fCurNote ===
|
|
2167
|
+
FaustWebAudioDspVoice.kFreeVoice
|
|
2168
|
+
) {
|
|
2169
|
+
return this.allocVoice(
|
|
2170
|
+
voice,
|
|
2171
|
+
FaustWebAudioDspVoice.kActiveVoice
|
|
2172
|
+
);
|
|
1569
2173
|
}
|
|
1570
2174
|
}
|
|
1571
2175
|
|
|
@@ -1574,9 +2178,13 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1574
2178
|
let oldestDateRelease = Number.MAX_VALUE;
|
|
1575
2179
|
let oldestDatePlaying = Number.MAX_VALUE;
|
|
1576
2180
|
|
|
1577
|
-
for (let voice = 0; voice < this.fInstance.voices; voice++) {
|
|
2181
|
+
for (let voice = 0; voice < this.fInstance.voices; voice++) {
|
|
2182
|
+
// Scan all voices
|
|
1578
2183
|
// Try to steal a voice in DspVoice.kReleaseVoice mode...
|
|
1579
|
-
if (
|
|
2184
|
+
if (
|
|
2185
|
+
this.fVoiceTable[voice].fCurNote ===
|
|
2186
|
+
FaustWebAudioDspVoice.kReleaseVoice
|
|
2187
|
+
) {
|
|
1580
2188
|
// Keeps oldest release voice
|
|
1581
2189
|
if (this.fVoiceTable[voice].fDate < oldestDateRelease) {
|
|
1582
2190
|
oldestDateRelease = this.fVoiceTable[voice].fDate;
|
|
@@ -1589,19 +2197,28 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1589
2197
|
}
|
|
1590
2198
|
// Then decide which one to steal
|
|
1591
2199
|
if (oldestDateRelease !== Number.MAX_VALUE) {
|
|
1592
|
-
console.log(
|
|
1593
|
-
|
|
2200
|
+
console.log(
|
|
2201
|
+
`Steal release voice : voice_date = ${this.fVoiceTable[voiceRelease].fDate} voice = ${voiceRelease}`
|
|
2202
|
+
);
|
|
2203
|
+
return this.allocVoice(
|
|
2204
|
+
voiceRelease,
|
|
2205
|
+
FaustWebAudioDspVoice.kLegatoVoice
|
|
2206
|
+
);
|
|
1594
2207
|
}
|
|
1595
2208
|
if (oldestDatePlaying !== Number.MAX_VALUE) {
|
|
1596
|
-
console.log(
|
|
1597
|
-
|
|
2209
|
+
console.log(
|
|
2210
|
+
`Steal playing voice : voice_date = ${this.fVoiceTable[voicePlaying].fDate} voice = ${voicePlaying}`
|
|
2211
|
+
);
|
|
2212
|
+
return this.allocVoice(
|
|
2213
|
+
voicePlaying,
|
|
2214
|
+
FaustWebAudioDspVoice.kLegatoVoice
|
|
2215
|
+
);
|
|
1598
2216
|
}
|
|
1599
2217
|
return FaustWebAudioDspVoice.kNoVoice;
|
|
1600
2218
|
}
|
|
1601
2219
|
|
|
1602
2220
|
// Public API
|
|
1603
2221
|
compute(input: Float32Array[], output: Float32Array[]) {
|
|
1604
|
-
|
|
1605
2222
|
// Check DSP state
|
|
1606
2223
|
if (this.fDestroyed) return false;
|
|
1607
2224
|
|
|
@@ -1615,20 +2232,30 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1615
2232
|
if (!this.fProcessing) return true;
|
|
1616
2233
|
|
|
1617
2234
|
// Check inputs
|
|
1618
|
-
if (
|
|
2235
|
+
if (
|
|
2236
|
+
this.getNumInputs() > 0 &&
|
|
2237
|
+
(!input || !input[0] || input[0].length === 0)
|
|
2238
|
+
) {
|
|
1619
2239
|
// console.log("Process input error");
|
|
1620
2240
|
return true;
|
|
1621
2241
|
}
|
|
1622
2242
|
|
|
1623
2243
|
// Check outputs
|
|
1624
|
-
if (
|
|
2244
|
+
if (
|
|
2245
|
+
this.getNumOutputs() > 0 &&
|
|
2246
|
+
(!output || !output[0] || output[0].length === 0)
|
|
2247
|
+
) {
|
|
1625
2248
|
// console.log("Process output error");
|
|
1626
2249
|
return true;
|
|
1627
2250
|
}
|
|
1628
2251
|
|
|
1629
2252
|
// Copy inputs
|
|
1630
2253
|
if (input !== undefined) {
|
|
1631
|
-
for (
|
|
2254
|
+
for (
|
|
2255
|
+
let chan = 0;
|
|
2256
|
+
chan < Math.min(this.getNumInputs(), input.length);
|
|
2257
|
+
++chan
|
|
2258
|
+
) {
|
|
1632
2259
|
const dspInput = this.fInChannels[chan];
|
|
1633
2260
|
dspInput.set(input[chan]);
|
|
1634
2261
|
}
|
|
@@ -1638,41 +2265,85 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1638
2265
|
if (this.fComputeHandler) this.fComputeHandler(this.fBufferSize);
|
|
1639
2266
|
|
|
1640
2267
|
// Compute
|
|
1641
|
-
this.fInstance.mixerAPI.clearOutput(
|
|
2268
|
+
this.fInstance.mixerAPI.clearOutput(
|
|
2269
|
+
this.fBufferSize,
|
|
2270
|
+
this.getNumOutputs(),
|
|
2271
|
+
this.fAudioOutputs
|
|
2272
|
+
);
|
|
1642
2273
|
this.fVoiceTable.forEach((voice) => {
|
|
1643
2274
|
if (voice.fCurNote === FaustWebAudioDspVoice.kLegatoVoice) {
|
|
1644
2275
|
// Play from current note and next note
|
|
1645
|
-
voice.computeLegato(
|
|
2276
|
+
voice.computeLegato(
|
|
2277
|
+
this.fBufferSize,
|
|
2278
|
+
this.fAudioInputs,
|
|
2279
|
+
this.fAudioMixing,
|
|
2280
|
+
this.fAudioMixingHalf
|
|
2281
|
+
);
|
|
1646
2282
|
// FadeOut on first half buffer
|
|
1647
|
-
this.fInstance.mixerAPI.fadeOut(
|
|
2283
|
+
this.fInstance.mixerAPI.fadeOut(
|
|
2284
|
+
this.fBufferSize / 2,
|
|
2285
|
+
this.getNumOutputs(),
|
|
2286
|
+
this.fAudioMixing
|
|
2287
|
+
);
|
|
1648
2288
|
// Mix it in result
|
|
1649
|
-
voice.fLevel = this.fInstance.mixerAPI.mixCheckVoice(
|
|
2289
|
+
voice.fLevel = this.fInstance.mixerAPI.mixCheckVoice(
|
|
2290
|
+
this.fBufferSize,
|
|
2291
|
+
this.getNumOutputs(),
|
|
2292
|
+
this.fAudioMixing,
|
|
2293
|
+
this.fAudioOutputs
|
|
2294
|
+
);
|
|
1650
2295
|
} else if (voice.fCurNote !== FaustWebAudioDspVoice.kFreeVoice) {
|
|
1651
2296
|
// Compute current note
|
|
1652
|
-
voice.compute(
|
|
2297
|
+
voice.compute(
|
|
2298
|
+
this.fBufferSize,
|
|
2299
|
+
this.fAudioInputs,
|
|
2300
|
+
this.fAudioMixing
|
|
2301
|
+
);
|
|
1653
2302
|
// Mix it in result
|
|
1654
|
-
voice.fLevel = this.fInstance.mixerAPI.mixCheckVoice(
|
|
2303
|
+
voice.fLevel = this.fInstance.mixerAPI.mixCheckVoice(
|
|
2304
|
+
this.fBufferSize,
|
|
2305
|
+
this.getNumOutputs(),
|
|
2306
|
+
this.fAudioMixing,
|
|
2307
|
+
this.fAudioOutputs
|
|
2308
|
+
);
|
|
1655
2309
|
// Check the level to possibly set the voice in kFreeVoice again
|
|
1656
|
-
if (
|
|
2310
|
+
if (
|
|
2311
|
+
voice.fCurNote == FaustWebAudioDspVoice.kReleaseVoice &&
|
|
2312
|
+
voice.fLevel < FaustWebAudioDspVoice.VOICE_STOP_LEVEL
|
|
2313
|
+
) {
|
|
1657
2314
|
voice.fCurNote = FaustWebAudioDspVoice.kFreeVoice;
|
|
1658
2315
|
}
|
|
1659
2316
|
}
|
|
1660
2317
|
});
|
|
1661
|
-
if (this.fInstance.effectAPI)
|
|
2318
|
+
if (this.fInstance.effectAPI)
|
|
2319
|
+
this.fInstance.effectAPI.compute(
|
|
2320
|
+
this.fEffect,
|
|
2321
|
+
this.fBufferSize,
|
|
2322
|
+
this.fAudioOutputs,
|
|
2323
|
+
this.fAudioOutputs
|
|
2324
|
+
);
|
|
1662
2325
|
|
|
1663
2326
|
// Update bargraph
|
|
1664
2327
|
this.updateOutputs();
|
|
1665
2328
|
|
|
1666
2329
|
if (output !== undefined) {
|
|
1667
2330
|
// Copy outputs
|
|
1668
|
-
for (
|
|
2331
|
+
for (
|
|
2332
|
+
let chan = 0;
|
|
2333
|
+
chan < Math.min(this.getNumOutputs(), output.length);
|
|
2334
|
+
chan++
|
|
2335
|
+
) {
|
|
1669
2336
|
const dspOutput = this.fOutChannels[chan];
|
|
1670
2337
|
output[chan].set(dspOutput);
|
|
1671
2338
|
}
|
|
1672
2339
|
|
|
1673
|
-
// PlotHandler handling
|
|
2340
|
+
// PlotHandler handling
|
|
1674
2341
|
if (this.fPlotHandler) {
|
|
1675
|
-
this.fPlotHandler(
|
|
2342
|
+
this.fPlotHandler(
|
|
2343
|
+
output,
|
|
2344
|
+
this.fBufferNum++,
|
|
2345
|
+
this.fCachedEvents.length ? this.fCachedEvents : undefined
|
|
2346
|
+
);
|
|
1676
2347
|
this.fCachedEvents = [];
|
|
1677
2348
|
}
|
|
1678
2349
|
}
|
|
@@ -1688,10 +2359,10 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1688
2359
|
}
|
|
1689
2360
|
|
|
1690
2361
|
private static findPath(o: any, p: string) {
|
|
1691
|
-
if (typeof o !==
|
|
2362
|
+
if (typeof o !== 'object') {
|
|
1692
2363
|
return false;
|
|
1693
2364
|
} else if (o.address) {
|
|
1694
|
-
return
|
|
2365
|
+
return o.address === p;
|
|
1695
2366
|
} else {
|
|
1696
2367
|
for (const k in o) {
|
|
1697
2368
|
if (FaustPolyWebAudioDsp.findPath(o[k], p)) return true;
|
|
@@ -1701,16 +2372,34 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1701
2372
|
}
|
|
1702
2373
|
|
|
1703
2374
|
setParamValue(path: string, value: number) {
|
|
1704
|
-
if (this.fPlotHandler)
|
|
1705
|
-
|
|
1706
|
-
|
|
2375
|
+
if (this.fPlotHandler)
|
|
2376
|
+
this.fCachedEvents.push({ type: 'param', data: { path, value } });
|
|
2377
|
+
if (
|
|
2378
|
+
this.fJSONEffect &&
|
|
2379
|
+
FaustPolyWebAudioDsp.findPath(this.fJSONEffect.ui, path) &&
|
|
2380
|
+
this.fInstance.effectAPI
|
|
2381
|
+
) {
|
|
2382
|
+
this.fInstance.effectAPI.setParamValue(
|
|
2383
|
+
this.fEffect,
|
|
2384
|
+
this.fPathTable[path],
|
|
2385
|
+
value
|
|
2386
|
+
);
|
|
1707
2387
|
} else {
|
|
1708
|
-
this.fVoiceTable.forEach(voice =>
|
|
2388
|
+
this.fVoiceTable.forEach((voice) =>
|
|
2389
|
+
voice.setParamValue(this.fPathTable[path], value)
|
|
2390
|
+
);
|
|
1709
2391
|
}
|
|
1710
2392
|
}
|
|
1711
2393
|
getParamValue(path: string) {
|
|
1712
|
-
if (
|
|
1713
|
-
|
|
2394
|
+
if (
|
|
2395
|
+
this.fJSONEffect &&
|
|
2396
|
+
FaustPolyWebAudioDsp.findPath(this.fJSONEffect.ui, path) &&
|
|
2397
|
+
this.fInstance.effectAPI
|
|
2398
|
+
) {
|
|
2399
|
+
return this.fInstance.effectAPI.getParamValue(
|
|
2400
|
+
this.fEffect,
|
|
2401
|
+
this.fPathTable[path]
|
|
2402
|
+
);
|
|
1714
2403
|
} else {
|
|
1715
2404
|
return this.fVoiceTable[0].getParamValue(this.fPathTable[path]);
|
|
1716
2405
|
}
|
|
@@ -1721,18 +2410,24 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1721
2410
|
const e = this.fJSONEffect;
|
|
1722
2411
|
const r = { ...o };
|
|
1723
2412
|
if (e) {
|
|
1724
|
-
r.ui = [
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
2413
|
+
r.ui = [
|
|
2414
|
+
{
|
|
2415
|
+
type: 'tgroup',
|
|
2416
|
+
label: 'Sequencer',
|
|
2417
|
+
items: [
|
|
2418
|
+
{ type: 'vgroup', label: 'Instrument', items: o.ui },
|
|
2419
|
+
{ type: 'vgroup', label: 'Effect', items: e.ui }
|
|
2420
|
+
]
|
|
2421
|
+
}
|
|
2422
|
+
];
|
|
1730
2423
|
} else {
|
|
1731
|
-
r.ui = [
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2424
|
+
r.ui = [
|
|
2425
|
+
{
|
|
2426
|
+
type: 'tgroup',
|
|
2427
|
+
label: 'Polyphonic',
|
|
2428
|
+
items: [{ type: 'vgroup', label: 'Voices', items: o.ui }]
|
|
2429
|
+
}
|
|
2430
|
+
];
|
|
1736
2431
|
}
|
|
1737
2432
|
return r as FaustDspMeta;
|
|
1738
2433
|
}
|
|
@@ -1745,17 +2440,20 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1745
2440
|
return this.getMeta().ui;
|
|
1746
2441
|
}
|
|
1747
2442
|
|
|
1748
|
-
getDescriptors() {
|
|
2443
|
+
getDescriptors() {
|
|
2444
|
+
return this.fDescriptor;
|
|
2445
|
+
}
|
|
1749
2446
|
|
|
1750
2447
|
midiMessage(data: number[] | Uint8Array) {
|
|
1751
2448
|
const cmd = data[0] >> 4;
|
|
1752
2449
|
const channel = data[0] & 0xf;
|
|
1753
2450
|
const data1 = data[1];
|
|
1754
2451
|
const data2 = data[2];
|
|
1755
|
-
if (cmd === 8 || (cmd === 9 && data2 === 0))
|
|
2452
|
+
if (cmd === 8 || (cmd === 9 && data2 === 0))
|
|
2453
|
+
return this.keyOff(channel, data1, data2);
|
|
1756
2454
|
else if (cmd === 9) return this.keyOn(channel, data1, data2);
|
|
1757
2455
|
else super.midiMessage(data);
|
|
1758
|
-
}
|
|
2456
|
+
}
|
|
1759
2457
|
|
|
1760
2458
|
ctrlChange(channel: number, ctrl: number, value: number) {
|
|
1761
2459
|
if (ctrl === 123 || ctrl === 120) {
|
|
@@ -1766,23 +2464,36 @@ export class FaustPolyWebAudioDsp extends FaustBaseWebAudioDsp implements IFaust
|
|
|
1766
2464
|
}
|
|
1767
2465
|
|
|
1768
2466
|
keyOn(channel: number, pitch: number, velocity: number) {
|
|
1769
|
-
if (this.fPlotHandler)
|
|
2467
|
+
if (this.fPlotHandler)
|
|
2468
|
+
this.fCachedEvents.push({
|
|
2469
|
+
type: 'keyOn',
|
|
2470
|
+
data: [channel, pitch, velocity]
|
|
2471
|
+
});
|
|
1770
2472
|
const voice = this.getFreeVoice();
|
|
1771
|
-
this.fVoiceTable[voice].keyOn(
|
|
2473
|
+
this.fVoiceTable[voice].keyOn(
|
|
2474
|
+
pitch,
|
|
2475
|
+
velocity,
|
|
2476
|
+
this.fVoiceTable[voice].fCurNote ==
|
|
2477
|
+
FaustWebAudioDspVoice.kLegatoVoice
|
|
2478
|
+
);
|
|
1772
2479
|
}
|
|
1773
2480
|
|
|
1774
2481
|
keyOff(channel: number, pitch: number, velocity: number) {
|
|
1775
|
-
if (this.fPlotHandler)
|
|
2482
|
+
if (this.fPlotHandler)
|
|
2483
|
+
this.fCachedEvents.push({
|
|
2484
|
+
type: 'keyOff',
|
|
2485
|
+
data: [channel, pitch, velocity]
|
|
2486
|
+
});
|
|
1776
2487
|
const voice = this.getPlayingVoice(pitch);
|
|
1777
2488
|
if (voice !== FaustWebAudioDspVoice.kNoVoice) {
|
|
1778
2489
|
this.fVoiceTable[voice].keyOff();
|
|
1779
2490
|
} else {
|
|
1780
|
-
console.log(
|
|
2491
|
+
console.log('Playing pitch = %d not found\n', pitch);
|
|
1781
2492
|
}
|
|
1782
2493
|
}
|
|
1783
2494
|
|
|
1784
2495
|
allNotesOff(hard: boolean = true) {
|
|
1785
|
-
this.fCachedEvents.push({ type:
|
|
1786
|
-
this.fVoiceTable.forEach(voice => voice.keyOff(hard));
|
|
2496
|
+
this.fCachedEvents.push({ type: 'ctrlChange', data: [0, 123, 0] });
|
|
2497
|
+
this.fVoiceTable.forEach((voice) => voice.keyOff(hard));
|
|
1787
2498
|
}
|
|
1788
2499
|
}
|