2026-05-16 05:49:44 +01:00
|
|
|
# AquaSynth
|
2026-05-08 03:00:53 +01:00
|
|
|
|
2026-05-17 14:02:07 +01:00
|
|
|
<p align="center">
|
2026-05-17 19:20:52 +01:00
|
|
|
<img src="icon.png" alt="AquaSynth icon" width="260" />
|
2026-05-17 14:02:07 +01:00
|
|
|
</p>
|
|
|
|
|
|
2026-05-17 19:20:52 +01:00
|
|
|
C# patch authoring, analysis, and Faust toolchain for AquaSynth. It parses the
|
|
|
|
|
`.aqua` DSL into a serializable patch graph, emits Faust `.dsp`, and owns the
|
|
|
|
|
path from patch intent to generated DSP artifacts.
|
2026-05-08 03:00:53 +01:00
|
|
|
|
2026-05-16 05:49:44 +01:00
|
|
|
This repo is the C# bridge for AquaSynth/Vortice-land. Rust remains the reference
|
2026-05-08 03:00:53 +01:00
|
|
|
lab; this project is the tool that lets the engine script Faust without dragging
|
2026-05-16 05:49:44 +01:00
|
|
|
the AquaSynth-rs crate into the room by the ankle.
|
2026-05-08 03:00:53 +01:00
|
|
|
|
|
|
|
|
## Run
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
dotnet test
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-13 17:39:30 +01:00
|
|
|
DX7 reference-render tests use `dexed-py` when a Python with `dexed` and
|
2026-05-16 05:49:44 +01:00
|
|
|
`numpy` is available. Set `AQUASYNTH_DX7_PYTHON` to that interpreter to force a
|
2026-05-13 17:39:30 +01:00
|
|
|
specific runtime; otherwise the tests skip the render path and still parse the
|
|
|
|
|
vendored public-domain fixture.
|
|
|
|
|
|
2026-05-14 23:31:33 +01:00
|
|
|
ZynAddSubFX is pinned as a GPL test/development reference under
|
|
|
|
|
`external/zynaddsubfx`. Initialize it with:
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
git submodule update --init --recursive
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-16 05:49:44 +01:00
|
|
|
That source is a parity oracle only. AquaSynth does not ship Zyn code, link it
|
2026-05-17 19:17:59 +01:00
|
|
|
from published packages, or treat it as runtime machinery.
|
2026-05-14 23:31:33 +01:00
|
|
|
|
2026-05-13 00:19:02 +01:00
|
|
|
## Package Boundary
|
|
|
|
|
|
2026-05-17 19:20:52 +01:00
|
|
|
Downstream consumers should pin the NuGet packages from this repo, not use live
|
2026-05-17 19:17:59 +01:00
|
|
|
project references. Breaking synth-library work should happen here freely, then
|
|
|
|
|
consumers should intentionally update only after new package versions are packed
|
|
|
|
|
and tested.
|
2026-05-13 00:19:02 +01:00
|
|
|
|
|
|
|
|
```powershell
|
2026-05-17 19:17:59 +01:00
|
|
|
dotnet pack src\AquaSynth.Core\AquaSynth.Core.csproj -c Release
|
|
|
|
|
dotnet pack src\AquaSynth.Faust\AquaSynth.Faust.csproj -c Release
|
2026-05-13 00:19:02 +01:00
|
|
|
```
|
|
|
|
|
|
2026-05-17 19:20:52 +01:00
|
|
|
`AquaSynth.Core` owns patch meaning: model records, the `.aqua` parser,
|
|
|
|
|
authoring helpers, analysis/scoring tools, presets, and Faust source emission.
|
|
|
|
|
`AquaSynth.Faust` depends on Core and owns toolchain/rendering work: Faust CLI
|
2026-05-18 10:11:00 +01:00
|
|
|
validation, target-code generation, live patch compilation, native `libfaust`
|
|
|
|
|
loading, compile manifests, DSP factory lifetime, and offline/sample rendering.
|
2026-05-17 19:17:59 +01:00
|
|
|
|
2026-05-14 23:31:33 +01:00
|
|
|
The test suite verifies that development fixtures, Python render helpers,
|
|
|
|
|
external reference synth sources, and SysEx banks do not ship in
|
2026-05-17 19:17:59 +01:00
|
|
|
published packages.
|
2026-05-13 17:39:30 +01:00
|
|
|
|
2026-05-08 03:00:53 +01:00
|
|
|
## Shape
|
|
|
|
|
|
|
|
|
|
- `PatchScript.Parse(script)` lowers terse script into `SynthPatch`.
|
|
|
|
|
- `FaustEmitter.Emit(patch)` emits Faust source.
|
2026-05-17 19:17:59 +01:00
|
|
|
- `FaustCompiler.ValidateAsync(source)` compile-checks with Faust when present
|
|
|
|
|
from `AquaSynth.Faust`.
|
2026-05-17 14:52:58 +01:00
|
|
|
- `FaustCompiler.CompileAsync(source, options)` writes generated backend code
|
2026-05-17 19:17:59 +01:00
|
|
|
through an installed or resolved Faust compiler from `AquaSynth.Faust`.
|
2026-05-18 12:20:32 +01:00
|
|
|
- `FaustCompiler.CompileWebAssemblyScriptAsync(...)` and
|
|
|
|
|
`CompileWebAssemblyAsync(...)` emit browser-ready `.dsp`, `.wasm`, and Faust
|
|
|
|
|
JSON UI metadata artifacts.
|
2026-05-18 10:11:00 +01:00
|
|
|
- `AquaSynthPatchCompiler` from `AquaSynth.Faust` is the live host-facing
|
|
|
|
|
boundary for compiling `.aqua` scripts into hosted DSP factories.
|
|
|
|
|
- `AquaSynthNativeCompiler` remains the lower native `libfaust` compiler
|
|
|
|
|
machinery behind that boundary.
|
2026-05-12 22:10:02 +01:00
|
|
|
- `BuiltInScripts.ReferenceScripts()` carries readable SFXR, BFXR-flavored,
|
2026-05-12 22:38:24 +01:00
|
|
|
808, FM bell, wobble bass, and advanced layered patches. They are stable
|
|
|
|
|
references for testing and for judging whether the DSL can express useful
|
|
|
|
|
sound designs cleanly.
|
2026-05-08 10:34:07 +01:00
|
|
|
- `SfxrParams`, `PatchScriptScoring`, `AudioAnalyzer`, and `Presets` carry the
|
2026-05-16 05:49:44 +01:00
|
|
|
reusable AquaSynth-rs-side analysis, scoring, SFXR, and preset tools.
|
2026-05-08 03:00:53 +01:00
|
|
|
|
2026-05-17 14:52:58 +01:00
|
|
|
The authoring/test lane can compile Faust-generated C# for easy .NET rendering
|
|
|
|
|
and parity checks:
|
2026-05-08 03:00:53 +01:00
|
|
|
|
|
|
|
|
```csharp
|
2026-05-12 22:10:02 +01:00
|
|
|
var patch = PatchScript.Parse("""
|
|
|
|
|
voice
|
|
|
|
|
wave=saw
|
|
|
|
|
freq=55
|
|
|
|
|
gain=0.2
|
|
|
|
|
sustain=0.5
|
|
|
|
|
decay=0.2
|
|
|
|
|
""");
|
2026-05-08 03:00:53 +01:00
|
|
|
var export = FaustEmitter.Emit(patch, new FaustExportOptions("bass"));
|
|
|
|
|
await FaustCompiler.CompileAsync(
|
|
|
|
|
export.Source,
|
|
|
|
|
new FaustCompileOptions(FaustTargetLanguage.CSharp, "Generated/Bass.cs"));
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-18 12:20:32 +01:00
|
|
|
The browser lane uses Faust's WebAssembly backend and keeps the same ownership:
|
|
|
|
|
AquaSynth emits the `.dsp`, `.wasm`, and JSON UI description; the host supplies
|
|
|
|
|
WebAudio scheduling and parameter binding.
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
await FaustCompiler.CompileWebAssemblyScriptAsync(
|
|
|
|
|
"v w=sin f=880 g=.12 sustain=.04 decay=.2",
|
|
|
|
|
new FaustWebAssemblyCompileOptions("Generated/Web", "ui_select"));
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-17 14:52:58 +01:00
|
|
|
The runtime lane is different. AquaSynth should own Faust toolchain selection,
|
|
|
|
|
compile cache identity, generated artifacts, parameter manifests, and dynamic
|
|
|
|
|
patch compilation outside the realtime path. Aquarium Engine should load and
|
|
|
|
|
host the finished artifact, bind controls, and schedule audio. Native Faust
|
|
|
|
|
targets are the preferred shipping path when the platform allows them; C# output
|
|
|
|
|
is convenient authoring machinery, not the ceiling.
|
|
|
|
|
|
2026-05-18 10:11:00 +01:00
|
|
|
`AquaSynthPatchCompiler` is the live runtime-facing version of that boundary: it
|
|
|
|
|
owns native `libfaust` loading policy, script parsing, Faust emission, compile
|
|
|
|
|
keys, DSP source artifacts, manifests, and compiled products. Engine callers
|
|
|
|
|
keep their device and scheduling code, and ask AquaSynth for compiled/renderable
|
|
|
|
|
synth products.
|
2026-05-17 15:03:30 +01:00
|
|
|
|
2026-05-17 14:52:58 +01:00
|
|
|
See [`docs/faust-toolchain-boundary.md`](docs/faust-toolchain-boundary.md).
|
|
|
|
|
|
2026-05-08 03:00:53 +01:00
|
|
|
## Status
|
|
|
|
|
|
2026-05-12 22:10:02 +01:00
|
|
|
The current slice covers the modular graph surface needed by the reference
|
2026-05-08 10:34:07 +01:00
|
|
|
scripts, SFXR atoms, script scoring, audio comparison, presets, Faust emission,
|
2026-05-17 19:20:52 +01:00
|
|
|
native Faust compilation/rendering, and installed Faust validation. Migration
|
|
|
|
|
coverage is tracked in
|
2026-05-08 10:21:32 +01:00
|
|
|
[`docs/migration-checklist.md`](docs/migration-checklist.md), because leaving
|
|
|
|
|
important things behind in the old repo would be a very efficient way to become
|
|
|
|
|
our own haunted house.
|
2026-05-12 22:53:22 +01:00
|
|
|
|
|
|
|
|
Reference-driven DSL growth is tracked in
|
|
|
|
|
[`docs/reference-synth-roadmap.md`](docs/reference-synth-roadmap.md). Agent
|
|
|
|
|
handoff state lives in [`state/spine.yaml`](state/spine.yaml).
|