@grame/faustwasm 0.7.10 → 0.8.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.
Files changed (38) hide show
  1. package/README.md +2 -2
  2. package/assets/standalone/create-node.js +31 -1
  3. package/assets/standalone/faust-ui/index.js +2 -2
  4. package/assets/standalone/faust-ui/index.js.map +1 -1
  5. package/assets/standalone/faustwasm/index.d.ts +76 -9
  6. package/assets/standalone/faustwasm/index.js +272 -107
  7. package/assets/standalone/faustwasm/index.js.map +4 -4
  8. package/assets/standalone/index-pwa.js +132 -98
  9. package/assets/standalone/index-template.js +44 -23
  10. package/assets/standalone/index.js +95 -80
  11. package/assets/standalone/service-worker.js +47 -8
  12. package/dist/cjs/index.d.ts +76 -9
  13. package/dist/cjs/index.js +272 -107
  14. package/dist/cjs/index.js.map +4 -4
  15. package/dist/cjs-bundle/index.d.ts +76 -9
  16. package/dist/cjs-bundle/index.js +272 -107
  17. package/dist/cjs-bundle/index.js.map +4 -4
  18. package/dist/esm/index.d.ts +76 -9
  19. package/dist/esm/index.js +272 -107
  20. package/dist/esm/index.js.map +4 -4
  21. package/dist/esm-bundle/index.d.ts +76 -9
  22. package/dist/esm-bundle/index.js +272 -107
  23. package/dist/esm-bundle/index.js.map +4 -4
  24. package/package.json +2 -2
  25. package/src/FaustAudioWorkletCommunicator.ts +143 -0
  26. package/src/FaustAudioWorkletNode.ts +27 -42
  27. package/src/FaustAudioWorkletProcessor.ts +34 -15
  28. package/src/FaustDspGenerator.ts +20 -2
  29. package/src/FaustFFTAudioWorkletProcessor.ts +31 -2
  30. package/src/FaustOfflineProcessor.ts +6 -0
  31. package/src/FaustScriptProcessorNode.ts +8 -31
  32. package/src/FaustWebAudioDsp.ts +38 -10
  33. package/test/faustlive-wasm/faust-ui/index.js +2 -2
  34. package/test/faustlive-wasm/faust-ui/index.js.map +1 -1
  35. package/test/faustlive-wasm/faustwasm/index.d.ts +76 -9
  36. package/test/faustlive-wasm/faustwasm/index.js +272 -107
  37. package/test/faustlive-wasm/faustwasm/index.js.map +4 -4
  38. package/assets/standalone/coi-serviceworker.js +0 -146
@@ -29,121 +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 } = await import("./create-node.js");
38
+ // Import the create-node module
39
+ const { createFaustNode, createFaustUI } = await import("./create-node.js");
40
+
35
41
  // To test the ScriptProcessorNode mode
36
- //const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "osc", FAUST_DSP_VOICES, true, 512);
37
- 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
38
45
  if (!faustNode) throw new Error("Faust DSP not compiled");
39
46
 
40
47
  // Create the Faust UI
41
- const { createFaustUI } = await import("./create-node.js");
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
62
 
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
- }
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.");
80
79
  }
80
+ }
81
81
 
