@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/index.html CHANGED
@@ -14,6 +14,7 @@ declare license "MIT";
14
14
  declare copyright "(c)Mike Olsen, CCRMA (Stanford University)";
15
15
 
16
16
  import("stdfaust.lib");
17
+ declare options "[midi:on][nvoices:12]";
17
18
 
18
19
  process = pm.SFFormantModelFofSmooth_ui_MIDI <: _,_;
19
20
  -->
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.4",
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.7.8",
46
- "@shren/faust-ui": "^1.1.15",
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
- import { IFaustMonoWebAudioNode, IFaustPolyWebAudioNode, FaustCompiler, FaustMonoDspGenerator, FaustPolyDspGenerator, FaustSvgDiagrams, LibFaust, instantiateFaustModuleFromFile } from "@grame/faustwasm"
2
- import jsURL from "@grame/faustwasm/libfaust-wasm/libfaust-wasm.js?url"
3
- import dataURL from "@grame/faustwasm/libfaust-wasm/libfaust-wasm.data?url"
4
- import wasmURL from "@grame/faustwasm/libfaust-wasm/libfaust-wasm.wasm?url"
5
- import { library } from "@fortawesome/fontawesome-svg-core"
6
- import { faPlay, faStop, faUpRightFromSquare, faSquareCaretLeft, faAnglesLeft, faAnglesRight, faSliders, faDiagramProject, faWaveSquare, faChartLine, faPowerOff } from "@fortawesome/free-solid-svg-icons"
7
-
8
- for (const icon of [faPlay, faStop, faUpRightFromSquare, faSquareCaretLeft, faAnglesLeft, faAnglesRight, faSliders, faDiagramProject, faWaveSquare, faChartLine, faPowerOff]) {
9
- library.add(icon)
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
- export let compiler: FaustCompiler
13
- export let svgDiagrams: FaustSvgDiagrams
14
- export const default_generator = new FaustMonoDspGenerator()
15
- export const get_mono_generator = () => new FaustMonoDspGenerator()
16
- export const get_poly_generator = () => new FaustPolyDspGenerator()
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
- async function loadFaust() {
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
- export const faustPromise = loadFaust()
27
- export const audioCtx = new AudioContext({ latencyHint: 0.00001 })
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: ((d: MediaDeviceInfo[]) => void)[] = []
31
- let devices: MediaDeviceInfo[] = []
32
- async function _getInputDevices() {
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
- devices = await navigator.mediaDevices.enumerateDevices()
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 === undefined) {
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
- return (data) => { node.midiMessage(data); }
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): { midi: boolean, nvoices: number } {
87
- const optionsMetadata = jsonData.meta.find(meta => meta.options);
88
- if (optionsMetadata) {
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
+ }
@@ -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 = undefined;
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
  }