@grame/faustwasm 0.10.1 → 0.10.2

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.
@@ -40,19 +40,20 @@ $buttonDsp.onclick = async () => {
40
40
  }
41
41
  }
42
42
 
43
+ // Play some notes using the Faust node
44
+ const play = (node) => {
45
+ node.keyOn(0, 60, 100);
46
+ setTimeout(() => node.keyOn(0, 64, 100), 1000);
47
+ setTimeout(() => node.keyOn(0, 67, 100), 2000);
48
+ setTimeout(() => node.allNotesOff(), 5000);
49
+ setTimeout(() => play(node), 7000);
50
+ }
51
+
43
52
  // Called at load time
44
53
  (async () => {
45
54
 
46
55
  const { createFaustNode, connectToAudioInput } = await import("./create-node.js");
47
56
 
48
- const play = (node) => {
49
- node.keyOn(0, 60, 100);
50
- setTimeout(() => node.keyOn(0, 64, 100), 1000);
51
- setTimeout(() => node.keyOn(0, 67, 100), 2000);
52
- setTimeout(() => node.allNotesOff(), 5000);
53
- setTimeout(() => play(node), 7000);
54
- }
55
-
56
57
  // Create Faust node
57
58
  const result = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES);
58
59
  faustNode = result.faustNode; // Assign to the global variable
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grame/faustwasm",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "WebAssembly version of Faust Compiler",
5
5
  "main": "dist/cjs/index.js",
6
6
  "types": "dist/esm/index.d.ts",
@@ -1,171 +0,0 @@
1
- // Set to > 0 if the DSP is polyphonic
2
- const FAUST_DSP_VOICES = 0;
3
-
4
- /**
5
- * @typedef {import("./faustwasm").FaustAudioWorkletNode} FaustAudioWorkletNode
6
- * @typedef {import("./faustwasm").FaustDspMeta} FaustDspMeta
7
- * @typedef {import("./faustwasm").FaustUIDescriptor} FaustUIDescriptor
8
- * @typedef {import("./faustwasm").FaustUIGroup} FaustUIGroup
9
- * @typedef {import("./faustwasm").FaustUIItem} FaustUIItem
10
- */
11
-
12
- /**
13
- * Registers the service worker.
14
- */
15
- if ("serviceWorker" in navigator) {
16
- window.addEventListener("load", () => {
17
- navigator.serviceWorker.register("./service-worker.js")
18
- .then(reg => console.log("Service Worker registered", reg))
19
- .catch(err => console.log("Service Worker registration failed", err));
20
- });
21
- }
22
-
23
- /** @type {HTMLSpanElement} */
24
- const $spanAudioInput = document.getElementById("audio-input");
25
- /** @type {HTMLSpanElement} */
26
- const $spanMidiInput = document.getElementById("midi-input");
27
- /** @type {HTMLSelectElement} */
28
- const $selectAudioInput = document.getElementById("select-audio-input");
29
- /** @type {HTMLSelectElement} */
30
- const $selectMidiInput = document.getElementById("select-midi-input");
31
- /** @type {HTMLSelectElement} */
32
- const $buttonDsp = document.getElementById("button-dsp");
33
- /** @type {HTMLDivElement} */
34
- const $divFaustUI = document.getElementById("div-faust-ui");
35
-
36
- /** @type {typeof AudioContext} */
37
- const AudioCtx = window.AudioContext || window.webkitAudioContext;
38
- const audioContext = new AudioCtx({ latencyHint: 0.00001 });
39
- audioContext.destination.channelInterpretation = "discrete";
40
- audioContext.suspend();
41
-
42
- $buttonDsp.disabled = true;
43
-
44
- // Declare faustNode as a global variable
45
- let faustNode;
46
-
47
- // Called at load time
48
- (async () => {
49
-
50
- const { createFaustNode, createKey2MIDI, connectToAudioInput, createFaustUI } = await import("./create-node.js");
51
-
52
- /**
53
- * @param {FaustAudioWorkletNode} faustNode
54
- */
55
- const buildAudioDeviceMenu = async (faustNode) => {
56
-
57
- let inputStreamNode = null;
58
- const handleDeviceChange = async () => {
59
- const devicesInfo = await navigator.mediaDevices.enumerateDevices();
60
- $selectAudioInput.innerHTML = "";
61
- devicesInfo.forEach((deviceInfo, i) => {
62
- const { kind, deviceId, label } = deviceInfo;
63
- if (kind === "audioinput") {
64
- const option = new Option(label || `microphone ${i + 1}`, deviceId);
65
- $selectAudioInput.add(option);
66
- }
67
- });
68
- }
69
- await handleDeviceChange();
70
- navigator.mediaDevices.addEventListener("devicechange", handleDeviceChange)
71
- $selectAudioInput.onchange = async () => {
72
- const id = $selectAudioInput.value;
73
- if (faustNode.getNumInputs() > 0) {
74
- inputStreamNode = await connectToAudioInput(audioContext, id, faustNode, inputStreamNode);
75
- }
76
- };
77
-
78
- // Connect to the default audio input device
79
- if (faustNode.getNumInputs() > 0) {
80
- inputStreamNode = await connectToAudioInput(audioContext, null, faustNode, inputStreamNode);
81
- }
82
- };
83
-
84
- /**
85
- * @param {FaustAudioWorkletNode} faustNode
86
- */
87
- const buildMidiDeviceMenu = async (faustNode) => {
88
-
89
- // Keyboard to MIDI handing
90
- let keyboard2MIDI = createKey2MIDI((event) => faustNode.midiMessage(event));
91
- keyboard2MIDI.start();
92
-
93
- const midiAccess = await navigator.requestMIDIAccess();
94
- /** @type {WebMidi.MIDIInput} */
95
- let currentInput;
96
- /**
97
- * @param {WebMidi.MIDIMessageEvent} e
98
- */
99
- const handleMidiMessage = e => faustNode.midiMessage(e.data);
100
- const handleStateChange = () => {
101
- const { inputs } = midiAccess;
102
- if ($selectMidiInput.options.length === inputs.size + 1) return;
103
- if (currentInput) currentInput.removeEventListener("midimessage", handleMidiMessage);
104
- $selectMidiInput.innerHTML = '<option value="-1" disabled selected>Select...</option>';
105
- inputs.forEach((midiInput) => {
106
- const { name, id } = midiInput;
107
- const option = new Option(name, id);
108
- $selectMidiInput.add(option);
109
- });
110
- };
111
- handleStateChange();
112
- midiAccess.addEventListener("statechange", handleStateChange);
113
- $selectMidiInput.onchange = () => {
114
- if (currentInput) currentInput.removeEventListener("midimessage", handleMidiMessage);
115
- const id = $selectMidiInput.value;
116
- currentInput = midiAccess.inputs.get(id);
117
- currentInput.addEventListener("midimessage", handleMidiMessage);
118
- };
119
- };
120
-
121
- // To test the ScriptProcessorNode mode
122
- // const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES, true);
123
- const result = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES);
124
- faustNode = result.faustNode; // Assign to the global variable
125
- if (!faustNode) throw new Error("Faust DSP not compiled");
126
-
127
- // Create the Faust UI
128
- await createFaustUI($divFaustUI, faustNode);
129
-
130
- // Connect the Faust node to the audio output
131
- faustNode.connect(audioContext.destination);
132
-
133
- // Build the audio device menu
134
- if (faustNode.numberOfInputs > 0) await buildAudioDeviceMenu(faustNode);
135
- else $spanAudioInput.hidden = true;
136
-
137
- // Build the MIDI device menu
138
- if (navigator.requestMIDIAccess) await buildMidiDeviceMenu(faustNode);
139
- else $spanMidiInput.hidden = true;
140
-
141
- })();
142
-
143
- // Set the title and enable the DSP button
144
- $buttonDsp.disabled = false;
145
- document.title = name;
146
- let sensorHandlersBound = false;
147
-
148
- // Activate AudioContext and Sensors on user interaction
149
- $buttonDsp.onclick = async () => {
150
-
151
- // Import the requestPermissions function
152
- const { requestPermissions } = await import("./create-node.js");
153
-
154
- // Request permission for sensors
155
- await requestPermissions();
156
-
157
- // Activate sensor listeners
158
- if (!sensorHandlersBound) {
159
- await faustNode.startSensors();
160
- sensorHandlersBound = true;
161
- }
162
-
163
- // Activate or suspend the AudioContext
164
- if (audioContext.state === "running") {
165
- $buttonDsp.textContent = "Suspended";
166
- await audioContext.suspend();
167
- } else if (audioContext.state === "suspended") {
168
- $buttonDsp.textContent = "Running";
169
- await audioContext.resume();
170
- }
171
- }
@@ -1,254 +0,0 @@
1
- // Set to > 0 if the DSP is polyphonic
2
- const FAUST_DSP_VOICES = 0;
3
-
4
- /**
5
- * @typedef {import("./faustwasm").FaustAudioWorkletNode} FaustAudioWorkletNode
6
- * @typedef {import("./faustwasm").FaustDspMeta} FaustDspMeta
7
- * @typedef {import("./faustwasm").FaustUIDescriptor} FaustUIDescriptor
8
- * @typedef {import("./faustwasm").FaustUIGroup} FaustUIGroup
9
- * @typedef {import("./faustwasm").FaustUIItem} FaustUIItem
10
- */
11
-
12
- /**
13
- * Registers the service worker.
14
- */
15
- if ("serviceWorker" in navigator) {
16
- window.addEventListener("load", () => {
17
- navigator.serviceWorker.register("./service-worker.js")
18
- .then(reg => console.log("Service Worker registered", reg))
19
- .catch(err => console.log("Service Worker registration failed", err));
20
- });
21
- }
22
-
23
- /** @type {HTMLDivElement} */
24
- const $divFaustUI = document.getElementById("div-faust-ui");
25
-
26
- /** @type {typeof AudioContext} */
27
- const AudioCtx = window.AudioContext || window.webkitAudioContext;
28
- const audioContext = new AudioCtx({ latencyHint: 0.00001 });
29
- audioContext.destination.channelInterpretation = "discrete";
30
- audioContext.suspend();
31
-
32
- // Declare faustNode as a global variable
33
- let faustNode;
34
-
35
- // Called at load time
36
- (async () => {
37
-
38
- // Import the create-node module
39
- const { createFaustNode, createFaustUI } = await import("./create-node.js");
40
-
41
- // To test the ScriptProcessorNode mode
42
- // const result = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES, true, 512);
43
- const result = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES);
44
- faustNode = result.faustNode; // Assign to the global variable
45
- if (!faustNode) throw new Error("Faust DSP not compiled");
46
-
47
- // Create the Faust UI
48
- await createFaustUI($divFaustUI, faustNode);
49
-
50
- })();
51
-
52
- // Synchronous function to resume AudioContext, to be called first in the synchronous event listener
53
- function resumeAudioContext() {
54
- if (audioContext.state === 'suspended') {
55
- audioContext.resume().then(() => {
56
- console.log('AudioContext resumed successfully');
57
- }).catch(error => {
58
- console.error('Error when resuming AudioContext:', error);
59
- });
60
- }
61
- }
62
-
63
- // Function to start MIDI
64
- function startMIDI() {
65
- // Check if the browser supports the Web MIDI API
66
- if (navigator.requestMIDIAccess) {
67
- navigator.requestMIDIAccess().then(
68
- midiAccess => {
69
- console.log("MIDI Access obtained.");
70
- for (let input of midiAccess.inputs.values()) {
71
- input.onmidimessage = (event) => faustNode.midiMessage(event.data);
72
- console.log(`Connected to input: ${input.name}`);
73
- }
74
- },
75
- () => console.error("Failed to access MIDI devices.")
76
- );
77
- } else {
78
- console.log("Web MIDI API is not supported in this browser.");
79
- }
80
- }
81
-
82
- // Function to stop MIDI
83
- function stopMIDI() {
84
- // Check if the browser supports the Web MIDI API
85
- if (navigator.requestMIDIAccess) {
86
- navigator.requestMIDIAccess().then(
87
- midiAccess => {
88
- console.log("MIDI Access obtained.");
89
- for (let input of midiAccess.inputs.values()) {
90
- input.onmidimessage = null;
91
- console.log(`Disconnected from input: ${input.name}`);
92
- }
93
- },
94
- () => console.error("Failed to access MIDI devices.")
95
- );
96
- } else {
97
- console.log("Web MIDI API is not supported in this browser.");
98
- }
99
- }
100
-
101
- // Flag to check if sensor handlers are bound
102
- let sensorHandlersBound = false;
103
- // Flag to check if MIDI handlers are bound
104
- let midiHandlersBound = false;
105
-
106
- // === Wake Lock Manager ===
107
- const WakeLockManager = (() => {
108
- let wakeLock = null;
109
- let enabled = false;
110
-
111
- function request() {
112
- if (!('wakeLock' in navigator)) return;
113
- if (document.visibilityState !== 'visible') return;
114
- if (wakeLock === null) {
115
- navigator.wakeLock.request('screen')
116
- .then(lock => {
117
- wakeLock = lock;
118
- console.log('[WakeLockManager] Wake Lock acquired');
119
- wakeLock.addEventListener('release', () => {
120
- console.log('[WakeLockManager] Wake Lock was released');
121
- wakeLock = null;
122
- if (enabled && document.visibilityState === 'visible') {
123
- request();
124
- }
125
- });
126
- })
127
- .catch(err => {
128
- console.error('[WakeLockManager] Failed to acquire Wake Lock:', err);
129
- });
130
- }
131
- }
132
-
133
- function release() {
134
- if (wakeLock !== null) {
135
- wakeLock.release()
136
- .then(() => {
137
- console.log('[WakeLockManager] Wake Lock manually released');
138
- wakeLock = null;
139
- })
140
- .catch(err => {
141
- console.error('[WakeLockManager] Failed to release Wake Lock:', err);
142
- });
143
- }
144
- }
145
-
146
- function enable() {
147
- enabled = true;
148
- request();
149
- }
150
-
151
- function disable() {
152
- enabled = false;
153
- release();
154
- }
155
-
156
- document.addEventListener('visibilitychange', () => {
157
- if (enabled && document.visibilityState === 'visible') {
158
- request();
159
- }
160
- });
161
-
162
- return {
163
- enable,
164
- disable,
165
- isActive: () => wakeLock !== null,
166
- isEnabled: () => enabled,
167
- };
168
- })();
169
-
170
- // Function to activate MIDI and Sensors on user interaction
171
- async function activateMIDISensors() {
172
-
173
- // Import the create-node module
174
- const { connectToAudioInput, requestPermissions } = await import("./create-node.js");
175
-
176
- // Request permission for sensors
177
- await requestPermissions();
178
-
179
- // Activate sensor listeners
180
- if (!sensorHandlersBound) {
181
- await faustNode.startSensors();
182
- sensorHandlersBound = true;
183
- }
184
-
185
- // Initialize the MIDI setup
186
- if (!midiHandlersBound) {
187
- startMIDI();
188
- midiHandlersBound = true;
189
- }
190
-
191
- // Connect the Faust node to the audio output
192
- faustNode.connect(audioContext.destination);
193
-
194
- // Connect the Faust node to the audio input
195
- if (faustNode.numberOfInputs > 0) {
196
- await connectToAudioInput(audioContext, null, faustNode, null);
197
- }
198
-
199
- // Resume the AudioContext
200
- if (audioContext.state === 'suspended') {
201
- await audioContext.resume();
202
- }
203
- }
204
-
205
- // Function to suspend AudioContext, deactivate MIDI and Sensors on user interaction
206
- async function deactivateAudioMIDISensors() {
207
-
208
- // Suspend the AudioContext
209
- if (audioContext.state === 'running') {
210
- await audioContext.suspend();
211
- }
212
-
213
- // Deactivate sensor listeners
214
- if (sensorHandlersBound) {
215
- faustNode.stopSensors();
216
- sensorHandlersBound = false;
217
- }
218
-
219
- // Deactivate the MIDI setup
220
- if (midiHandlersBound && FAUST_DSP_VOICES > 0) {
221
- stopMIDI();
222
- midiHandlersBound = false;
223
- }
224
- }
225
-
226
- // Event listener to handle user interaction
227
- function handleUserInteraction() {
228
-
229
- if (WakeLockManager.isEnabled()) {
230
- WakeLockManager.disable();
231
- } else {
232
- WakeLockManager.enable();
233
- }
234
-
235
- // Resume AudioContext synchronously
236
- resumeAudioContext();
237
-
238
- // Launch the activation of MIDI and Sensors
239
- activateMIDISensors().catch(error => {
240
- console.error('Error when activating audio, MIDI and sensors:', error);
241
- });
242
- }
243
-
244
- // Activate AudioContext, MIDI and Sensors on user interaction
245
- window.addEventListener('click', handleUserInteraction);
246
- window.addEventListener('touchstart', handleUserInteraction);
247
-
248
- // Deactivate AudioContext, MIDI and Sensors on user interaction
249
- window.addEventListener('visibilitychange', function () {
250
- if (document.visibilityState === 'hidden') {
251
- WakeLockManager.disable();
252
- deactivateAudioMIDISensors();
253
- }
254
- });