Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
5201b503e6
|
|||
|
79544f64c3
|
|||
|
df06d7fa36
|
|||
|
af5a345458
|
|||
|
cf05f6a796
|
|||
|
cce05dbfcc
|
|||
|
70543e210a
|
|||
|
ccfb20f8bf
|
|||
|
0bb335e429
|
|||
|
754527d029
|
@@ -25,12 +25,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install system dependencies
|
|
||||||
if: matrix.arch == 'x86_64'
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libncurses-dev
|
|
||||||
|
|
||||||
- name: Install Rust stable
|
- name: Install Rust stable
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
|
|||||||
Generated
+271
-23
@@ -38,7 +38,7 @@ version = "1.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -48,7 +48,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -57,6 +57,12 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -64,13 +70,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cfg-if"
|
||||||
version = "1.2.1"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
dependencies = [
|
|
||||||
"shlex",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
@@ -118,6 +121,83 @@ version = "1.0.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crossterm_winapi",
|
||||||
|
"derive_more",
|
||||||
|
"document-features",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"rustix",
|
||||||
|
"signal-hook",
|
||||||
|
"signal-hook-mio",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm_winapi"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more-impl"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustc_version",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "document-features"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
|
||||||
|
dependencies = [
|
||||||
|
"litrs",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -132,19 +212,47 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.164"
|
version = "0.2.186"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ncurses"
|
name = "linux-raw-sys"
|
||||||
version = "6.0.1"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fbd71afa95710e841d173521e8483e764004eb332bdf47bd01d00f568688027f"
|
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litrs"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
|
||||||
"libc",
|
"libc",
|
||||||
"pkg-config",
|
"log",
|
||||||
|
"wasi",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -175,10 +283,27 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "parking_lot"
|
||||||
version = "0.3.31"
|
version = "0.12.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "primal-check"
|
name = "primal-check"
|
||||||
@@ -207,6 +332,24 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustfft"
|
name = "rustfft"
|
||||||
version = "6.2.0"
|
version = "6.2.0"
|
||||||
@@ -223,18 +366,74 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "rustix"
|
||||||
version = "1.3.0"
|
version = "1.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-mio"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"signal-hook",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||||
|
dependencies = [
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spectrust"
|
name = "spectrust"
|
||||||
version = "0.2.0"
|
version = "0.2.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"clap",
|
"clap",
|
||||||
"ncurses",
|
"crossterm",
|
||||||
"rustfft",
|
"rustfft",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -277,6 +476,12 @@ version = "1.0.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -289,6 +494,40 @@ version = "0.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
|
[[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-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
@@ -298,6 +537,15 @@ dependencies = [
|
|||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "spectrust"
|
name = "spectrust"
|
||||||
version = "0.2.0"
|
version = "0.2.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
@@ -10,5 +10,5 @@ codegen-units = 1
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
clap = { version = "4.5.21", features = ["derive"] }
|
clap = { version = "4.5.21", features = ["derive"] }
|
||||||
ncurses = "6.0.1"
|
crossterm = "0.29.0"
|
||||||
rustfft = "6.2.0"
|
rustfft = "6.2.0"
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Bendik Aagaard Lynghaug
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
# Maintainer: Bendik Aagaard Lynghaug <bendik.lynghaug@gmail.com>
|
# Maintainer: Bendik Aagaard Lynghaug <bendik.lynghaug@gmail.com>
|
||||||
pkgname=spectrust
|
pkgname=spectrust
|
||||||
pkgver=0.2.0
|
pkgver=0.2.5
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Terminal audio spectrum analyzer using ncurses"
|
pkgdesc="Terminal audio spectrum analyzer"
|
||||||
arch=('x86_64' 'aarch64')
|
arch=('x86_64' 'aarch64')
|
||||||
url="https://prosjekt.klingenbergbygg.no/bl/spectrust"
|
url="https://prosjekt.klingenbergbygg.no/bl/spectrust"
|
||||||
license=('MIT')
|
license=('MIT')
|
||||||
depends=('ncurses')
|
depends=()
|
||||||
provides=('spectrust')
|
provides=('spectrust')
|
||||||
conflicts=('spectrust-git' 'spectrust-bin')
|
conflicts=('spectrust-git' 'spectrust-bin')
|
||||||
source_x86_64=("spectrust-v${pkgver}-x86_64.tar.gz::https://prosjekt.klingenbergbygg.no/bl/spectrust/releases/download/v${pkgver}/spectrust-v${pkgver}-x86_64.tar.gz")
|
source_x86_64=("spectrust-v${pkgver}-x86_64.tar.gz::https://prosjekt.klingenbergbygg.no/bl/spectrust/releases/download/v${pkgver}/spectrust-v${pkgver}-x86_64.tar.gz")
|
||||||
|
|||||||
+126
-125
@@ -1,10 +1,20 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use ncurses::*;
|
use crossterm::{
|
||||||
use ncurses::{LcCategory, setlocale};
|
cursor::{Hide, MoveTo},
|
||||||
|
event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
|
||||||
|
execute, queue,
|
||||||
|
style::Print,
|
||||||
|
terminal::{
|
||||||
|
self, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen,
|
||||||
|
disable_raw_mode, enable_raw_mode,
|
||||||
|
},
|
||||||
|
};
|
||||||
use rustfft::{algorithm::Radix4, num_complex::Complex, Fft, FftDirection};
|
use rustfft::{algorithm::Radix4, num_complex::Complex, Fft, FftDirection};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::io::{stdin, Read};
|
use std::io::{stdout, Read, Write};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
@@ -20,7 +30,7 @@ struct Cell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ViewDiff {
|
struct ViewDiff {
|
||||||
changes: Vec<(i32, i32, char)>,
|
changes: Vec<(u16, u16, char)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewDiff {
|
impl ViewDiff {
|
||||||
@@ -33,13 +43,13 @@ impl ViewDiff {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_change(&mut self, y: usize, x: usize, character: char) {
|
fn add_change(&mut self, y: usize, x: usize, character: char) {
|
||||||
self.changes.push((y as i32, x as i32, character));
|
self.changes.push((y as u16, x as u16, character));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(&self) {
|
fn apply(&self) {
|
||||||
let mut buf = [0u8; 4];
|
let mut out = stdout();
|
||||||
for &(y, x, ch) in &self.changes {
|
for &(y, x, ch) in &self.changes {
|
||||||
let _ = mvprintw(y, x, ch.encode_utf8(&mut buf));
|
let _ = queue!(out, MoveTo(x, y), Print(ch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,14 +66,14 @@ fn render_bar(buf: &mut Vec<Vec<Cell>>, col: usize, sr: usize, bar_height_f: f32
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_terminal_resize(
|
fn handle_terminal_resize(
|
||||||
rows: &mut i32,
|
rows: &mut u16,
|
||||||
cols: &mut i32,
|
cols: &mut u16,
|
||||||
new_rows: i32,
|
new_rows: u16,
|
||||||
new_cols: i32,
|
new_cols: u16,
|
||||||
max_width: i32,
|
max_width: u16,
|
||||||
max_height: i32,
|
max_height: u16,
|
||||||
safe_rows: &mut i32,
|
safe_rows: &mut u16,
|
||||||
safe_cols: &mut i32,
|
safe_cols: &mut u16,
|
||||||
current_buffer: &mut Vec<Vec<Cell>>,
|
current_buffer: &mut Vec<Vec<Cell>>,
|
||||||
next_buffer: &mut Vec<Vec<Cell>>,
|
next_buffer: &mut Vec<Vec<Cell>>,
|
||||||
last_bars: &mut Vec<f32>,
|
last_bars: &mut Vec<f32>,
|
||||||
@@ -95,8 +105,9 @@ fn handle_terminal_resize(
|
|||||||
freq_mapping.push(index.min(fft_size / 2 - 1));
|
freq_mapping.push(index.min(fft_size / 2 - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
clear();
|
let mut out = stdout();
|
||||||
refresh();
|
let _ = queue!(out, Clear(ClearType::All));
|
||||||
|
let _ = out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
@@ -129,16 +140,18 @@ struct Cli {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
let _ = setlocale(LcCategory::all, "");
|
enable_raw_mode().expect("failed to enable raw mode");
|
||||||
initscr();
|
let mut out = stdout();
|
||||||
noecho();
|
execute!(out, EnterAlternateScreen, Hide).expect("failed to initialize terminal");
|
||||||
curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);
|
|
||||||
keypad(stdscr(), true);
|
|
||||||
timeout(0);
|
|
||||||
|
|
||||||
let mut rows: i32 = 0;
|
let default_hook = std::panic::take_hook();
|
||||||
let mut cols: i32 = 0;
|
std::panic::set_hook(Box::new(move |info| {
|
||||||
getmaxyx(stdscr(), &mut rows, &mut cols);
|
let _ = disable_raw_mode();
|
||||||
|
let _ = execute!(stdout(), LeaveAlternateScreen);
|
||||||
|
default_hook(info);
|
||||||
|
}));
|
||||||
|
|
||||||
|
let (mut cols, mut rows) = terminal::size().expect("failed to get terminal size");
|
||||||
|
|
||||||
let sample_rate: usize = 48000;
|
let sample_rate: usize = 48000;
|
||||||
let bit_depth: usize = 16;
|
let bit_depth: usize = 16;
|
||||||
@@ -148,39 +161,69 @@ fn main() {
|
|||||||
|
|
||||||
let fft = Radix4::new(fft_size, FftDirection::Forward);
|
let fft = Radix4::new(fft_size, FftDirection::Forward);
|
||||||
|
|
||||||
let mut input_buffer = vec![0u8; samples_per_frame * num_channels * (bit_depth / 8)];
|
|
||||||
let mut complex_buffer = vec![Complex { re: 0.0f32, im: 0.0f32 }; fft_size];
|
let mut complex_buffer = vec![Complex { re: 0.0f32, im: 0.0f32 }; fft_size];
|
||||||
|
|
||||||
let hann_window: Vec<f32> = (0..fft_size)
|
let hann_window: Vec<f32> = (0..fft_size)
|
||||||
.map(|i| 0.5 * (1.0 - (2.0 * std::f32::consts::PI * i as f32 / fft_size as f32).cos()))
|
.map(|i| 0.5 * (1.0 - (2.0 * std::f32::consts::PI * i as f32 / fft_size as f32).cos()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
enum AudioSource {
|
let _ = out.flush();
|
||||||
StdIn(std::io::StdinLock<'static>),
|
|
||||||
File(File),
|
|
||||||
PipeWire(std::process::ChildStdout),
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh();
|
let reader: Box<dyn Read + Send + 'static> = if cli.pipewire {
|
||||||
|
|
||||||
let mut audio_source = if cli.pipewire {
|
|
||||||
let process = Command::new("pw-record")
|
let process = Command::new("pw-record")
|
||||||
.args(["--format=s16", "--rate=48000", "--channels=2", "--raw", "-"])
|
.args(["--format=s16", "--rate=48000", "--channels=2", "--raw", "-"])
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("Failed to start pw-record");
|
.expect("failed to start pw-record");
|
||||||
let stdout = process.stdout.expect("Failed to capture pw-record stdout");
|
let pipe = process.stdout.expect("failed to capture pw-record stdout");
|
||||||
refresh();
|
let _ = out.flush();
|
||||||
AudioSource::PipeWire(stdout)
|
Box::new(pipe)
|
||||||
} else if let Some(filename) = &cli.input {
|
} else if let Some(filename) = &cli.input {
|
||||||
let file = File::open(filename).expect("Failed to open input file");
|
Box::new(File::open(filename).expect("failed to open input file"))
|
||||||
AudioSource::File(file)
|
|
||||||
} else {
|
} else {
|
||||||
AudioSource::StdIn(stdin().lock())
|
Box::new(std::io::stdin())
|
||||||
};
|
};
|
||||||
|
|
||||||
let max_width = 1000;
|
let (tx, rx) = mpsc::channel::<Vec<f32>>();
|
||||||
let max_height = 1000;
|
let _audio_thread = thread::spawn(move || {
|
||||||
|
let mut reader = reader;
|
||||||
|
let mut input_buffer = vec![0u8; samples_per_frame * num_channels * (bit_depth / 8)];
|
||||||
|
loop {
|
||||||
|
match reader.read(&mut input_buffer) {
|
||||||
|
Ok(0) => break,
|
||||||
|
Ok(n) => {
|
||||||
|
let mut samples = Vec::with_capacity(n / (num_channels * (bit_depth / 8)));
|
||||||
|
let mut cursor = std::io::Cursor::new(&input_buffer[..n]);
|
||||||
|
while cursor.position() < (n as u64 - (num_channels * (bit_depth / 8)) as u64 + 1) {
|
||||||
|
let mut frame_sum = 0.0f32;
|
||||||
|
let mut valid_channels = 0u32;
|
||||||
|
for _ in 0..num_channels {
|
||||||
|
match cursor.read_i16::<LittleEndian>() {
|
||||||
|
Ok(sample) => {
|
||||||
|
frame_sum += sample as f32;
|
||||||
|
valid_channels += 1;
|
||||||
|
}
|
||||||
|
Err(_) => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if valid_channels > 0 {
|
||||||
|
samples.push((frame_sum / valid_channels as f32) / 32768.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !samples.is_empty() && tx.send(samples).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||||
|
thread::sleep(Duration::from_millis(1));
|
||||||
|
}
|
||||||
|
Err(_) => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let max_width: u16 = 1000;
|
||||||
|
let max_height: u16 = 1000;
|
||||||
|
|
||||||
let mut safe_cols = cols.min(max_width);
|
let mut safe_cols = cols.min(max_width);
|
||||||
let mut safe_rows = rows.min(max_height);
|
let mut safe_rows = rows.min(max_height);
|
||||||
@@ -215,10 +258,32 @@ fn main() {
|
|||||||
let mut max_magnitude: f32 = 1.0;
|
let mut max_magnitude: f32 = 1.0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
let time_to_next_frame = interval.saturating_sub(last_frame_time.elapsed());
|
||||||
|
if event::poll(time_to_next_frame).unwrap_or(false) {
|
||||||
|
match event::read() {
|
||||||
|
Ok(Event::Key(KeyEvent { code: KeyCode::Char('q'), .. })) => break,
|
||||||
|
Ok(Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Char('c'),
|
||||||
|
modifiers: KeyModifiers::CONTROL,
|
||||||
|
..
|
||||||
|
})) => break,
|
||||||
|
Ok(Event::Resize(new_cols, new_rows)) => {
|
||||||
|
handle_terminal_resize(
|
||||||
|
&mut rows, &mut cols, new_rows, new_cols,
|
||||||
|
max_width, max_height, &mut safe_rows, &mut safe_cols,
|
||||||
|
&mut current_buffer, &mut next_buffer,
|
||||||
|
&mut last_bars, &mut freq_bands, &mut freq_mapping,
|
||||||
|
cli.log_power, fft_size,
|
||||||
|
);
|
||||||
|
sc = safe_cols as usize;
|
||||||
|
sr = safe_rows as usize;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if last_terminal_resize.elapsed() >= resize_check_interval {
|
if last_terminal_resize.elapsed() >= resize_check_interval {
|
||||||
let mut new_rows = 0;
|
if let Ok((new_cols, new_rows)) = terminal::size() {
|
||||||
let mut new_cols = 0;
|
|
||||||
getmaxyx(stdscr(), &mut new_rows, &mut new_cols);
|
|
||||||
if new_rows != rows || new_cols != cols {
|
if new_rows != rows || new_cols != cols {
|
||||||
handle_terminal_resize(
|
handle_terminal_resize(
|
||||||
&mut rows, &mut cols, new_rows, new_cols,
|
&mut rows, &mut cols, new_rows, new_cols,
|
||||||
@@ -230,23 +295,8 @@ fn main() {
|
|||||||
sc = safe_cols as usize;
|
sc = safe_cols as usize;
|
||||||
sr = safe_rows as usize;
|
sr = safe_rows as usize;
|
||||||
}
|
}
|
||||||
last_terminal_resize = Instant::now();
|
|
||||||
}
|
}
|
||||||
|
last_terminal_resize = Instant::now();
|
||||||
let ch = getch();
|
|
||||||
if ch == KEY_RESIZE {
|
|
||||||
let mut new_rows = 0;
|
|
||||||
let mut new_cols = 0;
|
|
||||||
getmaxyx(stdscr(), &mut new_rows, &mut new_cols);
|
|
||||||
handle_terminal_resize(
|
|
||||||
&mut rows, &mut cols, new_rows, new_cols,
|
|
||||||
max_width, max_height, &mut safe_rows, &mut safe_cols,
|
|
||||||
&mut current_buffer, &mut next_buffer,
|
|
||||||
&mut last_bars, &mut freq_bands, &mut freq_mapping,
|
|
||||||
cli.log_power, fft_size,
|
|
||||||
);
|
|
||||||
sc = safe_cols as usize;
|
|
||||||
sr = safe_rows as usize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if last_frame_time.elapsed() >= interval {
|
if last_frame_time.elapsed() >= interval {
|
||||||
@@ -255,68 +305,16 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
all_samples.clear();
|
all_samples.clear();
|
||||||
let mut buffer_is_empty = false;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let read_result = match &mut audio_source {
|
match rx.try_recv() {
|
||||||
AudioSource::StdIn(input) => input.read(&mut input_buffer),
|
Ok(batch) => all_samples.extend_from_slice(&batch),
|
||||||
AudioSource::File(file) => file.read(&mut input_buffer),
|
|
||||||
AudioSource::PipeWire(stdout) => stdout.read(&mut input_buffer),
|
|
||||||
};
|
|
||||||
|
|
||||||
match read_result {
|
|
||||||
Ok(0) => {
|
|
||||||
buffer_is_empty = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Ok(n) => {
|
|
||||||
let mut cursor = std::io::Cursor::new(&input_buffer[..n]);
|
|
||||||
while cursor.position() < (n as u64 - (num_channels * (bit_depth / 8)) as u64 + 1) {
|
|
||||||
let mut frame_sum = 0.0f32;
|
|
||||||
let mut valid_channels = 0u32;
|
|
||||||
for _ in 0..num_channels {
|
|
||||||
match cursor.read_i16::<LittleEndian>() {
|
|
||||||
Ok(sample) => {
|
|
||||||
frame_sum += sample as f32;
|
|
||||||
valid_channels += 1;
|
|
||||||
}
|
|
||||||
Err(_) => break,
|
Err(_) => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if valid_channels > 0 {
|
// Latency guard: if the audio thread got ahead, drop old samples
|
||||||
all_samples.push((frame_sum / valid_channels as f32) / 32768.0);
|
if all_samples.len() > fft_size * 2 {
|
||||||
}
|
let excess = all_samples.len() - fft_size * 2;
|
||||||
}
|
all_samples.drain(..excess);
|
||||||
if all_samples.len() >= fft_size * 2 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => break,
|
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
|
||||||
buffer_is_empty = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let error_msg = format!("Error: {}", e);
|
|
||||||
for (i, c) in error_msg.chars().enumerate() {
|
|
||||||
if i < sc {
|
|
||||||
next_buffer[0][i] = Cell { character: c, filled: true };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
view_diff.clear();
|
|
||||||
for y in 0..sr {
|
|
||||||
for x in 0..sc {
|
|
||||||
if current_buffer[y][x] != next_buffer[y][x] {
|
|
||||||
view_diff.add_change(y, x, next_buffer[y][x].character);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
view_diff.apply();
|
|
||||||
std::mem::swap(&mut current_buffer, &mut next_buffer);
|
|
||||||
refresh();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !all_samples.is_empty() {
|
if !all_samples.is_empty() {
|
||||||
@@ -347,7 +345,7 @@ fn main() {
|
|||||||
if mapping_idx < fft_size / 2 {
|
if mapping_idx < fft_size / 2 {
|
||||||
let magnitude = complex_buffer[mapping_idx].norm();
|
let magnitude = complex_buffer[mapping_idx].norm();
|
||||||
let freq_boost = 1.0 + (mapping_idx as f32 / (fft_size / 2) as f32) * 4.0;
|
let freq_boost = 1.0 + (mapping_idx as f32 / (fft_size / 2) as f32) * 4.0;
|
||||||
let scaled = (magnitude * 80.0 * freq_boost).powf(0.5);
|
let scaled = (magnitude * 80.0 * freq_boost).sqrt();
|
||||||
freq_bands[i] = if scaled > last_bars[i] { scaled } else { last_bars[i] * drop_factor };
|
freq_bands[i] = if scaled > last_bars[i] { scaled } else { last_bars[i] * drop_factor };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,8 +377,8 @@ fn main() {
|
|||||||
view_diff.apply();
|
view_diff.apply();
|
||||||
std::mem::swap(&mut current_buffer, &mut next_buffer);
|
std::mem::swap(&mut current_buffer, &mut next_buffer);
|
||||||
last_frame_time = Instant::now();
|
last_frame_time = Instant::now();
|
||||||
refresh();
|
let _ = out.flush();
|
||||||
} else if buffer_is_empty {
|
} else {
|
||||||
for i in 0..sc {
|
for i in 0..sc {
|
||||||
last_bars[i] *= drop_factor;
|
last_bars[i] *= drop_factor;
|
||||||
render_bar(&mut next_buffer, i, sr, (last_bars[i] / max_magnitude) * sr as f32);
|
render_bar(&mut next_buffer, i, sr, (last_bars[i] / max_magnitude) * sr as f32);
|
||||||
@@ -397,8 +395,11 @@ fn main() {
|
|||||||
view_diff.apply();
|
view_diff.apply();
|
||||||
std::mem::swap(&mut current_buffer, &mut next_buffer);
|
std::mem::swap(&mut current_buffer, &mut next_buffer);
|
||||||
last_frame_time = Instant::now();
|
last_frame_time = Instant::now();
|
||||||
refresh();
|
let _ = out.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ = execute!(out, LeaveAlternateScreen);
|
||||||
|
let _ = disable_raw_mode();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user