summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWavy Harp <wavyharp@gmail.com>2023-05-07 23:04:53 -0600
committerKyle McFarland <tfkyle@gmail.com>2023-05-07 23:04:53 -0600
commit991849b32acf83dd14a5096540bb053d2572502a (patch)
tree279b59d75d4ad6081f5242cf77d843ae6b37fc3d
downloadrustynotes-master.zip
rustynotes-master.tar.gz
rustynotes-master.tar.bz2
initial importHEADmaster
currently everything is very tied to alsa and my system, for the moment you'll need to manually change the device names and maybe channels/period_size in src/main.rs, src/bin/smolguitar.rs and src/bin/loopbacker.rs, i'll fix that and add runtime period size/updater allocation soon, (and probably make a cpal backend as well so it can work on other platforms), but doing this initial commit to play around with stereo for now~
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock833
-rw-r--r--Cargo.toml33
-rw-r--r--colourschemes/adam_neely.json14
-rw-r--r--colourschemes/notes.txt27
-rw-r--r--colourschemes/tango.json9
-rw-r--r--colourschemes/wavy1.json9
-rw-r--r--colourschemes/wavy2.json9
-rw-r--r--colourschemes/wavy3.json9
-rw-r--r--src/args.rs140
-rw-r--r--src/bin/loopbacker.rs48
-rw-r--r--src/bin/smolguitar.rs44
-rw-r--r--src/bin/test_serde.rs22
-rw-r--r--src/bin/testrfft.rs20
-rw-r--r--src/buf.rs244
-rw-r--r--src/lib.rs6
-rw-r--r--src/main.rs66
-rw-r--r--src/notes.rs168
-rw-r--r--src/outputers.rs266
-rw-r--r--src/proc.rs162
-rw-r--r--src/rfft.rs179
21 files changed, 2309 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..485fb13
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,833 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "alsa"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8512c9117059663fb5606788fbca3619e2a91dac0e3fe516242eab1fa6be5e44"
+dependencies = [
+ "alsa-sys",
+ "bitflags",
+ "libc",
+ "nix",
+]
+
+[[package]]
+name = "alsa-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "ansi_colours"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7db9d9767fde724f83933a716ee182539788f293828244e9d999695ce0f7ba1e"
+dependencies = [
+ "rgb",
+]
+
+[[package]]
+name = "anstream"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is-terminal",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bytemuck"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
+
+[[package]]
+name = "cassowary"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+ "once_cell",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "bitflags",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
+name = "crossterm"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13"
+dependencies = [
+ "bitflags",
+ "crossterm_winapi",
+ "libc",
+ "mio",
+ "parking_lot",
+ "serde",
+ "signal-hook",
+ "signal-hook-mio",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm_winapi"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "derive_more"
+version = "0.99.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
+dependencies = [
+ "hermit-abi",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+
+[[package]]
+name = "libc"
+version = "0.2.141"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
+
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
+dependencies = [
+ "libc",
+ "log",
+ "wasi",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "nix"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+
+[[package]]
+name = "primal-check"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9df7f93fd637f083201473dab4fee2db4c429d32e55e3299980ab3957ab916a0"
+dependencies = [
+ "num-integer",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ratatui"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcc0d032bccba900ee32151ec0265667535c230169f5a011154cdcd984e16829"
+dependencies = [
+ "bitflags",
+ "cassowary",
+ "crossterm",
+ "serde",
+ "unicode-segmentation",
+ "unicode-width",
+]
+
+[[package]]
+name = "realfft"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93d6b8e8f0c6d2234aa58048d7290c60bf92cd36fd2888cd8331c66ad4f2e1d2"
+dependencies = [
+ "rustfft",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "rgb"
+version = "0.8.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustfft"
+version = "6.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e17d4f6cbdb180c9f4b2a26bbf01c4e647f1e1dea22fe8eb9db54198b32f9434"
+dependencies = [
+ "num-complex",
+ "num-integer",
+ "num-traits",
+ "primal-check",
+ "strength_reduce",
+ "transpose",
+ "version_check",
+]
+
+[[package]]
+name = "rustix"
+version = "0.37.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rustynotes"
+version = "0.1.0"
+dependencies = [
+ "alsa",
+ "ansi_colours",
+ "clap",
+ "crossterm",
+ "derive_more",
+ "num-traits",
+ "ratatui",
+ "realfft",
+ "rustfft",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "semver"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
+
+[[package]]
+name = "serde"
+version = "1.0.160"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.160"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "signal-hook"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-mio"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
+dependencies = [
+ "libc",
+ "mio",
+ "signal-hook",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+
+[[package]]
+name = "strength_reduce"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[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.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "transpose"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6522d49d03727ffb138ae4cbc1283d3774f0d10aa7f9bf52e6784c45daf9b23"
+dependencies = [
+ "num-integer",
+ "strength_reduce",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[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-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.0",
+ "windows_aarch64_msvc 0.48.0",
+ "windows_i686_gnu 0.48.0",
+ "windows_i686_msvc 0.48.0",
+ "windows_x86_64_gnu 0.48.0",
+ "windows_x86_64_gnullvm 0.48.0",
+ "windows_x86_64_msvc 0.48.0",
+]
+
+[[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_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[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_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[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_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[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_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..1f08b77
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,33 @@
+[package]
+name = "rustynotes"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[profile.release]
+opt-level = 3
+# incremental seems to allow the compiler to save autovectorization
+# info and makes the fold version autovectorize properly :o
+incremental = true
+
+[dependencies]
+alsa = "0.7.0"
+derive_more = {version = "0.99.17", features=["display"]}
+rustfft = "6.1.0"
+realfft = "3.2.0"
+# TODO: crossterm 0.25 currently because tui hasn't updated to 0.26 yet,
+# update this dependency when tui updates to 0.26
+# (added serde here, double check that doesn't break performance again)
+crossterm = { version = "0.26", features = ["serde"] }
+#tui = "0.19.0"
+# serde and larger things like clap mess with the autovectorization in the main code i think (or make runtime slower atleast), gotta explicitly vectorize and see if that helps or if it's something else going on
+# even without actually using them just including them as deps messes with it, unless they're disabling incremental linking or forcing lower opt-levels
+# weirdly building both of them with features in release gets rid of the spikes up to 4-7%, for now atleast :o, but individually they both start causing spikes :o
+# building both together did cause serde to update to 1.0.160, so maybe problems with older versions of serde or somethins
+tui = { package = "ratatui", version = "0.20.1", features = ["serde"] }
+clap = { version = "4.2.2", features = ["derive"] }
+num-traits = "0.2.15"
+serde_json = "1.0.96"
+serde = { version = "1.0.160", features = ["derive"] }
+ansi_colours = "1.2.1"
diff --git a/colourschemes/adam_neely.json b/colourschemes/adam_neely.json
new file mode 100644
index 0000000..3439755
--- /dev/null
+++ b/colourschemes/adam_neely.json
@@ -0,0 +1,14 @@
+{
+ "a": "rgb_(247,0,49)",
+ "as_": "rgb_(155,0,106)",
+ "b": "rgb_(1,0,200)",
+ "c": "rgb_(255,238,72)",
+ "cs": "rgb_(186,241,106)",
+ "d": "rgb_(72,247,163)",
+ "ds": "rgb_(84,221,168)",
+ "e": "rgb_(165,37,200)",
+ "f": "rgb_(86,230,89)",
+ "fs": "rgb_(89,208,84)",
+ "g": "rgb_(109,53,47)",
+ "gs": "rgb_(161,33,48)"
+}
diff --git a/colourschemes/notes.txt b/colourschemes/notes.txt
new file mode 100644
index 0000000..07d75f9
--- /dev/null
+++ b/colourschemes/notes.txt
@@ -0,0 +1,27 @@
+adam_neely.json:
+ * he doesn't mention if sharps/flats have their own colours for him in
+ his video, right now i'm probably going to make them a
+ blended colour between the note and the next note, hopefully choosing
+ decent colours
+ * using brighter versions for the sharps also seems like a good idea,
+ (or darker for flats if you go the other way) that could look weird
+ though because i'm using brightness for value too, i might need to also
+ make the brightness for the max value rgb higher too for the b fex
+ so it doesn't get too dark too fast (probably a good idea to make sure
+ the notes have atleast close brightnesses to each other via the val
+ value in kcolorchooser)
+
+wavy2:
+ * created with:
+ hsv.py 344,90,255 30,80,255 30 0.23
+ * strikes a bit of a balance, probably a bit hard to read still
+
+wavy3:
+ * created with:
+ hsv.py 344,90,255 40,80,255 24 0.22
+ and
+ hsv.py 134,66,255
+ to generate the g# colour
+ * with the e colour switched to g and visa-versa (green <-> yellow)
+ and the new g# colour generated as above
+
diff --git a/colourschemes/tango.json b/colourschemes/tango.json
new file mode 100644
index 0000000..cbde1b6
--- /dev/null
+++ b/colourschemes/tango.json
@@ -0,0 +1,9 @@
+{
+ "a": "dark_red", "as_": "red",
+ "b": "white",
+ "c": "dark_green", "cs": "green",
+ "d": "dark_cyan", "ds": "cyan",
+ "e": "yellow",
+ "f": "dark_magenta", "fs": "magenta",
+ "g": "dark_blue", "gs": "blue"
+}
diff --git a/colourschemes/wavy1.json b/colourschemes/wavy1.json
new file mode 100644
index 0000000..63bd02f
--- /dev/null
+++ b/colourschemes/wavy1.json
@@ -0,0 +1,9 @@
+{
+ "c": "rgb_(255,175,237)", "cs": "rgb_(231,175,255)",
+ "d": "rgb_(185,175,255)", "ds": "rgb_(175,210,255)",
+ "e": "rgb_(175,249,255)",
+ "f": "rgb_(175,255,211)", "fs": "rgb_(190,255,175)",
+ "g": "rgb_(213,255,175)", "gs": "rgb_(243,255,175)",
+ "a": "rgb_(255,217,175)", "as_": "rgb_(255,199,175)",
+ "b": "rgb_(255,175,199)"
+}
diff --git a/colourschemes/wavy2.json b/colourschemes/wavy2.json
new file mode 100644
index 0000000..d6101b9
--- /dev/null
+++ b/colourschemes/wavy2.json
@@ -0,0 +1,9 @@
+{
+ "c": "rgb_(165,165,255)", "cs": "rgb_(195,207,255)",
+ "d": "rgb_(165,243,255)", "ds": "rgb_(195,255,251)",
+ "e": "rgb_(165,255,189)",
+ "f": "rgb_(219,255,165)", "fs": "rgb_(243,255,195)",
+ "g": "rgb_(255,213,165)", "gs": "rgb_(255,215,195)",
+ "a": "rgb_(255,165,189)", "as_": "rgb_(255,195,223)",
+ "b": "rgb_(243,165,255)"
+}
diff --git a/colourschemes/wavy3.json b/colourschemes/wavy3.json
new file mode 100644
index 0000000..7d81fca
--- /dev/null
+++ b/colourschemes/wavy3.json
@@ -0,0 +1,9 @@
+{
+ "c": "rgb_(171,165,255)", "cs": "rgb_(189,197,255)",
+ "d": "rgb_(165,234,255)", "ds": "rgb_(189,252,255)",
+ "e": "rgb_(255,231,165)",
+ "f": "rgb_(204,255,165)", "fs": "rgb_(230,255,189)",
+ "g": "rgb_(165,255,201)", "gs": "rgb_(189,255,204)",
+ "a": "rgb_(255,165,189)", "as_": "rgb_(255,189,219)",
+ "b": "rgb_(246,165,255)"
+}
diff --git a/src/args.rs b/src/args.rs
new file mode 100644
index 0000000..fe4737e
--- /dev/null
+++ b/src/args.rs
@@ -0,0 +1,140 @@
+use clap::{Parser, ValueEnum, Args};
+use crate::outputers::{ColourScheme, Outputers, SimpleOutputer, LineLayout, TuiOutputer};
+use derive_more::Display;
+use crate::notes::Note;
+use std::path::{PathBuf, Path};
+use std::error::Error;
+use std::fs::File;
+use std::io::BufReader;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Display)]
+pub enum OutputerTypes {
+ #[display(fmt="simple")]
+ Simple,
+ #[display(fmt="line-layout")]
+ LineLayout,
+ #[display(fmt="tui")]
+ Tui,
+}
+
+// TODO: it might make sense to add a step and quanitize values (they're quanitized to 1/i16::MAX
+// anyway because it's 16bit signed ints from alsa), but i think just scaling by
+// max_threshold will work for now
+
+#[derive(Parser, Debug)]
+#[command(author, version, about, long_about = None)]
+pub struct SimpleArgs {
+ /// outputter to use
+ #[arg(short, long, default_value_t = OutputerTypes::Simple)]
+ pub outputer: OutputerTypes,
+
+ /// threshold for displaying the value, the values have a range of
+ /// 0 - 1 for the most part, a square wave at the max volume
+ /// of a single note will be ~1 (pure sine will be ~sqrt(0.5) or ~0.707ish)
+ /// but in practice most sounds won't get close to that, normally being in
+ /// the range of 0.005-0.10 will give you something usable without being too noisy,
+ /// highly depending on your input though
+ #[arg(short, long, default_value_t = 0.00526532149076897)]
+ pub threshold: f32,
+
+ /// max (saturating) threshold, for colour output in LineLayout and maybe with
+ /// certain other outputers this is used as a value for which any values higher
+ /// then this is considered a max value and displayed at full brightness.
+ ///
+ /// For LineLayout this contols the brightness of the colour output,
+ /// anything >= max_threshold will display at full brightness and anything below
+ /// will be scaled linearly, like brightness = min(1, note_val / max_threshold) and of course
+ /// r = r_max * brightness, g = g_max * brightness, b = b_max * brightness.
+ ///
+ /// it's recommended to set this somewhat close to what you
+ /// expect your highest value is going to be (you can play with that by experimenting
+ /// with the Simple output, in Simple output each value is scaled by 10,000 so 2000 for
+ /// example would be 0.2)
+ ///
+ /// because the max sine wave volume is sqrt(0.5) for now this defaults to sqrt(0.5), but
+ /// it's recommended to set it lower especially if the brightness of the bars seems too low for
+ /// your input.
+ ///
+ /// 0.2 - 0.3 seems like an actual good value in practice, but it's worth playing with
+ #[arg(short, long, default_value_t = 0.7071067811865476)]
+ pub max_threshold: f32,
+
+ /// don't use colour to output for outputers that support it
+ /// (currently LineLayout)
+ #[arg(long, default_value_t = false)]
+ pub plain: bool,
+
+ /*/// max brightness colour for LineLayout and maybe other outputers,
+ /// this is the r/g/b colour at >= max_threshold, values
+ /// threshold > val > max_threshold will scale linearly from
+ /// brightness = threshold to brightness = 1, currently
+ /// threshold just determines if the value is displayed and not
+ /// its colour, that's purely from the max threshold
+ /// (though that could be something to change, make the calculation something like
+ /// diff = note_val - threshold
+ /// max_diff = max_threshold - threshold
+ /// brightness = diff / max_diff)
+ ///
+ /// These should have a range of (0.0 - 255.0) because each value gets converted
+ /// to a u8 to determine the final rgb colour
+ #[arg(long, default_value_t = String::from("78ffdc"))]
+ pub colour: String,*/
+
+
+ /// File that contains the colourscheme as JSON, see the colourschemes/ directory for examples
+ /// we'll use adam neely's as the default for the moment, but this should be changed
+ /// TODO
+ /// NOTE (The default here is set at compile time, so you have to recompile if you change the
+ /// dir you're in)
+ #[arg(short, long)]
+ colours: Option<PathBuf>,
+
+ /// Output device
+ #[arg(long)]
+ pub outdev: Option<Option<String>>
+}
+
+impl SimpleArgs {
+ pub fn get_outputer(&self, notes: &[Note]) -> Outputers {
+ match self.outputer {
+ OutputerTypes::Simple => Outputers::Simple(SimpleOutputer),
+ // TODO: change this unwrap to a ? and a Result return value
+ OutputerTypes::LineLayout => Outputers::LineLayout(LineLayout::new(self.max_threshold, !self.plain, self.get_colours().unwrap(), &notes)),
+ OutputerTypes::Tui => Outputers::Tui(TuiOutputer::new().unwrap()),
+ }
+ }
+
+ pub fn get_outdev(&self) -> Option<String> {
+ match &self.outdev {
+ Some(outdev) => outdev.clone(),
+ None => Some(String::from("default")),
+ }
+ }
+
+ pub fn get_colours(&self) -> Result<ColourScheme, Box<dyn Error>> {
+ match (self.plain, &self.colours) {
+ (true, _) => Ok(ColourScheme::default()),
+ (false, None) => Ok(ColourScheme::default()),
+ (false, Some(cf)) => {
+ // first check if the file exists either as an absolute path or in the current
+ // directory, if it does try to load it
+ let mut fres = File::open(&cf);
+ // otherwise try to load the colourscheme from colourschemes/ in the project directory
+ if fres.is_err() {
+ //let pth: PathBuf = [env!("CARGO_MANIFEST_DIR"), "colourschemes", cf.as_path()].iter().collect();
+ let mut pth: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+ pth.push("colourschemes");
+ pth.push(&cf);
+ fres = File::open(pth);
+ }
+ let file = fres?;
+ let reader = BufReader::new(file);
+ let colours: ColourScheme = serde_json::from_reader(reader)?;
+ // if either loaded successfully, make sure all of the colours are Rgb for later
+ Ok(colours)
+ }
+ }
+ }
+
+}
+
diff --git a/src/bin/loopbacker.rs b/src/bin/loopbacker.rs
new file mode 100644
index 0000000..ec3d136
--- /dev/null
+++ b/src/bin/loopbacker.rs
@@ -0,0 +1,48 @@
+use rustynotes::notes::{Notes, note_range, ProcerNotes};
+use rustynotes::proc::{Procer, ProcerData};
+use rustynotes::rfft::RFftProcer;
+use rustynotes::buf::{StaticBuffer, Procers};
+use rustynotes::outputers::{Outputers, SimpleOutputer};
+use rustynotes::args::SimpleArgs;
+use clap::{Parser, CommandFactory, FromArgMatches};
+
+fn main() {
+ const CHANNELS: u32 = 2;
+ const PERIOD_SIZE: usize = 800;
+ const PROC_SIZE: usize = 5600; //1 << 13; // 8k, 16k, 32k
+ //let disp_threshold: f32 = 0.07;
+ //let disp_t_str = "0.07";
+ let disp_t_str = "0.00526532149076897";
+ let sample_rate = 48000;
+ let notes = note_range((Notes::C, 2), (Notes::B, 10));
+ let rfproc: RFftProcer<f32, PERIOD_SIZE, PROC_SIZE> = RFftProcer::new(sample_rate);
+ let pnotes = rfproc.make_pnotes(&notes);
+ println!("notes len: {}, pnotes len: {}", notes.len(), pnotes.len());
+ for pnote in &pnotes {
+ println!("{}", pnote);
+ }
+ println!("{}", rfproc);
+
+ let mut cmd = SimpleArgs::command();
+ cmd = cmd.mut_arg("threshold", |ta| ta.default_value(disp_t_str));
+ let mut matches = cmd.get_matches();
+ let args = SimpleArgs::from_arg_matches_mut(&mut matches).unwrap();
+ let disp_threshold = args.threshold;
+ println!("Args: {:?}", args);
+
+ let rfpdata = ProcerData::new(&rfproc, ProcerNotes(pnotes, disp_threshold));
+ let outputer = args.get_outputer(&notes);
+ let mut buf: StaticBuffer<f32, PERIOD_SIZE, PROC_SIZE> = StaticBuffer::new(48000, CHANNELS,
+ vec![(Procers::Rfft(rfproc), rfpdata)], "mpd_c_snoop".to_string(), args.get_outdev(),
+ outputer);
+ println!("{}", buf);
+ let mut aout = alsa::Output::buffer_open().unwrap();
+ buf.adev.dump(&mut aout);
+ //buf.outdev.dump(&mut aout);
+ match &buf.outdev {
+ Some(outdev) => { outdev.dump(&mut aout); },
+ None => {},
+ }
+ println!("{}", aout);
+ buf.capture_loop();
+}
diff --git a/src/bin/smolguitar.rs b/src/bin/smolguitar.rs
new file mode 100644
index 0000000..bb36b64
--- /dev/null
+++ b/src/bin/smolguitar.rs
@@ -0,0 +1,44 @@
+use rustynotes::notes::{Notes, note_range, ProcerNotes};
+use rustynotes::proc::{Procer, ProcerData};
+use rustynotes::rfft::RFftProcer;
+use rustynotes::buf::{StaticBuffer, Procers};
+use rustynotes::outputers::{Outputers, SimpleOutputer, LineLayout};
+use rustynotes::args::SimpleArgs;
+use clap::Parser;
+
+fn main() {
+ const CHANNELS: u32 = 1;
+ const PERIOD_SIZE: usize = 480;
+ const PROC_SIZE: usize = 1 << 13; // 8k
+ //let disp_threshold = 0.00372314453125;
+ //let disp_threshold = 0.00526532149076897;
+ //let disp_threshold = 0.05;
+ let sample_rate = 48000;
+ let notes = note_range((Notes::C, 2), (Notes::E, 7));
+ let rfproc: RFftProcer<f32, PERIOD_SIZE, PROC_SIZE> = RFftProcer::new(sample_rate);
+ let pnotes = rfproc.make_pnotes(&notes);
+ for pnote in &pnotes {
+ println!("{}", pnote);
+ }
+ println!("{}", rfproc);
+ //let outputer = Outputers::Simple(SimpleOutputer);
+ //(2f32).sqrt()
+ //let outputer = Outputers::LineLayout(LineLayout::new(0.25, true, (60., 255., 220.), &notes));
+ let args = SimpleArgs::parse();
+ let disp_threshold = args.threshold;
+ let outputer = args.get_outputer(&notes);
+ let rfpdata = ProcerData::new(&rfproc, ProcerNotes(pnotes, disp_threshold));
+ let mut buf: StaticBuffer<f32, PERIOD_SIZE, PROC_SIZE> = StaticBuffer::new(48000, CHANNELS,
+ vec![(Procers::Rfft(rfproc), rfpdata)], "guitar_c".to_string(), args.get_outdev(),
+ outputer);
+ println!("{}", buf);
+ let mut aout = alsa::Output::buffer_open().unwrap();
+ buf.adev.dump(&mut aout);
+ //buf.outdev.dump(&mut aout);
+ match &buf.outdev {
+ Some(outdev) => { outdev.dump(&mut aout); },
+ None => {},
+ }
+ println!("{}", aout);
+ buf.capture_loop();
+}
diff --git a/src/bin/test_serde.rs b/src/bin/test_serde.rs
new file mode 100644
index 0000000..02b42e4
--- /dev/null
+++ b/src/bin/test_serde.rs
@@ -0,0 +1,22 @@
+use crossterm::style::Color;
+use serde::{Serialize, Deserialize};
+use serde_json::{to_string_pretty, from_str};
+
+#[derive(Serialize, Deserialize, Debug)]
+struct cscheme {
+ a: Color,
+ asharp: Color,
+ b: Color,
+}
+
+fn main() {
+ let val = Color::Rgb {r: 110, g: 255, b: 220};
+ let val2 = Color::AnsiValue(51);
+ let val3 = Color::DarkMagenta;
+ let scheme = cscheme {a: val, asharp: val2, b: val3};
+ let json = to_string_pretty(&scheme).unwrap();
+ println!("{}", json);
+ let result: cscheme = from_str(&json).unwrap();
+ println!("{:?}", result);
+ //println!("{} {}", to_string_pretty
+}
diff --git a/src/bin/testrfft.rs b/src/bin/testrfft.rs
new file mode 100644
index 0000000..ee8c72f
--- /dev/null
+++ b/src/bin/testrfft.rs
@@ -0,0 +1,20 @@
+use realfft::{RealFftPlanner, FftNum, RealToComplex};
+
+fn main() {
+ let mut planner: RealFftPlanner<f32> = RealFftPlanner::new();
+ //let rfft = planner.plan_fft_forward(4);
+ let rfft = planner.plan_fft_forward(40);
+ let mut scratch = rfft.make_scratch_vec();
+ let mut out_data = rfft.make_output_vec();
+ //let mut in_data = [1., 0.4, 0.3, -1.];
+ let mut in_data = [1.; 40];
+ //let mut in_data = [
+ rfft.process_with_scratch(&mut in_data, &mut out_data, &mut scratch).unwrap();
+ println!("{:?}", &out_data);
+ let scale = 1./40.;
+ for elem in out_data {
+ println!("{}", elem.norm_sqr().sqrt() * scale);
+ println!("{}", elem.l1_norm() * scale);
+ //println!("{}", elem * scale);
+ }
+}
diff --git a/src/buf.rs b/src/buf.rs
new file mode 100644
index 0000000..fb4a6dd
--- /dev/null
+++ b/src/buf.rs
@@ -0,0 +1,244 @@
+use crate::proc::{Procer, ProcerData, Update};
+use crate::notes::{Note, NoteValue, ProcerNote};
+use crate::outputers::{Outputer, Outputers, SimpleOutputer};
+use crate::rfft::RFftProcer;
+use realfft::FftNum;
+use rustfft::num_traits::float::Float;
+use alsa::{Direction, ValueOr};
+use alsa::pcm::{PCM, HwParams, Format, Access, State};
+use derive_more::Display;
+use std::fmt::Debug;
+use std::io::Write;
+//use std::collections::VecDeque;
+
+pub enum Procers<I: Default + Clone + FftNum + Float, const US: usize, const BS: usize> {
+ Rfft(RFftProcer<I, US, BS>),
+ // XXX: if i add a fir filter that doesn't need a buffer i think
+ // i can just spec it as Fir12Tet(Fir12TetProcer<I, US>) or similar
+ // here
+}
+
+#[derive(Display)]
+#[display(fmt="StaticBuffer(rate={}, psize={}, channels={}, opsize={}, adev=(name={}), outdev=(name={:?}), cperiods={}, operiods={})", rate, period_size, channels, out_period_size, adev_name, outdev_name, capture_periods, output_periods)]
+pub struct StaticBuffer<'nl, I: Default + Debug + Clone + FftNum + Float + NoteValue, const US: usize, const BS: usize> {
+ pub rate: u32,
+ pub period_size: usize,
+ pub channels: u32,
+ pub out_period_size: usize,
+ pub procers: Vec<(Procers<I, US, BS>, ProcerData<'nl, I>)>,
+ pub adev_name: String,
+ pub adev: PCM,
+ pub outdev_name: Option<String>,
+ pub outdev: Option<PCM>,
+ pub outputer: Outputers,
+ pub capture_periods: u32,
+ pub output_periods: u32,
+}
+
+impl<'nl, I: Default + Debug + Clone + FftNum + Float + From<i16> + NoteValue, const US: usize, const BS: usize> StaticBuffer<'nl, I, US, BS> {
+ pub fn new(rate: u32, channels: u32, procers: Vec<(Procers<I, US, BS>, ProcerData<'nl, I>)>, adev_name: String, outdev_name: Option<String>, outputer: Outputers) -> Self {
+ let mut adev = PCM::new(&adev_name, Direction::Capture, true).unwrap();
+ let mut capture_periods = 0;
+ {
+ let hwp = HwParams::any(&adev).unwrap();
+ hwp.set_channels(channels).unwrap();
+ //hwp.set_rate_resample(false);
+ hwp.set_rate(rate, ValueOr::Nearest).unwrap();
+ hwp.set_format(Format::s16()).unwrap();
+ hwp.set_access(Access::MMapInterleaved).unwrap();
+ hwp.set_period_size(US as i64, ValueOr::Nearest).unwrap();
+ hwp.set_periods(8, ValueOr::Nearest).unwrap();
+ // TODO: directly using the ALSA ring buffer and copying directly from it instead of
+ // going through the Update/VecDeque might be interesting in the future, i think it'd
+ // require changing the const sizes to runtime determined sizes though, so future
+ // changes to try~
+ adev.hw_params(&hwp).unwrap();
+ capture_periods = hwp.get_periods().unwrap();
+ println!("capture periods: {}", capture_periods);
+ }
+ let (outdev, out_period_size, output_periods) = match &outdev_name {
+ None => (None, 0, 0),
+ Some(od_name) => {
+ let mut outdev = PCM::new(&od_name, Direction::Playback, true).unwrap();
+ let mut output_periods = 0;
+ let mut out_period_size = 0;
+ {
+ let hwp = HwParams::any(&outdev).unwrap();
+ hwp.set_channels(channels).unwrap();
+ hwp.set_rate(rate, ValueOr::Nearest).unwrap();
+ hwp.set_format(Format::s16()).unwrap();
+ hwp.set_access(Access::RWInterleaved).unwrap();
+ //hwp.set_period_size(US as i64, ValueOr::Nearest).unwrap();
+ //hwp.set_periods(8, ValueOr::Nearest).unwrap();
+ outdev.hw_params(&hwp).unwrap();
+ output_periods = hwp.get_periods().unwrap();
+ out_period_size = hwp.get_period_size().unwrap() as usize;
+ println!("output periods: {}", output_periods);
+ }
+ (Some(outdev), out_period_size, output_periods)
+ }
+ };
+ return Self {
+ rate, procers, adev_name, adev, outdev_name, outdev,
+ period_size: US, out_period_size: out_period_size,
+ channels: channels,
+ outputer,
+ capture_periods, output_periods,
+ }
+ }
+
+ pub fn capture_loop(&mut self) {
+ let empty: Vec<i16> = vec![0i16; US * self.channels as usize];
+ //let mut line_str: String = format!("\r{:80}", "");
+ let in_io = self.adev.io_i16().unwrap();
+ let out_io_opt = {
+ match &self.outdev {
+ None => (None),
+ Some(outdev) => {
+ //self.adev.link(&outdev).unwrap();
+ let out_io = outdev.io_i16().unwrap();
+ // queue up an empty period in the outdev so we can sync between in and out devices
+ // with a 4 period latency
+ for _i in 0..std::cmp::min(4, self.output_periods/2) {
+ out_io.writei(&empty).unwrap();
+ }
+ outdev.start();//.unwrap();
+ println!("output available: {}", outdev.avail().unwrap());
+ Some(out_io)
+ }
+ }
+ };
+ self.adev.start();//.unwrap();
+ //let vt: I = I::from(112);
+ let conv_scale: I = <I as From<i16>>::from(i16::MAX);
+ println!("input available: {}", self.adev.avail_update().unwrap());
+ let mut latentframes = 0;
+ let mut unstarted = false;
+ loop {
+ /*println!("output available: {}", self.outdev.avail().unwrap());
+ println!("input available: {}", self.adev.avail_update().unwrap());*/
+ self.adev.wait(None).unwrap();
+ //println!("a");
+ // should be called right before mmap_begin according to:
+ // https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___direct.html#ga6d4acf42de554d4d1177fb035d484ea4
+ let avail_frames = self.adev.avail_update().unwrap();
+ in_io.mmap(US, |in_slice| {
+ let size = in_slice.len();
+ //println!("slice length: {}", size);
+ if size == 0 {
+ println!("warning: passed a 0-sized buffer from mmap\n");
+ return 0;
+ }
+ let stride = self.channels as usize;
+ if size != (US * stride) {
+ println!("warning: passed a {} sized buffer from mmap\n\n", size);
+ return 0;
+ }
+ assert_eq!(size, US * stride);
+ let (written, oa) = match &out_io_opt {
+ Some(out_io) => {
+ let wri = out_io.writei(&in_slice).unwrap();
+ assert_eq!(wri, (size/stride));
+ let oaa = self.outdev.as_ref().unwrap().avail().unwrap();
+ (wri, oaa)
+ }
+ None => (size/stride, 0i64),
+ };
+ let ia = self.adev.avail_update().unwrap();
+ let total_outbuf: i64 = oa - ia;
+ /*print!("\rtotal: {}, out: {}, in: {}", total_outbuf, oa, ia);
+ std::io::stdout().flush().unwrap();*/
+ //return written;
+ //let constrained_slice = in_slice[0..US*stride];
+ let mut update: Update<I, US> = [I::default(); US];
+ // copy over the left channel data (even indexes)
+ // TODO/XXX: see if this uses SIMD or needs optimizing :o
+ // using iter and map/function that does strides would prob also work well :o
+ // gotta scale this from i16::MAX to float, alsa
+ // seems to divide by i16::MAX, not sure if there's a better way to do this
+ for i in 0..US {
+ update[i] = <I as From<i16>>::from(in_slice[i*stride]) / conv_scale;
+ };
+ // hmm, i only need to use 1 procer here for displaying, not sure what to do with
+ // the rest :o
+ //for (procer, pdata) in self.procers { }
+ let (ref mut procer, ref mut pdata) = self.procers.first_mut().unwrap();
+ let mut processed = false;
+ {
+ let mut pnotes = pdata.pnotes.0.as_mut_slice();
+ //let processed = true;
+ processed = procer.process_data(&update, pnotes);
+ }
+ if processed {
+ self.outputer.handle_pnotes(&mut pdata.pnotes);
+ //let mut position = 1;
+ // TODO: maybe copy, inplace probably works for now though
+ /*pnotes.sort_unstable_by(|pn1, pn2| (-pn1.2).partial_cmp(&-pn2.2).unwrap());
+ print!("\r{:-80}", pdata.pnotes);
+ std::io::stdout().flush();*/
+ }
+ //let mut position = 0;
+ //let mut remaining = in_slice.len();
+ //let mut remain_frames = remaining/(self.channels as usize);
+ //let mut handled = false;
+ //self.outdev.wait(None).unwrap();
+ //println!("wrote {} frames", written);
+ /*while handled == false {
+ self.outdev.wait(None).unwrap();
+ out_io.mmap(remain_frames, |out_slice| {
+ let outlen = out_slice.len();
+ println!("out slice len: {}", out_slice.len());
+ if outlen == 0 {
+ return 0;
+ }
+ out_slice.clone_from_slice(&in_slice);
+ handled = true;
+ return out_slice.len()/(self.channels as usize);
+ }).unwrap();
+ }*/
+ /*if latentframes > 0 {
+ latentframes -= 1;
+ } else if unstarted {
+ unstarted = false;
+ println!("starting outdev");
+ self.outdev.start().unwrap();
+ }*/
+ return in_slice.len()/(self.channels as usize);
+ }).unwrap();
+ }
+ }
+ //fn new(
+}
+
+/*impl StaticBuffer<'nl, B> {
+}*/
+
+//impl<'nl, B> StaticBuffer<'nl, A> {
+ //pub fn fill_initial(&self,
+ //pub fn new(rate: usize, period_size: usize, out_period_size: usize, procers: Vec<(Procers<f64>, ProcerData<'nl>)>,
+//}
+
+
+impl<I: Default + Clone + FftNum + Float + NoteValue, const US: usize, const BS: usize> Procer<I, US> for Procers<I, US, BS> {
+ fn get_size(&self) -> usize {
+ match self {
+ Procers::Rfft(rfp) => rfp.get_size(),
+ }
+ }
+ fn get_frequency(&self) -> usize {
+ match self {
+ Procers::Rfft(rfp) => rfp.get_frequency(),
+ }
+ }
+ fn make_pnotes<'nl>(&self, notes: &'nl [Note]) -> Vec<ProcerNote<'nl, I>> {
+ match self {
+ Procers::Rfft(rfp) => rfp.make_pnotes(notes),
+ }
+ }
+ fn process_data(&mut self, input: &Update<I, US>, notes: &mut [ProcerNote<I>]) -> bool {
+ match self {
+ Procers::Rfft(rfp) => rfp.process_data(input, notes),
+ }
+ }
+}
+
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..3a1eb7d
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,6 @@
+pub mod notes;
+pub mod proc;
+pub mod outputers;
+pub mod buf;
+pub mod rfft;
+pub mod args;
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..022b6ce
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,66 @@
+mod notes;
+mod proc;
+mod outputers;
+mod buf;
+mod rfft;
+mod args;
+
+use crate::notes::{Notes, note_range, ProcerNotes};
+use crate::proc::{Procer, ProcerData};
+use crate::rfft::RFftProcer;
+use crate::buf::{StaticBuffer, Procers};
+use crate::outputers::{Outputers, SimpleOutputer, LineLayout};
+use crate::args::SimpleArgs;
+use clap::{Parser, CommandFactory, FromArgMatches};
+
+//use std::collections::VecDeque;
+
+fn main() {
+ const CHANNELS: u32 = 2;
+ const PERIOD_SIZE: usize = 240; // 1024 frames
+ const PROC_SIZE: usize = 1 << 14; // 16k frames (so about .3413_ of a second)
+ //let disp_threshold = 0.00372314453125;
+ //let disp_threshold = 0.00526532149076897;
+ //let disp_threshold = 50. / (i16::MAX as f32);
+ //const PERIOD_SIZE: usize = 480;
+ //const PROC_SIZE: usize = 1 << 13; // 8k
+ //let period_size = 1024;
+ let sample_rate = 48000;
+ //let proc_size = 1 << 14; // 16k (so about .3413_ of a second)
+ // we update every period_size so it updates every 21.3_ms but we keep a running average of the
+ // last 341.3_ms so low frequencies can be interpreted with the fft (using fir or iir filters
+ // or wavelets or inner product spaces and frames is something to do in the future, but for now i'm just
+ // porting older python code over for performance that used ffts, and maybe messing with dct
+ // but it'll have the same limitations of ffts mostly probably)
+ let notes = note_range((Notes::C, 2), (Notes::E, 7));
+ // US and prob BS here should be *2 when dealing with stereo, but going with mono rn
+ let rfproc: RFftProcer<f32, PERIOD_SIZE, PROC_SIZE> = RFftProcer::new(sample_rate);
+ let pnotes = rfproc.make_pnotes(&notes);
+ //println!("{:#?}", pnotes);
+ for pnote in &pnotes {
+ println!("{}", pnote);
+ }
+ println!("{}", rfproc);
+ let args = SimpleArgs::parse();
+ println!("Args: {:?}", args);
+ // TODO: having an app-specific default threshold might be a good idea for smolguitar,
+ // not sure how to do that yet, maybe there's an API for changing the default of an arg in clap
+ let disp_threshold = args.threshold;
+ //let outputer = Outputers::Simple(SimpleOutputer);
+ //let outputer = Outputers::LineLayout(LineLayout::new(0.2, true, (0., 255., 220.), &notes));
+ let outputer = args.get_outputer(&notes);
+ let rfpdata = ProcerData::new(&rfproc, ProcerNotes(pnotes, disp_threshold));
+ let mut buf: StaticBuffer<f32, PERIOD_SIZE, PROC_SIZE> = StaticBuffer::new(48000, CHANNELS,
+ vec![(Procers::Rfft(rfproc), rfpdata)], "Microphone_c".to_string(), args.get_outdev(),
+ outputer);
+ println!("{}", buf);
+ let mut aout = alsa::Output::buffer_open().unwrap();
+ buf.adev.dump(&mut aout);
+ match &buf.outdev {
+ Some(outdev) => { outdev.dump(&mut aout); },
+ None => {},
+ }
+ println!("{}", aout);
+ buf.capture_loop();
+ //println!("{:#?}", pdata);
+}
diff --git a/src/notes.rs b/src/notes.rs
new file mode 100644
index 0000000..41eb45e
--- /dev/null
+++ b/src/notes.rs
@@ -0,0 +1,168 @@
+use derive_more::Display;
+use std::fmt::{Debug, Write};
+use num_traits::{NumOps, ToPrimitive};
+
+#[derive(Debug, Clone, Display, Copy, PartialEq)]
+pub enum Notes {
+ #[display(fmt=" c")]
+ C,
+ #[display(fmt="c#")]
+ Cs,
+ #[display(fmt=" d")]
+ D,
+ #[display(fmt="d#")]
+ Ds,
+ #[display(fmt=" e")]
+ E,
+ #[display(fmt=" f")]
+ F,
+ #[display(fmt="f#")]
+ Fs,
+ #[display(fmt=" g")]
+ G,
+ #[display(fmt="g#")]
+ Gs,
+ #[display(fmt=" a")]
+ A,
+ #[display(fmt="a#")]
+ As,
+ #[display(fmt=" b")]
+ B,
+}
+
+pub type BaseNote = (Notes, f64);
+
+const MIDDLE_A_VAL: f64 = 440.;
+//const MIDDLE_A_LOWER: f64 = MIDDLE_A_VAL * (2f64).powf(-0.5f64 / 12f64);
+//const MIDDLE_A_UPPER: f64 = MIDDLE_A_VAL * (2f64).powf(0.5f64 / 12f64);
+
+pub fn calc_freq_from_offset(offset: f64) -> f64 {
+ return MIDDLE_A_VAL * (2f64).powf(offset / 12f64);
+}
+
+pub const NOTE_MA_OFFSETS: [BaseNote; 12] = [
+ (Notes::C, -57.), (Notes::Cs, -56.),
+ (Notes::D, -55.), (Notes::Ds, -54.), (Notes::E, -53.),
+ (Notes::F, -52.), (Notes::Fs, -51.),
+ (Notes::G, -50.), (Notes::Gs, -49.),
+ (Notes::A, -48.), (Notes::As, -47.),
+ (Notes::B, -46.),
+];
+
+/*const NOTE_ORDER: [BaseNote; 12] = [
+ (Notes::C, 16.352), (Notes::Cs, 17.324),
+ (Notes::D, 18.354), (Notes::Ds, 19.445), (Notes::E, 20.602),
+ (Notes::F, 21.827), (Notes::Fs, 23.125),
+ (Notes::G, 24.5), (Notes::Gs, 25.957),
+ (Notes::A, 27.5), (Notes::As, 29.135),
+ (Notes::B, 30.868)
+];*/
+
+#[derive(Debug, Display)]
+#[display(fmt="{}{}", "note.0", octave)]
+pub struct Note {
+ pub note: BaseNote,
+ pub low_freq: f64,
+ pub freq: f64,
+ pub high_freq: f64,
+ pub octave: i32,
+ //value: f64,
+}
+
+pub trait NoteValue: Debug + PartialOrd + From<f32> + ToPrimitive + std::fmt::Display + Copy + NumOps {}
+//where <Self as std::ops::Mul>::Output: std::fmt::Display {}
+
+//impl NoteValue for f32 {}
+//impl NoteValue for f64 {}
+impl<T> NoteValue for T
+where T: Debug + PartialOrd + From<f32> + ToPrimitive + std::fmt::Display + Copy + NumOps {}
+
+// elements here are (Note, fft/proc data range for calculating the value, summed value, max value
+// of each element in the bin)
+#[derive(Debug, Display)]
+//#[display(fmt="{}({:?}) {}", _0, _1 ,_2)]
+//#[display(bound="I: From<f32> + std::ops::Mul + std::fmt::Display")]
+#[display(fmt="{} {:.0} ", _0, "*_3*I::from(10000.0)")]
+pub struct ProcerNote<'nl, I: NoteValue>(pub &'nl Note, pub core::ops::Range<usize>, pub I, pub I);
+// .0 here is the notes data, .1 is the threshold for printing~
+#[derive(Debug)]
+pub struct ProcerNotes<'nl, I: NoteValue>(pub Vec<ProcerNote<'nl, I>>, pub I);
+
+impl<'nl, I: NoteValue> std::fmt::Display for ProcerNotes<'nl, I> {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
+ //let threshold: I = I::from(0.00372314453125f32);
+ let threshold = self.1;
+ let mut tmp: String = String::with_capacity(1024);
+ for pn in self.0.iter().filter(|pn| pn.3 >= threshold) {
+ write!(tmp, "{pn}")?;
+ };
+ fmt.pad(&tmp);
+ Ok(())
+ }
+}
+
+
+/*impl<'nl, I: Debug + PartialOrd + From<f32>> Debug for ProcerNotes<'nl, I> {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
+ let threshold: I = I::from(122.0f32);
+ fmt.debug_list().entries(self.0.iter().filter(|pn| pn.2 > threshold)).finish()
+ }
+}*/
+
+pub trait Octavable {
+ fn get_octave(&self, octave: i32) -> Note;
+}
+
+impl Octavable for BaseNote {
+ fn get_octave(&self, octave: i32) -> Note {
+ let offset = self.1 + ((12i32 * octave) as f64);
+ let low_offset = offset - 0.5;
+ let high_offset = offset + 0.5;
+ match octave {
+ 0 => Note {
+ note: *self,
+ low_freq: calc_freq_from_offset(self.1 - 0.5),
+ freq: calc_freq_from_offset(self.1),
+ high_freq: calc_freq_from_offset(self.1 + 0.5),
+ octave: 0//, value: 0.
+ },
+ _ => Note {
+ note: *self,
+ low_freq: calc_freq_from_offset(low_offset),
+ freq: calc_freq_from_offset(offset),
+ high_freq: calc_freq_from_offset(high_offset),
+ octave: octave//, value: 0.
+ }
+ }
+ }
+}
+
+// start and end should be tuples of (Note, octave)
+pub fn note_range(start: (Notes, i32), end: (Notes, i32)) -> Vec<Note> {
+ let mut ret = Vec::with_capacity((end.1+3 - start.1) as usize *12);
+ let mut started: bool = false;
+ let start_note = start.0;
+ let (end_note, mut end_octave) = end;
+ end_octave += 2;
+ for octave in (start.1)..=end_octave {
+ for bnote in NOTE_MA_OFFSETS {
+ match (started, bnote.0, octave) {
+ (false, cur_note, _) if cur_note == start_note => { started = true; ret.push(bnote.get_octave(octave)) },
+ (true, cur_note, _) if cur_note == end_note && octave == end_octave => { ret.push(bnote.get_octave(octave)); return ret; },
+ (true, _, _) => { ret.push(bnote.get_octave(octave)) },
+ (_, _, _) => {},
+ }
+ }
+ }
+ return ret;
+}
+
+/*impl Octavable for Note {
+ fn get_octave(&self, octave: i32) -> Self {
+ match octave {
+ 0 => Note {freq: self.note.1, octave: 0, value: 0., ..*self},
+ _ => Note {freq: self.note.1 * ((1 << octave) as f64), octave: octave, value: 0., ..*self}
+ }
+ }
+}*/
+
diff --git a/src/outputers.rs b/src/outputers.rs
new file mode 100644
index 0000000..4c56e56
--- /dev/null
+++ b/src/outputers.rs
@@ -0,0 +1,266 @@
+use crate::notes::{Note, BaseNote, Notes, NoteValue, ProcerNote, ProcerNotes};
+use std::collections::VecDeque;
+use std::io::Write;
+use std::fmt::{Debug};
+use realfft::FftNum;
+//use clap::{Args,Subcommand};
+
+//use std::io;
+use crossterm::terminal;
+use crossterm::style::{Stylize, Color};
+use tui::{backend::CrosstermBackend, Terminal};
+use serde::{Serialize, Deserialize};
+use ansi_colours::rgb_from_ansi256;
+
+
+trait ToRgbVal {
+ fn to_rgb_value(&self) -> (u8, u8, u8);
+}
+
+impl ToRgbVal for Color {
+ fn to_rgb_value(&self) -> (u8, u8, u8) {
+ match self {
+ Color::Rgb {r, g, b} => (*r, *g, *b),
+ // I'll use the tango colours for the colour constants
+ // but the Ansi lookup table for the Ansi versions
+ Color::Reset => (0, 0, 0),
+ Color::Black => (0, 0, 0),
+ Color::DarkRed => (204, 0, 0),
+ Color::DarkGreen => (78, 154, 6),
+ Color::DarkYellow => (196, 160, 0),
+ Color::DarkBlue => (54, 101, 164),
+ Color::DarkMagenta => (117, 80, 123),
+ Color::DarkCyan => (6, 152, 154),
+ Color::Grey => (211, 215, 207),
+ Color::DarkGrey => (85, 87, 83),
+ Color::Red => (239, 41, 41),
+ Color::Green => (138, 226, 52),
+ Color::Yellow => (252, 233, 79),
+ Color::Blue => (114, 159, 207),
+ Color::Magenta => (173, 127, 168),
+ Color::Cyan => (52, 226, 226),
+ Color::White => (238, 238, 236),
+ Color::AnsiValue(ansi) => rgb_from_ansi256(*ansi),
+ }
+ }
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+pub struct ColourScheme {
+ pub a: Color, pub as_: Color,
+ pub b: Color,
+ pub c: Color, pub cs: Color,
+ pub d: Color, pub ds: Color,
+ pub e: Color,
+ pub f: Color, pub fs: Color,
+ pub g: Color, pub gs: Color,
+}
+
+impl ColourScheme {
+ fn get_colour(&self, note: Notes) -> Color {
+ match note {
+ Notes::C => self.c,
+ Notes::Cs => self.cs,
+ Notes::D => self.d,
+ Notes::Ds => self.ds,
+ Notes::E => self.e,
+ Notes::F => self.f,
+ Notes::Fs => self.fs,
+ Notes::G => self.g,
+ Notes::Gs => self.gs,
+ Notes::A => self.a,
+ Notes::As => self.as_,
+ Notes::B => self.b,
+ }
+ }
+}
+
+impl Default for ColourScheme {
+ fn default() -> Self {
+ let c = Color::Rgb {r: 140, g: 255, b: 210};
+ return Self {
+ a: c, as_: c,
+ b: c,
+ c: c, cs: c,
+ d: c, ds: c,
+ e: c,
+ f: c, fs: c,
+ g: c, gs: c,
+ }
+ }
+}
+
+pub trait Outputer<I: FftNum + NoteValue> {
+ fn setup(&mut self);
+ fn handle_pnotes(&mut self, notes: &mut ProcerNotes<I>);
+}
+
+pub enum Outputers {
+ Simple(SimpleOutputer),
+ LineLayout(LineLayout),
+ Tui(TuiOutputer),
+}
+
+pub struct SimpleOutputer;
+pub struct LineLayout {
+ term_width: usize,
+ notes_per_line: usize,
+ last_line: String,
+ empty: String,
+ last_was_empty: bool,
+ use_colour: bool,
+ //max_c: (f32, f32, f32),
+ colours: ColourScheme,
+ max_threshold: f32,
+ //threshold: f32,
+}
+pub struct TuiOutputer {
+ terminal: Terminal<tui::backend::CrosstermBackend<std::io::Stdout>>,
+}
+
+impl<I: FftNum + NoteValue> Outputer<I> for SimpleOutputer {
+ fn setup(&mut self) {}
+ fn handle_pnotes(&mut self, notes: &mut ProcerNotes<I>) {
+ notes.0.sort_unstable_by(|pn1, pn2| (-(pn1.3)).partial_cmp(&-(pn2.3)).unwrap());
+ print!("\r{:-80}", notes);
+ std::io::stdout().flush().unwrap();
+ }
+}
+
+pub fn line_note_char(note: &Note) -> char {
+ match note.note.0 {
+ Notes::C => 'c',
+ Notes::Cs => match note.octave % 10 {
+ 0 => '0',
+ 1 => '1',
+ 2 => '2',
+ 3 => '3',
+ 4 => '4',
+ 5 => '5',
+ 6 => '6',
+ 7 => '7',
+ 8 => '8',
+ 9 => '9',
+ _ => ' ',
+ },
+ Notes::D => 'd',
+ Notes::Ds => ' ',
+ Notes::E => 'e',
+ Notes::F => 'f',
+ Notes::Fs => ' ',
+ Notes::G => 'g',
+ Notes::Gs => ' ',
+ Notes::A => 'a',
+ Notes::As => ' ',
+ Notes::B => 'b'
+ }
+}
+
+impl LineLayout {
+ pub fn new(max_threshold: f32, use_colour: bool, colours: ColourScheme, notes: &[Note]) -> Self {
+ let term_width = terminal::size().unwrap().0 as usize;
+ let note_count = std::cmp::min(notes.len(), term_width);
+ return Self {
+ term_width, use_colour, colours, max_threshold, notes_per_line: note_count,
+ last_line: notes[0..note_count].iter().map(line_note_char).collect(),
+ empty: " ".repeat(term_width),
+ last_was_empty: true,
+ }
+ }
+
+ pub fn line_bw<I: FftNum + NoteValue>(&self, note_count: usize, notes: &ProcerNotes<I>) {
+ print!("\r");
+ let itx: String = notes.0[0..note_count].iter().map(|note| {
+ if note.3 >= notes.1 {
+ '▉'
+ } else {
+ ' '
+ }
+ }).collect();
+ print!("{}\n{}", itx, self.last_line);
+ std::io::stdout().flush().unwrap();
+ }
+
+ pub fn line_colour<I: FftNum + NoteValue>(&self, note_count: usize, notes: &ProcerNotes<I>) {
+ print!("\r");
+ // TODO: do grouping to avoid excessive control characters
+ // for now this just does the simplest thing though
+ for note in &notes.0[0..note_count] {
+ if note.3 >= notes.1 {
+ let mut bright: f32 = note.3.to_f32().unwrap() / self.max_threshold;
+ if bright > 1.0 {
+ bright = 1.0;
+ }
+ let (r, g, b) = self.colours.get_colour(note.0.note.0).to_rgb_value();
+ print!("{}", ' '.on(Color::Rgb {
+ r: (r as f32 * bright) as u8,
+ g: (g as f32 * bright) as u8,
+ b: (b as f32 * bright) as u8,
+ }));
+ } else {
+ print!(" ");
+ }
+ }
+ print!("\n{}", self.last_line);
+ std::io::stdout().flush().unwrap();
+ }
+}
+
+impl<I: FftNum + NoteValue> Outputer<I> for LineLayout {
+ fn setup(&mut self) {}
+ fn handle_pnotes(&mut self, notes: &mut ProcerNotes<I>) {
+ let notes_len = notes.0.len();
+ let note_count = std::cmp::min(notes_len, self.notes_per_line);
+ if notes.0[0..note_count].iter().all(|note| note.3 < notes.1) {
+ if self.last_was_empty {
+ return
+ } else {
+ self.last_was_empty = true;
+ }
+ } else {
+ self.last_was_empty = false;
+ }
+ match self.use_colour {
+ true => self.line_colour(note_count, notes),
+ false => self.line_bw(note_count, notes)
+ }
+ }
+}
+
+impl TuiOutputer {
+ pub fn new() -> Result<Self, std::io::Error> {
+ let stdout = std::io::stdout();
+ let backend = CrosstermBackend::new(stdout);
+ let mut terminal = Terminal::new(backend)?;
+ return Ok(Self {
+ terminal,
+ })
+ }
+}
+
+impl<I: FftNum + NoteValue> Outputer<I> for TuiOutputer {
+ fn setup(&mut self) {}
+ fn handle_pnotes(&mut self, notes: &mut ProcerNotes<I>) {
+ //self.terminal.draw(|frame| {})
+ }
+}
+
+
+impl<I: FftNum + NoteValue> Outputer<I> for Outputers {
+ fn setup(&mut self) {
+ match self {
+ Outputers::Simple(so) => <SimpleOutputer as Outputer<I>>::setup(so),
+ Outputers::LineLayout(llo) => <LineLayout as Outputer<I>>::setup(llo),
+ Outputers::Tui(to) => <TuiOutputer as Outputer<I>>::setup(to)
+ }
+ }
+
+ fn handle_pnotes(&mut self, notes: &mut ProcerNotes<I>) {
+ match self {
+ Outputers::Simple(so) => so.handle_pnotes(notes),
+ Outputers::LineLayout(llo) => llo.handle_pnotes(notes),
+ Outputers::Tui(to) => to.handle_pnotes(notes),
+ }
+ }
+}
+
diff --git a/src/proc.rs b/src/proc.rs
new file mode 100644
index 0000000..2b32747
--- /dev/null
+++ b/src/proc.rs
@@ -0,0 +1,162 @@
+//#![feature(split_array)]
+//#![feature(adt_const_params)]
+//#![feature(generic_const_exprs)]
+use crate::notes::{Note, NoteValue, ProcerNote, ProcerNotes};
+//use std::sync::Arc;
+use std::collections::VecDeque;
+
+// Arc does heap allocate, so it might make sense to just pass owned arrays around and copy/clone
+// when needed
+//pub type Update<const S: usize> = Arc<[f64; S]>;
+pub type Update<I, const S: usize> = [I; S];
+pub type UpDequer<I, const S: usize> = VecDeque<Update<I, S>>;
+
+pub trait Procer<I: NoteValue, const US: usize> {
+ fn get_size(&self) -> usize;
+ fn get_frequency(&self) -> usize;
+ fn make_pnotes<'nl>(&self, notes: &'nl [Note]) -> Vec<ProcerNote<'nl, I>>;
+ // this function only modifies ProcerNotes when you have enough data
+ // otherwise it leaves them alone or sets them to 0 (not sure which yet)
+ fn process_data(&mut self, input: &Update<I, US>, notes: &mut [ProcerNote<I>]) -> bool;
+}
+
+pub trait DequerUtils<I, const BS: usize> {
+ fn cur_max_buffer_size(&self) -> usize;
+ fn update_buffer_array(&self, buf: &mut [I; BS]) -> bool;
+ //fn make_buffer(&self) -> Option<[I; BS]>;
+}
+
+// the tuple this returns is (plan_size (ie the - index of the first element to copy from),
+// Option<copy_size>), if the option is None copy the entire update, otherwise copy
+// copy_size from the end of the first array
+const fn make_buf_plan(buf_size: usize, update_size: usize) -> (usize, Option<usize>) {
+ let remainder_size = buf_size % update_size;
+ let plan_size = buf_size / update_size;
+ if remainder_size > 0 {
+ return (plan_size+1, Some(remainder_size));
+ } else {
+ return (plan_size, None);
+ }
+}
+
+impl<I: Clone, const S: usize, const BS: usize> DequerUtils<I, BS> for UpDequer<I, S> {
+ #[inline(always)]
+ fn cur_max_buffer_size(&self) -> usize {
+ S * self.len()
+ }
+
+ fn update_buffer_array(&self, buf: &mut [I; BS]) -> bool {
+ let total_bufs = self.len();
+ let max_size = S * total_bufs;
+ if BS > max_size {
+ return false;
+ }
+ // XXX: calculate this at runtime for now, const generics (atleast currently) can't be used
+ // for calculating consts
+ // <https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.md#when-a-const-variable-can-be-used>
+ // TODO: this could be made const itself by making a consts mod and putting the update and
+ // buffer sizes as consts in there instead of having them as const generic arguments
+ let plan = make_buf_plan(BS, S);
+ //let mut cur_start = 0;
+ let mut cur_index = total_bufs - plan.0;
+ //println!("{:?}, {}, {}", plan, cur_index, S);
+ let (copy_s, slicey) = match plan.1 {
+ None => (S, self.get(cur_index).unwrap().as_slice()),
+ Some(copy_size) => (copy_size, &self.get(cur_index).unwrap()[(S-copy_size)..])
+ };
+ //println!("{:?}", slicey);
+ // do the copy n stuff
+ let (mut left, mut right) = buf.split_at_mut(copy_s);
+ left.clone_from_slice(slicey);
+ //cur_start += copy_s;
+ cur_index += 1;
+ for up_i in cur_index..total_bufs {
+ //let (mut l2, r2) = right.split_at_mut(S);
+ //right = r2;
+ (left, right) = right.split_at_mut(S);
+ left.clone_from_slice(self.get(up_i).unwrap().as_slice());
+ }
+ return true;
+ }
+
+ /*fn make_buffer_vec(&self) -> Option<[I; BS]> {
+ let total_bufs = self.len();
+ let max_size = S * total_bufs;
+ if BS > max_size {
+ return None;
+ }
+ // convert this bit to a const fn, because it can be :o, could maybe make a const
+ // copy_bytes_from_iter function or something
+ //const remainder_size: usize = BS / S;
+ //const S_2: usize = S;
+ //const PLAN: (usize, Option<usize>) = make_buf_plan(BS_2, S_2);
+ /*const PARTIAL_FIRST: bool = PLAN.1.is_some();
+ let mut cur_start = 0;
+ let mut cur_index = total_bufs - PLAN.0;
+ let mut ret: [f64; BS] = [0.0; BS];
+ if PARTIAL_FIRST {
+ const Some(PVAL): usize = PARTIAL_FIRST;
+ let slicey = self.get(cur_index).rsplit_array_ref(copy_size);
+ let (left, _) = ret.split_array_mut::<PVAL>();
+ left.clone_from(slicey);
+ }*/
+ /*let slicey = match PLAN.1 {
+ None => &self.get(cur_index)
+ Some(copy_size) => &self.get(cur_index).rsplit_array_ref(copy_size)
+ }*/
+ let mut ret: [I; BS] = [I::default(); BS];
+ return Some(ret);
+ }*/
+}
+
+#[derive(Debug)]
+pub struct ProcerData<'nl, I: NoteValue> {
+ pub pnotes: ProcerNotes<'nl, I>,
+ size: usize,
+ frequency: usize,
+ pub current: bool,
+}
+
+impl<'nl, I: NoteValue> ProcerData<'nl, I> {
+ pub fn new<const US: usize>(procer: &impl Procer<I, US>, pnotes: ProcerNotes<'nl, I>) -> Self {
+ return Self {
+ pnotes: pnotes,
+ size: procer.get_size(),
+ frequency: procer.get_frequency(),
+ current: false,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::proc::{Update, UpDequer, DequerUtils};
+ use std::collections::VecDeque;
+ #[test]
+ fn test_update_buffer_array() {
+ let mut data_dq: UpDequer<f32, 5> = VecDeque::with_capacity(6);
+ let updates: [Update<f32, 5>; 5] = [[1.0, 2.0, 3.0, 4.0, 5.0], [6.0, 7.0, 8.0, 9.0, 10.0], [11.0, 12.0, 13.0, 14.0, 15.0], [16.0, 17.0, 18.0, 19.0, 20.0], [21.0, 22.0, 23.0, 24.0, 25.0]];
+ data_dq.push_back(updates[0]);
+ data_dq.push_back(updates[1]);
+ data_dq.push_back(updates[2]);
+ data_dq.push_back(updates[3]);
+ data_dq.push_back(updates[4]);
+ let mut ba: [f32; 24] = [0.0; 24];
+ let copied = data_dq.update_buffer_array(&mut ba);
+ assert_eq!(copied, true);
+ assert_eq!(ba, [2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0]);
+ let mut small_ba: [f32; 3] = [0.0; 3];
+ let copied2 = data_dq.update_buffer_array(&mut small_ba);
+ assert_eq!(copied2, true);
+ assert_eq!(small_ba, [23.0, 24.0, 25.0]);
+ let mut full_ba: [f32; 25] = [0.0; 25];
+ let copied3 = data_dq.update_buffer_array(&mut full_ba);
+ assert_eq!(copied2, true);
+ assert_eq!(full_ba, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0]);
+ let mut toobig: [f32; 26] = [0.0; 26];
+ let copied4 = data_dq.update_buffer_array(&mut toobig);
+ assert_eq!(copied4, false);
+ assert_eq!(toobig, [0.0; 26]);
+ }
+}
+
diff --git a/src/rfft.rs b/src/rfft.rs
new file mode 100644
index 0000000..8e5fc0f
--- /dev/null
+++ b/src/rfft.rs
@@ -0,0 +1,179 @@
+use crate::proc::{Procer, UpDequer, DequerUtils, Update};
+use crate::notes::{Note, NoteValue, ProcerNote};
+use core::ops::Range;
+//use std::collections::VecDeque;
+use std::sync::Arc;
+
+use realfft::{RealFftPlanner, FftNum, RealToComplex};
+use rustfft::num_complex::Complex;
+//use rustfft::num_traits::Zero;
+use rustfft::num_traits::One;
+use rustfft::num_traits::float::Float;
+use derive_more::Display;
+use std::fmt::Debug;
+
+
+trait DebugableRTC<I>: RealToComplex<I> + Debug {}
+
+
+// the Arc<dyn> here really increases compile times, but that might just be because it has to link
+// in realfft/rustfft where before it didn't get linked in
+
+#[derive(Display)]
+#[display(fmt="RfftProcer(size={}, outsize={}, freq={}, deque=(size={}, cap={}), in_buf_size={}, scratch=(size={}, cap={}) out_data=(size={}, cap={}))", size, outsize, frequency, "in_deque.len()", "in_deque.capacity()", "in_buf.len()", "scratch.len()", "scratch.capacity()", "out_data.len()", "out_data.capacity()")]
+pub struct RFftProcer<I: Clone + FftNum, const US: usize, const BS: usize> {
+ pub size: usize,
+ pub outsize: usize,
+ pub frequency: usize,
+ pub in_deque: UpDequer<I, US>,
+ pub in_buf: [I; BS],
+ pub rfft: Arc<dyn RealToComplex<I>>,
+ // TODO: could make this a fixed array instead of a vec too maybe
+ pub scratch: Vec<Complex<I>>,
+ pub out_data: Vec<Complex<I>>,
+ pub scale: I,
+ //pub in_deque: VecDeque<Update<A>>,
+}
+
+impl<I: Default + Copy + FftNum + Float, const US: usize, const BS: usize> RFftProcer<I, US, BS> {
+ pub fn new(frequency: usize) -> Self {
+ let retsize = BS/2+1;
+ // TODO: could do some cleverness with using double then you'd only need to pop elements
+ // off after you're double-full, but i think just having a buffer of 1 or 2 elements is
+ // easier here and popping before each add in process_data if we're full
+ let dq_capacity = (BS/US)+2;
+ assert!((dq_capacity * US) > BS);
+ let mut planner = RealFftPlanner::new();
+ let rfft = planner.plan_fft_forward(BS);
+ let scratch = rfft.make_scratch_vec();
+ let out_data = rfft.make_output_vec();
+ return Self {
+ size: BS,
+ outsize: retsize,
+ frequency: frequency,
+ in_deque: UpDequer::with_capacity(dq_capacity),
+ in_buf: [I::default(); BS],
+ rfft: rfft,
+ //out_data: Vec::with_capacity(retsize),
+ scratch: scratch,
+ out_data: out_data,
+ // the docs say to scale by 1/sqrt(len), but it seems like 1/len (where len is the real
+ // input size) is the correct scaling to get results with norm_sqrt results between 0-1
+ // I'm still not entirely sure about this though, l1_norm also seems to max out at len
+ // without scaling so 1/len i think is right for it too, but it also gives lower
+ // results when there's an imaginary part
+ //scale: /*I::from_usize(10).unwrap()*/I::one() / I::from_usize(retsize/*-1*/).unwrap().sqrt(),
+ scale: I::one() / I::from_usize(BS).unwrap(),
+ }
+ }
+}
+
+impl<I: Default + Clone + FftNum + Float + NoteValue, const US: usize, const BS: usize> Procer<I, US> for RFftProcer<I, US, BS> {
+ fn get_size(&self) -> usize {
+ self.size
+ }
+ fn get_frequency(&self) -> usize {
+ self.frequency
+ }
+
+ fn make_pnotes<'nl>(&self, notes: &'nl [Note]) -> Vec<ProcerNote<'nl, I>> {
+ // TODO: +1 or nah? (might have to handle it differently for even or odd, hopefully not
+ // though)
+ //let retsize = self.size/2+1;
+ let retsize = self.outsize;
+ let noteslen = notes.len();
+ let mut ret: Vec<ProcerNote<'nl, I>> = Vec::with_capacity(noteslen);
+ let mut cur_note_idx = 0;
+ let mut cutoff = notes[0].high_freq;
+ let scale: f64 = (self.frequency as f64)/(self.size as f64);
+ // TODO: since we do have .low_freq here as well you can start the low size of the range
+ // close to .low_freq (how close?) but for now we'll start it at 1
+ let mut cur_range: Range<usize> = 1..1;
+ for i in 1..retsize {
+ let freq: f64 = (i as f64) * scale;
+ if freq > cutoff {
+ while freq > cutoff {
+ let pnote = ProcerNote::<'nl, I>(&notes[cur_note_idx], cur_range.clone(), I::default(), I::default());
+ ret.push(pnote);
+ cur_range.start = cur_range.end;
+ // worth noting this is different than the python code,
+ // here frequencies above the high note aren't added in
+ // where the python code adds them all to the highest
+ // note, you change that by changing the return below
+ // here to a cutoff = <however you express infinite> i
+ // think
+ cur_note_idx += 1;
+ if cur_note_idx >= noteslen {
+ return ret;
+ }
+ cutoff = notes[cur_note_idx].high_freq;
+ }
+ }
+ //else {
+ cur_range.end += 1;
+ //}
+ }
+ // add the final pnote if we ran out of frequencies before running
+ // out of notes
+ ret.push(ProcerNote::<'nl, I>(&notes[cur_note_idx], cur_range, I::default(), I::default()));
+ return ret;
+ }
+
+ fn process_data(&mut self, input: &Update<I, US>, notes: &mut [ProcerNote<I>]) -> bool {
+ let dq_len = self.in_deque.len();
+ let dq_cap = self.in_deque.capacity();
+ // we should probably make sure buf_size can be made with the length of dq as well though
+ // so capacity can grow if new() messed up with the capacity
+ if dq_len == dq_cap {
+ self.in_deque.pop_front();
+ }
+ let scale = self.scale;
+ // XXX: mm this copies twice per update (once to add it to the deque and once to the buffer)
+ // which i kinda wanted to avoid (using an Arc would avoid it but it'd be extra heap
+ // allocations) atleast it doesn't realloc though so hopefully that's ok
+ // depending on how this works with Alsa i might wanna switch to passing in a slice and
+ // storing references in the UpDeque but that makes lifetime soup
+ // or something else maybes
+ // OHH though since i'm probably getting i16 from alsa and want to convert to f32 anyway it
+ // makes sense to copy (question is will that add a third copy or will the compiler be
+ // smart enough to convert inplace, i think ideally the conversion would happen here in
+ // this push_back actually, maybe the input Update should be AI and this push_back should
+ // be as I or Into or whatever)
+ self.in_deque.push_back(input.clone());
+ let updated = self.in_deque.update_buffer_array(&mut self.in_buf);
+ //println!("updated = {}", updated);
+ if updated {
+ //assert_eq!(self.in_buf, [I::default(); BS]);
+ let post_scale = I::from_u8(2).unwrap().sqrt();
+ self.rfft.process_with_scratch(&mut self.in_buf, &mut self.out_data, &mut self.scratch).unwrap();
+ for pnote in notes {
+ // TODO: figure out the best calculation for this
+ // TODO: map/sum or product might be quicker then fold here, since fold can't
+ // really use simd, also look at what the perftest code did to use simd
+ // XXX: python's abs uses the equiv of sqrt(norm_sqr) (https://docs.rs/num-complex/0.4.3/num_complex/struct.Complex.html#method.norm_sqr)
+ // ie. sqrt(|real|**2 + |imag|**2), gonna just use norm_sqrt here for now
+ // l1_norm might also be a good option (just |real| + |imag|), and gets rid of the
+ // pow too, but i dunno what's best here
+ // TODO: the python test2.py code actually currently uses max instead of folding here too
+ //let complexes = self.out_data[pnote.1.clone()].iter().fold((I::default(), Complex::<I>::default()), |(sum, mx), c| {let c_sqrt = c.scale(scale).norm_sqr().sqrt(); (sum + c_sqrt, std::cmp::max_by(mx, *c, |a: &Complex<I>, b: &Complex<I>| { a.norm_sqr().partial_cmp(&b.norm_sqr()).unwrap() }))});
+ let complexes = self.out_data[pnote.1.clone()].iter().fold((I::default(), Complex::<I>::default(), I::default()), |(sum, mx_c, mx_sqrt), c| {
+ let c_sqr = c.scale(scale).norm_sqr();
+ let c_sqrt = c_sqr.sqrt();
+ if c_sqrt > mx_sqrt {
+ (sum + c_sqr, *c, c_sqrt)
+ } else {
+ (sum + c_sqr, mx_c, mx_sqrt)
+ }
+ });
+ pnote.2 = complexes.0.sqrt() * post_scale; // / I::from_usize(pnote.1.len()).unwrap();
+ //pnote.3 = complexes.1.scale(scale).norm_sqr() * I::from_f32(2.0).unwrap();
+ //pnote.2 = complexes.0.scale(scale).norm_sqr().sqrt();
+ //pnote.3 = complexes.1.scale(scale).norm_sqr().sqrt();
+ pnote.3 = complexes.2 * post_scale;
+ //pnote.2 = self.out_data[pnote.1.clone()].iter().fold(I::default(), |acc, c| acc + (c.norm_sqr().sqrt()*scale))/*.sqrt()*/;
+ //pnote.2 = self.out_data[pnote.1.clone()].iter().map(|c| c.norm_sqr()).max_by(|a, b| { a.partial_cmp(b).unwrap() } );
+ }
+ }
+ return updated;
+ }
+}