Skip to content

Commit

Permalink
Initial support for headless mode
Browse files Browse the repository at this point in the history
 * Add a new webpack config which compiles the project as a library, exporting an init function which can be called by importers to set up the app and optionally load a composition when it's started
 * Update some things in the app to support working in headless mode
   * Change some fetches of static assets to use `process.env.ASSET_PATH` rather than `/` as a base.  This is needed if web synth is being imported from a different domain.
 * Add new headless entrypoint
 * Updates to build scripts for headless mode
 * I think I bumped some deps
  • Loading branch information
Ameobea committed Aug 29, 2022
1 parent b446b23 commit fcb1586
Show file tree
Hide file tree
Showing 37 changed files with 460 additions and 283 deletions.
17 changes: 13 additions & 4 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ opt-public-profiling:
for file in `ls ./public | grep "\\.wasm"`; do echo $file && wasm-opt ./public/$file -O4 --enable-simd --precompute-propagate --fast-math --detect-features -g -c -o ./public/$file; done

build-docs:
cd docs/_layouts && yarn build
cd docs/_layouts && NODE_OPTIONS=--openssl-legacy-provider yarn build
rm -rf ./dist/docs
cp -r ./docs/_layouts/public ./dist/docs

Expand Down Expand Up @@ -49,6 +49,8 @@ build-all:
&& wasm-bindgen ./target/wasm32-unknown-unknown/release/waveform_renderer.wasm --browser --remove-producers-section --out-dir ./build \
&& wasm-bindgen ./target/wasm32-unknown-unknown/release/note_container.wasm --browser --remove-producers-section --out-dir ./build \
&& wasm-bindgen ./target/wasm32-unknown-unknown/release/wav_decoder.wasm --browser --remove-producers-section --out-dir ./build

