diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..8d4565b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,52 @@ +name: Bug Report +description: File a bug report +title: "[Title here. keep it short]" +labels: ["bug"] +assignees: + - octocat +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + One second before you create, search if it's already created [issues](https://github.com/thewh1teagle/vibe/issues?q=is:issue+label:bug+) + + Also, you can try enable debug mode by set `debug: true` in the creation of the struct and rerun to see more logs. + + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Also tell us, what did you expect to happen? + placeholder: Tell us what you see! + value: "A bug happened!" + validations: + required: true + - type: textarea + id: steps-to-reproduce + attributes: + label: Steps to reproduce + description: Also tell us, what did you expect to happen? + placeholder: Tell us what you see! + value: | + 1. Step one... + 2. Step two... + validations: + required: true + - type: dropdown + id: browsers + attributes: + label: What OS are you seeing the problem on? + multiple: true + options: + - Window + - Linux + - MacOS + - type: textarea + id: logs + attributes: + label: Relevant log output + description: | + Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + + render: shell diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 0000000..622eca2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,22 @@ +name: Feature request +description: Suggest an idea for this project +title: '[Title here. keep it short]' +labels: ['feature'] +assignees: + - thewh1teagle +body: + - type: markdown + attributes: + value: | + 💚💜 Thank you for interest. ❤️💛 + *Please prioritize checking existing issues first*. [bugs](https://github.com/thewh1teagle/sherpa-rs/issues?q=is:issue+label:bug+) + I will repay with higher-quality code. + + - type: textarea + id: describe-the-feature + attributes: + label: Describe the feature + description: Also tell us why you think it useful + placeholder: Description... + validations: + required: true \ No newline at end of file diff --git a/scripts/setup_cuda.ps1 b/.github/scripts/setup_cuda.ps1 similarity index 100% rename from scripts/setup_cuda.ps1 rename to .github/scripts/setup_cuda.ps1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 205dae5..bdbd9a2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,15 +39,15 @@ jobs: options: "" - platform: "windows-latest" # Windows Cuda - options: '--features "cuda"' + options: '--features "cuda" --no-default-features' cuda-version: "12.5.0" - platform: "windows-latest" # Windows Cuda - options: '--features "cuda"' + options: '--features "cuda" --no-default-features' cuda-version: "11.8.0" - platform: "windows-latest" # Windows DirectML - options: '--features "directml"' + options: '--features "directml" --no-default-features' - platform: "ubuntu-22.04" # Linux Cuda options: '--features "cuda"' cuda-version: "12.4.1" @@ -58,7 +58,7 @@ jobs: submodules: "true" - name: Setup cuda for Windows - run: scripts/setup_cuda.ps1 + run: .github/scripts/setup_cuda.ps1 env: INPUT_CUDA_VERSION: ${{ matrix.cuda-version }} if: matrix.platform == 'windows-latest' && contains(matrix.options, 'cuda') @@ -88,11 +88,6 @@ jobs: run: | cargo build ${{ matrix.options }} ${{ github.event.inputs.cargo_args }} - - name: Modify shared libraries path - if: contains(matrix.platform, 'ubuntu') && contains(matrix.options, 'cuda') - run: | - echo "LD_LIBRARY_PATH=$PWD/target/debug:$LD_LIBRARY_PATH" >> $GITHUB_ENV - - name: Test Whisper # Github actions doesn't have Nvidia GPUs if: contains(matrix.options, 'cuda') != true diff --git a/.gitignore b/.gitignore index 05e190a..623f251 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea target/ *.onnx *.onnx.* @@ -6,7 +7,24 @@ tokens.txt venv/ vits-piper* *.wav +*.vocab !samples/*.wav *.bz2 sherpa-onnx-whisper* -*.txt \ No newline at end of file +sherpa-onnx-pyannote-* +sherpa-onnx-zipformer-* +sherpa-onnx-moonshine-* +*.txt +!checksum.txt +sherpa-onnx-v* +jniLibs +!sys/dist.txt +sherpa-onnx-punct* +vits-* +sherpa-onnx-kws-* +.tmp/ +jniLibs/ +build/ +kokoro-en-*/ +matcha-* +kokoro-multi-lang-v1_0/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 8931e17..b834818 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "sys/sherpa-onnx"] - path = sys/sherpa-onnx +[submodule "crates/sherpa-rs-sys/sherpa-onnx"] + path = crates/sherpa-rs-sys/sherpa-onnx url = https://github.com/k2-fsa/sherpa-onnx diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 955c52b..0000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "configurations": [ - { - "name": "Mac", - "includePath": [ - "${default}", - "${workspaceFolder}/sys/sherpa-onnx", - "${workspaceFolder}/sys/sherpa-onnx/build/_deps/**" - ], - "defines": [], - "macFrameworkPath": [ - "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks" - ], - "compilerPath": "/usr/bin/clang", - "cStandard": "c17", - "cppStandard": "c++17", - "intelliSenseMode": "macos-clang-arm64" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 0f3165e..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "editor.formatOnSave": true, - // "rust-analyzer.check.command": "clippy" -} \ No newline at end of file diff --git a/BUILDING.md b/BUILDING.md index c524e27..cdda9d3 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -11,10 +11,19 @@ sudo apt-get update sudo apt-get install -y pkg-config build-essential clang cmake ``` +### Windows + +For convenience, I recommend installing these packages. +Additionally, when using wget to run examples, use `wget.exe` instead. + +```console +winget install llvm +``` + ### Prepare repository ```console -git clone https://github.com/thewh1teagle/sherpa-rs --recursive +git clone --recursive https://github.com/thewh1teagle/sherpa-rs cd sherpa-rs ``` @@ -32,6 +41,50 @@ cargo build --release 4. Restart your shell!!! 5. Cargo build +### Compile with prebuild sherpa-onnx manually (For fast compilation) + +Note: sherpa-onnx already download and cache the sherpa-onnx binaries. You can do this manually instead. +Note: to link sherpa-onnx libs dynamically set `SHERPA_BUILD_SHARED_LIBS` to `1`. +Note: you should disable rust-analyzer while doing this. otherwise it will rebuild it with different environment variable on each save which will take long.... time. +Note: on Linux when linking statically you should set this env: `RUSTFLAGS="-C relocation-model=dynamic-no-pic"` + +
+macOS (arm64/x86-64) + +```console +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.10.28/sherpa-onnx-v1.10.28-osx-universal2-static.tar.bz2 +tar xf sherpa-onnx-v1.10.28-osx-universal2-static.tar.bz2 +export SHERPA_LIB_PATH="$(pwd)/sherpa-onnx-v1.10.28-osx-universal2-static" +cargo build +``` + +
+ +
+Windows (x86-64) + +```console +wget.exe https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.10.28/sherpa-onnx-v1.10.28-win-x64-static.tar.bz2 +tar.exe xf sherpa-onnx-v1.10.28-win-x64-static.tar.bz2 +$env:SHERPA_LIB_PATH="$pwd/sherpa-onnx-v1.10.28-win-x64-static" +cargo build +``` + +
+ +
+Linux (x86-64) + +```console +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.10.28/sherpa-onnx-v1.10.28-linux-x64-static.tar.bz2 +tar xf sherpa-onnx-v1.10.28-linux-x64-static.tar.bz2 +export SHERPA_LIB_PATH="$(pwd)/sherpa-onnx-v1.10.28-linux-x64-static" +export RUSTFLAGS="-C relocation-model=dynamic-no-pic" +cargo build +``` + +
+ ### Resample wav file for 16khz ```console @@ -43,10 +96,16 @@ ffmpeg -i -ar 16000 -ac 1 -c:a pcm_s16le ```console cd sys/sherpa-onnx git pull origin master +git checkout ``` ### Gotachas +On Linux you should set `RUSTFLAGS="-C relocation-model=dynamic-no-pic"` + +
+When using GPU such as DirectML or Cuda + --- When running `--example` with dynamic libraries eg. with `directml` or `cuda` you need to have the DLLs from `target` folder in PATH. @@ -58,15 +117,154 @@ copy target\debug\examples\transcribe.exe target\debug target\debug\transcribe.exe motivation.wav ``` +When building with cuda you should use cuda `11.x` +In addition install `cudnn` with `sudo apt install nvidia-cudnn` + +
+ +
+Whisper limits + --- Currently whisper can transcribe only chunks of 30s max. --- -When building with cuda you should use cuda `11.x` -In addition install `cudnn` with `sudo apt install nvidia-cudnn` +
+ +
+Static linking failed on Windows + +You can resolve it by creating `.cargo/config.toml` next to `Cargo.toml` with the following: + +```toml +[target.'cfg(windows)'] +rustflags = ["-C target-feature=+crt-static"] +``` + +Or set the environment variable `RUSTFLAGS` to `-C target-feature=+crt-static` + +If it doesn't help make sure all of your dependencies also links MSVC runtime statically. +You can inspect the build with the following: + +1. Set `RUSTC_LOG` to `rustc_codegen_ssa::back::link=info` +2. Build with + +```console +cargo build -vv +``` + +Since there's a lot of output, it's good idea to pipe it to file and check later: + +```console +cargo build -vv >log.txt 2>&1 +``` + +Look for the flags `/MD` (Meaning it links it dynamically) and `/MT` or `-MT` (Meaning it links it statically). See [MSVC_RUNTIME_LIBRARY](https://cmake.org/cmake/help/latest/prop_tgt/MSVC_RUNTIME_LIBRARY.html) and [pyannote-rs/issues/1](https://github.com/thewh1teagle/pyannote-rs/issues/1) + +
+ +
+Fix build issues with build flags + +Controlling build flags +Please see `env::var` calls in `build.rs`. + +
+ +
+Cmake error: path exceeded + +Cmake filed with error about maxium paths exceeded. eg. `The fully qualified file name must be less than 260 characters.` + +1. Open PowerShell as admin and execute: + +```powershell +New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" ` +-Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force +``` + +2. Restart PC + +
+ +### Shared library not found while it's in the same directly of executable + +Modify the shared librarie and binary files to load other shared libraries located in the same folder. + +```console +patchelf --set-rpath '$ORIGIN' /path/to/binary/or/shared/library.so +``` ### Debug build -For debug the build process of sherpa-onnx, please set `BUILD_DEBUG=1` environment variable before build. +For debug the build process of sherpa-onnx, please set `SHERPA_BUILD_DEBUG=1` environment variable before build. + +## Release new version + +```console +gh release create v0.4.1 --title v0.4.1 --generate-notes +``` + +## Calculate sha256 for dist table + +```console +shasum -a 256 | tr 'a-z' 'A-Z' +``` + +## See debug log from build + +``` +SHERPA_BUILD_DEBUG=1 cargo build -vv +``` + +## Build for Android + +You must install NDK from Android Studio settings. + +```console +rustup target add aarch64-linux-android +cargo install cargo-ndk +export NDK_HOME="$HOME/Library/Android/sdk/ndk/27.0.12077973" # ls $HOME/Library/Android/sdk/ndk/ +cargo ndk -t arm64-v8a build --release +``` + +## Build for IOS + +Install Xcode command line tools + +```console +xcode-select --install +``` + +Install toolchain + +```console +# IOS +rustup target add aarch64-apple-ios +# Intel chip emulator +rustup target add x86_64-apple-ios +# Apple chip emulator +rustup target add aarch64-apple-ios-sim +``` + +Build + +```console +export RUSTFLAGS="-C link-arg=-fapple-link-rtlib" # See https://github.com/bmrlab/gendam/issues/96 +cargo build --release --target aarch64-apple-ios +``` + +## Build for Windows arm64 + +1. Install toolchain in Visual Studio + +Open Visual Studio Installer, go to Individual Components, search ARM64, select MSVC v[version] - ARM64 build tools, and click Modify. + +1. Build + +```console +rustup target add aarch64-pc-windows-msvc +cargo build --no-default-features --target aarch64-pc-windows-msvc --release +``` \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 1912a0b..a0befbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aho-corasick" version = "1.1.3" @@ -11,32 +17,11 @@ dependencies = [ "memchr", ] -[[package]] -name = "alsa" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37fe60779335388a88c01ac6c3be40304d1e349de3ada3b15f7808bb90fa9dce" -dependencies = [ - "alsa-sys", - "bitflags 2.6.0", - "libc", -] - -[[package]] -name = "alsa-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" -dependencies = [ - "libc", - "pkg-config", -] - [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -49,57 +34,52 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell_polyfill", + "windows-sys 0.60.2", ] [[package]] -name = "anyhow" -version = "1.0.86" +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" - -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cexpr", "clang-sys", "itertools", @@ -118,44 +98,54 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.2" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] -name = "bitflags" -version = "2.6.0" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] -name = "bumpalo" -version = "3.16.0" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "bytes" -version = "1.7.1" +name = "bzip2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] [[package]] -name = "cc" -version = "1.1.0" +name = "bzip2-sys" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ - "jobserver", - "libc", - "once_cell", + "cc", + "pkg-config", ] [[package]] -name = "cesu8" -version = "1.1.0" +name = "cc" +version = "1.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" +dependencies = [ + "find-msvc-tools", + "shlex", +] [[package]] name = "cexpr" @@ -168,9 +158,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "clang-sys" @@ -185,9 +175,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.9" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" +checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", "clap_derive", @@ -195,9 +185,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.9" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" +checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" dependencies = [ "anstream", "anstyle", @@ -207,9 +197,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck", "proc-macro2", @@ -219,110 +209,109 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "cmake" -version = "0.1.50" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" dependencies = [ "cc", ] [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] -name = "combine" -version = "4.6.7" +name = "cpufeatures" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ - "bytes", - "memchr", + "libc", ] [[package]] -name = "core-foundation-sys" -version = "0.8.6" +name = "crc32fast" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] [[package]] -name = "coreaudio-rs" -version = "0.11.3" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "bitflags 1.3.2", - "core-foundation-sys", - "coreaudio-sys", + "generic-array", + "typenum", ] [[package]] -name = "coreaudio-sys" -version = "0.2.15" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f01585027057ff5f0a5bf276174ae4c1594a2c5bde93d5f46a016d76270f5a9" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "bindgen", + "block-buffer", + "crypto-common", ] [[package]] -name = "cpal" -version = "0.15.3" +name = "dirs" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "alsa", - "core-foundation-sys", - "coreaudio-rs", - "dasp_sample", - "jni", - "js-sys", - "libc", - "mach2", - "ndk", - "ndk-context", - "oboe", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows", + "dirs-sys", ] [[package]] -name = "dasp_sample" -version = "0.11.0" +name = "dirs-sys" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] [[package]] -name = "either" -version = "1.13.0" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "equivalent" -version = "1.0.1" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.0", ] [[package]] @@ -336,16 +325,68 @@ dependencies = [ ] [[package]] -name = "glob" -version = "0.3.1" +name = "filetime" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.60.2", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" + +[[package]] +name = "flate2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] [[package]] -name = "hashbrown" -version = "0.14.5" +name = "form_urlencoded" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "heck" @@ -355,11 +396,11 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -369,76 +410,139 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" [[package]] -name = "indenter" -version = "0.3.3" +name = "icu_collections" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] [[package]] -name = "indexmap" -version = "2.3.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ - "equivalent", - "hashbrown", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.0" +name = "icu_normalizer" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] [[package]] -name = "itertools" -version = "0.12.1" +name = "icu_normalizer_data" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ - "either", + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", ] [[package]] -name = "jni" -version = "0.21.1" +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", - "windows-sys 0.45.0", + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", ] [[package]] -name = "jni-sys" -version = "0.3.0" +name = "idna" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] [[package]] -name = "jobserver" -version = "0.1.32" +name = "idna_adapter" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ - "libc", + "icu_normalizer", + "icu_properties", ] [[package]] -name = "js-sys" -version = "0.3.69" +name = "indenter" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ - "wasm-bindgen", + "either", ] +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "lazy_static" version = "1.5.0" @@ -453,80 +557,74 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.3", ] [[package]] -name = "linux-raw-sys" -version = "0.4.14" +name = "libredox" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] [[package]] -name = "log" -version = "0.4.22" +name = "linux-raw-sys" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] -name = "mach2" -version = "0.4.2" +name = "linux-raw-sys" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" -dependencies = [ - "libc", -] +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] -name = "memchr" -version = "2.7.4" +name = "litemap" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "log" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] -name = "ndk" -version = "0.8.0" +name = "memchr" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" -dependencies = [ - "bitflags 2.6.0", - "jni-sys", - "log", - "ndk-sys", - "num_enum", - "thiserror", -] +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] -name = "ndk-context" -version = "0.1.1" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "ndk-sys" -version = "0.5.0+25.2.9519653" +name = "miniz_oxide" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "jni-sys", + "adler2", ] [[package]] @@ -540,123 +638,103 @@ dependencies = [ ] [[package]] -name = "num-derive" -version = "0.4.2" +name = "once_cell" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "num-traits" -version = "0.2.19" +name = "once_cell_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] -name = "num_enum" -version = "0.7.3" +name = "option-ext" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" -dependencies = [ - "num_enum_derive", -] +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] -name = "num_enum_derive" -version = "0.7.3" +name = "percent-encoding" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] -name = "oboe" -version = "0.6.1" +name = "pin-project-lite" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" -dependencies = [ - "jni", - "ndk", - "ndk-context", - "num-derive", - "num-traits", - "oboe-sys", -] +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] -name = "oboe-sys" -version = "0.6.1" +name = "pkg-config" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d" -dependencies = [ - "cc", -] +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] -name = "once_cell" -version = "1.19.0" +name = "potential_utf" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] [[package]] name = "prettyplease" -version = "0.2.20" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", ] [[package]] -name = "proc-macro-crate" -version = "3.1.0" +name = "proc-macro2" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ - "toml_edit", + "unicode-ident", ] [[package]] -name = "proc-macro2" -version = "1.0.86" +name = "quote" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ - "unicode-ident", + "proc-macro2", ] [[package]] -name = "quote" -version = "1.0.36" +name = "redox_syscall" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "proc-macro2", + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", ] [[package]] name = "regex" -version = "1.10.5" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", @@ -666,9 +744,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", @@ -677,9 +755,23 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "untrusted", + "windows-sys 0.52.0", +] [[package]] name = "rustc-hash" @@ -689,47 +781,152 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags", "errno", "libc", - "linux-raw-sys", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.0", +] + +[[package]] +name = "rustls" +version = "0.23.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", ] [[package]] -name = "same-file" -version = "1.0.6" +name = "rustls-pki-types" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ - "winapi-util", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a37813727b78798e53c2bec3f5e8fe12a6d6f8389bf9ca7802add4c9905ad8" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "serde" +version = "1.0.223" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a505d71960adde88e293da5cb5eda57093379f64e61cf77bf0e6a63af07a7bac" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.223" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20f57cbd357666aa7b3ac84a90b4ea328f1d4ddb6772b430caa5d9e1309bb9e9" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.223" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d428d07faf17e306e699ec1e91996e5a165ba5d6bce5b5155173e91a8a01a56" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", ] [[package]] name = "sherpa-rs" -version = "0.1.8-beta.3" +version = "0.6.6" dependencies = [ - "anyhow", "clap", - "cpal", "eyre", "hound", - "log", - "num-traits", "sherpa-rs-sys", + "tracing", ] [[package]] name = "sherpa-rs-sys" -version = "0.1.8-beta.3" +version = "0.6.6" dependencies = [ "bindgen", + "bzip2", "cmake", + "dirs", + "flate2", "glob", + "lazy_static", + "serde", + "serde_json", + "sha2", + "tar", + "ureq", ] [[package]] @@ -738,37 +935,88 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" -version = "2.0.70" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -776,118 +1024,132 @@ dependencies = [ ] [[package]] -name = "toml_datetime" -version = "0.6.8" +name = "tinystr" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] [[package]] -name = "toml_edit" -version = "0.21.1" +name = "tracing" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "indexmap", - "toml_datetime", - "winnow", + "pin-project-lite", + "tracing-attributes", + "tracing-core", ] [[package]] -name = "unicode-ident" -version = "1.0.12" +name = "tracing-attributes" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "utf8parse" -version = "0.2.2" +name = "tracing-core" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] [[package]] -name = "walkdir" -version = "2.5.0" +name = "typenum" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] -name = "wasm-bindgen" -version = "0.2.92" +name = "unicode-ident" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" dependencies = [ - "bumpalo", + "base64", "log", "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", + "rustls", + "rustls-pki-types", + "socks", + "url", + "webpki-roots 0.26.11", ] [[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" +name = "url" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", + "form_urlencoded", + "idna", + "percent-encoding", + "serde", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" +name = "utf8_iter" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "web-sys" -version = "0.3.69" +name = "webpki-roots" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "js-sys", - "wasm-bindgen", + "webpki-roots 1.0.2", +] + +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", ] [[package]] @@ -899,78 +1161,101 @@ dependencies = [ "either", "home", "once_cell", - "rustix", + "rustix 0.38.44", ] [[package]] -name = "winapi-util" -version = "0.1.9" +name = "winapi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "windows-sys 0.52.0", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] -name = "windows" -version = "0.54.0" +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +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.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-core", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] -name = "windows-core" -version = "0.54.0" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-result", "windows-targets 0.52.6", ] [[package]] -name = "windows-result" -version = "0.1.2" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.53.3", ] [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" dependencies = [ - "windows-targets 0.52.6", + "windows-link 0.2.0", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -982,18 +1267,35 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link 0.1.3", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" @@ -1001,11 +1303,17 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" @@ -1013,11 +1321,17 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" @@ -1025,17 +1339,29 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" @@ -1043,11 +1369,17 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" @@ -1055,11 +1387,17 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" @@ -1067,11 +1405,17 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" @@ -1080,10 +1424,107 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winnow" -version = "0.5.40" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "xattr" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" dependencies = [ - "memchr", + "libc", + "rustix 1.1.2", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index c7b50e2..c4a4556 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,42 +1,4 @@ -[package] -name = "sherpa-rs" -version = "0.1.8-beta.3" -edition = "2021" -authors = ["thewh1teagle"] -license = "MIT" -repository = "https://github.com/thewh1teagle/sherpa-rs" -description = "Rust bindings to https://github.com/k2-fsa/sherpa-onnx" -readme = "README.md" -keywords = [ - "audio", - "embeddings", - "speech-recognition", - "sherpa", - "diarization", -] - -[dependencies] -eyre = "0.6.12" -hound = { version = "3.5.1" } -log = "0.4.22" -num-traits = "0.2.19" -sherpa-rs-sys = { path = "sys", version = "0.1.8-beta.1" } - -[dev-dependencies] -clap = { version = "4.5.8", features = ["derive"] } -cpal = "0.15.3" -anyhow = "*" - [workspace] -members = ["sys"] - -[features] -default = [] -tts = ["sherpa-rs-sys/tts"] -cuda = ["sherpa-rs-sys/cuda"] -directml = ["sherpa-rs-sys/directml"] - - -[[example]] -name = "tts" -required-features = ["tts"] +resolver = "2" +members = ["crates/sherpa-rs", "crates/sherpa-rs-sys"] +exclude = ["examples/tauri-app/src-tauri"] diff --git a/README.md b/README.md index b609d30..a6bd2b9 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,21 @@ Rust bindings to [sherpa-onnx](https://github.com/k2-fsa/sherpa-onnx) - Spoken language detection - Speaker embedding (labeling) +- Speaker diarization - Speech to text - Text to speech +- Text punctuation - Voice activity detection +- Audio tagging +- Keyword spotting ## Supported Platforms - Windows - Linux - macOS +- Android +- IOS ## Install @@ -34,11 +40,20 @@ Please see [BUILDING.md](BUILDING.md). - `cuda`: enable CUDA support - `directml`: enable DirectML support - `tts`: enable TTS +- `download-binaries`: use prebuilt sherpa-onnx libraries for faster builds. cached. +- `static`: use static sherpa-onnx libraries and link them statically. +- `sys`: expose raw c bindings (sys crate) -## Docs +## Documentation -See [sherpa/intro.html](https://k2-fsa.github.io/sherpa/intro.html) +For the documentation on `sherpa_rs`, please visit [docs.rs/sherpa_rs](https://docs.rs/sherpa-rs/latest/sherpa_rs). + +For documentation on `sherpa-onnx`, refer to the [sherpa/intro.html](https://k2-fsa.github.io/sherpa/intro.html). ## Examples See [examples](examples) + +## Models + +All pretrained models available at [sherpa/onnx/pretrained_models](https://k2-fsa.github.io/sherpa/onnx/pretrained_models/index.html) diff --git a/sys/.gitignore b/crates/sherpa-rs-sys/.gitignore similarity index 100% rename from sys/.gitignore rename to crates/sherpa-rs-sys/.gitignore diff --git a/sys/Cargo.toml b/crates/sherpa-rs-sys/Cargo.toml similarity index 52% rename from sys/Cargo.toml rename to crates/sherpa-rs-sys/Cargo.toml index c517f0c..08b4877 100644 --- a/sys/Cargo.toml +++ b/crates/sherpa-rs-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sherpa-rs-sys" -version = "0.1.8-beta.3" +version = "0.6.6" edition = "2021" authors = ["thewh1teagle"] homepage = "https://github.com/thewh1teagle/sherpa-rs" @@ -27,18 +27,46 @@ include = [ "sherpa-onnx/CMakeLists.txt", "sherpa-onnx/LICENSE", "sherpa-onnx/.clang*", - "src/*.rs", + "src/", "build.rs", "wrapper.h", + "dist.json", + "checksum.txt", ] [build-dependencies] bindgen = "0.69.4" cmake = "0.1" glob = "0.3.1" +lazy_static = { version = "1.5.0" } + +# Download binaries +ureq = { version = "2.1", optional = true, default-features = false, features = [ + "tls", + "socks-proxy", +] } +tar = { version = "0.4", optional = true } +bzip2 = { version = "0.4.4", optional = true, features = ["static"] } +flate2 = { version = "1.0", optional = true } +sha2 = { version = "0.10", optional = true } +dirs = { version = "5.0.1", optional = true } +serde_json = { version = "1.0.134", optional = true } +serde = { version = "1.0.216", features = ["derive"], optional = true } + [features] -default = [] +default = ["download-binaries"] +download-binaries = [ + "dep:ureq", + "dep:tar", + "dep:bzip2", + "dep:flate2", + "dep:sha2", + "dep:dirs", + "dep:serde_json", + "dep:serde", +] +static = [] tts = [] cuda = [] directml = [] diff --git a/crates/sherpa-rs-sys/build.rs b/crates/sherpa-rs-sys/build.rs new file mode 100644 index 0000000..aafc0e5 --- /dev/null +++ b/crates/sherpa-rs-sys/build.rs @@ -0,0 +1,559 @@ +use cmake::Config; +use glob::glob; +use std::collections::HashMap; +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +#[path = "src/download_binaries.rs"] +#[cfg(feature = "download-binaries")] +mod download_binaries; + +macro_rules! debug_log { + ($($arg:tt)*) => { + // SHERPA_BUILD_DEBUG=1 cargo build + if std::env::var("SHERPA_BUILD_DEBUG").unwrap_or_default() == "1" { + println!("cargo:warning=[DEBUG] {}", format!($($arg)*)); + } + }; +} + +lazy_static::lazy_static! { + // clang --print-targets + // rustc --print target-list + static ref RUST_CLANG_TARGET_MAP: HashMap = { + let mut m = HashMap::new(); + m.insert("aarch64-linux-android".to_string(), "armv8-linux-androideabi".to_string()); + m.insert("aarch64-apple-ios-sim".to_string(), "arm64-apple-ios-simulator".to_string()); + m + }; +} + +fn link_lib(lib: &str, is_dynamic: bool) { + let lib_kind = if is_dynamic { "dylib" } else { "static" }; + debug_log!("cargo:rustc-link-lib={}={}", lib_kind, lib); + println!("cargo:rustc-link-lib={}={}", lib_kind, lib); +} + +fn link_framework(framework: &str) { + debug_log!("cargo:rustc-link-lib=framework={}", framework); + println!("cargo:rustc-link-lib=framework={}", framework); +} + +fn add_search_path>(path: P) { + debug_log!("cargo:rustc-link-search={}", path.as_ref().display()); + println!("cargo:rustc-link-search={}", path.as_ref().display()); +} + +fn copy_file(src: PathBuf, dst: PathBuf) { + if let Err(err) = std::fs::hard_link(&src, &dst) { + debug_log!("Failed to hardlink {:?}. fallback to copy.", err); + fs::copy(&src, &dst) + .unwrap_or_else(|_| panic!("Failed to copy {} to {}", src.display(), dst.display())); + } +} + +fn get_cargo_target_dir() -> Result> { + let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR")?); + let profile = std::env::var("PROFILE")?; + let mut target_dir = None; + let mut sub_path = out_dir.as_path(); + while let Some(parent) = sub_path.parent() { + if parent.ends_with(&profile) { + target_dir = Some(parent); + break; + } + sub_path = parent; + } + let target_dir = target_dir.ok_or("not found")?; + Ok(target_dir.to_path_buf()) +} + +fn delete_folder(src: &Path) -> std::io::Result<()> { + if src.exists() { + fs::remove_dir_all(src)?; + } + Ok(()) +} + +fn copy_folder(src: &Path, dst: &Path) { + std::fs::create_dir_all(dst).expect("Failed to create dst directory"); + if cfg!(windows) { + std::process::Command::new("robocopy.exe") + .arg("/e") + .arg(src) + .arg(dst) + .status() + .expect("Failed to execute robocopy command"); + } else { + std::process::Command::new("cp") + .arg("-rf") + .arg(src) + .arg(dst.parent().unwrap()) + .status() + .expect("Failed to execute cp command"); + } +} + +fn extract_lib_names(out_dir: &Path, is_dynamic: bool, target_os: &str) -> Vec { + let lib_pattern = if target_os == "windows" { + "*.lib" + } else if target_os == "macos" { + if is_dynamic { + "*.dylib" + } else { + "*.a" + } + } else if + // Linux, Android + is_dynamic { + "*.so" + } else { + "*.a" + }; + + let libs_dir = out_dir.join("lib"); + let pattern = libs_dir.join(lib_pattern); + debug_log!("Extract libs {}", pattern.display()); + + let mut lib_names: Vec = Vec::new(); + + // Process the libraries based on the pattern + for entry in glob(pattern.to_str().unwrap()).unwrap() { + match entry { + Ok(path) => { + let stem = path.file_stem().unwrap(); + let stem_str = stem.to_str().unwrap(); + + // Remove the "lib" prefix if present + let lib_name = if stem_str.starts_with("lib") { + stem_str.strip_prefix("lib").unwrap_or(stem_str) + } else { + stem_str + }; + lib_names.push(lib_name.to_string()); + } + Err(e) => println!("cargo:warning=error={}", e), + } + } + lib_names +} + +fn extract_lib_assets(out_dir: &Path, target_os: &str) -> Vec { + let shared_lib_pattern = if target_os == "windows" { + "*.dll" + } else if target_os == "macos" { + "*.dylib" + } else { + "*.so" + }; + + let libs_dir = out_dir.join("lib"); + let pattern = libs_dir.join(shared_lib_pattern); + debug_log!("Extract lib assets {}", pattern.display()); + let mut files = Vec::new(); + + for entry in glob(pattern.to_str().unwrap()).unwrap() { + match entry { + Ok(path) => { + files.push(path); + } + Err(e) => eprintln!("cargo:warning=error={}", e), + } + } + + files +} + +fn macos_link_search_path() -> Option { + let output = Command::new("clang") + .arg("--print-search-dirs") + .output() + .ok()?; + if !output.status.success() { + println!( + "failed to run 'clang --print-search-dirs', continuing without a link search path" + ); + return None; + } + + let stdout = String::from_utf8_lossy(&output.stdout); + for line in stdout.lines() { + if line.contains("libraries: =") { + let path = line.split('=').nth(1)?; + return Some(format!("{}/lib/darwin", path)); + } + } + + println!("failed to determine link search path, continuing without it"); + None +} + +fn rerun_on_env_changes(vars: &[&str]) { + for env in vars { + println!("cargo::rerun-if-env-changed={}", env); + } +} + +fn rerun_if_changed(vars: &[&str]) { + for var in vars { + println!("cargo:rerun-if-changed={}", var); + } +} + +fn main() { + rerun_if_changed(&["wrapper.h", "dist.json", "checksum.txt", "./sherpa-onnx"]); + rerun_on_env_changes(&[ + "SHERPA_BUILD_SHARED_LIBS", + "CMAKE_BUILD_PARALLEL_LEVEL", + "CMAKE_VERBOSE", + "SHERPA_LIB_PATH", + "SHERPA_STATIC_CRT", + "SHERPA_LIB_PROFILE", + "BUILD_DEBUG", + ]); + + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); + debug_log!("target_os = {}", target_os); + + // Show warning if static enabled on Linux without RUSTFLAGS + #[cfg(all( + feature = "static", + target_os = "linux", + target_arch = "x86_64", + feature = "download-binaries" + ))] + { + if !env::var("RUSTFLAGS") + .unwrap_or_default() + .contains("relocation-model=dynamic-no-pic") + { + panic!( + "cargo:warning=\ + Please enable the following environment variable when static feature enabled on Linux: RUSTFLAGS=\"-C relocation-model=dynamic-no-pic\"" + ) + } + } + + let target = env::var("TARGET").unwrap(); + let is_mobile = target.contains("android") || target.contains("ios"); + debug_log!("TARGET: {:?}", target); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + let target_dir = get_cargo_target_dir().unwrap(); + let sherpa_dst = out_dir.join("sherpa-onnx"); + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("Failed to get CARGO_MANIFEST_DIR"); + let sherpa_src = Path::new(&manifest_dir).join("sherpa-onnx"); + + // Dynamic by default + #[allow(unused_mut)] + let mut is_dynamic = if cfg!(feature = "static") { + false // Static feature is enabled + } else if cfg!(any(feature = "directml", feature = "cuda")) { + true // DirectML or CUDA feature is enabled + } else if let Ok(val) = env::var("SHERPA_BUILD_SHARED_LIBS") { + val == "1" // Environment variable determines dynamic state + } else { + true // Default to true + }; + + debug_log!("TARGET: {}", target); + debug_log!("CARGO_MANIFEST_DIR: {}", manifest_dir); + debug_log!("TARGET_DIR: {}", target_dir.display()); + debug_log!("OUT_DIR: {}", out_dir.display()); + + // Prepare sherpa-onnx source + if !sherpa_dst.exists() { + debug_log!("Copy {} to {}", sherpa_src.display(), sherpa_dst.display()); + delete_folder(&sherpa_src.join("scripts")).unwrap(); + copy_folder(&sherpa_src, &sherpa_dst); + } + // Speed up build + env::set_var( + "CMAKE_BUILD_PARALLEL_LEVEL", + std::thread::available_parallelism() + .unwrap() + .get() + .to_string(), + ); + + // Bindings + if env::var("SHERPA_SKIP_GENERATE_BINDINGS").is_ok() { + debug_log!("Skip generate bindings"); + std::fs::copy("src/bindings.rs", out_dir.join("bindings.rs")) + .expect("Failed to copy bindings.rs"); + } else { + let mut bindings_builder = bindgen::Builder::default() + .header("wrapper.h") + .clang_arg(format!("-I{}", sherpa_dst.display())) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); + + if let Some(clang_target) = RUST_CLANG_TARGET_MAP.get(&target) { + // Explicitly set target in case we are cross-compiling. + // See https://github.com/rust-lang/rust-bindgen/issues/1780 for context. + debug_log!("mapped clang target: {}", clang_target); + bindings_builder = bindings_builder.clang_arg(format!("--target={}", clang_target)); + } + + debug_log!("Generating bindings..."); + let bindings_builder = bindings_builder + .generate() + .expect("Failed to generate bindings"); + + // Write the generated bindings to an output file + let bindings_path = out_dir.join("bindings.rs"); + + debug_log!("Writing bindings to {:?}", bindings_path); + bindings_builder + .write_to_file(bindings_path) + .expect("Failed to write bindings"); + debug_log!("Bindings Created"); + } + + // Skip build when docs.rs website built this crate + // Only build the bindings.rs file. + if env::var("DOCS_RS") == Ok("1".to_string()) { + println!("cargo:warning=Detected DOCS_RS. Skipping build / fetch."); + return; + } + + #[cfg(feature = "download-binaries")] + let mut optional_dist: Option = None; + + let mut sherpa_libs: Vec = Vec::new(); + + #[cfg(feature = "download-binaries")] + { + // Download libraries, cache and set SHERPA_LIB_PATH + use download_binaries::{extract_tbz, fetch_file, get_cache_dir, sha256, DIST_TABLE}; + debug_log!("Download binaries enabled"); + // debug_log!("Dist table: {:?}", DIST_TABLE.targets); + // Try download sherpa libs and set SHERPA_LIB_PATH + if let Some(dist) = DIST_TABLE.get(&target, &mut is_dynamic) { + debug_log!("is_dynamic after: {}", is_dynamic); + optional_dist = Some(dist.clone()); + let mut cache_dir = if let Some(dir) = get_cache_dir() { + dir.join(target.clone()).join(&dist.checksum) + } else { + println!("cargo:warning=Could not determine cache directory, using OUT_DIR"); + PathBuf::from(env::var("OUT_DIR").unwrap()) + }; + if fs::create_dir_all(&cache_dir).is_err() { + println!("cargo:warning=Could not create cache directory, using OUT_DIR"); + cache_dir = env::var("OUT_DIR").unwrap().into(); + } + debug_log!("Cache dir: {}", cache_dir.display()); + + let lib_dir = cache_dir.join(&dist.name); + + // if is mobile then check if cache dir not empty + // Sherpa uses special directory structure for mobile + let cache_dir_empty = cache_dir + .read_dir() + .map(|mut entries| entries.next().is_none()) + .unwrap_or(true); + + if (is_mobile && cache_dir_empty) || (!is_mobile && !lib_dir.exists()) { + let downloaded_file = fetch_file(&dist.url); + let hash = sha256(&downloaded_file); + // verify checksum + assert_eq!( + hash, dist.checksum, + "Checksum mismatch: {} != {}", + hash, dist.checksum + ); + + extract_tbz(&downloaded_file, &cache_dir); + } else { + debug_log!("Skip fetch file. Using cache from {}", lib_dir.display()); + } + + // In Android, we need to set SHERPA_LIB_PATH to the cache directory sincie it has jniLibs + if is_mobile { + env::set_var("SHERPA_LIB_PATH", &cache_dir); + } else { + env::set_var("SHERPA_LIB_PATH", cache_dir.join(&dist.name)); + } + + debug_log!("dist libs: {:?}", dist.libs); + if let Some(libs) = dist.libs { + for lib in libs.iter() { + let lib_path = cache_dir.join(lib); + let lib_parent = lib_path.parent().unwrap(); + add_search_path(lib_parent); + } + + sherpa_libs = libs + .iter() + .map(download_binaries::extract_lib_name) + .collect(); + } else { + sherpa_libs = extract_lib_names(&lib_dir, is_dynamic, &target_os); + } + } else { + println!("cargo:warning=Failed to download binaries. fallback to manual build."); + } + } + + if let Ok(sherpa_lib_path) = env::var("SHERPA_LIB_PATH") { + // Skip build if SHERPA_LIB_PATH specified + debug_log!("Skpping build with Cmake..."); + debug_log!("SHERPA_LIB_PATH: {}", sherpa_lib_path); + add_search_path(Path::new(&sherpa_lib_path).join("lib")); + if sherpa_libs.is_empty() { + sherpa_libs = extract_lib_names(Path::new(&sherpa_lib_path), is_dynamic, &target_os); + } + } else { + // Build with CMake + let profile = env::var("SHERPA_LIB_PROFILE").unwrap_or("Release".to_string()); + let static_crt = env::var("SHERPA_STATIC_CRT") + .map(|v| v == "1") + .unwrap_or(true); + let mut config = Config::new(&sherpa_dst); + + config.define("CMAKE_POLICY_VERSION_MINIMUM", "3.5"); + + config + .define("SHERPA_ONNX_ENABLE_C_API", "ON") + .define("SHERPA_ONNX_ENABLE_BINARY", "OFF") + .define("BUILD_SHARED_LIBS", if is_dynamic { "ON" } else { "OFF" }) + .define("SHERPA_ONNX_ENABLE_WEBSOCKET", "OFF") + .define("SHERPA_ONNX_ENABLE_TTS", "OFF") + .define("SHERPA_ONNX_BUILD_C_API_EXAMPLES", "OFF"); + + if target_os == "windows" { + config.static_crt(static_crt); + } + + // TTS + if cfg!(feature = "tts") { + config.define("SHERPA_ONNX_ENABLE_TTS", "ON"); + } + + // Cuda https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html + if cfg!(feature = "cuda") { + debug_log!("Cuda enabled"); + config.define("SHERPA_ONNX_ENABLE_GPU", "ON"); + config.define("BUILD_SHARED_LIBS", "ON"); + } + + // DirectML https://onnxruntime.ai/docs/execution-providers/DirectML-ExecutionProvider.html + if cfg!(feature = "directml") { + debug_log!("DirectML enabled"); + config.define("SHERPA_ONNX_ENABLE_DIRECTML", "ON"); + config.define("BUILD_SHARED_LIBS", "ON"); + } + + if target_os == "windows" || target_os == "linux" || target == "android" { + config.define("SHERPA_ONNX_ENABLE_PORTAUDIO", "ON"); + } + + // General + config + .profile(&profile) + .very_verbose(std::env::var("CMAKE_VERBOSE").is_ok()) // Not verbose by default + .always_configure(false); + + let build_dir = config.build(); + add_search_path(&build_dir); + + // Extract libs on desktop platforms + if !is_mobile { + sherpa_libs = extract_lib_names(&build_dir, is_dynamic, &target_os); + } + } + + // Linking + + debug_log!("Sherpa libs: {:?}", sherpa_libs); + add_search_path(out_dir.join("lib")); + + for lib in sherpa_libs { + if lib.contains("cxx") { + continue; + } + link_lib(&lib, is_dynamic); + } + + // Windows debug + if cfg!(all(debug_assertions, windows)) { + link_lib("msvcrtd", true); + } + + // macOS + if target_os == "macos" || target_os == "ios" { + link_framework("CoreML"); + link_framework("Foundation"); + link_lib("c++", true); + } + + // Linux + if target_os == "linux" || target == "android" { + link_lib("stdc++", true); + } + + // macOS + if target_os == "macos" { + // On (older) OSX we need to link against the clang runtime, + // which is hidden in some non-default path. + // + // More details at https://github.com/alexcrichton/curl-rust/issues/279. + if let Some(path) = macos_link_search_path() { + add_search_path(path); + link_lib("clang_rt.osx", is_dynamic); + } + } + + // Copy dynamic libraries + + if is_dynamic { + let mut libs_assets = extract_lib_assets(&out_dir, &target_os); + if let Ok(sherpa_lib_path) = env::var("SHERPA_LIB_PATH") { + libs_assets.extend(extract_lib_assets(Path::new(&sherpa_lib_path), &target_os)); + } + + #[cfg(feature = "download-binaries")] + if let Some(dist) = optional_dist { + if let Some(assets) = dist.libs { + if let Ok(sherpa_lib_path) = env::var("SHERPA_LIB_PATH") { + let sherpa_lib_path = Path::new(&sherpa_lib_path); + libs_assets.extend(assets.iter().map(|p| sherpa_lib_path.join(p))); + } + } + } + + for asset in libs_assets { + let asset_clone = asset.clone(); + let filename = asset_clone.file_name().unwrap(); + let filename = filename.to_str().unwrap(); + let dst = target_dir.join(filename); + // debug_log!("HARD LINK {} TO {}", asset.display(), dst.display()); + if !dst.exists() { + copy_file(asset.clone(), dst); + } + + // Copy DLLs to examples as well + if target_dir.join("examples").exists() { + let dst = target_dir.join("examples").join(filename); + // debug_log!("HARD LINK {} TO {}", asset.display(), dst.display()); + if !dst.exists() { + copy_file(asset.clone(), dst); + } + } + + // Copy DLLs to target/profile/deps as well for tests + let dst = target_dir.join("deps").join(filename); + // debug_log!("HARD LINK {} TO {}", asset.display(), dst.display()); + if !dst.exists() { + copy_file(asset.clone(), dst); + } + } + + // TODO: add rpath for Android and iOS so it can find its dependencies in the same directory of executable + // if is_mobile { + // // Add rpath for Android and iOS so that the shared library can find its dependencies in the same directory as well + // println!("cargo:rustc-link-arg=-Wl,-rpath,'$ORIGIN'"); + // } + } +} diff --git a/crates/sherpa-rs-sys/checksum.txt b/crates/sherpa-rs-sys/checksum.txt new file mode 100644 index 0000000..ceb0e91 --- /dev/null +++ b/crates/sherpa-rs-sys/checksum.txt @@ -0,0 +1,82 @@ +sherpa-onnx-1.11.5-rknn.aar c9177e475f85e61922ced3d10277cb261a985c75007307cd01df39423710bce0 +sherpa-onnx-1.11.5.aar 1fd9dfefb19df233f868553e872e98b8bd636ac428c0a0b709bc1405bc7eafa0 +sherpa-onnx-non-streaming-asr-x64-v1.11.5.exe c049394c10beca44de540b3da6c81618b2c198efe53d87c471145c429294ec1c +sherpa-onnx-non-streaming-asr-x86-v1.11.5.exe f72fb5ed449e90455a4de5b2c1548b5416daa117e067fc5a11ddf7d8a1306fc6 +sherpa-onnx-non-streaming-tts-x64-v1.11.5.exe 904c920fe6b500a455ac48832d1689d966a8bcf0b5eed26e62ce1bc6ba4a9df5 +sherpa-onnx-non-streaming-tts-x86-v1.11.5.exe 7c42aa3df603fe6186cb96c5301ab70d7bfbd0d4f9ef7ccb95f77ab33e89471c +sherpa-onnx-static-link-onnxruntime-1.11.5.aar 5701bf4e6d4ff16c72cc58fd606e9d27e43ed6d72919216199451ddf673c78a4 +sherpa-onnx-streaming-asr-x64-v1.11.5.exe a0b6cfb44e9fde8cd78e516c59d59c1f7ac0bc82bfe76cca497452e218ca9a3d +sherpa-onnx-streaming-asr-x86-v1.11.5.exe 4cba9b3a20abd87c5422869411c53a722622cdd82e768587b653f6b01a8f7460 +sherpa-onnx-v1.11.5-android-rknn.tar.bz2 4da67093851a44991eac90b1a1e6519db51ea62dd796cad3e1669a1880f24c14 +sherpa-onnx-v1.11.5-android-static-link-onnxruntime.tar.bz2 7e7f7d91617fa77361eb2fbfd9f90aa5d9b7a73be8764e64f6c0b90bcd71af34 +sherpa-onnx-v1.11.5-android.tar.bz2 76e21f461f9527d7ba46c0deb2dec035aaa87e78b8cb46ade883ac9d4d9b853b +sherpa-onnx-v1.11.5-ios-no-tts.tar.bz2 84533a2d9287907178b68d0aeb0ae6939d4a109f34675d46655f2ff6d6185dc7 +sherpa-onnx-v1.11.5-ios.tar.bz2 7daf9111f30200416e504428ef457a9f60009ae67a2bd4e3bfb829f5d096fab5 +sherpa-onnx-v1.11.5-java11.jar 7b86e00abc8667d606bc94f47a954ebb9359fc7f764a227b644de3f3962a85ff +sherpa-onnx-v1.11.5-java16.jar fe844835b8df5bb4e2973d12b172779ff961b3944fa6caed63fde3cbfd8d500c +sherpa-onnx-v1.11.5-java17.jar 46826ae8972cb98a72e9437a2d31ca12c715ce31786e31a525d9db8b1b0265a7 +sherpa-onnx-v1.11.5-java18.jar 90504699e7e6a91dc3c2552c16759127d95f71ef3caf7c0c11b804a51e772cab +sherpa-onnx-v1.11.5-java19.jar a1f4d27f77a1720328b0ce7aa149759b9dabe2ca2ccba52fc0d0cc3857e7c2e9 +sherpa-onnx-v1.11.5-java20.jar af3e213d29fc36ba2428d275aa7c6f5a8aeae9bbf794101397a1c240ccf30a54 +sherpa-onnx-v1.11.5-java21.jar 5a89cb1a48160703e78d4d66f4e61404c48493d1507f1529b0c1a67f7d472075 +sherpa-onnx-v1.11.5-java22.jar a6c49e60cf15efb341059593668d3fa34d42e03b695f4dfcc838cee196ca400c +sherpa-onnx-v1.11.5-java23.jar 6dd7e9610c5ded573432e461a807ede2672714c9cca1982bf5e57827d7dc74f0 +sherpa-onnx-v1.11.5-java8.jar 3043da43f887f4ecea1aff05df70ea2b31a92d23a90131a3ea1165b35f3c12be +sherpa-onnx-v1.11.5-linux-aarch64-jni.tar.bz2 12faa7a6fdf25748388713ea7e1bb8c9664372a6291a334a4a9ec4ea5f02f211 +sherpa-onnx-v1.11.5-linux-aarch64-shared-cpu.tar.bz2 18403204bd3dfbe303cc7785ff82449daaaf964ca4fa67ec94fa7be71308173d +sherpa-onnx-v1.11.5-linux-aarch64-shared-gpu-onnxruntime-1.11.0.tar.bz2 f607b4e5fc6e02d08776ec4a1d4756aa6b8df2d98945c4b20450cf09e3ffa591 +sherpa-onnx-v1.11.5-linux-aarch64-shared-gpu-onnxruntime-1.16.0.tar.bz2 dea5c525647001f73262323cb048516010cd867404061a278b554a408dc82e25 +sherpa-onnx-v1.11.5-linux-aarch64-shared-gpu-onnxruntime-1.18.0.tar.bz2 9f0817095111f5edfab109a82967116ef15fad3076fabbe982a1eba74de135a5 +sherpa-onnx-v1.11.5-linux-aarch64-shared-gpu-onnxruntime-1.18.1.tar.bz2 a3493baaff5e4ef6d9c3413aadc9b5670df8612db7155ec53c292e9449ddd77e +sherpa-onnx-v1.11.5-linux-aarch64-static.tar.bz2 818a78fd2c7b92df09c65a806087679368bb58df49d344172cf113ad95f2dcd1 +sherpa-onnx-v1.11.5-linux-riscv64-shared.tar.bz2 d5a02212c3b43f98682e7c088691acba23e1fa3ba4f86e186aeeaa525e3eaee6 +sherpa-onnx-v1.11.5-linux-x64-gpu.tar.bz2 01844c0534883cb9b4ae032f6b4500ecc9ce5f2031d5e9459fbdada9d1f6b8e9 +sherpa-onnx-v1.11.5-linux-x64-jni.tar.bz2 a052bcf0b795ee2c6f36e65adbf9acaddbe595f95723708765b1435e372e5a79 +sherpa-onnx-v1.11.5-linux-x64-shared-no-tts.tar.bz2 96d790688fbb422f6073b61a6298709e3a54ec94978450037c4ef2adacbd0bc2 +sherpa-onnx-v1.11.5-linux-x64-shared.tar.bz2 596962a9ca9b839fd80587cd20d859c2262132e08471a27406a1105635dca652 +sherpa-onnx-v1.11.5-linux-x64-static-no-tts.tar.bz2 4e16de788d2ce79ab576d97adabf4c67981deed0ac35af0860eb120f710b28a6 +sherpa-onnx-v1.11.5-linux-x64-static.tar.bz2 c53d52615101b09610871ef66e34efc11e611b7d9b2ddd81468382b7efa72d16 +sherpa-onnx-v1.11.5-macos-xcframework-static.tar.bz2 fdff377597fb5ac0ef124b19a807bdcd512c44df2352942dd388f2d3b28210a4 +sherpa-onnx-v1.11.5-ohos-arm64-v8a.tar.bz2 3418b81d7dda64aa6fcb9212bf7240d132a102edfad059407ca9a316c2875039 +sherpa-onnx-v1.11.5-ohos-armeabi-v7a.tar.bz2 09d8195b6aa2a69b60f9a3ee61060822756a2af09371b7b6c719400ebf65be7c +sherpa-onnx-v1.11.5-ohos-x86_64.tar.bz2 261b35d9b16772198487492e0d87bfcfb2cdc26abac166f9879a731d547c2ca1 +sherpa-onnx-v1.11.5-osx-arm64-jni.tar.bz2 e7e62cada2fd5510f114a191367a33de689a63f6aeb40ef0e652aca5b92c5c87 +sherpa-onnx-v1.11.5-osx-universal2-shared-no-tts.tar.bz2 5cf8ea5658751577b0b79f66a6969cb74efe66b8c4fb2ce4eb8341ccbfc78c76 +sherpa-onnx-v1.11.5-osx-universal2-shared.tar.bz2 741cd84e42b5615f395b40510d9a12a73cb89cb38b3e540e1c11bc0eb5e92c29 +sherpa-onnx-v1.11.5-osx-universal2-static-no-tts.tar.bz2 433d03ac9240bb1091e82c10de4cc3c15f9031e7eeea85dbebb06c6681440b24 +sherpa-onnx-v1.11.5-osx-universal2-static.tar.bz2 24ea1b1a0205c8408e22ee5459596aa2e46e32512cac2ec3075ee489039d2fd3 +sherpa-onnx-v1.11.5-osx-x86_64-jni.tar.bz2 ed90b3a5a0fa561c2ccd1491b9ddabd5fc03ce9b145669c3705d4e542936e97f +sherpa-onnx-v1.11.5-rknn-linux-aarch64-shared.tar.bz2 44d6999f8ff1df4aa868f1678d72bfbb0949b8c2377f8a998d236127e4f6cf5e +sherpa-onnx-v1.11.5-rknn-linux-aarch64-static.tar.bz2 077cd6bb55c47c2ab527bbaa54485b5e9f7b076f1f49b200d4c62456365f4164 +sherpa-onnx-v1.11.5-win-x64-cuda.tar.bz2 f05b5062659420b41200515dfaad2ec68eb1b7a985bfabf41018cbb71b559094 +sherpa-onnx-v1.11.5-win-x64-jni.tar.bz2 b54bc561b496c374a00a0e5ccfecca7de7222fa5d1ea641d8c11445b82a46373 +sherpa-onnx-v1.11.5-win-x64-shared-no-tts.tar.bz2 efe8a46ce1ca7f93b9c4343ac953bb2fad9aea99fb3f7d0d7b71e8a4139a2f89 +sherpa-onnx-v1.11.5-win-x64-shared.tar.bz2 0c3955581cad3e8267968f83adb816536ef3002a09d20c7a3bcc57df1770a73f +sherpa-onnx-v1.11.5-win-x64-static-no-tts.tar.bz2 114b9f0f5a99c07126f0d50cccbb73474ff4ca7c5204bd7a18ed1d3b19dbc3b9 +sherpa-onnx-v1.11.5-win-x64-static.tar.bz2 b403f3abab3d9122c0253163d41e13b2c37453635fa0182bce4fded284b374a7 +sherpa-onnx-v1.11.5-win-x86-shared-no-tts.tar.bz2 9b7f2b6a7796065cc43e61483765219d6baede2692f4f1bdd049a1be002268c4 +sherpa-onnx-v1.11.5-win-x86-shared.tar.bz2 7c067fae12f55bcd06ca4abd371041b49a9200fb5179587e393d1cf6af6f6e3b +sherpa-onnx-v1.11.5-win-x86-static-no-tts.tar.bz2 5b82c8304d208704e913fba70ce4a770dacdeee5b812ac7f59e3fc21d0bd458d +sherpa-onnx-v1.11.5-win-x86-static.tar.bz2 7a5c15d68d1aaa978c18e0fa844055d75e97d36ef580e760c321232a1c36f161 +sherpa-onnx-v1.11.5.jar 6dd7e9610c5ded573432e461a807ede2672714c9cca1982bf5e57827d7dc74f0 +sherpa-onnx-wasm-simd-1.11.5-vad-asr-en-moonshine_tiny.tar.bz2 b77beb68216dbadc9a5a11fac17b34770727c7f879a268aa2273901ec4dc3d5f +sherpa-onnx-wasm-simd-1.11.5-vad-asr-en-whisper_tiny.tar.bz2 b8e76f841f5467b49e27f155f7dcc1658cd0db168016173b8d6c0ed8a371af0b +sherpa-onnx-wasm-simd-1.11.5-vad-asr-en-zipformer_gigaspeech.tar.bz2 17f7bbd154c41a130ce198a293684db1a8eb23a6641cdab55385675dd6f2245b +sherpa-onnx-wasm-simd-1.11.5-vad-asr-ja-zipformer_reazonspeech.tar.bz2 1ec8ad2a8429c6c79b4c68cfa60d7068e64a88c715f2058225ad23374f9ff7cc +sherpa-onnx-wasm-simd-1.11.5-vad-asr-multi_lang-dolphin_ctc.tar.bz2 558635e721d990cb3fa348f168e914a65832a17e1a15cd9c76299eb913c554c6 +sherpa-onnx-wasm-simd-1.11.5-vad-asr-th-zipformer_gigaspeech2.tar.bz2 1edcb15d712b38de04c8afe94df4d9bcf6a72f328ce22b11d5aeb3995b4fc768 +sherpa-onnx-wasm-simd-1.11.5-vad-asr-zh-telespeech.tar.bz2 cc94e3a228b27dddef6976cfd267a5a6a7a65aeeb303035b5f6e7e3400cf656a +sherpa-onnx-wasm-simd-1.11.5-vad-asr-zh-zipformer_wenetspeech.tar.bz2 eb4ebff87c4f84b5c2e006af8b28d7f903060efd4a0e32ded54c72fd0a7c2dad +sherpa-onnx-wasm-simd-1.11.5-vad-asr-zh_en-paraformer_large.tar.bz2 f140f95040aeab96f2afd5eb6156c2ab3ab953a204348a8bcea07cee5e941cec +sherpa-onnx-wasm-simd-1.11.5-vad-asr-zh_en-paraformer_small.tar.bz2 04e7109dc4c9fbec3b3f7f5afe9fa9e3324abd602acdf0e3a27e9e01ecde2f98 +sherpa-onnx-wasm-simd-1.11.5-vad-asr-zh_en_ja_ko_cantonese-sense_voice_small.tar.bz2 715d836bf52dc57ab12dee1c5e7354fa1c41e0da2c1c38bdbff97679d1a84d6e +sherpa-onnx-wasm-simd-v1.11.5-de-tts.tar.bz2 7dae01ffb67ecd899d1a17b3050e1301d9c7b2e97f44e974f4624e9e0d08d9c8 +sherpa-onnx-wasm-simd-v1.11.5-en-asr-zipformer.tar.bz2 2f6dae1df1f6d41e30a74a4931e724b6d4f3da548f0dbef18df9ac8aaaf1c8bd +sherpa-onnx-wasm-simd-v1.11.5-en-tts.tar.bz2 6887019933848c143fb28ec1c09532f3418b3e497e801e04613e1f4faf22f16d +sherpa-onnx-wasm-simd-v1.11.5-speaker-diarization.tar.bz2 45cc511cfcf334544d159399f149581c955a96cac9b2624888b1871657c9f819 +sherpa-onnx-wasm-simd-v1.11.5-speech-enhancement-gtcrn.tar.bz2 a231a75a2cb4f8b91c0d698b0bd7c7854c14056865affaf359064edda48e00c4 +sherpa-onnx-wasm-simd-v1.11.5-vad.tar.bz2 ca626fcf30fa6235324ef27daa4166359f925f4eb455182cf04c226f1d123b14 +sherpa-onnx-wasm-simd-v1.11.5-zh-cantonese-en-asr-paraformer.tar.bz2 96d056f3248702569833e641233e43cc32cff3e319c95e7f67f2165d6c63002c +sherpa-onnx-wasm-simd-v1.11.5-zh-en-asr-paraformer.tar.bz2 305be043713097b1a475f1f7c0df005c1dece026c9e1347b4028919579e39eab +sherpa-onnx-wasm-simd-v1.11.5-zh-en-asr-zipformer.tar.bz2 435e695b1df8e1fbee4a4144190876770bb3dbb6bca6137730de9040019a4148 +sherpa_onnx-v1.11.5.har 3c85d9feaa004c7d2ff0c1fc49f8c8ac37142c51c34cad79aa95efc631a3d361 diff --git a/crates/sherpa-rs-sys/dist.json b/crates/sherpa-rs-sys/dist.json new file mode 100644 index 0000000..201aed7 --- /dev/null +++ b/crates/sherpa-rs-sys/dist.json @@ -0,0 +1,58 @@ +{ + "tag": "v1.11.5", + "url": "https://github.com/k2-fsa/sherpa-onnx/releases/download/{tag}/{archive}", + "targets": { + "x86_64-pc-windows-msvc": { + "static": "sherpa-onnx-{tag}-win-x64-static.tar.bz2", + "dynamic": "sherpa-onnx-{tag}-win-x64-shared.tar.bz2" + }, + "x86_64-unknown-linux-gnu": { + "static": "sherpa-onnx-{tag}-linux-x64-static.tar.bz2", + "dynamic": "sherpa-onnx-{tag}-linux-x64-shared.tar.bz2" + }, + "aarch64-apple-darwin": { + "static": "sherpa-onnx-{tag}-osx-universal2-static.tar.bz2", + "dynamic": "sherpa-onnx-{tag}-osx-universal2-shared.tar.bz2" + }, + "x86_64-apple-darwin": { + "static": "sherpa-onnx-{tag}-osx-universal2-static.tar.bz2", + "dynamic": "sherpa-onnx-{tag}-osx-universal2-shared.tar.bz2" + }, + "android": { + "archive": "sherpa-onnx-{tag}-android.tar.bz2", + "is_dynamic": true, + "targets": { + "aarch64-linux-android": [ + "jniLibs/arm64-v8a/libsherpa-onnx-c-api.so", + "jniLibs/arm64-v8a/libonnxruntime.so" + ], + "x86_64-linux-android": [ + "jniLibs/x86_64/libsherpa-onnx-c-api.so", + "jniLibs/x86_64/libonnxruntime.so" + ], + "armv7-linux-androideabi": [ + "jniLibs/armeabi-v7a/libsherpa-onnx-c-api.so", + "jniLibs/armeabi-v7a/libonnxruntime.so" + ] + } + }, + "ios": { + "archive": "sherpa-onnx-{tag}-ios.tar.bz2", + "is_dynamic": false, + "targets": { + "aarch64-apple-ios": [ + "build-ios/ios-onnxruntime/1.17.1/onnxruntime.xcframework/ios-arm64/libonnxruntime.a", + "build-ios/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a" + ], + "aarch64-apple-ios-sim": [ + "build-ios/ios-onnxruntime/1.17.1/onnxruntime.xcframework/ios-arm64_x86_64-simulator/libonnxruntime.a", + "build-ios/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a" + ], + "x86_64-apple-ios": [ + "build-ios/ios-onnxruntime/1.17.1/onnxruntime.xcframework/ios-arm64_x86_64-simulator/libonnxruntime.a", + "build-ios/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a" + ] + } + } + } +} \ No newline at end of file diff --git a/crates/sherpa-rs-sys/sherpa-onnx b/crates/sherpa-rs-sys/sherpa-onnx new file mode 160000 index 0000000..baec2da --- /dev/null +++ b/crates/sherpa-rs-sys/sherpa-onnx @@ -0,0 +1 @@ +Subproject commit baec2da74524697f5c88ea6063d0c6fc06a72d36 diff --git a/crates/sherpa-rs-sys/src/download_binaries.rs b/crates/sherpa-rs-sys/src/download_binaries.rs new file mode 100644 index 0000000..41af8bd --- /dev/null +++ b/crates/sherpa-rs-sys/src/download_binaries.rs @@ -0,0 +1,258 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; + +use serde::Deserialize; +use serde_json::Value; + +// Prebuilt sherpa-onnx doesn't have Cuda support +#[cfg(all( + any(target_os = "windows", target_os = "linux"), + feature = "download-binaries", + feature = "cuda" +))] +compile_error!( + "The 'download-binaries' and 'cuda' features cannot be enabled at the same time.\n\ + To resolve this, please disable the 'download-binaries' feature when using 'cuda'.\n\ + For example, in your Cargo.toml:\n\ + [dependencies]\n\ + sherpa-rs = { default-features = false, features = [\"cuda\"] }" +); + +// Prebuilt sherpa-onnx doesn't have DirectML support +#[cfg(all(windows, feature = "download-binaries", feature = "directml"))] +compile_error!( + "The 'download-binaries' and 'directml' features cannot be enabled at the same time.\n\ + To resolve this, please disable the 'download-binaries' feature when using 'directml'.\n\ + For example, in your Cargo.toml:\n\ + [dependencies]\n\ + sherpa-rs = { default-features = false, features = [\"directml\"] }" +); + +// Prebuilt sherpa-onnx does not include TTS in static builds. +#[cfg(all( + windows, + feature = "download-binaries", + feature = "static", + feature = "tts" +))] +compile_error!( + "The 'download-binaries', 'static', and 'tts' features cannot be enabled at the same time.\n\ + To resolve this, please disable the 'tts' feature when using 'static' and 'download-binaries' together.\n\ + For example, in your Cargo.toml:\n\ + [dependencies]\n\ + sherpa-rs = { default-features = false, features = [\"static\", \"tts\"] }" +); + +macro_rules! debug_log { + ($($arg:tt)*) => { + // SHERPA_BUILD_DEBUG=1 cargo build + if std::env::var("SHERPA_BUILD_DEBUG").unwrap_or_default() == "1" { + println!("cargo:warning=[DEBUG] {}", format!($($arg)*)); + } + }; +} + +pub fn fetch_file(source_url: &str) -> Vec { + let resp = ureq::AgentBuilder::new() + .try_proxy_from_env(true) + .build() + .get(source_url) + .timeout(std::time::Duration::from_secs(1800)) + .call() + .unwrap_or_else(|err| panic!("Failed to GET `{source_url}`: {err}")); + + let len = resp + .header("Content-Length") + .and_then(|s| s.parse::().ok()) + .expect("Content-Length header should be present on archive response"); + debug_log!("Fetch file {} {}", source_url, len); + let mut reader = resp.into_reader(); + let mut buffer = Vec::new(); + reader + .read_to_end(&mut buffer) + .unwrap_or_else(|err| panic!("Failed to download from `{source_url}`: {err}")); + assert_eq!(buffer.len(), len); + buffer +} + +static DIST_CONTENT: &str = include_str!("../dist.json"); +static DIST_CHECKSUM_CONTENT: &str = include_str!("../checksum.txt"); +lazy_static::lazy_static! { + pub static ref DIST_TABLE: DistTable = DistTable::new(DIST_CONTENT); + pub static ref DIST_CHECKSUM: HashMap = { + DIST_CHECKSUM_CONTENT + .lines() + .map(|line| { + let mut parts = line.split_whitespace(); + let key = parts.next().unwrap().to_string(); + let value = parts.next().unwrap().to_string(); + (key, value) + }) + .collect() + }; +} + +#[derive(Debug, Deserialize)] +pub struct DistTable { + pub tag: String, + pub url: String, + pub targets: HashMap, +} + +#[derive(Debug, Clone)] +pub struct Dist { + pub url: String, + pub name: String, + pub checksum: String, + pub libs: Option>, // Paths to the extracted libraries +} + +impl DistTable { + fn new(content: &str) -> Self { + let mut table: DistTable = serde_json::from_str(content) + .unwrap_or_else(|_| panic!("Failed to parse dist.json: {}", content)); + table.url = table.url.replace("{tag}", &table.tag); + for value in table.targets.values_mut() { + // expand static with {tag} + if let Some(static_value) = value.get("static") { + let static_value = static_value.as_str().unwrap(); + value["static"] = Value::String(static_value.replace("{tag}", &table.tag)); + } + // expand dynamic with {tag} + if let Some(dynamic_value) = value.get("dynamic") { + let dynamic_value = dynamic_value.as_str().unwrap(); + value["dynamic"] = Value::String(dynamic_value.replace("{tag}", &table.tag)); + } + // expand archive with {tag} + if let Some(archive_value) = value.get("archive") { + let archive_value = archive_value.as_str().unwrap(); + value["archive"] = Value::String(archive_value.replace("{tag}", &table.tag)); + } + } + table + } + + pub fn get(&self, target: &str, is_dynamic: &mut bool) -> Option { + debug_log!("Extracting dist for target: {}", target); + // debug_log!("dist table: {:?}", self); + let target_dist = if target.contains("android") { + self.targets.get("android").unwrap() + } else if target.contains("ios") { + self.targets.get("ios").unwrap() + } else { + self.targets + .get(target) + .unwrap_or_else(|| + panic!("Target {} not found. try to disable download-feature with --no-default-features.", target) + ) + }; + debug_log!( + "raw target_dist: {:?}", + serde_json::to_string(target_dist).unwrap() + ); + let archive = if target_dist.get("archive").is_some() { + // archive name + // static/dynamic located in 'is_dynamic' field + target_dist.get("archive").unwrap().as_str().unwrap() + } else if *is_dynamic { + // dynamic archive name + target_dist.get("dynamic").unwrap().as_str().unwrap() + } else { + // static archive name + target_dist.get("static").unwrap().as_str().unwrap() + }; + let name = archive.replace(".tar.bz2", ""); + let name = name.replace(".tar.gz", ""); + + let libs: Option> = target_dist["targets"][target].as_array().map(|libs| { + libs.iter() + .map(|lib| lib.as_str().unwrap().to_string()) + .collect() + }); + + let url = self.url.replace("{archive}", archive); + let checksum = DIST_CHECKSUM.get(archive)?; + + // modify is_dynamic + debug_log!("checking is_dynamic"); + if let Some(target_dist) = target_dist.get("is_dynamic") { + *is_dynamic = target_dist.as_bool()?; + debug_log!("is_dynamic: {}", *is_dynamic); + } + + let dist = Dist { + url, + name, + checksum: checksum.to_string(), + libs, + }; + debug_log!("dist: {:?}", dist); + Some(dist) + } +} + +#[allow(unused)] +fn hex_str_to_bytes(c: impl AsRef<[u8]>) -> Vec { + fn nibble(c: u8) -> u8 { + match c { + b'A'..=b'F' => c - b'A' + 10, + b'a'..=b'f' => c - b'a' + 10, + b'0'..=b'9' => c - b'0', + _ => panic!(), + } + } + + c.as_ref() + .chunks(2) + .map(|n| (nibble(n[0]) << 4) | nibble(n[1])) + .collect() +} + +fn bytes_to_hex_str(bytes: Vec) -> String { + let mut s = String::with_capacity(bytes.len() * 2); + for byte in bytes { + s.push_str(&format!("{:02x}", byte)); + } + s +} + +pub fn sha256(buf: &[u8]) -> String { + let hash_bytes: Vec = ::digest(buf).to_vec(); + bytes_to_hex_str(hash_bytes) +} + +pub fn get_cache_dir() -> Option { + dirs::cache_dir().map(|p| p.join("sherpa-rs")) +} + +#[allow(unused)] +pub fn extract_tgz(buf: &[u8], output: &Path) { + let buf: std::io::BufReader<&[u8]> = std::io::BufReader::new(buf); + let tar = flate2::read::GzDecoder::new(buf); + let mut archive = tar::Archive::new(tar); + archive.unpack(output).expect("Failed to extract .tgz file"); +} + +pub fn extract_tbz(buf: &[u8], output: &Path) { + debug_log!("extracging tbz to {}", output.display()); + let buf: std::io::BufReader<&[u8]> = std::io::BufReader::new(buf); + let tar = bzip2::read::BzDecoder::new(buf); // Use BzDecoder for .bz2 + let mut archive = tar::Archive::new(tar); + archive.unpack(output).expect("Failed to extract .tbz file"); +} + +pub fn extract_lib_name>(path: P) -> String { + path.as_ref() + .file_name() + .and_then(|name| name.to_str()) + .map(|name| { + name.strip_prefix("lib") + .unwrap_or(name) + .replace(".so", "") + .replace(".dylib", "") + .replace(".a", "") + }) + .unwrap_or_else(|| "".to_string()) +} diff --git a/sys/src/lib.rs b/crates/sherpa-rs-sys/src/lib.rs similarity index 100% rename from sys/src/lib.rs rename to crates/sherpa-rs-sys/src/lib.rs diff --git a/sys/wrapper.h b/crates/sherpa-rs-sys/wrapper.h similarity index 100% rename from sys/wrapper.h rename to crates/sherpa-rs-sys/wrapper.h diff --git a/crates/sherpa-rs/Cargo.toml b/crates/sherpa-rs/Cargo.toml new file mode 100644 index 0000000..927dd40 --- /dev/null +++ b/crates/sherpa-rs/Cargo.toml @@ -0,0 +1,128 @@ +[package] +name = "sherpa-rs" +version = "0.6.6" +edition = "2021" +authors = ["thewh1teagle"] +license = "MIT" +repository = "https://github.com/thewh1teagle/sherpa-rs" +description = "Rust bindings to https://github.com/k2-fsa/sherpa-onnx" +readme = "../../README.md" +keywords = [ + "audio", + "embeddings", + "speech-recognition", + "sherpa", + "diarization", +] + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +eyre = "0.6.12" +hound = { version = "3.5.1" } +sherpa-rs-sys = { path = "../sherpa-rs-sys", version = "0.6.6", default-features = false } +tracing = "0.1.40" + +[dev-dependencies] +clap = { version = "4.5.8", features = ["derive"] } + +[features] +default = ["download-binaries", "tts"] +download-binaries = ["sherpa-rs-sys/download-binaries"] +static = ["sherpa-rs-sys/static"] +sys = [] +tts = ["sherpa-rs-sys/tts"] +cuda = ["sherpa-rs-sys/cuda"] +directml = ["sherpa-rs-sys/directml"] + +[[example]] +name = "tts_kokoro" +required-features = ["tts"] +path = "../../examples/tts_kokoro.rs" + +[[example]] +name = "tts_vits" +required-features = ["tts"] +path = "../../examples/tts_vits.rs" + +[[example]] +name = "tts_matcha" +required-features = ["tts"] +path = "../../examples/tts_matcha.rs" + +[[example]] +name = "audio_tag" +path = "../../examples/audio_tag.rs" + +[[example]] +name = "keyword_spot" +path = "../../examples/keyword_spot.rs" + +[[example]] +name = "punctuate" +path = "../../examples/punctuate.rs" + +[[example]] +name = "speaker_id" +path = "../../examples/speaker_id.rs" + +[[example]] +name = "vad" +path = "../../examples/vad.rs" + +[[example]] +name = "vad_whisper" +path = "../../examples/vad_whisper.rs" + +[[example]] +name = "zipformer" +path = "../../examples/zipformer.rs" + +[[example]] +name = "diarize" +path = "../../examples/diarize.rs" + +[[example]] +name = "language_id" +path = "../../examples/language_id.rs" + +[[example]] +name = "speaker_embedding" +path = "../../examples/speaker_embedding.rs" + +[[example]] +name = "vad_segment" +path = "../../examples/vad_segment.rs" + +[[example]] +name = "whisper" +path = "../../examples/whisper.rs" + +[[example]] +name = "moonshine" +path = "../../examples/moonshine.rs" + +[[example]] +name = "sense_voice" +path = "../../examples/sense_voice.rs" + +[[example]] +name = "paraformer" +path = "../../examples/paraformer.rs" + +[[example]] +name = "transducer" +path = "../../examples/transducer.rs" + +[[example]] +name = "transducer_vosk" +path = "../../examples/transducer_vosk.rs" + +[[example]] +name = "dolphin" +path = "../../examples/dolphin.rs" + +[[example]] +name = "parakeet" +path = "../../examples/parakeet.rs" \ No newline at end of file diff --git a/src/add_punctuation.rs b/crates/sherpa-rs/src/add_punctuation.rs similarity index 100% rename from src/add_punctuation.rs rename to crates/sherpa-rs/src/add_punctuation.rs diff --git a/crates/sherpa-rs/src/audio_tag.rs b/crates/sherpa-rs/src/audio_tag.rs new file mode 100644 index 0000000..aa32a3e --- /dev/null +++ b/crates/sherpa-rs/src/audio_tag.rs @@ -0,0 +1,95 @@ +use eyre::{bail, Result}; + +use crate::{ + get_default_provider, + utils::{cstr_to_string, cstring_from_str}, +}; + +#[derive(Debug, Default, Clone)] +pub struct AudioTagConfig { + pub model: String, + pub labels: String, + pub top_k: i32, + pub ced: Option, + pub debug: bool, + pub num_threads: Option, + pub provider: Option, +} + +pub struct AudioTag { + audio_tag: *const sherpa_rs_sys::SherpaOnnxAudioTagging, + config: AudioTagConfig, +} + +impl AudioTag { + pub fn new(config: AudioTagConfig) -> Result { + let config_clone = config.clone(); + + let model = cstring_from_str(&config.model); + let ced = cstring_from_str(&config.ced.unwrap_or_default()); + let labels = cstring_from_str(&config.labels); + let provider = cstring_from_str(&config.provider.unwrap_or(get_default_provider())); + + let sherpa_config = sherpa_rs_sys::SherpaOnnxAudioTaggingConfig { + model: sherpa_rs_sys::SherpaOnnxAudioTaggingModelConfig { + zipformer: sherpa_rs_sys::SherpaOnnxOfflineZipformerAudioTaggingModelConfig { + model: model.as_ptr(), + }, + ced: ced.as_ptr(), + num_threads: config.num_threads.unwrap_or(1), + debug: config.debug.into(), + provider: provider.as_ptr(), + }, + labels: labels.as_ptr(), + top_k: config.top_k, + }; + let audio_tag = unsafe { sherpa_rs_sys::SherpaOnnxCreateAudioTagging(&sherpa_config) }; + + if audio_tag.is_null() { + bail!("Failed to create audio tagging"); + } + Ok(Self { + audio_tag, + config: config_clone, + }) + } + + pub fn compute(&mut self, samples: Vec, sample_rate: u32) -> Vec { + let mut events = Vec::new(); + unsafe { + let stream = sherpa_rs_sys::SherpaOnnxAudioTaggingCreateOfflineStream(self.audio_tag); + sherpa_rs_sys::SherpaOnnxAcceptWaveformOffline( + stream, + sample_rate as i32, + samples.as_ptr(), + samples.len() as i32, + ); + + let results = sherpa_rs_sys::SherpaOnnxAudioTaggingCompute( + self.audio_tag, + stream, + self.config.top_k, + ); + + for i in 0..self.config.top_k { + let event = *results.add(i.try_into().unwrap()); + let event_name = cstr_to_string((*event).name as _); + events.push(event_name); + } + + sherpa_rs_sys::SherpaOnnxDestroyOfflineStream(stream); + } + events + } +} + +unsafe impl Send for AudioTag {} +unsafe impl Sync for AudioTag {} + +impl Drop for AudioTag { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyAudioTagging(self.audio_tag); + } + } +} diff --git a/crates/sherpa-rs/src/diarize.rs b/crates/sherpa-rs/src/diarize.rs new file mode 100644 index 0000000..74c30f1 --- /dev/null +++ b/crates/sherpa-rs/src/diarize.rs @@ -0,0 +1,169 @@ +use crate::{get_default_provider, utils::cstring_from_str}; +use eyre::{bail, Result}; +use std::{path::Path, ptr::null_mut}; + +#[derive(Debug)] +pub struct Diarize { + sd: *const sherpa_rs_sys::SherpaOnnxOfflineSpeakerDiarization, +} + +#[derive(Debug, Clone)] +pub struct Segment { + pub start: f32, + pub end: f32, + pub speaker: i32, +} + +type ProgressCallback = Box i32) + Send + 'static>; + +#[derive(Debug, Clone)] +pub struct DiarizeConfig { + pub num_clusters: Option, + pub threshold: Option, + pub min_duration_on: Option, + pub min_duration_off: Option, + pub provider: Option, + pub debug: bool, +} + +impl Default for DiarizeConfig { + fn default() -> Self { + Self { + num_clusters: Some(4), + threshold: Some(0.5), + min_duration_on: Some(0.0), + min_duration_off: Some(0.0), + provider: None, + debug: false, + } + } +} + +impl Diarize { + pub fn new>( + segmentation_model: P, + embedding_model: P, + config: DiarizeConfig, + ) -> Result { + let provider = config.provider.unwrap_or(get_default_provider()); + + let debug = config.debug; + let debug = if debug { 1 } else { 0 }; + + let embedding_model = embedding_model.as_ref().to_str().unwrap(); + let segmentation_model = segmentation_model.as_ref().to_str().unwrap(); + + let clustering_config = sherpa_rs_sys::SherpaOnnxFastClusteringConfig { + num_clusters: config.num_clusters.unwrap_or(4), + threshold: config.threshold.unwrap_or(0.5), + }; + + let embedding_model = cstring_from_str(embedding_model); + let provider = cstring_from_str(&provider.clone()); + let segmentation_model = cstring_from_str(segmentation_model); + + let config = sherpa_rs_sys::SherpaOnnxOfflineSpeakerDiarizationConfig { + embedding: sherpa_rs_sys::SherpaOnnxSpeakerEmbeddingExtractorConfig { + model: embedding_model.as_ptr(), + num_threads: 1, + debug, + provider: provider.as_ptr(), + }, + clustering: clustering_config, + min_duration_off: config.min_duration_off.unwrap_or(0.0), + min_duration_on: config.min_duration_on.unwrap_or(0.0), + segmentation: sherpa_rs_sys::SherpaOnnxOfflineSpeakerSegmentationModelConfig { + pyannote: sherpa_rs_sys::SherpaOnnxOfflineSpeakerSegmentationPyannoteModelConfig { + model: segmentation_model.as_ptr(), + }, + num_threads: 1, + debug, + provider: provider.as_ptr(), + }, + }; + + let sd = unsafe { sherpa_rs_sys::SherpaOnnxCreateOfflineSpeakerDiarization(&config) }; + + if sd.is_null() { + bail!("Failed to initialize offline speaker diarization"); + } + Ok(Self { sd }) + } + + pub fn compute( + &mut self, + mut samples: Vec, + progress_callback: Option, + ) -> Result> { + let samples_ptr = samples.as_mut_ptr(); + let mut segments = Vec::new(); + unsafe { + let mut callback_box = + progress_callback.map(|cb| Box::new(cb) as Box); + let callback_ptr = callback_box + .as_mut() + .map(|b| b.as_mut() as *mut ProgressCallback as *mut std::ffi::c_void) + .unwrap_or(null_mut()); + + let result = sherpa_rs_sys::SherpaOnnxOfflineSpeakerDiarizationProcessWithCallback( + self.sd, + samples_ptr, + samples.len() as i32, + if callback_box.is_some() { + Some(progress_callback_wrapper) + } else { + None + }, + callback_ptr, + ); + + let num_segments = + sherpa_rs_sys::SherpaOnnxOfflineSpeakerDiarizationResultGetNumSegments(result); + let segments_ptr: *const sherpa_rs_sys::SherpaOnnxOfflineSpeakerDiarizationSegment = + sherpa_rs_sys::SherpaOnnxOfflineSpeakerDiarizationResultSortByStartTime(result); + + if !segments_ptr.is_null() && num_segments > 0 { + let segments_result: &[ + sherpa_rs_sys::SherpaOnnxOfflineSpeakerDiarizationSegment + ] = std::slice::from_raw_parts(segments_ptr, num_segments as usize); + + for segment in segments_result { + // Use segment here + + segments.push(Segment { + start: segment.start, + end: segment.end, + speaker: segment.speaker, + }); + } + } else { + bail!("No segments found or invalid pointer."); + } + + sherpa_rs_sys::SherpaOnnxOfflineSpeakerDiarizationDestroySegment(segments_ptr); + sherpa_rs_sys::SherpaOnnxOfflineSpeakerDiarizationDestroyResult(result); + + Ok(segments) + } + } +} + +unsafe extern "C" fn progress_callback_wrapper( + num_processed_chunk: i32, + num_total_chunks: i32, + arg: *mut std::ffi::c_void, +) -> i32 { + let callback = &mut *(arg as *mut ProgressCallback); + callback(num_processed_chunk, num_total_chunks) +} + +unsafe impl Send for Diarize {} +unsafe impl Sync for Diarize {} + +impl Drop for Diarize { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflineSpeakerDiarization(self.sd); + } + } +} diff --git a/crates/sherpa-rs/src/dolphin.rs b/crates/sherpa-rs/src/dolphin.rs new file mode 100644 index 0000000..985b7c8 --- /dev/null +++ b/crates/sherpa-rs/src/dolphin.rs @@ -0,0 +1,131 @@ +use crate::{get_default_provider, utils::cstring_from_str}; +use eyre::{bail, Result}; +use std::mem; + +#[derive(Debug)] +pub struct DolphinRecognizer { + recognizer: *const sherpa_rs_sys::SherpaOnnxOfflineRecognizer, +} + +pub type DolphinRecognizerResult = super::OfflineRecognizerResult; + +#[derive(Debug, Clone)] +pub struct DolphinConfig { + pub model: String, + pub tokens: String, + pub decoding_method: String, + + pub provider: Option, + pub num_threads: Option, + pub debug: bool, +} + +impl Default for DolphinConfig { + fn default() -> Self { + Self { + model: String::new(), + tokens: String::new(), + decoding_method: String::from("greedy_search"), + debug: false, + provider: None, + num_threads: Some(1), + } + } +} + +impl DolphinRecognizer { + pub fn new(config: DolphinConfig) -> Result { + let debug = config.debug.into(); + let provider = config.provider.unwrap_or(get_default_provider()); + + let provider_ptr = cstring_from_str(&provider); + let num_threads = config.num_threads.unwrap_or(2); + let model_ptr = cstring_from_str(&config.model); + let tokens_ptr = cstring_from_str(&config.tokens); + let decoding_method_ptr = cstring_from_str(&config.decoding_method); + + let model_config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineModelConfig { + debug, + num_threads, + provider: provider_ptr.as_ptr(), + dolphin: sherpa_rs_sys::SherpaOnnxOfflineDolphinModelConfig { + model: model_ptr.as_ptr(), + }, + tokens: tokens_ptr.as_ptr(), + + // Zeros + nemo_ctc: mem::zeroed::<_>(), + paraformer: mem::zeroed::<_>(), + tdnn: mem::zeroed::<_>(), + telespeech_ctc: mem::zeroed::<_>(), + fire_red_asr: mem::zeroed::<_>(), + transducer: mem::zeroed::<_>(), + whisper: mem::zeroed::<_>(), + sense_voice: mem::zeroed::<_>(), + moonshine: mem::zeroed::<_>(), + bpe_vocab: mem::zeroed::<_>(), + model_type: mem::zeroed::<_>(), + modeling_unit: mem::zeroed::<_>(), + } + }; + + let config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineRecognizerConfig { + decoding_method: decoding_method_ptr.as_ptr(), + model_config, + feat_config: sherpa_rs_sys::SherpaOnnxFeatureConfig { + sample_rate: 16000, + feature_dim: 80, + }, + hotwords_file: mem::zeroed::<_>(), + hotwords_score: mem::zeroed::<_>(), + lm_config: mem::zeroed::<_>(), + max_active_paths: mem::zeroed::<_>(), + rule_fars: mem::zeroed::<_>(), + rule_fsts: mem::zeroed::<_>(), + blank_penalty: mem::zeroed::<_>(), + hr: mem::zeroed::<_>(), + } + }; + + let recognizer = unsafe { sherpa_rs_sys::SherpaOnnxCreateOfflineRecognizer(&config) }; + + if recognizer.is_null() { + bail!("Failed to create recognizer"); + } + + Ok(Self { recognizer }) + } + + pub fn transcribe(&mut self, sample_rate: u32, samples: &[f32]) -> DolphinRecognizerResult { + unsafe { + let stream = sherpa_rs_sys::SherpaOnnxCreateOfflineStream(self.recognizer); + sherpa_rs_sys::SherpaOnnxAcceptWaveformOffline( + stream, + sample_rate as i32, + samples.as_ptr(), + samples.len().try_into().unwrap(), + ); + sherpa_rs_sys::SherpaOnnxDecodeOfflineStream(self.recognizer, stream); + let result_ptr = sherpa_rs_sys::SherpaOnnxGetOfflineStreamResult(stream); + let raw_result = result_ptr.read(); + let result = DolphinRecognizerResult::new(&raw_result); + // Free + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizerResult(result_ptr); + sherpa_rs_sys::SherpaOnnxDestroyOfflineStream(stream); + result + } + } +} + +unsafe impl Send for DolphinRecognizer {} +unsafe impl Sync for DolphinRecognizer {} + +impl Drop for DolphinRecognizer { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizer(self.recognizer); + } + } +} diff --git a/src/embedding_manager.rs b/crates/sherpa-rs/src/embedding_manager.rs similarity index 86% rename from src/embedding_manager.rs rename to crates/sherpa-rs/src/embedding_manager.rs index 345ab93..f4f67fb 100644 --- a/src/embedding_manager.rs +++ b/crates/sherpa-rs/src/embedding_manager.rs @@ -1,7 +1,5 @@ +use crate::utils::{cstr_to_string, cstring_from_str}; use eyre::{bail, Result}; -use std::ffi::{CStr, CString}; - -use crate::cstr_to_string; #[derive(Debug, Clone)] pub struct EmbeddingManager { @@ -32,8 +30,8 @@ impl EmbeddingManager { if name.is_null() { return None; } - let cstr = CStr::from_ptr(name); - Some(cstr.to_str().unwrap_or_default().to_string()) + let name = cstr_to_string(name as _); + Some(name) } } @@ -59,7 +57,7 @@ impl EmbeddingManager { let mut matches: Vec = Vec::new(); for i in 0..result.count { let match_c = matches_c[i as usize]; - let name = cstr_to_string!(match_c.name); + let name = cstr_to_string(match_c.name as _); let score = match_c.score; matches.push(SpeakerMatch { name, score }); } @@ -69,16 +67,16 @@ impl EmbeddingManager { } pub fn add(&mut self, name: String, embedding: &mut [f32]) -> Result<()> { - let name_cstr = CString::new(name.clone())?; - + let name_c = cstring_from_str(&name.clone()); unsafe { let status = sherpa_rs_sys::SherpaOnnxSpeakerEmbeddingManagerAdd( self.manager, - name_cstr.into_raw(), + name_c.as_ptr(), embedding.as_mut_ptr(), ); + if status.is_negative() { - bail!("Failed to register {}", name) + bail!("Failed to register {}", name); } Ok(()) } @@ -92,6 +90,6 @@ impl Drop for EmbeddingManager { fn drop(&mut self) { unsafe { sherpa_rs_sys::SherpaOnnxDestroySpeakerEmbeddingManager(self.manager); - }; + } } } diff --git a/crates/sherpa-rs/src/keyword_spot.rs b/crates/sherpa-rs/src/keyword_spot.rs new file mode 100644 index 0000000..941838d --- /dev/null +++ b/crates/sherpa-rs/src/keyword_spot.rs @@ -0,0 +1,161 @@ +use std::mem; + +use crate::{ + get_default_provider, + utils::{cstr_to_string, cstring_from_str}, +}; +use eyre::{bail, Result}; + +#[derive(Debug, Clone)] +pub struct KeywordSpotConfig { + pub zipformer_encoder: String, + pub zipformer_decoder: String, + pub zipformer_joiner: String, + + pub tokens: String, + pub keywords: String, + pub max_active_path: i32, + pub keywords_threshold: f32, + pub keywords_score: f32, + + pub num_trailing_blanks: i32, + + pub sample_rate: i32, + pub feature_dim: i32, + + pub debug: bool, + pub num_threads: Option, + pub provider: Option, +} + +impl Default for KeywordSpotConfig { + fn default() -> Self { + Self { + keywords_threshold: 0.1, + max_active_path: 4, + keywords_score: 3.0, + keywords: String::new(), + tokens: String::new(), + + sample_rate: 16000, + feature_dim: 80, + num_trailing_blanks: 1, + + zipformer_decoder: String::new(), + zipformer_encoder: String::new(), + zipformer_joiner: String::new(), + + debug: false, + num_threads: None, + provider: Some("cpu".into()), + } + } +} + +pub struct KeywordSpot { + spotter: *const sherpa_rs_sys::SherpaOnnxKeywordSpotter, + stream: *const sherpa_rs_sys::SherpaOnnxOnlineStream, +} + +impl KeywordSpot { + // Create new keyboard spotter along with stream + // Ready for streaming or regular use + pub fn new(config: KeywordSpotConfig) -> Result { + let provider = cstring_from_str(&config.provider.unwrap_or(get_default_provider())); + + let zipformer_encoder = cstring_from_str(&config.zipformer_encoder); + let zipformer_decoder = cstring_from_str(&config.zipformer_decoder); + let zipformer_joiner = cstring_from_str(&config.zipformer_joiner); + + let tokens = cstring_from_str(&config.tokens); + let keywords = cstring_from_str(&config.keywords); + + let sherpa_config = unsafe { + sherpa_rs_sys::SherpaOnnxKeywordSpotterConfig { + feat_config: sherpa_rs_sys::SherpaOnnxFeatureConfig { + sample_rate: config.sample_rate, + feature_dim: config.feature_dim, + }, + keywords_buf: mem::zeroed::<_>(), + keywords_buf_size: 0, + keywords_file: keywords.as_ptr(), + max_active_paths: config.max_active_path, + keywords_score: config.keywords_score, + keywords_threshold: config.keywords_threshold, + num_trailing_blanks: config.num_trailing_blanks, + model_config: sherpa_rs_sys::SherpaOnnxOnlineModelConfig { + transducer: sherpa_rs_sys::SherpaOnnxOnlineTransducerModelConfig { + encoder: zipformer_encoder.as_ptr(), + decoder: zipformer_decoder.as_ptr(), + joiner: zipformer_joiner.as_ptr(), + }, + num_threads: config.num_threads.unwrap_or(1), + provider: provider.as_ptr(), + debug: config.debug.into(), + tokens: tokens.as_ptr(), + + paraformer: mem::zeroed::<_>(), + zipformer2_ctc: mem::zeroed::<_>(), + model_type: mem::zeroed::<_>(), + modeling_unit: mem::zeroed::<_>(), + bpe_vocab: mem::zeroed::<_>(), + tokens_buf: mem::zeroed::<_>(), + tokens_buf_size: mem::zeroed::<_>(), + }, + } + }; + let spotter = unsafe { sherpa_rs_sys::SherpaOnnxCreateKeywordSpotter(&sherpa_config) }; + + if spotter.is_null() { + bail!("Failed to create keyword spotter"); + } + let stream = unsafe { sherpa_rs_sys::SherpaOnnxCreateKeywordStream(spotter) }; + if stream.is_null() { + bail!("Failed to create SherpaOnnx keyword stream"); + } + + Ok(Self { spotter, stream }) + } + + pub fn extract_keyword( + &mut self, + samples: Vec, + sample_rate: u32, + ) -> Result> { + // Create keyword spotting stream + unsafe { + sherpa_rs_sys::SherpaOnnxOnlineStreamAcceptWaveform( + self.stream, + sample_rate as i32, + samples.as_ptr(), + samples.len() as i32, + ); + sherpa_rs_sys::SherpaOnnxOnlineStreamInputFinished(self.stream); + while sherpa_rs_sys::SherpaOnnxIsKeywordStreamReady(self.spotter, self.stream) == 1 { + sherpa_rs_sys::SherpaOnnxDecodeKeywordStream(self.spotter, self.stream); + } + let result_ptr = sherpa_rs_sys::SherpaOnnxGetKeywordResult(self.spotter, self.stream); + let mut keyword = None; + if !result_ptr.is_null() { + let decoded_keyword = cstr_to_string((*result_ptr).keyword as _); + if !decoded_keyword.is_empty() { + keyword = Some(decoded_keyword); + } + sherpa_rs_sys::SherpaOnnxDestroyKeywordResult(result_ptr); + } + Ok(keyword) + } + } +} + +unsafe impl Send for KeywordSpot {} +unsafe impl Sync for KeywordSpot {} + +impl Drop for KeywordSpot { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOnlineStream(self.stream); + sherpa_rs_sys::SherpaOnnxDestroyKeywordSpotter(self.spotter); + } + } +} diff --git a/src/language_id.rs b/crates/sherpa-rs/src/language_id.rs similarity index 55% rename from src/language_id.rs rename to crates/sherpa-rs/src/language_id.rs index 8d20e4a..0c7cd12 100644 --- a/src/language_id.rs +++ b/crates/sherpa-rs/src/language_id.rs @@ -1,60 +1,65 @@ -use crate::get_default_provider; +use crate::{ + get_default_provider, + utils::{cstr_to_string, cstring_from_str}, +}; use eyre::{bail, Result}; -use std::ffi::{CStr, CString}; #[derive(Debug)] pub struct SpokenLanguageId { slid: *const sherpa_rs_sys::SherpaOnnxSpokenLanguageIdentification, } +#[derive(Debug, Default)] +pub struct SpokenLanguageIdConfig { + pub encoder: String, + pub decoder: String, + pub debug: bool, + pub provider: Option, + pub num_threads: Option, +} + impl SpokenLanguageId { - pub fn new( - encoder: String, - decoder: String, - debug: Option, - provider: Option<&str>, - num_threads: Option, - ) -> Self { - let provider = provider.unwrap_or(get_default_provider()); - let provider_c = CString::new(provider).unwrap(); - let debug = debug.unwrap_or_default(); - let debug = if debug { 0 } else { 1 }; + pub fn new(config: SpokenLanguageIdConfig) -> Self { + let debug = config.debug.into(); + + let decoder = cstring_from_str(&config.decoder); + let encoder = cstring_from_str(&config.encoder); + let provider = cstring_from_str(&config.provider.unwrap_or(get_default_provider())); - let encoder_c = CString::new(encoder).unwrap(); - let decoder_c = CString::new(decoder).unwrap(); let whisper = sherpa_rs_sys::SherpaOnnxSpokenLanguageIdentificationWhisperConfig { - decoder: decoder_c.into_raw(), - encoder: encoder_c.into_raw(), + decoder: decoder.as_ptr(), + encoder: encoder.as_ptr(), tail_paddings: 0, }; - let config = sherpa_rs_sys::SherpaOnnxSpokenLanguageIdentificationConfig { + let sherpa_config = sherpa_rs_sys::SherpaOnnxSpokenLanguageIdentificationConfig { debug, - num_threads: num_threads.unwrap_or(2), - provider: provider_c.into_raw(), + num_threads: config.num_threads.unwrap_or(1), + provider: provider.as_ptr(), whisper, }; - let slid = unsafe { sherpa_rs_sys::SherpaOnnxCreateSpokenLanguageIdentification(&config) }; + let slid = + unsafe { sherpa_rs_sys::SherpaOnnxCreateSpokenLanguageIdentification(&sherpa_config) }; + Self { slid } } - pub fn compute(&mut self, samples: Vec, sample_rate: i32) -> Result { + pub fn compute(&mut self, samples: Vec, sample_rate: u32) -> Result { unsafe { let stream = sherpa_rs_sys::SherpaOnnxSpokenLanguageIdentificationCreateOfflineStream(self.slid); sherpa_rs_sys::SherpaOnnxAcceptWaveformOffline( stream, - sample_rate, + sample_rate as i32, samples.as_ptr(), samples.len().try_into().unwrap(), ); let language_result_ptr = sherpa_rs_sys::SherpaOnnxSpokenLanguageIdentificationCompute(self.slid, stream); if language_result_ptr.is_null() || (*language_result_ptr).lang.is_null() { - bail!("language ptr is null") + bail!("language ptr is null"); } let language_ptr = (*language_result_ptr).lang; - let c_language = CStr::from_ptr(language_ptr); - let language = c_language.to_str().unwrap().to_string(); + let language = cstr_to_string(language_ptr as _); // Free sherpa_rs_sys::SherpaOnnxDestroySpokenLanguageIdentificationResult(language_result_ptr); sherpa_rs_sys::SherpaOnnxDestroyOfflineStream(stream); diff --git a/crates/sherpa-rs/src/lib.rs b/crates/sherpa-rs/src/lib.rs new file mode 100644 index 0000000..3e7af39 --- /dev/null +++ b/crates/sherpa-rs/src/lib.rs @@ -0,0 +1,137 @@ +pub mod audio_tag; +pub mod diarize; +pub mod dolphin; +pub mod embedding_manager; +pub mod keyword_spot; +pub mod language_id; +pub mod moonshine; +pub mod online; +pub mod paraformer; +pub mod punctuate; +pub mod sense_voice; +pub mod speaker_id; +pub mod transducer; +pub mod vad; +pub mod whisper; +pub mod zipformer; + +mod utils; + +#[cfg(feature = "tts")] +pub mod tts; + +use std::ffi::CStr; + +#[cfg(feature = "sys")] +pub use sherpa_rs_sys; + +use eyre::{bail, Result}; +use utils::cstr_to_string; + +pub fn get_default_provider() -> String { + "cpu".into() + // Other providers has many issues with different models!! + // if cfg!(feature = "cuda") { + // "cuda" + // } else if cfg!(target_os = "macos") { + // "coreml" + // } else if cfg!(feature = "directml") { + // "directml" + // } else { + // "cpu" + // } + // .into() +} + +pub fn read_audio_file(path: &str) -> Result<(Vec, u32)> { + let mut reader = hound::WavReader::open(path)?; + let sample_rate = reader.spec().sample_rate; + + // Check if the sample rate is 16000 + if sample_rate != 16000 { + bail!("The sample rate must be 16000."); + } + + // Collect samples into a Vec + let samples: Vec = reader + .samples::() + .map(|s| (s.unwrap() as f32) / (i16::MAX as f32)) + .collect(); + + Ok((samples, sample_rate)) +} + +pub fn write_audio_file(path: &str, samples: &[f32], sample_rate: u32) -> Result<()> { + // Create a WAV file writer + let spec = hound::WavSpec { + channels: 1, + sample_rate, + bits_per_sample: 16, + sample_format: hound::SampleFormat::Int, + }; + + let mut writer = hound::WavWriter::create(path, spec)?; + + // Convert samples from f32 to i16 and write them to the WAV file + for &sample in samples { + let scaled_sample = + (sample * (i16::MAX as f32)).clamp(i16::MIN as f32, i16::MAX as f32) as i16; + writer.write_sample(scaled_sample)?; + } + + writer.finalize()?; + Ok(()) +} + +pub struct OnnxConfig { + pub provider: String, + pub debug: bool, + pub num_threads: i32, +} + +#[derive(Debug, Clone)] +pub struct OfflineRecognizerResult { + pub lang: String, + pub text: String, + pub timestamps: Vec, + pub tokens: Vec, +} + +impl OfflineRecognizerResult { + fn new(result: &sherpa_rs_sys::SherpaOnnxOfflineRecognizerResult) -> Self { + let lang = unsafe { cstr_to_string(result.lang) }; + let text = unsafe { cstr_to_string(result.text) }; + let count = result.count.try_into().unwrap(); + let timestamps = if result.timestamps.is_null() { + Vec::new() + } else { + unsafe { std::slice::from_raw_parts(result.timestamps, count).to_vec() } + }; + let mut tokens = Vec::with_capacity(count); + let mut next_token = result.tokens; + + for _ in 0..count { + let token = unsafe { CStr::from_ptr(next_token) }; + tokens.push(token.to_string_lossy().into_owned()); + next_token = next_token + .wrapping_byte_offset(token.to_bytes_with_nul().len().try_into().unwrap()); + } + + Self { + lang, + text, + timestamps, + tokens, + } + } +} + +impl Default for OnnxConfig { + fn default() -> Self { + Self { + provider: get_default_provider(), + debug: false, + num_threads: 1, + } + } +} diff --git a/crates/sherpa-rs/src/moonshine.rs b/crates/sherpa-rs/src/moonshine.rs new file mode 100644 index 0000000..cc5c57e --- /dev/null +++ b/crates/sherpa-rs/src/moonshine.rs @@ -0,0 +1,148 @@ +use crate::{get_default_provider, utils::cstring_from_str}; +use eyre::{bail, Result}; +use std::{mem, ptr::null}; + +#[derive(Debug)] +pub struct MoonshineRecognizer { + recognizer: *const sherpa_rs_sys::SherpaOnnxOfflineRecognizer, +} + +pub type MoonshineRecognizerResult = super::OfflineRecognizerResult; + +#[derive(Debug, Clone)] +pub struct MoonshineConfig { + pub preprocessor: String, + + pub encoder: String, + pub uncached_decoder: String, + pub cached_decoder: String, + + pub tokens: String, + + pub provider: Option, + pub num_threads: Option, + pub debug: bool, +} + +impl Default for MoonshineConfig { + fn default() -> Self { + Self { + preprocessor: String::new(), + encoder: String::new(), + cached_decoder: String::new(), + uncached_decoder: String::new(), + tokens: String::new(), + + debug: false, + provider: None, + num_threads: Some(1), + } + } +} + +impl MoonshineRecognizer { + pub fn new(config: MoonshineConfig) -> Result { + let debug = config.debug.into(); + let provider = config.provider.unwrap_or(get_default_provider()); + + // Onnx + let provider_ptr = cstring_from_str(&provider); + let num_threads = config.num_threads.unwrap_or(2); + + // Moonshine + let preprocessor_ptr = cstring_from_str(&config.preprocessor); + let encoder_ptr = cstring_from_str(&config.encoder); + let cached_decoder_ptr = cstring_from_str(&config.cached_decoder); + let uncached_decoder_ptr = cstring_from_str(&config.uncached_decoder); + let tokens_ptr = cstring_from_str(&config.tokens); + + let model_config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineModelConfig { + debug, + num_threads, + moonshine: sherpa_rs_sys::SherpaOnnxOfflineMoonshineModelConfig { + preprocessor: preprocessor_ptr.as_ptr(), + encoder: encoder_ptr.as_ptr(), + uncached_decoder: uncached_decoder_ptr.as_ptr(), + cached_decoder: cached_decoder_ptr.as_ptr(), + }, + tokens: tokens_ptr.as_ptr(), + provider: provider_ptr.as_ptr(), + + model_type: mem::zeroed::<_>(), + modeling_unit: mem::zeroed::<_>(), + dolphin: mem::zeroed::<_>(), + bpe_vocab: mem::zeroed::<_>(), + nemo_ctc: mem::zeroed::<_>(), + paraformer: mem::zeroed::<_>(), + tdnn: mem::zeroed::<_>(), + telespeech_ctc: mem::zeroed::<_>(), + fire_red_asr: mem::zeroed::<_>(), + transducer: mem::zeroed::<_>(), + whisper: mem::zeroed::<_>(), + sense_voice: mem::zeroed::<_>(), + } + }; + + let config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineRecognizerConfig { + decoding_method: null(), + feat_config: sherpa_rs_sys::SherpaOnnxFeatureConfig { + sample_rate: 16000, + feature_dim: 512, + }, + hotwords_file: null(), + hotwords_score: 0.0, + lm_config: sherpa_rs_sys::SherpaOnnxOfflineLMConfig { + model: null(), + scale: 0.0, + }, + max_active_paths: 0, + model_config, + rule_fars: null(), + rule_fsts: null(), + blank_penalty: 0.0, + hr: mem::zeroed::<_>(), + } + }; + + let recognizer = unsafe { sherpa_rs_sys::SherpaOnnxCreateOfflineRecognizer(&config) }; + + if recognizer.is_null() { + bail!("Failed to create recognizer"); + } + + Ok(Self { recognizer }) + } + + pub fn transcribe(&mut self, sample_rate: u32, samples: &[f32]) -> MoonshineRecognizerResult { + unsafe { + let stream = sherpa_rs_sys::SherpaOnnxCreateOfflineStream(self.recognizer); + sherpa_rs_sys::SherpaOnnxAcceptWaveformOffline( + stream, + sample_rate as i32, + samples.as_ptr(), + samples.len().try_into().unwrap(), + ); + sherpa_rs_sys::SherpaOnnxDecodeOfflineStream(self.recognizer, stream); + let result_ptr = sherpa_rs_sys::SherpaOnnxGetOfflineStreamResult(stream); + let raw_result = result_ptr.read(); + let result = MoonshineRecognizerResult::new(&raw_result); + // Free + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizerResult(result_ptr); + sherpa_rs_sys::SherpaOnnxDestroyOfflineStream(stream); + result + } + } +} + +unsafe impl Send for MoonshineRecognizer {} +unsafe impl Sync for MoonshineRecognizer {} + +impl Drop for MoonshineRecognizer { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizer(self.recognizer); + } + } +} diff --git a/crates/sherpa-rs/src/online/keyword_spotter.rs b/crates/sherpa-rs/src/online/keyword_spotter.rs new file mode 100644 index 0000000..58f98ff --- /dev/null +++ b/crates/sherpa-rs/src/online/keyword_spotter.rs @@ -0,0 +1,160 @@ +use std::{ + ffi::{CStr, CString}, + path::Path, + sync::Arc, +}; + +use eyre::Result; +use sherpa_rs_sys::{ + SherpaOnnxFeatureConfig, SherpaOnnxKeywordSpotterConfig, SherpaOnnxOnlineModelConfig, +}; + +use crate::{online::transducer::Transducer, OnnxConfig}; + +use super::Stream; + +pub struct KeywordSpotter { + spotter: *const sherpa_rs_sys::SherpaOnnxKeywordSpotter, +} + +impl KeywordSpotter { + pub fn from_transducer( + transducer: Transducer, + tokens: &Path, + keyword_file: &Path, + + onnx_config: OnnxConfig, + ) -> Result> { + let tokens_c = CString::new(tokens.to_str().unwrap_or(&tokens.to_string_lossy())).unwrap(); + let provider_c = CString::new(onnx_config.provider).unwrap(); + let model_type_c = transducer.model_type(); + let modeling_unit_c = CString::new("cjkchar").unwrap(); + let files_c = CString::new( + keyword_file + .to_str() + .unwrap_or(&keyword_file.to_string_lossy()), + ) + .unwrap(); + + let mut model_config = unsafe { std::mem::zeroed::() }; + model_config.transducer = transducer.as_config(); + model_config.tokens = tokens_c.as_ptr(); + model_config.num_threads = onnx_config.num_threads; + model_config.provider = provider_c.as_ptr(); + model_config.debug = onnx_config.debug as i32; + model_config.modeling_unit = modeling_unit_c.as_ptr(); + model_config.model_type = model_type_c.as_ptr(); + + let mut config = unsafe { std::mem::zeroed::() }; + config.feat_config = SherpaOnnxFeatureConfig { + sample_rate: 16000, + feature_dim: 80, + }; + config.model_config = model_config; + config.num_trailing_blanks = 1; + config.keywords_score = 1.0; + config.keywords_threshold = 0.25; + config.keywords_file = files_c.as_ptr(); + let spotter = unsafe { sherpa_rs_sys::SherpaOnnxCreateKeywordSpotter(&config) }; + if spotter.is_null() { + eyre::bail!("Failed to create keyword spotter"); + } + + Ok(Arc::new(Self { spotter })) + } + + pub fn create_stream( + self: &Arc, + keywords: Option<&str>, + ) -> Result { + KeywordSpottingStream::new(self.clone(), keywords) + } +} + +unsafe impl Send for KeywordSpotter {} +unsafe impl Sync for KeywordSpotter {} + +impl Drop for KeywordSpotter { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyKeywordSpotter(self.spotter); + } + } +} + +pub struct KeywordSpottingStream { + spotter: Arc, + stream: *const sherpa_rs_sys::SherpaOnnxOnlineStream, +} + +impl KeywordSpottingStream { + pub fn new(spotter: Arc, keywords: Option<&str>) -> Result { + let stream = if let Some(keywords) = keywords { + let keywords = CString::new(keywords).unwrap(); + unsafe { + sherpa_rs_sys::SherpaOnnxCreateKeywordStreamWithKeywords( + spotter.spotter, + keywords.as_ptr(), + ) + } + } else { + unsafe { sherpa_rs_sys::SherpaOnnxCreateKeywordStream(spotter.spotter) } + }; + + if stream.is_null() { + eyre::bail!("Failed to create SherpaOnnxOnlineStream"); + } + Ok(Self { spotter, stream }) + } +} + +impl Drop for KeywordSpottingStream { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOnlineStream(self.stream); + } + } +} + +impl Stream for KeywordSpottingStream { + fn accept_waveform(&mut self, sample_rate: i32, samples: impl AsRef<[f32]>) { + unsafe { + sherpa_rs_sys::SherpaOnnxOnlineStreamAcceptWaveform( + self.stream, + sample_rate, + samples.as_ref().as_ptr(), + samples.as_ref().len() as i32, + ); + } + } + + fn decode_stream(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDecodeKeywordStream(self.spotter.spotter, self.stream); + } + } + + fn is_ready(&mut self) -> bool { + unsafe { + sherpa_rs_sys::SherpaOnnxIsKeywordStreamReady(self.spotter.spotter, self.stream) == 1 + } + } + + fn get_result(&mut self) -> String { + unsafe { + let raw_result = + sherpa_rs_sys::SherpaOnnxGetKeywordResult(self.spotter.spotter, self.stream); + let result = raw_result.read(); + let keyword = CStr::from_ptr(result.keyword).to_string_lossy().to_string(); + + sherpa_rs_sys::SherpaOnnxDestroyKeywordResult(raw_result); + keyword + } + } + + fn reset(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxResetKeywordStream(self.spotter.spotter, self.stream); + } + } +} diff --git a/src/online/stream/mod.rs b/crates/sherpa-rs/src/online/mod.rs similarity index 57% rename from src/online/stream/mod.rs rename to crates/sherpa-rs/src/online/mod.rs index 72a44d9..f74b97e 100644 --- a/src/online/stream/mod.rs +++ b/crates/sherpa-rs/src/online/mod.rs @@ -1,8 +1,11 @@ pub mod keyword_spotter; +pub mod paraformer; pub mod recognizer; +pub mod transducer; +pub mod zipformer2_ctc; -pub trait OnlineStream { - fn accept_waveform(&mut self, sample_rate: i32, samples: Vec); +pub trait Stream { + fn accept_waveform(&mut self, sample_rate: i32, samples: impl AsRef<[f32]>); fn decode_stream(&mut self); fn get_result(&mut self) -> String; fn is_ready(&mut self) -> bool; @@ -11,6 +14,5 @@ pub trait OnlineStream { fn is_endpoint(&mut self) -> bool { true } - /// better only use for recognizer - fn reset(&mut self) {} + fn reset(&mut self); } diff --git a/src/online/paraformer.rs b/crates/sherpa-rs/src/online/paraformer.rs similarity index 100% rename from src/online/paraformer.rs rename to crates/sherpa-rs/src/online/paraformer.rs diff --git a/src/online/stream/recognizer.rs b/crates/sherpa-rs/src/online/recognizer.rs similarity index 56% rename from src/online/stream/recognizer.rs rename to crates/sherpa-rs/src/online/recognizer.rs index 3c2a62c..5166f2f 100644 --- a/src/online/stream/recognizer.rs +++ b/crates/sherpa-rs/src/online/recognizer.rs @@ -3,6 +3,7 @@ use std::{ path::Path, }; +use eyre::Result; use sherpa_rs_sys::{ SherpaOnnxCreateOnlineRecognizer, SherpaOnnxCreateOnlineStream, SherpaOnnxFeatureConfig, SherpaOnnxOnlineCtcFstDecoderConfig, SherpaOnnxOnlineModelConfig, @@ -11,11 +12,11 @@ use sherpa_rs_sys::{ }; use crate::{ - get_default_provider, online::{paraformer::Paraformer, transducer::Transducer, zipformer2_ctc::Zipformer2Ctc}, + OnnxConfig, }; -use super::OnlineStream; +use super::Stream; use std::sync::Arc; @@ -26,7 +27,7 @@ pub enum Search { } impl Search { - fn into_raw(self) -> CString { + fn to_cstring(&self) -> CString { match self { Self::Greedy => CString::new("greedy_search").unwrap(), Self::Beam => CString::new("modified_beam_search").unwrap(), @@ -36,34 +37,33 @@ impl Search { #[derive(Debug)] pub struct Recognizer { - recognizer: *mut sherpa_rs_sys::SherpaOnnxOnlineRecognizer, + recognizer: *const sherpa_rs_sys::SherpaOnnxOnlineRecognizer, } -unsafe impl Send for Recognizer {} -unsafe impl Sync for Recognizer {} - impl Recognizer { pub fn from_transducer( transducer: Transducer, - provider: Option<&str>, tokens: &Path, search: Search, - debug: bool, - num_threads: Option, + onnx_config: OnnxConfig, hotwords: Option<&Path>, hotwords_score: Option, - ) -> Arc { + ) -> Result> { let tokens_c = CString::new(tokens.to_str().unwrap()).unwrap(); - let provider_c = CString::new(provider.unwrap_or(get_default_provider())).unwrap(); + let provider_c = CString::new(onnx_config.provider).unwrap(); + let decoding_method = search.to_cstring(); + let modeling_unit_c = CString::new("cjkchar").unwrap(); + let hotwords_c = + hotwords.map(|p| CString::new(p.to_str().unwrap_or(&p.to_string_lossy())).unwrap()); let mut model_config = unsafe { std::mem::zeroed::() }; model_config.transducer = transducer.as_config(); - model_config.tokens = tokens_c.into_raw(); - model_config.num_threads = num_threads.unwrap_or(1); - model_config.provider = provider_c.into_raw(); - model_config.debug = debug as i32; - model_config.modeling_unit = CString::new("cjkchar").unwrap().into_raw(); + model_config.tokens = tokens_c.as_ptr(); + model_config.num_threads = onnx_config.num_threads; + model_config.provider = provider_c.as_ptr(); + model_config.debug = onnx_config.debug as i32; + model_config.modeling_unit = modeling_unit_c.as_ptr(); let mut rec_config = unsafe { std::mem::zeroed::() }; rec_config.feat_config = SherpaOnnxFeatureConfig { @@ -71,46 +71,50 @@ impl Recognizer { feature_dim: 80, }; rec_config.model_config = model_config; - rec_config.decoding_method = search.into_raw().into_raw(); + rec_config.decoding_method = decoding_method.as_ptr(); rec_config.max_active_paths = 4; rec_config.enable_endpoint = 1; rec_config.rule1_min_trailing_silence = 2.4; rec_config.rule2_min_trailing_silence = 1.2; rec_config.rule3_min_utterance_length = 300.0; - if hotwords.is_some() { - let hotwords_c = - hotwords.map(|p| CString::new(p.to_str().unwrap()).unwrap().into_raw()); - rec_config.hotwords_file = hotwords_c.unwrap_or(std::ptr::null_mut()); + if let Some(ref hw) = hotwords_c { + rec_config.hotwords_file = hw.as_ptr(); rec_config.hotwords_score = hotwords_score.unwrap_or(1.5); } let recognizer = unsafe { SherpaOnnxCreateOnlineRecognizer(&rec_config) }; - Arc::new(Self { recognizer }) + if recognizer.is_null() { + eyre::bail!("Failed to create recognizer"); + } + Ok(Arc::new(Self { recognizer })) } pub fn from_paraformer( paraformer: Paraformer, - provider: Option<&str>, tokens: &Path, search: Search, - debug: bool, - num_threads: Option, + onnx_config: OnnxConfig, hotwords: Option<&Path>, hotwords_score: Option, - ) -> Arc { + ) -> Result> { let tokens_c = CString::new(tokens.to_str().unwrap()).unwrap(); - let provider_c = CString::new(provider.unwrap_or(get_default_provider())).unwrap(); + let provider_c = CString::new(onnx_config.provider).unwrap(); + let decoding_method_c = search.to_cstring(); + let model_type_c = paraformer.model_type(); + let modeling_unit_c = CString::new("cjkchar").unwrap(); + let hotwords_c = + hotwords.map(|p| CString::new(p.to_str().unwrap_or(&p.to_string_lossy())).unwrap()); let mut model_config = unsafe { std::mem::zeroed::() }; - model_config.model_type = paraformer.model_type().into_raw(); + model_config.model_type = model_type_c.as_ptr(); model_config.paraformer = paraformer.as_config(); - model_config.tokens = tokens_c.into_raw(); - model_config.num_threads = num_threads.unwrap_or(1); - model_config.provider = provider_c.into_raw(); - model_config.debug = debug as i32; - model_config.modeling_unit = CString::new("cjkchar").unwrap().into_raw(); + model_config.tokens = tokens_c.as_ptr(); + model_config.num_threads = onnx_config.num_threads; + model_config.provider = provider_c.as_ptr(); + model_config.debug = onnx_config.debug as i32; + model_config.modeling_unit = modeling_unit_c.as_ptr(); let mut rec_config = unsafe { std::mem::zeroed::() }; rec_config.feat_config = SherpaOnnxFeatureConfig { @@ -118,48 +122,51 @@ impl Recognizer { feature_dim: 80, }; rec_config.model_config = model_config; - rec_config.decoding_method = search.into_raw().into_raw(); + rec_config.decoding_method = decoding_method_c.as_ptr(); rec_config.max_active_paths = 4; rec_config.enable_endpoint = 1; rec_config.rule1_min_trailing_silence = 2.4; rec_config.rule2_min_trailing_silence = 1.2; rec_config.rule3_min_utterance_length = 300.0; - if hotwords.is_some() { - let hotwords_c = - hotwords.map(|p| CString::new(p.to_str().unwrap()).unwrap().into_raw()); - rec_config.hotwords_file = hotwords_c.unwrap_or(std::ptr::null_mut()); + if let Some(ref hw) = hotwords_c { + rec_config.hotwords_file = hw.as_ptr(); rec_config.hotwords_score = hotwords_score.unwrap_or(1.5); } let recognizer = unsafe { SherpaOnnxCreateOnlineRecognizer(&rec_config) }; - Arc::new(Self { recognizer }) + if recognizer.is_null() { + eyre::bail!("Failed to create recognizer"); + } + + Ok(Arc::new(Self { recognizer })) } pub fn from_zipformer( zipformer: Zipformer2Ctc, - provider: Option<&str>, tokens: &Path, + graph: &Path, search: Search, - debug: bool, - num_threads: Option, - graph: Option<&Path>, + onnx_config: OnnxConfig, hotwords: Option<&Path>, hotwords_score: Option, - ) -> Arc { - let tokens_c = CString::new(tokens.to_str().unwrap()).unwrap(); - let provider_c = CString::new(provider.unwrap_or(get_default_provider())).unwrap(); - let graph_c = CString::new(graph.unwrap().to_str().unwrap()).unwrap(); + ) -> Result> { + let tokens_c = CString::new(tokens.to_str().unwrap_or(&tokens.to_string_lossy())).unwrap(); + let provider_c = CString::new(onnx_config.provider).unwrap(); + let graph_c = CString::new(graph.to_str().unwrap_or(&graph.to_string_lossy())).unwrap(); + let hotwords_c = + hotwords.map(|p| CString::new(p.to_str().unwrap_or(&p.to_string_lossy())).unwrap()); + let decoding_method_c = search.to_cstring(); + let modeling_unit_c = CString::new("cjkchar").unwrap(); let mut model_config = unsafe { std::mem::zeroed::() }; - // model_config.model_type = zipformer.model_type().into_raw(); model_config.zipformer2_ctc = zipformer.as_config(); - model_config.tokens = tokens_c.into_raw(); - model_config.num_threads = num_threads.unwrap_or(1); - model_config.provider = provider_c.into_raw(); - model_config.debug = debug as i32; - model_config.modeling_unit = CString::new("cjkchar").unwrap().into_raw(); + model_config.tokens = tokens_c.as_ptr(); + model_config.num_threads = onnx_config.num_threads; + model_config.provider = provider_c.as_ptr(); + model_config.debug = onnx_config.debug as i32; + model_config.modeling_unit = modeling_unit_c.as_ptr(); let mut rec_config = unsafe { std::mem::zeroed::() }; rec_config.feat_config = SherpaOnnxFeatureConfig { @@ -167,29 +174,38 @@ impl Recognizer { feature_dim: 80, }; rec_config.model_config = model_config; - rec_config.decoding_method = search.into_raw().into_raw(); + rec_config.decoding_method = decoding_method_c.as_ptr(); rec_config.max_active_paths = 4; rec_config.enable_endpoint = 1; rec_config.rule1_min_trailing_silence = 2.4; rec_config.rule2_min_trailing_silence = 1.2; rec_config.rule3_min_utterance_length = 300.0; rec_config.ctc_fst_decoder_config = SherpaOnnxOnlineCtcFstDecoderConfig { - graph: graph_c.into_raw(), + graph: graph_c.as_ptr(), max_active: 3000, }; - if hotwords.is_some() { - let hotwords_c = - hotwords.map(|p| CString::new(p.to_str().unwrap()).unwrap().into_raw()); - rec_config.hotwords_file = hotwords_c.unwrap_or(std::ptr::null_mut()); + if let Some(ref hw) = hotwords_c { + rec_config.hotwords_file = hw.as_ptr(); rec_config.hotwords_score = hotwords_score.unwrap_or(1.5); } let recognizer = unsafe { SherpaOnnxCreateOnlineRecognizer(&rec_config) }; - Arc::new(Self { recognizer }) + if recognizer.is_null() { + eyre::bail!("Failed to create recognizer"); + } + + Ok(Arc::new(Self { recognizer })) + } + + pub fn create_stream(self: &Arc) -> Result { + RecognizerStream::new(Arc::clone(self)) } } +unsafe impl Send for Recognizer {} +unsafe impl Sync for Recognizer {} + impl Drop for Recognizer { fn drop(&mut self) { unsafe { @@ -198,57 +214,38 @@ impl Drop for Recognizer { } } -pub struct Stream { +pub struct RecognizerStream { recognizer: Arc, - stream: *mut sherpa_rs_sys::SherpaOnnxOnlineStream, - // display: *mut sherpa_rs_sys::SherpaOnnxDisplay, + stream: *const sherpa_rs_sys::SherpaOnnxOnlineStream, } -unsafe impl Send for Stream {} -unsafe impl Sync for Stream {} - -impl Stream { - pub fn from_recognizer( - recognizer: Arc, - // display: bool - ) -> Self { - let recognizer = recognizer.clone(); +impl RecognizerStream { + pub fn new(recognizer: Arc) -> Result { let stream = unsafe { SherpaOnnxCreateOnlineStream(recognizer.recognizer) }; - // let display = if display { - // unsafe { SherpaOnnxCreateDisplay() } - // } else { - // std::ptr::null_mut() - // }; - - println!("recognizer: {:?}, stream: {:?}", recognizer, stream); - Self { - recognizer, - stream, - // display + if stream.is_null() { + eyre::bail!("Failed to create stream"); } + + Ok(Self { recognizer, stream }) } } -impl Drop for Stream { +impl Drop for RecognizerStream { fn drop(&mut self) { unsafe { - // if self.display != std::ptr::null_mut() { - // sherpa_rs_sys::SherpaOnnxDestroyDisplay(self.display); - // } - sherpa_rs_sys::SherpaOnnxDestroyOnlineStream(self.stream); } } } -impl OnlineStream for Stream { - fn accept_waveform(&mut self, sample_rate: i32, samples: Vec) { +impl Stream for RecognizerStream { + fn accept_waveform(&mut self, sample_rate: i32, samples: impl AsRef<[f32]>) { unsafe { sherpa_rs_sys::SherpaOnnxOnlineStreamAcceptWaveform( self.stream, sample_rate, - samples.as_ptr(), - samples.len() as i32, + samples.as_ref().as_ptr(), + samples.as_ref().len() as i32, ); } } @@ -268,19 +265,15 @@ impl OnlineStream for Stream { fn get_result(&mut self) -> String { unsafe { - let result = sherpa_rs_sys::SherpaOnnxGetOnlineStreamResult( + let raw_result = sherpa_rs_sys::SherpaOnnxGetOnlineStreamResult( self.recognizer.recognizer, self.stream, ); - let raw_result = result.read(); - let text = CStr::from_ptr(raw_result.text); - let text = text.to_str().unwrap().to_string(); - // let timestamps: &[f32] = - // std::slice::from_raw_parts(raw_result.timestamps, raw_result.count as usize); - let str = text; - // Free - sherpa_rs_sys::SherpaOnnxDestroyOnlineRecognizerResult(result); - str + let result = raw_result.read(); + let text = CStr::from_ptr(result.text).to_string_lossy().to_string(); + + sherpa_rs_sys::SherpaOnnxDestroyOnlineRecognizerResult(raw_result); + text } } diff --git a/src/online/transducer.rs b/crates/sherpa-rs/src/online/transducer.rs similarity index 100% rename from src/online/transducer.rs rename to crates/sherpa-rs/src/online/transducer.rs diff --git a/src/online/zipformer2_ctc.rs b/crates/sherpa-rs/src/online/zipformer2_ctc.rs similarity index 100% rename from src/online/zipformer2_ctc.rs rename to crates/sherpa-rs/src/online/zipformer2_ctc.rs diff --git a/crates/sherpa-rs/src/paraformer.rs b/crates/sherpa-rs/src/paraformer.rs new file mode 100644 index 0000000..e26a603 --- /dev/null +++ b/crates/sherpa-rs/src/paraformer.rs @@ -0,0 +1,136 @@ +use crate::{get_default_provider, utils::cstring_from_str}; +use eyre::{bail, Result}; +use std::{mem, ptr::null}; + +#[derive(Debug)] +pub struct ParaformerRecognizer { + recognizer: *const sherpa_rs_sys::SherpaOnnxOfflineRecognizer, +} + +pub type ParaformerRecognizerResult = super::OfflineRecognizerResult; + +#[derive(Debug, Clone)] +pub struct ParaformerConfig { + pub model: String, + pub tokens: String, + pub provider: Option, + pub num_threads: Option, + pub debug: bool, +} + +impl Default for ParaformerConfig { + fn default() -> Self { + Self { + model: String::new(), + tokens: String::new(), + debug: false, + provider: None, + num_threads: Some(1), + } + } +} + +impl ParaformerRecognizer { + pub fn new(config: ParaformerConfig) -> Result { + let debug = config.debug.into(); + let provider = config.provider.unwrap_or(get_default_provider()); + + // Prepare C strings + let provider_ptr = cstring_from_str(&provider); + let model_ptr = cstring_from_str(&config.model); + let tokens_ptr = cstring_from_str(&config.tokens); + + // 创建 decoding_method 的 CString 对象并绑定到变量 + let decoding_method_ptr = cstring_from_str("greedy_search"); + + // Paraformer model config + let paraformer_config = sherpa_rs_sys::SherpaOnnxOfflineParaformerModelConfig { + model: model_ptr.as_ptr(), + }; + + // Offline model config + let model_config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineModelConfig { + debug, + num_threads: config.num_threads.unwrap_or(1), + provider: provider_ptr.as_ptr(), + tokens: tokens_ptr.as_ptr(), + paraformer: paraformer_config, + + // Null other model types + bpe_vocab: mem::zeroed::<_>(), + model_type: mem::zeroed::<_>(), + modeling_unit: mem::zeroed::<_>(), + nemo_ctc: mem::zeroed::<_>(), + tdnn: mem::zeroed::<_>(), + telespeech_ctc: null(), + fire_red_asr: mem::zeroed::<_>(), + transducer: mem::zeroed::<_>(), + whisper: mem::zeroed::<_>(), + sense_voice: mem::zeroed::<_>(), + moonshine: mem::zeroed::<_>(), + dolphin: mem::zeroed::<_>(), + } + }; + + // Recognizer config + let recognizer_config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineRecognizerConfig { + decoding_method: decoding_method_ptr.as_ptr(), + feat_config: sherpa_rs_sys::SherpaOnnxFeatureConfig { + sample_rate: 16000, + feature_dim: 80, + }, + model_config, + hotwords_file: null(), + hotwords_score: 0.0, + lm_config: mem::zeroed::<_>(), + max_active_paths: 0, + rule_fars: null(), + rule_fsts: null(), + blank_penalty: 0.0, + hr: mem::zeroed::<_>(), + } + }; + + let recognizer = + unsafe { sherpa_rs_sys::SherpaOnnxCreateOfflineRecognizer(&recognizer_config) }; + if recognizer.is_null() { + bail!("Failed to create Paraformer recognizer"); + } + + Ok(Self { recognizer }) + } + + pub fn transcribe(&mut self, sample_rate: u32, samples: &[f32]) -> ParaformerRecognizerResult { + unsafe { + let stream = sherpa_rs_sys::SherpaOnnxCreateOfflineStream(self.recognizer); + sherpa_rs_sys::SherpaOnnxAcceptWaveformOffline( + stream, + sample_rate as i32, + samples.as_ptr(), + samples.len() as i32, + ); + sherpa_rs_sys::SherpaOnnxDecodeOfflineStream(self.recognizer, stream); + let result_ptr = sherpa_rs_sys::SherpaOnnxGetOfflineStreamResult(stream); + let raw_result = result_ptr.read(); + let result = ParaformerRecognizerResult::new(&raw_result); + + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizerResult(result_ptr); + sherpa_rs_sys::SherpaOnnxDestroyOfflineStream(stream); + + result + } + } +} + +unsafe impl Send for ParaformerRecognizer {} +unsafe impl Sync for ParaformerRecognizer {} + +impl Drop for ParaformerRecognizer { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizer(self.recognizer); + } + } +} diff --git a/crates/sherpa-rs/src/punctuate.rs b/crates/sherpa-rs/src/punctuate.rs new file mode 100644 index 0000000..5045473 --- /dev/null +++ b/crates/sherpa-rs/src/punctuate.rs @@ -0,0 +1,70 @@ +use eyre::{bail, Result}; + +use crate::{ + get_default_provider, + utils::{cstr_to_string, cstring_from_str}, +}; + +#[derive(Debug, Default, Clone)] +pub struct PunctuationConfig { + pub model: String, + pub debug: bool, + pub num_threads: Option, + pub provider: Option, +} + +pub struct Punctuation { + audio_punctuation: *const sherpa_rs_sys::SherpaOnnxOfflinePunctuation, +} + +impl Punctuation { + pub fn new(config: PunctuationConfig) -> Result { + let model = cstring_from_str(&config.model); + let provider = cstring_from_str(&config.provider.unwrap_or(if cfg!(target_os = "macos") { + // TODO: sherpa-onnx/issues/1448 + "cpu".into() + } else { + get_default_provider() + })); + + let sherpa_config = sherpa_rs_sys::SherpaOnnxOfflinePunctuationConfig { + model: sherpa_rs_sys::SherpaOnnxOfflinePunctuationModelConfig { + ct_transformer: model.as_ptr(), + num_threads: config.num_threads.unwrap_or(1), + debug: config.debug.into(), + provider: provider.as_ptr(), + }, + }; + let audio_punctuation = + unsafe { sherpa_rs_sys::SherpaOnnxCreateOfflinePunctuation(&sherpa_config) }; + + if audio_punctuation.is_null() { + bail!("Failed to create audio punctuation"); + } + Ok(Self { audio_punctuation }) + } + + pub fn add_punctuation(&mut self, text: &str) -> String { + let text = cstring_from_str(text); + unsafe { + let text_with_punct_ptr = sherpa_rs_sys::SherpaOfflinePunctuationAddPunct( + self.audio_punctuation, + text.as_ptr(), + ); + let text_with_punct = cstr_to_string(text_with_punct_ptr as _); + sherpa_rs_sys::SherpaOfflinePunctuationFreeText(text_with_punct_ptr); + text_with_punct + } + } +} + +unsafe impl Send for Punctuation {} +unsafe impl Sync for Punctuation {} + +impl Drop for Punctuation { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflinePunctuation(self.audio_punctuation); + } + } +} diff --git a/crates/sherpa-rs/src/sense_voice.rs b/crates/sherpa-rs/src/sense_voice.rs new file mode 100644 index 0000000..44365ce --- /dev/null +++ b/crates/sherpa-rs/src/sense_voice.rs @@ -0,0 +1,141 @@ +use crate::{get_default_provider, utils::cstring_from_str}; +use eyre::{bail, Result}; +use std::mem; + +#[derive(Debug)] +pub struct SenseVoiceRecognizer { + recognizer: *const sherpa_rs_sys::SherpaOnnxOfflineRecognizer, +} + +pub type SenseVoiceRecognizerResult = super::OfflineRecognizerResult; + +#[derive(Debug, Clone)] +pub struct SenseVoiceConfig { + pub model: String, + pub language: String, + pub use_itn: bool, + pub provider: Option, + pub num_threads: Option, + pub debug: bool, + pub tokens: String, +} + +impl Default for SenseVoiceConfig { + fn default() -> Self { + Self { + model: String::new(), + language: "auto".into(), + use_itn: true, + provider: None, + num_threads: Some(1), + debug: false, + tokens: String::new(), + } + } +} + +impl SenseVoiceRecognizer { + pub fn new(config: SenseVoiceConfig) -> Result { + let debug = config.debug.into(); + let provider = config.provider.unwrap_or(get_default_provider()); + let provider_ptr = cstring_from_str(&provider); + let num_threads = config.num_threads.unwrap_or(1); + + // SenseVoice specific config + let model_ptr = cstring_from_str(&config.model); + let language_ptr = cstring_from_str(&config.language); + let use_itn = if config.use_itn { 1 } else { 0 }; + + let sense_voice_config = sherpa_rs_sys::SherpaOnnxOfflineSenseVoiceModelConfig { + model: model_ptr.as_ptr(), + language: language_ptr.as_ptr(), + use_itn, + }; + + // General model config + let tokens_ptr = cstring_from_str(&config.tokens); + let model_config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineModelConfig { + tokens: tokens_ptr.as_ptr(), + provider: provider_ptr.as_ptr(), + num_threads, + debug, + sense_voice: sense_voice_config, + // Other fields set to default/null + bpe_vocab: mem::zeroed::<_>(), + model_type: mem::zeroed::<_>(), + modeling_unit: mem::zeroed::<_>(), + nemo_ctc: mem::zeroed::<_>(), + paraformer: mem::zeroed::<_>(), + tdnn: mem::zeroed::<_>(), + telespeech_ctc: mem::zeroed::<_>(), + fire_red_asr: mem::zeroed::<_>(), + transducer: mem::zeroed::<_>(), + whisper: mem::zeroed::<_>(), + moonshine: mem::zeroed::<_>(), + dolphin: mem::zeroed::<_>(), + } + }; + + // Recognizer config + let config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineRecognizerConfig { + decoding_method: mem::zeroed::<_>(), + feat_config: sherpa_rs_sys::SherpaOnnxFeatureConfig { + sample_rate: 16000, + feature_dim: 80, + }, + hotwords_file: mem::zeroed::<_>(), + hotwords_score: 0.0, + lm_config: sherpa_rs_sys::SherpaOnnxOfflineLMConfig { + model: mem::zeroed::<_>(), + scale: 0.0, + }, + max_active_paths: 0, + model_config, + rule_fars: mem::zeroed::<_>(), + rule_fsts: mem::zeroed::<_>(), + blank_penalty: 0.0, + hr: mem::zeroed::<_>(), + } + }; + + let recognizer = unsafe { sherpa_rs_sys::SherpaOnnxCreateOfflineRecognizer(&config) }; + if recognizer.is_null() { + bail!("Failed to create recognizer"); + } + + Ok(Self { recognizer }) + } + + pub fn transcribe(&mut self, sample_rate: u32, samples: &[f32]) -> SenseVoiceRecognizerResult { + unsafe { + let stream = sherpa_rs_sys::SherpaOnnxCreateOfflineStream(self.recognizer); + sherpa_rs_sys::SherpaOnnxAcceptWaveformOffline( + stream, + sample_rate as i32, + samples.as_ptr(), + samples.len().try_into().unwrap(), + ); + sherpa_rs_sys::SherpaOnnxDecodeOfflineStream(self.recognizer, stream); + let result_ptr = sherpa_rs_sys::SherpaOnnxGetOfflineStreamResult(stream); + let raw_result = result_ptr.read(); + let result = SenseVoiceRecognizerResult::new(&raw_result); + // Free resources + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizerResult(result_ptr); + sherpa_rs_sys::SherpaOnnxDestroyOfflineStream(stream); + result + } + } +} + +unsafe impl Send for SenseVoiceRecognizer {} +unsafe impl Sync for SenseVoiceRecognizer {} + +impl Drop for SenseVoiceRecognizer { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizer(self.recognizer); + } + } +} diff --git a/src/speaker_id.rs b/crates/sherpa-rs/src/speaker_id.rs similarity index 70% rename from src/speaker_id.rs rename to crates/sherpa-rs/src/speaker_id.rs index 35c3512..4f78fdd 100644 --- a/src/speaker_id.rs +++ b/crates/sherpa-rs/src/speaker_id.rs @@ -1,15 +1,17 @@ use eyre::{bail, Result}; -use std::{ffi::CString, path::PathBuf}; +use std::path::PathBuf; -use crate::get_default_provider; +use crate::{get_default_provider, utils::cstring_from_str}; /// If similarity is greater or equal to thresold than it's a match! pub const DEFAULT_SIMILARITY_THRESHOLD: f32 = 0.5; -#[derive(Debug)] +#[derive(Debug, Default)] pub struct ExtractorConfig { - pub(crate) cfg: sherpa_rs_sys::SherpaOnnxSpeakerEmbeddingExtractorConfig, - model: String, + pub model: String, + pub provider: Option, + pub num_threads: Option, + pub debug: bool, } #[derive(Debug)] @@ -18,40 +20,28 @@ pub struct EmbeddingExtractor { pub embedding_size: usize, } -impl ExtractorConfig { - pub fn new( - model: String, - provider: Option<&str>, - num_threads: Option, - debug: bool, - ) -> Self { - let provider = provider.unwrap_or(get_default_provider()); - let num_threads = num_threads.unwrap_or(2); - let debug = if debug { 1 } else { 0 }; - let model_cstr = CString::new(model.clone()).unwrap(); - let provider = CString::new(provider).unwrap(); - let cfg = sherpa_rs_sys::SherpaOnnxSpeakerEmbeddingExtractorConfig { - debug, - model: model_cstr.into_raw(), - num_threads, - provider: provider.into_raw(), - }; - Self { cfg, model } - } +impl EmbeddingExtractor { + pub fn new(config: ExtractorConfig) -> Result { + let provider = config.provider.unwrap_or(get_default_provider()); - pub fn as_ptr(&self) -> *const sherpa_rs_sys::SherpaOnnxSpeakerEmbeddingExtractorConfig { - &self.cfg - } -} + let num_threads = config.num_threads.unwrap_or(1); + let debug = config.debug.into(); -impl EmbeddingExtractor { - pub fn new_from_config(config: ExtractorConfig) -> Result { let model_path = PathBuf::from(&config.model); if !model_path.exists() { bail!("model not found at {}", model_path.display()) } + let model = cstring_from_str(&config.model); + let provider = cstring_from_str(&provider); + + let extractor_config = sherpa_rs_sys::SherpaOnnxSpeakerEmbeddingExtractorConfig { + debug, + model: model.as_ptr(), + num_threads: num_threads as i32, + provider: provider.as_ptr(), + }; let extractor = - unsafe { sherpa_rs_sys::SherpaOnnxCreateSpeakerEmbeddingExtractor(config.as_ptr()) }; + unsafe { sherpa_rs_sys::SherpaOnnxCreateSpeakerEmbeddingExtractor(&extractor_config) }; // Assume embedding size is known or can be retrieved let embedding_size = unsafe { sherpa_rs_sys::SherpaOnnxSpeakerEmbeddingExtractorDim(extractor) } @@ -65,8 +55,8 @@ impl EmbeddingExtractor { pub fn compute_speaker_embedding( &mut self, - sample_rate: i32, samples: Vec, + sample_rate: u32, ) -> Result> { unsafe { let stream = @@ -77,7 +67,7 @@ impl EmbeddingExtractor { sherpa_rs_sys::SherpaOnnxOnlineStreamAcceptWaveform( stream, - sample_rate, + sample_rate as i32, samples.as_ptr(), samples.len() as i32, ); @@ -94,7 +84,7 @@ impl EmbeddingExtractor { if embedding_ptr.is_null() { bail!("Failed to compute speaker embedding"); } - log::debug!("using dimensions {}", self.embedding_size); + tracing::debug!("using dimensions {}", self.embedding_size); let embedding = std::slice::from_raw_parts(embedding_ptr, self.embedding_size).to_vec(); // Free sherpa_rs_sys::SherpaOnnxDestroyOnlineStream(stream); diff --git a/crates/sherpa-rs/src/transducer.rs b/crates/sherpa-rs/src/transducer.rs new file mode 100644 index 0000000..5a1ca04 --- /dev/null +++ b/crates/sherpa-rs/src/transducer.rs @@ -0,0 +1,156 @@ +use crate::utils::cstr_to_string; +use crate::{get_default_provider, utils::cstring_from_str}; +use eyre::{bail, Result}; +use std::mem; + +pub struct TransducerRecognizer { + recognizer: *const sherpa_rs_sys::SherpaOnnxOfflineRecognizer, +} + +#[derive(Debug, Clone)] +pub struct TransducerConfig { + pub decoder: String, + pub encoder: String, + pub joiner: String, + pub tokens: String, + pub num_threads: i32, + pub sample_rate: i32, + pub feature_dim: i32, + pub decoding_method: String, + pub hotwords_file: String, + pub hotwords_score: f32, + pub modeling_unit: String, + pub bpe_vocab: String, + pub blank_penalty: f32, + pub model_type: String, + pub debug: bool, + pub provider: Option, +} + +impl Default for TransducerConfig { + fn default() -> Self { + TransducerConfig { + decoder: String::new(), + encoder: String::new(), + joiner: String::new(), + tokens: String::new(), + model_type: String::from("transducer"), + num_threads: 1, + sample_rate: 0, + feature_dim: 0, + decoding_method: String::new(), + hotwords_file: String::new(), + hotwords_score: 0.0, + modeling_unit: String::new(), + bpe_vocab: String::new(), + blank_penalty: 0.0, + debug: false, + provider: None, + } + } +} + +impl TransducerRecognizer { + pub fn new(config: TransducerConfig) -> Result { + let recognizer = unsafe { + let debug = config.debug.into(); + let provider = config.provider.unwrap_or(get_default_provider()); + let provider_ptr = cstring_from_str(&provider); + + let encoder = cstring_from_str(&config.encoder); + let decoder = cstring_from_str(&config.decoder); + let joiner = cstring_from_str(&config.joiner); + let model_type = cstring_from_str(&config.model_type); + let modeling_unit = cstring_from_str(&config.modeling_unit); + let bpe_vocab = cstring_from_str(&config.bpe_vocab); + let hotwords_file = cstring_from_str(&config.hotwords_file); + let tokens = cstring_from_str(&config.tokens); + let decoding_method = cstring_from_str(&config.decoding_method); + + let offline_model_config = sherpa_rs_sys::SherpaOnnxOfflineModelConfig { + transducer: sherpa_rs_sys::SherpaOnnxOfflineTransducerModelConfig { + encoder: encoder.as_ptr(), + decoder: decoder.as_ptr(), + joiner: joiner.as_ptr(), + }, + tokens: tokens.as_ptr(), + num_threads: config.num_threads, + debug, + provider: provider_ptr.as_ptr(), + model_type: model_type.as_ptr(), + modeling_unit: modeling_unit.as_ptr(), + bpe_vocab: bpe_vocab.as_ptr(), + + // NULLs + telespeech_ctc: mem::zeroed::<_>(), + paraformer: mem::zeroed::<_>(), + tdnn: mem::zeroed::<_>(), + nemo_ctc: mem::zeroed::<_>(), + whisper: mem::zeroed::<_>(), + sense_voice: mem::zeroed::<_>(), + moonshine: mem::zeroed::<_>(), + fire_red_asr: mem::zeroed::<_>(), + dolphin: mem::zeroed::<_>(), + }; + + let recognizer_config = sherpa_rs_sys::SherpaOnnxOfflineRecognizerConfig { + model_config: offline_model_config, + feat_config: sherpa_rs_sys::SherpaOnnxFeatureConfig { + sample_rate: config.sample_rate, + feature_dim: config.feature_dim, + }, + hotwords_file: hotwords_file.as_ptr(), + blank_penalty: config.blank_penalty, + decoding_method: decoding_method.as_ptr(), + hotwords_score: config.hotwords_score, + + // NULLs + lm_config: mem::zeroed::<_>(), + rule_fsts: mem::zeroed::<_>(), + rule_fars: mem::zeroed::<_>(), + max_active_paths: mem::zeroed::<_>(), + hr: mem::zeroed::<_>(), + }; + + let recognizer = sherpa_rs_sys::SherpaOnnxCreateOfflineRecognizer(&recognizer_config); + if recognizer.is_null() { + bail!("SherpaOnnxCreateOfflineRecognizer failed"); + } + recognizer + }; + + Ok(Self { recognizer }) + } + + pub fn transcribe(&mut self, sample_rate: u32, samples: &[f32]) -> String { + unsafe { + let stream = sherpa_rs_sys::SherpaOnnxCreateOfflineStream(self.recognizer); + sherpa_rs_sys::SherpaOnnxAcceptWaveformOffline( + stream, + sample_rate as i32, + samples.as_ptr(), + samples.len().try_into().unwrap(), + ); + sherpa_rs_sys::SherpaOnnxDecodeOfflineStream(self.recognizer, stream); + let result_ptr = sherpa_rs_sys::SherpaOnnxGetOfflineStreamResult(stream); + let raw_result = result_ptr.read(); + let text = cstr_to_string(raw_result.text as _); + + // Free + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizerResult(result_ptr); + sherpa_rs_sys::SherpaOnnxDestroyOfflineStream(stream); + text + } + } +} + +unsafe impl Send for TransducerRecognizer {} +unsafe impl Sync for TransducerRecognizer {} + +impl Drop for TransducerRecognizer { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizer(self.recognizer); + } + } +} diff --git a/crates/sherpa-rs/src/tts/kokoro.rs b/crates/sherpa-rs/src/tts/kokoro.rs new file mode 100644 index 0000000..26dab65 --- /dev/null +++ b/crates/sherpa-rs/src/tts/kokoro.rs @@ -0,0 +1,83 @@ +use std::{mem, ptr::null}; + +use crate::{utils::cstring_from_str, OnnxConfig}; +use eyre::Result; +use sherpa_rs_sys; + +use super::{CommonTtsConfig, TtsAudio}; + +pub struct KokoroTts { + tts: *const sherpa_rs_sys::SherpaOnnxOfflineTts, +} + +#[derive(Default)] +pub struct KokoroTtsConfig { + pub model: String, + pub voices: String, + pub tokens: String, + pub data_dir: String, + pub dict_dir: String, + pub lexicon: String, + pub length_scale: f32, + pub onnx_config: OnnxConfig, + pub common_config: CommonTtsConfig, +} + +impl KokoroTts { + pub fn new(config: KokoroTtsConfig) -> Self { + let tts = unsafe { + let model = cstring_from_str(&config.model); + let voices = cstring_from_str(&config.voices); + let tokens = cstring_from_str(&config.tokens); + let data_dir = cstring_from_str(&config.data_dir); + let dict_dir = cstring_from_str(&config.dict_dir); + let lexicon = cstring_from_str(&config.lexicon); + + let provider = cstring_from_str(&config.onnx_config.provider); + + let tts_config = config.common_config.to_raw(); + + let model_config = sherpa_rs_sys::SherpaOnnxOfflineTtsModelConfig { + vits: mem::zeroed::<_>(), + num_threads: config.onnx_config.num_threads, + debug: config.onnx_config.debug.into(), + provider: provider.as_ptr(), + matcha: mem::zeroed::<_>(), + kokoro: sherpa_rs_sys::SherpaOnnxOfflineTtsKokoroModelConfig { + model: model.as_ptr(), + voices: voices.as_ptr(), + tokens: tokens.as_ptr(), + data_dir: data_dir.as_ptr(), + length_scale: config.length_scale, + dict_dir: dict_dir.as_ptr(), + lexicon: lexicon.as_ptr(), + }, + }; + let config = sherpa_rs_sys::SherpaOnnxOfflineTtsConfig { + max_num_sentences: config.common_config.max_num_sentences, + model: model_config, + rule_fars: tts_config.rule_fars.map(|v| v.as_ptr()).unwrap_or(null()), + rule_fsts: tts_config.rule_fsts.map(|v| v.as_ptr()).unwrap_or(null()), + silence_scale: config.common_config.silence_scale, + }; + sherpa_rs_sys::SherpaOnnxCreateOfflineTts(&config) + }; + + Self { tts } + } + + pub fn create(&mut self, text: &str, sid: i32, speed: f32) -> Result { + unsafe { super::create(self.tts, text, sid, speed) } + } +} + +unsafe impl Send for KokoroTts {} +unsafe impl Sync for KokoroTts {} + +impl Drop for KokoroTts { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflineTts(self.tts); + } + } +} diff --git a/crates/sherpa-rs/src/tts/matcha.rs b/crates/sherpa-rs/src/tts/matcha.rs new file mode 100644 index 0000000..ab1dd76 --- /dev/null +++ b/crates/sherpa-rs/src/tts/matcha.rs @@ -0,0 +1,90 @@ +use std::{mem, ptr::null}; + +use crate::{utils::cstring_from_str, OnnxConfig}; +use eyre::Result; +use sherpa_rs_sys; + +use super::{CommonTtsConfig, TtsAudio}; + +pub struct MatchaTts { + tts: *const sherpa_rs_sys::SherpaOnnxOfflineTts, +} + +#[derive(Default)] +pub struct MatchaTtsConfig { + pub model: String, + pub lexicon: String, + pub dict_dir: String, + pub tokens: String, + pub data_dir: String, + pub acoustic_model: String, + pub vocoder: String, + pub length_scale: f32, + pub noise_scale: f32, + pub noise_scale_w: f32, + pub silence_scale: f32, + + pub common_config: CommonTtsConfig, + pub onnx_config: OnnxConfig, +} + +impl MatchaTts { + pub fn new(config: MatchaTtsConfig) -> Self { + let tts = unsafe { + let tokens = cstring_from_str(&config.tokens); + let data_dir = cstring_from_str(&config.data_dir); + let lexicon = cstring_from_str(&config.lexicon); + let dict_dir = cstring_from_str(&config.dict_dir); + + let vocoder = cstring_from_str(&config.vocoder); + let acoustic_model = cstring_from_str(&config.acoustic_model); + + let provider = cstring_from_str(&config.onnx_config.provider); + + let tts_config = config.common_config.to_raw(); + + let model_config = sherpa_rs_sys::SherpaOnnxOfflineTtsModelConfig { + num_threads: config.onnx_config.num_threads, + vits: mem::zeroed::<_>(), + debug: config.onnx_config.debug.into(), + provider: provider.as_ptr(), + matcha: sherpa_rs_sys::SherpaOnnxOfflineTtsMatchaModelConfig { + acoustic_model: acoustic_model.as_ptr(), + vocoder: vocoder.as_ptr(), + lexicon: lexicon.as_ptr(), + tokens: tokens.as_ptr(), + data_dir: data_dir.as_ptr(), + noise_scale: config.noise_scale, + length_scale: config.length_scale, + dict_dir: dict_dir.as_ptr(), + }, + kokoro: mem::zeroed::<_>(), + }; + let config = sherpa_rs_sys::SherpaOnnxOfflineTtsConfig { + max_num_sentences: config.common_config.max_num_sentences, + model: model_config, + rule_fars: tts_config.rule_fars.map(|v| v.as_ptr()).unwrap_or(null()), + rule_fsts: tts_config.rule_fsts.map(|v| v.as_ptr()).unwrap_or(null()), + silence_scale: config.silence_scale, + }; + sherpa_rs_sys::SherpaOnnxCreateOfflineTts(&config) + }; + + Self { tts } + } + + pub fn create(&mut self, text: &str, sid: i32, speed: f32) -> Result { + unsafe { super::create(self.tts, text, sid, speed) } + } +} + +unsafe impl Send for MatchaTts {} +unsafe impl Sync for MatchaTts {} + +impl Drop for MatchaTts { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflineTts(self.tts); + } + } +} diff --git a/crates/sherpa-rs/src/tts/mod.rs b/crates/sherpa-rs/src/tts/mod.rs new file mode 100644 index 0000000..1a79341 --- /dev/null +++ b/crates/sherpa-rs/src/tts/mod.rs @@ -0,0 +1,94 @@ +mod kokoro; +mod matcha; +mod vits; + +use std::ffi::CString; + +use eyre::{bail, Result}; + +pub use kokoro::{KokoroTts, KokoroTtsConfig}; +pub use matcha::{MatchaTts, MatchaTtsConfig}; +pub use vits::{VitsTts, VitsTtsConfig}; + +use crate::utils::cstring_from_str; + +#[derive(Debug)] +pub struct TtsAudio { + pub samples: Vec, + pub sample_rate: u32, + pub duration: i32, +} + +#[derive(Default)] +pub struct CommonTtsConfig { + pub rule_fars: String, + pub rule_fsts: String, + pub max_num_sentences: i32, + pub silence_scale: f32, +} + +pub struct CommonTtsRaw { + pub rule_fars: Option, + pub rule_fsts: Option, + pub max_num_sentences: i32, +} + +impl CommonTtsConfig { + pub fn to_raw(&self) -> CommonTtsRaw { + let rule_fars = if self.rule_fars.is_empty() { + None + } else { + Some(cstring_from_str(&self.rule_fars)) + }; + + let rule_fsts = if self.rule_fsts.is_empty() { + None + } else { + Some(cstring_from_str(&self.rule_fsts)) + }; + + CommonTtsRaw { + rule_fars, + rule_fsts, + max_num_sentences: self.max_num_sentences, + } + } +} + +/// # Safety +/// +/// This function dereference sherpa_rs_sys::SherpaOnnxOfflineTts +pub unsafe fn create( + tts: *const sherpa_rs_sys::SherpaOnnxOfflineTts, + text: &str, + sid: i32, + speed: f32, +) -> Result { + let text = cstring_from_str(text); + let audio_ptr = sherpa_rs_sys::SherpaOnnxOfflineTtsGenerate(tts, text.as_ptr(), sid, speed); + + if audio_ptr.is_null() { + bail!("audio is null"); + } + let audio = audio_ptr.read(); + + if audio.n.is_negative() { + bail!("no samples found"); + } + if audio.samples.is_null() { + bail!("audio samples are null"); + } + let samples: &[f32] = std::slice::from_raw_parts(audio.samples, audio.n as usize); + let samples = samples.to_vec(); + let sample_rate = audio.sample_rate; + let duration = (samples.len() as i32) / sample_rate; + + // Free + sherpa_rs_sys::SherpaOnnxDestroyOfflineTtsGeneratedAudio(audio_ptr); + + Ok(TtsAudio { + samples, + sample_rate: sample_rate as u32, + duration, + }) +} diff --git a/crates/sherpa-rs/src/tts/vits.rs b/crates/sherpa-rs/src/tts/vits.rs new file mode 100644 index 0000000..70611d7 --- /dev/null +++ b/crates/sherpa-rs/src/tts/vits.rs @@ -0,0 +1,86 @@ +use std::{mem, ptr::null}; + +use crate::{utils::cstring_from_str, OnnxConfig}; +use eyre::Result; +use sherpa_rs_sys; + +use super::{CommonTtsConfig, TtsAudio}; + +pub struct VitsTts { + tts: *const sherpa_rs_sys::SherpaOnnxOfflineTts, +} + +#[derive(Default)] +pub struct VitsTtsConfig { + pub model: String, + pub lexicon: String, + pub dict_dir: String, + pub tokens: String, + pub data_dir: String, + pub length_scale: f32, + pub noise_scale: f32, + pub noise_scale_w: f32, + pub silence_scale: f32, + + pub onnx_config: OnnxConfig, + pub tts_config: CommonTtsConfig, +} + +impl VitsTts { + pub fn new(config: VitsTtsConfig) -> Self { + let tts = unsafe { + let model = cstring_from_str(&config.model); + let tokens = cstring_from_str(&config.tokens); + let data_dir = cstring_from_str(&config.data_dir); + let lexicon = cstring_from_str(&config.lexicon); + let dict_dir = cstring_from_str(&config.dict_dir); + + let provider = cstring_from_str(&config.onnx_config.provider); + + let tts_config = config.tts_config.to_raw(); + + let model_config = sherpa_rs_sys::SherpaOnnxOfflineTtsModelConfig { + num_threads: config.onnx_config.num_threads, + vits: sherpa_rs_sys::SherpaOnnxOfflineTtsVitsModelConfig { + model: model.as_ptr(), + lexicon: lexicon.as_ptr(), + tokens: tokens.as_ptr(), + data_dir: data_dir.as_ptr(), + noise_scale: config.noise_scale, + noise_scale_w: config.noise_scale_w, + length_scale: config.length_scale, + dict_dir: dict_dir.as_ptr(), + }, + debug: config.onnx_config.debug.into(), + provider: provider.as_ptr(), + matcha: mem::zeroed::<_>(), + kokoro: mem::zeroed::<_>(), + }; + let config = sherpa_rs_sys::SherpaOnnxOfflineTtsConfig { + max_num_sentences: config.tts_config.max_num_sentences, + model: model_config, + rule_fars: tts_config.rule_fars.map(|v| v.as_ptr()).unwrap_or(null()), + rule_fsts: tts_config.rule_fsts.map(|v| v.as_ptr()).unwrap_or(null()), + silence_scale: config.silence_scale, + }; + sherpa_rs_sys::SherpaOnnxCreateOfflineTts(&config) + }; + + Self { tts } + } + + pub fn create(&mut self, text: &str, sid: i32, speed: f32) -> Result { + unsafe { super::create(self.tts, text, sid, speed) } + } +} + +unsafe impl Send for VitsTts {} +unsafe impl Sync for VitsTts {} + +impl Drop for VitsTts { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflineTts(self.tts); + } + } +} diff --git a/crates/sherpa-rs/src/utils.rs b/crates/sherpa-rs/src/utils.rs new file mode 100644 index 0000000..8e15276 --- /dev/null +++ b/crates/sherpa-rs/src/utils.rs @@ -0,0 +1,13 @@ +use std::ffi::{c_char, CString}; + +pub fn cstring_from_str(s: &str) -> CString { + CString::new(s).expect("CString::new failed") +} + +pub unsafe fn cstr_to_string(ptr: *const c_char) -> String { + if ptr.is_null() { + String::new() + } else { + std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned() + } +} diff --git a/src/vad.rs b/crates/sherpa-rs/src/vad.rs similarity index 59% rename from src/vad.rs rename to crates/sherpa-rs/src/vad.rs index effadad..fb7e3d7 100644 --- a/src/vad.rs +++ b/crates/sherpa-rs/src/vad.rs @@ -1,54 +1,39 @@ -use crate::get_default_provider; +use crate::{get_default_provider, utils::cstring_from_str}; use eyre::Result; -use std::ffi::CString; #[derive(Debug)] -pub struct VadConfig { - pub(crate) cfg: sherpa_rs_sys::SherpaOnnxVadModelConfig, +pub struct Vad { + pub(crate) vad: *const sherpa_rs_sys::SherpaOnnxVoiceActivityDetector, } #[derive(Debug)] -pub struct Vad { - pub(crate) vad: *mut sherpa_rs_sys::SherpaOnnxVoiceActivityDetector, +pub struct VadConfig { + pub model: String, + pub min_silence_duration: f32, + pub min_speech_duration: f32, + pub max_speech_duration: f32, + pub threshold: f32, + pub sample_rate: u32, + pub window_size: i32, + pub provider: Option, + pub num_threads: Option, + pub debug: bool, } -impl VadConfig { - pub fn new( - model: String, - min_silence_duration: f32, - min_speech_duration: f32, - threshold: f32, - sample_rate: i32, - window_size: i32, - provider: Option<&str>, - num_threads: Option, - debug: Option, - ) -> Self { - let provider = provider.unwrap_or(get_default_provider()); - let provider = CString::new(provider).unwrap(); - let model = CString::new(model).unwrap(); - - let silero_vad = sherpa_rs_sys::SherpaOnnxSileroVadModelConfig { - model: model.into_raw(), - min_silence_duration, - min_speech_duration, - threshold, - window_size, - }; - let debug = debug.unwrap_or(false); - let debug = if debug { 1 } else { 0 }; - let cfg = sherpa_rs_sys::SherpaOnnxVadModelConfig { - debug, - provider: provider.into_raw(), - num_threads: num_threads.unwrap_or(1), - sample_rate, - silero_vad, - }; - Self { cfg } - } - - pub fn as_ptr(&self) -> *const sherpa_rs_sys::SherpaOnnxVadModelConfig { - &self.cfg +impl Default for VadConfig { + fn default() -> Self { + Self { + model: String::new(), + min_silence_duration: 0.5, + min_speech_duration: 0.5, + max_speech_duration: 0.5, + threshold: 0.5, + sample_rate: 16000, + window_size: 512, + provider: None, + num_threads: Some(1), + debug: false, + } } } @@ -59,12 +44,35 @@ pub struct SpeechSegment { } impl Vad { - pub fn new_from_config(config: VadConfig, buffer_size_in_seconds: f32) -> Result { + pub fn new(config: VadConfig, buffer_size_in_seconds: f32) -> Result { + let provider = config.provider.unwrap_or(get_default_provider()); + + let model = cstring_from_str(&config.model); + let provider = cstring_from_str(&provider); + + let silero_vad = sherpa_rs_sys::SherpaOnnxSileroVadModelConfig { + model: model.as_ptr(), + min_silence_duration: config.min_silence_duration, + min_speech_duration: config.min_speech_duration, + threshold: config.threshold, + window_size: config.window_size, + max_speech_duration: config.max_speech_duration, + }; + let debug = config.debug.into(); + let vad_config = sherpa_rs_sys::SherpaOnnxVadModelConfig { + debug, + provider: provider.as_ptr(), + num_threads: config.num_threads.unwrap_or(1), + sample_rate: config.sample_rate as i32, + silero_vad, + }; + unsafe { let vad = sherpa_rs_sys::SherpaOnnxCreateVoiceActivityDetector( - config.as_ptr(), + &vad_config, buffer_size_in_seconds, ); + Ok(Self { vad }) } } diff --git a/crates/sherpa-rs/src/whisper.rs b/crates/sherpa-rs/src/whisper.rs new file mode 100644 index 0000000..6d94ec7 --- /dev/null +++ b/crates/sherpa-rs/src/whisper.rs @@ -0,0 +1,150 @@ +use crate::{get_default_provider, utils::cstring_from_str}; +use eyre::{bail, Result}; +use std::mem; + +#[derive(Debug)] +pub struct WhisperRecognizer { + recognizer: *const sherpa_rs_sys::SherpaOnnxOfflineRecognizer, +} + +pub type WhisperRecognizerResult = super::OfflineRecognizerResult; + +#[derive(Debug, Clone)] +pub struct WhisperConfig { + pub decoder: String, + pub encoder: String, + pub tokens: String, + pub language: String, + pub bpe_vocab: Option, + pub tail_paddings: Option, + + pub provider: Option, + pub num_threads: Option, + pub debug: bool, +} + +impl Default for WhisperConfig { + fn default() -> Self { + Self { + decoder: String::new(), + encoder: String::new(), + tokens: String::new(), + language: String::from("en"), + bpe_vocab: None, + tail_paddings: None, + debug: false, + provider: None, + num_threads: Some(1), + } + } +} + +impl WhisperRecognizer { + pub fn new(config: WhisperConfig) -> Result { + let debug = config.debug.into(); + let provider = config.provider.unwrap_or(get_default_provider()); + + // Onnx + let provider_ptr = cstring_from_str(&provider); + let num_threads = config.num_threads.unwrap_or(2); + + // Whisper + let bpe_vocab_ptr = cstring_from_str(&config.bpe_vocab.unwrap_or("".into())); + let tail_paddings = config.tail_paddings.unwrap_or(0); + let decoder_ptr = cstring_from_str(&config.decoder); + let encoder_ptr = cstring_from_str(&config.encoder); + let language_ptr = cstring_from_str(&config.language); + let task_ptr = cstring_from_str("transcribe"); + let tokens_ptr = cstring_from_str(&config.tokens); + let decoding_method_ptr = cstring_from_str("greedy_search"); + + let whisper_config = sherpa_rs_sys::SherpaOnnxOfflineWhisperModelConfig { + decoder: decoder_ptr.as_ptr(), + encoder: encoder_ptr.as_ptr(), + language: language_ptr.as_ptr(), + task: task_ptr.as_ptr(), + tail_paddings, + }; + let model_config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineModelConfig { + whisper: whisper_config, + debug, + num_threads, + provider: provider_ptr.as_ptr(), + bpe_vocab: bpe_vocab_ptr.as_ptr(), + tokens: tokens_ptr.as_ptr(), + + // nulls + model_type: std::ptr::null(), + modeling_unit: mem::zeroed::<_>(), + nemo_ctc: mem::zeroed::<_>(), + paraformer: mem::zeroed::<_>(), + tdnn: mem::zeroed::<_>(), + telespeech_ctc: mem::zeroed::<_>(), + transducer: mem::zeroed::<_>(), + fire_red_asr: mem::zeroed::<_>(), + sense_voice: mem::zeroed::<_>(), + moonshine: mem::zeroed::<_>(), + dolphin: mem::zeroed::<_>(), + } + }; + + let config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineRecognizerConfig { + decoding_method: decoding_method_ptr.as_ptr(), // greedy_search, modified_beam_search + feat_config: sherpa_rs_sys::SherpaOnnxFeatureConfig { + sample_rate: 16000, + feature_dim: 512, + }, + model_config, + + hotwords_file: mem::zeroed::<_>(), + hotwords_score: mem::zeroed::<_>(), + lm_config: mem::zeroed::<_>(), + max_active_paths: mem::zeroed::<_>(), + rule_fars: mem::zeroed::<_>(), + rule_fsts: mem::zeroed::<_>(), + blank_penalty: mem::zeroed::<_>(), + hr: mem::zeroed::<_>(), + } + }; + let recognizer = unsafe { sherpa_rs_sys::SherpaOnnxCreateOfflineRecognizer(&config) }; + + if recognizer.is_null() { + bail!("Failed to create recognizer"); + } + + Ok(Self { recognizer }) + } + + pub fn transcribe(&mut self, sample_rate: u32, samples: &[f32]) -> WhisperRecognizerResult { + unsafe { + let stream = sherpa_rs_sys::SherpaOnnxCreateOfflineStream(self.recognizer); + sherpa_rs_sys::SherpaOnnxAcceptWaveformOffline( + stream, + sample_rate as i32, + samples.as_ptr(), + samples.len().try_into().unwrap(), + ); + sherpa_rs_sys::SherpaOnnxDecodeOfflineStream(self.recognizer, stream); + let result_ptr = sherpa_rs_sys::SherpaOnnxGetOfflineStreamResult(stream); + let raw_result = result_ptr.read(); + let result = WhisperRecognizerResult::new(&raw_result); + // Free + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizerResult(result_ptr); + sherpa_rs_sys::SherpaOnnxDestroyOfflineStream(stream); + result + } + } +} + +unsafe impl Send for WhisperRecognizer {} +unsafe impl Sync for WhisperRecognizer {} + +impl Drop for WhisperRecognizer { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizer(self.recognizer); + } + } +} diff --git a/crates/sherpa-rs/src/zipformer.rs b/crates/sherpa-rs/src/zipformer.rs new file mode 100644 index 0000000..13c7aa4 --- /dev/null +++ b/crates/sherpa-rs/src/zipformer.rs @@ -0,0 +1,120 @@ +use crate::{ + get_default_provider, + utils::{cstr_to_string, cstring_from_str}, +}; +use eyre::{bail, Result}; +use std::mem; + +#[derive(Debug, Default)] +pub struct ZipFormerConfig { + pub decoder: String, + pub encoder: String, + pub joiner: String, + pub tokens: String, + + pub num_threads: Option, + pub provider: Option, + pub debug: bool, +} + +pub struct ZipFormer { + recognizer: *const sherpa_rs_sys::SherpaOnnxOfflineRecognizer, +} + +impl ZipFormer { + pub fn new(config: ZipFormerConfig) -> Result { + // Zipformer config + let decoder_ptr = cstring_from_str(&config.decoder); + let encoder_ptr = cstring_from_str(&config.encoder); + let joiner_ptr = cstring_from_str(&config.joiner); + let provider_ptr = cstring_from_str(&config.provider.unwrap_or(get_default_provider())); + let tokens_ptr = cstring_from_str(&config.tokens); + let decoding_method_ptr = cstring_from_str("greedy_search"); + + let transcuder_config = sherpa_rs_sys::SherpaOnnxOfflineTransducerModelConfig { + decoder: decoder_ptr.as_ptr(), + encoder: encoder_ptr.as_ptr(), + joiner: joiner_ptr.as_ptr(), + }; + // Offline model config + let model_config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineModelConfig { + num_threads: config.num_threads.unwrap_or(1), + debug: config.debug.into(), + provider: provider_ptr.as_ptr(), + transducer: transcuder_config, + tokens: tokens_ptr.as_ptr(), + // NULLs + bpe_vocab: mem::zeroed::<_>(), + model_type: mem::zeroed::<_>(), + modeling_unit: mem::zeroed::<_>(), + paraformer: mem::zeroed::<_>(), + tdnn: mem::zeroed::<_>(), + fire_red_asr: mem::zeroed::<_>(), + telespeech_ctc: mem::zeroed::<_>(), + nemo_ctc: mem::zeroed::<_>(), + whisper: mem::zeroed::<_>(), + sense_voice: mem::zeroed::<_>(), + moonshine: mem::zeroed::<_>(), + dolphin: mem::zeroed::<_>(), + } + }; + // Recognizer config + let recognizer_config = unsafe { + sherpa_rs_sys::SherpaOnnxOfflineRecognizerConfig { + model_config, + decoding_method: decoding_method_ptr.as_ptr(), + // NULLs + blank_penalty: mem::zeroed::<_>(), + feat_config: mem::zeroed::<_>(), + hotwords_file: mem::zeroed::<_>(), + hotwords_score: mem::zeroed::<_>(), + lm_config: mem::zeroed::<_>(), + max_active_paths: mem::zeroed::<_>(), + rule_fars: mem::zeroed::<_>(), + rule_fsts: mem::zeroed::<_>(), + hr: mem::zeroed::<_>(), + } + }; + + let recognizer = + unsafe { sherpa_rs_sys::SherpaOnnxCreateOfflineRecognizer(&recognizer_config) }; + + if recognizer.is_null() { + bail!("Failed to create recognizer"); + } + Ok(Self { recognizer }) + } + + pub fn decode(&mut self, sample_rate: u32, samples: Vec) -> String { + unsafe { + let stream = sherpa_rs_sys::SherpaOnnxCreateOfflineStream(self.recognizer); + sherpa_rs_sys::SherpaOnnxAcceptWaveformOffline( + stream, + sample_rate as i32, + samples.as_ptr(), + samples.len().try_into().unwrap(), + ); + sherpa_rs_sys::SherpaOnnxDecodeOfflineStream(self.recognizer, stream); + let result_ptr = sherpa_rs_sys::SherpaOnnxGetOfflineStreamResult(stream); + let raw_result = result_ptr.read(); + let text = cstr_to_string(raw_result.text as _); + + // Free + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizerResult(result_ptr); + sherpa_rs_sys::SherpaOnnxDestroyOfflineStream(stream); + text + } + } +} + +unsafe impl Send for ZipFormer {} +unsafe impl Sync for ZipFormer {} + +impl Drop for ZipFormer { + fn drop(&mut self) { + unsafe { + sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizer(self.recognizer); + } + } +} diff --git a/examples/audio_tag.rs b/examples/audio_tag.rs new file mode 100644 index 0000000..1840c14 --- /dev/null +++ b/examples/audio_tag.rs @@ -0,0 +1,28 @@ +/* +Audio tagging identifies specific audio events from audio file. + +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/audio-tagging-models/sherpa-onnx-zipformer-audio-tagging-2024-04-09.tar.bz2 +tar xvf sherpa-onnx-zipformer-audio-tagging-2024-04-09.tar.bz2 +rm sherpa-onnx-zipformer-audio-tagging-2024-04-09.tar.bz2 + +cargo run --example audio_tag +*/ + +fn main() { + let model = "./sherpa-onnx-zipformer-audio-tagging-2024-04-09/model.int8.onnx"; + let labels_path = "./sherpa-onnx-zipformer-audio-tagging-2024-04-09/class_labels_indices.csv"; + let wav_path = "./sherpa-onnx-zipformer-audio-tagging-2024-04-09/test_wavs/1.wav"; + let top_k = 5; + + let (samples, sample_rate) = sherpa_rs::read_audio_file(wav_path).unwrap(); + + let config = sherpa_rs::audio_tag::AudioTagConfig { + model: model.into(), + labels: labels_path.into(), + top_k, + ..Default::default() + }; + let mut audio_tag = sherpa_rs::audio_tag::AudioTag::new(config).unwrap(); + let events = audio_tag.compute(samples, sample_rate); + println!("✅ Events ({}): {}", events.len(), events.join(", ")); +} diff --git a/examples/diarize.rs b/examples/diarize.rs index ca5da3e..23a7d0b 100644 --- a/examples/diarize.rs +++ b/examples/diarize.rs @@ -1,161 +1,48 @@ /* -wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx -wget https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/nemo_en_speakerverification_speakernet.onnx -wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/motivation.wav -O motivation.wav -cargo run --example diarize motivation.wav -*/ - -use eyre::{bail, Result}; -use sherpa_rs::{ - embedding_manager, speaker_id, - vad::{Vad, VadConfig}, -}; -use std::io::Cursor; +Diarize audio file with pyannote-audio for segmentation (start and stop marks) and 3dspeaker for speaker recognition. -fn get_speaker_name( - embedding_manager: &mut embedding_manager::EmbeddingManager, - embedding: &mut [f32], - speaker_counter: &mut i32, - max_speakers: i32, -) -> String { - let mut name = String::from("unknown"); +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-segmentation-models/sherpa-onnx-pyannote-segmentation-3-0.tar.bz2 +tar xvf sherpa-onnx-pyannote-segmentation-3-0.tar.bz2 +rm sherpa-onnx-pyannote-segmentation-3-0.tar.bz2 - if *speaker_counter == 0 { - name = format!("speaker {}", speaker_counter); - embedding_manager.add(name.clone(), embedding).unwrap(); - *speaker_counter += 1; - } else if *speaker_counter <= max_speakers { - if let Some(search_result) = embedding_manager.search(embedding, 0.5) { - name = search_result; - } else { - name = format!("speaker {}", speaker_counter); - embedding_manager.add(name.clone(), embedding).unwrap(); - *speaker_counter += 1; - } - } else { - let matches = embedding_manager.get_best_matches(embedding, 0.2, *speaker_counter); - if let Some(name_match) = matches.first().map(|m| m.name.clone()) { - name = name_match; - } - } +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-segmentation-models/0-four-speakers-zh.wav - name -} - -fn process_speech_segment( - vad: &mut Vad, - sample_rate: i32, - mut embedding_manager: &mut embedding_manager::EmbeddingManager, - extractor: &mut speaker_id::EmbeddingExtractor, - speaker_counter: &mut i32, - max_speakers: i32, -) -> Result<()> { - while !vad.is_empty() { - let segment = vad.front(); - let start_sec = (segment.start as f32) / sample_rate as f32; - let duration_sec = (segment.samples.len() as f32) / sample_rate as f32; +cargo run --example diarize ./sherpa-onnx-pyannote-segmentation-3-0/model.onnx ./3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx ./0-four-speakers-zh.wav +*/ - // Compute the speaker embedding - let mut embedding = extractor.compute_speaker_embedding(sample_rate, segment.samples)?; +fn main() { + let args: Vec = std::env::args().collect(); + let (segment_model_path, embedding_model_path, wav_path) = ( + args.get(1) + .expect("Missing path argument for segmentation model"), + args.get(2) + .expect("Missing path argument for embedding model"), + args.get(3).expect("Missing path argument for wav file"), + ); - let name = get_speaker_name( - &mut embedding_manager, - &mut embedding, - speaker_counter, - max_speakers, - ); + let config = sherpa_rs::diarize::DiarizeConfig { + num_clusters: Some(5), + ..Default::default() + }; + + let progress_callback = |n_computed_chunks: i32, n_total_chunks: i32| -> i32 { + let progress = 100 * n_computed_chunks / n_total_chunks; + println!("🗣️ Diarizing... {}% 🎯", progress); + 0 + }; + + let mut sd = + sherpa_rs::diarize::Diarize::new(segment_model_path, embedding_model_path, config).unwrap(); + let (samples, _) = sherpa_rs::read_audio_file(wav_path).unwrap(); + + let segments = sd + .compute(samples, Some(Box::new(progress_callback))) + .unwrap(); + for segment in segments { println!( - "({}) start={}s end={}s", - name, - start_sec, - start_sec + duration_sec + "start = {} end = {} speaker = {}", + segment.start, segment.end, segment.speaker ); - vad.pop(); - } - Ok(()) -} - -fn main() -> Result<()> { - let file_path = std::env::args().nth(1).expect("Missing file path argument"); - let audio_data = std::fs::read(file_path)?; - let max_speakers = 2; - - let cursor = Cursor::new(audio_data); - let mut reader = hound::WavReader::new(cursor)?; - let sample_rate = reader.spec().sample_rate as i32; - - if sample_rate != 16000 { - bail!("The sample rate must be 16000."); } - - let mut samples: Vec = reader - .samples::() - .map(|s| s.unwrap() as f32 / i16::MAX as f32) - .collect(); - - // Pad with 3 seconds of slience so vad will able to detect stop - for _ in 0..3 * sample_rate { - samples.push(0.0); - } - - let extractor_config = speaker_id::ExtractorConfig::new( - "nemo_en_speakerverification_speakernet.onnx".into(), - None, - None, - false, - ); - let mut extractor = speaker_id::EmbeddingExtractor::new_from_config(extractor_config).unwrap(); - let mut embedding_manager = - embedding_manager::EmbeddingManager::new(extractor.embedding_size.try_into().unwrap()); // Assuming dimension 512 for embeddings - - let mut speaker_counter = 1; - - let vad_model = "silero_vad.onnx".into(); - let window_size: usize = 512; - let config = VadConfig::new( - vad_model, - 0.5, - 0.5, - 0.5, - sample_rate, - window_size.try_into().unwrap(), - None, - None, - Some(false), - ); - - let mut vad = Vad::new_from_config(config, 60.0 * 10.0).unwrap(); - let mut index = 0; - while index + window_size <= samples.len() { - let window = &samples[index..index + window_size]; - vad.accept_waveform(window.to_vec()); // Convert slice to Vec - if vad.is_speech() { - while !vad.is_empty() { - process_speech_segment( - &mut vad, - sample_rate, - &mut embedding_manager, - &mut extractor, - &mut speaker_counter, - max_speakers, - )?; - } - } - - index += window_size; - } - vad.flush(); - // process reamaining - while !vad.is_empty() { - process_speech_segment( - &mut vad, - sample_rate, - &mut embedding_manager, - &mut extractor, - &mut speaker_counter, - max_speakers, - )?; - } - - Ok(()) } diff --git a/examples/dolphin.rs b/examples/dolphin.rs new file mode 100644 index 0000000..8b929d8 --- /dev/null +++ b/examples/dolphin.rs @@ -0,0 +1,33 @@ +/* +Transcribe wav file using Dolphin ASR +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-dolphin-base-ctc-multi-lang-int8-2025-04-02.tar.bz2 +tar xvf sherpa-onnx-dolphin-base-ctc-multi-lang-int8-2025-04-02.tar.bz2 +rm sherpa-onnx-moonshine-tiny-en-int8.tar.bz2 +wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/motivation.wav -O motivation.wav +cargo run --example dolphin motivation.wav +*/ + +use sherpa_rs::{ + dolphin::{DolphinConfig, DolphinRecognizer}, + read_audio_file, +}; + +fn main() { + let path = std::env::args().nth(1).expect("Missing file path argument"); + let provider = std::env::args().nth(2).unwrap_or("cpu".into()); + let (samples, sample_rate) = read_audio_file(&path).unwrap(); + assert_eq!(sample_rate, 16000, "The sample rate must be 16000."); + + let config = DolphinConfig { + model: "./sherpa-onnx-dolphin-base-ctc-multi-lang-int8-2025-04-02/model.int8.onnx".into(), + tokens: "./sherpa-onnx-dolphin-base-ctc-multi-lang-int8-2025-04-02/tokens.txt".into(), + provider: Some(provider), + ..Default::default() // fill in any missing fields with defaults + }; + let mut recognizer = DolphinRecognizer::new(config).unwrap(); + + let start_t = std::time::Instant::now(); + let result = recognizer.transcribe(sample_rate, &samples); + println!("✅ Text: {}", result.text); + println!("⏱️ Time taken for transcription: {:?}", start_t.elapsed()); +} diff --git a/examples/keyword_spot.rs b/examples/keyword_spot.rs new file mode 100644 index 0000000..84b206e --- /dev/null +++ b/examples/keyword_spot.rs @@ -0,0 +1,73 @@ +/* +We assume you have pre-downloaded the model files for testing +from https://k2-fsa.github.io/sherpa/onnx/kws/pretrained_models/index.html + + +English: +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/kws-models/sherpa-onnx-kws-zipformer-gigaspeech-3.3M-2024-01-01.tar.bz2 +tar xvf sherpa-onnx-kws-zipformer-gigaspeech-3.3M-2024-01-01.tar.bz2 +rm sherpa-onnx-kws-zipformer-gigaspeech-3.3M-2024-01-01.tar.bz2 + +cargo run --example keyword_spot \ + "sherpa-onnx-kws-zipformer-gigaspeech-3.3M-2024-01-01/test_wavs/1.wav" \ + "sherpa-onnx-kws-zipformer-gigaspeech-3.3M-2024-01-01/encoder-epoch-12-avg-2-chunk-16-left-64.int8.onnx" \ + "sherpa-onnx-kws-zipformer-gigaspeech-3.3M-2024-01-01/decoder-epoch-12-avg-2-chunk-16-left-64.int8.onnx" \ + "sherpa-onnx-kws-zipformer-gigaspeech-3.3M-2024-01-01/joiner-epoch-12-avg-2-chunk-16-left-64.int8.onnx" \ + "sherpa-onnx-kws-zipformer-gigaspeech-3.3M-2024-01-01/tokens.txt" \ + "sherpa-onnx-kws-zipformer-gigaspeech-3.3M-2024-01-01/test_wavs/test_keywords.txt" + +Chinese: +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/kws-models/sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01.tar.bz2 +tar xf sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01.tar.bz2 +rm sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01.tar.bz2 + +cargo run --example keyword_spot \ + sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01-mobile/test_wavs/6.wav \ + sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01-mobile/encoder-epoch-12-avg-2-chunk-16-left-64.int8.onnx \ + sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01-mobile/decoder-epoch-12-avg-2-chunk-16-left-64.onnx \ + sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01-mobile/joiner-epoch-12-avg-2-chunk-16-left-64.int8.onnx \ + sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01-mobile/tokens.txt \ + sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01-mobile/test_wavs/test_keywords.txt + +*/ + +use std::env::args; + +fn main() { + let audio_path = args().nth(1).expect("Please specify the audio file path"); + let zipformer_encoder = args() + .nth(2) + .expect("Please specify the zipformer encoder file"); + let zipformer_decoder = args() + .nth(3) + .expect("Please specify the zipformer decoder file"); + let zipformer_joiner = args() + .nth(4) + .expect("Please specify the zipformer joiner file"); + let tokens = args().nth(5).expect("Please specify the tokens file"); + let keywords = args().nth(6).expect("Please specify the keywords file"); + + let (samples, sample_rate) = sherpa_rs::read_audio_file(&audio_path).unwrap(); + + let config = sherpa_rs::keyword_spot::KeywordSpotConfig { + zipformer_encoder, + zipformer_decoder, + zipformer_joiner, + tokens, + keywords, + max_active_path: 4, + keywords_threshold: 0.1, + keywords_score: 3.0, + num_trailing_blanks: 1, + sample_rate: 16000, + feature_dim: 80, + ..Default::default() + }; + let mut spotter = sherpa_rs::keyword_spot::KeywordSpot::new(config).unwrap(); + + let keyword = spotter + .extract_keyword(samples, sample_rate) + .unwrap() + .unwrap_or("?".into()); + println!("Keyword: {}", keyword); +} diff --git a/examples/language_id.rs b/examples/language_id.rs index 84a6be9..ffaadc9 100644 --- a/examples/language_id.rs +++ b/examples/language_id.rs @@ -1,4 +1,6 @@ /* +Detect language spoken in audio file + wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-whisper-tiny.tar.bz2 tar xvf sherpa-onnx-whisper-tiny.tar.bz2 rm sherpa-onnx-whisper-tiny.tar.bz2 @@ -6,33 +8,19 @@ wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/16hz_mon cargo run --example language_id 16hz_mono_pcm_s16le.wav */ -use eyre::{bail, Result}; -use sherpa_rs::language_id; -use std::io::Cursor; - -fn main() -> Result<()> { +fn main() { let file_path = std::env::args().nth(1).expect("Missing file path argument"); - let audio_data = std::fs::read(file_path)?; - - let cursor = Cursor::new(audio_data); - let mut reader = hound::WavReader::new(cursor)?; - let sample_rate = reader.spec().sample_rate as i32; - if sample_rate != 16000 { - bail!("The sample rate must be 16000."); - } + let (samples, sample_rate) = sherpa_rs::read_audio_file(&file_path).unwrap(); + assert_eq!(sample_rate, 16000, "The sample rate must be 16000."); - let samples: Vec = reader - .samples::() - .map(|s| s.unwrap() as f32 / i16::MAX as f32) - .collect(); + let config = sherpa_rs::language_id::SpokenLanguageIdConfig { + encoder: "sherpa-onnx-whisper-tiny/tiny-encoder.onnx".into(), + decoder: "sherpa-onnx-whisper-tiny/tiny-decoder.onnx".into(), + ..Default::default() + }; + let mut extractor = sherpa_rs::language_id::SpokenLanguageId::new(config); - let encoder = "sherpa-onnx-whisper-tiny/tiny-encoder.onnx".into(); - let decoder = "sherpa-onnx-whisper-tiny/tiny-decoder.onnx".into(); - let mut extractor = language_id::SpokenLanguageId::new(encoder, decoder, None, None, None); - - let language = extractor.compute(samples, sample_rate)?; + let language = extractor.compute(samples, sample_rate).unwrap(); println!("Spoken language: {}", language); - - Ok(()) } diff --git a/examples/moonshine.rs b/examples/moonshine.rs new file mode 100644 index 0000000..19c05e7 --- /dev/null +++ b/examples/moonshine.rs @@ -0,0 +1,38 @@ +/* +Transcribe wav file using Moonshine (English only) + +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-moonshine-tiny-en-int8.tar.bz2 +tar xvf sherpa-onnx-moonshine-tiny-en-int8.tar.bz2 +rm sherpa-onnx-moonshine-tiny-en-int8.tar.bz2 +wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/motivation.wav -O motivation.wav +cargo run --example moonshine motivation.wav +*/ + +use sherpa_rs::{ + moonshine::{MoonshineConfig, MoonshineRecognizer}, + read_audio_file, +}; + +fn main() { + let path = std::env::args().nth(1).expect("Missing file path argument"); + let provider = std::env::args().nth(2).unwrap_or("cpu".into()); + let (samples, sample_rate) = read_audio_file(&path).unwrap(); + assert_eq!(sample_rate, 16000, "The sample rate must be 16000."); + + let config = MoonshineConfig { + preprocessor: "./sherpa-onnx-moonshine-tiny-en-int8/preprocess.onnx".into(), + encoder: "./sherpa-onnx-moonshine-tiny-en-int8/encode.int8.onnx".into(), + uncached_decoder: "./sherpa-onnx-moonshine-tiny-en-int8/uncached_decode.int8.onnx".into(), + cached_decoder: "./sherpa-onnx-moonshine-tiny-en-int8/cached_decode.int8.onnx".into(), + tokens: "./sherpa-onnx-moonshine-tiny-en-int8/tokens.txt".into(), + provider: Some(provider), + num_threads: None, + ..Default::default() // fill in any missing fields with defaults + }; + let mut recognizer = MoonshineRecognizer::new(config).unwrap(); + + let start_t = std::time::Instant::now(); + let result = recognizer.transcribe(sample_rate, &samples); + println!("✅ Text: {}", result.text); + println!("⏱️ Time taken for transcription: {:?}", start_t.elapsed()); +} diff --git a/examples/paraformer.rs b/examples/paraformer.rs new file mode 100644 index 0000000..1317fa7 --- /dev/null +++ b/examples/paraformer.rs @@ -0,0 +1,35 @@ +/* +Transcribe wav file using SenseVoice + +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-paraformer-zh-2024-03-09.tar.bz2 +tar xvf sherpa-onnx-paraformer-zh-small-2024-03-09.tar.bz2 +wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/motivation.wav -O motivation.wav +cargo run --example paraformer motivation.wav +*/ + +use sherpa_rs::{ + paraformer::{ParaformerConfig, ParaformerRecognizer}, + read_audio_file, +}; + +fn main() { + let path = std::env::args().nth(1).expect("Missing file path argument"); + let provider = std::env::args().nth(2).unwrap_or("cpu".into()); + let (samples, sample_rate) = read_audio_file(&path).unwrap(); + assert_eq!(sample_rate, 16000, "The sample rate must be 16000."); + + let config = ParaformerConfig { + model: "sherpa-onnx-paraformer-zh-2024-03-09/model.int8.onnx".into(), + tokens: "sherpa-onnx-paraformer-zh-2024-03-09/tokens.txt".into(), + provider: Some(provider), + + ..Default::default() + }; + + let mut recognizer: ParaformerRecognizer = ParaformerRecognizer::new(config).unwrap(); + + let start_t = std::time::Instant::now(); + let result = recognizer.transcribe(sample_rate, &samples); + println!("✅ Text: {}", result.text); + println!("⏱️ Time taken for transcription: {:?}", start_t.elapsed()); +} diff --git a/examples/parakeet.rs b/examples/parakeet.rs new file mode 100644 index 0000000..c2000d1 --- /dev/null +++ b/examples/parakeet.rs @@ -0,0 +1,44 @@ +/* +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8.tar.bz2 +tar xvf sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8.tar.bz2 +rm sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8.tar.bz2 +wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/motivation.wav -O motivation.wav +cargo run --example parakeet motivation.wav +*/ + +use sherpa_rs::read_audio_file; +use sherpa_rs::transducer::{TransducerConfig, TransducerRecognizer}; +use std::time::Instant; + +pub fn main() { + let path = std::env::args().nth(1).expect("Missing file path argument"); + let (samples, sample_rate) = read_audio_file(&path).unwrap(); + + // Check if the sample rate is 16000 + if sample_rate != 16000 { + panic!("The sample rate must be 16000."); + } + + let config = TransducerConfig { + decoder: "./sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8/decoder.int8.onnx".to_string(), + encoder: "./sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8/encoder.int8.onnx".to_string(), + joiner: "./sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8/joiner.int8.onnx".to_string(), + tokens: "./sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8/tokens.txt".to_string(), + num_threads: 1, + sample_rate: 16_000, + feature_dim: 80, + debug: true, + model_type: "nemo_transducer".to_string(), + ..Default::default() + }; + + let mut recognizer = TransducerRecognizer::new(config).unwrap(); + + let start_t = Instant::now(); + let result = recognizer.transcribe(sample_rate, &samples); + let lower_case = result.to_lowercase(); + let trimmed_result = lower_case.trim(); + + println!("Time taken for decode: {:?}", start_t.elapsed()); + println!("Transcribe result: {:?}", trimmed_result); +} diff --git a/examples/punctuate.rs b/examples/punctuate.rs new file mode 100644 index 0000000..df598e4 --- /dev/null +++ b/examples/punctuate.rs @@ -0,0 +1,34 @@ +/* +We assume you have pre-downloaded the model files for testing +from https://github.com/k2-fsa/sherpa-onnx/releases/tag/punctuation-models + +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/punctuation-models/sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12.tar.bz2 +tar xvf sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12.tar.bz2 +rm sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12.tar.bz2 + +cargo run --example punctuate ./sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12/model.onnx +*/ + +fn main() { + let args: Vec = std::env::args().collect(); + let model = args.get(1).expect("Please specify model path"); + let sentences = [ + "这是一个测试你好吗How are you我很好thank you are you ok谢谢你", + "我们都是木头人不会说话不会动", + "The African blogosphere is rapidly expanding bringing more voices online in the form of commentaries opinions analyses rants and poetry" + ]; + + let config = sherpa_rs::punctuate::PunctuationConfig { + model: model.into(), + ..Default::default() + }; + let mut punctuate = sherpa_rs::punctuate::Punctuation::new(config).unwrap(); + + println!("--------------------"); + for sentence in sentences { + let punctuated = punctuate.add_punctuation(sentence); + println!("Input text: {}", sentence); + println!("Output text: {}", punctuated); + println!("--------------------"); + } +} diff --git a/examples/sense_voice.rs b/examples/sense_voice.rs new file mode 100644 index 0000000..13029b6 --- /dev/null +++ b/examples/sense_voice.rs @@ -0,0 +1,35 @@ +/* +Transcribe wav file using SenseVoice + +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2 +tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2 +wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/motivation.wav -O motivation.wav +cargo run --example sense_voice motivation.wav +*/ + +use sherpa_rs::{ + read_audio_file, + sense_voice::{SenseVoiceConfig, SenseVoiceRecognizer}, +}; + +fn main() { + let path = std::env::args().nth(1).expect("Missing file path argument"); + let provider = std::env::args().nth(2).unwrap_or("cpu".into()); + let (samples, sample_rate) = read_audio_file(&path).unwrap(); + assert_eq!(sample_rate, 16000, "The sample rate must be 16000."); + + let config = SenseVoiceConfig { + model: "sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/model.int8.onnx".into(), + tokens: "sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/tokens.txt".into(), + provider: Some(provider), + + ..Default::default() + }; + + let mut recognizer: SenseVoiceRecognizer = SenseVoiceRecognizer::new(config).unwrap(); + + let start_t = std::time::Instant::now(); + let result = recognizer.transcribe(sample_rate, &samples); + println!("✅ Text: {}", result.text); + println!("⏱️ Time taken for transcription: {:?}", start_t.elapsed()); +} diff --git a/examples/speaker_embedding.rs b/examples/speaker_embedding.rs index 6e783b1..f54238a 100644 --- a/examples/speaker_embedding.rs +++ b/examples/speaker_embedding.rs @@ -1,54 +1,27 @@ /* +Create voice embedding for voice in audio file + wget https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/nemo_en_speakerverification_speakernet.onnx wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/16hz_mono_pcm_s16le.wav -O 16hz_mono_pcm_s16le.wav cargo run --example speaker_embedding 16hz_mono_pcm_s16le.wav */ -use eyre::{bail, Result}; -use sherpa_rs::speaker_id; -use std::io::Cursor; -use std::path::PathBuf; - -fn main() -> Result<()> { +fn main() { let file_path = std::env::args().nth(1).expect("Missing file path argument"); - let audio_data = std::fs::read(file_path)?; - - // Use Cursor to create a reader from the byte slice - let cursor = Cursor::new(audio_data); - let mut reader = hound::WavReader::new(cursor)?; - let sample_rate = reader.spec().sample_rate as i32; - - // Check if the sample rate is 16000 - if sample_rate != 16000 { - bail!("The sample rate must be 16000."); - } + let (samples, sample_rate) = sherpa_rs::read_audio_file(&file_path).unwrap(); + assert_eq!(sample_rate, 16000, "The sample rate must be 16000."); - // Collect samples into a Vec - let samples: Vec = reader - .samples::() - .map(|s| s.unwrap() as f32 / i16::MAX as f32) - .collect(); - - // Create the extractor configuration and extractor - let mut model_path = PathBuf::from(std::env::current_dir()?); - model_path.push("nemo_en_speakerverification_speakernet.onnx"); - - println!("loading model from {}", model_path.display()); - - // Create the extractor configuration and extractor - let config = speaker_id::ExtractorConfig::new( - model_path.into_os_string().into_string().unwrap(), - None, - None, - false, - ); - let mut extractor = speaker_id::EmbeddingExtractor::new_from_config(config).unwrap(); + let config = sherpa_rs::speaker_id::ExtractorConfig { + model: "nemo_en_speakerverification_speakernet.onnx".into(), + ..Default::default() + }; + let mut extractor = sherpa_rs::speaker_id::EmbeddingExtractor::new(config).unwrap(); // Compute the speaker embedding - let embedding = extractor.compute_speaker_embedding(sample_rate, samples)?; + let embedding = extractor + .compute_speaker_embedding(samples, sample_rate) + .unwrap(); // Use the embedding as needed println!("Speaker embedding: {:?}", embedding); - - Ok(()) } diff --git a/examples/speaker_id.rs b/examples/speaker_id.rs index 538f3c9..c5230a9 100644 --- a/examples/speaker_id.rs +++ b/examples/speaker_id.rs @@ -1,55 +1,32 @@ /* +Recognize speakers in audio file + wget https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/nemo_en_speakerverification_speakernet.onnx wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/biden.wav -O biden.wav wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/obama.wav -O obama.wav cargo run --example speaker_id */ -use eyre::{bail, Result}; use sherpa_rs::{embedding_manager, speaker_id}; use std::collections::HashMap; -use std::path::PathBuf; - -fn read_audio_file(path: &str) -> Result<(i32, Vec)> { - let mut reader = hound::WavReader::open(path)?; - let sample_rate = reader.spec().sample_rate as i32; - - // Check if the sample rate is 16000 - if sample_rate != 16000 { - bail!("The sample rate must be 16000."); - } - - // Collect samples into a Vec - let samples: Vec = reader - .samples::() - .map(|s| s.unwrap() as f32 / i16::MAX as f32) - .collect(); - Ok((sample_rate, samples)) -} - -fn main() -> Result<()> { +fn main() { // Define paths to the audio files - let audio_files = vec!["samples/obama.wav", "biden.wav"]; - - // Create the extractor configuration and extractor - let mut model_path = PathBuf::from(std::env::current_dir()?); - model_path.push("nemo_en_speakerverification_speakernet.onnx"); + let audio_files = vec!["obama.wav", "biden.wav"]; - println!("🎤 Loading model from {}", model_path.display()); - - let config = speaker_id::ExtractorConfig::new( - model_path.into_os_string().into_string().unwrap(), - None, - None, - false, - ); - let mut extractor = speaker_id::EmbeddingExtractor::new_from_config(config)?; + let config = speaker_id::ExtractorConfig { + model: "nemo_en_speakerverification_speakernet.onnx".into(), + ..Default::default() + }; + let mut extractor = speaker_id::EmbeddingExtractor::new(config).unwrap(); // Read and process each audio file, compute embeddings let mut embeddings = Vec::new(); for file in &audio_files { - let (sample_rate, samples) = read_audio_file(file)?; - let embedding = extractor.compute_speaker_embedding(sample_rate, samples)?; + let (samples, sample_rate) = sherpa_rs::read_audio_file(file).unwrap(); + assert_eq!(sample_rate, 16000, "The sample rate must be 16000."); + let embedding = extractor + .compute_speaker_embedding(samples, sample_rate) + .unwrap(); embeddings.push((file.to_string(), embedding)); } @@ -71,10 +48,12 @@ fn main() -> Result<()> { .push(file.clone()); } else { // Register a new speaker and add the embedding - embedding_manager.add( - format!("speaker {}", speaker_counter), - &mut embedding.clone(), - )?; + embedding_manager + .add( + format!("speaker {}", speaker_counter), + &mut embedding.clone(), + ) + .unwrap(); speaker_map .entry(format!("speaker {}", speaker_counter)) .or_default() @@ -89,6 +68,4 @@ fn main() -> Result<()> { for (speaker_id, files) in &speaker_map { println!("Speaker {}: {:?}", speaker_id, files); } - - Ok(()) } diff --git a/examples/tauri-app/.gitignore b/examples/tauri-app/.gitignore new file mode 100644 index 0000000..18b804d --- /dev/null +++ b/examples/tauri-app/.gitignore @@ -0,0 +1,28 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +schemas/ +autogenerated/ +src-tauri/icons/* +!src-tauri/icons/icon.png \ No newline at end of file diff --git a/examples/tauri-app/.vscode/extensions.json b/examples/tauri-app/.vscode/extensions.json new file mode 100644 index 0000000..2993336 --- /dev/null +++ b/examples/tauri-app/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"], +} diff --git a/examples/tauri-app/README.md b/examples/tauri-app/README.md new file mode 100644 index 0000000..9ff5901 --- /dev/null +++ b/examples/tauri-app/README.md @@ -0,0 +1,50 @@ +# Tauri + sherpa-rs + + + +## Prepare model + +```console +cd src-tauri +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/punctuation-models/sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12.tar.bz2 +tar xvf sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12.tar.bz2 +mv sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12/model.onnx model.onnx +rm -rf rm sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12 +rm sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12.tar.bz2 +adb push model.onnx /data/local/tmp/model.onnx # currently hardcoded in the APK +``` + +## Build + +See https://v2.tauri.app/start/prerequisites + +See [Building](../../BUILDING.md) + +```console +# Setup environment variables +export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home" +export ANDROID_HOME="$HOME/Library/Android/sdk" +export NDK_HOME="$HOME/Library/Android/sdk/ndk/27.0.12077973" # ls $HOME/Library/Android/sdk/ndk + +# Setup UI +bun install +bunx tauri icon src-tauri/icons/icon.png + +cd src-tauri +export CARGO_TARGET_DIR="$(pwd)/target" +cargo ndk -t arm64-v8a build +mkdir -p gen/android/app/src/main/jniLibs/arm64-v8a +ln -s $(pwd)/target/aarch64-linux-android/debug/libonnxruntime.so $(pwd)/gen/android/app/src/main/jniLibs/arm64-v8a/libonnxruntime.so +ln -s $(pwd)/target/aarch64-linux-android/debug/libsherpa-onnx-c-api.so $(pwd)/gen/android/app/src/main/jniLibs/arm64-v8a/libsherpa-onnx-c-api.so +bun run tauri android dev +``` + +## Debug + +```console +adb logcat -c && adb logcat | grep -i -E "tauri|rust|sherpa" +``` + +## Debug webview + +Open `chrome://inspect` in the chrome browser and click `inspect` diff --git a/examples/tauri-app/bun.lockb b/examples/tauri-app/bun.lockb new file mode 100644 index 0000000..4e3202e Binary files /dev/null and b/examples/tauri-app/bun.lockb differ diff --git a/examples/tauri-app/index.html b/examples/tauri-app/index.html new file mode 100644 index 0000000..ba1813d --- /dev/null +++ b/examples/tauri-app/index.html @@ -0,0 +1,66 @@ + + + + + + + sherpa-rs with tauri + + + + + +
+

