@grame/faustwasm 0.8.0 → 0.9.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.
Files changed (67) hide show
  1. package/assets/standalone/create-node.js +46 -9
  2. package/assets/standalone/faust-ui/index.js +2 -2
  3. package/assets/standalone/faust-ui/index.js.map +1 -1
  4. package/assets/standalone/faustwasm/index.d.ts +42 -2
  5. package/assets/standalone/faustwasm/index.js +117 -66
  6. package/assets/standalone/faustwasm/index.js.map +2 -2
  7. package/assets/standalone/index-pwa.js +125 -90
  8. package/assets/standalone/index-template.js +43 -22
  9. package/assets/standalone/index.js +36 -19
  10. package/assets/standalone/service-worker.js +58 -5
  11. package/dist/cjs/index.d.ts +42 -2
  12. package/dist/cjs/index.js +117 -66
  13. package/dist/cjs/index.js.map +2 -2
  14. package/dist/cjs-bundle/index.d.ts +42 -2
  15. package/dist/cjs-bundle/index.js +117 -66
  16. package/dist/cjs-bundle/index.js.map +2 -2
  17. package/dist/esm/index.d.ts +42 -2
  18. package/dist/esm/index.js +117 -66
  19. package/dist/esm/index.js.map +2 -2
  20. package/dist/esm-bundle/index.d.ts +42 -2
  21. package/dist/esm-bundle/index.js +117 -66
  22. package/dist/esm-bundle/index.js.map +2 -2
  23. package/package.json +2 -2
  24. package/src/FaustAudioWorkletNode.ts +14 -30
  25. package/src/FaustAudioWorkletProcessor.ts +16 -0
  26. package/src/FaustOfflineProcessor.ts +2 -0
  27. package/src/FaustScriptProcessorNode.ts +6 -30
  28. package/src/FaustWebAudioDsp.ts +88 -0
  29. package/src/copyWebStandaloneAssets.js +14 -0
  30. package/test/faustlive-wasm/faust-ui/index.js +2 -2
  31. package/test/faustlive-wasm/faust-ui/index.js.map +1 -1
  32. package/test/faustlive-wasm/faustwasm/index.d.ts +42 -2
  33. package/test/faustlive-wasm/faustwasm/index.js +117 -66
  34. package/test/faustlive-wasm/faustwasm/index.js.map +2 -2
  35. package/test/midi/create-node.js +177 -0
  36. package/test/midi/dsp-meta.json +133 -0
  37. package/test/midi/dsp-module.wasm +0 -0
  38. package/test/midi/faust-ui/index.css +442 -0
  39. package/test/midi/faust-ui/index.css.map +1 -0
  40. package/test/midi/faust-ui/index.d.ts +1 -0
  41. package/test/midi/faust-ui/index.js +3729 -0
  42. package/test/midi/faust-ui/index.js.map +1 -0
  43. package/test/midi/faustwasm/index.d.ts +1680 -0
  44. package/test/midi/faustwasm/index.js +4884 -0
  45. package/test/midi/faustwasm/index.js.map +7 -0
  46. package/test/midi/icon.png +0 -0
  47. package/test/midi/index.html +76 -0
  48. package/test/midi/index.js +183 -0
  49. package/test/midi/manifest.json +17 -0
  50. package/test/midi/service-worker.js +165 -0
  51. package/test/midi.dsp +8 -1
  52. package/test/organ1/create-node.js +177 -0
  53. package/test/organ1/dsp-meta.json +126 -0
  54. package/test/organ1/dsp-module.wasm +0 -0
  55. package/test/organ1/faust-ui/index.css +442 -0
  56. package/test/organ1/faust-ui/index.css.map +1 -0
  57. package/test/organ1/faust-ui/index.d.ts +1 -0
  58. package/test/organ1/faust-ui/index.js +3729 -0
  59. package/test/organ1/faust-ui/index.js.map +1 -0
  60. package/test/organ1/faustwasm/index.d.ts +1656 -0
  61. package/test/organ1/faustwasm/index.js +4783 -0
  62. package/test/organ1/faustwasm/index.js.map +7 -0
  63. package/test/organ1/icon.png +0 -0
  64. package/test/organ1/index.html +76 -0
  65. package/test/organ1/index.js +203 -0
  66. package/test/organ1/manifest.json +17 -0
  67. package/test/organ1/service-worker.js +181 -0
