Compare commits

...
Sign in to create a new pull request.
Author SHA1 Message Date
01719538f1 Merge pull request 'aqua-trial: add the v5 thruster mix as a variant (unheard, browser-unrun)' (#21) from aqua/trial-v5-variant into main 2026-11-10 14:03:15 +00:00
8744975094 Merge pull request 'aqua-feel: diff faustwasm against the native libfaust path (unheard)' (#20) from aqua/aqua-feel-native-diff into main 2026-11-10 14:01:40 +00:00
Aqua
46188d13a2 native-diff: fix report and header text found in review (tolerance wording, 13 sources, dx7-32 values, stray table cells)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 14:01:20 +00:00
Aqua
e0f3f18dd8 aqua-trial: add the v5 thruster mix (c6561da) as a variant against the current mix
The 2026-11-05 aqua-feel edit cut the idle rasp and rattle floors and opened the
rumble low-pass at low load, which narrowed the noise sweep. thruster-trial.dsp gains
a `mix` control (0 = current thruster.dsp, 1 = the v5 mix as cut) and the trial gains
the variant v5noise100: five variants, ten comparisons. thruster-v5-c6561da.dsp is the
old file, a fixture: test.mjs asserts mix=1 equals it sample for sample and mix=0 still
equals patches/thruster/thruster.dsp. analyze.mjs prints mix beside noise%.
Never run in a browser; nothing has been heard.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 13:57:58 +00:00
Aqua
4e6a64b311 native-diff: stdlib identity, 12 smoother sources under three smoothers, tolerance
Record which faustlibraries text each host compiles against (Starfire
2.85.5 has the stalling smooth; the sandbox master has imp(3)). Add
--items smoother and --smoother old|imp3 (same rewritten source on both
sides) and limits fit to the measured deltas; control matching normalises
the leading slash. Report extended; faustlibraries pin proposed, not made.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 06:44:09 +00:00
Aqua
878f91c062 native-diff: correct the cause (stdlib text, not the host) and harden the tool
A Soul pass found the two sides compile against different stdlib text:
the wasm bundle's si.smooth stalls in float32, the sandbox's faustlibraries
master does not. Reword the report, README and render.js; fail on length
mismatch and render errors; check output count; match controls exactly;
default output under the temp dir.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 06:31:54 +00:00
Aqua
e849c624f9 aqua-feel: diff faustwasm against the native libfaust path
Add FaustNativeRender and native-diff.mjs; render thruster, two sfxr
patches and a tract vowel through both and record the numbers.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 06:23:08 +00:00
91bea79b30 Merge pull request 'StoreFileName tests: device lookalikes and IPA pair distinctness' (#15) from aqua/store-filename-hardening into main 2026-11-07 11:52:47 +00:00
Aqua
aec15751cc StoreFileName tests: device lookalikes and IPA pair distinctness
Adds CON.tar.gz to the device-stem cases, a lookalike theory (trailing
space, superscript digit, accented letter, trailing dot) and a 6x6 IPA
round grid whose ids and script paths must stay distinct. Tests only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 11:51:44 +00:00
84f892fc99 Merge pull request 'IpaGestureExperiment onto StoreFileName (stacked on #11)' (#14) from aqua/store-filename-ipa into main 2026-11-07 10:39:13 +00:00
d11872e815 Merge pull request 'One StoreFileName in Core: no id collisions, no '..' escape' (#11) from aqua/safe-filename-collide-dotdot into main 2026-11-07 10:39:10 +00:00
Aqua
5a77dc8e83 AquaLower names lowered .dsp files through StoreFileName; pin Windows forms; note limits
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 10:38:28 +00:00
Aqua
30b926c776 Merge branch 'aqua/safe-filename-collide-dotdot' into aqua/store-filename-ipa
# Conflicts:
#	tests/AquaSynth.Dsl.Tests/StoreFileNameTests.cs
2026-11-07 10:33:56 +00:00
Aqua
80ebf39033 StoreFileName.Combine: batch ids from the command line cannot leave the artifact root
IpaTrialWorker song-score took --batch-id raw into Path.Combine; the orchestrator
sites share the new Combine. Tests cover traversal and absolute batch ids and the
underscore on device stems (nul.x, LPT1.x); drop the double From on the emitter name.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 10:33:47 +00:00
Aqua
b906d98ed5 Merge remote-tracking branch 'origin/aqua/safe-filename-collide-dotdot' into aqua/store-filename-ipa 2026-11-07 09:54:00 +00:00
Aqua
afd0cc9ec3 StoreFileName: device stems no longer survive as the stem; IpaTrialWorker paths use it
"nul.x" was rewritten to "nul.x-<hash>", whose stem is still NUL on Windows;
rewritten device names now get a leading underscore, and the test asserts the
stem is not reserved. IpaTrialWorker candidate directories go through
StoreFileName.From; its remaining SafeName only slugs uuid-suffixed record ids.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 09:54:00 +00:00
Aqua
4865830d82 IpaGestureExperiment: make the target/variant candidate id injective
("a","b-c") and ("a-b","c") both joined to "a-b-c" before the map and wrote the
same files. Each part goes through StoreFileName.From first and the pair is
joined with '/', which no mapped part can contain.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 09:54:00 +00:00
Aqua
4a3921c1a9 Merge remote-tracking branch 'origin/aqua/safe-filename-collide-dotdot' into aqua/store-filename-ipa 2026-11-07 09:29:55 +00:00
Aqua
de754b5b8c StoreFileName: route IpaTrialOrchestrator and FaustCompiler through it; daemon test uses ".."
Nibu's review at abfc816: the daemon test passed against the old sanitizer
because "../../escape" never escaped there; the bare ".." did. IpaTrialOrchestrator
(SafeName, raw BatchId in Path.Combine) and FaustCompiler.SafeArtifactName were
second owners of the same decision.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 09:29:43 +00:00
Aqua
28e37f5cbb IpaGestureExperiment: use StoreFileName, delete the last SafeFileName copy
Round and candidate ids in the IPA experiment bundle went through a private
sanitiser that mapped distinct ids ("a/b", "a-b") to one file and accepted
".." as a round id. They now go through the shared StoreFileName.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 06:35:25 +00:00
Aqua
81362a7013 Merge remote-tracking branch 'origin/main' into aqua/store-filename-ipa 2026-11-07 06:33:52 +00:00
d6252dd6a5 Merge pull request 'Spike: AOT-compile thruster v5 to a native lib with bounded rpm (Linux; Windows/Unity unverified)' (#13) from aqua/faust-aot-thruster-spike into main 2026-11-07 06:33:47 +00:00
Aqua
dda7a4f26b spike: README reports 74 of 400000 as a single run's figure
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 05:33:49 +00:00
Aqua
a42cc13e5f spike: reword to Nibu's #13 review (no preemption reading, cheap-on-this-host, control counts)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 04:28:50 +00:00
c136e8fea0 Merge pull request 'Scoping: Faust DSP (thruster v5) in a Unity player on Windows and Linux' (#12) from aqua/faust-unity-player-scoping into main 2026-11-07 02:24:21 +00:00
Aqua
e4c6b2d3b3 spike: pin cores, per-overrun wall timestamps, noop control; max block time marked unexplained
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 01:24:21 +00:00
Aqua
489405a377 Merge branch 'aqua/faust-unity-player-scoping' into aqua/faust-aot-thruster-spike 2026-11-07 01:24:21 +00:00
Aqua
9c3ae654ca docs: mark spike max block time unexplained; spike is a bare C loop, not a player
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 01:24:09 +00:00
Aqua
5d724f5f5b spike: drop stray generated header (run.sh generates it into build/)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 00:21:17 +00:00
Aqua
22af1e935e spike: AOT-compile thruster v5 to a C-ABI native lib with bounded rpm, test, timing script
Linux only; Windows/Unity unverified.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 00:21:17 +00:00
Aqua
ecf381e714 docs: apply review 824 to Faust Unity scoping
Restate the AOT limit in Recommendation and Unverified list, drop the unsourced
"browser lane", and make the p99.9/max pass lines Aqua's ruling with the
quiet-host caveat for the max line.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-07 00:21:10 +00:00
Aqua
0efce042f3 docs: state the AOT patch-set limitation up front
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 23:19:35 +00:00
Aqua
448e3b04b0 docs: scope Faust DSP delivery to a Unity player (Windows/Linux)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 22:17:59 +00:00
Aqua
abfc816804 StoreFileName: close review findings (collisions, case, device stems, length, renderId)
- Pass-through ids may not end in "-<16 hex>", so a rewritten name fed back as an id cannot alias it; hash widened 32 -> 64 bits.
- Ids with uppercase are rewritten (case-insensitive filesystems).
- Windows device stems (CON PRN AUX NUL COM0-9 LPT0-9, any extension) are rewritten.
- Stem capped at 100 chars; longer ids carry the hash.
- Daemon samples/ write now goes through StoreFileName.From(renderId).
- Tests: rewritten-as-id, case-only, device stems, overlong; daemon test uses a compiling patch so the samples/ write is reached when Faust is present.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 20:00:30 +00:00
Aqua
396da106cd One StoreFileName in Core: no id collisions, no '..' escape
The daemon and native-runtime SafeFileName copies sanitised differently:
the daemon mapped a/b, a b, a:b and a-b to the same name (a later patch
overwrote an earlier artifact) and returned ".." unchanged, so a
sessionId or runId of ".." resolved one directory above the store.

Replace both with AquaSynth.Dsl.StoreFileName.From. Ids within
[A-Za-z0-9._-] pass through; any other id is sanitised and suffixed with
the first 8 hex of SHA-256 of the original. Empty, "." and all-dot ids
are refused (ArgumentException).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 18:29:25 +00:00
df21ea7bea Merge pull request 'Earcon Workshop: static earcon family generator with WAV/JSON export (unheard)' (#9) from aqua/earcon-workshop into main 2026-11-06 05:36:47 +00:00
Aqua
33a4ccc0f2 earcon-workshop: review fixes (check count 882, lowpass comment says two poles)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 05:36:46 +00:00
Aqua
0c9708e376 Add Earcon Workshop: static earcon family generator, WAV/zip/JSON pack, node check
Six events x four Dings-derived identities, pure-JS deterministic renderer shared by
page and node, five sliders, offline export. Unheard: signal checks only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 02:07:26 +00:00
fa1bf3fdbc Merge pull request 'aqua-feel: headless feel meter + gate; fix PRC SYNTH1 clipping and thruster low-rpm hiss' (#8) from aqua/aqua-feel into main 2026-11-05 15:44:33 +00:00
Aqua
d4dce3ec0c aqua-feel: keep the held envelope for attack only; activeEnd, crest and the plot use the raw one
The 30 ms release hold inflated activeEnd by up to ~170 ms on percussive items and
shifted crest, rms and noise-to-tone (808-hat activeMs 61 -> 187). Found in the second
review pass; values return to the first run's. Doc cell for PRC attackMs corrected.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 15:44:32 +00:00
Aqua
1bf99e5c3f aqua-feel: review fixes (attack on low tones, disarmed guards, bounded waivers, drift test)
- attack: peak-hold envelope and a 150 ms window; a 261 Hz tone no longer reads its
  first half-cycle as the attack (warm-bell 0.67 ms -> 3.8 ms, declared 4 ms).
- gate: a rule whose id matches no item fails a full run; waivers carry a bound and
  cover only up to it; a missing metric fails its rule; silent items fail once;
  --only runs gate as partial; silent threshold is -70 dBFS.
- DingCatalogTests: lowered Dings fed to the meter must match the catalog.
- README states NTR window/Q dependence, short-buffer loudness padding and the assumed
  Ding command gain; faust-toolchain-boundary notes the v5 manifest predates the mix change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 15:42:13 +00:00
Aqua
eb35ca545d Add aqua-feel: headless feel meter and gate; fix PRC SYNTH1 clipping and thruster low-rpm hiss
tools/aqua-feel renders every playground patch (thruster at four rpm) and every Ding
event on every instrument offline through faustwasm, measures onset latency, attack,
crest, true peak and clipping, BS.1770 loudness, centroid/flux over time, noise-to-tone
ratio (flat and A-weighted) and a low-motion flag, writes report.json plus a static
gallery, and gates against thresholds.json (waivers must cite a follow-up and go stale).
Nothing is heard; metrics are evidence, not verdicts.

Uses it: PRC SYNTH1 calibrated peaked at 2.10 (18,633 overs); shipped patch gain 1.4 to
0.45, peak 0.67. Thruster low-rpm A-weighted noise-to-tone 20.7/23.0 dB at rpm 300/600
down to 13.1/5.0 by cutting the idle rasp and rattle floors and opening the rumble
low-pass at low load. Trial copy mirrored; its README notes the narrower sweep.

AquaLower gains --dings to lower DingCatalog instruments and events.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 15:31:35 +00:00
d8c625c532 Merge pull request 'Docs: soundscape grammar for the Ratatoskr generative symphony (draft)' (#4) from aqua/docs/soundscape-grammar into main 2026-11-05 11:54:51 +00:00
810b44db9e Merge pull request 'Faust on Linux: FaustRender probe, toolchain notes, thruster v5 manifest' (#5) from aqua/faust-linux-render into main 2026-11-05 11:54:44 +00:00
5f85effc07 Merge pull request 'Listening trial: blinded A/B thruster noise sweep (static page)' (#7) from aqua/listening-trial into main 2026-11-05 11:54:39 +00:00
c1d2a9468e Merge pull request 'Static in-browser playground (faustwasm) for AquaSynth patches' (#6) from aqua/playground into main 2026-11-05 11:54:38 +00:00
4b965f39b5 Merge pull request 'Add thruster Faust patch with an rpm control' (#3) from aqua/thruster-patch into main 2026-11-05 11:54:38 +00:00
b4d203ddcb Merge pull request 'Faust SetControls: remove per-call allocation' (#2) from aqua/setcontrols-no-alloc into main 2026-11-05 11:54:37 +00:00
Aqua
46da9975df Playground README: state plainly that it is unheard and unrun in a browser
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 11:46:43 +00:00
Aqua
d348a19ddd Docs: soundscape grammar names the Mimir harvest follow-up it could not see
The follow-up is in force in Eureka's mind and names Mimir's bioacoustic
research; the draft's earlier grep missed it. Section 4 stays recalled, not
re-read, and the harvest stays owed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 11:46:38 +00:00
Aqua
45f62277f0 Rename logic.mjs to logic.js so static hosts serve a JavaScript MIME type
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 21:34:34 +00:00
Aqua
7593917e32 Add blinded A/B listening trial page for the thruster noise sweep
Static page, trial patch (thruster + noise control), shared logic, dependency-free analyzer, node tests.
Never run in a browser; nothing has been heard.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 21:34:18 +00:00
Aqua
6080f9b8f1 Playground: rename ui.mjs to ui.js so static hosts serve a JavaScript MIME type
The minds host sends .mjs as application/octet-stream, which browsers refuse for module imports.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 13:39:11 +00:00
Aqua
39843ae95c Add static in-browser playground for AquaSynth patches
tools/AquaLower lowers .aqua to Faust (FaustEmitter.EmitScript); tools/aqua-playground compiles
the lowered Faust to wasm with @grame/faustwasm in the page and plays it through an AudioWorklet,
with sliders from the DSP's UI and the Faust source shown. Thruster plus 41 .aqua patches.
Headless node test compiles and renders every patch (non-silent, finite).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 13:38:49 +00:00
Aqua
b7a5142555 Note sweep render duration explains the 10/31 hash difference
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 09:56:06 +00:00
Aqua
ceaa597420 Add FaustRender probe, Linux toolchain notes and thruster v5 manifest
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-03 09:37:02 +00:00
Aqua
ab9bcdc6b7 Docs: soundscape grammar for the Ratatoskr generative symphony (draft)
One typed activity frame drives both orbit visuals and voices. Grammar only;
no notes, nothing rendered or listened to. Marks the two inputs that could not
be read (creature-projection-v2, Mimir harvest).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 12:30:12 +00:00
Aqua
c6561da8d4 thruster: v5 gain term 0.9-0.5*load plus 0.425 output trim
Peak under 0.4 at rpm 600, rpm 4800 and the 600-4800 sweep.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 20:54:51 +00:00
Aqua
882b7203f5 thruster: lower hull-mode Q and soft-limit the rattle so high rpm is louder than low rpm
Hull modes Q 40/35/30/25 -> 20/18/16/14; tanh on the rattle alone; drive floor 0.1 -> 0.35.
Linux render, 48 kHz, 512-frame blocks: rpm 4800 steady RMS 0.1252 > rpm 600 0.0850; peaks 0.216/0.354/0.325.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 20:48:55 +00:00
Aqua
e567b44c24 thruster: drive falls with load so tanh no longer pins the peak at high rpm
Drive 2.2 -> max(0.1, 0.9 - load); rasp load weight 0.6 -> 1.5.
rpm=4800 peak 0.500 -> 0.358.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 20:45:51 +00:00
Aqua
36ff76f03a Add thruster Faust patch with an rpm control
Sustained heavy thruster: low rotor rumble, blade-chopped noise rasp,
high-Q hull rattle modes, tanh clip. Compiled as Faust source through
CompileSource; not a .aqua script and not indexed in library.yaml.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 20:42:06 +00:00
Aqua
aa45571b3a Faust SetControls: write control zones without allocating
Marshal.Copy([value], ...) allocated a float[1] per control and the
IReadOnlyDictionary foreach boxed an enumerator per call. Write the float
bits straight to the zone and use Dictionary's struct enumerator when the
caller passes one. 10000-block smoke on a 2-control patch: 120 B/block -> 0.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-31 20:36:47 +00:00
3b43b7bd72 Merge pull request 'Faust loader: platform-aware library lookup and AQUASYNTH_FAUST_LIBRARY override' (#1) from faust-loader-platform-aware into main 2026-10-31 09:40:59 +00:00
127 changed files with 10600 additions and 61 deletions

1
.gitignore vendored
View file

@ -18,3 +18,4 @@ NuGet.Local.Config
_tmp_*.txt _tmp_*.txt
*.user *.user
*.suo *.suo
tools/aqua-feel/out/

View file

@ -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.

View 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.

View 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).

View file

@ -0,0 +1,278 @@
{
"libfaustWasm": "2.90.19",
"sampleRate": 48000,
"items": "core",
"smoother": "library",
"rows": [
{
"id": "thruster@rpm300",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.026072267442941666,
"maxErrAt": 142707,
"rmsErr": 0.002385837227503057,
"relRmsErrDb": -28.770505442344437,
"wasm": {
"samplePeak": 0.14264661073684692,
"truePeakDb": -16.90120405295226,
"loudnessLufs": -30.226833288468896,
"centroidMedianHz": 751.9185607592701,
"overSamples": 0,
"rmsDb": -23.676437109698263,
"crestDb": 6.761666256511239,
"noiseToToneDb": -6.937988717513285,
"noiseToToneADb": 13.092512355240387,
"lowMotion": false
},
"native": {
"samplePeak": 0.14242461323738098,
"truePeakDb": -16.915261179402926,
"loudnessLufs": -30.216031113774466,
"centroidMedianHz": 751.528437557621,
"overSamples": 0,
"rmsDb": -23.670746205832152,
"crestDb": 6.74244718138185,
"noiseToToneDb": -6.888378522251635,
"noiseToToneADb": 13.056342933565631,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "thruster@rpm600",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.049423644319176674,
"maxErrAt": 142945,
"rmsErr": 0.004812282166272209,
"relRmsErrDb": -23.51217894411906,
"wasm": {
"samplePeak": 0.15958738327026367,
"truePeakDb": -15.926835268189564,
"loudnessLufs": -28.69474712970973,
"centroidMedianHz": 1227.9568144573402,
"overSamples": 0,
"rmsDb": -22.840558133782523,
"crestDb": 6.900529208325618,
"noiseToToneDb": -0.7035305515542589,
"noiseToToneADb": 5.044756311984671,
"lowMotion": false
},
"native": {
"samplePeak": 0.1601162701845169,
"truePeakDb": -15.90057398970821,
"loudnessLufs": -28.709339901369557,
"centroidMedianHz": 1224.4350816353904,
"overSamples": 0,
"rmsDb": -22.846021449137904,
"crestDb": 6.934730747403392,
"noiseToToneDb": -0.6166079485198495,
"noiseToToneADb": 6.053873560512358,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "thruster@rpm1200",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.08972565084695816,
"maxErrAt": 143228,
"rmsErr": 0.012448641233006897,
"relRmsErrDb": -16.610480431926955,
"wasm": {
"samplePeak": 0.19161102175712585,
"truePeakDb": -14.343339234769212,
"loudnessLufs": -26.051033400616078,
"centroidMedianHz": 2114.222071076734,
"overSamples": 0,
"rmsDb": -21.48680915202533,
"crestDb": 7.1352188868162605,
"noiseToToneDb": -4.949059402813134,
"noiseToToneADb": 12.652391835948997,
"lowMotion": false
},
"native": {
"samplePeak": 0.1893547624349594,
"truePeakDb": -14.423961460001248,
"loudnessLufs": -26.05070520432609,
"centroidMedianHz": 2115.6363534197994,
"overSamples": 0,
"rmsDb": -21.484457233344667,
"crestDb": 7.029981882815694,
"noiseToToneDb": -4.965083035181173,
"noiseToToneADb": 12.716143920958068,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "thruster@rpm4800",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.2870774045586586,
"maxErrAt": 142979,
"rmsErr": 0.054912511748541046,
"relRmsErrDb": -8.246006212467824,
"wasm": {
"samplePeak": 0.36383503675460815,
"truePeakDb": -8.715719927350808,
"loudnessLufs": -17.418922989049733,
"centroidMedianHz": 4914.23686401765,
"overSamples": 0,
"rmsDb": -16.959964506391177,
"crestDb": 8.17805487903365,
"noiseToToneDb": -2.834032055202698,
"noiseToToneADb": 9.67560044209106,
"lowMotion": false
},
"native": {
"samplePeak": 0.3622153401374817,
"truePeakDb": -8.742560302886037,
"loudnessLufs": -17.424886445264,
"centroidMedianHz": 4908.148280531137,
"overSamples": 0,
"rmsDb": -16.961323502311725,
"crestDb": 8.140660284709858,
"noiseToToneDb": -2.9540607279645457,
"noiseToToneADb": 9.40924191267517,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "sfxr-classic-classic-sfxr-abstract-golf",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.000002171844244003296,
"maxErrAt": 117685,
"rmsErr": 9.912912629276112e-8,
"relRmsErrDb": -134.45251378934543,
"wasm": {
"samplePeak": 0.9895820021629333,
"truePeakDb": 0.9076630707842905,
"loudnessLufs": -5.327743141800993,
"centroidMedianHz": 8147.614236982103,
"overSamples": 0,
"rmsDb": -5.623460642702886,
"crestDb": 5.532496403571967,
"noiseToToneDb": -0.9960950126656059,
"noiseToToneADb": 7.586387572827178,
"lowMotion": true
},
"native": {
"samplePeak": 0.9895820021629333,
"truePeakDb": 0.9076628030277624,
"loudnessLufs": -5.327743184793801,
"centroidMedianHz": 8147.614175634302,
"overSamples": 0,
"rmsDb": -5.623460696287697,
"crestDb": 5.5324964571567765,
"noiseToToneDb": -0.9960947920917818,
"noiseToToneADb": 7.5863877077205615,
"lowMotion": true
},
"flagsWasm": [
"waived truePeakDb",
"waived lowMotion",
"waived loudnessLufs",
"warn truePeakDb"
],
"flagsNative": [
"waived truePeakDb",
"waived lowMotion",
"waived loudnessLufs",
"warn truePeakDb"
],
"violations": []
},
{
"id": "sfxr-explosion",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.000002158805727958679,
"maxErrAt": 129292,
"rmsErr": 1.1522412234189382e-7,
"relRmsErrDb": -127.13848106038596,
"wasm": {
"samplePeak": 0.6732206344604492,
"truePeakDb": -3.0561513953602306,
"loudnessLufs": -8.634121016036506,
"centroidMedianHz": 9795.322296183533,
"overSamples": 0,
"rmsDb": -11.630620604104497,
"crestDb": 8.193768979977403,
"noiseToToneDb": 60,
"noiseToToneADb": 60,
"lowMotion": true
},
"native": {
"samplePeak": 0.6732206344604492,
"truePeakDb": -3.0561512376276347,
"loudnessLufs": -8.634121064908708,
"centroidMedianHz": 9795.322310734966,
"overSamples": 0,
"rmsDb": -11.630620652864625,
"crestDb": 8.193769028737531,
"noiseToToneDb": 60,
"noiseToToneADb": 60,
"lowMotion": true
},
"flagsWasm": [
"waived lowMotion"
],
"flagsNative": [
"waived lowMotion"
],
"violations": []
},
{
"id": "tract/vowel-a",
"lenWasm": 34560,
"lenNative": 34560,
"maxErr": 0.0001921914517879486,
"maxErrAt": 28187,
"rmsErr": 0.000046111049670587934,
"relRmsErrDb": -52.60441410619507,
"wasm": {
"samplePeak": 0.08378415554761887,
"truePeakDb": -21.518327105526673,
"loudnessLufs": -34.251568232392565,
"centroidMedianHz": 1937.1220888731689,
"overSamples": 0,
"rmsDb": -33.80275018649363,
"crestDb": 12.265988122620389,
"noiseToToneDb": -10.436050344670264,
"noiseToToneADb": -12.885220061403881,
"lowMotion": false
},
"native": {
"samplePeak": 0.08390921354293823,
"truePeakDb": -21.50539262673788,
"loudnessLufs": -34.23471535099468,
"centroidMedianHz": 1937.2296872141594,
"overSamples": 0,
"rmsDb": -33.78684837329708,
"crestDb": 12.263041385142849,
"noiseToToneDb": -10.447393994101596,
"noiseToToneADb": -12.89873810612136,
"lowMotion": false
},
"flagsWasm": [
"FAIL onsetMs"
],
"flagsNative": [
"FAIL onsetMs"
],
"violations": []
}
]
}

View file

@ -0,0 +1,118 @@
# aqua-feel: faustwasm vs the daemon's native libfaust path
**Nothing here was heard.** Numbers from offline buffers, same Faust source, 48 kHz, mono, 512-frame blocks, same params.
- wasm side: `tools/aqua-feel/render.js` (faustwasm 0.20.0, libfaust 2.90.19 and the stdlib bundled with it, `-ftz 2`).
- native side: `tools/FaustNativeRender` over `AquaSynthPatchCompiler` (`NativeFaustRuntime.cs:530`: `-single -ftz 2 -vec -lv 1`), libfaust 2.90.20 + LLVM built in the Linux sandbox 2026-10-31, with the stdlib `faustlibraries-20261031` (master 3d8c6ef) from `AQUASYNTH_FAUST_HOME`. **The two sides compile against different standard-library text**, and that decides the thruster result below; "native" here means this sandbox's library, which the repo does not pin (`docs/faust-toolchain-boundary.md`: the host's `share/faust`).
- driver: `node tools/aqua-feel/native-diff.mjs <FaustNativeRender dir> --tract <source.dsp>`; raw numbers in `aqua-feel-native-diff-2026-11-10.json`.
- tract voice: `vowel-a` Faust source from the 2026-11-08 render kit (0.72 s, default controls). Not in the repo (`--tract` takes a path), not an aqua-feel catalog item, so its `FAIL onsetMs` is the patch-group rule applied by this script, not a repo gate result.
- the script exits 1 on a render error, a length mismatch, or a violation of its `LIMITS` (see "Tolerance in `native-diff.mjs`" below); the verdicts here are the reader's.
- Nothing here was heard.
Columns are wasm / native.
| item | max abs err (at sample) | err vs signal RMS | sample peak | centroid Hz | LUFS | true peak dBTP | gate flags |
|---|---|---|---|---|---|---|---|
| thruster rpm300 | 2.6e-2 (142707) | -28.8 dB | .1426/.1424 | 751.9/751.5 | -30.227/-30.216 | -16.90/-16.92 | none / none |
| thruster rpm600 | 4.9e-2 | -23.5 dB | .1596/.1601 | 1228.0/1224.4 | -28.695/-28.709 | -15.93/-15.90 | none / none |
| thruster rpm1200 | 9.0e-2 | -16.6 dB | .1916/.1894 | 2114.2/2115.6 | -26.051/-26.051 | -14.34/-14.42 | none / none |
| thruster rpm4800 | 2.9e-1 | -8.2 dB | .3638/.3622 | 4914.2/4908.1 | -17.419/-17.425 | -8.72/-8.74 | none / none |
| sfxr classic abstract-golf | 2.2e-6 | -134.5 dB | .9896/.9896 | 8147.6/8147.6 | -5.328/-5.328 | +0.908/+0.908 | waived truePeakDb, lowMotion, loudnessLufs; warn truePeakDb / identical |
| sfxr explosion | 2.2e-6 | -127.1 dB | .6732/.6732 | 9795.3/9795.3 | -8.634/-8.634 | -3.06/-3.06 | waived lowMotion / identical |
| tract vowel-a | 1.9e-4 | -52.6 dB | .08378/.08391 | 1937.1/1937.2 | -34.252/-34.235 | -21.52/-21.51 | FAIL onsetMs / identical |
Hiss caps (noiseToToneADb, wasm / native; cap): rpm300 13.09/13.06 (16), rpm600 5.04/6.05 (8), rpm1200 12.65/12.72 (15). rpm4800 9.68/9.41 (no cap).
## Verdict
**sfxr (two items): the paths agree** to float rounding (2e-6). These two patches use no library smoother (`smooth01` and a local `seg_smooth`), so the agreement says nothing about the library difference below. The gate flags are the script's `gate()` applied to each side's metrics for these two items only; they match.
**Tract voice: agrees to -52.6 dB** (max 1.9e-4 on a 0.084 peak); metrics within 0.02 dB and 0.01 % centroid. The residual is the same library difference: the source calls `si.smoo` 101 times; with the old smoother form pinned on both sides the error is -61.2 dB, with the new form pinned on both it is 3.1e-7 (-109.3 dB).
**Thruster: waveforms diverge, level and spectrum metrics stay close.** Relative RMS error -28.8 dB at rpm300 to -8.2 dB at rpm4800. At rpm4800 it grows over time: windowed -53, -33, -22, -15, -10 and -4 dB for 0-0.25, 0.25-0.5, 0.5-1, 1-1.5, 1.5-2 and 2-3 s. Differences in the metrics: LUFS 0.015 dB, true peak 0.081 dB, sample peak 1.2 %, centroid 0.3 %, crest 0.11 dB, hiss within 0.27 dB except rpm600 A-weighted, 1.0 dB higher native.
### Cause (isolated by a separate Soul pass, 2026-11-10)
The thruster smooths `rpm` with `si.smooth(ba.tau2pole(0.2))`, and every oscillator integrates that into phase. The two library texts define `si.smooth` differently:
- the wasm bundle (libfaust 2.90.19, `/usr/share/faust`): `smooth(s) = *(1.0 - s) : + ~ *(s)`. In float32 it stalls: the rpm freezes at **4796.632** from sample ~72063 (1.5 s) for a target of 4800. A float32 simulation without fused multiply-add reproduces 4796.6318359375.
- the native overlay (faustlibraries master 3d8c6ef, `signals.lib:391`): `smooth = imp(3)`, an error-form recursion whose comment says it cannot stall. It reaches 4800.0001.
Evidence that the library text, not `-vec`, `-lv`, `-double` or contraction, decides it: the `faust` CLI emits the same update expression for the same `si.smooth` source under scalar, `-vec -lv 0/1` and `-double`. With the old explicit form pinned in the DSP on both sides, native ends at 4797.6573 (LLVM does contract to FMA, which moves the stall point from 4796.632 to 4797.657, matching the FMA simulation) and wasm at 4796.6319. With the explicit `imp(3)` form pinned on both sides, thruster error falls from 2.61e-2 to 5.7e-7 (rpm300, -120.3 dB) and from 2.87e-1 to 1.13e-4 (rpm4800, -80.5 dB). The stall point is also hypersensitive: one ulp on the pole constant gives 4799.38 or 4798.57.
The earlier version of this report said the cause was the float32 rounding of the smoother on a native path that "does not stall for an unisolated reason" and suggested `-vec`; that was wrong. The `wander` term is not the cause (removing it leaves rpm300 2.62e-2, rpm4800 2.89e-1).
The isolation probes are scratch scripts in `/var/lib/mind-aqua-self/soul-nd-1` (not committed); the numbers above are not in the `.json`, which holds the seven main rows plus the tract.
### What this does and does not invalidate
- Level, true-peak, centroid, crest and hiss claims for the thruster hold on both libraries within the numbers above. The rpm600 hiss cap (8 dB) has 1.95 dB of headroom with the native library, not the 2.96 dB the threshold note assumes; still passes.
- Sample- or phase-level comparisons of the thruster (null tests, exact waveform diffs, sub-ms timing of the rotor tone) are specific to the library that rendered them.
- Which library a given daemon uses depends on its `share/faust`. A daemon shipping a release stdlib with the old `si.smooth` would stall like wasm; this sandbox's would not. The only Windows Faust install found (Starfire, 2.85.5) has the old form; see the section on the production stdlib below.
- **Not tested:** the other aqua-feel items that do not call `si.smooth`/`si.smoo` (the 81 items are 45 patch renders and 36 Ding event mixes; this report covers 12 sources plus the tract), the Ding event mixes (only one note per instrument is rendered below), block sizes other than 512, and any live (non-offline) host.
## Toolchain
dotnet 10.0.403 builds `FaustNativeRender` with `/var/lib/mind-aqua-self/env-review.sh`. Re-running the script reproduces the JSON byte for byte.
## Which stdlib the production daemon compiles against (2026-11-10)
The repo neither vendors nor pins faustlibraries (`.gitmodules` holds only zynaddsubfx and pink-trombone-mod). `NativeFaustRuntime.cs:530` passes `-I <home>/share/faust`, and `AquaSynthFaustToolchain.Find` (`NativeFaustRuntime.cs:651`) takes `home` from `AQUASYNTH_FAUST_HOME`, then `AQUARIUM_FAUST_HOME`, then `Tools/Faust` beside the app, then `C:\Program Files\Faust` (Windows) or `/usr/local`, `/usr`. So the daemon compiles against whatever Faust install the host has.
| host | Faust | `si.smooth` in its `signals.lib` | stalls (float32)? |
|---|---|---|---|
| Starfire `C:\Program Files\Faust` (read over ssh; faust.exe 2.85.5, `signals.lib` 2026-03-20, `smooth = smooth_imp(0)`, line 382) | 2.85.5 | `(1.0 - s) * x + s * y`, the same recursion as wasm's | yes, by the same mechanism |
| aqua-feel's wasm bundle (faustwasm 0.20.0, libfaust 2.90.19; embedded `signals.lib`) | 2.90.19 | `smooth(s) = *(1.0 - s) : + ~ *(s)` | yes: 4796.632 |
| this sandbox (`faustlibraries-20261031`, master 3d8c6ef, a one-commit shallow clone) | 2.90.20 | `smooth = imp(3)` with a comment on the stall | no |
I read only that Starfire file's `smooth` definition; I did not render on Starfire. On the evidence, the Windows daemon uses the old form, so **aqua-feel's wasm numbers are the closer model of that host than this sandbox's native render is**; the sandbox's imp(3) library is not what any shipped host has. Whether the daemon runs on Starfire in production is not established here; it is the only Windows Faust install found.
## The twelve sources that call `si.smooth` / `si.smoo`
The earlier "eleven" was seven patches, three Ding instruments and the larynx graph; the catalog has seven patches besides the thruster **including** the larynx graph, and **four** Ding instruments (`muted-pluck` was missed). Rendered: the thruster at rpm 1200, those seven patches, and one note per instrument (261.6256 Hz, 1.5 s, not the mixed event). Three conditions, each the same Faust text on both sides: as shipped (each side's own stdlib), the old smoother pinned (`nd_smooth(s) = *(1.0 - s) : + ~ *(s)`), and imp(3) pinned. Pinning rewrites `si.smooth` and `si.smoo` in the source text; a smoother called inside another library function is not rewritten, which may account for the `dx7-algorithm-32` residual below. The tract voice rides along as a thirteenth row. Columns are native minus wasm. Raw rows: `aqua-feel-native-diff-smoother-{library,old,imp3}-2026-11-10.json`.
| source | stdlib as shipped (wasm bundle vs sandbox): max err / rel RMS dB | old smoother pinned | imp3 pinned | ΔLUFS lib / old / imp3 | Δ true peak dB lib / old / imp3 | Δ centroid % lib / old / imp3 |
|---|---|---|---|---|---|---|
| thruster@rpm1200 | 9.0e-2 / -16.6 | 4.5e-2 / -21.2 | 7.1e-6 / -99.9 | 0.000 / 0.001 / -0.000 | -0.081 / -0.152 / 0.000 | 0.07 / -0.08 / 0.00 |
| advanced-alien-multivalve-graph | 2.9e-5 / -68.4 | 1.0e-5 / -72.5 | 7.9e-7 / -97.9 | -0.000 / -0.000 / -0.000 | -0.001 / -0.000 / -0.000 | -0.00 / 0.00 / 0.00 |
| advanced-bird-syrinx | 2.8e-3 / -27.3 | 1.6e-3 / -31.7 | 3.2e-6 / -88.3 | -0.001 / -0.001 / -0.000 | -0.000 / -0.000 / -0.000 | -0.00 / -0.00 / 0.00 |
| advanced-bird-syrinx-graph | 9.4e-4 / -38.4 | 2.7e-4 / -47.1 | 1.5e-5 / -75.4 | 0.000 / -0.000 / 0.000 | 0.000 / 0.000 / -0.000 | -0.01 / 0.00 / -0.00 |
| advanced-syrinx-voice | 1.3e-3 / -23.9 | 3.6e-4 / -34.6 | 2.3e-6 / -86.3 | 0.001 / 0.000 / 0.000 | 0.003 / 0.003 / -0.000 | -0.00 / -0.00 / 0.00 |
| advanced-human-larynx-graph | 9.6e-5 / -38.1 | 3.3e-5 / -46.6 | 7.6e-7 / -94.2 | 0.002 / 0.001 / -0.000 | 0.002 / 0.001 / -0.000 | 0.05 / 0.05 / 0.04 |
| dx7-algorithm-32-additive-organ | 1.7e-4 / -79.3 | 1.6e-4 / -79.5 | 1.6e-4 / -79.5 | 0.000 / -0.000 / 0.000 | -0.000 / -0.000 / -0.000 | -0.00 / -0.00 / -0.00 |
| dx7-algorithm-8-bright-pair | 4.1e-6 / -94.5 | 4.2e-6 / -94.2 | 3.9e-6 / -94.7 | 0.000 / 0.000 / 0.000 | -0.000 / -0.000 / -0.000 | 0.00 / 0.00 / -0.00 |
| ding-instrument/glass-chime | 3.8e-2 / -18.4 | 2.5e-2 / -21.9 | 3.3e-7 / -130.0 | 0.000 / 0.000 / 0.000 | -0.000 / 0.000 / -0.000 | 0.02 / 0.01 / -0.00 |
| ding-instrument/muted-pluck | 6.6e-4 / -49.8 | 6.1e-4 / -50.2 | 3.0e-8 / -141.7 | -0.000 / -0.000 / -0.000 | -0.000 / -0.000 / -0.000 | 0.00 / -0.01 / -0.00 |
| ding-instrument/soft-triangle | 4.3e-3 / -30.3 | 2.4e-3 / -35.2 | 4.5e-8 / -142.0 | 0.000 / -0.000 / -0.000 | -0.000 / -0.000 / 0.000 | 0.16 / 0.12 / -0.00 |
| ding-instrument/warm-bell | 5.4e-2 / -18.3 | 3.2e-2 / -22.9 | 2.3e-7 / -134.2 | 0.000 / 0.000 / -0.000 | -0.000 / 0.000 / -0.000 | 0.01 / 0.00 / -0.00 |
| tract/vowel-a | 1.9e-4 / -52.6 | 6.7e-5 / -61.2 | 3.1e-7 / -109.3 | 0.017 / 0.006 / -0.000 | 0.013 / 0.004 / -0.000 | 0.01 / 0.01 / -0.00 |
Largest metric deltas over all sources and the three conditions: |LUFS| 0.017 dB (tract), |true peak| 0.152 dB (thruster, old pinned), |centroid| 0.16 % (soft-triangle; the earlier core run has 0.3 % at thruster rpm600). **No gate flag differs on any source in any condition**; the tract's patch-group `FAIL onsetMs` is on both sides (see above).
What the data says:
- With imp(3) pinned on both sides all 13 rows agree to -75 dB or better (Ding instruments -130 to -142 dB, thruster -99.9 dB, tract -109 dB; worst: `advanced-bird-syrinx-graph` -75.4 dB). `dx7-algorithm-32-additive-organ` sits at -79.3 (library), -79.5 (old) and -79.5 dB (imp3), so its residual is not the pinned smoother (a smoother inside a library function, or float rounding; not isolated).
- With the old smoother pinned the thruster still differs (-21.2 dB): native's LLVM contracts the recursion to FMA and stalls at 4797.657 instead of wasm's 4796.632. The old form's stall point depends on the arithmetic; the imp(3) form does not.
- As shipped, nine of the 13 rows differ at waveform level by -16 to -50 dB (thruster, the three syrinx patches, the larynx graph, all four Ding instruments); `advanced-alien-multivalve-graph` (-68 dB), the tract (-52.6 dB) and the two dx7 patches are closer.
## Tolerance in `native-diff.mjs`
Added, because the data shows a gap. Across all 13 sources and the 3 conditions the metric deltas stay under |LUFS| 0.017 dB, |true peak| 0.152 dB, centroid 0.3 %. A deliberate +1 dB gain on the native buffer moves LUFS and true peak by 1.0 dB. The limits sit between: 0.1 dB, 0.5 dB, 1 %. Sample-level error has no limit with the library smoother (the waveforms legitimately diverge), but with `--smoother imp3` it has one at -60 dB (the worst row is -75.4 dB; a one-sample shift reads about -4 dB).
Mutation check on the tool, `--items smoother --smoother imp3`: unmutated, 12 of 12 clean, exit 0; +1 dB native gain, 12 of 12 flagged (`loudnessLufs`, `truePeakDb`, `relRmsErrDb`), exit 1; one-sample native shift, 12 of 12 flagged (`relRmsErrDb`), exit 1. A one-sample shift is not detectable with the library smoother; `loudnessLufs`, `truePeakDb` and centroid also cannot see it. The limits are tripwires fit to these 13 sources, not taste.
## What changes in aqua-feel's conclusions
- Nothing in the gate changes: no flag differs on any tested source, and every metric delta is far below any threshold in `thresholds.json` that I looked at except the rpm600 thruster hiss cap noted above (1.0 dB, still under the cap).
- aqua-feel's WAVs are a fair rendering of what a host with the old `si.smooth` produces (the only Windows install found). They are **not** sample-identical to this sandbox's native render, and no sample-level claim about the thruster, the syrinx patches or the Ding instruments should be carried across them.
- The listening trial heard wasm renders; for a Windows daemon that is the closer path. For a daemon built on the sandbox's library, the same sources have the same level and spectrum and a different phase trajectory.
- Nothing was heard.
## Proposed faustlibraries pin (not made)
The cause is that `si.smooth` is whatever the host's `share/faust` says. Options, in order of my preference (confidence moderate; none of this is Soul-checked):
1. Vendor a pinned faustlibraries tree under the daemon's `Tools/Faust/share/faust` at build, record its commit in the compile manifest next to `faustLibrary`, and have `AquaSynthNativeOptions` prefer it over the system install. This makes the wasm bundle, Starfire and the sandbox agree only if aqua-feel's wasm path pins the same text too (faustwasm takes a library via its virtual FS), so pin both.
2. Decide the smoother once, in AquaSynth's emitted source: lowering emits a local smoother definition instead of calling `si.smooth`, so no host's stdlib decides it. Smaller, but changes every lowered patch and does not cover library functions that smooth internally.
3. Leave unpinned and record the host library identity in each compile manifest, accepting skew.
Which smoother is the right one is a sound decision, not a measurement: imp(3) cannot stall (the library's own comment), the old form lands 0.07 % short of an rpm target at 4800. That is the owner's call.

View file

@ -0,0 +1,480 @@
{
"libfaustWasm": "2.90.19",
"sampleRate": 48000,
"items": "smoother",
"smoother": "imp3",
"rows": [
{
"id": "thruster@rpm1200",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.000007145106792449951,
"maxErrAt": 129748,
"rmsErr": 8.55413991926508e-7,
"relRmsErrDb": -99.87174472945775,
"wasm": {
"samplePeak": 0.18935100734233856,
"truePeakDb": -14.4241663695795,
"loudnessLufs": -26.05070420011194,
"centroidMedianHz": 2115.6325442720204,
"overSamples": 0,
"rmsDb": -21.48445687766135,
"crestDb": 7.029809275623743,
"noiseToToneDb": -4.965082233659885,
"noiseToToneADb": 12.71614612278843,
"lowMotion": false
},
"native": {
"samplePeak": 0.1893547624349594,
"truePeakDb": -14.423961460001248,
"loudnessLufs": -26.05070520432609,
"centroidMedianHz": 2115.6363534197994,
"overSamples": 0,
"rmsDb": -21.484457233344667,
"crestDb": 7.029981882815694,
"noiseToToneDb": -4.965083035181173,
"noiseToToneADb": 12.716143920958068,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-alien-multivalve-graph",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 7.934868335723877e-7,
"maxErrAt": 35537,
"rmsErr": 8.335841454269617e-8,
"relRmsErrDb": -97.93819913427402,
"wasm": {
"samplePeak": 0.03596395254135132,
"truePeakDb": -28.873901668042834,
"loudnessLufs": -44.01583944248863,
"centroidMedianHz": 1719.746341068706,
"overSamples": 0,
"rmsDb": -38.86915824557575,
"crestDb": 9.986506563399741,
"noiseToToneDb": 2.2949371593837427,
"noiseToToneADb": -35.26674665881937,
"lowMotion": false
},
"native": {
"samplePeak": 0.03596379607915878,
"truePeakDb": -28.87393371231402,
"loudnessLufs": -44.01584006251751,
"centroidMedianHz": 1719.7470242831325,
"overSamples": 0,
"rmsDb": -38.86915865101764,
"crestDb": 9.986469180550948,
"noiseToToneDb": 2.2949373787323424,
"noiseToToneADb": -35.266660047022036,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-bird-syrinx",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.0000031944364309310913,
"maxErrAt": 45510,
"rmsErr": 3.8508989002632593e-7,
"relRmsErrDb": -88.25157230135844,
"wasm": {
"samplePeak": 0.07248446345329285,
"truePeakDb": -22.789810284125757,
"loudnessLufs": -39.38587031070514,
"centroidMedianHz": 3246.9416863147353,
"overSamples": 0,
"rmsDb": -35.86910042371045,
"crestDb": 13.073998993865658,
"noiseToToneDb": 3.398154713653433,
"noiseToToneADb": -0.2652457381911617,
"lowMotion": false
},
"native": {
"samplePeak": 0.07248446345329285,
"truePeakDb": -22.789811642249525,
"loudnessLufs": -39.38587068917686,
"centroidMedianHz": 3246.965668802187,
"overSamples": 0,
"rmsDb": -35.86909904000429,
"crestDb": 13.0739976101595,
"noiseToToneDb": 3.3981546091622192,
"noiseToToneADb": -0.2652490593489034,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-bird-syrinx-graph",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.000015079975128173828,
"maxErrAt": 32899,
"rmsErr": 0.000001467630377556357,
"relRmsErrDb": -75.44121356490483,
"wasm": {
"samplePeak": 0.06643311679363251,
"truePeakDb": -23.549215787508704,
"loudnessLufs": -41.27251453792896,
"centroidMedianHz": 3608.1313492575864,
"overSamples": 0,
"rmsDb": -35.83554258887882,
"crestDb": 12.283235156759115,
"noiseToToneDb": 3.2057938097979033,
"noiseToToneADb": -14.047787523198789,
"lowMotion": false
},
"native": {
"samplePeak": 0.06643310934305191,
"truePeakDb": -23.549216695315987,
"loudnessLufs": -41.27251147778152,
"centroidMedianHz": 3608.0824292245743,
"overSamples": 0,
"rmsDb": -35.83554221039036,
"crestDb": 12.28323380413414,
"noiseToToneDb": 3.205791878004028,
"noiseToToneADb": -14.047777587591895,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-syrinx-voice",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.000002266373485326767,
"maxErrAt": 34649,
"rmsErr": 1.2956788367741612e-7,
"relRmsErrDb": -86.30175634080427,
"wasm": {
"samplePeak": 0.01358692068606615,
"truePeakDb": -37.32686366831106,
"loudnessLufs": -43.745499382417364,
"centroidMedianHz": 3723.182853016622,
"overSamples": 0,
"rmsDb": -46.7956174011546,
"crestDb": 9.458038205292446,
"noiseToToneDb": 1.791072654520292,
"noiseToToneADb": 1.7998750278369968,
"lowMotion": false
},
"native": {
"samplePeak": 0.013587156310677528,
"truePeakDb": -37.32703848354456,
"loudnessLufs": -43.74549926644062,
"centroidMedianHz": 3723.2127010397694,
"overSamples": 0,
"rmsDb": -46.7956172502661,
"crestDb": 9.45818868394479,
"noiseToToneDb": 1.7910715795642531,
"noiseToToneADb": 1.7998739270327846,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-human-larynx-graph",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 7.587950676679611e-7,
"maxErrAt": 23972,
"rmsErr": 3.7846162388541006e-8,
"relRmsErrDb": -94.24621859478574,
"wasm": {
"samplePeak": 0.007788178510963917,
"truePeakDb": -42.17101862717542,
"loudnessLufs": -50.40721974974582,
"centroidMedianHz": 310.7891589769418,
"overSamples": 0,
"rmsDb": -50.08572494872536,
"crestDb": 7.914442895151346,
"noiseToToneDb": -30.816131815537496,
"noiseToToneADb": -27.766650651768487,
"lowMotion": false
},
"native": {
"samplePeak": 0.0077881766483187675,
"truePeakDb": -42.17102078454992,
"loudnessLufs": -50.40722107125285,
"centroidMedianHz": 310.8999754545593,
"overSamples": 0,
"rmsDb": -50.08572618353941,
"crestDb": 7.914442052620592,
"noiseToToneDb": -30.816127938801678,
"noiseToToneADb": -27.766649805181505,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "dx7-algorithm-32-additive-organ",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.000164765864610672,
"maxErrAt": 23021,
"rmsErr": 0.000020540035138682098,
"relRmsErrDb": -79.45828076486667,
"wasm": {
"samplePeak": 0.6862082481384277,
"truePeakDb": -3.2699164206425015,
"loudnessLufs": -9.759124233116152,
"centroidMedianHz": 656.4226191119797,
"overSamples": 0,
"rmsDb": -8.539759068566523,
"crestDb": 5.268877746918222,
"noiseToToneDb": -37.01983113652173,
"noiseToToneADb": -37.788337502316935,
"lowMotion": false
},
"native": {
"samplePeak": 0.6861962676048279,
"truePeakDb": -3.2700776073676936,
"loudnessLufs": -9.75912403739766,
"centroidMedianHz": 656.4212037746422,
"overSamples": 0,
"rmsDb": -8.53975888452333,
"crestDb": 5.268725914302816,
"noiseToToneDb": -37.0197944928564,
"noiseToToneADb": -37.78830410408929,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "dx7-algorithm-8-bright-pair",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.000003874767571687698,
"maxErrAt": 4727,
"rmsErr": 3.855039254064681e-7,
"relRmsErrDb": -94.6889441823849,
"wasm": {
"samplePeak": 0.150354266166687,
"truePeakDb": -16.445140264060527,
"loudnessLufs": -27.707281021900723,
"centroidMedianHz": 570.7301006928219,
"overSamples": 0,
"rmsDb": -25.228220791943087,
"crestDb": 8.770535898454384,
"noiseToToneDb": -33.00376708572004,
"noiseToToneADb": -31.168271339188635,
"lowMotion": false
},
"native": {
"samplePeak": 0.15035313367843628,
"truePeakDb": -16.44521182640461,
"loudnessLufs": -27.707279746651498,
"centroidMedianHz": 570.7300173443439,
"overSamples": 0,
"rmsDb": -25.228219801277547,
"crestDb": 8.770469484271047,
"noiseToToneDb": -33.00374306131664,
"noiseToToneADb": -31.168260425434468,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/glass-chime",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 3.3155083656311035e-7,
"maxErrAt": 1305,
"rmsErr": 1.7352308119215504e-8,
"relRmsErrDb": -130.0126824087709,
"wasm": {
"samplePeak": 0.17621399462223053,
"truePeakDb": -15.062043767848326,
"loudnessLufs": -21.567272532108692,
"centroidMedianHz": 1602.876747515682,
"overSamples": 0,
"rmsDb": -22.84411377344017,
"crestDb": 7.764921701255961,
"noiseToToneDb": -3.1568108379227766,
"noiseToToneADb": -2.864104015545095,
"lowMotion": false
},
"native": {
"samplePeak": 0.17621399462223053,
"truePeakDb": -15.062043917300066,
"loudnessLufs": -21.567272049422396,
"centroidMedianHz": 1602.87659074774,
"overSamples": 0,
"rmsDb": -22.844113441684826,
"crestDb": 7.764921369500614,
"noiseToToneDb": -3.156811070401325,
"noiseToToneADb": -2.8641043762574725,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/muted-pluck",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 2.9802322387695312e-8,
"maxErrAt": 39,
"rmsErr": 2.9894375811414372e-9,
"relRmsErrDb": -141.73373986178612,
"wasm": {
"samplePeak": 0.184849813580513,
"truePeakDb": -14.6610699229291,
"loudnessLufs": -27.093755336399102,
"centroidMedianHz": 337.57025931776576,
"overSamples": 0,
"rmsDb": -22.505770402082735,
"crestDb": 7.842150740622795,
"noiseToToneDb": -1.8131372817002074,
"noiseToToneADb": -3.773645767164254,
"lowMotion": false
},
"native": {
"samplePeak": 0.184849813580513,
"truePeakDb": -14.66107041311351,
"loudnessLufs": -27.093755376725216,
"centroidMedianHz": 337.5700092248225,
"overSamples": 0,
"rmsDb": -22.505770501962672,
"crestDb": 7.842150840502738,
"noiseToToneDb": -1.813137412998607,
"noiseToToneADb": -3.773645881399787,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/soft-triangle",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 4.470348358154297e-8,
"maxErrAt": 8043,
"rmsErr": 4.533743604935639e-9,
"relRmsErrDb": -141.9875633660136,
"wasm": {
"samplePeak": 0.19771063327789307,
"truePeakDb": -14.073779284051348,
"loudnessLufs": -24.40759733605614,
"centroidMedianHz": 432.727373830872,
"overSamples": 0,
"rmsDb": -22.83707655432693,
"crestDb": 8.757677097882045,
"noiseToToneDb": -7.168489439744874,
"noiseToToneADb": -9.027978911866263,
"lowMotion": false
},
"native": {
"samplePeak": 0.19771063327789307,
"truePeakDb": -14.073779075736933,
"loudnessLufs": -24.407597412236093,
"centroidMedianHz": 432.72721346965216,
"overSamples": 0,
"rmsDb": -22.83707661841455,
"crestDb": 8.757677161969662,
"noiseToToneDb": -7.168489371555516,
"noiseToToneADb": -9.027978838572636,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/warm-bell",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 2.3096799850463867e-7,
"maxErrAt": 4434,
"rmsErr": 1.76394996238211e-8,
"relRmsErrDb": -134.18055622815194,
"wasm": {
"samplePeak": 0.2345581352710724,
"truePeakDb": -12.586384128665326,
"loudnessLufs": -19.507703963974553,
"centroidMedianHz": 930.5955363634018,
"overSamples": 0,
"rmsDb": -19.84892961990138,
"crestDb": 7.25393962687934,
"noiseToToneDb": -4.493021207525722,
"noiseToToneADb": -3.568407049551638,
"lowMotion": false
},
"native": {
"samplePeak": 0.2345581352710724,
"truePeakDb": -12.586384726471685,
"loudnessLufs": -19.50770471111663,
"centroidMedianHz": 930.5954188145085,
"overSamples": 0,
"rmsDb": -19.84893033283087,
"crestDb": 7.253940339808832,
"noiseToToneDb": -4.49302081177872,
"noiseToToneADb": -3.568406831675894,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "tract/vowel-a",
"lenWasm": 34560,
"lenNative": 34560,
"maxErr": 3.129243850708008e-7,
"maxErrAt": 32270,
"rmsErr": 6.777997120190261e-8,
"relRmsErrDb": -109.27441638992673,
"wasm": {
"samplePeak": 0.08390945941209793,
"truePeakDb": -21.50535980428806,
"loudnessLufs": -34.23468728787301,
"centroidMedianHz": 1937.2312975664222,
"overSamples": 0,
"rmsDb": -33.786820379265095,
"crestDb": 12.26303884229992,
"noiseToToneDb": -10.447396268131833,
"noiseToToneADb": -12.898741047906523,
"lowMotion": false
},
"native": {
"samplePeak": 0.08390921354293823,
"truePeakDb": -21.50539262673788,
"loudnessLufs": -34.23471535099468,
"centroidMedianHz": 1937.2296872141594,
"overSamples": 0,
"rmsDb": -33.78684837329708,
"crestDb": 12.263041385142849,
"noiseToToneDb": -10.447393994101596,
"noiseToToneADb": -12.89873810612136,
"lowMotion": false
},
"flagsWasm": [
"FAIL onsetMs"
],
"flagsNative": [
"FAIL onsetMs"
],
"violations": []
}
]
}

View file

@ -0,0 +1,480 @@
{
"libfaustWasm": "2.90.19",
"sampleRate": 48000,
"items": "smoother",
"smoother": "library",
"rows": [
{
"id": "thruster@rpm1200",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.08972565084695816,
"maxErrAt": 143228,
"rmsErr": 0.012448641233006897,
"relRmsErrDb": -16.610480431926955,
"wasm": {
"samplePeak": 0.19161102175712585,
"truePeakDb": -14.343339234769212,
"loudnessLufs": -26.051033400616078,
"centroidMedianHz": 2114.222071076734,
"overSamples": 0,
"rmsDb": -21.48680915202533,
"crestDb": 7.1352188868162605,
"noiseToToneDb": -4.949059402813134,
"noiseToToneADb": 12.652391835948997,
"lowMotion": false
},
"native": {
"samplePeak": 0.1893547624349594,
"truePeakDb": -14.423961460001248,
"loudnessLufs": -26.05070520432609,
"centroidMedianHz": 2115.6363534197994,
"overSamples": 0,
"rmsDb": -21.484457233344667,
"crestDb": 7.029981882815694,
"noiseToToneDb": -4.965083035181173,
"noiseToToneADb": 12.716143920958068,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-alien-multivalve-graph",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.000028988346457481384,
"maxErrAt": 35885,
"rmsErr": 0.0000024852740234353792,
"relRmsErrDb": -68.44968710695318,
"wasm": {
"samplePeak": 0.03596847131848335,
"truePeakDb": -28.872530673301476,
"loudnessLufs": -44.01558191383624,
"centroidMedianHz": 1719.821403397199,
"overSamples": 0,
"rmsDb": -38.86917357972277,
"crestDb": 9.98761318843934,
"noiseToToneDb": 2.2946722668875523,
"noiseToToneADb": -35.26574829949449,
"lowMotion": false
},
"native": {
"samplePeak": 0.03596379607915878,
"truePeakDb": -28.87393371231402,
"loudnessLufs": -44.01584006251751,
"centroidMedianHz": 1719.7470242831325,
"overSamples": 0,
"rmsDb": -38.86915865101764,
"crestDb": 9.986469180550948,
"noiseToToneDb": 2.2949373787323424,
"noiseToToneADb": -35.266660047022036,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-bird-syrinx",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.0027769599109888077,
"maxErrAt": 45282,
"rmsErr": 0.0004283705044505341,
"relRmsErrDb": -27.32626534167103,
"wasm": {
"samplePeak": 0.0724848210811615,
"truePeakDb": -22.789532845508607,
"loudnessLufs": -39.384826220237244,
"centroidMedianHz": 3246.9736644772215,
"overSamples": 0,
"rmsDb": -35.86925853361916,
"crestDb": 13.074199958593006,
"noiseToToneDb": 3.395163753535492,
"noiseToToneADb": -0.26769809662280863,
"lowMotion": false
},
"native": {
"samplePeak": 0.07248446345329285,
"truePeakDb": -22.789811642249525,
"loudnessLufs": -39.38587068917686,
"centroidMedianHz": 3246.965668802187,
"overSamples": 0,
"rmsDb": -35.86909904000429,
"crestDb": 13.0739976101595,
"noiseToToneDb": 3.3981546091622192,
"noiseToToneADb": -0.2652490593489034,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-bird-syrinx-graph",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.0009388141334056854,
"maxErrAt": 33098,
"rmsErr": 0.00010481991412365385,
"relRmsErrDb": -38.36457659477791,
"wasm": {
"samplePeak": 0.06643299758434296,
"truePeakDb": -23.549220126103343,
"loudnessLufs": -41.272964930830874,
"centroidMedianHz": 3608.3773722521782,
"overSamples": 0,
"rmsDb": -35.835637421519465,
"crestDb": 12.28331440320251,
"noiseToToneDb": 3.205981107524842,
"noiseToToneADb": -14.050756471755903,
"lowMotion": false
},
"native": {
"samplePeak": 0.06643310934305191,
"truePeakDb": -23.549216695315987,
"loudnessLufs": -41.27251147778152,
"centroidMedianHz": 3608.0824292245743,
"overSamples": 0,
"rmsDb": -35.83554221039036,
"crestDb": 12.28323380413414,
"noiseToToneDb": 3.205791878004028,
"noiseToToneADb": -14.047777587591895,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-syrinx-voice",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.0012954180128872395,
"maxErrAt": 33655,
"rmsErr": 0.00017071330356165833,
"relRmsErrDb": -23.90529822473789,
"wasm": {
"samplePeak": 0.013509727083146572,
"truePeakDb": -37.32985445982076,
"loudnessLufs": -43.74666608622773,
"centroidMedianHz": 3723.272760511277,
"overSamples": 0,
"rmsDb": -46.796675488586175,
"crestDb": 9.409607002734177,
"noiseToToneDb": 1.790967089244596,
"noiseToToneADb": 1.7997448212983471,
"lowMotion": false
},
"native": {
"samplePeak": 0.013587156310677528,
"truePeakDb": -37.32703848354456,
"loudnessLufs": -43.74549926644062,
"centroidMedianHz": 3723.2127010397694,
"overSamples": 0,
"rmsDb": -46.7956172502661,
"crestDb": 9.45818868394479,
"noiseToToneDb": 1.7910715795642531,
"noiseToToneADb": 1.7998739270327846,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-human-larynx-graph",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.00009577462333254516,
"maxErrAt": 34584,
"rmsErr": 0.00002426493085728514,
"relRmsErrDb": -38.10546679970197,
"wasm": {
"samplePeak": 0.007785667199641466,
"truePeakDb": -42.173192592326515,
"loudnessLufs": -50.40895750738575,
"centroidMedianHz": 310.7359227777714,
"overSamples": 0,
"rmsDb": -50.08733256154268,
"crestDb": 7.913249276521016,
"noiseToToneDb": -30.80971405508631,
"noiseToToneADb": -27.762876726971946,
"lowMotion": false
},
"native": {
"samplePeak": 0.0077881766483187675,
"truePeakDb": -42.17102078454992,
"loudnessLufs": -50.40722107125285,
"centroidMedianHz": 310.8999754545593,
"overSamples": 0,
"rmsDb": -50.08572618353941,
"crestDb": 7.914442052620592,
"noiseToToneDb": -30.816127938801678,
"noiseToToneADb": -27.766649805181505,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "dx7-algorithm-32-additive-organ",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.00016526132822036743,
"maxErrAt": 23020,
"rmsErr": 0.000020994103574012447,
"relRmsErrDb": -79.26822857998425,
"wasm": {
"samplePeak": 0.6861975789070129,
"truePeakDb": -3.270050577733717,
"loudnessLufs": -9.759294906714691,
"centroidMedianHz": 656.4219631461098,
"overSamples": 0,
"rmsDb": -8.539888188883904,
"crestDb": 5.268871817143614,
"noiseToToneDb": -37.019826930432856,
"noiseToToneADb": -37.7883341551492,
"lowMotion": false
},
"native": {
"samplePeak": 0.6861962676048279,
"truePeakDb": -3.2700776073676936,
"loudnessLufs": -9.75912403739766,
"centroidMedianHz": 656.4212037746422,
"overSamples": 0,
"rmsDb": -8.53975888452333,
"crestDb": 5.268725914302816,
"noiseToToneDb": -37.0197944928564,
"noiseToToneADb": -37.78830410408929,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "dx7-algorithm-8-bright-pair",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.000004090368747711182,
"maxErrAt": 4395,
"rmsErr": 3.9566542791342453e-7,
"relRmsErrDb": -94.46295503093202,
"wasm": {
"samplePeak": 0.1503547728061676,
"truePeakDb": -16.445110330444138,
"loudnessLufs": -27.707288476356375,
"centroidMedianHz": 570.7272310041899,
"overSamples": 0,
"rmsDb": -25.22822394507269,
"crestDb": 8.7705683198403,
"noiseToToneDb": -33.00382123099267,
"noiseToToneADb": -31.168321209089953,
"lowMotion": false
},
"native": {
"samplePeak": 0.15035313367843628,
"truePeakDb": -16.44521182640461,
"loudnessLufs": -27.707279746651498,
"centroidMedianHz": 570.7300173443439,
"overSamples": 0,
"rmsDb": -25.228219801277547,
"crestDb": 8.770469484271047,
"noiseToToneDb": -33.00374306131664,
"noiseToToneADb": -31.168260425434468,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/glass-chime",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 0.03821572195738554,
"maxErrAt": 19431,
"rmsErr": 0.006593494603898945,
"relRmsErrDb": -18.417512948342896,
"wasm": {
"samplePeak": 0.17621681094169617,
"truePeakDb": -15.062032445423984,
"loudnessLufs": -21.56737037885148,
"centroidMedianHz": 1602.6138671100102,
"overSamples": 0,
"rmsDb": -22.844945161193554,
"crestDb": 7.765891909110638,
"noiseToToneDb": -3.1846833757850774,
"noiseToToneADb": -2.8894771746074213,
"lowMotion": false
},
"native": {
"samplePeak": 0.17621399462223053,
"truePeakDb": -15.062043917300066,
"loudnessLufs": -21.567272049422396,
"centroidMedianHz": 1602.87659074774,
"overSamples": 0,
"rmsDb": -22.844113441684826,
"crestDb": 7.764921369500614,
"noiseToToneDb": -3.156811070401325,
"noiseToToneADb": -2.8641043762574725,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/muted-pluck",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 0.000662742939312011,
"maxErrAt": 13239,
"rmsErr": 0.00011858435565109058,
"relRmsErrDb": -49.764983408100534,
"wasm": {
"samplePeak": 0.18485014140605927,
"truePeakDb": -14.661061484678669,
"loudnessLufs": -27.09375077529527,
"centroidMedianHz": 337.55371147245654,
"overSamples": 0,
"rmsDb": -22.505768680170274,
"crestDb": 7.8421644228588985,
"noiseToToneDb": -1.8131427242661857,
"noiseToToneADb": -3.7732237451762884,
"lowMotion": false
},
"native": {
"samplePeak": 0.184849813580513,
"truePeakDb": -14.66107041311351,
"loudnessLufs": -27.093755376725216,
"centroidMedianHz": 337.5700092248225,
"overSamples": 0,
"rmsDb": -22.505770501962672,
"crestDb": 7.842150840502738,
"noiseToToneDb": -1.813137412998607,
"noiseToToneADb": -3.773645881399787,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/soft-triangle",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 0.004339124541729689,
"maxErrAt": 27366,
"rmsErr": 0.0017425528838580662,
"relRmsErrDb": -30.292986249468473,
"wasm": {
"samplePeak": 0.19771148264408112,
"truePeakDb": -14.073758822230804,
"loudnessLufs": -24.407607566689737,
"centroidMedianHz": 432.02737524513924,
"overSamples": 0,
"rmsDb": -22.837073455193796,
"crestDb": 8.757711313308086,
"noiseToToneDb": -7.16850817229934,
"noiseToToneADb": -9.027202849078392,
"lowMotion": false
},
"native": {
"samplePeak": 0.19771063327789307,
"truePeakDb": -14.073779075736933,
"loudnessLufs": -24.407597412236093,
"centroidMedianHz": 432.72721346965216,
"overSamples": 0,
"rmsDb": -22.83707661841455,
"crestDb": 8.757677161969662,
"noiseToToneDb": -7.168489371555516,
"noiseToToneADb": -9.027978838572636,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/warm-bell",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 0.05384007841348648,
"maxErrAt": 22907,
"rmsErr": 0.010932589918993823,
"relRmsErrDb": -18.335820982841014,
"wasm": {
"samplePeak": 0.23455719649791718,
"truePeakDb": -12.586378660039248,
"loudnessLufs": -19.50785299525781,
"centroidMedianHz": 930.5251827853554,
"overSamples": 0,
"rmsDb": -19.848928933876216,
"crestDb": 7.2539041772066515,
"noiseToToneDb": -4.487748875412059,
"noiseToToneADb": -3.561906168663185,
"lowMotion": false
},
"native": {
"samplePeak": 0.2345581352710724,
"truePeakDb": -12.586384726471685,
"loudnessLufs": -19.50770471111663,
"centroidMedianHz": 930.5954188145085,
"overSamples": 0,
"rmsDb": -19.84893033283087,
"crestDb": 7.253940339808832,
"noiseToToneDb": -4.49302081177872,
"noiseToToneADb": -3.568406831675894,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "tract/vowel-a",
"lenWasm": 34560,
"lenNative": 34560,
"maxErr": 0.0001921914517879486,
"maxErrAt": 28187,
"rmsErr": 0.000046111049670587934,
"relRmsErrDb": -52.60441410619507,
"wasm": {
"samplePeak": 0.08378415554761887,
"truePeakDb": -21.518327105526673,
"loudnessLufs": -34.251568232392565,
"centroidMedianHz": 1937.1220888731689,
"overSamples": 0,
"rmsDb": -33.80275018649363,
"crestDb": 12.265988122620389,
"noiseToToneDb": -10.436050344670264,
"noiseToToneADb": -12.885220061403881,
"lowMotion": false
},
"native": {
"samplePeak": 0.08390921354293823,
"truePeakDb": -21.50539262673788,
"loudnessLufs": -34.23471535099468,
"centroidMedianHz": 1937.2296872141594,
"overSamples": 0,
"rmsDb": -33.78684837329708,
"crestDb": 12.263041385142849,
"noiseToToneDb": -10.447393994101596,
"noiseToToneADb": -12.89873810612136,
"lowMotion": false
},
"flagsWasm": [
"FAIL onsetMs"
],
"flagsNative": [
"FAIL onsetMs"
],
"violations": []
}
]
}

View file

@ -0,0 +1,480 @@
{
"libfaustWasm": "2.90.19",
"sampleRate": 48000,
"items": "smoother",
"smoother": "old",
"rows": [
{
"id": "thruster@rpm1200",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.04497168958187103,
"maxErrAt": 141577,
"rmsErr": 0.007318071121139892,
"relRmsErrDb": -21.224986931184873,
"wasm": {
"samplePeak": 0.19161102175712585,
"truePeakDb": -14.343339234769212,
"loudnessLufs": -26.051033400616078,
"centroidMedianHz": 2114.222071076734,
"overSamples": 0,
"rmsDb": -21.48680915202533,
"crestDb": 7.1352188868162605,
"noiseToToneDb": -4.949059402813134,
"noiseToToneADb": 12.652391835948997,
"lowMotion": false
},
"native": {
"samplePeak": 0.1881352812051773,
"truePeakDb": -14.4953016770333,
"loudnessLufs": -26.05000842555788,
"centroidMedianHz": 2112.509030190081,
"overSamples": 0,
"rmsDb": -21.48554731845163,
"crestDb": 6.974952256138389,
"noiseToToneDb": -4.986880144493831,
"noiseToToneADb": 12.399428225827492,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-alien-multivalve-graph",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.000010244548320770264,
"maxErrAt": 35688,
"rmsErr": 0.0000015676294788398326,
"relRmsErrDb": -72.45230428385759,
"wasm": {
"samplePeak": 0.03596847131848335,
"truePeakDb": -28.872530673301476,
"loudnessLufs": -44.01558191383624,
"centroidMedianHz": 1719.821403397199,
"overSamples": 0,
"rmsDb": -38.86917357972277,
"crestDb": 9.98761318843934,
"noiseToToneDb": 2.2946722668875523,
"noiseToToneADb": -35.26574829949449,
"lowMotion": false
},
"native": {
"samplePeak": 0.03596773371100426,
"truePeakDb": -28.872900426797354,
"loudnessLufs": -44.015640274857304,
"centroidMedianHz": 1719.8495576360149,
"overSamples": 0,
"rmsDb": -38.86920427795782,
"crestDb": 9.987465762817079,
"noiseToToneDb": 2.294718621072647,
"noiseToToneADb": -35.265694920035834,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-bird-syrinx",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.0016491450369358063,
"maxErrAt": 45282,
"rmsErr": 0.00025934850824015885,
"relRmsErrDb": -31.68498145060413,
"wasm": {
"samplePeak": 0.0724848210811615,
"truePeakDb": -22.789532845508607,
"loudnessLufs": -39.384826220237244,
"centroidMedianHz": 3246.9736644772215,
"overSamples": 0,
"rmsDb": -35.86925853361916,
"crestDb": 13.074199958593006,
"noiseToToneDb": 3.395163753535492,
"noiseToToneADb": -0.26769809662280863,
"lowMotion": false
},
"native": {
"samplePeak": 0.07248431444168091,
"truePeakDb": -22.78984322120366,
"loudnessLufs": -39.38565490955099,
"centroidMedianHz": 3246.899582775747,
"overSamples": 0,
"rmsDb": -35.86920348626081,
"crestDb": 13.074084200179179,
"noiseToToneDb": 3.396666156772234,
"noiseToToneADb": -0.2667876831196381,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-bird-syrinx-graph",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.0002693869173526764,
"maxErrAt": 32700,
"rmsErr": 0.00003826655739568763,
"relRmsErrDb": -47.117064721842894,
"wasm": {
"samplePeak": 0.06643299758434296,
"truePeakDb": -23.549220126103343,
"loudnessLufs": -41.272964930830874,
"centroidMedianHz": 3608.3773722521782,
"overSamples": 0,
"rmsDb": -35.835637421519465,
"crestDb": 12.28331440320251,
"noiseToToneDb": 3.205981107524842,
"noiseToToneADb": -14.050756471755903,
"lowMotion": false
},
"native": {
"samplePeak": 0.06643303483724594,
"truePeakDb": -23.549216558140902,
"loudnessLufs": -41.27307677467835,
"centroidMedianHz": 3608.449964447863,
"overSamples": 0,
"rmsDb": -35.835623258716865,
"crestDb": 12.283305111089557,
"noiseToToneDb": 3.20619543415809,
"noiseToToneADb": -14.048989011382716,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-syrinx-voice",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.00035776360891759396,
"maxErrAt": 33071,
"rmsErr": 0.00004987960856320729,
"relRmsErrDb": -34.59218482563713,
"wasm": {
"samplePeak": 0.013509727083146572,
"truePeakDb": -37.32985445982076,
"loudnessLufs": -43.74666608622773,
"centroidMedianHz": 3723.272760511277,
"overSamples": 0,
"rmsDb": -46.796675488586175,
"crestDb": 9.409607002734177,
"noiseToToneDb": 1.790967089244596,
"noiseToToneADb": 1.7997448212983471,
"lowMotion": false
},
"native": {
"samplePeak": 0.013522684574127197,
"truePeakDb": -37.32721733719029,
"loudnessLufs": -43.74636063400179,
"centroidMedianHz": 3723.1202654076387,
"overSamples": 0,
"rmsDb": -46.79638101226304,
"crestDb": 9.41763937099232,
"noiseToToneDb": 1.7908757840446228,
"noiseToToneADb": 1.7996603730072778,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "advanced-human-larynx-graph",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.00003339786780998111,
"maxErrAt": 29445,
"rmsErr": 0.0000091717665877173,
"relRmsErrDb": -46.55598808562419,
"wasm": {
"samplePeak": 0.007785667199641466,
"truePeakDb": -42.173192592326515,
"loudnessLufs": -50.40895750738575,
"centroidMedianHz": 310.7359227777714,
"overSamples": 0,
"rmsDb": -50.08733256154268,
"crestDb": 7.913249276521016,
"noiseToToneDb": -30.80971405508631,
"noiseToToneADb": -27.762876726971946,
"lowMotion": false
},
"native": {
"samplePeak": 0.00778665067628026,
"truePeakDb": -42.172393196766,
"loudnessLufs": -50.40839338870849,
"centroidMedianHz": 310.88482517283643,
"overSamples": 0,
"rmsDb": -50.086764610534054,
"crestDb": 7.913778447935089,
"noiseToToneDb": -30.8115791514923,
"noiseToToneADb": -27.76354669965494,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "dx7-algorithm-32-additive-organ",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.00016482919454574585,
"maxErrAt": 23021,
"rmsErr": 0.000020542966407864508,
"relRmsErrDb": -79.45691216935256,
"wasm": {
"samplePeak": 0.6861975789070129,
"truePeakDb": -3.270050577733717,
"loudnessLufs": -9.759294906714691,
"centroidMedianHz": 656.4219631461098,
"overSamples": 0,
"rmsDb": -8.539888188883904,
"crestDb": 5.268871817143614,
"noiseToToneDb": -37.019826930432856,
"noiseToToneADb": -37.7883341551492,
"lowMotion": false
},
"native": {
"samplePeak": 0.6861848831176758,
"truePeakDb": -3.2702216053436386,
"loudnessLufs": -9.759310761718336,
"centroidMedianHz": 656.4199149750565,
"overSamples": 0,
"rmsDb": -8.539903940430642,
"crestDb": 5.268726863889608,
"noiseToToneDb": -37.01979015752506,
"noiseToToneADb": -37.78830074856767,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "dx7-algorithm-8-bright-pair",
"lenWasm": 144000,
"lenNative": 144000,
"maxErr": 0.000004217028617858887,
"maxErrAt": 4395,
"rmsErr": 4.0587175382789543e-7,
"relRmsErrDb": -94.24174055880029,
"wasm": {
"samplePeak": 0.1503547728061676,
"truePeakDb": -16.445110330444138,
"loudnessLufs": -27.707288476356375,
"centroidMedianHz": 570.7272310041899,
"overSamples": 0,
"rmsDb": -25.22822394507269,
"crestDb": 8.7705683198403,
"noiseToToneDb": -33.00382123099267,
"noiseToToneADb": -31.168321209089953,
"lowMotion": false
},
"native": {
"samplePeak": 0.1503528207540512,
"truePeakDb": -16.44522744967477,
"loudnessLufs": -27.707277125976997,
"centroidMedianHz": 570.7333021952353,
"overSamples": 0,
"rmsDb": -25.228219761996577,
"crestDb": 8.770451367352216,
"noiseToToneDb": -33.00373339984028,
"noiseToToneADb": -31.168249293529612,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/glass-chime",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 0.0249517853371799,
"maxErrAt": 16135,
"rmsErr": 0.004421638829626474,
"relRmsErrDb": -21.888160741392436,
"wasm": {
"samplePeak": 0.17621681094169617,
"truePeakDb": -15.062032445423984,
"loudnessLufs": -21.56737037885148,
"centroidMedianHz": 1602.6138671100102,
"overSamples": 0,
"rmsDb": -22.844945161193554,
"crestDb": 7.765891909110638,
"noiseToToneDb": -3.1846833757850774,
"noiseToToneADb": -2.8894771746074213,
"lowMotion": false
},
"native": {
"samplePeak": 0.1762169897556305,
"truePeakDb": -15.06203177829347,
"loudnessLufs": -21.567324293584342,
"centroidMedianHz": 1602.7309843808985,
"overSamples": 0,
"rmsDb": -22.8449443529706,
"crestDb": 7.765899914787472,
"noiseToToneDb": -3.15615732806026,
"noiseToToneADb": -2.8631443461744297,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/muted-pluck",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 0.0006083704647608101,
"maxErrAt": 11771,
"rmsErr": 0.00011333511339155152,
"relRmsErrDb": -50.15824170095642,
"wasm": {
"samplePeak": 0.18485014140605927,
"truePeakDb": -14.661061484678669,
"loudnessLufs": -27.09375077529527,
"centroidMedianHz": 337.55371147245654,
"overSamples": 0,
"rmsDb": -22.505768680170274,
"crestDb": 7.8421644228588985,
"noiseToToneDb": -1.8131427242661857,
"noiseToToneADb": -3.7732237451762884,
"lowMotion": false
},
"native": {
"samplePeak": 0.18485012650489807,
"truePeakDb": -14.661062836676077,
"loudnessLufs": -27.093759010723893,
"centroidMedianHz": 337.5238499031714,
"overSamples": 0,
"rmsDb": -22.505770398210146,
"crestDb": 7.842165440710797,
"noiseToToneDb": -1.8131374325665277,
"noiseToToneADb": -3.773589944716136,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/soft-triangle",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 0.0023703736951574683,
"maxErrAt": 25715,
"rmsErr": 0.0009930534248776234,
"relRmsErrDb": -35.17725332142618,
"wasm": {
"samplePeak": 0.19771148264408112,
"truePeakDb": -14.073758822230804,
"loudnessLufs": -24.407607566689737,
"centroidMedianHz": 432.02737524513924,
"overSamples": 0,
"rmsDb": -22.837073455193796,
"crestDb": 8.757711313308086,
"noiseToToneDb": -7.16850817229934,
"noiseToToneADb": -9.027202849078392,
"lowMotion": false
},
"native": {
"samplePeak": 0.19771143794059753,
"truePeakDb": -14.07376035946359,
"loudnessLufs": -24.407608271539363,
"centroidMedianHz": 432.56670373115156,
"overSamples": 0,
"rmsDb": -22.837076588859357,
"crestDb": 8.757712483053472,
"noiseToToneDb": -7.168496214436587,
"noiseToToneADb": -9.027680538222803,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "ding-instrument/warm-bell",
"lenWasm": 72000,
"lenNative": 72000,
"maxErr": 0.031899021938443184,
"maxErrAt": 20346,
"rmsErr": 0.0064883595529472505,
"relRmsErrDb": -22.867583977030858,
"wasm": {
"samplePeak": 0.23455719649791718,
"truePeakDb": -12.586378660039248,
"loudnessLufs": -19.50785299525781,
"centroidMedianHz": 930.5251827853554,
"overSamples": 0,
"rmsDb": -19.848928933876216,
"crestDb": 7.2539041772066515,
"noiseToToneDb": -4.487748875412059,
"noiseToToneADb": -3.561906168663185,
"lowMotion": false
},
"native": {
"samplePeak": 0.23455692827701569,
"truePeakDb": -12.58637491873251,
"loudnessLufs": -19.50777054747485,
"centroidMedianHz": 930.5575374762606,
"overSamples": 0,
"rmsDb": -19.84893016359035,
"crestDb": 7.253895474424504,
"noiseToToneDb": -4.485889851528931,
"noiseToToneADb": -3.56069167084123,
"lowMotion": false
},
"flagsWasm": [],
"flagsNative": [],
"violations": []
},
{
"id": "tract/vowel-a",
"lenWasm": 34560,
"lenNative": 34560,
"maxErr": 0.00006673485040664673,
"maxErrAt": 28187,
"rmsErr": 0.000017135153866313287,
"relRmsErrDb": -61.202754106950046,
"wasm": {
"samplePeak": 0.08378415554761887,
"truePeakDb": -21.518327105526673,
"loudnessLufs": -34.251568232392565,
"centroidMedianHz": 1937.1220888731689,
"overSamples": 0,
"rmsDb": -33.80275018649363,
"crestDb": 12.265988122620389,
"noiseToToneDb": -10.436050344670264,
"noiseToToneADb": -12.885220061403881,
"lowMotion": false
},
"native": {
"samplePeak": 0.08382102102041245,
"truePeakDb": -21.514509846938733,
"loudnessLufs": -34.245380439304775,
"centroidMedianHz": 1937.234382427543,
"overSamples": 0,
"rmsDb": -33.796727413065646,
"crestDb": 12.263786346131285,
"noiseToToneDb": -10.440493522060173,
"noiseToToneADb": -12.890432956436477,
"lowMotion": false
},
"flagsWasm": [
"FAIL onsetMs"
],
"flagsNative": [
"FAIL onsetMs"
],
"violations": []
}
]
}

View 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
View 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.

View file

@ -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

View 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
View file

@ -0,0 +1 @@
build/

View 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.

View 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"

View 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

View 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 */
}

View 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)"

View 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;
}

View 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);

View file

@ -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);

View 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');
}
}

View file

@ -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('\\', '/')}";

View file

@ -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)
{ {

View file

@ -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)}\""

View file

@ -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()

View file

@ -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.");
}
} }

View 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;
}

View 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>

View 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;

View 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>

View file

@ -0,0 +1,55 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using AquaSynth.Faust;
// Offline render of one Faust source through the daemon's native libfaust path (AquaSynthPatchCompiler, -vec -lv 1)
// to a mono float32 WAV, for diffing against aqua-feel's faustwasm render. 48 kHz, 512-frame blocks, like render.js.
// usage: FaustNativeRender <patch.dsp> <out.wav> <seconds> [control=value ...]
const int Rate = 48000, Frames = 512;
if (args.Length < 3) { Console.Error.WriteLine("usage: FaustNativeRender <patch.dsp> <out.wav> <seconds> [control=value ...]"); return 2; }
var dspPath = Path.GetFullPath(args[0]);
var outPath = Path.GetFullPath(args[1]);
var total = (int)Math.Round(double.Parse(args[2]) * Rate);
var source = File.ReadAllText(dspPath);
Directory.CreateDirectory(Path.GetDirectoryName(outPath)!);
using var compiler = new AquaSynthPatchCompiler(new AquaSynthNativeOptions(SampleRate: Rate));
var name = Path.GetFileNameWithoutExtension(dspPath);
if (!compiler.TryCompileSource(new AquaSynthCompileIdentity(name, name, ""), source, (float)total / Rate, out var patch0, out var cerr))
{
Console.Error.WriteLine("COMPILE FAILED: " + cerr);
return 1;
}
using var patch = patch0!;
if (patch.Manifest.OutputCount != 1) { Console.Error.WriteLine($"expected 1 output, patch has {patch.Manifest.OutputCount}"); return 1; }
var ctl = new Dictionary<string, float>();
foreach (var spec in args.Skip(3))
{
var eq = spec.IndexOf('=');
var key = spec[..eq];
var hits = patch.ControlPaths.Where(p => p.TrimStart('/') == key.TrimStart('/') || p.EndsWith("/" + key.TrimStart('/'))).ToList();
if (hits.Count != 1) { Console.Error.WriteLine($"control \"{key}\" matches {hits.Count} of: {string.Join(", ", patch.ControlPaths)}"); return 1; }
var path = hits[0];
ctl[path] = float.Parse(spec[(eq + 1)..], System.Globalization.CultureInfo.InvariantCulture);
}
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 pcm = new float[total];
for (var at = 0; at < total; at += Frames)
{
stream.ProcessBlock(Frames, IntPtr.Zero, outP, ctl);
Array.Copy(buf, 0, pcm, at, Math.Min(Frames, total - at));
}
using (var w = new BinaryWriter(File.Create(outPath)))
{
var db = total * 4;
w.Write("RIFF"u8); w.Write(36 + db); w.Write("WAVEfmt "u8); w.Write(16); w.Write((short)3); w.Write((short)1);
w.Write(Rate); w.Write(Rate * 4); w.Write((short)4); w.Write((short)32); w.Write("data"u8); w.Write(db);
foreach (var s in pcm) w.Write(s);
}
Console.WriteLine($"{outPath} frames={total} controls={ctl.Count}");
return 0;

View 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>

View 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;

View file

@ -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");

72
tools/aqua-feel/README.md Normal file
View file

@ -0,0 +1,72 @@
# 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`); on a thruster the two diverge in waveform because the wasm bundle's `si.smooth` and the sandbox's native stdlib are different library text (not the host, not `-vec`); level and spectrum metrics stay close: `docs/renders/aqua-feel-native-diff-2026-11-10.md`, `node native-diff.mjs`.
## 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.

View 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
View 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]();

View 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) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[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
View 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");
}

View 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
}

View 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);

View 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);

View 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);

View 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
View 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,
};
}

View file

@ -0,0 +1,81 @@
// Diff aqua-feel's faustwasm render against the daemon's native libfaust render (tools/FaustNativeRender) of the same
// Faust source, at the same params, 48 kHz and length. Nothing here was heard.
// Both sides compile against their own stdlib (faustwasm bundles libfaust's; native reads AQUASYNTH_FAUST_HOME's share/faust), so a
// difference here can be library text, not the host: the report records the identities. The script exits 1
// on a render error, a length mismatch, or a LIMITS violation. LIMITS sit between the largest deltas seen across 13 sources and
// three conditions (|LUFS| 0.017, |true peak| 0.152 dB, centroid 0.3 %) and a deliberate +1 dB gain (LUFS and true peak 1.0 dB);
// the sample-level bound applies only with --smoother imp3, where the largest error seen is -75.4 dB (dx7-32, unexplained, is -79.5) and a one-sample
// shift reads about -4 dB. With the library smoother the waveforms legitimately diverge (thruster -8 to -29 dB), so no sample bound.
// usage: node native-diff.mjs <FaustNativeRender dir|dll> [--tract PATH.dsp] [--out DIR] [--items core|smoother] [--smoother library|old|imp3]
// --items smoother: the 12 catalog sources that call si.smooth/si.smoo (thruster at rpm 1200, 7 patches, 4 Ding instruments, one note at 261.6256 Hz for 1.5 s)
// --smoother old|imp3: rewrite si.smooth/si.smoo in the source text to a local definition (old = `*(1-s) : + ~ *(s)`, imp3 = the error-form
// recursion), the same text on both sides, so the standard library's own si.smooth no longer decides. Smoothers called inside other
// library functions are not rewritten.
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
import { spawnSync } from "node:child_process";
import { join, dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { tmpdir } from "node:os";
import { SR, measure } from "./metrics.js";
import { gate } from "./gate.js";
import { loadRuntime, compile, renderOnce, writeWav, readText, playground } from "./render.js";
const LIMITS = { lufsDb: 0.1, truePeakDb: 0.5, centroidPct: 1, relRmsErrDbImp3: -60 };
const here = dirname(fileURLToPath(import.meta.url)), args = process.argv.slice(2);
const opt = (k) => { const i = args.indexOf(k); return i < 0 ? null : args[i + 1]; };
const bin = resolve(args[0]), out = resolve(opt("--out") ?? join(tmpdir(), "aqua-feel-native-diff"));
const low = (n) => readText(join(playground, "lowered", n));
const mode = opt("--smoother") ?? "library", set = opt("--items") ?? "core";
const DEFS = {
old: "nd_smooth(s) = *(1.0 - s) : + ~ *(s);",
imp3: "nd_smooth(s) = \\(x).(x + (fb ~ _ with { fb(e) = s * (e + (x' - x : min(3.4e38))); }));",
};
const pin = (src) => mode === "library" ? src : src.replace(/\bsi\.smoo\b/g, "nd_smoo").replace(/\bsi\.smooth\b/g, "nd_smooth").replace(/import\("stdfaust\.lib"\);/, (m) => `${m}\n${DEFS[mode]}\nnd_smoo = nd_smooth(1 - 44.1/ma.SR);`);
if (!["library", "old", "imp3"].includes(mode) || !["core", "smoother"].includes(set)) { console.error("bad --smoother or --items"); process.exit(2); }
const SMOOTHER_PATCHES = ["thruster", "advanced-alien-multivalve-graph", "advanced-bird-syrinx", "advanced-bird-syrinx-graph", "advanced-syrinx-voice", "advanced-human-larynx-graph", "dx7-algorithm-32-additive-organ", "dx7-algorithm-8-bright-pair"];
function smootherItems() {
const dir = join(here, "lowered-dings"), d = JSON.parse(readText(join(dir, "dings.json")));
return [
...SMOOTHER_PATCHES.map((n) => ({ id: n === "thruster" ? "thruster@rpm1200" : n, group: "patch", file: `${n}.dsp`, source: low(`${n}.dsp`), params: n === "thruster" ? { rpm: 1200 } : {}, seconds: 3 })),
...d.instruments.map((i) => ({ id: `ding-instrument/${i.id}`, group: "ding", file: `ding-${i.id}.dsp`, source: readText(join(dir, i.dsp)), params: { [i.controlPath]: i.rootFrequency }, seconds: d.noteSeconds })),
];
}
const items = set === "smoother" ? smootherItems() : [
...[300, 600, 1200, 4800].map((rpm) => ({ id: `thruster@rpm${rpm}`, group: "patch", file: "thruster.dsp", source: low("thruster.dsp"), params: { rpm }, seconds: 3 })),
...["sfxr-classic-classic-sfxr-abstract-golf", "sfxr-explosion"].map((n) => ({ id: n, group: "patch", file: `${n}.dsp`, source: low(`${n}.dsp`), params: {}, seconds: 3 })),
];
if (opt("--tract")) items.push({ id: "tract/vowel-a", group: "patch", file: "tract-vowel-a.dsp", source: readText(opt("--tract")), params: {}, seconds: 0.72 });
for (const it of items) it.source = pin(it.source);
mkdirSync(join(out, "dsp"), { recursive: true });
const readF32Wav = (p) => { const b = readFileSync(p), n = b.readUInt32LE(40) / 4, x = new Float32Array(n); for (let i = 0; i < n; i++) x[i] = b.readFloatLE(44 + i * 4); return x; };
const thresholds = JSON.parse(readText(join(here, "thresholds.json")));
const flagsOf = (id, group, m) => { const g = gate({ items: [{ id, group, metrics: m }] }, thresholds, { partial: true }); return [...g.failures.map((f) => `FAIL ${f.metric}`), ...g.waived.map((f) => `waived ${f.metric}`), ...g.warnings.map((f) => `warn ${f.metric}`)]; };
const rt = await loadRuntime(), rows = [];
for (const it of items) {
const w = (await renderOnce(await compile(rt, it.id, it.source), it.params, it.seconds)).samples;
const dsp = join(out, "dsp", it.file); writeFileSync(dsp, it.source);
const wav = join(out, `${it.id.replace(/[^A-Za-z0-9._@-]+/g, "_")}.native.wav`);
const r = spawnSync("dotnet", [bin.endsWith(".dll") ? bin : join(bin, "FaustNativeRender.dll"), dsp, wav, String(it.seconds), ...Object.entries(it.params).map(([k, v]) => `${k}=${v}`)], { encoding: "utf8" });
if (r.status !== 0) { rows.push({ id: it.id, error: (r.stderr || r.stdout).slice(0, 400) }); continue; }
const n = readF32Wav(wav); writeWav(join(out, `${it.id.replace(/[^A-Za-z0-9._@-]+/g, "_")}.wasm.wav`), w);
if (w.length !== n.length) { rows.push({ id: it.id, error: `length mismatch: wasm ${w.length}, native ${n.length}` }); continue; }
const len = w.length; let maxErr = 0, at = 0, sq = 0, sqRef = 0;
for (let i = 0; i < len; i++) { const d = Math.abs(w[i] - n[i]); if (d > maxErr) { maxErr = d; at = i; } sq += d * d; sqRef += w[i] * w[i]; }
const mw = measure(w), mn = measure(n);
rows.push({ id: it.id, lenWasm: w.length, lenNative: n.length, maxErr, maxErrAt: at, rmsErr: Math.sqrt(sq / len), relRmsErrDb: 10 * Math.log10(sq / Math.max(sqRef, 1e-30)),
wasm: pick(mw), native: pick(mn), flagsWasm: flagsOf(it.id, it.group, mw), flagsNative: flagsOf(it.id, it.group, mn) });
}
function pick(m) { return { samplePeak: m.samplePeak, truePeakDb: m.truePeakDb, loudnessLufs: m.loudnessLufs, centroidMedianHz: m.centroidMedianHz, overSamples: m.overSamples, rmsDb: m.rmsDb, crestDb: m.crestDb, noiseToToneDb: m.noiseToToneDb, noiseToToneADb: m.noiseToToneADb, lowMotion: m.lowMotion }; }
for (const r of rows) {
if (r.error) continue;
r.violations = [];
if (Math.abs(r.native.loudnessLufs - r.wasm.loudnessLufs) > LIMITS.lufsDb) r.violations.push("loudnessLufs");
if (Math.abs(r.native.truePeakDb - r.wasm.truePeakDb) > LIMITS.truePeakDb) r.violations.push("truePeakDb");
if (100 * Math.abs(r.native.centroidMedianHz - r.wasm.centroidMedianHz) / r.wasm.centroidMedianHz > LIMITS.centroidPct) r.violations.push("centroidMedianHz");
if (mode === "imp3" && r.relRmsErrDb > LIMITS.relRmsErrDbImp3) r.violations.push("relRmsErrDb");
}
writeFileSync(join(out, "native-diff.json"), JSON.stringify({ libfaustWasm: rt.version, sampleRate: SR, items: set, smoother: mode, rows }, null, 1));
if (rows.some((r) => r.error || r.violations.length)) process.exitCode = 1;
console.log(JSON.stringify({ libfaustWasm: rt.version, items: set, smoother: mode, rows }, null, 1));

View 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
View 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 text but a different compiler build and standard library, so not the same arithmetic; see docs/renders/aqua-feel-native-diff-2026-11-10.md).
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
View 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
View 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`);

View 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
View file

@ -0,0 +1,2 @@
node_modules/
dist/

View 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.

View 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
View 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[@]}"

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View file

@ -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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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": []
}
]

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View file

@ -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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

View 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);

Some files were not shown because too many files have changed in this diff Show more