sherpa-rs

+ +
+ +
+ +
+
+

+
+ + diff --git a/examples/tauri-app/package.json b/examples/tauri-app/package.json new file mode 100644 index 0000000..76102a9 --- /dev/null +++ b/examples/tauri-app/package.json @@ -0,0 +1,21 @@ +{ + "name": "tauri-app", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "tauri": "tauri" + }, + "dependencies": { + "@tauri-apps/api": "^2", + "@tauri-apps/plugin-opener": "^2" + }, + "devDependencies": { + "@tauri-apps/cli": "^2", + "vite": "^6.0.3", + "typescript": "~5.6.2" + } +} diff --git a/examples/tauri-app/src-tauri/.gitignore b/examples/tauri-app/src-tauri/.gitignore new file mode 100644 index 0000000..b21bd68 --- /dev/null +++ b/examples/tauri-app/src-tauri/.gitignore @@ -0,0 +1,7 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Generated by Tauri +# will have schema files for capabilities auto-completion +/gen/schemas diff --git a/examples/tauri-app/src-tauri/Cargo.lock b/examples/tauri-app/src-tauri/Cargo.lock new file mode 100644 index 0000000..1e0c2f2 --- /dev/null +++ b/examples/tauri-app/src-tauri/Cargo.lock @@ -0,0 +1,5398 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[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.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", +] + +[[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 2.0.91", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[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.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.91", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +dependencies = [ + "serde", +] + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.6.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "cargo_toml" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719" +dependencies = [ + "serde", + "toml 0.8.2", +] + +[[package]] +name = "cc" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +dependencies = [ + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.52.6", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.6", +] + +[[package]] +name = "cmake" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" +dependencies = [ + "cc", +] + +[[package]] +name = "cocoa" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" +dependencies = [ + "bitflags 2.6.0", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" +dependencies = [ + "bitflags 2.6.0", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[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 = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.91", +] + +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn 2.0.91", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.91", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.91", +] + +[[package]] +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 = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "embed-resource" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b68b6f9f63a0b6a38bc447d4ce84e2b388f3ec95c99c641c8ff0dd3ef89a6379" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.8.2", + "vswhom", + "winreg", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[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 = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[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", +] + +[[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 2.0.91", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.6.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.14", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa 1.0.14", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "infer" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847" +dependencies = [ + "cfb", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.6.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading 0.7.4", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "muda" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdae9c00e61cc0579bcac625e8ad22104c60548a025bfc972dc83868a28e1484" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", + "png", + "serde", + "thiserror 1.0.69", + "windows-sys 0.59.0", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.6.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate 2.0.2", + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" +dependencies = [ + "cc", +] + +[[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-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68bc69301064cebefc6c4c90ce9cba69225239e4b8ff99d445a2b5563797da65" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "open" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ecd52f0b8d15c40ce4820aa251ed5de032e5d91fab27f7db2f40d42a8bdf69c" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[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 = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros 0.11.2", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +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.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "plist" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +dependencies = [ + "base64 0.22.1", + "indexmap 2.7.0", + "quick-xml", + "serde", + "time", +] + +[[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.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.91", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars_derive" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.91", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2676ba99bd82f75cae5cbd2c8eda6fa0b8760f18978ea840e980dd5567b5c5b6" +dependencies = [ + "erased-serde", + "serde", + "typeid", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "serde_json" +version = "1.0.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +dependencies = [ + "itoa 1.0.14", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.14", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sherpa-rs" +version = "0.6.6" +dependencies = [ + "eyre", + "hound", + "sherpa-rs-sys", + "tracing", +] + +[[package]] +name = "sherpa-rs-sys" +version = "0.6.6" +dependencies = [ + "bindgen", + "bzip2", + "cmake", + "dirs", + "flate2", + "glob", + "lazy_static", + "serde", + "serde_json", + "sha2", + "tar", + "ureq", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + +[[package]] +name = "softbuffer" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" +dependencies = [ + "bytemuck", + "cfg_aliases", + "core-graphics", + "foreign-types", + "js-sys", + "log", + "objc2", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall", + "wasm-bindgen", + "web-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.30.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6682a07cf5bab0b8a2bd20d0a542917ab928b5edb75ebd4eda6b05cbaab872da" +dependencies = [ + "bitflags 2.6.0", + "cocoa", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "raw-window-handle", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core 0.58.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "tar" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e545de0a2dfe296fa67db208266cd397c5a55ae782da77973ef4c4fac90e9f2c" +dependencies = [ + "anyhow", + "bytes", + "dirs", + "dunce", + "embed_plist", + "futures-util", + "getrandom 0.2.15", + "glob", + "gtk", + "heck 0.5.0", + "http", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.9", + "tokio", + "tray-icon", + "url", + "urlpattern", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-app" +version = "0.1.0" +dependencies = [ + "once_cell", + "serde", + "serde_json", + "sherpa-rs", + "tauri", + "tauri-build", + "tauri-plugin-opener", +] + +[[package]] +name = "tauri-build" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd2a4bcfaf5fb9f4be72520eefcb61ae565038f8ccba2a497d8c28f463b8c01" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs", + "glob", + "heck 0.5.0", + "json-patch", + "schemars", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "toml 0.8.2", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf79faeecf301d3e969b1fae977039edb77a4c1f25cc0a961be298b54bff97cf" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.91", + "tauri-utils", + "thiserror 2.0.9", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c52027c8c5afb83166dacddc092ee8fff50772f9646d461d8c33ee887e447a03" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.91", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e753f2a30933a9bbf0a202fa47d7cc4a3401f06e8d6dcc53b79aa62954828c79" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars", + "serde", + "serde_json", + "tauri-utils", + "toml 0.8.2", + "walkdir", +] + +[[package]] +name = "tauri-plugin-opener" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63ac39033ef1bb4d52da4878c3d8ab6d80b0a569d69208c884e6d4d54eb427b9" +dependencies = [ + "dunce", + "glob", + "objc2-app-kit", + "objc2-foundation", + "open", + "schemars", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.9", + "url", + "windows", + "zbus", +] + +[[package]] +name = "tauri-runtime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce18d43f80d4aba3aa8a0c953bbe835f3d0f2370aca75e8dbb14bd4bab27958" +dependencies = [ + "dpi", + "gtk", + "http", + "jni", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.9", + "url", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f442a38863e10129ffe2cec7bd09c2dcf8a098a3a27801a476a304d5bb991d2" +dependencies = [ + "gtk", + "http", + "jni", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9271a88f99b4adea0dc71d0baca4505475a0bbd139fb135f62958721aaa8fe54" +dependencies = [ + "brotli", + "cargo_metadata", + "ctor", + "dunce", + "glob", + "html5ever", + "http", + "infer", + "json-patch", + "kuchikiki", + "log", + "memchr", + "phf 0.11.2", + "proc-macro2", + "quote", + "regex", + "schemars", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.9", + "toml 0.8.2", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" +dependencies = [ + "embed-resource", + "toml 0.7.8", +] + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +dependencies = [ + "thiserror-impl 2.0.9", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa 1.0.14", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.7.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.7.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tray-icon" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48a05076dd272615d03033bf04f480199f7d1b66a8ac64d75c625fc4a70c06b" +dependencies = [ + "core-graphics", + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", + "png", + "serde", + "thiserror 1.0.69", + "windows-sys 0.59.0", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[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 = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "socks", + "url", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom 0.2.15", + "serde", +] + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.91", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core 0.58.0", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "webview2-com-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" +dependencies = [ + "thiserror 1.0.69", + "windows", + "windows-core 0.58.0", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[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 = "window-vibrancy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ea403deff7b51fff19e261330f71608ff2cdef5721d72b64180bb95be7c4150" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-foundation", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-version" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wry" +version = "0.47.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ce51277d65170f6379d8cda935c80e3c2d1f0ff712a123c8bddb11b31a4b73" +dependencies = [ + "base64 0.22.1", + "block2", + "cookie", + "crossbeam-channel", + "dpi", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2", + "soup3", + "tao-macros", + "thiserror 1.0.69", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core 0.58.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys", + "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 = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", + "synstructure", +] + +[[package]] +name = "zbus" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "derivative", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "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_macros" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "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 = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + +[[package]] +name = "zvariant" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/examples/tauri-app/src-tauri/Cargo.toml b/examples/tauri-app/src-tauri/Cargo.toml new file mode 100644 index 0000000..fb2f440 --- /dev/null +++ b/examples/tauri-app/src-tauri/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "tauri-app" +version = "0.1.0" +description = "sherpa-rs example tauri app" +authors = ["thewh1teagle"] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +# The `_lib` suffix may seem redundant but it is necessary +# to make the lib name unique and wouldn't conflict with the bin name. +# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519 +name = "tauri_app_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +tauri-plugin-opener = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +sherpa-rs = { path = "../../../crates/sherpa-rs" } +once_cell = "1.20.2" diff --git a/examples/tauri-app/src-tauri/build.rs b/examples/tauri-app/src-tauri/build.rs new file mode 100644 index 0000000..d860e1e --- /dev/null +++ b/examples/tauri-app/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/examples/tauri-app/src-tauri/capabilities/default.json b/examples/tauri-app/src-tauri/capabilities/default.json new file mode 100644 index 0000000..4cdbf49 --- /dev/null +++ b/examples/tauri-app/src-tauri/capabilities/default.json @@ -0,0 +1,10 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Capability for the main window", + "windows": ["main"], + "permissions": [ + "core:default", + "opener:default" + ] +} diff --git a/examples/tauri-app/src-tauri/gen/android/.editorconfig b/examples/tauri-app/src-tauri/gen/android/.editorconfig new file mode 100644 index 0000000..ebe51d3 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/.gitignore b/examples/tauri-app/src-tauri/gen/android/.gitignore new file mode 100644 index 0000000..b248203 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/.gitignore @@ -0,0 +1,19 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +build +/captures +.externalNativeBuild +.cxx +local.properties +key.properties + +/.tauri +/tauri.settings.gradle \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/app/.gitignore b/examples/tauri-app/src-tauri/gen/android/app/.gitignore new file mode 100644 index 0000000..9c7c735 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/.gitignore @@ -0,0 +1,6 @@ +/src/main/java/com/tauri_app/app/generated +/src/main/jniLibs/**/*.so +/src/main/assets/tauri.conf.json +/tauri.build.gradle.kts +/proguard-tauri.pro +/tauri.properties \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/app/build.gradle.kts b/examples/tauri-app/src-tauri/gen/android/app/build.gradle.kts new file mode 100644 index 0000000..df592fd --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/build.gradle.kts @@ -0,0 +1,70 @@ +import java.util.Properties + +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("rust") +} + +val tauriProperties = Properties().apply { + val propFile = file("tauri.properties") + if (propFile.exists()) { + propFile.inputStream().use { load(it) } + } +} + +android { + compileSdk = 34 + namespace = "com.tauri_app.app" + defaultConfig { + manifestPlaceholders["usesCleartextTraffic"] = "false" + applicationId = "com.tauri_app.app" + minSdk = 24 + targetSdk = 34 + versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt() + versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0") + } + buildTypes { + getByName("debug") { + manifestPlaceholders["usesCleartextTraffic"] = "true" + isDebuggable = true + isJniDebuggable = true + isMinifyEnabled = false + packaging { + jniLibs.keepDebugSymbols.add("*/arm64-v8a/libtauri*.so") + jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so") + jniLibs.keepDebugSymbols.add("*/x86/*.so") + jniLibs.keepDebugSymbols.add("*/x86_64/*.so") + } + } + getByName("release") { + isMinifyEnabled = true + proguardFiles( + *fileTree(".") { include("**/*.pro") } + .plus(getDefaultProguardFile("proguard-android-optimize.txt")) + .toList().toTypedArray() + ) + } + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + buildConfig = true + } +} + +rust { + rootDirRel = "../../../" +} + +dependencies { + implementation("androidx.webkit:webkit:1.6.1") + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.8.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.4") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") +} + +apply(from = "tauri.build.gradle.kts") \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/app/proguard-rules.pro b/examples/tauri-app/src-tauri/gen/android/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/AndroidManifest.xml b/examples/tauri-app/src-tauri/gen/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e2020b8 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/java/com/tauri_app/app/MainActivity.kt b/examples/tauri-app/src-tauri/gen/android/app/src/main/java/com/tauri_app/app/MainActivity.kt new file mode 100644 index 0000000..176a25f --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/src/main/java/com/tauri_app/app/MainActivity.kt @@ -0,0 +1,3 @@ +package com.tauri_app.app + +class MainActivity : TauriActivity() \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..4fc2444 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..28f1aa1 Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..85d0c88 Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..28f1aa1 Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..73e48db Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..13dd214 Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..73e48db Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..1d98044 Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..a888b33 Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..1d98044 Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..0818324 Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..a2a838e Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..0818324 Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..b18bceb Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..3f8a57f Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..b18bceb Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values-night/themes.xml b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..222f3a7 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values-night/themes.xml @@ -0,0 +1,6 @@ + + + + diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values/colors.xml b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values/strings.xml b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..e6c8671 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + tauri-app + tauri-app + \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values/themes.xml b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..222f3a7 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/values/themes.xml @@ -0,0 +1,6 @@ + + + + diff --git a/examples/tauri-app/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..782d63b --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/examples/tauri-app/src-tauri/gen/android/build.gradle.kts b/examples/tauri-app/src-tauri/gen/android/build.gradle.kts new file mode 100644 index 0000000..c5ef452 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/build.gradle.kts @@ -0,0 +1,22 @@ +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.android.tools.build:gradle:8.5.1") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25") + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +tasks.register("clean").configure { + delete("build") +} + diff --git a/examples/tauri-app/src-tauri/gen/android/buildSrc/build.gradle.kts b/examples/tauri-app/src-tauri/gen/android/buildSrc/build.gradle.kts new file mode 100644 index 0000000..39e90b0 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/buildSrc/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + `kotlin-dsl` +} + +gradlePlugin { + plugins { + create("pluginsForCoolKids") { + id = "rust" + implementationClass = "RustPlugin" + } + } +} + +repositories { + google() + mavenCentral() +} + +dependencies { + compileOnly(gradleApi()) + implementation("com.android.tools.build:gradle:8.5.1") +} + diff --git a/examples/tauri-app/src-tauri/gen/android/buildSrc/src/main/java/com/tauri_app/app/kotlin/BuildTask.kt b/examples/tauri-app/src-tauri/gen/android/buildSrc/src/main/java/com/tauri_app/app/kotlin/BuildTask.kt new file mode 100644 index 0000000..6276570 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/buildSrc/src/main/java/com/tauri_app/app/kotlin/BuildTask.kt @@ -0,0 +1,52 @@ +import java.io.File +import org.apache.tools.ant.taskdefs.condition.Os +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.logging.LogLevel +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction + +open class BuildTask : DefaultTask() { + @Input + var rootDirRel: String? = null + @Input + var target: String? = null + @Input + var release: Boolean? = null + + @TaskAction + fun assemble() { + val executable = """bun"""; + try { + runTauriCli(executable) + } catch (e: Exception) { + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + runTauriCli("$executable.cmd") + } else { + throw e; + } + } + } + + fun runTauriCli(executable: String) { + val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") + val target = target ?: throw GradleException("target cannot be null") + val release = release ?: throw GradleException("release cannot be null") + val args = listOf("tauri", "android", "android-studio-script"); + + project.exec { + workingDir(File(project.projectDir, rootDirRel)) + executable(executable) + args(args) + if (project.logger.isEnabled(LogLevel.DEBUG)) { + args("-vv") + } else if (project.logger.isEnabled(LogLevel.INFO)) { + args("-v") + } + if (release) { + args("--release") + } + args(listOf("--target", target)) + }.assertNormalExitValue() + } +} \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/buildSrc/src/main/java/com/tauri_app/app/kotlin/RustPlugin.kt b/examples/tauri-app/src-tauri/gen/android/buildSrc/src/main/java/com/tauri_app/app/kotlin/RustPlugin.kt new file mode 100644 index 0000000..4aa7fca --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/buildSrc/src/main/java/com/tauri_app/app/kotlin/RustPlugin.kt @@ -0,0 +1,85 @@ +import com.android.build.api.dsl.ApplicationExtension +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.get + +const val TASK_GROUP = "rust" + +open class Config { + lateinit var rootDirRel: String +} + +open class RustPlugin : Plugin { + private lateinit var config: Config + + override fun apply(project: Project) = with(project) { + config = extensions.create("rust", Config::class.java) + + val defaultAbiList = listOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64"); + val abiList = (findProperty("abiList") as? String)?.split(',') ?: defaultAbiList + + val defaultArchList = listOf("arm64", "arm", "x86", "x86_64"); + val archList = (findProperty("archList") as? String)?.split(',') ?: defaultArchList + + val targetsList = (findProperty("targetList") as? String)?.split(',') ?: listOf("aarch64", "armv7", "i686", "x86_64") + + extensions.configure { + @Suppress("UnstableApiUsage") + flavorDimensions.add("abi") + productFlavors { + create("universal") { + dimension = "abi" + ndk { + abiFilters += abiList + } + } + defaultArchList.forEachIndexed { index, arch -> + create(arch) { + dimension = "abi" + ndk { + abiFilters.add(defaultAbiList[index]) + } + } + } + } + } + + afterEvaluate { + for (profile in listOf("debug", "release")) { + val profileCapitalized = profile.replaceFirstChar { it.uppercase() } + val buildTask = tasks.maybeCreate( + "rustBuildUniversal$profileCapitalized", + DefaultTask::class.java + ).apply { + group = TASK_GROUP + description = "Build dynamic library in $profile mode for all targets" + } + + tasks["mergeUniversal${profileCapitalized}JniLibFolders"].dependsOn(buildTask) + + for (targetPair in targetsList.withIndex()) { + val targetName = targetPair.value + val targetArch = archList[targetPair.index] + val targetArchCapitalized = targetArch.replaceFirstChar { it.uppercase() } + val targetBuildTask = project.tasks.maybeCreate( + "rustBuild$targetArchCapitalized$profileCapitalized", + BuildTask::class.java + ).apply { + group = TASK_GROUP + description = "Build dynamic library in $profile mode for $targetArch" + rootDirRel = config.rootDirRel + target = targetName + release = profile == "release" + } + + buildTask.dependsOn(targetBuildTask) + tasks["merge$targetArchCapitalized${profileCapitalized}JniLibFolders"].dependsOn( + targetBuildTask + ) + } + } + } + } +} \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/gradle.properties b/examples/tauri-app/src-tauri/gen/android/gradle.properties new file mode 100644 index 0000000..2a7ec69 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/gradle.properties @@ -0,0 +1,24 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/examples/tauri-app/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar b/examples/tauri-app/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/examples/tauri-app/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/examples/tauri-app/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties b/examples/tauri-app/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0df10d5 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue May 10 19:22:52 CST 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/examples/tauri-app/src-tauri/gen/android/gradlew b/examples/tauri-app/src-tauri/gen/android/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/examples/tauri-app/src-tauri/gen/android/gradlew.bat b/examples/tauri-app/src-tauri/gen/android/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/tauri-app/src-tauri/gen/android/settings.gradle b/examples/tauri-app/src-tauri/gen/android/settings.gradle new file mode 100644 index 0000000..3939116 --- /dev/null +++ b/examples/tauri-app/src-tauri/gen/android/settings.gradle @@ -0,0 +1,3 @@ +include ':app' + +apply from: 'tauri.settings.gradle' diff --git a/examples/tauri-app/src-tauri/icons/icon.png b/examples/tauri-app/src-tauri/icons/icon.png new file mode 100644 index 0000000..f794030 Binary files /dev/null and b/examples/tauri-app/src-tauri/icons/icon.png differ diff --git a/examples/tauri-app/src-tauri/src/lib.rs b/examples/tauri-app/src-tauri/src/lib.rs new file mode 100644 index 0000000..1ab39f9 --- /dev/null +++ b/examples/tauri-app/src-tauri/src/lib.rs @@ -0,0 +1,52 @@ +use once_cell::sync::OnceCell; +use std::sync::{Arc, Mutex}; +use tauri::Manager; + + +type PunctuatorHandleType = Arc>; +static PUNCTUATOR: OnceCell> = + OnceCell::new(); + + +#[tauri::command] +fn punctuate(sentence: &str, app: tauri::AppHandle) -> Result { + println!("Input text: {}", sentence); + + // Initialize the Punctuation object if it hasn't been done already + let punctuater = PUNCTUATOR.get_or_init(|| { + + // TODO: download the model? or load it from downloads? + // let resource_dir = app.path().resource_dir().map_err(|e| e.to_string())?; + // let model_path = resource_dir.join("model.onnx"); + // let temp_model_path = std::env::temp_dir().join(&model_path); + // std::fs::copy(&model_path, std::env::temp_dir().join(&model_path)).map_err(|e| e.to_string())?; + + let config = sherpa_rs::punctuate::PunctuationConfig { + // model: temp_model_path.to_string_lossy().to_string(), + model: "/data/local/tmp/model.onnx".to_string(), + ..Default::default() + }; + let punctuator = sherpa_rs::punctuate::Punctuation::new(config) + .map_err(|e| format!("Failed to initialize Punctuation object: {}", e))?; + + Ok(Arc::new(Mutex::new(punctuator))) + }); + + // Lock the mutex to safely access the Punctuation object + let punctuater = punctuater.clone().map_err(|e| e.to_string())?; + let mut punctuater = punctuater + .lock() + .map_err(|e| format!("Failed to get punctuator: {:?}", e))?; + let punctuated = punctuater.add_punctuation(sentence); + + Ok(punctuated) +} + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_opener::init()) + .invoke_handler(tauri::generate_handler![punctuate]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/examples/tauri-app/src-tauri/src/main.rs b/examples/tauri-app/src-tauri/src/main.rs new file mode 100644 index 0000000..2abccd9 --- /dev/null +++ b/examples/tauri-app/src-tauri/src/main.rs @@ -0,0 +1,6 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + tauri_app_lib::run() +} diff --git a/examples/tauri-app/src-tauri/tauri.conf.json b/examples/tauri-app/src-tauri/tauri.conf.json new file mode 100644 index 0000000..15c89b7 --- /dev/null +++ b/examples/tauri-app/src-tauri/tauri.conf.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "tauri-app", + "version": "0.1.0", + "identifier": "com.tauri-app.app", + "build": { + "beforeDevCommand": "bun run dev", + "devUrl": "http://localhost:1420", + "beforeBuildCommand": "bun run build", + "frontendDist": "../dist" + }, + "app": { + "withGlobalTauri": true, + "windows": [ + { + "title": "tauri-app", + "width": 800, + "height": 600 + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } +} \ No newline at end of file diff --git a/examples/tauri-app/src/main.ts b/examples/tauri-app/src/main.ts new file mode 100644 index 0000000..afab729 --- /dev/null +++ b/examples/tauri-app/src/main.ts @@ -0,0 +1,30 @@ +import { invoke } from "@tauri-apps/api/core"; + +let inputElement: HTMLInputElement | null; +let outputElement: HTMLElement | null; + +async function punctuate() { + console.log('punctuate clicked') + if (outputElement && inputElement) { + // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ + try { + const result = await invoke("punctuate", { + sentence: inputElement.value, + }); + console.log('result', result) + outputElement.textContent = result + } catch (error) { + console.error(error) + } + + } +} + +window.addEventListener("DOMContentLoaded", () => { + inputElement = document.querySelector("#input"); + outputElement = document.querySelector("#output"); + document.querySelector("#input-form")?.addEventListener("submit", (e) => { + e.preventDefault(); + punctuate(); + }); +}); diff --git a/examples/tauri-app/tsconfig.json b/examples/tauri-app/tsconfig.json new file mode 100644 index 0000000..75abdef --- /dev/null +++ b/examples/tauri-app/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/examples/tauri-app/vite.config.ts b/examples/tauri-app/vite.config.ts new file mode 100644 index 0000000..e8e567a --- /dev/null +++ b/examples/tauri-app/vite.config.ts @@ -0,0 +1,30 @@ +import { defineConfig } from "vite"; + +// @ts-expect-error process is a nodejs global +const host = process.env.TAURI_DEV_HOST; + +// https://vitejs.dev/config/ +export default defineConfig(async () => ({ + + // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` + // + // 1. prevent vite from obscuring rust errors + clearScreen: false, + // 2. tauri expects a fixed port, fail if that port is not available + server: { + port: 1420, + strictPort: true, + host: host || false, + hmr: host + ? { + protocol: "ws", + host, + port: 1421, + } + : undefined, + watch: { + // 3. tell vite to ignore watching `src-tauri` + ignored: ["**/src-tauri/**"], + }, + }, +})); diff --git a/examples/transducer.rs b/examples/transducer.rs new file mode 100644 index 0000000..ad861b5 --- /dev/null +++ b/examples/transducer.rs @@ -0,0 +1,44 @@ +use sherpa_rs::read_audio_file; +use sherpa_rs::transducer::{TransducerConfig, TransducerRecognizer}; +use std::time::Instant; + +/* +wget https://huggingface.co/csukuangfj/sherpa-onnx-zipformer-en-libriheavy-20230926-small/resolve/main/decoder-epoch-90-avg-20.onnx +wget https://huggingface.co/csukuangfj/sherpa-onnx-zipformer-en-libriheavy-20230926-small/resolve/main/encoder-epoch-90-avg-20.onnx +wget https://huggingface.co/csukuangfj/sherpa-onnx-zipformer-en-libriheavy-20230926-small/resolve/main/joiner-epoch-90-avg-20.onnx +wget https://huggingface.co/csukuangfj/sherpa-onnx-zipformer-en-libriheavy-20230926-small/resolve/main/tokens.txt +wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/motivation.wav -O motivation.wav +cargo run --example transducer motivation.wav +*/ + +pub fn main() { + let path = std::env::args().nth(1).expect("Missing file path argument"); + let (samples, sample_rate) = read_audio_file(&path).unwrap(); + + // Check if the sample rate is 16000 + if sample_rate != 16000 { + panic!("The sample rate must be 16000."); + } + + let config = TransducerConfig { + decoder: "decoder-epoch-90-avg-20.onnx".to_string(), + encoder: "encoder-epoch-90-avg-20.onnx".to_string(), + joiner: "joiner-epoch-90-avg-20.onnx".to_string(), + tokens: "tokens.txt".to_string(), + num_threads: 1, + sample_rate: 16_000, + feature_dim: 80, + debug: true, + ..Default::default() + }; + + let mut recognizer = TransducerRecognizer::new(config).unwrap(); + + let start_t = Instant::now(); + let result = recognizer.transcribe(sample_rate, &samples); + let lower_case = result.to_lowercase(); + let trimmed_result = lower_case.trim(); + + println!("Time taken for decode: {:?}", start_t.elapsed()); + println!("Transcribe result: {:?}", trimmed_result); +} diff --git a/examples/transducer_vosk.rs b/examples/transducer_vosk.rs new file mode 100644 index 0000000..0514234 --- /dev/null +++ b/examples/transducer_vosk.rs @@ -0,0 +1,50 @@ +use sherpa_rs::read_audio_file; +use sherpa_rs::transducer::{TransducerConfig, TransducerRecognizer}; +use std::time::Instant; + +/* +wget https://huggingface.co/alphacep/vosk-model-ru/resolve/main/am-onnx/decoder.onnx +wget https://huggingface.co/alphacep/vosk-model-ru/resolve/main/am-onnx/encoder.onnx +wget https://huggingface.co/alphacep/vosk-model-ru/resolve/main/am-onnx/joiner.onnx +wget https://huggingface.co/alphacep/vosk-model-ru/resolve/main/lang/tokens.txt +wget https://huggingface.co/alphacep/vosk-model-ru/resolve/main/lang/unigram_500.vocab +wget https://huggingface.co/alphacep/vosk-model-ru/resolve/main/test.wav +touch hotwords.txt +cargo run --example transducer_vosk test.wav +*/ +pub fn main() { + let path = std::env::args().nth(1).expect("Missing file path argument"); + let (samples, sample_rate) = read_audio_file(&path).unwrap(); + + // Check if the sample rate is 16000 + if sample_rate != 16000 { + panic!("The sample rate must be 16000."); + } + + let config = TransducerConfig { + decoder: "decoder.onnx".to_string(), + encoder: "encoder.onnx".to_string(), + joiner: "joiner.onnx".to_string(), + tokens: "tokens.txt".to_string(), + bpe_vocab: "unigram_500.vocab".to_string(), + hotwords_file: "hotwords.txt".to_string(), + hotwords_score: 1.2, + num_threads: 1, + sample_rate: 16_000, + feature_dim: 80, + modeling_unit: "bpe".to_string(), + decoding_method: "modified_beam_search".to_string(), + debug: true, + ..Default::default() + }; + + let mut recognizer = TransducerRecognizer::new(config).unwrap(); + + let start_t = Instant::now(); + let result = recognizer.transcribe(sample_rate, &samples); + let lower_case = result.to_lowercase(); + let trimmed_result = lower_case.trim(); + + println!("Time taken for decode: {:?}", start_t.elapsed()); + println!("Transcribe result: {:?}", trimmed_result); +} diff --git a/examples/tts.rs b/examples/tts.rs deleted file mode 100644 index 879e82e..0000000 --- a/examples/tts.rs +++ /dev/null @@ -1,85 +0,0 @@ -/* -Piper English model -wget https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-en_US-amy-low.tar.bz2 -tar xf vits-piper-en_US-amy-low.tar.bz2 -cargo run --example tts --features="tts" -- --text "liliana, the most beautiful and lovely assistant of our team!" --output audio.wav --tokens "vits-piper-en_US-amy-low/tokens.txt" --model "vits-piper-en_US-amy-low/en_US-amy-low.onnx" --data-dir "vits-piper-en_US-amy-low/espeak-ng-data" - -High quality vits-ljs with emotions voice -wget https://huggingface.co/csukuangfj/vits-ljs/resolve/main/vits-ljs.onnx -wget https://huggingface.co/csukuangfj/vits-ljs/resolve/main/lexicon.txt -wget https://huggingface.co/csukuangfj/vits-ljs/resolve/main/tokens.txt -cargo run --example tts --features="tts" -- --text "liliana, the most beautiful and lovely assistant of our team!"" --output audio.wav --tokens "tokens.txt" --model "vits-ljs.onnx" --lexicon lexicon.txt - -MMS Hebrew model -wget https://huggingface.co/thewh1teagle/mms-tts-heb/resolve/main/model_sherpa.onnx -wget https://huggingface.co/thewh1teagle/mms-tts-heb/resolve/main/tokens.txt -cargo run --example tts --features="tts" -- --text "שלום וברכה, ניפרד בשמחה" --output audio.wav --tokens "tokens.txt" --model "model_sherpa.onnx" -*/ -use clap::Parser; - -/// TTS -#[derive(Parser, Debug)] -#[command(version, about, long_about = None)] -struct Args { - #[arg(short, long)] - tokens: String, - - #[arg(short, long)] - model: String, - - #[arg(long)] - text: Option, - - #[arg(long)] - text_file_input: Option, - - #[arg(short, long)] - output: String, - - #[arg(long)] - dict_dir: Option, - - #[arg(long)] - data_dir: Option, - - #[arg(long)] - lexicon: Option, - - #[arg(long)] - provider: Option, - - #[arg(long)] - debug: bool, -} - -fn main() { - // Parse command-line arguments into `Args` struct - let args = Args::parse(); - let text; - if args.text.is_some() { - text = args.text.unwrap(); - } else { - text = std::fs::read_to_string(args.text_file_input.unwrap()).unwrap(); - } - - let vits_cfg = sherpa_rs::tts::TtsVitsModelConfig::new( - args.model, - args.lexicon.unwrap_or_default(), - args.tokens, - args.data_dir.unwrap_or_default(), - 0.0, - 0.0, - args.dict_dir.unwrap_or_default(), - 0.0, - ); - let max_num_sentences = 2; - let model_cfg = - sherpa_rs::tts::OfflineTtsModelConfig::new(args.debug, vits_cfg, args.provider, 1); - let tts_cfg = - sherpa_rs::tts::OfflineTtsConfig::new(model_cfg, max_num_sentences, "".into(), "".into()); - let mut tts = sherpa_rs::tts::OfflineTts::new(tts_cfg); - let speed = 1.0; - let audio = tts.generate(text, 0, speed).unwrap(); - audio.write_to_wav(&args.output).unwrap(); // Use the provided output path - println!("Created {}", args.output); -} diff --git a/examples/tts_kokoro.rs b/examples/tts_kokoro.rs new file mode 100644 index 0000000..eb857ee --- /dev/null +++ b/examples/tts_kokoro.rs @@ -0,0 +1,33 @@ +/* +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/kokoro-multi-lang-v1_0.tar.bz2 +tar xf kokoro-multi-lang-v1_0.tar.bz2 +rm kokoro-multi-lang-v1_0.tar.bz2 +cargo run --example tts_kokoro + +For Chinese: + let sid = 50; + let text = "中英文语音合成测试。This is generated by next generation Kaldi using Kokoro without Misaki. 你觉得中英文说的如何呢?"; +*/ +pub use sherpa_rs::tts::{KokoroTts, KokoroTtsConfig}; + +fn main() { + let config = KokoroTtsConfig { + model: "./kokoro-multi-lang-v1_0/model.onnx".to_string(), + voices: "./kokoro-multi-lang-v1_0/voices.bin".into(), + tokens: "./kokoro-multi-lang-v1_0/tokens.txt".into(), + data_dir: "./kokoro-multi-lang-v1_0/espeak-ng-data".into(), + dict_dir: "./kokoro-multi-lang-v1_0/dict".into(), + lexicon: + "./kokoro-multi-lang-v1_0/lexicon-us-en.txt,./kokoro-multi-lang-v1_0/lexicon-zh.txt" + .into(), + length_scale: 1.0, + ..Default::default() + }; + let mut tts = KokoroTts::new(config); + + let sid = 0; + let text = "This is generated by next generation Kaldi using Kokoro without Misaki."; + let audio = tts.create(&text, sid, 1.0).unwrap(); + sherpa_rs::write_audio_file("audio.wav", &audio.samples, audio.sample_rate).unwrap(); + println!("Created audio.wav") +} diff --git a/examples/tts_matcha.rs b/examples/tts_matcha.rs new file mode 100644 index 0000000..7bf33c1 --- /dev/null +++ b/examples/tts_matcha.rs @@ -0,0 +1,26 @@ +/* +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/matcha-icefall-en_US-ljspeech.tar.bz2 +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/vocoder-models/hifigan_v2.onnx +tar xvf matcha-icefall-en_US-ljspeech.tar.bz2 +rm matcha-icefall-en_US-ljspeech.tar.bz2 + +cargo run --example tts_matcha --features="tts" +*/ +pub use sherpa_rs::tts::{MatchaTts, MatchaTtsConfig}; + +fn main() { + let config = MatchaTtsConfig { + acoustic_model: "./matcha-icefall-en_US-ljspeech/model-steps-3.onnx".into(), + vocoder: "./hifigan_v2.onnx".into(), + tokens: "./matcha-icefall-en_US-ljspeech/tokens.txt".into(), + data_dir: "./matcha-icefall-en_US-ljspeech/espeak-ng-data".into(), + ..Default::default() + }; + let mut tts = MatchaTts::new(config); + let sid = 0; + let audio = tts + .create("Hello! This audio generated by onnx model!", sid, 1.0) + .unwrap(); + sherpa_rs::write_audio_file("audio.wav", &audio.samples, audio.sample_rate).unwrap(); + println!("Created audio.wav") +} diff --git a/examples/tts_vits.rs b/examples/tts_vits.rs new file mode 100644 index 0000000..e4f72e3 --- /dev/null +++ b/examples/tts_vits.rs @@ -0,0 +1,24 @@ +/* +wget https://huggingface.co/csukuangfj/vits-ljs/resolve/main/vits-ljs.onnx +wget https://huggingface.co/csukuangfj/vits-ljs/resolve/main/lexicon.txt +wget https://huggingface.co/csukuangfj/vits-ljs/resolve/main/tokens.txt +cargo run --example tts_vits --features="tts" +*/ +pub use sherpa_rs::tts::{VitsTts, VitsTtsConfig}; + +fn main() { + let config = VitsTtsConfig { + model: "./vits-ljs.onnx".into(), + lexicon: "./lexicon.txt".into(), + tokens: "./tokens.txt".into(), + length_scale: 1.0, + ..Default::default() + }; + let mut tts = VitsTts::new(config); + let sid = 0; + let audio = tts + .create("Hello! This audio generated by onnx model!", sid, 1.0) + .unwrap(); + sherpa_rs::write_audio_file("audio.wav", &audio.samples, audio.sample_rate).unwrap(); + println!("Created audio.wav") +} diff --git a/examples/vad.rs b/examples/vad.rs new file mode 100644 index 0000000..6e45c37 --- /dev/null +++ b/examples/vad.rs @@ -0,0 +1,137 @@ +/* +Detect voice in audio file and mark start and stop. + +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/nemo_en_speakerverification_speakernet.onnx +wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/motivation.wav -O motivation.wav +cargo run --example vad motivation.wav +*/ +use sherpa_rs::{ + embedding_manager, speaker_id, + vad::{Vad, VadConfig}, +}; + +fn get_speaker_name( + embedding_manager: &mut embedding_manager::EmbeddingManager, + embedding: &mut [f32], + speaker_counter: &mut i32, + max_speakers: i32, +) -> String { + let mut name = String::from("unknown"); + + if *speaker_counter == 0 { + name = format!("speaker {}", speaker_counter); + embedding_manager.add(name.clone(), embedding).unwrap(); + *speaker_counter += 1; + } else if *speaker_counter <= max_speakers { + if let Some(search_result) = embedding_manager.search(embedding, 0.5) { + name = search_result; + } else { + name = format!("speaker {}", speaker_counter); + embedding_manager.add(name.clone(), embedding).unwrap(); + *speaker_counter += 1; + } + } else { + let matches = embedding_manager.get_best_matches(embedding, 0.2, *speaker_counter); + if let Some(name_match) = matches.first().map(|m| m.name.clone()) { + name = name_match; + } + } + + name +} + +fn process_speech_segment( + vad: &mut Vad, + sample_rate: u32, + embedding_manager: &mut embedding_manager::EmbeddingManager, + extractor: &mut speaker_id::EmbeddingExtractor, + speaker_counter: &mut i32, + max_speakers: i32, +) { + while !vad.is_empty() { + let segment = vad.front(); + let start_sec = (segment.start as f32) / sample_rate as f32; + let duration_sec = (segment.samples.len() as f32) / sample_rate as f32; + + // Compute the speaker embedding + let mut embedding = extractor + .compute_speaker_embedding(segment.samples, sample_rate) + .unwrap(); + + let name = get_speaker_name( + embedding_manager, + &mut embedding, + speaker_counter, + max_speakers, + ); + println!( + "({}) start={}s end={}s", + name, + start_sec, + start_sec + duration_sec + ); + vad.pop(); + } +} + +fn main() { + let file_path = std::env::args().nth(1).expect("Missing file path argument"); + let max_speakers = 2; + + let (mut samples, sample_rate) = sherpa_rs::read_audio_file(&file_path).unwrap(); + assert_eq!(sample_rate, 16000, "The sample rate must be 16000."); + + // Pad with 3 seconds of silence so vad will be able to detect stop + samples.extend(vec![0.0; (3 * sample_rate) as usize]); + + let extractor_config = speaker_id::ExtractorConfig { + model: "nemo_en_speakerverification_speakernet.onnx".into(), + ..Default::default() + }; + let mut extractor = speaker_id::EmbeddingExtractor::new(extractor_config).unwrap(); + let mut embedding_manager = + embedding_manager::EmbeddingManager::new(extractor.embedding_size.try_into().unwrap()); // Assuming dimension 512 for embeddings + + let mut speaker_counter = 1; + + let window_size = 512; + let vad_config = VadConfig { + model: "silero_vad.onnx".into(), + window_size: window_size as i32, + ..Default::default() + }; + + let mut vad = Vad::new(vad_config, 60.0 * 10.0).unwrap(); + let mut index = 0; + while index + window_size <= samples.len() { + let window = &samples[index..index + window_size]; + vad.accept_waveform(window.to_vec()); // Convert slice to Vec + if vad.is_speech() { + while !vad.is_empty() { + process_speech_segment( + &mut vad, + sample_rate, + &mut embedding_manager, + &mut extractor, + &mut speaker_counter, + max_speakers, + ); + } + } + + index += window_size; + } + vad.flush(); + // process reamaining + while !vad.is_empty() { + process_speech_segment( + &mut vad, + sample_rate, + &mut embedding_manager, + &mut extractor, + &mut speaker_counter, + max_speakers, + ); + } +} diff --git a/examples/vad_segment.rs b/examples/vad_segment.rs index 70e5d49..b4d3865 100644 --- a/examples/vad_segment.rs +++ b/examples/vad_segment.rs @@ -1,44 +1,25 @@ /* -wget https://github.com/snakers4/silero-vad/raw/master/files/silero_vad.onnx +Detect speech in audio file and segment it (mark start and slow time) + +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/motivation.wav -O motivation.wav -cargo run --example vad_segment +cargo run --example vad_segment motivation.wav */ -use eyre::{bail, Result}; use sherpa_rs::vad::{Vad, VadConfig}; -use std::io::Cursor; - -fn main() -> Result<()> { - let path = std::env::args().nth(1).expect("Missing file path argument"); - let audio_data = std::fs::read(path)?; - - let cursor = Cursor::new(audio_data); - let mut reader = hound::WavReader::new(cursor)?; - let sample_rate = reader.spec().sample_rate as i32; - - if sample_rate != 16000 { - bail!("The sample rate must be 16000."); - } - let mut samples: Vec = reader - .samples::() - .map(|s| s.unwrap() as f32 / i16::MAX as f32) - .collect(); +fn main() { + let file_path = std::env::args().nth(1).expect("Missing file path argument"); + let (mut samples, sample_rate) = sherpa_rs::read_audio_file(&file_path).unwrap(); + assert_eq!(sample_rate, 16000, "The sample rate must be 16000."); - let model = "silero_vad.onnx".into(); let window_size: usize = 512; - let config = VadConfig::new( - model, - 0.5, - 0.5, - 0.5, - sample_rate, - window_size.try_into().unwrap(), - None, - None, - Some(true), - ); + let config = VadConfig { + model: "silero_vad.onnx".into(), + window_size: window_size as i32, + ..Default::default() + }; - let mut vad = Vad::new_from_config(config, 3.0).unwrap(); + let mut vad = Vad::new(config, 3.0).unwrap(); while samples.len() > window_size { let window = &samples[..window_size]; vad.accept_waveform(window.to_vec()); // Convert slice to Vec @@ -53,5 +34,4 @@ fn main() -> Result<()> { } samples = samples[window_size..].to_vec(); // Move the remaining samples to the next iteration } - Ok(()) } diff --git a/examples/diarize_whisper.rs b/examples/vad_whisper.rs similarity index 61% rename from examples/diarize_whisper.rs rename to examples/vad_whisper.rs index d4a1670..6a5412e 100644 --- a/examples/diarize_whisper.rs +++ b/examples/vad_whisper.rs @@ -1,85 +1,56 @@ /* +Detect speech in audio file and transcribe it + wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-whisper-tiny.tar.bz2 wget https://github.com/snakers4/silero-vad/raw/master/files/silero_vad.onnx wget https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/nemo_en_speakerverification_speakernet.onnx tar xvf sherpa-onnx-whisper-tiny.tar.bz2 wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/sam_altman.wav -O sam_altman.wav -cargo run --example diarize_whisper sam_altman.wav +cargo run --example vad_whisper sam_altman.wav */ - -use eyre::{bail, Result}; use sherpa_rs::{ - embedding_manager, speaker_id, - transcribe::whisper::WhisperRecognizer, + embedding_manager, read_audio_file, speaker_id, vad::{Vad, VadConfig}, + whisper::{WhisperConfig, WhisperRecognizer}, }; -fn read_audio_file(path: &str) -> Result<(i32, Vec)> { - let mut reader = hound::WavReader::open(path)?; - let sample_rate = reader.spec().sample_rate as i32; - - // Check if the sample rate is 16000 - if sample_rate != 16000 { - bail!("The sample rate must be 16000."); - } - - // Collect samples into a Vec - let samples: Vec = reader - .samples::() - .map(|s| s.unwrap() as f32 / i16::MAX as f32) - .collect(); - - Ok((sample_rate, samples)) -} - -fn main() -> Result<()> { +fn main() { // Read audio data from the file let path = std::env::args().nth(1).expect("Missing file path argument"); - let (sample_rate, mut samples) = read_audio_file(&path)?; + let (mut samples, sample_rate) = read_audio_file(&path).unwrap(); + assert_eq!(sample_rate, 16000, "The sample rate must be 16000."); - // Pad with 3 seconds of slience so vad will able to detect stop - for _ in 0..3 * sample_rate { - samples.push(0.0); - } + // Pad with 3 seconds of silence so vad will be able to detect stop + samples.extend(vec![0.0; (3 * sample_rate) as usize]); - let extractor_config = speaker_id::ExtractorConfig::new( - "nemo_en_speakerverification_speakernet.onnx".into(), - None, - None, - false, - ); - let mut extractor = speaker_id::EmbeddingExtractor::new_from_config(extractor_config).unwrap(); + let extractor_config = speaker_id::ExtractorConfig { + model: "nemo_en_speakerverification_speakernet.onnx".into(), + ..Default::default() + }; + let mut extractor = speaker_id::EmbeddingExtractor::new(extractor_config).unwrap(); let mut embedding_manager = embedding_manager::EmbeddingManager::new(extractor.embedding_size.try_into().unwrap()); // Assuming dimension 512 for embeddings - let mut recognizer = WhisperRecognizer::new( - "sherpa-onnx-whisper-tiny/tiny-decoder.onnx".into(), - "sherpa-onnx-whisper-tiny/tiny-encoder.onnx".into(), - "sherpa-onnx-whisper-tiny/tiny-tokens.txt".into(), - "en".into(), - Some(false), - None, - None, - None, - ); + let config = WhisperConfig { + decoder: "sherpa-onnx-whisper-tiny/tiny-decoder.onnx".into(), + encoder: "sherpa-onnx-whisper-tiny/tiny-encoder.onnx".into(), + tokens: "sherpa-onnx-whisper-tiny/tiny-tokens.txt".into(), + language: "en".into(), + ..Default::default() // fill in any missing fields with defaults + }; + + let mut recognizer = WhisperRecognizer::new(config).unwrap(); let mut speaker_counter = 0; - let vad_model = "silero_vad.onnx".into(); let window_size: usize = 512; - let config = VadConfig::new( - vad_model, - 0.4, - 0.4, - 0.5, - sample_rate, - window_size.try_into().unwrap(), - None, - None, - Some(false), - ); + let vad_config = VadConfig { + model: "silero_vad.onnx".into(), + window_size: window_size as i32, + ..Default::default() + }; - let mut vad = Vad::new_from_config(config, 60.0 * 10.0).unwrap(); + let mut vad = Vad::new(vad_config, 60.0 * 10.0).unwrap(); let mut index = 0; while index + window_size <= samples.len() { let window = &samples[index..index + window_size]; @@ -89,17 +60,18 @@ fn main() -> Result<()> { let segment = vad.front(); let start_sec = (segment.start as f32) / sample_rate as f32; let duration_sec = (segment.samples.len() as f32) / sample_rate as f32; - let transcript = recognizer.transcribe(sample_rate, segment.samples.clone()); + let transcript = recognizer.transcribe(sample_rate, &segment.samples); // Compute the speaker embedding - let mut embedding = - extractor.compute_speaker_embedding(sample_rate, segment.samples)?; + let mut embedding = extractor + .compute_speaker_embedding(segment.samples, sample_rate) + .unwrap(); let name = if let Some(speaker_name) = embedding_manager.search(&embedding, 0.4) { speaker_name } else { // Register a new speaker and add the embedding let name = format!("speaker {}", speaker_counter); - embedding_manager.add(name.clone(), &mut embedding)?; + embedding_manager.add(name.clone(), &mut embedding).unwrap(); speaker_counter += 1; name @@ -124,18 +96,19 @@ fn main() -> Result<()> { let segment = vad.front(); let start_sec = (segment.start as f32) / sample_rate as f32; let duration_sec = (segment.samples.len() as f32) / sample_rate as f32; - let transcript = recognizer.transcribe(sample_rate, segment.samples.clone()); + let transcript = recognizer.transcribe(sample_rate, &segment.samples); // Compute the speaker embedding - let mut embedding = - extractor.compute_speaker_embedding(sample_rate, segment.samples)?; + let mut embedding = extractor + .compute_speaker_embedding(segment.samples, sample_rate) + .unwrap(); let name = if let Some(speaker_name) = embedding_manager.search(&embedding, 0.4) { speaker_name } else { // Register a new speaker and add the embedding let name = format!("speaker {}", speaker_counter); - embedding_manager.add(name.clone(), &mut embedding)?; + embedding_manager.add(name.clone(), &mut embedding).unwrap(); speaker_counter += 1; name @@ -150,5 +123,4 @@ fn main() -> Result<()> { vad.pop(); } } - Ok(()) } diff --git a/examples/whisper.rs b/examples/whisper.rs new file mode 100644 index 0000000..4e9bcda --- /dev/null +++ b/examples/whisper.rs @@ -0,0 +1,38 @@ +/* +Transcribe wav file using OpenAI Whisper + +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-whisper-tiny.tar.bz2 +tar xvf sherpa-onnx-whisper-tiny.tar.bz2 +wget https://github.com/thewh1teagle/sherpa-rs/releases/download/v0.1.0/motivation.wav -O motivation.wav +cargo run --example whisper motivation.wav +*/ + +use sherpa_rs::{ + read_audio_file, + whisper::{WhisperConfig, WhisperRecognizer}, +}; + +fn main() { + let path = std::env::args().nth(1).expect("Missing file path argument"); + let provider = std::env::args().nth(2).unwrap_or("cpu".into()); + let (samples, sample_rate) = read_audio_file(&path).unwrap(); + assert_eq!(sample_rate, 16000, "The sample rate must be 16000."); + + let config = WhisperConfig { + decoder: "sherpa-onnx-whisper-tiny/tiny-decoder.onnx".into(), + encoder: "sherpa-onnx-whisper-tiny/tiny-encoder.onnx".into(), + tokens: "sherpa-onnx-whisper-tiny/tiny-tokens.txt".into(), + language: "en".into(), + provider: Some(provider), + num_threads: None, + bpe_vocab: None, + ..Default::default() // fill in any missing fields with defaults + }; + + let mut recognizer = WhisperRecognizer::new(config).unwrap(); + + let start_t = std::time::Instant::now(); + let result = recognizer.transcribe(sample_rate, &samples); + println!("✅ Text: {}", result.text); + println!("⏱️ Time taken for transcription: {:?}", start_t.elapsed()); +} diff --git a/examples/zipformer.rs b/examples/zipformer.rs new file mode 100644 index 0000000..9355fb9 --- /dev/null +++ b/examples/zipformer.rs @@ -0,0 +1,53 @@ +/* +Use ASR models for extract text from audio + +English: +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-zipformer-small-en-2023-06-26.tar.bz2 +tar xvf sherpa-onnx-zipformer-small-en-2023-06-26.tar.bz2 +rm sherpa-onnx-zipformer-small-en-2023-06-26.tar.bz2 + +cargo run --example zipformer -- \ + "sherpa-onnx-zipformer-small-en-2023-06-26/test_wavs/0.wav" \ + "sherpa-onnx-zipformer-small-en-2023-06-26/encoder-epoch-99-avg-1.onnx" \ + "sherpa-onnx-zipformer-small-en-2023-06-26/decoder-epoch-99-avg-1.onnx" \ + "sherpa-onnx-zipformer-small-en-2023-06-26/joiner-epoch-99-avg-1.onnx" \ + "sherpa-onnx-zipformer-small-en-2023-06-26/tokens.txt" + +Japanse: +wget https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-zipformer-ja-reazonspeech-2024-08-01.tar.bz2 +tar xvf sherpa-onnx-zipformer-ja-reazonspeech-2024-08-01.tar.bz2 +rm sherpa-onnx-zipformer-ja-reazonspeech-2024-08-01.tar.bz2 + +cargo run --example zipformer -- \ + "sherpa-onnx-zipformer-ja-reazonspeech-2024-08-01/test_wavs/1.wav" \ + "sherpa-onnx-zipformer-ja-reazonspeech-2024-08-01/encoder-epoch-99-avg-1.onnx" \ + "sherpa-onnx-zipformer-ja-reazonspeech-2024-08-01/decoder-epoch-99-avg-1.onnx" \ + "sherpa-onnx-zipformer-ja-reazonspeech-2024-08-01/joiner-epoch-99-avg-1.onnx" \ + "sherpa-onnx-zipformer-ja-reazonspeech-2024-08-01/tokens.txt" +*/ + +use sherpa_rs::zipformer::ZipFormer; +fn main() { + let args: Vec = std::env::args().collect(); + let (wav_path, encoder_path, decoder_path, joiner_path, tokens_path) = ( + args.get(1).expect("Missing wav file path argument"), + args.get(2).expect("Missing encoder path argument"), + args.get(3).expect("Missing decoder path argument"), + args.get(4).expect("Missing joiner path argument"), + args.get(5).expect("Missing tokens path argument"), + ); + + // Read the WAV file + let (samples, sample_rate) = sherpa_rs::read_audio_file(wav_path).unwrap(); + + let config = sherpa_rs::zipformer::ZipFormerConfig { + encoder: encoder_path.into(), + decoder: decoder_path.into(), + joiner: joiner_path.into(), + tokens: tokens_path.into(), + ..Default::default() + }; + let mut zipformer = ZipFormer::new(config).unwrap(); + let text = zipformer.decode(sample_rate, samples); + println!("✅ Text: {}", text); +} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 3d4a484..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,75 +0,0 @@ -pub mod add_punctuation; -pub mod embedding_manager; -pub mod language_id; -pub mod online; -pub mod speaker_id; -pub mod transcribe; -pub mod vad; - -use eyre::{bail, Result}; - -#[cfg(feature = "tts")] -pub mod tts; - -#[derive(Debug, Clone, Copy)] -pub enum Provider { - Cpu, - Cuda, - Coreml, - Directml, -} - -impl Provider { - pub fn to_ptr(&self) -> *const u8 { - match self { - Provider::Cpu => "cpu\0".as_ptr(), - Provider::Cuda => "cuda\0".as_ptr(), - Provider::Coreml => "coreml\0".as_ptr(), - Provider::Directml => "directml\0".as_ptr(), - } - } -} - -pub const fn get_default_provider() -> &'static str { - if cfg!(feature = "cuda") { - "cuda" - } else if cfg!(target_os = "macos") { - "coreml" - } else if cfg!(feature = "directml") { - "directml" - } else { - "cpu" - } -} - -pub fn read_audio_file(path: &str) -> Result<(i32, Vec)> { - let mut reader = hound::WavReader::open(path)?; - let sample_rate = reader.spec().sample_rate as i32; - - // Check if the sample rate is 16000 - if sample_rate != 16000 { - bail!("The sample rate must be 16000."); - } - - // Collect samples into a Vec - let samples: Vec = reader - .samples::() - .map(|s| s.unwrap() as f32 / i16::MAX as f32) - .collect(); - - Ok((sample_rate, samples)) -} - -#[macro_export] -macro_rules! cstr { - ($s:expr) => { - CString::new($s).expect("Failed to create CString") - }; -} - -#[macro_export] -macro_rules! cstr_to_string { - ($ptr:expr) => { - std::ffi::CStr::from_ptr($ptr).to_string_lossy().to_string() - }; -} diff --git a/src/online/mod.rs b/src/online/mod.rs deleted file mode 100644 index b24741f..0000000 --- a/src/online/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod paraformer; -pub mod stream; -pub mod transducer; -pub mod zipformer2_ctc; diff --git a/src/online/stream/keyword_spotter.rs b/src/online/stream/keyword_spotter.rs deleted file mode 100644 index 5d8507e..0000000 --- a/src/online/stream/keyword_spotter.rs +++ /dev/null @@ -1,113 +0,0 @@ -use std::{ - ffi::{CStr, CString}, - path::Path, -}; - -use sherpa_rs_sys::{ - SherpaOnnxFeatureConfig, SherpaOnnxKeywordSpotterConfig, SherpaOnnxOnlineModelConfig, -}; - -use crate::{get_default_provider, online::transducer::Transducer}; - -use super::OnlineStream; - -pub struct KeywordSpottingStream { - spotter: *mut sherpa_rs_sys::SherpaOnnxKeywordSpotter, - stream: *mut sherpa_rs_sys::SherpaOnnxOnlineStream, -} -impl KeywordSpottingStream { - pub fn from_transducer( - transducer: Transducer, - provider: Option<&str>, - tokens: &Path, - debug: bool, - - file: &Path, - num_threads: Option, - - keywords: Option<&str>, - ) -> Self { - let tokens_c = CString::new(tokens.to_str().unwrap()).unwrap(); - let provider_c = CString::new(provider.unwrap_or(get_default_provider())).unwrap(); - let model_type = transducer.model_type(); - let mut model_config = unsafe { std::mem::zeroed::() }; - // model_config.model_type = zipformer.model_type().into_raw(); - model_config.transducer = transducer.as_config(); - model_config.tokens = tokens_c.into_raw(); - model_config.num_threads = num_threads.unwrap_or(1); - model_config.provider = provider_c.into_raw(); - model_config.debug = debug as i32; - model_config.modeling_unit = CString::new("cjkchar").unwrap().into_raw(); - model_config.model_type = model_type.into_raw(); - - Self::new(model_config, file, keywords) - } - - pub fn new(model: SherpaOnnxOnlineModelConfig, file: &Path, keywords: Option<&str>) -> Self { - let files_c = CString::new(file.to_str().unwrap()).unwrap(); - - let mut config = unsafe { std::mem::zeroed::() }; - config.feat_config = SherpaOnnxFeatureConfig { - sample_rate: 16000, - feature_dim: 80, - }; - config.model_config = model; - config.num_trailing_blanks = 1; - config.keywords_score = 1.0; - config.keywords_threshold = 0.25; - config.keywords_file = files_c.into_raw(); - let spotter = unsafe { sherpa_rs_sys::SherpaOnnxCreateKeywordSpotter(&config) }; - - let stream = if let Some(keywords) = keywords { - let keywords = std::ffi::CString::new(keywords).unwrap(); - unsafe { - sherpa_rs_sys::SherpaOnnxCreateKeywordStreamWithKeywords(spotter, keywords.as_ptr()) - } - } else { - unsafe { sherpa_rs_sys::SherpaOnnxCreateKeywordStream(spotter) } - }; - Self { spotter, stream } - } -} - -impl Drop for KeywordSpottingStream { - fn drop(&mut self) { - unsafe { - sherpa_rs_sys::SherpaOnnxDestroyOnlineStream(self.stream); - sherpa_rs_sys::SherpaOnnxDestroyKeywordSpotter(self.spotter); - } - } -} - -impl OnlineStream for KeywordSpottingStream { - fn accept_waveform(&mut self, sample_rate: i32, samples: Vec) { - unsafe { - sherpa_rs_sys::SherpaOnnxOnlineStreamAcceptWaveform( - self.stream, - sample_rate, - samples.as_ptr(), - samples.len() as i32, - ); - } - } - - fn decode_stream(&mut self) { - unsafe { - sherpa_rs_sys::SherpaOnnxDecodeKeywordStream(self.spotter, self.stream); - } - } - - fn is_ready(&mut self) -> bool { - unsafe { sherpa_rs_sys::SherpaOnnxIsKeywordStreamReady(self.spotter, self.stream) == 1 } - } - - fn get_result(&mut self) -> String { - unsafe { - let result = sherpa_rs_sys::SherpaOnnxGetKeywordResult(self.spotter, self.stream); - let result = std::ptr::read(result); - let keyword = CStr::from_ptr(result.keyword); - let keyword = keyword.to_str().unwrap().to_string(); - keyword - } - } -} diff --git a/src/transcribe/mod.rs b/src/transcribe/mod.rs deleted file mode 100644 index e658f36..0000000 --- a/src/transcribe/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod whisper; diff --git a/src/transcribe/whisper.rs b/src/transcribe/whisper.rs deleted file mode 100644 index fdb8151..0000000 --- a/src/transcribe/whisper.rs +++ /dev/null @@ -1,171 +0,0 @@ -use crate::{cstr, get_default_provider}; -use std::{ - ffi::{CStr, CString}, - ptr::null, -}; - -#[derive(Debug)] -pub struct WhisperRecognizer { - recognizer: *mut sherpa_rs_sys::SherpaOnnxOfflineRecognizer, -} - -#[derive(Debug)] -pub struct WhisperRecognizerResult { - pub text: String, - // pub timestamps: Vec, -} - -impl WhisperRecognizer { - pub fn new( - decoder: String, - encoder: String, - tokens: String, - language: String, - debug: Option, - provider: Option<&str>, - num_threads: Option, - bpe_vocab: Option, - ) -> Self { - let decoder_c = cstr!(decoder); - let encoder_c = cstr!(encoder); - let langauge_c = cstr!(language); - let task_c = cstr!("transcribe".to_string()); - let tail_paddings = 0; - let tokens_c = cstr!(tokens); - - let debug = debug.unwrap_or_default(); - let debug = if debug { 1 } else { 0 }; - let provider = provider.unwrap_or(get_default_provider()); - let provider_c = cstr!(provider); - let num_threads = num_threads.unwrap_or(2); - let bpe_vocab = bpe_vocab.unwrap_or("".into()); - let bpe_vocab_c = cstr!(bpe_vocab); - - let whisper = sherpa_rs_sys::SherpaOnnxOfflineWhisperModelConfig { - decoder: decoder_c.into_raw(), - encoder: encoder_c.into_raw(), - language: langauge_c.into_raw(), - task: task_c.into_raw(), - tail_paddings, - }; - - let sense_voice_model_c = cstr!("".to_string()); - let sense_voice_language_c = cstr!("".to_string()); - let sense_voice = sherpa_rs_sys::SherpaOnnxOfflineSenseVoiceModelConfig { - model: sense_voice_model_c.into_raw(), - language: sense_voice_language_c.into_raw(), - use_itn: 0, - }; - - let model_config = sherpa_rs_sys::SherpaOnnxOfflineModelConfig { - bpe_vocab: bpe_vocab_c.into_raw(), - debug, - model_type: null(), - modeling_unit: null(), - nemo_ctc: sherpa_rs_sys::SherpaOnnxOfflineNemoEncDecCtcModelConfig { model: null() }, - num_threads, - paraformer: sherpa_rs_sys::SherpaOnnxOfflineParaformerModelConfig { model: null() }, - provider: provider_c.into_raw(), - tdnn: sherpa_rs_sys::SherpaOnnxOfflineTdnnModelConfig { model: null() }, - telespeech_ctc: null(), - tokens: tokens_c.into_raw(), - transducer: sherpa_rs_sys::SherpaOnnxOfflineTransducerModelConfig { - encoder: null(), - decoder: null(), - joiner: null(), - }, - whisper, - sense_voice, - }; - let decoding_method_c = CString::new("greedy_search").unwrap(); - let config = sherpa_rs_sys::SherpaOnnxOfflineRecognizerConfig { - decoding_method: decoding_method_c.into_raw(), // greedy_search, modified_beam_search - feat_config: sherpa_rs_sys::SherpaOnnxFeatureConfig { - sample_rate: 16000, - feature_dim: 512, - }, - hotwords_file: null(), - hotwords_score: 0.0, - lm_config: sherpa_rs_sys::SherpaOnnxOfflineLMConfig { - model: null(), - scale: 0.0, - }, - max_active_paths: 0, - model_config, - rule_fars: null(), - rule_fsts: null(), - }; - let recognizer = unsafe { sherpa_rs_sys::SherpaOnnxCreateOfflineRecognizer(&config) }; - - Self { recognizer } - } - - pub fn transcribe(&mut self, sample_rate: i32, samples: Vec) -> WhisperRecognizerResult { - unsafe { - let stream = sherpa_rs_sys::SherpaOnnxCreateOfflineStream(self.recognizer); - sherpa_rs_sys::SherpaOnnxAcceptWaveformOffline( - stream, - sample_rate, - samples.as_ptr(), - samples.len().try_into().unwrap(), - ); - sherpa_rs_sys::SherpaOnnxDecodeOfflineStream(self.recognizer, stream); - let result_ptr = sherpa_rs_sys::SherpaOnnxGetOfflineStreamResult(stream); - let raw_result = result_ptr.read(); - let text = CStr::from_ptr(raw_result.text); - let text = text.to_str().unwrap().to_string(); - // let timestamps: &[f32] = - // std::slice::from_raw_parts(raw_result.timestamps, raw_result.count as usize); - let result = WhisperRecognizerResult { text }; - // Free - sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizerResult(result_ptr); - sherpa_rs_sys::SherpaOnnxDestroyOfflineStream(stream); - return result; - } - } -} - -unsafe impl Send for WhisperRecognizer {} -unsafe impl Sync for WhisperRecognizer {} - -impl Drop for WhisperRecognizer { - fn drop(&mut self) { - unsafe { - sherpa_rs_sys::SherpaOnnxDestroyOfflineRecognizer(self.recognizer); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::read_audio_file; - use std::time::Instant; - - #[test] - fn test_whisper_transcribe() { - let path = "motivation.wav"; - let (sample_rate, samples) = read_audio_file(&path).expect("file not found"); - - // Check if the sample rate is 16000 - if sample_rate != 16000 { - panic!("The sample rate must be 16000."); - } - - let mut recognizer = WhisperRecognizer::new( - "sherpa-onnx-whisper-tiny/tiny-decoder.onnx".into(), - "sherpa-onnx-whisper-tiny/tiny-encoder.onnx".into(), - "sherpa-onnx-whisper-tiny/tiny-tokens.txt".into(), - "en".into(), - Some(true), - None, - None, - None, - ); - - let start_t = Instant::now(); - let result = recognizer.transcribe(sample_rate, samples); - println!("{:?}", result); - println!("Time taken for transcription: {:?}", start_t.elapsed()); - } -} diff --git a/src/tts.rs b/src/tts.rs deleted file mode 100644 index 9470d37..0000000 --- a/src/tts.rs +++ /dev/null @@ -1,179 +0,0 @@ -use crate::get_default_provider; -use eyre::{bail, Result}; -use hound::{WavSpec, WavWriter}; -use std::ffi::CString; - -#[derive(Debug)] -pub struct TtsVitsModelConfig { - pub(crate) cfg: sherpa_rs_sys::SherpaOnnxOfflineTtsVitsModelConfig, -} - -#[derive(Debug)] -pub struct OfflineTtsModelConfig { - pub(crate) cfg: sherpa_rs_sys::SherpaOnnxOfflineTtsModelConfig, -} - -#[derive(Debug)] -pub struct OfflineTtsConfig { - pub(crate) cfg: sherpa_rs_sys::SherpaOnnxOfflineTtsConfig, -} - -#[derive(Debug)] -pub struct OfflineTts { - pub(crate) tts: *mut sherpa_rs_sys::SherpaOnnxOfflineTts, -} - -impl TtsVitsModelConfig { - pub fn new( - model: String, - lexicon: String, - tokens: String, - data_dir: String, - noise_scale: f32, - noise_scale_w: f32, - dict_dir: String, - length_scale: f32, - ) -> Self { - let c_model = CString::new(model).unwrap(); - let c_lexicon = CString::new(lexicon).unwrap(); - let c_tokens = CString::new(tokens).unwrap(); - let c_data_dir = CString::new(data_dir).unwrap(); - let c_dict_dir = CString::new(dict_dir).unwrap(); - - let cfg = sherpa_rs_sys::SherpaOnnxOfflineTtsVitsModelConfig { - model: c_model.into_raw(), - lexicon: c_lexicon.into_raw(), - tokens: c_tokens.into_raw(), - data_dir: c_data_dir.into_raw(), - noise_scale, - noise_scale_w, - dict_dir: c_dict_dir.into_raw(), - length_scale, - }; - Self { cfg } - } -} - -impl OfflineTtsModelConfig { - pub fn new( - debug: bool, - vits_config: TtsVitsModelConfig, - provider: Option, - num_threads: i32, - ) -> Self { - let debug = if debug { 1 } else { 0 }; - - let provider = provider.unwrap_or(get_default_provider().to_owned()); - let provider_c = CString::new(provider).unwrap(); - - let cfg = sherpa_rs_sys::SherpaOnnxOfflineTtsModelConfig { - debug, - num_threads, - vits: vits_config.cfg, - provider: provider_c.into_raw(), - }; - Self { cfg } - } -} - -impl OfflineTtsConfig { - pub fn new( - model: OfflineTtsModelConfig, - max_num_sentences: i32, - rule_fars: String, - rule_fsts: String, - ) -> Self { - let rule_fars_c = CString::new(rule_fars).unwrap(); - let rule_fsts_c = CString::new(rule_fsts).unwrap(); - - let cfg = sherpa_rs_sys::SherpaOnnxOfflineTtsConfig { - max_num_sentences, - model: model.cfg, - rule_fars: rule_fars_c.into_raw(), - rule_fsts: rule_fsts_c.into_raw(), - }; - OfflineTtsConfig { cfg } - } -} - -#[derive(Debug)] -pub struct TtsSample { - pub samples: Vec, - pub sample_rate: i32, - pub duration: i32, -} - -impl TtsSample { - pub fn write_to_wav(&self, filename: &str) -> Result<()> { - let spec = WavSpec { - channels: 1, - sample_rate: self.sample_rate as u32, - bits_per_sample: 32, - sample_format: hound::SampleFormat::Float, - }; - - let mut writer = WavWriter::create(filename, spec)?; - - for &sample in &self.samples { - writer.write_sample(sample)?; - } - - writer.finalize()?; - - Ok(()) - } -} - -impl OfflineTts { - pub fn new(config: OfflineTtsConfig) -> Self { - let tts = unsafe { sherpa_rs_sys::SherpaOnnxCreateOfflineTts(&config.cfg) }; - Self { tts } - } - - pub fn generate(&mut self, text: String, sid: i32, speed: f32) -> Result { - let text_c = CString::new(text).unwrap(); - unsafe { - let audio_ptr = sherpa_rs_sys::SherpaOnnxOfflineTtsGenerate( - self.tts, - text_c.into_raw(), - sid, - speed, - ); - if audio_ptr.is_null() { - bail!("audio is null") - } - let audio = audio_ptr.read(); - - if audio.n.is_negative() { - bail!("no samples found") - } - if audio.samples.is_null() { - bail!("audio samples are null") - } - let samples: &[f32] = std::slice::from_raw_parts(audio.samples, audio.n as usize); - let samples = samples.to_vec(); - let sample_rate = audio.sample_rate; - let duration = samples.len() as i32 / sample_rate; - - // Free - sherpa_rs_sys::SherpaOnnxDestroyOfflineTtsGeneratedAudio(audio_ptr); - - Ok(TtsSample { - samples, - sample_rate, - duration, - }) - } - } -} - -unsafe impl Send for OfflineTts {} -unsafe impl Sync for OfflineTts {} - -impl Drop for OfflineTts { - fn drop(&mut self) { - unsafe { - sherpa_rs_sys::SherpaOnnxDestroyOfflineTts(self.tts); - } - } -} diff --git a/sys/build.rs b/sys/build.rs deleted file mode 100644 index d1ea75e..0000000 --- a/sys/build.rs +++ /dev/null @@ -1,322 +0,0 @@ -use cmake::Config; -use glob::glob; -use std::env; -use std::path::{Path, PathBuf}; -use std::process::Command; - -macro_rules! debug_log { - ($($arg:tt)*) => { - if std::env::var("BUILD_DEBUG").is_ok() { - println!("cargo:warning=[DEBUG] {}", format!($($arg)*)); - } - }; -} - -fn get_cargo_target_dir() -> Result> { - let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR")?); - let profile = std::env::var("PROFILE")?; - let mut target_dir = None; - let mut sub_path = out_dir.as_path(); - while let Some(parent) = sub_path.parent() { - if parent.ends_with(&profile) { - target_dir = Some(parent); - break; - } - sub_path = parent; - } - let target_dir = target_dir.ok_or("not found")?; - Ok(target_dir.to_path_buf()) -} - -fn copy_folder(src: &Path, dst: &Path) { - std::fs::create_dir_all(dst).expect("Failed to create dst directory"); - if cfg!(unix) { - std::process::Command::new("cp") - .arg("-rf") - .arg(src) - .arg(dst.parent().unwrap()) - .status() - .expect("Failed to execute cp command"); - } - - if cfg!(windows) { - std::process::Command::new("robocopy.exe") - .arg("/e") - .arg(src) - .arg(dst) - .status() - .expect("Failed to execute robocopy command"); - } -} - -fn extract_lib_names(out_dir: &Path, build_shared_libs: bool) -> Vec { - let lib_pattern = if cfg!(windows) { - "*.lib" - } else if cfg!(target_os = "macos") { - if build_shared_libs { - "*.dylib" - } else { - "*.a" - } - } else { - if build_shared_libs { - "*.so" - } else { - "*.a" - } - }; - let libs_dir = out_dir.join("lib"); - let pattern = libs_dir.join(lib_pattern); - debug_log!("Extract libs {}", pattern.display()); - - let mut lib_names: Vec = Vec::new(); - - // Process the libraries based on the pattern - for entry in glob(pattern.to_str().unwrap()).unwrap() { - match entry { - Ok(path) => { - let stem = path.file_stem().unwrap(); - let stem_str = stem.to_str().unwrap(); - - // Remove the "lib" prefix if present - let lib_name = if stem_str.starts_with("lib") { - stem_str.strip_prefix("lib").unwrap_or(stem_str) - } else { - stem_str - }; - lib_names.push(lib_name.to_string()); - } - Err(e) => println!("cargo:warning=error={}", e), - } - } - lib_names -} - -fn extract_lib_assets(out_dir: &Path) -> Vec { - let shared_lib_pattern = if cfg!(windows) { - "*.dll" - } else if cfg!(target_os = "macos") { - "*.dylib" - } else { - "*.so" - }; - - let libs_dir = out_dir.join("lib"); - let pattern = libs_dir.join(shared_lib_pattern); - debug_log!("Extract lib assets {}", pattern.display()); - let mut files = Vec::new(); - - for entry in glob(pattern.to_str().unwrap()).unwrap() { - match entry { - Ok(path) => { - files.push(path); - } - Err(e) => eprintln!("cargo:warning=error={}", e), - } - } - - files -} - -fn macos_link_search_path() -> Option { - let output = Command::new("clang") - .arg("--print-search-dirs") - .output() - .ok()?; - if !output.status.success() { - println!( - "failed to run 'clang --print-search-dirs', continuing without a link search path" - ); - return None; - } - - let stdout = String::from_utf8_lossy(&output.stdout); - for line in stdout.lines() { - if line.contains("libraries: =") { - let path = line.split('=').nth(1)?; - return Some(format!("{}/lib/darwin", path)); - } - } - - println!("failed to determine link search path, continuing without it"); - None -} - -fn main() { - let target = env::var("TARGET").unwrap(); - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - - let target_dir = get_cargo_target_dir().unwrap(); - let sherpa_dst = out_dir.join("sherpa-onnx"); - let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("Failed to get CARGO_MANIFEST_DIR"); - let sherpa_src = Path::new(&manifest_dir).join("sherpa-onnx"); - let build_shared_libs = cfg!(feature = "directml") || cfg!(feature = "cuda"); - let profile = if cfg!(debug_assertions) { - "Debug" - } else { - "Release" - }; - - debug_log!("TARGET: {}", target); - debug_log!("CARGO_MANIFEST_DIR: {}", manifest_dir); - debug_log!("TARGET_DIR: {}", target_dir.display()); - debug_log!("OUT_DIR: {}", out_dir.display()); - debug_log!("BUILD_SHARED: {}", build_shared_libs); - - // Prepare sherpa-onnx source - if !sherpa_dst.exists() { - debug_log!("Copy {} to {}", sherpa_src.display(), sherpa_dst.display()); - copy_folder(&sherpa_src, &sherpa_dst); - } - // Speed up build - env::set_var( - "CMAKE_BUILD_PARALLEL_LEVEL", - std::thread::available_parallelism() - .unwrap() - .get() - .to_string(), - ); - - // Bindings - let bindings = bindgen::Builder::default() - .header("wrapper.h") - .clang_arg(format!("-I{}", sherpa_dst.display())) - .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) - .generate() - .expect("Failed to generate bindings"); - - // Write the generated bindings to an output file - let bindings_path = out_dir.join("bindings.rs"); - bindings - .write_to_file(bindings_path) - .expect("Failed to write bindings"); - - println!("cargo:rerun-if-changed=wrapper.h"); - println!("cargo:rerun-if-changed=./sherpa-onnx"); - - debug_log!("Bindings Created"); - - // Build with Cmake - - let mut config = Config::new(&sherpa_dst); - - config - .define("SHERPA_ONNX_ENABLE_C_API", "ON") - .define("SHERPA_ONNX_ENABLE_BINARY", "OFF") - .define("BUILD_SHARED_LIBS", "OFF") - .define("SHERPA_ONNX_ENABLE_WEBSOCKET", "OFF") - .define("SHERPA_ONNX_ENABLE_TTS", "OFF") - .define("SHERPA_ONNX_BUILD_C_API_EXAMPLES", "OFF"); - - if cfg!(windows) { - config.static_crt(true); - } - - // TTS - if cfg!(feature = "tts") { - config.define("SHERPA_ONNX_ENABLE_TTS", "ON"); - } - - // Cuda https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html - if cfg!(feature = "cuda") { - debug_log!("Cuda enabled"); - config.define("SHERPA_ONNX_ENABLE_GPU", "ON"); - config.define("BUILD_SHARED_LIBS", "ON"); - } - - // DirectML https://onnxruntime.ai/docs/execution-providers/DirectML-ExecutionProvider.html - if cfg!(feature = "directml") { - debug_log!("DirectML enabled"); - config.define("SHERPA_ONNX_ENABLE_DIRECTML", "ON"); - config.define("BUILD_SHARED_LIBS", "ON"); - } - - if cfg!(any(windows, target_os = "linux")) { - config.define("SHERPA_ONNX_ENABLE_PORTAUDIO", "ON"); - } - - // General - config - .profile(profile) - .very_verbose(std::env::var("CMAKE_VERBOSE").is_ok()) // Not verbose by default - .always_configure(false); - - let bindings_dir = config.build(); - - // Search paths - println!("cargo:rustc-link-search={}", out_dir.join("lib").display()); - println!("cargo:rustc-link-search={}", bindings_dir.display()); - - // Link libraries - let sherpa_libs_kind = if build_shared_libs { "dylib" } else { "static" }; - let sherpa_libs = extract_lib_names(&out_dir, build_shared_libs); - - for lib in sherpa_libs { - debug_log!( - "LINK {}", - format!("cargo:rustc-link-lib={}={}", sherpa_libs_kind, lib) - ); - println!( - "{}", - format!("cargo:rustc-link-lib={}={}", sherpa_libs_kind, lib) - ); - } - - // Windows debug - if cfg!(all(debug_assertions, windows)) { - println!("cargo:rustc-link-lib=dylib=msvcrtd"); - } - - // macOS - if cfg!(target_os = "macos") { - println!("cargo:rustc-link-lib=framework=Foundation"); - println!("cargo:rustc-link-lib=c++"); - } - - // Linux - if cfg!(target_os = "linux") { - println!("cargo:rustc-link-lib=dylib=stdc++"); - } - - if target.contains("apple") { - // On (older) OSX we need to link against the clang runtime, - // which is hidden in some non-default path. - // - // More details at https://github.com/alexcrichton/curl-rust/issues/279. - if let Some(path) = macos_link_search_path() { - println!("cargo:rustc-link-lib=clang_rt.osx"); - println!("cargo:rustc-link-search={}", path); - } - } - - // copy DLLs to target - if build_shared_libs { - let libs_assets = extract_lib_assets(&out_dir); - for asset in libs_assets { - let asset_clone = asset.clone(); - let filename = asset_clone.file_name().unwrap(); - let filename = filename.to_str().unwrap(); - let dst = target_dir.join(filename); - debug_log!("HARD LINK {} TO {}", asset.display(), dst.display()); - if !dst.exists() { - std::fs::hard_link(asset.clone(), dst).unwrap(); - } - - // Copy DLLs to examples as well - if target_dir.join("examples").exists() { - let dst = target_dir.join("examples").join(filename); - debug_log!("HARD LINK {} TO {}", asset.display(), dst.display()); - if !dst.exists() { - std::fs::hard_link(asset.clone(), dst).unwrap(); - } - } - - // Copy DLLs to target/profile/deps as well for tests - let dst = target_dir.join("deps").join(filename); - debug_log!("HARD LINK {} TO {}", asset.display(), dst.display()); - if !dst.exists() { - std::fs::hard_link(asset.clone(), dst).unwrap(); - } - } - } -} diff --git a/sys/sherpa-onnx b/sys/sherpa-onnx deleted file mode 160000 index d5f4868..0000000 --- a/sys/sherpa-onnx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d5f486878d895ece13a0673e383fc8f69dfcd5d1