mirror of
https://github.com/ivabus/speakersafetyd
synced 2024-11-10 02:15:16 +03:00
Wheee it works
Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
parent
1bb490871f
commit
21b3e49456
472
Cargo.lock
generated
472
Cargo.lock
generated
|
@ -22,23 +22,107 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[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 = "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.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap-verbosity-flag"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23e2b6c3dcdb73299f48ae05b294da14e2f560b3ed2c09e742269eb1b22af231"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"lazy_static",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "configparser"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5458d9d1a587efaf5091602c59d299696a3877a439c8f6d461a2d3cce11df87a"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
|
@ -46,6 +130,27 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[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 = "half"
|
||||
version = "2.1.0"
|
||||
|
@ -55,12 +160,98 @@ dependencies = [
|
|||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[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.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.2"
|
||||
|
@ -72,6 +263,27 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.26"
|
||||
|
@ -79,10 +291,268 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "speakersafety-2"
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
|
||||
[[package]]
|
||||
name = "simple_logger"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e190a521c2044948158666916d9e872cbb9984f755e9bb3b5b75a836205affcd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"colored",
|
||||
"log",
|
||||
"time",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "speakersafetyd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"alsa",
|
||||
"clap",
|
||||
"clap-verbosity-flag",
|
||||
"configparser",
|
||||
"half",
|
||||
"log",
|
||||
"simple_logger",
|
||||
]
|
||||
|
||||
[[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.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[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.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[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.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
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-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
|
||||
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.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "speakersafety-2"
|
||||
name = "speakersafetyd"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
@ -8,4 +8,8 @@ edition = "2021"
|
|||
[dependencies]
|
||||
half = "^2.1.0"
|
||||
alsa = { path = "./alsa" }
|
||||
configparser = "^3.0.2"
|
||||
configparser = { version = "^3.0.2", features=["indexmap"] }
|
||||
clap = { version = "4.1.6", features=["derive"] }
|
||||
log = "0.4.17"
|
||||
clap-verbosity-flag = "2.0.0"
|
||||
simple_logger = "4.0.0"
|
||||
|
|
141
j314.conf
141
j314.conf
|
@ -1,71 +1,92 @@
|
|||
[Left Tweeter]
|
||||
r_shunt = 1
|
||||
r_dc = 2
|
||||
r_amp = 3
|
||||
tau_coil = 4
|
||||
tau_magnet = 5
|
||||
tr_coil = 6
|
||||
ramp_factor = 7
|
||||
temp_limit = 100
|
||||
vs_chan = 1
|
||||
[Globals]
|
||||
visense_pcm = 2
|
||||
t_ambient = 50.0
|
||||
t_safe_max = 100
|
||||
t_hysteresis = 10
|
||||
channels = 12
|
||||
period = 4096
|
||||
|
||||
[Speaker/Left Woofer 1]
|
||||
group = 1
|
||||
tr_coil = 28.09
|
||||
tr_magnet = 34.43
|
||||
tau_coil = 3.05
|
||||
tau_magnet = 192.45
|
||||
t_limit = 140.0
|
||||
t_headroom = 10.0
|
||||
z_nominal = 3.2
|
||||
is_scale = 3.75
|
||||
vs_scale = 14
|
||||
is_chan = 0
|
||||
vs_chan = 1
|
||||
|
||||
[Right Tweeter]
|
||||
r_shunt = 1
|
||||
r_dc = 2
|
||||
r_amp = 3
|
||||
tau_coil = 4
|
||||
tau_magnet = 5
|
||||
tr_coil = 6
|
||||
ramp_factor = 7
|
||||
temp_limit = 100
|
||||
vs_chan = 3
|
||||
[Speaker/Right Woofer 1]
|
||||
group = 1
|
||||
tr_coil = 28.09
|
||||
tr_magnet = 34.43
|
||||
tau_coil = 3.05
|
||||
tau_magnet = 192.45
|
||||
t_limit = 140.0
|
||||
t_headroom = 10.0
|
||||
z_nominal = 3.2
|
||||
is_scale = 3.75
|
||||
vs_scale = 14
|
||||
is_chan = 2
|
||||
vs_chan = 3
|
||||
|
||||
[Left Woofer 1]
|
||||
r_shunt = 1
|
||||
r_dc = 2
|
||||
r_amp = 3
|
||||
tau_coil = 4
|
||||
tau_magnet = 5
|
||||
tr_coil = 6
|
||||
ramp_factor = 7
|
||||
temp_limit = 100
|
||||
vs_chan = 5
|
||||
[Speaker/Left Tweeter]
|
||||
group = 0
|
||||
tr_coil = 34.5
|
||||
tr_magnet = 48.2
|
||||
tau_coil = 2.31
|
||||
tau_magnet = 61.4
|
||||
t_limit = 140.0
|
||||
t_headroom = 10.0
|
||||
z_nominal = 3.2
|
||||
is_scale = 3.75
|
||||
vs_scale = 14
|
||||
is_chan = 4
|
||||
vs_chan = 5
|
||||
|
||||
[Right Woofer 1]
|
||||
r_shunt = 1
|
||||
r_dc = 2
|
||||
r_amp = 3
|
||||
tau_coil = 4
|
||||
tau_magnet = 5
|
||||
tr_coil = 6
|
||||
ramp_factor = 7
|
||||
temp_limit = 100
|
||||
vs_chan = 7
|
||||
[Speaker/Right Tweeter]
|
||||
group = 0
|
||||
tr_coil = 34.5
|
||||
tr_magnet = 48.2
|
||||
tau_coil = 2.31
|
||||
tau_magnet = 61.4
|
||||
t_limit = 140.0
|
||||
t_headroom = 10.0
|
||||
z_nominal = 3.2
|
||||
is_scale = 3.75
|
||||
vs_scale = 14
|
||||
is_chan = 6
|
||||
vs_chan = 7
|
||||
|
||||
[Left Woofer 2]
|
||||
r_shunt = 1
|
||||
r_dc = 2
|
||||
r_amp = 3
|
||||
tau_coil = 4
|
||||
tau_magnet = 5
|
||||
tr_coil = 6
|
||||
ramp_factor = 7
|
||||
temp_limit = 100
|
||||
vs_chan = 9
|
||||
[Speaker/Left Woofer 2]
|
||||
group = 1
|
||||
tr_coil = 28.09
|
||||
tr_magnet = 34.43
|
||||
tau_coil = 3.05
|
||||
tau_magnet = 192.45
|
||||
t_limit = 140.0
|
||||
t_headroom = 10.0
|
||||
z_nominal = 3.2
|
||||
is_scale = 3.75
|
||||
vs_scale = 14
|
||||
is_chan = 8
|
||||
vs_chan = 9
|
||||
|
||||
[Right Woofer 2]
|
||||
r_shunt = 1
|
||||
r_dc = 2
|
||||
r_amp = 3
|
||||
tau_coil = 4
|
||||
tau_magnet = 5
|
||||
tr_coil = 6
|
||||
ramp_factor = 7
|
||||
temp_limit = 100
|
||||
vs_chan = 11
|
||||
[Speaker/Right Woofer 2]
|
||||
group = 1
|
||||
tr_coil = 28.09
|
||||
tr_magnet = 34.43
|
||||
tau_coil = 3.05
|
||||
tau_magnet = 192.45
|
||||
t_limit = 140.0
|
||||
t_headroom = 10.0
|
||||
z_nominal = 3.2
|
||||
is_scale = 3.75
|
||||
vs_scale = 14
|
||||
is_chan = 10
|
||||
vs_chan = 11
|
||||
|
||||
|
|
130
src/helpers.rs
130
src/helpers.rs
|
@ -1,9 +1,9 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// (C) 2022 The Asahi Linux Contributors
|
||||
|
||||
use configparser::ini::Ini;
|
||||
use alsa::mixer::MilliBel;
|
||||
use alsa;
|
||||
use alsa::mixer::MilliBel;
|
||||
use configparser::ini::Ini;
|
||||
|
||||
/**
|
||||
Failsafe: Limit speaker volume massively and bail.
|
||||
|
@ -34,20 +34,21 @@ pub fn open_card(card: &str) -> alsa::ctl::Ctl {
|
|||
println!("{}: Could not open sound card! Error: {}", card, e);
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return ctldev;
|
||||
}
|
||||
|
||||
pub fn open_pcm(dev: &str, chans: &u32) -> alsa::pcm::PCM {
|
||||
let pcm = alsa::pcm::PCM::new(dev, alsa::Direction::Capture, false)
|
||||
.unwrap();
|
||||
pub fn open_pcm(dev: &str, chans: u32, sample_rate: u32) -> alsa::pcm::PCM {
|
||||
let pcm = alsa::pcm::PCM::new(dev, alsa::Direction::Capture, false).unwrap();
|
||||
{
|
||||
let params = alsa::pcm::HwParams::any(&pcm).unwrap();
|
||||
|
||||
params.set_channels(*chans).unwrap();
|
||||
params.set_rate(48000, alsa::ValueOr::Nearest).unwrap();
|
||||
params.set_channels(chans).unwrap();
|
||||
params
|
||||
.set_rate(sample_rate, alsa::ValueOr::Nearest)
|
||||
.unwrap();
|
||||
params.set_format(alsa::pcm::Format::s16()).unwrap();
|
||||
params.set_access(alsa::pcm::Access::RWInterleaved).unwrap();
|
||||
pcm.hw_params(¶ms).unwrap();
|
||||
|
@ -58,29 +59,19 @@ pub fn open_pcm(dev: &str, chans: &u32) -> alsa::pcm::PCM {
|
|||
|
||||
/**
|
||||
Wrapper around configparser::ini::Ini.getint()
|
||||
to safely unwrap the Result<Option<f64>, E> returned by
|
||||
to safely unwrap the Result<Option<i64>, E> returned by
|
||||
it.
|
||||
*/
|
||||
pub fn parse_int(config: &Ini, section: &str, key: &str) -> i64 {
|
||||
let _result: Option<i64> = match config.getint(section, key) {
|
||||
Ok(result) => match result{
|
||||
Some(inner) => {
|
||||
let integer: i64 = inner;
|
||||
return integer;
|
||||
},
|
||||
None => {
|
||||
println!("{}: Failed to parse {}", section, key);
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
},
|
||||
Err(e) => {
|
||||
println!("{}: Invalid value for {}. Error: {}", section, key, e);
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
};
|
||||
|
||||
pub fn parse_int<T: TryFrom<i64>>(config: &Ini, section: &str, key: &str) -> T
|
||||
where
|
||||
<T as TryFrom<i64>>::Error: std::fmt::Debug,
|
||||
{
|
||||
config
|
||||
.getint(section, key)
|
||||
.expect(&format!("{}/{}: Invalid value", section, key))
|
||||
.expect(&format!("{}/{}: Missing key", section, key))
|
||||
.try_into()
|
||||
.expect("{}/{}: Out of bounds")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,25 +80,13 @@ pub fn parse_int(config: &Ini, section: &str, key: &str) -> i64 {
|
|||
it.
|
||||
*/
|
||||
pub fn parse_float(config: &Ini, section: &str, key: &str) -> f32 {
|
||||
let _result: Option<f64> = match config.getfloat(section, key) {
|
||||
Ok(result) => match result{
|
||||
Some(inner) => {
|
||||
let float: f32 = inner as f32;
|
||||
return float;
|
||||
},
|
||||
None => {
|
||||
println!("{}: Failed to parse {}", section, key);
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
},
|
||||
Err(e) => {
|
||||
println!("{}: Invalid value for {}. Error: {}", section, key, e);
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
};
|
||||
let val = config
|
||||
.getfloat(section, key)
|
||||
.expect(&format!("{}/{}: Invalid value", section, key))
|
||||
.expect(&format!("{}/{}: Missing key", section, key)) as f32;
|
||||
|
||||
assert!(val.is_finite());
|
||||
val
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,49 +100,59 @@ pub fn new_elemvalue(t: alsa::ctl::ElemType) -> alsa::ctl::ElemValue {
|
|||
println!("Could not open a handle to an element!");
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Wrapper for alsa::ctl::Ctl::elem_read().
|
||||
*/
|
||||
pub fn read_ev(card: &alsa::ctl::Ctl, ev: &mut alsa::ctl::ElemValue, name: &str) {
|
||||
let _val = match card.elem_read(ev) { // alsa:Result<()>
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
println!("Could not read elem value {}. alsa-lib error: {:?}", name, e);
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
};
|
||||
let _val = match card.elem_read(ev) {
|
||||
// alsa:Result<()>
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
println!(
|
||||
"Could not read elem value {}. alsa-lib error: {:?}",
|
||||
name, e
|
||||
);
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
Wrapper for alsa::ctl::Ctl::elem_write().
|
||||
*/
|
||||
pub fn write_ev(card: &alsa::ctl::Ctl, ev: &alsa::ctl::ElemValue, name: &str) {
|
||||
let _val = match card.elem_write(ev) { // alsa:Result<()>
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
println!("Could not write elem value {}. alsa-lib error: {:?}", name, e);
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
};
|
||||
let _val = match card.elem_write(ev) {
|
||||
// alsa:Result<()>
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
println!(
|
||||
"Could not write elem value {}. alsa-lib error: {:?}",
|
||||
name, e
|
||||
);
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn int_to_db(card: &alsa::ctl::Ctl, id: &alsa::ctl::ElemId, val: i32) -> MilliBel {
|
||||
let db = match card.convert_to_db(id, val.into()) {
|
||||
Ok(inner) => inner,
|
||||
Err(e) => {
|
||||
println!("Could not convert val {} to dB! alsa-lib error: {:?}", val, e);
|
||||
println!(
|
||||
"Could not convert val {} to dB! alsa-lib error: {:?}",
|
||||
val, e
|
||||
);
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return db;
|
||||
|
@ -174,10 +163,13 @@ pub fn db_to_int(card: &alsa::ctl::Ctl, id: &alsa::ctl::ElemId, val: f32) -> i32
|
|||
let new_int = match card.convert_from_db(id, mb, alsa::Round::Floor) {
|
||||
Ok(inner) => inner as i32,
|
||||
Err(e) => {
|
||||
println!("Could not convert MilliBel {:?} to int! alsa-lib error: {:?}", val, e);
|
||||
println!(
|
||||
"Could not convert MilliBel {:?} to int! alsa-lib error: {:?}",
|
||||
val, e
|
||||
);
|
||||
fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
return new_int;
|
||||
|
|
186
src/main.rs
186
src/main.rs
|
@ -1,88 +1,172 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// (C) 2022 The Asahi Linux Contributors
|
||||
/**
|
||||
/*!
|
||||
Handles speaker safety on Apple Silicon machines. This code is designed to
|
||||
fail safe. The speaker should not be enabled until this daemon has successfully
|
||||
initialised. If at any time we run into an unrecoverable error (we shouldn't),
|
||||
we gracefully bail and use an IOCTL to shut off the speakers.
|
||||
*/
|
||||
use std::io;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::read_to_string;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::{thread::sleep, time};
|
||||
|
||||
use clap::Parser;
|
||||
use clap_verbosity_flag::{InfoLevel, Verbosity};
|
||||
use configparser::ini::Ini;
|
||||
use log;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use simple_logger::SimpleLogger;
|
||||
|
||||
mod types;
|
||||
mod helpers;
|
||||
mod types;
|
||||
|
||||
use crate::types::SafetyMonitor;
|
||||
static VERSION: &str = "0.0.1";
|
||||
|
||||
static ASAHI_DEVICE: &str = "hw:0";
|
||||
static VISENSE_PCM: &str = "hw:0,2";
|
||||
const DEFAULT_CONFIG_PATH: &str = "share/speakersafetyd";
|
||||
|
||||
// Will eventually be /etc/speakersafetyd/ or similar
|
||||
static CONFIG_DIR: &str = "./";
|
||||
static SUPPORTED: [&str; 1] = [
|
||||
"j314",
|
||||
];
|
||||
/// Simple program to greet a person
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Options {
|
||||
/// Path to the configuration file base directory
|
||||
#[arg(short, long)]
|
||||
config_path: Option<PathBuf>,
|
||||
|
||||
const BUF_SZ: usize = 128 * 6 * 2;
|
||||
/// Increase the log level
|
||||
#[command(flatten)]
|
||||
verbose: Verbosity<InfoLevel>,
|
||||
}
|
||||
|
||||
fn get_machine() -> String {
|
||||
let _compat: io::Result<String> = match read_to_string("/proc/device-tree/compatible") {
|
||||
Ok(compat) => {
|
||||
return compat[6..10].to_string();
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Could not read devicetree compatible: {}", e);
|
||||
std::process::exit(1);
|
||||
read_to_string("/proc/device-tree/compatible")
|
||||
.expect("Could not read device tree compatible")
|
||||
.strip_prefix("apple,")
|
||||
.expect("Unexpected compatible format")
|
||||
.split_once("\0")
|
||||
.expect("Unexpected compatible format")
|
||||
.0
|
||||
.trim_end_matches(|c: char| c.is_ascii_alphabetic())
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn get_speakers(config: &Ini) -> Vec<String> {
|
||||
config
|
||||
.sections()
|
||||
.iter()
|
||||
.filter_map(|a| a.strip_prefix("Speaker/"))
|
||||
.map(|a| a.to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
struct SpeakerGroup {
|
||||
speakers: Vec<types::Speaker>,
|
||||
gain: f32,
|
||||
}
|
||||
|
||||
impl Default for SpeakerGroup {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
speakers: Default::default(),
|
||||
gain: f32::NAN,
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn get_drivers(config: &Ini) -> Vec<String> {
|
||||
|
||||
let drivers = config.sections();
|
||||
|
||||
return drivers;
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
let args = Options::parse();
|
||||
|
||||
SimpleLogger::new()
|
||||
.with_level(args.verbose.log_level_filter())
|
||||
.without_timestamps()
|
||||
.init()
|
||||
.unwrap();
|
||||
info!("Starting up");
|
||||
|
||||
let mut config_path = args.config_path.unwrap_or_else(|| {
|
||||
let mut path = PathBuf::new();
|
||||
path.push(option_env!("PREFIX").unwrap_or("/usr/local"));
|
||||
path.push(DEFAULT_CONFIG_PATH);
|
||||
path
|
||||
});
|
||||
info!("Config base: {:?}", config_path);
|
||||
|
||||
let model: String = get_machine();
|
||||
info!("Model: {}", model);
|
||||
|
||||
config_path.push(&model);
|
||||
config_path.set_extension("conf");
|
||||
info!("Config file: {:?}", config_path);
|
||||
|
||||
let device = format!("hw:{}", model.to_ascii_uppercase());
|
||||
info!("Device: {}", device);
|
||||
|
||||
let mut cfg: Ini = Ini::new_cs();
|
||||
let mut speakers: Vec<types::Speaker> = Vec::new();
|
||||
let card: alsa::ctl::Ctl = helpers::open_card(&ASAHI_DEVICE);
|
||||
cfg.load(config_path).expect("Failed to read config file");
|
||||
|
||||
if SUPPORTED.contains(&model.as_str()) {
|
||||
cfg.load(CONFIG_DIR.to_owned() + &model + ".conf").unwrap();
|
||||
} else {
|
||||
println!("Unsupported machine {}", model);
|
||||
std::process::exit(1);
|
||||
let globals = types::Globals::parse(&cfg);
|
||||
|
||||
let speaker_names = get_speakers(&cfg);
|
||||
let speaker_count = speaker_names.len();
|
||||
info!("Found {} speakers", speaker_count);
|
||||
|
||||
info!("Opening control device");
|
||||
let ctl: alsa::ctl::Ctl = helpers::open_card(&device);
|
||||
|
||||
let mut groups: BTreeMap<usize, SpeakerGroup> = BTreeMap::new();
|
||||
|
||||
for i in speaker_names {
|
||||
let speaker: types::Speaker = types::Speaker::new(&globals, &i, &cfg, &ctl);
|
||||
|
||||
groups
|
||||
.entry(speaker.group)
|
||||
.or_default()
|
||||
.speakers
|
||||
.push(speaker);
|
||||
}
|
||||
|
||||
let list_drivers = get_drivers(&cfg);
|
||||
|
||||
for i in list_drivers {
|
||||
let new_speaker: types::Speaker = types::SafetyMonitor::new(&i, &cfg, &card);
|
||||
speakers.push(new_speaker);
|
||||
}
|
||||
|
||||
let num_chans: u32 = speakers.len().try_into().unwrap();
|
||||
assert!(
|
||||
groups
|
||||
.values()
|
||||
.map(|a| a.speakers.len())
|
||||
.fold(0, |a, b| a + b)
|
||||
== speaker_count
|
||||
);
|
||||
assert!(2 * speaker_count <= globals.channels);
|
||||
|
||||
let pcm_name = format!("{},{}", device, globals.visense_pcm);
|
||||
// Set up PCM to buffer in V/ISENSE
|
||||
let cap: alsa::pcm::PCM = helpers::open_pcm(&VISENSE_PCM, &num_chans);
|
||||
let mut buf = [0i16; BUF_SZ]; // 128 samples from V and I for 6 channels
|
||||
let io = cap.io_i16().unwrap();
|
||||
let pcm: alsa::pcm::PCM =
|
||||
helpers::open_pcm(&pcm_name, globals.channels.try_into().unwrap(), 48000);
|
||||
let mut buf = Vec::new();
|
||||
buf.resize(globals.period * globals.channels, 0i16);
|
||||
|
||||
let io = pcm.io_i16().unwrap();
|
||||
|
||||
let hwp = pcm.hw_params_current().unwrap();
|
||||
let sample_rate = hwp.get_rate().unwrap();
|
||||
|
||||
info!("Sample rate: {}", sample_rate);
|
||||
|
||||
loop {
|
||||
// Block while we're reading into the buffer
|
||||
io.readi(&mut buf).unwrap();
|
||||
for i in &mut speakers {
|
||||
i.run(&card, &buf);
|
||||
|
||||
for (idx, group) in groups.iter_mut() {
|
||||
let gain = group
|
||||
.speakers
|
||||
.iter_mut()
|
||||
.map(|s| s.run_model(&buf, sample_rate as f32))
|
||||
.reduce(f32::min)
|
||||
.unwrap();
|
||||
if gain != group.gain {
|
||||
if group.gain == 0. {
|
||||
warn!("Speaker group {} gain limited to {}", idx, gain);
|
||||
}
|
||||
group.speakers.iter_mut().for_each(|s| s.update(&ctl, gain));
|
||||
group.gain = gain;
|
||||
}
|
||||
}
|
||||
buf = [0i16; BUF_SZ];
|
||||
}
|
||||
}
|
||||
|
|
337
src/types.rs
337
src/types.rs
|
@ -1,9 +1,10 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
// (C) 2022 The Asahi Linux Contributors
|
||||
|
||||
use std::ffi::{CString, CStr};
|
||||
use configparser::ini::Ini;
|
||||
use alsa::ctl::Ctl;
|
||||
use configparser::ini::Ini;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
use crate::helpers;
|
||||
|
||||
|
@ -19,21 +20,19 @@ struct Elem {
|
|||
val: alsa::ctl::ElemValue,
|
||||
}
|
||||
|
||||
trait ALSAElem {
|
||||
fn new(name: String, card: &Ctl, t: alsa::ctl::ElemType) -> Self;
|
||||
}
|
||||
|
||||
impl ALSAElem for Elem {
|
||||
fn new(name: String, card: &Ctl, t: alsa::ctl::ElemType) -> Elem {
|
||||
impl Elem {
|
||||
fn new(name: String, card: &Ctl, t: alsa::ctl::ElemType) -> Elem {
|
||||
// CString::new() cannot borrow a String. We want name for the elem
|
||||
// for error identification though, so it can't consume name directly.
|
||||
let borrow: String = name.clone();
|
||||
|
||||
let mut new_elem: Elem = { Elem {
|
||||
elem_name: name,
|
||||
id: alsa::ctl::ElemId::new(alsa::ctl::ElemIface::Mixer),
|
||||
val: helpers::new_elemvalue(t),
|
||||
}};
|
||||
let mut new_elem: Elem = {
|
||||
Elem {
|
||||
elem_name: name,
|
||||
id: alsa::ctl::ElemId::new(alsa::ctl::ElemIface::Mixer),
|
||||
val: helpers::new_elemvalue(t),
|
||||
}
|
||||
};
|
||||
|
||||
let cname: CString = CString::new(borrow).unwrap();
|
||||
let cstr: &CStr = cname.as_c_str();
|
||||
|
@ -42,7 +41,7 @@ impl ALSAElem for Elem {
|
|||
new_elem.val.set_id(&new_elem.id);
|
||||
helpers::read_ev(card, &mut new_elem.val, &new_elem.elem_name);
|
||||
|
||||
return new_elem;
|
||||
return new_elem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,68 +57,111 @@ impl ALSAElem for Elem {
|
|||
struct Mixer {
|
||||
drv: String,
|
||||
level: Elem,
|
||||
vsense: Elem,
|
||||
isense: Elem,
|
||||
amp_gain: Elem,
|
||||
}
|
||||
|
||||
trait ALSACtl {
|
||||
fn new(name: &str, card: &Ctl) -> Self;
|
||||
|
||||
fn get_lvl(&mut self, card: &Ctl) -> f32;
|
||||
fn set_lvl(&mut self, card: &Ctl, lvl: f32);
|
||||
}
|
||||
|
||||
impl ALSACtl for Mixer {
|
||||
impl Mixer {
|
||||
// TODO: implement turning on V/ISENSE
|
||||
fn new(name: &str, card: &Ctl) -> Mixer {
|
||||
let new_mixer: Mixer = { Mixer {
|
||||
drv: name.to_owned(),
|
||||
level: ALSAElem::new(name.to_owned() + " Speaker Volume", card,
|
||||
alsa::ctl::ElemType::Integer),
|
||||
vsense: ALSAElem::new(name.to_owned() + " VSENSE Switch", card,
|
||||
alsa::ctl::ElemType::Boolean),
|
||||
isense: ALSAElem::new(name.to_owned() + " ISENSE Switch", card,
|
||||
alsa::ctl::ElemType::Boolean),
|
||||
}};
|
||||
let mut vs = Elem::new(
|
||||
name.to_owned() + " VSENSE Switch",
|
||||
card,
|
||||
alsa::ctl::ElemType::Boolean,
|
||||
);
|
||||
|
||||
return new_mixer;
|
||||
vs.val.set_boolean(0, true);
|
||||
helpers::write_ev(card, &vs.val, &vs.elem_name);
|
||||
helpers::read_ev(card, &mut vs.val, &vs.elem_name);
|
||||
assert!(vs.val.get_boolean(0).unwrap());
|
||||
|
||||
let mut is = Elem::new(
|
||||
name.to_owned() + " ISENSE Switch",
|
||||
card,
|
||||
alsa::ctl::ElemType::Boolean,
|
||||
);
|
||||
|
||||
is.val.set_boolean(0, true);
|
||||
helpers::write_ev(card, &is.val, &is.elem_name);
|
||||
helpers::read_ev(card, &mut vs.val, &vs.elem_name);
|
||||
assert!(vs.val.get_boolean(0).unwrap());
|
||||
|
||||
Mixer {
|
||||
drv: name.to_owned(),
|
||||
level: Elem::new(
|
||||
name.to_owned() + " Speaker Volume",
|
||||
card,
|
||||
alsa::ctl::ElemType::Integer,
|
||||
),
|
||||
amp_gain: Elem::new(
|
||||
name.to_owned() + " Amp Gain Volume",
|
||||
card,
|
||||
alsa::ctl::ElemType::Integer,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_amp_gain(&mut self, card: &Ctl) -> f32 {
|
||||
helpers::read_ev(card, &mut self.amp_gain.val, &self.amp_gain.elem_name);
|
||||
|
||||
let val = self
|
||||
.amp_gain
|
||||
.val
|
||||
.get_integer(0)
|
||||
.expect(&format!("Could not read amp gain for {}", self.drv));
|
||||
|
||||
helpers::int_to_db(card, &self.amp_gain.id, val).to_db()
|
||||
}
|
||||
|
||||
fn get_lvl(&mut self, card: &Ctl) -> f32 {
|
||||
helpers::read_ev(card, &mut self.level.val, &self.level.elem_name);
|
||||
|
||||
let val: i32 = match self.level.val.get_integer(0) {
|
||||
Some(inner) => inner,
|
||||
None => {
|
||||
println!("Could not read level from {}", self.drv);
|
||||
helpers::fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
};
|
||||
let val = self
|
||||
.level
|
||||
.val
|
||||
.get_integer(0)
|
||||
.expect(&format!("Could not read level for {}", self.drv));
|
||||
|
||||
let db: f32 = helpers::int_to_db(card, &self.level.id, val).to_db();
|
||||
|
||||
return db;
|
||||
helpers::int_to_db(card, &self.level.id, val).to_db()
|
||||
}
|
||||
|
||||
fn set_lvl(&mut self, card: &Ctl, lvl: f32) {
|
||||
|
||||
let new_val: i32 = helpers::db_to_int(card, &self.level.id, lvl);
|
||||
|
||||
match self.level.val.set_integer(0, new_val) {
|
||||
Some(_) => {},
|
||||
Some(_) => {}
|
||||
None => {
|
||||
println!("Could not set level for {}", self.drv);
|
||||
helpers::fail();
|
||||
std::process::exit(1);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
helpers::write_ev(card, &self.level.val, &self.level.elem_name);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Globals {
|
||||
pub visense_pcm: usize,
|
||||
pub channels: usize,
|
||||
pub period: usize,
|
||||
pub t_ambient: f32,
|
||||
pub t_safe_max: f32,
|
||||
pub t_hysteresis: f32,
|
||||
}
|
||||
|
||||
impl Globals {
|
||||
pub fn parse(config: &Ini) -> Self {
|
||||
Self {
|
||||
visense_pcm: helpers::parse_int(config, "Globals", "visense_pcm"),
|
||||
channels: helpers::parse_int(config, "Globals", "channels"),
|
||||
period: helpers::parse_int(config, "Globals", "period"),
|
||||
t_ambient: helpers::parse_float(config, "Globals", "t_ambient"),
|
||||
t_safe_max: helpers::parse_float(config, "Globals", "t_safe_max"),
|
||||
t_hysteresis: helpers::parse_float(config, "Globals", "t_hysteresis"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Struct representing a driver. Parameters are parsed out of a config
|
||||
|
@ -132,91 +174,170 @@ impl ALSACtl for Mixer {
|
|||
tau_coil: voice coil ramp time constant (seconds)
|
||||
tau_magnet: magnet ramp time constant (seconds)
|
||||
tr_coil: thermal resistance of voice coil (*C/W)
|
||||
temp_limit: absolute max temp of the voice coil (*C)
|
||||
t_limit: absolute max temp of the voice coil (*C)
|
||||
|
||||
Borrows the handle to the control interface to do calculations.
|
||||
*/
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SpeakerState {
|
||||
t_coil: f64,
|
||||
t_magnet: f64,
|
||||
|
||||
t_coil_hyst: f32,
|
||||
t_magnet_hyst: f32,
|
||||
|
||||
min_gain: f32,
|
||||
gain: f32,
|
||||
}
|
||||
|
||||
pub struct Speaker {
|
||||
name: String,
|
||||
pub name: String,
|
||||
pub group: usize,
|
||||
alsa_iface: Mixer,
|
||||
tau_coil: f32,
|
||||
tau_magnet: f32,
|
||||
tr_coil: f32,
|
||||
temp_limit: f32,
|
||||
vs_chan: i64,
|
||||
is_chan: i64,
|
||||
tr_magnet: f32,
|
||||
t_limit: f32,
|
||||
t_headroom: f32,
|
||||
z_nominal: f32,
|
||||
is_scale: f32,
|
||||
vs_scale: f32,
|
||||
is_chan: usize,
|
||||
vs_chan: usize,
|
||||
|
||||
g: Globals,
|
||||
s: SpeakerState,
|
||||
}
|
||||
|
||||
impl Speaker {
|
||||
pub fn new(globals: &Globals, name: &str, config: &Ini, ctl: &Ctl) -> Speaker {
|
||||
info!("Speaker [{}]:", name);
|
||||
|
||||
pub trait SafetyMonitor {
|
||||
fn new(driver_name: &str, config: &Ini, card: &Ctl) -> Self;
|
||||
fn power_now(&mut self, vs: &[i16], is: &[i16]) -> f32;
|
||||
fn run(&mut self, card: &Ctl, buf: &[i16; 128 * 6 * 2]);
|
||||
}
|
||||
let section = "Speaker/".to_owned() + name;
|
||||
let mut new_speaker: Speaker = Speaker {
|
||||
name: name.to_string(),
|
||||
alsa_iface: Mixer::new(&name, ctl),
|
||||
group: helpers::parse_int(config, §ion, "group"),
|
||||
tau_coil: helpers::parse_float(config, §ion, "tau_coil"),
|
||||
tau_magnet: helpers::parse_float(config, §ion, "tau_magnet"),
|
||||
tr_coil: helpers::parse_float(config, §ion, "tr_coil"),
|
||||
tr_magnet: helpers::parse_float(config, §ion, "tr_magnet"),
|
||||
t_limit: helpers::parse_float(config, §ion, "t_limit"),
|
||||
t_headroom: helpers::parse_float(config, §ion, "t_headroom"),
|
||||
z_nominal: helpers::parse_float(config, §ion, "z_nominal"),
|
||||
is_scale: helpers::parse_float(config, §ion, "is_scale"),
|
||||
vs_scale: helpers::parse_float(config, §ion, "vs_scale"),
|
||||
is_chan: helpers::parse_int(config, §ion, "is_chan"),
|
||||
vs_chan: helpers::parse_int(config, §ion, "vs_chan"),
|
||||
g: *globals,
|
||||
s: Default::default(),
|
||||
};
|
||||
|
||||
impl SafetyMonitor for Speaker {
|
||||
fn new(driver_name: &str, config: &Ini, card: &Ctl) -> Speaker {
|
||||
let new_speaker: Speaker = { Speaker {
|
||||
name: driver_name.to_string(),
|
||||
alsa_iface: ALSACtl::new(&driver_name, card),
|
||||
tau_coil: helpers::parse_float(config, driver_name, "tau_coil"),
|
||||
tau_magnet: helpers::parse_float(config, driver_name, "tau_magnet"),
|
||||
tr_coil: helpers::parse_float(config, driver_name, "tr_coil"),
|
||||
temp_limit: helpers::parse_float(config, driver_name, "temp_limit"),
|
||||
vs_chan: helpers::parse_int(config, driver_name, "vs_chan"),
|
||||
is_chan: helpers::parse_int(config, driver_name, "is_chan"),
|
||||
let s = &mut new_speaker.s;
|
||||
|
||||
}};
|
||||
// Worst case startup assumption
|
||||
s.t_coil = (new_speaker.t_limit - new_speaker.t_headroom) as f64;
|
||||
s.t_magnet = s.t_coil
|
||||
* (new_speaker.tr_magnet / (new_speaker.tr_magnet + new_speaker.tr_coil)) as f64;
|
||||
|
||||
return new_speaker;
|
||||
// s.t_coil = globals.t_ambient as f64;
|
||||
// s.t_magnet = globals.t_ambient as f64;
|
||||
|
||||
let max_dt = new_speaker.t_limit - new_speaker.t_headroom - globals.t_ambient;
|
||||
let max_pwr = max_dt / (new_speaker.tr_magnet + new_speaker.tr_coil);
|
||||
|
||||
let amp_gain = new_speaker.alsa_iface.get_amp_gain(ctl);
|
||||
|
||||
// Worst-case peak power is 2x RMS power
|
||||
let peak_pwr = 10f32.powf(amp_gain / 10.) / new_speaker.z_nominal * 2.;
|
||||
|
||||
s.min_gain = ((max_pwr / peak_pwr).log10() * 10.).min(0.);
|
||||
|
||||
assert!(new_speaker.is_chan < globals.channels);
|
||||
assert!(new_speaker.vs_chan < globals.channels);
|
||||
assert!(new_speaker.t_limit - new_speaker.t_headroom > globals.t_safe_max);
|
||||
|
||||
info!(" Group: {}", new_speaker.group);
|
||||
info!(" Max temperature: {:.1} °C", new_speaker.t_limit);
|
||||
info!(" Amp gain: {} dBV", amp_gain);
|
||||
info!(" Max power: {:.2} W", max_pwr);
|
||||
info!(" Peak power: {} W", peak_pwr);
|
||||
info!(" Min gain: {:.2} dB", s.min_gain);
|
||||
|
||||
new_speaker
|
||||
}
|
||||
|
||||
fn power_now(&mut self, vs: &[i16], is: &[i16]) -> f32 {
|
||||
let v_avg: f32 = helpers::average(vs) * (14 / (2 ^ 15)) as f32;
|
||||
let i_avg: f32 = helpers::average(is) * (3.75 / (2 ^ 15) as f32) as f32;
|
||||
pub fn run_model(&mut self, buf: &[i16], sample_rate: f32) -> f32 {
|
||||
let s = &mut self.s;
|
||||
|
||||
return v_avg * i_avg;
|
||||
}
|
||||
let step = 1. / sample_rate;
|
||||
let alpha_coil = (step / (self.tau_coil + step)) as f64;
|
||||
let alpha_magnet = (step / (self.tau_magnet + step)) as f64;
|
||||
|
||||
// I'm not sure on the maths here for determining when to start dropping the volume.
|
||||
fn run(&mut self, card: &Ctl, buf: &[i16; 128 * 6 * 2]) {
|
||||
let lvl: f32 = self.alsa_iface.get_lvl(card);
|
||||
let vsense = &buf[(128 * self.vs_chan) as usize .. (128 * (self.vs_chan + 1) - 1) as usize];
|
||||
let isense = &buf[(128 * self.is_chan) as usize .. (128 * (self.is_chan + 1) - 1) as usize];
|
||||
let mut pwr_sum = 0f32;
|
||||
|
||||
// Estimate temperature of VC and magnet
|
||||
let temp0: f32 = 35f32;
|
||||
let mut temp_vc: f32 = temp0;
|
||||
let mut temp_magnet: f32 = temp0;
|
||||
let alpha_vc: f32 = 0.01 / (temp_vc + 0.01);
|
||||
let alpha_magnet: f32 = 0.01 / (temp_magnet + 0.01);
|
||||
for sample in buf.chunks(self.g.channels) {
|
||||
assert!(sample.len() == self.g.channels);
|
||||
|
||||
// Power through the voice coil (average of most recent 128 samples)
|
||||
let pwr: f32 = self.power_now(&vsense, &isense);
|
||||
println!("Power now is {:.2} mW", pwr);
|
||||
let v = sample[self.vs_chan] as f32 / 32768.0 * self.vs_scale;
|
||||
let i = sample[self.is_chan] as f32 / 32768.0 * self.is_scale;
|
||||
let p = v * i;
|
||||
|
||||
let vc_target: f32 = temp_magnet + pwr * self.tau_coil;
|
||||
temp_vc = vc_target * alpha_vc + temp_vc * (1.0 - alpha_vc);
|
||||
println!("Current voice coil temp: {:.2} *C", temp_vc);
|
||||
let t_coil_target = s.t_magnet + (p * self.tr_coil) as f64;
|
||||
let t_magnet_target = (self.g.t_ambient + p * self.tr_magnet) as f64;
|
||||
|
||||
let magnet_target: f32 = temp0 + pwr * self.tau_magnet;
|
||||
temp_magnet = magnet_target * alpha_magnet + temp_magnet * (1.0 - alpha_magnet);
|
||||
println!("Current magnet temp: {:.2} *C", temp_magnet);
|
||||
s.t_coil = t_coil_target * alpha_coil + s.t_coil * (1. - alpha_coil);
|
||||
s.t_magnet = t_magnet_target * alpha_magnet + s.t_magnet * (1. - alpha_magnet);
|
||||
|
||||
if temp_vc < self.temp_limit {
|
||||
println!("Voice coil for {} below temp limit, ramping back up.", self.name);
|
||||
// For every degree below temp_limit, raise level by 0.5 dB
|
||||
let new_lvl: f32 = lvl + ((self.temp_limit - temp_vc) * 0.5);
|
||||
self.alsa_iface.set_lvl(card, new_lvl);
|
||||
if s.t_coil > self.t_limit as f64 {
|
||||
panic!(
|
||||
"{}: Coil temperature limit exceeded ({} > {})",
|
||||
self.name, s.t_coil, self.t_limit
|
||||
);
|
||||
}
|
||||
if s.t_magnet > self.t_limit as f64 {
|
||||
panic!(
|
||||
"{}: Magnet temperature limit exceeded ({} > {})",
|
||||
self.name, s.t_magnet, self.t_limit
|
||||
);
|
||||
}
|
||||
|
||||
pwr_sum += p;
|
||||
}
|
||||
|
||||
if temp_vc > (self.temp_limit - 15f32) {
|
||||
println!("Voice coil at {}*C on {}! Dropping volume!", temp_vc, self.name);
|
||||
// For every degree above temp_limit, drop the level by 1.5 dB
|
||||
let new_lvl: f32 = lvl - ((temp_vc - (self.temp_limit - 15f32)) * 1.5);
|
||||
self.alsa_iface.set_lvl(card, new_lvl);
|
||||
let pwr_avg: f32 = pwr_sum / ((buf.len() / self.g.channels) as f32);
|
||||
|
||||
s.t_coil_hyst = s
|
||||
.t_coil_hyst
|
||||
.max(s.t_coil as f32)
|
||||
.min(s.t_coil as f32 + self.g.t_hysteresis);
|
||||
s.t_magnet_hyst = s
|
||||
.t_magnet_hyst
|
||||
.max(s.t_magnet as f32)
|
||||
.min(s.t_magnet as f32 + self.g.t_hysteresis);
|
||||
|
||||
let temp = s.t_coil_hyst.max(s.t_magnet_hyst);
|
||||
|
||||
let reduction =
|
||||
(temp - self.g.t_safe_max) / (self.t_limit - self.t_headroom - self.g.t_safe_max);
|
||||
let gain = s.min_gain * reduction.max(0.);
|
||||
|
||||
s.gain = gain;
|
||||
|
||||
debug!(
|
||||
"{}: Coil {:.2} °C Magnet {:.2} °C Power {:.2} W Gain {:.2} dB",
|
||||
self.name, s.t_coil, s.t_magnet, pwr_avg, gain
|
||||
);
|
||||
|
||||
if s.gain > -0.01 {
|
||||
s.gain = 0.;
|
||||
}
|
||||
|
||||
println!("Volume on {} is now {} dB", self.name, self.alsa_iface.get_lvl(card));
|
||||
s.gain
|
||||
}
|
||||
|
||||
pub fn update(&mut self, ctl: &Ctl, gain: f32) {
|
||||
self.alsa_iface.set_lvl(ctl, gain);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue