Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 91bea79b30 | |||
|
|
aec15751cc | ||
| 84f892fc99 | |||
| d11872e815 | |||
|
|
5a77dc8e83 | ||
|
|
30b926c776 | ||
|
|
80ebf39033 | ||
|
|
b906d98ed5 | ||
|
|
afd0cc9ec3 | ||
|
|
4865830d82 | ||
|
|
4a3921c1a9 | ||
|
|
de754b5b8c | ||
|
|
28e37f5cbb | ||
|
|
81362a7013 | ||
| d6252dd6a5 | |||
|
|
dda7a4f26b | ||
|
|
a42cc13e5f | ||
| c136e8fea0 | |||
|
|
e4c6b2d3b3 | ||
|
|
489405a377 | ||
|
|
9c3ae654ca | ||
|
|
5d724f5f5b | ||
|
|
22af1e935e | ||
|
|
ecf381e714 | ||
|
|
0efce042f3 | ||
|
|
448e3b04b0 | ||
|
|
abfc816804 | ||
|
|
396da106cd | ||
| df21ea7bea | |||
|
|
33a4ccc0f2 | ||
|
|
0c9708e376 | ||
| fa1bf3fdbc | |||
|
|
d4dce3ec0c | ||
|
|
1bf99e5c3f | ||
|
|
eb35ca545d | ||
| d8c625c532 | |||
| 810b44db9e | |||
| 5f85effc07 | |||
| c1d2a9468e | |||
| 4b965f39b5 | |||
| b4d203ddcb | |||
|
|
46da9975df | ||
|
|
d348a19ddd | ||
|
|
45f62277f0 | ||
|
|
7593917e32 | ||
|
|
6080f9b8f1 | ||
|
|
39843ae95c | ||
|
|
b7a5142555 | ||
|
|
ceaa597420 | ||
|
|
ab9bcdc6b7 | ||
|
|
c6561da8d4 | ||
|
|
882b7203f5 | ||
|
|
e567b44c24 | ||
|
|
36ff76f03a | ||
|
|
aa45571b3a | ||
| 3b43b7bd72 |
118 changed files with 8557 additions and 61 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -18,3 +18,4 @@ NuGet.Local.Config
|
||||||
_tmp_*.txt
|
_tmp_*.txt
|
||||||
*.user
|
*.user
|
||||||
*.suo
|
*.suo
|
||||||
|
tools/aqua-feel/out/
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,27 @@ search; `FaustHome` then supplies `share/faust` if the prefix is not the file's
|
||||||
grandparent. The loader prepends `lib` and `bin` to `PATH` using the platform
|
grandparent. The loader prepends `lib` and `bin` to `PATH` using the platform
|
||||||
path separator.
|
path separator.
|
||||||
|
|
||||||
|
## Linux Native Toolchain
|
||||||
|
|
||||||
|
Built and run on Debian 13 (2026-11-03). Faust `aa30fd7` (2.90.20), LLVM 19
|
||||||
|
(`LLVM_CONFIG=/usr/lib/llvm-19/bin/llvm-config`, `INCLUDE_DYNAMIC=ON`,
|
||||||
|
`backends/most.cmake`), installed to a prefix; `make -j4`. The first link
|
||||||
|
failed with `cannot find -lPolly -lPollyISL -lzstd`; the install prefix that
|
||||||
|
resulted has `lib/libfaust.so` but `share/faust` holds only the architecture
|
||||||
|
files, not `stdfaust.lib` and the other `.lib` files (they live in the separate
|
||||||
|
faustlibraries repo). Compiling any patch that imports `stdfaust.lib` then
|
||||||
|
fails with `unable to open file stdfaust.lib`. Point `AQUASYNTH_FAUST_HOME` at a
|
||||||
|
home whose `lib/` has `libfaust.so` and whose `share/faust` has both.
|
||||||
|
|
||||||
|
`tools/FaustRender` renders one `.dsp` with an `rpm` control to WAVs plus
|
||||||
|
`manifest.json` (hashes, peak, rms, non-finite flag). It measures; it does not
|
||||||
|
listen. `docs/renders/thruster-v5-manifest.json` is its output for
|
||||||
|
`patches/thruster/thruster.dsp` at PR #3 head `c6561da`, 4 s per case. The render is
|
||||||
|
deterministic (two reruns byte-identical); the 2026-10-31 sweep file is a 6 s
|
||||||
|
render, which is why its hash differs from a 4 s sweep. That manifest describes the mix at `c6561da`;
|
||||||
|
`patches/thruster/thruster.dsp` changed on 2026-11-05 (lower idle noise floors, rumble low-pass opens at low load; see
|
||||||
|
`docs/renders/aqua-feel-2026-11-05.md`), so its `dspSha256` and WAV hashes no longer match the repo's patch.
|
||||||
|
|
||||||
## Invariants
|
## Invariants
|
||||||
|
|
||||||
- AquaSynth remains the authority for what a patch means.
|
- AquaSynth remains the authority for what a patch means.
|
||||||
|
|
|
||||||
316
docs/faust-unity-player-scoping.md
Normal file
316
docs/faust-unity-player-scoping.md
Normal file
|
|
@ -0,0 +1,316 @@
|
||||||
|
# Faust DSP in a built Unity player (Windows + Linux): scoping
|
||||||
|
|
||||||
|
Scoping only. No code changed. Follow-up `aqua-body:follow_up:chat-s3500-3500`, 2026-11-06.
|
||||||
|
|
||||||
|
Question: how does Faust DSP (the thruster, `patches/thruster/thruster.dsp`, v5) reach a
|
||||||
|
built Unity player on Windows and Linux? (a) ahead-of-time (AOT) Faust to C++ per patch
|
||||||
|
inside a native plugin, or (b) runtime `libfaust`/LLVM inside the player.
|
||||||
|
|
||||||
|
**Limitation:** AOT Faust-to-C++ fixes the patch set at build time. Player-authored or modded patches cannot run in a built player; live-edited patches run only in the editor (the developer-time lane of path (b)).
|
||||||
|
|
||||||
|
Marks: **[V]** verified here by reading source or running a tool on 2026-11-06.
|
||||||
|
**[U]** unverified: from memory, or needs a Unity editor/player or a Windows host this run
|
||||||
|
did not have. Nothing here has been run inside Unity.
|
||||||
|
|
||||||
|
## Recommendation
|
||||||
|
|
||||||
|
Take (a) for shipped patches such as the thruster. Keep (b) as a developer-time lane only, if
|
||||||
|
at all. Confidence: about 80% on the choice, lower on the details marked [U].
|
||||||
|
|
||||||
|
The cost of (a): the patch set is fixed at build time. A built player cannot run
|
||||||
|
player-authored or modded patches; live-edited patches run only in the editor, through (b).
|
||||||
|
|
||||||
|
- (a) fits the thruster: a fixed graph with ordinary controls, which
|
||||||
|
`docs/faust-toolchain-boundary.md` ("Parameter changes are not recompiles"; "Shipping
|
||||||
|
builds should prefer Faust-generated native targets") already says is the shipping shape.
|
||||||
|
- (b) buys live recompilation of `.aqua` text in the player. Nothing asks for that yet. It
|
||||||
|
costs a ~130 MB LLVM dependency [V: `libLLVM.so.19.1` is 129,673,080 bytes on this host],
|
||||||
|
the Faust library tree at runtime, and a JIT in the player.
|
||||||
|
- Faust already ships a Unity target that is path (a) with the bridge written
|
||||||
|
(`architecture/unity/`, `faust2unity`, section "What Faust ships"). Start from it and do
|
||||||
|
not hand-roll a plugin.
|
||||||
|
|
||||||
|
## What Faust ships for Unity [V]
|
||||||
|
|
||||||
|
Checked in the 2.90.20 source tree this host built (`faust-llvm-build-20261031/src/architecture/unity/`).
|
||||||
|
|
||||||
|
| File | Role |
|
||||||
|
|---|---|
|
||||||
|
| `unity.cpp` (253 lines) | Unity **native audio SDK** effect plugin: `UnityGetAudioEffectDefinitions`, `ProcessCallback`, `SetFloatParameterCallback`. |
|
||||||
|
| `unityplugin.cpp` (307 lines) | Plain C exports (`Faust_setParameterValue` etc.) for P/Invoke. |
|
||||||
|
| `FaustPlugin_template.cs` | `MonoBehaviour` on an `AudioSource`; `OnAudioFilterRead` calls `ctx.process(...)`. |
|
||||||
|
| `FaustUtilities_template.cs`, `FaustPolyUtilities_template.cs` | `DllImport` glue, inspector UI, polyphonic variant. |
|
||||||
|
|
||||||
|
So Faust provides both bridge shapes the question names. The `FaustPlugin_template.cs`
|
||||||
|
path is the **OnAudioFilterRead bridge**: Unity calls `OnAudioFilterRead(float[] buffer, int
|
||||||
|
numchannels)` on its audio thread and C# P/Invokes the native `process`. `unity.cpp` is the
|
||||||
|
**native audio SDK** shape: Unity's mixer calls `ProcessCallback` directly with no managed
|
||||||
|
frame in the audio path.
|
||||||
|
|
||||||
|
`faust2unity` builds the native libraries and C# scripts and packs a `.unitypackage`;
|
||||||
|
platform scripts are `faust2winunity` (MinGW cross-compile: `-w32`/`-w64`) and
|
||||||
|
`faust2linuxunity` (`g++`, `-fPIC -shared`, `.so`) [V: scripts and README read; not run].
|
||||||
|
|
||||||
|
## Path (a): AOT Faust to C++ in a native plugin
|
||||||
|
|
||||||
|
### Per-platform outputs
|
||||||
|
|
||||||
|
| Platform | Output | Toolchain | Status |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Windows x64 | `FaustPlugin_<name>.dll` | `faust2winunity -64` uses MinGW (`x86_64-w64-mingw32-gcc`) [V: script]. An MSVC build of the generated C++ is plausible but not in Faust's scripts. | [U] runs in a Unity Windows player |
|
||||||
|
| Linux x86_64 | `libFaustPlugin_<name>.so` | `g++ -fPIC -shared` (`faust2linuxunity`) [V: script] | [U] runs in a Unity Linux player |
|
||||||
|
| Inputs | `thruster.dsp` + `stdfaust.lib` tree | `faust` CLI with `-I` at faustlibraries | [V] |
|
||||||
|
|
||||||
|
Generating the thruster as C++ works [V]: `faust -I faustlibraries-20261031 -a minimal.cpp
|
||||||
|
-single -ftz 2 -vec -lv 1 thruster.dsp` gave 1,105 lines (40,045 bytes) with Faust 2.90.20.
|
||||||
|
Those are the same flags `AquaSynthNativeCompiler.CompileSource` passes to the JIT
|
||||||
|
(`src/AquaSynth.Faust/NativeFaustRuntime.cs:530`). The AOT lane should pick its flags
|
||||||
|
deliberately, not inherit them: `-vec` is a choice that the measurement plan below should
|
||||||
|
test against scalar output.
|
||||||
|
|
||||||
|
Windows and Linux builds need two different hosts or a cross-compiler. Starfire (Windows)
|
||||||
|
has Faust and a compiler only if someone installs them there [U].
|
||||||
|
|
||||||
|
### What is reusable from AquaSynth
|
||||||
|
|
||||||
|
| Part | Reusable for (a)? | Evidence |
|
||||||
|
|---|---|---|
|
||||||
|
| `patches/thruster/thruster.dsp` | Yes, as is. It is hand-written Faust, "not a `.aqua` script" (file header). | [V] |
|
||||||
|
| `FaustExport` / `FaustEmitter` (`src/AquaSynth.Core/FaustExport.cs`, 2,558 lines) | Only for `.aqua` patches. The thruster does not go through it. For an `.aqua` patch it yields the `.dsp` source to feed `faust -lang cpp`; `DspSourceDirectory` already writes it (`NativeFaustRuntime.cs:628`). | [V] |
|
||||||
|
| `FaustCompiler.CompileAsync` (`src/AquaSynth.Faust/FaustCompiler.cs:56`) | Yes. Runs the `faust` CLI with `-lang Cpp`; `FaustTargetLanguage.Cpp` exists. This is the AOT generator step. | [V] |
|
||||||
|
| `NativeFaustRuntime.cs` (1,132 lines: `AquaSynthPatchCompiler`, `AquaSynthStreamingPatch`, `FaustNativeToolchain`) | **Not in the shipped player.** It is the libfaust loader and the managed host. Under (a) it stays on the build/authoring side: it renders reference audio and the manifest that the plugin's output is checked against. | [V] |
|
||||||
|
| `tools/FaustRender` + `docs/renders/thruster-v5-manifest.json` | Yes, as the golden reference: hashes, peak, RMS, `nonFinite` per rpm case, 48 kHz / 512-frame blocks. The native plugin should be diffed against this. The manifest hashes predate the 2026-11-05 edit to `thruster.dsp` (`docs/faust-toolchain-boundary.md`, "Linux Native Toolchain"), so re-render first. | [V] |
|
||||||
|
| `AquaSynthNativeManifest` (id, compile key, sample rate, I/O counts, Faust version) | Yes as the artifact provenance record. It has **no parameter table** (no min, max, default). That is a gap for crossing params (below). | [V] |
|
||||||
|
|
||||||
|
Missing for (a), not in AquaSynth today: a build step that runs `faust2unity`-style
|
||||||
|
generation per patch and records the result as artifacts. That should be a script in
|
||||||
|
`tools/`, not a new library. Per the boundary doc, AquaSynth owns "generated
|
||||||
|
source/native artifact layout" and "parameter and bus manifests", and Aquarium/Unity owns
|
||||||
|
"artifact loading policy for its platform".
|
||||||
|
|
||||||
|
### Hot-path hazards in the managed bridge (if OnAudioFilterRead + managed host)
|
||||||
|
|
||||||
|
If the player used `AquaSynthStreamingPatch` directly (managed host, native `libfaust`
|
||||||
|
instance), these are in the audio callback [V: `NativeFaustRuntime.cs`]:
|
||||||
|
|
||||||
|
- `ProcessBlock(float[][] ...)` (line 251) allocates `IntPtr[]` arrays and pins six
|
||||||
|
`GCHandle`s per call. Use the pointer overload (line 235) with preallocated pinned buffers
|
||||||
|
(what `tools/FaustRender/Program.cs:37-40` already does).
|
||||||
|
- `SetControls` does a string dictionary lookup per control per block and a `Marshal.WriteInt32`
|
||||||
|
(lines 841-869). It also throws `ArgumentException` for an unknown path (line 865): an
|
||||||
|
exception in an audio callback. Resolve zone pointers once, off-thread.
|
||||||
|
- The `FaustNativeToolchain` delegates are `Marshal.GetDelegateForFunctionPointer` thunks
|
||||||
|
(line 886), not function pointers; fine for control rate, avoidable per block.
|
||||||
|
|
||||||
|
Under the AOT plugin, none of this exists: the cost moves to the plugin's `setParamValue`
|
||||||
|
and `compute`.
|
||||||
|
|
||||||
|
### Native audio SDK vs OnAudioFilterRead
|
||||||
|
|
||||||
|
| | Native audio SDK (`unity.cpp`) | `OnAudioFilterRead` bridge (`FaustPlugin_template.cs`) |
|
||||||
|
|---|---|---|
|
||||||
|
| Where it runs | Mixer effect, native audio thread, no managed code | Managed callback on the audio thread, then P/Invoke |
|
||||||
|
| Source/generator | Effect slot on a mixer group; `ProcessCallback` returns `ERR_UNSUPPORTED` if `inchannels != outchannels` (`unity.cpp:179`) [V] | Component on a GameObject with `AudioSource` |
|
||||||
|
| Params | `SetFloatParameterCallback`, set from mixer exposed params or `AudioMixer.SetFloat` [U] | `setParameter(int, float)` per patch component |
|
||||||
|
| Risk | Mixer setup per patch; 15-char parameter names (below) | GC pauses or managed allocation can overrun the callback if the managed side allocates [U] |
|
||||||
|
| Fit for thruster | Better on determinism; whether a generator-only (0-input) effect behaves well in a mixer slot is [U] | Matches Faust's template; many instances are easy (one component each) |
|
||||||
|
|
||||||
|
The thruster has 0 inputs and 1 output. In both shapes a 1-channel Faust output goes to
|
||||||
|
the first interleaved channel only and zero-fills the rest (`interleavedWrite`,
|
||||||
|
`architecture/faust/dsp/dsp-tools.h:216-223`) [V]. A stereo player would hear left only
|
||||||
|
unless the patch is made to output two channels or the plugin is edited.
|
||||||
|
|
||||||
|
## Path (b): runtime `libfaust`/LLVM in the player
|
||||||
|
|
||||||
|
`AquaSynthPatchCompiler` already is this, host-side: `createCDSPFactoryFromString` via
|
||||||
|
`NativeLibrary.Load` (`NativeFaustRuntime.cs:793`), the C API delegates (727-737), instances
|
||||||
|
created and computed through `computeCDSPInstance`.
|
||||||
|
|
||||||
|
Per-platform outputs:
|
||||||
|
|
||||||
|
| Platform | Ship | Evidence |
|
||||||
|
|---|---|---|
|
||||||
|
| Linux x64 | `libfaust.so.2.90.20` (8.4 MB) + system `libLLVM.so.19.1` (129.7 MB) + `libz3.so.4`, `libffi.so.8`, `libedit.so.2`, `libzstd`, `libz` (all dynamic deps) + `share/faust/*.lib` | [V: `ldd`, `ls`, doc] |
|
||||||
|
| Windows x64 | `libfaust.dll` plus `share/faust` | [U] no Windows libfaust built or run by this run. `AquaSynthFaustToolchain.LibraryFileNames` looks for `libfaust.dll`/`faust.dll` (`NativeFaustRuntime.cs:654-657`); the doc says `C:\Program Files\Faust` is the default home. |
|
||||||
|
|
||||||
|
Costs specific to AquaSynth's own history, from `docs/faust-toolchain-boundary.md`:
|
||||||
|
the Linux link first failed (`cannot find -lPolly -lPollyISL -lzstd`), and the prefix
|
||||||
|
`share/faust` lacked `stdfaust.lib` until the faustlibraries tree was added. The library
|
||||||
|
tree has to travel with the player (`AquaSynthFaustToolchain.Find` probes
|
||||||
|
`<AppContext.BaseDirectory>/Tools/Faust`, line 666).
|
||||||
|
|
||||||
|
Beyond size:
|
||||||
|
|
||||||
|
- A JIT needs executable memory. Desktop Windows/Linux allow it; iOS and consoles do not
|
||||||
|
[U]. A player that needs those later cannot have (b) as its only path.
|
||||||
|
- The compile step is multi-hundred-millisecond to seconds and must run on a worker thread
|
||||||
|
with a finished-DSP handoff (boundary doc, "Runtime Rule"). `AquaSynthNativeManifest.CompileMilliseconds`
|
||||||
|
records it [V: field exists; no number measured here].
|
||||||
|
- Output from a JIT with `-vec` at optLevel `-1` is whatever LLVM emits on that machine.
|
||||||
|
It is not bit-identical across hosts [U], so the reproducibility invariant in the
|
||||||
|
boundary doc ("artifacts are reproducible from script, version, target, options") weakens.
|
||||||
|
|
||||||
|
## Licensing if shipped
|
||||||
|
|
||||||
|
Read from files in the tree this run built [V] unless marked.
|
||||||
|
|
||||||
|
| Component | Licence | Ship implication |
|
||||||
|
|---|---|---|
|
||||||
|
| Faust compiler / `libfaust` 2.90.20 | LGPL-2.1-or-later (header of `COPYING.txt`: "GNU Lesser General Public License ... version 2.1") | Dynamic loading of an unmodified `libfaust.so/.dll` satisfies LGPL if the user can replace it. Ship licence text and source offer. [U] legal review. |
|
||||||
|
| Faust architecture files (`unity.cpp`) | LGPL-3.0-or-later **with exception**: you may create a larger work with the architecture section and distribute it under terms of your choice, **so long as the architecture section is not modified** | Use `unity.cpp` / `unityplugin.cpp` unmodified and the rest of the plugin has no copyleft. Editing them (e.g. to add param clamping) drops the exception for that file. Prefer clamping elsewhere (below). |
|
||||||
|
| Generated C++ from `faust` (the DSP section of the output) | Not governed by the compiler licence per Faust's licence note [U: from Faust FAQ, not checked here] | Verify before shipping. |
|
||||||
|
| `faustlibraries` (`stdfaust.lib`, used by the thruster via `os`, `fi`, `no`, `si`, `ba`, `ma`) | Per-function: `oscillators.lib`, `noises.lib`, `signals.lib`, `basics.lib`, `maths.lib` headers say LGPL (GRAME); `filters.lib` says "Each function in this library has its own license" (e.g. Julius O. Smith III) | Compiled into the generated code. Need a per-function licence inventory of the functions the thruster calls before shipping. Not done. |
|
||||||
|
| LLVM (path (b) only) | Apache-2.0 with LLVM exceptions [U: from memory, not in tree] | Permissive; ship notice. Also notices for z3 (MIT), libffi (MIT), libedit (BSD) [U]. |
|
||||||
|
|
||||||
|
Net: (a) puts only unmodified-architecture-file and faustlibraries notices in the player. (b)
|
||||||
|
adds LGPL relinking obligations for `libfaust` and a third-party LLVM stack.
|
||||||
|
|
||||||
|
## Crossing bounded parameters (rpm now; load/velocity/quality later)
|
||||||
|
|
||||||
|
Today's thruster has one control:
|
||||||
|
`rpm = hslider("rpm", 1200, 300, 6000, 1) : si.smooth(ba.tau2pole(0.2))`
|
||||||
|
(`thruster.dsp` line 6). `load` is derived from it (`load = (rpm - 300) / 5700`). The
|
||||||
|
generated C++ declares it with `addHorizontalSlider("rpm", &fHslider0, 1.2e+03f, 3e+02f, 6e+03f, 1.0f)`
|
||||||
|
[V]. Later controls (load, velocity, quality) must each be declared the same way, with a
|
||||||
|
default, min and max.
|
||||||
|
|
||||||
|
### What crosses and how
|
||||||
|
|
||||||
|
- **AOT / `OnAudioFilterRead`**: C# calls `Faust_setParameterValue(ctx, index, float)`
|
||||||
|
(`FaustUtilities_template.cs:307`), which does `*fZone = v` with no clamp
|
||||||
|
(`APIUI.h` `setParamValue(int, FAUSTFLOAT)` = `*fItems[p].fZone = v`) [V]. Parameters are
|
||||||
|
addressed by integer index in declaration order. The `.cs` file prints min/max/init
|
||||||
|
(`getParameterMin/Max`).
|
||||||
|
- **AOT / native audio SDK**: `ComputeUnityParameters` fills Unity's
|
||||||
|
`UnityAudioParameterDefinition` with `min`, `max`, `defaultval` from the Faust UI
|
||||||
|
(`unity.cpp:116-118`) [V]. Unity then knows the range and the default; the inspector and
|
||||||
|
mixer use it. **Names are cut to 15 characters** (`strncpy(pdef[i].name, ..., 15)`,
|
||||||
|
`unity.cpp:112`) [V]. `rpm` fits; Aqua-style paths such as `/curves/<name>/depth` do not.
|
||||||
|
The AquaSynth path lengths need a unique-prefix rule or the control must be
|
||||||
|
addressed by index.
|
||||||
|
- **Runtime `libfaust` (b)**: `AquaSynthStreamingPatch.SetControls` writes the float's
|
||||||
|
bits to the zone pointer with `Marshal.WriteInt32` (`NativeFaustRuntime.cs:868`). No
|
||||||
|
range or default crosses; `FaustUiGlue.AddControl` discards `init, min, max, step`
|
||||||
|
(line 1028) [V].
|
||||||
|
|
||||||
|
Float writes are the only thing crossing. A single aligned 32-bit store is atomic on x86-64,
|
||||||
|
so no lock is needed for one scalar control [U: assumes the compiler does not tear; the
|
||||||
|
generated code reads each zone once per block, `float fSlow0 = static_cast<float>(fHslider0)`,
|
||||||
|
`compute` line 430 in the generated file] [V for the read]. A control that moves within a
|
||||||
|
block takes effect at the next block, smoothed by `si.smooth` (rpm uses a 0.2 s time
|
||||||
|
constant).
|
||||||
|
|
||||||
|
### Needed for several bounded params
|
||||||
|
|
||||||
|
1. A parameter table per patch alongside the existing `AquaSynthNativeManifest`:
|
||||||
|
`{path, index, default, min, max}`. (b) can get it from `FaustUiGlue` if
|
||||||
|
`AddControl` stops discarding `init/min/max`; (a) can get it from the same
|
||||||
|
`faust -json` output or from the C++ UI callbacks at build time.
|
||||||
|
2. One clamp site (below), driven from that table, owned by AquaSynth, not by
|
||||||
|
Unity code.
|
||||||
|
3. A normalized-to-range mapping owned by AquaSynth for load/velocity/quality when the game
|
||||||
|
supplies 0..1, if that is the game's contract. `ControlSurfaceCatalog` already holds
|
||||||
|
normalized surfaces for `.aqua` (`hslider(path, DefaultNormalized, 0, 1, 0.001)`,
|
||||||
|
`FaustExport.cs:157`) [V]; the thruster has none.
|
||||||
|
|
||||||
|
## Are param inputs clamped today?
|
||||||
|
|
||||||
|
No, for the thruster path. Layer by layer [V]:
|
||||||
|
|
||||||
|
| Layer | Clamp? |
|
||||||
|
|---|---|
|
||||||
|
| `thruster.dsp` | No. Slider min/max are UI metadata. The generated `compute` reads the zone raw (`fSlow0 = fHslider0`). An rpm of 20000 or -5 reaches `si.smooth` and every formula (`shaft = 28 + rpm*0.025`, `load = (rpm-300)/5700`, band centres, drive). |
|
||||||
|
| `FaustNativeToolchain.WriteControl` (`NativeFaustRuntime.cs:860-869`) | No. Only unknown-path rejection, and `NaN`/infinity written as-is. |
|
||||||
|
| `AquaSynthStreamingPatch.SetControls` / `ProcessBlock` | No. |
|
||||||
|
| `tools/FaustRender/Program.cs:51` | No; it only sweeps within its CLI arguments. |
|
||||||
|
| `NativeFaustRuntimeTests.cs` | No test for out-of-range, NaN, or infinity control values found (grep for clamp/throws/range: no hit). |
|
||||||
|
| `ControlSplineTimeline` (`src/AquaSynth.Core/ControlSplineTimeline.cs:167-231`) | Yes, but only normalized `[0,1]` for `.aqua` control splines. It is not in the thruster path. |
|
||||||
|
| Faust `APIUI::setParamValue` (Unity bridge) | No (`*fZone = v`). |
|
||||||
|
| Unity side | The mixer/inspector clamp to the declared `min/max` in the native SDK path [U]. A script calling `setParameter` or `SetFloat` directly is not clamped by Faust's glue [V]. |
|
||||||
|
|
||||||
|
The final `tanh` and the 0.425 trim (`thruster.dsp` last line) bound the output. Peak at
|
||||||
|
rpm 4800 is 0.36 (`docs/renders/thruster-v5-manifest.json`) [V]. Whether out-of-range rpm stays
|
||||||
|
finite and bounded is **not measured**: the manifest covers 300..6000 only. Hence
|
||||||
|
`nonFinite` coverage in the plan below includes out-of-range and non-finite inputs.
|
||||||
|
|
||||||
|
Recommended clamp site: in the AquaSynth-owned manifest/host layer (for (a), a small
|
||||||
|
wrapper in the managed script that Faust's unmodified architecture files sit under; for (b),
|
||||||
|
`WriteControl`), clamping to the declared min/max and rejecting NaN. Do not edit
|
||||||
|
`unity.cpp`/`APIUI.h` (loses the architecture exception). The DSP itself can also carry the
|
||||||
|
bound: `hslider(...) : max(300) : min(6000)` in the `.dsp` is the one place both
|
||||||
|
lanes would share. That is a patch edit and a design choice for the thruster's owner, not
|
||||||
|
decided here.
|
||||||
|
|
||||||
|
## Callback-deadline measurement plan (rerunnable)
|
||||||
|
|
||||||
|
Goal: show, per path and platform, that `compute` fits the audio callback with margin,
|
||||||
|
including at the worst rpm sweep, under CPU load. Nothing here has been run.
|
||||||
|
|
||||||
|
**Deadline.** `D = frames / sampleRate`. Unity's block comes from `AudioSettings.GetDSPBufferSize`
|
||||||
|
(`FaustPlugin_template.cs` `getBufferSize`). Test 256, 512, 1024 frames at 44.1 k and
|
||||||
|
48 k (the render manifest uses 48 k / 512, i.e. D = 10.67 ms). Proposed pass line:
|
||||||
|
p99.9 under 25% of D, max under 50% of D (Aqua's ruling, 2026-11-07). The spike's
|
||||||
|
shared-host outliers (max 10.8-17.1 ms, 4-5 blocks over deadline) are **unexplained**: the spike
|
||||||
|
(`spikes/faust-aot-thruster/`) shows they are not per-block DSP cost (an empty timed busy-wait stalls
|
||||||
|
the same way), but not what causes them. Until a quiet or realtime host explains or removes them, the
|
||||||
|
max line has no verdict from this host.
|
||||||
|
|
||||||
|
**What the spike measures, and what it does not.** It times a bare C loop calling the compiled
|
||||||
|
`process` back to back on Linux. That shows the patch is cheap (median 0.05 ms against 10.67 ms). It
|
||||||
|
does not show that a Unity player meets its deadline: the player's audio thread, the managed-to-native
|
||||||
|
transition, IL2CPP, mixer and other DSP sharing the callback, and Windows scheduling are all absent.
|
||||||
|
|
||||||
|
**What to time.**
|
||||||
|
|
||||||
|
1. `compute(count, ...)` alone, pure C++ (AOT): build `thruster.cpp` with Faust's
|
||||||
|
`architecture/bench.cpp` or a 30-line driver; `std::chrono::steady_clock` around each
|
||||||
|
`compute`; 200,000 blocks; discard the first 1,000. [V: `bench.cpp` exists in
|
||||||
|
`architecture/`; not run.]
|
||||||
|
2. The whole plugin callback: `ProcessCallback` or `OnAudioFilterRead` including
|
||||||
|
`interleavedWrite` and the parameter write; timing taken inside the plugin with a
|
||||||
|
preallocated ring of timestamps, no logging in the callback; dumped on unload.
|
||||||
|
3. Managed host (b): `FaustRender`-style loop over `AquaSynthStreamingPatch.ProcessBlock`
|
||||||
|
with the pointer overload, `Stopwatch.GetTimestamp` around each call, GC allocation count
|
||||||
|
(`GC.GetAllocatedBytesForCurrentThread`) asserted zero across the loop.
|
||||||
|
|
||||||
|
**Inputs.** The rpm cases from the manifest (600, 4800, sweeps) plus 300 and 6000 steady, a
|
||||||
|
full 300->6000->300 ramp changing rpm every block, and the out-of-range set
|
||||||
|
(`-1e9`, `0`, `1e6`, `NaN`, `+Inf`) with finite-and-bounded output asserted. Add the
|
||||||
|
rpm-and-load product of any new controls. Include denormal check (`-ftz 2` build vs without).
|
||||||
|
|
||||||
|
**Conditions.** Idle host and with the Starfire load other Minds create (record
|
||||||
|
`nvidia-smi` and `query user` before and after; she wants it saturated, but a GPU hang resets
|
||||||
|
her display, so the run is CPU-only); one thread pinned; power plan noted; Debug and Release
|
||||||
|
for managed pieces. Linux on Yggdrasil or this host, Windows on Starfire over `ssh starfire`
|
||||||
|
(not run, and it is Meta's shared machine, so one job at a time).
|
||||||
|
|
||||||
|
**Variants to compare**, same inputs: AOT `-vec` vs scalar; AOT `-O2` vs `-O3 -march=x86-64-v2`
|
||||||
|
(avoid `-march=native`, which the player's CPU may not match); JIT optLevel `-1` vs 3 [U: flag
|
||||||
|
meaning]. First-callback and instantiation time recorded separately from steady state.
|
||||||
|
|
||||||
|
**Output.** One JSON per run: host, OS, CPU model, Faust version, compile flags, block size,
|
||||||
|
sample rate, n, min/p50/p99/p99.9/max in microseconds, % of D, overrun count (`t > D`), peak,
|
||||||
|
rms, `nonFinite`, `dspSha256`. Same shape and location as `docs/renders/thruster-v5-manifest.json`
|
||||||
|
so the existing hashes can be compared. A script for it belongs in `tools/` next to
|
||||||
|
`FaustRender`.
|
||||||
|
|
||||||
|
**In the real player.** Unity does not expose callback timing directly. Time inside
|
||||||
|
the plugin as above, and read `AudioSettings` underrun counters / Profiler `Audio` markers
|
||||||
|
[U: exact counter names] over a 10-minute run in a development build on each OS.
|
||||||
|
|
||||||
|
## Unverified list, in one place
|
||||||
|
|
||||||
|
- That a built player can run anything but the patches compiled into it: player-authored or
|
||||||
|
modded patches are out of reach by construction under (a); live edit needs the editor and (b).
|
||||||
|
|
||||||
|
- That the generated Windows `.dll` and Linux `.so` load and play in a Unity Windows/Linux
|
||||||
|
player (no Unity available in this run).
|
||||||
|
- `faust2winunity` end-to-end on any host (MinGW not checked).
|
||||||
|
- Behaviour of a 0-input generator in a mixer effect slot; mixer parameter exposure.
|
||||||
|
- Generated-code licence position, LLVM/z3/libffi/libedit licences, and a per-function
|
||||||
|
inventory of `faustlibraries` licences for the thruster's calls.
|
||||||
|
- A Windows `libfaust.dll` build and its dependencies.
|
||||||
|
- Compile latency in milliseconds for the thruster (the field exists, no number taken).
|
||||||
|
- Out-of-range / non-finite rpm behaviour of the thruster output.
|
||||||
|
- Atomicity of the float zone write across a block boundary in a real plugin.
|
||||||
|
- Unity's DSP buffer size defaults and underrun counter names.
|
||||||
46
docs/renders/aqua-feel-2026-11-05.md
Normal file
46
docs/renders/aqua-feel-2026-11-05.md
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# aqua-feel first run, 2026-11-05
|
||||||
|
|
||||||
|
Nothing here was heard. These are metrics from offline renders (faustwasm, mono, 48 kHz, 3 s per patch); they are evidence,
|
||||||
|
not verdicts. `tools/aqua-feel/README.md` says what each number is and where it breaks.
|
||||||
|
|
||||||
|
Before: `patches/` at `origin/main` d8c625c under the same rules: the gate fails on 3 items (PRC SYNTH1: overs, true peak,
|
||||||
|
loudness; sfxr golf: true peak, loudness, low motion; sfxr explosion: low motion). After: PRC SYNTH1 gain trimmed 1.4 to
|
||||||
|
0.45 (a uniform gain: crest and centroid are unchanged), and `thruster.dsp` mix edited (below). Gate after: pass, 4 waivers
|
||||||
|
(golf x3, explosion x1), 1 warning (golf, +0.9 dBTP).
|
||||||
|
|
||||||
|
## prc-synth1-calibrated
|
||||||
|
|
||||||
|
samplePeak 2.10 -> 0.67 -1.42
|
||||||
|
truePeakDb 6.44 -> -3.41 -9.86
|
||||||
|
overSamples 18633 -> 0 -18633.00
|
||||||
|
loudnessLufs -0.67 -> -10.53 -9.86
|
||||||
|
crestDb 6.67 -> 6.67 +0.00
|
||||||
|
centroidMedianHz 794.44 -> 794.44 +0.00
|
||||||
|
noiseToToneDb -17.18 -> -17.18 +0.00
|
||||||
|
noiseToToneADb -19.14 -> -19.14 +0.00
|
||||||
|
onsetMs 0.13 -> 0.13 +0.00
|
||||||
|
attackMs 0.33 -> 4.50 (the metric changed to a 150 ms window with a held envelope during review; the patch did not)
|
||||||
|
lowMotion false -> false
|
||||||
|
|
||||||
|
The shipped file's `patch gain=1.4` is the constant the parity probe in `Dx7ReferenceParityTests` writes; the probe asserts
|
||||||
|
RMS ratio against Dexed, so the test is untouched and still builds its own script at 1.4. Only the shipped library patch
|
||||||
|
carries the trim. Whether 0.45 is the right level for a listener is unchecked.
|
||||||
|
|
||||||
|
## thruster
|
||||||
|
|
||||||
|
Edits to `patches/thruster/thruster.dsp` (mirrored in `tools/aqua-trial/thruster-trial.dsp`): rasp gain `0.3 + 1.5*load` to
|
||||||
|
`0.05 + 1.75*load`; rattle floor `0.1 + 0.9*load^2` to `0.015 + 0.985*load^2`; rumble low-pass `60 + 2.5*shaft` to
|
||||||
|
`60 + 2.5*shaft + 140*(1-load)`. At load 1 (rpm 6000) every term equals its old value (rasp 1.8, rattle 1, low-pass 60 + 2.5*shaft).
|
||||||
|
Decomposition at rpm 300 before the edit (A-weighted noise-to-tone, dB): full mix +21, rasp removed +20.5, rattle removed
|
||||||
|
+13.0, both removed +12.5. The rattle modes, not the rasp, were most of the audible-band noise at low rpm; with both gone a
|
||||||
|
floor of +12.5 dB remains (the pitch-wandering saw smears its harmonics, which the line detector counts as noise).
|
||||||
|
|
||||||
|
rpm A-weighted noise-to-tone dB (before -> after) loudness LUFS centroid Hz
|
||||||
|
300 20.7 -> 13.1 -29.5 -> -30.2 2388 -> 752
|
||||||
|
600 23.0 -> 5.0 -28.0 -> -28.7 2531 -> 1228
|
||||||
|
1200 20.1 -> 12.7 -25.4 -> -26.1 2913 -> 2114
|
||||||
|
4800 10.3 -> 9.7 -17.3 -> -17.4 4987 -> 4914
|
||||||
|
|
||||||
|
The centroid falls a lot at low rpm: the sound is darker as well as less noisy. Whether that is "more tone" to a listener or
|
||||||
|
"muffled" is exactly what this meter cannot say. The listening trial (`tools/aqua-trial`) now sweeps a narrower noise range at
|
||||||
|
rpm 600 (see its README).
|
||||||
46
docs/renders/thruster-v5-manifest.json
Normal file
46
docs/renders/thruster-v5-manifest.json
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"patch": "thruster",
|
||||||
|
"dspSha256": "8afc045eab94f396e21d96a262c5d71910103acd51da5913d3eb3036b48a5809",
|
||||||
|
"sampleRate": 48000,
|
||||||
|
"blockFrames": 512,
|
||||||
|
"seconds": 4,
|
||||||
|
"rpmControl": "rpm",
|
||||||
|
"os": "Debian GNU/Linux 13 (trixie)",
|
||||||
|
"faustHome": "/var/lib/mind-aqua-self/faust-home-overlay-20261031",
|
||||||
|
"faustLibrary": "/var/lib/mind-aqua-self/faust-home-overlay-20261031/lib/libfaust.so",
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"label": "low-rpm600",
|
||||||
|
"file": "thruster-low-rpm600.wav",
|
||||||
|
"rpmStart": 600,
|
||||||
|
"rpmEnd": 600,
|
||||||
|
"frames": 192000,
|
||||||
|
"peak": 0.18832043,
|
||||||
|
"rms": 0.07348131804614891,
|
||||||
|
"nonFinite": false,
|
||||||
|
"sha256": "4aeaa1f4d9aa5d63025dd8b3d45b25fc6b030a366ba05237c0021b508308e0ba"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "high-rpm4800",
|
||||||
|
"file": "thruster-high-rpm4800.wav",
|
||||||
|
"rpmStart": 4800,
|
||||||
|
"rpmEnd": 4800,
|
||||||
|
"frames": 192000,
|
||||||
|
"peak": 0.3635283,
|
||||||
|
"rms": 0.14323418081979195,
|
||||||
|
"nonFinite": false,
|
||||||
|
"sha256": "390874aa1d5c4d50f251fa0735b82f859bba43ffee4236fd24d5b06a4733ef7d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "sweep-600-4800",
|
||||||
|
"file": "thruster-sweep-600-4800.wav",
|
||||||
|
"rpmStart": 600,
|
||||||
|
"rpmEnd": 4800,
|
||||||
|
"frames": 192000,
|
||||||
|
"peak": 0.34473976,
|
||||||
|
"rms": 0.11363349353245127,
|
||||||
|
"nonFinite": false,
|
||||||
|
"sha256": "ad8d1508aa2b37b5038a6acfcfb21fc46ae04f0180adcd6ea60edc7e918e4068"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
222
docs/soundscape-grammar.md
Normal file
222
docs/soundscape-grammar.md
Normal file
|
|
@ -0,0 +1,222 @@
|
||||||
|
# Soundscape grammar for the Ratatoskr generative symphony (spec)
|
||||||
|
|
||||||
|
Serves follow-up `aqua-body:follow_up:chat-s938-943`. **Grammar only.** No notes,
|
||||||
|
no motifs, no pitches, no instrument choices. Nothing here was rendered, run,
|
||||||
|
listened to, or measured; every number marked *proposed* is a design choice
|
||||||
|
awaiting a listening trial and a device measurement. Nothing in this document
|
||||||
|
may be cited as heard.
|
||||||
|
|
||||||
|
## 0. Inputs not seen
|
||||||
|
|
||||||
|
Two inputs named in the brief could not be read when this was written, and the
|
||||||
|
grammar is drafted without them. Each is a place to reconcile before the doc is
|
||||||
|
treated as in force.
|
||||||
|
|
||||||
|
- `heimdall-body:follow_up:ratatoskr-creature-projection-v2`: `huginn view`
|
||||||
|
from instance `aqua` answers `absent` (and `absent` from Eureka's mind on
|
||||||
|
2026-11-05), and a search of the Heimdall checkout
|
||||||
|
(`GameCult/Heimdall`, forge, 2026-11-02) found no file mentioning it. The
|
||||||
|
constraints taken from the brief are used as stated there: coarse buckets,
|
||||||
|
neutral overrides, activity comes from the Heimdall session. Section 2 is the
|
||||||
|
place that must be reconciled against that follow-up's actual field list.
|
||||||
|
- `ratatoskr:follow_up:symphony-grammar-harvest-mimir` (Mimir birdsong
|
||||||
|
research): absent from aqua's mind, but it is in force in Eureka's mind
|
||||||
|
(read 2026-11-05, after this draft was written). It points at Mimir's
|
||||||
|
`docs/bioacoustic-timeline-watermark.md`, `research/bioacoustic-contestants-2026-05-24.md`
|
||||||
|
and `calibration/bioacoustic/` (at `c6f4541`), which this draft does not cite
|
||||||
|
and nobody has read for it. The earlier grep of `GameCult/Mimir` for a
|
||||||
|
symphony or voice-separation note found none; it did not look for bioacoustic research.
|
||||||
|
Section 4 is therefore built from this repo's own `research/syrinx-modeling`
|
||||||
|
plus the general auditory-scene-analysis and acoustic-niche literature named
|
||||||
|
there, *recalled, not re-read for this draft*. The harvest is still owed and
|
||||||
|
is the likeliest thing to change section 4.
|
||||||
|
|
||||||
|
Context actually read: `docs/gamecult/ratatoskr-target.md` in `GameCult/Ratatoskr`
|
||||||
|
at `b33d8de` (the agreement that one activity vocabulary drives both sound and
|
||||||
|
motion, the member-safe projection, "kind, Mind, time and intensity, never
|
||||||
|
briefs or mind contents"), and `docs/agent-activity-grammar.md` (an earlier,
|
||||||
|
unmerged AquaSynth draft for the Dings event source; not part of this change).
|
||||||
|
|
||||||
|
## 1. Purpose and owner
|
||||||
|
|
||||||
|
One typed **activity frame** per Mind, produced once, read by two consumers:
|
||||||
|
|
||||||
|
- the **orbit renderer** (Persona motion around Yggdrasil), and
|
||||||
|
- the **voice scheduler** (which instrument voice speaks, how densely).
|
||||||
|
|
||||||
|
Neither consumer owns the vocabulary; neither adds a field. The vocabulary owner
|
||||||
|
is this grammar. The frame's producer is the Heimdall session (section 2). A
|
||||||
|
consumer that needs a distinction the vocabulary lacks raises it as a change to
|
||||||
|
this document, not as a private side channel.
|
||||||
|
|
||||||
|
## 2. The activity frame
|
||||||
|
|
||||||
|
Typed CultNet/CultCache document, projected member-safe. No private pipe: the
|
||||||
|
frame reaches both consumers by the same subscription, and a consumer that
|
||||||
|
cannot read it plays nothing and draws the neutral pose.
|
||||||
|
|
||||||
|
| field | type | values | note |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `mind` | id | instance name | stable identity, not a run/session label |
|
||||||
|
| `kind` | enum, 7 | `Idle`, `Reading`, `Thinking`, `Working`, `Speaking`, `Waiting`, `Alert` | coarse; see below |
|
||||||
|
| `energy` | enum, 4 | `Low`, `Mid`, `High`, `Peak` | coarse bucket of rate/intensity, never a float |
|
||||||
|
| `seq` | u32 | monotonic per `mind` | ordering and staleness; consumers drop `seq <= last` |
|
||||||
|
| `at` | time bucket | 1 s resolution | producer's observation time, for the staleness rule |
|
||||||
|
| `override` | enum, 3 | `None`, `Neutral`, `Muted` | see rules O1..O3 |
|
||||||
|
|
||||||
|
Why this small: 7 x 4 = 28 states fit in one byte on the wire and in a lookup
|
||||||
|
table on a phone; both consumers map state to behaviour by table, no arithmetic
|
||||||
|
on unbounded values. `kind` and `energy` are orthogonal on purpose, because
|
||||||
|
orbit radius/speed wants energy and voice choice wants kind.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- **A1. Source.** The frame comes from the Heimdall session and only from it.
|
||||||
|
Neither consumer infers activity from Matrix presence, typing, message
|
||||||
|
traffic, or audio. (The target already notes presence/typing are too coarse
|
||||||
|
for Minds.) *Reconcile the exact kind list against the projection-v2
|
||||||
|
follow-up before use.*
|
||||||
|
- **A2. Coarse by construction.** There is no continuous field in the frame. A
|
||||||
|
renderer that wants smooth motion interpolates between buckets locally; it
|
||||||
|
does not request finer data.
|
||||||
|
- **A3. Member-safe.** The frame carries kind, Mind, time and energy bucket.
|
||||||
|
Never brief text, document ids, claims, or mind contents.
|
||||||
|
- **A4. Missing is Idle.** No frame within the staleness window (section 5, S2)
|
||||||
|
means `Idle`/`Low`, rendered and sounded as nothing. Silence is the default
|
||||||
|
state, not a failure state.
|
||||||
|
|
||||||
|
### Overrides
|
||||||
|
|
||||||
|
- **O1. `Neutral`** (set by the session or by the member's own setting): the
|
||||||
|
Mind renders at the neutral pose and contributes no voice, regardless of
|
||||||
|
`kind`/`energy`. Neutral is not an error; it hides activity, it does not
|
||||||
|
pretend there is none to other consumers.
|
||||||
|
- **O2. `Muted`**: no voice, orbit unchanged. Lets a listener silence one Mind
|
||||||
|
without hiding its motion.
|
||||||
|
- **O3.** Overrides are data in the frame, set upstream. A consumer never
|
||||||
|
applies a local override and then reports it as the Mind's state. (A *user's*
|
||||||
|
own mute/volume is a client preference and stays local; it never rewrites a
|
||||||
|
frame.)
|
||||||
|
|
||||||
|
## 3. Mapping tables (shape only)
|
||||||
|
|
||||||
|
The grammar defines the mapping *axes*, not the values. Each consumer owns one
|
||||||
|
table keyed by the 28 states; the tables are checked in beside this document
|
||||||
|
when values exist.
|
||||||
|
|
||||||
|
| axis | owner | driven by | notes |
|
||||||
|
|---|---|---|---|
|
||||||
|
| orbit radius / speed / phase coupling | orbit renderer | `energy`, `kind` | Nibu's side; not specified here |
|
||||||
|
| voice **presence** (sounding or silent) | voice scheduler | `kind` | `Idle`, `Waiting` default silent |
|
||||||
|
| voice **density** (events per window) | voice scheduler | `energy` | capped, section 5 |
|
||||||
|
| voice **role** (lead / support / texture) | voice scheduler | `kind` | role, not instrument |
|
||||||
|
| voice **identity** (which instrument) | voice scheduler | `mind` | never `kind`; section 4 |
|
||||||
|
|
||||||
|
The one rule that ties the two consumers: **`mind` selects identity, `kind`
|
||||||
|
selects behaviour, `energy` selects amount.** A given Mind sounds like itself
|
||||||
|
in every state; what it does changes.
|
||||||
|
|
||||||
|
## 4. Keeping voices distinct when mixed
|
||||||
|
|
||||||
|
Principle, from the acoustic-niche and auditory-scene-analysis literature
|
||||||
|
(recalled, to be confirmed by the Mimir harvest): voices that share a niche in
|
||||||
|
frequency, time and space mask each other; voices that differ on several
|
||||||
|
independent axes stay separable even at high density. Assign each Mind a
|
||||||
|
distinct **niche** on independent axes, and make the scheduler protect those
|
||||||
|
niches. Axes:
|
||||||
|
|
||||||
|
1. **Register band.** Each Mind owns a band; bands do not overlap among
|
||||||
|
concurrently audible voices. Most robust single cue.
|
||||||
|
2. **Timbre family.** Distinct spectral envelope and onset shape. Identity is
|
||||||
|
carried by instrument (`ratatoskr-target.md`: "the instrument says who
|
||||||
|
spoke"); this grammar does not choose instruments.
|
||||||
|
3. **Rhythmic slot.** Each Mind owns a phase offset within a shared pulse grid,
|
||||||
|
so onsets interleave instead of colliding. Collisions are the main cause of
|
||||||
|
masking at low density.
|
||||||
|
4. **Stereo position.** Fixed pan per Mind, stable across sessions. Free on
|
||||||
|
phones with headphones, nearly useless on speakers; never the only cue.
|
||||||
|
5. **Role contrast.** At most one `lead` voice sounding at a time; the rest
|
||||||
|
support or texture (quieter, sparser, narrower band).
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- **V1. Identity is stable.** The niche (axes 1-4) is a function of `mind`
|
||||||
|
only. It never moves with `kind`, `energy`, or which other Minds are present.
|
||||||
|
A listener learns a Mind once.
|
||||||
|
- **V2. Niche table is data**, held beside the Mind's other typed state
|
||||||
|
(the target already says the instrument per Mind belongs in a typed document
|
||||||
|
on the Mind side, not hardcoded). Not defined here.
|
||||||
|
- **V3. Voice cap.** At most **4** voices audible at once (*proposed*; the
|
||||||
|
human limit on following concurrent streams is small and phones are
|
||||||
|
small). Over the cap, the lowest-`energy` voices drop first, never the
|
||||||
|
`lead`. The cap is a requirement to measure, not a finding.
|
||||||
|
- **V4. Collision backoff.** If two voices would start within one pulse of each
|
||||||
|
other in the same register band, the lower-priority one slips to its next
|
||||||
|
slot. It does not stack.
|
||||||
|
- **V5. More Minds than niches.** When Minds exceed available niches, the
|
||||||
|
excess are *not* squeezed in by shrinking bands. They share a `texture`
|
||||||
|
voice or stay silent. Distinctness beats completeness.
|
||||||
|
- **V6. Identification is unproven.** Nominal separation (different bands,
|
||||||
|
different families) is not human discriminability. No Mind's niche is
|
||||||
|
accepted until a listener-identification trial passes (the existing
|
||||||
|
notification ledger requires the same). That trial has not happened.
|
||||||
|
|
||||||
|
## 5. Timing, staleness and the callback deadline
|
||||||
|
|
||||||
|
Hard rule: **the audio callback deadline wins over everything in this
|
||||||
|
document.** The callback never waits on the grammar, the network, a decode, an
|
||||||
|
allocation, or a lock.
|
||||||
|
|
||||||
|
- **C1. Frame handoff is a snapshot.** The network/projection thread writes the
|
||||||
|
latest frame per `mind` into a preallocated table; the scheduler thread reads
|
||||||
|
it and writes a preallocated voice-command queue; the audio callback only
|
||||||
|
reads that queue. No thread but the callback touches the output buffer; the
|
||||||
|
callback never blocks on, or is blocked by, the others (single-producer
|
||||||
|
single-consumer, fixed capacity).
|
||||||
|
- **C2. Drop, never delay.** If the queue is full or the scheduler is late, the
|
||||||
|
frame or command is dropped. A late sound is not played to catch up and its
|
||||||
|
internal timing is never stretched.
|
||||||
|
- **S1. Latest wins.** Per `mind`, only the newest `seq` matters. Intermediate
|
||||||
|
frames missed during a stall are not replayed.
|
||||||
|
- **S2. Staleness.** A frame older than **10 s** (*proposed*) is treated as
|
||||||
|
absent (rule A4). Sounding on stale activity is misleading.
|
||||||
|
- **S3. Rate.** Voice density per `energy` is bounded by a ceiling per Mind per
|
||||||
|
window (*proposed*, set with the device measurement); rule V3 bounds the
|
||||||
|
total. Bursts are coalesced, not queued.
|
||||||
|
- **S4. Pre-rendered material.** The client ships rendered audio assets, no
|
||||||
|
synth in the browser (`ratatoskr-target.md` agreements). The scheduler
|
||||||
|
selects and positions assets; it generates nothing at callback time. Browsers
|
||||||
|
block audio until a first user gesture: before it, the scheduler runs but the
|
||||||
|
output is silent, and no state is lost.
|
||||||
|
- **S5. Backgrounded or hidden page.** Voice scheduler pauses with the orbit
|
||||||
|
renderer's pause (the target already pauses the background when hidden). On
|
||||||
|
resume, S1/S2 apply; there is no catch-up burst.
|
||||||
|
- **S6. Failure is silent.** No frame source, a dead scheduler, a refused
|
||||||
|
autoplay, or a full queue all produce silence and neutral pose. Nothing
|
||||||
|
retries, alerts, or alters the frame source.
|
||||||
|
|
||||||
|
## 6. What this grammar does not decide
|
||||||
|
|
||||||
|
- Motifs, notes, pitches, instruments, envelopes. (Next layer; needs a
|
||||||
|
listening trial per the repo's audio doctrine.)
|
||||||
|
- Orbit geometry and shader work (Nibu's side).
|
||||||
|
- The feed transport. `ratatoskr:question:activity-feed-source` owns where the
|
||||||
|
frame comes from; this document fixes only its *shape*, and that it is a
|
||||||
|
typed CultNet document, not a private channel.
|
||||||
|
- Per-device budgets. All numbers marked *proposed* need a phone measurement.
|
||||||
|
|
||||||
|
## 7. Verification owed
|
||||||
|
|
||||||
|
None run. To be done before the grammar leaves draft:
|
||||||
|
|
||||||
|
1. Reconcile section 2 with `heimdall-body:follow_up:ratatoskr-creature-projection-v2`.
|
||||||
|
2. Reconcile section 4 with the Mimir harvest.
|
||||||
|
3. Round-trip the frame as a CultCache document and check the size claim (one
|
||||||
|
byte per state, 28 states).
|
||||||
|
4. Measure voice cap, density ceilings and scheduler cost on a real phone.
|
||||||
|
5. Listener-identification trial for any niche table (rule V6).
|
||||||
|
|
||||||
|
## 8. Status
|
||||||
|
|
||||||
|
Draft. Not implemented. No code changed. No audio was produced and none is
|
||||||
|
claimed.
|
||||||
|
|
@ -2,9 +2,11 @@
|
||||||
# Family: dx7/public-domain
|
# Family: dx7/public-domain
|
||||||
# Source: analog1.syx voice 17 parity probe, generated from calibrated DX7 lowering
|
# Source: analog1.syx voice 17 parity probe, generated from calibrated DX7 lowering
|
||||||
# Notes: Hard DX7 parity target that forced applied EG, summed route, feedback, detune, and COM calibration.
|
# Notes: Hard DX7 parity target that forced applied EG, summed route, feedback, detune, and COM calibration.
|
||||||
|
# Headroom: patch gain is 0.45, not the 1.4 the parity probe in Dx7ReferenceParityTests uses. At 1.4 this graph peaked at 2.10
|
||||||
|
# (18,633 samples past full scale in a 3 s render, found by tools/aqua-feel); 0.45 keeps the same signal at 0.675, about -3.4 dBFS.
|
||||||
|
|
||||||
patch
|
patch
|
||||||
gain=1.4
|
gain=0.45
|
||||||
soft_clip=false
|
soft_clip=false
|
||||||
|
|
||||||
opgraph
|
opgraph
|
||||||
|
|
|
||||||
30
patches/thruster/thruster.dsp
Normal file
30
patches/thruster/thruster.dsp
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// thruster: sustained, heavy, brittle-hull thruster with an RPM control.
|
||||||
|
// Compile as Faust source (AquaSynthPatchCompiler.CompileSource); it is not a .aqua script.
|
||||||
|
// Control: rpm (300..6000). Rotor tone = 28 Hz + rpm*0.025 (about 36..180 Hz); three blades chop the rasp at 3x that.
|
||||||
|
declare name "thruster";
|
||||||
|
import("stdfaust.lib");
|
||||||
|
|
||||||
|
rpm = hslider("rpm", 1200, 300, 6000, 1) : si.smooth(ba.tau2pole(0.2));
|
||||||
|
load = (rpm - 300) / 5700;
|
||||||
|
shaft = 28 + rpm * 0.025;
|
||||||
|
|
||||||
|
// effort: slow random wander of the shaft speed
|
||||||
|
wander = no.noise : fi.lowpass(2, 5) : *(0.35);
|
||||||
|
f0 = shaft * (1 + 0.04 * wander);
|
||||||
|
|
||||||
|
// low rumble: saw at shaft speed plus a square a sub-octave down, low-passed
|
||||||
|
// the low-pass opens up as load falls, so at low rpm the rotor tone reaches the 150-300 Hz range a small speaker can show
|
||||||
|
rumble = (os.sawtooth(f0) + 0.7 * os.square(f0 * 0.5)) : fi.lowpass(3, 60 + 2.5 * shaft + 140 * (1 - load));
|
||||||
|
|
||||||
|
// blade pass: rasp noise chopped at 3x shaft, band tracks rpm
|
||||||
|
chop = 0.5 + 0.5 * os.osc(f0 * 3) ^ 2;
|
||||||
|
rasp = no.noise : fi.resonbp(700 + rpm * 0.6, 1.5, 1) : *(chop);
|
||||||
|
|
||||||
|
// brittle hull: high-Q modes rattled by noise, louder with load (a 1.5% floor at idle, so low rpm is tone-led); tanh on the rattle alone keeps ring-up spikes from setting the peak
|
||||||
|
mode(f, q) = no.noise : fi.resonbp(f, q, 1);
|
||||||
|
rattle = (mode(173, 20) * (0.3 + 0.7 * load) + 0.8 * mode(311, 18) + 0.6 * mode(467, 16) + 0.4 * mode(1130, 14)) * (0.015 + 0.985 * load ^ 2) * 3 : *(4) : ma.tanh : /(4);
|
||||||
|
|
||||||
|
mix = rumble * (0.5 + 0.5 * load) + rasp * (0.05 + 1.75 * load) + rattle;
|
||||||
|
// drive falls only gently with load so tanh stays out of saturation while high rpm still ends up louder than low rpm
|
||||||
|
// final 0.425 trim keeps peak under 0.4 at rpm 4800
|
||||||
|
process = mix * (0.4 + 0.6 * load) * (0.9 - 0.5 * load) : ma.tanh : *(0.425);
|
||||||
1
spikes/faust-aot-thruster/.gitignore
vendored
Normal file
1
spikes/faust-aot-thruster/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
build/
|
||||||
50
spikes/faust-aot-thruster/README.md
Normal file
50
spikes/faust-aot-thruster/README.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# Faust AOT spike: thruster v5 as a native lib (Linux)
|
||||||
|
|
||||||
|
Spike for `docs/faust-unity-player-scoping.md` path (a). One patch, `patches/thruster/thruster.dsp`
|
||||||
|
(copied here as `thruster.dsp`; re-copy if the patch changes), compiled by the `faust` CLI to C++ and
|
||||||
|
wrapped behind a C ABI. Not wired into the solution or into Unity.
|
||||||
|
|
||||||
|
Limitation: AOT fixes the patch set at build time, so player-authored patches cannot run this way.
|
||||||
|
|
||||||
|
- `aqs_thruster.h`: the C ABI. `rpm` enters only via `aqs_thruster_set_rpm`: bounds are read from the
|
||||||
|
compiled patch's `hslider` (300..6000, default 1200); out-of-range and +-inf clamp to the nearest bound
|
||||||
|
(`AQS_PARAM_CLAMPED`), NaN applies the default (`AQS_PARAM_NAN`); both are counted and returned.
|
||||||
|
`aqs_thruster_process` rejects NULL, negative or >`AQS_MAX_BLOCK` frames instead of truncating.
|
||||||
|
- `test_thruster.c`: plain-C behaviour test (bounds, clamp, NaN, inf, hostile values give finite bounded
|
||||||
|
output, rpm actually changes the output, instances are deterministic). A flipped clamp bound fails it.
|
||||||
|
- `bench_thruster.c`: per-block `process` time at 48 kHz / 512 frames against the 10.667 ms deadline; prints one `OVERRUN` line
|
||||||
|
per block over the deadline with wall-clock timestamp, wall ms and thread CPU ms.
|
||||||
|
- `run.sh`: generate, build, test, bench. `FAUST`, `FAUST_PREFIX` (needs `include/faust`), `FAUST_LIBS`
|
||||||
|
(needs `stdfaust.lib`), `BLOCKS`, `RATE`, `FRAMES`, `CORE` (pin the bench to one cpu via `taskset`), `NOOP=1` (time a busy-wait
|
||||||
|
instead of the patch) override defaults. Exit status is test status; timing
|
||||||
|
is reported, not gated.
|
||||||
|
|
||||||
|
**Scope: this measures a bare C loop.** `bench_thruster` calls `aqs_thruster_process` back to back on one
|
||||||
|
Linux thread. It shows the patch is cheap, not that a Unity player meets its deadline (no Unity audio
|
||||||
|
thread, P/Invoke, IL2CPP, mixer or Windows scheduling in it).
|
||||||
|
|
||||||
|
Measured 2026-11-06/07, Faust 2.90.20, AMD EPYC-Genoa, shared Linux host (load average ~8 on 16 cores):
|
||||||
|
median 0.052 ms (0.49% of the 10.667 ms deadline), p99 0.08-0.16 ms (~1-1.5%). Cheap on this host:
|
||||||
|
median 0.052 ms, p99 under 0.16 ms. The host's tail is not judgeable (see below).
|
||||||
|
|
||||||
|
**Max block time: UNEXPLAINED. It is not a pass.** Max was 10.8-17.1 ms, with 1-5 of 20000 blocks over
|
||||||
|
the deadline per run (400000-block `process`: 74 over in one run, core 3; see the control below). What the instrumented runs (`run.sh`, `CORE=<cpu>` pins with
|
||||||
|
`taskset`; every overrun prints a `wall_unix` timestamp, wall ms and thread CPU ms) do show:
|
||||||
|
|
||||||
|
- Not per-block DSP cost: `NOOP=1` times a 0.05 ms busy-wait instead of `process`, and stalls about the
|
||||||
|
same way (core 3, 400000 blocks: 74 overruns with `process`, 67 with the busy-wait, both ~11 ms; counts
|
||||||
|
differ by ~10%, no run-to-run spread measured for the control).
|
||||||
|
- Not a DSP-state pattern: overrun block indices differ every run and are not tied to the rpm sweep.
|
||||||
|
- Periodic in wall time, per core: pinned to core 3, overruns recur every ~0.3-0.4 s (mostly); pinned
|
||||||
|
to core 9, every ~1-2 s (17 in 400000 blocks). Cause of the periodicity untested (not shown to be a timer). Unpinned runs get 1-5 per 20000.
|
||||||
|
- The thread's CPU time over a stalled block is about equal to its wall time (e.g. 10.84 vs 10.85 ms),
|
||||||
|
so the stall is charged to the thread rather than showing as time off-CPU. An ordinary guest
|
||||||
|
run-queue preemption would be expected to show off-CPU time; this does not. The host (vCPU steal) is
|
||||||
|
left open. The earlier claim that these were scheduler preemption is withdrawn, not refuted.
|
||||||
|
|
||||||
|
Cause not identified (candidates: hypervisor/container CPU accounting, per-core host timer or housekeeping
|
||||||
|
work, SMI; none tested). Whether the player would see it is unknown. A quiet, realtime-priority or
|
||||||
|
isolated-core host is needed before the max can be called anything.
|
||||||
|
|
||||||
|
**Unverified:** Windows (MSVC build, `__declspec(dllexport)`), Unity P/Invoke, IL2CPP, the Unity audio
|
||||||
|
thread, any device. Nothing here ran inside Unity.
|
||||||
71
spikes/faust-aot-thruster/aqs_thruster.cpp
Normal file
71
spikes/faust-aot-thruster/aqs_thruster.cpp
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
#include "aqs_thruster.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
#include <faust/dsp/dsp.h>
|
||||||
|
#include <faust/gui/APIUI.h>
|
||||||
|
#include <faust/gui/meta.h>
|
||||||
|
#include "thruster_dsp.h"
|
||||||
|
|
||||||
|
struct aqs_thruster {
|
||||||
|
ThrusterDsp dsp;
|
||||||
|
APIUI ui;
|
||||||
|
float lo, hi, init, applied;
|
||||||
|
unsigned clamps = 0, nans = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
aqs_thruster* aqs_thruster_create(int sample_rate) {
|
||||||
|
if (sample_rate < 8000 || sample_rate > 384000) return nullptr;
|
||||||
|
aqs_thruster* t = new (std::nothrow) aqs_thruster();
|
||||||
|
if (!t) return nullptr;
|
||||||
|
t->dsp.init(sample_rate);
|
||||||
|
t->dsp.buildUserInterface(&t->ui);
|
||||||
|
t->lo = t->ui.getParamMin(0);
|
||||||
|
t->hi = t->ui.getParamMax(0);
|
||||||
|
t->init = t->ui.getParamInit(0);
|
||||||
|
t->applied = t->init;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void aqs_thruster_destroy(aqs_thruster* t) { delete t; }
|
||||||
|
|
||||||
|
float aqs_thruster_rpm_min(const aqs_thruster* t) { return t->lo; }
|
||||||
|
float aqs_thruster_rpm_max(const aqs_thruster* t) { return t->hi; }
|
||||||
|
float aqs_thruster_rpm_default(const aqs_thruster* t) { return t->init; }
|
||||||
|
|
||||||
|
int aqs_thruster_set_rpm(aqs_thruster* t, float rpm) {
|
||||||
|
if (!t) return -1;
|
||||||
|
int status = AQS_PARAM_OK;
|
||||||
|
if (std::isnan(rpm)) {
|
||||||
|
rpm = t->init;
|
||||||
|
status = AQS_PARAM_NAN;
|
||||||
|
++t->nans;
|
||||||
|
} else if (rpm < t->lo) {
|
||||||
|
rpm = t->lo;
|
||||||
|
status = AQS_PARAM_CLAMPED;
|
||||||
|
++t->clamps;
|
||||||
|
} else if (rpm > t->hi) {
|
||||||
|
rpm = t->hi;
|
||||||
|
status = AQS_PARAM_CLAMPED;
|
||||||
|
++t->clamps;
|
||||||
|
}
|
||||||
|
t->applied = rpm;
|
||||||
|
t->ui.setParamValue(0, rpm);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
float aqs_thruster_rpm(const aqs_thruster* t) { return t->applied; }
|
||||||
|
unsigned aqs_thruster_clamp_count(const aqs_thruster* t) { return t->clamps; }
|
||||||
|
unsigned aqs_thruster_nan_count(const aqs_thruster* t) { return t->nans; }
|
||||||
|
|
||||||
|
int aqs_thruster_process(aqs_thruster* t, float* out, int frames) {
|
||||||
|
if (!t || !out || frames < 0 || frames > AQS_MAX_BLOCK) return -1;
|
||||||
|
float* outs[1] = {out};
|
||||||
|
t->dsp.compute(frames, nullptr, outs);
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
52
spikes/faust-aot-thruster/aqs_thruster.h
Normal file
52
spikes/faust-aot-thruster/aqs_thruster.h
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
/* C ABI over the AOT-compiled thruster (patches/thruster/thruster.dsp).
|
||||||
|
* The host never touches Faust types. rpm enters only through aqs_thruster_set_rpm,
|
||||||
|
* which enforces the bounds declared in the .dsp (hslider min/max/init). */
|
||||||
|
#ifndef AQS_THRUSTER_H
|
||||||
|
#define AQS_THRUSTER_H
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define AQS_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define AQS_API __attribute__((visibility("default")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct aqs_thruster aqs_thruster;
|
||||||
|
|
||||||
|
/* Result of a parameter write. The applied value is always inside [min,max]. */
|
||||||
|
enum {
|
||||||
|
AQS_PARAM_OK = 0, /* applied as given */
|
||||||
|
AQS_PARAM_CLAMPED = 1, /* out of range (including +-inf): applied the nearest bound */
|
||||||
|
AQS_PARAM_NAN = 2 /* NaN: applied the declared default */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Largest frame count one process call accepts; larger requests are rejected, not truncated. */
|
||||||
|
#define AQS_MAX_BLOCK 16384
|
||||||
|
|
||||||
|
AQS_API aqs_thruster* aqs_thruster_create(int sample_rate); /* NULL on bad rate (<8000 or >384000) or OOM */
|
||||||
|
AQS_API void aqs_thruster_destroy(aqs_thruster*);
|
||||||
|
|
||||||
|
/* Declared bounds, read from the compiled patch. */
|
||||||
|
AQS_API float aqs_thruster_rpm_min(const aqs_thruster*);
|
||||||
|
AQS_API float aqs_thruster_rpm_max(const aqs_thruster*);
|
||||||
|
AQS_API float aqs_thruster_rpm_default(const aqs_thruster*);
|
||||||
|
|
||||||
|
/* Returns AQS_PARAM_*; negative on NULL handle. */
|
||||||
|
AQS_API int aqs_thruster_set_rpm(aqs_thruster*, float rpm);
|
||||||
|
/* Value currently applied (post-clamp). */
|
||||||
|
AQS_API float aqs_thruster_rpm(const aqs_thruster*);
|
||||||
|
|
||||||
|
/* Counters since create, for the host to report: out-of-range and NaN writes. */
|
||||||
|
AQS_API unsigned aqs_thruster_clamp_count(const aqs_thruster*);
|
||||||
|
AQS_API unsigned aqs_thruster_nan_count(const aqs_thruster*);
|
||||||
|
|
||||||
|
/* Writes `frames` mono samples. Returns frames written, or -1 on NULL handle/out,
|
||||||
|
* frames < 0, or frames > AQS_MAX_BLOCK. No allocation, no locks. */
|
||||||
|
AQS_API int aqs_thruster_process(aqs_thruster*, float* out, int frames);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
59
spikes/faust-aot-thruster/bench_thruster.c
Normal file
59
spikes/faust-aot-thruster/bench_thruster.c
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
/* Per-block callback time vs deadline. usage: bench_thruster [blocks] [rate] [frames] [noop]
|
||||||
|
noop: time a 0.05 ms busy-wait instead of process(), to tell host stalls from DSP cost */
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "aqs_thruster.h"
|
||||||
|
|
||||||
|
static int cmp(const void* a, const void* b) { double x = *(const double*)a, y = *(const double*)b; return (x > y) - (x < y); }
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
int blocks = argc > 1 ? atoi(argv[1]) : 20000;
|
||||||
|
int rate = argc > 2 ? atoi(argv[2]) : 48000;
|
||||||
|
int frames = argc > 3 ? atoi(argv[3]) : 512;
|
||||||
|
int noop = argc > 4;
|
||||||
|
aqs_thruster* t = aqs_thruster_create(rate);
|
||||||
|
if (!t || blocks < 100) return 2;
|
||||||
|
float* out = malloc(sizeof(float) * frames);
|
||||||
|
double* ms = malloc(sizeof(double) * blocks);
|
||||||
|
double* cpu = malloc(sizeof(double) * blocks); /* thread CPU time per block: a large wall-cpu gap means time off-CPU; no gap means the stall is charged to the thread */
|
||||||
|
double* wall = malloc(sizeof(double) * blocks); /* CLOCK_REALTIME at block end, seconds, for lining up with host load */
|
||||||
|
const int warm = 200;
|
||||||
|
for (int i = 0; i < warm + blocks; ++i) {
|
||||||
|
/* sweep rpm across its whole range so the smoother and filters keep moving */
|
||||||
|
aqs_thruster_set_rpm(t, 300.f + 5700.f * (0.5f + 0.5f * (float)((i % 997) - 498) / 498.f));
|
||||||
|
struct timespec a, b, ca, cb, w;
|
||||||
|
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ca);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &a);
|
||||||
|
if (noop) { struct timespec s; do clock_gettime(CLOCK_MONOTONIC, &s); while ((s.tv_sec - a.tv_sec) * 1e9 + (s.tv_nsec - a.tv_nsec) < 5e4); }
|
||||||
|
else aqs_thruster_process(t, out, frames);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &b);
|
||||||
|
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cb);
|
||||||
|
clock_gettime(CLOCK_REALTIME, &w);
|
||||||
|
if (i >= warm) {
|
||||||
|
ms[i - warm] = (b.tv_sec - a.tv_sec) * 1e3 + (b.tv_nsec - a.tv_nsec) / 1e6;
|
||||||
|
cpu[i - warm] = (cb.tv_sec - ca.tv_sec) * 1e3 + (cb.tv_nsec - ca.tv_nsec) / 1e6;
|
||||||
|
wall[i - warm] = w.tv_sec + w.tv_nsec / 1e9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double deadline = 1e3 * frames / rate, sum = 0;
|
||||||
|
int over = 0;
|
||||||
|
printf("start_cpu=%d\n", sched_getcpu());
|
||||||
|
for (int i = 0; i < blocks; ++i) {
|
||||||
|
sum += ms[i];
|
||||||
|
if (ms[i] > deadline) {
|
||||||
|
++over;
|
||||||
|
printf("OVERRUN block=%d wall_unix=%.6f wall_ms=%.4f thread_cpu_ms=%.4f\n", i, wall[i], ms[i], cpu[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qsort(ms, blocks, sizeof *ms, cmp);
|
||||||
|
double med = ms[blocks / 2], p99 = ms[(int)(blocks * 0.99)], mx = ms[blocks - 1];
|
||||||
|
printf("rate=%d frames=%d blocks=%d deadline_ms=%.4f\n", rate, frames, blocks, deadline);
|
||||||
|
printf("median_ms=%.4f p99_ms=%.4f max_ms=%.4f mean_ms=%.4f\n", med, p99, mx, sum / blocks);
|
||||||
|
printf("median_pct_deadline=%.2f p99_pct_deadline=%.2f max_pct_deadline=%.2f over_deadline=%d\n",
|
||||||
|
100 * med / deadline, 100 * p99 / deadline, 100 * mx / deadline, over);
|
||||||
|
free(out); free(ms); free(cpu); free(wall); aqs_thruster_destroy(t);
|
||||||
|
return 0; /* timing is reported, not gated: a shared host stalls */
|
||||||
|
}
|
||||||
27
spikes/faust-aot-thruster/run.sh
Executable file
27
spikes/faust-aot-thruster/run.sh
Executable file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build the AOT thruster spike, run the boundary test, then the per-block timing report.
|
||||||
|
# Linux only. usage: run.sh [--bench-only] ; env: FAUST (faust CLI), FAUST_PREFIX (has include/faust),
|
||||||
|
# FAUST_LIBS (dir with stdfaust.lib), CORE (cpu to pin the bench to via taskset; default: unpinned), OUT (build dir), BLOCKS, RATE, FRAMES.
|
||||||
|
set -euo pipefail
|
||||||
|
here=$(cd "$(dirname "$0")" && pwd)
|
||||||
|
FAUST=${FAUST:-faust}
|
||||||
|
FAUST_PREFIX=${FAUST_PREFIX:-$(dirname "$(dirname "$(command -v "$FAUST")")")}
|
||||||
|
FAUST_LIBS=${FAUST_LIBS:-$FAUST_PREFIX/share/faust}
|
||||||
|
OUT=${OUT:-$here/build}
|
||||||
|
mkdir -p "$OUT"
|
||||||
|
[ -f "$FAUST_LIBS/stdfaust.lib" ] || { echo "no stdfaust.lib in FAUST_LIBS=$FAUST_LIBS" >&2; exit 2; }
|
||||||
|
"$FAUST" -lang cpp -cn ThrusterDsp -I "$FAUST_LIBS" "$here/thruster.dsp" -o "$OUT/thruster_dsp.h"
|
||||||
|
"$FAUST" --version | head -1
|
||||||
|
CXXF="-O2 -std=c++17 -fvisibility=hidden -I$OUT -I$here -I$FAUST_PREFIX/include"
|
||||||
|
g++ $CXXF -shared -fPIC -o "$OUT/libaqs_thruster.so" "$here/aqs_thruster.cpp"
|
||||||
|
gcc -O2 -I"$here" -o "$OUT/test_thruster" "$here/test_thruster.c" -L"$OUT" -laqs_thruster -lm -Wl,-rpath,"$OUT"
|
||||||
|
gcc -O2 -I"$here" -o "$OUT/bench_thruster" "$here/bench_thruster.c" -L"$OUT" -laqs_thruster -lm -Wl,-rpath,"$OUT"
|
||||||
|
"$OUT/test_thruster"
|
||||||
|
echo "--- timing (Linux $(uname -r), $(grep -m1 'model name' /proc/cpuinfo | cut -d: -f2 | xargs)); Windows/Unity: unverified"
|
||||||
|
PIN=()
|
||||||
|
if [ -n "${CORE:-}" ]; then
|
||||||
|
command -v taskset >/dev/null && PIN=(taskset -c "$CORE") || echo "CORE set but taskset missing: running unpinned" >&2
|
||||||
|
fi
|
||||||
|
echo "bench start $(date -u +%FT%T.%NZ) pin=${PIN[*]:-none} loadavg_before=$(cut -d' ' -f1-3 /proc/loadavg)"
|
||||||
|
"${PIN[@]}" "$OUT/bench_thruster" "${BLOCKS:-20000}" "${RATE:-48000}" "${FRAMES:-512}" ${NOOP:+noop}
|
||||||
|
echo "bench end $(date -u +%FT%T.%NZ) loadavg_after=$(cut -d' ' -f1-3 /proc/loadavg)"
|
||||||
94
spikes/faust-aot-thruster/test_thruster.c
Normal file
94
spikes/faust-aot-thruster/test_thruster.c
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
/* Behaviour test for the bounded-parameter boundary. Plain C: proves the ABI is callable without C++. */
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "aqs_thruster.h"
|
||||||
|
|
||||||
|
static int fails;
|
||||||
|
#define CHECK(c) do { if (!(c)) { printf("FAIL line %d: %s\n", __LINE__, #c); ++fails; } } while (0)
|
||||||
|
|
||||||
|
static float peak_after(aqs_thruster* t, float rpm, int* finite) {
|
||||||
|
static float buf[512];
|
||||||
|
float pk = 0;
|
||||||
|
*finite = 1;
|
||||||
|
aqs_thruster_set_rpm(t, rpm);
|
||||||
|
for (int b = 0; b < 200; ++b) { /* ~2 s: lets the 0.2 s rpm smoother settle */
|
||||||
|
CHECK(aqs_thruster_process(t, buf, 512) == 512);
|
||||||
|
for (int i = 0; i < 512; ++i) {
|
||||||
|
if (!isfinite(buf[i])) *finite = 0;
|
||||||
|
if (fabsf(buf[i]) > pk) pk = fabsf(buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pk;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
CHECK(aqs_thruster_create(100) == NULL);
|
||||||
|
CHECK(aqs_thruster_create(1000000) == NULL);
|
||||||
|
aqs_thruster* t = aqs_thruster_create(48000);
|
||||||
|
CHECK(t != NULL);
|
||||||
|
if (!t) return 1;
|
||||||
|
|
||||||
|
/* bounds come from the .dsp hslider("rpm", 1200, 300, 6000, 1) */
|
||||||
|
CHECK(aqs_thruster_rpm_min(t) == 300.f);
|
||||||
|
CHECK(aqs_thruster_rpm_max(t) == 6000.f);
|
||||||
|
CHECK(aqs_thruster_rpm_default(t) == 1200.f);
|
||||||
|
CHECK(aqs_thruster_rpm(t) == 1200.f);
|
||||||
|
|
||||||
|
CHECK(aqs_thruster_set_rpm(t, 4800.f) == AQS_PARAM_OK && aqs_thruster_rpm(t) == 4800.f);
|
||||||
|
CHECK(aqs_thruster_set_rpm(t, 300.f) == AQS_PARAM_OK && aqs_thruster_rpm(t) == 300.f);
|
||||||
|
CHECK(aqs_thruster_set_rpm(t, 6000.f) == AQS_PARAM_OK && aqs_thruster_rpm(t) == 6000.f);
|
||||||
|
CHECK(aqs_thruster_clamp_count(t) == 0 && aqs_thruster_nan_count(t) == 0);
|
||||||
|
|
||||||
|
CHECK(aqs_thruster_set_rpm(t, 299.9f) == AQS_PARAM_CLAMPED && aqs_thruster_rpm(t) == 300.f);
|
||||||
|
CHECK(aqs_thruster_set_rpm(t, -50.f) == AQS_PARAM_CLAMPED && aqs_thruster_rpm(t) == 300.f);
|
||||||
|
CHECK(aqs_thruster_set_rpm(t, 6000.1f) == AQS_PARAM_CLAMPED && aqs_thruster_rpm(t) == 6000.f);
|
||||||
|
CHECK(aqs_thruster_set_rpm(t, INFINITY) == AQS_PARAM_CLAMPED && aqs_thruster_rpm(t) == 6000.f);
|
||||||
|
CHECK(aqs_thruster_set_rpm(t, -INFINITY) == AQS_PARAM_CLAMPED && aqs_thruster_rpm(t) == 300.f);
|
||||||
|
CHECK(aqs_thruster_clamp_count(t) == 5);
|
||||||
|
|
||||||
|
aqs_thruster_set_rpm(t, 5000.f);
|
||||||
|
CHECK(aqs_thruster_set_rpm(t, NAN) == AQS_PARAM_NAN && aqs_thruster_rpm(t) == 1200.f);
|
||||||
|
CHECK(aqs_thruster_nan_count(t) == 1);
|
||||||
|
|
||||||
|
CHECK(aqs_thruster_set_rpm(NULL, 1000.f) < 0);
|
||||||
|
float buf[16];
|
||||||
|
CHECK(aqs_thruster_process(t, buf, -1) == -1);
|
||||||
|
CHECK(aqs_thruster_process(t, buf, AQS_MAX_BLOCK + 1) == -1);
|
||||||
|
CHECK(aqs_thruster_process(t, NULL, 16) == -1);
|
||||||
|
CHECK(aqs_thruster_process(NULL, buf, 16) == -1);
|
||||||
|
CHECK(aqs_thruster_process(t, buf, 0) == 0);
|
||||||
|
|
||||||
|
/* hostile inputs never reach the DSP: output stays finite and under the patch's own peak bound */
|
||||||
|
float hostile[] = {NAN, INFINITY, -INFINITY, 1e30f, -1e30f, 0.f};
|
||||||
|
for (unsigned i = 0; i < sizeof hostile / sizeof *hostile; ++i) {
|
||||||
|
aqs_thruster* h = aqs_thruster_create(48000);
|
||||||
|
int fin;
|
||||||
|
float pk = peak_after(h, hostile[i], &fin);
|
||||||
|
CHECK(fin);
|
||||||
|
CHECK(pk > 0.01f && pk < 1.0f);
|
||||||
|
aqs_thruster_destroy(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the patch is audibly rpm-dependent and loud at high rpm (guards against a stuck parameter) */
|
||||||
|
int fin;
|
||||||
|
aqs_thruster* lo = aqs_thruster_create(48000);
|
||||||
|
aqs_thruster* hi = aqs_thruster_create(48000);
|
||||||
|
float plo = peak_after(lo, 300.f, &fin), phi = peak_after(hi, 4800.f, &fin);
|
||||||
|
CHECK(phi > plo);
|
||||||
|
CHECK(phi < 0.45f); /* patch comment: peak under 0.4 at rpm 4800, small margin */
|
||||||
|
aqs_thruster_destroy(lo); aqs_thruster_destroy(hi);
|
||||||
|
|
||||||
|
/* same rpm, same sample rate: two instances render identical bytes (noise is seeded) */
|
||||||
|
aqs_thruster* a = aqs_thruster_create(48000);
|
||||||
|
aqs_thruster* b = aqs_thruster_create(48000);
|
||||||
|
static float xa[2048], xb[2048];
|
||||||
|
aqs_thruster_set_rpm(a, 2000.f); aqs_thruster_set_rpm(b, 2000.f);
|
||||||
|
aqs_thruster_process(a, xa, 2048); aqs_thruster_process(b, xb, 2048);
|
||||||
|
CHECK(memcmp(xa, xb, sizeof xa) == 0);
|
||||||
|
aqs_thruster_destroy(a); aqs_thruster_destroy(b);
|
||||||
|
|
||||||
|
aqs_thruster_destroy(t);
|
||||||
|
printf(fails ? "FAILED (%d)\n" : "ok\n", fails);
|
||||||
|
return fails != 0;
|
||||||
|
}
|
||||||
30
spikes/faust-aot-thruster/thruster.dsp
Normal file
30
spikes/faust-aot-thruster/thruster.dsp
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// thruster: sustained, heavy, brittle-hull thruster with an RPM control.
|
||||||
|
// Compile as Faust source (AquaSynthPatchCompiler.CompileSource); it is not a .aqua script.
|
||||||
|
// Control: rpm (300..6000). Rotor tone = 28 Hz + rpm*0.025 (about 36..180 Hz); three blades chop the rasp at 3x that.
|
||||||
|
declare name "thruster";
|
||||||
|
import("stdfaust.lib");
|
||||||
|
|
||||||
|
rpm = hslider("rpm", 1200, 300, 6000, 1) : si.smooth(ba.tau2pole(0.2));
|
||||||
|
load = (rpm - 300) / 5700;
|
||||||
|
shaft = 28 + rpm * 0.025;
|
||||||
|
|
||||||
|
// effort: slow random wander of the shaft speed
|
||||||
|
wander = no.noise : fi.lowpass(2, 5) : *(0.35);
|
||||||
|
f0 = shaft * (1 + 0.04 * wander);
|
||||||
|
|
||||||
|
// low rumble: saw at shaft speed plus a square a sub-octave down, low-passed
|
||||||
|
// the low-pass opens up as load falls, so at low rpm the rotor tone reaches the 150-300 Hz range a small speaker can show
|
||||||
|
rumble = (os.sawtooth(f0) + 0.7 * os.square(f0 * 0.5)) : fi.lowpass(3, 60 + 2.5 * shaft + 140 * (1 - load));
|
||||||
|
|
||||||
|
// blade pass: rasp noise chopped at 3x shaft, band tracks rpm
|
||||||
|
chop = 0.5 + 0.5 * os.osc(f0 * 3) ^ 2;
|
||||||
|
rasp = no.noise : fi.resonbp(700 + rpm * 0.6, 1.5, 1) : *(chop);
|
||||||
|
|
||||||
|
// brittle hull: high-Q modes rattled by noise, louder with load (a 1.5% floor at idle, so low rpm is tone-led); tanh on the rattle alone keeps ring-up spikes from setting the peak
|
||||||
|
mode(f, q) = no.noise : fi.resonbp(f, q, 1);
|
||||||
|
rattle = (mode(173, 20) * (0.3 + 0.7 * load) + 0.8 * mode(311, 18) + 0.6 * mode(467, 16) + 0.4 * mode(1130, 14)) * (0.015 + 0.985 * load ^ 2) * 3 : *(4) : ma.tanh : /(4);
|
||||||
|
|
||||||
|
mix = rumble * (0.5 + 0.5 * load) + rasp * (0.05 + 1.75 * load) + rattle;
|
||||||
|
// drive falls only gently with load so tanh stays out of saturation while high rpm still ends up louder than low rpm
|
||||||
|
// final 0.425 trim keeps peak under 0.4 at rpm 4800
|
||||||
|
process = mix * (0.4 + 0.6 * load) * (0.9 - 0.5 * load) : ma.tanh : *(0.425);
|
||||||
|
|
@ -108,7 +108,7 @@ public static class IpaGestureExperiment
|
||||||
throw new ArgumentException("At least one experiment variant is required.", nameof(variants));
|
throw new ArgumentException("At least one experiment variant is required.", nameof(variants));
|
||||||
}
|
}
|
||||||
|
|
||||||
var roundDirectory = Path.Combine(rootDirectory, SafeFileName(roundId));
|
var roundDirectory = Path.Combine(rootDirectory, StoreFileName.From(roundId));
|
||||||
var candidateDirectory = Path.Combine(roundDirectory, "candidates");
|
var candidateDirectory = Path.Combine(roundDirectory, "candidates");
|
||||||
var timelineDirectory = Path.Combine(roundDirectory, "timelines");
|
var timelineDirectory = Path.Combine(roundDirectory, "timelines");
|
||||||
Directory.CreateDirectory(candidateDirectory);
|
Directory.CreateDirectory(candidateDirectory);
|
||||||
|
|
@ -122,7 +122,7 @@ public static class IpaGestureExperiment
|
||||||
{
|
{
|
||||||
foreach (var variant in variantList)
|
foreach (var variant in variantList)
|
||||||
{
|
{
|
||||||
var candidateId = SafeFileName($"{target.Id}-{variant.Id}");
|
var candidateId = StoreFileName.From($"{StoreFileName.From(target.Id)}/{StoreFileName.From(variant.Id)}");
|
||||||
var script = BuildCandidateScript(target, variant);
|
var script = BuildCandidateScript(target, variant);
|
||||||
var scriptPath = Path.Combine(candidateDirectory, $"{candidateId}.aqua");
|
var scriptPath = Path.Combine(candidateDirectory, $"{candidateId}.aqua");
|
||||||
File.WriteAllText(scriptPath, script, Encoding.UTF8);
|
File.WriteAllText(scriptPath, script, Encoding.UTF8);
|
||||||
|
|
@ -705,13 +705,6 @@ public static class IpaGestureExperiment
|
||||||
private static string SafeDslName(string value) =>
|
private static string SafeDslName(string value) =>
|
||||||
new(value.Select(character => char.IsLetterOrDigit(character) ? character : '_').ToArray());
|
new(value.Select(character => char.IsLetterOrDigit(character) ? character : '_').ToArray());
|
||||||
|
|
||||||
private static string SafeFileName(string value)
|
|
||||||
{
|
|
||||||
var invalid = Path.GetInvalidFileNameChars().ToHashSet();
|
|
||||||
var safe = new string(value.Select(character => invalid.Contains(character) || char.IsWhiteSpace(character) ? '-' : character).ToArray());
|
|
||||||
return safe.Length == 0 ? "candidate" : safe;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string Format(float value) =>
|
private static string Format(float value) =>
|
||||||
value.ToString("0.######", CultureInfo.InvariantCulture);
|
value.ToString("0.######", CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
|
|
||||||
95
src/AquaSynth.Core/StoreFileName.cs
Normal file
95
src/AquaSynth.Core/StoreFileName.cs
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace AquaSynth.Dsl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The one mapping from a caller-chosen id to a single file-name component in a store.
|
||||||
|
/// An id passes through unchanged only if it is already a fixed point: ASCII [A-Za-z0-9._-],
|
||||||
|
/// all lowercase (stores live on case-insensitive filesystems), at most <see cref="MaxStem"/> long,
|
||||||
|
/// no trailing dot, not a Windows device stem (CON, NUL, COM1, ...), and not ending in a
|
||||||
|
/// "-" plus 16 hex digits. Every other id is sanitised and suffixed with "-" plus the first
|
||||||
|
/// 16 hex of SHA-256 of the original id. Passed-through names never end in the hash shape and
|
||||||
|
/// rewritten names always do, so the two sets cannot meet; distinct ids stay distinct up to a 64-bit
|
||||||
|
/// hash. The result is never a path: no separators, never "." or "..".
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Not handled: symlinks and junctions already inside a store are followed, and Windows 8.3 short names
|
||||||
|
/// were not verified. An id with an uppercase letter is rewritten, so a batch directory named for such
|
||||||
|
/// an id gains a hash suffix.
|
||||||
|
/// </remarks>
|
||||||
|
public static class StoreFileName
|
||||||
|
{
|
||||||
|
private const int MaxStem = 100;
|
||||||
|
private const int HashLength = 16;
|
||||||
|
|
||||||
|
public static string From(string id)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(id);
|
||||||
|
if (id.Length == 0 || id.All(c => c == '.'))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Id '{id}' cannot name a store file.", nameof(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new StringBuilder(Math.Min(id.Length, MaxStem));
|
||||||
|
foreach (var c in id.Take(MaxStem))
|
||||||
|
{
|
||||||
|
builder.Append(c is >= 'a' and <= 'z' or >= 'A' and <= 'Z' or >= '0' and <= '9' or '-' or '_' or '.' ? c : '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
var safe = builder.ToString();
|
||||||
|
if (IsFixedPoint(id, safe))
|
||||||
|
{
|
||||||
|
return safe;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "nul.x-<hash>" still has the device stem "nul"; the underscore moves the whole name off it.
|
||||||
|
if (IsDeviceStem(safe))
|
||||||
|
{
|
||||||
|
safe = "_" + safe;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(id)))[..HashLength].ToLowerInvariant();
|
||||||
|
return $"{safe}-{hash}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>A child of <paramref name="root"/> named for an external id; the result never leaves the root.</summary>
|
||||||
|
public static string Combine(string root, string id) => Path.Combine(root, From(id));
|
||||||
|
|
||||||
|
private static bool IsFixedPoint(string id, string safe)
|
||||||
|
{
|
||||||
|
if (safe != id || id.EndsWith('.') || id.Any(char.IsAsciiLetterUpper) || IsDeviceStem(id) || EndsWithHashShape(id))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows resolves CON, PRN, AUX, NUL, COM0-9, LPT0-9 as devices whatever the extension.
|
||||||
|
private static bool IsDeviceStem(string id)
|
||||||
|
{
|
||||||
|
var dot = id.IndexOf('.');
|
||||||
|
var stem = (dot < 0 ? id : id[..dot]).TrimEnd(' ');
|
||||||
|
return stem.Length switch
|
||||||
|
{
|
||||||
|
3 => stem.Equals("con", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| stem.Equals("prn", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| stem.Equals("aux", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| stem.Equals("nul", StringComparison.OrdinalIgnoreCase),
|
||||||
|
4 => char.IsAsciiDigit(stem[3])
|
||||||
|
&& (stem.StartsWith("com", StringComparison.OrdinalIgnoreCase) || stem.StartsWith("lpt", StringComparison.OrdinalIgnoreCase)),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool EndsWithHashShape(string id)
|
||||||
|
{
|
||||||
|
if (id.Length <= HashLength || id[^(HashLength + 1)] != '-')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return id[^HashLength..].All(c => c is >= '0' and <= '9' or >= 'a' and <= 'f');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -556,9 +556,9 @@ public sealed class AquaSynthDaemonService : IDisposable
|
||||||
session.Stream.ProcessBlock([], outputs, command.FrameCount, session.Controls);
|
session.Stream.ProcessBlock([], outputs, command.FrameCount, session.Controls);
|
||||||
var mono = MixMono(outputs, command.FrameCount, command.Gain * session.Gain);
|
var mono = MixMono(outputs, command.FrameCount, command.Gain * session.Gain);
|
||||||
var blockId = BlockId(command.CommandId);
|
var blockId = BlockId(command.CommandId);
|
||||||
var blockDir = Path.Combine(options.StoreRoot, "live", SafeFileName(command.SessionId));
|
var blockDir = Path.Combine(options.StoreRoot, "live", StoreFileName.From(command.SessionId));
|
||||||
Directory.CreateDirectory(blockDir);
|
Directory.CreateDirectory(blockDir);
|
||||||
var floatPath = Path.Combine(blockDir, $"{SafeFileName(blockId)}.f32");
|
var floatPath = Path.Combine(blockDir, $"{StoreFileName.From(blockId)}.f32");
|
||||||
await WriteFloat32Async(floatPath, mono).ConfigureAwait(false);
|
await WriteFloat32Async(floatPath, mono).ConfigureAwait(false);
|
||||||
|
|
||||||
var sequence = session.NextSequence();
|
var sequence = session.NextSequence();
|
||||||
|
|
@ -661,8 +661,9 @@ public sealed class AquaSynthDaemonService : IDisposable
|
||||||
var renderId = RenderId(command.CommandId);
|
var renderId = RenderId(command.CommandId);
|
||||||
var sampleDir = Path.Combine(options.StoreRoot, "samples");
|
var sampleDir = Path.Combine(options.StoreRoot, "samples");
|
||||||
Directory.CreateDirectory(sampleDir);
|
Directory.CreateDirectory(sampleDir);
|
||||||
var floatPath = Path.Combine(sampleDir, $"{renderId}.f32");
|
var sampleStem = StoreFileName.From(renderId);
|
||||||
var wavPath = Path.Combine(sampleDir, $"{renderId}.wav");
|
var floatPath = Path.Combine(sampleDir, $"{sampleStem}.f32");
|
||||||
|
var wavPath = Path.Combine(sampleDir, $"{sampleStem}.wav");
|
||||||
await WriteFloat32Async(floatPath, samples).ConfigureAwait(false);
|
await WriteFloat32Async(floatPath, samples).ConfigureAwait(false);
|
||||||
await WriteWavAsync(wavPath, samples, patch.Manifest.SampleRate).ConfigureAwait(false);
|
await WriteWavAsync(wavPath, samples, patch.Manifest.SampleRate).ConfigureAwait(false);
|
||||||
|
|
||||||
|
|
@ -703,8 +704,8 @@ public sealed class AquaSynthDaemonService : IDisposable
|
||||||
|
|
||||||
var runId = $"{command.CommandId}-stream";
|
var runId = $"{command.CommandId}-stream";
|
||||||
var verseId = "aquasynth.instrument";
|
var verseId = "aquasynth.instrument";
|
||||||
var audioStreamId = $"aquasynth.audio.{SafeFileName(command.CommandId)}";
|
var audioStreamId = $"aquasynth.audio.{StoreFileName.From(command.CommandId)}";
|
||||||
var controlStreamId = $"aquasynth.controls.{SafeFileName(command.CommandId)}";
|
var controlStreamId = $"aquasynth.controls.{StoreFileName.From(command.CommandId)}";
|
||||||
|
|
||||||
if (!string.Equals(compile.Status, "succeeded", StringComparison.Ordinal))
|
if (!string.Equals(compile.Status, "succeeded", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
|
|
@ -741,7 +742,7 @@ public sealed class AquaSynthDaemonService : IDisposable
|
||||||
.GroupBy(frame => frame.Block)
|
.GroupBy(frame => frame.Block)
|
||||||
.ToDictionary(group => group.Key, group => NormalizeControls(group.Last().Controls), EqualityComparer<int>.Default);
|
.ToDictionary(group => group.Key, group => NormalizeControls(group.Last().Controls), EqualityComparer<int>.Default);
|
||||||
|
|
||||||
var streamDir = Path.Combine(options.StoreRoot, "streams", SafeFileName(runId));
|
var streamDir = Path.Combine(options.StoreRoot, "streams", StoreFileName.From(runId));
|
||||||
Directory.CreateDirectory(streamDir);
|
Directory.CreateDirectory(streamDir);
|
||||||
var packets = new List<AquaSynthAutomationPacketReceipt>();
|
var packets = new List<AquaSynthAutomationPacketReceipt>();
|
||||||
using (var stream = patch.CreateStreamingPatch())
|
using (var stream = patch.CreateStreamingPatch())
|
||||||
|
|
@ -918,7 +919,7 @@ public sealed class AquaSynthDaemonService : IDisposable
|
||||||
private async Task WriteReceiptAsync<T>(string family, string id, T document, bool global = false)
|
private async Task WriteReceiptAsync<T>(string family, string id, T document, bool global = false)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
var filePath = Path.Combine(options.StoreRoot, family, $"{SafeFileName(id)}.cc");
|
var filePath = Path.Combine(options.StoreRoot, family, $"{StoreFileName.From(id)}.cc");
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
|
Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
|
||||||
// Opening hydrates; a receipt file holds exactly one document, so everything else goes before the write.
|
// Opening hydrates; a receipt file holds exactly one document, so everything else goes before the write.
|
||||||
using var cache = CultCacheMessagePack.Create(filePath);
|
using var cache = CultCacheMessagePack.Create(filePath);
|
||||||
|
|
@ -1129,18 +1130,7 @@ public sealed class AquaSynthDaemonService : IDisposable
|
||||||
|
|
||||||
private static string BlockId(string commandId) => $"{commandId}-block";
|
private static string BlockId(string commandId) => $"{commandId}-block";
|
||||||
|
|
||||||
private static string SessionId(string patchId, string compileKey) => $"{SafeFileName(patchId)}-{compileKey[..Math.Min(12, compileKey.Length)]}";
|
private static string SessionId(string patchId, string compileKey) => $"{StoreFileName.From(patchId)}-{compileKey[..Math.Min(12, compileKey.Length)]}";
|
||||||
|
|
||||||
private static string SafeFileName(string value)
|
|
||||||
{
|
|
||||||
var builder = new StringBuilder(value.Length);
|
|
||||||
foreach (var c in value)
|
|
||||||
{
|
|
||||||
builder.Append(char.IsLetterOrDigit(c) || c is '-' or '_' or '.' ? c : '-');
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.ToString().Trim('-');
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ToArtifactUri(string path) => $"file:///{Path.GetFullPath(path).Replace('\\', '/')}";
|
private static string ToArtifactUri(string path) => $"file:///{Path.GetFullPath(path).Replace('\\', '/')}";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -222,12 +222,8 @@ public static class FaustCompiler
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(language), language, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(language), language, null)
|
||||||
};
|
};
|
||||||
|
|
||||||
private static string SafeArtifactName(string value)
|
private static string SafeArtifactName(string value) =>
|
||||||
{
|
string.IsNullOrWhiteSpace(value) ? "aquasynth_patch" : StoreFileName.From(value);
|
||||||
var sanitized = new string(value.Select(character =>
|
|
||||||
char.IsAsciiLetterOrDigit(character) || character is '-' or '_' ? character : '_').ToArray());
|
|
||||||
return string.IsNullOrWhiteSpace(sanitized) ? "aquasynth_patch" : sanitized;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<string?> FaustArchDirAsync(string faustPath, CancellationToken cancellationToken)
|
private static async Task<string?> FaustArchDirAsync(string faustPath, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ public static class IpaTrialOrchestrator
|
||||||
throw new ArgumentException("At least one IPA trial target set is required.", nameof(trialSets));
|
throw new ArgumentException("At least one IPA trial target set is required.", nameof(trialSets));
|
||||||
}
|
}
|
||||||
|
|
||||||
var batchDirectory = Path.Combine(artifactRoot, options.BatchId);
|
var batchDirectory = StoreFileName.Combine(artifactRoot, options.BatchId);
|
||||||
Directory.CreateDirectory(batchDirectory);
|
Directory.CreateDirectory(batchDirectory);
|
||||||
var storePath = Path.Combine(batchDirectory, options.TrialResultStoreFileName);
|
var storePath = Path.Combine(batchDirectory, options.TrialResultStoreFileName);
|
||||||
var referenceRenderer = new PinkTromboneReferenceRenderer();
|
var referenceRenderer = new PinkTromboneReferenceRenderer();
|
||||||
|
|
@ -156,7 +156,7 @@ public static class IpaTrialOrchestrator
|
||||||
var candidateDir = Path.Combine(round.RoundDirectory, "audio", candidate.Id);
|
var candidateDir = Path.Combine(round.RoundDirectory, "audio", candidate.Id);
|
||||||
Directory.CreateDirectory(candidateDir);
|
Directory.CreateDirectory(candidateDir);
|
||||||
var script = await File.ReadAllTextAsync(candidate.ScriptPath, cancellationToken).ConfigureAwait(false);
|
var script = await File.ReadAllTextAsync(candidate.ScriptPath, cancellationToken).ConfigureAwait(false);
|
||||||
var source = FaustEmitter.EmitScript(script, new FaustExportOptions(SafeName(candidate.Id))).Source;
|
var source = FaustEmitter.EmitScript(script, new FaustExportOptions(candidate.Id)).Source;
|
||||||
var dspPath = Path.Combine(candidateDir, "candidate.dsp");
|
var dspPath = Path.Combine(candidateDir, "candidate.dsp");
|
||||||
await File.WriteAllTextAsync(dspPath, source, cancellationToken).ConfigureAwait(false);
|
await File.WriteAllTextAsync(dspPath, source, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
|
@ -274,7 +274,7 @@ public static class IpaTrialOrchestrator
|
||||||
throw new ArgumentException("At least one IPA script candidate is required.", nameof(candidates));
|
throw new ArgumentException("At least one IPA script candidate is required.", nameof(candidates));
|
||||||
}
|
}
|
||||||
|
|
||||||
var batchDirectory = Path.Combine(artifactRoot, options.BatchId);
|
var batchDirectory = StoreFileName.Combine(artifactRoot, options.BatchId);
|
||||||
var candidateRoot = Path.Combine(batchDirectory, "agent-candidates");
|
var candidateRoot = Path.Combine(batchDirectory, "agent-candidates");
|
||||||
var timelineRoot = Path.Combine(batchDirectory, "agent-timelines");
|
var timelineRoot = Path.Combine(batchDirectory, "agent-timelines");
|
||||||
Directory.CreateDirectory(candidateRoot);
|
Directory.CreateDirectory(candidateRoot);
|
||||||
|
|
@ -298,7 +298,7 @@ public static class IpaTrialOrchestrator
|
||||||
|
|
||||||
var startedAt = DateTimeOffset.UtcNow;
|
var startedAt = DateTimeOffset.UtcNow;
|
||||||
var startedStamp = Stopwatch.GetTimestamp();
|
var startedStamp = Stopwatch.GetTimestamp();
|
||||||
var safeCandidateId = SafeName(candidate.CandidateId);
|
var safeCandidateId = StoreFileName.From(candidate.CandidateId);
|
||||||
var candidateDir = Path.Combine(candidateRoot, safeCandidateId);
|
var candidateDir = Path.Combine(candidateRoot, safeCandidateId);
|
||||||
Directory.CreateDirectory(candidateDir);
|
Directory.CreateDirectory(candidateDir);
|
||||||
var audioDir = Path.Combine(candidateDir, "audio");
|
var audioDir = Path.Combine(candidateDir, "audio");
|
||||||
|
|
@ -721,9 +721,6 @@ public static class IpaTrialOrchestrator
|
||||||
return samples;
|
return samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string SafeName(string value) =>
|
|
||||||
new(value.Select(character => char.IsLetterOrDigit(character) ? character : '_').ToArray());
|
|
||||||
|
|
||||||
private static string Escape(string value) =>
|
private static string Escape(string value) =>
|
||||||
value.Contains(',') || value.Contains('"') || value.Contains('\n') || value.Contains('\r')
|
value.Contains(',') || value.Contains('"') || value.Contains('\n') || value.Contains('\r')
|
||||||
? $"\"{value.Replace("\"", "\"\"", StringComparison.Ordinal)}\""
|
? $"\"{value.Replace("\"", "\"\"", StringComparison.Ordinal)}\""
|
||||||
|
|
|
||||||
|
|
@ -633,18 +633,10 @@ public sealed class AquaSynthNativeCompiler : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
Directory.CreateDirectory(outputRoot);
|
Directory.CreateDirectory(outputRoot);
|
||||||
var path = Path.Combine(outputRoot, $"{SafeFileName(patchId)}.dsp");
|
var path = Path.Combine(outputRoot, $"{StoreFileName.From(patchId)}.dsp");
|
||||||
File.WriteAllText(path, source);
|
File.WriteAllText(path, source);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string SafeFileName(string value)
|
|
||||||
{
|
|
||||||
var invalid = Path.GetInvalidFileNameChars().ToHashSet();
|
|
||||||
var chars = value.Select(ch => invalid.Contains(ch) ? '_' : ch).ToArray();
|
|
||||||
var name = new string(chars).Trim();
|
|
||||||
return string.IsNullOrWhiteSpace(name) ? "aquasynth_patch" : name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AquaSynthFaustToolchain
|
public static class AquaSynthFaustToolchain
|
||||||
|
|
@ -840,16 +832,32 @@ internal sealed class FaustNativeToolchain : IDisposable
|
||||||
|
|
||||||
public void SetControls(IntPtr dsp, IReadOnlyDictionary<string, float> controls, IReadOnlyDictionary<string, IntPtr> map)
|
public void SetControls(IntPtr dsp, IReadOnlyDictionary<string, float> controls, IReadOnlyDictionary<string, IntPtr> map)
|
||||||
{
|
{
|
||||||
foreach (var (path, value) in controls)
|
// Dictionary's struct enumerator avoids boxing one through the interface on every audio block.
|
||||||
|
if (controls is Dictionary<string, float> dictionary)
|
||||||
{
|
{
|
||||||
if (!map.TryGetValue(path, out var zone) &&
|
foreach (var (path, value) in dictionary)
|
||||||
!map.TryGetValue(path.TrimStart('/'), out zone))
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Faust control `{path}` was not exported by the compiled patch.", nameof(controls));
|
WriteControl(map, path, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Marshal.Copy([value], 0, zone, 1);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var (path, value) in controls)
|
||||||
|
{
|
||||||
|
WriteControl(map, path, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteControl(IReadOnlyDictionary<string, IntPtr> map, string path, float value)
|
||||||
|
{
|
||||||
|
if (!map.TryGetValue(path, out var zone) &&
|
||||||
|
!map.TryGetValue(path.TrimStart('/'), out zone))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Faust control `{path}` was not exported by the compiled patch.", "controls");
|
||||||
|
}
|
||||||
|
|
||||||
|
Marshal.WriteInt32(zone, BitConverter.SingleToInt32Bits(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Text.Json;
|
||||||
using AquaSynth.Dings;
|
using AquaSynth.Dings;
|
||||||
|
|
||||||
namespace AquaSynth.Dsl.Tests;
|
namespace AquaSynth.Dsl.Tests;
|
||||||
|
|
@ -32,4 +33,37 @@ public sealed class DingCatalogTests
|
||||||
foreach (var pair in dingEvent.Notes.Zip(dingEvent.Notes.Skip(1)))
|
foreach (var pair in dingEvent.Notes.Zip(dingEvent.Notes.Skip(1)))
|
||||||
Assert.InRange(pair.Second.DelayMilliseconds - pair.First.DelayMilliseconds, 180, 320);
|
Assert.InRange(pair.Second.DelayMilliseconds - pair.First.DelayMilliseconds, 180, 320);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tools/aqua-feel measures the lowered files in tools/aqua-feel/lowered-dings; regenerate them with
|
||||||
|
// `dotnet run --project tools/AquaLower -- --dings tools/aqua-feel/lowered-dings` when the catalog changes.
|
||||||
|
[Fact]
|
||||||
|
public void LoweredDingsFedToTheFeelMeterMatchTheCatalog()
|
||||||
|
{
|
||||||
|
var dir = Path.Combine(RepositoryRoot(), "tools", "aqua-feel", "lowered-dings");
|
||||||
|
foreach (var instrument in DingCatalog.Instruments.Values)
|
||||||
|
{
|
||||||
|
var expected = FaustEmitter.EmitScript(instrument.Script, new FaustExportOptions("dings_" + instrument.Id.Replace('-', '_'))).Source;
|
||||||
|
Assert.Equal(expected, File.ReadAllText(Path.Combine(dir, instrument.Id + ".dsp")));
|
||||||
|
}
|
||||||
|
|
||||||
|
using var doc = JsonDocument.Parse(File.ReadAllText(Path.Combine(dir, "dings.json")));
|
||||||
|
var instruments = doc.RootElement.GetProperty("instruments").EnumerateArray().ToDictionary(e => e.GetProperty("id").GetString()!);
|
||||||
|
Assert.Equal(DingCatalog.Instruments.Keys.Order(), instruments.Keys.Order());
|
||||||
|
foreach (var instrument in DingCatalog.Instruments.Values)
|
||||||
|
Assert.Equal(instrument.RootFrequency, instruments[instrument.Id].GetProperty("rootFrequency").GetSingle());
|
||||||
|
var events = doc.RootElement.GetProperty("events").EnumerateArray().ToDictionary(e => e.GetProperty("id").GetString()!);
|
||||||
|
Assert.Equal(DingCatalog.Events.Keys.Order(), events.Keys.Order());
|
||||||
|
foreach (var ev in DingCatalog.Events.Values)
|
||||||
|
{
|
||||||
|
var notes = events[ev.Id].GetProperty("notes").EnumerateArray().Select(n => (n.GetProperty("semitones").GetDouble(), n.GetProperty("delayMilliseconds").GetInt32(), n.GetProperty("gain").GetSingle())).ToArray();
|
||||||
|
Assert.Equal(ev.Notes.Select(n => (n.Semitones, n.DelayMilliseconds, n.Gain)).ToArray(), notes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string RepositoryRoot()
|
||||||
|
{
|
||||||
|
var dir = new DirectoryInfo(AppContext.BaseDirectory);
|
||||||
|
while (dir is not null && !File.Exists(Path.Combine(dir.FullName, "AquaSynth.sln"))) dir = dir.Parent;
|
||||||
|
return dir?.FullName ?? throw new InvalidOperationException("AquaSynth.sln not found above the test binaries.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
227
tests/AquaSynth.Dsl.Tests/StoreFileNameTests.cs
Normal file
227
tests/AquaSynth.Dsl.Tests/StoreFileNameTests.cs
Normal file
|
|
@ -0,0 +1,227 @@
|
||||||
|
using AquaSynth.Faust;
|
||||||
|
|
||||||
|
namespace AquaSynth.Dsl.Tests;
|
||||||
|
|
||||||
|
public sealed class StoreFileNameTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void DistinctIdsGetDistinctNames()
|
||||||
|
{
|
||||||
|
var ids = new[] { "a/b", "a b", "a:b", "a-b", "a_b", "a\\b", "a?b" };
|
||||||
|
var names = ids.Select(StoreFileName.From).ToArray();
|
||||||
|
Assert.Equal(ids.Length, names.Distinct(StringComparer.OrdinalIgnoreCase).Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("dings.warm-bell")]
|
||||||
|
[InlineData("demo-stream-block")]
|
||||||
|
[InlineData("a_b.c-1")]
|
||||||
|
[InlineData("conx")]
|
||||||
|
[InlineData("com10")]
|
||||||
|
public void CleanIdsPassThroughUnchanged(string id) => Assert.Equal(id, StoreFileName.From(id));
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RewrittenIdOutputAsAnIdDoesNotCollideWithTheOriginal()
|
||||||
|
{
|
||||||
|
var rewritten = StoreFileName.From("a b");
|
||||||
|
Assert.NotEqual(rewritten, StoreFileName.From(rewritten));
|
||||||
|
Assert.NotEqual(StoreFileName.From("a_b"), rewritten);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IdsDifferingOnlyByCaseGetDistinctNames()
|
||||||
|
{
|
||||||
|
var names = new[] { "A", "a", "Ab", "aB", "ab" }.Select(StoreFileName.From).ToArray();
|
||||||
|
Assert.Equal(names.Length, names.Distinct(StringComparer.OrdinalIgnoreCase).Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("CON")]
|
||||||
|
[InlineData("nul")]
|
||||||
|
[InlineData("Aux.txt")]
|
||||||
|
[InlineData("com1")]
|
||||||
|
[InlineData("COM9.f32")]
|
||||||
|
[InlineData("lpt0")]
|
||||||
|
[InlineData("prn.cc")]
|
||||||
|
[InlineData("nul.x")]
|
||||||
|
[InlineData("LPT1.x")]
|
||||||
|
[InlineData("CON.tar.gz")]
|
||||||
|
public void WindowsDeviceStemsAreRewritten(string id)
|
||||||
|
{
|
||||||
|
var name = StoreFileName.From(id);
|
||||||
|
Assert.StartsWith("_", name);
|
||||||
|
Assert.NotEqual(id.ToLowerInvariant(), name.ToLowerInvariant());
|
||||||
|
Assert.Matches("-[0-9a-f]{16}$", name);
|
||||||
|
var stem = name.Split('.')[0].TrimEnd(' ');
|
||||||
|
Assert.DoesNotMatch("^(?i:con|prn|aux|nul|com[0-9]|lpt[0-9])$", stem);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("nul ")]
|
||||||
|
[InlineData("COM\u00b9")]
|
||||||
|
[InlineData("COM1 .x")]
|
||||||
|
[InlineData("con\u00e9.x")]
|
||||||
|
[InlineData("NUL.")]
|
||||||
|
public void DeviceLookalikesNeverLeaveAReservedStem(string id)
|
||||||
|
{
|
||||||
|
var name = StoreFileName.From(id);
|
||||||
|
Assert.Matches("^[A-Za-z0-9._-]+$", name);
|
||||||
|
Assert.DoesNotMatch("^(?i:con|prn|aux|nul|com[0-9]|lpt[0-9])$", name.Split('.')[0].TrimEnd(' '));
|
||||||
|
Assert.Matches("-[0-9a-f]{16}$", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OverlongIdsAreBoundedAndStayDistinct()
|
||||||
|
{
|
||||||
|
var a = StoreFileName.From(new string('x', 400));
|
||||||
|
var b = StoreFileName.From(new string('x', 399) + "y");
|
||||||
|
Assert.True(a.Length + ".cc".Length <= 255);
|
||||||
|
Assert.NotEqual(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RewrittenIdsCarryHashOfTheOriginal()
|
||||||
|
{
|
||||||
|
// sha256("a b") begins c2a5..., checked independently of the implementation.
|
||||||
|
var expected = Convert.ToHexString(System.Security.Cryptography.SHA256.HashData("a b"u8.ToArray()))[..16].ToLowerInvariant();
|
||||||
|
Assert.Equal($"a_b-{expected}", StoreFileName.From("a b"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(".")]
|
||||||
|
[InlineData("..")]
|
||||||
|
[InlineData("")]
|
||||||
|
public void DotsAndEmptyAreRefused(string id) => Assert.Throws<ArgumentException>(() => StoreFileName.From(id));
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("../..")]
|
||||||
|
[InlineData("../../etc/passwd")]
|
||||||
|
[InlineData("..\\..\\x")]
|
||||||
|
[InlineData("/abs/path")]
|
||||||
|
[InlineData("C:\\abs")]
|
||||||
|
[InlineData("a.")]
|
||||||
|
[InlineData("a/..")]
|
||||||
|
public void NoIdCanMakePathCombineEscapeTheRoot(string id)
|
||||||
|
{
|
||||||
|
var root = Path.Combine(Path.GetTempPath(), "store-root-probe", "store");
|
||||||
|
var name = StoreFileName.From(id);
|
||||||
|
Assert.Equal(name, Path.GetFileName(name));
|
||||||
|
Assert.NotEqual(".", name);
|
||||||
|
Assert.NotEqual("..", name);
|
||||||
|
var full = Path.GetFullPath(Path.Combine(root, name));
|
||||||
|
Assert.Equal(Path.GetFullPath(root), Path.GetDirectoryName(full));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DaemonSampleFilesForDotDotCommandIdStayInsideStore()
|
||||||
|
{
|
||||||
|
var parent = Path.Combine(Path.GetTempPath(), "aquasynth-sfn-" + Guid.NewGuid().ToString("N"));
|
||||||
|
var storeRoot = Path.Combine(parent, "store");
|
||||||
|
using var service = new AquaSynthDaemonService(new AquaSynthDaemonOptions(storeRoot));
|
||||||
|
|
||||||
|
// A patch that compiles reaches the samples/ write; without native Faust only the compile-failed branch runs.
|
||||||
|
var result = await service.SampleAsync(new AquaSynthInstrumentSampleCommand(
|
||||||
|
"..", "test.sine", "test_sine",
|
||||||
|
"voice wave=sine freq=440 gain=.2 attack=.001 sustain=.05 decay=.05", DurationSeconds: 0.1f));
|
||||||
|
|
||||||
|
Assert.Empty(Directory.GetFiles(parent));
|
||||||
|
Assert.Empty(Directory.GetDirectories(parent).Where(d => Path.GetFileName(d) != "store"));
|
||||||
|
if (string.Equals(result.RenderReceipt.Status, "succeeded", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
Assert.Equal(storeRoot, Path.GetDirectoryName(Path.GetDirectoryName(UriPath(result.RenderReceipt.Float32Uri))));
|
||||||
|
Assert.Equal(storeRoot, Path.GetDirectoryName(Path.GetDirectoryName(UriPath(result.RenderReceipt.WavUri))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IpaRoundCandidatesWithCollidingSanitisedIdsKeepDistinctFilesAndStayInRoot()
|
||||||
|
{
|
||||||
|
var root = Path.Combine(Path.GetTempPath(), $"aquasynth-ipa-sfn-{Guid.NewGuid():N}");
|
||||||
|
var result = IpaGestureExperiment.WriteRound(
|
||||||
|
root,
|
||||||
|
"round-sfn",
|
||||||
|
[new IpaGestureExperimentTarget("a/b", "p", "voiceless_bilabial_plosive", .1f), new IpaGestureExperimentTarget("a-b", "m", "voiced_bilabial_nasal", .1f)],
|
||||||
|
[new IpaGestureExperimentVariant("base")]);
|
||||||
|
|
||||||
|
Assert.Equal(2, result.Candidates.Select(c => c.Id).Distinct(StringComparer.OrdinalIgnoreCase).Count());
|
||||||
|
Assert.All(result.Candidates, c => Assert.StartsWith(Path.GetFullPath(root), Path.GetFullPath(c.ScriptPath)));
|
||||||
|
Assert.Throws<ArgumentException>(() => IpaGestureExperiment.WriteRound(root, "..", [new IpaGestureExperimentTarget("a", "p", "voiceless_bilabial_plosive")], [new IpaGestureExperimentVariant("base")]));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IpaRoundJoinedTargetVariantIdsThatConcatenateEquallyKeepDistinctFiles()
|
||||||
|
{
|
||||||
|
var root = Path.Combine(Path.GetTempPath(), $"aquasynth-ipa-join-{Guid.NewGuid():N}");
|
||||||
|
var result = IpaGestureExperiment.WriteRound(
|
||||||
|
root,
|
||||||
|
"round-join",
|
||||||
|
[new IpaGestureExperimentTarget("a", "p", "voiceless_bilabial_plosive", .1f), new IpaGestureExperimentTarget("a-b", "m", "voiced_bilabial_nasal", .1f)],
|
||||||
|
[new IpaGestureExperimentVariant("b-c"), new IpaGestureExperimentVariant("c")]);
|
||||||
|
|
||||||
|
Assert.Equal(4, result.Candidates.Count);
|
||||||
|
Assert.Equal(4, result.Candidates.Select(c => c.Id).Distinct(StringComparer.OrdinalIgnoreCase).Count());
|
||||||
|
Assert.Equal(4, result.Candidates.Select(c => c.ScriptPath).Distinct(StringComparer.OrdinalIgnoreCase).Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IpaRoundPairsWithSeparatorCharactersInIdsStayDistinct()
|
||||||
|
{
|
||||||
|
var root = Path.Combine(Path.GetTempPath(), $"aquasynth-ipa-pair-{Guid.NewGuid():N}");
|
||||||
|
string[] targets = ["a", "a/b", "a-b", "a_b", "A", "a b"];
|
||||||
|
string[] variants = ["b-c", "c", "b/c", "b_c", "B-C", "b c"];
|
||||||
|
var result = IpaGestureExperiment.WriteRound(
|
||||||
|
root,
|
||||||
|
"round-pairs",
|
||||||
|
targets.Select(t => new IpaGestureExperimentTarget(t, "p", "voiceless_bilabial_plosive", .1f)).ToArray(),
|
||||||
|
variants.Select(v => new IpaGestureExperimentVariant(v)).ToArray());
|
||||||
|
|
||||||
|
Assert.Equal(36, result.Candidates.Count);
|
||||||
|
Assert.Equal(36, result.Candidates.Select(c => c.Id).Distinct(StringComparer.OrdinalIgnoreCase).Count());
|
||||||
|
Assert.Equal(36, result.Candidates.Select(c => c.ScriptPath).Distinct(StringComparer.OrdinalIgnoreCase).Count());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("../x")]
|
||||||
|
[InlineData("..\\x")]
|
||||||
|
[InlineData("/etc/x")]
|
||||||
|
[InlineData("C:\\x")]
|
||||||
|
public void BatchIdsFromTheCommandLineCannotLeaveTheArtifactRoot(string batchId)
|
||||||
|
{
|
||||||
|
var root = Path.Combine(Path.GetTempPath(), "artifact-root-probe", "artifacts");
|
||||||
|
var full = Path.GetFullPath(StoreFileName.Combine(root, batchId));
|
||||||
|
Assert.Equal(Path.GetFullPath(root), Path.GetDirectoryName(full));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("\\\\host\\share\\x")]
|
||||||
|
[InlineData("C:x")]
|
||||||
|
[InlineData("a:b")]
|
||||||
|
[InlineData("x.")]
|
||||||
|
[InlineData("x ")]
|
||||||
|
[InlineData("x. ")]
|
||||||
|
public void WindowsAbsoluteAndTrailingDotOrSpaceIdsStayContainedAndPlain(string id)
|
||||||
|
{
|
||||||
|
var root = Path.Combine(Path.GetTempPath(), "artifact-root-probe", "artifacts");
|
||||||
|
var name = StoreFileName.From(id);
|
||||||
|
Assert.Matches("^[A-Za-z0-9._-]+$", name);
|
||||||
|
Assert.False(name.EndsWith('.') || name.EndsWith(' '));
|
||||||
|
Assert.Equal(Path.GetFullPath(root), Path.GetDirectoryName(Path.GetFullPath(StoreFileName.Combine(root, id))));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("../x")]
|
||||||
|
[InlineData("a/b")]
|
||||||
|
[InlineData("name:with:colons")]
|
||||||
|
public void LoweredDspFileNameStaysInTheOutputDirectory(string name)
|
||||||
|
{
|
||||||
|
var outDir = Path.Combine(Path.GetTempPath(), "aqualower-probe", "out");
|
||||||
|
var file = StoreFileName.From(name) + ".dsp";
|
||||||
|
Assert.Equal(Path.GetFullPath(outDir), Path.GetDirectoryName(Path.GetFullPath(Path.Combine(outDir, file))));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BareDotDotBatchIdIsRefused() =>
|
||||||
|
Assert.Throws<ArgumentException>(() => StoreFileName.Combine(Path.GetTempPath(), ".."));
|
||||||
|
|
||||||
|
private static string UriPath(string uri) => new Uri(uri).LocalPath;
|
||||||
|
}
|
||||||
16
tools/AquaLower/AquaLower.csproj
Normal file
16
tools/AquaLower/AquaLower.csproj
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\AquaSynth.Core\AquaSynth.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\..\src\AquaSynth.Dings\AquaSynth.Dings.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
56
tools/AquaLower/Program.cs
Normal file
56
tools/AquaLower/Program.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using AquaSynth.Dings;
|
||||||
|
using AquaSynth.Dsl;
|
||||||
|
|
||||||
|
// Lowers .aqua patches to Faust source for the static playground (tools/aqua-playground).
|
||||||
|
// usage: AquaLower <outDir> <patchesRoot> <name>=<relative .aqua or .dsp path> ...
|
||||||
|
// A .aqua entry goes through FaustEmitter.EmitScript, the same lowering the daemon uses; a .dsp entry
|
||||||
|
// (the thruster) is already Faust and is copied verbatim. Writes <outDir>/<name>.dsp, and
|
||||||
|
// catalog.json for the browser (the one non-CultNet xeno at this boundary).
|
||||||
|
// usage: AquaLower --dings <outDir>: lowers each DingCatalog instrument to <outDir>/<id>.dsp and writes dings.json
|
||||||
|
// (instruments with their root frequency, events with their notes) so aqua-feel can mix an event the way DingsPlaybackHost does.
|
||||||
|
if (args.Length == 2 && args[0] == "--dings")
|
||||||
|
{
|
||||||
|
var dingDir = Path.GetFullPath(args[1]);
|
||||||
|
Directory.CreateDirectory(dingDir);
|
||||||
|
var instruments = new List<object>();
|
||||||
|
foreach (var ins in DingCatalog.Instruments.Values.OrderBy(i => i.Id, StringComparer.Ordinal))
|
||||||
|
{
|
||||||
|
var export = FaustEmitter.EmitScript(ins.Script, new FaustExportOptions("dings_" + ins.Id.Replace('-', '_')));
|
||||||
|
File.WriteAllText(Path.Combine(dingDir, ins.Id + ".dsp"), export.Source);
|
||||||
|
instruments.Add(new { id = ins.Id, rootFrequency = ins.RootFrequency, dsp = ins.Id + ".dsp", controlPath = "/ding/frequency", warnings = export.Warnings });
|
||||||
|
Console.WriteLine($"{ins.Id}: {export.Source.Length} chars, {export.Warnings.Count} warnings");
|
||||||
|
}
|
||||||
|
var events = DingCatalog.Events.Values.OrderBy(e => e.Id, StringComparer.Ordinal)
|
||||||
|
.Select(e => new { id = e.Id, meaning = e.Meaning, notes = e.Notes.Select(n => new { semitones = n.Semitones, delayMilliseconds = n.DelayMilliseconds, gain = n.Gain }) });
|
||||||
|
File.WriteAllText(Path.Combine(dingDir, "dings.json"), JsonSerializer.Serialize(new { instruments, events, noteSeconds = 1.5 }, new JsonSerializerOptions { WriteIndented = true }));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (args.Length < 3) { Console.Error.WriteLine("usage: AquaLower <outDir> <patchesRoot> <name>=<path> ..."); return 2; }
|
||||||
|
var outDir = Path.GetFullPath(args[0]);
|
||||||
|
var root = Path.GetFullPath(args[1]);
|
||||||
|
Directory.CreateDirectory(outDir);
|
||||||
|
var entries = new List<object>();
|
||||||
|
foreach (var spec in args.Skip(2))
|
||||||
|
{
|
||||||
|
var eq = spec.IndexOf('=');
|
||||||
|
var name = spec[..eq];
|
||||||
|
var rel = spec[(eq + 1)..];
|
||||||
|
var path = Path.Combine(root, rel);
|
||||||
|
var text = File.ReadAllText(path);
|
||||||
|
string source;
|
||||||
|
var warnings = new List<string>();
|
||||||
|
if (path.EndsWith(".aqua", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var export = FaustEmitter.EmitScript(text, new FaustExportOptions(name));
|
||||||
|
source = export.Source;
|
||||||
|
warnings.AddRange(export.Warnings);
|
||||||
|
}
|
||||||
|
else source = text;
|
||||||
|
var dspFile = StoreFileName.From(name) + ".dsp";
|
||||||
|
File.WriteAllText(Path.Combine(outDir, dspFile), source);
|
||||||
|
entries.Add(new { name, origin = rel.Replace('\\', '/'), kind = path.EndsWith(".aqua", StringComparison.Ordinal) ? "aqua" : "faust", dsp = dspFile, warnings });
|
||||||
|
Console.WriteLine($"{name}: {source.Length} chars from {rel}, {warnings.Count} warnings");
|
||||||
|
}
|
||||||
|
File.WriteAllText(Path.Combine(outDir, "catalog.json"), JsonSerializer.Serialize(entries, new JsonSerializerOptions { WriteIndented = true }));
|
||||||
|
return 0;
|
||||||
12
tools/FaustRender/FaustRender.csproj
Normal file
12
tools/FaustRender/FaustRender.csproj
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\AquaSynth.Faust\AquaSynth.Faust.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
90
tools/FaustRender/Program.cs
Normal file
90
tools/FaustRender/Program.cs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text.Json;
|
||||||
|
using AquaSynth.Faust;
|
||||||
|
|
||||||
|
// Offline probe: render one Faust source with a swept `rpm` control to mono 16-bit WAVs through the
|
||||||
|
// native libfaust path, and write manifest.json (hashes, levels, toolchain). It measures peak, rms and
|
||||||
|
// NaN only; it cannot say how anything sounds.
|
||||||
|
// usage: FaustRender <patch.dsp> <outDir> <seconds> <label>=<rpmA>[:<rpmB>] ...
|
||||||
|
const int Rate = 48000, Frames = 512;
|
||||||
|
if (args.Length < 4) { Console.Error.WriteLine("usage: FaustRender <patch.dsp> <outDir> <seconds> <label>=<rpmA>[:<rpmB>] ..."); return 2; }
|
||||||
|
var dspPath = Path.GetFullPath(args[0]);
|
||||||
|
var outDir = Path.GetFullPath(args[1]);
|
||||||
|
var seconds = double.Parse(args[2]);
|
||||||
|
var source = File.ReadAllText(dspPath);
|
||||||
|
Directory.CreateDirectory(outDir);
|
||||||
|
|
||||||
|
var home = AquaSynthFaustToolchain.Find();
|
||||||
|
Console.WriteLine($"faust home: {home ?? "(not found)"}");
|
||||||
|
using var compiler = new AquaSynthPatchCompiler(new AquaSynthNativeOptions(SampleRate: Rate));
|
||||||
|
var name = Path.GetFileNameWithoutExtension(dspPath);
|
||||||
|
if (!compiler.TryCompileSource(new AquaSynthCompileIdentity(name, name, ""), source, (float)seconds, out var patch0, out var cerr))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("COMPILE FAILED: " + cerr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var patch = patch0!;
|
||||||
|
var rpmPath = patch.ControlPaths.First(p => p.EndsWith("rpm"));
|
||||||
|
var cases = new List<object>();
|
||||||
|
foreach (var spec in args.Skip(3))
|
||||||
|
{
|
||||||
|
var eq = spec.IndexOf('=');
|
||||||
|
var label = spec[..eq];
|
||||||
|
var range = spec[(eq + 1)..].Split(':');
|
||||||
|
var a = float.Parse(range[0]);
|
||||||
|
var b = range.Length > 1 ? float.Parse(range[1]) : a;
|
||||||
|
|
||||||
|
using var stream = patch.CreateStreamingPatch();
|
||||||
|
var buf = GC.AllocateArray<float>(Frames, pinned: true);
|
||||||
|
var ptrs = GC.AllocateArray<IntPtr>(1, pinned: true);
|
||||||
|
unsafe { ptrs[0] = (IntPtr)Unsafe.AsPointer(ref buf[0]); }
|
||||||
|
var outP = Marshal.UnsafeAddrOfPinnedArrayElement(ptrs, 0);
|
||||||
|
var blocks = (int)(seconds * Rate) / Frames;
|
||||||
|
var ctl = new Dictionary<string, float>();
|
||||||
|
var pcm = new short[blocks * Frames];
|
||||||
|
double sum = 0; float peak = 0; var nan = false;
|
||||||
|
for (var blk = 0; blk < blocks; blk++)
|
||||||
|
{
|
||||||
|
ctl[rpmPath] = a + (b - a) * blk / Math.Max(1, blocks - 1);
|
||||||
|
stream.ProcessBlock(Frames, IntPtr.Zero, outP, ctl);
|
||||||
|
for (var i = 0; i < Frames; i++)
|
||||||
|
{
|
||||||
|
var v = buf[i];
|
||||||
|
nan |= float.IsNaN(v) || float.IsInfinity(v);
|
||||||
|
peak = Math.Max(peak, Math.Abs(v));
|
||||||
|
sum += (double)v * v;
|
||||||
|
pcm[blk * Frames + i] = (short)(Math.Clamp(v, -1f, 1f) * 32767);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var wav = Path.Combine(outDir, $"{name}-{label}.wav");
|
||||||
|
using (var w = new BinaryWriter(File.Create(wav)))
|
||||||
|
{
|
||||||
|
var db = pcm.Length * 2;
|
||||||
|
w.Write("RIFF"u8); w.Write(36 + db); w.Write("WAVEfmt "u8); w.Write(16); w.Write((short)1); w.Write((short)1);
|
||||||
|
w.Write(Rate); w.Write(Rate * 2); w.Write((short)2); w.Write((short)16); w.Write("data"u8); w.Write(db);
|
||||||
|
foreach (var s in pcm) w.Write(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
cases.Add(new
|
||||||
|
{
|
||||||
|
label, file = Path.GetFileName(wav), rpmStart = a, rpmEnd = b, frames = pcm.Length,
|
||||||
|
peak, rms = Math.Sqrt(sum / pcm.Length), nonFinite = nan,
|
||||||
|
sha256 = Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(wav))).ToLowerInvariant()
|
||||||
|
});
|
||||||
|
Console.WriteLine($"{wav} rpm={a}->{b} peak={peak:F3} rms={Math.Sqrt(sum / pcm.Length):F4} nonFinite={nan}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var manifest = new
|
||||||
|
{
|
||||||
|
patch = name, dspSha256 = Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(dspPath))).ToLowerInvariant(),
|
||||||
|
sampleRate = Rate, blockFrames = Frames, seconds, rpmControl = rpmPath,
|
||||||
|
os = RuntimeInformation.OSDescription, faustHome = home,
|
||||||
|
faustLibrary = home is null ? null : AquaSynthFaustToolchain.FindLibrary(home),
|
||||||
|
cases
|
||||||
|
};
|
||||||
|
File.WriteAllText(Path.Combine(outDir, "manifest.json"), JsonSerializer.Serialize(manifest, new JsonSerializerOptions { WriteIndented = true }));
|
||||||
|
return 0;
|
||||||
|
|
@ -214,7 +214,7 @@ static async Task SongScoreAsync(Dictionary<string, string> options)
|
||||||
throw new InvalidDataException($"No .aqua song candidates found under `{patchRoot}`.");
|
throw new InvalidDataException($"No .aqua song candidates found under `{patchRoot}`.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var batchDirectory = Path.Combine(artifactRoot, batchId);
|
var batchDirectory = StoreFileName.Combine(artifactRoot, batchId);
|
||||||
var candidateRoot = Path.Combine(batchDirectory, "song-candidates");
|
var candidateRoot = Path.Combine(batchDirectory, "song-candidates");
|
||||||
var timelineRoot = Path.Combine(batchDirectory, "song-timelines");
|
var timelineRoot = Path.Combine(batchDirectory, "song-timelines");
|
||||||
Directory.CreateDirectory(candidateRoot);
|
Directory.CreateDirectory(candidateRoot);
|
||||||
|
|
@ -228,7 +228,7 @@ static async Task SongScoreAsync(Dictionary<string, string> options)
|
||||||
{
|
{
|
||||||
var startedAt = DateTimeOffset.UtcNow;
|
var startedAt = DateTimeOffset.UtcNow;
|
||||||
var startedStamp = Stopwatch.GetTimestamp();
|
var startedStamp = Stopwatch.GetTimestamp();
|
||||||
var safeCandidateId = SafeName(candidate.CandidateId);
|
var safeCandidateId = StoreFileName.From(candidate.CandidateId);
|
||||||
var candidateDir = Path.Combine(candidateRoot, safeCandidateId);
|
var candidateDir = Path.Combine(candidateRoot, safeCandidateId);
|
||||||
var audioDir = Path.Combine(candidateDir, "audio");
|
var audioDir = Path.Combine(candidateDir, "audio");
|
||||||
Directory.CreateDirectory(audioDir);
|
Directory.CreateDirectory(audioDir);
|
||||||
|
|
@ -2658,10 +2658,10 @@ static bool IsOfficialMusicArtifactPath(string path)
|
||||||
}
|
}
|
||||||
|
|
||||||
static string CandidatePatchPath(SongSummaryRow row) =>
|
static string CandidatePatchPath(SongSummaryRow row) =>
|
||||||
Path.Combine(Path.GetDirectoryName(row.SummaryPath)!, "song-candidates", SafeName(row.CandidateId), "candidate.aqua");
|
Path.Combine(Path.GetDirectoryName(row.SummaryPath)!, "song-candidates", StoreFileName.From(row.CandidateId), "candidate.aqua");
|
||||||
|
|
||||||
static string CandidateAnalysisPath(SongSummaryRow row) =>
|
static string CandidateAnalysisPath(SongSummaryRow row) =>
|
||||||
Path.Combine(Path.GetDirectoryName(row.SummaryPath)!, "song-candidates", SafeName(row.CandidateId), "candidate-analysis.md");
|
Path.Combine(Path.GetDirectoryName(row.SummaryPath)!, "song-candidates", StoreFileName.From(row.CandidateId), "candidate-analysis.md");
|
||||||
|
|
||||||
static string EvaluatorReportPath(SongSummaryRow row) =>
|
static string EvaluatorReportPath(SongSummaryRow row) =>
|
||||||
Path.Combine(Path.GetDirectoryName(row.SummaryPath)!, "evaluator-report.md");
|
Path.Combine(Path.GetDirectoryName(row.SummaryPath)!, "evaluator-report.md");
|
||||||
|
|
|
||||||
73
tools/aqua-feel/README.md
Normal file
73
tools/aqua-feel/README.md
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
# aqua-feel
|
||||||
|
|
||||||
|
Headless feel meter for AquaSynth. It renders every shipped patch and every Ding offline, measures the buffers, writes
|
||||||
|
JSON and a static HTML gallery, and gates the numbers against thresholds.
|
||||||
|
|
||||||
|
**Nothing here was heard.** Every number is a measurement of an offline-rendered buffer. Metrics are evidence for a
|
||||||
|
listener to check, not verdicts: a render can pass every threshold and sound wrong, and fail one and sound fine.
|
||||||
|
The WAVs and the gallery exist so a person can press play.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
```
|
||||||
|
(cd ../aqua-playground && npm ci) # faustwasm runtime, once
|
||||||
|
node test.mjs # 26 checks of the meter on synthetic signals; renders no patch
|
||||||
|
node feel.mjs run # render 81 items (~15 s), write out/{report.json,index.html,wav/}, gate; exit 1 on failure
|
||||||
|
node feel.mjs run --only thruster # a subset (gate skips id-rule and waiver bookkeeping); --out DIR; --no-gate
|
||||||
|
node feel.mjs gate out/report.json # re-gate a saved report (--partial for a subset)
|
||||||
|
node feel.mjs compare before.json after.json [--only SUBSTR] # before/after table
|
||||||
|
node feel.mjs correlate trial-export.json ... # feel metrics vs aqua-trial listener ratings
|
||||||
|
```
|
||||||
|
|
||||||
|
Open `out/index.html` in a browser (it references `wav/` relatively). The gate is the CI step: `node feel.mjs run` exits
|
||||||
|
non-zero on any unwaived failure or stale waiver. No CI runner exists in this repo yet; nothing runs it automatically.
|
||||||
|
|
||||||
|
## What it renders
|
||||||
|
|
||||||
|
- Every patch in `tools/aqua-playground/lowered/` (41 `.aqua` files lowered by `tools/AquaLower`, plus the hand-written
|
||||||
|
`thruster.dsp` at rpm 300 / 600 / 1200 / 4800), 3 s each at 48 kHz, no gate or trigger: the sound the patch makes when started.
|
||||||
|
`advanced/additive-pad` and `advanced/electro-drop-demo` lower to ~1.5 MB of Faust each and are not in the playground, so not here.
|
||||||
|
- Every `DingCatalog` event on every instrument (9 x 4 = 36), mixed the way `DingsPlaybackHost` does it: each note rendered at
|
||||||
|
`root * 2^(semitones/12)` for 1.5 s, scaled by command gain 0.6 and the note gain, placed at its delay.
|
||||||
|
`lowered-dings/` is `dotnet run --project tools/AquaLower -- --dings tools/aqua-feel/lowered-dings`; `DingCatalogTests.LoweredDingsFedToTheFeelMeterMatchTheCatalog` fails when it drifts from `DingCatalog.cs`.
|
||||||
|
The command gain 0.6 is an assumption (`DingsPlaybackHost` takes it from the caller as 0..1), so the Ding loudness window depends on it.
|
||||||
|
|
||||||
|
Render path: the same faustwasm offline processor the playground and the listening trial use (libfaust 2.90.x, `-ftz 2`),
|
||||||
|
mono channel 0. It is **not** the daemon's native libfaust path (`-vec`); the same Faust source should render alike, but
|
||||||
|
nobody has diffed the two.
|
||||||
|
|
||||||
|
## What it measures (per item, `report.json` `items[].metrics`)
|
||||||
|
|
||||||
|
| metric | meaning | limits |
|
||||||
|
|---|---|---|
|
||||||
|
| `onsetMs` | first sample at or above max(-40 dB re peak, -70 dBFS), from note-on | Offline render has no device latency; this is the patch's own lead-in only |
|
||||||
|
| `attackMs`, `attackSlopeDbPerMs`, `attackWindowLimited` | 10% to 90% of the envelope maximum inside the first 150 ms after onset, on a 0.17 ms peak-hold envelope (30 ms release, so a low tone's zero crossings are not read as dips) | A faster attack reads as one hop. Ripple in the first peaks pulls the 90% point late (muted-pluck declares 1 ms and reads about 7); sustained noise beds read an arbitrary 60-130 ms. A swell longer than 150 ms reports a lower bound and sets `attackWindowLimited`. The envelope is a peak follower, so a tone below ~30 Hz would still ripple |
|
||||||
|
| `samplePeak`, `truePeakDb`, `overSamples`, `flatTopRuns` | peak, 4x-oversampled true peak, samples beyond 1.0, runs of 3+ samples at 0.999 | WAVs are float32 so overs survive to disk |
|
||||||
|
| `loudnessLufs` | BS.1770-4 integrated, mono, K-weighted (48 kHz coefficients; renders are fixed at 48 kHz), gated | A mono sound with no reference level; compare items, do not read it as a delivered loudness. A buffer under 400 ms is zero-padded to one block and reads low (0.1 s of full 1 kHz sine: -9.0, true -3.0); every shipped item is 1.5 s or longer |
|
||||||
|
| `crestDb`, `rmsDb` | over the active span (onset to last frame within 50 dB of peak) | |
|
||||||
|
| `centroidMedianHz`, `centroidCurve`, `fluxMean`, `fluxCurve` | STFT 2048/512 over active frames; flux is the change between unit-norm spectra | Flux on stationary noise is high; it is reported, not gated |
|
||||||
|
| `noiseToToneDb`, `noiseToToneADb` | energy not in stable spectral lines vs energy in them, flat and A-weighted | A heuristic split (see `metrics.js`): steady or decaying partials read tonal, noise-excited resonances read noise, gliding partials read noise. 8192-point window for spans of 1 s or more, else 2048, so compare the same sound across edits, not different sounds. It is window- and Q-dependent: white noise through a Q20 band-pass at 1 kHz reads as noise over 3 s (8192 window) but partly tonal over 0.5 s (2048 window), and a Q100 resonance reads tonal. The thruster rpm caps are fit to this heuristic at its 8192 window |
|
||||||
|
| `lowMotion` | span of 1 s or more with centroid spread under 0.08 octave and level range under 6 dB | The song-swarm collapse shape (docs/music-agent-orchestration-lessons.md): a held drone or a wash. A legitimate sustained drone is flagged too |
|
||||||
|
|
||||||
|
## The gate
|
||||||
|
|
||||||
|
`thresholds.json`: rules scoped to `all`, `patch`, `ding` or an id glob, each with a `why`; `level: warn` prints without failing.
|
||||||
|
`waivers` turn a known failure into a printed line and must name a reason and a follow-up id; a numeric waiver carries a bound
|
||||||
|
(`max`/`min`) and covers the item only up to it, so a waived item cannot get worse unseen. A waiver that no longer matches a
|
||||||
|
failing rule, and a rule with an `id` that matches no item (a renamed or dropped guard), are failures on a full run, so waivers
|
||||||
|
are deleted when the thing they excuse is fixed and a rename cannot silently disarm a cap. A missing metric fails its rule. The numbers were chosen after looking
|
||||||
|
at the shipped renders. They are tripwires, not taste.
|
||||||
|
|
||||||
|
Current waivers: `sfxr-classic-classic-sfxr-abstract-golf` (+0.9 dBTP, -5.3 LUFS, held noise) and `sfxr-explosion` (3 s of
|
||||||
|
steady noise without decay). Nobody has listened to either to see if the number is the sound.
|
||||||
|
|
||||||
|
## Reuse
|
||||||
|
|
||||||
|
`report.js` imports `mean`, `spearman` and `permutationP` from `../aqua-trial/analyze.mjs` and the variants from
|
||||||
|
`../aqua-trial/logic.js`: `correlate` renders the trial's four thruster variants, measures them, and asks the trial's own
|
||||||
|
question (|rho| >= 0.5 and p < 0.05) of each metric against real ratings once listeners export some. With four variants
|
||||||
|
every correlation is really about the variant ordering.
|
||||||
|
|
||||||
|
## Recorded first run
|
||||||
|
|
||||||
|
`docs/renders/aqua-feel-2026-11-05.md`: before/after numbers for the PRC SYNTH1 and thruster edits.
|
||||||
27
tools/aqua-feel/catalog.js
Normal file
27
tools/aqua-feel/catalog.js
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
// What aqua-feel renders: every playground patch, the thruster at four rpm, and every Ding event on every instrument.
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { readText, playground } from "./render.js";
|
||||||
|
import { dirname } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const here = dirname(fileURLToPath(import.meta.url));
|
||||||
|
export const THRUSTER_RPM = [300, 600, 1200, 4800];
|
||||||
|
export const PATCH_SECONDS = 3, DING_COMMAND_GAIN = 0.6;
|
||||||
|
|
||||||
|
export function patchItems() {
|
||||||
|
const dir = join(playground, "lowered"), cat = JSON.parse(readText(join(dir, "catalog.json"))), items = [];
|
||||||
|
for (const e of cat) {
|
||||||
|
const source = readText(join(dir, e.dsp));
|
||||||
|
if (e.name === "thruster") for (const rpm of THRUSTER_RPM) items.push({ id: `thruster@rpm${rpm}`, group: "patch", origin: e.origin, source, params: { rpm }, seconds: PATCH_SECONDS });
|
||||||
|
else items.push({ id: e.name, group: "patch", origin: e.origin, source, params: {}, seconds: PATCH_SECONDS });
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dingItems() {
|
||||||
|
const dir = join(here, "lowered-dings"), d = JSON.parse(readText(join(dir, "dings.json"))), items = [];
|
||||||
|
for (const ins of d.instruments) for (const ev of d.events)
|
||||||
|
items.push({ id: `ding/${ev.id}/${ins.id}`, group: "ding", origin: `DingCatalog ${ev.id} on ${ins.id}`, ding: { instrument: ins, event: ev, source: readText(join(dir, ins.dsp)), noteSeconds: d.noteSeconds, commandGain: DING_COMMAND_GAIN } });
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
export const allItems = () => [...patchItems(), ...dingItems()];
|
||||||
66
tools/aqua-feel/feel.mjs
Normal file
66
tools/aqua-feel/feel.mjs
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
// aqua-feel: headless feel meter. Renders every shipped patch and Ding offline, measures the buffers, writes
|
||||||
|
// report.json plus a static gallery, and gates the numbers against thresholds.json.
|
||||||
|
// NOTHING HERE HEARS ANYTHING. These are measurements of rendered buffers; a number is evidence, never a verdict.
|
||||||
|
// Usage: node feel.mjs run [--out DIR] [--only SUBSTR] [--no-gate] render, measure, write report + gallery, gate
|
||||||
|
// node feel.mjs gate REPORT.json [THRESHOLDS.json] [--partial] re-gate a report (--partial: a subset; skips id-rule and waiver bookkeeping)
|
||||||
|
// node feel.mjs compare BEFORE.json AFTER.json [--only SUBSTR] before/after table
|
||||||
|
// node feel.mjs correlate TRIAL_EXPORT.json ... feel metrics vs listening-trial ratings
|
||||||
|
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
||||||
|
import { dirname, join, resolve } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { measure, SR } from "./metrics.js";
|
||||||
|
import { loadRuntime, compile, renderOnce, renderDing, writeWav } from "./render.js";
|
||||||
|
import { allItems } from "./catalog.js";
|
||||||
|
import { gate, formatGate } from "./gate.js";
|
||||||
|
import { gallery } from "./gallery.js";
|
||||||
|
import { compareTable, correlate } from "./report.js";
|
||||||
|
|
||||||
|
const here = dirname(fileURLToPath(import.meta.url));
|
||||||
|
export const CAVEAT = "Nothing here was heard. These are measurements of offline-rendered buffers (faustwasm, mono channel 0); metrics are evidence for a listener to check, not verdicts.";
|
||||||
|
const args = process.argv.slice(2), cmd = args.shift();
|
||||||
|
const flag = (n) => { const i = args.indexOf(n); if (i < 0) return undefined; return args.splice(i, i + 2 > args.length ? 1 : 2).pop(); };
|
||||||
|
const bool = (n) => { const i = args.indexOf(n); if (i < 0) return false; args.splice(i, 1); return true; };
|
||||||
|
const readJson = (p) => JSON.parse(readFileSync(p, "utf8"));
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
const out = resolve(flag("--out") ?? join(here, "out")), only = flag("--only"), noGate = bool("--no-gate");
|
||||||
|
const items = allItems().filter((it) => !only || it.id.includes(only));
|
||||||
|
if (!items.length) { console.error(`no item matches ${only}`); process.exit(2); }
|
||||||
|
const rt = await loadRuntime();
|
||||||
|
console.log(`libfaust ${rt.version}; ${items.length} items; ${SR} Hz`);
|
||||||
|
const records = [];
|
||||||
|
for (const it of items) {
|
||||||
|
const t0 = Date.now();
|
||||||
|
try {
|
||||||
|
const r = it.ding ? await renderDing(rt, it.ding.instrument, it.ding.source, it.ding.event, it.ding) : await renderOnce(await compile(rt, it.id, it.source), it.params, it.seconds);
|
||||||
|
const wav = `wav/${it.id.replace(/[^A-Za-z0-9._@-]+/g, "_")}.wav`;
|
||||||
|
writeWav(join(out, wav), r.samples);
|
||||||
|
records.push({ id: it.id, group: it.group, origin: it.origin, params: it.params ?? {}, seconds: r.samples.length / SR, channels: r.channels, wav, metrics: measure(r.samples) });
|
||||||
|
console.log(`ok ${it.id} (${Date.now() - t0} ms)`);
|
||||||
|
} catch (e) {
|
||||||
|
records.push({ id: it.id, group: it.group, origin: it.origin, error: String(e.message ?? e) });
|
||||||
|
console.log(`FAIL ${it.id}: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const thresholds = readJson(join(here, "thresholds.json"));
|
||||||
|
const report = { schema: "aqua-feel.report.v1", caveat: CAVEAT, libfaust: rt.version, sampleRate: SR, items: records };
|
||||||
|
report.gate = gate(report, thresholds, { partial: Boolean(only) });
|
||||||
|
mkdirSync(out, { recursive: true });
|
||||||
|
writeFileSync(join(out, "report.json"), JSON.stringify(report, null, 1));
|
||||||
|
writeFileSync(join(out, "index.html"), gallery(report));
|
||||||
|
console.log(`\nwrote ${join(out, "report.json")} and index.html\n${CAVEAT}\n\n${formatGate(report.gate)}`);
|
||||||
|
process.exit(!noGate && !report.gate.pass ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmds = {
|
||||||
|
run,
|
||||||
|
gate() {
|
||||||
|
const partial = bool("--partial"), report = readJson(args[0]), g = gate(report, readJson(args[1] ?? join(here, "thresholds.json")), { partial });
|
||||||
|
console.log(formatGate(g)); process.exit(g.pass ? 0 : 1);
|
||||||
|
},
|
||||||
|
compare() { const only = flag("--only"); console.log(compareTable(readJson(args[0]), readJson(args[1]), only)); },
|
||||||
|
async correlate() { console.log(await correlate(args.map(readJson), join(here, "../aqua-trial"))); },
|
||||||
|
};
|
||||||
|
if (!cmds[cmd]) { console.error(readFileSync(fileURLToPath(import.meta.url), "utf8").split("\n").slice(1, 9).join("\n")); process.exit(2); }
|
||||||
|
await cmds[cmd]();
|
||||||
51
tools/aqua-feel/gallery.js
Normal file
51
tools/aqua-feel/gallery.js
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Static HTML gallery from a report: no scripts, no network. Each card has the WAV (native audio controls, for a human
|
||||||
|
// to press), an SVG level envelope, an SVG spectral-centroid curve, and the metric table with gate failures marked.
|
||||||
|
// The page says on its face that nothing was heard.
|
||||||
|
const esc = (s) => String(s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
||||||
|
const num = (v, d = 1) => (typeof v === "number" && Number.isFinite(v) ? v.toFixed(d) : "–");
|
||||||
|
|
||||||
|
function plot(points, { w = 260, h = 56, ymin, ymax, log = false, color }) {
|
||||||
|
if (!points?.length) return `<svg width="${w}" height="${h}"></svg>`;
|
||||||
|
const tmax = points[points.length - 1].t || 1, ys = points.map((p) => (log ? Math.log2(Math.max(p.v, 20)) : p.v));
|
||||||
|
const lo = ymin ?? Math.min(...ys), hi = ymax ?? Math.max(...ys), span = hi - lo || 1;
|
||||||
|
const path = points.map((p, i) => `${i ? "L" : "M"}${((p.t / tmax) * (w - 2) + 1).toFixed(1)},${(h - 2 - ((ys[i] - lo) / span) * (h - 4)).toFixed(1)}`).join("");
|
||||||
|
return `<svg width="${w}" height="${h}" viewBox="0 0 ${w} ${h}" role="img"><rect width="${w}" height="${h}" fill="#10161c"/><path d="${path}" fill="none" stroke="${color}" stroke-width="1.5"/></svg>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ROWS = [
|
||||||
|
["onset ms", "onsetMs", 2], ["attack ms (10→90% in first 150 ms)", "attackMs", 2], ["attack window-limited", "attackWindowLimited", 0], ["attack slope dB/ms", "attackSlopeDbPerMs", 1],
|
||||||
|
["sample peak", "samplePeak", 3], ["true peak dBTP", "truePeakDb", 1], ["over-1.0 samples", "overSamples", 0], ["flat-top runs", "flatTopRuns", 0],
|
||||||
|
["loudness LUFS", "loudnessLufs", 1], ["crest dB", "crestDb", 1], ["centroid median Hz", "centroidMedianHz", 0], ["flux mean", "fluxMean", 3],
|
||||||
|
["noise-to-tone dB (flat)", "noiseToToneDb", 1], ["noise-to-tone dB (A-weighted)", "noiseToToneADb", 1], ["level range dB", "envRangeDb", 1], ["centroid spread (oct)", "centroidSpreadOct", 2],
|
||||||
|
["active ms", "activeMs", 0], ["low motion", "lowMotion", 0],
|
||||||
|
];
|
||||||
|
|
||||||
|
export function gallery(report) {
|
||||||
|
const bad = new Map();
|
||||||
|
for (const [kind, list] of [["fail", report.gate?.failures ?? []], ["waived", report.gate?.waived ?? []], ["warn", report.gate?.warnings ?? []]])
|
||||||
|
for (const e of list) { const k = `${e.id}|${e.metric}`; if (!bad.has(k) || kind === "fail") bad.set(k, kind); }
|
||||||
|
const cards = report.items.map((it) => {
|
||||||
|
const m = it.metrics;
|
||||||
|
if (!m) return `<section class="card fail"><h3>${esc(it.id)}</h3><p>did not render: ${esc(it.error ?? "unknown")}</p></section>`;
|
||||||
|
const rows = ROWS.map(([label, key, d]) => {
|
||||||
|
const v = m[key], cls = bad.get(`${it.id}|${key}`) ?? "", shown = typeof v === "boolean" ? String(v) : num(v, d);
|
||||||
|
return `<tr class="${cls}"><th>${label}</th><td>${shown}${cls ? ` <b>${cls}</b>` : ""}</td></tr>`;
|
||||||
|
}).join("");
|
||||||
|
const worst = ["fail", "waived", "warn"].find((k) => [...bad].some(([key, v]) => key.startsWith(it.id + "|") && v === k)) ?? "";
|
||||||
|
return `<section class="card ${worst}" id="${esc(it.id)}"><h3>${esc(it.id)} <small>${it.group}</small></h3>
|
||||||
|
<audio controls preload="none" src="${esc(it.wav)}"></audio>
|
||||||
|
<p class="cap">level envelope (dB, ${num(m.envelopeCurve?.at(-1)?.t, 1)} s)</p>${plot(m.envelopeCurve, { ymin: -90, ymax: 6, color: "#6fb1c7" })}
|
||||||
|
<p class="cap">spectral centroid (log Hz)</p>${plot(m.centroidCurve, { log: true, ymin: Math.log2(20), ymax: Math.log2(16000), color: "#c7a46f" })}
|
||||||
|
<table>${rows}</table><p class="cap">${esc(it.origin ?? "")}</p></section>`;
|
||||||
|
});
|
||||||
|
const g = report.gate;
|
||||||
|
return `<!doctype html><meta charset="utf-8"><title>aqua-feel gallery</title>
|
||||||
|
<style>body{font:14px/1.4 system-ui,sans-serif;background:#0b0f13;color:#d8dee4;margin:1.5rem}h1{font-size:1.3rem}h3{margin:.2rem 0;font-size:.95rem}small{color:#7d8a96;font-weight:400}
|
||||||
|
.banner{border:1px solid #c7a46f;padding:.7rem 1rem;margin:1rem 0;color:#f0e2c4}.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px}
|
||||||
|
.card{background:#141b22;border:1px solid #232d37;padding:10px}.card.fail{border-color:#d4574a}.card.waived{border-color:#c7a46f}.card.warn{border-color:#8a7f4a}
|
||||||
|
table{border-collapse:collapse;width:100%;margin-top:6px}th{text-align:left;font-weight:400;color:#8c99a5;padding:1px 6px 1px 0}td{padding:1px 0}tr.fail td{color:#ff8a7d}tr.waived td{color:#e0c07f}tr.warn td{color:#c9be85}
|
||||||
|
.cap{margin:6px 0 2px;color:#7d8a96;font-size:.75rem}audio{width:100%;height:30px}b{font-size:.7rem;text-transform:uppercase}</style>
|
||||||
|
<h1>aqua-feel gallery</h1>
|
||||||
|
<div class="banner"><b>Nothing here was heard.</b> Every number is a measurement of an offline-rendered buffer (faustwasm, mono channel 0, ${report.sampleRate} Hz, libfaust ${esc(report.libfaust)}). Metrics are evidence for a listener to check, not verdicts; the WAVs are there so a person can press play. Gate: <b>${g?.pass ? "pass" : "fail"}</b> (${g?.failures.length ?? 0} failing, ${g?.waived.length ?? 0} waived, ${g?.warnings.length ?? 0} warnings).</div>
|
||||||
|
<div class="grid">${cards.join("\n")}</div>`;
|
||||||
|
}
|
||||||
64
tools/aqua-feel/gate.js
Normal file
64
tools/aqua-feel/gate.js
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Threshold gate over a report. Rules come from thresholds.json: { group: all|patch|ding, id?: glob, metric, min?, max?,
|
||||||
|
// equals?, level?: fail|warn, why }. A waiver { id, metric, reason, followUp, max?|min? } turns a known failure into a
|
||||||
|
// printed 'waived' line, but only up to the bound it states: a numeric rule needs a waiver bound on the same side
|
||||||
|
// (max for a max rule, min for a min rule) and the value must stay inside it, so a waived item cannot get worse unseen.
|
||||||
|
// A waiver that matches nothing failing, and a rule with an `id` that matches no item, are failures on a full run, so
|
||||||
|
// waivers get deleted when the thing they excuse is fixed and a catalog rename cannot silently disarm a guard.
|
||||||
|
// Thresholds are tripwires for a listener to check, not a judgement of the sound.
|
||||||
|
const glob = (pattern) => new RegExp("^" + pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$");
|
||||||
|
const get = (m, path) => path.split(".").reduce((o, k) => o?.[k], m);
|
||||||
|
const fmt = (v) => (typeof v === "number" ? +v.toFixed(2) : v);
|
||||||
|
|
||||||
|
function violation(rule, value) {
|
||||||
|
if (value === undefined || value === null || Number.isNaN(value)) return "no value";
|
||||||
|
if (rule.equals !== undefined && value !== rule.equals) return `${value} (want ${rule.equals})`;
|
||||||
|
if (rule.max !== undefined && !(value <= rule.max)) return `${fmt(value)} (max ${rule.max})`;
|
||||||
|
if (rule.min !== undefined && !(value >= rule.min)) return `${fmt(value)} (min ${rule.min})`;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does waiver `w` cover this violation? Boolean/equals rules: any waiver for the metric. Numeric rules: the waiver must
|
||||||
|
// state a bound on the violated side and the value must lie within it.
|
||||||
|
function covers(w, rule, value) {
|
||||||
|
if (rule.equals !== undefined) return true;
|
||||||
|
if (typeof value !== "number" || Number.isNaN(value)) return false;
|
||||||
|
if (rule.max !== undefined && !(value <= rule.max)) return typeof w.max === "number" && value <= w.max;
|
||||||
|
if (rule.min !== undefined && !(value >= rule.min)) return typeof w.min === "number" && value >= w.min;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function gate(report, thresholds, { partial = false } = {}) {
|
||||||
|
const failures = [], warnings = [], waived = [], hits = new Set(), armed = new Set();
|
||||||
|
const waivers = thresholds.waivers ?? [];
|
||||||
|
for (const it of report.items) {
|
||||||
|
if (it.error || it.metrics?.broken) { failures.push({ id: it.id, metric: "render", detail: it.error ?? `${it.metrics.nonFinite} non-finite samples`, why: "item did not render cleanly" }); continue; }
|
||||||
|
const silent = it.metrics?.silent === true;
|
||||||
|
if (silent) failures.push({ id: it.id, metric: "silent", detail: "peak under -70 dBFS", why: "an item that renders nothing" });
|
||||||
|
thresholds.rules.forEach((rule, ri) => {
|
||||||
|
if (rule.group !== "all" && rule.group !== it.group) return;
|
||||||
|
if (rule.id && !glob(rule.id).test(it.id)) return;
|
||||||
|
armed.add(ri);
|
||||||
|
if (silent) return; // reported once above; its other metrics do not exist
|
||||||
|
const value = get(it.metrics, rule.metric), detail = violation(rule, value);
|
||||||
|
if (!detail) return;
|
||||||
|
const entry = { id: it.id, metric: rule.metric, detail, why: rule.why };
|
||||||
|
if (rule.level === "warn") { warnings.push(entry); return; }
|
||||||
|
const w = waivers.findIndex((x) => x.metric === rule.metric && glob(x.id).test(it.id) && covers(x, rule, value));
|
||||||
|
if (w >= 0) { hits.add(w); waived.push({ ...entry, reason: waivers[w].reason, followUp: waivers[w].followUp }); }
|
||||||
|
else failures.push(entry);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!partial) {
|
||||||
|
thresholds.rules.forEach((r, i) => { if (r.id && !armed.has(i)) failures.push({ id: r.id, metric: r.metric, detail: "rule matches no item", why: "disarmed guard: the item was renamed or dropped" }); });
|
||||||
|
waivers.forEach((w, i) => { if (!hits.has(i)) failures.push({ id: w.id, metric: w.metric, detail: "waiver matches nothing failing within its bound", why: "stale waiver: delete it (or the item got worse than the bound)" }); });
|
||||||
|
}
|
||||||
|
return { pass: failures.length === 0, items: report.items.length, partial, failures, warnings, waived };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatGate(g) {
|
||||||
|
const line = (e) => ` ${e.id} ${e.metric}: ${e.detail} [${e.why}]${e.reason ? `\n waived: ${e.reason}${e.followUp ? ` (${e.followUp})` : ""}` : ""}`;
|
||||||
|
const out = [`gate: ${g.pass ? "PASS" : "FAIL"} (${g.items} items${g.partial ? ", partial run: id-rule and waiver bookkeeping skipped" : ""}, ${g.failures.length} failing, ${g.waived.length} waived, ${g.warnings.length} warnings)`];
|
||||||
|
for (const [title, list] of [["FAILING", g.failures], ["WAIVED", g.waived], ["WARNINGS", g.warnings]]) if (list.length) out.push(title, ...list.map(line));
|
||||||
|
out.push("", "Thresholds are tripwires for a listener to check. Passing means nothing was heard to be wrong; it does not mean it sounds right.");
|
||||||
|
return out.join("\n");
|
||||||
|
}
|
||||||
184
tools/aqua-feel/lowered-dings/dings.json
Normal file
184
tools/aqua-feel/lowered-dings/dings.json
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
{
|
||||||
|
"instruments": [
|
||||||
|
{
|
||||||
|
"id": "glass-chime",
|
||||||
|
"rootFrequency": 261.6256,
|
||||||
|
"dsp": "glass-chime.dsp",
|
||||||
|
"controlPath": "/ding/frequency",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "muted-pluck",
|
||||||
|
"rootFrequency": 261.6256,
|
||||||
|
"dsp": "muted-pluck.dsp",
|
||||||
|
"controlPath": "/ding/frequency",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "soft-triangle",
|
||||||
|
"rootFrequency": 261.6256,
|
||||||
|
"dsp": "soft-triangle.dsp",
|
||||||
|
"controlPath": "/ding/frequency",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "warm-bell",
|
||||||
|
"rootFrequency": 261.6256,
|
||||||
|
"dsp": "warm-bell.dsp",
|
||||||
|
"controlPath": "/ding/frequency",
|
||||||
|
"warnings": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"id": "input.required",
|
||||||
|
"meaning": "Input requested",
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"semitones": 12,
|
||||||
|
"delayMilliseconds": 0,
|
||||||
|
"gain": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semitones": 5,
|
||||||
|
"delayMilliseconds": 260,
|
||||||
|
"gain": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "resource.limit",
|
||||||
|
"meaning": "Resource is holding",
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"semitones": 0,
|
||||||
|
"delayMilliseconds": 0,
|
||||||
|
"gain": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semitones": 0,
|
||||||
|
"delayMilliseconds": 280,
|
||||||
|
"gain": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semitones": 0,
|
||||||
|
"delayMilliseconds": 560,
|
||||||
|
"gain": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "session.end",
|
||||||
|
"meaning": "Session closure",
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"semitones": 7,
|
||||||
|
"delayMilliseconds": 0,
|
||||||
|
"gain": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semitones": 0,
|
||||||
|
"delayMilliseconds": 240,
|
||||||
|
"gain": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "session.start",
|
||||||
|
"meaning": "Opening welcome",
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"semitones": 0,
|
||||||
|
"delayMilliseconds": 0,
|
||||||
|
"gain": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semitones": 7,
|
||||||
|
"delayMilliseconds": 220,
|
||||||
|
"gain": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "task.acknowledge",
|
||||||
|
"meaning": "Quiet ongoing acknowledgement",
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"semitones": 7,
|
||||||
|
"delayMilliseconds": 0,
|
||||||
|
"gain": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "task.complete",
|
||||||
|
"meaning": "Satisfying closure",
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"semitones": 7,
|
||||||
|
"delayMilliseconds": 0,
|
||||||
|
"gain": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semitones": 12,
|
||||||
|
"delayMilliseconds": 220,
|
||||||
|
"gain": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "task.error",
|
||||||
|
"meaning": "Gentle failure notice",
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"semitones": 7,
|
||||||
|
"delayMilliseconds": 0,
|
||||||
|
"gain": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semitones": 4,
|
||||||
|
"delayMilliseconds": 240,
|
||||||
|
"gain": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semitones": 0,
|
||||||
|
"delayMilliseconds": 480,
|
||||||
|
"gain": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "task.progress",
|
||||||
|
"meaning": "Faint ignorable progress",
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"semitones": -5,
|
||||||
|
"delayMilliseconds": 0,
|
||||||
|
"gain": 0.45
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "user.spam",
|
||||||
|
"meaning": "Curt rate warning",
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"semitones": 9,
|
||||||
|
"delayMilliseconds": 0,
|
||||||
|
"gain": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semitones": 9,
|
||||||
|
"delayMilliseconds": 190,
|
||||||
|
"gain": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"semitones": 9,
|
||||||
|
"delayMilliseconds": 380,
|
||||||
|
"gain": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"noteSeconds": 1.5
|
||||||
|
}
|
||||||
56
tools/aqua-feel/lowered-dings/glass-chime.dsp
Normal file
56
tools/aqua-feel/lowered-dings/glass-chime.dsp
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "dings_glass_chime";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_param_0 = hslider("/ding/frequency", 261.6256, 40, 4000, 0.001) : si.smoo;
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, patch_param_0 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, patch_param_0 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, patch_param_0 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 4.12))) * (max(0.0, 4.1 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.28, 0.0001))) / ma.PI));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.04 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.04 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.001, 0, 1, 0.85, 0.025) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.12);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
56
tools/aqua-feel/lowered-dings/muted-pluck.dsp
Normal file
56
tools/aqua-feel/lowered-dings/muted-pluck.dsp
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "dings_muted_pluck";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_param_0 = hslider("/ding/frequency", 261.6256, 40, 4000, 0.001) : si.smoo;
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, patch_param_0 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, patch_param_0 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 0.5);
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.58 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.001, 0, 1, 0.34, 0.018) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.13);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
56
tools/aqua-feel/lowered-dings/soft-triangle.dsp
Normal file
56
tools/aqua-feel/lowered-dings/soft-triangle.dsp
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "dings_soft_triangle";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_param_0 = hslider("/ding/frequency", 261.6256, 40, 4000, 0.001) : si.smoo;
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, patch_param_0 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, patch_param_0 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 0.5);
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.72 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.006, 0, 1, 0.9, 0.04) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.14);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
56
tools/aqua-feel/lowered-dings/warm-bell.dsp
Normal file
56
tools/aqua-feel/lowered-dings/warm-bell.dsp
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "dings_warm_bell";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_param_0 = hslider("/ding/frequency", 261.6256, 40, 4000, 0.001) : si.smoo;
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, patch_param_0 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, patch_param_0 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, patch_param_0 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 3.01))) * (max(0.0, 2.6 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.42, 0.0001))) / ma.PI));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.86 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.004, 0, 1, 1.15, 0.035) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.17);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
265
tools/aqua-feel/metrics.js
Normal file
265
tools/aqua-feel/metrics.js
Normal file
|
|
@ -0,0 +1,265 @@
|
||||||
|
// Pure signal metrics for aqua-feel. Dependency-free; every function takes mono Float32Array/number[] at SR.
|
||||||
|
// These are measurements of a rendered buffer. Nothing here hears anything, and no number is a verdict.
|
||||||
|
export const SR = 48000;
|
||||||
|
export const SILENT_PEAK = 10 ** (-70 / 20); // a buffer whose peak is below -70 dBFS counts as silent
|
||||||
|
const db = (x) => (x > 0 ? 20 * Math.log10(x) : -Infinity);
|
||||||
|
const pdb = (x) => (x > 0 ? 10 * Math.log10(x) : -Infinity);
|
||||||
|
const median = (xs) => { const s = Float64Array.from(xs).sort(); return s.length ? (s.length % 2 ? s[s.length >> 1] : (s[s.length / 2 - 1] + s[s.length / 2]) / 2) : NaN; };
|
||||||
|
const mean = (xs) => (xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : NaN);
|
||||||
|
const sd = (xs) => (xs.length < 2 ? 0 : Math.sqrt(xs.reduce((a, x) => a + (x - mean(xs)) ** 2, 0) / (xs.length - 1)));
|
||||||
|
const quantile = (xs, q) => { const s = Float64Array.from(xs).sort(); return s.length ? s[Math.min(s.length - 1, Math.floor(q * s.length))] : NaN; };
|
||||||
|
|
||||||
|
// ---- levels ------------------------------------------------------------------------------------------------------
|
||||||
|
export function samplePeak(x) { let p = 0; for (const v of x) { const a = Math.abs(v); if (a > p) p = a; } return p; }
|
||||||
|
|
||||||
|
// True peak: 4x oversampled with a Hann-windowed sinc (BS.1770 Annex 2 style, 48 taps). Returns linear peak.
|
||||||
|
export function truePeak(x) {
|
||||||
|
const T = 12, phases = [0.25, 0.5, 0.75];
|
||||||
|
const taps = phases.map((ph) => Array.from({ length: 2 * T }, (_, i) => {
|
||||||
|
const k = i - T + 1, t = k - ph, s = Math.abs(t) < 1e-12 ? 1 : Math.sin(Math.PI * t) / (Math.PI * t);
|
||||||
|
return s * (0.5 + 0.5 * Math.cos((Math.PI * t) / T));
|
||||||
|
}));
|
||||||
|
let peak = samplePeak(x);
|
||||||
|
for (let n = 0; n < x.length; n++) {
|
||||||
|
for (let p = 0; p < 3; p++) {
|
||||||
|
let acc = 0;
|
||||||
|
for (let i = 0; i < 2 * T; i++) { const j = n + i - T + 1; if (j >= 0 && j < x.length) acc += x[j] * taps[p][i]; }
|
||||||
|
if (Math.abs(acc) > peak) peak = Math.abs(acc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peak;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Over = would clip at a +/-1.0 converter. Flat tops = runs of >=3 consecutive samples at >=0.999 (a clipped waveform's signature).
|
||||||
|
export function clipping(x) {
|
||||||
|
let over = 0, flat = 0, run = 0;
|
||||||
|
for (const v of x) {
|
||||||
|
const a = Math.abs(v);
|
||||||
|
if (a > 1) over++;
|
||||||
|
if (a >= 0.999) { run++; if (run === 3) flat++; } else run = 0;
|
||||||
|
}
|
||||||
|
return { overSamples: over, overFraction: x.length ? over / x.length : 0, flatTopRuns: flat };
|
||||||
|
}
|
||||||
|
|
||||||
|
const K1 = { b: [1.53512485958697, -2.69169618940638, 1.19839281085285], a: [1, -1.69065929318241, 0.73248077421585] };
|
||||||
|
const K2 = { b: [1, -2, 1], a: [1, -1.99004745483398, 0.99007225036621] };
|
||||||
|
function biquad(x, { b, a }) {
|
||||||
|
const y = new Float64Array(x.length); let x1 = 0, x2 = 0, y1 = 0, y2 = 0;
|
||||||
|
for (let n = 0; n < x.length; n++) {
|
||||||
|
const v = b[0] * x[n] + b[1] * x1 + b[2] * x2 - a[1] * y1 - a[2] * y2;
|
||||||
|
x2 = x1; x1 = x[n]; y2 = y1; y1 = v; y[n] = v;
|
||||||
|
}
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
// ITU-R BS.1770-4 integrated loudness, mono, 48 kHz coefficients, 400 ms blocks at 75% overlap, -70 LUFS absolute gate,
|
||||||
|
// -10 LU relative gate. A buffer shorter than 400 ms is zero-padded to one block. Returns LUFS, or null when silent.
|
||||||
|
export function integratedLoudness(x) {
|
||||||
|
const k = biquad(biquad(x, K1), K2), B = 19200, H = 4800;
|
||||||
|
const n = Math.max(1, Math.ceil(Math.max(0, k.length - B) / H) + 1), ms = [];
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
let s = 0; for (let j = i * H; j < i * H + B && j < k.length; j++) s += k[j] * k[j];
|
||||||
|
ms.push(s / B);
|
||||||
|
}
|
||||||
|
const lufs = (e) => -0.691 + pdb(e);
|
||||||
|
const pass1 = ms.filter((e) => lufs(e) > -70);
|
||||||
|
if (!pass1.length) return null;
|
||||||
|
const rel = lufs(mean(pass1)) - 10, pass2 = pass1.filter((e) => lufs(e) > rel);
|
||||||
|
return pass2.length ? lufs(mean(pass2)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- time structure ----------------------------------------------------------------------------------------------
|
||||||
|
const HOP = 8; // envelope hop in samples (0.167 ms)
|
||||||
|
// Envelope: max |x| per hop. With held=true it also decays with a 30 ms release so the zero crossings of a low tone do not
|
||||||
|
// read as dips; only the attack uses that (the hold smears decays, so activeEnd, crest and the gallery plot use the raw one).
|
||||||
|
const RELEASE_S = 0.03;
|
||||||
|
export function envelope(x, held = false) {
|
||||||
|
const env = new Float64Array(Math.ceil(x.length / HOP)), hold = Math.exp(-HOP / SR / RELEASE_S);
|
||||||
|
for (let i = 0; i < x.length; i++) { const a = Math.abs(x[i]); if (a > env[(i / HOP) | 0]) env[(i / HOP) | 0] = a; }
|
||||||
|
if (held) for (let i = 1; i < env.length; i++) env[i] = Math.max(env[i], env[i - 1] * hold);
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
const maxIndex = (a) => { let k = 0; for (let i = 1; i < a.length; i++) if (a[i] > a[k]) k = i; return k; };
|
||||||
|
const hopMs = (HOP / SR) * 1000;
|
||||||
|
|
||||||
|
// Silent = peak under -70 dBFS. Onset: first sample at or above max(-40 dB re peak, -70 dBFS). Attack is measured inside
|
||||||
|
// the first 150 ms after the onset (shorter than the 180 ms minimum gap between Ding notes, so a later, louder note cannot
|
||||||
|
// stretch it): 10% -> 90% of the envelope's maximum within that window. If that maximum falls in the last tenth of the
|
||||||
|
// window the sound is still swelling and attackMs is only a lower bound (attackWindowLimited). The envelope hop is
|
||||||
|
// 0.167 ms, so an attack faster than that is reported as one hop (resolutionLimited). activeEnd = last envelope hop
|
||||||
|
// within 50 dB of the peak.
|
||||||
|
const ATTACK_WINDOW_S = 0.15;
|
||||||
|
export function timing(x) {
|
||||||
|
const peak = samplePeak(x);
|
||||||
|
if (!(peak >= SILENT_PEAK)) return { silent: true };
|
||||||
|
const thr = Math.max(peak * 0.01, SILENT_PEAK);
|
||||||
|
let onsetSample = 0; while (onsetSample < x.length && Math.abs(x[onsetSample]) < thr) onsetSample++;
|
||||||
|
const env = envelope(x, true), raw = envelope(x), start = Math.floor(onsetSample / HOP), end = Math.min(env.length - 1, start + Math.round(ATTACK_WINDOW_S * SR / HOP));
|
||||||
|
let top = start; for (let i = start; i <= end; i++) if (env[i] > env[top]) top = i;
|
||||||
|
const first = env[top], at = (f) => { let i = start; while (i < top && env[i] < first * f) i++; return i; };
|
||||||
|
const i10 = at(0.1), i90 = at(0.9);
|
||||||
|
const dt = Math.max(hopMs, (i90 - i10) * hopMs), floor = peak * 10 ** (-50 / 20);
|
||||||
|
let last = raw.length - 1; while (last > 0 && raw[last] < floor) last--;
|
||||||
|
return {
|
||||||
|
silent: false, onsetMs: (onsetSample / SR) * 1000, attackMs: dt, attackSlopeDbPerMs: db(0.9 / 0.1) / dt,
|
||||||
|
resolutionLimited: i90 - i10 <= 1, attackWindowLimited: top - start > 0.9 * (end - start) && end - start > 10,
|
||||||
|
activeStartSample: onsetSample, activeEndSample: Math.min(x.length, (last + 1) * HOP),
|
||||||
|
peakAtMs: (maxIndex(raw) * HOP / SR) * 1000, firstPeak: first, firstPeakAtMs: top * hopMs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function crest(x, t) {
|
||||||
|
const seg = x.subarray ? x.subarray(t.activeStartSample, t.activeEndSample) : x.slice(t.activeStartSample, t.activeEndSample);
|
||||||
|
let s = 0; for (const v of seg) s += v * v;
|
||||||
|
const rms = Math.sqrt(s / Math.max(1, seg.length)), peak = samplePeak(seg);
|
||||||
|
return { rms, rmsDb: db(rms), crestDb: db(peak / rms) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- spectrum ----------------------------------------------------------------------------------------------------
|
||||||
|
export function fft(re, im) {
|
||||||
|
const n = re.length;
|
||||||
|
for (let i = 1, j = 0; i < n; i++) {
|
||||||
|
let bit = n >> 1; for (; j & bit; bit >>= 1) j ^= bit; j ^= bit;
|
||||||
|
if (i < j) { [re[i], re[j]] = [re[j], re[i]]; [im[i], im[j]] = [im[j], im[i]]; }
|
||||||
|
}
|
||||||
|
for (let len = 2; len <= n; len <<= 1) {
|
||||||
|
const ang = (-2 * Math.PI) / len, wr = Math.cos(ang), wi = Math.sin(ang);
|
||||||
|
for (let i = 0; i < n; i += len) {
|
||||||
|
let cr = 1, ci = 0;
|
||||||
|
for (let k = 0; k < len / 2; k++) {
|
||||||
|
const a = i + k, b = a + len / 2, tr = re[b] * cr - im[b] * ci, ti = re[b] * ci + im[b] * cr;
|
||||||
|
re[b] = re[a] - tr; im[b] = im[a] - ti; re[a] += tr; im[a] += ti;
|
||||||
|
[cr, ci] = [cr * wr - ci * wi, cr * wi + ci * wr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const N = 2048, SHOP = 512;
|
||||||
|
const hanns = new Map();
|
||||||
|
const hann = (n) => hanns.get(n) ?? (hanns.set(n, Float64Array.from({ length: n }, (_, i) => 0.5 - 0.5 * Math.cos((2 * Math.PI * i) / n))), hanns.get(n));
|
||||||
|
export function stft(x, N = 2048, SHOP = 512) {
|
||||||
|
const frames = [], HANN = hann(N);
|
||||||
|
for (let s = 0; s + N <= Math.max(N, x.length); s += SHOP) {
|
||||||
|
const re = new Float64Array(N), im = new Float64Array(N);
|
||||||
|
for (let i = 0; i < N; i++) re[i] = (x[s + i] ?? 0) * HANN[i];
|
||||||
|
fft(re, im);
|
||||||
|
const mag = new Float64Array(N / 2); let e = 0;
|
||||||
|
for (let k = 0; k < N / 2; k++) { mag[k] = Math.hypot(re[k], im[k]); e += mag[k] * mag[k]; }
|
||||||
|
frames.push({ t: (s + N / 2) / SR, mag, energy: e, n: N });
|
||||||
|
}
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tone vs noise, per time-frequency cell. A cell (bin k, frame t) is TONAL when (1) bin k is a local maximum at least
|
||||||
|
// 12 dB (power x16) above the median of its +/-12 neighbours in frame t, (2) that holds for a run of at least 4
|
||||||
|
// consecutive frames, and (3) over that run the level is temporally smooth: the median absolute second difference of
|
||||||
|
// the dB level (max over k-1..k+1, so slow drift stays inside) is under 6 dB. A steady or exponentially decaying partial
|
||||||
|
// passes; a noise-excited resonance (a rattle, a vocal formant) passes (1) but fails (3), because noise gives each
|
||||||
|
// frame a Rayleigh-random level (about 9 dB median second difference). Tonal cells take +/-2 bins either side (the Hann
|
||||||
|
// main lobe). Everything else is noise. Limits: a gliding or strongly vibrato partial moves bins and reads as noise;
|
||||||
|
// with under 5 frames only (1) applies.
|
||||||
|
// A-weighting as a power factor per FFT bin (IEC 61672 curve, 0 dB at 1 kHz).
|
||||||
|
const aWeightPower = (hz) => {
|
||||||
|
const f2 = hz * hz, ra = (12194 ** 2 * f2 * f2) / ((f2 + 20.6 ** 2) * Math.sqrt((f2 + 107.7 ** 2) * (f2 + 737.9 ** 2)) * (f2 + 12194 ** 2));
|
||||||
|
return (ra * 1.2589) ** 2;
|
||||||
|
};
|
||||||
|
const prominent = (p, k) => {
|
||||||
|
const nb = []; for (let j = Math.max(1, k - 12); j <= Math.min(p.length - 1, k + 12); j++) if (j !== k) nb.push(p[j]);
|
||||||
|
return p[k] >= p[k - 1] && p[k] >= p[k + 1] && p[k] >= 16 * median(nb);
|
||||||
|
};
|
||||||
|
function toneNoiseSpan(frames) {
|
||||||
|
const bins = frames[0].mag.length, T = frames.length, P = frames.map((f) => Float64Array.from(f.mag, (m) => m * m));
|
||||||
|
const tonal = P.map(() => new Uint8Array(bins)), prom = P.map((p) => { const r = new Uint8Array(bins); for (let k = 2; k < bins - 2; k++) r[k] = prominent(p, k) ? 1 : 0; return r; });
|
||||||
|
const mark = (t, k) => { for (let j = -2; j <= 2; j++) tonal[t][k + j] = 1; };
|
||||||
|
for (let k = 2; k < bins - 2; k++) {
|
||||||
|
for (let t = 0; t < T;) {
|
||||||
|
if (!prom[t][k]) { t++; continue; }
|
||||||
|
let e = t; while (e + 1 < T && prom[e + 1][k]) e++;
|
||||||
|
if (T < 5) { for (let i = t; i <= e; i++) mark(i, k); }
|
||||||
|
else if (e - t + 1 >= 4) {
|
||||||
|
const lv = []; for (let i = t; i <= e; i++) lv.push(20 * Math.log10(Math.max(frames[i].mag[k - 1], frames[i].mag[k], frames[i].mag[k + 1], 1e-12)));
|
||||||
|
const d2 = []; for (let i = 1; i < lv.length - 1; i++) d2.push(Math.abs(lv[i + 1] - 2 * lv[i] + lv[i - 1]));
|
||||||
|
if (median(d2) < 6) for (let i = t; i <= e; i++) mark(i, k);
|
||||||
|
}
|
||||||
|
t = e + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const aw = Float64Array.from({ length: bins }, (_, k) => aWeightPower((k * SR) / (2 * bins)));
|
||||||
|
let tone = 0, noise = 0, toneA = 0, noiseA = 0;
|
||||||
|
for (let t = 0; t < T; t++) for (let k = 1; k < bins; k++) { const v = P[t][k]; if (tonal[t][k]) { tone += v; toneA += v * aw[k]; } else { noise += v; noiseA += v * aw[k]; } }
|
||||||
|
return { tone, noise, toneA, noiseA };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Noise-to-tone ratio in dB over the active span (energy-weighted across frames), flat and A-weighted. Flat power is
|
||||||
|
// dominated by whatever is loudest and lowest (a 35 Hz rumble hides a 1 kHz hiss); the A-weighted ratio is closer to what
|
||||||
|
// a listener could notice, which is a reason to look at it, not proof of what they notice. Uses an 8192-point window
|
||||||
|
// (5.9 Hz bins) when the active span is 1 s or longer, so a 35 Hz rumble resolves into lines; shorter sounds use 2048
|
||||||
|
// (23 Hz bins). Compare it between renders of the same sound, not across sounds of different lengths. Clamped to +/-60 dB.
|
||||||
|
export function noiseToTone(x, t) {
|
||||||
|
const spanS = (t.activeEndSample - t.activeStartSample) / SR, n = spanS >= 1 ? 8192 : 2048;
|
||||||
|
const frames = stft(x.slice(t.activeStartSample, t.activeEndSample), n, n / 4), maxE = frames.reduce((m, f) => Math.max(m, f.energy), 0);
|
||||||
|
const live = frames.filter((f) => f.energy >= maxE * 1e-5), r = toneNoiseSpan(live);
|
||||||
|
const ratio = (nz, tn) => Math.max(-60, Math.min(60, pdb(nz) - pdb(tn || 1e-30)));
|
||||||
|
return { db: ratio(r.noise, r.tone), dbA: ratio(r.noiseA, r.toneA), window: n };
|
||||||
|
}
|
||||||
|
|
||||||
|
const downsample = (pts, n) => {
|
||||||
|
if (pts.length <= n) return pts;
|
||||||
|
return Array.from({ length: n }, (_, i) => {
|
||||||
|
const a = Math.floor((i * pts.length) / n), b = Math.max(a + 1, Math.floor(((i + 1) * pts.length) / n)), seg = pts.slice(a, b);
|
||||||
|
return { t: mean(seg.map((s) => s.t)), v: mean(seg.map((s) => s.v)) };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Centroid and flux over time, noise-to-tone ratio, and the low-motion flag, over frames within 50 dB of the loudest
|
||||||
|
// frame and inside the active span. Flux is the half-wave-rectified L2 change between consecutive unit-norm magnitude
|
||||||
|
// spectra, so it tracks timbre change and not loudness change.
|
||||||
|
export function spectral(x, t, curvePoints = 48) {
|
||||||
|
const all = stft(x), maxE = all.reduce((m, f) => Math.max(m, f.energy), 0), t0 = t.activeStartSample / SR, t1 = t.activeEndSample / SR;
|
||||||
|
const idx = all.map((f, i) => (f.energy >= maxE * 1e-5 && f.t >= t0 - N / 2 / SR && f.t <= t1 + N / 2 / SR ? i : -1)).filter((i) => i >= 0);
|
||||||
|
if (idx.length < 2) return { frames: idx.length, centroidMedianHz: NaN, centroidCurve: [], fluxCurve: [], fluxMean: 0, fluxP95: 0, centroidSpreadOct: 0, envRangeDb: 0, motion: 0, lowMotion: false, lowMotionReason: "fewer than 2 active frames" };
|
||||||
|
const cent = [], flux = [], hz = (k) => (k * SR) / N;
|
||||||
|
let prev = null;
|
||||||
|
for (const i of idx) {
|
||||||
|
const f = all[i]; let num = 0, den = 0;
|
||||||
|
for (let k = 1; k < f.mag.length; k++) { num += hz(k) * f.mag[k]; den += f.mag[k]; }
|
||||||
|
cent.push({ t: f.t, v: den > 0 ? num / den : 0 });
|
||||||
|
const norm = Math.sqrt(f.energy) || 1;
|
||||||
|
if (prev) { let s = 0; for (let k = 1; k < f.mag.length; k++) { const d = Math.max(0, f.mag[k] / norm - prev[k]); s += d * d; } flux.push({ t: f.t, v: Math.sqrt(s) }); }
|
||||||
|
prev = Float64Array.from(f.mag, (m) => m / norm);
|
||||||
|
}
|
||||||
|
const cs = cent.map((c) => Math.log2(Math.max(c.v, 20))), fl = flux.slice(3).map((f) => f.v), fe = idx.map((i) => pdb(all[i].energy));
|
||||||
|
const centroidSpreadOct = sd(cs), envRangeDb = fe.reduce((a, b) => Math.max(a, b), -Infinity) - fe.reduce((a, b) => Math.min(a, b), Infinity), fluxMean = mean(fl.length ? fl : [0]);
|
||||||
|
// Low motion = the sound stays put: timbre barely moves AND level barely moves, over a span long enough to matter.
|
||||||
|
// A decaying bell has a large level range, so it is not flagged; a held drone or a pink wash is. Flux is reported
|
||||||
|
// but not used: stationary noise has high flux (periodogram scatter) while going nowhere.
|
||||||
|
const spanS = t1 - t0, still = centroidSpreadOct < 0.08 && envRangeDb < 6;
|
||||||
|
return {
|
||||||
|
frames: idx.length, centroidMedianHz: median(cent.map((c) => c.v)), centroidCurve: downsample(cent, curvePoints), fluxCurve: downsample(flux, curvePoints),
|
||||||
|
fluxMean, fluxP95: quantile(fl.length ? fl : [0], 0.95),
|
||||||
|
centroidSpreadOct, envRangeDb, motion: Math.min(1, centroidSpreadOct / 0.5 + envRangeDb / 30 + fluxMean / 0.5),
|
||||||
|
lowMotion: spanS >= 1 && still, lowMotionReason: spanS < 1 ? "active span under 1 s: not judged" : still ? `centroid spread ${centroidSpreadOct.toFixed(3)} oct, level range ${envRangeDb.toFixed(1)} dB` : "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peak level in dB per bucket (about `n` buckets across the whole buffer), floored at -90 dB, for the gallery's envelope plot.
|
||||||
|
export function envelopeCurve(x, n = 96) {
|
||||||
|
const env = envelope(x), per = Math.max(1, Math.ceil(env.length / n)), out = [];
|
||||||
|
for (let i = 0; i < env.length; i += per) { let m = 0; for (let j = i; j < Math.min(env.length, i + per); j++) m = Math.max(m, env[j]); out.push({ t: ((i + per / 2) * HOP) / SR, v: Math.max(-90, db(m)) }); }
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- one buffer -> one record ------------------------------------------------------------------------------------
|
||||||
|
export function measure(x, { curvePoints = 48 } = {}) {
|
||||||
|
const peak = samplePeak(x), nonFinite = x.reduce((n, v) => n + (Number.isFinite(v) ? 0 : 1), 0);
|
||||||
|
if (nonFinite) return { nonFinite, silent: false, broken: true };
|
||||||
|
const t = timing(x);
|
||||||
|
if (t.silent) return { silent: true, nonFinite: 0, samplePeak: peak };
|
||||||
|
const tp = truePeak(x), c = crest(x, t), sp = spectral(x, t, curvePoints), clip = clipping(x), nt = noiseToTone(x, t);
|
||||||
|
return {
|
||||||
|
silent: false, nonFinite: 0, samplePeak: peak, samplePeakDb: db(peak), truePeak: tp, truePeakDb: db(tp), ...clip,
|
||||||
|
loudnessLufs: integratedLoudness(x), rms: c.rms, rmsDb: c.rmsDb, crestDb: c.crestDb,
|
||||||
|
onsetMs: t.onsetMs, attackMs: t.attackMs, attackSlopeDbPerMs: t.attackSlopeDbPerMs, attackResolutionLimited: t.resolutionLimited, attackWindowLimited: t.attackWindowLimited, peakAtMs: t.peakAtMs,
|
||||||
|
activeMs: ((t.activeEndSample - t.activeStartSample) / SR) * 1000, noiseToToneDb: nt.db, noiseToToneADb: nt.dbA, noiseToToneWindow: nt.window, envelopeCurve: envelopeCurve(x), ...sp,
|
||||||
|
};
|
||||||
|
}
|
||||||
7
tools/aqua-feel/package.json
Normal file
7
tools/aqua-feel/package.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "aqua-feel",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"description": "Headless feel meter for AquaSynth: renders every shipped patch and Ding offline and measures them. Measures; does not listen.",
|
||||||
|
"scripts": { "test": "node test.mjs", "feel": "node feel.mjs" }
|
||||||
|
}
|
||||||
70
tools/aqua-feel/render.js
Normal file
70
tools/aqua-feel/render.js
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Offline render through the same faustwasm runtime the playground and the listening trial load (not the daemon's
|
||||||
|
// native libfaust path: same Faust source, same compiler version family, but a different host; see README).
|
||||||
|
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
||||||
|
import { dirname, join, resolve } from "node:path";
|
||||||
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||||
|
import { SR } from "./metrics.js";
|
||||||
|
|
||||||
|
const here = dirname(fileURLToPath(import.meta.url));
|
||||||
|
export const playground = resolve(here, "../aqua-playground");
|
||||||
|
|
||||||
|
export async function loadRuntime() {
|
||||||
|
const fw = join(playground, "node_modules/@grame/faustwasm");
|
||||||
|
const mod = await import(pathToFileURL(join(fw, "dist/esm/index.js")).href);
|
||||||
|
const libfaust = new mod.LibFaust(await mod.instantiateFaustModuleFromFile(join(fw, "libfaust-wasm/libfaust-wasm.js")));
|
||||||
|
return { mod, libfaust, compiler: new mod.FaustCompiler(libfaust), version: libfaust.version(), cache: new Map() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compiled generator per DSP text (a Ding event reuses one compile for all its notes).
|
||||||
|
export async function compile(rt, name, source) {
|
||||||
|
if (rt.cache.has(source)) return rt.cache.get(source);
|
||||||
|
const g = new rt.mod.FaustMonoDspGenerator();
|
||||||
|
if (!(await g.compile(rt.compiler, name.replace(/[^A-Za-z0-9_]/g, "_"), source, "-ftz 2"))) throw new Error(`compile ${name}: ${rt.compiler.getErrorMessage()}`);
|
||||||
|
const controls = [];
|
||||||
|
const walk = (items) => { for (const it of items ?? []) { if (it.items) walk(it.items); else if (it.address) controls.push({ label: it.label, address: it.address }); } };
|
||||||
|
walk(JSON.parse(g.factory.json).ui);
|
||||||
|
const entry = { g, controls };
|
||||||
|
rt.cache.set(source, entry);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render `seconds` of one generator with named controls; returns channel 0 and the channel count.
|
||||||
|
// Multi-channel output is reported, not mixed, so a metric always describes a buffer someone can hear.
|
||||||
|
export async function renderOnce(entry, params, seconds) {
|
||||||
|
const p = await entry.g.createOfflineProcessor(SR, 512);
|
||||||
|
for (const [label, value] of Object.entries(params ?? {})) {
|
||||||
|
const bare = (t) => t.replace(/^\//, ""), c = entry.controls.find((x) => bare(x.label) === bare(label) || x.address === label);
|
||||||
|
if (!c) throw new Error(`no control "${label}" (has ${entry.controls.map((x) => x.label).join(", ") || "none"})`);
|
||||||
|
p.setParamValue(c.address, value);
|
||||||
|
}
|
||||||
|
const out = p.render(null, Math.round(SR * seconds));
|
||||||
|
return { samples: out[0], channels: out.length };
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Ding event is the instrument's note rendered per DingNote at root * 2^(semitones/12), scaled by commandGain * note.gain
|
||||||
|
// and mixed at the note's delay: DingsPlaybackHost's recipe, minus the audio device.
|
||||||
|
export async function renderDing(rt, instrument, instrumentSource, event, { noteSeconds, commandGain }) {
|
||||||
|
const entry = await compile(rt, `ding_${instrument.id}`, instrumentSource);
|
||||||
|
const last = Math.max(...event.notes.map((n) => n.delayMilliseconds));
|
||||||
|
const mix = new Float32Array(Math.round(SR * (noteSeconds + last / 1000)));
|
||||||
|
for (const n of event.notes) {
|
||||||
|
const hz = instrument.rootFrequency * 2 ** (n.semitones / 12);
|
||||||
|
const { samples } = await renderOnce(entry, { [instrument.controlPath]: hz }, noteSeconds);
|
||||||
|
const off = Math.round((n.delayMilliseconds / 1000) * SR), g = commandGain * n.gain;
|
||||||
|
for (let i = 0; i < samples.length && off + i < mix.length; i++) mix[off + i] += samples[i] * g;
|
||||||
|
}
|
||||||
|
return { samples: mix, channels: 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 32-bit float mono WAV, so overs above 1.0 survive to disk instead of being clamped on the way out.
|
||||||
|
export function wavFloat32(samples) {
|
||||||
|
const dataBytes = samples.length * 4, b = Buffer.alloc(44 + dataBytes);
|
||||||
|
b.write("RIFF", 0); b.writeUInt32LE(36 + dataBytes, 4); b.write("WAVEfmt ", 8); b.writeUInt32LE(16, 16);
|
||||||
|
b.writeUInt16LE(3, 20); b.writeUInt16LE(1, 22); b.writeUInt32LE(SR, 24); b.writeUInt32LE(SR * 4, 28); b.writeUInt16LE(4, 32); b.writeUInt16LE(32, 34);
|
||||||
|
b.write("data", 36); b.writeUInt32LE(dataBytes, 40);
|
||||||
|
for (let i = 0; i < samples.length; i++) b.writeFloatLE(samples[i], 44 + i * 4);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
export function writeWav(path, samples) { mkdirSync(dirname(path), { recursive: true }); writeFileSync(path, wavFloat32(samples)); }
|
||||||
|
|
||||||
|
export const readText = (p) => readFileSync(p, "utf8");
|
||||||
46
tools/aqua-feel/report.js
Normal file
46
tools/aqua-feel/report.js
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Before/after table and the listening-trial correlation.
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { mean, spearman, permutationP } from "../aqua-trial/analyze.mjs";
|
||||||
|
import { variants, SECONDS } from "../aqua-trial/logic.js";
|
||||||
|
import { measure } from "./metrics.js";
|
||||||
|
import { loadRuntime, compile, renderOnce } from "./render.js";
|
||||||
|
|
||||||
|
export const COMPARE_METRICS = ["samplePeak", "truePeakDb", "overSamples", "loudnessLufs", "crestDb", "centroidMedianHz", "noiseToToneDb", "noiseToToneADb", "onsetMs", "attackMs", "lowMotion"];
|
||||||
|
const f = (v) => (typeof v === "number" ? (Number.isInteger(v) ? String(v) : v.toFixed(2)) : String(v));
|
||||||
|
|
||||||
|
export function compareTable(before, after, only) {
|
||||||
|
const b = new Map(before.items.map((i) => [i.id, i])), out = [];
|
||||||
|
for (const a of after.items) {
|
||||||
|
if ((only && !a.id.includes(only)) || !b.has(a.id) || !a.metrics || !b.get(a.id).metrics) continue;
|
||||||
|
out.push(`${a.id}`);
|
||||||
|
for (const k of COMPARE_METRICS) {
|
||||||
|
const x = b.get(a.id).metrics[k], y = a.metrics[k];
|
||||||
|
if (x === undefined) continue;
|
||||||
|
const d = typeof x === "number" && typeof y === "number" ? (y - x >= 0 ? "+" : "") + (y - x).toFixed(2) : x === y ? "" : "changed";
|
||||||
|
out.push(` ${k.padEnd(18)} ${f(x).padStart(10)} -> ${f(y).padStart(10)} ${d}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out.length ? out.join("\n") : "no common items";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Joins trial exports (aqua-trial) to feel metrics by rendering the same four variants, then asks the trial's own question
|
||||||
|
// of each metric: does it rank-correlate with the ratings (|rho| >= 0.5 and p < 0.05, fixed before any data)? With few
|
||||||
|
// listeners this is deliberately hard to meet; "no evidence" is the expected answer, and it is not evidence of absence.
|
||||||
|
export function correlate(exports, trialDir) {
|
||||||
|
return correlateAsync(exports, trialDir);
|
||||||
|
}
|
||||||
|
async function correlateAsync(exports, trialDir) {
|
||||||
|
const rt = await loadRuntime(), entry = await compile(rt, "thruster_trial", readFileSync(join(trialDir, "thruster-trial.dsp"), "utf8"));
|
||||||
|
const metricsBy = {};
|
||||||
|
for (const v of variants()) metricsBy[v.id] = measure((await renderOnce(entry, v.params, SECONDS)).samples);
|
||||||
|
const records = exports.flatMap((e) => e.records);
|
||||||
|
const lines = [`${exports.length} listener export(s), ${records.length} ratings; variants rendered here at ${SECONDS} s`, "", "metric rho p n -> predicts?"];
|
||||||
|
for (const k of ["noiseToToneDb", "noiseToToneADb", "loudnessLufs", "samplePeak", "rms", "centroidMedianHz", "fluxMean", "crestDb"]) {
|
||||||
|
const xs = records.map((r) => metricsBy[r.variant][k]), ys = records.map((r) => r.rating), rho = spearman(xs, ys), p = permutationP(xs, ys, rho);
|
||||||
|
lines.push(`${k.padEnd(18)} ${Number.isFinite(rho) ? rho.toFixed(2).padStart(6) : " n/a"} ${Number.isFinite(p) ? p.toFixed(3).padStart(6) : " n/a"} ${String(xs.length).padStart(3)} -> ${Number.isFinite(rho) && Math.abs(rho) >= 0.5 && p < 0.05 ? "PREDICTS" : "no evidence it predicts"}`);
|
||||||
|
}
|
||||||
|
lines.push("", `mean rating by variant: ${variants().map((v) => `${v.id} ${mean(records.filter((r) => r.variant === v.id).map((r) => r.rating)).toFixed(2)}`).join(", ")}`);
|
||||||
|
lines.push("Four variants are four distinct metric values, so every rank correlation here is really a statement about the variant ordering. Nothing was heard by this script.");
|
||||||
|
return lines.join("\n");
|
||||||
|
}
|
||||||
190
tools/aqua-feel/test.mjs
Normal file
190
tools/aqua-feel/test.mjs
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
// Checks of the meter itself on synthetic signals with known answers, the gate's waiver bookkeeping, the WAV writer, the
|
||||||
|
// gallery, and the catalog. It renders no patch; `node feel.mjs run` does that. Nothing here hears anything.
|
||||||
|
// Usage: node test.mjs
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { dirname, join } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { SR, measure, truePeak, samplePeak, integratedLoudness, clipping, fft, timing } from "./metrics.js";
|
||||||
|
import { gate } from "./gate.js";
|
||||||
|
import { gallery } from "./gallery.js";
|
||||||
|
import { wavFloat32 } from "./render.js";
|
||||||
|
import { patchItems, dingItems } from "./catalog.js";
|
||||||
|
|
||||||
|
const here = dirname(fileURLToPath(import.meta.url));
|
||||||
|
let n = 0;
|
||||||
|
const t = (name, fn) => { fn(); n++; console.log(`ok ${name}`); };
|
||||||
|
const sine = (hz, amp, sec, ph = 0) => Float32Array.from({ length: Math.round(SR * sec) }, (_, i) => amp * Math.sin(2 * Math.PI * hz * i / SR + ph));
|
||||||
|
let seed = 1; const rand = () => { seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0; return seed / 2 ** 32 - 0.5; };
|
||||||
|
const noise = (amp, sec) => Float32Array.from({ length: Math.round(SR * sec) }, () => amp * 2 * rand());
|
||||||
|
const decay = (x, tau) => x.map((v, i) => v * Math.exp(-i / SR / tau));
|
||||||
|
const near = (a, b, tol, msg) => assert.ok(Math.abs(a - b) <= tol, `${msg}: ${a} vs ${b} (+/-${tol})`);
|
||||||
|
|
||||||
|
t("fft: a bin-centred cosine lands in its bin", () => {
|
||||||
|
const re = Float64Array.from({ length: 256 }, (_, i) => Math.cos(2 * Math.PI * 16 * i / 256)), im = new Float64Array(256);
|
||||||
|
fft(re, im); near(Math.hypot(re[16], im[16]), 128, 1e-9, "bin 16"); near(Math.hypot(re[17], im[17]), 0, 1e-9, "bin 17");
|
||||||
|
});
|
||||||
|
|
||||||
|
t("levels: a 0.5 sine reads peak 0.5, rms -9.03 dB, crest 3.01 dB, loudness -9.7 LUFS (BS.1770)", () => {
|
||||||
|
const m = measure(sine(440, 0.5, 2));
|
||||||
|
near(m.samplePeak, 0.5, 1e-6, "peak"); near(m.rmsDb, -9.03, 0.05, "rms"); near(m.crestDb, 3.01, 0.05, "crest"); near(m.loudnessLufs, -9.7, 0.2, "LUFS");
|
||||||
|
});
|
||||||
|
|
||||||
|
t("loudness: -6 dB is -6 LU; silence is null", () => {
|
||||||
|
near(integratedLoudness(sine(1000, 0.5, 2)) - integratedLoudness(sine(1000, 0.25, 2)), 6.02, 0.01, "6 dB step");
|
||||||
|
assert.equal(integratedLoudness(new Float32Array(SR)), null);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("loudness: gating drops the silent tail (the naive 3 s mean would read 7.8 dB lower); block-edge dilution stays under 2 dB", () => {
|
||||||
|
const x = new Float32Array(SR * 3); x.set(sine(1000, 0.5, 0.5));
|
||||||
|
near(integratedLoudness(x), integratedLoudness(sine(1000, 0.5, 0.5)), 2, "gated vs bare");
|
||||||
|
});
|
||||||
|
|
||||||
|
t("true peak: a tone sampled between its crests exceeds the sample peak; a sample-aligned one does not", () => {
|
||||||
|
const x = sine(12000, 0.9, 0.1, Math.PI / 4); // samples sit 45 degrees off the crests
|
||||||
|
assert.ok(samplePeak(x) < 0.7 && truePeak(x) > 0.85 && truePeak(x) < 0.95, `${samplePeak(x)} ${truePeak(x)}`);
|
||||||
|
const y = sine(1000, 0.5, 0.1); near(truePeak(y), 0.5, 0.01, "oversampling must not invent peaks on a slow tone");
|
||||||
|
});
|
||||||
|
|
||||||
|
t("clipping: counts samples over 1.0 and flat-top runs, ignores a clean 0.99", () => {
|
||||||
|
const x = sine(100, 2, 0.1).map((v) => Math.max(-1.2, Math.min(1.2, v)));
|
||||||
|
const c = clipping(x); assert.ok(c.overSamples > 100 && c.flatTopRuns > 0);
|
||||||
|
const y = clipping(sine(100, 0.99, 0.1)); assert.equal(y.overSamples, 0); assert.equal(y.flatTopRuns, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("timing: onset latency is where the sound starts, attack is the first-note rise", () => {
|
||||||
|
const x = new Float32Array(SR); x.set(sine(1000, 0.5, 0.5).map((v, i) => v * Math.min(1, i / (0.01 * SR))), Math.round(0.02 * SR));
|
||||||
|
const tm = timing(x); near(tm.onsetMs, 20 + 1, 1.5, "onset"); near(tm.attackMs, 8, 1.5, "10-90% of a 10 ms ramp is 8 ms");
|
||||||
|
});
|
||||||
|
|
||||||
|
t("timing: a two-note event reports the first note's attack, even when a louder note follows 200 ms later before the first has decayed", () => {
|
||||||
|
const x = new Float32Array(SR * 2);
|
||||||
|
x.set(decay(sine(500, 0.3, 0.6), 0.5)); const b = decay(sine(800, 0.9, 0.5), 0.1); for (let i = 0; i < b.length; i++) x[Math.round(0.2 * SR) + i] += b[i];
|
||||||
|
assert.ok(timing(x).attackMs < 3, `${timing(x).attackMs}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("timing: a swell longer than the 150 ms attack window is flagged window-limited", () => {
|
||||||
|
assert.equal(timing(sine(220, 0.5, 2).map((v, i) => v * i / (2 * SR))).attackWindowLimited, true);
|
||||||
|
assert.equal(timing(decay(sine(220, 0.5, 1), 0.2)).attackWindowLimited, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("attack: a low tone's ramp is not mistaken for its zero crossings (261 Hz, 30 ms ramp; 220 Hz, 2 s swell)", () => {
|
||||||
|
const ramp = (hz, ms) => { const x = sine(hz, 0.5, 1); return x.map((v, i) => v * Math.min(1, i / (ms / 1000 * SR))); };
|
||||||
|
for (const hz of [130, 261.6, 523, 1000]) near(timing(ramp(hz, 30)).attackMs, 24, 3, `${hz} Hz, 30 ms ramp`);
|
||||||
|
assert.ok(timing(sine(220, 0.5, 2).map((v, i) => v * i / (2 * SR))).attackMs > 100, "a 2 s swell is not a click");
|
||||||
|
});
|
||||||
|
|
||||||
|
t("silent: a -80 dBFS tone is silent, a -60 dBFS tone is not", () => {
|
||||||
|
assert.equal(measure(sine(1000, 1e-4, 1)).silent, true); assert.equal(measure(sine(1000, 1e-3, 1)).silent, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("noise-to-tone: a sine reads far tonal, white noise far noisy, and the A-weighted ratio sees a quiet 1 kHz hiss under a loud 40 Hz tone", () => {
|
||||||
|
assert.ok(measure(sine(440, 0.5, 2)).noiseToToneDb < -30);
|
||||||
|
assert.ok(measure(noise(0.3, 2)).noiseToToneDb > 20);
|
||||||
|
const low = sine(40, 0.5, 3), hiss = noise(0.02, 3), x = low.map((v, i) => v + hiss[i]), m = measure(x);
|
||||||
|
assert.ok(m.noiseToToneADb > m.noiseToToneDb + 8, `flat ${m.noiseToToneDb}, A-weighted ${m.noiseToToneADb}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("noise-to-tone: a noise-excited resonance is not called a tone, a steady sine is (temporal smoothness)", () => {
|
||||||
|
// two-pole resonator at 600 Hz, Q~25, fed with noise: spectrally narrow but randomly modulated
|
||||||
|
const w = 2 * Math.PI * 600 / SR, r = 1 - w / 50, a1 = -2 * r * Math.cos(w), a2 = r * r, e = noise(1, 3), y = new Float32Array(e.length);
|
||||||
|
for (let i = 2; i < y.length; i++) y[i] = e[i] - a1 * y[i - 1] - a2 * y[i - 2];
|
||||||
|
const peak = samplePeak(y); const ring = y.map((v) => (0.4 * v) / peak);
|
||||||
|
assert.ok(measure(ring).noiseToToneDb > measure(sine(600, 0.4, 3)).noiseToToneDb + 25, "ringing noise must read noisier than a sine");
|
||||||
|
});
|
||||||
|
|
||||||
|
t("low motion: a held tone and held noise are flagged; a decaying bell is not; under 1 s is not judged", () => {
|
||||||
|
assert.equal(measure(sine(440, 0.5, 2)).lowMotion, true);
|
||||||
|
assert.equal(measure(noise(0.3, 2)).lowMotion, true);
|
||||||
|
assert.equal(measure(decay(sine(880, 0.5, 2), 0.25)).lowMotion, false);
|
||||||
|
assert.equal(measure(sine(440, 0.5, 0.6)).lowMotion, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("centroid: follows a sweep upward", () => {
|
||||||
|
const x = Float32Array.from({ length: SR * 2 }, (_, i) => 0.5 * Math.sin(2 * Math.PI * (200 * i / SR + 1000 * (i / SR) ** 2 / 2)));
|
||||||
|
const c = measure(x).centroidCurve; assert.ok(c.at(-2).v > c[1].v * 3, `${c[1].v} -> ${c.at(-2).v}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("silence and non-finite: reported, not crashed on", () => {
|
||||||
|
assert.equal(measure(new Float32Array(SR)).silent, true);
|
||||||
|
const x = sine(440, 0.5, 0.1); x[10] = NaN; const m = measure(x); assert.equal(m.broken, true); assert.equal(m.nonFinite, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
const item = (id, group, metrics) => ({ id, group, metrics: { nonFinite: 0, silent: false, overSamples: 0, truePeakDb: -6, lowMotion: false, ...metrics } });
|
||||||
|
const rules = [{ group: "all", metric: "overSamples", equals: 0, why: "clips" }, { group: "ding", metric: "onsetMs", max: 5, why: "late" }, { group: "all", metric: "truePeakDb", max: -1, level: "warn", why: "headroom" }];
|
||||||
|
|
||||||
|
t("gate: failing rule fails; group scoping respected; warn does not fail", () => {
|
||||||
|
const g = gate({ items: [item("a", "patch", { overSamples: 3, onsetMs: 50 }), item("b", "ding", { onsetMs: 50 }), item("c", "patch", { truePeakDb: -0.5 })] }, { rules });
|
||||||
|
assert.equal(g.pass, false); assert.deepEqual(g.failures.map((f) => `${f.id}:${f.metric}`), ["a:overSamples", "b:onsetMs"]); assert.equal(g.warnings.length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("gate: a waiver downgrades a failure, a stale waiver fails, partial runs skip the stale check", () => {
|
||||||
|
const rep = { items: [item("a", "patch", { overSamples: 3 }), item("c", "patch", {})] };
|
||||||
|
const waivers = [{ id: "a", metric: "overSamples", reason: "r" }];
|
||||||
|
assert.equal(gate(rep, { rules, waivers }).pass, true); assert.equal(gate(rep, { rules, waivers }).waived.length, 1);
|
||||||
|
const stale = gate(rep, { rules, waivers: [...waivers, { id: "c", metric: "overSamples", reason: "r" }] });
|
||||||
|
assert.equal(stale.pass, false); assert.match(stale.failures[0].why, /stale waiver/);
|
||||||
|
assert.equal(gate(rep, { rules, waivers: [{ id: "c", metric: "overSamples", reason: "r" }] }, { partial: true }).pass, false); // a still fails
|
||||||
|
});
|
||||||
|
|
||||||
|
t("gate: a waiver covers only up to its stated bound; a numeric waiver without a bound covers nothing", () => {
|
||||||
|
const r = [{ group: "all", metric: "truePeakDb", max: 0, why: "tp" }];
|
||||||
|
const mk = (tp) => ({ items: [item("g", "patch", { truePeakDb: tp })] });
|
||||||
|
const w = [{ id: "g", metric: "truePeakDb", max: 1, reason: "r" }];
|
||||||
|
assert.equal(gate(mk(0.9), { rules: r, waivers: w }).pass, true);
|
||||||
|
assert.equal(gate(mk(6), { rules: r, waivers: w }).pass, false);
|
||||||
|
assert.equal(gate(mk(0.9), { rules: r, waivers: [{ id: "g", metric: "truePeakDb", reason: "r" }] }).pass, false);
|
||||||
|
assert.equal(gate(mk(null), { rules: r, waivers: w }).pass, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("gate: a rule whose id matches no item fails a full run (disarmed guard) and is skipped on a partial run; a missing metric fails", () => {
|
||||||
|
const th = { rules: [{ group: "patch", id: "thruster@rpm600", metric: "truePeakDb", max: 0, why: "w" }] };
|
||||||
|
const rep = { items: [item("thr@rpm600", "patch", {})] };
|
||||||
|
assert.equal(gate(rep, th).pass, false); assert.match(gate(rep, th).failures[0].detail, /matches no item/);
|
||||||
|
assert.equal(gate(rep, th, { partial: true }).pass, true);
|
||||||
|
assert.equal(gate({ items: [item("a", "patch", {})] }, { rules: [{ group: "all", metric: "lowMotion", equals: false, why: "w" }] }).pass, true);
|
||||||
|
const gone = item("a", "patch", {}); delete gone.metrics.lowMotion;
|
||||||
|
assert.equal(gate({ items: [gone] }, { rules: [{ group: "all", metric: "lowMotion", equals: false, why: "w" }] }).pass, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("gate: a silent item fails once and does not also trip every other rule", () => {
|
||||||
|
const g = gate({ items: [{ id: "s", group: "patch", metrics: { silent: true, nonFinite: 0, samplePeak: 0 } }] }, { rules: [{ group: "patch", metric: "onsetMs", max: 5, why: "w" }] });
|
||||||
|
assert.equal(g.failures.length, 1); assert.equal(g.failures[0].metric, "silent");
|
||||||
|
});
|
||||||
|
|
||||||
|
t("gate: a render error, NaN output, or glob rule id is handled", () => {
|
||||||
|
const g = gate({ items: [{ id: "x", group: "patch", error: "boom" }, { id: "y", group: "patch", metrics: { broken: true, nonFinite: 2 } }] }, { rules });
|
||||||
|
assert.equal(g.failures.length, 2);
|
||||||
|
const g2 = gate({ items: [item("thruster@rpm300", "patch", { onsetMs: 1, ntr: 9 })] }, { rules: [{ group: "patch", id: "thruster@*", metric: "ntr", max: 5, why: "w" }] });
|
||||||
|
assert.equal(g2.pass, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("thresholds.json: every rule names a metric and a why; every waiver names a reason and a follow-up", () => {
|
||||||
|
const th = JSON.parse(readFileSync(join(here, "thresholds.json"), "utf8"));
|
||||||
|
for (const r of th.rules) { assert.ok(r.metric && r.why && ["all", "patch", "ding"].includes(r.group), JSON.stringify(r)); assert.ok(["min", "max", "equals"].some((k) => k in r)); }
|
||||||
|
for (const w of th.waivers) assert.ok(w.id && w.metric && w.reason && /^aqua-body:follow_up:/.test(w.followUp), JSON.stringify(w));
|
||||||
|
for (const w of th.waivers) {
|
||||||
|
const rules = th.rules.filter((r) => r.metric === w.metric && (r.group === "all" || r.group === "patch"));
|
||||||
|
assert.ok(rules.every((r) => r.equals !== undefined || "max" in w || "min" in w), `numeric waiver without a bound: ${JSON.stringify(w)}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
t("wav: 32-bit float mono header, length, and samples above 1.0 survive", () => {
|
||||||
|
const b = wavFloat32(Float32Array.from([0, 2.5, -1.5]));
|
||||||
|
assert.equal(b.toString("ascii", 0, 4), "RIFF"); assert.equal(b.readUInt16LE(20), 3); assert.equal(b.readUInt16LE(34), 32); assert.equal(b.readUInt32LE(24), SR);
|
||||||
|
assert.equal(b.readUInt32LE(40), 12); assert.equal(b.readFloatLE(48), 2.5); assert.equal(b.length, 44 + 12);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("catalog: the playground patches with the thruster fanned to four rpm, and 9 events on 4 instruments (36 Dings)", () => {
|
||||||
|
const p = patchItems(), d = dingItems();
|
||||||
|
assert.equal(p.filter((i) => i.id.startsWith("thruster")).length, 4); assert.ok(p.length >= 41 && p.every((i) => i.source.includes("process")));
|
||||||
|
assert.equal(d.length, 36); assert.ok(new Set(d.map((i) => i.id)).size === 36);
|
||||||
|
});
|
||||||
|
|
||||||
|
t("gallery: says on its face that nothing was heard, escapes ids, marks failing metrics", () => {
|
||||||
|
const rep = { sampleRate: SR, libfaust: "x", gate: { pass: false, failures: [{ id: "a<b", metric: "overSamples" }], waived: [], warnings: [] }, items: [{ id: "a<b", group: "patch", wav: "wav/a.wav", metrics: { ...measure(sine(440, 0.5, 0.3)), overSamples: 9 } }, { id: "z", group: "ding", error: "no" }] };
|
||||||
|
const html = gallery(rep);
|
||||||
|
assert.match(html, /Nothing here was heard/); assert.ok(!html.includes("a<b")); assert.match(html, /class="fail"/); assert.match(html, /did not render/);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`${n} passed`);
|
||||||
154
tools/aqua-feel/thresholds.json
Normal file
154
tools/aqua-feel/thresholds.json
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
{
|
||||||
|
"schema": "aqua-feel.thresholds.v1",
|
||||||
|
"note": "Regression tripwires, not taste. A threshold here says 'a render this far out is worth a listener's ear before it ships'; none says a sound is good. Every number was chosen after looking at the shipped renders (see README), and every waiver names the reason and a follow-up.",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"group": "all",
|
||||||
|
"metric": "nonFinite",
|
||||||
|
"equals": 0,
|
||||||
|
"why": "NaN or Inf in a render"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "all",
|
||||||
|
"metric": "silent",
|
||||||
|
"equals": false,
|
||||||
|
"why": "an item that renders nothing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "all",
|
||||||
|
"metric": "overSamples",
|
||||||
|
"equals": 0,
|
||||||
|
"why": "samples beyond +/-1.0 clip at a converter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "all",
|
||||||
|
"metric": "truePeakDb",
|
||||||
|
"max": 0,
|
||||||
|
"why": "inter-sample peak above 0 dBTP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "all",
|
||||||
|
"metric": "truePeakDb",
|
||||||
|
"max": -1,
|
||||||
|
"level": "warn",
|
||||||
|
"why": "under 1 dB of true-peak headroom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "all",
|
||||||
|
"metric": "lowMotion",
|
||||||
|
"equals": false,
|
||||||
|
"why": "a span of 1 s or more that goes nowhere in level or timbre (the song-swarm collapse shape)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "patch",
|
||||||
|
"metric": "loudnessLufs",
|
||||||
|
"max": -6,
|
||||||
|
"why": "integrated loudness within 6 LU of full scale"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "patch",
|
||||||
|
"metric": "loudnessLufs",
|
||||||
|
"min": -55,
|
||||||
|
"why": "nearly inaudible next to the rest of the set"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "patch",
|
||||||
|
"metric": "onsetMs",
|
||||||
|
"max": 25,
|
||||||
|
"why": "first sample within 40 dB of the peak (and above -70 dBFS) later than 25 ms after note-on"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "ding",
|
||||||
|
"metric": "loudnessLufs",
|
||||||
|
"max": -12,
|
||||||
|
"min": -40,
|
||||||
|
"why": "a notification louder than -12 or quieter than -40 LUFS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "ding",
|
||||||
|
"metric": "truePeakDb",
|
||||||
|
"max": -6,
|
||||||
|
"why": "a notification within 6 dB of full scale"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "ding",
|
||||||
|
"metric": "onsetMs",
|
||||||
|
"max": 5,
|
||||||
|
"why": "a notification whose first sample within 40 dB of the peak comes later than 5 ms after note-on"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "ding",
|
||||||
|
"metric": "attackMs",
|
||||||
|
"max": 12,
|
||||||
|
"why": "first-note attack slower than 12 ms (DingCatalog notes attack in 1-6 ms)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "ding",
|
||||||
|
"metric": "crestDb",
|
||||||
|
"max": 15,
|
||||||
|
"why": "spiky next to its own body"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "ding",
|
||||||
|
"metric": "noiseToToneADb",
|
||||||
|
"max": 10,
|
||||||
|
"why": "DingCatalog forbids broadband noise; A-weighted noise should clearly not dominate tone (heuristic split: gliding partials read as noise, so 10 not 0)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "patch",
|
||||||
|
"id": "thruster@rpm300",
|
||||||
|
"metric": "noiseToToneADb",
|
||||||
|
"max": 16,
|
||||||
|
"why": "thruster low-rpm hiss: before the 2026-11-05 mix change this was 20.7 dB; the cap sits 2-3 dB over the 13.1 dB measured after it, so the hiss cannot creep back unnoticed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "patch",
|
||||||
|
"id": "thruster@rpm600",
|
||||||
|
"metric": "noiseToToneADb",
|
||||||
|
"max": 8,
|
||||||
|
"why": "thruster low-rpm hiss: before the 2026-11-05 mix change this was 23.0 dB; the cap sits 2-3 dB over the 5.0 dB measured after it, so the hiss cannot creep back unnoticed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "patch",
|
||||||
|
"id": "thruster@rpm1200",
|
||||||
|
"metric": "noiseToToneADb",
|
||||||
|
"max": 15,
|
||||||
|
"why": "thruster low-rpm hiss: before the 2026-11-05 mix change this was 20.1 dB; the cap sits 2-3 dB over the 12.7 dB measured after it, so the hiss cannot creep back unnoticed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "patch",
|
||||||
|
"id": "dx7-public-domain-prc-synth1-calibrated",
|
||||||
|
"metric": "samplePeak",
|
||||||
|
"max": 0.9,
|
||||||
|
"why": "PRC SYNTH1 peaked at 2.10 before the 2026-11-05 gain trim to 0.67; this holds it under 0.9"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"waivers": [
|
||||||
|
{
|
||||||
|
"id": "sfxr-classic-classic-sfxr-abstract-golf",
|
||||||
|
"metric": "truePeakDb",
|
||||||
|
"reason": "renders 0.99 sample peak and +0.9 dBTP inter-sample",
|
||||||
|
"followUp": "aqua-body:follow_up:sfxr-golf-hot",
|
||||||
|
"max": 1.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sfxr-classic-classic-sfxr-abstract-golf",
|
||||||
|
"metric": "loudnessLufs",
|
||||||
|
"reason": "-5.3 LUFS integrated over the 3 s render",
|
||||||
|
"followUp": "aqua-body:follow_up:sfxr-golf-hot",
|
||||||
|
"max": -5.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sfxr-classic-classic-sfxr-abstract-golf",
|
||||||
|
"metric": "lowMotion",
|
||||||
|
"reason": "3 s of steady bright noise, 4 dB level range; may be longer than the render window",
|
||||||
|
"followUp": "aqua-body:follow_up:sfxr-golf-hot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sfxr-explosion",
|
||||||
|
"metric": "lowMotion",
|
||||||
|
"reason": "3 s of steady noise, 1 dB level range, no decay inside the render window; an explosion that does not decay is worth a listener's check",
|
||||||
|
"followUp": "aqua-body:follow_up:sfxr-explosion-static"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
2
tools/aqua-playground/.gitignore
vendored
Normal file
2
tools/aqua-playground/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
34
tools/aqua-playground/README.md
Normal file
34
tools/aqua-playground/README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# AquaSynth Playground
|
||||||
|
|
||||||
|
Static, serverless audition page for AquaSynth patches. Pick a patch, read the Faust it lowers to,
|
||||||
|
move the sliders the DSP itself declares, press Play.
|
||||||
|
|
||||||
|
**Status: unheard, and never run in a browser.** `node test.mjs` is the only check that has run: it
|
||||||
|
compiles each patch to wasm in Node and renders it offline (non-silent, finite). No patch has been listened to;
|
||||||
|
the page itself, its AudioWorklet path, sliders and layout have not been opened in any browser. One patch
|
||||||
|
clipped until 2026-11-05: `dx7-public-domain-prc-synth1-calibrated` peaked at 2.10 (patch gain trimmed 1.4 → 0.45, now 0.67; `tools/aqua-feel` measures it). Browser verification is open as
|
||||||
|
`aqua-body:follow_up:playground-browser-verify`.
|
||||||
|
|
||||||
|
**What is live:** 42 patches (`catalog.json`): the hand-written Faust `thruster` (with its `rpm`
|
||||||
|
control) and 41 of the 43 `.aqua` files, lowered by `tools/AquaLower` through `FaustEmitter.EmitScript`
|
||||||
|
(the lowering the daemon uses). Faust is compiled to WebAssembly in the page
|
||||||
|
(`@grame/faustwasm` 0.20.0, libfaust 2.90), played through an AudioWorklet, and the lowered source
|
||||||
|
is shown in an editable box with a Recompile button.
|
||||||
|
|
||||||
|
**What it is not:** `.aqua` is lowered ahead of time (`lowered/`), not in the page; editing Faust works,
|
||||||
|
editing `.aqua` does not. `advanced/additive-pad` and `advanced/electro-drop-demo` lower to ~1.5 MB of
|
||||||
|
Faust each and are left out. One-shot patches sound once per Play press and mostly declare no
|
||||||
|
controls, so their slider panel is empty.
|
||||||
|
|
||||||
|
## Entry points
|
||||||
|
|
||||||
|
- `lower.sh`: regenerate `lowered/*.dsp` and `catalog.json` (needs .NET SDK and `CULTLIB_ROOT`).
|
||||||
|
- `node build.mjs [outDir]`: assemble the site in `dist/` (run `npm install` first).
|
||||||
|
- `node test.mjs [dist]`: headless check of `dist/`: each patch compiles to wasm, renders 2 s
|
||||||
|
offline, is non-silent and finite; the thruster is also run at rpm 600 and 4800.
|
||||||
|
It cannot hear and does not run an AudioWorklet or a browser.
|
||||||
|
- `web/`: the page (`index.html`, `app.js`, `ui.js`, `styles.css`).
|
||||||
|
|
||||||
|
Serve `dist/` from any static host (`python3 -m http.server`); AudioWorklet needs `localhost` or https.
|
||||||
|
The older `tools/vocal-tract-playground` renders server-side through the .NET renderer; this one
|
||||||
|
does not need a server.
|
||||||
18
tools/aqua-playground/build.mjs
Normal file
18
tools/aqua-playground/build.mjs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Assembles dist/ (the static site): web/, lowered/ patches + catalog.json, and the faustwasm runtime
|
||||||
|
// (ESM wrapper + libfaust wasm) copied from node_modules. Usage: node build.mjs [outDir]
|
||||||
|
import { cpSync, mkdirSync, rmSync } from "node:fs";
|
||||||
|
import { dirname, join, resolve } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const here = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const out = resolve(process.argv[2] ?? join(here, "dist"));
|
||||||
|
const fw = join(here, "node_modules/@grame/faustwasm");
|
||||||
|
rmSync(out, { recursive: true, force: true });
|
||||||
|
mkdirSync(join(out, "vendor/faustwasm"), { recursive: true });
|
||||||
|
cpSync(join(here, "web"), out, { recursive: true });
|
||||||
|
cpSync(join(here, "lowered"), out, { recursive: true });
|
||||||
|
cpSync(join(fw, "dist/esm/index.js"), join(out, "vendor/faustwasm/index.js"));
|
||||||
|
cpSync(join(fw, "COPYING.txt"), join(out, "vendor/faustwasm/COPYING.txt"));
|
||||||
|
for (const f of ["libfaust-wasm.js", "libfaust-wasm.wasm", "libfaust-wasm.data"])
|
||||||
|
cpSync(join(fw, "libfaust-wasm", f), join(out, "vendor/libfaust-wasm", f));
|
||||||
|
console.log(`built ${out}`);
|
||||||
13
tools/aqua-playground/lower.sh
Executable file
13
tools/aqua-playground/lower.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Regenerates lowered/*.dsp and catalog.json from patches/ through the AquaLower tool (needs the .NET SDK
|
||||||
|
# and a CultLib checkout at the admitted revision: set CULTLIB_ROOT). The two largest patches
|
||||||
|
# (additive-pad, electro-drop-demo lower to about 1.5 MB of Faust each) stay out of the catalog.
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")/../.."
|
||||||
|
entries=(thruster=thruster/thruster.dsp)
|
||||||
|
while IFS= read -r rel; do
|
||||||
|
case "$rel" in advanced/additive-pad.aqua|advanced/electro-drop-demo.aqua) continue;; esac
|
||||||
|
name=$(echo "${rel%.aqua}" | tr '/' '-')
|
||||||
|
entries+=("$name=$rel")
|
||||||
|
done < <(cd patches && find . -name '*.aqua' | sed 's|^\./||' | sort)
|
||||||
|
dotnet run -c Release --project tools/AquaLower -- tools/aqua-playground/lowered patches "${entries[@]}"
|
||||||
76
tools/aqua-playground/lowered/808-clap.dsp
Normal file
76
tools/aqua-playground/lowered/808-clap.dsp
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "808-clap";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 1800 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = no.noise;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0.95 + patch_mod_noise + 0.0)) + no.noise * clip01(0.95 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.42 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.55 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.55 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered + (voice_0_filtered : de.fdelay(2048, min(2047.0, max(0.0, abs(0.004 + 0 * voice_0_age) * ma.SR))));
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.11, 0.018) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 2200 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = no.noise;
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0.95 + patch_mod_noise + 0.0)) + no.noise * clip01(0.95 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.42 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.55 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.55 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered + (voice_1_filtered : de.fdelay(2048, min(2047.0, max(0.0, abs(0.008 + 0 * voice_1_age) * ma.SR))));
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.11, 0.022) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
voice_2_age = age;
|
||||||
|
voice_2_freq = ((max(20, 2600 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_2_osc = no.noise;
|
||||||
|
voice_2_colored = (voice_2_osc * (1.0 - clip01(0.95 + patch_mod_noise + 0.0)) + no.noise * clip01(0.95 + patch_mod_noise + 0.0));
|
||||||
|
voice_2_driven = ma.tanh(voice_2_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_2_folded = voice_2_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_2_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_2_filtered = voice_2_folded : fi.lowpass(1, max(20.0, clip01(0.42 * (1.0 + 0 * voice_2_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.55 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.55 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_2_phased = voice_2_filtered + (voice_2_filtered : de.fdelay(2048, min(2047.0, max(0.0, abs(0.012 + 0 * voice_2_age) * ma.SR))));
|
||||||
|
voice_2_formants = voice_2_phased;
|
||||||
|
voice_2 = ((voice_2_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_2_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_2_age, 0, 0, 1, 0.11, 0.028) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1 + voice_2) * 1);
|
||||||
65
tools/aqua-playground/lowered/808-cowbell.dsp
Normal file
65
tools/aqua-playground/lowered/808-cowbell.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "808-cowbell";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 540 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = select2(wrap01(os.phasor(1.0, ((max(20, 540 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) < clip01(0.43 + 0 * voice_0_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.16 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.16 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.82 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.18 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.18 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.18, 0.05) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.16);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 800 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = select2(wrap01(os.phasor(1.0, ((max(20, 800 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) < clip01(0.47 + 0 * voice_1_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.16 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.16 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.82 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.18 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.18 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.16, 0.045) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.12);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
65
tools/aqua-playground/lowered/808-hat.dsp
Normal file
65
tools/aqua-playground/lowered/808-hat.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "808-hat";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 9000 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = no.noise;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(1 + patch_mod_noise + 0.0)) + no.noise * clip01(1 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.24 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.9 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.9 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.055, 0.006) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.16);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 6800 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = select2(wrap01(os.phasor(1.0, ((max(20, 6800 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) < clip01(0.5 + 0 * voice_1_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.24 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.9 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.9 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.04, 0.005) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.045);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
54
tools/aqua-playground/lowered/808-kick.dsp
Normal file
54
tools/aqua-playground/lowered/808-kick.dsp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "808-kick";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(32, 58 * pow(2.0, -3.8 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(32, 58 * pow(2.0, -3.8 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.18 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.18 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.85 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 0.6060606, 0.42, 0.045) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 1.32);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
65
tools/aqua-playground/lowered/808-snare.dsp
Normal file
65
tools/aqua-playground/lowered/808-snare.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "808-snare";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 180 * pow(2.0, -1.2 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 180 * pow(2.0, -1.2 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.12, 0.02) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.08);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 140 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = no.noise;
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0.85 + patch_mod_noise + 0.0)) + no.noise * clip01(0.85 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.55 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.45 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.45 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.2, 0.035) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.55);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
54
tools/aqua-playground/lowered/808-tom.dsp
Normal file
54
tools/aqua-playground/lowered/808-tom.dsp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "808-tom";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(62, 115 * pow(2.0, -1.45 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(62, 115 * pow(2.0, -1.45 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.78 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 0.78125, 0.34, 0.055) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.704);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
|
|
@ -0,0 +1,231 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "advanced-alien-multivalve-graph";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_param_0_base = hslider("/alien/core/pressure", 0.74, 0, 1.5, 0.001) : si.smoo;
|
||||||
|
patch_param_0_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_0_curve_value = select2(patch_param_0_curve_time < 0.1, select2(patch_param_0_curve_time < 0.5, select2(patch_param_0_curve_time < 0.9, 0.22, seg(patch_param_0_curve_time, 0.5, 0.4, 0.82, 0.22)), seg(patch_param_0_curve_time, 0.1, 0.4, 0.76, 0.82)), seg(patch_param_0_curve_time, 0, 0.1, 0.08, 0.76));
|
||||||
|
patch_param_0_curve_depth = hslider("/curves/core_breath/depth", 0.9, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_0 = min(1.5, max(0, patch_param_0_base * (1.0 - patch_param_0_curve_depth) + patch_param_0_curve_value * patch_param_0_curve_depth));
|
||||||
|
patch_param_1_base = hslider("/alien/side/pressure", 0.52, 0, 1.5, 0.001) : si.smoo;
|
||||||
|
patch_param_1_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_1_curve_value = select2(patch_param_1_curve_time < 0.18, select2(patch_param_1_curve_time < 0.38, select2(patch_param_1_curve_time < 0.62, select2(patch_param_1_curve_time < 0.9, 0.12, seg(patch_param_1_curve_time, 0.62, 0.28, 0.7, 0.12)), seg(patch_param_1_curve_time, 0.38, 0.24, 0.22, 0.7)), seg(patch_param_1_curve_time, 0.18, 0.2, 0.54, 0.22)), seg(patch_param_1_curve_time, 0, 0.18, 0.02, 0.54));
|
||||||
|
patch_param_1_curve_depth = hslider("/curves/side_breath/depth", 0.9, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_1 = min(1.5, max(0, patch_param_1_base * (1.0 - patch_param_1_curve_depth) + patch_param_1_curve_value * patch_param_1_curve_depth));
|
||||||
|
patch_param_2 = hslider("/alien/core/opening", 0.24, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_3 = hslider("/alien/side/opening", 0.3, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_4_base = hslider("/alien/aperture", 0.85, 0, 2, 0.001) : si.smoo;
|
||||||
|
patch_param_4_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_4_curve_value = select2(patch_param_4_curve_time < 0.22, select2(patch_param_4_curve_time < 0.55, select2(patch_param_4_curve_time < 0.9, 0.9, seg(patch_param_4_curve_time, 0.55, 0.35, 0.62, 0.9)), seg(patch_param_4_curve_time, 0.22, 0.33, 1.1, 0.62)), seg(patch_param_4_curve_time, 0, 0.22, 0.42, 1.1));
|
||||||
|
patch_param_4_curve_depth = hslider("/curves/mouth_shape/depth", 0.8, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_4 = min(2, max(0, patch_param_4_base * (1.0 - patch_param_4_curve_depth) + patch_param_4_curve_value * patch_param_4_curve_depth));
|
||||||
|
patch_param_5 = hslider("/alien/branch/opening", 0.46, 0, 1, 0.001) : si.smoo;
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_graph_drive = 1.0;
|
||||||
|
voice_0_graph_terminal_area_core_valve = max(0.000001, ((max(0.0, min((0.48 + ((2.43) - 0.48) * (exp(0.0 - pow((0 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.48) * 0), (patch_param_4) + max(0.0, ((0.48 + ((2.43) - 0.48) * (exp(0.0 - pow((0 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.48) * 0)) - (patch_param_4)) * (1.0 - (exp(0.0 - pow((0 - (3.4)) / (max(0.001, 0.09 * 6)), 2.0))))))) * (max(0.0, min((0.48 + ((2.43) - 0.48) * (exp(0.0 - pow((0 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.48) * 0), (patch_param_4) + max(0.0, ((0.48 + ((2.43) - 0.48) * (exp(0.0 - pow((0 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.48) * 0)) - (patch_param_4)) * (1.0 - (exp(0.0 - pow((0 - (3.4)) / (max(0.001, 0.09 * 6)), 2.0)))))))) * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_core_valve = 0;
|
||||||
|
voice_0_graph_terminal_area_side_valve = max(0.000001, 0.04 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_side_valve = 0;
|
||||||
|
voice_0_graph_terminal_area_aperture = max(0.000001, ((max(0.0, min((0.44 + ((2.43) - 0.44) * (exp(0.0 - pow((5 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.44) * 1), (patch_param_4) + max(0.0, ((0.44 + ((2.43) - 0.44) * (exp(0.0 - pow((5 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.44) * 1)) - (patch_param_4)) * (1.0 - (exp(0.0 - pow((5 - (3.4)) / (max(0.001, 0.09 * 6)), 2.0))))))) * (max(0.0, min((0.44 + ((2.43) - 0.44) * (exp(0.0 - pow((5 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.44) * 1), (patch_param_4) + max(0.0, ((0.44 + ((2.43) - 0.44) * (exp(0.0 - pow((5 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.44) * 1)) - (patch_param_4)) * (1.0 - (exp(0.0 - pow((5 - (3.4)) / (max(0.001, 0.09 * 6)), 2.0)))))))) * max(0.0, 0.85));
|
||||||
|
voice_0_graph_terminal_reflection_aperture = ((0.72) * (1.0 - clip01(max(0.0, patch_param_4))) + (-0.58) * clip01(max(0.0, patch_param_4)));
|
||||||
|
voice_0_graph_terminal_area_side_coupler_from = max(0.000001, ((max(0.0, min((0.933 + ((2.43) - 0.933) * (exp(0.0 - pow((2.9 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.933) * 0), (patch_param_4) + max(0.0, ((0.933 + ((2.43) - 0.933) * (exp(0.0 - pow((2.9 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.933) * 0)) - (patch_param_4)) * (1.0 - (exp(0.0 - pow((2.9 - (3.4)) / (max(0.001, 0.09 * 6)), 2.0))))))) * (max(0.0, min((0.933 + ((2.43) - 0.933) * (exp(0.0 - pow((2.9 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.933) * 0), (patch_param_4) + max(0.0, ((0.933 + ((2.43) - 0.933) * (exp(0.0 - pow((2.9 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.933) * 0)) - (patch_param_4)) * (1.0 - (exp(0.0 - pow((2.9 - (3.4)) / (max(0.001, 0.09 * 6)), 2.0)))))))) * max(0.0, 0.62));
|
||||||
|
voice_0_graph_terminal_reflection_side_coupler_from = 0;
|
||||||
|
voice_0_graph_terminal_area_side_coupler_to = max(0.000001, 0.0324 * max(0.0, 0.46));
|
||||||
|
voice_0_graph_terminal_reflection_side_coupler_to = 0;
|
||||||
|
voice_0_graph_node_area_core_valve = max(0.000001, voice_0_graph_terminal_area_core_valve);
|
||||||
|
voice_0_graph_node_reflection_core_valve = (voice_0_graph_terminal_reflection_core_valve) / 1;
|
||||||
|
voice_0_graph_node_area_side_coupler_from = max(0.000001, voice_0_graph_terminal_area_side_coupler_from);
|
||||||
|
voice_0_graph_node_reflection_side_coupler_from = (voice_0_graph_terminal_reflection_side_coupler_from) / 1;
|
||||||
|
voice_0_graph_node_area_aperture = max(0.000001, voice_0_graph_terminal_area_aperture);
|
||||||
|
voice_0_graph_node_reflection_aperture = (voice_0_graph_terminal_reflection_aperture) / 1;
|
||||||
|
voice_0_graph_node_area_side_valve = max(0.000001, voice_0_graph_terminal_area_side_valve);
|
||||||
|
voice_0_graph_node_reflection_side_valve = (voice_0_graph_terminal_reflection_side_valve) / 1;
|
||||||
|
voice_0_graph_node_area_side_coupler_to = max(0.000001, voice_0_graph_terminal_area_side_coupler_to);
|
||||||
|
voice_0_graph_node_reflection_side_coupler_to = (voice_0_graph_terminal_reflection_side_coupler_to) / 1;
|
||||||
|
voice_0_graph_segment_area_0 = max(0.000001, ((max(0.0, min((0.8685 + ((2.43) - 0.8685) * (exp(0.0 - pow((1.45 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.8685) * 0), (patch_param_4) + max(0.0, ((0.8685 + ((2.43) - 0.8685) * (exp(0.0 - pow((1.45 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.8685) * 0)) - (patch_param_4)) * (1.0 - (exp(0.0 - pow((1.45 - (3.4)) / (max(0.001, 0.09 * 6)), 2.0))))))) * (max(0.0, min((0.8685 + ((2.43) - 0.8685) * (exp(0.0 - pow((1.45 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.8685) * 0), (patch_param_4) + max(0.0, ((0.8685 + ((2.43) - 0.8685) * (exp(0.0 - pow((1.45 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.8685) * 0)) - (patch_param_4)) * (1.0 - (exp(0.0 - pow((1.45 - (3.4)) / (max(0.001, 0.09 * 6)), 2.0)))))))));
|
||||||
|
voice_0_graph_segment_area_1 = max(0.000001, ((max(0.0, min((0.692 + ((2.43) - 0.692) * (exp(0.0 - pow((3.95 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.692) * 0), (patch_param_4) + max(0.0, ((0.692 + ((2.43) - 0.692) * (exp(0.0 - pow((3.95 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.692) * 0)) - (patch_param_4)) * (1.0 - (exp(0.0 - pow((3.95 - (3.4)) / (max(0.001, 0.09 * 6)), 2.0))))))) * (max(0.0, min((0.692 + ((2.43) - 0.692) * (exp(0.0 - pow((3.95 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.692) * 0), (patch_param_4) + max(0.0, ((0.692 + ((2.43) - 0.692) * (exp(0.0 - pow((3.95 - (12.9)) / (max(0.001, 0.18 * 6)), 2.0))) + ((patch_param_4) - 0.692) * 0)) - (patch_param_4)) * (1.0 - (exp(0.0 - pow((3.95 - (3.4)) / (max(0.001, 0.09 * 6)), 2.0)))))))));
|
||||||
|
voice_0_graph_segment_area_2 = max(0.000001, 0.2916);
|
||||||
|
voice_0_graph_connection_coupling_side_coupler_connection = clip01(0.62);
|
||||||
|
voice_0_graph_connection_loss_side_coupler_connection = clip01(1);
|
||||||
|
voice_0_graph_radiation_gain = 1.0;
|
||||||
|
voice_0_graph_source_core_valve = 0.0;
|
||||||
|
voice_0_graph_source_side_valve = 0.0;
|
||||||
|
voice_0_graph(x) = voice_0_graph_loop ~ si.bus(6) : (si.block(6), _) with {
|
||||||
|
voice_0_graph_loop(r0, r1, r2, l0, l1, l2) = voice_0_graph_next_r0, voice_0_graph_next_r1, voice_0_graph_next_r2, voice_0_graph_next_l0, voice_0_graph_next_l1, voice_0_graph_next_l2, voice_0_graph_out with {
|
||||||
|
voice_0_graph_in_0_start = l0;
|
||||||
|
voice_0_graph_in_0_end = r0;
|
||||||
|
voice_0_graph_in_1_start = l1;
|
||||||
|
voice_0_graph_in_1_end = r1;
|
||||||
|
voice_0_graph_in_2_start = l2;
|
||||||
|
voice_0_graph_in_2_end = r2;
|
||||||
|
voice_0_graph_source_core_valve_core_valve_load_pressure = voice_0_graph_node_incident_pressure_core_valve;
|
||||||
|
voice_0_graph_source_core_valve_core_valve_stiffness_hint = max(0.00002, min(0.16, pow(2.0 * ma.PI * max(20.0, ((max(20, 190 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) / ma.SR, 2.0)));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_flow_scale = max(0.0, 1.1);
|
||||||
|
voice_0_graph_source_core_valve_core_valve_tissue_loss = clip01(0.18);
|
||||||
|
voice_0_graph_source_core_valve_core_valve_aperture_shape = clip01(0.44);
|
||||||
|
voice_0_graph_source_core_valve_core_valve_flow_loss = max(0.0, 0.38);
|
||||||
|
voice_0_graph_source_core_valve_core_valve_reservoir_pressure = (clip01(patch_param_0)) * (max(0.0, 1));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_downstream_pressure = (max(0.0, 0.03)) + (max(0.0, 0.38)) * ma.tanh(voice_0_graph_source_core_valve_core_valve_load_pressure);
|
||||||
|
voice_0_graph_source_core_valve_core_valve_pressure_drive = max(0.0, voice_0_graph_source_core_valve_core_valve_reservoir_pressure - voice_0_graph_source_core_valve_core_valve_downstream_pressure);
|
||||||
|
voice_0_graph_source_core_valve_core_valve_stiffness = voice_0_graph_source_core_valve_core_valve_stiffness_hint * (0.35 + 1.65 * (clip01(0.44)));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_modal_frequency = max(45.0, min(10000.0, (ma.SR / (2.0 * ma.PI)) * sqrt(max(0.00002, voice_0_graph_source_core_valve_core_valve_stiffness) / (max(0.02, 0.35)))));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_load_detune = 1.0 - 0.035 * ma.tanh(voice_0_graph_source_core_valve_core_valve_load_pressure * (max(0.0, 0.38)));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_oscillation_threshold = 0.030 + 0.070 * (max(0.0, 0.18)) + 0.080 * voice_0_graph_source_core_valve_core_valve_tissue_loss + 0.18 * clip01(max(0.0, patch_param_2));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_oscillation_gate = clip01((voice_0_graph_source_core_valve_core_valve_pressure_drive - voice_0_graph_source_core_valve_core_valve_oscillation_threshold) * (3.0 + 1.6 * max(0.0, 1.0 - (max(0.0, 0.18)))));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_modal_q = 3.0 + 22.0 * clip01((clip01(patch_param_0)) * (1.0 - 0.45 * (max(0.0, 0.18)) - 0.35 * voice_0_graph_source_core_valve_core_valve_tissue_loss) + 0.35 * (clip01(0.44)));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_modal_seed = (no.noise * 0.018 + voice_0_graph_source_core_valve_core_valve_pressure_drive * max(0.0, 0.22 - clip01(max(0.0, patch_param_2))) * 0.16);
|
||||||
|
voice_0_graph_source_core_valve_core_valve_modal_ring = voice_0_graph_source_core_valve_core_valve_modal_seed : fi.resonbp(voice_0_graph_source_core_valve_core_valve_modal_frequency, voice_0_graph_source_core_valve_core_valve_modal_q, 1);
|
||||||
|
voice_0_graph_source_core_valve_core_valve_modal_oscillator = os.osc(voice_0_graph_source_core_valve_core_valve_modal_frequency * voice_0_graph_source_core_valve_core_valve_load_detune) * voice_0_graph_source_core_valve_core_valve_oscillation_gate;
|
||||||
|
voice_0_graph_source_core_valve_core_valve_modal_tissue = (voice_0_graph_source_core_valve_core_valve_modal_oscillator * (0.70 + 0.80 * (clip01(0.44))) + voice_0_graph_source_core_valve_core_valve_modal_ring * 0.35) * max(0.0, 1.0 - 0.35 * (max(0.0, 0.18)) - 0.45 * voice_0_graph_source_core_valve_core_valve_tissue_loss);
|
||||||
|
voice_0_graph_source_core_valve_core_valve_upper_stiffness_effective = max((max(0.0, 0)), voice_0_graph_source_core_valve_core_valve_stiffness * (0.80 + 0.45 * (clip01(0.44))));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_lower_stiffness_effective = max((max(0.0, 0)), voice_0_graph_source_core_valve_core_valve_stiffness * (1.05 + 0.55 * (clip01(0.44))));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_coupling_stiffness = (max(0.0, 0.12)) * (1.0 + 1.5 * (clip01(0.44)));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_lower_velocity_decay = min(0.9995, max(0.20, 1.0 - ((0.010 + 0.090 * (max(0.0, 0.18)) + 0.040 * (max(0.0, 0.12)) + 0.055 * voice_0_graph_source_core_valve_core_valve_tissue_loss) / (max(0.02, 0.3)))));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_upper_velocity_decay = min(0.9995, max(0.20, 1.0 - ((0.012 + 0.105 * (max(0.0, 0.18)) + 0.030 * (max(0.0, 0.12)) + 0.055 * voice_0_graph_source_core_valve_core_valve_tissue_loss) / (max(0.02, 0.18)))));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_lower_force = (voice_0_graph_source_core_valve_core_valve_pressure_drive * (0.0005 + 0.0028 * (1.0 - clip01(max(0.0, patch_param_2)))) - (max(0.0, 0.38)) * voice_0_graph_source_core_valve_core_valve_load_pressure * 0.0012) / (max(0.02, 0.3));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_upper_force = (voice_0_graph_source_core_valve_core_valve_pressure_drive * (0.0003 + 0.0018 * (1.0 - clip01(max(0.0, patch_param_2)))) - (max(0.0, 0.38)) * voice_0_graph_source_core_valve_core_valve_load_pressure * 0.0008) / (max(0.02, 0.18));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_lower_velocity = voice_0_graph_source_core_valve_core_valve_lower_force : + ~ *(voice_0_graph_source_core_valve_core_valve_lower_velocity_decay / (1.0 + 10.0 * voice_0_graph_source_core_valve_core_valve_lower_stiffness_effective + voice_0_graph_source_core_valve_core_valve_coupling_stiffness + 0.4 * (max(0.0, 0.8))));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_upper_velocity = (voice_0_graph_source_core_valve_core_valve_upper_force + voice_0_graph_source_core_valve_core_valve_lower_velocity * voice_0_graph_source_core_valve_core_valve_coupling_stiffness * 0.35) : + ~ *(voice_0_graph_source_core_valve_core_valve_upper_velocity_decay / (1.0 + 10.0 * voice_0_graph_source_core_valve_core_valve_upper_stiffness_effective + voice_0_graph_source_core_valve_core_valve_coupling_stiffness + 0.4 * (max(0.0, 0.8))));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_lower_displacement = voice_0_graph_source_core_valve_core_valve_lower_velocity : + ~ *(min(0.9998, max(0.20, 1.0 - 0.0008 / (max(0.02, 0.3)))));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_upper_displacement = voice_0_graph_source_core_valve_core_valve_upper_velocity : + ~ *(min(0.9998, max(0.20, 1.0 - 0.0008 / (max(0.02, 0.18)))));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_collision = (max(0.0, 0.45)) * max(0.0, 0.0 - ((max(0.0, 0.03)) + (max(0.0, patch_param_2)) + voice_0_graph_source_core_valve_core_valve_upper_displacement));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_displacement = voice_0_graph_source_core_valve_core_valve_lower_displacement * (1.0 - (clip01(0.42))) + (voice_0_graph_source_core_valve_core_valve_upper_displacement - voice_0_graph_source_core_valve_core_valve_collision) * (clip01(0.42));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_aperture_linear = max(0.0, (max(0.0, 0.03)) + (max(0.0, patch_param_2)) + voice_0_graph_source_core_valve_core_valve_displacement - (max(0.0, 0.8)) * pow(voice_0_graph_source_core_valve_core_valve_displacement, 3.0));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_aperture = voice_0_graph_source_core_valve_core_valve_aperture_linear * (1.0 - 0.55 * voice_0_graph_source_core_valve_core_valve_aperture_shape) + pow(voice_0_graph_source_core_valve_core_valve_aperture_linear, 2.0) * (0.55 * voice_0_graph_source_core_valve_core_valve_aperture_shape);
|
||||||
|
voice_0_graph_source_core_valve_core_valve_flow_resistance = 1.0 + voice_0_graph_source_core_valve_core_valve_flow_loss * (0.35 + 1.8 / max(0.035, voice_0_graph_source_core_valve_core_valve_aperture + (max(0.0, 0.03))));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_turbulence = no.noise : fi.highpass(2, 1200.0 + 2800.0 * clip01(1.0 - voice_0_graph_source_core_valve_core_valve_aperture));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_voicing = voice_0_graph_source_core_valve_core_valve_modal_tissue * (0.18 + 1.45 * (clip01(0.44)));
|
||||||
|
voice_0_graph_source_core_valve_core_valve_flow = ma.tanh(((voice_0_graph_source_core_valve_core_valve_pressure_drive * voice_0_graph_source_core_valve_core_valve_aperture * (1.5 + 3.5 * voice_0_graph_source_core_valve_core_valve_flow_scale)) + (voice_0_graph_source_core_valve_core_valve_voicing * voice_0_graph_source_core_valve_core_valve_pressure_drive * (0.45 + 1.55 * (clip01(0.44))))) / voice_0_graph_source_core_valve_core_valve_flow_resistance);
|
||||||
|
voice_0_graph_source_core_valve_core_valve_noise = voice_0_graph_source_core_valve_core_valve_turbulence * (clip01(0.05)) * voice_0_graph_source_core_valve_core_valve_pressure_drive * clip01(voice_0_graph_source_core_valve_core_valve_aperture) * (1.0 - 0.55 * (clip01(0.44))) / voice_0_graph_source_core_valve_core_valve_flow_resistance;
|
||||||
|
voice_0_graph_source_core_valve_core_valve_out = (voice_0_graph_source_core_valve_core_valve_flow * voice_0_graph_source_core_valve_core_valve_flow_scale * (0.55 + 2.6 * voice_0_graph_source_core_valve_core_valve_pressure_drive) + voice_0_graph_source_core_valve_core_valve_noise) * ((1) * (1.0));
|
||||||
|
voice_0_graph_node_incident_pressure_core_valve = (voice_0_graph_in_0_start) / 1;
|
||||||
|
voice_0_graph_node_source_core_valve = voice_0_graph_source_core_valve_core_valve_out;
|
||||||
|
voice_0_graph_node_incident_pressure_side_coupler_from = (voice_0_graph_in_0_end + voice_0_graph_in_1_start) / 2;
|
||||||
|
voice_0_graph_node_source_side_coupler_from = 0.0;
|
||||||
|
voice_0_graph_node_incident_pressure_aperture = (voice_0_graph_in_1_end) / 1;
|
||||||
|
voice_0_graph_node_source_aperture = 0.0;
|
||||||
|
voice_0_graph_source_side_valve_side_valve_load_pressure = voice_0_graph_node_incident_pressure_side_valve;
|
||||||
|
voice_0_graph_source_side_valve_side_valve_stiffness_hint = max(0.00002, min(0.16, pow(2.0 * ma.PI * max(20.0, ((max(20, 190 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) / ma.SR, 2.0)));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_flow_scale = max(0.0, 0.95);
|
||||||
|
voice_0_graph_source_side_valve_side_valve_tissue_loss = clip01(0.22);
|
||||||
|
voice_0_graph_source_side_valve_side_valve_aperture_shape = clip01(0.4);
|
||||||
|
voice_0_graph_source_side_valve_side_valve_flow_loss = max(0.0, 0.36);
|
||||||
|
voice_0_graph_source_side_valve_side_valve_reservoir_pressure = (clip01(patch_param_1)) * (max(0.0, 1));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_downstream_pressure = (max(0.0, 0.02)) + (max(0.0, 0.34)) * ma.tanh(voice_0_graph_source_side_valve_side_valve_load_pressure);
|
||||||
|
voice_0_graph_source_side_valve_side_valve_pressure_drive = max(0.0, voice_0_graph_source_side_valve_side_valve_reservoir_pressure - voice_0_graph_source_side_valve_side_valve_downstream_pressure);
|
||||||
|
voice_0_graph_source_side_valve_side_valve_stiffness = max(0.00002, (max(0.0, 0.026)) * (0.55 + 1.10 * (clip01(0.34))));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_modal_frequency = max(45.0, min(10000.0, (ma.SR / (2.0 * ma.PI)) * sqrt(max(0.00002, voice_0_graph_source_side_valve_side_valve_stiffness) / (max(0.02, 0.22)))));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_load_detune = 1.0 - 0.035 * ma.tanh(voice_0_graph_source_side_valve_side_valve_load_pressure * (max(0.0, 0.34)));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_oscillation_threshold = 0.030 + 0.070 * (max(0.0, 0.15)) + 0.080 * voice_0_graph_source_side_valve_side_valve_tissue_loss + 0.18 * clip01(max(0.0, patch_param_3));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_oscillation_gate = clip01((voice_0_graph_source_side_valve_side_valve_pressure_drive - voice_0_graph_source_side_valve_side_valve_oscillation_threshold) * (3.0 + 1.6 * max(0.0, 1.0 - (max(0.0, 0.15)))));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_modal_q = 3.0 + 22.0 * clip01((clip01(patch_param_1)) * (1.0 - 0.45 * (max(0.0, 0.15)) - 0.35 * voice_0_graph_source_side_valve_side_valve_tissue_loss) + 0.35 * (clip01(0.34)));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_modal_seed = (no.noise * 0.018 + voice_0_graph_source_side_valve_side_valve_pressure_drive * max(0.0, 0.22 - clip01(max(0.0, patch_param_3))) * 0.16);
|
||||||
|
voice_0_graph_source_side_valve_side_valve_modal_ring = voice_0_graph_source_side_valve_side_valve_modal_seed : fi.resonbp(voice_0_graph_source_side_valve_side_valve_modal_frequency, voice_0_graph_source_side_valve_side_valve_modal_q, 1);
|
||||||
|
voice_0_graph_source_side_valve_side_valve_modal_oscillator = os.osc(voice_0_graph_source_side_valve_side_valve_modal_frequency * voice_0_graph_source_side_valve_side_valve_load_detune) * voice_0_graph_source_side_valve_side_valve_oscillation_gate;
|
||||||
|
voice_0_graph_source_side_valve_side_valve_modal_tissue = (voice_0_graph_source_side_valve_side_valve_modal_oscillator * (0.70 + 0.80 * (clip01(0.34))) + voice_0_graph_source_side_valve_side_valve_modal_ring * 0.35) * max(0.0, 1.0 - 0.35 * (max(0.0, 0.15)) - 0.45 * voice_0_graph_source_side_valve_side_valve_tissue_loss);
|
||||||
|
voice_0_graph_source_side_valve_side_valve_velocity_decay = min(0.9995, max(0.20, 1.0 - ((0.008 + 0.08 * (max(0.0, 0.15)) + 0.055 * voice_0_graph_source_side_valve_side_valve_tissue_loss) / (max(0.02, 0.22)))));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_displacement_decay = min(0.9998, max(0.20, 1.0 - 0.0008 / (max(0.02, 0.22))));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_force = (voice_0_graph_source_side_valve_side_valve_pressure_drive * (0.0004 + 0.0024 * (1.0 - clip01(max(0.0, patch_param_3)))) - (max(0.0, 0.34)) * voice_0_graph_source_side_valve_side_valve_load_pressure * 0.001) / (max(0.02, 0.22));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_velocity = voice_0_graph_source_side_valve_side_valve_force : + ~ *(voice_0_graph_source_side_valve_side_valve_velocity_decay / (1.0 + 10.0 * voice_0_graph_source_side_valve_side_valve_stiffness + 0.4 * (max(0.0, 0.8))));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_displacement = voice_0_graph_source_side_valve_side_valve_velocity : + ~ *(voice_0_graph_source_side_valve_side_valve_displacement_decay / (1.0 + 1.5 * voice_0_graph_source_side_valve_side_valve_stiffness));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_aperture_linear = max(0.0, (max(0.0, 0.04)) + (max(0.0, patch_param_3)) + voice_0_graph_source_side_valve_side_valve_displacement - (max(0.0, 0.8)) * pow(voice_0_graph_source_side_valve_side_valve_displacement, 3.0));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_aperture = voice_0_graph_source_side_valve_side_valve_aperture_linear * (1.0 - 0.55 * voice_0_graph_source_side_valve_side_valve_aperture_shape) + pow(voice_0_graph_source_side_valve_side_valve_aperture_linear, 2.0) * (0.55 * voice_0_graph_source_side_valve_side_valve_aperture_shape);
|
||||||
|
voice_0_graph_source_side_valve_side_valve_flow_resistance = 1.0 + voice_0_graph_source_side_valve_side_valve_flow_loss * (0.35 + 1.8 / max(0.035, voice_0_graph_source_side_valve_side_valve_aperture + (max(0.0, 0.04))));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_turbulence = no.noise : fi.highpass(2, 1200.0 + 2800.0 * clip01(1.0 - voice_0_graph_source_side_valve_side_valve_aperture));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_voicing = voice_0_graph_source_side_valve_side_valve_modal_tissue * (0.18 + 1.45 * (clip01(0.34)));
|
||||||
|
voice_0_graph_source_side_valve_side_valve_flow = ma.tanh(((voice_0_graph_source_side_valve_side_valve_pressure_drive * voice_0_graph_source_side_valve_side_valve_aperture * (1.5 + 3.5 * voice_0_graph_source_side_valve_side_valve_flow_scale)) + (voice_0_graph_source_side_valve_side_valve_voicing * voice_0_graph_source_side_valve_side_valve_pressure_drive * (0.45 + 1.55 * (clip01(0.34))))) / voice_0_graph_source_side_valve_side_valve_flow_resistance);
|
||||||
|
voice_0_graph_source_side_valve_side_valve_noise = voice_0_graph_source_side_valve_side_valve_turbulence * (clip01(0.04)) * voice_0_graph_source_side_valve_side_valve_pressure_drive * clip01(voice_0_graph_source_side_valve_side_valve_aperture) * (1.0 - 0.55 * (clip01(0.34))) / voice_0_graph_source_side_valve_side_valve_flow_resistance;
|
||||||
|
voice_0_graph_source_side_valve_side_valve_out = (voice_0_graph_source_side_valve_side_valve_flow * voice_0_graph_source_side_valve_side_valve_flow_scale * (0.55 + 2.6 * voice_0_graph_source_side_valve_side_valve_pressure_drive) + voice_0_graph_source_side_valve_side_valve_noise) * ((1) * (1.0));
|
||||||
|
voice_0_graph_node_incident_pressure_side_valve = (voice_0_graph_in_2_start) / 1;
|
||||||
|
voice_0_graph_node_source_side_valve = voice_0_graph_source_side_valve_side_valve_out;
|
||||||
|
voice_0_graph_node_incident_pressure_side_coupler_to = (voice_0_graph_in_2_end) / 1;
|
||||||
|
voice_0_graph_node_source_side_coupler_to = 0.0;
|
||||||
|
voice_0_graph_connection_reflection_side_coupler_connection_side_coupler_from_0_end = (2.0 * ((voice_0_graph_terminal_area_side_coupler_from / 2)) - ((voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_to / 1))) / max(0.000001, (voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_to / 1));
|
||||||
|
voice_0_graph_connection_scattered_side_coupler_connection_side_coupler_from_0_end = voice_0_graph_connection_reflection_side_coupler_connection_side_coupler_from_0_end * voice_0_graph_in_0_end + (1.0 + voice_0_graph_connection_reflection_side_coupler_connection_side_coupler_from_0_end) * (voice_0_graph_in_1_start + voice_0_graph_in_2_end);
|
||||||
|
voice_0_graph_out_0_end = ((voice_0_graph_connection_scattered_side_coupler_connection_side_coupler_from_0_end) * (voice_0_graph_connection_coupling_side_coupler_connection) + voice_0_graph_in_0_end * (1.0 - (voice_0_graph_connection_coupling_side_coupler_connection)) + (voice_0_graph_node_source_side_coupler_from) / 2) * voice_0_graph_connection_loss_side_coupler_connection;
|
||||||
|
voice_0_graph_connection_reflection_side_coupler_connection_side_coupler_from_1_start = (2.0 * ((voice_0_graph_terminal_area_side_coupler_from / 2)) - ((voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_to / 1))) / max(0.000001, (voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_to / 1));
|
||||||
|
voice_0_graph_connection_scattered_side_coupler_connection_side_coupler_from_1_start = voice_0_graph_connection_reflection_side_coupler_connection_side_coupler_from_1_start * voice_0_graph_in_1_start + (1.0 + voice_0_graph_connection_reflection_side_coupler_connection_side_coupler_from_1_start) * (voice_0_graph_in_0_end + voice_0_graph_in_2_end);
|
||||||
|
voice_0_graph_out_1_start = ((voice_0_graph_connection_scattered_side_coupler_connection_side_coupler_from_1_start) * (voice_0_graph_connection_coupling_side_coupler_connection) + voice_0_graph_in_1_start * (1.0 - (voice_0_graph_connection_coupling_side_coupler_connection)) + (voice_0_graph_node_source_side_coupler_from) / 2) * voice_0_graph_connection_loss_side_coupler_connection;
|
||||||
|
voice_0_graph_connection_reflection_side_coupler_connection_side_coupler_to_2_end = (2.0 * ((voice_0_graph_terminal_area_side_coupler_to / 1)) - ((voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_to / 1))) / max(0.000001, (voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_from / 2) + (voice_0_graph_terminal_area_side_coupler_to / 1));
|
||||||
|
voice_0_graph_connection_scattered_side_coupler_connection_side_coupler_to_2_end = voice_0_graph_connection_reflection_side_coupler_connection_side_coupler_to_2_end * voice_0_graph_in_2_end + (1.0 + voice_0_graph_connection_reflection_side_coupler_connection_side_coupler_to_2_end) * (voice_0_graph_in_0_end + voice_0_graph_in_1_start);
|
||||||
|
voice_0_graph_out_2_end = ((voice_0_graph_connection_scattered_side_coupler_connection_side_coupler_to_2_end) * (voice_0_graph_connection_coupling_side_coupler_connection) + voice_0_graph_in_2_end * (1.0 - (voice_0_graph_connection_coupling_side_coupler_connection)) + (voice_0_graph_node_source_side_coupler_to) / 1) * voice_0_graph_connection_loss_side_coupler_connection;
|
||||||
|
voice_0_graph_connection_energy_in_side_coupler_connection = ((voice_0_graph_terminal_area_side_coupler_from / 2)) * pow(voice_0_graph_in_0_end, 2.0) + ((voice_0_graph_terminal_area_side_coupler_from / 2)) * pow(voice_0_graph_in_1_start, 2.0) + ((voice_0_graph_terminal_area_side_coupler_to / 1)) * pow(voice_0_graph_in_2_end, 2.0);
|
||||||
|
voice_0_graph_connection_energy_out_side_coupler_connection = ((voice_0_graph_terminal_area_side_coupler_from / 2)) * pow(voice_0_graph_out_0_end, 2.0) + ((voice_0_graph_terminal_area_side_coupler_from / 2)) * pow(voice_0_graph_out_1_start, 2.0) + ((voice_0_graph_terminal_area_side_coupler_to / 1)) * pow(voice_0_graph_out_2_end, 2.0);
|
||||||
|
voice_0_graph_out_0_start = voice_0_graph_in_0_start * voice_0_graph_node_reflection_core_valve + (voice_0_graph_node_source_core_valve) / 1;
|
||||||
|
voice_0_graph_radiation_boundary_load_aperture = clip01((voice_0_graph_radiation_admittance_aperture) * (voice_0_graph_radiation_differentiation_aperture) * (clip01(0.96)));
|
||||||
|
voice_0_graph_radiation_reflected_raw_aperture = (voice_0_graph_in_1_end) * (voice_0_graph_terminal_reflection_aperture) + (voice_0_graph_node_source_aperture);
|
||||||
|
voice_0_graph_radiation_reflected_loaded_aperture = (voice_0_graph_radiation_reflected_raw_aperture) * (1.0 - (voice_0_graph_radiation_boundary_load_aperture)) + ((voice_0_graph_radiation_reflected_raw_aperture) : fi.lowpass(1, voice_0_graph_radiation_highpass_aperture)) * (voice_0_graph_radiation_boundary_load_aperture);
|
||||||
|
voice_0_graph_out_1_end = voice_0_graph_radiation_reflected_loaded_aperture;
|
||||||
|
voice_0_graph_radiation_model_aperture = 4;
|
||||||
|
voice_0_graph_radiation_reference_area_aperture = 0.35;
|
||||||
|
voice_0_graph_radiation_differentiation_aperture = 0.18;
|
||||||
|
voice_0_graph_radiation_highpass_aperture = 15.0;
|
||||||
|
voice_0_graph_radiation_admittance_aperture = sqrt(clip01((voice_0_graph_terminal_area_aperture) / max(0.000001, (voice_0_graph_terminal_area_aperture) + (voice_0_graph_radiation_reference_area_aperture))));
|
||||||
|
voice_0_graph_radiation_flow_aperture = ((voice_0_graph_in_1_end - voice_0_graph_out_1_end));
|
||||||
|
voice_0_graph_out_2_start = voice_0_graph_in_2_start * voice_0_graph_node_reflection_side_valve + (voice_0_graph_node_source_side_valve) / 1;
|
||||||
|
voice_0_graph_delay_0 = max(0.000001, 0.00033819 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_0 = clip01((0.996) * (sqrt(clip01((voice_0_graph_segment_area_0) / max(0.000001, (voice_0_graph_segment_area_0) + 0.012))) * (0.998 - 0.010 * clip01(0.080 / max(0.000001, (voice_0_graph_segment_area_0) + 0.080)))));
|
||||||
|
voice_0_graph_next_r0 = voice_0_graph_out_0_start : de.fdelay(4096, min(4095, max(0.000001, voice_0_graph_delay_0))) * voice_0_graph_segment_loss_0;
|
||||||
|
voice_0_graph_next_l0 = voice_0_graph_out_0_end : de.fdelay(4096, min(4095, max(0.000001, voice_0_graph_delay_0))) * voice_0_graph_segment_loss_0;
|
||||||
|
voice_0_graph_delay_1 = max(0.000001, 0.0002449 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_1 = clip01((0.996) * (sqrt(clip01((voice_0_graph_segment_area_1) / max(0.000001, (voice_0_graph_segment_area_1) + 0.012))) * (0.998 - 0.010 * clip01(0.080 / max(0.000001, (voice_0_graph_segment_area_1) + 0.080)))));
|
||||||
|
voice_0_graph_next_r1 = voice_0_graph_out_1_start : de.fdelay(4096, min(4095, max(0.000001, voice_0_graph_delay_1))) * voice_0_graph_segment_loss_1;
|
||||||
|
voice_0_graph_next_l1 = voice_0_graph_out_1_end : de.fdelay(4096, min(4095, max(0.000001, voice_0_graph_delay_1))) * voice_0_graph_segment_loss_1;
|
||||||
|
voice_0_graph_delay_2 = max(0.000001, 0.00026239 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_2 = clip01((0.994) * ((0.996 - 0.018 * clip01(0.050 / max(0.000001, (voice_0_graph_segment_area_2) + 0.050)))));
|
||||||
|
voice_0_graph_next_r2 = voice_0_graph_out_2_start : de.fdelay(4096, min(4095, max(0.000001, voice_0_graph_delay_2))) * voice_0_graph_segment_loss_2;
|
||||||
|
voice_0_graph_next_l2 = voice_0_graph_out_2_end : de.fdelay(4096, min(4095, max(0.000001, voice_0_graph_delay_2))) * voice_0_graph_segment_loss_2;
|
||||||
|
voice_0_graph_out = (((voice_0_graph_radiation_flow_aperture * (1.0 - (voice_0_graph_radiation_differentiation_aperture)) + (voice_0_graph_radiation_flow_aperture : fi.highpass(1, voice_0_graph_radiation_highpass_aperture)) * (voice_0_graph_radiation_differentiation_aperture)) * clip01(0.96) * voice_0_graph_radiation_admittance_aperture)) * voice_0_graph_radiation_gain;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
voice_0_acoustic_graph_radiated = voice_0_graph(voice_0_graph_drive);
|
||||||
|
voice_0_freq = ((max(20, 190 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = voice_0_acoustic_graph_radiated;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.12, 0.9) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.2);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 0.36);
|
||||||
87
tools/aqua-playground/lowered/advanced-aurora-pad.dsp
Normal file
87
tools/aqua-playground/lowered/advanced-aurora-pad.dsp
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "advanced-aurora-pad";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0 + 0.08 * lfo_sin(0.21, 0);
|
||||||
|
patch_mod_pitch = 0 + 0.012 * lfo_sin(0.21, 0) + 0 + 0.004 * lfo_tri(4.6, 0);
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0 + 0.09 * lfo_sin(0.21, 0);
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0 + 0.035 * lfo_tri(4.6, 0);
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0 + 0.16 * lfo_sin(0.21, 0);
|
||||||
|
patch_mod_fm_index = 0 + 0.35 * lfo_sin(0.21, 0);
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 220 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 220 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 220 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2.414))) * (max(0.0, 1.4 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.8, 0.0001))) / ma.PI));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.86 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.018 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.018 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = ((voice_0_phased : fi.resonbp(420, 5.25, 1.0)) * 0.25 + (voice_0_phased : fi.resonbp(1180, 6.555555, 1.0)) * 0.75 + (voice_0_phased : fi.resonbp(2450, 6.805555, 1.0)) * 0.42) / 1.42;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0.18 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0.18 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.018, 0, 1, 1.6, 1.15) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.12);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 330 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 330 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 330 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 3.01))) * (max(0.0, 0.9 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.8, 0.0001))) / ma.PI));
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.86 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.018 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.018 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = ((voice_1_phased : fi.resonbp(420, 5.25, 1.0)) * 0.25 + (voice_1_phased : fi.resonbp(1180, 6.555555, 1.0)) * 0.75 + (voice_1_phased : fi.resonbp(2450, 6.805555, 1.0)) * 0.42) / 1.42;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0.18 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0.18 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0.018, 0, 1, 1.6, 1.15) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.08);
|
||||||
|
|
||||||
|
voice_2_age = age;
|
||||||
|
voice_2_freq = ((max(20, 440 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_2_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 440 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 440 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 1.618))) * (max(0.0, 0.7 + patch_mod_fm_index + 0.0) * exp(-voice_2_age / max(0.8, 0.0001))) / ma.PI));
|
||||||
|
voice_2_colored = (voice_2_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_2_driven = ma.tanh(voice_2_colored * (1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_2_folded = voice_2_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_2_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_2_filtered = voice_2_folded : fi.lowpass(1, max(20.0, clip01(0.86 * (1.0 + 0 * voice_2_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.018 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.018 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_2_phased = voice_2_filtered;
|
||||||
|
voice_2_formants = ((voice_2_phased : fi.resonbp(420, 5.25, 1.0)) * 0.25 + (voice_2_phased : fi.resonbp(1180, 6.555555, 1.0)) * 0.75 + (voice_2_phased : fi.resonbp(2450, 6.805555, 1.0)) * 0.42) / 1.42;
|
||||||
|
voice_2 = ((voice_2_phased * (1.0 - clip01(0.26 + patch_mod_formant_mix + 0.0)) + voice_2_formants * clip01(0.26 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_2_age, 0.018, 0, 1, 1.6, 1.15) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.06);
|
||||||
|
|
||||||
|
voice_3_age = age;
|
||||||
|
voice_3_freq = ((max(20, 1600 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_3_osc = no.noise;
|
||||||
|
voice_3_colored = (voice_3_osc * (1.0 - clip01(0.72 + patch_mod_noise + 0.0)) + no.noise * clip01(0.72 + patch_mod_noise + 0.0));
|
||||||
|
voice_3_driven = ma.tanh(voice_3_colored * (1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.05 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_3_folded = voice_3_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_3_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_3_filtered = voice_3_folded : fi.lowpass(1, max(20.0, clip01(0.38 * (1.0 + 0 * voice_3_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.62 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.62 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_3_phased = voice_3_filtered;
|
||||||
|
voice_3_formants = voice_3_phased;
|
||||||
|
voice_3 = ((voice_3_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_3_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_3_age, 0.018, 0, 1, 2, 1.4) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.018);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1 + voice_2 + voice_3) * 0.58);
|
||||||
238
tools/aqua-playground/lowered/advanced-bird-syrinx-graph.dsp
Normal file
238
tools/aqua-playground/lowered/advanced-bird-syrinx-graph.dsp
Normal file
|
|
@ -0,0 +1,238 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "advanced-bird-syrinx-graph";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_param_0_base = hslider("/bird/left/pressure", 0.84, 0, 1.5, 0.001) : si.smoo;
|
||||||
|
patch_param_0_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_0_curve_value = select2(patch_param_0_curve_time < 0.025, select2(patch_param_0_curve_time < 0.22, select2(patch_param_0_curve_time < 0.54, select2(patch_param_0_curve_time < 0.82, 0.18, seg(patch_param_0_curve_time, 0.54, 0.28, 0.88, 0.18)), seg(patch_param_0_curve_time, 0.22, 0.32, 0.78, 0.88)), seg(patch_param_0_curve_time, 0.025, 0.195, 0.92, 0.78)), seg(patch_param_0_curve_time, 0, 0.025, 0.04, 0.92));
|
||||||
|
patch_param_0_curve_depth = hslider("/curves/left_air/depth", 0.95, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_0 = min(1.5, max(0, patch_param_0_base * (1.0 - patch_param_0_curve_depth) + patch_param_0_curve_value * patch_param_0_curve_depth));
|
||||||
|
patch_param_1_base = hslider("/bird/right/pressure", 0.72, 0, 1.5, 0.001) : si.smoo;
|
||||||
|
patch_param_1_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_1_curve_value = select2(patch_param_1_curve_time < 0.035, select2(patch_param_1_curve_time < 0.18, select2(patch_param_1_curve_time < 0.48, select2(patch_param_1_curve_time < 0.82, 0.12, seg(patch_param_1_curve_time, 0.48, 0.34, 0.62, 0.12)), seg(patch_param_1_curve_time, 0.18, 0.3, 0.82, 0.62)), seg(patch_param_1_curve_time, 0.035, 0.145, 0.75, 0.82)), seg(patch_param_1_curve_time, 0, 0.035, 0.02, 0.75));
|
||||||
|
patch_param_1_curve_depth = hslider("/curves/right_air/depth", 0.95, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_1 = min(1.5, max(0, patch_param_1_base * (1.0 - patch_param_1_curve_depth) + patch_param_1_curve_value * patch_param_1_curve_depth));
|
||||||
|
patch_param_2_base = hslider("/bird/left/opening", 0.2, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_2_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_2_curve_value = select2(patch_param_2_curve_time < 0.03, select2(patch_param_2_curve_time < 0.62, select2(patch_param_2_curve_time < 0.82, 0.02, seg(patch_param_2_curve_time, 0.62, 0.2, 0.18, 0.02)), seg(patch_param_2_curve_time, 0.03, 0.59, 0.21, 0.18)), seg(patch_param_2_curve_time, 0, 0.03, 0.01, 0.21));
|
||||||
|
patch_param_2_curve_depth = hslider("/curves/left_labium_gate/depth", 0.9, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_2 = min(1, max(0, patch_param_2_base * (1.0 - patch_param_2_curve_depth) + patch_param_2_curve_value * patch_param_2_curve_depth));
|
||||||
|
patch_param_3_base = hslider("/bird/right/opening", 0.18, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_3_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_3_curve_value = select2(patch_param_3_curve_time < 0.035, select2(patch_param_3_curve_time < 0.54, select2(patch_param_3_curve_time < 0.82, 0.02, seg(patch_param_3_curve_time, 0.54, 0.28, 0.15, 0.02)), seg(patch_param_3_curve_time, 0.035, 0.505, 0.18, 0.15)), seg(patch_param_3_curve_time, 0, 0.035, 0.01, 0.18));
|
||||||
|
patch_param_3_curve_depth = hslider("/curves/right_labium_gate/depth", 0.9, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_3 = min(1, max(0, patch_param_3_base * (1.0 - patch_param_3_curve_depth) + patch_param_3_curve_value * patch_param_3_curve_depth));
|
||||||
|
patch_param_4 = hslider("/bird/left/tension", 0.46, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_5 = hslider("/bird/right/tension", 0.52, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_6_base = hslider("/bird/beak/opening", 0.92, 0, 1.5, 0.001) : si.smoo;
|
||||||
|
patch_param_6_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_6_curve_value = select2(patch_param_6_curve_time < 0.04, select2(patch_param_6_curve_time < 0.44, select2(patch_param_6_curve_time < 0.82, 0.35, seg(patch_param_6_curve_time, 0.44, 0.38, 1.08, 0.35)), seg(patch_param_6_curve_time, 0.04, 0.4, 0.92, 1.08)), seg(patch_param_6_curve_time, 0, 0.04, 0.22, 0.92));
|
||||||
|
patch_param_6_curve_depth = hslider("/curves/beak_gate/depth", 0.75, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_6 = min(1.5, max(0, patch_param_6_base * (1.0 - patch_param_6_curve_depth) + patch_param_6_curve_value * patch_param_6_curve_depth));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_graph_drive = 1.0;
|
||||||
|
voice_0_graph_terminal_area_left_merge = max(0.000001, 0.1444 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_left_merge = 0;
|
||||||
|
voice_0_graph_terminal_area_right_merge = max(0.000001, 0.1296 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_right_merge = 0;
|
||||||
|
voice_0_graph_terminal_area_trachea_base = max(0.000001, 0.1296 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_trachea_base = 0;
|
||||||
|
voice_0_graph_terminal_area_left_labium = max(0.000001, 0.0324 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_left_labium = 0;
|
||||||
|
voice_0_graph_terminal_area_right_labium = max(0.000001, 0.0289 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_right_labium = 0;
|
||||||
|
voice_0_graph_terminal_area_beak = max(0.000001, 0.1444 * max(0.0, 0.92));
|
||||||
|
voice_0_graph_terminal_reflection_beak = ((0.96) * (1.0 - clip01(pow(clip01((max(0.0, patch_param_6)) / 1.5), 2.0))) + (-0.72) * clip01(pow(clip01((max(0.0, patch_param_6)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_node_area_left_labium = max(0.000001, voice_0_graph_terminal_area_left_labium);
|
||||||
|
voice_0_graph_node_reflection_left_labium = (voice_0_graph_terminal_reflection_left_labium) / 1;
|
||||||
|
voice_0_graph_node_area_left_merge = max(0.000001, voice_0_graph_terminal_area_left_merge);
|
||||||
|
voice_0_graph_node_reflection_left_merge = (voice_0_graph_terminal_reflection_left_merge) / 1;
|
||||||
|
voice_0_graph_node_area_right_labium = max(0.000001, voice_0_graph_terminal_area_right_labium);
|
||||||
|
voice_0_graph_node_reflection_right_labium = (voice_0_graph_terminal_reflection_right_labium) / 1;
|
||||||
|
voice_0_graph_node_area_right_merge = max(0.000001, voice_0_graph_terminal_area_right_merge);
|
||||||
|
voice_0_graph_node_reflection_right_merge = (voice_0_graph_terminal_reflection_right_merge) / 1;
|
||||||
|
voice_0_graph_node_area_trachea_base = max(0.000001, voice_0_graph_terminal_area_trachea_base);
|
||||||
|
voice_0_graph_node_reflection_trachea_base = (voice_0_graph_terminal_reflection_trachea_base) / 1;
|
||||||
|
voice_0_graph_node_area_beak = max(0.000001, voice_0_graph_terminal_area_beak);
|
||||||
|
voice_0_graph_node_reflection_beak = (voice_0_graph_terminal_reflection_beak) / 1;
|
||||||
|
voice_0_graph_segment_area_0 = max(0.000001, 0.075625);
|
||||||
|
voice_0_graph_segment_area_1 = max(0.000001, 0.07022501);
|
||||||
|
voice_0_graph_segment_area_2 = max(0.000001, 0.2916);
|
||||||
|
voice_0_graph_connection_coupling_syrinx_merge = clip01(1);
|
||||||
|
voice_0_graph_connection_loss_syrinx_merge = clip01(1);
|
||||||
|
voice_0_graph_radiation_gain = 1.0;
|
||||||
|
voice_0_graph_source_left_labium = 0.0;
|
||||||
|
voice_0_graph_source_right_labium = 0.0;
|
||||||
|
voice_0_graph(x) = voice_0_graph_loop ~ si.bus(6) : (si.block(6), _) with {
|
||||||
|
voice_0_graph_loop(r0, r1, r2, l0, l1, l2) = voice_0_graph_next_r0, voice_0_graph_next_r1, voice_0_graph_next_r2, voice_0_graph_next_l0, voice_0_graph_next_l1, voice_0_graph_next_l2, voice_0_graph_out with {
|
||||||
|
voice_0_graph_in_0_start = l0;
|
||||||
|
voice_0_graph_in_0_end = r0;
|
||||||
|
voice_0_graph_in_1_start = l1;
|
||||||
|
voice_0_graph_in_1_end = r1;
|
||||||
|
voice_0_graph_in_2_start = l2;
|
||||||
|
voice_0_graph_in_2_end = r2;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_load_pressure = voice_0_graph_node_incident_pressure_left_labium;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_stiffness_hint = max(0.00002, min(0.16, pow(2.0 * ma.PI * max(20.0, ((max(20, 980 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) / ma.SR, 2.0)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow_scale = max(0.0, 1.22);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_tissue_loss = clip01(0.16);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_aperture_shape = clip01(0.38);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow_loss = max(0.0, 0.32);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_reservoir_pressure = (clip01(patch_param_0)) * (max(0.0, 1));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_downstream_pressure = (max(0.0, 0.02)) + (max(0.0, 0.46)) * ma.tanh(voice_0_graph_source_left_labium_left_labium_load_pressure);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_pressure_drive = max(0.0, voice_0_graph_source_left_labium_left_labium_reservoir_pressure - voice_0_graph_source_left_labium_left_labium_downstream_pressure);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_stiffness = max(0.00002, (max(0.0, 0.032)) * (0.55 + 1.10 * (clip01(patch_param_4))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_frequency = max(45.0, min(10000.0, (ma.SR / (2.0 * ma.PI)) * sqrt(max(0.00002, voice_0_graph_source_left_labium_left_labium_stiffness) / (max(0.02, 0.24)))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_load_detune = 1.0 - 0.035 * ma.tanh(voice_0_graph_source_left_labium_left_labium_load_pressure * (max(0.0, 0.46)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_oscillation_threshold = 0.030 + 0.070 * (max(0.0, 0.11)) + 0.080 * voice_0_graph_source_left_labium_left_labium_tissue_loss + 0.18 * clip01(max(0.0, patch_param_2));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_oscillation_gate = clip01((voice_0_graph_source_left_labium_left_labium_pressure_drive - voice_0_graph_source_left_labium_left_labium_oscillation_threshold) * (3.0 + 1.6 * max(0.0, 1.0 - (max(0.0, 0.11)))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_q = 3.0 + 22.0 * clip01((clip01(patch_param_0)) * (1.0 - 0.45 * (max(0.0, 0.11)) - 0.35 * voice_0_graph_source_left_labium_left_labium_tissue_loss) + 0.35 * (clip01(patch_param_4)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_seed = (no.noise * 0.018 + voice_0_graph_source_left_labium_left_labium_pressure_drive * max(0.0, 0.22 - clip01(max(0.0, patch_param_2))) * 0.16);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_ring = voice_0_graph_source_left_labium_left_labium_modal_seed : fi.resonbp(voice_0_graph_source_left_labium_left_labium_modal_frequency, voice_0_graph_source_left_labium_left_labium_modal_q, 1);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_oscillator = os.osc(voice_0_graph_source_left_labium_left_labium_modal_frequency * voice_0_graph_source_left_labium_left_labium_load_detune) * voice_0_graph_source_left_labium_left_labium_oscillation_gate;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_tissue = (voice_0_graph_source_left_labium_left_labium_modal_oscillator * (0.70 + 0.80 * (clip01(patch_param_4))) + voice_0_graph_source_left_labium_left_labium_modal_ring * 0.35) * max(0.0, 1.0 - 0.35 * (max(0.0, 0.11)) - 0.45 * voice_0_graph_source_left_labium_left_labium_tissue_loss);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_velocity_decay = min(0.9995, max(0.20, 1.0 - ((0.008 + 0.08 * (max(0.0, 0.11)) + 0.055 * voice_0_graph_source_left_labium_left_labium_tissue_loss) / (max(0.02, 0.24)))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_displacement_decay = min(0.9998, max(0.20, 1.0 - 0.0008 / (max(0.02, 0.24))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_force = (voice_0_graph_source_left_labium_left_labium_pressure_drive * (0.0004 + 0.0024 * (1.0 - clip01(max(0.0, patch_param_2)))) - (max(0.0, 0.46)) * voice_0_graph_source_left_labium_left_labium_load_pressure * 0.001) / (max(0.02, 0.24));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_velocity = voice_0_graph_source_left_labium_left_labium_force : + ~ *(voice_0_graph_source_left_labium_left_labium_velocity_decay / (1.0 + 10.0 * voice_0_graph_source_left_labium_left_labium_stiffness + 0.4 * (max(0.0, 0.82))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_displacement = voice_0_graph_source_left_labium_left_labium_velocity : + ~ *(voice_0_graph_source_left_labium_left_labium_displacement_decay / (1.0 + 1.5 * voice_0_graph_source_left_labium_left_labium_stiffness));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_aperture_linear = max(0.0, (max(0.0, 0.018)) + (max(0.0, patch_param_2)) + voice_0_graph_source_left_labium_left_labium_displacement - (max(0.0, 0.82)) * pow(voice_0_graph_source_left_labium_left_labium_displacement, 3.0));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_aperture = voice_0_graph_source_left_labium_left_labium_aperture_linear * (1.0 - 0.55 * voice_0_graph_source_left_labium_left_labium_aperture_shape) + pow(voice_0_graph_source_left_labium_left_labium_aperture_linear, 2.0) * (0.55 * voice_0_graph_source_left_labium_left_labium_aperture_shape);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow_resistance = 1.0 + voice_0_graph_source_left_labium_left_labium_flow_loss * (0.35 + 1.8 / max(0.035, voice_0_graph_source_left_labium_left_labium_aperture + (max(0.0, 0.018))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_turbulence = no.noise : fi.highpass(2, 1200.0 + 2800.0 * clip01(1.0 - voice_0_graph_source_left_labium_left_labium_aperture));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_voicing = voice_0_graph_source_left_labium_left_labium_modal_tissue * (0.18 + 1.45 * (clip01(patch_param_4)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow = ma.tanh(((voice_0_graph_source_left_labium_left_labium_pressure_drive * voice_0_graph_source_left_labium_left_labium_aperture * (1.5 + 3.5 * voice_0_graph_source_left_labium_left_labium_flow_scale)) + (voice_0_graph_source_left_labium_left_labium_voicing * voice_0_graph_source_left_labium_left_labium_pressure_drive * (0.45 + 1.55 * (clip01(patch_param_4))))) / voice_0_graph_source_left_labium_left_labium_flow_resistance);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_noise = voice_0_graph_source_left_labium_left_labium_turbulence * (clip01(0.018)) * voice_0_graph_source_left_labium_left_labium_pressure_drive * clip01(voice_0_graph_source_left_labium_left_labium_aperture) * (1.0 - 0.55 * (clip01(patch_param_4))) / voice_0_graph_source_left_labium_left_labium_flow_resistance;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_out = (voice_0_graph_source_left_labium_left_labium_flow * voice_0_graph_source_left_labium_left_labium_flow_scale * (0.55 + 2.6 * voice_0_graph_source_left_labium_left_labium_pressure_drive) + voice_0_graph_source_left_labium_left_labium_noise) * ((1) * (1.0));
|
||||||
|
voice_0_graph_node_incident_pressure_left_labium = (voice_0_graph_in_0_start) / 1;
|
||||||
|
voice_0_graph_node_source_left_labium = voice_0_graph_source_left_labium_left_labium_out;
|
||||||
|
voice_0_graph_node_incident_pressure_left_merge = (voice_0_graph_in_0_end) / 1;
|
||||||
|
voice_0_graph_node_source_left_merge = 0.0;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_load_pressure = voice_0_graph_node_incident_pressure_right_labium;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_stiffness_hint = max(0.00002, min(0.16, pow(2.0 * ma.PI * max(20.0, ((max(20, 980 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) / ma.SR, 2.0)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow_scale = max(0.0, 1.18);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_tissue_loss = clip01(0.18);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_aperture_shape = clip01(0.38);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow_loss = max(0.0, 0.32);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_reservoir_pressure = (clip01(patch_param_1)) * (max(0.0, 1));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_downstream_pressure = (max(0.0, 0.02)) + (max(0.0, 0.46)) * ma.tanh(voice_0_graph_source_right_labium_right_labium_load_pressure);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_pressure_drive = max(0.0, voice_0_graph_source_right_labium_right_labium_reservoir_pressure - voice_0_graph_source_right_labium_right_labium_downstream_pressure);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_stiffness = max(0.00002, (max(0.0, 0.036)) * (0.55 + 1.10 * (clip01(patch_param_5))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_frequency = max(45.0, min(10000.0, (ma.SR / (2.0 * ma.PI)) * sqrt(max(0.00002, voice_0_graph_source_right_labium_right_labium_stiffness) / (max(0.02, 0.25)))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_load_detune = 1.0 - 0.035 * ma.tanh(voice_0_graph_source_right_labium_right_labium_load_pressure * (max(0.0, 0.46)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_oscillation_threshold = 0.030 + 0.070 * (max(0.0, 0.12)) + 0.080 * voice_0_graph_source_right_labium_right_labium_tissue_loss + 0.18 * clip01(max(0.0, patch_param_3));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_oscillation_gate = clip01((voice_0_graph_source_right_labium_right_labium_pressure_drive - voice_0_graph_source_right_labium_right_labium_oscillation_threshold) * (3.0 + 1.6 * max(0.0, 1.0 - (max(0.0, 0.12)))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_q = 3.0 + 22.0 * clip01((clip01(patch_param_1)) * (1.0 - 0.45 * (max(0.0, 0.12)) - 0.35 * voice_0_graph_source_right_labium_right_labium_tissue_loss) + 0.35 * (clip01(patch_param_5)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_seed = (no.noise * 0.018 + voice_0_graph_source_right_labium_right_labium_pressure_drive * max(0.0, 0.22 - clip01(max(0.0, patch_param_3))) * 0.16);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_ring = voice_0_graph_source_right_labium_right_labium_modal_seed : fi.resonbp(voice_0_graph_source_right_labium_right_labium_modal_frequency, voice_0_graph_source_right_labium_right_labium_modal_q, 1);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_oscillator = os.osc(voice_0_graph_source_right_labium_right_labium_modal_frequency * voice_0_graph_source_right_labium_right_labium_load_detune) * voice_0_graph_source_right_labium_right_labium_oscillation_gate;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_tissue = (voice_0_graph_source_right_labium_right_labium_modal_oscillator * (0.70 + 0.80 * (clip01(patch_param_5))) + voice_0_graph_source_right_labium_right_labium_modal_ring * 0.35) * max(0.0, 1.0 - 0.35 * (max(0.0, 0.12)) - 0.45 * voice_0_graph_source_right_labium_right_labium_tissue_loss);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_velocity_decay = min(0.9995, max(0.20, 1.0 - ((0.008 + 0.08 * (max(0.0, 0.12)) + 0.055 * voice_0_graph_source_right_labium_right_labium_tissue_loss) / (max(0.02, 0.25)))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_displacement_decay = min(0.9998, max(0.20, 1.0 - 0.0008 / (max(0.02, 0.25))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_force = (voice_0_graph_source_right_labium_right_labium_pressure_drive * (0.0004 + 0.0024 * (1.0 - clip01(max(0.0, patch_param_3)))) - (max(0.0, 0.46)) * voice_0_graph_source_right_labium_right_labium_load_pressure * 0.001) / (max(0.02, 0.25));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_velocity = voice_0_graph_source_right_labium_right_labium_force : + ~ *(voice_0_graph_source_right_labium_right_labium_velocity_decay / (1.0 + 10.0 * voice_0_graph_source_right_labium_right_labium_stiffness + 0.4 * (max(0.0, 0.84))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_displacement = voice_0_graph_source_right_labium_right_labium_velocity : + ~ *(voice_0_graph_source_right_labium_right_labium_displacement_decay / (1.0 + 1.5 * voice_0_graph_source_right_labium_right_labium_stiffness));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_aperture_linear = max(0.0, (max(0.0, 0.018)) + (max(0.0, patch_param_3)) + voice_0_graph_source_right_labium_right_labium_displacement - (max(0.0, 0.84)) * pow(voice_0_graph_source_right_labium_right_labium_displacement, 3.0));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_aperture = voice_0_graph_source_right_labium_right_labium_aperture_linear * (1.0 - 0.55 * voice_0_graph_source_right_labium_right_labium_aperture_shape) + pow(voice_0_graph_source_right_labium_right_labium_aperture_linear, 2.0) * (0.55 * voice_0_graph_source_right_labium_right_labium_aperture_shape);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow_resistance = 1.0 + voice_0_graph_source_right_labium_right_labium_flow_loss * (0.35 + 1.8 / max(0.035, voice_0_graph_source_right_labium_right_labium_aperture + (max(0.0, 0.018))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_turbulence = no.noise : fi.highpass(2, 1200.0 + 2800.0 * clip01(1.0 - voice_0_graph_source_right_labium_right_labium_aperture));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_voicing = voice_0_graph_source_right_labium_right_labium_modal_tissue * (0.18 + 1.45 * (clip01(patch_param_5)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow = ma.tanh(((voice_0_graph_source_right_labium_right_labium_pressure_drive * voice_0_graph_source_right_labium_right_labium_aperture * (1.5 + 3.5 * voice_0_graph_source_right_labium_right_labium_flow_scale)) + (voice_0_graph_source_right_labium_right_labium_voicing * voice_0_graph_source_right_labium_right_labium_pressure_drive * (0.45 + 1.55 * (clip01(patch_param_5))))) / voice_0_graph_source_right_labium_right_labium_flow_resistance);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_noise = voice_0_graph_source_right_labium_right_labium_turbulence * (clip01(0.016)) * voice_0_graph_source_right_labium_right_labium_pressure_drive * clip01(voice_0_graph_source_right_labium_right_labium_aperture) * (1.0 - 0.55 * (clip01(patch_param_5))) / voice_0_graph_source_right_labium_right_labium_flow_resistance;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_out = (voice_0_graph_source_right_labium_right_labium_flow * voice_0_graph_source_right_labium_right_labium_flow_scale * (0.55 + 2.6 * voice_0_graph_source_right_labium_right_labium_pressure_drive) + voice_0_graph_source_right_labium_right_labium_noise) * ((0.94) * (1.0));
|
||||||
|
voice_0_graph_node_incident_pressure_right_labium = (voice_0_graph_in_1_start) / 1;
|
||||||
|
voice_0_graph_node_source_right_labium = voice_0_graph_source_right_labium_right_labium_out;
|
||||||
|
voice_0_graph_node_incident_pressure_right_merge = (voice_0_graph_in_1_end) / 1;
|
||||||
|
voice_0_graph_node_source_right_merge = 0.0;
|
||||||
|
voice_0_graph_node_incident_pressure_trachea_base = (voice_0_graph_in_2_start) / 1;
|
||||||
|
voice_0_graph_node_source_trachea_base = 0.0;
|
||||||
|
voice_0_graph_node_incident_pressure_beak = (voice_0_graph_in_2_end) / 1;
|
||||||
|
voice_0_graph_node_source_beak = 0.0;
|
||||||
|
voice_0_graph_connection_reflection_syrinx_merge_left_merge_0_end = (2.0 * (voice_0_graph_segment_area_0) - (voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2)) / max(0.000001, voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2);
|
||||||
|
voice_0_graph_connection_scattered_syrinx_merge_left_merge_0_end = voice_0_graph_connection_reflection_syrinx_merge_left_merge_0_end * voice_0_graph_in_0_end + (1.0 + voice_0_graph_connection_reflection_syrinx_merge_left_merge_0_end) * (voice_0_graph_in_1_end + voice_0_graph_in_2_start);
|
||||||
|
voice_0_graph_out_0_end = ((voice_0_graph_connection_scattered_syrinx_merge_left_merge_0_end) * (voice_0_graph_connection_coupling_syrinx_merge) + voice_0_graph_in_0_end * (1.0 - (voice_0_graph_connection_coupling_syrinx_merge)) + (voice_0_graph_node_source_left_merge) / 1) * voice_0_graph_connection_loss_syrinx_merge;
|
||||||
|
voice_0_graph_connection_reflection_syrinx_merge_right_merge_1_end = (2.0 * (voice_0_graph_segment_area_1) - (voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2)) / max(0.000001, voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2);
|
||||||
|
voice_0_graph_connection_scattered_syrinx_merge_right_merge_1_end = voice_0_graph_connection_reflection_syrinx_merge_right_merge_1_end * voice_0_graph_in_1_end + (1.0 + voice_0_graph_connection_reflection_syrinx_merge_right_merge_1_end) * (voice_0_graph_in_0_end + voice_0_graph_in_2_start);
|
||||||
|
voice_0_graph_out_1_end = ((voice_0_graph_connection_scattered_syrinx_merge_right_merge_1_end) * (voice_0_graph_connection_coupling_syrinx_merge) + voice_0_graph_in_1_end * (1.0 - (voice_0_graph_connection_coupling_syrinx_merge)) + (voice_0_graph_node_source_right_merge) / 1) * voice_0_graph_connection_loss_syrinx_merge;
|
||||||
|
voice_0_graph_connection_reflection_syrinx_merge_trachea_base_2_start = (2.0 * (voice_0_graph_segment_area_2) - (voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2)) / max(0.000001, voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2);
|
||||||
|
voice_0_graph_connection_scattered_syrinx_merge_trachea_base_2_start = voice_0_graph_connection_reflection_syrinx_merge_trachea_base_2_start * voice_0_graph_in_2_start + (1.0 + voice_0_graph_connection_reflection_syrinx_merge_trachea_base_2_start) * (voice_0_graph_in_0_end + voice_0_graph_in_1_end);
|
||||||
|
voice_0_graph_out_2_start = ((voice_0_graph_connection_scattered_syrinx_merge_trachea_base_2_start) * (voice_0_graph_connection_coupling_syrinx_merge) + voice_0_graph_in_2_start * (1.0 - (voice_0_graph_connection_coupling_syrinx_merge)) + (voice_0_graph_node_source_trachea_base) / 1) * voice_0_graph_connection_loss_syrinx_merge;
|
||||||
|
voice_0_graph_connection_energy_in_syrinx_merge = (voice_0_graph_segment_area_0) * pow(voice_0_graph_in_0_end, 2.0) + (voice_0_graph_segment_area_1) * pow(voice_0_graph_in_1_end, 2.0) + (voice_0_graph_segment_area_2) * pow(voice_0_graph_in_2_start, 2.0);
|
||||||
|
voice_0_graph_connection_energy_out_syrinx_merge = (voice_0_graph_segment_area_0) * pow(voice_0_graph_out_0_end, 2.0) + (voice_0_graph_segment_area_1) * pow(voice_0_graph_out_1_end, 2.0) + (voice_0_graph_segment_area_2) * pow(voice_0_graph_out_2_start, 2.0);
|
||||||
|
voice_0_graph_out_0_start = voice_0_graph_in_0_start * voice_0_graph_node_reflection_left_labium + (voice_0_graph_node_source_left_labium) / 1;
|
||||||
|
voice_0_graph_out_1_start = voice_0_graph_in_1_start * voice_0_graph_node_reflection_right_labium + (voice_0_graph_node_source_right_labium) / 1;
|
||||||
|
voice_0_graph_radiation_boundary_load_beak = clip01((voice_0_graph_radiation_admittance_beak) * (voice_0_graph_radiation_differentiation_beak) * (clip01(0.98)));
|
||||||
|
voice_0_graph_radiation_reflected_raw_beak = (voice_0_graph_in_2_end) * (voice_0_graph_terminal_reflection_beak) + (voice_0_graph_node_source_beak);
|
||||||
|
voice_0_graph_radiation_reflected_loaded_beak = (voice_0_graph_radiation_reflected_raw_beak) * (1.0 - (voice_0_graph_radiation_boundary_load_beak)) + ((voice_0_graph_radiation_reflected_raw_beak) : fi.lowpass(1, voice_0_graph_radiation_highpass_beak)) * (voice_0_graph_radiation_boundary_load_beak);
|
||||||
|
voice_0_graph_out_2_end = voice_0_graph_radiation_reflected_loaded_beak;
|
||||||
|
voice_0_graph_radiation_model_beak = 2;
|
||||||
|
voice_0_graph_radiation_reference_area_beak = max(0.10, 1.10 * 1.10 * (0.25 + 0.75 * clip01(pow(clip01((max(0.0, patch_param_6)) / 1.5), 2.0))));
|
||||||
|
voice_0_graph_radiation_differentiation_beak = (0.50 + 0.22 * clip01(pow(clip01((max(0.0, patch_param_6)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_radiation_highpass_beak = (180.0 + 220.0 * clip01(pow(clip01((max(0.0, patch_param_6)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_radiation_admittance_beak = sqrt(clip01((voice_0_graph_terminal_area_beak) / max(0.000001, (voice_0_graph_terminal_area_beak) + (voice_0_graph_radiation_reference_area_beak))));
|
||||||
|
voice_0_graph_radiation_flow_beak = ((voice_0_graph_in_2_end - voice_0_graph_out_2_end));
|
||||||
|
voice_0_graph_delay_0 = max(0.000001, 0.00010496 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_0 = clip01((0.996) * ((0.996 - 0.018 * clip01(0.050 / max(0.000001, (voice_0_graph_segment_area_0) + 0.050)))));
|
||||||
|
voice_0_graph_next_r0 = voice_0_graph_out_0_start : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_0))) * voice_0_graph_segment_loss_0;
|
||||||
|
voice_0_graph_next_l0 = voice_0_graph_out_0_end : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_0))) * voice_0_graph_segment_loss_0;
|
||||||
|
voice_0_graph_delay_1 = max(0.000001, 0.00009913 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_1 = clip01((0.996) * ((0.996 - 0.018 * clip01(0.050 / max(0.000001, (voice_0_graph_segment_area_1) + 0.050)))));
|
||||||
|
voice_0_graph_next_r1 = voice_0_graph_out_1_start : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_1))) * voice_0_graph_segment_loss_1;
|
||||||
|
voice_0_graph_next_l1 = voice_0_graph_out_1_end : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_1))) * voice_0_graph_segment_loss_1;
|
||||||
|
voice_0_graph_delay_2 = max(0.000001, 0.00023907 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_2 = clip01((0.997) * (sqrt(clip01((voice_0_graph_segment_area_2) / max(0.000001, (voice_0_graph_segment_area_2) + 0.020)))));
|
||||||
|
voice_0_graph_next_r2 = voice_0_graph_out_2_start : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_2))) * voice_0_graph_segment_loss_2;
|
||||||
|
voice_0_graph_next_l2 = voice_0_graph_out_2_end : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_2))) * voice_0_graph_segment_loss_2;
|
||||||
|
voice_0_graph_out = (((voice_0_graph_radiation_flow_beak * (1.0 - (voice_0_graph_radiation_differentiation_beak)) + (voice_0_graph_radiation_flow_beak : fi.highpass(1, voice_0_graph_radiation_highpass_beak)) * (voice_0_graph_radiation_differentiation_beak)) * clip01(0.98) * voice_0_graph_radiation_admittance_beak)) * voice_0_graph_radiation_gain;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
voice_0_acoustic_graph_radiated = voice_0_graph(voice_0_graph_drive);
|
||||||
|
voice_0_freq = ((max(20, 980 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = voice_0_acoustic_graph_radiated;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.06, 0.82) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.24);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 0.4);
|
||||||
253
tools/aqua-playground/lowered/advanced-bird-syrinx.dsp
Normal file
253
tools/aqua-playground/lowered/advanced-bird-syrinx.dsp
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "advanced-bird-syrinx";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_param_0_base = hslider("/bird/left/pressure", 0.82, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_0_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_0_curve_value = select2(patch_param_0_curve_time < 0.035, select2(patch_param_0_curve_time < 0.32, select2(patch_param_0_curve_time < 0.78, select2(patch_param_0_curve_time < 1.08, 0.38, seg(patch_param_0_curve_time, 0.78, 0.3000001, 0.74, 0.38)), seg(patch_param_0_curve_time, 0.32, 0.46, 0.88, 0.74)), seg(patch_param_0_curve_time, 0.035, 0.285, 0.82, 0.88)), seg(patch_param_0_curve_time, 0, 0.035, 0.08, 0.82));
|
||||||
|
patch_param_0_curve_depth = hslider("/curves/left_pressure/depth", 1, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_0 = min(1, max(0, patch_param_0_base * (1.0 - patch_param_0_curve_depth) + patch_param_0_curve_value * patch_param_0_curve_depth));
|
||||||
|
patch_param_1_base = hslider("/bird/right/pressure", 0.7, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_1_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_1_curve_value = select2(patch_param_1_curve_time < 0.04, select2(patch_param_1_curve_time < 0.28, select2(patch_param_1_curve_time < 0.72, select2(patch_param_1_curve_time < 1.08, 0.28, seg(patch_param_1_curve_time, 0.72, 0.36, 0.62, 0.28)), seg(patch_param_1_curve_time, 0.28, 0.44, 0.74, 0.62)), seg(patch_param_1_curve_time, 0.04, 0.24, 0.68, 0.74)), seg(patch_param_1_curve_time, 0, 0.04, 0.04, 0.68));
|
||||||
|
patch_param_1_curve_depth = hslider("/curves/right_pressure/depth", 1, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_1 = min(1, max(0, patch_param_1_base * (1.0 - patch_param_1_curve_depth) + patch_param_1_curve_value * patch_param_1_curve_depth));
|
||||||
|
patch_param_2_base = hslider("/bird/left/opening", 0.24, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_2_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_2_curve_value = select2(patch_param_2_curve_time < 0.03, select2(patch_param_2_curve_time < 0.76, select2(patch_param_2_curve_time < 1.08, 0.04, seg(patch_param_2_curve_time, 0.76, 0.3200001, 0.2, 0.04)), seg(patch_param_2_curve_time, 0.03, 0.73, 0.24, 0.2)), seg(patch_param_2_curve_time, 0, 0.03, 0.02, 0.24));
|
||||||
|
patch_param_2_curve_depth = hslider("/curves/left_opening/depth", 0.85, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_2 = min(1, max(0, patch_param_2_base * (1.0 - patch_param_2_curve_depth) + patch_param_2_curve_value * patch_param_2_curve_depth));
|
||||||
|
patch_param_3_base = hslider("/bird/right/opening", 0.21, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_3_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_3_curve_value = select2(patch_param_3_curve_time < 0.035, select2(patch_param_3_curve_time < 0.7, select2(patch_param_3_curve_time < 1.08, 0.04, seg(patch_param_3_curve_time, 0.7, 0.3800001, 0.18, 0.04)), seg(patch_param_3_curve_time, 0.035, 0.665, 0.21, 0.18)), seg(patch_param_3_curve_time, 0, 0.035, 0.02, 0.21));
|
||||||
|
patch_param_3_curve_depth = hslider("/curves/right_opening/depth", 0.85, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_3 = min(1, max(0, patch_param_3_base * (1.0 - patch_param_3_curve_depth) + patch_param_3_curve_value * patch_param_3_curve_depth));
|
||||||
|
patch_param_4 = hslider("/bird/left/tension", 0.38, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_5 = hslider("/bird/right/tension", 0.45, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_6 = hslider("/bird/left/mass", 0.32, 0.02, 2, 0.001) : si.smoo;
|
||||||
|
patch_param_7 = hslider("/bird/right/mass", 0.34, 0.02, 2, 0.001) : si.smoo;
|
||||||
|
patch_param_8 = hslider("/bird/left/damping", 0.14, 0, 2, 0.001) : si.smoo;
|
||||||
|
patch_param_9 = hslider("/bird/right/damping", 0.16, 0, 2, 0.001) : si.smoo;
|
||||||
|
patch_param_10 = hslider("/bird/left/stiffness", 0.025, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_11 = hslider("/bird/right/stiffness", 0.03, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_12 = hslider("/bird/left/flow_scale", 1.2, 0, 4, 0.001) : si.smoo;
|
||||||
|
patch_param_13 = hslider("/bird/right/flow_scale", 1.1, 0, 4, 0.001) : si.smoo;
|
||||||
|
patch_param_14 = hslider("/bird/left/tissue_loss", 0.18, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_15 = hslider("/bird/right/tissue_loss", 0.2, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_16 = hslider("/bird/left/load_coupling", 0.42, 0, 2, 0.001) : si.smoo;
|
||||||
|
patch_param_17 = hslider("/bird/right/load_coupling", 0.42, 0, 2, 0.001) : si.smoo;
|
||||||
|
patch_param_18 = hslider("/bird/left/rest_opening", 0.025, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_19 = hslider("/bird/right/rest_opening", 0.025, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_20 = hslider("/bird/load", 0.85, 0, 2, 0.001) : si.smoo;
|
||||||
|
patch_param_21_base = hslider("/bird/beak/opening", 0.95, 0, 1.5, 0.001) : si.smoo;
|
||||||
|
patch_param_21_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_21_curve_value = select2(patch_param_21_curve_time < 0.06, select2(patch_param_21_curve_time < 0.82, select2(patch_param_21_curve_time < 1.08, 0.55, seg(patch_param_21_curve_time, 0.82, 0.2600001, 1.05, 0.55)), seg(patch_param_21_curve_time, 0.06, 0.76, 0.92, 1.05)), seg(patch_param_21_curve_time, 0, 0.06, 0.35, 0.92));
|
||||||
|
patch_param_21_curve_depth = hslider("/curves/beak_opening/depth", 0.7, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_21 = min(1.5, max(0, patch_param_21_base * (1.0 - patch_param_21_curve_depth) + patch_param_21_curve_value * patch_param_21_curve_depth));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_graph_drive = 1.0;
|
||||||
|
voice_0_graph_terminal_area_left_merge = max(0.000001, 0.1764 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_left_merge = 0;
|
||||||
|
voice_0_graph_terminal_area_right_merge = max(0.000001, 0.16 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_right_merge = 0;
|
||||||
|
voice_0_graph_terminal_area_trachea_base = max(0.000001, 0.1444 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_trachea_base = 0;
|
||||||
|
voice_0_graph_terminal_area_left_labium = max(0.000001, 0.0484 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_left_labium = 0;
|
||||||
|
voice_0_graph_terminal_area_right_labium = max(0.000001, 0.04 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_right_labium = 0;
|
||||||
|
voice_0_graph_terminal_area_beak = max(0.000001, 0.2116 * max(0.0, 0.95));
|
||||||
|
voice_0_graph_terminal_reflection_beak = ((0.96) * (1.0 - clip01(pow(clip01((max(0.0, patch_param_21)) / 1.5), 2.0))) + (-0.72) * clip01(pow(clip01((max(0.0, patch_param_21)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_node_area_left_labium = max(0.000001, voice_0_graph_terminal_area_left_labium);
|
||||||
|
voice_0_graph_node_reflection_left_labium = (voice_0_graph_terminal_reflection_left_labium) / 1;
|
||||||
|
voice_0_graph_node_area_left_merge = max(0.000001, voice_0_graph_terminal_area_left_merge);
|
||||||
|
voice_0_graph_node_reflection_left_merge = (voice_0_graph_terminal_reflection_left_merge) / 1;
|
||||||
|
voice_0_graph_node_area_right_labium = max(0.000001, voice_0_graph_terminal_area_right_labium);
|
||||||
|
voice_0_graph_node_reflection_right_labium = (voice_0_graph_terminal_reflection_right_labium) / 1;
|
||||||
|
voice_0_graph_node_area_right_merge = max(0.000001, voice_0_graph_terminal_area_right_merge);
|
||||||
|
voice_0_graph_node_reflection_right_merge = (voice_0_graph_terminal_reflection_right_merge) / 1;
|
||||||
|
voice_0_graph_node_area_trachea_base = max(0.000001, voice_0_graph_terminal_area_trachea_base);
|
||||||
|
voice_0_graph_node_reflection_trachea_base = (voice_0_graph_terminal_reflection_trachea_base) / 1;
|
||||||
|
voice_0_graph_node_area_beak = max(0.000001, voice_0_graph_terminal_area_beak);
|
||||||
|
voice_0_graph_node_reflection_beak = (voice_0_graph_terminal_reflection_beak) / 1;
|
||||||
|
voice_0_graph_segment_area_0 = max(0.000001, 0.1089);
|
||||||
|
voice_0_graph_segment_area_1 = max(0.000001, 0.0961);
|
||||||
|
voice_0_graph_segment_area_2 = max(0.000001, 0.2704);
|
||||||
|
voice_0_graph_connection_coupling_syrinx_merge = clip01(1);
|
||||||
|
voice_0_graph_connection_loss_syrinx_merge = clip01(1);
|
||||||
|
voice_0_graph_radiation_gain = 1.0;
|
||||||
|
voice_0_graph_source_left_labium = 0.0;
|
||||||
|
voice_0_graph_source_right_labium = 0.0;
|
||||||
|
voice_0_graph(x) = voice_0_graph_loop ~ si.bus(6) : (si.block(6), _) with {
|
||||||
|
voice_0_graph_loop(r0, r1, r2, l0, l1, l2) = voice_0_graph_next_r0, voice_0_graph_next_r1, voice_0_graph_next_r2, voice_0_graph_next_l0, voice_0_graph_next_l1, voice_0_graph_next_l2, voice_0_graph_out with {
|
||||||
|
voice_0_graph_in_0_start = l0;
|
||||||
|
voice_0_graph_in_0_end = r0;
|
||||||
|
voice_0_graph_in_1_start = l1;
|
||||||
|
voice_0_graph_in_1_end = r1;
|
||||||
|
voice_0_graph_in_2_start = l2;
|
||||||
|
voice_0_graph_in_2_end = r2;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_load_pressure = voice_0_graph_node_incident_pressure_left_labium;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_stiffness_hint = max(0.00002, min(0.16, pow(2.0 * ma.PI * max(20.0, ((max(20, 1120 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) / ma.SR, 2.0)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow_scale = max(0.0, patch_param_12);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_tissue_loss = clip01(patch_param_14);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_aperture_shape = clip01(0.4);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow_loss = max(0.0, 0.35);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_reservoir_pressure = (clip01(patch_param_0)) * (max(0.0, 1));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_downstream_pressure = (max(0.0, 0)) + (max(0.0, patch_param_16)) * ma.tanh(voice_0_graph_source_left_labium_left_labium_load_pressure);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_pressure_drive = max(0.0, voice_0_graph_source_left_labium_left_labium_reservoir_pressure - voice_0_graph_source_left_labium_left_labium_downstream_pressure);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_stiffness = max(0.00002, (max(0.0, patch_param_10)) * (0.55 + 1.10 * (clip01(patch_param_4))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_frequency = max(45.0, min(10000.0, (ma.SR / (2.0 * ma.PI)) * sqrt(max(0.00002, voice_0_graph_source_left_labium_left_labium_stiffness) / (max(0.02, patch_param_6)))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_load_detune = 1.0 - 0.035 * ma.tanh(voice_0_graph_source_left_labium_left_labium_load_pressure * (max(0.0, patch_param_16)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_oscillation_threshold = 0.030 + 0.070 * (max(0.0, patch_param_8)) + 0.080 * voice_0_graph_source_left_labium_left_labium_tissue_loss + 0.18 * clip01(max(0.0, patch_param_2));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_oscillation_gate = clip01((voice_0_graph_source_left_labium_left_labium_pressure_drive - voice_0_graph_source_left_labium_left_labium_oscillation_threshold) * (3.0 + 1.6 * max(0.0, 1.0 - (max(0.0, patch_param_8)))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_q = 3.0 + 22.0 * clip01((clip01(patch_param_0)) * (1.0 - 0.45 * (max(0.0, patch_param_8)) - 0.35 * voice_0_graph_source_left_labium_left_labium_tissue_loss) + 0.35 * (clip01(patch_param_4)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_seed = (no.noise * 0.018 + voice_0_graph_source_left_labium_left_labium_pressure_drive * max(0.0, 0.22 - clip01(max(0.0, patch_param_2))) * 0.16);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_ring = voice_0_graph_source_left_labium_left_labium_modal_seed : fi.resonbp(voice_0_graph_source_left_labium_left_labium_modal_frequency, voice_0_graph_source_left_labium_left_labium_modal_q, 1);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_oscillator = os.osc(voice_0_graph_source_left_labium_left_labium_modal_frequency * voice_0_graph_source_left_labium_left_labium_load_detune) * voice_0_graph_source_left_labium_left_labium_oscillation_gate;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_tissue = (voice_0_graph_source_left_labium_left_labium_modal_oscillator * (0.70 + 0.80 * (clip01(patch_param_4))) + voice_0_graph_source_left_labium_left_labium_modal_ring * 0.35) * max(0.0, 1.0 - 0.35 * (max(0.0, patch_param_8)) - 0.45 * voice_0_graph_source_left_labium_left_labium_tissue_loss);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_velocity_decay = min(0.9995, max(0.20, 1.0 - ((0.008 + 0.08 * (max(0.0, patch_param_8)) + 0.055 * voice_0_graph_source_left_labium_left_labium_tissue_loss) / (max(0.02, patch_param_6)))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_displacement_decay = min(0.9998, max(0.20, 1.0 - 0.0008 / (max(0.02, patch_param_6))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_force = (voice_0_graph_source_left_labium_left_labium_pressure_drive * (0.0004 + 0.0024 * (1.0 - clip01(max(0.0, patch_param_2)))) - (max(0.0, patch_param_16)) * voice_0_graph_source_left_labium_left_labium_load_pressure * 0.001) / (max(0.02, patch_param_6));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_velocity = voice_0_graph_source_left_labium_left_labium_force : + ~ *(voice_0_graph_source_left_labium_left_labium_velocity_decay / (1.0 + 10.0 * voice_0_graph_source_left_labium_left_labium_stiffness + 0.4 * (max(0.0, 0.9))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_displacement = voice_0_graph_source_left_labium_left_labium_velocity : + ~ *(voice_0_graph_source_left_labium_left_labium_displacement_decay / (1.0 + 1.5 * voice_0_graph_source_left_labium_left_labium_stiffness));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_aperture_linear = max(0.0, (max(0.0, patch_param_18)) + (max(0.0, patch_param_2)) + voice_0_graph_source_left_labium_left_labium_displacement - (max(0.0, 0.9)) * pow(voice_0_graph_source_left_labium_left_labium_displacement, 3.0));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_aperture = voice_0_graph_source_left_labium_left_labium_aperture_linear * (1.0 - 0.55 * voice_0_graph_source_left_labium_left_labium_aperture_shape) + pow(voice_0_graph_source_left_labium_left_labium_aperture_linear, 2.0) * (0.55 * voice_0_graph_source_left_labium_left_labium_aperture_shape);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow_resistance = 1.0 + voice_0_graph_source_left_labium_left_labium_flow_loss * (0.35 + 1.8 / max(0.035, voice_0_graph_source_left_labium_left_labium_aperture + (max(0.0, patch_param_18))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_turbulence = no.noise : fi.highpass(2, 1200.0 + 2800.0 * clip01(1.0 - voice_0_graph_source_left_labium_left_labium_aperture));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_voicing = voice_0_graph_source_left_labium_left_labium_modal_tissue * (0.18 + 1.45 * (clip01(patch_param_4)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow = ma.tanh(((voice_0_graph_source_left_labium_left_labium_pressure_drive * voice_0_graph_source_left_labium_left_labium_aperture * (1.5 + 3.5 * voice_0_graph_source_left_labium_left_labium_flow_scale)) + (voice_0_graph_source_left_labium_left_labium_voicing * voice_0_graph_source_left_labium_left_labium_pressure_drive * (0.45 + 1.55 * (clip01(patch_param_4))))) / voice_0_graph_source_left_labium_left_labium_flow_resistance);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_noise = voice_0_graph_source_left_labium_left_labium_turbulence * (clip01(0.025)) * voice_0_graph_source_left_labium_left_labium_pressure_drive * clip01(voice_0_graph_source_left_labium_left_labium_aperture) * (1.0 - 0.55 * (clip01(patch_param_4))) / voice_0_graph_source_left_labium_left_labium_flow_resistance;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_out = (voice_0_graph_source_left_labium_left_labium_flow * voice_0_graph_source_left_labium_left_labium_flow_scale * (0.55 + 2.6 * voice_0_graph_source_left_labium_left_labium_pressure_drive) + voice_0_graph_source_left_labium_left_labium_noise) * ((1) * (1.0));
|
||||||
|
voice_0_graph_node_incident_pressure_left_labium = (voice_0_graph_in_0_start) / 1;
|
||||||
|
voice_0_graph_node_source_left_labium = voice_0_graph_source_left_labium_left_labium_out;
|
||||||
|
voice_0_graph_node_incident_pressure_left_merge = (voice_0_graph_in_0_end) / 1;
|
||||||
|
voice_0_graph_node_source_left_merge = 0.0;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_load_pressure = voice_0_graph_node_incident_pressure_right_labium;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_stiffness_hint = max(0.00002, min(0.16, pow(2.0 * ma.PI * max(20.0, ((max(20, 1120 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) / ma.SR, 2.0)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow_scale = max(0.0, patch_param_13);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_tissue_loss = clip01(patch_param_15);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_aperture_shape = clip01(0.4);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow_loss = max(0.0, 0.35);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_reservoir_pressure = (clip01(patch_param_1)) * (max(0.0, 1));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_downstream_pressure = (max(0.0, 0)) + (max(0.0, patch_param_17)) * ma.tanh(voice_0_graph_source_right_labium_right_labium_load_pressure);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_pressure_drive = max(0.0, voice_0_graph_source_right_labium_right_labium_reservoir_pressure - voice_0_graph_source_right_labium_right_labium_downstream_pressure);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_stiffness = max(0.00002, (max(0.0, patch_param_11)) * (0.55 + 1.10 * (clip01(patch_param_5))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_frequency = max(45.0, min(10000.0, (ma.SR / (2.0 * ma.PI)) * sqrt(max(0.00002, voice_0_graph_source_right_labium_right_labium_stiffness) / (max(0.02, patch_param_7)))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_load_detune = 1.0 - 0.035 * ma.tanh(voice_0_graph_source_right_labium_right_labium_load_pressure * (max(0.0, patch_param_17)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_oscillation_threshold = 0.030 + 0.070 * (max(0.0, patch_param_9)) + 0.080 * voice_0_graph_source_right_labium_right_labium_tissue_loss + 0.18 * clip01(max(0.0, patch_param_3));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_oscillation_gate = clip01((voice_0_graph_source_right_labium_right_labium_pressure_drive - voice_0_graph_source_right_labium_right_labium_oscillation_threshold) * (3.0 + 1.6 * max(0.0, 1.0 - (max(0.0, patch_param_9)))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_q = 3.0 + 22.0 * clip01((clip01(patch_param_1)) * (1.0 - 0.45 * (max(0.0, patch_param_9)) - 0.35 * voice_0_graph_source_right_labium_right_labium_tissue_loss) + 0.35 * (clip01(patch_param_5)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_seed = (no.noise * 0.018 + voice_0_graph_source_right_labium_right_labium_pressure_drive * max(0.0, 0.22 - clip01(max(0.0, patch_param_3))) * 0.16);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_ring = voice_0_graph_source_right_labium_right_labium_modal_seed : fi.resonbp(voice_0_graph_source_right_labium_right_labium_modal_frequency, voice_0_graph_source_right_labium_right_labium_modal_q, 1);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_oscillator = os.osc(voice_0_graph_source_right_labium_right_labium_modal_frequency * voice_0_graph_source_right_labium_right_labium_load_detune) * voice_0_graph_source_right_labium_right_labium_oscillation_gate;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_tissue = (voice_0_graph_source_right_labium_right_labium_modal_oscillator * (0.70 + 0.80 * (clip01(patch_param_5))) + voice_0_graph_source_right_labium_right_labium_modal_ring * 0.35) * max(0.0, 1.0 - 0.35 * (max(0.0, patch_param_9)) - 0.45 * voice_0_graph_source_right_labium_right_labium_tissue_loss);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_velocity_decay = min(0.9995, max(0.20, 1.0 - ((0.008 + 0.08 * (max(0.0, patch_param_9)) + 0.055 * voice_0_graph_source_right_labium_right_labium_tissue_loss) / (max(0.02, patch_param_7)))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_displacement_decay = min(0.9998, max(0.20, 1.0 - 0.0008 / (max(0.02, patch_param_7))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_force = (voice_0_graph_source_right_labium_right_labium_pressure_drive * (0.0004 + 0.0024 * (1.0 - clip01(max(0.0, patch_param_3)))) - (max(0.0, patch_param_17)) * voice_0_graph_source_right_labium_right_labium_load_pressure * 0.001) / (max(0.02, patch_param_7));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_velocity = voice_0_graph_source_right_labium_right_labium_force : + ~ *(voice_0_graph_source_right_labium_right_labium_velocity_decay / (1.0 + 10.0 * voice_0_graph_source_right_labium_right_labium_stiffness + 0.4 * (max(0.0, 0.9))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_displacement = voice_0_graph_source_right_labium_right_labium_velocity : + ~ *(voice_0_graph_source_right_labium_right_labium_displacement_decay / (1.0 + 1.5 * voice_0_graph_source_right_labium_right_labium_stiffness));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_aperture_linear = max(0.0, (max(0.0, patch_param_19)) + (max(0.0, patch_param_3)) + voice_0_graph_source_right_labium_right_labium_displacement - (max(0.0, 0.9)) * pow(voice_0_graph_source_right_labium_right_labium_displacement, 3.0));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_aperture = voice_0_graph_source_right_labium_right_labium_aperture_linear * (1.0 - 0.55 * voice_0_graph_source_right_labium_right_labium_aperture_shape) + pow(voice_0_graph_source_right_labium_right_labium_aperture_linear, 2.0) * (0.55 * voice_0_graph_source_right_labium_right_labium_aperture_shape);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow_resistance = 1.0 + voice_0_graph_source_right_labium_right_labium_flow_loss * (0.35 + 1.8 / max(0.035, voice_0_graph_source_right_labium_right_labium_aperture + (max(0.0, patch_param_19))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_turbulence = no.noise : fi.highpass(2, 1200.0 + 2800.0 * clip01(1.0 - voice_0_graph_source_right_labium_right_labium_aperture));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_voicing = voice_0_graph_source_right_labium_right_labium_modal_tissue * (0.18 + 1.45 * (clip01(patch_param_5)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow = ma.tanh(((voice_0_graph_source_right_labium_right_labium_pressure_drive * voice_0_graph_source_right_labium_right_labium_aperture * (1.5 + 3.5 * voice_0_graph_source_right_labium_right_labium_flow_scale)) + (voice_0_graph_source_right_labium_right_labium_voicing * voice_0_graph_source_right_labium_right_labium_pressure_drive * (0.45 + 1.55 * (clip01(patch_param_5))))) / voice_0_graph_source_right_labium_right_labium_flow_resistance);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_noise = voice_0_graph_source_right_labium_right_labium_turbulence * (clip01(0.02)) * voice_0_graph_source_right_labium_right_labium_pressure_drive * clip01(voice_0_graph_source_right_labium_right_labium_aperture) * (1.0 - 0.55 * (clip01(patch_param_5))) / voice_0_graph_source_right_labium_right_labium_flow_resistance;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_out = (voice_0_graph_source_right_labium_right_labium_flow * voice_0_graph_source_right_labium_right_labium_flow_scale * (0.55 + 2.6 * voice_0_graph_source_right_labium_right_labium_pressure_drive) + voice_0_graph_source_right_labium_right_labium_noise) * ((0.96) * (1.0));
|
||||||
|
voice_0_graph_node_incident_pressure_right_labium = (voice_0_graph_in_1_start) / 1;
|
||||||
|
voice_0_graph_node_source_right_labium = voice_0_graph_source_right_labium_right_labium_out;
|
||||||
|
voice_0_graph_node_incident_pressure_right_merge = (voice_0_graph_in_1_end) / 1;
|
||||||
|
voice_0_graph_node_source_right_merge = 0.0;
|
||||||
|
voice_0_graph_node_incident_pressure_trachea_base = (voice_0_graph_in_2_start) / 1;
|
||||||
|
voice_0_graph_node_source_trachea_base = 0.0;
|
||||||
|
voice_0_graph_node_incident_pressure_beak = (voice_0_graph_in_2_end) / 1;
|
||||||
|
voice_0_graph_node_source_beak = 0.0;
|
||||||
|
voice_0_graph_connection_reflection_syrinx_merge_left_merge_0_end = (2.0 * (voice_0_graph_segment_area_0) - (voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2)) / max(0.000001, voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2);
|
||||||
|
voice_0_graph_connection_scattered_syrinx_merge_left_merge_0_end = voice_0_graph_connection_reflection_syrinx_merge_left_merge_0_end * voice_0_graph_in_0_end + (1.0 + voice_0_graph_connection_reflection_syrinx_merge_left_merge_0_end) * (voice_0_graph_in_1_end + voice_0_graph_in_2_start);
|
||||||
|
voice_0_graph_out_0_end = ((voice_0_graph_connection_scattered_syrinx_merge_left_merge_0_end) * (voice_0_graph_connection_coupling_syrinx_merge) + voice_0_graph_in_0_end * (1.0 - (voice_0_graph_connection_coupling_syrinx_merge)) + (voice_0_graph_node_source_left_merge) / 1) * voice_0_graph_connection_loss_syrinx_merge;
|
||||||
|
voice_0_graph_connection_reflection_syrinx_merge_right_merge_1_end = (2.0 * (voice_0_graph_segment_area_1) - (voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2)) / max(0.000001, voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2);
|
||||||
|
voice_0_graph_connection_scattered_syrinx_merge_right_merge_1_end = voice_0_graph_connection_reflection_syrinx_merge_right_merge_1_end * voice_0_graph_in_1_end + (1.0 + voice_0_graph_connection_reflection_syrinx_merge_right_merge_1_end) * (voice_0_graph_in_0_end + voice_0_graph_in_2_start);
|
||||||
|
voice_0_graph_out_1_end = ((voice_0_graph_connection_scattered_syrinx_merge_right_merge_1_end) * (voice_0_graph_connection_coupling_syrinx_merge) + voice_0_graph_in_1_end * (1.0 - (voice_0_graph_connection_coupling_syrinx_merge)) + (voice_0_graph_node_source_right_merge) / 1) * voice_0_graph_connection_loss_syrinx_merge;
|
||||||
|
voice_0_graph_connection_reflection_syrinx_merge_trachea_base_2_start = (2.0 * (voice_0_graph_segment_area_2) - (voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2)) / max(0.000001, voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2);
|
||||||
|
voice_0_graph_connection_scattered_syrinx_merge_trachea_base_2_start = voice_0_graph_connection_reflection_syrinx_merge_trachea_base_2_start * voice_0_graph_in_2_start + (1.0 + voice_0_graph_connection_reflection_syrinx_merge_trachea_base_2_start) * (voice_0_graph_in_0_end + voice_0_graph_in_1_end);
|
||||||
|
voice_0_graph_out_2_start = ((voice_0_graph_connection_scattered_syrinx_merge_trachea_base_2_start) * (voice_0_graph_connection_coupling_syrinx_merge) + voice_0_graph_in_2_start * (1.0 - (voice_0_graph_connection_coupling_syrinx_merge)) + (voice_0_graph_node_source_trachea_base) / 1) * voice_0_graph_connection_loss_syrinx_merge;
|
||||||
|
voice_0_graph_connection_energy_in_syrinx_merge = (voice_0_graph_segment_area_0) * pow(voice_0_graph_in_0_end, 2.0) + (voice_0_graph_segment_area_1) * pow(voice_0_graph_in_1_end, 2.0) + (voice_0_graph_segment_area_2) * pow(voice_0_graph_in_2_start, 2.0);
|
||||||
|
voice_0_graph_connection_energy_out_syrinx_merge = (voice_0_graph_segment_area_0) * pow(voice_0_graph_out_0_end, 2.0) + (voice_0_graph_segment_area_1) * pow(voice_0_graph_out_1_end, 2.0) + (voice_0_graph_segment_area_2) * pow(voice_0_graph_out_2_start, 2.0);
|
||||||
|
voice_0_graph_out_0_start = voice_0_graph_in_0_start * voice_0_graph_node_reflection_left_labium + (voice_0_graph_node_source_left_labium) / 1;
|
||||||
|
voice_0_graph_out_1_start = voice_0_graph_in_1_start * voice_0_graph_node_reflection_right_labium + (voice_0_graph_node_source_right_labium) / 1;
|
||||||
|
voice_0_graph_radiation_boundary_load_beak = clip01((voice_0_graph_radiation_admittance_beak) * (voice_0_graph_radiation_differentiation_beak) * (clip01(1)));
|
||||||
|
voice_0_graph_radiation_reflected_raw_beak = (voice_0_graph_in_2_end) * (voice_0_graph_terminal_reflection_beak) + (voice_0_graph_node_source_beak);
|
||||||
|
voice_0_graph_radiation_reflected_loaded_beak = (voice_0_graph_radiation_reflected_raw_beak) * (1.0 - (voice_0_graph_radiation_boundary_load_beak)) + ((voice_0_graph_radiation_reflected_raw_beak) : fi.lowpass(1, voice_0_graph_radiation_highpass_beak)) * (voice_0_graph_radiation_boundary_load_beak);
|
||||||
|
voice_0_graph_out_2_end = voice_0_graph_radiation_reflected_loaded_beak;
|
||||||
|
voice_0_graph_radiation_model_beak = 2;
|
||||||
|
voice_0_graph_radiation_reference_area_beak = max(0.10, 1.10 * 1.10 * (0.25 + 0.75 * clip01(pow(clip01((max(0.0, patch_param_21)) / 1.5), 2.0))));
|
||||||
|
voice_0_graph_radiation_differentiation_beak = (0.50 + 0.22 * clip01(pow(clip01((max(0.0, patch_param_21)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_radiation_highpass_beak = (180.0 + 220.0 * clip01(pow(clip01((max(0.0, patch_param_21)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_radiation_admittance_beak = sqrt(clip01((voice_0_graph_terminal_area_beak) / max(0.000001, (voice_0_graph_terminal_area_beak) + (voice_0_graph_radiation_reference_area_beak))));
|
||||||
|
voice_0_graph_radiation_flow_beak = ((voice_0_graph_in_2_end - voice_0_graph_out_2_end));
|
||||||
|
voice_0_graph_delay_0 = max(0.000001, 0.00011079 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_0 = clip01((0.999) * (sqrt(clip01((voice_0_graph_segment_area_0) / max(0.000001, (voice_0_graph_segment_area_0) + 0.02)))));
|
||||||
|
voice_0_graph_next_r0 = voice_0_graph_out_0_start : de.fdelay(1024, min(1023, max(0.000001, voice_0_graph_delay_0))) * voice_0_graph_segment_loss_0;
|
||||||
|
voice_0_graph_next_l0 = voice_0_graph_out_0_end : de.fdelay(1024, min(1023, max(0.000001, voice_0_graph_delay_0))) * voice_0_graph_segment_loss_0;
|
||||||
|
voice_0_graph_delay_1 = max(0.000001, 0.00010496 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_1 = clip01((0.999) * (sqrt(clip01((voice_0_graph_segment_area_1) / max(0.000001, (voice_0_graph_segment_area_1) + 0.02)))));
|
||||||
|
voice_0_graph_next_r1 = voice_0_graph_out_1_start : de.fdelay(1024, min(1023, max(0.000001, voice_0_graph_delay_1))) * voice_0_graph_segment_loss_1;
|
||||||
|
voice_0_graph_next_l1 = voice_0_graph_out_1_end : de.fdelay(1024, min(1023, max(0.000001, voice_0_graph_delay_1))) * voice_0_graph_segment_loss_1;
|
||||||
|
voice_0_graph_delay_2 = max(0.000001, 0.0002449 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_2 = clip01((0.999) * (sqrt(clip01((voice_0_graph_segment_area_2) / max(0.000001, (voice_0_graph_segment_area_2) + 0.02)))));
|
||||||
|
voice_0_graph_next_r2 = voice_0_graph_out_2_start : de.fdelay(1024, min(1023, max(0.000001, voice_0_graph_delay_2))) * voice_0_graph_segment_loss_2;
|
||||||
|
voice_0_graph_next_l2 = voice_0_graph_out_2_end : de.fdelay(1024, min(1023, max(0.000001, voice_0_graph_delay_2))) * voice_0_graph_segment_loss_2;
|
||||||
|
voice_0_graph_out = (((voice_0_graph_radiation_flow_beak * (1.0 - (voice_0_graph_radiation_differentiation_beak)) + (voice_0_graph_radiation_flow_beak : fi.highpass(1, voice_0_graph_radiation_highpass_beak)) * (voice_0_graph_radiation_differentiation_beak)) * clip01(1) * voice_0_graph_radiation_admittance_beak)) * voice_0_graph_radiation_gain;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
voice_0_acoustic_graph_radiated = voice_0_graph(voice_0_graph_drive);
|
||||||
|
voice_0_freq = ((max(20, 1120 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = voice_0_acoustic_graph_radiated;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.08, 1.08) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.32);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 0.42);
|
||||||
98
tools/aqua-playground/lowered/advanced-glass-creature.dsp
Normal file
98
tools/aqua-playground/lowered/advanced-glass-creature.dsp
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "advanced-glass-creature";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0 + 0.18 * lfo_sin(1.8, 0);
|
||||||
|
patch_mod_pitch = 0 + 0.02 * lfo_sin(1.8, 0);
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0 + 0.8 * lfo_sin(1.8, 0);
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 523.25 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 6.4) * 0.018))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, 523.25 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 6.4) * 0.018))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 523.25 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 6.4) * 0.018))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 3.5))) * (max(0.0, 2.8 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.5, 0.0001))) / ma.PI) - 0.5);
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.92 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.03 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.03 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.002, 0, 0.8928571, 1.05, 0.18) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.1344);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 659.25 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age))) * (1.0 + select2(voice_1_age < 0, 0.0, sin(2.0 * ma.PI * (voice_1_age - 0) * 6.4) * 0.018))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, 659.25 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age))) * (1.0 + select2(voice_1_age < 0, 0.0, sin(2.0 * ma.PI * (voice_1_age - 0) * 6.4) * 0.018))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 659.25 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age))) * (1.0 + select2(voice_1_age < 0, 0.0, sin(2.0 * ma.PI * (voice_1_age - 0) * 6.4) * 0.018))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 4.2))) * (max(0.0, 2.8 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.5, 0.0001))) / ma.PI) - 0.5);
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.92 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.03 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.03 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0.002, 0, 1, 0.84, 0.11) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.08);
|
||||||
|
|
||||||
|
voice_2_age = age;
|
||||||
|
voice_2_freq = ((max(20, 783.99 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age))) * (1.0 + select2(voice_2_age < 0, 0.0, sin(2.0 * ma.PI * (voice_2_age - 0) * 6.4) * 0.018))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_2_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, 783.99 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age))) * (1.0 + select2(voice_2_age < 0, 0.0, sin(2.0 * ma.PI * (voice_2_age - 0) * 6.4) * 0.018))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 783.99 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age))) * (1.0 + select2(voice_2_age < 0, 0.0, sin(2.0 * ma.PI * (voice_2_age - 0) * 6.4) * 0.018))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 3.5))) * (max(0.0, 1.9 + patch_mod_fm_index + 0.0) * exp(-voice_2_age / max(0.5, 0.0001))) / ma.PI) - 0.5);
|
||||||
|
voice_2_colored = (voice_2_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_2_driven = ma.tanh(voice_2_colored * (1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_2_folded = voice_2_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_2_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_2_filtered = voice_2_folded : fi.lowpass(1, max(20.0, clip01(0.92 * (1.0 + 0 * voice_2_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.03 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.03 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_2_phased = voice_2_filtered;
|
||||||
|
voice_2_formants = voice_2_phased;
|
||||||
|
voice_2 = ((voice_2_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_2_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_2_age, 0.002, 0, 1, 0.72, 0.08) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.07);
|
||||||
|
|
||||||
|
voice_3_age = age;
|
||||||
|
voice_3_freq = ((max(20, 261.63 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_3_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 261.63 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 261.63 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 1.5))) * (max(0.0, 0.65 + patch_mod_fm_index + 0.0) * exp(-voice_3_age / max(1.2, 0.0001))) / ma.PI));
|
||||||
|
voice_3_colored = (voice_3_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_3_driven = ma.tanh(voice_3_colored * (1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_3_folded = voice_3_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_3_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_3_filtered = voice_3_folded : fi.lowpass(1, max(20.0, clip01(0.92 * (1.0 + 0 * voice_3_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.03 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.03 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_3_phased = voice_3_filtered;
|
||||||
|
voice_3_formants = voice_3_phased;
|
||||||
|
voice_3 = ((voice_3_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_3_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_3_age, 0.002, 0, 1, 1.3, 0.7) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.045);
|
||||||
|
|
||||||
|
voice_4_age = age;
|
||||||
|
voice_4_freq = ((max(20, 2400 * pow(2.0, 0 * voice_4_age + 0.5 * 0 * voice_4_age * voice_4_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_4_osc = no.noise;
|
||||||
|
voice_4_colored = (voice_4_osc * (1.0 - clip01(0.9 + patch_mod_noise + 0.0)) + no.noise * clip01(0.9 + patch_mod_noise + 0.0));
|
||||||
|
voice_4_driven = ma.tanh(voice_4_colored * (1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_4_folded = voice_4_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_4_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_4_filtered = voice_4_folded : fi.lowpass(1, max(20.0, clip01(0.5 * (1.0 + 0 * voice_4_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.72 * (1.0 + 0 * voice_4_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.72 * (1.0 + 0 * voice_4_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_4_phased = voice_4_filtered;
|
||||||
|
voice_4_formants = voice_4_phased;
|
||||||
|
voice_4 = ((voice_4_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_4_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_4_age, 0.002, 0, 1, 0.55, 0.04) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.012);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1 + voice_2 + voice_3 + voice_4) * 0.52);
|
||||||
185
tools/aqua-playground/lowered/advanced-human-larynx-graph.dsp
Normal file
185
tools/aqua-playground/lowered/advanced-human-larynx-graph.dsp
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "advanced-human-larynx-graph";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_param_0_base = hslider("/human/subglottal", 0.78, 0, 1.5, 0.001) : si.smoo;
|
||||||
|
patch_param_0_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_0_curve_value = select2(patch_param_0_curve_time < 0.08, select2(patch_param_0_curve_time < 0.72, select2(patch_param_0_curve_time < 1.1, 0.18, seg(patch_param_0_curve_time, 0.72, 0.38, 0.76, 0.18)), seg(patch_param_0_curve_time, 0.08, 0.64, 0.8, 0.76)), seg(patch_param_0_curve_time, 0, 0.08, 0.05, 0.8));
|
||||||
|
patch_param_0_curve_depth = hslider("/curves/breath_phrase/depth", 0.9, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_0 = min(1.5, max(0, patch_param_0_base * (1.0 - patch_param_0_curve_depth) + patch_param_0_curve_value * patch_param_0_curve_depth));
|
||||||
|
patch_param_1_base = hslider("/human/fold/opening", 0.16, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_1_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_1_curve_value = select2(patch_param_1_curve_time < 0.06, select2(patch_param_1_curve_time < 0.88, select2(patch_param_1_curve_time < 1.1, 0.03, seg(patch_param_1_curve_time, 0.88, 0.22, 0.14, 0.03)), seg(patch_param_1_curve_time, 0.06, 0.82, 0.18, 0.14)), seg(patch_param_1_curve_time, 0, 0.06, 0.02, 0.18));
|
||||||
|
patch_param_1_curve_depth = hslider("/curves/fold_onset/depth", 0.8, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_1 = min(1, max(0, patch_param_1_base * (1.0 - patch_param_1_curve_depth) + patch_param_1_curve_value * patch_param_1_curve_depth));
|
||||||
|
patch_param_2 = hslider("/human/fold/tension", 0.58, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_3 = hslider("/human/tongue/index", 3.3, 0, 6, 0.001) : si.smoo;
|
||||||
|
patch_param_4 = hslider("/human/tongue/diameter", 1.25, 0.05, 3, 0.001) : si.smoo;
|
||||||
|
patch_param_5 = hslider("/human/constriction/index", 4.8, 0, 6, 0.001) : si.smoo;
|
||||||
|
patch_param_6 = hslider("/human/constriction/diameter", 0.95, 0.02, 3, 0.001) : si.smoo;
|
||||||
|
patch_param_7_base = hslider("/human/lip/opening", 1.2, 0, 2, 0.001) : si.smoo;
|
||||||
|
patch_param_7_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_7_curve_value = select2(patch_param_7_curve_time < 0.35, select2(patch_param_7_curve_time < 0.75, select2(patch_param_7_curve_time < 1.1, 1.05, seg(patch_param_7_curve_time, 0.75, 0.35, 0.72, 1.05)), seg(patch_param_7_curve_time, 0.35, 0.4, 1.25, 0.72)), seg(patch_param_7_curve_time, 0, 0.35, 0.8, 1.25));
|
||||||
|
patch_param_7_curve_depth = hslider("/curves/lip_vowel/depth", 0.65, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_7 = min(2, max(0, patch_param_7_base * (1.0 - patch_param_7_curve_depth) + patch_param_7_curve_value * patch_param_7_curve_depth));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_graph_drive = 1.0;
|
||||||
|
voice_0_graph_terminal_area_subglottal_wall = max(0.000001, 0.1024 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_subglottal_wall = 0.78;
|
||||||
|
voice_0_graph_terminal_area_trachea_top = max(0.000001, 0.49 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_trachea_top = 0;
|
||||||
|
voice_0_graph_terminal_area_oral_back = max(0.000001, ((max(0.0, min((0.55 + ((patch_param_4) - 0.55) * (exp(0.0 - pow((0 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 0.55) * 0), (patch_param_6) + max(0.0, ((0.55 + ((patch_param_4) - 0.55) * (exp(0.0 - pow((0 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 0.55) * 0)) - (patch_param_6)) * (1.0 - (exp(0.0 - pow((0 - (patch_param_5)) / (max(0.001, 0.09 * 7)), 2.0))))))) * (max(0.0, min((0.55 + ((patch_param_4) - 0.55) * (exp(0.0 - pow((0 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 0.55) * 0), (patch_param_6) + max(0.0, ((0.55 + ((patch_param_4) - 0.55) * (exp(0.0 - pow((0 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 0.55) * 0)) - (patch_param_6)) * (1.0 - (exp(0.0 - pow((0 - (patch_param_5)) / (max(0.001, 0.09 * 7)), 2.0)))))))) * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_oral_back = 0;
|
||||||
|
voice_0_graph_terminal_area_folds = max(0.000001, 0.49 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_folds = 0;
|
||||||
|
voice_0_graph_terminal_area_mouth = max(0.000001, ((max(0.0, min((0.82 + ((patch_param_4) - 0.82) * (exp(0.0 - pow((6 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 0.82) * 1), (patch_param_6) + max(0.0, ((0.82 + ((patch_param_4) - 0.82) * (exp(0.0 - pow((6 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 0.82) * 1)) - (patch_param_6)) * (1.0 - (exp(0.0 - pow((6 - (patch_param_5)) / (max(0.001, 0.09 * 7)), 2.0))))))) * (max(0.0, min((0.82 + ((patch_param_4) - 0.82) * (exp(0.0 - pow((6 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 0.82) * 1), (patch_param_6) + max(0.0, ((0.82 + ((patch_param_4) - 0.82) * (exp(0.0 - pow((6 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 0.82) * 1)) - (patch_param_6)) * (1.0 - (exp(0.0 - pow((6 - (patch_param_5)) / (max(0.001, 0.09 * 7)), 2.0)))))))) * max(0.0, 1.2));
|
||||||
|
voice_0_graph_terminal_reflection_mouth = ((0.96) * (1.0 - clip01(pow(clip01((max(0.0, patch_param_7)) / 1.5), 2.0))) + (-0.82) * clip01(pow(clip01((max(0.0, patch_param_7)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_node_area_subglottal_wall = max(0.000001, voice_0_graph_terminal_area_subglottal_wall);
|
||||||
|
voice_0_graph_node_reflection_subglottal_wall = (voice_0_graph_terminal_reflection_subglottal_wall) / 1;
|
||||||
|
voice_0_graph_node_area_folds_trachea_top = max(0.000001, voice_0_graph_terminal_area_trachea_top);
|
||||||
|
voice_0_graph_node_reflection_folds_trachea_top = (voice_0_graph_terminal_reflection_trachea_top) / 1;
|
||||||
|
voice_0_graph_node_area_oral_back = max(0.000001, voice_0_graph_terminal_area_oral_back);
|
||||||
|
voice_0_graph_node_reflection_oral_back = (voice_0_graph_terminal_reflection_oral_back) / 1;
|
||||||
|
voice_0_graph_node_area_mouth = max(0.000001, voice_0_graph_terminal_area_mouth);
|
||||||
|
voice_0_graph_node_reflection_mouth = (voice_0_graph_terminal_reflection_mouth) / 1;
|
||||||
|
voice_0_graph_segment_area_0 = max(0.000001, 0.3364);
|
||||||
|
voice_0_graph_segment_area_1 = max(0.000001, ((max(0.0, min((1.45 + ((patch_param_4) - 1.45) * (exp(0.0 - pow((3 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 1.45) * 0), (patch_param_6) + max(0.0, ((1.45 + ((patch_param_4) - 1.45) * (exp(0.0 - pow((3 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 1.45) * 0)) - (patch_param_6)) * (1.0 - (exp(0.0 - pow((3 - (patch_param_5)) / (max(0.001, 0.09 * 7)), 2.0))))))) * (max(0.0, min((1.45 + ((patch_param_4) - 1.45) * (exp(0.0 - pow((3 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 1.45) * 0), (patch_param_6) + max(0.0, ((1.45 + ((patch_param_4) - 1.45) * (exp(0.0 - pow((3 - (patch_param_3)) / (max(0.001, 0.18 * 7)), 2.0))) + ((patch_param_7) - 1.45) * 0)) - (patch_param_6)) * (1.0 - (exp(0.0 - pow((3 - (patch_param_5)) / (max(0.001, 0.09 * 7)), 2.0)))))))));
|
||||||
|
voice_0_graph_connection_coupling_larynx_to_tract = clip01(0.92);
|
||||||
|
voice_0_graph_connection_loss_larynx_to_tract = clip01(1);
|
||||||
|
voice_0_graph_radiation_gain = 1.0;
|
||||||
|
voice_0_graph_source_folds = 0.0;
|
||||||
|
voice_0_graph(x) = voice_0_graph_loop ~ si.bus(4) : (si.block(4), _) with {
|
||||||
|
voice_0_graph_loop(r0, r1, l0, l1) = voice_0_graph_next_r0, voice_0_graph_next_r1, voice_0_graph_next_l0, voice_0_graph_next_l1, voice_0_graph_out with {
|
||||||
|
voice_0_graph_in_0_start = l0;
|
||||||
|
voice_0_graph_in_0_end = r0;
|
||||||
|
voice_0_graph_in_1_start = l1;
|
||||||
|
voice_0_graph_in_1_end = r1;
|
||||||
|
voice_0_graph_node_incident_pressure_subglottal_wall = (voice_0_graph_in_0_start) / 1;
|
||||||
|
voice_0_graph_node_source_subglottal_wall = 0.0;
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_load_pressure = voice_0_graph_node_incident_pressure_folds_trachea_top;
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_stiffness_hint = max(0.00002, min(0.16, pow(2.0 * ma.PI * max(20.0, ((max(20, 145 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) / ma.SR, 2.0)));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_flow_scale = max(0.0, 1.05);
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_tissue_loss = clip01(0.2);
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_aperture_shape = clip01(0.48);
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_flow_loss = max(0.0, 0.4);
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_reservoir_pressure = (clip01(patch_param_0)) * (max(0.0, 1));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_downstream_pressure = (max(0.0, 0.05)) + (max(0.0, 0.42)) * ma.tanh(voice_0_graph_source_folds_folds_trachea_top_load_pressure);
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_pressure_drive = max(0.0, voice_0_graph_source_folds_folds_trachea_top_reservoir_pressure - voice_0_graph_source_folds_folds_trachea_top_downstream_pressure);
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_stiffness = voice_0_graph_source_folds_folds_trachea_top_stiffness_hint * (0.35 + 1.65 * (clip01(patch_param_2)));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_modal_frequency = max(45.0, min(10000.0, (ma.SR / (2.0 * ma.PI)) * sqrt(max(0.00002, voice_0_graph_source_folds_folds_trachea_top_stiffness) / (max(0.02, 0.35)))));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_load_detune = 1.0 - 0.035 * ma.tanh(voice_0_graph_source_folds_folds_trachea_top_load_pressure * (max(0.0, 0.42)));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_oscillation_threshold = 0.030 + 0.070 * (max(0.0, 0.18)) + 0.080 * voice_0_graph_source_folds_folds_trachea_top_tissue_loss + 0.18 * clip01(max(0.0, patch_param_1));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_oscillation_gate = clip01((voice_0_graph_source_folds_folds_trachea_top_pressure_drive - voice_0_graph_source_folds_folds_trachea_top_oscillation_threshold) * (3.0 + 1.6 * max(0.0, 1.0 - (max(0.0, 0.18)))));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_modal_q = 3.0 + 22.0 * clip01((clip01(patch_param_0)) * (1.0 - 0.45 * (max(0.0, 0.18)) - 0.35 * voice_0_graph_source_folds_folds_trachea_top_tissue_loss) + 0.35 * (clip01(patch_param_2)));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_modal_seed = (no.noise * 0.018 + voice_0_graph_source_folds_folds_trachea_top_pressure_drive * max(0.0, 0.22 - clip01(max(0.0, patch_param_1))) * 0.16);
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_modal_ring = voice_0_graph_source_folds_folds_trachea_top_modal_seed : fi.resonbp(voice_0_graph_source_folds_folds_trachea_top_modal_frequency, voice_0_graph_source_folds_folds_trachea_top_modal_q, 1);
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_modal_oscillator = os.osc(voice_0_graph_source_folds_folds_trachea_top_modal_frequency * voice_0_graph_source_folds_folds_trachea_top_load_detune) * voice_0_graph_source_folds_folds_trachea_top_oscillation_gate;
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_modal_tissue = (voice_0_graph_source_folds_folds_trachea_top_modal_oscillator * (0.70 + 0.80 * (clip01(patch_param_2))) + voice_0_graph_source_folds_folds_trachea_top_modal_ring * 0.35) * max(0.0, 1.0 - 0.35 * (max(0.0, 0.18)) - 0.45 * voice_0_graph_source_folds_folds_trachea_top_tissue_loss);
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_upper_stiffness_effective = max((max(0.0, 0.04)), voice_0_graph_source_folds_folds_trachea_top_stiffness * (0.80 + 0.45 * (clip01(patch_param_2))));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_lower_stiffness_effective = max((max(0.0, 0.06)), voice_0_graph_source_folds_folds_trachea_top_stiffness * (1.05 + 0.55 * (clip01(patch_param_2))));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_coupling_stiffness = (max(0.0, 0.09)) * (1.0 + 1.5 * (clip01(patch_param_2)));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_lower_velocity_decay = min(0.9995, max(0.20, 1.0 - ((0.010 + 0.090 * (max(0.0, 0.18)) + 0.040 * (max(0.0, 0.18)) + 0.055 * voice_0_graph_source_folds_folds_trachea_top_tissue_loss) / (max(0.02, 0.36)))));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_upper_velocity_decay = min(0.9995, max(0.20, 1.0 - ((0.012 + 0.105 * (max(0.0, 0.18)) + 0.030 * (max(0.0, 0.18)) + 0.055 * voice_0_graph_source_folds_folds_trachea_top_tissue_loss) / (max(0.02, 0.22)))));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_lower_force = (voice_0_graph_source_folds_folds_trachea_top_pressure_drive * (0.0005 + 0.0028 * (1.0 - clip01(max(0.0, patch_param_1)))) - (max(0.0, 0.42)) * voice_0_graph_source_folds_folds_trachea_top_load_pressure * 0.0012) / (max(0.02, 0.36));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_upper_force = (voice_0_graph_source_folds_folds_trachea_top_pressure_drive * (0.0003 + 0.0018 * (1.0 - clip01(max(0.0, patch_param_1)))) - (max(0.0, 0.42)) * voice_0_graph_source_folds_folds_trachea_top_load_pressure * 0.0008) / (max(0.02, 0.22));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_lower_velocity = voice_0_graph_source_folds_folds_trachea_top_lower_force : + ~ *(voice_0_graph_source_folds_folds_trachea_top_lower_velocity_decay / (1.0 + 10.0 * voice_0_graph_source_folds_folds_trachea_top_lower_stiffness_effective + voice_0_graph_source_folds_folds_trachea_top_coupling_stiffness + 0.4 * (max(0.0, 0.8))));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_upper_velocity = (voice_0_graph_source_folds_folds_trachea_top_upper_force + voice_0_graph_source_folds_folds_trachea_top_lower_velocity * voice_0_graph_source_folds_folds_trachea_top_coupling_stiffness * 0.35) : + ~ *(voice_0_graph_source_folds_folds_trachea_top_upper_velocity_decay / (1.0 + 10.0 * voice_0_graph_source_folds_folds_trachea_top_upper_stiffness_effective + voice_0_graph_source_folds_folds_trachea_top_coupling_stiffness + 0.4 * (max(0.0, 0.8))));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_lower_displacement = voice_0_graph_source_folds_folds_trachea_top_lower_velocity : + ~ *(min(0.9998, max(0.20, 1.0 - 0.0008 / (max(0.02, 0.36)))));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_upper_displacement = voice_0_graph_source_folds_folds_trachea_top_upper_velocity : + ~ *(min(0.9998, max(0.20, 1.0 - 0.0008 / (max(0.02, 0.22)))));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_collision = (max(0.0, 0.62)) * max(0.0, 0.0 - ((max(0.0, 0.012)) + (max(0.0, patch_param_1)) + voice_0_graph_source_folds_folds_trachea_top_upper_displacement));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_displacement = voice_0_graph_source_folds_folds_trachea_top_lower_displacement * (1.0 - (clip01(0.24))) + (voice_0_graph_source_folds_folds_trachea_top_upper_displacement - voice_0_graph_source_folds_folds_trachea_top_collision) * (clip01(0.24));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_aperture_linear = max(0.0, (max(0.0, 0.012)) + (max(0.0, patch_param_1)) + voice_0_graph_source_folds_folds_trachea_top_displacement - (max(0.0, 0.8)) * pow(voice_0_graph_source_folds_folds_trachea_top_displacement, 3.0));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_aperture = voice_0_graph_source_folds_folds_trachea_top_aperture_linear * (1.0 - 0.55 * voice_0_graph_source_folds_folds_trachea_top_aperture_shape) + pow(voice_0_graph_source_folds_folds_trachea_top_aperture_linear, 2.0) * (0.55 * voice_0_graph_source_folds_folds_trachea_top_aperture_shape);
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_flow_resistance = 1.0 + voice_0_graph_source_folds_folds_trachea_top_flow_loss * (0.35 + 1.8 / max(0.035, voice_0_graph_source_folds_folds_trachea_top_aperture + (max(0.0, 0.012))));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_turbulence = no.noise : fi.highpass(2, 1200.0 + 2800.0 * clip01(1.0 - voice_0_graph_source_folds_folds_trachea_top_aperture));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_voicing = voice_0_graph_source_folds_folds_trachea_top_modal_tissue * (0.18 + 1.45 * (clip01(patch_param_2)));
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_flow = ma.tanh(((voice_0_graph_source_folds_folds_trachea_top_pressure_drive * voice_0_graph_source_folds_folds_trachea_top_aperture * (1.5 + 3.5 * voice_0_graph_source_folds_folds_trachea_top_flow_scale)) + (voice_0_graph_source_folds_folds_trachea_top_voicing * voice_0_graph_source_folds_folds_trachea_top_pressure_drive * (0.45 + 1.55 * (clip01(patch_param_2))))) / voice_0_graph_source_folds_folds_trachea_top_flow_resistance);
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_noise = voice_0_graph_source_folds_folds_trachea_top_turbulence * (clip01(0.035)) * voice_0_graph_source_folds_folds_trachea_top_pressure_drive * clip01(voice_0_graph_source_folds_folds_trachea_top_aperture) * (1.0 - 0.55 * (clip01(patch_param_2))) / voice_0_graph_source_folds_folds_trachea_top_flow_resistance;
|
||||||
|
voice_0_graph_source_folds_folds_trachea_top_out = (voice_0_graph_source_folds_folds_trachea_top_flow * voice_0_graph_source_folds_folds_trachea_top_flow_scale * (0.55 + 2.6 * voice_0_graph_source_folds_folds_trachea_top_pressure_drive) + voice_0_graph_source_folds_folds_trachea_top_noise) * ((1) * (1.0));
|
||||||
|
voice_0_graph_node_incident_pressure_folds_trachea_top = (voice_0_graph_in_0_end) / 1;
|
||||||
|
voice_0_graph_node_source_folds_trachea_top = voice_0_graph_source_folds_folds_trachea_top_out;
|
||||||
|
voice_0_graph_node_incident_pressure_oral_back = (voice_0_graph_in_1_start) / 1;
|
||||||
|
voice_0_graph_node_source_oral_back = 0.0;
|
||||||
|
voice_0_graph_node_incident_pressure_mouth = (voice_0_graph_in_1_end) / 1;
|
||||||
|
voice_0_graph_node_source_mouth = 0.0;
|
||||||
|
voice_0_graph_connection_reflection_larynx_to_tract_trachea_top_0_end = (2.0 * ((voice_0_graph_terminal_area_trachea_top / 1)) - ((voice_0_graph_terminal_area_trachea_top / 1) + voice_0_graph_segment_area_1)) / max(0.000001, (voice_0_graph_terminal_area_trachea_top / 1) + voice_0_graph_segment_area_1);
|
||||||
|
voice_0_graph_connection_scattered_larynx_to_tract_trachea_top_0_end = voice_0_graph_connection_reflection_larynx_to_tract_trachea_top_0_end * voice_0_graph_in_0_end + (1.0 + voice_0_graph_connection_reflection_larynx_to_tract_trachea_top_0_end) * (voice_0_graph_in_1_start);
|
||||||
|
voice_0_graph_out_0_end = ((voice_0_graph_connection_scattered_larynx_to_tract_trachea_top_0_end) * (voice_0_graph_connection_coupling_larynx_to_tract) + voice_0_graph_in_0_end * (1.0 - (voice_0_graph_connection_coupling_larynx_to_tract)) + (voice_0_graph_node_source_folds_trachea_top) / 1) * voice_0_graph_connection_loss_larynx_to_tract;
|
||||||
|
voice_0_graph_connection_reflection_larynx_to_tract_oral_back_1_start = (2.0 * (voice_0_graph_segment_area_1) - ((voice_0_graph_terminal_area_trachea_top / 1) + voice_0_graph_segment_area_1)) / max(0.000001, (voice_0_graph_terminal_area_trachea_top / 1) + voice_0_graph_segment_area_1);
|
||||||
|
voice_0_graph_connection_scattered_larynx_to_tract_oral_back_1_start = voice_0_graph_connection_reflection_larynx_to_tract_oral_back_1_start * voice_0_graph_in_1_start + (1.0 + voice_0_graph_connection_reflection_larynx_to_tract_oral_back_1_start) * (voice_0_graph_in_0_end);
|
||||||
|
voice_0_graph_out_1_start = ((voice_0_graph_connection_scattered_larynx_to_tract_oral_back_1_start) * (voice_0_graph_connection_coupling_larynx_to_tract) + voice_0_graph_in_1_start * (1.0 - (voice_0_graph_connection_coupling_larynx_to_tract)) + (voice_0_graph_node_source_oral_back) / 1) * voice_0_graph_connection_loss_larynx_to_tract;
|
||||||
|
voice_0_graph_connection_energy_in_larynx_to_tract = ((voice_0_graph_terminal_area_trachea_top / 1)) * pow(voice_0_graph_in_0_end, 2.0) + (voice_0_graph_segment_area_1) * pow(voice_0_graph_in_1_start, 2.0);
|
||||||
|
voice_0_graph_connection_energy_out_larynx_to_tract = ((voice_0_graph_terminal_area_trachea_top / 1)) * pow(voice_0_graph_out_0_end, 2.0) + (voice_0_graph_segment_area_1) * pow(voice_0_graph_out_1_start, 2.0);
|
||||||
|
voice_0_graph_out_0_start = voice_0_graph_in_0_start * voice_0_graph_node_reflection_subglottal_wall + (voice_0_graph_node_source_subglottal_wall) / 1;
|
||||||
|
voice_0_graph_radiation_boundary_load_mouth = clip01((voice_0_graph_radiation_admittance_mouth) * (voice_0_graph_radiation_differentiation_mouth) * (clip01(0.98)));
|
||||||
|
voice_0_graph_radiation_reflected_raw_mouth = (voice_0_graph_in_1_end) * (voice_0_graph_terminal_reflection_mouth) + (voice_0_graph_node_source_mouth);
|
||||||
|
voice_0_graph_radiation_reflected_loaded_mouth = (voice_0_graph_radiation_reflected_raw_mouth) * (1.0 - (voice_0_graph_radiation_boundary_load_mouth)) + ((voice_0_graph_radiation_reflected_raw_mouth) : fi.lowpass(1, voice_0_graph_radiation_highpass_mouth)) * (voice_0_graph_radiation_boundary_load_mouth);
|
||||||
|
voice_0_graph_out_1_end = voice_0_graph_radiation_reflected_loaded_mouth;
|
||||||
|
voice_0_graph_radiation_model_mouth = 1;
|
||||||
|
voice_0_graph_radiation_reference_area_mouth = max(0.18, 1.90 * 1.90 * (0.30 + 0.70 * clip01(pow(clip01((max(0.0, patch_param_7)) / 1.5), 2.0))));
|
||||||
|
voice_0_graph_radiation_differentiation_mouth = (0.62 + 0.24 * clip01(pow(clip01((max(0.0, patch_param_7)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_radiation_highpass_mouth = (120.0 + 180.0 * clip01(pow(clip01((max(0.0, patch_param_7)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_radiation_admittance_mouth = sqrt(clip01((voice_0_graph_terminal_area_mouth) / max(0.000001, (voice_0_graph_terminal_area_mouth) + (voice_0_graph_radiation_reference_area_mouth))));
|
||||||
|
voice_0_graph_radiation_flow_mouth = ((voice_0_graph_in_1_end - voice_0_graph_out_1_end));
|
||||||
|
voice_0_graph_delay_0 = max(0.000001, 0.00034985 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_0 = clip01((0.998) * (sqrt(clip01((voice_0_graph_segment_area_0) / max(0.000001, (voice_0_graph_segment_area_0) + 0.020)))));
|
||||||
|
voice_0_graph_next_r0 = voice_0_graph_out_0_start : de.fdelay(4096, min(4095, max(0.000001, voice_0_graph_delay_0))) * voice_0_graph_segment_loss_0;
|
||||||
|
voice_0_graph_next_l0 = voice_0_graph_out_0_end : de.fdelay(4096, min(4095, max(0.000001, voice_0_graph_delay_0))) * voice_0_graph_segment_loss_0;
|
||||||
|
voice_0_graph_delay_1 = max(0.000001, 0.00049563 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_1 = clip01((0.997) * (sqrt(clip01((voice_0_graph_segment_area_1) / max(0.000001, (voice_0_graph_segment_area_1) + 0.012))) * (0.998 - 0.010 * clip01(0.080 / max(0.000001, (voice_0_graph_segment_area_1) + 0.080)))));
|
||||||
|
voice_0_graph_next_r1 = voice_0_graph_out_1_start : de.fdelay(4096, min(4095, max(0.000001, voice_0_graph_delay_1))) * voice_0_graph_segment_loss_1;
|
||||||
|
voice_0_graph_next_l1 = voice_0_graph_out_1_end : de.fdelay(4096, min(4095, max(0.000001, voice_0_graph_delay_1))) * voice_0_graph_segment_loss_1;
|
||||||
|
voice_0_graph_out = (((voice_0_graph_radiation_flow_mouth * (1.0 - (voice_0_graph_radiation_differentiation_mouth)) + (voice_0_graph_radiation_flow_mouth : fi.highpass(1, voice_0_graph_radiation_highpass_mouth)) * (voice_0_graph_radiation_differentiation_mouth)) * clip01(0.98) * voice_0_graph_radiation_admittance_mouth)) * voice_0_graph_radiation_gain;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
voice_0_acoustic_graph_radiated = voice_0_graph(voice_0_graph_drive);
|
||||||
|
voice_0_freq = ((max(20, 145 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = voice_0_acoustic_graph_radiated;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.08, 1.1) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.18);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 0.34);
|
||||||
87
tools/aqua-playground/lowered/advanced-machine-breath.dsp
Normal file
87
tools/aqua-playground/lowered/advanced-machine-breath.dsp
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "advanced-machine-breath";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0 + 0.16 * lfo_hold(5.5, 0);
|
||||||
|
patch_mod_pitch = 0 + 0.028 * lfo_hold(5.5, 0);
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0 + 0.24 * lfo_hold(5.5, 0);
|
||||||
|
patch_mod_hpf = 0 + 0.12 * lfo_sq(2.75, 0);
|
||||||
|
patch_mod_noise = 0 + 0.18 * lfo_sq(2.75, 0);
|
||||||
|
patch_mod_drive = 0 + 0.18 * lfo_hold(5.5, 0);
|
||||||
|
patch_mod_fold = 0 + 0.16 * lfo_hold(5.5, 0);
|
||||||
|
patch_mod_formant_mix = 0 + 0.25 * lfo_sq(2.75, 0);
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 72 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 72 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 72 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 1.5))) * (max(0.0, 0.8 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.45, 0.0001))) / ma.PI) - 1.0;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.34 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.34 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0.12 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0.12 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.12 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.28 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.025 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.025 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = ((voice_0_phased : fi.resonbp(290, 5.272727, 1.0)) * 0.55 + (voice_0_phased : fi.resonbp(760, 6.333333, 1.0)) * 1 + (voice_0_phased : fi.resonbp(1780, 6.846154, 1.0)) * 0.5) / 2.05;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0.22 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0.22 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.004, 0, 1, 0.55, 0.72) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.1);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 36 * pow(2.0, -0.18 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = select2(wrap01(os.phasor(1.0, ((max(20, 36 * pow(2.0, -0.18 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 36 * pow(2.0, -0.18 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 1.5))) * (max(0.0, 0.8 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.45, 0.0001))) / ma.PI) < clip01(0.37 + 0 * voice_1_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.34 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.34 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0.12 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0.12 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.12 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.28 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.025 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.025 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0.004, 0, 1, 0.55, 0.72) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.13);
|
||||||
|
|
||||||
|
voice_2_age = age;
|
||||||
|
voice_2_freq = ((max(20, 700 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_2_osc = no.noise;
|
||||||
|
voice_2_colored = (voice_2_osc * (1.0 - clip01(0.8 + patch_mod_noise + 0.0)) + no.noise * clip01(0.8 + patch_mod_noise + 0.0));
|
||||||
|
voice_2_driven = ma.tanh(voice_2_colored * (1.0 + clip01(0.34 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.34 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_2_folded = voice_2_driven * (1.0 - clip01(0.12 + patch_mod_fold + 0.0)) + fold(voice_2_driven * (1.0 + clip01(0.12 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.12 + patch_mod_fold + 0.0);
|
||||||
|
voice_2_filtered = voice_2_folded : fi.lowpass(1, max(20.0, clip01(0.45 * (1.0 + 0 * voice_2_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.42 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.42 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_2_phased = voice_2_filtered;
|
||||||
|
voice_2_formants = voice_2_phased;
|
||||||
|
voice_2 = ((voice_2_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_2_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_2_age, 0.004, 0, 1, 0.35, 0.5) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.055);
|
||||||
|
|
||||||
|
voice_3_age = age;
|
||||||
|
voice_3_freq = ((max(20, 144 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_3_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, 144 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 144 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2.25))) * (max(0.0, 1.1 + patch_mod_fm_index + 0.0) * exp(-voice_3_age / max(0.5, 0.0001))) / ma.PI) - 0.5);
|
||||||
|
voice_3_colored = (voice_3_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_3_driven = ma.tanh(voice_3_colored * (1.0 + clip01(0.34 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.34 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_3_folded = voice_3_driven * (1.0 - clip01(0.12 + patch_mod_fold + 0.0)) + fold(voice_3_driven * (1.0 + clip01(0.12 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.12 + patch_mod_fold + 0.0);
|
||||||
|
voice_3_filtered = voice_3_folded : fi.lowpass(1, max(20.0, clip01(0.28 * (1.0 + 0 * voice_3_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.025 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.025 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_3_phased = voice_3_filtered;
|
||||||
|
voice_3_formants = ((voice_3_phased : fi.resonbp(510, 5.666667, 1.0)) * 0.35 + (voice_3_phased : fi.resonbp(1320, 7.764706, 1.0)) * 0.9 + (voice_3_phased : fi.resonbp(2100, 7, 1.0)) * 0.45) / 1.7;
|
||||||
|
voice_3 = ((voice_3_phased * (1.0 - clip01(0.18 + patch_mod_formant_mix + 0.0)) + voice_3_formants * clip01(0.18 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_3_age, 0.004, 0, 1, 0.48, 0.62) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.045);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1 + voice_2 + voice_3) * 0.7);
|
||||||
87
tools/aqua-playground/lowered/advanced-ritual-sequence.dsp
Normal file
87
tools/aqua-playground/lowered/advanced-ritual-sequence.dsp
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "advanced-ritual-sequence";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time - floor(time / 0.42) * 0.42;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0 + 0.2 * lfo_tri(2.38, 0);
|
||||||
|
patch_mod_pitch = 0 + 0.018 * lfo_tri(2.38, 0);
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0 + 0.12 * lfo_tri(2.38, 0);
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0 + 0.75 * lfo_tri(2.38, 0);
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 196 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.065, 1.4983, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 196 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.065, 1.4983, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 196 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.065, 1.4983, 1.0) * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, 1.8 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.32, 0.0001))) / ma.PI));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.82 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.035 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.035 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.001, 0, 0.8474576, 0.5, 0.045) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.1534);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 293.66 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * select2(voice_1_age < 0.065, 1.3348, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 293.66 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * select2(voice_1_age < 0.065, 1.3348, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 293.66 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * select2(voice_1_age < 0.065, 1.3348, 1.0) * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, 1.8 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.32, 0.0001))) / ma.PI));
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.82 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.035 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.035 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0.001, 0, 0.8474576, 0.38, 0.035) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.09);
|
||||||
|
|
||||||
|
voice_2_age = age;
|
||||||
|
voice_2_freq = ((max(20, 392 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * select2(voice_2_age < 0.065, 1.2599, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_2_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 392 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * select2(voice_2_age < 0.065, 1.2599, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 392 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * select2(voice_2_age < 0.065, 1.2599, 1.0) * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, 1.8 + patch_mod_fm_index + 0.0) * exp(-voice_2_age / max(0.32, 0.0001))) / ma.PI));
|
||||||
|
voice_2_colored = (voice_2_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_2_driven = ma.tanh(voice_2_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_2_folded = voice_2_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_2_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_2_filtered = voice_2_folded : fi.lowpass(1, max(20.0, clip01(0.82 * (1.0 + 0 * voice_2_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.035 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.035 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_2_phased = voice_2_filtered;
|
||||||
|
voice_2_formants = voice_2_phased;
|
||||||
|
voice_2 = ((voice_2_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_2_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_2_age, 0.001, 0, 0.8474576, 0.32, 0.028) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.07);
|
||||||
|
|
||||||
|
voice_3_age = age;
|
||||||
|
voice_3_freq = ((max(20, 3100 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_3_osc = no.noise;
|
||||||
|
voice_3_colored = (voice_3_osc * (1.0 - clip01(1 + patch_mod_noise + 0.0)) + no.noise * clip01(1 + patch_mod_noise + 0.0));
|
||||||
|
voice_3_driven = ma.tanh(voice_3_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_3_folded = voice_3_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_3_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_3_filtered = voice_3_folded : fi.lowpass(1, max(20.0, clip01(0.46 * (1.0 + 0 * voice_3_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.68 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.68 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_3_phased = voice_3_filtered;
|
||||||
|
voice_3_formants = voice_3_phased;
|
||||||
|
voice_3 = ((voice_3_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_3_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_3_age, 0.001, 0, 0.8474576, 0.16, 0.018) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.025);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1 + voice_2 + voice_3) * 0.62);
|
||||||
98
tools/aqua-playground/lowered/advanced-subtractive-drums.dsp
Normal file
98
tools/aqua-playground/lowered/advanced-subtractive-drums.dsp
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "advanced-subtractive-drums";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(32, 58 * pow(2.0, -3.6 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(32, 58 * pow(2.0, -3.6 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.14 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.14 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.resonlp(max(20.0, clip01(0.78 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0), max(0.1, 0.28), 1.0) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 0.62, 0.38, 0.038) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.82);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 1700 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = no.noise;
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0.95 + patch_mod_noise + 0.0)) + no.noise * clip01(0.95 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.14 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.14 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.48 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.48 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0)) : fi.resonbp(max(20.0, clip01(0.38) * 18000.0), max(0.1, 2.2), 1.0);
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 0.62, 0.18, 0.018) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.26);
|
||||||
|
|
||||||
|
voice_2_age = age;
|
||||||
|
voice_2_freq = ((max(20, 188 * pow(2.0, -1.05 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_2_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, 188 * pow(2.0, -1.05 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 0.5);
|
||||||
|
voice_2_colored = (voice_2_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_2_driven = ma.tanh(voice_2_colored * (1.0 + clip01(0.14 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.14 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_2_folded = voice_2_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_2_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_2_filtered = voice_2_folded : fi.lowpass(1, max(20.0, clip01(0.52 * (1.0 + 0 * voice_2_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_2_phased = voice_2_filtered;
|
||||||
|
voice_2_formants = voice_2_phased;
|
||||||
|
voice_2 = ((voice_2_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_2_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_2_age, 0, 0, 0.62, 0.13, 0.026) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.12);
|
||||||
|
|
||||||
|
voice_3_age = age;
|
||||||
|
voice_3_freq = ((max(20, 8200 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_3_osc = no.noise;
|
||||||
|
voice_3_colored = (voice_3_osc * (1.0 - clip01(1 + patch_mod_noise + 0.0)) + no.noise * clip01(1 + patch_mod_noise + 0.0));
|
||||||
|
voice_3_driven = ma.tanh(voice_3_colored * (1.0 + clip01(0.14 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.14 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_3_folded = voice_3_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_3_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_3_filtered = voice_3_folded : fi.lowpass(1, max(20.0, clip01(0.2 * (1.0 + 0 * voice_3_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.88 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.88 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_3_phased = voice_3_filtered;
|
||||||
|
voice_3_formants = voice_3_phased;
|
||||||
|
voice_3 = ((voice_3_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_3_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_3_age, 0, 0, 0.62, 0.052, 0.005) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.11);
|
||||||
|
|
||||||
|
voice_4_age = age;
|
||||||
|
voice_4_freq = ((max(20, 540 * pow(2.0, 0 * voice_4_age + 0.5 * 0 * voice_4_age * voice_4_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_4_osc = select2(wrap01(os.phasor(1.0, ((max(20, 540 * pow(2.0, 0 * voice_4_age + 0.5 * 0 * voice_4_age * voice_4_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) < clip01(0.42 + 0 * voice_4_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_4_colored = (voice_4_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_4_driven = ma.tanh(voice_4_colored * (1.0 + clip01(0.14 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.14 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_4_folded = voice_4_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_4_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_4_filtered = voice_4_folded : fi.lowpass(1, max(20.0, clip01(0.72 * (1.0 + 0 * voice_4_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.18 * (1.0 + 0 * voice_4_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.18 * (1.0 + 0 * voice_4_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_4_phased = voice_4_filtered;
|
||||||
|
voice_4_formants = voice_4_phased;
|
||||||
|
voice_4 = ((voice_4_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_4_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_4_age, 0, 0, 0.62, 0.16, 0.035) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.08);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1 + voice_2 + voice_3 + voice_4) * 0.72);
|
||||||
238
tools/aqua-playground/lowered/advanced-syrinx-voice.dsp
Normal file
238
tools/aqua-playground/lowered/advanced-syrinx-voice.dsp
Normal file
|
|
@ -0,0 +1,238 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "advanced-syrinx-voice";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_param_0_base = hslider("/voice/left/pressure", 0.78, 0, 1.5, 0.001) : si.smoo;
|
||||||
|
patch_param_0_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_0_curve_value = select2(patch_param_0_curve_time < 0.04, select2(patch_param_0_curve_time < 0.28, select2(patch_param_0_curve_time < 0.56, select2(patch_param_0_curve_time < 0.92, 0.24, seg(patch_param_0_curve_time, 0.56, 0.36, 0.88, 0.24)), seg(patch_param_0_curve_time, 0.28, 0.28, 0.76, 0.88)), seg(patch_param_0_curve_time, 0.04, 0.24, 0.82, 0.76)), seg(patch_param_0_curve_time, 0, 0.04, 0.05, 0.82));
|
||||||
|
patch_param_0_curve_depth = hslider("/curves/left_phrase_pressure/depth", 0.92, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_0 = min(1.5, max(0, patch_param_0_base * (1.0 - patch_param_0_curve_depth) + patch_param_0_curve_value * patch_param_0_curve_depth));
|
||||||
|
patch_param_1_base = hslider("/voice/right/pressure", 0.66, 0, 1.5, 0.001) : si.smoo;
|
||||||
|
patch_param_1_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_1_curve_value = select2(patch_param_1_curve_time < 0.06, select2(patch_param_1_curve_time < 0.24, select2(patch_param_1_curve_time < 0.5, select2(patch_param_1_curve_time < 0.92, 0.18, seg(patch_param_1_curve_time, 0.5, 0.42, 0.58, 0.18)), seg(patch_param_1_curve_time, 0.24, 0.26, 0.74, 0.58)), seg(patch_param_1_curve_time, 0.06, 0.18, 0.68, 0.74)), seg(patch_param_1_curve_time, 0, 0.06, 0.03, 0.68));
|
||||||
|
patch_param_1_curve_depth = hslider("/curves/right_phrase_pressure/depth", 0.92, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_1 = min(1.5, max(0, patch_param_1_base * (1.0 - patch_param_1_curve_depth) + patch_param_1_curve_value * patch_param_1_curve_depth));
|
||||||
|
patch_param_2_base = hslider("/voice/left/opening", 0.23, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_2_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_2_curve_value = select2(patch_param_2_curve_time < 0.035, select2(patch_param_2_curve_time < 0.46, select2(patch_param_2_curve_time < 0.92, 0.05, seg(patch_param_2_curve_time, 0.46, 0.46, 0.18, 0.05)), seg(patch_param_2_curve_time, 0.035, 0.425, 0.24, 0.18)), seg(patch_param_2_curve_time, 0, 0.035, 0.02, 0.24));
|
||||||
|
patch_param_2_curve_depth = hslider("/curves/left_phrase_opening/depth", 0.85, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_2 = min(1, max(0, patch_param_2_base * (1.0 - patch_param_2_curve_depth) + patch_param_2_curve_value * patch_param_2_curve_depth));
|
||||||
|
patch_param_3_base = hslider("/voice/right/opening", 0.19, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_3_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_3_curve_value = select2(patch_param_3_curve_time < 0.04, select2(patch_param_3_curve_time < 0.42, select2(patch_param_3_curve_time < 0.92, 0.05, seg(patch_param_3_curve_time, 0.42, 0.5, 0.15, 0.05)), seg(patch_param_3_curve_time, 0.04, 0.38, 0.2, 0.15)), seg(patch_param_3_curve_time, 0, 0.04, 0.02, 0.2));
|
||||||
|
patch_param_3_curve_depth = hslider("/curves/right_phrase_opening/depth", 0.85, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_3 = min(1, max(0, patch_param_3_base * (1.0 - patch_param_3_curve_depth) + patch_param_3_curve_value * patch_param_3_curve_depth));
|
||||||
|
patch_param_4 = hslider("/voice/left/tension", 0.43, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_5 = hslider("/voice/right/tension", 0.5, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_6_base = hslider("/voice/beak/opening", 0.88, 0, 1.5, 0.001) : si.smoo;
|
||||||
|
patch_param_6_curve_time = max(0.0, age * 1 - 0);
|
||||||
|
patch_param_6_curve_value = select2(patch_param_6_curve_time < 0.08, select2(patch_param_6_curve_time < 0.52, select2(patch_param_6_curve_time < 0.92, 0.58, seg(patch_param_6_curve_time, 0.52, 0.4, 1.08, 0.58)), seg(patch_param_6_curve_time, 0.08, 0.44, 0.94, 1.08)), seg(patch_param_6_curve_time, 0, 0.08, 0.34, 0.94));
|
||||||
|
patch_param_6_curve_depth = hslider("/curves/beak_phrase/depth", 0.72, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_6 = min(1.5, max(0, patch_param_6_base * (1.0 - patch_param_6_curve_depth) + patch_param_6_curve_value * patch_param_6_curve_depth));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_graph_drive = 1.0;
|
||||||
|
voice_0_graph_terminal_area_left_merge = max(0.000001, 0.1764 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_left_merge = 0;
|
||||||
|
voice_0_graph_terminal_area_right_merge = max(0.000001, 0.16 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_right_merge = 0;
|
||||||
|
voice_0_graph_terminal_area_trachea_base = max(0.000001, 0.1444 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_trachea_base = 0;
|
||||||
|
voice_0_graph_terminal_area_left_labium = max(0.000001, 0.0484 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_left_labium = 0;
|
||||||
|
voice_0_graph_terminal_area_right_labium = max(0.000001, 0.04 * max(0.0, 1));
|
||||||
|
voice_0_graph_terminal_reflection_right_labium = 0;
|
||||||
|
voice_0_graph_terminal_area_beak = max(0.000001, 0.2116 * max(0.0, 0.88));
|
||||||
|
voice_0_graph_terminal_reflection_beak = ((0.96) * (1.0 - clip01(pow(clip01((max(0.0, patch_param_6)) / 1.5), 2.0))) + (-0.72) * clip01(pow(clip01((max(0.0, patch_param_6)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_node_area_left_labium = max(0.000001, voice_0_graph_terminal_area_left_labium);
|
||||||
|
voice_0_graph_node_reflection_left_labium = (voice_0_graph_terminal_reflection_left_labium) / 1;
|
||||||
|
voice_0_graph_node_area_left_merge = max(0.000001, voice_0_graph_terminal_area_left_merge);
|
||||||
|
voice_0_graph_node_reflection_left_merge = (voice_0_graph_terminal_reflection_left_merge) / 1;
|
||||||
|
voice_0_graph_node_area_right_labium = max(0.000001, voice_0_graph_terminal_area_right_labium);
|
||||||
|
voice_0_graph_node_reflection_right_labium = (voice_0_graph_terminal_reflection_right_labium) / 1;
|
||||||
|
voice_0_graph_node_area_right_merge = max(0.000001, voice_0_graph_terminal_area_right_merge);
|
||||||
|
voice_0_graph_node_reflection_right_merge = (voice_0_graph_terminal_reflection_right_merge) / 1;
|
||||||
|
voice_0_graph_node_area_trachea_base = max(0.000001, voice_0_graph_terminal_area_trachea_base);
|
||||||
|
voice_0_graph_node_reflection_trachea_base = (voice_0_graph_terminal_reflection_trachea_base) / 1;
|
||||||
|
voice_0_graph_node_area_beak = max(0.000001, voice_0_graph_terminal_area_beak);
|
||||||
|
voice_0_graph_node_reflection_beak = (voice_0_graph_terminal_reflection_beak) / 1;
|
||||||
|
voice_0_graph_segment_area_0 = max(0.000001, 0.1089);
|
||||||
|
voice_0_graph_segment_area_1 = max(0.000001, 0.0961);
|
||||||
|
voice_0_graph_segment_area_2 = max(0.000001, 0.2704);
|
||||||
|
voice_0_graph_connection_coupling_syrinx_merge = clip01(1);
|
||||||
|
voice_0_graph_connection_loss_syrinx_merge = clip01(1);
|
||||||
|
voice_0_graph_radiation_gain = 1.0;
|
||||||
|
voice_0_graph_source_left_labium = 0.0;
|
||||||
|
voice_0_graph_source_right_labium = 0.0;
|
||||||
|
voice_0_graph(x) = voice_0_graph_loop ~ si.bus(6) : (si.block(6), _) with {
|
||||||
|
voice_0_graph_loop(r0, r1, r2, l0, l1, l2) = voice_0_graph_next_r0, voice_0_graph_next_r1, voice_0_graph_next_r2, voice_0_graph_next_l0, voice_0_graph_next_l1, voice_0_graph_next_l2, voice_0_graph_out with {
|
||||||
|
voice_0_graph_in_0_start = l0;
|
||||||
|
voice_0_graph_in_0_end = r0;
|
||||||
|
voice_0_graph_in_1_start = l1;
|
||||||
|
voice_0_graph_in_1_end = r1;
|
||||||
|
voice_0_graph_in_2_start = l2;
|
||||||
|
voice_0_graph_in_2_end = r2;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_load_pressure = voice_0_graph_node_incident_pressure_left_labium;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_stiffness_hint = max(0.00002, min(0.16, pow(2.0 * ma.PI * max(20.0, ((max(20, 920 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 5.2) * 0.008))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) / ma.SR, 2.0)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow_scale = max(0.0, 1.15);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_tissue_loss = clip01(0.18);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_aperture_shape = clip01(0.4);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow_loss = max(0.0, 0.35);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_reservoir_pressure = (clip01(patch_param_0)) * (max(0.0, 1));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_downstream_pressure = (max(0.0, 0)) + (max(0.0, 0.42)) * ma.tanh(voice_0_graph_source_left_labium_left_labium_load_pressure);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_pressure_drive = max(0.0, voice_0_graph_source_left_labium_left_labium_reservoir_pressure - voice_0_graph_source_left_labium_left_labium_downstream_pressure);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_stiffness = max(0.00002, (max(0.0, 0.028)) * (0.55 + 1.10 * (clip01(patch_param_4))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_frequency = max(45.0, min(10000.0, (ma.SR / (2.0 * ma.PI)) * sqrt(max(0.00002, voice_0_graph_source_left_labium_left_labium_stiffness) / (max(0.02, 0.3)))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_load_detune = 1.0 - 0.035 * ma.tanh(voice_0_graph_source_left_labium_left_labium_load_pressure * (max(0.0, 0.42)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_oscillation_threshold = 0.030 + 0.070 * (max(0.0, 0.14)) + 0.080 * voice_0_graph_source_left_labium_left_labium_tissue_loss + 0.18 * clip01(max(0.0, patch_param_2));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_oscillation_gate = clip01((voice_0_graph_source_left_labium_left_labium_pressure_drive - voice_0_graph_source_left_labium_left_labium_oscillation_threshold) * (3.0 + 1.6 * max(0.0, 1.0 - (max(0.0, 0.14)))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_q = 3.0 + 22.0 * clip01((clip01(patch_param_0)) * (1.0 - 0.45 * (max(0.0, 0.14)) - 0.35 * voice_0_graph_source_left_labium_left_labium_tissue_loss) + 0.35 * (clip01(patch_param_4)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_seed = (no.noise * 0.018 + voice_0_graph_source_left_labium_left_labium_pressure_drive * max(0.0, 0.22 - clip01(max(0.0, patch_param_2))) * 0.16);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_ring = voice_0_graph_source_left_labium_left_labium_modal_seed : fi.resonbp(voice_0_graph_source_left_labium_left_labium_modal_frequency, voice_0_graph_source_left_labium_left_labium_modal_q, 1);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_oscillator = os.osc(voice_0_graph_source_left_labium_left_labium_modal_frequency * voice_0_graph_source_left_labium_left_labium_load_detune) * voice_0_graph_source_left_labium_left_labium_oscillation_gate;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_modal_tissue = (voice_0_graph_source_left_labium_left_labium_modal_oscillator * (0.70 + 0.80 * (clip01(patch_param_4))) + voice_0_graph_source_left_labium_left_labium_modal_ring * 0.35) * max(0.0, 1.0 - 0.35 * (max(0.0, 0.14)) - 0.45 * voice_0_graph_source_left_labium_left_labium_tissue_loss);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_velocity_decay = min(0.9995, max(0.20, 1.0 - ((0.008 + 0.08 * (max(0.0, 0.14)) + 0.055 * voice_0_graph_source_left_labium_left_labium_tissue_loss) / (max(0.02, 0.3)))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_displacement_decay = min(0.9998, max(0.20, 1.0 - 0.0008 / (max(0.02, 0.3))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_force = (voice_0_graph_source_left_labium_left_labium_pressure_drive * (0.0004 + 0.0024 * (1.0 - clip01(max(0.0, patch_param_2)))) - (max(0.0, 0.42)) * voice_0_graph_source_left_labium_left_labium_load_pressure * 0.001) / (max(0.02, 0.3));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_velocity = voice_0_graph_source_left_labium_left_labium_force : + ~ *(voice_0_graph_source_left_labium_left_labium_velocity_decay / (1.0 + 10.0 * voice_0_graph_source_left_labium_left_labium_stiffness + 0.4 * (max(0.0, 0.88))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_displacement = voice_0_graph_source_left_labium_left_labium_velocity : + ~ *(voice_0_graph_source_left_labium_left_labium_displacement_decay / (1.0 + 1.5 * voice_0_graph_source_left_labium_left_labium_stiffness));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_aperture_linear = max(0.0, (max(0.0, 0.024)) + (max(0.0, patch_param_2)) + voice_0_graph_source_left_labium_left_labium_displacement - (max(0.0, 0.88)) * pow(voice_0_graph_source_left_labium_left_labium_displacement, 3.0));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_aperture = voice_0_graph_source_left_labium_left_labium_aperture_linear * (1.0 - 0.55 * voice_0_graph_source_left_labium_left_labium_aperture_shape) + pow(voice_0_graph_source_left_labium_left_labium_aperture_linear, 2.0) * (0.55 * voice_0_graph_source_left_labium_left_labium_aperture_shape);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow_resistance = 1.0 + voice_0_graph_source_left_labium_left_labium_flow_loss * (0.35 + 1.8 / max(0.035, voice_0_graph_source_left_labium_left_labium_aperture + (max(0.0, 0.024))));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_turbulence = no.noise : fi.highpass(2, 1200.0 + 2800.0 * clip01(1.0 - voice_0_graph_source_left_labium_left_labium_aperture));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_voicing = voice_0_graph_source_left_labium_left_labium_modal_tissue * (0.18 + 1.45 * (clip01(patch_param_4)));
|
||||||
|
voice_0_graph_source_left_labium_left_labium_flow = ma.tanh(((voice_0_graph_source_left_labium_left_labium_pressure_drive * voice_0_graph_source_left_labium_left_labium_aperture * (1.5 + 3.5 * voice_0_graph_source_left_labium_left_labium_flow_scale)) + (voice_0_graph_source_left_labium_left_labium_voicing * voice_0_graph_source_left_labium_left_labium_pressure_drive * (0.45 + 1.55 * (clip01(patch_param_4))))) / voice_0_graph_source_left_labium_left_labium_flow_resistance);
|
||||||
|
voice_0_graph_source_left_labium_left_labium_noise = voice_0_graph_source_left_labium_left_labium_turbulence * (clip01(0.018)) * voice_0_graph_source_left_labium_left_labium_pressure_drive * clip01(voice_0_graph_source_left_labium_left_labium_aperture) * (1.0 - 0.55 * (clip01(patch_param_4))) / voice_0_graph_source_left_labium_left_labium_flow_resistance;
|
||||||
|
voice_0_graph_source_left_labium_left_labium_out = (voice_0_graph_source_left_labium_left_labium_flow * voice_0_graph_source_left_labium_left_labium_flow_scale * (0.55 + 2.6 * voice_0_graph_source_left_labium_left_labium_pressure_drive) + voice_0_graph_source_left_labium_left_labium_noise) * ((1) * (1.0));
|
||||||
|
voice_0_graph_node_incident_pressure_left_labium = (voice_0_graph_in_0_start) / 1;
|
||||||
|
voice_0_graph_node_source_left_labium = voice_0_graph_source_left_labium_left_labium_out;
|
||||||
|
voice_0_graph_node_incident_pressure_left_merge = (voice_0_graph_in_0_end) / 1;
|
||||||
|
voice_0_graph_node_source_left_merge = 0.0;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_load_pressure = voice_0_graph_node_incident_pressure_right_labium;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_stiffness_hint = max(0.00002, min(0.16, pow(2.0 * ma.PI * max(20.0, ((max(20, 920 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 5.2) * 0.008))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) / ma.SR, 2.0)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow_scale = max(0.0, 1.08);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_tissue_loss = clip01(0.2);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_aperture_shape = clip01(0.4);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow_loss = max(0.0, 0.35);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_reservoir_pressure = (clip01(patch_param_1)) * (max(0.0, 1));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_downstream_pressure = (max(0.0, 0)) + (max(0.0, 0.42)) * ma.tanh(voice_0_graph_source_right_labium_right_labium_load_pressure);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_pressure_drive = max(0.0, voice_0_graph_source_right_labium_right_labium_reservoir_pressure - voice_0_graph_source_right_labium_right_labium_downstream_pressure);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_stiffness = max(0.00002, (max(0.0, 0.032)) * (0.55 + 1.10 * (clip01(patch_param_5))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_frequency = max(45.0, min(10000.0, (ma.SR / (2.0 * ma.PI)) * sqrt(max(0.00002, voice_0_graph_source_right_labium_right_labium_stiffness) / (max(0.02, 0.32)))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_load_detune = 1.0 - 0.035 * ma.tanh(voice_0_graph_source_right_labium_right_labium_load_pressure * (max(0.0, 0.42)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_oscillation_threshold = 0.030 + 0.070 * (max(0.0, 0.16)) + 0.080 * voice_0_graph_source_right_labium_right_labium_tissue_loss + 0.18 * clip01(max(0.0, patch_param_3));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_oscillation_gate = clip01((voice_0_graph_source_right_labium_right_labium_pressure_drive - voice_0_graph_source_right_labium_right_labium_oscillation_threshold) * (3.0 + 1.6 * max(0.0, 1.0 - (max(0.0, 0.16)))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_q = 3.0 + 22.0 * clip01((clip01(patch_param_1)) * (1.0 - 0.45 * (max(0.0, 0.16)) - 0.35 * voice_0_graph_source_right_labium_right_labium_tissue_loss) + 0.35 * (clip01(patch_param_5)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_seed = (no.noise * 0.018 + voice_0_graph_source_right_labium_right_labium_pressure_drive * max(0.0, 0.22 - clip01(max(0.0, patch_param_3))) * 0.16);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_ring = voice_0_graph_source_right_labium_right_labium_modal_seed : fi.resonbp(voice_0_graph_source_right_labium_right_labium_modal_frequency, voice_0_graph_source_right_labium_right_labium_modal_q, 1);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_oscillator = os.osc(voice_0_graph_source_right_labium_right_labium_modal_frequency * voice_0_graph_source_right_labium_right_labium_load_detune) * voice_0_graph_source_right_labium_right_labium_oscillation_gate;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_modal_tissue = (voice_0_graph_source_right_labium_right_labium_modal_oscillator * (0.70 + 0.80 * (clip01(patch_param_5))) + voice_0_graph_source_right_labium_right_labium_modal_ring * 0.35) * max(0.0, 1.0 - 0.35 * (max(0.0, 0.16)) - 0.45 * voice_0_graph_source_right_labium_right_labium_tissue_loss);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_velocity_decay = min(0.9995, max(0.20, 1.0 - ((0.008 + 0.08 * (max(0.0, 0.16)) + 0.055 * voice_0_graph_source_right_labium_right_labium_tissue_loss) / (max(0.02, 0.32)))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_displacement_decay = min(0.9998, max(0.20, 1.0 - 0.0008 / (max(0.02, 0.32))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_force = (voice_0_graph_source_right_labium_right_labium_pressure_drive * (0.0004 + 0.0024 * (1.0 - clip01(max(0.0, patch_param_3)))) - (max(0.0, 0.42)) * voice_0_graph_source_right_labium_right_labium_load_pressure * 0.001) / (max(0.02, 0.32));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_velocity = voice_0_graph_source_right_labium_right_labium_force : + ~ *(voice_0_graph_source_right_labium_right_labium_velocity_decay / (1.0 + 10.0 * voice_0_graph_source_right_labium_right_labium_stiffness + 0.4 * (max(0.0, 0.88))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_displacement = voice_0_graph_source_right_labium_right_labium_velocity : + ~ *(voice_0_graph_source_right_labium_right_labium_displacement_decay / (1.0 + 1.5 * voice_0_graph_source_right_labium_right_labium_stiffness));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_aperture_linear = max(0.0, (max(0.0, 0.024)) + (max(0.0, patch_param_3)) + voice_0_graph_source_right_labium_right_labium_displacement - (max(0.0, 0.88)) * pow(voice_0_graph_source_right_labium_right_labium_displacement, 3.0));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_aperture = voice_0_graph_source_right_labium_right_labium_aperture_linear * (1.0 - 0.55 * voice_0_graph_source_right_labium_right_labium_aperture_shape) + pow(voice_0_graph_source_right_labium_right_labium_aperture_linear, 2.0) * (0.55 * voice_0_graph_source_right_labium_right_labium_aperture_shape);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow_resistance = 1.0 + voice_0_graph_source_right_labium_right_labium_flow_loss * (0.35 + 1.8 / max(0.035, voice_0_graph_source_right_labium_right_labium_aperture + (max(0.0, 0.024))));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_turbulence = no.noise : fi.highpass(2, 1200.0 + 2800.0 * clip01(1.0 - voice_0_graph_source_right_labium_right_labium_aperture));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_voicing = voice_0_graph_source_right_labium_right_labium_modal_tissue * (0.18 + 1.45 * (clip01(patch_param_5)));
|
||||||
|
voice_0_graph_source_right_labium_right_labium_flow = ma.tanh(((voice_0_graph_source_right_labium_right_labium_pressure_drive * voice_0_graph_source_right_labium_right_labium_aperture * (1.5 + 3.5 * voice_0_graph_source_right_labium_right_labium_flow_scale)) + (voice_0_graph_source_right_labium_right_labium_voicing * voice_0_graph_source_right_labium_right_labium_pressure_drive * (0.45 + 1.55 * (clip01(patch_param_5))))) / voice_0_graph_source_right_labium_right_labium_flow_resistance);
|
||||||
|
voice_0_graph_source_right_labium_right_labium_noise = voice_0_graph_source_right_labium_right_labium_turbulence * (clip01(0.016)) * voice_0_graph_source_right_labium_right_labium_pressure_drive * clip01(voice_0_graph_source_right_labium_right_labium_aperture) * (1.0 - 0.55 * (clip01(patch_param_5))) / voice_0_graph_source_right_labium_right_labium_flow_resistance;
|
||||||
|
voice_0_graph_source_right_labium_right_labium_out = (voice_0_graph_source_right_labium_right_labium_flow * voice_0_graph_source_right_labium_right_labium_flow_scale * (0.55 + 2.6 * voice_0_graph_source_right_labium_right_labium_pressure_drive) + voice_0_graph_source_right_labium_right_labium_noise) * ((0.94) * (1.0));
|
||||||
|
voice_0_graph_node_incident_pressure_right_labium = (voice_0_graph_in_1_start) / 1;
|
||||||
|
voice_0_graph_node_source_right_labium = voice_0_graph_source_right_labium_right_labium_out;
|
||||||
|
voice_0_graph_node_incident_pressure_right_merge = (voice_0_graph_in_1_end) / 1;
|
||||||
|
voice_0_graph_node_source_right_merge = 0.0;
|
||||||
|
voice_0_graph_node_incident_pressure_trachea_base = (voice_0_graph_in_2_start) / 1;
|
||||||
|
voice_0_graph_node_source_trachea_base = 0.0;
|
||||||
|
voice_0_graph_node_incident_pressure_beak = (voice_0_graph_in_2_end) / 1;
|
||||||
|
voice_0_graph_node_source_beak = 0.0;
|
||||||
|
voice_0_graph_connection_reflection_syrinx_merge_left_merge_0_end = (2.0 * (voice_0_graph_segment_area_0) - (voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2)) / max(0.000001, voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2);
|
||||||
|
voice_0_graph_connection_scattered_syrinx_merge_left_merge_0_end = voice_0_graph_connection_reflection_syrinx_merge_left_merge_0_end * voice_0_graph_in_0_end + (1.0 + voice_0_graph_connection_reflection_syrinx_merge_left_merge_0_end) * (voice_0_graph_in_1_end + voice_0_graph_in_2_start);
|
||||||
|
voice_0_graph_out_0_end = ((voice_0_graph_connection_scattered_syrinx_merge_left_merge_0_end) * (voice_0_graph_connection_coupling_syrinx_merge) + voice_0_graph_in_0_end * (1.0 - (voice_0_graph_connection_coupling_syrinx_merge)) + (voice_0_graph_node_source_left_merge) / 1) * voice_0_graph_connection_loss_syrinx_merge;
|
||||||
|
voice_0_graph_connection_reflection_syrinx_merge_right_merge_1_end = (2.0 * (voice_0_graph_segment_area_1) - (voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2)) / max(0.000001, voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2);
|
||||||
|
voice_0_graph_connection_scattered_syrinx_merge_right_merge_1_end = voice_0_graph_connection_reflection_syrinx_merge_right_merge_1_end * voice_0_graph_in_1_end + (1.0 + voice_0_graph_connection_reflection_syrinx_merge_right_merge_1_end) * (voice_0_graph_in_0_end + voice_0_graph_in_2_start);
|
||||||
|
voice_0_graph_out_1_end = ((voice_0_graph_connection_scattered_syrinx_merge_right_merge_1_end) * (voice_0_graph_connection_coupling_syrinx_merge) + voice_0_graph_in_1_end * (1.0 - (voice_0_graph_connection_coupling_syrinx_merge)) + (voice_0_graph_node_source_right_merge) / 1) * voice_0_graph_connection_loss_syrinx_merge;
|
||||||
|
voice_0_graph_connection_reflection_syrinx_merge_trachea_base_2_start = (2.0 * (voice_0_graph_segment_area_2) - (voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2)) / max(0.000001, voice_0_graph_segment_area_0 + voice_0_graph_segment_area_1 + voice_0_graph_segment_area_2);
|
||||||
|
voice_0_graph_connection_scattered_syrinx_merge_trachea_base_2_start = voice_0_graph_connection_reflection_syrinx_merge_trachea_base_2_start * voice_0_graph_in_2_start + (1.0 + voice_0_graph_connection_reflection_syrinx_merge_trachea_base_2_start) * (voice_0_graph_in_0_end + voice_0_graph_in_1_end);
|
||||||
|
voice_0_graph_out_2_start = ((voice_0_graph_connection_scattered_syrinx_merge_trachea_base_2_start) * (voice_0_graph_connection_coupling_syrinx_merge) + voice_0_graph_in_2_start * (1.0 - (voice_0_graph_connection_coupling_syrinx_merge)) + (voice_0_graph_node_source_trachea_base) / 1) * voice_0_graph_connection_loss_syrinx_merge;
|
||||||
|
voice_0_graph_connection_energy_in_syrinx_merge = (voice_0_graph_segment_area_0) * pow(voice_0_graph_in_0_end, 2.0) + (voice_0_graph_segment_area_1) * pow(voice_0_graph_in_1_end, 2.0) + (voice_0_graph_segment_area_2) * pow(voice_0_graph_in_2_start, 2.0);
|
||||||
|
voice_0_graph_connection_energy_out_syrinx_merge = (voice_0_graph_segment_area_0) * pow(voice_0_graph_out_0_end, 2.0) + (voice_0_graph_segment_area_1) * pow(voice_0_graph_out_1_end, 2.0) + (voice_0_graph_segment_area_2) * pow(voice_0_graph_out_2_start, 2.0);
|
||||||
|
voice_0_graph_out_0_start = voice_0_graph_in_0_start * voice_0_graph_node_reflection_left_labium + (voice_0_graph_node_source_left_labium) / 1;
|
||||||
|
voice_0_graph_out_1_start = voice_0_graph_in_1_start * voice_0_graph_node_reflection_right_labium + (voice_0_graph_node_source_right_labium) / 1;
|
||||||
|
voice_0_graph_radiation_boundary_load_beak = clip01((voice_0_graph_radiation_admittance_beak) * (voice_0_graph_radiation_differentiation_beak) * (clip01(0.98)));
|
||||||
|
voice_0_graph_radiation_reflected_raw_beak = (voice_0_graph_in_2_end) * (voice_0_graph_terminal_reflection_beak) + (voice_0_graph_node_source_beak);
|
||||||
|
voice_0_graph_radiation_reflected_loaded_beak = (voice_0_graph_radiation_reflected_raw_beak) * (1.0 - (voice_0_graph_radiation_boundary_load_beak)) + ((voice_0_graph_radiation_reflected_raw_beak) : fi.lowpass(1, voice_0_graph_radiation_highpass_beak)) * (voice_0_graph_radiation_boundary_load_beak);
|
||||||
|
voice_0_graph_out_2_end = voice_0_graph_radiation_reflected_loaded_beak;
|
||||||
|
voice_0_graph_radiation_model_beak = 2;
|
||||||
|
voice_0_graph_radiation_reference_area_beak = max(0.10, 1.10 * 1.10 * (0.25 + 0.75 * clip01(pow(clip01((max(0.0, patch_param_6)) / 1.5), 2.0))));
|
||||||
|
voice_0_graph_radiation_differentiation_beak = (0.50 + 0.22 * clip01(pow(clip01((max(0.0, patch_param_6)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_radiation_highpass_beak = (180.0 + 220.0 * clip01(pow(clip01((max(0.0, patch_param_6)) / 1.5), 2.0)));
|
||||||
|
voice_0_graph_radiation_admittance_beak = sqrt(clip01((voice_0_graph_terminal_area_beak) / max(0.000001, (voice_0_graph_terminal_area_beak) + (voice_0_graph_radiation_reference_area_beak))));
|
||||||
|
voice_0_graph_radiation_flow_beak = ((voice_0_graph_in_2_end - voice_0_graph_out_2_end));
|
||||||
|
voice_0_graph_delay_0 = max(0.000001, 0.00011079 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_0 = clip01((0.999) * (sqrt(clip01((voice_0_graph_segment_area_0) / max(0.000001, (voice_0_graph_segment_area_0) + 0.02)))));
|
||||||
|
voice_0_graph_next_r0 = voice_0_graph_out_0_start : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_0))) * voice_0_graph_segment_loss_0;
|
||||||
|
voice_0_graph_next_l0 = voice_0_graph_out_0_end : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_0))) * voice_0_graph_segment_loss_0;
|
||||||
|
voice_0_graph_delay_1 = max(0.000001, 0.00010496 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_1 = clip01((0.999) * (sqrt(clip01((voice_0_graph_segment_area_1) / max(0.000001, (voice_0_graph_segment_area_1) + 0.02)))));
|
||||||
|
voice_0_graph_next_r1 = voice_0_graph_out_1_start : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_1))) * voice_0_graph_segment_loss_1;
|
||||||
|
voice_0_graph_next_l1 = voice_0_graph_out_1_end : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_1))) * voice_0_graph_segment_loss_1;
|
||||||
|
voice_0_graph_delay_2 = max(0.000001, 0.0002449 * ma.SR);
|
||||||
|
voice_0_graph_segment_loss_2 = clip01((0.999) * (sqrt(clip01((voice_0_graph_segment_area_2) / max(0.000001, (voice_0_graph_segment_area_2) + 0.02)))));
|
||||||
|
voice_0_graph_next_r2 = voice_0_graph_out_2_start : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_2))) * voice_0_graph_segment_loss_2;
|
||||||
|
voice_0_graph_next_l2 = voice_0_graph_out_2_end : de.fdelay(2048, min(2047, max(0.000001, voice_0_graph_delay_2))) * voice_0_graph_segment_loss_2;
|
||||||
|
voice_0_graph_out = (((voice_0_graph_radiation_flow_beak * (1.0 - (voice_0_graph_radiation_differentiation_beak)) + (voice_0_graph_radiation_flow_beak : fi.highpass(1, voice_0_graph_radiation_highpass_beak)) * (voice_0_graph_radiation_differentiation_beak)) * clip01(0.98) * voice_0_graph_radiation_admittance_beak)) * voice_0_graph_radiation_gain;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
voice_0_acoustic_graph_radiated = voice_0_graph(voice_0_graph_drive);
|
||||||
|
voice_0_freq = ((max(20, 920 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 5.2) * 0.008))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = voice_0_acoustic_graph_radiated;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.82 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.03 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.03 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0)) : fi.resonbp(max(20.0, clip01(0.36) * 18000.0), max(0.1, 1.5), 1.0);
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.12, 0.92) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.3);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 0.46);
|
||||||
65
tools/aqua-playground/lowered/bfxr-coin-spark.dsp
Normal file
65
tools/aqua-playground/lowered/bfxr-coin-spark.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "bfxr-coin-spark";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 1180 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 1180 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 1180 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 5))) * (max(0.0, 3.2 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.16, 0.0001))) / ma.PI));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.05 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.05 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.42, 0.022) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.18);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 1770 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 1770 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 1770 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 7))) * (max(0.0, 1.7 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.11, 0.0001))) / ma.PI));
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.05 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.05 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.42, 0.022) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.09);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
65
tools/aqua-playground/lowered/bfxr-portal-chirp.dsp
Normal file
65
tools/aqua-playground/lowered/bfxr-portal-chirp.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "bfxr-portal-chirp";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0 + 0.018 * lfo_sin(9, 0);
|
||||||
|
patch_mod_duty = 0 + 0.06 * lfo_sin(9, 0);
|
||||||
|
patch_mod_lpf = 0 + 0.08 * lfo_sin(9, 0);
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 320 * pow(2.0, 0.85 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 320 * pow(2.0, 0.85 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 1.0;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.16 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.16 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0.08 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0.08 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.08 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.72 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.08 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.08 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.28, 0.09) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.14);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 640 * pow(2.0, 0.85 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = select2(wrap01(os.phasor(1.0, ((max(20, 640 * pow(2.0, 0.85 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) < clip01(0.44 + 0 * voice_1_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.16 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.16 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0.08 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0.08 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.08 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.72 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.08 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.08 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.28, 0.09) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.05);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
65
tools/aqua-playground/lowered/bfxr-shield-pop.dsp
Normal file
65
tools/aqua-playground/lowered/bfxr-shield-pop.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "bfxr-shield-pop";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 260 * pow(2.0, -0.75 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 7.2) * 0.04))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, 260 * pow(2.0, -0.75 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 7.2) * 0.04))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 0.5);
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.68 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.03 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.03 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 0.7575758, 0.36, 0.04) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.264);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 900 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = no.noise;
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0.7 + patch_mod_noise + 0.0)) + no.noise * clip01(0.7 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.68 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.4 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.4 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 0.7575758, 0.16, 0.018) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.05);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
65
tools/aqua-playground/lowered/bfxr-ui-bloom.dsp
Normal file
65
tools/aqua-playground/lowered/bfxr-ui-bloom.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "bfxr-ui-bloom";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time - floor(time / 0.18) * 0.18;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 392 * pow(2.0, 0.18 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.08, 1.25, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 392 * pow(2.0, 0.18 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.08, 1.25, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.06 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.06 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.78 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 0.8196721, 0.5, 0.05) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.1464);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 588 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 588 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.06 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.06 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.78 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0.004, 0, 0.8196721, 0.38, 0.04) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.06);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
300
tools/aqua-playground/lowered/catalog.json
Normal file
300
tools/aqua-playground/lowered/catalog.json
Normal file
|
|
@ -0,0 +1,300 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "thruster",
|
||||||
|
"origin": "thruster/thruster.dsp",
|
||||||
|
"kind": "faust",
|
||||||
|
"dsp": "thruster.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "808-clap",
|
||||||
|
"origin": "808/clap.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "808-clap.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "808-cowbell",
|
||||||
|
"origin": "808/cowbell.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "808-cowbell.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "808-hat",
|
||||||
|
"origin": "808/hat.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "808-hat.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "808-kick",
|
||||||
|
"origin": "808/kick.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "808-kick.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "808-snare",
|
||||||
|
"origin": "808/snare.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "808-snare.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "808-tom",
|
||||||
|
"origin": "808/tom.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "808-tom.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "advanced-alien-multivalve-graph",
|
||||||
|
"origin": "advanced/alien-multivalve-graph.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "advanced-alien-multivalve-graph.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "advanced-aurora-pad",
|
||||||
|
"origin": "advanced/aurora-pad.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "advanced-aurora-pad.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "advanced-bird-syrinx",
|
||||||
|
"origin": "advanced/bird-syrinx.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "advanced-bird-syrinx.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "advanced-bird-syrinx-graph",
|
||||||
|
"origin": "advanced/bird-syrinx-graph.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "advanced-bird-syrinx-graph.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "advanced-glass-creature",
|
||||||
|
"origin": "advanced/glass-creature.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "advanced-glass-creature.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "advanced-human-larynx-graph",
|
||||||
|
"origin": "advanced/human-larynx-graph.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "advanced-human-larynx-graph.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "advanced-machine-breath",
|
||||||
|
"origin": "advanced/machine-breath.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "advanced-machine-breath.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "advanced-ritual-sequence",
|
||||||
|
"origin": "advanced/ritual-sequence.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "advanced-ritual-sequence.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "advanced-subtractive-drums",
|
||||||
|
"origin": "advanced/subtractive-drums.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "advanced-subtractive-drums.dsp",
|
||||||
|
"warnings": [
|
||||||
|
"voice_0: low-pass resonance is approximated with Faust resonlp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "advanced-syrinx-voice",
|
||||||
|
"origin": "advanced/syrinx-voice.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "advanced-syrinx-voice.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bfxr-coin-spark",
|
||||||
|
"origin": "bfxr/coin-spark.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "bfxr-coin-spark.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bfxr-portal-chirp",
|
||||||
|
"origin": "bfxr/portal-chirp.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "bfxr-portal-chirp.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bfxr-shield-pop",
|
||||||
|
"origin": "bfxr/shield-pop.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "bfxr-shield-pop.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bfxr-ui-bloom",
|
||||||
|
"origin": "bfxr/ui-bloom.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "bfxr-ui-bloom.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dx7-algorithm-32-additive-organ",
|
||||||
|
"origin": "dx7/algorithm-32-additive-organ.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "dx7-algorithm-32-additive-organ.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dx7-algorithm-8-bright-pair",
|
||||||
|
"origin": "dx7/algorithm-8-bright-pair.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "dx7-algorithm-8-bright-pair.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dx7-public-domain-mc-mm-5-3",
|
||||||
|
"origin": "dx7/public-domain-mc-mm-5-3.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "dx7-public-domain-mc-mm-5-3.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dx7-public-domain-prc-synth1-calibrated",
|
||||||
|
"origin": "dx7/public-domain/prc-synth1-calibrated.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "dx7-public-domain-prc-synth1-calibrated.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "examples-language-tour",
|
||||||
|
"origin": "examples/language-tour.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "examples-language-tour.dsp",
|
||||||
|
"warnings": [
|
||||||
|
"voice_2: low-pass resonance is approximated with Faust resonlp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fm-bell-bell",
|
||||||
|
"origin": "fm-bell/bell.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "fm-bell-bell.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fm-bell-chime",
|
||||||
|
"origin": "fm-bell/chime.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "fm-bell-chime.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fm-bell-coin",
|
||||||
|
"origin": "fm-bell/coin.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "fm-bell-coin.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fm-bell-gong",
|
||||||
|
"origin": "fm-bell/gong.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "fm-bell-gong.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sfxr-blip",
|
||||||
|
"origin": "sfxr/blip.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "sfxr-blip.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sfxr-classic-classic-sfxr-abstract-golf",
|
||||||
|
"origin": "sfxr/classic/classic-sfxr-abstract-golf.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "sfxr-classic-classic-sfxr-abstract-golf.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sfxr-explosion",
|
||||||
|
"origin": "sfxr/explosion.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "sfxr-explosion.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sfxr-hit",
|
||||||
|
"origin": "sfxr/hit.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "sfxr-hit.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sfxr-jump",
|
||||||
|
"origin": "sfxr/jump.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "sfxr-jump.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sfxr-laser",
|
||||||
|
"origin": "sfxr/laser.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "sfxr-laser.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sfxr-pickup",
|
||||||
|
"origin": "sfxr/pickup.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "sfxr-pickup.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sfxr-powerup",
|
||||||
|
"origin": "sfxr/powerup.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "sfxr-powerup.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wobble-bass-growl",
|
||||||
|
"origin": "wobble-bass/growl.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "wobble-bass-growl.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wobble-bass-neuro",
|
||||||
|
"origin": "wobble-bass/neuro.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "wobble-bass-neuro.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wobble-bass-talker",
|
||||||
|
"origin": "wobble-bass/talker.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "wobble-bass-talker.dsp",
|
||||||
|
"warnings": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wobble-bass-yoy",
|
||||||
|
"origin": "wobble-bass/yoy.aqua",
|
||||||
|
"kind": "aqua",
|
||||||
|
"dsp": "wobble-bass-yoy.dsp",
|
||||||
|
"warnings": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "dx7-algorithm-32-additive-organ";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_param_0 = hslider("/macro/drawbar", 0.78, 0, 1, 0.001) : si.smoo;
|
||||||
|
patch_param_1 = hslider("/macro/shimmer", 0.08, 0, 0.3, 0.001) : si.smoo;
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 196 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 196 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.96 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.015 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.015 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.002, 0, 1, 0.32, 0.48) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.14);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 392 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 392 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.96 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.015 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.015 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0.002, 0, 1, 0.32, 0.48) * max(0.0, 1.0 + patch_mod_gain + 0.0) * patch_param_0);
|
||||||
|
|
||||||
|
voice_2_age = age;
|
||||||
|
voice_2_freq = ((max(20, 588 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_2_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 588 * pow(2.0, 0 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_2_colored = (voice_2_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_2_driven = ma.tanh(voice_2_colored * (1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_2_folded = voice_2_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_2_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_2_filtered = voice_2_folded : fi.lowpass(1, max(20.0, clip01(0.96 * (1.0 + 0 * voice_2_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.015 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.015 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_2_phased = voice_2_filtered;
|
||||||
|
voice_2_formants = voice_2_phased;
|
||||||
|
voice_2 = ((voice_2_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_2_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_2_age, 0.002, 0, 1, 0.32, 0.48) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.12);
|
||||||
|
|
||||||
|
voice_3_age = age;
|
||||||
|
voice_3_freq = ((max(20, 784 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_3_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 784 * pow(2.0, 0 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_3_colored = (voice_3_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_3_driven = ma.tanh(voice_3_colored * (1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_3_folded = voice_3_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_3_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_3_filtered = voice_3_folded : fi.lowpass(1, max(20.0, clip01(0.96 * (1.0 + 0 * voice_3_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.015 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.015 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_3_phased = voice_3_filtered;
|
||||||
|
voice_3_formants = voice_3_phased;
|
||||||
|
voice_3 = ((voice_3_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_3_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_3_age, 0.002, 0, 1, 0.32, 0.48) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.08);
|
||||||
|
|
||||||
|
voice_4_age = age;
|
||||||
|
voice_4_freq = ((max(20, 980 * pow(2.0, 0 * voice_4_age + 0.5 * 0 * voice_4_age * voice_4_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_4_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 980 * pow(2.0, 0 * voice_4_age + 0.5 * 0 * voice_4_age * voice_4_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_4_colored = (voice_4_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_4_driven = ma.tanh(voice_4_colored * (1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_4_folded = voice_4_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_4_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_4_filtered = voice_4_folded : fi.lowpass(1, max(20.0, clip01(0.96 * (1.0 + 0 * voice_4_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.015 * (1.0 + 0 * voice_4_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.015 * (1.0 + 0 * voice_4_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_4_phased = voice_4_filtered;
|
||||||
|
voice_4_formants = voice_4_phased;
|
||||||
|
voice_4 = ((voice_4_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_4_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_4_age, 0.002, 0, 1, 0.32, 0.48) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.055);
|
||||||
|
|
||||||
|
voice_5_age = age;
|
||||||
|
voice_5_freq = ((max(20, 1176 * pow(2.0, 0 * voice_5_age + 0.5 * 0 * voice_5_age * voice_5_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_5_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 1176 * pow(2.0, 0 * voice_5_age + 0.5 * 0 * voice_5_age * voice_5_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 1176 * pow(2.0, 0 * voice_5_age + 0.5 * 0 * voice_5_age * voice_5_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 1))) * (max(0.0, 0.18 + patch_mod_fm_index + 0.0) * exp(-voice_5_age / max(0.24, 0.0001))) / ma.PI));
|
||||||
|
voice_5_colored = (voice_5_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_5_driven = ma.tanh(voice_5_colored * (1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.03 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_5_folded = voice_5_driven * (1.0 - clip01(patch_param_1 + patch_mod_fold + 0.0)) + fold(voice_5_driven * (1.0 + clip01(patch_param_1 + patch_mod_fold + 0.0) * 3.5)) * clip01(patch_param_1 + patch_mod_fold + 0.0);
|
||||||
|
voice_5_filtered = voice_5_folded : fi.lowpass(1, max(20.0, clip01(0.96 * (1.0 + 0 * voice_5_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.015 * (1.0 + 0 * voice_5_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.015 * (1.0 + 0 * voice_5_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_5_phased = voice_5_filtered;
|
||||||
|
voice_5_formants = voice_5_phased;
|
||||||
|
voice_5 = ((voice_5_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_5_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_5_age, 0.002, 0, 1, 0.32, 0.48) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.035);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1 + voice_2 + voice_3 + voice_4 + voice_5) * 0.58);
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "dx7-algorithm-8-bright-pair";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_param_0 = hslider("/macro/brightness", 2.6, 0, 5, 0.001) : si.smoo;
|
||||||
|
patch_param_1 = hslider("/macro/strike", 0.18, 0.02, 0.6, 0.001) : si.smoo;
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 330 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 330 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 330 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, patch_param_0 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.22, 0.0001))) / ma.PI));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.9 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.02 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.02 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.001, 0, 1, patch_param_1, 0.045) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.04);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 660 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 660 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0.05 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0.05 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.05 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.9 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.02 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.02 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0.001, 0, 1, patch_param_1, 0.045) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.025);
|
||||||
|
|
||||||
|
opgraph_0_freq = 330;
|
||||||
|
opgraph_0_op_6 = sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 4)) + (0.0) / ma.PI)) * oneshot_adsr_at(age, 0.035, 0.18, 0, 0, 0.215) * 0.9;
|
||||||
|
opgraph_0_op_4 = ((_ * 0.18 + (0.0)) : \(pm).(sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 2)) + pm / ma.PI)) * oneshot_adsr_at(age, 0.05, 0.24, 0, 0, 0.29) * 0.7)) ~ _;
|
||||||
|
opgraph_0_op_2 = sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 1)) + (0.0) / ma.PI)) * oneshot_adsr_at(age, 0.045, 0.2, 0, 0, 0.245) * 0.75;
|
||||||
|
opgraph_0_op_5 = sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 3)) + (opgraph_0_op_6 * 1.1) / ma.PI)) * oneshot_adsr_at(age, 0.04, 0.2, 0, 0, 0.24) * 0.82;
|
||||||
|
opgraph_0_op_1 = sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 1)) + (opgraph_0_op_2 * 0.85) / ma.PI)) * oneshot_adsr_at(age, 0.005, 0.08, 0.68, 0.34, 0.1) * 0.82;
|
||||||
|
opgraph_0_op_3 = sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 2)) + (opgraph_0_op_5 * 0.9 + opgraph_0_op_4 * 0.75) / ma.PI)) * oneshot_adsr_at(age, 0.06, 0.3, 0, 0, 0.36) * 0.6;
|
||||||
|
opgraph_0 = (opgraph_0_op_1 + opgraph_0_op_3) * 0.18;
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1 + opgraph_0) * 0.52);
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "dx7-public-domain-mc-mm-5-3";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 654.0639 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 654.0639 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.004, 0.008, 1, 0.004, 0.08) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 1);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 0.82);
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "dx7-public-domain-prc-synth1-calibrated";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
opgraph_0_freq = 261.6256;
|
||||||
|
opgraph_0_op_6 = sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 7.983435)) + (0.0) / ma.PI)) * rl4_env_at(age, 0.001451, 0.413879, 0, 0.296962, 0.399165, 1, 0.424308, 0.370614, 1, 0.549977, 0, 1, 0.85) * 0.125;
|
||||||
|
opgraph_0_op_4 = ((_ * 0.19 + (0.0)) : \(pm).(sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 0.501384)) + pm / ma.PI)) * rl4_env_at(age, 0.001451, 0.413879, 0, 0.296962, 0.353601, 1, 0.424308, 0.276106, 1, 0.549977, 0, 1, 0.85) * 0.917004)) ~ _;
|
||||||
|
opgraph_0_op_2 = sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 0.501384)) + (0.0) / ma.PI)) * rl4_env_at(age, 0.011383, 0.981531, 0, 0.293515, 0.842813, 1, 0.41932, 0.660018, 1, 0.549977, 0, 1, 0.85) * 0.545254;
|
||||||
|
opgraph_0_op_5 = sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 1)) + (opgraph_0_op_6 * 6) / ma.PI)) * rl4_env_at(age, 0.001451, 0.413879, 0, 0.296962, 0.241589, 1, 0.424308, 0.109464, 1, 0.549977, 0, 1, 0.85) * 0.32421;
|
||||||
|
opgraph_0_op_1 = sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 0.500346)) + (opgraph_0_op_2 * 6.275) / ma.PI)) * rl4_env_at(age, 0.001451, 0.984375, 0, 0.296962, 1, 1, 0.424308, 1, 1, 0.549977, 0, 1, 0.85) * 3;
|
||||||
|
opgraph_0_op_3 = sin(2.0 * ma.PI * (os.phasor(1.0, opgraph_0_freq * max(0.0, 0.499655)) + (opgraph_0_op_4 * 6 + opgraph_0_op_5 * 6) / ma.PI)) * rl4_env_at(age, 0.001451, 0.984375, 0, 0.296962, 1, 1, 0.424308, 1, 1, 0.549977, 0, 1, 0.85) * 3;
|
||||||
|
opgraph_0 = (opgraph_0_op_1 + opgraph_0_op_3) * 0.25;
|
||||||
|
|
||||||
|
process = (opgraph_0) * 0.45;
|
||||||
76
tools/aqua-playground/lowered/examples-language-tour.dsp
Normal file
76
tools/aqua-playground/lowered/examples-language-tour.dsp
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "examples-language-tour";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time - floor(time / 0.4739614) * 0.4739614;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0 + 0.08 * lfo_tri(2, 0);
|
||||||
|
patch_mod_pitch = 0 + 0.01 * lfo_sin(5, 0);
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0 + -0.04 * lfo_tri(2, 0);
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0 + 0.08 * lfo_sin(5, 0) + 0 + 0.12 * lfo_tri(2, 0);
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 220 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 5) * 0.02))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 220 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 5) * 0.02))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = ((voice_0_phased : fi.resonbp(620, 6.888889, 1.0)) * 1 + (voice_0_phased : fi.resonbp(1040, 6.933333, 1.0)) * 0.8) / 1.8;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0.45 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0.45 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.002, 0, 1, 0.2, 0.03) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.12);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 440 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, 440 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 0.5);
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.7 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.02 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.02 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.18, 0.02) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.04);
|
||||||
|
|
||||||
|
voice_2_age = age;
|
||||||
|
voice_2_freq = ((max(20, 230.3403 * pow(2.0, 0.7460228 * voice_2_age + 0.5 * 0.00000008 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_2_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 230.3403 * pow(2.0, 0.7460228 * voice_2_age + 0.5 * 0.00000008 * voice_2_age * voice_2_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 1.0;
|
||||||
|
voice_2_colored = (voice_2_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_2_driven = ma.tanh(voice_2_colored * (1.0 + clip01(0.1212819 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1212819 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_2_folded = voice_2_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_2_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_2_filtered = voice_2_folded : fi.resonlp(max(20.0, clip01(1 * (1.0 + -0.00208181 * voice_2_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0), 0.7 + clip01(0.00000026) * 18.0, 1.0) : fi.highpass(1, max(5.0, (clip01(0.0479493 * (1.0 + -0.0020957 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.0479493 * (1.0 + -0.0020957 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_2_phased = voice_2_filtered + (voice_2_filtered : de.fdelay(2048, min(2047.0, max(0.0, abs(0.0001425 + -0.0005662 * voice_2_age) * ma.SR))));
|
||||||
|
voice_2_formants = voice_2_phased;
|
||||||
|
voice_2 = ((voice_2_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_2_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_2_age, 0.00014083, 0, 0.9929289, 0.07120296, 0.07388992) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.2215667);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1 + voice_2) * 0.7);
|
||||||
65
tools/aqua-playground/lowered/fm-bell-bell.dsp
Normal file
65
tools/aqua-playground/lowered/fm-bell-bell.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "fm-bell-bell";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 440 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 440 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 440 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 4.1))) * (max(0.0, 5.8 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.55, 0.0001))) / ma.PI));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.9 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.002, 0, 1, 1.2, 0.04) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.24);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 880 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 880 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 880 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 4.1))) * (max(0.0, 2.4 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.55, 0.0001))) / ma.PI));
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.9 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0.002, 0, 1, 1.2, 0.04) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.08);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
65
tools/aqua-playground/lowered/fm-bell-chime.dsp
Normal file
65
tools/aqua-playground/lowered/fm-bell-chime.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "fm-bell-chime";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 660 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 660 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 660 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 3))) * (max(0.0, 4.2 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.38, 0.0001))) / ma.PI));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.02 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.02 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.001, 0, 1, 0.9, 0.025) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.18);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 990 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 990 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 990 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 3))) * (max(0.0, 2.1 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.38, 0.0001))) / ma.PI));
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.02 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.02 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0.001, 0, 1, 0.9, 0.025) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.07);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
65
tools/aqua-playground/lowered/fm-bell-coin.dsp
Normal file
65
tools/aqua-playground/lowered/fm-bell-coin.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "fm-bell-coin";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 1200 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 1200 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 1200 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 5))) * (max(0.0, 3.4 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.18, 0.0001))) / ma.PI));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.04 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.04 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.45, 0.02) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.18);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 1800 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 1800 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 1800 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 7))) * (max(0.0, 1.8 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.12, 0.0001))) / ma.PI));
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.08 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.04 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.04 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.45, 0.02) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.09);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
65
tools/aqua-playground/lowered/fm-bell-gong.dsp
Normal file
65
tools/aqua-playground/lowered/fm-bell-gong.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "fm-bell-gong";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 196 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 196 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 196 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2.414))) * (max(0.0, 7.2 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.9, 0.0001))) / ma.PI));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.82 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0.003, 0, 1, 1.6, 0.08) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.24);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 311 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 311 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 311 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 3.73))) * (max(0.0, 4.1 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.7, 0.0001))) / ma.PI));
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.1 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.82 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0.003, 0, 1, 1.6, 0.08) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.12);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
54
tools/aqua-playground/lowered/sfxr-blip.dsp
Normal file
54
tools/aqua-playground/lowered/sfxr-blip.dsp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "sfxr-blip";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 78.2334 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 78.2334 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.1 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.1 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.01451247, 0.03832199) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "sfxr-classic-classic-sfxr-abstract-golf";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time - floor(time / 0.11315) * 0.11315;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 148.7934 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.081844, 1.116121, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = select2(wrap01(os.phasor(1.0, ((max(20, 148.7934 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.081844, 1.116121, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0) < clip01(0.5 + 0 * voice_0_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.201 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.201 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 0.6896552, 0.1306122, 0.01451247) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.319);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 229.0554 * pow(2.0, 0.703836 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 229.0554 * pow(2.0, 0.703836 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 1.0;
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.04 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.04 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered + (voice_1_filtered : de.fdelay(2048, min(2047.0, max(0.0, abs(0.0001458 + -0.000504 * voice_1_age) * ma.SR))));
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.07346939, 0.08185941) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
voice_2_age = age;
|
||||||
|
voice_2_freq = ((max(20, 20 * pow(2.0, 0.0320625 * voice_2_age + 0.5 * 0 * voice_2_age * voice_2_age))) * (1.0 + select2(voice_2_age < 0, 0.0, sin(2.0 * ma.PI * (voice_2_age - 0) * 3.4112) * 0.11))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_2_osc = no.noise;
|
||||||
|
voice_2_colored = (voice_2_osc * (1.0 - clip01(0.35 + patch_mod_noise + 0.0)) + no.noise * clip01(0.35 + patch_mod_noise + 0.0));
|
||||||
|
voice_2_driven = ma.tanh(voice_2_colored * (1.0 + clip01(0.2136 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.2136 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_2_folded = voice_2_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_2_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_2_filtered = voice_2_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_2_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_2_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_2_phased = voice_2_filtered + (voice_2_filtered : de.fdelay(2048, min(2047.0, max(0.0, abs(-0.0008712 + -0.00035 * voice_2_age) * ma.SR))));
|
||||||
|
voice_2_formants = voice_2_phased;
|
||||||
|
voice_2 = ((voice_2_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_2_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_2_age, 0, 0, 0.6578947, 0.2938775, 0.1907029) * (1.0 - 0.0264 * (0.5 + 0.5 * lfo_sin(13.04, 0.0))) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.3344);
|
||||||
|
|
||||||
|
voice_3_age = age;
|
||||||
|
voice_3_freq = ((max(20, 57.5946 * pow(2.0, -0.208544 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age))) * (1.0 + select2(voice_3_age < 0, 0.0, sin(2.0 * ma.PI * (voice_3_age - 0) * 3.9602) * 0.09))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_3_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 57.5946 * pow(2.0, -0.208544 * voice_3_age + 0.5 * 0 * voice_3_age * voice_3_age))) * (1.0 + select2(voice_3_age < 0, 0.0, sin(2.0 * ma.PI * (voice_3_age - 0) * 3.9602) * 0.09))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_3_colored = (voice_3_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_3_driven = ma.tanh(voice_3_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_3_folded = voice_3_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_3_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_3_filtered = voice_3_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_3_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_3_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_3_phased = voice_3_filtered;
|
||||||
|
voice_3_formants = voice_3_phased;
|
||||||
|
voice_3 = ((voice_3_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_3_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_3_age, 0, 0, 1, 0.1777778, 0.1306122) * (1.0 - 0.0216 * (0.5 + 0.5 * lfo_sin(13.94, 0.0))) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
voice_4_age = age;
|
||||||
|
voice_4_freq = ((max(20, 51.4206 * pow(2.0, 1.050624 * voice_4_age + 0.5 * 0 * voice_4_age * voice_4_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_4_osc = no.noise;
|
||||||
|
voice_4_colored = (voice_4_osc * (1.0 - clip01(0.35 + patch_mod_noise + 0.0)) + no.noise * clip01(0.35 + patch_mod_noise + 0.0));
|
||||||
|
voice_4_driven = ma.tanh(voice_4_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_4_folded = voice_4_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_4_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_4_filtered = voice_4_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_4_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.12 * (1.0 + 0 * voice_4_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.12 * (1.0 + 0 * voice_4_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_4_phased = voice_4_filtered;
|
||||||
|
voice_4_formants = voice_4_phased;
|
||||||
|
voice_4 = ((voice_4_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_4_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_4_age, 0, 0, 1, 0.09070295, 0.00566893) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
voice_5_age = age;
|
||||||
|
voice_5_freq = ((max(20, 78.2334 * pow(2.0, -0.101156 * voice_5_age + 0.5 * 0 * voice_5_age * voice_5_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_5_osc = select2(wrap01(os.phasor(1.0, ((max(20, 78.2334 * pow(2.0, -0.101156 * voice_5_age + 0.5 * 0 * voice_5_age * voice_5_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) < clip01(0.38 + 0 * voice_5_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_5_colored = (voice_5_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_5_driven = ma.tanh(voice_5_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_5_folded = voice_5_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_5_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_5_filtered = voice_5_folded : fi.lowpass(1, max(20.0, clip01(0.72 * (1.0 + 0 * voice_5_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.05 * (1.0 + 0 * voice_5_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.05 * (1.0 + 0 * voice_5_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_5_phased = voice_5_filtered;
|
||||||
|
voice_5_formants = voice_5_phased;
|
||||||
|
voice_5 = ((voice_5_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_5_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_5_age, 0, 0, 1, 0.07346939, 0.1097506) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
voice_6_age = age;
|
||||||
|
voice_6_freq = ((max(20, 78.2334 * pow(2.0, 0 * voice_6_age + 0.5 * 0 * voice_6_age * voice_6_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_6_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 78.2334 * pow(2.0, 0 * voice_6_age + 0.5 * 0 * voice_6_age * voice_6_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_6_colored = (voice_6_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_6_driven = ma.tanh(voice_6_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_6_folded = voice_6_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_6_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_6_filtered = voice_6_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_6_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.1 * (1.0 + 0 * voice_6_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.1 * (1.0 + 0 * voice_6_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_6_phased = voice_6_filtered;
|
||||||
|
voice_6_formants = voice_6_phased;
|
||||||
|
voice_6 = ((voice_6_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_6_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_6_age, 0, 0, 1, 0.01451247, 0.03832199) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1 + voice_2 + voice_3 + voice_4 + voice_5 + voice_6) * 1);
|
||||||
54
tools/aqua-playground/lowered/sfxr-explosion.dsp
Normal file
54
tools/aqua-playground/lowered/sfxr-explosion.dsp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "sfxr-explosion";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time - floor(time / 0.174744) * 0.174744;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 20 * pow(2.0, 0.0320625 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 3.4112) * 0.11))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = no.noise;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0.35 + patch_mod_noise + 0.0)) + no.noise * clip01(0.35 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.2136 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.2136 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered + (voice_0_filtered : de.fdelay(2048, min(2047.0, max(0.0, abs(-0.0008712 + -0.00035 * voice_0_age) * ma.SR))));
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 0.6578947, 0.2938775, 0.1907029) * (1.0 - 0.0264 * (0.5 + 0.5 * lfo_sin(13.04, 0.0))) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.3344);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
54
tools/aqua-playground/lowered/sfxr-hit.dsp
Normal file
54
tools/aqua-playground/lowered/sfxr-hit.dsp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "sfxr-hit";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 51.4206 * pow(2.0, 1.050624 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = no.noise;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0.35 + patch_mod_noise + 0.0)) + no.noise * clip01(0.35 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.12 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.12 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.09070295, 0.00566893) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
54
tools/aqua-playground/lowered/sfxr-jump.dsp
Normal file
54
tools/aqua-playground/lowered/sfxr-jump.dsp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "sfxr-jump";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 78.2334 * pow(2.0, -0.101156 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = select2(wrap01(os.phasor(1.0, ((max(20, 78.2334 * pow(2.0, -0.101156 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) < clip01(0.38 + 0 * voice_0_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.72 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.05 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.05 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.07346939, 0.1097506) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
54
tools/aqua-playground/lowered/sfxr-laser.dsp
Normal file
54
tools/aqua-playground/lowered/sfxr-laser.dsp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "sfxr-laser";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 229.0554 * pow(2.0, 0.703836 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 229.0554 * pow(2.0, 0.703836 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0) - 1.0;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.04 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.04 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered + (voice_0_filtered : de.fdelay(2048, min(2047.0, max(0.0, abs(0.0001458 + -0.000504 * voice_0_age) * ma.SR))));
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.07346939, 0.08185941) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
54
tools/aqua-playground/lowered/sfxr-pickup.dsp
Normal file
54
tools/aqua-playground/lowered/sfxr-pickup.dsp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "sfxr-pickup";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 148.7934 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.081844, 1.116121, 1.0) * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = select2(wrap01(os.phasor(1.0, ((max(20, 148.7934 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * select2(voice_0_age < 0.081844, 1.116121, 1.0) * pow(2.0, patch_mod_pitch + 0.0)) + 0) < clip01(0.5 + 0 * voice_0_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.201 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.201 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 0.6896552, 0.1306122, 0.01451247) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.319);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
54
tools/aqua-playground/lowered/sfxr-powerup.dsp
Normal file
54
tools/aqua-playground/lowered/sfxr-powerup.dsp
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "sfxr-powerup";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time - floor(time / 0.11315) * 0.11315;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0.0;
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0.0;
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 57.5946 * pow(2.0, -0.208544 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 3.9602) * 0.09))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = sin(2.0 * ma.PI * wrap01(os.phasor(1.0, ((max(20, 57.5946 * pow(2.0, -0.208544 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age))) * (1.0 + select2(voice_0_age < 0, 0.0, sin(2.0 * ma.PI * (voice_0_age - 0) * 3.9602) * 0.09))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0));
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.12 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(1 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.1777778, 0.1306122) * (1.0 - 0.0216 * (0.5 + 0.5 * lfo_sin(13.94, 0.0))) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.22);
|
||||||
|
|
||||||
|
process = softclip((voice_0) * 1);
|
||||||
30
tools/aqua-playground/lowered/thruster.dsp
Normal file
30
tools/aqua-playground/lowered/thruster.dsp
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// thruster: sustained, heavy, brittle-hull thruster with an RPM control.
|
||||||
|
// Compile as Faust source (AquaSynthPatchCompiler.CompileSource); it is not a .aqua script.
|
||||||
|
// Control: rpm (300..6000). Rotor tone = 28 Hz + rpm*0.025 (about 36..180 Hz); three blades chop the rasp at 3x that.
|
||||||
|
declare name "thruster";
|
||||||
|
import("stdfaust.lib");
|
||||||
|
|
||||||
|
rpm = hslider("rpm", 1200, 300, 6000, 1) : si.smooth(ba.tau2pole(0.2));
|
||||||
|
load = (rpm - 300) / 5700;
|
||||||
|
shaft = 28 + rpm * 0.025;
|
||||||
|
|
||||||
|
// effort: slow random wander of the shaft speed
|
||||||
|
wander = no.noise : fi.lowpass(2, 5) : *(0.35);
|
||||||
|
f0 = shaft * (1 + 0.04 * wander);
|
||||||
|
|
||||||
|
// low rumble: saw at shaft speed plus a square a sub-octave down, low-passed
|
||||||
|
// the low-pass opens up as load falls, so at low rpm the rotor tone reaches the 150-300 Hz range a small speaker can show
|
||||||
|
rumble = (os.sawtooth(f0) + 0.7 * os.square(f0 * 0.5)) : fi.lowpass(3, 60 + 2.5 * shaft + 140 * (1 - load));
|
||||||
|
|
||||||
|
// blade pass: rasp noise chopped at 3x shaft, band tracks rpm
|
||||||
|
chop = 0.5 + 0.5 * os.osc(f0 * 3) ^ 2;
|
||||||
|
rasp = no.noise : fi.resonbp(700 + rpm * 0.6, 1.5, 1) : *(chop);
|
||||||
|
|
||||||
|
// brittle hull: high-Q modes rattled by noise, louder with load (a 1.5% floor at idle, so low rpm is tone-led); tanh on the rattle alone keeps ring-up spikes from setting the peak
|
||||||
|
mode(f, q) = no.noise : fi.resonbp(f, q, 1);
|
||||||
|
rattle = (mode(173, 20) * (0.3 + 0.7 * load) + 0.8 * mode(311, 18) + 0.6 * mode(467, 16) + 0.4 * mode(1130, 14)) * (0.015 + 0.985 * load ^ 2) * 3 : *(4) : ma.tanh : /(4);
|
||||||
|
|
||||||
|
mix = rumble * (0.5 + 0.5 * load) + rasp * (0.05 + 1.75 * load) + rattle;
|
||||||
|
// drive falls only gently with load so tanh stays out of saturation while high rpm still ends up louder than low rpm
|
||||||
|
// final 0.425 trim keeps peak under 0.4 at rpm 4800
|
||||||
|
process = mix * (0.4 + 0.6 * load) * (0.9 - 0.5 * load) : ma.tanh : *(0.425);
|
||||||
65
tools/aqua-playground/lowered/wobble-bass-growl.dsp
Normal file
65
tools/aqua-playground/lowered/wobble-bass-growl.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "wobble-bass-growl";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0 + 0.32 * lfo_sin(6, 0);
|
||||||
|
patch_mod_pitch = 0 + 0.035 * lfo_sin(6, 0);
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0 + 0.55 * lfo_sin(6, 0);
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0 + 0.08 * lfo_sin(6, 0);
|
||||||
|
patch_mod_drive = 0 + 0.28 * lfo_sin(6, 0);
|
||||||
|
patch_mod_fold = 0 + 0.22 * lfo_sin(6, 0);
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0 + 2.2 * lfo_sin(6, 0);
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 44 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 44 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 44 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 1.5))) * (max(0.0, 1.2 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.45, 0.0001))) / ma.PI) - 1.0;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0.05 + patch_mod_noise + 0.0)) + no.noise * clip01(0.05 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.42 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.42 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0.18 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0.18 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.18 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.28 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.01 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.01 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.3, 0.7) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.2);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 88 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = select2(wrap01(os.phasor(1.0, ((max(20, 88 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 88 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 1.5))) * (max(0.0, 1.2 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.45, 0.0001))) / ma.PI) < clip01(0.36 + 0 * voice_1_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0.05 + patch_mod_noise + 0.0)) + no.noise * clip01(0.05 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.42 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.42 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0.18 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0.18 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.18 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.28 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.01 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.01 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.3, 0.7) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.09);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
65
tools/aqua-playground/lowered/wobble-bass-neuro.dsp
Normal file
65
tools/aqua-playground/lowered/wobble-bass-neuro.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "wobble-bass-neuro";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0 + 0.28 * lfo_hold(7, 0);
|
||||||
|
patch_mod_pitch = 0 + 0.04 * lfo_hold(7, 0);
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0 + 0.52 * lfo_hold(7, 0);
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0 + 0.1 * lfo_hold(7, 0);
|
||||||
|
patch_mod_drive = 0 + 0.25 * lfo_hold(7, 0);
|
||||||
|
patch_mod_fold = 0 + 0.3 * lfo_hold(7, 0);
|
||||||
|
patch_mod_formant_mix = 0.0;
|
||||||
|
patch_mod_fm_index = 0 + 2.8 * lfo_hold(7, 0);
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 49 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 49 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 49 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2.7))) * (max(0.0, 1.5 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.35, 0.0001))) / ma.PI) - 1.0;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0.04 + patch_mod_noise + 0.0)) + no.noise * clip01(0.04 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.38 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.38 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0.24 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0.24 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.24 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.25 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.04 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.04 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = voice_0_phased;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.28, 0.75) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.16);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 147 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = 1.0 - 4.0 * abs(wrap01(os.phasor(1.0, ((max(20, 147 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 147 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2.7))) * (max(0.0, 1.5 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.35, 0.0001))) / ma.PI) - 0.5);
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0.04 + patch_mod_noise + 0.0)) + no.noise * clip01(0.04 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.38 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.38 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0.24 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0.24 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.24 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.25 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.04 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.04 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = voice_1_phased;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.28, 0.75) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.06);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
65
tools/aqua-playground/lowered/wobble-bass-talker.dsp
Normal file
65
tools/aqua-playground/lowered/wobble-bass-talker.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "wobble-bass-talker";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0 + 0.42 * lfo_tri(4, 0);
|
||||||
|
patch_mod_pitch = 0.0;
|
||||||
|
patch_mod_duty = 0.0;
|
||||||
|
patch_mod_lpf = 0 + 0.48 * lfo_tri(4, 0);
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0 + 0.2 * lfo_tri(4, 0);
|
||||||
|
patch_mod_fold = 0 + 0.14 * lfo_tri(4, 0);
|
||||||
|
patch_mod_formant_mix = 0 + 0.38 * lfo_tri(4, 0);
|
||||||
|
patch_mod_fm_index = 0 + 1.6 * lfo_tri(4, 0);
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 55 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 55 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 55 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, 0.8 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.7, 0.0001))) / ma.PI) - 1.0;
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.3 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.3 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0.08 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0.08 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.08 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.34 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.02 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.02 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = ((voice_0_phased : fi.resonbp(520, 5.777778, 1.0)) * 0.7 + (voice_0_phased : fi.resonbp(1250, 7.352941, 1.0)) * 1 + (voice_0_phased : fi.resonbp(2600, 8.125, 1.0)) * 0.45) / 2.15;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0.35 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0.35 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.25, 0.8) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.18);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 110 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 110 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 110 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 2))) * (max(0.0, 0.8 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.7, 0.0001))) / ma.PI) - 1.0;
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.3 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.3 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0.08 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0.08 + patch_mod_fold + 0.0) * 3.5)) * clip01(0.08 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.34 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.02 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.02 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = ((voice_1_phased : fi.resonbp(520, 5.777778, 1.0)) * 0.7 + (voice_1_phased : fi.resonbp(1250, 7.352941, 1.0)) * 1 + (voice_1_phased : fi.resonbp(2600, 8.125, 1.0)) * 0.45) / 2.15;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0.35 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0.35 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.25, 0.8) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.08);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
65
tools/aqua-playground/lowered/wobble-bass-yoy.dsp
Normal file
65
tools/aqua-playground/lowered/wobble-bass-yoy.dsp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import("stdfaust.lib");
|
||||||
|
declare name "wobble-bass-yoy";
|
||||||
|
declare options "[nvoices:1]";
|
||||||
|
|
||||||
|
time = ba.time / ma.SR;
|
||||||
|
age = time;
|
||||||
|
clip01(x) = min(1.0, max(0.0, x));
|
||||||
|
wrap01(x) = x - floor(x);
|
||||||
|
slew(rate,x) = x : *(1.0 - exp((0.0 - max(0.0001, rate)) / ma.SR)) : + ~ *(exp((0.0 - max(0.0001, rate)) / ma.SR));
|
||||||
|
softclip(x) = ma.tanh(x * 1.35);
|
||||||
|
fold(x) = 2.0 * abs(2.0 * (x / 4.0 - floor(x / 4.0)) - 1.0) - 1.0;
|
||||||
|
release_start(a,d,g) = max(g, a + d);
|
||||||
|
oneshot_adsr_at(t,a,d,s,r,g) = select2(t < a, select2(t < a + d, select2(t < release_start(a,d,g), select2(t < release_start(a,d,g) + r, 0.0, s * (1.0 - (t - release_start(a,d,g)) / max(0.0001, r))), s), 1.0 - (1.0 - s) * ((t - a) / max(0.0001, d))), t / max(0.0001, a));
|
||||||
|
oneshot_adsr(a,d,s,r,g) = oneshot_adsr_at(age,a,d,s,r,g);
|
||||||
|
seg(t,t0,d,a,b) = a + (b - a) * clip01((t - t0) / max(0.0001, d));
|
||||||
|
bez(t,a,b,c,d) = (1.0 - t) * (1.0 - t) * (1.0 - t) * a + 3.0 * (1.0 - t) * (1.0 - t) * t * b + 3.0 * (1.0 - t) * t * t * c + t * t * t * d;
|
||||||
|
seg_bez(t,t0,d,a,b,c,e) = bez(clip01((t - t0) / max(0.0001, d)), a, b, c, e);
|
||||||
|
smooth01(x) = clip01(x) * clip01(x) * (3.0 - 2.0 * clip01(x));
|
||||||
|
seg_smooth(t,t0,d,a,b) = a + (b - a) * smooth01((t - t0) / max(0.0001, d));
|
||||||
|
seg_exp(t,t0,d,a,b) = exp(log(max(0.00001, a)) + (log(max(0.00001, b)) - log(max(0.00001, a))) * clip01((t - t0) / max(0.0001, d)));
|
||||||
|
seg_curve(c,t,t0,d,a,b) = select2(c < 0.5, seg_exp(t,t0,d,a,b), seg(t,t0,d,a,b));
|
||||||
|
rl_release_start(r1,r2,r3,g) = max(g, r1 + r2 + r3);
|
||||||
|
rl4_env_from_at(t,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = select2(t < r1, select2(t < r1 + r2, select2(t < r1 + r2 + r3, select2(t < rl_release_start(r1,r2,r3,g), select2(t < rl_release_start(r1,r2,r3,g) + r4, l4, seg_curve(c4, t, rl_release_start(r1,r2,r3,g), r4, l3, l4)), l3), seg_curve(c3, t, r1 + r2, r3, l2, l3)), seg_curve(c2, t, r1, r2, l1, l2)), seg_curve(c1, t, 0, r1, s0, l1));
|
||||||
|
rl4_env_at(t,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(t,0.0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env_from(s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_from_at(age,s0,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
rl4_env(r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g) = rl4_env_at(age,r1,l1,c1,r2,l2,c2,r3,l3,c3,r4,l4,c4,g);
|
||||||
|
lfo_sin(hz, phase) = sin(2.0 * ma.PI * (age * hz + phase));
|
||||||
|
lfo_tri(hz, phase) = 1.0 - 4.0 * abs((age * hz + phase - floor(age * hz + phase)) - 0.5);
|
||||||
|
lfo_sq(hz, phase) = select2((age * hz + phase - floor(age * hz + phase)) < 0.5, -1.0, 1.0);
|
||||||
|
lfo_hold(hz, phase) = no.noise : ba.latch(os.oscrs(hz));
|
||||||
|
|
||||||
|
patch_mod_gain = 0 + 0.5 * lfo_sq(5, 0);
|
||||||
|
patch_mod_pitch = 0 + 0.025 * lfo_sq(5, 0);
|
||||||
|
patch_mod_duty = 0 + 0.08 * lfo_sq(5, 0);
|
||||||
|
patch_mod_lpf = 0 + 0.5 * lfo_sq(5, 0);
|
||||||
|
patch_mod_hpf = 0.0;
|
||||||
|
patch_mod_noise = 0.0;
|
||||||
|
patch_mod_drive = 0.0;
|
||||||
|
patch_mod_fold = 0.0;
|
||||||
|
patch_mod_formant_mix = 0 + 0.45 * lfo_sq(5, 0);
|
||||||
|
patch_mod_fm_index = 0.0;
|
||||||
|
|
||||||
|
voice_0_age = age;
|
||||||
|
voice_0_freq = ((max(20, 62 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_0_osc = select2(wrap01(os.phasor(1.0, ((max(20, 62 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 62 * pow(2.0, 0 * voice_0_age + 0.5 * 0 * voice_0_age * voice_0_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 3))) * (max(0.0, 0.7 + patch_mod_fm_index + 0.0) * exp(-voice_0_age / max(0.5, 0.0001))) / ma.PI) < clip01(0.5 + 0 * voice_0_age + patch_mod_duty + 0.0), -0.5, 0.5);
|
||||||
|
voice_0_colored = (voice_0_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_0_driven = ma.tanh(voice_0_colored * (1.0 + clip01(0.25 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.25 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_0_folded = voice_0_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_0_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_0_filtered = voice_0_folded : fi.lowpass(1, max(20.0, clip01(0.3 * (1.0 + 0 * voice_0_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.03 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.03 * (1.0 + 0 * voice_0_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_0_phased = voice_0_filtered;
|
||||||
|
voice_0_formants = ((voice_0_phased : fi.resonbp(400, 5, 1.0)) * 0.6 + (voice_0_phased : fi.resonbp(900, 7.5, 1.0)) * 1 + (voice_0_phased : fi.resonbp(2100, 8.076923, 1.0)) * 0.4) / 2;
|
||||||
|
voice_0 = ((voice_0_phased * (1.0 - clip01(0.28 + patch_mod_formant_mix + 0.0)) + voice_0_formants * clip01(0.28 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_0_age, 0, 0, 1, 0.22, 0.65) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.16);
|
||||||
|
|
||||||
|
voice_1_age = age;
|
||||||
|
voice_1_freq = ((max(20, 124 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0);
|
||||||
|
voice_1_osc = 2.0 * wrap01(os.phasor(1.0, ((max(20, 124 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0)) + 0 + sin(2.0 * ma.PI * os.phasor(1.0, ((max(20, 124 * pow(2.0, 0 * voice_1_age + 0.5 * 0 * voice_1_age * voice_1_age)))) * 1.0 * pow(2.0, patch_mod_pitch + 0.0) * max(0.0, 3))) * (max(0.0, 0.7 + patch_mod_fm_index + 0.0) * exp(-voice_1_age / max(0.5, 0.0001))) / ma.PI) - 1.0;
|
||||||
|
voice_1_colored = (voice_1_osc * (1.0 - clip01(0 + patch_mod_noise + 0.0)) + no.noise * clip01(0 + patch_mod_noise + 0.0));
|
||||||
|
voice_1_driven = ma.tanh(voice_1_colored * (1.0 + clip01(0.25 + patch_mod_drive + 0.0) * 12.0)) / ma.tanh(1.0 + clip01(0.25 + patch_mod_drive + 0.0) * 12.0);
|
||||||
|
voice_1_folded = voice_1_driven * (1.0 - clip01(0 + patch_mod_fold + 0.0)) + fold(voice_1_driven * (1.0 + clip01(0 + patch_mod_fold + 0.0) * 3.5)) * clip01(0 + patch_mod_fold + 0.0);
|
||||||
|
voice_1_filtered = voice_1_folded : fi.lowpass(1, max(20.0, clip01(0.3 * (1.0 + 0 * voice_1_age * 1.8) + patch_mod_lpf + 0.0) * 18000.0)) : fi.highpass(1, max(5.0, (clip01(0.03 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * (clip01(0.03 * (1.0 + 0 * voice_1_age * 2.0) + patch_mod_hpf + 0.0)) * 7000.0));
|
||||||
|
voice_1_phased = voice_1_filtered;
|
||||||
|
voice_1_formants = ((voice_1_phased : fi.resonbp(400, 5, 1.0)) * 0.6 + (voice_1_phased : fi.resonbp(900, 7.5, 1.0)) * 1 + (voice_1_phased : fi.resonbp(2100, 8.076923, 1.0)) * 0.4) / 2;
|
||||||
|
voice_1 = ((voice_1_phased * (1.0 - clip01(0.28 + patch_mod_formant_mix + 0.0)) + voice_1_formants * clip01(0.28 + patch_mod_formant_mix + 0.0)) * oneshot_adsr_at(voice_1_age, 0, 0, 1, 0.22, 0.65) * max(0.0, 1.0 + patch_mod_gain + 0.0) * 0.07);
|
||||||
|
|
||||||
|
process = softclip((voice_0 + voice_1) * 1);
|
||||||
35
tools/aqua-playground/package-lock.json
generated
Normal file
35
tools/aqua-playground/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "aqua-playground",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "aqua-playground",
|
||||||
|
"devDependencies": {
|
||||||
|
"@grame/faustwasm": "0.20.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@grame/faustwasm": {
|
||||||
|
"version": "0.20.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@grame/faustwasm/-/faustwasm-0.20.0.tgz",
|
||||||
|
"integrity": "sha512-oxy1MgkGVMxmWdmpiP0OGFv9T0nUfYYINFMoYhErzCipdfkMYv35KgVg5OojHa9NgXD9leLlAB+IIzJZ0KoIQQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "LGPL-2.1",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/emscripten": "^1.39.10"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"faust2sndfile-ts": "scripts/faust2sndfile.js",
|
||||||
|
"faust2svg-ts": "scripts/faust2svg.js",
|
||||||
|
"faust2wasm-ts": "scripts/faust2wasm.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/emscripten": {
|
||||||
|
"version": "1.41.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.41.6.tgz",
|
||||||
|
"integrity": "sha512-uN+9i8bFT5CUcZfyIEYDrSueACEyKGbUs5kC/72DGlZZoinh84sJfVV0i8UOJD1asdzkvLPBRrKs41kZ8MdEXg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
tools/aqua-playground/package.json
Normal file
13
tools/aqua-playground/package.json
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "aqua-playground",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"description": "Static in-browser playground for AquaSynth patches: .aqua -> Faust (AquaLower) -> wasm (faustwasm) -> AudioWorklet.",
|
||||||
|
"scripts": {
|
||||||
|
"build": "node build.mjs",
|
||||||
|
"test": "node test.mjs"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@grame/faustwasm": "0.20.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
49
tools/aqua-playground/test.mjs
Normal file
49
tools/aqua-playground/test.mjs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Headless check of the built site (dist/): every catalog patch compiles to wasm through the same
|
||||||
|
// faustwasm runtime the browser loads, exposes controls, and renders audible, finite samples.
|
||||||
|
// It measures non-silence and NaN only; it cannot hear anything. Usage: node build.mjs && node test.mjs
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { dirname, join, resolve } from "node:path";
|
||||||
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||||
|
|
||||||
|
const dist = resolve(process.argv[2] ?? join(dirname(fileURLToPath(import.meta.url)), "dist"));
|
||||||
|
const { instantiateFaustModuleFromFile, LibFaust, FaustCompiler, FaustMonoDspGenerator } =
|
||||||
|
await import(pathToFileURL(join(dist, "vendor/faustwasm/index.js")).href);
|
||||||
|
const { controlsOf } = await import(pathToFileURL(join(dist, "ui.js")).href);
|
||||||
|
|
||||||
|
const libfaust = new LibFaust(await instantiateFaustModuleFromFile(join(dist, "vendor/libfaust-wasm/libfaust-wasm.js")));
|
||||||
|
const compiler = new FaustCompiler(libfaust);
|
||||||
|
const catalog = JSON.parse(readFileSync(join(dist, "catalog.json"), "utf8"));
|
||||||
|
const SR = 48000, SECONDS = 2;
|
||||||
|
let failed = 0;
|
||||||
|
console.log(`libfaust ${libfaust.version()}; ${catalog.length} patches; ${SECONDS} s at ${SR} Hz`);
|
||||||
|
|
||||||
|
function stats(out) {
|
||||||
|
let peak = 0, bad = 0;
|
||||||
|
for (const ch of out) for (const v of ch) { if (!Number.isFinite(v)) bad++; else peak = Math.max(peak, Math.abs(v)); }
|
||||||
|
return { peak, bad };
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const e of catalog) {
|
||||||
|
const g = new FaustMonoDspGenerator();
|
||||||
|
const ok = await g.compile(compiler, e.name.replace(/[^A-Za-z0-9_]/g, "_"), readFileSync(join(dist, e.dsp), "utf8"), "-ftz 2");
|
||||||
|
if (!ok) { failed++; console.log(`FAIL ${e.name}: compile: ${compiler.getErrorMessage()}`); continue; }
|
||||||
|
const controls = controlsOf(JSON.parse(g.factory.json));
|
||||||
|
const { peak, bad } = stats((await g.createOfflineProcessor(SR, 512)).render(null, SR * SECONDS));
|
||||||
|
const problems = [];
|
||||||
|
if (bad) problems.push(`${bad} non-finite samples`);
|
||||||
|
if (!(peak > 1e-4)) problems.push(`silent (peak ${peak})`);
|
||||||
|
if (e.name === "thruster") {
|
||||||
|
if (!controls.some((c) => c.label === "rpm")) problems.push("no rpm control in the DSP UI");
|
||||||
|
for (const rpm of [600, 4800]) {
|
||||||
|
const p = await g.createOfflineProcessor(SR, 512);
|
||||||
|
p.setParamValue(controls.find((c) => c.label === "rpm").address, rpm);
|
||||||
|
const s = stats(p.render(null, SR * SECONDS));
|
||||||
|
console.log(` thruster rpm=${rpm}: peak ${s.peak.toFixed(4)}, non-finite ${s.bad}`);
|
||||||
|
if (s.bad || !(s.peak > 1e-4)) problems.push(`rpm ${rpm}: peak ${s.peak}, non-finite ${s.bad}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
failed += problems.length ? 1 : 0;
|
||||||
|
console.log(`${problems.length ? "FAIL" : "ok "} ${e.name}: peak ${peak.toFixed(4)}${peak > 1 ? " (CLIPS)" : ""}, ${controls.length} controls${problems.length ? " :: " + problems.join("; ") : ""}`);
|
||||||
|
}
|
||||||
|
console.log(failed ? `${failed} FAILED` : "all passed");
|
||||||
|
process.exit(failed ? 1 : 0);
|
||||||
91
tools/aqua-playground/web/app.js
Normal file
91
tools/aqua-playground/web/app.js
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { instantiateFaustModuleFromFile, LibFaust, FaustCompiler, FaustMonoDspGenerator } from "./vendor/faustwasm/index.js";
|
||||||
|
import { controlsOf } from "./ui.js";
|
||||||
|
|
||||||
|
const $ = (id) => document.getElementById(id);
|
||||||
|
const status = (t) => { $("status").textContent = t; };
|
||||||
|
let compiler, ctx, node, gain, analyser, generator, entry;
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
const catalog = await (await fetch("./catalog.json")).json();
|
||||||
|
for (const e of catalog) $("patch").add(new Option(`${e.name}${e.kind === "faust" ? " (hand-written Faust)" : ""}`, e.name));
|
||||||
|
compiler = new FaustCompiler(new LibFaust(await instantiateFaustModuleFromFile("./vendor/libfaust-wasm/libfaust-wasm.js")));
|
||||||
|
window.aquaCatalog = catalog;
|
||||||
|
$("patch").onchange = () => load(catalog.find((e) => e.name === $("patch").value));
|
||||||
|
$("play").onclick = play; $("stop").onclick = stop;
|
||||||
|
$("recompile").onclick = () => compile($("source").value);
|
||||||
|
$("volume").oninput = () => { if (gain) gain.gain.value = Number($("volume").value); };
|
||||||
|
const start = new URLSearchParams(location.search).get("patch");
|
||||||
|
const first = catalog.find((e) => e.name === start) ?? catalog[0];
|
||||||
|
$("patch").value = first.name;
|
||||||
|
await load(first);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function load(e) {
|
||||||
|
stop();
|
||||||
|
entry = e;
|
||||||
|
$("origin").textContent = `from ${e.origin}`;
|
||||||
|
$("source").value = await (await fetch(`./${e.dsp}`)).text();
|
||||||
|
await compile($("source").value);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compile(code) {
|
||||||
|
stop();
|
||||||
|
status("Compiling Faust to wasm…");
|
||||||
|
const t0 = performance.now();
|
||||||
|
const g = new FaustMonoDspGenerator();
|
||||||
|
const ok = await g.compile(compiler, entry.name.replace(/[^A-Za-z0-9_]/g, "_"), code, "-ftz 2");
|
||||||
|
if (!ok) { status(`Compile failed: ${compiler.getErrorMessage()}`); $("play").disabled = true; return; }
|
||||||
|
generator = g;
|
||||||
|
$("play").disabled = false;
|
||||||
|
const controls = controlsOf(JSON.parse(g.factory.json));
|
||||||
|
buildControls(controls);
|
||||||
|
status(`Compiled in ${Math.round(performance.now() - t0)} ms; ${controls.length} controls. Press Play.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildControls(controls) {
|
||||||
|
const box = $("controls"); box.replaceChildren();
|
||||||
|
let group = null;
|
||||||
|
for (const c of controls) {
|
||||||
|
if (c.group !== group) { group = c.group; const h = document.createElement("div"); h.className = "grp"; h.textContent = group; box.append(h); }
|
||||||
|
const row = document.createElement("div"); row.className = "row";
|
||||||
|
const label = document.createElement("label"); label.textContent = c.label; label.title = c.address;
|
||||||
|
const input = document.createElement("input");
|
||||||
|
const out = document.createElement("output");
|
||||||
|
if (c.type === "checkbox" || c.type === "button") { input.type = "checkbox"; input.checked = c.init > 0; }
|
||||||
|
else { input.type = "range"; input.min = c.min; input.max = c.max; input.step = c.step; input.value = c.init; }
|
||||||
|
const show = () => { out.textContent = input.type === "checkbox" ? (input.checked ? "on" : "off") : Number(input.value).toFixed(c.step < 0.01 ? 4 : c.step < 1 ? 2 : 0); };
|
||||||
|
input.oninput = () => { show(); node?.setParamValue(c.address, input.type === "checkbox" ? Number(input.checked) : Number(input.value)); };
|
||||||
|
input.dataset.address = c.address; show();
|
||||||
|
row.append(label, input, out); box.append(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function play() {
|
||||||
|
stop();
|
||||||
|
ctx ??= new AudioContext({ latencyHint: "interactive" });
|
||||||
|
await ctx.resume();
|
||||||
|
// A fresh node each press: one-shot .aqua patches run on a clock that starts at instantiation.
|
||||||
|
node = await generator.createNode(ctx);
|
||||||
|
if (!node) { status("createNode failed"); return; }
|
||||||
|
for (const input of $("controls").querySelectorAll("input")) input.dispatchEvent(new Event("input"));
|
||||||
|
gain = ctx.createGain(); gain.gain.value = Number($("volume").value);
|
||||||
|
analyser = ctx.createAnalyser(); analyser.fftSize = 1024;
|
||||||
|
node.connect(gain); gain.connect(analyser); analyser.connect(ctx.destination);
|
||||||
|
status(`Playing ${entry.name} at ${ctx.sampleRate} Hz.`);
|
||||||
|
meter();
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop() {
|
||||||
|
if (node) { node.disconnect(); node.destroy?.(); node = null; }
|
||||||
|
gain?.disconnect(); analyser?.disconnect(); gain = analyser = null;
|
||||||
|
$("level").value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function meter() {
|
||||||
|
if (!analyser) return;
|
||||||
|
const buf = new Float32Array(analyser.fftSize); analyser.getFloatTimeDomainData(buf);
|
||||||
|
$("level").value = buf.reduce((m, v) => Math.max(m, Math.abs(v)), 0);
|
||||||
|
requestAnimationFrame(meter);
|
||||||
|
}
|
||||||
|
|
||||||
|
init().catch((err) => { status(`Failed to start: ${err}`); console.error(err); });
|
||||||
38
tools/aqua-playground/web/index.html
Normal file
38
tools/aqua-playground/web/index.html
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>AquaSynth Playground</title>
|
||||||
|
<link rel="stylesheet" href="./styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<header>
|
||||||
|
<p class="eyebrow">AquaSynth lab</p>
|
||||||
|
<h1>Patch Playground</h1>
|
||||||
|
<p class="lede">An AquaSynth <code>.aqua</code> patch is lowered to Faust, compiled to WebAssembly in this page, and played through an AudioWorklet. Nothing here is rendered on a server.</p>
|
||||||
|
</header>
|
||||||
|
<section class="bar">
|
||||||
|
<label>Patch <select id="patch"></select></label>
|
||||||
|
<button id="play" type="button">Play</button>
|
||||||
|
<button id="stop" type="button">Stop</button>
|
||||||
|
<label>Volume <input id="volume" type="range" min="0" max="1" step="0.01" value="0.5"></label>
|
||||||
|
<meter id="level" min="0" max="1" value="0" title="output peak"></meter>
|
||||||
|
<span id="status" role="status">Loading the Faust compiler…</span>
|
||||||
|
</section>
|
||||||
|
<section class="cols">
|
||||||
|
<div>
|
||||||
|
<h2>Controls <small>(from the DSP's own UI)</small></h2>
|
||||||
|
<div id="controls" class="controls"></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Lowered Faust source <small id="origin"></small></h2>
|
||||||
|
<textarea id="source" spellcheck="false"></textarea>
|
||||||
|
<button id="recompile" type="button">Recompile edited source</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<script type="module" src="./app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
19
tools/aqua-playground/web/styles.css
Normal file
19
tools/aqua-playground/web/styles.css
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
:root { color-scheme: dark; --bg:#0b1418; --fg:#d7e6ea; --dim:#7f9aa3; --acc:#3fc1c9; --line:#1f3640; }
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin:0; background:var(--bg); color:var(--fg); font:15px/1.45 system-ui, sans-serif; }
|
||||||
|
main { max-width:1200px; margin:0 auto; padding:24px; }
|
||||||
|
.eyebrow { margin:0; color:var(--acc); text-transform:uppercase; letter-spacing:.12em; font-size:12px; }
|
||||||
|
h1 { margin:.1em 0 .2em; } h2 { font-size:16px; margin:0 0 8px; } small { color:var(--dim); font-weight:400; }
|
||||||
|
.lede { color:var(--dim); max-width:70ch; }
|
||||||
|
.bar { display:flex; flex-wrap:wrap; gap:12px 16px; align-items:center; padding:12px 0; border-block:1px solid var(--line); margin-bottom:16px; }
|
||||||
|
button, select { background:#10242b; color:var(--fg); border:1px solid var(--line); padding:6px 14px; border-radius:4px; font:inherit; }
|
||||||
|
button:hover { border-color:var(--acc); } button:disabled { opacity:.4; }
|
||||||
|
#status { color:var(--dim); }
|
||||||
|
.cols { display:grid; grid-template-columns:minmax(280px,1fr) minmax(320px,1.4fr); gap:24px; }
|
||||||
|
@media (max-width:800px) { .cols { grid-template-columns:1fr; } }
|
||||||
|
.controls { display:grid; gap:6px; max-height:70vh; overflow:auto; padding-right:8px; }
|
||||||
|
.controls .row { display:grid; grid-template-columns:minmax(0,1fr) 120px 58px; gap:8px; align-items:center; }
|
||||||
|
.controls .grp { color:var(--dim); font-size:12px; margin-top:8px; }
|
||||||
|
.controls label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||||
|
.controls output { text-align:right; color:var(--acc); font-variant-numeric:tabular-nums; }
|
||||||
|
textarea { width:100%; height:62vh; background:#071013; color:#bcd3d8; border:1px solid var(--line); border-radius:4px; font:12px/1.4 ui-monospace, monospace; padding:8px; resize:vertical; }
|
||||||
17
tools/aqua-playground/web/ui.js
Normal file
17
tools/aqua-playground/web/ui.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Flattens a Faust DSP JSON `ui` tree (factory.json) into the controls a slider panel needs.
|
||||||
|
// Shared by the browser app and the node test so both read the DSP's own UI declaration.
|
||||||
|
export function controlsOf(json) {
|
||||||
|
const out = [];
|
||||||
|
const walk = (items, group) => {
|
||||||
|
for (const it of items ?? []) {
|
||||||
|
if (it.items) walk(it.items, it.label ? [...group, it.label] : group);
|
||||||
|
else if (/slider|nentry|checkbox|button/.test(it.type))
|
||||||
|
out.push({
|
||||||
|
address: it.address, label: it.label, group: group.join(" / "), type: it.type,
|
||||||
|
init: Number(it.init ?? 0), min: Number(it.min ?? 0), max: Number(it.max ?? 1), step: Number(it.step ?? 0.01),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
walk(json.ui, []);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
1
tools/aqua-trial/.gitignore
vendored
Normal file
1
tools/aqua-trial/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
dist/
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue