@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.
- package/README.md +2 -2
- package/assets/standalone/create-node.js +31 -1
- package/assets/standalone/faust-ui/index.js +2 -2
- package/assets/standalone/faust-ui/index.js.map +1 -1
- package/assets/standalone/faustwasm/index.d.ts +76 -9
- package/assets/standalone/faustwasm/index.js +272 -107
- package/assets/standalone/faustwasm/index.js.map +4 -4
- package/assets/standalone/index-pwa.js +132 -98
- package/assets/standalone/index-template.js +44 -23
- package/assets/standalone/index.js +95 -80
- package/assets/standalone/service-worker.js +47 -8
- package/dist/cjs/index.d.ts +76 -9
- package/dist/cjs/index.js +272 -107
- package/dist/cjs/index.js.map +4 -4
- package/dist/cjs-bundle/index.d.ts +76 -9
- package/dist/cjs-bundle/index.js +272 -107
- package/dist/cjs-bundle/index.js.map +4 -4
- package/dist/esm/index.d.ts +76 -9
- package/dist/esm/index.js +272 -107
- package/dist/esm/index.js.map +4 -4
- package/dist/esm-bundle/index.d.ts +76 -9
- package/dist/esm-bundle/index.js +272 -107
- package/dist/esm-bundle/index.js.map +4 -4
- package/package.json +2 -2
- package/src/FaustAudioWorkletCommunicator.ts +143 -0
- package/src/FaustAudioWorkletNode.ts +27 -42
- package/src/FaustAudioWorkletProcessor.ts +34 -15
- package/src/FaustDspGenerator.ts +20 -2
- package/src/FaustFFTAudioWorkletProcessor.ts +31 -2
- package/src/FaustOfflineProcessor.ts +6 -0
- package/src/FaustScriptProcessorNode.ts +8 -31
- package/src/FaustWebAudioDsp.ts +38 -10
- package/test/faustlive-wasm/faust-ui/index.js +2 -2
- package/test/faustlive-wasm/faust-ui/index.js.map +1 -1
- package/test/faustlive-wasm/faustwasm/index.d.ts +76 -9
- package/test/faustlive-wasm/faustwasm/index.js +272 -107
- package/test/faustlive-wasm/faustwasm/index.js.map +4 -4
- 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
|
-
|
|
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
|
|
37
|
-
const
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
//
|
|
125
|
+
// Connect the Faust node to the audio output
|
|
126
|
+
faustNode.connect(audioContext.destination);
|
|
137
127
|
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
5
|
-
|
|
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
|
-
//
|
|
8
|
-
const
|
|
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
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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,
|
|
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 {
|
|
85
|
+
* @param {FaustAudioWorkletNode} faustNode
|
|
87
86
|
*/
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
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
|
-
|
|
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", () =>
|
|
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
|
-
|
|
95
|
+
|
|
63
96
|
if (event.request.method === "GET" && fetchResponse && fetchResponse.status === 200 && fetchResponse.type === "basic") {
|
|
64
|
-
|
|
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 (
|
|
68
|
-
|
|
69
|
-
|
|
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
|
+
|