82
- let sensorHandlersBound = false;
83
- let midiHandlersBound = false;
84
-
85
- // Function to resume AudioContext, activate MIDI and Sensors on user interaction
86
- async function activateAudioMIDISensors() {
87
-
88
- // Resume the AudioContext
89
- if (audioContext.state === 'suspended') {
90
- await audioContext.resume();
91
- }
92
-
93
- // Activate sensor listeners
94
- if (!sensorHandlersBound) {
95
- await faustNode.startSensors();
96
- sensorHandlersBound = true;
97
- }
98
-
99
- // Initialize the MIDI setup
100
- if (!midiHandlersBound && FAUST_DSP_VOICES > 0) {
101
- startMIDI();
102
- midiHandlersBound = true;
103
- }
104
-
105
- // Connect the Faust node to the audio output
106
- faustNode.connect(audioContext.destination);
107
-
108
- // Connect the Faust node to the audio input
109
- if (faustNode.numberOfInputs > 0) {
110
- const { connectToAudioInput } = await import("./create-node.js");
111
- await connectToAudioInput(audioContext, null, faustNode, null);
112
- }
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.");
113
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() {
114
106
 
115
- // Function to suspend AudioContext, deactivate MIDI and Sensors on user interaction
116
- async function deactivateAudioMIDISensors() {
117
-
118
- // Suspend the AudioContext
119
- if (audioContext.state === 'running') {
120
- await audioContext.suspend();
121
- }
122
-
123
- // Deactivate sensor listeners
124
- if (sensorHandlersBound) {
125
- faustNode.stopSensors();
126
- sensorHandlersBound = false;
127
- }
128
-
129
- // Deactivate the MIDI setup
130
- if (midiHandlersBound && FAUST_DSP_VOICES > 0) {
131
- stopMIDI();
132
- midiHandlersBound = false;
133
- }
107
+ // Import the create-node module
108
+ const { connectToAudioInput, requestPermissions } = await import("./create-node.js");
109
+
110
+ // Request permission for sensors
111
+ await requestPermissions();
112
+
113
+ // Activate sensor listeners
114
+ if (!sensorHandlersBound) {
115
+ await faustNode.startSensors();
116
+ sensorHandlersBound = true;
117
+ }
118
+
119
+ // Initialize the MIDI setup
120
+ if (!midiHandlersBound && FAUST_DSP_VOICES > 0) {
121
+ startMIDI();
122
+ midiHandlersBound = true;
134
123
  }
135
124
 
136
- // Add event listeners for user interactions
125
+ // Connect the Faust node to the audio output
126
+ faustNode.connect(audioContext.destination);
137
127
 
138
- // Activate AudioContext, MIDI and Sensors on user interaction
139
- window.addEventListener('click', activateAudioMIDISensors);
140
- window.addEventListener('touchstart', activateAudioMIDISensors);
128
+ // Connect the Faust node to the audio input
129
+ if (faustNode.numberOfInputs > 0) {
130
+ await connectToAudioInput(audioContext, null, faustNode, null);
131
+ }
132
+
133
+ // Resume the AudioContext
134
+ if (audioContext.state === 'suspended') {
135
+ await audioContext.resume();
136
+ }
137
+ }
141
138
 
142
- // Deactivate AudioContext, MIDI and Sensors on user interaction
143
- window.addEventListener('visibilitychange', function () {
144
- if (window.visibilityState === 'hidden') {
145
- deactivateAudioMIDISensors();
146
- }
139
+ // Function to suspend AudioContext, deactivate MIDI and Sensors on user interaction
140
+ async function deactivateAudioMIDISensors() {
141
+
142
+ // Suspend the AudioContext
143
+ if (audioContext.state === 'running') {
144
+ await audioContext.suspend();
145
+ }
146
+
147
+ // Deactivate sensor listeners
148
+ if (sensorHandlersBound) {
149
+ faustNode.stopSensors();
150
+ sensorHandlersBound = false;
151
+ }
152
+
153
+ // Deactivate the MIDI setup
154
+ if (midiHandlersBound && FAUST_DSP_VOICES > 0) {
155
+ stopMIDI();
156
+ midiHandlersBound = false;
157
+ }
158
+ }
159
+
160
+ // Event listener to handle user interaction
161
+ function handleUserInteraction() {
162
+
163
+ // Resume AudioContext synchronously
164
+ resumeAudioContext();
165
+
166
+ // Launch the activation of MIDI and Sensors
167
+ activateMIDISensors().catch(error => {
168
+ console.error('Error when activating audio, MIDI and sensors:', error);
147
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
+
148
183
 
149
- })();
@@ -1,17 +1,49 @@
1
1
  // Set to > 0 if the DSP is polyphonic
2
2
  const FAUST_DSP_VOICES = 0;
3
3
 
4
- (async () => {
5
- const { createFaustNode } = await import("./create-node.js");
4
+ // Declare faustNode as a global variable
5
+ let faustNode;
6
+
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 () => {
6
19
 
7
- // Create audio context
8
- const AudioCtx = window.AudioContext || window.webkitAudioContext;
9
- const audioContext = new AudioCtx({ latencyHint: 0.00001 });
10
- audioContext.suspend();
20
+ // Import the requestPermissions function
21
+ const { requestPermissions } = await import("./create-node.js");
11
22
 
12
- // Create audio context activation button
13
- /** @type {HTMLButtonElement} */
14
- const $buttonDsp = document.getElementById("button-dsp");
23
+ // Request permission for sensors
24
+ await requestPermissions();
25
+
26
+ // Activate sensor listeners
27
+ if (!sensorHandlersBound) {
28
+ await faustNode.startSensors();
29
+ sensorHandlersBound = true;
30
+ }
31
+
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
+ }
42
+
43
+ // Called at load time
44
+ (async () => {
45
+
46
+ const { createFaustNode, connectToAudioInput } = await import("./create-node.js");
15
47
 
16
48
  const play = (node) => {
17
49
  node.keyOn(0, 60, 100);
@@ -20,21 +52,10 @@ const FAUST_DSP_VOICES = 0;
20
52
  setTimeout(() => node.allNotesOff(), 5000);
21
53
  setTimeout(() => play(node), 7000);
22
54
  }
23
- // Function to activate audio context
24
- $buttonDsp.disabled = true;
25
- $buttonDsp.onclick = async () => {
26
- if (audioContext.state === "running") {
27
- $buttonDsp.textContent = "Suspended";
28
- await audioContext.suspend();
29
- } else if (audioContext.state === "suspended") {
30
- $buttonDsp.textContent = "Running";
31
- await audioContext.resume();
32
- if (FAUST_DSP_VOICES) play(faustNode);
33
- }
34
- }
35
55
 
36
56
  // Create Faust node
37
- 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
38
59
  if (!faustNode) throw new Error("Faust DSP not compiled");
39
60
 
40
61
  // Connect the Faust node to the audio output
@@ -42,7 +63,6 @@ const FAUST_DSP_VOICES = 0;
42
63
 
43
64
  // Connect the Faust node to the audio input
44
65
  if (faustNode.getNumInputs() > 0) {
45
- const { connectToAudioInput } = await import("./create-node.js");
46
66
  await connectToAudioInput(audioContext, null, faustNode, null);
47
67
  }
48
68
 
@@ -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,82 +41,85 @@ audioContext.suspend();
41
41
 
42
42
  $buttonDsp.disabled = true;
43
43
 
44
- /**
45
- * @param {FaustAudioWorkletNode} faustNode
46
- */
47
- const buildAudioDeviceMenu = async (faustNode) => {
48
-
49
- const { connectToAudioInput } = await import("./create-node.js");
50
-
51
- let inputStreamNode = null;
52
- const handleDeviceChange = async () => {
53
- const devicesInfo = await navigator.mediaDevices.enumerateDevices();
54
- $selectAudioInput.innerHTML = "";
55
- devicesInfo.forEach((deviceInfo, i) => {
56
- const { kind, deviceId, label } = deviceInfo;
57
- if (kind === "audioinput") {
58
- const option = new Option(label || `microphone ${i + 1}`, deviceId);
59
- $selectAudioInput.add(option);
44
+ // Declare faustNode as a global variable
45
+ let faustNode;
46
+
47
+ // Called at load time
48
+ (async () => {
49
+
50
+ const { createFaustNode, 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);
60
75
  }
61
- });
62
- }
63
- await handleDeviceChange();
64
- navigator.mediaDevices.addEventListener("devicechange", handleDeviceChange)
65
- $selectAudioInput.onchange = async () => {
66
- const id = $selectAudioInput.value;
76
+ };
77
+
78
+ // Connect to the default audio input device
67
79
  if (faustNode.getNumInputs() > 0) {
68
- inputStreamNode = await connectToAudioInput(audioContext, id, faustNode, inputStreamNode);
80
+ inputStreamNode = await connectToAudioInput(audioContext, null, faustNode, inputStreamNode);
69
81
  }
70
82
  };
71
83
 
72
- // Connect to the default audio input device
73
- if (faustNode.getNumInputs() > 0) {
74
- inputStreamNode = await connectToAudioInput(audioContext, null, faustNode, inputStreamNode);
75
- }
76
- };
77
-
78
- /**
79
- * @param {FaustAudioWorkletNode} faustNode
80
- */
81
- const buildMidiDeviceMenu = async (faustNode) => {
82
- const midiAccess = await navigator.requestMIDIAccess();
83
- /** @type {WebMidi.MIDIInput} */
84
- let currentInput;
85
84
  /**
86
- * @param {WebMidi.MIDIMessageEvent} e
85
+ * @param {FaustAudioWorkletNode} faustNode
87
86
  */
88
- const handleMidiMessage = e => faustNode.midiMessage(e.data);
89
- const handleStateChange = () => {
90
- const { inputs } = midiAccess;
91
- if ($selectMidiInput.options.length === inputs.size + 1) return;
92
- if (currentInput) currentInput.removeEventListener("midimessage", handleMidiMessage);
93
- $selectMidiInput.innerHTML = '<option value="-1" disabled selected>Select...</option>';
94
- inputs.forEach((midiInput) => {
95
- const { name, id } = midiInput;
96
- const option = new Option(name, id);
97
- $selectMidiInput.add(option);
98
- });
87
+ const buildMidiDeviceMenu = async (faustNode) => {
88
+ const midiAccess = await navigator.requestMIDIAccess();
89
+ /** @type {WebMidi.MIDIInput} */
90
+ let currentInput;
91
+ /**
92
+ * @param {WebMidi.MIDIMessageEvent} e
93
+ */
94
+ const handleMidiMessage = e => faustNode.midiMessage(e.data);
95
+ const handleStateChange = () => {
96
+ const { inputs } = midiAccess;
97
+ if ($selectMidiInput.options.length === inputs.size + 1) return;
98
+ if (currentInput) currentInput.removeEventListener("midimessage", handleMidiMessage);
99
+ $selectMidiInput.innerHTML = '<option value="-1" disabled selected>Select...</option>';
100
+ inputs.forEach((midiInput) => {
101
+ const { name, id } = midiInput;
102
+ const option = new Option(name, id);
103
+ $selectMidiInput.add(option);
104
+ });
105
+ };
106
+ handleStateChange();
107
+ midiAccess.addEventListener("statechange", handleStateChange);
108
+ $selectMidiInput.onchange = () => {
109
+ if (currentInput) currentInput.removeEventListener("midimessage", handleMidiMessage);
110
+ const id = $selectMidiInput.value;
111
+ currentInput = midiAccess.inputs.get(id);
112
+ currentInput.addEventListener("midimessage", handleMidiMessage);
113
+ };
99
114
  };
100
- handleStateChange();
101
- midiAccess.addEventListener("statechange", handleStateChange);
102
- $selectMidiInput.onchange = () => {
103
- if (currentInput) currentInput.removeEventListener("midimessage", handleMidiMessage);
104
- const id = $selectMidiInput.value;
105
- currentInput = midiAccess.inputs.get(id);
106
- currentInput.addEventListener("midimessage", handleMidiMessage);
107
- };
108
- };
109
-
110
- (async () => {
111
115
 
112
- const { createFaustNode } = await import("./create-node.js");
113
116
  // To test the ScriptProcessorNode mode
114
117
  // const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES, true);
115
- 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
116
120
  if (!faustNode) throw new Error("Faust DSP not compiled");
117
121
 
118
122
  // Create the Faust UI
119
- const { createFaustUI } = await import("./create-node.js");
120
123
  await createFaustUI($divFaustUI, faustNode);
121
124
 
122
125
  // Connect the Faust node to the audio output
@@ -130,22 +133,34 @@ const buildMidiDeviceMenu = async (faustNode) => {
130
133
  if (navigator.requestMIDIAccess) await buildMidiDeviceMenu(faustNode);
131
134
  else $spanMidiInput.hidden = true;
132
135
 
133
- // Set the title and enable the DSP button
134
- $buttonDsp.disabled = false;
135
- document.title = name;
136
- let sensorHandlersBound = false;
137
- $buttonDsp.onclick = async () => {
138
- // Activate sensor listeners
139
- if (!sensorHandlersBound) {
140
- await faustNode.startSensors();
141
- sensorHandlersBound = true;
142
- }
143
- if (audioContext.state === "running") {
144
- $buttonDsp.textContent = "Suspended";
145
- await audioContext.suspend();
146
- } else if (audioContext.state === "suspended") {
147
- $buttonDsp.textContent = "Running";
148
- await audioContext.resume();
149
- }
150
- }
151
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
+ }
@@ -29,7 +29,7 @@ const POLY_EFFECT_RESOURCES = [
29
29
  "./effect-meta.json",
30
30
  ];
31
31
 
32
- /**@type {ServiceWorkerGlobalScope} */
32
+ /** @type {ServiceWorkerGlobalScope} */
33
33
  const serviceWorkerGlobalScope = self;
34
34
 
35
35
  /**
@@ -48,26 +48,65 @@ serviceWorkerGlobalScope.addEventListener("install", (event) => {
48
48
  })());
49
49
  });
50
50
 
51
- serviceWorkerGlobalScope.addEventListener("activate", () => console.log("Service worker activated"));
51
+ serviceWorkerGlobalScope.addEventListener("activate", (event) => {
52
+ console.log("Service worker activated");
53
+ event.waitUntil(
54
+ clients.claim().then(() => {
55
+ return clients.matchAll({ type: "window" }).then((clients) => {
56
+ clients.forEach((client) => {
57
+ client.navigate(client.url);
58
+ });
59
+ });
60
+ })
61
+ );
62
+ });
63
+
64
+ /** @type {(response: Response) => Response} */
65
+ const getCrossOriginIsolatedResponse = (response) => {
66
+ // Modify headers to include COOP & COEP
67
+ const headers = new Headers(response.headers);
68
+ headers.set("Cross-Origin-Opener-Policy", "same-origin");
69
+ headers.set("Cross-Origin-Embedder-Policy", "require-corp");
70
+
71
+ // Create a new response with the modified headers
72
+ const modifiedResponse = new Response(response.body, {
73
+ status: response.status,
74
+ statusText: response.statusText,
75
+ headers
76
+ });
52
77
 
78
+ return modifiedResponse;
79
+ };
80
+
81
+ /**
82
+ * Intercept fetch requests to enforce COOP and COEP headers.
83
+ */
53
84
  serviceWorkerGlobalScope.addEventListener("fetch", (event) => {
85
+
54
86
  event.respondWith((async () => {
55
87
  const cache = await caches.open(CACHE_NAME);
56
88
  const cachedResponse = await cache.match(event.request);
89
+
57
90
  if (cachedResponse) {
58
- return cachedResponse;
91
+ return getCrossOriginIsolatedResponse(cachedResponse);
59
92
  } else {
60
93
  try {
61
94
  const fetchResponse = await fetch(event.request);
62
- // Ensure the response is valid before caching it
95
+
63
96
  if (event.request.method === "GET" && fetchResponse && fetchResponse.status === 200 && fetchResponse.type === "basic") {
64
- cache.put(event.request, fetchResponse.clone());
97
+ const modifiedResponse = getCrossOriginIsolatedResponse(fetchResponse);
98
+ // Store the modified response in the cache
99
+ await cache.put(event.request, modifiedResponse.clone());
100
+ // Return the modified response to the browser
101
+ return modifiedResponse;
65
102
  }
103
+
66
104
  return fetchResponse;
67
- } catch (e) {
68
- // Network access failure
69
- console.log("Network access error", e);
105
+ } catch (error) {
106
+ console.error("Network access error", error);
107
+ return new Response("Network error", { status: 503, statusText: "Service Unavailable" });
70
108
  }
71
109
  }
72
110
  })());
73
111
  });
112
+