@grame/faustwasm 0.4.10 → 0.5.1
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 +20 -5
- package/assets/standalone/create-node.js +28 -1
- package/assets/standalone/index-pwa.html +76 -0
- package/assets/standalone/index-pwa.js +139 -0
- package/assets/standalone/index-template.js +9 -3
- package/assets/standalone/index.js +11 -26
- package/build +25 -0
- package/package.json +1 -1
- package/scripts/faust2wasm.js +8 -2
- package/src/copyWebStandaloneAssets.d.ts +2 -1
- package/src/copyWebStandaloneAssets.js +40 -3
package/README.md
CHANGED
|
@@ -59,17 +59,34 @@ node scripts/faust2wasm.js test/poly.dsp test/out -poly
|
|
|
59
59
|
```
|
|
60
60
|
will create a set of files: `index.js`, `dsp-module.wasm`, `dsp-meta.json`, `index.html` (and possibly `effect-module.wasm`, `effect-meta.json`) in the `out` folder.
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
#### Creating a Progressive Web Application of a Faust DSP
|
|
62
|
+
#### Creating a Progressive Web Application (PWA) of a Faust DSP
|
|
64
63
|
|
|
65
64
|
You can create a standalone Progressive Web Application using the same command line:
|
|
66
65
|
|
|
66
|
+
```bash
|
|
67
|
+
node scripts/faust2wasm.js test/rev.dsp test/rev -pwa
|
|
68
|
+
```
|
|
69
|
+
will create a set of files: `icon.png`, `service-worker.js`, `manifest.json`, `index.js`, `dsp-module.wasm`, `dsp-meta.json`, `index.html`, and the `faustwasm`, `faust-ui` folders in the `rev` folder.
|
|
70
|
+
|
|
71
|
+
The folder contains the necessary ressources to deploy the Faust application as a PWA on a server, to be installed and used offline. Note that audio files used by the `soundfile` primitives in the DSP code will have to be mannually added in the folder.
|
|
72
|
+
|
|
73
|
+
A standalone polyphonic and MIDI standalone Progressive Web Application can be created with:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
node scripts/faust2wasm.js test/organ1.dsp test/organ1 -poly -pwa
|
|
77
|
+
```
|
|
78
|
+
will create a set of files: `icon.png`, `service-worker.js`, `manifest.json`, `index.js`, `dsp-module.wasm`, `dsp-meta.json`, `effect-module.wasm`, `effect-meta.json`, `index.html`, and the `faustwasm`, `faust-ui` folders in the `organ1` folder.
|
|
79
|
+
|
|
80
|
+
#### Creating a standalone version of a Faust DSP, with audio and MIDI devices selector
|
|
81
|
+
|
|
82
|
+
You can create a standalone using the same command line:
|
|
83
|
+
|
|
67
84
|
```bash
|
|
68
85
|
node scripts/faust2wasm.js test/rev.dsp test/rev -standalone
|
|
69
86
|
```
|
|
70
87
|
will create a set of files: `icon.png`, `service-worker.js`, `manifest.json`, `index.js`, `dsp-module.wasm`, `dsp-meta.json`, `index.html`, and the `faustwasm`, `faust-ui` folders in the `rev` folder.
|
|
71
88
|
|
|
72
|
-
The folder contains the necessary ressources to deploy the Faust application as a PWA on a server, to be installed and used offline. Note that audio files used by the `soundfile` primitives in the DSP code will have to be mannually added in the folder.
|
|
89
|
+
The folder contains the necessary ressources to deploy the Faust application as a PWA on a server, to be installed and used offline. Note that audio files used by the `soundfile` primitives in the DSP code will have to be mannually added in the folder.
|
|
73
90
|
|
|
74
91
|
A standalone polyphonic and MIDI standalone Progressive Web Application can be created with:
|
|
75
92
|
|
|
@@ -78,8 +95,6 @@ node scripts/faust2wasm.js test/organ1.dsp test/organ1 -poly -standalone
|
|
|
78
95
|
```
|
|
79
96
|
will create a set of files: `icon.png`, `service-worker.js`, `manifest.json`, `index.js`, `dsp-module.wasm`, `dsp-meta.json`, `effect-module.wasm`, `effect-meta.json`, `index.html`, and the `faustwasm`, `faust-ui` folders in the `organ1` folder.
|
|
80
97
|
|
|
81
|
-
To test, be sure to launch a local web server at the `test` folder level, then go inside `test\organ1` folder.
|
|
82
|
-
|
|
83
98
|
#### Generate SVG Diagrams of a Faust DSP
|
|
84
99
|
|
|
85
100
|
For example:
|
|
@@ -75,4 +75,31 @@ const createFaustNode = async (audioContext, dspName = "template", voices = 0, s
|
|
|
75
75
|
return { faustNode, dspMeta };
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Connects an audio input stream to a Faust audio node.
|
|
80
|
+
*
|
|
81
|
+
* @param {AudioContext} audioContext - The Web Audio API AudioContext to which the Faust audio node is connected.
|
|
82
|
+
* @param {string} id - The ID of the audio input device to connect.
|
|
83
|
+
* @param {FaustNode} faustNode - The Faust audio node to which the audio input stream will be connected.
|
|
84
|
+
* @param {MediaStreamAudioSourceNode} inputStreamNode - The audio input stream node to be connected to the Faust audio node.
|
|
85
|
+
* @returns {Promise<MediaStreamAudioSourceNode>} - The audio input stream node connected to the Faust audio node.
|
|
86
|
+
*/
|
|
87
|
+
async function connectToAudioInput(audioContext, id, faustNode, inputStreamNode) {
|
|
88
|
+
const constraints = {
|
|
89
|
+
audio: {
|
|
90
|
+
echoCancellation: false,
|
|
91
|
+
noiseSuppression: false,
|
|
92
|
+
autoGainControl: false,
|
|
93
|
+
deviceId: id ? { exact: id } : undefined,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
97
|
+
if (stream) {
|
|
98
|
+
if (inputStreamNode) inputStreamNode.disconnect();
|
|
99
|
+
inputStreamNode = audioContext.createMediaStreamSource(stream);
|
|
100
|
+
inputStreamNode.connect(faustNode);
|
|
101
|
+
}
|
|
102
|
+
return inputStreamNode;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export { createFaustNode, connectToAudioInput };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<link rel="manifest" href="./manifest.json">
|
|
5
|
+
|
|
6
|
+
<head>
|
|
7
|
+
<meta charset="UTF-8">
|
|
8
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
10
|
+
<title>Faust DSP</title>
|
|
11
|
+
<style>
|
|
12
|
+
body {
|
|
13
|
+
color: #b0b0b0;
|
|
14
|
+
background-color: #1f1f1f;
|
|
15
|
+
display: flex;
|
|
16
|
+
top: 0;
|
|
17
|
+
left: 0;
|
|
18
|
+
width: 100%;
|
|
19
|
+
height: 100%;
|
|
20
|
+
margin: 0;
|
|
21
|
+
position: absolute;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
main {
|
|
25
|
+
margin: 20px;
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
flex: 1 1 100%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main span {
|
|
32
|
+
position: relative;
|
|
33
|
+
display: flex;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
main span[hidden] {
|
|
37
|
+
display: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
main span span {
|
|
41
|
+
margin: 0px 10px;
|
|
42
|
+
flex: 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
main span select {
|
|
46
|
+
flex: 2;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#dsp-switch {
|
|
50
|
+
margin: 20px auto;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#button-dsp {
|
|
54
|
+
padding: 10px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#div-faust-ui {
|
|
58
|
+
flex: 1 1 auto;
|
|
59
|
+
display: flex;
|
|
60
|
+
position: relative;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
color: black;
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
65
|
+
<link rel="stylesheet" href="./faust-ui/index.css">
|
|
66
|
+
</head>
|
|
67
|
+
|
|
68
|
+
<body>
|
|
69
|
+
<main>
|
|
70
|
+
<div id="div-faust-ui">
|
|
71
|
+
</div>
|
|
72
|
+
</main>
|
|
73
|
+
<script src="./index.js"></script>
|
|
74
|
+
</body>
|
|
75
|
+
|
|
76
|
+
</html>
|
|
@@ -0,0 +1,139 @@
|
|
|
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
|
+
/**
|
|
33
|
+
* @param {FaustAudioWorkletNode} faustNode
|
|
34
|
+
*/
|
|
35
|
+
const createFaustUI = async (faustNode) => {
|
|
36
|
+
const { FaustUI } = await import("./faust-ui/index.js");
|
|
37
|
+
const $container = document.createElement("div");
|
|
38
|
+
$container.style.margin = "0";
|
|
39
|
+
$container.style.position = "absolute";
|
|
40
|
+
$container.style.overflow = "auto";
|
|
41
|
+
$container.style.display = "flex";
|
|
42
|
+
$container.style.flexDirection = "column";
|
|
43
|
+
$container.style.width = "100%";
|
|
44
|
+
$container.style.height = "100%";
|
|
45
|
+
$divFaustUI.appendChild($container);
|
|
46
|
+
const faustUI = new FaustUI({
|
|
47
|
+
ui: faustNode.getUI(),
|
|
48
|
+
root: $container,
|
|
49
|
+
listenWindowMessage: false,
|
|
50
|
+
listenWindowResize: true,
|
|
51
|
+
});
|
|
52
|
+
faustUI.paramChangeByUI = (path, value) => faustNode.setParamValue(path, value);
|
|
53
|
+
faustNode.setOutputParamHandler((path, value) => faustUI.paramChangeByDSP(path, value));
|
|
54
|
+
$container.style.minWidth = `${faustUI.minWidth}px`;
|
|
55
|
+
$container.style.minHeight = `${faustUI.minHeight}px`;
|
|
56
|
+
faustUI.resize();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
(async () => {
|
|
60
|
+
|
|
61
|
+
const { createFaustNode } = await import("./create-node.js");
|
|
62
|
+
|
|
63
|
+
// To test the ScriptProcessorNode mode
|
|
64
|
+
// const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "osc", FAUST_DSP_VOICES, true);
|
|
65
|
+
const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "osc", FAUST_DSP_VOICES);
|
|
66
|
+
if (!faustNode) throw new Error("Faust DSP not compiled");
|
|
67
|
+
|
|
68
|
+
// Create the Faust UI
|
|
69
|
+
await createFaustUI(faustNode);
|
|
70
|
+
|
|
71
|
+
// Connect the Faust node to the audio output
|
|
72
|
+
faustNode.connect(audioContext.destination);
|
|
73
|
+
|
|
74
|
+
// Connect the Faust node to the audio input
|
|
75
|
+
if (faustNode.getNumInputs() > 0) {
|
|
76
|
+
const { connectToAudioInput } = await import("./create-node.js");
|
|
77
|
+
await connectToAudioInput(audioContext, null, faustNode, null);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Activate sensor listeners
|
|
81
|
+
await faustNode.listenSensors();
|
|
82
|
+
|
|
83
|
+
// Function to initialize MIDI
|
|
84
|
+
function initMIDI() {
|
|
85
|
+
// Check if the browser supports the Web MIDI API
|
|
86
|
+
if (navigator.requestMIDIAccess) {
|
|
87
|
+
navigator.requestMIDIAccess()
|
|
88
|
+
.then(onMIDISuccess, onMIDIFailure);
|
|
89
|
+
} else {
|
|
90
|
+
console.log("Web MIDI API is not supported in this browser.");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Success callback for requesting MIDI access
|
|
95
|
+
function onMIDISuccess(midiAccess) {
|
|
96
|
+
console.log("MIDI Access obtained.");
|
|
97
|
+
// Iterate through all available MIDI inputs
|
|
98
|
+
for (let input of midiAccess.inputs.values()) {
|
|
99
|
+
// Attach the event listener to each input
|
|
100
|
+
input.onmidimessage = handleMIDIMessage;
|
|
101
|
+
console.log(`Connected to input: ${input.name}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Failure callback for requesting MIDI access
|
|
106
|
+
function onMIDIFailure() {
|
|
107
|
+
console.error("Failed to access MIDI devices.");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Dummy event handler for MIDI messages
|
|
111
|
+
function handleMIDIMessage(event) {
|
|
112
|
+
faustNode.midiMessage(event.data);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Initialize the MIDI setup
|
|
116
|
+
if (FAUST_DSP_VOICES > 0) {
|
|
117
|
+
initMIDI();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
})();
|
|
121
|
+
|
|
122
|
+
// Function to resume AudioContext on user interaction
|
|
123
|
+
function resumeAudioContext() {
|
|
124
|
+
if (audioContext.state === 'suspended') {
|
|
125
|
+
audioContext.resume();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Add event listeners for user interactions
|
|
130
|
+
window.addEventListener('click', resumeAudioContext);
|
|
131
|
+
window.addEventListener('touchstart', resumeAudioContext);
|
|
132
|
+
|
|
133
|
+
// Optional: Remove event listeners once the context is resumed
|
|
134
|
+
audioContext.onstatechange = function () {
|
|
135
|
+
if (audioContext.state === 'running') {
|
|
136
|
+
window.removeEventListener('click', resumeAudioContext);
|
|
137
|
+
window.removeEventListener('touchstart', resumeAudioContext);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const FAUST_DSP_VOICES = 0;
|
|
3
3
|
|
|
4
4
|
(async () => {
|
|
5
|
-
const {
|
|
5
|
+
const { createFaustNode } = await import("./create-node.js");
|
|
6
6
|
|
|
7
7
|
// Create audio context
|
|
8
8
|
const AudioCtx = window.AudioContext || window.webkitAudioContext;
|
|
@@ -37,12 +37,18 @@ const FAUST_DSP_VOICES = 0;
|
|
|
37
37
|
const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES);
|
|
38
38
|
if (!faustNode) throw new Error("Faust DSP not compiled");
|
|
39
39
|
|
|
40
|
-
// Connect Faust node to audio
|
|
40
|
+
// Connect the Faust node to the audio output
|
|
41
41
|
faustNode.connect(audioContext.destination);
|
|
42
42
|
|
|
43
|
+
// Connect the Faust node to the audio input
|
|
44
|
+
if (faustNode.getNumInputs() > 0) {
|
|
45
|
+
const { connectToAudioInput } = await import("./create-node.js");
|
|
46
|
+
await connectToAudioInput(audioContext, null, faustNode, null);
|
|
47
|
+
}
|
|
48
|
+
|
|
43
49
|
// Create Faust node activation button
|
|
44
50
|
$buttonDsp.disabled = false;
|
|
45
|
-
|
|
51
|
+
|
|
46
52
|
// Set page title to the DSP name
|
|
47
53
|
document.title = name;
|
|
48
54
|
})();
|
|
@@ -44,8 +44,10 @@ $buttonDsp.disabled = true;
|
|
|
44
44
|
* @param {FaustAudioWorkletNode} faustNode
|
|
45
45
|
*/
|
|
46
46
|
const buildAudioDeviceMenu = async (faustNode) => {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
|
|
48
|
+
const { connectToAudioInput } = await import("./create-node.js");
|
|
49
|
+
|
|
50
|
+
let inputStreamNode = null;
|
|
49
51
|
const handleDeviceChange = async () => {
|
|
50
52
|
const devicesInfo = await navigator.mediaDevices.enumerateDevices();
|
|
51
53
|
$selectAudioInput.innerHTML = "";
|
|
@@ -61,31 +63,14 @@ const buildAudioDeviceMenu = async (faustNode) => {
|
|
|
61
63
|
navigator.mediaDevices.addEventListener("devicechange", handleDeviceChange)
|
|
62
64
|
$selectAudioInput.onchange = async () => {
|
|
63
65
|
const id = $selectAudioInput.value;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
echoCancellation: false,
|
|
67
|
-
noiseSuppression: false,
|
|
68
|
-
autoGainControl: false,
|
|
69
|
-
deviceId: id ? { exact: id } : undefined,
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
73
|
-
if (inputStreamNode) inputStreamNode.disconnect();
|
|
74
|
-
inputStreamNode = audioContext.createMediaStreamSource(stream);
|
|
75
|
-
inputStreamNode.connect(faustNode);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const defaultConstraints = {
|
|
79
|
-
audio: {
|
|
80
|
-
echoCancellation: false,
|
|
81
|
-
mozNoiseSuppression: false,
|
|
82
|
-
mozAutoGainControl: false
|
|
66
|
+
if (faustNode.getNumInputs() > 0) {
|
|
67
|
+
inputStreamNode = await connectToAudioInput(audioContext, id, faustNode, inputStreamNode);
|
|
83
68
|
}
|
|
84
69
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
inputStreamNode
|
|
70
|
+
|
|
71
|
+
// Connect to the default audio input device
|
|
72
|
+
if (faustNode.getNumInputs() > 0) {
|
|
73
|
+
inputStreamNode = await connectToAudioInput(audioContext, null, faustNode, inputStreamNode);
|
|
89
74
|
}
|
|
90
75
|
};
|
|
91
76
|
|
|
@@ -149,7 +134,7 @@ const createFaustUI = async (faustNode) => {
|
|
|
149
134
|
};
|
|
150
135
|
|
|
151
136
|
(async () => {
|
|
152
|
-
const {
|
|
137
|
+
const { createFaustNode } = await import("./create-node.js");
|
|
153
138
|
// To test the ScriptProcessorNode mode
|
|
154
139
|
// const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES, true);
|
|
155
140
|
const { faustNode, dspMeta: { name } } = await createFaustNode(audioContext, "FAUST_DSP_NAME", FAUST_DSP_VOICES);
|
package/build
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
node scripts/faust2wasm.js /Users/letz/Developpements/GameLAN/atomicro/atomicro.dsp /Users/letz/Developpements/sletz.github.io/atomicro -pwa
|
|
2
|
+
|
|
3
|
+
node scripts/faust2wasm.js /Users/letz/Developpements/GameLAN/attackey/attackey.dsp /Users/letz/Developpements/sletz.github.io/attackey -pwa
|
|
4
|
+
|
|
5
|
+
node scripts/faust2wasm.js /Users/letz/Developpements/GameLAN/baliphone/baliphone.dsp /Users/letz/Developpements/sletz.github.io/baliphone -pwa
|
|
6
|
+
|
|
7
|
+
node scripts/faust2wasm.js /Users/letz/Developpements/GameLAN/drone/drone.dsp /Users/letz/Developpements/sletz.github.io/drone -pwa
|
|
8
|
+
|
|
9
|
+
node scripts/faust2wasm.js /Users/letz/Developpements/GameLAN/sequenceur/sequenceur.dsp /Users/letz/Developpements/sletz.github.io/sequenceur -pwa
|
|
10
|
+
|
|
11
|
+
node scripts/faust2wasm.js /Users/letz/Developpements/GameLAN/shakerxy/shakerxy.dsp /Users/letz/Developpements/sletz.github.io/shakerxy -pwa
|
|
12
|
+
|
|
13
|
+
node scripts/faust2wasm.js /Users/letz/Developpements/GameLAN/sinusoide/sinusoide.dsp /Users/letz/Developpements/sletz.github.io/sinusoide -pwa
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
#node scripts/faust2wasm.js /Users/letz/Developpements/GameLAN/test.dsp /Users/letz/Developpements/sletz.github.io/out -standalone
|
|
17
|
+
#node scripts/faust2wasm.js test/organ.dsp /Users/letz/Developpements/sletz.github.io/organ -poly -standalone
|
|
18
|
+
|
|
19
|
+
#node scripts/faust2wasm.js test/organ1.dsp /Users/letz/Developpements/sletz.github.io/organ1 -poly -standalone
|
|
20
|
+
|
|
21
|
+
#node scripts/faust2wasm.js test/osc2.dsp /Users/letz/Developpements/sletz.github.io/osc2 -standalone
|
|
22
|
+
|
|
23
|
+
#node scripts/faust2wasm.js /Users/letz/Developpements/faust-sampler/sampler.dsp /Users/letz/Developpements/sletz.github.io/sampler -poly -standalone
|
|
24
|
+
|
|
25
|
+
#node scripts/faust2wasm.js /Users/letz/Developpements/faust-sampler/piano.dsp /Users/letz/Developpements/sletz.github.io/piano -poly -standalone
|
package/package.json
CHANGED
package/scripts/faust2wasm.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
//@ts-check
|
|
3
3
|
import * as process from "process";
|
|
4
4
|
import faust2wasmFiles from "../src/faust2wasmFiles.js";
|
|
5
|
-
import { copyWebStandaloneAssets, copyWebTemplateAssets } from "../src/copyWebStandaloneAssets.js";
|
|
5
|
+
import { copyWebStandaloneAssets, copyWebPWAAssets, copyWebTemplateAssets } from "../src/copyWebStandaloneAssets.js";
|
|
6
6
|
|
|
7
7
|
const argv = process.argv.slice(2);
|
|
8
8
|
|
|
9
9
|
if (argv[0] === "-help" || argv[0] === "-h") {
|
|
10
10
|
console.log(`
|
|
11
|
-
faust2wasm.js <file.dsp> <outputDir> [-poly] [-standalone] [-no-template]
|
|
11
|
+
faust2wasm.js <file.dsp> <outputDir> [-poly] [-standalone] [-pwa] [-no-template]
|
|
12
12
|
Generates WebAssembly and metadata JSON files of a given Faust DSP.
|
|
13
13
|
`);
|
|
14
14
|
process.exit();
|
|
@@ -22,6 +22,10 @@ const $standalone = argv.indexOf("-standalone");
|
|
|
22
22
|
const standalone = $standalone !== -1;
|
|
23
23
|
if (standalone) argv.splice($standalone, 1);
|
|
24
24
|
|
|
25
|
+
const $pwa = argv.indexOf("-pwa");
|
|
26
|
+
const pwa = $pwa !== -1;
|
|
27
|
+
if (pwa) argv.splice($pwa, 1);
|
|
28
|
+
|
|
25
29
|
const $noTemplate = argv.indexOf("-no-template");
|
|
26
30
|
const noTemplate = $noTemplate !== -1;
|
|
27
31
|
if (noTemplate) argv.splice($noTemplate, 1);
|
|
@@ -35,6 +39,8 @@ const dspName = fileName.replace(/\.dsp$/, '');
|
|
|
35
39
|
const { dspMeta, effectMeta } = await faust2wasmFiles(inputFile, outputDir, argvFaust, poly);
|
|
36
40
|
if (standalone) {
|
|
37
41
|
copyWebStandaloneAssets(outputDir, dspName, poly, !!effectMeta);
|
|
42
|
+
} else if (pwa) {
|
|
43
|
+
copyWebPWAAssets(outputDir, dspName, poly, !!effectMeta);
|
|
38
44
|
} else if (!noTemplate) {
|
|
39
45
|
copyWebTemplateAssets(outputDir, dspName, poly, !!effectMeta);
|
|
40
46
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
declare const copyWebStandaloneAssets: (outputDir: string, dspName: string, poly?: boolean, effect?: boolean) => void;
|
|
2
|
+
declare const copyWebPWAAssets: (outputDir: string, dspName: string, poly?: boolean, effect?: boolean) => void;
|
|
2
3
|
declare const copyWebTemplateAssets: (outputDir: string, dspName: string, poly?: boolean, effect?: boolean) => void;
|
|
3
4
|
|
|
4
|
-
export { copyWebStandaloneAssets, copyWebTemplateAssets };
|
|
5
|
+
export { copyWebStandaloneAssets, copyWebPWAAssets, copyWebTemplateAssets };
|
|
@@ -15,7 +15,6 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
15
15
|
*/
|
|
16
16
|
const copyWebStandaloneAssets = (outputDir, dspName, poly = false, effect = false) => {
|
|
17
17
|
console.log(`Writing assets files.`)
|
|
18
|
-
const assetsPath = path.join(__dirname, "../assets/standalone");
|
|
19
18
|
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir);
|
|
20
19
|
|
|
21
20
|
const findAndReplace = ["FAUST_DSP_NAME", dspName];
|
|
@@ -30,7 +29,6 @@ const copyWebStandaloneAssets = (outputDir, dspName, poly = false, effect = fals
|
|
|
30
29
|
cpSyncModify(templateJSPath, outputDir + `/index.js`, ...findAndReplace);
|
|
31
30
|
|
|
32
31
|
const templateHTMLPath = path.join(__dirname, "../assets/standalone/index.html");
|
|
33
|
-
//cpSyncModify(templateHTMLPath, outputDir + `/${dspName}.html`, "FAUST_DSP_NAME", dspName);
|
|
34
32
|
cpSyncModify(templateHTMLPath, outputDir + `/index.html`, ...findAndReplace);
|
|
35
33
|
|
|
36
34
|
const templateWorkerPath = path.join(__dirname, "../assets/standalone/service-worker.js");
|
|
@@ -49,6 +47,45 @@ const copyWebStandaloneAssets = (outputDir, dspName, poly = false, effect = fals
|
|
|
49
47
|
cpSyncModify(templateManifestPath, outputDir + `/manifest.json`, ...findAndReplace);
|
|
50
48
|
};
|
|
51
49
|
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} outputDir - The output directory.
|
|
52
|
+
* @param {string} dspName - The name of the DSP to be loaded.
|
|
53
|
+
* @param {boolean} [poly] - Whether the DSP is polyphonic.
|
|
54
|
+
* @param {boolean} [effect] - Whether the DSP has an effect module.
|
|
55
|
+
*/
|
|
56
|
+
const copyWebPWAAssets = (outputDir, dspName, poly = false, effect = false) => {
|
|
57
|
+
console.log(`Writing assets files.`)
|
|
58
|
+
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir);
|
|
59
|
+
|
|
60
|
+
const findAndReplace = ["FAUST_DSP_NAME", dspName];
|
|
61
|
+
if (poly) findAndReplace.push("FAUST_DSP_VOICES = 0", "FAUST_DSP_VOICES = 16");
|
|
62
|
+
if (poly && effect) findAndReplace.push("FAUST_DSP_HAS_EFFECT = false", "FAUST_DSP_HAS_EFFECT = true");
|
|
63
|
+
|
|
64
|
+
// Copy some files
|
|
65
|
+
const createNodeJSPath = path.join(__dirname, "../assets/standalone/create-node.js");
|
|
66
|
+
cpSyncModify(createNodeJSPath, outputDir + `/create-node.js`, ...findAndReplace);
|
|
67
|
+
|
|
68
|
+
const templateJSPath = path.join(__dirname, "../assets/standalone/index-pwa.js");
|
|
69
|
+
cpSyncModify(templateJSPath, outputDir + `/index.js`, ...findAndReplace);
|
|
70
|
+
|
|
71
|
+
const templateHTMLPath = path.join(__dirname, "../assets/standalone/index-pwa.html");
|
|
72
|
+
cpSyncModify(templateHTMLPath, outputDir + `/index.html`, ...findAndReplace);
|
|
73
|
+
|
|
74
|
+
const templateWorkerPath = path.join(__dirname, "../assets/standalone/service-worker.js");
|
|
75
|
+
cpSyncModify(templateWorkerPath, outputDir + `/service-worker.js`, ...findAndReplace);
|
|
76
|
+
|
|
77
|
+
const templateIconPath = path.join(__dirname, "../assets/standalone/icon.png");
|
|
78
|
+
cpSync(templateIconPath, outputDir + `/icon.png`);
|
|
79
|
+
|
|
80
|
+
const faustwasmPath = path.join(__dirname, "../assets/standalone/faustwasm");
|
|
81
|
+
cpSync(faustwasmPath, outputDir + "/faustwasm");
|
|
82
|
+
|
|
83
|
+
const faustuiPath = path.join(__dirname, "../assets/standalone/faust-ui");
|
|
84
|
+
cpSync(faustuiPath, outputDir + "/faust-ui");
|
|
85
|
+
|
|
86
|
+
const templateManifestPath = path.join(__dirname, "../assets/standalone/manifest.json");
|
|
87
|
+
cpSyncModify(templateManifestPath, outputDir + `/manifest.json`, ...findAndReplace);
|
|
88
|
+
};
|
|
52
89
|
/**
|
|
53
90
|
* @param {string} outputDir - The output directory.
|
|
54
91
|
* @param {string} dspName - The name of the DSP to be loaded.
|
|
@@ -78,5 +115,5 @@ const copyWebTemplateAssets = (outputDir, dspName, poly = false, effect = false)
|
|
|
78
115
|
cpSync(faustwasmPath, outputDir + "/faustwasm");
|
|
79
116
|
};
|
|
80
117
|
|
|
81
|
-
export { copyWebStandaloneAssets, copyWebTemplateAssets };
|
|
118
|
+
export { copyWebStandaloneAssets, copyWebPWAAssets, copyWebTemplateAssets };
|
|
82
119
|
|