@grame/faust-web-component 0.4.4 → 0.5.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.
- package/README.md +2 -2
- package/dist/faust-web-component.js +444 -172
- package/index.html +1 -0
- package/package.json +3 -3
- package/src/common.ts +102 -45
- package/src/faust-editor.ts +95 -96
- package/src/faust-widget.ts +45 -46
package/index.html
CHANGED
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@grame/faust-web-component",
|
3
3
|
"description": "Web component embedding the Faust Compiler",
|
4
|
-
"version": "0.
|
4
|
+
"version": "0.5.0",
|
5
5
|
"module": "dist/faust-web-component.js",
|
6
6
|
"files": [
|
7
7
|
"src/",
|
@@ -42,8 +42,8 @@
|
|
42
42
|
"@codemirror/legacy-modes": "^6.3.3",
|
43
43
|
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
44
44
|
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
45
|
-
"@grame/faustwasm": "^0.
|
46
|
-
"@shren/faust-ui": "^1.1.
|
45
|
+
"@grame/faustwasm": "^0.9.1",
|
46
|
+
"@shren/faust-ui": "^1.1.16",
|
47
47
|
"codemirror": "^6.0.1",
|
48
48
|
"split.js": "^1.6.5"
|
49
49
|
}
|
package/src/common.ts
CHANGED
@@ -1,58 +1,107 @@
|
|
1
|
-
|
2
|
-
import
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
// Import Faust Web Audio API
|
2
|
+
import {
|
3
|
+
IFaustMonoWebAudioNode,
|
4
|
+
IFaustPolyWebAudioNode,
|
5
|
+
FaustCompiler,
|
6
|
+
FaustMonoDspGenerator,
|
7
|
+
FaustPolyDspGenerator,
|
8
|
+
FaustSvgDiagrams,
|
9
|
+
LibFaust,
|
10
|
+
instantiateFaustModuleFromFile
|
11
|
+
} from "@grame/faustwasm";
|
12
|
+
|
13
|
+
// Import Faust module URLs
|
14
|
+
import jsURL from "@grame/faustwasm/libfaust-wasm/libfaust-wasm.js?url";
|
15
|
+
import dataURL from "@grame/faustwasm/libfaust-wasm/libfaust-wasm.data?url";
|
16
|
+
import wasmURL from "@grame/faustwasm/libfaust-wasm/libfaust-wasm.wasm?url";
|
17
|
+
|
18
|
+
// Import FontAwesome icons
|
19
|
+
import { library } from "@fortawesome/fontawesome-svg-core";
|
20
|
+
import {
|
21
|
+
faPlay,
|
22
|
+
faStop,
|
23
|
+
faUpRightFromSquare,
|
24
|
+
faSquareCaretLeft,
|
25
|
+
faAnglesLeft,
|
26
|
+
faAnglesRight,
|
27
|
+
faSliders,
|
28
|
+
faDiagramProject,
|
29
|
+
faWaveSquare,
|
30
|
+
faChartLine,
|
31
|
+
faPowerOff
|
32
|
+
} from "@fortawesome/free-solid-svg-icons";
|
33
|
+
|
34
|
+
// Add icons to FontAwesome library
|
35
|
+
for (const icon of [
|
36
|
+
faPlay,
|
37
|
+
faStop,
|
38
|
+
faUpRightFromSquare,
|
39
|
+
faSquareCaretLeft,
|
40
|
+
faAnglesLeft,
|
41
|
+
faAnglesRight,
|
42
|
+
faSliders,
|
43
|
+
faDiagramProject,
|
44
|
+
faWaveSquare,
|
45
|
+
faChartLine,
|
46
|
+
faPowerOff
|
47
|
+
]) {
|
48
|
+
library.add(icon);
|
10
49
|
}
|
11
50
|
|
12
|
-
|
13
|
-
export let
|
14
|
-
export
|
15
|
-
export const
|
16
|
-
export const
|
51
|
+
// Global variables for Faust
|
52
|
+
export let compiler: FaustCompiler;
|
53
|
+
export let svgDiagrams: FaustSvgDiagrams;
|
54
|
+
export const default_generator = new FaustMonoDspGenerator();
|
55
|
+
export const get_mono_generator = (): FaustMonoDspGenerator => new FaustMonoDspGenerator();
|
56
|
+
export const get_poly_generator = (): FaustPolyDspGenerator => new FaustPolyDspGenerator();
|
17
57
|
|
18
|
-
|
58
|
+
// Load Faust module
|
59
|
+
async function loadFaust(): Promise<void> {
|
19
60
|
// Setup Faust
|
20
|
-
const module = await instantiateFaustModuleFromFile(jsURL, dataURL, wasmURL)
|
21
|
-
const libFaust = new LibFaust(module)
|
22
|
-
compiler = new FaustCompiler(libFaust)
|
23
|
-
svgDiagrams = new FaustSvgDiagrams(compiler)
|
61
|
+
const module = await instantiateFaustModuleFromFile(jsURL, dataURL, wasmURL);
|
62
|
+
const libFaust = new LibFaust(module);
|
63
|
+
compiler = new FaustCompiler(libFaust);
|
64
|
+
svgDiagrams = new FaustSvgDiagrams(compiler);
|
24
65
|
}
|
25
66
|
|
26
|
-
|
27
|
-
export const
|
67
|
+
// Initialize Faust
|
68
|
+
export const faustPromise: Promise<void> = loadFaust();
|
69
|
+
|
70
|
+
// Create an AudioContext
|
71
|
+
export const audioCtx: AudioContext = new AudioContext({ latencyHint: 0.00001 });
|
28
72
|
audioCtx.destination.channelInterpretation = "discrete";
|
29
73
|
|
30
|
-
export const deviceUpdateCallbacks: (
|
31
|
-
let devices: MediaDeviceInfo[] = []
|
32
|
-
|
74
|
+
export const deviceUpdateCallbacks: Array<(devices: MediaDeviceInfo[]) => void> = [];
|
75
|
+
let devices: MediaDeviceInfo[] = [];
|
76
|
+
|
77
|
+
// Get input devices
|
78
|
+
async function _getInputDevices(): Promise<void> {
|
33
79
|
if (navigator.mediaDevices) {
|
34
|
-
navigator.mediaDevices.ondevicechange = _getInputDevices
|
80
|
+
navigator.mediaDevices.ondevicechange = _getInputDevices;
|
35
81
|
try {
|
36
|
-
await navigator.mediaDevices.getUserMedia({ audio: true })
|
37
|
-
} catch (e) {
|
38
|
-
|
82
|
+
await navigator.mediaDevices.getUserMedia({ audio: true });
|
83
|
+
} catch (e) {
|
84
|
+
// Ignore permission errors
|
85
|
+
}
|
86
|
+
devices = await navigator.mediaDevices.enumerateDevices();
|
39
87
|
for (const callback of deviceUpdateCallbacks) {
|
40
|
-
callback(devices)
|
88
|
+
callback(devices);
|
41
89
|
}
|
42
90
|
}
|
43
91
|
}
|
44
92
|
|
45
|
-
let getInputDevicesPromise: Promise<void> | undefined
|
46
|
-
export async function getInputDevices() {
|
47
|
-
if (getInputDevicesPromise
|
48
|
-
getInputDevicesPromise = _getInputDevices()
|
93
|
+
let getInputDevicesPromise: Promise<void> | undefined;
|
94
|
+
export async function getInputDevices(): Promise<MediaDeviceInfo[]> {
|
95
|
+
if (!getInputDevicesPromise) {
|
96
|
+
getInputDevicesPromise = _getInputDevices();
|
49
97
|
}
|
50
|
-
await getInputDevicesPromise
|
51
|
-
return devices
|
98
|
+
await getInputDevicesPromise;
|
99
|
+
return devices;
|
52
100
|
}
|
53
101
|
|
102
|
+
// Access MIDI device
|
54
103
|
export async function accessMIDIDevice(
|
55
|
-
onMIDIMessage: (data) => void
|
104
|
+
onMIDIMessage: (data: Uint8Array) => void
|
56
105
|
): Promise<void> {
|
57
106
|
return new Promise<void>((resolve, reject) => {
|
58
107
|
if (navigator.requestMIDIAccess) {
|
@@ -60,7 +109,6 @@ export async function accessMIDIDevice(
|
|
60
109
|
.requestMIDIAccess()
|
61
110
|
.then((midiAccess) => {
|
62
111
|
const inputDevices = midiAccess.inputs.values();
|
63
|
-
let midiInput: WebMidi.MIDIInput | null = null;
|
64
112
|
for (const midiInput of inputDevices) {
|
65
113
|
midiInput.onmidimessage = (event) => {
|
66
114
|
onMIDIMessage(event.data);
|
@@ -78,14 +126,23 @@ export async function accessMIDIDevice(
|
|
78
126
|
}
|
79
127
|
|
80
128
|
// Set up MIDI input callback
|
81
|
-
export const midiInputCallback = (node: IFaustMonoWebAudioNode | IFaustPolyWebAudioNode)
|
82
|
-
|
129
|
+
export const midiInputCallback = (node: IFaustMonoWebAudioNode | IFaustPolyWebAudioNode)
|
130
|
+
: ((data: Uint8Array) => void) => {
|
131
|
+
return (data: Uint8Array) => {
|
132
|
+
node.midiMessage(data);
|
133
|
+
};
|
134
|
+
};
|
135
|
+
|
136
|
+
// Define type for JSON data with metadata
|
137
|
+
interface JSONData {
|
138
|
+
meta: Array<{ options?: string; }>;
|
83
139
|
}
|
84
140
|
|
85
|
-
// Analyze the metadata of a Faust JSON file extract the [midi:on] and [nvoices:n] options
|
86
|
-
export function extractMidiAndNvoices(jsonData: JSONData)
|
87
|
-
|
88
|
-
|
141
|
+
// Analyze the metadata of a Faust JSON file and extract the [midi:on] and [nvoices:n] options
|
142
|
+
export function extractMidiAndNvoices(jsonData: JSONData)
|
143
|
+
: { midi: boolean; nvoices: number } {
|
144
|
+
const optionsMetadata = jsonData.meta.find((meta) => meta.options);
|
145
|
+
if (optionsMetadata && optionsMetadata.options) {
|
89
146
|
const options = optionsMetadata.options;
|
90
147
|
|
91
148
|
const midiRegex = /\[midi:(on|off)\]/;
|
@@ -95,10 +152,10 @@ export function extractMidiAndNvoices(jsonData: JSONData): { midi: boolean, nvoi
|
|
95
152
|
const nvoicesMatch = options.match(nvoicesRegex);
|
96
153
|
|
97
154
|
const midi = midiMatch ? midiMatch[1] === "on" : false;
|
98
|
-
const nvoices = nvoicesMatch ? parseInt(nvoicesMatch[1]) : -1;
|
155
|
+
const nvoices = nvoicesMatch ? parseInt(nvoicesMatch[1], 10) : -1;
|
99
156
|
|
100
157
|
return { midi, nvoices };
|
101
158
|
} else {
|
102
159
|
return { midi: false, nvoices: -1 };
|
103
160
|
}
|
104
|
-
}
|
161
|
+
}
|
package/src/faust-editor.ts
CHANGED
@@ -10,7 +10,6 @@ import {
|
|
10
10
|
compiler,
|
11
11
|
svgDiagrams,
|
12
12
|
default_generator,
|
13
|
-
get_mono_generator,
|
14
13
|
get_poly_generator,
|
15
14
|
getInputDevices,
|
16
15
|
deviceUpdateCallbacks,
|
@@ -230,34 +229,34 @@ template.innerHTML = `
|
|
230
229
|
// FaustEditor Web Component
|
231
230
|
export default class FaustEditor extends HTMLElement {
|
232
231
|
constructor() {
|
233
|
-
super()
|
232
|
+
super();
|
234
233
|
}
|
235
234
|
|
236
235
|
connectedCallback() {
|
237
236
|
// Initial setup when the component is attached to the DOM
|
238
|
-
const code = this.innerHTML.replace("<!--", "").replace("-->", "").trim()
|
239
|
-
this.attachShadow({ mode: "open" }).appendChild(template.content.cloneNode(true))
|
237
|
+
const code = this.innerHTML.replace("<!--", "").replace("-->", "").trim();
|
238
|
+
this.attachShadow({ mode: "open" }).appendChild(template.content.cloneNode(true));
|
240
239
|
|
241
240
|
// Set up links, buttons, and editor
|
242
|
-
const ideLink = this.shadowRoot!.querySelector("#ide") as HTMLAnchorElement
|
243
|
-
const editorEl = this.shadowRoot!.querySelector("#editor") as HTMLDivElement
|
244
|
-
const editor = createEditor(editorEl, code)
|
241
|
+
const ideLink = this.shadowRoot!.querySelector("#ide") as HTMLAnchorElement;
|
242
|
+
const editorEl = this.shadowRoot!.querySelector("#editor") as HTMLDivElement;
|
243
|
+
const editor = createEditor(editorEl, code);
|
245
244
|
|
246
245
|
ideLink.onfocus = () => {
|
247
246
|
// Open current contents of editor in IDE
|
248
|
-
const urlParams = new URLSearchParams()
|
249
|
-
urlParams.set("inline", btoa(editor.state.doc.toString()).replace("+", "-").replace("/", "_"))
|
250
|
-
ideLink.href = `https://faustide.grame.fr/?${urlParams.toString()}
|
247
|
+
const urlParams = new URLSearchParams();
|
248
|
+
urlParams.set("inline", btoa(editor.state.doc.toString()).replace("+", "-").replace("/", "_"));
|
249
|
+
ideLink.href = `https://faustide.grame.fr/?${urlParams.toString()}`;
|
251
250
|
}
|
252
251
|
|
253
|
-
const runButton = this.shadowRoot!.querySelector("#run") as HTMLButtonElement
|
254
|
-
const stopButton = this.shadowRoot!.querySelector("#stop") as HTMLButtonElement
|
255
|
-
const faustUIRoot = this.shadowRoot!.querySelector("#faust-ui") as HTMLDivElement
|
256
|
-
const faustDiagram = this.shadowRoot!.querySelector("#faust-diagram") as HTMLDivElement
|
257
|
-
const sidebar = this.shadowRoot!.querySelector("#sidebar") as HTMLDivElement
|
258
|
-
const sidebarContent = this.shadowRoot!.querySelector("#sidebar-content") as HTMLDivElement
|
259
|
-
const tabButtons = [...this.shadowRoot!.querySelectorAll(".tab")] as HTMLButtonElement[]
|
260
|
-
const tabContents = [...sidebarContent.querySelectorAll("div")] as HTMLDivElement[]
|
252
|
+
const runButton = this.shadowRoot!.querySelector("#run") as HTMLButtonElement;
|
253
|
+
const stopButton = this.shadowRoot!.querySelector("#stop") as HTMLButtonElement;
|
254
|
+
const faustUIRoot = this.shadowRoot!.querySelector("#faust-ui") as HTMLDivElement;
|
255
|
+
const faustDiagram = this.shadowRoot!.querySelector("#faust-diagram") as HTMLDivElement;
|
256
|
+
const sidebar = this.shadowRoot!.querySelector("#sidebar") as HTMLDivElement;
|
257
|
+
const sidebarContent = this.shadowRoot!.querySelector("#sidebar-content") as HTMLDivElement;
|
258
|
+
const tabButtons = [...this.shadowRoot!.querySelectorAll(".tab")] as HTMLButtonElement[];
|
259
|
+
const tabContents = [...sidebarContent.querySelectorAll("div")] as HTMLDivElement[];
|
261
260
|
|
262
261
|
// Initialize split.js for resizable editor and sidebar
|
263
262
|
const split = Split([editorEl, sidebar], {
|
@@ -268,44 +267,44 @@ export default class FaustEditor extends HTMLElement {
|
|
268
267
|
onDragEnd: () => { scope?.onResize(); spectrum?.onResize() },
|
269
268
|
})
|
270
269
|
|
271
|
-
faustPromise.then(() => runButton.disabled = false)
|
270
|
+
faustPromise.then(() => runButton.disabled = false);
|
272
271
|
|
273
272
|
// Default sizes for sidebar
|
274
|
-
const defaultSizes = [70, 30]
|
275
|
-
let sidebarOpen = false
|
273
|
+
const defaultSizes = [70, 30];
|
274
|
+
let sidebarOpen = false;
|
276
275
|
|
277
276
|
// Function to open the sidebar with predefined sizes
|
278
277
|
const openSidebar = () => {
|
279
278
|
if (!sidebarOpen) {
|
280
|
-
split.setSizes(defaultSizes)
|
279
|
+
split.setSizes(defaultSizes);
|
281
280
|
}
|
282
|
-
sidebarOpen = true
|
281
|
+
sidebarOpen = true;
|
283
282
|
}
|
284
283
|
|
285
284
|
// Variables for audio and visualization nodes
|
286
|
-
let node: IFaustMonoWebAudioNode | undefined
|
287
|
-
let input: MediaStreamAudioSourceNode | undefined
|
288
|
-
let analyser: AnalyserNode | undefined
|
289
|
-
let scope: Scope | undefined
|
290
|
-
let spectrum: Scope | undefined
|
291
|
-
let gmidi = false
|
292
|
-
let gnvoices = -1
|
293
|
-
let sourceNode: AudioBufferSourceNode
|
285
|
+
let node: IFaustMonoWebAudioNode | undefined;
|
286
|
+
let input: MediaStreamAudioSourceNode | undefined;
|
287
|
+
let analyser: AnalyserNode | undefined;
|
288
|
+
let scope: Scope | undefined;
|
289
|
+
let spectrum: Scope | undefined;
|
290
|
+
let gmidi = false;
|
291
|
+
let gnvoices = -1;
|
292
|
+
let sourceNode: AudioBufferSourceNode | undefined;
|
294
293
|
|
295
294
|
// Event handler for the run button
|
296
295
|
runButton.onclick = async () => {
|
297
296
|
if (audioCtx.state === "suspended") {
|
298
|
-
await audioCtx.resume()
|
297
|
+
await audioCtx.resume();
|
299
298
|
}
|
300
|
-
await faustPromise
|
299
|
+
await faustPromise;
|
301
300
|
|
302
301
|
// Compile Faust code
|
303
|
-
const code = editor.state.doc.toString()
|
304
|
-
let generator = null
|
302
|
+
const code = editor.state.doc.toString();
|
303
|
+
let generator = null;
|
305
304
|
try {
|
306
305
|
// Compile Faust code to access JSON metadata
|
307
|
-
await default_generator.compile(compiler, "main", code, "-ftz 2")
|
308
|
-
const json = default_generator.getMeta()
|
306
|
+
await default_generator.compile(compiler, "main", code, "-ftz 2");
|
307
|
+
const json = default_generator.getMeta();
|
309
308
|
let { midi, nvoices } = extractMidiAndNvoices(json);
|
310
309
|
gmidi = midi;
|
311
310
|
gnvoices = nvoices;
|
@@ -315,34 +314,34 @@ export default class FaustEditor extends HTMLElement {
|
|
315
314
|
await generator.compile(compiler, "main", code, "-ftz 2");
|
316
315
|
|
317
316
|
} catch (e: any) {
|
318
|
-
setError(editor, e)
|
317
|
+
setError(editor, e);
|
319
318
|
return
|
320
319
|
}
|
321
320
|
|
322
321
|
// Clear any old errors
|
323
|
-
clearError(editor)
|
322
|
+
clearError(editor);
|
324
323
|
|
325
324
|
// Create an audio node from compiled Faust
|
326
|
-
if (node !== undefined) node.disconnect()
|
325
|
+
if (node !== undefined) node.disconnect();
|
327
326
|
if (gnvoices > 0) {
|
328
|
-
node = (await (generator as FaustPolyDspGenerator).createNode(audioCtx, gnvoices))
|
327
|
+
node = (await (generator as FaustPolyDspGenerator).createNode(audioCtx, gnvoices))!;
|
329
328
|
} else {
|
330
|
-
node = (await (generator as FaustMonoDspGenerator).createNode(audioCtx))
|
329
|
+
node = (await (generator as FaustMonoDspGenerator).createNode(audioCtx))!;
|
331
330
|
}
|
332
331
|
|
333
332
|
// Set up audio input if necessary
|
334
333
|
if (node.numberOfInputs > 0) {
|
335
|
-
audioInputSelector.disabled = false
|
336
|
-
updateInputDevices(await getInputDevices())
|
337
|
-
await connectInput()
|
334
|
+
audioInputSelector.disabled = false;
|
335
|
+
updateInputDevices(await getInputDevices());
|
336
|
+
await connectInput();
|
338
337
|
} else {
|
339
|
-
audioInputSelector.disabled = true
|
340
|
-
audioInputSelector.innerHTML = "<option>Audio input</option>"
|
338
|
+
audioInputSelector.disabled = true;
|
339
|
+
audioInputSelector.innerHTML = "<option>Audio input</option>";
|
341
340
|
}
|
342
|
-
node.connect(audioCtx.destination)
|
343
|
-
stopButton.disabled = false
|
341
|
+
node.connect(audioCtx.destination);
|
342
|
+
stopButton.disabled = false;
|
344
343
|
for (const tabButton of tabButtons) {
|
345
|
-
tabButton.disabled = false
|
344
|
+
tabButton.disabled = false;
|
346
345
|
}
|
347
346
|
|
348
347
|
// Start sensors if available
|
@@ -359,50 +358,50 @@ export default class FaustEditor extends HTMLElement {
|
|
359
358
|
});
|
360
359
|
}
|
361
360
|
|
362
|
-
openSidebar()
|
361
|
+
openSidebar();
|
363
362
|
|
364
363
|
// Clear old tab contents
|
365
364
|
for (const tab of tabContents) {
|
366
|
-
while (tab.lastChild) tab.lastChild.remove()
|
365
|
+
while (tab.lastChild) tab.lastChild.remove();
|
367
366
|
}
|
368
367
|
// Create scope & spectrum plots
|
369
368
|
analyser = new AnalyserNode(audioCtx, {
|
370
369
|
fftSize: Math.pow(2, 11), minDecibels: -96, maxDecibels: 0, smoothingTimeConstant: 0.85
|
371
|
-
})
|
372
|
-
node.connect(analyser)
|
373
|
-
scope = new Scope(tabContents[2])
|
374
|
-
spectrum = new Scope(tabContents[3])
|
370
|
+
});
|
371
|
+
node.connect(analyser);
|
372
|
+
scope = new Scope(tabContents[2]);
|
373
|
+
spectrum = new Scope(tabContents[3]);
|
375
374
|
|
376
375
|
// If there are UI elements, open Faust UI (controls tab); otherwise open spectrum analyzer.
|
377
|
-
const ui = node.getUI()
|
378
|
-
openTab(ui.length > 1 || ui[0].items.length > 0 ? 0 : 3)
|
376
|
+
const ui = node.getUI();
|
377
|
+
openTab(ui.length > 1 || ui[0].items.length > 0 ? 0 : 3);
|
379
378
|
|
380
379
|
// Create controls via Faust UI
|
381
|
-
const faustUI = new FaustUI({ ui, root: faustUIRoot })
|
382
|
-
faustUI.paramChangeByUI = (path, value) => node?.setParamValue(path, value)
|
383
|
-
node.setOutputParamHandler((path, value) => faustUI.paramChangeByDSP(path, value))
|
380
|
+
const faustUI = new FaustUI({ ui, root: faustUIRoot });
|
381
|
+
faustUI.paramChangeByUI = (path, value) => node?.setParamValue(path, value);
|
382
|
+
node.setOutputParamHandler((path, value) => faustUI.paramChangeByDSP(path, value));
|
384
383
|
|
385
384
|
// Set editor size to fit UI size
|
386
385
|
editorEl.style.height = `${Math.max(125, faustUI.minHeight)}px`;
|
387
|
-
faustUIRoot.style.width = faustUI.minWidth * 1.25 + "px"
|
388
|
-
faustUIRoot.style.height = faustUI.minHeight * 1.25 + "px"
|
386
|
+
faustUIRoot.style.width = faustUI.minWidth * 1.25 + "px";
|
387
|
+
faustUIRoot.style.height = faustUI.minHeight * 1.25 + "px";
|
389
388
|
}
|
390
389
|
|
391
390
|
// Function to set SVG in the block diagram tab
|
392
391
|
const setSVG = (svgString: string) => {
|
393
|
-
faustDiagram.innerHTML = svgString
|
392
|
+
faustDiagram.innerHTML = svgString;
|
394
393
|
|
395
394
|
for (const a of faustDiagram.querySelectorAll("a")) {
|
396
395
|
a.onclick = e => {
|
397
|
-
e.preventDefault()
|
398
|
-
const filename = (a.href as any as SVGAnimatedString).baseVal
|
399
|
-
const svgString = compiler.fs().readFile("main-svg/" + filename, { encoding: "utf8" }) as string
|
400
|
-
setSVG(svgString)
|
396
|
+
e.preventDefault();
|
397
|
+
const filename = (a.href as any as SVGAnimatedString).baseVal;
|
398
|
+
const svgString = compiler.fs().readFile("main-svg/" + filename, { encoding: "utf8" }) as string;
|
399
|
+
setSVG(svgString);
|
401
400
|
}
|
402
401
|
}
|
403
402
|
}
|
404
403
|
|
405
|
-
let animPlot: number | undefined
|
404
|
+
let animPlot: number | undefined;
|
406
405
|
|
407
406
|
// Function to render the scope
|
408
407
|
const drawScope = () => {
|
@@ -411,24 +410,24 @@ export default class FaustEditor extends HTMLElement {
|
|
411
410
|
style: "rgb(212, 100, 100)",
|
412
411
|
edgeThreshold: 0.09,
|
413
412
|
}])
|
414
|
-
animPlot = requestAnimationFrame(drawScope)
|
413
|
+
animPlot = requestAnimationFrame(drawScope);
|
415
414
|
}
|
416
415
|
|
417
416
|
// Function to render the spectrum
|
418
417
|
const drawSpectrum = () => {
|
419
|
-
spectrum!.renderSpectrum(analyser!)
|
420
|
-
animPlot = requestAnimationFrame(drawSpectrum)
|
418
|
+
spectrum!.renderSpectrum(analyser!);
|
419
|
+
animPlot = requestAnimationFrame(drawSpectrum);
|
421
420
|
}
|
422
421
|
|
423
422
|
// Function to switch between tabs
|
424
423
|
const openTab = (i: number) => {
|
425
424
|
for (const [j, tab] of tabButtons.entries()) {
|
426
425
|
if (i === j) {
|
427
|
-
tab.classList.add("active")
|
428
|
-
tabContents[j].classList.add("active")
|
426
|
+
tab.classList.add("active");
|
427
|
+
tabContents[j].classList.add("active");
|
429
428
|
} else {
|
430
|
-
tab.classList.remove("active")
|
431
|
-
tabContents[j].classList.remove("active")
|
429
|
+
tab.classList.remove("active");
|
430
|
+
tabContents[j].classList.remove("active");
|
432
431
|
}
|
433
432
|
}
|
434
433
|
// Check if the clicked tab is the "Block Diagram" tab (index 1)
|
@@ -445,22 +444,22 @@ export default class FaustEditor extends HTMLElement {
|
|
445
444
|
}, 0);
|
446
445
|
}
|
447
446
|
} else if (i === 2) {
|
448
|
-
scope!.onResize()
|
449
|
-
if (animPlot !== undefined) cancelAnimationFrame(animPlot)
|
450
|
-
animPlot = requestAnimationFrame(drawScope)
|
447
|
+
scope!.onResize();
|
448
|
+
if (animPlot !== undefined) cancelAnimationFrame(animPlot);
|
449
|
+
animPlot = requestAnimationFrame(drawScope);
|
451
450
|
} else if (i === 3) {
|
452
|
-
spectrum!.onResize()
|
453
|
-
if (animPlot !== undefined) cancelAnimationFrame(animPlot)
|
454
|
-
animPlot = requestAnimationFrame(drawSpectrum)
|
451
|
+
spectrum!.onResize();
|
452
|
+
if (animPlot !== undefined) cancelAnimationFrame(animPlot);
|
453
|
+
animPlot = requestAnimationFrame(drawSpectrum);
|
455
454
|
} else if (animPlot !== undefined) {
|
456
|
-
cancelAnimationFrame(animPlot)
|
457
|
-
animPlot = undefined
|
455
|
+
cancelAnimationFrame(animPlot);
|
456
|
+
animPlot = undefined;
|
458
457
|
}
|
459
458
|
}
|
460
459
|
|
461
460
|
// Attach event listeners to tab buttons
|
462
461
|
for (const [i, tabButton] of tabButtons.entries()) {
|
463
|
-
tabButton.onclick = () => openTab(i)
|
462
|
+
tabButton.onclick = () => openTab(i);
|
464
463
|
}
|
465
464
|
|
466
465
|
// Event handler for the stop button
|
@@ -476,28 +475,28 @@ export default class FaustEditor extends HTMLElement {
|
|
476
475
|
}
|
477
476
|
|
478
477
|
// Audio input selector element
|
479
|
-
const audioInputSelector = this.shadowRoot!.querySelector("#audio-input") as HTMLSelectElement
|
478
|
+
const audioInputSelector = this.shadowRoot!.querySelector("#audio-input") as HTMLSelectElement;
|
480
479
|
|
481
480
|
// Update the audio input device list
|
482
481
|
const updateInputDevices = (devices: MediaDeviceInfo[]) => {
|
483
|
-
if (audioInputSelector.disabled) return
|
484
|
-
while (audioInputSelector.lastChild) audioInputSelector.lastChild.remove()
|
482
|
+
if (audioInputSelector.disabled) return;
|
483
|
+
while (audioInputSelector.lastChild) audioInputSelector.lastChild.remove();
|
485
484
|
for (const device of devices) {
|
486
485
|
if (device.kind === "audioinput") {
|
487
|
-
audioInputSelector.appendChild(new Option(device.label || device.deviceId, device.deviceId))
|
486
|
+
audioInputSelector.appendChild(new Option(device.label || device.deviceId, device.deviceId));
|
488
487
|
}
|
489
488
|
}
|
490
|
-
audioInputSelector.appendChild(new Option("Audio File", "Audio File"))
|
489
|
+
audioInputSelector.appendChild(new Option("Audio File", "Audio File"));
|
491
490
|
}
|
492
|
-
deviceUpdateCallbacks.push(updateInputDevices)
|
491
|
+
deviceUpdateCallbacks.push(updateInputDevices);
|
493
492
|
|
494
493
|
// Connect the selected audio input device
|
495
494
|
const connectInput = async () => {
|
496
|
-
const deviceId = audioInputSelector.value
|
497
|
-
const stream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId, echoCancellation: false, noiseSuppression: false, autoGainControl: false } })
|
495
|
+
const deviceId = audioInputSelector.value;
|
496
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId, echoCancellation: false, noiseSuppression: false, autoGainControl: false } });
|
498
497
|
if (input) {
|
499
|
-
input.disconnect()
|
500
|
-
input = undefined
|
498
|
+
input.disconnect();
|
499
|
+
input = undefined;
|
501
500
|
}
|
502
501
|
if (node && node.numberOfInputs > 0) {
|
503
502
|
if (deviceId == "Audio File") {
|
@@ -531,6 +530,6 @@ export default class FaustEditor extends HTMLElement {
|
|
531
530
|
}
|
532
531
|
}
|
533
532
|
|
534
|
-
audioInputSelector.onchange = connectInput
|
533
|
+
audioInputSelector.onchange = connectInput;
|
535
534
|
}
|
536
535
|
}
|