@@ -29,120 +29,155 @@ const audioContext = new AudioCtx({ latencyHint: 0.00001 });
29
29
  audioContext.destination.channelInterpretation = "discrete";
30
30
  audioContext.suspend();
31
31
 
32
+ // Declare faustNode as a global variable
33
+ let faustNode;
34
+
35
+ // Called at load time
32
36
  (async () => {
33
37
 
34
- const { createFaustNode, createFaustUI, connectToAudioInput } = await import("./create-node.js");
38
+ // Import the create-node module
39
+ const { createFaustNode, createFaustUI } = await import("./create-node.js");
35
40
 
36
41
  // To test the ScriptProcessorNode mode
37
- //const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "osc", FAUST_DSP_VOICES, true, 512);
38
- const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "osc", FAUST_DSP_VOICES);
42
+ // const result = await createFaustNode(audioContext, "osc", FAUST_DSP_VOICES, true, 512);
43
+ const result = await createFaustNode(audioContext, "osc", FAUST_DSP_VOICES);
44
+ faustNode = result.faustNode; // Assign to the global variable
39
45
  if (!faustNode) throw new Error("Faust DSP not compiled");
40
46
 
41
47
  // Create the Faust UI
42
48
  await createFaustUI($divFaustUI, faustNode);
43
49
 
44
- // Function to start MIDI
45
- function startMIDI() {
46
- // Check if the browser supports the Web MIDI API
47
- if (navigator.requestMIDIAccess) {
48
- navigator.requestMIDIAccess().then(
49
- midiAccess => {
50
- console.log("MIDI Access obtained.");
51
- for (let input of midiAccess.inputs.values()) {
52
- input.onmidimessage = (event) => faustNode.midiMessage(event.data);
53
- console.log(`Connected to input: ${input.name}`);
54
- }
55
- },
56
- () => console.error("Failed to access MIDI devices.")
57
- );
58
- } else {
59
- console.log("Web MIDI API is not supported in this browser.");
60
- }
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
+ });
61
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
+ let sensorHandlersBound = false;
102
+ let midiHandlersBound = false;
103
+
104
+ // Function to activate MIDI and Sensors on user interaction
105
+ async function activateMIDISensors() {
106
+
107
+ // Import the create-node module
108
+ const { connectToAudioInput, requestPermissions } = await import("./create-node.js");
62
109
 
63
- // Function to stop MIDI
64
- function stopMIDI() {
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 = null;
72
- console.log(`Disconnected from 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
- }
110
+ // Request permission for sensors
111
+ await requestPermissions();
112
+
113
+ // Activate sensor listeners
114
+ if (!sensorHandlersBound) {
115
+ await faustNode.startSensors();
116
+ sensorHandlersBound = true;
80
117
  }
81
118
 
82
- let sensorHandlersBound = false;
83
- let midiHandlersBound = false;
119
+ // Initialize the MIDI setup
120
+ if (!midiHandlersBound) {
121
+ startMIDI();
122
+ midiHandlersBound = true;
123
+ }
84
124
 
85
- // Function to resume AudioContext, activate MIDI and Sensors on user interaction
86
- async function activateAudioMIDISensors() {
125
+ // Connect the Faust node to the audio output
126
+ faustNode.connect(audioContext.destination);
87
127
 
88
- // Activate sensor listeners
89
- if (!sensorHandlersBound) {
90
- await faustNode.startSensors();
91
- sensorHandlersBound = true;
92
- }
128
+ // Connect the Faust node to the audio input
129
+ if (faustNode.numberOfInputs > 0) {
130
+ await connectToAudioInput(audioContext, null, faustNode, null);
131
+ }
93
132
 
94
- // Initialize the MIDI setup
95
- if (!midiHandlersBound && FAUST_DSP_VOICES > 0) {
96
- startMIDI();
97
- midiHandlersBound = true;
98
- }
133
+ // Resume the AudioContext
134
+ if (audioContext.state === 'suspended') {
135
+ await audioContext.resume();
136
+ }
137
+ }
99
138
 
100
- // Connect the Faust node to the audio output
101
- faustNode.connect(audioContext.destination);
139
+ // Function to suspend AudioContext, deactivate MIDI and Sensors on user interaction
140
+ async function deactivateAudioMIDISensors() {
102
141
 
103
- // Connect the Faust node to the audio input
104
- if (faustNode.numberOfInputs > 0) {
105
- await connectToAudioInput(audioContext, null, faustNode, null);
106
- }
142
+ // Suspend the AudioContext
143
+ if (audioContext.state === 'running') {
144
+ await audioContext.suspend();
145
+ }
107
146
 
108
- // Resume the AudioContext
109
- if (audioContext.state === 'suspended') {
110
- await audioContext.resume();
111
- }
147
+ // Deactivate sensor listeners
148
+ if (sensorHandlersBound) {
149
+ faustNode.stopSensors();
150
+ sensorHandlersBound = false;
112
151
  }
113
152
 
114
- // Function to suspend AudioContext, deactivate MIDI and Sensors on user interaction
115
- async function deactivateAudioMIDISensors() {
116
-
117
- // Suspend the AudioContext
118
- if (audioContext.state === 'running') {
119
- await audioContext.suspend();
120
- }
121
-
122
- // Deactivate sensor listeners
123
- if (sensorHandlersBound) {
124
- faustNode.stopSensors();
125
- sensorHandlersBound = false;
126
- }
127
-
128
- // Deactivate the MIDI setup
129
- if (midiHandlersBound && FAUST_DSP_VOICES > 0) {
130
- stopMIDI();
131
- midiHandlersBound = false;
132
- }
153
+ // Deactivate the MIDI setup
154
+ if (midiHandlersBound && FAUST_DSP_VOICES > 0) {
155
+ stopMIDI();
156
+ midiHandlersBound = false;
133
157
  }
158
+ }
134
159
 
135
- // Add event listeners for user interactions
160
+ // Event listener to handle user interaction
161
+ function handleUserInteraction() {
136
162
 
137
- // Activate AudioContext, MIDI and Sensors on user interaction
138
- window.addEventListener('click', activateAudioMIDISensors);
139
- window.addEventListener('touchstart', activateAudioMIDISensors);
163
+ // Resume AudioContext synchronously
164
+ resumeAudioContext();
140
165
 
141
- // Deactivate AudioContext, MIDI and Sensors on user interaction
142
- window.addEventListener('visibilitychange', function () {
143
- if (window.visibilityState === 'hidden') {
144
- deactivateAudioMIDISensors();
145
- }
166
+ // Launch the activation of MIDI and Sensors
167
+ activateMIDISensors().catch(error => {
168
+ console.error('Error when activating audio, MIDI and sensors:', error);
146
169
  });
170
+ }
171
+
172
+ // Activate AudioContext, MIDI and Sensors on user interaction
173
+ window.addEventListener('click', handleUserInteraction);
174
+ window.addEventListener('touchstart', handleUserInteraction);
175
+
176
+ // Deactivate AudioContext, MIDI and Sensors on user interaction
177
+ window.addEventListener('visibilitychange', function () {
178
+ if (window.visibilityState === 'hidden') {
179
+ deactivateAudioMIDISensors();
180
+ }
181
+ });
182
+
147
183
 
148
- })();
@@ -1,18 +1,49 @@
1
1
  // Set to > 0 if the DSP is polyphonic
2
2
  const FAUST_DSP_VOICES = 0;
3
3
 
4
- (async () => {
4
+ // Declare faustNode as a global variable
5
+ let faustNode;
5
6
 
6
- const { createFaustNode, connectToAudioInput } = await import("./create-node.js");
7
+ // Create audio context activation button
8
+ /** @type {HTMLButtonElement} */
9
+ const $buttonDsp = document.getElementById("button-dsp");
10
+
11
+ // Create audio context
12
+ const AudioCtx = window.AudioContext || window.webkitAudioContext;
13
+ const audioContext = new AudioCtx({ latencyHint: 0.00001 });
14
+
15
+ // Activate AudioContext and Sensors on user interaction
16
+ $buttonDsp.disabled = true;
17
+ let sensorHandlersBound = false;
18
+ $buttonDsp.onclick = async () => {
19
+
20
+ // Import the requestPermissions function
21
+ const { requestPermissions } = await import("./create-node.js");
22
+
23
+ // Request permission for sensors
24
+ await requestPermissions();
25
+
26
+ // Activate sensor listeners
27
+ if (!sensorHandlersBound) {
28
+ await faustNode.startSensors();
29
+ sensorHandlersBound = true;
30
+ }
7
31
 
8
- // Create audio context
9
- const AudioCtx = window.AudioContext || window.webkitAudioContext;
10
- const audioContext = new AudioCtx({ latencyHint: 0.00001 });
11
- audioContext.suspend();
32
+ // Activate or suspend the AudioContext
33
+ if (audioContext.state === "running") {
34
+ $buttonDsp.textContent = "Suspended";
35
+ await audioContext.suspend();
36
+ } else if (audioContext.state === "suspended") {
37
+ $buttonDsp.textContent = "Running";
38
+ await audioContext.resume();
39
+ if (FAUST_DSP_VOICES) play(faustNode);
40
+ }
41
+ }
12
42
 
13
- // Create audio context activation button
14
- /** @type {HTMLButtonElement} */
15
- const $buttonDsp = document.getElementById("button-dsp");
43
+ // Called at load time
44
+ (async () => {
45
+
46
+ const { createFaustNode, connectToAudioInput } = await import("./create-node.js");
16
47
 
17
48
  const play = (node) => {
18
49
  node.keyOn(0, 60, 100);
@@ -21,21 +52,10 @@ const FAUST_DSP_VOICES = 0;
21
52
  setTimeout(() => node.allNotesOff(), 5000);
22
53
  setTimeout(() => play(node), 7000);
23
54
  }
24
- // Function to activate audio context
25
- $buttonDsp.disabled = true;
26
- $buttonDsp.onclick = async () => {
27
- if (audioContext.state === "running") {
28
- $buttonDsp.textContent = "Suspended";
29
- await audioContext.suspend();
30
- } else if (audioContext.state === "suspended") {
31
- $buttonDsp.textContent = "Running";
32
- await audioContext.resume();
33
- if (FAUST_DSP_VOICES) play(faustNode);
34
- }
35
- }
36
55
 
37
56
  // Create Faust node
38
- const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES);
57
+ const result = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES);
58
+ faustNode = result.faustNode; // Assign to the global variable
39
59
  if (!faustNode) throw new Error("Faust DSP not compiled");
40
60
 
41
61
  // Connect the Faust node to the audio output
@@ -51,4 +71,5 @@ const FAUST_DSP_VOICES = 0;
51
71
 
52
72
  // Set page title to the DSP name
53
73
  document.title = name;
74
+
54
75
  })();
@@ -41,6 +41,10 @@ audioContext.suspend();
41
41
 
42
42
  $buttonDsp.disabled = true;
43
43
 
44
+ // Declare faustNode as a global variable
45
+ let faustNode;
46
+
47
+ // Called at load time
44
48
  (async () => {
45
49
 
46
50
  const { createFaustNode, connectToAudioInput, createFaustUI } = await import("./create-node.js");
@@ -111,7 +115,8 @@ $buttonDsp.disabled = true;
111
115
 
112
116
  // To test the ScriptProcessorNode mode
113
117
  // const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES, true);
114
- const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES);
118
+ const result = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES);
119
+ faustNode = result.faustNode; // Assign to the global variable
115
120
  if (!faustNode) throw new Error("Faust DSP not compiled");
116
121
 
117
122
  // Create the Faust UI
@@ -128,22 +133,34 @@ $buttonDsp.disabled = true;
128
133
  if (navigator.requestMIDIAccess) await buildMidiDeviceMenu(faustNode);
129
134
  else $spanMidiInput.hidden = true;
130
135
 
131
- // Set the title and enable the DSP button
132
- $buttonDsp.disabled = false;
133
- document.title = name;
134
- let sensorHandlersBound = false;
135
- $buttonDsp.onclick = async () => {
136
- // Activate sensor listeners
137
- if (!sensorHandlersBound) {
138
- await faustNode.startSensors();
139
- sensorHandlersBound = true;
140
- }
141
- if (audioContext.state === "running") {
142
- $buttonDsp.textContent = "Suspended";
143
- await audioContext.suspend();
144
- } else if (audioContext.state === "suspended") {
145
- $buttonDsp.textContent = "Running";
146
- await audioContext.resume();
147
- }
148
- }
149
136
  })();
137
+
138
+ // Set the title and enable the DSP button
139
+ $buttonDsp.disabled = false;
140
+ document.title = name;
141
+ let sensorHandlersBound = false;
142
+
143
+ // Activate AudioContext and Sensors on user interaction
144
+ $buttonDsp.onclick = async () => {
145
+
146
+ // Import the requestPermissions function
147
+ const { requestPermissions } = await import("./create-node.js");
148
+
149
+ // Request permission for sensors
150
+ await requestPermissions();
151
+
152
+ // Activate sensor listeners
153
+ if (!sensorHandlersBound) {
154
+ await faustNode.startSensors();
155
+ sensorHandlersBound = true;
156
+ }
157
+
158
+ // Activate or suspend the AudioContext
159
+ if (audioContext.state === "running") {
160
+ $buttonDsp.textContent = "Suspended";
161
+ await audioContext.suspend();
162
+ } else if (audioContext.state === "suspended") {
163
+ $buttonDsp.textContent = "Running";
164
+ await audioContext.resume();
165
+ }
166
+ }
@@ -5,8 +5,15 @@ const FAUST_DSP_VOICES = 0;
5
5
  // Set to true if the DSP has an effect
6
6
  const FAUST_DSP_HAS_EFFECT = false;
7
7
 
8
- const CACHE_NAME = "FAUST_DSP_NAME-static"; // Cache name without versioning
8
+ const CACHE_NAME = "FAUST_DSP_NAME_VERSION_DATE"; // Cache name with versioning
9
9
 
10
+ /**
11
+ * List of essential resources required for the **Mono DSP** version of the application.
12
+ *
13
+ * - These files are cached to enable offline functionality and improve loading speed.
14
+ * - Includes the main HTML, JavaScript, and CSS files required for the app.
15
+ * - Contains Faust-related files needed for DSP processing.
16
+ */
10
17
  const MONO_RESOURCES = [
11
18
  "./index.html",
12
19
  "./index.js",
@@ -18,11 +25,23 @@ const MONO_RESOURCES = [
18
25
  "./dsp-meta.json"
19
26
  ];
20
27
 
28
+ /**
29
+ * List of resources for the **Polyphonic DSP** version of the application.
30
+ *
31
+ * - Extends the mono resource list by adding a **mixer module**.
32
+ * - The mixer module is required to handle multiple simultaneous voices used in a polyphonic instrument.
33
+ */
21
34
  const POLY_RESOURCES = [
22
35
  ...MONO_RESOURCES,
23
36
  "./mixer-module.wasm",
24
37
  ];
25
38
 
39
+ /**
40
+ * List of resources for the **Polyphonic DSP with Effects** version.
41
+ *
42
+ * - Extends the polyphonic resource list by adding an **effect module**.
43
+ * - The effect module allows applying audio effects to the polyphonic instrument.
44
+ */
26
45
  const POLY_EFFECT_RESOURCES = [
27
46
  ...POLY_RESOURCES,
28
47
  "./effect-module.wasm",
@@ -33,13 +52,16 @@ const POLY_EFFECT_RESOURCES = [
33
52
  const serviceWorkerGlobalScope = self;
34
53
 
35
54
  /**
36
- * Install the service worker and cache the resources
55
+ * Install the service worker, cache essential resources, and prepare for immediate activation.
56
+ *
57
+ * - Opens the cache and stores required assets based on the app's configuration.
58
+ * - Ensures resources are preloaded for offline access.
37
59
  */
38
60
  serviceWorkerGlobalScope.addEventListener("install", (event) => {
39
61
  console.log("Service worker installed");
40
62
  event.waitUntil((async () => {
41
63
  const cache = await caches.open(CACHE_NAME);
42
- const resources = (FAUST_DSP_VOICES && FAUST_DSP_HAS_EFFECT) ? POLY_EFFECT_RESOURCES : FAUST_DSP_VOICES ? POLY_RESOURCES : MONO_RESOURCES;
64
+ const resources = (FAUST_DSP_VOICES && FAUST_DSP_HAS_EFFECT) ? POLY_EFFECT_RESOURCES : (FAUST_DSP_VOICES ? POLY_RESOURCES : MONO_RESOURCES);
43
65
  try {
44
66
  return cache.addAll(resources);
45
67
  } catch (error) {
@@ -48,6 +70,16 @@ serviceWorkerGlobalScope.addEventListener("install", (event) => {
48
70
  })());
49
71
  });
50
72
 
73
+ /**
74
+ * Handles the activation of the Service Worker.
75
+ *
76
+ * - Claims control over all clients immediately, bypassing the default behavior that
77
+ * requires a page reload before the new service worker takes effect.
78
+ * - Once claimed, it finds all active window clients and reloads them to ensure they are
79
+ * controlled by the latest version of the service worker.
80
+ * - This approach ensures that updates to the service worker take effect immediately
81
+ * across all open pages, preventing potential inconsistencies.
82
+ */
51
83
  serviceWorkerGlobalScope.addEventListener("activate", (event) => {
52
84
  console.log("Service worker activated");
53
85
  event.waitUntil(
@@ -61,7 +93,20 @@ serviceWorkerGlobalScope.addEventListener("activate", (event) => {
61
93
  );
62
94
  });
63
95
 
64
- /** @type {(response: Response) => Response} */
96
+ /**
97
+ * Adjusts response headers to enforce Cross-Origin Opener Policy (COOP) and Cross-Origin Embedder Policy (COEP).
98
+ *
99
+ * - Ensures that the response is served with `Cross-Origin-Opener-Policy: same-origin`
100
+ * and `Cross-Origin-Embedder-Policy: require-corp`.
101
+ * - Required for enabling **cross-origin isolated** environments in web applications.
102
+ * - Necessary for features like **SharedArrayBuffer**, WebAssembly threads, and
103
+ * high-performance APIs that require isolation.
104
+ * - Creates a new `Response` object with the modified headers while preserving
105
+ * the original response body and status.
106
+ *
107
+ * @param {Response} response - The original HTTP response object.
108
+ * @returns {Response} A new response with updated security headers.
109
+ */
65
110
  const getCrossOriginIsolatedResponse = (response) => {
66
111
  // Modify headers to include COOP & COEP
67
112
  const headers = new Headers(response.headers);
@@ -79,7 +124,15 @@ const getCrossOriginIsolatedResponse = (response) => {
79
124
  };
80
125
 
81
126
  /**
82
- * Intercept fetch requests to enforce COOP and COEP headers.
127
+ * Intercepts fetch requests and enforces COOP and COEP headers for security.
128
+ *
129
+ * - Checks if the requested resource is available in the cache:
130
+ * - If found, returns a cached response with `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` headers.
131
+ * - If not found, fetches the resource from the network, applies COOP/COEP headers, and caches the response (for GET requests).
132
+ * - Ensures cross-origin isolation, required for APIs like `SharedArrayBuffer` and WebAssembly threading.
133
+ * - Handles network errors gracefully by returning a 503 "Service Unavailable" response when needed.
134
+ *
135
+ * @param {FetchEvent} event - The fetch event triggered by the browser.
83
136
  */
84
137
  serviceWorkerGlobalScope.addEventListener("fetch", (event) => {
85
138
 
@@ -618,6 +618,20 @@ export interface IFaustBaseWebAudioDsp {
618
618
  * @param value - the MIDI controller value (0..16383)
619
619
  */
620
620
  pitchWheel(chan: number, value: number): void;
621
+ /**
622
+ * Handle MIDI keyOn messages.
623
+ * @param channel
624
+ * @param pitch
625
+ * @param velocity
626
+ */
627
+ keyOn(channel: number, pitch: number, velocity: number): void;
628
+ /**
629
+ * Handle MIDI keyOn messages.
630
+ * @param channel
631
+ * @param pitch
632
+ * @param velocity
633
+ */
634
+ keyOff(channel: number, pitch: number, velocity: number): void;
621
635
  /**
622
636
  * Set parameter value.
623
637
  *
@@ -767,6 +781,24 @@ export declare class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
767
781
  min: number;
768
782
  max: number;
769
783
  }[][];
784
+ protected fMidiKeyLabel: {
785
+ path: string;
786
+ chan: number;
787
+ min: number;
788
+ max: number;
789
+ }[][];
790
+ protected fMidiKeyOnLabel: {
791
+ path: string;
792
+ chan: number;
793
+ min: number;
794
+ max: number;
795
+ }[][];
796
+ protected fMidiKeyOffLabel: {
797
+ path: string;
798
+ chan: number;
799
+ min: number;
800
+ max: number;
801
+ }[][];
770
802
  protected fPathTable: {
771
803
  [address: string]: number;
772
804
  };
@@ -826,6 +858,8 @@ export declare class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
826
858
  getNumOutputs(): number;
827
859
  midiMessage(data: number[] | Uint8Array): void;
828
860
  ctrlChange(channel: number, ctrl: number, value: number): void;
861
+ keyOn(channel: number, pitch: number, velocity: number): void;
862
+ keyOff(channel: number, pitch: number, velocity: number): void;
829
863
  pitchWheel(channel: number, wheel: number): void;
830
864
  setParamValue(path: string, value: number): void;
831
865
  getParamValue(path: string): number;
@@ -835,11 +869,11 @@ export declare class FaustBaseWebAudioDsp implements IFaustBaseWebAudioDsp {
835
869
  getUI(): FaustUIDescriptor;
836
870
  getDescriptors(): FaustUIInputItem[];
837
871
  hasSoundfiles(): boolean;
872
+ startSensors(): void;
873
+ stopSensors(): void;
838
874
  start(): void;
839
875
  stop(): void;
840
876
  destroy(): void;
841
- startSensors(): void;
842
- stopSensors(): void;
843
877
  }
844
878
  export declare class FaustMonoWebAudioDsp extends FaustBaseWebAudioDsp implements IFaustMonoWebAudioDsp {
845
879
  private fInstance;
@@ -1180,6 +1214,8 @@ export declare class FaustOfflineProcessor<Poly extends boolean = false> {
1180
1214
  midiMessage(data: number[] | Uint8Array): void;
1181
1215
  ctrlChange(chan: number, ctrl: number, value: number): void;
1182
1216
  pitchWheel(chan: number, value: number): void;
1217
+ keyOn(channel: number, pitch: number, velocity: number): void;
1218
+ keyOff(channel: number, pitch: number, velocity: number): void;
1183
1219
  setParamValue(path: string, value: number): void;
1184
1220
  getParamValue(path: string): number;
1185
1221
  getParams(): string[];
@@ -1374,6 +1410,8 @@ export declare class FaustAudioWorkletNode<Poly extends boolean = false> extends
1374
1410
  midiMessage(data: number[] | Uint8Array): void;
1375
1411
  ctrlChange(channel: number, ctrl: number, value: number): void;
1376
1412
  pitchWheel(channel: number, wheel: number): void;
1413
+ keyOn(channel: number, pitch: number, velocity: number): void;
1414
+ keyOff(channel: number, pitch: number, velocity: number): void;
1377
1415
  get hasAccInput(): boolean;
1378
1416
  propagateAcc(accelerationIncludingGravity: NonNullable<DeviceMotionEvent["accelerationIncludingGravity"]>, invert?: boolean): void;
1379
1417
  get hasGyrInput(): boolean;
@@ -1441,6 +1479,8 @@ export declare class FaustScriptProcessorNode<Poly extends boolean = false> exte
1441
1479
  midiMessage(data: number[] | Uint8Array): void;
1442
1480
  ctrlChange(chan: number, ctrl: number, value: number): void;
1443
1481
  pitchWheel(chan: number, value: number): void;
1482
+ keyOn(channel: number, pitch: number, velocity: number): void;
1483
+ keyOff(channel: number, pitch: number, velocity: number): void;
1444
1484
  setParamValue(path: string, value: number): void;
1445
1485
  getParamValue(path: string): number;
1446
1486
  getParams(): string[];