cd -
cp ./engine/target/wasm32-unknown-unknown/release/*.wasm ./public
cp ./engine/target/wasm32-unknown-unknown/release/wavetable_no_simd.wasm ./public
cp ./engine/target/wasm32-unknown-unknown/release/granular.wasm ./public
Expand All @@ -68,12 +70,17 @@ build-all:

just build-sinsy

yarn build || npm build
NODE_OPTIONS=--openssl-legacy-provider yarn build || NODE_OPTIONS=--openssl-legacy-provider npm build

just build-headless

just opt

just build-docs

build-headless:
NODE_OPTIONS=--openssl-legacy-provider yarn build-headless || NODE_OPTIONS=--openssl-legacy-provider npm build-headless

run:
#!/bin/bash

Expand All @@ -95,6 +102,8 @@ run:
&& wasm-bindgen /tmp/wasm/waveform_renderer.wasm --browser --remove-producers-section --out-dir ./build \
&& wasm-bindgen /tmp/wasm/note_container.wasm --browser --remove-producers-section --out-dir ./build \
&& wasm-bindgen /tmp/wasm/wav_decoder.wasm --browser --remove-producers-section --out-dir ./build

cd -
cp ./engine/build/* ./src/
cp ./engine/target/wasm32-unknown-unknown/release/wavetable.wasm ./public
cp ./engine/target/wasm32-unknown-unknown/release/wavetable_no_simd.wasm ./public
Expand All @@ -114,10 +123,10 @@ run:

just debug-sinsy

yarn start
NODE_OPTIONS=--openssl-legacy-provider yarn start

run-frontend:
yarn start
NODE_OPTIONS=--openssl-legacy-provider yarn start

deploy:
# cd backend && just docker-build
Expand Down
2 changes: 1 addition & 1 deletion engine/engine/src/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use wasm_bindgen::prelude::*;

#[wasm_bindgen(raw_module = "./index")]
#[wasm_bindgen(raw_module = "./vcInterop")]
extern "C" {
pub fn init_view_contexts(
active_context_ix: usize,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"scripts": {
"start": "webpack serve",
"build": "rm -rf dist/* && NODE_OPTIONS=--experimental-worker BACKEND_BASE_URL=\"https://web-synth-api.ameo.design\" webpack --config webpack.prod.js && cp -r ./public/* ./dist",
"build-headless": "rm -rf dist/headless/* && NODE_OPTIONS=--experimental-worker BACKEND_BASE_URL=\"https://web-synth-api.ameo.design\" webpack --config webpack.headless.js && cp -r ./public/* ./dist/headless",
"lint": "eslint \"./src/**/*.@(ts|tsx|js|jsx)\"",
"cypress:open": "cypress open",
"cypress:run": "cypress run --browser chrome --record --key 58905cf6-d1cb-43de-b6cd-b55954039c1a",
Expand Down
6 changes: 4 additions & 2 deletions src/eventScheduler/eventScheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,13 @@ export const getCurGlobalBPM = () => {
// Init the scheduler AWP instance
Promise.all([
fetch(
'/event_scheduler.wasm?cacheBust=' +
process.env.ASSET_PATH +
'event_scheduler.wasm?cacheBust=' +
(window.location.host.includes('localhost') ? '' : genRandomStringID())
).then(res => res.arrayBuffer()),
ctx.audioWorklet.addModule(
'/EventSchedulerWorkletProcessor.js?cacheBust=' +
process.env.ASSET_PATH +
'EventSchedulerWorkletProcessor.js?cacheBust=' +
(window.location.host.includes('localhost') ? '' : genRandomStringID())
),
] as const).then(([wasmArrayBuffer]) => {
Expand Down
4 changes: 2 additions & 2 deletions src/faustEditor/DymanicCodeWorkletNode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ValueOf } from 'ameo-utils';
import type { ValueOf } from 'ameo-utils';

import { faustEditorContextMap } from 'src/faustEditor';
import type { faustEditorContextMap } from 'src/faustEditor';

export declare class DynamicCodeWorkletNode extends AudioWorkletNode {
constructor(ctx: AudioContext, moduleId: string, workletNameOverride?: string);
Expand Down
8 changes: 4 additions & 4 deletions src/faustEditor/FaustEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import React, { useState, Suspense, useMemo, useRef, useEffect, useCallback } fr
import { shallowEqual, useSelector } from 'react-redux';
import ControlPanel from 'react-control-panel';
import * as R from 'ramda';
import { Without, ValueOf, filterNils } from 'ameo-utils';
import { type Without, type ValueOf, filterNils } from 'ameo-utils';

import { Effect } from 'src/redux/modules/effects';
import type { Effect } from 'src/redux/modules/effects';
import { FAUST_COMPILER_ENDPOINT } from 'src/conf';
import { SpectrumVisualization } from 'src/visualizations/spectrum';
import { FaustWorkletNode, buildFaustWorkletNode } from 'src/faustEditor/FaustAudioWorklet';
import {
faustEditorContextMap,
FaustEditorReduxInfra,
type FaustEditorReduxInfra,
get_faust_editor_connectables,
} from 'src/faustEditor';
import { updateConnectables } from 'src/patchNetwork/interface';
import { fetchEffects, saveEffect } from 'src/api';
import { DynamicCodeWorkletNode } from 'src/faustEditor/DymanicCodeWorkletNode';
import type { DynamicCodeWorkletNode } from 'src/faustEditor/DymanicCodeWorkletNode';
import { buildSoulWorkletNode } from 'src/faustEditor/SoulAudioWorklet';
import { renderGenericPresetSaverWithModal } from 'src/controls/GenericPresetPicker/GenericPresetSaver';
import { pickPresetWithModal } from 'src/controls/GenericPresetPicker/GenericPresetPicker';
Expand Down
4 changes: 2 additions & 2 deletions src/faustEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import FaustEditor from './FaustEditor';
import type { AudioConnectables, ConnectableOutput, ConnectableInput } from 'src/patchNetwork';
import { createPassthroughNode, OverridableAudioParam } from 'src/graphEditor/nodes/util';
import {
SerializedFaustEditor,
type SerializedFaustEditor,
buildDefaultFaustEditorPolyphonyState,
buildFaustEditorReduxInfra,
} from 'src/redux/modules/faustEditor';
import { mkContainerRenderHelper, mkContainerCleanupHelper } from 'src/reactUtils';
import { mkFaustEditorSmallView } from 'src/faustEditor/FaustEditorSmallView';
import DummyNode from 'src/graphEditor/nodes/DummyNode';
import { DynamicCodeWorkletNode } from 'src/faustEditor/DymanicCodeWorkletNode';
import type { DynamicCodeWorkletNode } from 'src/faustEditor/DymanicCodeWorkletNode';

const ctx = new AudioContext();

Expand Down
3 changes: 3 additions & 0 deletions src/fmSynth/FMSynthUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ const ConfigureMainEffectChain: React.FC<ConfigureMainEffectChainProps> = ({

const initializeWavyJones = (getFMSynthOutput: () => Promise<AudioNode>) => {
const inst = buildWavyJonesInstance(ctx, 'fm-synth-oscilloscope', 490, 240);
if (!inst) {
return new AnalyserNode(ctx) as WavyJones;
}
getFMSynthOutput().then(fmSynthOutput => fmSynthOutput.connect(inst));
inst.connect(muted);
return inst;
Expand Down
7 changes: 5 additions & 2 deletions src/granulator/granulator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ const ctx = new AudioContext();

const GranulatorRegistered = new AsyncOnce(() =>
ctx.audioWorklet.addModule(
'/GranulatorWorkletProcessor.js?cacheBust=' + btoa(Math.random().toString())
process.env.ASSET_PATH +
'GranulatorWorkletProcessor.js?cacheBust=' +
btoa(Math.random().toString())
)
);

Expand Down Expand Up @@ -165,7 +167,8 @@ export const build_granulator_audio_connectables = (vcId: string): AudioConnecta

const GranularWasm = new AsyncOnce(() =>
fetch(
'/granular.wasm?cacheBust=' +
process.env.ASSET_PATH +
'granular.wasm?cacheBust=' +
(window.location.host.includes('localhost') ? '' : genRandomStringID())
).then(res => res.arrayBuffer())
);
Expand Down
6 changes: 4 additions & 2 deletions src/graphEditor/nodes/CustomAudio/Delay/Delay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { AsyncOnce } from 'src/util';

export const DelayWasmBytes = new AsyncOnce(() =>
fetch(
'/delay.wasm?cacheBust=' +
process.env.ASSET_PATH +
'delay.wasm?cacheBust=' +
(window.location.host.includes('localhost') ? '' : btoa(Math.random().toString()))
).then(res => res.arrayBuffer())
);
Expand Down Expand Up @@ -119,7 +120,8 @@ export default class DelayNode implements ForeignNode {
const [wasmBytes] = await Promise.all([
DelayWasmBytes.get(),
this.ctx.audioWorklet.addModule(
'/DelayAWP.js?cacheBust=' +
process.env.ASSET_PATH +
'DelayAWP.js?cacheBust=' +
(window.location.host.includes('localhost') ? '' : btoa(Math.random().toString()))
),
] as const);
Expand Down
5 changes: 3 additions & 2 deletions src/graphEditor/nodes/CustomAudio/Distortion/Distortion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { AsyncOnce, genRandomStringID } from 'src/util';

export const DistortionWasmBytes = new AsyncOnce(() =>
fetch(
'/distortion.wasm?cacheBust=' +
process.env.ASSET_PATH +
'distortion.wasm?cacheBust=' +
(window.location.host.includes('localhost') ? '' : genRandomStringID())
).then(res => res.arrayBuffer())
);
Expand Down Expand Up @@ -61,7 +62,7 @@ export default class DistortionNode implements ForeignNode {
const [wasmBytes] = await Promise.all([
DistortionWasmBytes.get(),
this.ctx.audioWorklet.addModule(
'/DistortionAWP.js?cacheBust=' + btoa(Math.random().toString())
process.env.ASSET_PATH + 'DistortionAWP.js?cacheBust=' + btoa(Math.random().toString())
),
] as const);
this.awpHandle = new AudioWorkletNode(this.ctx, 'distortion-awp');
Expand Down
4 changes: 3 additions & 1 deletion src/graphEditor/nodes/CustomAudio/Equalizer/Equalizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ const DEFAULT_POINTS: EqualizerPoint[] = [
const ctx = new AudioContext();
const EqualizerRegistered = new AsyncOnce(() =>
ctx.audioWorklet.addModule(
'/EqualizerWorkletProcessor.js?cacheBust=' + btoa(Math.random().toString())
process.env.ASSET_PATH +
'EqualizerWorkletProcessor.js?cacheBust=' +
btoa(Math.random().toString())
)
);

Expand Down
11 changes: 6 additions & 5 deletions src/graphEditor/nodes/CustomAudio/FMSynth/FMSynth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import {
} from 'src/graphEditor/nodes/CustomAudio/FMSynth/sampleMapping';
import type { GateUngateCallbackRegistrar } from 'src/fmSynth/midiSampleUI/types';
import { getSample, hashSampleDescriptor, type SampleDescriptor } from 'src/sampleLibrary';
import type SampleManager from 'src/sampleLibrary/SampleManager';

const OPERATOR_COUNT = 8;
const VOICE_COUNT = 10;
Expand All @@ -45,9 +44,10 @@ const ctx = new AudioContext();

const RegisterFMSynthAWP = new AsyncOnce(() =>
ctx.audioWorklet.addModule(
window.location.href.includes('localhost')
? '/FMSynthAWP.js'
: '/FMSynthAWP.js?randId=' + btoa(Math.random().toString())
process.env.ASSET_PATH +
(window.location.href.includes('localhost')
? 'FMSynthAWP.js'
: 'FMSynthAWP.js?randId=' + btoa(Math.random().toString()))
)
);

Expand Down Expand Up @@ -78,7 +78,8 @@ const WavetableWasmBytes = new AsyncOnce(async (): Promise<ArrayBuffer> => {
simdStatusElem.setAttribute('style', 'display:block; color: #cfeb1e;');
}
}
let path = hasSIMDSupport ? '/wavetable.wasm' : '/wavetable_no_simd.wasm';
let path =
process.env.ASSET_PATH + (hasSIMDSupport ? 'wavetable.wasm' : 'wavetable_no_simd.wasm');
if (!window.location.host.includes('localhost')) {
path += `?cacheBust=${genRandomStringID()}`;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import {
} from 'src/eventScheduler';

const MIDIQuantizerWasmBytes = new AsyncOnce(() =>
fetch('/midi_quantizer.wasm').then(res => res.arrayBuffer())
fetch(process.env.ASSET_PATH + 'midi_quantizer.wasm').then(res => res.arrayBuffer())
);

const ctx = new AudioContext();
const MIDIQuantizerAWPRegistered = new AsyncOnce(() =>
ctx.audioWorklet.addModule(
'/MIDIQuantizerAWP.js?cacheBust=' +
process.env.ASSET_PATH +
'MIDIQuantizerAWP.js?cacheBust=' +
(window.location.href.includes('localhost') ? '' : btoa(Math.random().toString()))
)
);
Expand Down
6 changes: 4 additions & 2 deletions src/graphEditor/nodes/CustomAudio/NoiseGen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ import DummyNode from 'src/graphEditor/nodes/DummyNode';
const NoiseGenAWPRegistered = new AsyncOnce(() =>
new AudioContext().audioWorklet
.addModule(
'/NoiseGenAWP.js?cacheBust=' +
process.env.ASSET_PATH +
'NoiseGenAWP.js?cacheBust=' +
(window.location.host.includes('localhost') ? '' : btoa(Math.random().toString()))
)
.catch(console.error)
);
const NoiseGenWasm = new AsyncOnce(() =>
fetch(
'/noise_gen.wasm?cacheBust=' +
process.env.ASSET_PATH +
'noise_gen.wasm?cacheBust=' +
(window.location.host.includes('localhost') ? '' : genRandomStringID())
).then(res => res.arrayBuffer())
);
Expand Down
6 changes: 4 additions & 2 deletions src/graphEditor/nodes/CustomAudio/Quantizer/QuantizerNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ import QuantizerNodeUI from './QuantizerNodeUI.svelte';

const QuantizerWasmBytes = new AsyncOnce(() =>
fetch(
'/quantizer.wasm?cacheBust=' +
process.env.ASSET_PATH +
'quantizer.wasm?cacheBust=' +
(window.location.href.includes('localhost') ? '' : genRandomStringID())
).then(res => res.arrayBuffer())
);

const ctx = new AudioContext();
const QuantizerAWPRegistered = new AsyncOnce(() =>
ctx.audioWorklet.addModule(
'/QuantizerAWP.js?cacheBust=' +
process.env.ASSET_PATH +
'QuantizerAWP.js?cacheBust=' +
(window.location.href.includes('localhost') ? '' : genRandomStringID())
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import { AsyncOnce } from 'src/util';

export const SamplePlayerWasmBytes = new AsyncOnce(() =>
fetch(
'/sample_player.wasm?cacheBust=' +
process.env.ASSET_PATH +
'sample_player.wasm?cacheBust=' +
(window.location.host.includes('localhost') ? '' : btoa(Math.random().toString()))
).then(res => res.arrayBuffer())
);
Expand Down Expand Up @@ -71,7 +72,8 @@ export default class SamplePlayerNode implements ForeignNode {
const [wasmBytes] = await Promise.all([
SamplePlayerWasmBytes.get(),
this.ctx.audioWorklet.addModule(
'/SamplePlayerAWP.js?cacheBust=' +
process.env.ASSET_PATH +
'SamplePlayerAWP.js?cacheBust=' +
(window.location.host.includes('localhost') ? '' : btoa(Math.random().toString()))
),
] as const);
Expand Down
12 changes: 8 additions & 4 deletions src/graphEditor/nodes/CustomAudio/Sidechain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@ import React from 'react';
import { Map as ImmMap } from 'immutable';
import ControlPanel from 'react-control-panel';

import { OverridableAudioParam } from 'src/graphEditor/nodes/util';
import type { OverridableAudioParam } from 'src/graphEditor/nodes/util';
import type { AudioConnectables, ConnectableInput, ConnectableOutput } from 'src/patchNetwork';
import { updateConnectables } from 'src/patchNetwork/interface';
import { mkContainerCleanupHelper, mkContainerRenderHelper } from 'src/reactUtils';
import { AsyncOnce } from 'src/util';
import { ForeignNode } from 'src/graphEditor/nodes/CustomAudio';
import type { ForeignNode } from 'src/graphEditor/nodes/CustomAudio';
import DummyNode from 'src/graphEditor/nodes/DummyNode';

const SidechainAWPRegistered = new AsyncOnce(() =>
new AudioContext().audioWorklet.addModule(
'/SidechainWorkletProcessor.js?cacheBust=' + btoa(Math.random().toString())
process.env.ASSET_PATH +
'SidechainWorkletProcessor.js?cacheBust=' +
btoa(Math.random().toString())
)
);
const SidechainWasm = new AsyncOnce(() => fetch('/sidechain.wasm').then(res => res.arrayBuffer()));
const SidechainWasm = new AsyncOnce(() =>
fetch(process.env.ASSET_PATH + 'sidechain.wasm').then(res => res.arrayBuffer())
);

const SidechainSmallView: React.FC<{
onChange: (key: string, val: number) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ const ctx = new AudioContext();

const StatisticsAWPRegistered = new AsyncOnce(() =>
ctx.audioWorklet.addModule(
'/StatisticsNodeProcessor.js?cacheBust=' +
process.env.ASSET_PATH +
'StatisticsNodeProcessor.js?cacheBust=' +
(window.location.host.includes('localhost') ? '' : btoa(Math.random().toString()))
)
);
Expand Down
7 changes: 5 additions & 2 deletions src/graphEditor/nodes/CustomAudio/WaveTable/WaveTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ const WavetableWasmBytes = new AsyncOnce(async () => {
);
}

let path = hasSIMDSupport ? '/wavetable.wasm' : '/wavetable_no_simd.wasm';
let path =
process.env.ASSET_PATH + (hasSIMDSupport ? 'wavetable.wasm' : 'wavetable_no_simd.wasm');
if (!window.location.href.includes('localhost')) {
path += `?cacheBust=${genRandomStringID()}`;
}
Expand Down Expand Up @@ -244,7 +245,9 @@ export default class WaveTable implements ForeignNode {

private async initWorklet() {
await this.ctx.audioWorklet.addModule(
'/WaveTableNodeProcessor.js?cacheBust=' + btoa(Math.random().toString())
process.env.ASSET_PATH +
'WaveTableNodeProcessor.js?cacheBust=' +
btoa(Math.random().toString())
);
this.workletHandle = new AudioWorkletNode(this.ctx, 'wavetable-node-processor');

Expand Down
Loading

0 comments on commit fcb1586

Please sign in to comment.