diff --git a/.clippy.toml b/.clippy.toml
index 3342bfed..e7da7b36 100644
--- a/.clippy.toml
+++ b/.clippy.toml
@@ -2,7 +2,7 @@
# Disallow println! and eprintln! in the library.
# This enforces the use of the tracing framework for logging.
-disallowed-methods = [
+disallowed-names = [
"std::println",
"std::eprintln",
]
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..69c5ebf4
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,113 @@
+name: CI
+
+on:
+ push:
+ branches: [main, develop]
+ pull_request:
+ branches: [main, develop]
+
+env:
+ CARGO_TERM_COLOR: always
+
+jobs:
+ test:
+ name: Test Suite
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ feature_set:
+ - name: "Default features"
+ features: ""
+ - name: "VAD only"
+ features: ""
+ - name: "STT with Vosk"
+ features: "vosk"
+ - name: "Text injection"
+ features: "text-injection"
+ - name: "Full features"
+ features: "vosk,text-injection"
+ - name: "Examples"
+ features: "examples"
+ - name: "Live hardware tests"
+ features: "live-hardware-tests"
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Rust
+ uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt, clippy
+
+ - name: Cache dependencies
+ uses: Swatinem/rust-cache@v2
+ with:
+ key: ${{ matrix.feature_set.name }}
+
+ - name: Install system dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ libasound2-dev \
+ libxdo-dev \
+ libxtst-dev \
+ libxinerama-dev \
+ libx11-dev \
+ libxcursor-dev \
+ libxi-dev \
+ libgl1-mesa-dev \
+ pkg-config
+
+ - name: Check formatting
+ run: cargo fmt --all -- --check
+
+ - name: Run clippy
+ run: |
+ if [ -n "${{ matrix.feature_set.features }}" ]; then
+ cargo clippy --workspace --features ${{ matrix.feature_set.features }} -- -D warnings
+ else
+ cargo clippy --workspace -- -D warnings
+ fi
+
+ - name: Run tests
+ run: |
+ if [ -n "${{ matrix.feature_set.features }}" ]; then
+ cargo test --workspace --features ${{ matrix.feature_set.features }}
+ else
+ cargo test --workspace
+ fi
+
+ - name: Build
+ run: |
+ if [ -n "${{ matrix.feature_set.features }}" ]; then
+ cargo build --workspace --features ${{ matrix.feature_set.features }}
+ else
+ cargo build --workspace
+ fi
+
+ # Separate job for documentation
+ docs:
+ name: Documentation
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Rust
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: Cache dependencies
+ uses: Swatinem/rust-cache@v2
+
+ - name: Install system dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ libasound2-dev \
+ libxdo-dev \
+ libxtst-dev
+
+ - name: Check documentation
+ run: cargo doc --workspace --no-deps --all-features
+ env:
+ RUSTDOCFLAGS: "-D warnings"
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 1d3604cd..9c7236a5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -131,6 +131,151 @@ version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
+[[package]]
+name = "arboard"
+version = "3.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
+dependencies = [
+ "clipboard-win",
+ "image",
+ "log",
+ "objc2 0.6.2",
+ "objc2-app-kit",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+ "objc2-foundation",
+ "parking_lot",
+ "percent-encoding",
+ "windows-sys 0.60.2",
+ "x11rb",
+]
+
+[[package]]
+name = "async-broadcast"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-channel"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
+dependencies = [
+ "concurrent-queue",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "async-fs"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09f7e37c0ed80b2a977691c47dae8625cfb21e205827106c64f7c588766b2e50"
+dependencies = [
+ "async-lock",
+ "blocking",
+ "futures-lite",
+]
+
+[[package]]
+name = "async-io"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca"
+dependencies = [
+ "async-lock",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite",
+ "parking",
+ "polling",
+ "rustix",
+ "slab",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00"
+dependencies = [
+ "async-channel",
+ "async-io",
+ "async-lock",
+ "async-signal",
+ "async-task",
+ "blocking",
+ "cfg-if",
+ "event-listener",
+ "futures-lite",
+ "rustix",
+]
+
+[[package]]
+name = "async-recursion"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-signal"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1"
+dependencies = [
+ "async-io",
+ "async-lock",
+ "atomic-waker",
+ "cfg-if",
+ "futures-core",
+ "futures-io",
+ "rustix",
+ "signal-hook-registry",
+ "slab",
+ "windows-sys 0.60.2",
+]
+
[[package]]
name = "async-stream"
version = "0.3.6"
@@ -153,6 +298,12 @@ dependencies = [
"syn",
]
+[[package]]
+name = "async-task"
+version = "4.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
+
[[package]]
name = "async-trait"
version = "0.1.89"
@@ -164,6 +315,63 @@ dependencies = [
"syn",
]
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "atspi"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be534b16650e35237bb1ed189ba2aab86ce65e88cc84c66f4935ba38575cecbf"
+dependencies = [
+ "atspi-common",
+ "atspi-connection",
+ "atspi-proxies",
+]
+
+[[package]]
+name = "atspi-common"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1909ed2dc01d0a17505d89311d192518507e8a056a48148e3598fef5e7bb6ba7"
+dependencies = [
+ "enumflags2",
+ "serde",
+ "static_assertions",
+ "zbus",
+ "zbus-lockstep",
+ "zbus-lockstep-macros",
+ "zbus_names",
+ "zvariant",
+]
+
+[[package]]
+name = "atspi-connection"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "430c5960624a4baaa511c9c0fcc2218e3b58f5dbcc47e6190cafee344b873333"
+dependencies = [
+ "atspi-common",
+ "atspi-proxies",
+ "futures-lite",
+ "zbus",
+]
+
+[[package]]
+name = "atspi-proxies"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e6c5de3e524cf967569722446bcd458d5032348554d9a17d7d72b041ab7496"
+dependencies = [
+ "atspi-common",
+ "serde",
+ "zbus",
+ "zvariant",
+]
+
[[package]]
name = "autocfg"
version = "1.5.0"
@@ -251,18 +459,62 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "block-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7"
+dependencies = [
+ "objc-sys",
+]
+
+[[package]]
+name = "block2"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e58aa60e59d8dbfcc36138f5f18be5f24394d33b38b24f7fd0b1caa33095f22f"
+dependencies = [
+ "block-sys",
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "blocking"
+version = "1.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "futures-io",
+ "futures-lite",
+ "piper",
+]
+
[[package]]
name = "bumpalo"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+[[package]]
+name = "bytemuck"
+version = "1.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677"
+
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
[[package]]
name = "bytes"
version = "1.10.1"
@@ -322,6 +574,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
[[package]]
name = "cfg_aliases"
version = "0.2.1"
@@ -421,6 +679,15 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
+[[package]]
+name = "clipboard-win"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
+dependencies = [
+ "error-code",
+]
+
[[package]]
name = "coldvox-app"
version = "0.1.0"
@@ -429,14 +696,21 @@ dependencies = [
"async-trait",
"chrono",
"clap",
+ "coldvox-audio",
+ "coldvox-foundation",
+ "coldvox-stt",
+ "coldvox-stt-vosk",
+ "coldvox-telemetry",
+ "coldvox-text-injection",
+ "coldvox-vad",
+ "coldvox-vad-silero",
"cpal",
"criterion",
"crossbeam-channel",
"crossterm",
"csv",
"ctrlc",
- "dasp",
- "device_query",
+ "device_query 4.0.1",
"env_logger",
"futures",
"hound",
@@ -446,7 +720,7 @@ dependencies = [
"proptest",
"rand 0.8.5",
"ratatui",
- "rtrb",
+ "regex",
"rubato",
"serde",
"serde_json",
@@ -458,10 +732,112 @@ dependencies = [
"tracing",
"tracing-appender",
"tracing-subscriber",
- "voice_activity_detector",
+]
+
+[[package]]
+name = "coldvox-audio"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "coldvox-foundation",
+ "coldvox-telemetry",
+ "cpal",
+ "dasp",
+ "parking_lot",
+ "rtrb",
+ "rubato",
+ "thiserror 1.0.69",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "coldvox-foundation"
+version = "0.1.0"
+dependencies = [
+ "cpal",
+ "crossbeam-channel",
+ "parking_lot",
+ "serde",
+ "thiserror 1.0.69",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "coldvox-gui"
+version = "0.1.0"
+
+[[package]]
+name = "coldvox-stt"
+version = "0.1.0"
+dependencies = [
+ "parking_lot",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "coldvox-stt-vosk"
+version = "0.1.0"
+dependencies = [
+ "coldvox-stt",
+ "tracing",
"vosk",
]
+[[package]]
+name = "coldvox-telemetry"
+version = "0.1.0"
+dependencies = [
+ "coldvox-text-injection",
+ "parking_lot",
+]
+
+[[package]]
+name = "coldvox-text-injection"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "arboard",
+ "async-trait",
+ "atspi",
+ "chrono",
+ "device_query 2.1.0",
+ "enigo",
+ "mockall",
+ "mouse-keyboard-input",
+ "parking_lot",
+ "regex",
+ "serde",
+ "serde_json",
+ "tempfile",
+ "thiserror 1.0.69",
+ "tokio",
+ "tokio-test",
+ "toml",
+ "tracing",
+ "wl-clipboard-rs",
+ "x11",
+]
+
+[[package]]
+name = "coldvox-vad"
+version = "0.1.0"
+dependencies = [
+ "rand 0.8.5",
+ "serde",
+]
+
+[[package]]
+name = "coldvox-vad-silero"
+version = "0.1.0"
+dependencies = [
+ "coldvox-vad",
+ "serde",
+ "voice_activity_detector",
+]
+
[[package]]
name = "colorchoice"
version = "1.0.4"
@@ -491,6 +867,15 @@ dependencies = [
"static_assertions",
]
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -507,6 +892,30 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+[[package]]
+name = "core-graphics"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types 0.5.0",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "libc",
+]
+
[[package]]
name = "coreaudio-rs"
version = "0.11.3"
@@ -706,7 +1115,7 @@ version = "3.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73"
dependencies = [
- "nix",
+ "nix 0.30.1",
"windows-sys 0.59.0",
]
@@ -848,6 +1257,32 @@ dependencies = [
"powerfmt",
]
+[[package]]
+name = "derive-new"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "device_query"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bafa241a89a5edccff5057d0b85fbc083a781bd03d766c11a688331604980985"
+dependencies = [
+ "lazy_static",
+ "macos-accessibility-client",
+ "pkg-config",
+ "readkey",
+ "readmouse",
+ "windows 0.48.0",
+ "x11",
+]
+
[[package]]
name = "device_query"
version = "4.0.1"
@@ -863,26 +1298,86 @@ dependencies = [
]
[[package]]
-name = "digest"
-version = "0.10.7"
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "dispatch2"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
+dependencies = [
+ "bitflags 2.9.3",
+ "objc2 0.6.2",
+]
+
+[[package]]
+name = "downcast"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1"
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "endi"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
+
+[[package]]
+name = "enigo"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0087a01fc8591217447d28005379fb5a183683cc83f0a4707af28cc6603f70fb"
dependencies = [
- "block-buffer",
- "crypto-common",
+ "core-graphics",
+ "foreign-types-shared 0.3.1",
+ "icrate",
+ "libc",
+ "log",
+ "objc2 0.5.2",
+ "windows 0.56.0",
+ "xkbcommon",
+ "xkeysym",
]
[[package]]
-name = "downcast"
-version = "0.11.0"
+name = "enumflags2"
+version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1"
+checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
+dependencies = [
+ "enumflags2_derive",
+ "serde",
+]
[[package]]
-name = "either"
-version = "1.15.0"
+name = "enumflags2_derive"
+version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
[[package]]
name = "env_filter"
@@ -923,12 +1418,48 @@ dependencies = [
"windows-sys 0.60.2",
]
+[[package]]
+name = "error-code"
+version = "3.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
+
+[[package]]
+name = "event-listener"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
+dependencies = [
+ "event-listener",
+ "pin-project-lite",
+]
+
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
[[package]]
name = "filetime"
version = "0.2.26"
@@ -941,6 +1472,12 @@ dependencies = [
"windows-sys 0.60.2",
]
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
[[package]]
name = "flate2"
version = "1.1.2"
@@ -969,7 +1506,28 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
- "foreign-types-shared",
+ "foreign-types-shared 0.1.1",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared 0.3.1",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
@@ -978,6 +1536,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
[[package]]
name = "fragile"
version = "2.0.1"
@@ -1032,6 +1596,19 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+[[package]]
+name = "futures-lite"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "parking",
+ "pin-project-lite",
+]
+
[[package]]
name = "futures-macro"
version = "0.3.31"
@@ -1083,6 +1660,16 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "gethostname"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55"
+dependencies = [
+ "rustix",
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "getrandom"
version = "0.2.16"
@@ -1151,6 +1738,12 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
[[package]]
name = "hound"
version = "3.5.1"
@@ -1198,6 +1791,29 @@ dependencies = [
"cc",
]
+[[package]]
+name = "icrate"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb69199826926eb864697bddd27f73d9fddcffc004f5733131e15b465e30642"
+dependencies = [
+ "block2",
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "image"
+version = "0.25.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a"
+dependencies = [
+ "bytemuck",
+ "byteorder-lite",
+ "num-traits",
+ "png",
+ "tiff",
+]
+
[[package]]
name = "indexmap"
version = "2.11.0"
@@ -1219,6 +1835,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "ioctl-sys"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bd11f3a29434026f5ff98c730b668ba74b1033637b8817940b54d040696133c"
+
[[package]]
name = "is-terminal"
version = "0.4.16"
@@ -1325,6 +1947,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07"
+
[[package]]
name = "js-sys"
version = "0.3.77"
@@ -1443,6 +2071,24 @@ version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
+[[package]]
+name = "memmap2"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -1456,6 +2102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
+ "simd-adler32",
]
[[package]]
@@ -1508,6 +2155,18 @@ dependencies = [
"syn",
]
+[[package]]
+name = "mouse-keyboard-input"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbf79b18a72442f15c270d0c97a98a4afb2e0b31fa8423e1301c724f1832bcea"
+dependencies = [
+ "crossbeam-channel",
+ "ioctl-sys",
+ "libc",
+ "nix 0.28.0",
+]
+
[[package]]
name = "native-tls"
version = "0.2.14"
@@ -1569,6 +2228,31 @@ dependencies = [
"jni-sys",
]
+[[package]]
+name = "nix"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
+dependencies = [
+ "bitflags 2.9.3",
+ "cfg-if",
+ "cfg_aliases 0.1.1",
+ "libc",
+]
+
+[[package]]
+name = "nix"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
+dependencies = [
+ "bitflags 2.9.3",
+ "cfg-if",
+ "cfg_aliases 0.2.1",
+ "libc",
+ "memoffset",
+]
+
[[package]]
name = "nix"
version = "0.30.1"
@@ -1577,7 +2261,7 @@ checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags 2.9.3",
"cfg-if",
- "cfg_aliases",
+ "cfg_aliases 0.2.1",
"libc",
]
@@ -1666,6 +2350,95 @@ dependencies = [
"syn",
]
+[[package]]
+name = "objc-sys"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
+
+[[package]]
+name = "objc2"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
+dependencies = [
+ "objc-sys",
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc"
+dependencies = [
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc"
+dependencies = [
+ "bitflags 2.9.3",
+ "objc2 0.6.2",
+ "objc2-core-graphics",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
+dependencies = [
+ "bitflags 2.9.3",
+ "dispatch2",
+ "objc2 0.6.2",
+]
+
+[[package]]
+name = "objc2-core-graphics"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4"
+dependencies = [
+ "bitflags 2.9.3",
+ "dispatch2",
+ "objc2 0.6.2",
+ "objc2-core-foundation",
+ "objc2-io-surface",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
+name = "objc2-foundation"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
+dependencies = [
+ "bitflags 2.9.3",
+ "objc2 0.6.2",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-io-surface"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c"
+dependencies = [
+ "bitflags 2.9.3",
+ "objc2 0.6.2",
+ "objc2-core-foundation",
+]
+
[[package]]
name = "object"
version = "0.36.7"
@@ -1724,7 +2497,7 @@ checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
dependencies = [
"bitflags 2.9.3",
"cfg-if",
- "foreign-types",
+ "foreign-types 0.3.2",
"libc",
"once_cell",
"openssl-macros",
@@ -1760,6 +2533,16 @@ dependencies = [
"vcpkg",
]
+[[package]]
+name = "ordered-stream"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+]
+
[[package]]
name = "ort"
version = "2.0.0-rc.10"
@@ -1785,6 +2568,22 @@ dependencies = [
"ureq",
]
+[[package]]
+name = "os_pipe"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db335f4760b14ead6290116f2427bf33a14d4f0617d49f78a246de10c1831224"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "parking"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
+
[[package]]
name = "parking_lot"
version = "0.12.4"
@@ -1829,6 +2628,16 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+[[package]]
+name = "petgraph"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
+dependencies = [
+ "fixedbitset",
+ "indexmap",
+]
+
[[package]]
name = "pin-project"
version = "1.1.10"
@@ -1861,6 +2670,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+[[package]]
+name = "piper"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
+dependencies = [
+ "atomic-waker",
+ "fastrand",
+ "futures-io",
+]
+
[[package]]
name = "pkg-config"
version = "0.3.32"
@@ -1887,12 +2707,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
[[package]]
-name = "plotters-svg"
-version = "0.3.7"
+name = "plotters-svg"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
+dependencies = [
+ "plotters-backend",
+]
+
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "polling"
+version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
+checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829"
dependencies = [
- "plotters-backend",
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -2004,6 +2851,25 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+[[package]]
+name = "quick-xml"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.37.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "quote"
version = "1.0.40"
@@ -2370,6 +3236,17 @@ dependencies = [
"serde",
]
+[[package]]
+name = "serde_repr"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "serde_spanned"
version = "0.6.9"
@@ -2379,6 +3256,17 @@ dependencies = [
"serde",
]
+[[package]]
+name = "sha1"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
[[package]]
name = "sha2"
version = "0.10.9"
@@ -2435,6 +3323,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
[[package]]
name = "slab"
version = "0.4.11"
@@ -2614,6 +3508,17 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "tiff"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
[[package]]
name = "time"
version = "0.3.41"
@@ -2834,6 +3739,18 @@ dependencies = [
"strength_reduce",
]
+[[package]]
+name = "tree_magic_mini"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f943391d896cdfe8eec03a04d7110332d445be7df856db382dd96a730667562c"
+dependencies = [
+ "memchr",
+ "nom",
+ "once_cell",
+ "petgraph",
+]
+
[[package]]
name = "typed-builder"
version = "0.20.1"
@@ -2860,6 +3777,17 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
+[[package]]
+name = "uds_windows"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
+dependencies = [
+ "memoffset",
+ "tempfile",
+ "winapi",
+]
+
[[package]]
name = "unarray"
version = "0.1.4"
@@ -3093,6 +4021,76 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "wayland-backend"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "rustix",
+ "smallvec 1.15.1",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.31.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d"
+dependencies = [
+ "bitflags 2.9.3",
+ "rustix",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.31.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
+dependencies = [
+ "bitflags 2.9.3",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
+dependencies = [
+ "bitflags 2.9.3",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.31.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3"
+dependencies = [
+ "proc-macro2",
+ "quick-xml 0.37.5",
+ "quote",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.31.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142"
+dependencies = [
+ "pkg-config",
+]
+
[[package]]
name = "web-sys"
version = "0.3.77"
@@ -3112,6 +4110,12 @@ dependencies = [
"rustls-pki-types",
]
+[[package]]
+name = "weezl"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
+
[[package]]
name = "winapi"
version = "0.3.9"
@@ -3162,6 +4166,16 @@ dependencies = [
"windows-targets 0.52.6",
]
+[[package]]
+name = "windows"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
+dependencies = [
+ "windows-core 0.56.0",
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows-core"
version = "0.54.0"
@@ -3172,19 +4186,42 @@ dependencies = [
"windows-targets 0.52.6",
]
+[[package]]
+name = "windows-core"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6"
+dependencies = [
+ "windows-implement 0.56.0",
+ "windows-interface 0.56.0",
+ "windows-result 0.1.2",
+ "windows-targets 0.52.6",
+]
+
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
- "windows-implement",
- "windows-interface",
+ "windows-implement 0.60.0",
+ "windows-interface 0.59.1",
"windows-link",
"windows-result 0.3.4",
"windows-strings",
]
+[[package]]
+name = "windows-implement"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "windows-implement"
version = "0.60.0"
@@ -3196,6 +4233,17 @@ dependencies = [
"syn",
]
+[[package]]
+name = "windows-interface"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "windows-interface"
version = "0.59.1"
@@ -3543,6 +4591,26 @@ version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
+[[package]]
+name = "wl-clipboard-rs"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12b41773911497b18ca8553c3daaf8ec9fe9819caf93d451d3055f69de028adb"
+dependencies = [
+ "derive-new",
+ "libc",
+ "log",
+ "nix 0.28.0",
+ "os_pipe",
+ "tempfile",
+ "thiserror 1.0.69",
+ "tree_magic_mini",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-protocols-wlr",
+]
+
[[package]]
name = "x11"
version = "2.21.0"
@@ -3553,6 +4621,23 @@ dependencies = [
"pkg-config",
]
+[[package]]
+name = "x11rb"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
+dependencies = [
+ "gethostname",
+ "rustix",
+ "x11rb-protocol",
+]
+
+[[package]]
+name = "x11rb-protocol"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
+
[[package]]
name = "xattr"
version = "1.5.1"
@@ -3563,6 +4648,132 @@ dependencies = [
"rustix",
]
+[[package]]
+name = "xdg-home"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "xkbcommon"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e"
+dependencies = [
+ "libc",
+ "memmap2",
+ "xkeysym",
+]
+
+[[package]]
+name = "xkeysym"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
+
+[[package]]
+name = "zbus"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
+dependencies = [
+ "async-broadcast",
+ "async-executor",
+ "async-fs",
+ "async-io",
+ "async-lock",
+ "async-process",
+ "async-recursion",
+ "async-task",
+ "async-trait",
+ "blocking",
+ "enumflags2",
+ "event-listener",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "hex",
+ "nix 0.29.0",
+ "ordered-stream",
+ "rand 0.8.5",
+ "serde",
+ "serde_repr",
+ "sha1",
+ "static_assertions",
+ "tracing",
+ "uds_windows",
+ "windows-sys 0.52.0",
+ "xdg-home",
+ "zbus_macros",
+ "zbus_names",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus-lockstep"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca2c5dceb099bddaade154055c926bb8ae507a18756ba1d8963fd7b51d8ed1d"
+dependencies = [
+ "zbus_xml",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus-lockstep-macros"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "zbus-lockstep",
+ "zbus_xml",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus_macros"
+version = "4.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zbus_names"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
+dependencies = [
+ "serde",
+ "static_assertions",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus_xml"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab3f374552b954f6abb4bd6ce979e6c9b38fb9d0cd7cc68a7d796e70c9f3a233"
+dependencies = [
+ "quick-xml 0.30.0",
+ "serde",
+ "static_assertions",
+ "zbus_names",
+ "zvariant",
+]
+
[[package]]
name = "zerocopy"
version = "0.8.26"
@@ -3588,3 +4799,40 @@ name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+
+[[package]]
+name = "zvariant"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
+dependencies = [
+ "endi",
+ "enumflags2",
+ "serde",
+ "static_assertions",
+ "zvariant_derive",
+]
+
+[[package]]
+name = "zvariant_derive"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_utils"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/Cargo.toml b/Cargo.toml
index c6e36e13..af3807e8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,14 @@
-
[workspace]
members = [
"crates/app",
+ "crates/coldvox-foundation",
+ "crates/coldvox-telemetry",
+ "crates/coldvox-audio",
+ "crates/coldvox-vad",
+ "crates/coldvox-vad-silero",
+ "crates/coldvox-text-injection",
+ "crates/coldvox-stt",
+ "crates/coldvox-stt-vosk",
+ "crates/coldvox-gui",
]
-resolver = "2"
+resolver = "2"
\ No newline at end of file
diff --git a/README.md b/README.md
index 887d4301..de201640 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,69 @@
# ColdVox – Voice AI audio pipeline
-[](docs/PROJECT_STATUS.md)
-[](docs/PROJECT_STATUS.md)
+[](docs/PROJECT_STATUS.md)
+[](https://github.com/YOUR_USERNAME/ColdVox/actions)
Rust-based real-time audio capture and processing with robust recovery, VAD, and STT integration.
+## Workspace Structure
+
+ColdVox is organized as a Cargo workspace with the following crates:
+
+- **`crates/app/`** - Main application binaries and CLI interface
+- **`crates/coldvox-foundation/`** - Core types, errors, and foundation functionality
+- **`crates/coldvox-audio/`** - Audio capture, processing, and device management
+- **`crates/coldvox-telemetry/`** - Metrics and performance monitoring
+- **`crates/coldvox-stt/`** - Speech-to-text framework and interfaces
+- **`crates/coldvox-stt-vosk/`** - Vosk STT implementation
+- **`crates/coldvox-text-injection/`** - Text injection for automation
+
## Quick Start
+### VAD-Only Mode (Recommended for getting started)
+
+```bash
+# Build the workspace
+cargo build --workspace
+
+# Run basic VAD pipeline without STT dependencies
+cargo run -p coldvox-app --bin coldvox
+
+# Run audio probe utilities
+cargo run -p coldvox-app --bin mic_probe -- --duration 30
+cargo run -p coldvox-app --bin tui_dashboard
+```
+
+### With Feature Flags
+
```bash
-# Build and run the app (STT requires vosk feature and system library)
-cargo run --bin mic_probe # Basic audio pipeline without STT
-cargo run --features vosk # With STT (requires libvosk installed)
+# STT with Vosk (requires system dependencies)
+cargo run -p coldvox-app --features vosk
+
+# Text injection capabilities
+cargo run -p coldvox-app --features text-injection
-# Probe binaries
-cargo run --bin mic_probe -- --duration 30 --silence_threshold 120
-cargo run --bin foundation_probe -- --duration 30
+# Full feature set
+cargo run -p coldvox-app --features vosk,text-injection
# Debug logging
-RUST_LOG=debug cargo run --features vosk
+RUST_LOG=debug cargo run -p coldvox-app --features vosk
```
## Features
+**Core (always available):**
- Reliable microphone capture with auto-recovery (watchdog)
-- Device‑native capture to ring buffer (no resampling on capture thread)
+- Device‑native capture to ring buffer (no resampling on capture thread)
- AudioChunker handles stereo→mono and resampling to 16 kHz
- Ring buffer and backpressure handling with stats
- Voice Activity Detection (Silero V5 via vendored fork)
-- STT framework implemented (Vosk - requires system dependencies)
-- Optional push-to-talk mode activated by holding Ctrl+Super with a small on-screen indicator centered one-third from the bottom of the screen
+- Optional push-to-talk mode activated by holding Ctrl+Super
+
+**Optional features (via feature flags):**
+- **`vosk`**: Speech-to-text using Vosk engine (requires system dependencies)
+- **`text-injection`**: Automated text input for transcribed speech
+- **`examples`**: Additional example programs and demos
+- **`live-hardware-tests`**: Hardware-specific test suites
## Configuration
diff --git a/crates/app/Cargo.toml b/crates/app/Cargo.toml
index 98b77a68..08e72b07 100644
--- a/crates/app/Cargo.toml
+++ b/crates/app/Cargo.toml
@@ -2,11 +2,11 @@
name = "coldvox-app"
version = "0.1.0"
edition = "2021"
+default-run = "coldvox"
[[bin]]
name = "coldvox"
path = "src/main.rs"
-required-features = ["vosk"]
[[bin]]
name = "tui_dashboard"
@@ -48,16 +48,15 @@ tokio = { version = "1.35", features = ["full"] }
anyhow = "1.0"
thiserror = "1.0"
tracing = "0.1"
+parking_lot = "0.12"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-appender = "0.2"
async-trait = "0.1"
-cpal = "0.15"
+# Some audio processing dependencies still needed for VAD adapter
hound = "3.5"
-dasp = { version = "0.11", features = ["all"] }
rubato = "0.16"
-rtrb = "0.3"
crossbeam-channel = "0.5"
-parking_lot = "0.12"
+# parking_lot moved to foundation and other crates
once_cell = "1.19"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
@@ -68,21 +67,19 @@ chrono = { version = "0.4", features = ["serde"] }
ratatui = "0.26"
crossterm = "0.27"
futures = "0.3"
-voice_activity_detector = { git = "https://github.com/nkeenan38/voice_activity_detector", rev = "234b7484860125014f06ad85da842da81b02e51a" }
-vosk = "0.3"
+# voice_activity_detector moved to coldvox-vad-silero
+coldvox-foundation = { path = "../coldvox-foundation" }
+coldvox-telemetry = { path = "../coldvox-telemetry" }
+coldvox-audio = { path = "../coldvox-audio" }
+coldvox-vad = { path = "../coldvox-vad" }
+coldvox-vad-silero = { path = "../coldvox-vad-silero", features = ["silero"] }
+coldvox-stt = { path = "../coldvox-stt" }
+coldvox-stt-vosk = { path = "../coldvox-stt-vosk", optional = true, features = ["vosk"] }
+coldvox-text-injection = { path = "../coldvox-text-injection", optional = true }
csv = "1.3"
device_query = "4.0"
-
-[features]
-default = []
-live-hardware-tests = []
-vosk = []
-examples = []
-text-injection = []
-
-[dependencies.rand]
-version = "0.8"
-optional = true
+cpal = "0.15.2"
+regex = { version = "1.10", optional = true }
[dev-dependencies]
tempfile = "3.8"
@@ -91,4 +88,20 @@ tokio-test = "0.4"
ctrlc = "3.4"
proptest = "1.4"
criterion = "0.5"
-rand = "0.8"
\ No newline at end of file
+rand = "0.8"
+
+[features]
+default = ["silero"]
+live-hardware-tests = []
+vosk = ["dep:coldvox-stt-vosk"]
+examples = []
+text-injection = ["dep:coldvox-text-injection"]
+silero = ["coldvox-vad-silero/silero"]
+level3 = ["coldvox-vad/level3"]
+text-injection-atspi = ["text-injection", "coldvox-text-injection/atspi"]
+text-injection-clipboard = ["text-injection", "coldvox-text-injection/wl_clipboard"]
+text-injection-ydotool = ["text-injection", "coldvox-text-injection/ydotool"]
+text-injection-enigo = ["text-injection", "coldvox-text-injection/enigo"]
+text-injection-mki = ["text-injection", "coldvox-text-injection/mki"]
+text-injection-kdotool = ["text-injection", "coldvox-text-injection/xdg_kdotool"]
+text-injection-regex = ["text-injection", "dep:regex"]
\ No newline at end of file
diff --git a/crates/app/src/audio/mod.rs b/crates/app/src/audio/mod.rs
index db007354..3b401f0c 100644
--- a/crates/app/src/audio/mod.rs
+++ b/crates/app/src/audio/mod.rs
@@ -1,20 +1,14 @@
-pub mod capture;
-pub mod detector;
-pub mod device;
-pub mod chunker;
-pub mod frame_reader;
-pub mod ring_buffer;
-pub mod resampler;
pub mod vad_adapter;
pub mod vad_processor;
-pub mod watchdog;
-pub use capture::*;
-pub use detector::*;
-pub use device::*;
-pub use chunker::*;
-pub use frame_reader::*;
-pub use ring_buffer::*;
-pub use resampler::*;
+// Re-export modules from coldvox-audio crate
+pub use coldvox_audio::{
+ chunker::{AudioChunker, ChunkerConfig, ResamplerQuality},
+ frame_reader::FrameReader,
+ ring_buffer::{AudioRingBuffer, AudioProducer},
+ capture::CaptureStats,
+};
+
pub use vad_adapter::*;
-pub use watchdog::*;
+pub use vad_processor::*;
+pub use coldvox_audio::AudioFrame;
diff --git a/crates/app/src/audio/vad_adapter.rs b/crates/app/src/audio/vad_adapter.rs
index 025fbc7b..c9d6db91 100644
--- a/crates/app/src/audio/vad_adapter.rs
+++ b/crates/app/src/audio/vad_adapter.rs
@@ -1,13 +1,15 @@
-use crate::vad::{
- config::{UnifiedVadConfig, VadMode},
- engine::{VadEngine, VadEngineBox},
- level3::Level3Vad,
- silero_wrapper::SileroEngine,
- types::{VadConfig, VadEvent, VadState},
+use coldvox_vad::{
+ UnifiedVadConfig, VadMode, VadEngine, VadEvent, VadState,
};
+#[cfg(feature = "level3")]
+use coldvox_vad::VadConfig;
+#[cfg(feature = "level3")]
+use coldvox_vad::level3::Level3Vad;
+#[cfg(feature = "silero")]
+use coldvox_vad_silero::SileroEngine;
pub struct VadAdapter {
- engine: VadEngineBox,
+ engine: Box,
config: UnifiedVadConfig,
resampler: Option,
}
@@ -15,6 +17,7 @@ pub struct VadAdapter {
impl VadAdapter {
pub fn new(config: UnifiedVadConfig) -> Result {
let engine: Box = match config.mode {
+ #[cfg(feature = "level3")]
VadMode::Level3 => {
// INTENTIONAL: Level3 VAD is disabled by default
// This check ensures it's not accidentally enabled without explicit configuration
@@ -33,8 +36,18 @@ impl VadAdapter {
};
Box::new(Level3Vad::new(level3_config))
}
+ #[cfg(not(feature = "level3"))]
+ VadMode::Level3 => {
+ return Err("Level3 VAD is not available in this build. Use Silero mode instead.".to_string());
+ }
VadMode::Silero => {
- Box::new(SileroEngine::new(config.silero.clone())?)
+ let silero_config = coldvox_vad_silero::SileroConfig {
+ threshold: config.silero.threshold,
+ min_speech_duration_ms: config.silero.min_speech_duration_ms,
+ min_silence_duration_ms: config.silero.min_silence_duration_ms,
+ window_size_samples: config.silero.window_size_samples,
+ };
+ Box::new(SileroEngine::new(silero_config)?)
}
};
@@ -52,7 +65,7 @@ impl VadAdapter {
};
Ok(Self {
- engine: VadEngineBox::new(engine),
+ engine,
config,
resampler,
})
diff --git a/crates/app/src/audio/vad_processor.rs b/crates/app/src/audio/vad_processor.rs
index dd95b56d..a3498cd9 100644
--- a/crates/app/src/audio/vad_processor.rs
+++ b/crates/app/src/audio/vad_processor.rs
@@ -1,6 +1,6 @@
-use crate::telemetry::pipeline_metrics::{FpsTracker, PipelineMetrics};
-use crate::vad::config::UnifiedVadConfig;
-use crate::vad::types::VadEvent;
+use coldvox_telemetry::{FpsTracker, PipelineMetrics};
+use coldvox_vad::{UnifiedVadConfig, VadEvent};
+use coldvox_audio::AudioFrame;
use std::sync::Arc;
use tokio::sync::broadcast;
use tokio::sync::mpsc::Sender;
@@ -9,12 +9,6 @@ use tracing::{debug, error, info};
use super::vad_adapter::VadAdapter;
-#[derive(Debug, Clone)]
-pub struct AudioFrame {
- pub data: Vec,
- pub timestamp_ms: u64,
-}
-
pub struct VadProcessor {
adapter: VadAdapter,
audio_rx: broadcast::Receiver,
@@ -66,7 +60,13 @@ impl VadProcessor {
}
}
- match self.adapter.process(&frame.data) {
+ // Convert f32 samples back to i16
+ let i16_data: Vec = frame.samples
+ .iter()
+ .map(|&s| (s * i16::MAX as f32) as i16)
+ .collect();
+
+ match self.adapter.process(&i16_data) {
Ok(Some(event)) => {
self.events_generated += 1;
diff --git a/crates/app/src/bin/mic_probe.rs b/crates/app/src/bin/mic_probe.rs
index 3b35a35c..ae12a06b 100644
--- a/crates/app/src/bin/mic_probe.rs
+++ b/crates/app/src/bin/mic_probe.rs
@@ -5,7 +5,7 @@ use coldvox_app::probes::{
};
use std::path::PathBuf;
use std::time::Duration;
-use tokio;
+
#[derive(Parser)]
#[command(name = "mic-probe")]
@@ -72,7 +72,7 @@ enum TestType {
async fn run_single_test(cli: &Cli, test_type: TestType) -> Result<(), Box> {
let context = create_test_context(cli);
- let results_dir = ensure_results_dir(cli.output_dir.as_ref().map(|v| v.as_path()))?;
+ let results_dir = ensure_results_dir(cli.output_dir.as_deref())?;
if cli.verbose {
println!("Starting {} test...", get_test_name(&test_type));
@@ -112,7 +112,7 @@ async fn run_single_test(cli: &Cli, test_type: TestType) -> Result<(), Box Result<(), Box> {
let context = create_test_context(cli);
- let results_dir = ensure_results_dir(cli.output_dir.as_ref().map(|v| v.as_path()))?;
+ let results_dir = ensure_results_dir(cli.output_dir.as_deref())?;
if cli.verbose {
println!("Running all audio tests...");
@@ -151,11 +151,7 @@ async fn run_all_tests(cli: &Cli) -> Result<(), Box> {
let result_path = write_result_json(&results_dir, &test_result)?;
results.push((display_name, test_result, result_path));
- if cli.verbose {
- println!("{}: {}", display_name, status);
- } else {
- println!("{}: {}", display_name, status);
- }
+ println!("{}: {}", display_name, status);
}
Err(e) => {
all_passed = false;
diff --git a/crates/app/src/bin/tui_dashboard.rs b/crates/app/src/bin/tui_dashboard.rs
index 4a53c26c..53fb412d 100644
--- a/crates/app/src/bin/tui_dashboard.rs
+++ b/crates/app/src/bin/tui_dashboard.rs
@@ -4,16 +4,17 @@
// - File output uses a non-blocking writer; logs/ is created if missing.
// - Useful for post-session analysis even when the TUI is active.
use clap::Parser;
-use coldvox_app::audio::capture::AudioCaptureThread;
-use coldvox_app::audio::chunker::{AudioChunker, ChunkerConfig};
-use coldvox_app::audio::frame_reader::FrameReader;
-use coldvox_app::audio::ring_buffer::AudioRingBuffer;
-use coldvox_app::audio::vad_processor::{AudioFrame as VadFrame, VadProcessor};
-use coldvox_app::foundation::error::AudioConfig;
-use coldvox_app::telemetry::pipeline_metrics::{PipelineMetrics, PipelineStage};
-use coldvox_app::vad::config::{UnifiedVadConfig, VadMode};
-use coldvox_app::vad::constants::{FRAME_SIZE_SAMPLES, SAMPLE_RATE_HZ};
-use coldvox_app::vad::types::VadEvent;
+use coldvox_audio::capture::AudioCaptureThread;
+use coldvox_audio::chunker::{AudioChunker, ChunkerConfig};
+use coldvox_audio::frame_reader::FrameReader;
+use coldvox_audio::ring_buffer::AudioRingBuffer;
+use coldvox_audio::chunker::AudioFrame as VadFrame;
+use coldvox_app::audio::vad_processor::VadProcessor;
+use coldvox_foundation::error::AudioConfig;
+use coldvox_telemetry::pipeline_metrics::{PipelineMetrics, PipelineStage};
+use coldvox_vad::config::{UnifiedVadConfig, VadMode};
+use coldvox_vad::constants::{FRAME_SIZE_SAMPLES, SAMPLE_RATE_HZ};
+use coldvox_vad::types::VadEvent;
#[cfg(feature = "vosk")]
use coldvox_app::stt::{processor::SttProcessor, TranscriptionConfig, TranscriptionEvent};
use crossterm::{
@@ -405,7 +406,7 @@ async fn run_audio_pipeline(tx: mpsc::Sender, device: String) {
let chunker_cfg = ChunkerConfig {
frame_size_samples: FRAME_SIZE_SAMPLES,
sample_rate_hz: SAMPLE_RATE_HZ,
- resampler_quality: coldvox_app::audio::chunker::ResamplerQuality::Balanced,
+ resampler_quality: coldvox_audio::chunker::ResamplerQuality::Balanced,
};
// Build FrameReader from ring buffer consumer and feed it to the chunker
let frame_reader = FrameReader::new(
@@ -488,7 +489,7 @@ async fn run_audio_pipeline(tx: mpsc::Sender, device: String) {
tokio::spawn(async move {
while let Some(ev) = raw_vad_rx_task.recv().await {
// Send to UI
- let _ = ui_vad_tx.send(ev.clone()).await;
+ let _ = ui_vad_tx.send(ev).await;
// Send to STT if available
#[cfg(feature = "vosk")]
if let Some(stt_tx) = &stt_vad_tx_clone {
diff --git a/crates/app/src/foundation/mod.rs b/crates/app/src/foundation/mod.rs
index e84e5f02..ff8fac78 100644
--- a/crates/app/src/foundation/mod.rs
+++ b/crates/app/src/foundation/mod.rs
@@ -1,9 +1,6 @@
-pub mod error;
-pub mod health;
-pub mod shutdown;
-pub mod state;
+//! Foundation module re-exports
+//!
+//! This module provides a unified interface to foundation functionality
+//! by re-exporting types from the coldvox-foundation crate.
-pub use error::*;
-pub use health::*;
-pub use shutdown::*;
-pub use state::*;
+pub use coldvox_foundation::*;
diff --git a/crates/app/src/hotkey/indicator.rs b/crates/app/src/hotkey/indicator.rs
index 1118a472..7d6aeb4b 100644
--- a/crates/app/src/hotkey/indicator.rs
+++ b/crates/app/src/hotkey/indicator.rs
@@ -53,3 +53,9 @@ impl RecordingIndicator {
self.displayed = false;
}
}
+
+impl Default for RecordingIndicator {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/crates/app/src/hotkey/listener.rs b/crates/app/src/hotkey/listener.rs
index d6212f12..a4ac80ea 100644
--- a/crates/app/src/hotkey/listener.rs
+++ b/crates/app/src/hotkey/listener.rs
@@ -1,7 +1,7 @@
use std::time::{Duration, Instant};
use tokio::sync::mpsc::Sender;
use device_query::{DeviceQuery, DeviceState, Keycode};
-use crate::vad::types::VadEvent;
+use coldvox_vad::types::VadEvent;
use super::indicator::RecordingIndicator;
/// Spawn a blocking task that listens for Ctrl+Super key combinations
diff --git a/crates/app/src/lib.rs b/crates/app/src/lib.rs
index 504585b6..7fa2c42e 100644
--- a/crates/app/src/lib.rs
+++ b/crates/app/src/lib.rs
@@ -1,8 +1,8 @@
pub mod audio;
-pub mod foundation;
pub mod probes;
pub mod stt;
-pub mod telemetry;
pub mod text_injection;
-pub mod vad;
pub mod hotkey;
+pub mod vad;
+pub mod telemetry;
+pub mod foundation;
diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs
index da6639db..209de567 100644
--- a/crates/app/src/main.rs
+++ b/crates/app/src/main.rs
@@ -4,21 +4,23 @@
// - The logs/ directory is created on startup if missing; file output uses a non-blocking writer.
// - This ensures persistent logs for post-run analysis while keeping console output for live use.
use anyhow::anyhow;
-use coldvox_app::audio::chunker::{AudioChunker, ChunkerConfig};
-use coldvox_app::audio::ring_buffer::AudioRingBuffer;
-use coldvox_app::audio::*;
-use coldvox_app::foundation::*;
-use coldvox_app::stt::{processor::SttProcessor, TranscriptionConfig, TranscriptionEvent};
+use coldvox_audio::{AudioChunker, ChunkerConfig, AudioRingBuffer, AudioCaptureThread, FrameReader};
+use coldvox_foundation::*;
+use coldvox_app::stt::TranscriptionConfig;
+#[cfg(feature = "vosk")]
+use coldvox_app::stt::{processor::SttProcessor, TranscriptionEvent};
#[cfg(feature = "vosk")]
use coldvox_app::stt::persistence::{PersistenceConfig, TranscriptFormat, AudioFormat, SessionMetadata};
-use coldvox_app::text_injection::{self, AsyncInjectionProcessor};
-use coldvox_app::vad::config::{UnifiedVadConfig, VadMode};
-use coldvox_app::vad::constants::{FRAME_SIZE_SAMPLES, SAMPLE_RATE_HZ};
-use coldvox_app::vad::types::VadEvent;
-use coldvox_app::telemetry::pipeline_metrics::PipelineMetrics;
+
+use coldvox_vad::{UnifiedVadConfig, VadMode, FRAME_SIZE_SAMPLES, SAMPLE_RATE_HZ, VadEvent};
+use coldvox_telemetry::PipelineMetrics;
use coldvox_app::hotkey::spawn_hotkey_listener;
+#[cfg(feature = "text-injection")]
+use coldvox_app::text_injection::{AsyncInjectionProcessor, InjectionConfig};
use std::time::Duration;
-use clap::{Args, Parser, ValueEnum};
+use clap::{Parser, ValueEnum};
+#[cfg(feature = "text-injection")]
+use clap::Args;
use tokio::sync::{broadcast, mpsc};
use tracing_appender::rolling::{RollingFileAppender, Rotation};
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
@@ -56,22 +58,27 @@ struct Cli {
#[arg(long = "resampler-quality", default_value = "balanced")]
resampler_quality: String,
+ #[cfg(feature = "vosk")]
/// Enable transcription persistence to disk
#[arg(long = "save-transcriptions")]
save_transcriptions: bool,
+ #[cfg(feature = "vosk")]
/// Save audio alongside transcriptions
#[arg(long = "save-audio", requires = "save_transcriptions")]
save_audio: bool,
+ #[cfg(feature = "vosk")]
/// Output directory for transcriptions
#[arg(long = "output-dir", default_value = "transcriptions")]
output_dir: String,
+ #[cfg(feature = "vosk")]
/// Transcription format: json, csv, text
#[arg(long = "transcript-format", default_value = "json")]
transcript_format: String,
+ #[cfg(feature = "vosk")]
/// Keep transcription files for N days (0 = forever)
#[arg(long = "retention-days", default_value = "30")]
retention_days: u32,
@@ -153,7 +160,7 @@ async fn main() -> Result<(), Box> {
let resampler_quality = std::env::var("COLDVOX_RESAMPLER_QUALITY").unwrap_or(cli.resampler_quality.clone());
if cli.list_devices {
- let dm = coldvox_app::audio::device::DeviceManager::new()?;
+ let dm = coldvox_audio::DeviceManager::new()?;
tracing::info!("CPAL host: {:?}", dm.host_id());
let devices = dm.enumerate_devices();
println!("Input devices (host: {:?}):", dm.host_id());
@@ -182,7 +189,7 @@ async fn main() -> Result<(), Box> {
tracing::info!("Audio capture thread started successfully.");
// --- 2. Audio Chunker ---
- let frame_reader = coldvox_app::audio::frame_reader::FrameReader::new(
+ let frame_reader = FrameReader::new(
audio_consumer,
device_cfg.sample_rate,
device_cfg.channels,
@@ -194,9 +201,9 @@ async fn main() -> Result<(), Box> {
// Target 16k for VAD; resampler in chunker will convert from device rate
sample_rate_hz: SAMPLE_RATE_HZ,
resampler_quality: match resampler_quality.to_lowercase().as_str() {
- "fast" => coldvox_app::audio::chunker::ResamplerQuality::Fast,
- "quality" => coldvox_app::audio::chunker::ResamplerQuality::Quality,
- _ => coldvox_app::audio::chunker::ResamplerQuality::Balanced, // default/balanced
+ "fast" => coldvox_audio::ResamplerQuality::Fast,
+ "quality" => coldvox_audio::ResamplerQuality::Quality,
+ _ => coldvox_audio::ResamplerQuality::Balanced, // default/balanced
},
};
@@ -210,7 +217,7 @@ async fn main() -> Result<(), Box> {
// This broadcast channel will distribute audio frames to all interested components.
let (audio_tx, _) =
- broadcast::channel::(200);
+ broadcast::channel::(200);
let chunker = AudioChunker::new(frame_reader, audio_tx.clone(), chunker_cfg)
.with_metrics(metrics.clone())
.with_device_config(device_config_rx.resubscribe());
@@ -247,34 +254,51 @@ async fn main() -> Result<(), Box> {
};
// --- 4. STT Processor ---
- // Check for Vosk model path from environment or use default
- let model_path = std::env::var("VOSK_MODEL_PATH")
- .unwrap_or_else(|_| "models/vosk-model-small-en-us-0.15".to_string());
-
- // Check if model exists to determine if STT should be enabled
- let stt_enabled = std::path::Path::new(&model_path).exists();
-
- if !stt_enabled && !model_path.is_empty() {
- tracing::warn!(
- "STT disabled: Vosk model not found at '{}'. \
- Download a model from https://alphacephei.com/vosk/models \
- or set VOSK_MODEL_PATH environment variable.",
- model_path
- );
- }
+ #[cfg(feature = "vosk")]
+ let stt_config = {
+ // Check for Vosk model path from environment or use default
+ let model_path = std::env::var("VOSK_MODEL_PATH")
+ .unwrap_or_else(|_| "models/vosk-model-small-en-us-0.15".to_string());
+
+ // Check if model exists to determine if STT should be enabled
+ let stt_enabled = std::path::Path::new(&model_path).exists();
+
+ if !stt_enabled && !model_path.is_empty() {
+ tracing::warn!(
+ "STT disabled: Vosk model not found at '{}'. \
+ Download a model from https://alphacephei.com/vosk/models \
+ or set VOSK_MODEL_PATH environment variable.",
+ model_path
+ );
+ }
- // Create STT configuration
- let stt_config = TranscriptionConfig {
- enabled: stt_enabled,
- model_path,
- partial_results: true,
- max_alternatives: 1,
- include_words: false,
- buffer_size_ms: 512,
+ // Create STT configuration
+ TranscriptionConfig {
+ enabled: stt_enabled,
+ model_path,
+ partial_results: true,
+ max_alternatives: 1,
+ include_words: false,
+ buffer_size_ms: 512,
+ }
+ };
+
+ #[cfg(not(feature = "vosk"))]
+ let _stt_config = {
+ tracing::info!("STT support not compiled - build with --features vosk to enable");
+ TranscriptionConfig {
+ enabled: false,
+ model_path: String::new(),
+ partial_results: false,
+ max_alternatives: 1,
+ include_words: false,
+ buffer_size_ms: 512,
+ }
};
// Only spawn STT processor if enabled
let mut injection_shutdown_tx: Option> = None;
+ #[cfg(feature = "vosk")]
let (stt_handle, _persistence_handle, injection_handle) = if stt_config.enabled {
// Create mpsc channel for STT processor to send transcription events
let (stt_transcription_tx, mut stt_transcription_rx) = mpsc::channel::(100);
@@ -334,18 +358,19 @@ async fn main() -> Result<(), Box> {
.map_err(|e| anyhow!("Failed to create STT processor: {}", e))?;
// --- 5. Text Injection Processor ---
- let injection_handle = if cfg!(feature = "text-injection") && cli.injection.enable {
+ #[cfg(feature = "text-injection")]
+ let injection_handle = if cli.injection.enable {
// Build the full injection config from CLI args and defaults
- let injection_config = text_injection::InjectionConfig {
+ let injection_config = InjectionConfig {
allow_ydotool: cli.injection.allow_ydotool,
allow_kdotool: cli.injection.allow_kdotool,
allow_enigo: cli.injection.allow_enigo,
allow_mki: cli.injection.allow_mki,
restore_clipboard: cli.injection.restore_clipboard,
inject_on_unknown_focus: cli.injection.inject_on_unknown_focus,
- max_total_latency_ms: cli.injection.max_total_latency_ms.unwrap_or(text_injection::types::InjectionConfig::default().max_total_latency_ms),
- per_method_timeout_ms: cli.injection.per_method_timeout_ms.unwrap_or(text_injection::types::InjectionConfig::default().per_method_timeout_ms),
- cooldown_initial_ms: cli.injection.cooldown_initial_ms.unwrap_or(text_injection::types::InjectionConfig::default().cooldown_initial_ms),
+ max_total_latency_ms: cli.injection.max_total_latency_ms.unwrap_or(InjectionConfig::default().max_total_latency_ms),
+ per_method_timeout_ms: cli.injection.per_method_timeout_ms.unwrap_or(InjectionConfig::default().per_method_timeout_ms),
+ cooldown_initial_ms: cli.injection.cooldown_initial_ms.unwrap_or(InjectionConfig::default().cooldown_initial_ms),
..Default::default()
};
@@ -369,6 +394,9 @@ async fn main() -> Result<(), Box> {
tracing::info!("Text injection disabled.");
None
};
+
+ #[cfg(not(feature = "text-injection"))]
+ let injection_handle: Option> = None;
// Note: For now, we've removed the separate transcription persistence handler
// since transcription events go directly to the injection processor.
@@ -432,6 +460,20 @@ async fn main() -> Result<(), Box> {
(None, None, None)
};
+ #[cfg(not(feature = "vosk"))]
+ let (stt_handle, _persistence_handle, injection_handle) = {
+ tracing::info!("STT processor disabled - no vosk feature");
+
+ // Consume VAD events even when STT is disabled to prevent channel backpressure
+ tokio::spawn(async move {
+ while let Some(_event) = event_rx.recv().await {
+ // Just consume the events - no STT processing when vosk is disabled
+ }
+ });
+
+ (None::>, None::>, None::>)
+ };
+
// --- Main Application Loop ---
let mut stats_interval = tokio::time::interval(Duration::from_secs(30));
loop {
diff --git a/crates/app/src/probes/mic_capture.rs b/crates/app/src/probes/mic_capture.rs
index ea76c560..3156b303 100644
--- a/crates/app/src/probes/mic_capture.rs
+++ b/crates/app/src/probes/mic_capture.rs
@@ -1,12 +1,10 @@
use std::sync::Arc;
-use crate::telemetry::pipeline_metrics::PipelineMetrics;
+use coldvox_telemetry::PipelineMetrics;
use crate::probes::MicCaptureThresholds;
use super::common::{LiveTestResult, TestContext, TestError, TestErrorKind};
-use crate::audio::capture::AudioCaptureThread;
-use crate::audio::frame_reader::FrameReader;
-use crate::audio::ring_buffer::AudioRingBuffer;
-use crate::foundation::error::{AudioConfig, AudioError};
+use coldvox_audio::{AudioCaptureThread, FrameReader, AudioRingBuffer};
+use coldvox_foundation::{AudioConfig, AudioError};
use serde_json::json;
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
diff --git a/crates/app/src/probes/text_injection.rs b/crates/app/src/probes/text_injection.rs
index 1dc51a7d..84e1e2ef 100644
--- a/crates/app/src/probes/text_injection.rs
+++ b/crates/app/src/probes/text_injection.rs
@@ -1,5 +1,5 @@
use std::sync::Arc;
-use crate::telemetry::pipeline_metrics::PipelineMetrics;
+use coldvox_telemetry::pipeline_metrics::PipelineMetrics;
use crate::text_injection::manager::StrategyManager;
use crate::text_injection::types::{InjectionConfig, InjectionMetrics};
use crate::probes::common::{LiveTestResult, TestContext, TestError};
diff --git a/crates/app/src/probes/vad_mic.rs b/crates/app/src/probes/vad_mic.rs
index a50dba32..174f04f1 100644
--- a/crates/app/src/probes/vad_mic.rs
+++ b/crates/app/src/probes/vad_mic.rs
@@ -1,15 +1,16 @@
use std::sync::Arc;
-use crate::telemetry::pipeline_metrics::PipelineMetrics;
+use coldvox_telemetry::pipeline_metrics::PipelineMetrics;
use super::common::{LiveTestResult, TestContext, TestError, TestErrorKind};
-use crate::audio::capture::AudioCaptureThread;
-use crate::audio::chunker::{AudioChunker, ChunkerConfig};
-use crate::audio::frame_reader::FrameReader;
-use crate::audio::ring_buffer::AudioRingBuffer;
-use crate::audio::vad_processor::{AudioFrame as VadFrame, VadProcessor};
-use crate::vad::types::VadEvent;
-use crate::vad::config::{UnifiedVadConfig, VadMode};
-use crate::foundation::error::AudioConfig;
+use coldvox_audio::capture::AudioCaptureThread;
+use coldvox_audio::chunker::{AudioChunker, ChunkerConfig, ResamplerQuality};
+use coldvox_audio::frame_reader::FrameReader;
+use coldvox_audio::ring_buffer::AudioRingBuffer;
+use coldvox_audio::chunker::AudioFrame as VadFrame;
+use crate::audio::vad_processor::VadProcessor;
+use coldvox_vad::types::VadEvent;
+use coldvox_vad::config::{UnifiedVadConfig, VadMode};
+use coldvox_foundation::error::AudioConfig;
use serde_json::json;
use std::collections::HashMap;
use std::time::{Duration, Instant};
@@ -64,7 +65,7 @@ impl VadMicCheck {
let chunker_cfg = ChunkerConfig {
frame_size_samples: 512,
sample_rate_hz: 16_000,
- resampler_quality: crate::audio::chunker::ResamplerQuality::Balanced,
+ resampler_quality: ResamplerQuality::Balanced,
};
let frame_reader = FrameReader::new(
@@ -102,9 +103,7 @@ impl VadMicCheck {
let start_time = Instant::now();
let mut vad_events = Vec::new();
let mut speech_segments = 0;
- // TODO: Add periodic logging of metrics here (e.g., FPS and buffer fill)
let mut total_speech_duration_ms = 0;
- let mut last_speech_start: Option = None;
let timeout = tokio::time::sleep(duration);
tokio::pin!(timeout);
@@ -113,19 +112,15 @@ impl VadMicCheck {
tokio::select! {
Some(event) = event_rx.recv() => {
let timestamp_ms = start_time.elapsed().as_millis() as u64;
- vad_events.push((timestamp_ms, event));
-
- match event {
+ match &event {
VadEvent::SpeechStart { .. } => {
speech_segments += 1;
- last_speech_start = Some(timestamp_ms);
}
VadEvent::SpeechEnd { duration_ms, .. } => {
- if let Some(_start_time) = last_speech_start.take() {
- total_speech_duration_ms += duration_ms;
- }
+ total_speech_duration_ms += *duration_ms;
}
}
+ vad_events.push((timestamp_ms, event));
}
_ = &mut timeout => break,
}
diff --git a/crates/app/src/stt/mod.rs b/crates/app/src/stt/mod.rs
index 9e1887da..e509a839 100644
--- a/crates/app/src/stt/mod.rs
+++ b/crates/app/src/stt/mod.rs
@@ -1,93 +1,14 @@
// STT abstraction and optional engine implementations (feature-gated)
-use std::sync::atomic::{AtomicU64, Ordering};
-
-/// Generates unique utterance IDs
-static UTTERANCE_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
-
-pub fn next_utterance_id() -> u64 {
- UTTERANCE_ID_COUNTER.fetch_add(1, Ordering::SeqCst)
-}
-
-/// Transcription event types
-#[derive(Debug, Clone)]
-pub enum TranscriptionEvent {
- /// Partial transcription result (ongoing speech)
- Partial {
- utterance_id: u64,
- text: String,
- /// Optional start time offset in seconds
- t0: Option,
- /// Optional end time offset in seconds
- t1: Option,
- },
- /// Final transcription result (speech segment complete)
- Final {
- utterance_id: u64,
- text: String,
- /// Optional word-level timing information
- words: Option>,
- },
- /// Transcription error
- Error {
- code: String,
- message: String,
- },
-}
-
-/// Word-level timing and confidence information
-#[derive(Debug, Clone)]
-pub struct WordInfo {
- /// Start time in seconds
- pub start: f32,
- /// End time in seconds
- pub end: f32,
- /// Confidence score (0.0-1.0)
- pub conf: f32,
- /// Word text
- pub text: String,
-}
-
-/// Transcription configuration
-#[derive(Debug, Clone)]
-pub struct TranscriptionConfig {
- /// Enable/disable transcription
- pub enabled: bool,
- /// Path to Vosk model directory
- pub model_path: String,
- /// Emit partial recognition results
- pub partial_results: bool,
- /// Maximum alternatives in results
- pub max_alternatives: u32,
- /// Include word-level timing in results
- pub include_words: bool,
- /// Buffer size in milliseconds
- pub buffer_size_ms: u32,
-}
-
-impl Default for TranscriptionConfig {
- fn default() -> Self {
- Self {
- enabled: false,
- model_path: String::new(),
- partial_results: true,
- max_alternatives: 1,
- include_words: false,
- buffer_size_ms: 512,
- }
- }
-}
-
-/// Minimal streaming transcription interface (deprecated - kept for backward compatibility)
-/// New code should use VoskTranscriber directly with TranscriptionEvent
-pub trait Transcriber {
- /// Feed 16 kHz, mono, S16LE PCM samples.
- /// Returns Some(final_text_or_json) when an utterance completes, else None.
- fn accept_pcm16(&mut self, pcm: &[i16]) -> Result