@grame/faustwasm 0.2.2 → 0.3.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/README.md +2 -0
- package/assets/standalone/faustwasm/index.d.ts +49 -8
- package/assets/standalone/faustwasm/index.js +538 -15
- package/assets/standalone/faustwasm/index.js.map +3 -3
- package/assets/standalone/index.js +17 -11
- package/dist/cjs/index.d.ts +49 -8
- package/dist/cjs/index.js +537 -15
- package/dist/cjs/index.js.map +3 -3
- package/dist/cjs-bundle/index.d.ts +49 -8
- package/dist/cjs-bundle/index.js +540 -18
- package/dist/cjs-bundle/index.js.map +3 -3
- package/dist/esm/index.d.ts +49 -8
- package/dist/esm/index.js +538 -15
- package/dist/esm/index.js.map +3 -3
- package/dist/esm-bundle/index.d.ts +49 -8
- package/dist/esm-bundle/index.js +540 -18
- package/dist/esm-bundle/index.js.map +3 -3
- package/libfaust-wasm/libfaust-wasm.data +0 -0
- package/libfaust-wasm/libfaust-wasm.js +1 -1
- package/libfaust-wasm/libfaust-wasm.wasm +0 -0
- package/package.json +1 -1
- package/src/FaustAudioWorkletNode.ts +73 -0
- package/src/FaustAudioWorkletProcessor.ts +33 -4
- package/src/FaustDspGenerator.ts +14 -2
- package/src/FaustOfflineProcessor.ts +10 -0
- package/src/FaustScriptProcessorNode.ts +61 -1
- package/src/FaustSensors.ts +370 -0
- package/src/FaustWebAudioDsp.ts +122 -23
- package/test/faustlive-wasm/faustwasm/index.d.ts +49 -8
- package/test/faustlive-wasm/faustwasm/index.js +538 -15
- package/test/faustlive-wasm/faustwasm/index.js.map +3 -3
- package/test/faustlive-wasm/index.js +2 -0
- package/test/mono.dsp +1 -0
- package/test/osc.dsp +4 -2
|
Binary file
|
package/package.json
CHANGED
|
@@ -15,6 +15,8 @@ export class FaustAudioWorkletNode<Poly extends boolean = false> extends (global
|
|
|
15
15
|
protected fPlotHandler: PlotHandler | null;
|
|
16
16
|
protected fUICallback: UIHandler;
|
|
17
17
|
protected fDescriptor: FaustUIInputItem[];
|
|
18
|
+
#hasAccInput = false;
|
|
19
|
+
#hasGyrInput = false;
|
|
18
20
|
|
|
19
21
|
constructor(context: BaseAudioContext, name: string, factory: LooseFaustDspFactory, options: FaustAudioWorkletNodeOptions<Poly>["processorOptions"], nodeOptions: Partial<FaustAudioWorkletNodeOptions> = {}) {
|
|
20
22
|
|
|
@@ -47,8 +49,15 @@ export class FaustAudioWorkletNode<Poly extends boolean = false> extends (global
|
|
|
47
49
|
// Keep inputs adresses
|
|
48
50
|
this.fInputsItems.push(item.address);
|
|
49
51
|
this.fDescriptor.push(item);
|
|
52
|
+
if (!item.meta) return;
|
|
53
|
+
item.meta.forEach((meta) => {
|
|
54
|
+
const { midi, acc, gyr } = meta;
|
|
55
|
+
if (acc) this.#hasAccInput = true;
|
|
56
|
+
if (gyr) this.#hasGyrInput = true;
|
|
57
|
+
});
|
|
50
58
|
}
|
|
51
59
|
}
|
|
60
|
+
|
|
52
61
|
FaustBaseWebAudioDsp.parseUI(this.fJSONDsp.ui, this.fUICallback);
|
|
53
62
|
|
|
54
63
|
// Patch it with additional functions
|
|
@@ -62,6 +71,55 @@ export class FaustAudioWorkletNode<Poly extends boolean = false> extends (global
|
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
// Public API
|
|
74
|
+
|
|
75
|
+
/** Setup accelerometer and gyroscope handlers */
|
|
76
|
+
async listenMotion() {
|
|
77
|
+
if (this.hasAccInput) {
|
|
78
|
+
const handleDeviceMotion = ({ accelerationIncludingGravity }: DeviceMotionEvent) => {
|
|
79
|
+
if (!accelerationIncludingGravity) return;
|
|
80
|
+
const { x, y, z } = accelerationIncludingGravity;
|
|
81
|
+
this.propagateAcc({ x, y, z });
|
|
82
|
+
};
|
|
83
|
+
if (window.DeviceMotionEvent) {
|
|
84
|
+
if (typeof (window.DeviceMotionEvent as any).requestPermission === "function") { // for iOS 13+
|
|
85
|
+
try {
|
|
86
|
+
const response = await (window.DeviceMotionEvent as any).requestPermission();
|
|
87
|
+
if (response !== "granted") throw new Error("Unable to access the accelerometer.");
|
|
88
|
+
window.addEventListener("devicemotion", handleDeviceMotion, true);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error(error);
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
window.addEventListener("devicemotion", handleDeviceMotion, true);
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
// Browser doesn't support DeviceMotionEvent
|
|
97
|
+
console.log("Cannot set the accelerometer handler.");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (this.hasGyrInput) {
|
|
101
|
+
const handleDeviceOrientation = ({ alpha, beta, gamma }: DeviceOrientationEvent) => {
|
|
102
|
+
this.propagateGyr({ alpha, beta, gamma });
|
|
103
|
+
};
|
|
104
|
+
if (window.DeviceMotionEvent) {
|
|
105
|
+
if (typeof (window.DeviceOrientationEvent as any).requestPermission === "function") { // for iOS 13+
|
|
106
|
+
try {
|
|
107
|
+
const response = await (window.DeviceOrientationEvent as any).requestPermission();
|
|
108
|
+
if (response !== "granted") throw new Error("Unable to access the gyroscope.");
|
|
109
|
+
window.addEventListener("deviceorientation", handleDeviceOrientation, true);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(error);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
window.addEventListener("deviceorientation", handleDeviceOrientation, true);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
// Browser doesn't support DeviceMotionEvent
|
|
118
|
+
console.log("Cannot set the gyroscope handler.");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
65
123
|
setOutputParamHandler(handler: OutputParamHandler | null) {
|
|
66
124
|
this.fOutputHandler = handler;
|
|
67
125
|
}
|
|
@@ -126,6 +184,20 @@ export class FaustAudioWorkletNode<Poly extends boolean = false> extends (global
|
|
|
126
184
|
this.port.postMessage(e);
|
|
127
185
|
}
|
|
128
186
|
|
|
187
|
+
get hasAccInput() { return this.#hasAccInput; }
|
|
188
|
+
propagateAcc(accelerationIncludingGravity: NonNullable<DeviceMotionEvent["accelerationIncludingGravity"]>) {
|
|
189
|
+
if (!accelerationIncludingGravity) return;
|
|
190
|
+
const e = { type: "acc", data: accelerationIncludingGravity };
|
|
191
|
+
this.port.postMessage(e);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
get hasGyrInput() { return this.#hasGyrInput; }
|
|
195
|
+
propagateGyr(event: Pick<DeviceOrientationEvent, "alpha" | "beta" | "gamma">) {
|
|
196
|
+
if (!event) return;
|
|
197
|
+
const e = { type: "gyr", data: event };
|
|
198
|
+
this.port.postMessage(e);
|
|
199
|
+
}
|
|
200
|
+
|
|
129
201
|
setParamValue(path: string, value: number) {
|
|
130
202
|
const e = { type: "param", data: { path, value } };
|
|
131
203
|
this.port.postMessage(e);
|
|
@@ -158,6 +230,7 @@ export class FaustAudioWorkletNode<Poly extends boolean = false> extends (global
|
|
|
158
230
|
this.port.postMessage({ type: "destroy" });
|
|
159
231
|
this.port.close();
|
|
160
232
|
}
|
|
233
|
+
|
|
161
234
|
}
|
|
162
235
|
|
|
163
236
|
/**
|
|
@@ -125,13 +125,34 @@ const getFaustAudioWorkletProcessor = <Poly extends boolean = false>(dependencie
|
|
|
125
125
|
const msg = e.data;
|
|
126
126
|
|
|
127
127
|
switch (msg.type) {
|
|
128
|
+
// Sensors messages
|
|
129
|
+
case "acc": {
|
|
130
|
+
this.propagateAcc(msg.data);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
case "gyr": {
|
|
134
|
+
this.propagateGyr(msg.data);
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
128
137
|
// Generic MIDI message
|
|
129
|
-
case "midi":
|
|
138
|
+
case "midi": {
|
|
139
|
+
this.midiMessage(msg.data);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
130
142
|
// Typed MIDI message
|
|
131
|
-
case "ctrlChange":
|
|
132
|
-
|
|
143
|
+
case "ctrlChange": {
|
|
144
|
+
this.ctrlChange(msg.data[0], msg.data[1], msg.data[2]);
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case "pitchWheel": {
|
|
148
|
+
this.pitchWheel(msg.data[0], msg.data[1]);
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
133
151
|
// Generic data message
|
|
134
|
-
case "param":
|
|
152
|
+
case "param": {
|
|
153
|
+
this.setParamValue(msg.data.path, msg.data.value);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
135
156
|
// Plot handler set on demand
|
|
136
157
|
case "setPlotHandler": {
|
|
137
158
|
if (msg.data) {
|
|
@@ -175,6 +196,14 @@ const getFaustAudioWorkletProcessor = <Poly extends boolean = false>(dependencie
|
|
|
175
196
|
protected pitchWheel(channel: number, wheel: number) {
|
|
176
197
|
this.fDSPCode.pitchWheel(channel, wheel);
|
|
177
198
|
}
|
|
199
|
+
|
|
200
|
+
protected propagateAcc(accelerationIncludingGravity: NonNullable<DeviceMotionEvent["accelerationIncludingGravity"]>) {
|
|
201
|
+
this.fDSPCode.propagateAcc(accelerationIncludingGravity);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
protected propagateGyr(event: Pick<DeviceOrientationEvent, "alpha" | "beta" | "gamma">) {
|
|
205
|
+
this.fDSPCode.propagateGyr(event);
|
|
206
|
+
}
|
|
178
207
|
}
|
|
179
208
|
|
|
180
209
|
/**
|
package/src/FaustDspGenerator.ts
CHANGED
|
@@ -6,9 +6,11 @@ import FaustWasmInstantiator from "./FaustWasmInstantiator";
|
|
|
6
6
|
import { FaustMonoOfflineProcessor, FaustPolyOfflineProcessor, IFaustMonoOfflineProcessor, IFaustPolyOfflineProcessor } from "./FaustOfflineProcessor";
|
|
7
7
|
import { FaustMonoScriptProcessorNode, FaustPolyScriptProcessorNode } from "./FaustScriptProcessorNode";
|
|
8
8
|
import { FaustBaseWebAudioDsp, FaustMonoWebAudioDsp, FaustPolyWebAudioDsp, FaustWebAudioDspVoice, IFaustMonoWebAudioNode, IFaustPolyWebAudioNode, Soundfile, WasmAllocator } from "./FaustWebAudioDsp";
|
|
9
|
+
import SoundfileReader from "./SoundfileReader";
|
|
10
|
+
import FaustSensors from "./FaustSensors";
|
|
9
11
|
import type { IFaustCompiler } from "./FaustCompiler";
|
|
10
12
|
import type { FaustDspFactory, FaustUIDescriptor, FaustDspMeta, FFTUtils, LooseFaustDspFactory, AudioData } from "./types";
|
|
11
|
-
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
export interface GeneratorSupportingSoundfiles {
|
|
14
16
|
/**
|
|
@@ -252,6 +254,7 @@ export class FaustMonoDspGenerator implements IFaustMonoDspGenerator {
|
|
|
252
254
|
if (sp) {
|
|
253
255
|
const instance = await FaustWasmInstantiator.createAsyncMonoDSPInstance(factory);
|
|
254
256
|
const monoDsp = new FaustMonoWebAudioDsp(instance, context.sampleRate, sampleSize, bufferSize, factory.soundfiles);
|
|
257
|
+
|
|
255
258
|
const sp = context.createScriptProcessor(bufferSize, monoDsp.getNumInputs(), monoDsp.getNumOutputs()) as FaustMonoScriptProcessorNode;
|
|
256
259
|
Object.setPrototypeOf(sp, FaustMonoScriptProcessorNode.prototype);
|
|
257
260
|
sp.init(monoDsp);
|
|
@@ -282,6 +285,8 @@ var ${Soundfile.name} = ${Soundfile.toString()}
|
|
|
282
285
|
var Soundfile = ${Soundfile.name};
|
|
283
286
|
var ${WasmAllocator.name} = ${WasmAllocator.toString()}
|
|
284
287
|
var WasmAllocator = ${WasmAllocator.name};
|
|
288
|
+
var ${FaustSensors.name} = ${FaustSensors.toString()}
|
|
289
|
+
var FaustSensors = ${FaustSensors.name};
|
|
285
290
|
// Put them in dependencies
|
|
286
291
|
const dependencies = {
|
|
287
292
|
FaustBaseWebAudioDsp,
|
|
@@ -302,7 +307,8 @@ const dependencies = {
|
|
|
302
307
|
}
|
|
303
308
|
}
|
|
304
309
|
// Create the AWN
|
|
305
|
-
const node = new FaustMonoAudioWorkletNode(context, processorName, factory, sampleSize)
|
|
310
|
+
const node = new FaustMonoAudioWorkletNode(context, processorName, factory, sampleSize);
|
|
311
|
+
|
|
306
312
|
return node as SP extends true ? FaustMonoScriptProcessorNode : FaustMonoAudioWorkletNode;
|
|
307
313
|
}
|
|
308
314
|
}
|
|
@@ -345,6 +351,8 @@ var ${Soundfile.name} = ${Soundfile.toString()}
|
|
|
345
351
|
var Soundfile = ${Soundfile.name};
|
|
346
352
|
var ${WasmAllocator.name} = ${WasmAllocator.toString()}
|
|
347
353
|
var WasmAllocator = ${WasmAllocator.name};
|
|
354
|
+
var ${FaustSensors.name} = ${FaustSensors.toString()}
|
|
355
|
+
var FaustSensors = ${FaustSensors.name};
|
|
348
356
|
var FFTUtils = ${fftUtils.toString()}
|
|
349
357
|
// Put them in dependencies
|
|
350
358
|
const dependencies = {
|
|
@@ -578,6 +586,7 @@ process = adaptorIns(dsp_code.process) : dsp_code.effect : adaptorOuts;
|
|
|
578
586
|
const instance = await FaustWasmInstantiator.createAsyncPolyDSPInstance(voiceFactory, mixerModule, voices, effectFactory || undefined);
|
|
579
587
|
const soundfiles = { ...effectFactory?.soundfiles, ...voiceFactory.soundfiles };
|
|
580
588
|
const polyDsp = new FaustPolyWebAudioDsp(instance, context.sampleRate, sampleSize, bufferSize, soundfiles);
|
|
589
|
+
|
|
581
590
|
const sp = context.createScriptProcessor(bufferSize, polyDsp.getNumInputs(), polyDsp.getNumOutputs()) as FaustPolyScriptProcessorNode;
|
|
582
591
|
Object.setPrototypeOf(sp, FaustPolyScriptProcessorNode.prototype);
|
|
583
592
|
sp.init(polyDsp);
|
|
@@ -611,6 +620,8 @@ var ${Soundfile.name} = ${Soundfile.toString()}
|
|
|
611
620
|
var Soundfile = ${Soundfile.name};
|
|
612
621
|
var ${WasmAllocator.name} = ${WasmAllocator.toString()}
|
|
613
622
|
var WasmAllocator = ${WasmAllocator.name};
|
|
623
|
+
var ${FaustSensors.name} = ${FaustSensors.toString()}
|
|
624
|
+
var FaustSensors = ${FaustSensors.name};
|
|
614
625
|
// Put them in dependencies
|
|
615
626
|
const dependencies = {
|
|
616
627
|
FaustBaseWebAudioDsp,
|
|
@@ -632,6 +643,7 @@ const dependencies = {
|
|
|
632
643
|
}
|
|
633
644
|
// Create the AWN
|
|
634
645
|
const node = new FaustPolyAudioWorkletNode(context, processorName, voiceFactory, mixerModule, voices, sampleSize, effectFactory || undefined);
|
|
646
|
+
|
|
635
647
|
return node as SP extends true ? FaustPolyScriptProcessorNode : FaustPolyAudioWorkletNode;
|
|
636
648
|
}
|
|
637
649
|
}
|
|
@@ -81,6 +81,16 @@ export class FaustOfflineProcessor<Poly extends boolean = false> {
|
|
|
81
81
|
|
|
82
82
|
destroy() { this.fDSPCode.destroy(); }
|
|
83
83
|
|
|
84
|
+
get hasAccInput() { return this.fDSPCode.hasAccInput; }
|
|
85
|
+
propagateAcc(accelerationIncludingGravity: NonNullable<DeviceMotionEvent["accelerationIncludingGravity"]>) {
|
|
86
|
+
this.fDSPCode.propagateAcc(accelerationIncludingGravity);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get hasGyrInput() { return this.fDSPCode.hasGyrInput; }
|
|
90
|
+
propagateGyr(event: Pick<DeviceOrientationEvent, "alpha" | "beta" | "gamma">) {
|
|
91
|
+
this.fDSPCode.propagateGyr(event);
|
|
92
|
+
}
|
|
93
|
+
|
|
84
94
|
/**
|
|
85
95
|
* Render frames in an array.
|
|
86
96
|
*
|
|
@@ -15,7 +15,7 @@ export class FaustScriptProcessorNode<Poly extends boolean = false> extends (glo
|
|
|
15
15
|
|
|
16
16
|
this.fInputs = new Array(this.fDSPCode.getNumInputs());
|
|
17
17
|
this.fOutputs = new Array(this.fDSPCode.getNumOutputs());
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
this.onaudioprocess = (e) => {
|
|
20
20
|
|
|
21
21
|
// Read inputs
|
|
@@ -33,7 +33,57 @@ export class FaustScriptProcessorNode<Poly extends boolean = false> extends (glo
|
|
|
33
33
|
|
|
34
34
|
this.start();
|
|
35
35
|
}
|
|
36
|
+
|
|
36
37
|
// Public API
|
|
38
|
+
|
|
39
|
+
/** Setup accelerometer and gyroscope handlers */
|
|
40
|
+
async listenMotion() {
|
|
41
|
+
if (this.hasAccInput) {
|
|
42
|
+
const handleDeviceMotion = ({ accelerationIncludingGravity }: DeviceMotionEvent) => {
|
|
43
|
+
if (!accelerationIncludingGravity) return;
|
|
44
|
+
const { x, y, z } = accelerationIncludingGravity;
|
|
45
|
+
this.propagateAcc({ x, y, z });
|
|
46
|
+
};
|
|
47
|
+
if (window.DeviceMotionEvent) {
|
|
48
|
+
if (typeof (window.DeviceMotionEvent as any).requestPermission === "function") { // for iOS 13+
|
|
49
|
+
try {
|
|
50
|
+
const response = await (window.DeviceMotionEvent as any).requestPermission();
|
|
51
|
+
if (response !== "granted") throw new Error("Unable to access the accelerometer.");
|
|
52
|
+
window.addEventListener("devicemotion", handleDeviceMotion, true);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error(error);
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
window.addEventListener("devicemotion", handleDeviceMotion, true);
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
// Browser doesn't support DeviceMotionEvent
|
|
61
|
+
console.log("Cannot set the accelerometer handler.");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (this.hasGyrInput) {
|
|
65
|
+
const handleDeviceOrientation = ({ alpha, beta, gamma }: DeviceOrientationEvent) => {
|
|
66
|
+
this.propagateGyr({ alpha, beta, gamma });
|
|
67
|
+
};
|
|
68
|
+
if (window.DeviceMotionEvent) {
|
|
69
|
+
if (typeof (window.DeviceOrientationEvent as any).requestPermission === "function") { // for iOS 13+
|
|
70
|
+
try {
|
|
71
|
+
const response = await (window.DeviceOrientationEvent as any).requestPermission();
|
|
72
|
+
if (response !== "granted") throw new Error("Unable to access the gyroscope.");
|
|
73
|
+
window.addEventListener("deviceorientation", handleDeviceOrientation, true);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error(error);
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
window.addEventListener("deviceorientation", handleDeviceOrientation, true);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
// Browser doesn't support DeviceMotionEvent
|
|
82
|
+
console.log("Cannot set the gyroscope handler.");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
37
87
|
compute(input: Float32Array[], output: Float32Array[]) { return this.fDSPCode.compute(input, output); }
|
|
38
88
|
|
|
39
89
|
setOutputParamHandler(handler: OutputParamHandler) { this.fDSPCode.setOutputParamHandler(handler); }
|
|
@@ -68,6 +118,16 @@ export class FaustScriptProcessorNode<Poly extends boolean = false> extends (glo
|
|
|
68
118
|
stop() { this.fDSPCode.stop(); }
|
|
69
119
|
|
|
70
120
|
destroy() { this.fDSPCode.destroy(); }
|
|
121
|
+
|
|
122
|
+
get hasAccInput() { return this.fDSPCode.hasAccInput; }
|
|
123
|
+
propagateAcc(accelerationIncludingGravity: NonNullable<DeviceMotionEvent["accelerationIncludingGravity"]>) {
|
|
124
|
+
this.fDSPCode.propagateAcc(accelerationIncludingGravity);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
get hasGyrInput() { return this.fDSPCode.hasGyrInput; }
|
|
128
|
+
propagateGyr(event: Pick<DeviceOrientationEvent, "alpha" | "beta" | "gamma">) {
|
|
129
|
+
this.fDSPCode.propagateGyr(event);
|
|
130
|
+
}
|
|
71
131
|
}
|
|
72
132
|
|
|
73
133
|
export class FaustMonoScriptProcessorNode extends FaustScriptProcessorNode<false> implements IFaustMonoWebAudioDsp {
|