commit 8629085d0ec9ec2ea231f14608265b52d996ec36
parent 799027d1080c0796c96d0451f4f90d59d64cb781
Author: minerva-jupiter <ryouturn@gmail.com>
Date: Thu, 30 Apr 2026 07:06:18 +0900
feat(synth): Implement initial polyphonic synthesizer
Diffstat:
| A | Cargo.lock | | | 1127 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | Cargo.toml | | | 7 | ++++--- |
| M | README.md | | | 2 | +- |
| M | src/lib.rs | | | 895 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
4 files changed, 1939 insertions(+), 92 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -0,0 +1,1127 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "addr2line"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "anyhow"
+version = "1.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+
+[[package]]
+name = "anymap3"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "170433209e817da6aae2c51aa0dd443009a613425dd041ebfb2492d1c4c11a25"
+
+[[package]]
+name = "assert_no_alloc"
+version = "1.1.2"
+source = "git+https://github.com/robbert-vdh/rust-assert-no-alloc.git?branch=feature%2Fnested-permit-forbid#a6fb4f62b9624715291e320ea5f0f70e73b035cf"
+dependencies = [
+ "backtrace",
+ "log",
+]
+
+[[package]]
+name = "atomic_float"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62af46d040ba9df09edc6528dae9d8e49f5f3e82f55b7d2ec31a733c38dbc49d"
+
+[[package]]
+name = "atomic_refcell"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21e4227379beff4205943696e6c3e0cd809bacdf3f0edd6e3dd153e2269571a4"
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-link",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
+
+[[package]]
+name = "camino"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "cargo-platform"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo_metadata"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
+dependencies = [
+ "camino",
+ "cargo-platform",
+ "semver",
+ "serde",
+ "serde_json",
+ "thiserror",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "chacha20"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "rand_core",
+]
+
+[[package]]
+name = "clap-sys"
+version = "0.5.0"
+source = "git+https://github.com/micahrj/clap-sys.git?rev=25d7f53fdb6363ad63fbd80049cb7a42a97ac156#25d7f53fdb6363ad63fbd80049cb7a42a97ac156"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "cpufeatures"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crossbeam"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "deranged"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "getrandom"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "rand_core",
+ "wasip2",
+ "wasip3",
+]
+
+[[package]]
+name = "gimli"
+version = "0.32.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
+
+[[package]]
+name = "goblin"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d6b4de4a8eb6c46a8c77e1d3be942cb9a8bf073c22374578e5ba4b08ed0ff68"
+dependencies = [
+ "log",
+ "plain",
+ "scroll",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+dependencies = [
+ "foldhash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "id-arena"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
+
+[[package]]
+name = "indexmap"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.17.0",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
+[[package]]
+name = "leb128fmt"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
+
+[[package]]
+name = "libc"
+version = "0.2.186"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
+
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+
+[[package]]
+name = "midi-consts"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f2dd5c7f8aaf48a76e389068ab25ed80bdbc226b887f9013844c415698c9952"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
+name = "molyxide"
+version = "0.1.0"
+dependencies = [
+ "nih_plug",
+ "rand",
+]
+
+[[package]]
+name = "nih_log"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0cdb52ef79af48ae110401c883bdb9c15e0306a99ab6ecf18bc52068b668e54"
+dependencies = [
+ "atty",
+ "log",
+ "once_cell",
+ "termcolor",
+ "time",
+ "windows",
+]
+
+[[package]]
+name = "nih_plug"
+version = "0.0.0"
+source = "git+https://github.com/robbert-vdh/nih-plug.git#28b149ec4d62757d0b448809148a0c3ca6e09a95"
+dependencies = [
+ "anyhow",
+ "anymap3",
+ "assert_no_alloc",
+ "atomic_float",
+ "atomic_refcell",
+ "backtrace",
+ "bitflags 1.3.2",
+ "cfg-if",
+ "clap-sys",
+ "core-foundation",
+ "crossbeam",
+ "libc",
+ "log",
+ "midi-consts",
+ "nih_log",
+ "nih_plug_derive",
+ "objc",
+ "parking_lot",
+ "raw-window-handle",
+ "serde",
+ "serde_json",
+ "widestring",
+ "windows",
+]
+
+[[package]]
+name = "nih_plug_derive"
+version = "0.1.0"
+source = "git+https://github.com/robbert-vdh/nih-plug.git#28b149ec4d62757d0b448809148a0c3ca6e09a95"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "nih_plug_xtask"
+version = "0.1.0"
+source = "git+https://github.com/robbert-vdh/nih-plug.git#28b149ec4d62757d0b448809148a0c3ca6e09a95"
+dependencies = [
+ "anyhow",
+ "cargo_metadata",
+ "goblin",
+ "reflink",
+ "serde",
+ "toml",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
+
+[[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
+[[package]]
+name = "object"
+version = "0.37.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-link",
+]
+
+[[package]]
+name = "plain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
+[[package]]
+name = "rand"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
+dependencies = [
+ "chacha20",
+ "getrandom",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
+
+[[package]]
+name = "raw-window-handle"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags 2.11.1",
+]
+
+[[package]]
+name = "reflink"
+version = "0.1.3"
+source = "git+https://github.com/nicokoch/reflink.git?rev=e8d93b465f5d9ad340cd052b64bbc77b8ee107e2#e8d93b465f5d9ad340cd052b64bbc77b8ee107e2"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "scroll"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
+dependencies = [
+ "scroll_derive",
+]
+
+[[package]]
+name = "scroll_derive"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
+dependencies = [
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "time"
+version = "0.3.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
+dependencies = [
+ "deranged",
+ "itoa",
+ "libc",
+ "num-conv",
+ "num_threads",
+ "powerfmt",
+ "serde_core",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
+
+[[package]]
+name = "time-macros"
+version = "0.2.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "toml"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
+[[package]]
+name = "wasip2"
+version = "1.0.3+wasi-0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
+dependencies = [
+ "wit-bindgen 0.57.1",
+]
+
+[[package]]
+name = "wasip3"
+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
+dependencies = [
+ "wit-bindgen 0.51.0",
+]
+
+[[package]]
+name = "wasm-encoder"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
+dependencies = [
+ "leb128fmt",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasm-metadata"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "wasm-encoder",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
+dependencies = [
+ "bitflags 2.11.1",
+ "hashbrown 0.15.5",
+ "indexmap",
+ "semver",
+]
+
+[[package]]
+name = "widestring"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.44.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "winnow"
+version = "0.5.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+dependencies = [
+ "wit-bindgen-rust-macro",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.57.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
+
+[[package]]
+name = "wit-bindgen-core"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
+dependencies = [
+ "anyhow",
+ "heck",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-bindgen-rust"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
+dependencies = [
+ "anyhow",
+ "heck",
+ "indexmap",
+ "prettyplease",
+ "syn 2.0.117",
+ "wasm-metadata",
+ "wit-bindgen-core",
+ "wit-component",
+]
+
+[[package]]
+name = "wit-bindgen-rust-macro"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
+dependencies = [
+ "anyhow",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "wit-bindgen-core",
+ "wit-bindgen-rust",
+]
+
+[[package]]
+name = "wit-component"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
+dependencies = [
+ "anyhow",
+ "bitflags 2.11.1",
+ "indexmap",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder",
+ "wasm-metadata",
+ "wasmparser",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-parser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser",
+]
+
+[[package]]
+name = "xtask"
+version = "0.1.0"
+dependencies = [
+ "nih_plug_xtask",
+]
+
+[[package]]
+name = "zmij"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
diff --git a/Cargo.toml b/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "Molyxide"
+name = "molyxide"
version = "0.1.0"
edition = "2021"
authors = ["Minerva_Juppiter <contact@minervajuppiter.net>"]
@@ -16,10 +16,11 @@ crate-type = ["cdylib"]
[dependencies]
# Remove the `assert_process_allocs` feature to allow allocations on the audio
# thread in debug builds.
-nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git", features = ["assert_process_allocs"] }
+# nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git", features = ["assert_process_allocs"] }
# Uncomment the below line to disable the on-by-default VST3 feature to remove
# the GPL compatibility requirement
-# nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git", default-features = false, features = ["assert_process_allocs"] }
+nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git", default-features = false, features = ["assert_process_allocs"] }
+rand = "0.10.1"
[profile.release]
lto = "thin"
diff --git a/README.md b/README.md
@@ -5,5 +5,5 @@
After installing [Rust](https://rustup.rs/), you can compile Molyxide as follows:
```shell
-cargo xtask bundle Molyxide --release
+cargo xtask bundle molyxide --release
```
diff --git a/src/lib.rs b/src/lib.rs
@@ -1,58 +1,690 @@
use nih_plug::prelude::*;
use std::sync::Arc;
-// This is a shortened version of the gain example with most comments removed, check out
-// https://github.com/robbert-vdh/nih-plug/blob/master/plugins/examples/gain/src/lib.rs to get
-// started
+// --- Enums ---
-struct Molyxide {
- params: Arc<MolyxideParams>,
+#[derive(Enum, PartialEq, Clone, Copy)]
+pub enum WaveformSelection {
+ #[name = "Sawtooth"]
+ Saw,
+ #[name = "Square"]
+ Square,
+ #[name = "Saw + Square"]
+ Both,
+}
+
+#[derive(Enum, PartialEq, Clone, Copy)]
+pub enum ResonatorMode {
+ #[name = "Low Pass"]
+ LowPass,
+ #[name = "Band Pass"]
+ BandPass,
+ #[name = "High Pass"]
+ HighPass,
+}
+
+#[derive(Enum, PartialEq, Clone, Copy)]
+pub enum FilterAlgorithm {
+ #[name = "Biquad (Standard)"]
+ Biquad,
+ #[name = "ZDF SVF (Analog-style)"]
+ ZdfSvf,
+}
+
+#[derive(Enum, PartialEq, Clone, Copy)]
+pub enum FinalDecayMode {
+ #[name = "Lock (No Release)"]
+ Lock,
+ #[name = "Manual (Release On)"]
+ Manual,
+}
+
+// --- DSP Modules ---
+
+/// ZDF SVF (Topology Preserving Transform)
+struct ZdfSvf {
+ s1: f32,
+ s2: f32,
+}
+
+impl ZdfSvf {
+ fn new() -> Self {
+ Self { s1: 0.0, s2: 0.0 }
+ }
+ fn process(&mut self, input: f32, mode: ResonatorMode, freq: f32, emph: f32, sr: f32) -> f32 {
+ let q = (0.5 + emph * 9.5).max(0.01);
+ let g = (std::f32::consts::PI * freq / sr).tan();
+ let k = 1.0 / q;
+ let a1 = 1.0 / (1.0 + g * (g + k));
+ let a2 = g * a1;
+ let a3 = g * a2;
+ let m_v3 = input - self.s2;
+ let v1 = a1 * self.s1 + a2 * m_v3;
+ let v2 = self.s2 + a2 * self.s1 + a3 * m_v3;
+ self.s1 = 2.0 * v1 - self.s1;
+ self.s2 = 2.0 * v2 - self.s2;
+ match mode {
+ ResonatorMode::LowPass => v2,
+ ResonatorMode::BandPass => v1,
+ ResonatorMode::HighPass => input - k * v1 - v2,
+ }
+ }
+}
+
+/// ZDF Ladder Filter (24dB/oct)
+/// Moog ăčăżă€ă«ăź 4-pole ăăŒăăčăăŁă«ăżăŒ
+struct LadderFilter {
+ s: [f32; 4],
+}
+
+impl LadderFilter {
+ fn new() -> Self {
+ Self { s: [0.0; 4] }
+ }
+ fn process(&mut self, input: f32, cutoff: f32, resonance: f32, sr: f32) -> f32 {
+ let g = (std::f32::consts::PI * cutoff / sr).tan();
+ let k = 4.0 * resonance.clamp(0.0, 0.99); // èȘ㱿ŻăéżăăăăćŸźèȘżæŽ
+
+ // 1-pole äżæ°
+ let a = 1.0 / (1.0 + g);
+
+ // ăăŁăŒăăăăŻèŁæŁ
+ let g_pow4 = g * g * g * g;
+ let sigma = g_pow4 * a * a * a * a;
+ let gamma = 1.0 / (1.0 + k * sigma);
+
+ // ć
éšç¶æ
ăăăźăăŁăŒăăăăŻ
+ let input_fb = (input
+ - k * (self.s[0] * a
+ + self.s[1] * a * a
+ + self.s[2] * a * a * a
+ + self.s[3] * a * a * a * a))
+ * gamma;
+
+ // 4æź”ăź 1-pole ăăŁă«ăżăŒăźçŽććŠç
+ let mut vin = input_fb;
+ for i in 0..4 {
+ let v = (vin - self.s[i]) * g * a;
+ let out = v + self.s[i];
+ self.s[i] = out + v;
+ vin = out;
+ }
+
+ vin // 4æź”çźăźćșć
+ }
+}
+
+// --- Parameters ---
+
+#[derive(Params)]
+pub struct MixerParams {
+ #[id = "m_tune"]
+ pub master_tune: FloatParam,
+ #[id = "mix_dir"]
+ pub direct: FloatParam,
+ #[id = "mix_mode"]
+ pub mode: FloatParam,
+ #[id = "mix_res"]
+ pub resonator: FloatParam,
+ #[id = "mix_vcf"]
+ pub vcf: FloatParam,
+}
+
+#[derive(Params)]
+pub struct OscParams {
+ #[id = "l_wave"]
+ pub lower_waveshape: EnumParam<WaveformSelection>,
+ #[id = "u_wave"]
+ pub upper_waveshape: EnumParam<WaveformSelection>,
+ #[id = "l_pw"]
+ pub lower_pw: FloatParam,
+ #[id = "u_pw"]
+ pub upper_pw: FloatParam,
+ #[id = "l_mix"]
+ pub lower_mix: FloatParam,
+ #[id = "u_mix"]
+ pub upper_mix: FloatParam,
+ #[id = "rank_a_tune"]
+ pub rank_a_tune: FloatParam,
+ #[id = "rank_b_beat"]
+ pub rank_b_beat: FloatParam,
+ #[id = "pwm_rate"]
+ pub pwm_rate: FloatParam,
+ #[id = "pwm_amt"]
+ pub pwm_amt: FloatParam,
+ #[id = "fm_rate"]
+ pub fm_rate: FloatParam,
+ #[id = "fm_amt"]
+ pub fm_amt: FloatParam,
+}
+
+#[derive(Params)]
+pub struct ResonatorParams {
+ #[id = "res_on"]
+ pub enabled: BoolParam,
+ #[id = "res_mode"]
+ pub mode: EnumParam<ResonatorMode>,
+ #[id = "res_l_cf"]
+ pub low_cf: FloatParam,
+ #[id = "res_l_emph"]
+ pub low_emph: FloatParam,
+ #[id = "res_l_gain"]
+ pub low_gain: FloatParam,
+ #[id = "res_m_cf"]
+ pub mid_cf: FloatParam,
+ #[id = "res_m_emph"]
+ pub mid_emph: FloatParam,
+ #[id = "res_m_gain"]
+ pub mid_gain: FloatParam,
+ #[id = "res_h_cf"]
+ pub high_cf: FloatParam,
+ #[id = "res_h_emph"]
+ pub high_emph: FloatParam,
+ #[id = "res_h_gain"]
+ pub high_gain: FloatParam,
+}
+
+#[derive(Params)]
+pub struct VcfParams {
+ #[id = "vcf_on"]
+ pub enabled: BoolParam,
+ #[id = "vcf_cutoff"]
+ pub cutoff: FloatParam,
+ #[id = "vcf_emph"]
+ pub emphasis: FloatParam,
+ #[id = "vcf_kb"]
+ pub kb_track: FloatParam,
+ #[id = "vcf_glide"]
+ pub kb_glide: FloatParam,
+ #[id = "vcf_lfo_r"]
+ pub lfo_rate: FloatParam,
+ #[id = "vcf_lfo_a"]
+ pub lfo_amt: FloatParam,
+ #[id = "vcf_lfo_sh"]
+ pub lfo_sh: FloatParam,
+ #[id = "vcf_env_a"]
+ pub attack: FloatParam,
+ #[id = "vcf_env_d"]
+ pub decay: FloatParam,
+ #[id = "vcf_env_s"]
+ pub sustain: FloatParam,
+ #[id = "vcf_env_amt"]
+ pub env_amt: FloatParam,
+}
+
+#[derive(Params)]
+pub struct AmpParams {
+ #[id = "amp_a"]
+ pub attack: FloatParam,
+ #[id = "amp_s"]
+ pub sustain: FloatParam,
+ #[id = "amp_dr"]
+ pub decay_release: FloatParam,
+ #[id = "amp_vel"]
+ pub velocity: FloatParam,
+ #[id = "amp_f_dec"]
+ pub final_decay: EnumParam<FinalDecayMode>,
}
#[derive(Params)]
struct MolyxideParams {
- /// The parameter's ID is used to identify the parameter in the wrappred plugin API. As long as
- /// these IDs remain constant, you can rename and reorder these fields as you wish. The
- /// parameters are exposed to the host in the same order they were defined. In this case, this
- /// gain parameter is stored as linear gain while the values are displayed in decibels.
+ #[nested(group = "1. Mixer & Tune")]
+ pub mixer: MixerParams,
+ #[nested(group = "2. Oscillators")]
+ pub osc: OscParams,
+ #[nested(group = "3. Resonator")]
+ pub res: ResonatorParams,
+ #[nested(group = "4. VCF (Master Filter)")]
+ pub vcf: VcfParams,
+ #[nested(group = "5. Loudness (Per-Voice)")]
+ pub amp: AmpParams,
#[id = "gain"]
pub gain: FloatParam,
}
-impl Default for Molyxide {
+impl Default for MolyxideParams {
fn default() -> Self {
Self {
- params: Arc::new(MolyxideParams::default()),
+ mixer: MixerParams {
+ master_tune: FloatParam::new(
+ "Master Tune",
+ 0.0,
+ FloatRange::Linear {
+ min: -200.0,
+ max: 200.0,
+ },
+ ),
+ direct: FloatParam::new(
+ "Direct Level",
+ 0.5,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ mode: FloatParam::new("Mode Level", 0.0, FloatRange::Linear { min: 0.0, max: 1.0 }),
+ resonator: FloatParam::new(
+ "Resonator Level",
+ 0.5,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ vcf: FloatParam::new("VCF Level", 0.5, FloatRange::Linear { min: 0.0, max: 1.0 }),
+ },
+ osc: OscParams {
+ lower_waveshape: EnumParam::new("Lower Waveshape", WaveformSelection::Saw),
+ upper_waveshape: EnumParam::new("Upper Waveshape", WaveformSelection::Square),
+ lower_pw: FloatParam::new(
+ "Lower PW",
+ 0.5,
+ FloatRange::Linear {
+ min: 0.05,
+ max: 0.5,
+ },
+ ),
+ upper_pw: FloatParam::new(
+ "Upper PW",
+ 0.2,
+ FloatRange::Linear {
+ min: 0.05,
+ max: 0.5,
+ },
+ ),
+ lower_mix: FloatParam::new(
+ "Lower Rank Mix",
+ 0.5,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ upper_mix: FloatParam::new(
+ "Upper Rank Mix",
+ 0.5,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ rank_a_tune: FloatParam::new(
+ "Rank A Tune",
+ 0.0,
+ FloatRange::Linear {
+ min: -600.0,
+ max: 600.0,
+ },
+ ),
+ rank_b_beat: FloatParam::new(
+ "Rank B Beat",
+ 0.0,
+ FloatRange::Linear {
+ min: -50.0,
+ max: 50.0,
+ },
+ ),
+ pwm_rate: FloatParam::new(
+ "PWM Rate",
+ 0.5,
+ FloatRange::Skewed {
+ min: 0.05,
+ max: 25.0,
+ factor: FloatRange::skew_factor(-0.5),
+ },
+ ),
+ pwm_amt: FloatParam::new(
+ "PWM Amount",
+ 0.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ fm_rate: FloatParam::new(
+ "FM Rate",
+ 6.0,
+ FloatRange::Skewed {
+ min: 0.05,
+ max: 25.0,
+ factor: FloatRange::skew_factor(-0.5),
+ },
+ ),
+ fm_amt: FloatParam::new(
+ "FM Amount",
+ 0.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ },
+ res: ResonatorParams {
+ enabled: BoolParam::new("Resonator Enabled", true),
+ mode: EnumParam::new("Pass Mode", ResonatorMode::LowPass),
+ low_cf: FloatParam::new(
+ "Low CF",
+ 200.0,
+ FloatRange::Skewed {
+ min: 20.0,
+ max: 20000.0,
+ factor: FloatRange::skew_factor(-0.9),
+ },
+ ),
+ low_emph: FloatParam::new(
+ "Low Emphasis",
+ 0.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ low_gain: FloatParam::new(
+ "Low Gain",
+ 0.5,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ mid_cf: FloatParam::new(
+ "Mid CF",
+ 1000.0,
+ FloatRange::Skewed {
+ min: 20.0,
+ max: 20000.0,
+ factor: FloatRange::skew_factor(-0.9),
+ },
+ ),
+ mid_emph: FloatParam::new(
+ "Mid Emphasis",
+ 0.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ mid_gain: FloatParam::new(
+ "Mid Gain",
+ 0.5,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ high_cf: FloatParam::new(
+ "High CF",
+ 5000.0,
+ FloatRange::Skewed {
+ min: 20.0,
+ max: 20000.0,
+ factor: FloatRange::skew_factor(-0.9),
+ },
+ ),
+ high_emph: FloatParam::new(
+ "High Emphasis",
+ 0.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ high_gain: FloatParam::new(
+ "High Gain",
+ 0.5,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ },
+ vcf: VcfParams {
+ enabled: BoolParam::new("VCF Enabled", true),
+ cutoff: FloatParam::new(
+ "VCF Cutoff",
+ 20000.0,
+ FloatRange::Skewed {
+ min: 20.0,
+ max: 20000.0,
+ factor: FloatRange::skew_factor(-0.9),
+ },
+ ),
+ emphasis: FloatParam::new(
+ "VCF Emphasis",
+ 0.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ kb_track: FloatParam::new(
+ "Keyboard Track",
+ 1.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ kb_glide: FloatParam::new(
+ "Keyboard Glide",
+ 0.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ lfo_rate: FloatParam::new(
+ "VCF LFO Rate",
+ 6.0,
+ FloatRange::Skewed {
+ min: 0.05,
+ max: 25.0,
+ factor: FloatRange::skew_factor(-0.5),
+ },
+ ),
+ lfo_amt: FloatParam::new(
+ "VCF LFO Amount",
+ 0.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ lfo_sh: FloatParam::new(
+ "VCF S&H Amount",
+ 0.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ attack: FloatParam::new(
+ "VCF Attack",
+ 0.01,
+ FloatRange::Skewed {
+ min: 0.01,
+ max: 10.0,
+ factor: FloatRange::skew_factor(-0.8),
+ },
+ ),
+ decay: FloatParam::new(
+ "VCF Decay",
+ 0.1,
+ FloatRange::Skewed {
+ min: 0.01,
+ max: 10.0,
+ factor: FloatRange::skew_factor(-0.8),
+ },
+ ),
+ sustain: FloatParam::new(
+ "VCF Sustain",
+ 1.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ env_amt: FloatParam::new(
+ "VCF Envelope Amount",
+ 0.0,
+ FloatRange::Linear {
+ min: -1.0,
+ max: 1.0,
+ },
+ ),
+ },
+ amp: AmpParams {
+ attack: FloatParam::new(
+ "Amp Attack",
+ 0.01,
+ FloatRange::Skewed {
+ min: 0.01,
+ max: 10.0,
+ factor: FloatRange::skew_factor(-0.8),
+ },
+ ),
+ sustain: FloatParam::new(
+ "Amp Sustain",
+ 1.0,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ decay_release: FloatParam::new(
+ "Amp Decay/Release",
+ 0.1,
+ FloatRange::Skewed {
+ min: 0.01,
+ max: 10.0,
+ factor: FloatRange::skew_factor(-0.8),
+ },
+ ),
+ velocity: FloatParam::new(
+ "Keyboard Dynamics",
+ 0.5,
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
+ final_decay: EnumParam::new("Final Decay", FinalDecayMode::Manual),
+ },
+ gain: FloatParam::new(
+ "Gain",
+ util::db_to_gain(0.0),
+ FloatRange::Linear { min: 0.0, max: 1.0 },
+ ),
}
}
}
-impl Default for MolyxideParams {
- fn default() -> Self {
+// --- TOG (Top Octave Generator) ---
+
+struct TopOctaveGenerator {
+ phases: [f64; 12],
+ wrap_counts: [u64; 12],
+}
+
+impl TopOctaveGenerator {
+ fn new() -> Self {
Self {
- // This gain is stored as linear gain. NIH-plug comes with useful conversion functions
- // to treat these kinds of parameters as if we were dealing with decibels. Storing this
- // as decibels is easier to work with, but requires a conversion for every sample.
- gain: FloatParam::new(
- "Gain",
- util::db_to_gain(0.0),
- FloatRange::Skewed {
- min: util::db_to_gain(-30.0),
- max: util::db_to_gain(30.0),
- // This makes the range appear as if it was linear when displaying the values as
- // decibels
- factor: FloatRange::gain_skew_factor(-30.0, 30.0),
- },
+ phases: [0.0; 12],
+ wrap_counts: [0; 12],
+ }
+ }
+ fn process(&mut self, sample_rate: f32, frequencies: &[f32; 12]) {
+ for i in 0..12 {
+ self.phases[i] += frequencies[i] as f64 / sample_rate as f64;
+ if self.phases[i] >= 1.0 {
+ self.phases[i] -= 1.0;
+ self.wrap_counts[i] = self.wrap_counts[i].wrapping_add(1);
+ }
+ }
+ }
+}
+
+// --- Poly Chip ---
+
+const NUM_CHIPS: usize = 71;
+const BASE_NOTE: u8 = 29; // F1
+
+struct PolyChip {
+ note: u8,
+ tog_index: usize,
+ division: f64,
+ gate: bool,
+ velocity: f32,
+ samples_since_on: u32,
+ samples_since_off: u32,
+ release_start_level: f32,
+}
+
+impl PolyChip {
+ fn new(note: u8) -> Self {
+ let tog_index = (note % 12) as usize;
+ let division = 2.0f64.powi((108 + (tog_index as i32) - (note as i32)) / 12);
+ Self {
+ note,
+ tog_index,
+ division,
+ gate: false,
+ velocity: 0.0,
+ samples_since_on: 0,
+ samples_since_off: 0,
+ release_start_level: 0.0,
+ }
+ }
+ fn get_envelope(&self, params: &AmpParams, sr: f32) -> f32 {
+ let a = params.attack.value();
+ let dr = params.decay_release.value();
+ let s = params.sustain.value();
+ if self.gate {
+ let t = self.samples_since_on as f32 / sr;
+ if t < a {
+ t / a.max(0.0001)
+ } else {
+ 1.0 - ((t - a) / dr.max(0.0001)).clamp(0.0, 1.0) * (1.0 - s)
+ }
+ } else {
+ let t = self.samples_since_off as f32 / sr;
+ if params.final_decay.value() == FinalDecayMode::Manual {
+ self.release_start_level * (1.0 - (t / dr.max(0.0001)).clamp(0.0, 1.0))
+ } else {
+ 0.0
+ }
+ }
+ }
+ fn process(
+ &mut self,
+ t_a: f64,
+ w_a: u64,
+ t_b: f64,
+ w_b: u64,
+ params: &MolyxideParams,
+ sr: f32,
+ pwm: f32,
+ ) -> f32 {
+ let env = self.get_envelope(¶ms.amp, sr);
+ if !self.gate && env <= 0.0 {
+ return 0.0;
+ }
+ let (ws, base_pw, mix) = if self.note < 49 {
+ (
+ params.osc.lower_waveshape.value(),
+ params.osc.lower_pw.value(),
+ params.osc.lower_mix.value(),
)
- // Because the gain parameter is stored as linear gain instead of storing the value as
- // decibels, we need logarithmic smoothing
- .with_smoother(SmoothingStyle::Logarithmic(50.0))
- .with_unit(" dB")
- // There are many predefined formatters we can use here. If the gain was stored as
- // decibels instead of as a linear gain value, we could have also used the
- // `.with_step_size(0.1)` function to get internal rounding.
- .with_value_to_string(formatters::v2s_f32_gain_to_db(2))
- .with_string_to_value(formatters::s2v_f32_gain_to_db()),
+ } else {
+ (
+ params.osc.upper_waveshape.value(),
+ params.osc.upper_pw.value(),
+ params.osc.upper_mix.value(),
+ )
+ };
+ let p_a = ((w_a as f64 + t_a) / self.division).fract();
+ let sig_a = (p_a * 2.0 - 1.0) as f32;
+ let p_b = ((w_b as f64 + t_b) / self.division).fract();
+ let sig_b = if p_b
+ < (base_pw as f64 + pwm as f64 * params.osc.pwm_amt.value() as f64 * 0.4)
+ .clamp(0.05, 0.95)
+ {
+ 1.0
+ } else {
+ -1.0
+ };
+ let raw = match ws {
+ WaveformSelection::Saw => sig_a,
+ WaveformSelection::Square => sig_b,
+ WaveformSelection::Both => sig_a * mix + sig_b * (1.0 - mix),
+ };
+ if self.gate {
+ self.samples_since_on = self.samples_since_on.wrapping_add(1);
+ } else {
+ self.samples_since_off = self.samples_since_off.wrapping_add(1);
+ }
+ raw * env * (1.0 - (1.0 - self.velocity) * params.amp.velocity.value())
+ }
+}
+
+// --- Main Plugin ---
+
+struct Molyxide {
+ params: Arc<MolyxideParams>,
+ sample_rate: f32,
+ tog_a: TopOctaveGenerator,
+ tog_b: TopOctaveGenerator,
+ chips: Vec<PolyChip>,
+ fm_phase: f32,
+ pwm_phase: f32,
+ res_low: ZdfSvf,
+ res_mid: ZdfSvf,
+ res_high: ZdfSvf,
+ vcf: LadderFilter,
+ vcf_samples_on: u32,
+ vcf_last_note_id: u8,
+}
+
+impl Default for Molyxide {
+ fn default() -> Self {
+ let mut chips = Vec::with_capacity(NUM_CHIPS);
+ for i in 0..NUM_CHIPS {
+ chips.push(PolyChip::new(BASE_NOTE + i as u8));
+ }
+ Self {
+ params: Arc::new(MolyxideParams::default()),
+ sample_rate: 44100.0,
+ tog_a: TopOctaveGenerator::new(),
+ tog_b: TopOctaveGenerator::new(),
+ chips,
+ fm_phase: 0.0,
+ pwm_phase: 0.0,
+ res_low: ZdfSvf::new(),
+ res_mid: ZdfSvf::new(),
+ res_high: ZdfSvf::new(),
+ vcf: LadderFilter::new(),
+ vcf_samples_on: 0,
+ vcf_last_note_id: 0,
}
}
}
@@ -62,96 +694,183 @@ impl Plugin for Molyxide {
const VENDOR: &'static str = "Minerva_Juppiter";
const URL: &'static str = env!("CARGO_PKG_HOMEPAGE");
const EMAIL: &'static str = "contact@minervajuppiter.net";
-
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
-
- // The first audio IO layout is used as the default. The other layouts may be selected either
- // explicitly or automatically by the host or the user depending on the plugin API/backend.
const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[AudioIOLayout {
main_input_channels: NonZeroU32::new(2),
main_output_channels: NonZeroU32::new(2),
-
aux_input_ports: &[],
aux_output_ports: &[],
-
- // Individual ports and the layout as a whole can be named here. By default these names
- // are generated as needed. This layout will be called 'Stereo', while a layout with
- // only one input and output channel would be called 'Mono'.
names: PortNames::const_default(),
}];
-
-
- const MIDI_INPUT: MidiConfig = MidiConfig::None;
+ const MIDI_INPUT: MidiConfig = MidiConfig::Basic;
const MIDI_OUTPUT: MidiConfig = MidiConfig::None;
-
const SAMPLE_ACCURATE_AUTOMATION: bool = true;
-
- // If the plugin can send or receive SysEx messages, it can define a type to wrap around those
- // messages here. The type implements the `SysExMessage` trait, which allows conversion to and
- // from plain byte buffers.
type SysExMessage = ();
- // More advanced plugins can use this to run expensive background tasks. See the field's
- // documentation for more information. `()` means that the plugin does not have any background
- // tasks.
type BackgroundTask = ();
fn params(&self) -> Arc<dyn Params> {
self.params.clone()
}
-
fn initialize(
&mut self,
- _audio_io_layout: &AudioIOLayout,
- _buffer_config: &BufferConfig,
- _context: &mut impl InitContext<Self>,
+ _: &AudioIOLayout,
+ bc: &BufferConfig,
+ _: &mut impl InitContext<Self>,
) -> bool {
- // Resize buffers and perform other potentially expensive initialization operations here.
- // The `reset()` function is always called right after this function. You can remove this
- // function if you do not need it.
+ self.sample_rate = bc.sample_rate;
true
}
-
- fn reset(&mut self) {
- // Reset buffers and envelopes here. This can be called from the audio thread and may not
- // allocate. You can remove this function if you do not need it.
- }
-
fn process(
&mut self,
buffer: &mut Buffer,
- _aux: &mut AuxiliaryBuffers,
- _context: &mut impl ProcessContext<Self>,
+ _: &mut AuxiliaryBuffers,
+ context: &mut impl ProcessContext<Self>,
) -> ProcessStatus {
- for channel_samples in buffer.iter_samples() {
- // Smoothing is optionally built into the parameters themselves
- let gain = self.params.gain.smoothed.next();
+ let mut next_event = context.next_event();
+ let sr = self.sample_rate;
+ for (sample_id, channel_samples) in buffer.iter_samples().enumerate() {
+ while let Some(event) = next_event {
+ if event.timing() > sample_id as u32 {
+ break;
+ }
+ match event {
+ NoteEvent::NoteOn { note, velocity, .. } => {
+ if let Some(c) = self.chips.iter_mut().find(|c| c.note == note) {
+ c.gate = true;
+ c.velocity = velocity;
+ c.samples_since_on = 0;
+ c.samples_since_off = 0;
+ // VCF Contour ăȘă»ăă
+ self.vcf_samples_on = 0;
+ self.vcf_last_note_id = note;
+ }
+ }
+ NoteEvent::NoteOff { note, .. } => {
+ if let Some(c) = self.chips.iter_mut().find(|c| c.note == note) {
+ if c.gate {
+ c.gate = false;
+ c.samples_since_off = 0;
+ c.release_start_level = c.get_envelope(&self.params.amp, sr);
+ }
+ }
+ }
+ _ => (),
+ }
+ next_event = context.next_event();
+ }
+ self.fm_phase += self.params.osc.fm_rate.value() / sr;
+ if self.fm_phase >= 1.0 {
+ self.fm_phase -= 1.0;
+ }
+ let fm_lfo = (self.fm_phase * 2.0 * std::f32::consts::PI).sin();
+ self.pwm_phase += self.params.osc.pwm_rate.value() / sr;
+ if self.pwm_phase >= 1.0 {
+ self.pwm_phase -= 1.0;
+ }
+ let pwm_lfo = (self.pwm_phase * 2.0 * std::f32::consts::PI).sin();
+
+ let tune = self.params.mixer.master_tune.value()
+ + fm_lfo * self.params.osc.fm_amt.value() * 50.0;
+ let mut freqs_a = [0.0f32; 12];
+ let mut freqs_b = [0.0f32; 12];
+ for i in 0..12 {
+ let base = util::midi_note_to_freq(108 + i as u8);
+ freqs_a[i] = base * 2.0f32.powf(tune / 1200.0);
+ freqs_b[i] = freqs_a[i] * 2.0f32.powf(self.params.osc.rank_b_beat.value() / 1200.0);
+ }
+ self.tog_a.process(sr, &freqs_a);
+ self.tog_b.process(sr, &freqs_b);
+
+ let mut mixed = 0.0;
+ for c in &mut self.chips {
+ mixed += c.process(
+ self.tog_a.phases[c.tog_index],
+ self.tog_a.wrap_counts[c.tog_index],
+ self.tog_b.phases[c.tog_index],
+ self.tog_b.wrap_counts[c.tog_index],
+ &self.params,
+ sr,
+ pwm_lfo,
+ );
+ }
+
+ // Resonator Path
+ let mut res_out = 0.0;
+ if self.params.res.enabled.value() {
+ let mode = self.params.res.mode.value();
+ res_out = self.res_low.process(
+ mixed,
+ mode,
+ self.params.res.low_cf.value(),
+ self.params.res.low_emph.value(),
+ sr,
+ ) * self.params.res.low_gain.value()
+ + self.res_mid.process(
+ mixed,
+ mode,
+ self.params.res.mid_cf.value(),
+ self.params.res.mid_emph.value(),
+ sr,
+ ) * self.params.res.mid_gain.value()
+ + self.res_high.process(
+ mixed,
+ mode,
+ self.params.res.high_cf.value(),
+ self.params.res.high_emph.value(),
+ sr,
+ ) * self.params.res.high_gain.value();
+ }
+
+ // VCF Path
+ let mut vcf_out = 0.0;
+ if self.params.vcf.enabled.value() {
+ // VCF Envelope (Contour)
+ let t = self.vcf_samples_on as f32 / sr;
+ let v_a = self.params.vcf.attack.value();
+ let v_d = self.params.vcf.decay.value();
+ let v_s = self.params.vcf.sustain.value();
+ let v_env = if t < v_a {
+ t / v_a.max(0.0001)
+ } else {
+ 1.0 - ((t - v_a) / v_d.max(0.0001)).clamp(0.0, 1.0) * (1.0 - v_s)
+ };
+
+ // Cutoff calculation with Envelope and Keyboard Tracking
+ let kb_offset =
+ (self.vcf_last_note_id as f32 - 60.0) / 12.0 * self.params.vcf.kb_track.value();
+ let cutoff = self.params.vcf.cutoff.value()
+ * 2.0f32.powf(kb_offset + v_env * self.params.vcf.env_amt.value() * 4.0);
+
+ vcf_out = self.vcf.process(
+ mixed,
+ cutoff.clamp(20.0, 20000.0),
+ self.params.vcf.emphasis.value(),
+ sr,
+ );
+ self.vcf_samples_on = self.vcf_samples_on.wrapping_add(1);
+ }
+
+ let direct = mixed * self.params.mixer.direct.value();
+ let res_path = res_out * self.params.mixer.resonator.value();
+ let vcf_path = vcf_out * self.params.mixer.vcf.value();
+
+ let output = (direct + res_path + vcf_path) * 0.02 * self.params.gain.value();
for sample in channel_samples {
- *sample *= gain;
+ *sample = output;
}
}
-
ProcessStatus::Normal
}
+ fn reset(&mut self) {}
}
impl ClapPlugin for Molyxide {
const CLAP_ID: &'static str = "net.minervajuppiter.Molyxide";
- const CLAP_DESCRIPTION: Option<&'static str> = Some("Simple Polyphonic Synthesizer");
+ const CLAP_DESCRIPTION: Option<&'static str> = Some("A Simple Polyphonic Synthesizer");
const CLAP_MANUAL_URL: Option<&'static str> = Some(Self::URL);
const CLAP_SUPPORT_URL: Option<&'static str> = None;
-
- // Don't forget to change these features
- const CLAP_FEATURES: &'static [ClapFeature] = &[ClapFeature::AudioEffect, ClapFeature::Stereo];
-}
-
-impl Vst3Plugin for Molyxide {
- const VST3_CLASS_ID: [u8; 16] = *b"Exactly16Chars!!";
-
- // And also don't forget to change these categories
- const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] =
- &[Vst3SubCategory::Fx, Vst3SubCategory::Dynamics];
+ const CLAP_FEATURES: &'static [ClapFeature] = &[ClapFeature::Synthesizer, ClapFeature::Stereo];
}
nih_export_clap!(Molyxide);
-nih_export_vst3!(Molyxide);