From 5a644cad50457d22cdde183020abdfb5d00fd800 Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Wed, 22 May 2024 09:34:59 +0900 Subject: [PATCH 01/36] =?UTF-8?q?onnxruntime-rs=E3=81=8B=E3=82=89ort?= =?UTF-8?q?=E3=81=AB=E4=B9=97=E3=82=8A=E6=8F=9B=E3=81=88=E3=82=8B=20(#725)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * onnxruntime-rsからortに乗り換える * `--features onnxruntime/disable-sys-build-script`を消す * ortをアップデート * ortをアップデート * `onnxruntimeVersion`をアップデート * libonnxruntimeのコピー処理を更新 * ortをアップデート * libonnxruntimeのコピー処理を更新 * ortをアップデート * ortをアップデート * `ort::ExecutionProvider::is_available`を使う * `todo!`を消す * ortをアップデート * ortにあったAPIを使う * ortをアップデート * `$ORT_OUT_DIR`を削除 * ortをアップデート * ログのフィルタを更新 * ortをアップデート * tracingのレベルでortのログを抑える * Minor refactor * ortをアップデート * Fix Cargo.lock * Gradleのlibonnxruntimeのバージョンを更新 * ort v2.0.0-rc.1ベースに切り替える * Gradleのlibonnxruntimeのバージョンを更新 * `with_execution_provider` → `register` * ort v2.0.0-rc.2ベースに切り替える * Gradleのlibonnxruntimeのバージョンを更新 * voicevox-ortを更新 * VOICEVOX/ort#2 に追従する --- .cargo/config.toml | 4 - .github/workflows/build_and_deploy.yml | 23 +- .github/workflows/test.yml | 14 +- Cargo.lock | 123 +++++--- Cargo.toml | 6 +- crates/voicevox_core/Cargo.toml | 5 +- crates/voicevox_core/src/infer.rs | 16 +- .../src/infer/runtimes/onnxruntime.rs | 284 ++++++++---------- crates/voicevox_core/src/infer/session_set.rs | 7 +- crates/voicevox_core_c_api/Cargo.toml | 1 + crates/voicevox_core_c_api/src/lib.rs | 8 +- .../tests/e2e/assert_cdylib.rs | 7 +- crates/voicevox_core_c_api/tests/e2e/main.rs | 10 - crates/voicevox_core_java_api/Cargo.toml | 1 + crates/voicevox_core_java_api/settings.gradle | 2 +- crates/voicevox_core_java_api/src/logger.rs | 12 +- .../src/inference_domain.rs | 18 +- crates/voicevox_core_python_api/Cargo.toml | 1 + 18 files changed, 269 insertions(+), 273 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 64fcc6aea..f015cd17a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -4,10 +4,6 @@ xtask = "run -p xtask --" [env] CARGO_WORKSPACE_DIR = { value = "", relative = true } -# Windows環境でテストエラーになるのを防ぐために設定するworkaround -# https://github.com/VOICEVOX/onnxruntime-rs/issues/3#issuecomment-1207381367 -ORT_OUT_DIR = { value = "target/debug/deps", relative = true } - [target.aarch64-unknown-linux-gnu] linker = "aarch64-linux-gnu-gcc" diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index 83d895c77..01ddfc434 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -58,7 +58,6 @@ jobs: "target": "x86_64-pc-windows-msvc", "artifact_name": "windows-x64-cpu", "whl_local_version": "cpu", - "use_cuda": false, "can_skip_in_simple_test": true }, { @@ -67,16 +66,14 @@ jobs: "target": "x86_64-pc-windows-msvc", "artifact_name": "windows-x64-directml", "whl_local_version": "directml", - "use_cuda": false, "can_skip_in_simple_test": false }, { "os": "windows-2019", - "features": "", + "features": "cuda", "target": "x86_64-pc-windows-msvc", "artifact_name": "windows-x64-cuda", "whl_local_version": "cuda", - "use_cuda": true, "can_skip_in_simple_test": true }, { @@ -85,7 +82,6 @@ jobs: "target": "i686-pc-windows-msvc", "artifact_name": "windows-x86-cpu", "whl_local_version": "cpu", - "use_cuda": false, "can_skip_in_simple_test": true }, { @@ -94,16 +90,14 @@ jobs: "target": "x86_64-unknown-linux-gnu", "artifact_name": "linux-x64-cpu", "whl_local_version": "cpu", - "use_cuda": false, "can_skip_in_simple_test": true }, { "os": "ubuntu-20.04", - "features": "", + "features": "cuda", "target": "x86_64-unknown-linux-gnu", "artifact_name": "linux-x64-gpu", "whl_local_version": "cuda", - "use_cuda": true, "can_skip_in_simple_test": false }, { @@ -112,7 +106,6 @@ jobs: "target": "aarch64-unknown-linux-gnu", "artifact_name": "linux-arm64-cpu", "whl_local_version": "cpu", - "use_cuda": false, "can_skip_in_simple_test": true }, { @@ -120,7 +113,6 @@ jobs: "features": "", "target": "aarch64-linux-android", "artifact_name": "android-arm64-cpu", - "use_cuda": false, "can_skip_in_simple_test": true }, { @@ -128,7 +120,6 @@ jobs: "features": "", "target": "x86_64-linux-android", "artifact_name": "android-x86_64-cpu", - "use_cuda": false, "can_skip_in_simple_test": true }, { @@ -137,7 +128,6 @@ jobs: "target": "aarch64-apple-darwin", "artifact_name": "osx-arm64-cpu", "whl_local_version": "cpu", - "use_cuda": false, "can_skip_in_simple_test": false }, { @@ -146,7 +136,6 @@ jobs: "target": "x86_64-apple-darwin", "artifact_name": "osx-x64-cpu", "whl_local_version": "cpu", - "use_cuda": false, "can_skip_in_simple_test": true }, { @@ -154,7 +143,6 @@ jobs: "features": "", "target": "aarch64-apple-ios", "artifact_name": "ios-arm64-cpu", - "use_cuda": false, "can_skip_in_simple_test": true }, { @@ -162,7 +150,6 @@ jobs: "features": "", "target": "aarch64-apple-ios-sim", "artifact_name": "ios-arm64-cpu-sim", - "use_cuda": false, "can_skip_in_simple_test": true }, { @@ -170,7 +157,6 @@ jobs: "features": "", "target": "x86_64-apple-ios", "artifact_name": "ios-x64-cpu", - "use_cuda": false, "can_skip_in_simple_test": true } ]' @@ -268,7 +254,6 @@ jobs: fi env: RUSTFLAGS: -C panic=abort - ORT_USE_CUDA: ${{ matrix.use_cuda }} - name: build voicevox_core_python_api if: matrix.whl_local_version id: build-voicevox-core-python-api @@ -286,8 +271,6 @@ jobs: build > /dev/null 2>&1 fi echo "whl=$(find ./target/wheels -type f)" >> "$GITHUB_OUTPUT" - env: - ORT_USE_CUDA: ${{ matrix.use_cuda }} - name: build voicevox_core_java_api if: contains(matrix.target, 'android') run: | @@ -305,7 +288,7 @@ jobs: cp -v crates/voicevox_core_c_api/include/voicevox_core.h "artifact/${{ env.ASSET_NAME }}" cp -v target/${{ matrix.target }}/release/*voicevox_core.{dll,so,dylib} "artifact/${{ env.ASSET_NAME }}" || true cp -v target/${{ matrix.target }}/release/voicevox_core.dll.lib "artifact/${{ env.ASSET_NAME }}/voicevox_core.lib" || true - cp -v -n target/${{ matrix.target }}/release/build/onnxruntime-sys-*/out/onnxruntime_*/onnxruntime-*/lib/*.{dll,so.*,so,dylib} "artifact/${{ env.ASSET_NAME }}" || true + cp -v -n target/${{ matrix.target }}/release/{,lib}onnxruntime*.{dll,so.*,so,dylib} "artifact/${{ env.ASSET_NAME }}" || true # libonnxruntimeについてはバージョン付のshared libraryを使用するためバージョンがついてないものを削除する rm -f artifact/${{ env.ASSET_NAME }}/libonnxruntime.{so,dylib} cp -v README.md "artifact/${{ env.ASSET_NAME }}/README.txt" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0496eebdb..d0639d045 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -72,8 +72,8 @@ jobs: with: python-version: "3.8" - uses: Swatinem/rust-cache@v2 - - run: cargo clippy -vv --all-features --features onnxruntime/disable-sys-build-script --tests -- -D clippy::all -D warnings --no-deps - - run: cargo clippy -vv --all-features --features onnxruntime/disable-sys-build-script -- -D clippy::all -D warnings --no-deps + - run: cargo clippy -vv --all-features --tests -- -D clippy::all -D warnings --no-deps + - run: cargo clippy -vv --all-features -- -D clippy::all -D warnings --no-deps - run: cargo fmt -- --check rust-unit-test: @@ -199,8 +199,8 @@ jobs: mkdir -p example/cpp/unix/voicevox_core/ cp -v crates/voicevox_core_c_api/include/voicevox_core.h example/cpp/unix/voicevox_core/ cp -v target/debug/libvoicevox_core.{so,dylib} example/cpp/unix/voicevox_core/ || true - cp -v target/debug/build/onnxruntime-sys-*/out/onnxruntime_*/onnxruntime-*/lib/libonnxruntime.so.* example/cpp/unix/voicevox_core/ || true - cp -v target/debug/build/onnxruntime-sys-*/out/onnxruntime_*/onnxruntime-*/lib/libonnxruntime.*.dylib example/cpp/unix/voicevox_core/ || true + cp -v target/debug/libonnxruntime.so.* example/cpp/unix/voicevox_core/ || true + cp -v target/debug/libonnxruntime.*.dylib example/cpp/unix/voicevox_core/ || true - if: startsWith(matrix.os, 'mac') uses: jwlawson/actions-setup-cmake@v1.13 @@ -281,9 +281,9 @@ jobs: - run: poetry run maturin develop --locked - name: 必要なDLLをコピーしてpytestを実行 run: | - cp -v ../../target/debug/build/onnxruntime-sys-*/out/onnxruntime_*/onnxruntime-*/lib/onnxruntime.dll . || true - cp -v ../../target/debug/build/onnxruntime-sys-*/out/onnxruntime_*/onnxruntime-*/lib/libonnxruntime.so.* . || true - cp -v ../../target/debug/build/onnxruntime-sys-*/out/onnxruntime_*/onnxruntime-*/lib/libonnxruntime.*.dylib . || true + cp -v ../../target/debug/onnxruntime.dll . || true + cp -v ../../target/debug/libonnxruntime.so.* . || true + cp -v ../../target/debug/libonnxruntime.*.dylib . || true poetry run pytest - name: Exampleを実行 diff --git a/Cargo.lock b/Cargo.lock index 4015b7e36..0f9a78469 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,12 +426,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "chunked_transfer" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" - [[package]] name = "cipher" version = "0.3.0" @@ -684,6 +678,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -1278,6 +1278,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1410,7 +1420,7 @@ checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ "http", "hyper", - "rustls", + "rustls 0.20.6", "tokio", "tokio-rustls", ] @@ -1993,30 +2003,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" -[[package]] -name = "onnxruntime" -version = "0.1.0" -source = "git+https://github.com/VOICEVOX/onnxruntime-rs.git?rev=ebb9dcb9b26ee681889b52b6db3b4f642b04a250#ebb9dcb9b26ee681889b52b6db3b4f642b04a250" -dependencies = [ - "lazy_static", - "ndarray", - "onnxruntime-sys", - "thiserror", - "tracing", -] - -[[package]] -name = "onnxruntime-sys" -version = "0.0.25" -source = "git+https://github.com/VOICEVOX/onnxruntime-rs.git?rev=ebb9dcb9b26ee681889b52b6db3b4f642b04a250#ebb9dcb9b26ee681889b52b6db3b4f642b04a250" -dependencies = [ - "flate2", - "once_cell", - "tar", - "ureq", - "zip", -] - [[package]] name = "opaque-debug" version = "0.3.0" @@ -2598,7 +2584,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", + "rustls 0.20.6", "rustls-pemfile", "serde", "serde_json", @@ -2611,7 +2597,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.22.5", "winreg", ] @@ -2728,6 +2714,18 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustls" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + [[package]] name = "rustls-pemfile" version = "1.0.2" @@ -2737,6 +2735,16 @@ dependencies = [ "base64 0.21.0", ] +[[package]] +name = "rustls-webpki" +version = "0.101.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.11" @@ -3262,7 +3270,7 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls", + "rustls 0.20.6", "tokio", "webpki", ] @@ -3480,19 +3488,17 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "ureq" -version = "2.5.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f" +checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3" dependencies = [ - "base64 0.13.0", - "chunked_transfer", - "flate2", + "base64 0.21.0", "log", "once_cell", - "rustls", + "rustls 0.21.7", + "rustls-webpki", "url", - "webpki", - "webpki-roots", + "webpki-roots 0.25.4", ] [[package]] @@ -3535,6 +3541,31 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "voicevox-ort" +version = "2.0.0-rc.2" +source = "git+https://github.com/VOICEVOX/ort.git?rev=a2d6ae22327869e896bf4c16828734d09516d2d9#a2d6ae22327869e896bf4c16828734d09516d2d9" +dependencies = [ + "half", + "js-sys", + "ndarray", + "thiserror", + "tracing", + "voicevox-ort-sys", + "web-sys", +] + +[[package]] +name = "voicevox-ort-sys" +version = "2.0.0-rc.2" +source = "git+https://github.com/VOICEVOX/ort.git?rev=a2d6ae22327869e896bf4c16828734d09516d2d9#a2d6ae22327869e896bf4c16828734d09516d2d9" +dependencies = [ + "flate2", + "sha2", + "tar", + "ureq", +] + [[package]] name = "voicevox_core" version = "0.0.0" @@ -3559,7 +3590,6 @@ dependencies = [ "nanoid", "ndarray", "once_cell", - "onnxruntime", "open_jtalk", "ouroboros", "pretty_assertions", @@ -3578,6 +3608,7 @@ dependencies = [ "tokio", "tracing", "uuid", + "voicevox-ort", "voicevox_core_macros", "windows", "zip", @@ -3797,6 +3828,12 @@ dependencies = [ "webpki", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "which" version = "4.3.0" diff --git a/Cargo.toml b/Cargo.toml index 69d924a16..7102127d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,9 +86,9 @@ voicevox_core = { path = "crates/voicevox_core" } windows = "0.43.0" zip = "0.6.3" -[workspace.dependencies.onnxruntime] -git = "https://github.com/VOICEVOX/onnxruntime-rs.git" -rev = "ebb9dcb9b26ee681889b52b6db3b4f642b04a250" +[workspace.dependencies.voicevox-ort] +git = "https://github.com/VOICEVOX/ort.git" +rev = "a2d6ae22327869e896bf4c16828734d09516d2d9" [workspace.dependencies.open_jtalk] git = "https://github.com/VOICEVOX/open_jtalk-rs.git" diff --git a/crates/voicevox_core/Cargo.toml b/crates/voicevox_core/Cargo.toml index 5c8dd440d..731862356 100644 --- a/crates/voicevox_core/Cargo.toml +++ b/crates/voicevox_core/Cargo.toml @@ -6,7 +6,8 @@ publish.workspace = true [features] default = [] -directml = ["onnxruntime/directml"] +cuda = ["voicevox-ort/cuda"] +directml = ["voicevox-ort/directml"] [dependencies] anyhow.workspace = true @@ -27,7 +28,6 @@ jlabel.workspace = true nanoid.workspace = true ndarray.workspace = true once_cell.workspace = true -onnxruntime.workspace = true open_jtalk.workspace = true ouroboros.workspace = true rayon.workspace = true @@ -43,6 +43,7 @@ tokio = { workspace = true, features = ["rt"] } # FIXME: feature-gateする tracing.workspace = true uuid = { workspace = true, features = ["v4", "serde"] } voicevox_core_macros = { path = "../voicevox_core_macros" } +voicevox-ort = { workspace = true, features = ["ndarray", "download-binaries"] } zip.workspace = true [dev-dependencies] diff --git a/crates/voicevox_core/src/infer.rs b/crates/voicevox_core/src/infer.rs index fc8954e7d..c2cad1d7d 100644 --- a/crates/voicevox_core/src/infer.rs +++ b/crates/voicevox_core/src/infer.rs @@ -79,16 +79,20 @@ pub(crate) trait InferenceSignature: Sized + Send + 'static { pub(crate) trait InferenceInputSignature: Send + 'static { type Signature: InferenceSignature; const PARAM_INFOS: &'static [ParamInfo]; - fn make_run_context(self, sess: &mut R::Session) -> R::RunContext<'_>; + fn make_run_context( + self, + sess: &mut R::Session, + ) -> anyhow::Result>; } pub(crate) trait InputScalar: Sized { const KIND: InputScalarKind; + // TODO: `Array`ではなく`ArrayView`を取ることができるかもしれない fn push_tensor_to_ctx( tensor: Array, visitor: &mut impl PushInputTensor, - ); + ) -> anyhow::Result<()>; } #[duplicate_item( @@ -102,8 +106,8 @@ impl InputScalar for T { fn push_tensor_to_ctx( tensor: Array, ctx: &mut impl PushInputTensor, - ) { - ctx.push(tensor); + ) -> anyhow::Result<()> { + ctx.push(tensor) } } @@ -117,8 +121,8 @@ pub(crate) enum InputScalarKind { } pub(crate) trait PushInputTensor { - fn push_int64(&mut self, tensor: Array); - fn push_float32(&mut self, tensor: Array); + fn push_int64(&mut self, tensor: Array) -> anyhow::Result<()>; + fn push_float32(&mut self, tensor: Array) -> anyhow::Result<()>; } /// 推論操作の出力シグネチャ。 diff --git a/crates/voicevox_core/src/infer/runtimes/onnxruntime.rs b/crates/voicevox_core/src/infer/runtimes/onnxruntime.rs index 0556b6d51..f8f376837 100644 --- a/crates/voicevox_core/src/infer/runtimes/onnxruntime.rs +++ b/crates/voicevox_core/src/infer/runtimes/onnxruntime.rs @@ -1,18 +1,15 @@ use std::{fmt::Debug, vec}; -use anyhow::anyhow; +use anyhow::{anyhow, bail, ensure}; use duplicate::duplicate_item; use ndarray::{Array, Dimension}; -use once_cell::sync::Lazy; -use onnxruntime::{ - environment::Environment, GraphOptimizationLevel, LoggingLevel, TensorElementDataType, - TypeToTensorElementDataType, +use ort::{ + CPUExecutionProvider, CUDAExecutionProvider, DirectMLExecutionProvider, ExecutionProvider as _, + GraphOptimizationLevel, IntoTensorElementType, TensorElementType, ValueType, }; use crate::{devices::SupportedDevices, error::ErrorRepr}; -use self::assert_send::AssertSend; - use super::super::{ DecryptModelError, InferenceRuntime, InferenceSessionOptions, InputScalarKind, OutputScalarKind, OutputTensor, ParamInfo, PushInputTensor, @@ -22,29 +19,28 @@ use super::super::{ pub(crate) enum Onnxruntime {} impl InferenceRuntime for Onnxruntime { - type Session = AssertSend>; + type Session = ort::Session; type RunContext<'a> = OnnxruntimeRunContext<'a>; fn supported_devices() -> crate::Result { - let mut cuda_support = false; - let mut dml_support = false; - for provider in onnxruntime::session::get_available_providers() - .map_err(Into::into) - .map_err(ErrorRepr::GetSupportedDevices)? - .iter() - { - match provider.as_str() { - "CUDAExecutionProvider" => cuda_support = true, - "DmlExecutionProvider" => dml_support = true, - _ => {} - } - } + // TODO: `InferenceRuntime::init`と`InitInferenceRuntimeError`を作る + build_ort_env_once().unwrap(); + + (|| { + let cpu = CPUExecutionProvider::default().is_available()?; + let cuda = CUDAExecutionProvider::default().is_available()?; + let dml = DirectMLExecutionProvider::default().is_available()?; - Ok(SupportedDevices { - cpu: true, - cuda: cuda_support, - dml: dml_support, - }) + ensure!(cpu, "missing `CPUExecutionProvider`"); + + Ok(SupportedDevices { + cpu: true, + cuda, + dml, + }) + })() + .map_err(ErrorRepr::GetSupportedDevices) + .map_err(Into::into) } fn new_session( @@ -55,48 +51,52 @@ impl InferenceRuntime for Onnxruntime { Vec>, Vec>, )> { - let mut builder = ENVIRONMENT - .new_session_builder()? - .with_optimization_level(GraphOptimizationLevel::Basic)? - .with_intra_op_num_threads(options.cpu_num_threads.into())? - .with_inter_op_num_threads(options.cpu_num_threads.into())?; - - if options.use_gpu { - #[cfg(feature = "directml")] - { - use onnxruntime::ExecutionMode; - - builder = builder - .with_disable_mem_pattern()? - .with_execution_mode(ExecutionMode::ORT_SEQUENTIAL)? - .with_append_execution_provider_directml(0)?; - } - - #[cfg(not(feature = "directml"))] - { - builder = builder.with_append_execution_provider_cuda(Default::default())?; - } + // TODO: `InferenceRuntime::init`と`InitInferenceRuntimeError`を作る + build_ort_env_once().unwrap(); + + let mut builder = ort::Session::builder()? + .with_optimization_level(GraphOptimizationLevel::Level1)? + .with_intra_threads(options.cpu_num_threads.into())?; + + if options.use_gpu && cfg!(feature = "directml") { + builder = builder + .with_parallel_execution(false)? + .with_memory_pattern(false)?; + DirectMLExecutionProvider::default().register(&builder)?; + } else if options.use_gpu && cfg!(feature = "cuda") { + CUDAExecutionProvider::default().register(&builder)?; } let model = model()?; - let sess = AssertSend::from(builder.with_model_from_memory(model)?); + let sess = builder.commit_from_memory(&{ model })?; let input_param_infos = sess .inputs .iter() .map(|info| { - let dt = match info.input_type { - TensorElementDataType::Float => Ok(InputScalarKind::Float32), - TensorElementDataType::Uint8 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8"), - TensorElementDataType::Int8 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8"), - TensorElementDataType::Uint16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16"), - TensorElementDataType::Int16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16"), - TensorElementDataType::Int32 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32"), - TensorElementDataType::Int64 => Ok(InputScalarKind::Int64), - TensorElementDataType::String => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING"), - TensorElementDataType::Double => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE"), - TensorElementDataType::Uint32 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32"), - TensorElementDataType::Uint64 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64"), + let ValueType::Tensor { ty, .. } = info.input_type else { + bail!( + "unexpected input value type for `{}`. currently `ONNX_TYPE_TENSOR` and \ + `ONNX_TYPE_SPARSETENSOR` is supported", + info.name, + ); + }; + + let dt = match ty { + TensorElementType::Float32 => Ok(InputScalarKind::Float32), + TensorElementType::Uint8 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8"), + TensorElementType::Int8 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8"), + TensorElementType::Uint16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16"), + TensorElementType::Int16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16"), + TensorElementType::Int32 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32"), + TensorElementType::Int64 => Ok(InputScalarKind::Int64), + TensorElementType::String => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING"), + TensorElementType::Bfloat16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16"), + TensorElementType::Float16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16"), + TensorElementType::Float64 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE"), + TensorElementType::Uint32 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32"), + TensorElementType::Uint64 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64"), + TensorElementType::Bool => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL"), } .map_err(|actual| { anyhow!("unsupported input datatype `{actual}` for `{}`", info.name) @@ -105,7 +105,7 @@ impl InferenceRuntime for Onnxruntime { Ok(ParamInfo { name: info.name.clone().into(), dt, - ndim: Some(info.dimensions.len()), + ndim: info.input_type.tensor_dimensions().map(Vec::len), }) }) .collect::>()?; @@ -114,18 +114,29 @@ impl InferenceRuntime for Onnxruntime { .outputs .iter() .map(|info| { - let dt = match info.output_type { - TensorElementDataType::Float => Ok(OutputScalarKind::Float32), - TensorElementDataType::Uint8 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8"), - TensorElementDataType::Int8 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8"), - TensorElementDataType::Uint16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16"), - TensorElementDataType::Int16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16"), - TensorElementDataType::Int32 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32"), - TensorElementDataType::Int64 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64"), - TensorElementDataType::String => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING"), - TensorElementDataType::Double => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE"), - TensorElementDataType::Uint32 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32"), - TensorElementDataType::Uint64 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64"), + let ValueType::Tensor { ty, .. } = info.output_type else { + bail!( + "unexpected output value type for `{}`. currently `ONNX_TYPE_TENSOR` and \ + `ONNX_TYPE_SPARSETENSOR` is supported", + info.name, + ); + }; + + let dt = match ty { + TensorElementType::Float32 => Ok(OutputScalarKind::Float32), + TensorElementType::Uint8 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8"), + TensorElementType::Int8 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8"), + TensorElementType::Uint16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16"), + TensorElementType::Int16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16"), + TensorElementType::Int32 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32"), + TensorElementType::Int64 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64"), + TensorElementType::String => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING"), + TensorElementType::Bfloat16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16"), + TensorElementType::Float16 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16"), + TensorElementType::Float64 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE"), + TensorElementType::Uint32 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32"), + TensorElementType::Uint64 => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64"), + TensorElementType::Bool => Err("ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL"), } .map_err(|actual| { anyhow!("unsupported output datatype `{actual}` for `{}`", info.name) @@ -134,73 +145,69 @@ impl InferenceRuntime for Onnxruntime { Ok(ParamInfo { name: info.name.clone().into(), dt, - ndim: Some(info.dimensions.len()), + ndim: info.output_type.tensor_dimensions().map(|d| d.len()), }) }) .collect::>()?; - return Ok((sess, input_param_infos, output_param_infos)); - - static ENVIRONMENT: Lazy = Lazy::new(|| { - Environment::builder() - .with_name(env!("CARGO_PKG_NAME")) - .with_log_level(LOGGING_LEVEL) - .build() - .unwrap() - }); - - const LOGGING_LEVEL: LoggingLevel = if cfg!(debug_assertions) { - LoggingLevel::Verbose - } else { - LoggingLevel::Warning - }; + Ok((sess, input_param_infos, output_param_infos)) } fn run( - OnnxruntimeRunContext { sess, mut inputs }: OnnxruntimeRunContext<'_>, + OnnxruntimeRunContext { sess, inputs }: OnnxruntimeRunContext<'_>, ) -> anyhow::Result> { - // FIXME: 現状では`f32`のみ対応。実行時にsessionからdatatypeが取れるので、別の型の対応も - // おそらく可能ではあるが、それが必要になるよりもortクレートへの引越しが先になると思われる - // のでこのままにする。 - - if !sess - .outputs - .iter() - .all(|info| matches!(info.output_type, TensorElementDataType::Float)) - { - unimplemented!( - "currently only `ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT` is supported for output", - ); - } - - let outputs = sess.run::(inputs.iter_mut().map(|t| &mut **t as &mut _).collect())?; - - Ok(outputs - .iter() - .map(|o| OutputTensor::Float32((*o).clone().into_owned())) - .collect()) + let outputs = sess.run(&*inputs)?; + + (0..outputs.len()) + .map(|i| { + let output = &outputs[i]; + + let ValueType::Tensor { ty, .. } = output.dtype()? else { + bail!( + "unexpected output. currently `ONNX_TYPE_TENSOR` and \ + `ONNX_TYPE_SPARSETENSOR` is supported", + ); + }; + + match ty { + TensorElementType::Float32 => { + let output = output.try_extract_tensor::()?; + Ok(OutputTensor::Float32(output.into_owned())) + } + _ => bail!("unexpected output tensor element data type"), + } + }) + .collect() } } +fn build_ort_env_once() -> ort::Result<()> { + static ONCE: once_cell::sync::OnceCell<()> = once_cell::sync::OnceCell::new(); + ONCE.get_or_try_init(|| ort::init().with_name(env!("CARGO_PKG_NAME")).commit())?; + Ok(()) +} + pub(crate) struct OnnxruntimeRunContext<'sess> { - sess: &'sess mut AssertSend>, - inputs: Vec>, + sess: &'sess ort::Session, + inputs: Vec>, } impl OnnxruntimeRunContext<'_> { fn push_input( &mut self, - input: Array, - ) { - self.inputs - .push(Box::new(onnxruntime::session::NdArray::new(input))); + input: Array< + impl IntoTensorElementType + Debug + Clone + 'static, + impl Dimension + 'static, + >, + ) -> anyhow::Result<()> { + let input = ort::Value::from_array(input)?.into(); + self.inputs.push(input); + Ok(()) } } -impl<'sess> From<&'sess mut AssertSend>> - for OnnxruntimeRunContext<'sess> -{ - fn from(sess: &'sess mut AssertSend>) -> Self { +impl<'sess> From<&'sess mut ort::Session> for OnnxruntimeRunContext<'sess> { + fn from(sess: &'sess mut ort::Session) -> Self { Self { sess, inputs: vec![], @@ -214,40 +221,7 @@ impl PushInputTensor for OnnxruntimeRunContext<'_> { [ push_int64 ] [ i64 ]; [ push_float32 ] [ f32 ]; )] - fn method(&mut self, tensor: Array) { - self.push_input(tensor); - } -} - -// FIXME: 以下のことをちゃんと確認した後、onnxruntime-rs側で`Session`が`Send`であると宣言する。 -// https://github.com/VOICEVOX/voicevox_core/issues/307#issuecomment-1276184614 -mod assert_send { - use std::ops::{Deref, DerefMut}; - - pub(crate) struct AssertSend(T); - - impl From> - for AssertSend> - { - fn from(session: onnxruntime::session::Session<'static>) -> Self { - Self(session) - } + fn method(&mut self, tensor: Array) -> anyhow::Result<()> { + self.push_input(tensor) } - - impl Deref for AssertSend { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl DerefMut for AssertSend { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - - // SAFETY: `Session` is probably "send"able. - unsafe impl Send for AssertSend {} } diff --git a/crates/voicevox_core/src/infer/session_set.rs b/crates/voicevox_core/src/infer/session_set.rs index 56d570f98..cdd179680 100644 --- a/crates/voicevox_core/src/infer/session_set.rs +++ b/crates/voicevox_core/src/infer/session_set.rs @@ -94,9 +94,8 @@ impl InferenceSessionCell input: I, ) -> crate::Result<::Output> { let inner = &mut self.inner.lock().unwrap(); - let ctx = input.make_run_context::(inner); - R::run(ctx) - .and_then(TryInto::try_into) - .map_err(|e| ErrorRepr::InferenceFailed(e).into()) + (|| R::run(input.make_run_context::(inner)?)?.try_into())() + .map_err(ErrorRepr::InferenceFailed) + .map_err(Into::into) } } diff --git a/crates/voicevox_core_c_api/Cargo.toml b/crates/voicevox_core_c_api/Cargo.toml index a6314f105..ad3a65fa7 100644 --- a/crates/voicevox_core_c_api/Cargo.toml +++ b/crates/voicevox_core_c_api/Cargo.toml @@ -13,6 +13,7 @@ harness = false name = "e2e" [features] +cuda = ["voicevox_core/cuda"] directml = ["voicevox_core/directml"] [dependencies] diff --git a/crates/voicevox_core_c_api/src/lib.rs b/crates/voicevox_core_c_api/src/lib.rs index a5da9b6d3..fbb0bf6bf 100644 --- a/crates/voicevox_core_c_api/src/lib.rs +++ b/crates/voicevox_core_c_api/src/lib.rs @@ -59,7 +59,13 @@ fn init_logger_once() { .with_env_filter(if env::var_os(EnvFilter::DEFAULT_ENV).is_some() { EnvFilter::from_default_env() } else { - "error,voicevox_core=info,voicevox_core_c_api=info,onnxruntime=info".into() + pub const ORT_LOGGING_LEVEL: &str = if cfg!(debug_assertions) { + "info" + } else { + "warn" + }; + format!("error,voicevox_core=info,voicevox_core_c_api=info,ort={ORT_LOGGING_LEVEL}") + .into() }) .with_timer(local_time as fn(&mut Writer<'_>) -> _) .with_ansi(ansi) diff --git a/crates/voicevox_core_c_api/tests/e2e/assert_cdylib.rs b/crates/voicevox_core_c_api/tests/e2e/assert_cdylib.rs index 1e4958eda..cfbec5c31 100644 --- a/crates/voicevox_core_c_api/tests/e2e/assert_cdylib.rs +++ b/crates/voicevox_core_c_api/tests/e2e/assert_cdylib.rs @@ -46,11 +46,7 @@ pub(crate) fn exec() -> anyhow::Result<()> { // テスト対象が無いときに`cargo build`をスキップしたいが、判定部分がプライベート。 // そのためスキップするのはCLIオプションに`--ignored`か`--include-ignored`が無いときのみ if args.ignored || args.include_ignored { - let mut cmd = cmd!(env!("CARGO"), "build", "--release", "--lib"); - for (k, v) in C::BUILD_ENVS { - cmd = cmd.env(k, v); - } - cmd.run()?; + cmd!(env!("CARGO"), "build", "--release", "--lib").run()?; ensure!( C::cdylib_path().exists(), @@ -102,7 +98,6 @@ pub(crate) fn exec() -> anyhow::Result<()> { pub(crate) trait TestContext { const TARGET_DIR: &'static str; const CDYLIB_NAME: &'static str; - const BUILD_ENVS: &'static [(&'static str, &'static str)]; const RUNTIME_ENVS: &'static [(&'static str, &'static str)]; } diff --git a/crates/voicevox_core_c_api/tests/e2e/main.rs b/crates/voicevox_core_c_api/tests/e2e/main.rs index 91f5e06e9..43dc3a95e 100644 --- a/crates/voicevox_core_c_api/tests/e2e/main.rs +++ b/crates/voicevox_core_c_api/tests/e2e/main.rs @@ -24,16 +24,6 @@ fn main() -> anyhow::Result<()> { impl assert_cdylib::TestContext for TestContext { const TARGET_DIR: &'static str = "../../target"; const CDYLIB_NAME: &'static str = "voicevox_core"; - const BUILD_ENVS: &'static [(&'static str, &'static str)] = &[ - // 他の単体テストが動いているときにonnxruntime-sysの初回ビルドを行うと、Windows環境だと - // `$ORT_OUT_DIR`のハックが問題を起こす。そのためこのハック自体を無効化する - // - // featuresの差分を出さないように`cargo build`することができればonnxruntime-sysの - // ビルド自体がされないのだが、このバイナリから`cargo build`の状況を知るのは無理に近い - ("ORT_OUT_DIR", ""), - // DirectMLとCUDAは無効化 - ("ORT_USE_CUDA", "0"), - ]; const RUNTIME_ENVS: &'static [(&'static str, &'static str)] = &[("VV_MODELS_ROOT_DIR", VV_MODELS_ROOT_DIR)]; } diff --git a/crates/voicevox_core_java_api/Cargo.toml b/crates/voicevox_core_java_api/Cargo.toml index 887813685..06b2af618 100644 --- a/crates/voicevox_core_java_api/Cargo.toml +++ b/crates/voicevox_core_java_api/Cargo.toml @@ -8,6 +8,7 @@ publish.workspace = true crate-type = ["cdylib"] [features] +cuda = ["voicevox_core/cuda"] directml = ["voicevox_core/directml"] [dependencies] diff --git a/crates/voicevox_core_java_api/settings.gradle b/crates/voicevox_core_java_api/settings.gradle index 20a5e2c6a..75f5810ac 100644 --- a/crates/voicevox_core_java_api/settings.gradle +++ b/crates/voicevox_core_java_api/settings.gradle @@ -40,5 +40,5 @@ gradle.ext { gsonVersion = '2.10.1' jakartaValidationVersion = '3.0.2' jakartaAnnotationVersion = '2.1.1' - onnxruntimeVersion = '1.14.0' + onnxruntimeVersion = '1.17.3' } diff --git a/crates/voicevox_core_java_api/src/logger.rs b/crates/voicevox_core_java_api/src/logger.rs index 4800452ca..30545725e 100644 --- a/crates/voicevox_core_java_api/src/logger.rs +++ b/crates/voicevox_core_java_api/src/logger.rs @@ -10,10 +10,11 @@ extern "system" fn Java_jp_hiroshiba_voicevoxcore_Dll_00024LoggerInitializer_ini android_logger::Config::default() .with_tag("VoicevoxCore") .with_filter( - android_logger::FilterBuilder::new() - .parse("error,voicevox_core=info,voicevox_core_java_api=info,onnxruntime=error") - .build(), - ), + android_logger::FilterBuilder::new() + // FIXME: ortも`warn`は出すべき + .parse("error,voicevox_core=info,voicevox_core_java_api=info,ort=error") + .build(), + ), ); } else { // TODO: Android以外でのログ出力を良い感じにする。(System.Loggerを使う?) @@ -29,7 +30,8 @@ extern "system" fn Java_jp_hiroshiba_voicevoxcore_Dll_00024LoggerInitializer_ini .with_env_filter(if env::var_os(EnvFilter::DEFAULT_ENV).is_some() { EnvFilter::from_default_env() } else { - "error,voicevox_core=info,voicevox_core_c_api=info,onnxruntime=error".into() + // FIXME: `c_api`じゃないし、ortも`warn`は出すべき + "error,voicevox_core=info,voicevox_core_c_api=info,ort=error".into() }) .with_timer(local_time as fn(&mut Writer<'_>) -> _) .with_ansi(out().is_terminal() && env_allows_ansi()) diff --git a/crates/voicevox_core_macros/src/inference_domain.rs b/crates/voicevox_core_macros/src/inference_domain.rs index 72bc4d18a..d24a20ab1 100644 --- a/crates/voicevox_core_macros/src/inference_domain.rs +++ b/crates/voicevox_core_macros/src/inference_domain.rs @@ -223,22 +223,28 @@ pub(crate) fn derive_inference_input_signature( fn make_run_context( self, sess: &mut R::Session, - ) -> R::RunContext<'_> { + ) -> ::anyhow::Result> { let mut ctx = as ::std::convert::From<_>>::from(sess); #( - __ArrayExt::push_to_ctx(self.#field_names, &mut ctx); + __ArrayExt::push_to_ctx(self.#field_names, &mut ctx)?; )* - return ctx; + return ::std::result::Result::Ok(ctx); trait __ArrayExt { - fn push_to_ctx(self, ctx: &mut impl crate::infer::PushInputTensor); + fn push_to_ctx( + self, + ctx: &mut impl crate::infer::PushInputTensor, + ) -> ::anyhow::Result<()>; } impl __ArrayExt for ::ndarray::Array { - fn push_to_ctx(self, ctx: &mut impl crate::infer::PushInputTensor) { - A::push_tensor_to_ctx(self, ctx); + fn push_to_ctx( + self, + ctx: &mut impl crate::infer::PushInputTensor, + ) -> ::anyhow::Result<()> { + A::push_tensor_to_ctx(self, ctx) } } } diff --git a/crates/voicevox_core_python_api/Cargo.toml b/crates/voicevox_core_python_api/Cargo.toml index be3ecbf27..5ccd1dc41 100644 --- a/crates/voicevox_core_python_api/Cargo.toml +++ b/crates/voicevox_core_python_api/Cargo.toml @@ -8,6 +8,7 @@ publish.workspace = true crate-type = ["cdylib"] [features] +cuda = ["voicevox_core/cuda"] directml = ["voicevox_core/directml"] [dependencies] From 9c3a94e663263595e9e9df0c0b763620c0e1615c Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Fri, 24 May 2024 08:00:24 +0900 Subject: [PATCH 02/36] =?UTF-8?q?change:=20`style=5Fid=5Fto=5Fmodel=5Finne?= =?UTF-8?q?r=5Fid`=20=E2=86=92=20`style=5Fid=5Fto=5Finner=5Fvoice=5Fid`=20?= =?UTF-8?q?(#795)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/voicevox_core/src/manifest.rs | 16 ++++---- crates/voicevox_core/src/status.rs | 50 ++++++++++++------------- crates/voicevox_core/src/synthesizer.rs | 12 +++--- crates/voicevox_core/src/voice_model.rs | 24 ++++++------ model/sample.vvm/manifest.json | 2 +- 5 files changed, 52 insertions(+), 52 deletions(-) diff --git a/crates/voicevox_core/src/manifest.rs b/crates/voicevox_core/src/manifest.rs index 3b17ae3f1..a22b66e8b 100644 --- a/crates/voicevox_core/src/manifest.rs +++ b/crates/voicevox_core/src/manifest.rs @@ -19,18 +19,18 @@ impl Display for ManifestVersion { } /// モデル内IDの実体 -pub type RawModelInnerId = u32; +pub type RawInnerVoiceId = u32; /// モデル内ID #[derive(PartialEq, Eq, Clone, Copy, Ord, PartialOrd, Deserialize, Serialize, new, Debug)] -pub struct ModelInnerId(RawModelInnerId); +pub struct InnerVoiceId(RawInnerVoiceId); -impl ModelInnerId { - pub fn raw_id(self) -> RawModelInnerId { +impl InnerVoiceId { + pub fn raw_id(self) -> RawInnerVoiceId { self.0 } } -impl Display for ModelInnerId { +impl Display for InnerVoiceId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.raw_id()) } @@ -58,12 +58,12 @@ pub(crate) struct TalkManifest { pub(crate) predict_intonation_filename: String, pub(crate) decode_filename: String, #[serde(default)] - pub(crate) style_id_to_model_inner_id: StyleIdToModelInnerId, + pub(crate) style_id_to_inner_voice_id: StyleIdToInnerVoiceId, } #[serde_as] #[derive(Default, Clone, Deref, Deserialize)] #[deref(forward)] -pub(crate) struct StyleIdToModelInnerId( - #[serde_as(as = "Arc>")] Arc>, +pub(crate) struct StyleIdToInnerVoiceId( + #[serde_as(as = "Arc>")] Arc>, ); diff --git a/crates/voicevox_core/src/status.rs b/crates/voicevox_core/src/status.rs index a47de689b..6980ab7fc 100644 --- a/crates/voicevox_core/src/status.rs +++ b/crates/voicevox_core/src/status.rs @@ -14,9 +14,9 @@ use crate::{ InferenceDomain, InferenceInputSignature, InferenceRuntime, InferenceSessionOptions, InferenceSignature, }, - manifest::{ModelInnerId, StyleIdToModelInnerId}, + manifest::{InnerVoiceId, StyleIdToInnerVoiceId}, metas::{self, SpeakerMeta, StyleId, StyleMeta, VoiceModelMeta}, - voice_model::{ModelBytesWithInnerIdsByDomain, VoiceModelHeader, VoiceModelId}, + voice_model::{ModelBytesWithInnerVoiceIdsByDomain, VoiceModelHeader, VoiceModelId}, Result, }; @@ -36,7 +36,7 @@ impl Status { pub(crate) fn insert_model( &self, model_header: &VoiceModelHeader, - model_contents: &InferenceDomainMap, + model_contents: &InferenceDomainMap, ) -> Result<()> { self.loaded_models .lock() @@ -66,14 +66,14 @@ impl Status { self.loaded_models.lock().unwrap().metas() } - /// あるスタイルに対応する`VoiceModelId`と`ModelInnerId`の組を返す。 + /// あるスタイルに対応する`VoiceModelId`と`InnerVoiceId`の組を返す。 /// - /// `StyleId` → `ModelInnerId`のマッピングが存在しない場合は、`ModelInnerId`としては + /// `StyleId` → `InnerVoiceId`のマッピングが存在しない場合は、`InnerVoiceId`としては /// `style_id`と同じ値を返す。 pub(crate) fn ids_for( &self, style_id: StyleId, - ) -> Result<(VoiceModelId, ModelInnerId)> { + ) -> Result<(VoiceModelId, InnerVoiceId)> { self.loaded_models.lock().unwrap().ids_for::(style_id) } @@ -122,7 +122,7 @@ struct LoadedModels(IndexMap>) struct LoadedModel { metas: VoiceModelMeta, - session_sets_with_inner_ids: InferenceDomainMap>, + session_sets_with_inner_ids: InferenceDomainMap>, } impl LoadedModels { @@ -133,7 +133,7 @@ impl LoadedModels { fn ids_for( &self, style_id: StyleId, - ) -> Result<(VoiceModelId, ModelInnerId)> { + ) -> Result<(VoiceModelId, InnerVoiceId)> { let ( model_id, LoadedModel { @@ -153,13 +153,13 @@ impl LoadedModels { style_types: D::style_types(), })?; - let model_inner_id = session_sets_with_inner_ids + let inner_voice_id = session_sets_with_inner_ids .get::() .as_ref() - .and_then(|(model_inner_ids, _)| model_inner_ids.get(&style_id).copied()) - .unwrap_or_else(|| ModelInnerId::new(style_id.raw_id())); + .and_then(|(inner_voice_ids, _)| inner_voice_ids.get(&style_id).copied()) + .unwrap_or_else(|| InnerVoiceId::new(style_id.raw_id())); - Ok((model_id.clone(), model_inner_id)) + Ok((model_id.clone(), inner_voice_id)) } /// # Panics @@ -250,7 +250,7 @@ impl LoadedModels { fn insert( &mut self, model_header: &VoiceModelHeader, - session_sets_with_inner_ids: InferenceDomainMap>, + session_sets_with_inner_ids: InferenceDomainMap>, ) -> Result<()> { self.ensure_acceptable(model_header)?; @@ -286,8 +286,8 @@ impl LoadedModels { pub(crate) trait InferenceDomainExt: InferenceDomain { fn visit( - map: &InferenceDomainMap>, - ) -> Option<&(StyleIdToModelInnerId, InferenceSessionSet)>; + map: &InferenceDomainMap>, + ) -> Option<&(StyleIdToInnerVoiceId, InferenceSessionSet)>; } #[duplicate_item( @@ -296,25 +296,25 @@ pub(crate) trait InferenceDomainExt: InferenceDomain { )] impl InferenceDomainExt for T { fn visit( - map: &InferenceDomainMap>, - ) -> Option<&(StyleIdToModelInnerId, InferenceSessionSet)> { + map: &InferenceDomainMap>, + ) -> Option<&(StyleIdToInnerVoiceId, InferenceSessionSet)> { map.field.as_ref() } } -impl InferenceDomainMap> { +impl InferenceDomainMap> { fn get( &self, - ) -> Option<&(StyleIdToModelInnerId, InferenceSessionSet)> { + ) -> Option<&(StyleIdToInnerVoiceId, InferenceSessionSet)> { D::visit(self) } } -impl InferenceDomainMap { +impl InferenceDomainMap { fn create_session_sets( &self, session_options: &InferenceDomainMap, - ) -> anyhow::Result>> { + ) -> anyhow::Result>> { duplicate! { [ field; @@ -323,9 +323,9 @@ impl InferenceDomainMap { let field = self .field .as_ref() - .map(|(model_inner_ids, model_bytes)| { + .map(|(inner_voice_ids, model_bytes)| { let session_set = InferenceSessionSet::new(model_bytes, &session_options.field)?; - Ok::<_, anyhow::Error>((model_inner_ids.clone(), session_set)) + Ok::<_, anyhow::Error>((inner_voice_ids.clone(), session_set)) }) .transpose()?; } @@ -336,8 +336,8 @@ impl InferenceDomainMap { type SessionOptionsByDomain = (EnumMap,); -type SessionSetsWithInnerIdsByDomain = - (Option<(StyleIdToModelInnerId, InferenceSessionSet)>,); +type SessionSetsWithInnerVoiceIdsByDomain = + (Option<(StyleIdToInnerVoiceId, InferenceSessionSet)>,); #[cfg(test)] mod tests { diff --git a/crates/voicevox_core/src/synthesizer.rs b/crates/voicevox_core/src/synthesizer.rs index 5e4894415..06555ea28 100644 --- a/crates/voicevox_core/src/synthesizer.rs +++ b/crates/voicevox_core/src/synthesizer.rs @@ -836,7 +836,7 @@ pub(crate) mod blocking { impl PerformInference for self::Synthesizer { fn predict_duration(&self, phoneme_vector: &[i64], style_id: StyleId) -> Result> { - let (model_id, model_inner_id) = self.status.ids_for::(style_id)?; + let (model_id, inner_voice_id) = self.status.ids_for::(style_id)?; let PredictDurationOutput { phoneme_length: output, @@ -844,7 +844,7 @@ pub(crate) mod blocking { &model_id, PredictDurationInput { phoneme_list: ndarray::arr1(phoneme_vector), - speaker_id: ndarray::arr1(&[model_inner_id.raw_id().into()]), + speaker_id: ndarray::arr1(&[inner_voice_id.raw_id().into()]), }, )?; let mut output = output.into_raw_vec(); @@ -871,7 +871,7 @@ pub(crate) mod blocking { end_accent_phrase_vector: &[i64], style_id: StyleId, ) -> Result> { - let (model_id, model_inner_id) = self.status.ids_for::(style_id)?; + let (model_id, inner_voice_id) = self.status.ids_for::(style_id)?; let PredictIntonationOutput { f0_list: output } = self.status.run_session( &model_id, @@ -883,7 +883,7 @@ pub(crate) mod blocking { end_accent_list: ndarray::arr1(end_accent_vector), start_accent_phrase_list: ndarray::arr1(start_accent_phrase_vector), end_accent_phrase_list: ndarray::arr1(end_accent_phrase_vector), - speaker_id: ndarray::arr1(&[model_inner_id.raw_id().into()]), + speaker_id: ndarray::arr1(&[inner_voice_id.raw_id().into()]), }, )?; @@ -898,7 +898,7 @@ pub(crate) mod blocking { phoneme_vector: &[f32], style_id: StyleId, ) -> Result> { - let (model_id, model_inner_id) = self.status.ids_for::(style_id)?; + let (model_id, inner_voice_id) = self.status.ids_for::(style_id)?; // 音が途切れてしまうのを避けるworkaround処理が入っている // TODO: 改善したらここのpadding処理を取り除く @@ -925,7 +925,7 @@ pub(crate) mod blocking { phoneme: ndarray::arr1(&phoneme_with_padding) .into_shape([length_with_padding, phoneme_size]) .unwrap(), - speaker_id: ndarray::arr1(&[model_inner_id.raw_id().into()]), + speaker_id: ndarray::arr1(&[inner_voice_id.raw_id().into()]), }, )?; diff --git a/crates/voicevox_core/src/voice_model.rs b/crates/voicevox_core/src/voice_model.rs index 364c8db0a..358d0153c 100644 --- a/crates/voicevox_core/src/voice_model.rs +++ b/crates/voicevox_core/src/voice_model.rs @@ -16,7 +16,7 @@ use crate::{ domains::{TalkDomain, TalkOperation}, InferenceDomain, }, - manifest::{Manifest, ManifestDomains, StyleIdToModelInnerId}, + manifest::{Manifest, ManifestDomains, StyleIdToInnerVoiceId}, SpeakerMeta, StyleMeta, StyleType, VoiceModelMeta, }; use std::path::{Path, PathBuf}; @@ -26,8 +26,8 @@ use std::path::{Path, PathBuf}; /// [`VoiceModelId`]: VoiceModelId pub type RawVoiceModelId = String; -pub(crate) type ModelBytesWithInnerIdsByDomain = - (Option<(StyleIdToModelInnerId, EnumMap>)>,); +pub(crate) type ModelBytesWithInnerVoiceIdsByDomain = + (Option<(StyleIdToInnerVoiceId, EnumMap>)>,); /// 音声モデルID。 #[derive( @@ -164,7 +164,7 @@ pub(crate) mod blocking { VoiceModelMeta, }; - use super::{ModelBytesWithInnerIdsByDomain, VoiceModelHeader, VoiceModelId}; + use super::{ModelBytesWithInnerVoiceIdsByDomain, VoiceModelHeader, VoiceModelId}; /// 音声モデル。 /// @@ -177,7 +177,7 @@ pub(crate) mod blocking { impl self::VoiceModel { pub(crate) fn read_inference_models( &self, - ) -> LoadModelResult> { + ) -> LoadModelResult> { let reader = BlockingVvmEntryReader::open(&self.header.path)?; let talk = self @@ -191,7 +191,7 @@ pub(crate) mod blocking { predict_duration_filename, predict_intonation_filename, decode_filename, - style_id_to_model_inner_id, + style_id_to_inner_voice_id, }| { let model_bytes = [ predict_duration_filename, @@ -206,7 +206,7 @@ pub(crate) mod blocking { let model_bytes = EnumMap::from_array(model_bytes); - Ok((style_id_to_model_inner_id.clone(), model_bytes)) + Ok((style_id_to_inner_voice_id.clone(), model_bytes)) }, ) .transpose()?; @@ -307,7 +307,7 @@ pub(crate) mod tokio { Result, VoiceModelMeta, }; - use super::{ModelBytesWithInnerIdsByDomain, VoiceModelHeader, VoiceModelId}; + use super::{ModelBytesWithInnerVoiceIdsByDomain, VoiceModelHeader, VoiceModelId}; /// 音声モデル。 /// @@ -320,7 +320,7 @@ pub(crate) mod tokio { impl self::VoiceModel { pub(crate) async fn read_inference_models( &self, - ) -> LoadModelResult> { + ) -> LoadModelResult> { let reader = AsyncVvmEntryReader::open(&self.header.path).await?; let talk = OptionFuture::from(self.header.manifest.domains().talk.as_ref().map( @@ -328,7 +328,7 @@ pub(crate) mod tokio { predict_duration_filename, predict_intonation_filename, decode_filename, - style_id_to_model_inner_id, + style_id_to_inner_voice_id, }| async { let ( decode_model_result, @@ -347,7 +347,7 @@ pub(crate) mod tokio { decode_model_result?, ]); - Ok((style_id_to_model_inner_id.clone(), model_bytes)) + Ok((style_id_to_inner_voice_id.clone(), model_bytes)) }, )) .await @@ -505,7 +505,7 @@ mod tests { predict_duration_filename: "".to_owned(), predict_intonation_filename: "".to_owned(), decode_filename: "".to_owned(), - style_id_to_model_inner_id: Default::default(), + style_id_to_inner_voice_id: Default::default(), }); #[fixture] diff --git a/model/sample.vvm/manifest.json b/model/sample.vvm/manifest.json index 2c6721d08..0b82d0c3f 100644 --- a/model/sample.vvm/manifest.json +++ b/model/sample.vvm/manifest.json @@ -5,7 +5,7 @@ "predict_duration_filename": "predict_duration.onnx", "predict_intonation_filename": "predict_intonation.onnx", "decode_filename": "decode.onnx", - "style_id_to_model_inner_id": { + "style_id_to_inner_voice_id": { "302": 2, "303": 3 } From 484bd6247a8d2500104e1fc0882a9e9a8a22d2dc Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Fri, 24 May 2024 22:27:50 +0900 Subject: [PATCH 03/36] =?UTF-8?q?change:=20VVM=E3=81=ABUUID=E3=82=92?= =?UTF-8?q?=E5=89=B2=E3=82=8A=E6=8C=AF=E3=82=8A=E3=80=81=E3=81=9D=E3=82=8C?= =?UTF-8?q?=E3=82=92`VoiceModelId`=E3=81=A8=E3=81=99=E3=82=8B=20(#796)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * change: VVMにUUIDを割り振り、それを`VoiceModelId`とする * 不要となったコメントを削除 * UUIDを分解する部分を修正し、テストも追加 以下のためuuidもアップデートする。 --- Cargo.lock | 17 ++---- Cargo.toml | 3 +- crates/voicevox_core/Cargo.toml | 1 - .../voicevox_core/src/__internal/interop.rs | 5 +- crates/voicevox_core/src/manifest.rs | 5 +- crates/voicevox_core/src/status.rs | 35 +++++------ crates/voicevox_core/src/synthesizer.rs | 14 ++--- crates/voicevox_core/src/voice_model.rs | 43 +++++++------- crates/voicevox_core_c_api/Cargo.toml | 1 + .../include/voicevox_core.h | 6 +- crates/voicevox_core_c_api/src/c_impls.rs | 8 ++- .../src/compatible_engine.rs | 7 +-- crates/voicevox_core_c_api/src/helpers.rs | 11 +++- crates/voicevox_core_c_api/src/lib.rs | 41 +++++-------- crates/voicevox_core_java_api/Cargo.toml | 3 + .../hiroshiba/voicevoxcore/Synthesizer.java | 9 +-- .../jp/hiroshiba/voicevoxcore/VoiceModel.java | 5 +- crates/voicevox_core_java_api/src/common.rs | 55 +++++++++++++++++- .../voicevox_core_java_api/src/synthesizer.rs | 14 ++--- .../voicevox_core_java_api/src/voice_model.rs | 6 +- .../python/voicevox_core/_models.py | 5 +- .../python/voicevox_core/_rust/asyncio.pyi | 4 +- .../python/voicevox_core/_rust/blocking.pyi | 4 +- .../voicevox_core_python_api/src/convert.rs | 1 + crates/voicevox_core_python_api/src/lib.rs | 58 ++++++++++--------- model/sample.vvm/manifest.json | 1 + 26 files changed, 209 insertions(+), 153 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f9a78469..d3846b8a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1822,15 +1822,6 @@ dependencies = [ "windows-sys 0.42.0", ] -[[package]] -name = "nanoid" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" -dependencies = [ - "rand", -] - [[package]] name = "ndarray" version = "0.15.6" @@ -3521,9 +3512,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", "serde", @@ -3587,7 +3578,6 @@ dependencies = [ "indexmap 2.0.0", "itertools 0.10.5", "jlabel", - "nanoid", "ndarray", "once_cell", "open_jtalk", @@ -3662,8 +3652,11 @@ dependencies = [ "android_logger", "chrono", "derive_more", + "easy-ext", "jni", "once_cell", + "pretty_assertions", + "rstest", "serde_json", "tracing", "tracing-subscriber", diff --git a/Cargo.toml b/Cargo.toml index 7102127d5..4ea3d3757 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,6 @@ libc = "0.2.134" libloading = "0.7.3" libtest-mimic = "0.6.0" log = "0.4.17" -nanoid = "0.4.0" ndarray = "0.15.6" ndarray-stats = "0.5.1" octocrab = { version = "0.19.0", default-features = false } @@ -81,7 +80,7 @@ tracing = "0.1.37" tracing-subscriber = "0.3.16" typetag = "0.2.5" url = "2.3.0" -uuid = "1.4.0" +uuid = "1.6.1" voicevox_core = { path = "crates/voicevox_core" } windows = "0.43.0" zip = "0.6.3" diff --git a/crates/voicevox_core/Cargo.toml b/crates/voicevox_core/Cargo.toml index 731862356..9957c1373 100644 --- a/crates/voicevox_core/Cargo.toml +++ b/crates/voicevox_core/Cargo.toml @@ -25,7 +25,6 @@ futures.workspace = true indexmap = { workspace = true, features = ["serde"] } itertools.workspace = true jlabel.workspace = true -nanoid.workspace = true ndarray.workspace = true once_cell.workspace = true open_jtalk.workspace = true diff --git a/crates/voicevox_core/src/__internal/interop.rs b/crates/voicevox_core/src/__internal/interop.rs index fe46d10bc..677f5515a 100644 --- a/crates/voicevox_core/src/__internal/interop.rs +++ b/crates/voicevox_core/src/__internal/interop.rs @@ -1 +1,4 @@ -pub use crate::{metas::merge as merge_metas, synthesizer::blocking::PerformInference}; +pub use crate::{ + metas::merge as merge_metas, synthesizer::blocking::PerformInference, + voice_model::blocking::IdRef, +}; diff --git a/crates/voicevox_core/src/manifest.rs b/crates/voicevox_core/src/manifest.rs index a22b66e8b..8464a48ab 100644 --- a/crates/voicevox_core/src/manifest.rs +++ b/crates/voicevox_core/src/manifest.rs @@ -6,7 +6,7 @@ use derive_new::new; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; -use crate::StyleId; +use crate::{StyleId, VoiceModelId}; pub type RawManifestVersion = String; #[derive(Deserialize, Clone, Debug, PartialEq, new)] @@ -38,10 +38,9 @@ impl Display for InnerVoiceId { #[derive(Deserialize, Getters, Clone)] pub struct Manifest { - // FIXME: UUIDにする - // https://github.com/VOICEVOX/voicevox_core/issues/581 #[allow(dead_code)] manifest_version: ManifestVersion, + pub(crate) id: VoiceModelId, metas_filename: String, #[serde(flatten)] domains: ManifestDomains, diff --git a/crates/voicevox_core/src/status.rs b/crates/voicevox_core/src/status.rs index 6980ab7fc..f590e18f4 100644 --- a/crates/voicevox_core/src/status.rs +++ b/crates/voicevox_core/src/status.rs @@ -58,7 +58,7 @@ impl Status { Ok(()) } - pub(crate) fn unload_model(&self, voice_model_id: &VoiceModelId) -> Result<()> { + pub(crate) fn unload_model(&self, voice_model_id: VoiceModelId) -> Result<()> { self.loaded_models.lock().unwrap().remove(voice_model_id) } @@ -77,7 +77,7 @@ impl Status { self.loaded_models.lock().unwrap().ids_for::(style_id) } - pub(crate) fn is_loaded_model(&self, voice_model_id: &VoiceModelId) -> bool { + pub(crate) fn is_loaded_model(&self, voice_model_id: VoiceModelId) -> bool { self.loaded_models .lock() .unwrap() @@ -101,7 +101,7 @@ impl Status { /// `self`が`model_id`を含んでいないとき、パニックする。 pub(crate) fn run_session( &self, - model_id: &VoiceModelId, + model_id: VoiceModelId, input: I, ) -> Result<::Output> where @@ -159,7 +159,7 @@ impl LoadedModels { .and_then(|(inner_voice_ids, _)| inner_voice_ids.get(&style_id).copied()) .unwrap_or_else(|| InnerVoiceId::new(style_id.raw_id())); - Ok((model_id.clone(), inner_voice_id)) + Ok((*model_id, inner_voice_id)) } /// # Panics @@ -168,12 +168,12 @@ impl LoadedModels { /// /// - `self`が`model_id`を含んでいないとき /// - 対応する`InferenceDomain`が欠けているとき - fn get(&self, model_id: &VoiceModelId) -> InferenceSessionCell + fn get(&self, model_id: VoiceModelId) -> InferenceSessionCell where I: InferenceInputSignature, ::Domain: InferenceDomainExt, { - let (_, session_set) = self.0[model_id] + let (_, session_set) = self.0[&model_id] .session_sets_with_inner_ids .get::<::Domain>() .as_ref() @@ -190,8 +190,8 @@ impl LoadedModels { session_set.get() } - fn contains_voice_model(&self, model_id: &VoiceModelId) -> bool { - self.0.contains_key(model_id) + fn contains_voice_model(&self, model_id: VoiceModelId) -> bool { + self.0.contains_key(&model_id) } fn contains_style(&self, style_id: StyleId) -> bool { @@ -216,9 +216,9 @@ impl LoadedModels { source: None, }; - if self.0.contains_key(&model_header.id) { + if self.0.contains_key(&model_header.manifest.id) { return Err(error(LoadModelErrorKind::ModelAlreadyLoaded { - id: model_header.id.clone(), + id: model_header.manifest.id, })); } @@ -255,7 +255,7 @@ impl LoadedModels { self.ensure_acceptable(model_header)?; let prev = self.0.insert( - model_header.id.clone(), + model_header.manifest.id, LoadedModel { metas: model_header.metas.clone(), session_sets_with_inner_ids, @@ -265,12 +265,9 @@ impl LoadedModels { Ok(()) } - fn remove(&mut self, model_id: &VoiceModelId) -> Result<()> { - if self.0.remove(model_id).is_none() { - return Err(ErrorRepr::ModelNotFound { - model_id: model_id.clone(), - } - .into()); + fn remove(&mut self, model_id: VoiceModelId) -> Result<()> { + if self.0.remove(&model_id).is_none() { + return Err(ErrorRepr::ModelNotFound { model_id }.into()); } Ok(()) } @@ -415,13 +412,13 @@ mod tests { let model_header = vvm.header(); let model_contents = &vvm.read_inference_models().await.unwrap(); assert!( - !status.is_loaded_model(&model_header.id), + !status.is_loaded_model(model_header.manifest.id), "model should not be loaded" ); let result = status.insert_model(model_header, model_contents); assert_debug_fmt_eq!(Ok(()), result); assert!( - status.is_loaded_model(&model_header.id), + status.is_loaded_model(model_header.manifest.id), "model should be loaded", ); } diff --git a/crates/voicevox_core/src/synthesizer.rs b/crates/voicevox_core/src/synthesizer.rs index 06555ea28..4b26eb56b 100644 --- a/crates/voicevox_core/src/synthesizer.rs +++ b/crates/voicevox_core/src/synthesizer.rs @@ -207,12 +207,12 @@ pub(crate) mod blocking { } /// 音声モデルの読み込みを解除する。 - pub fn unload_voice_model(&self, voice_model_id: &VoiceModelId) -> Result<()> { + pub fn unload_voice_model(&self, voice_model_id: VoiceModelId) -> Result<()> { self.status.unload_model(voice_model_id) } /// 指定したIDの音声モデルが読み込まれているか判定する。 - pub fn is_loaded_voice_model(&self, voice_model_id: &VoiceModelId) -> bool { + pub fn is_loaded_voice_model(&self, voice_model_id: VoiceModelId) -> bool { self.status.is_loaded_model(voice_model_id) } @@ -841,7 +841,7 @@ pub(crate) mod blocking { let PredictDurationOutput { phoneme_length: output, } = self.status.run_session( - &model_id, + model_id, PredictDurationInput { phoneme_list: ndarray::arr1(phoneme_vector), speaker_id: ndarray::arr1(&[inner_voice_id.raw_id().into()]), @@ -874,7 +874,7 @@ pub(crate) mod blocking { let (model_id, inner_voice_id) = self.status.ids_for::(style_id)?; let PredictIntonationOutput { f0_list: output } = self.status.run_session( - &model_id, + model_id, PredictIntonationInput { length: ndarray::arr0(length as i64), vowel_phoneme_list: ndarray::arr1(vowel_phoneme_vector), @@ -917,7 +917,7 @@ pub(crate) mod blocking { ); let DecodeOutput { wave: output } = self.status.run_session( - &model_id, + model_id, DecodeInput { f0: ndarray::arr1(&f0_with_padding) .into_shape([length_with_padding, 1]) @@ -1150,11 +1150,11 @@ pub(crate) mod tokio { self.0.status.insert_model(model.header(), model_bytes) } - pub fn unload_voice_model(&self, voice_model_id: &VoiceModelId) -> Result<()> { + pub fn unload_voice_model(&self, voice_model_id: VoiceModelId) -> Result<()> { self.0.unload_voice_model(voice_model_id) } - pub fn is_loaded_voice_model(&self, voice_model_id: &VoiceModelId) -> bool { + pub fn is_loaded_voice_model(&self, voice_model_id: VoiceModelId) -> bool { self.0.is_loaded_voice_model(voice_model_id) } diff --git a/crates/voicevox_core/src/voice_model.rs b/crates/voicevox_core/src/voice_model.rs index 358d0153c..f5e862854 100644 --- a/crates/voicevox_core/src/voice_model.rs +++ b/crates/voicevox_core/src/voice_model.rs @@ -4,11 +4,13 @@ use anyhow::anyhow; use derive_getters::Getters; +use derive_more::From; use derive_new::new; use easy_ext::ext; use enum_map::EnumMap; use itertools::Itertools as _; use serde::Deserialize; +use uuid::Uuid; use crate::{ error::{LoadModelError, LoadModelErrorKind, LoadModelResult}, @@ -24,7 +26,7 @@ use std::path::{Path, PathBuf}; /// [`VoiceModelId`]の実体。 /// /// [`VoiceModelId`]: VoiceModelId -pub type RawVoiceModelId = String; +pub type RawVoiceModelId = Uuid; pub(crate) type ModelBytesWithInnerVoiceIdsByDomain = (Option<(StyleIdToInnerVoiceId, EnumMap>)>,); @@ -34,6 +36,7 @@ pub(crate) type ModelBytesWithInnerVoiceIdsByDomain = PartialEq, Eq, Clone, + Copy, Ord, Hash, PartialOrd, @@ -42,7 +45,9 @@ pub(crate) type ModelBytesWithInnerVoiceIdsByDomain = Getters, derive_more::Display, Debug, + From, )] +#[serde(transparent)] pub struct VoiceModelId { raw_voice_model_id: RawVoiceModelId, } @@ -53,9 +58,7 @@ pub struct VoiceModelId { /// モデルの`[u8]`と分けて`Status`に渡す。 #[derive(Clone)] pub(crate) struct VoiceModelHeader { - /// ID。 - pub(crate) id: VoiceModelId, - manifest: Manifest, + pub(crate) manifest: Manifest, /// メタ情報。 /// /// `manifest`が対応していない`StyleType`のスタイルは含まれるべきではない。 @@ -64,12 +67,7 @@ pub(crate) struct VoiceModelHeader { } impl VoiceModelHeader { - fn new( - id: VoiceModelId, - manifest: Manifest, - metas: &[u8], - path: &Path, - ) -> LoadModelResult { + fn new(manifest: Manifest, metas: &[u8], path: &Path) -> LoadModelResult { let metas = serde_json::from_slice::(metas).map_err(|source| LoadModelError { path: path.to_owned(), @@ -94,7 +92,6 @@ impl VoiceModelHeader { })?; Ok(Self { - id, manifest, metas, path: path.to_owned(), @@ -151,8 +148,8 @@ pub(crate) mod blocking { path::Path, }; + use easy_ext::ext; use enum_map::EnumMap; - use nanoid::nanoid; use ouroboros::self_referencing; use rayon::iter::{IntoParallelIterator as _, ParallelIterator as _}; use serde::de::DeserializeOwned; @@ -220,14 +217,13 @@ pub(crate) mod blocking { let reader = BlockingVvmEntryReader::open(path)?; let manifest = reader.read_vvm_json::("manifest.json")?; let metas = &reader.read_vvm_entry(manifest.metas_filename())?; - let id = VoiceModelId::new(nanoid!()); - let header = VoiceModelHeader::new(id, manifest, metas, path)?; + let header = VoiceModelHeader::new(manifest, metas, path)?; Ok(Self { header }) } /// ID。 - pub fn id(&self) -> &VoiceModelId { - &self.header.id + pub fn id(&self) -> VoiceModelId { + self.header.manifest.id } /// メタ情報。 @@ -289,6 +285,13 @@ pub(crate) mod blocking { }) } } + + #[ext(IdRef)] + pub impl VoiceModel { + fn id_ref(&self) -> &VoiceModelId { + &self.header.manifest.id + } + } } pub(crate) mod tokio { @@ -297,7 +300,6 @@ pub(crate) mod tokio { use derive_new::new; use enum_map::EnumMap; use futures::future::{join3, OptionFuture}; - use nanoid::nanoid; use serde::de::DeserializeOwned; use crate::{ @@ -360,14 +362,13 @@ pub(crate) mod tokio { let reader = AsyncVvmEntryReader::open(path.as_ref()).await?; let manifest = reader.read_vvm_json::("manifest.json").await?; let metas = &reader.read_vvm_entry(manifest.metas_filename()).await?; - let id = VoiceModelId::new(nanoid!()); - let header = VoiceModelHeader::new(id, manifest, metas, path.as_ref())?; + let header = VoiceModelHeader::new(manifest, metas, path.as_ref())?; Ok(Self { header }) } /// ID。 - pub fn id(&self) -> &VoiceModelId { - &self.header.id + pub fn id(&self) -> VoiceModelId { + self.header.manifest.id } /// メタ情報。 diff --git a/crates/voicevox_core_c_api/Cargo.toml b/crates/voicevox_core_c_api/Cargo.toml index ad3a65fa7..f10e2a6f4 100644 --- a/crates/voicevox_core_c_api/Cargo.toml +++ b/crates/voicevox_core_c_api/Cargo.toml @@ -24,6 +24,7 @@ chrono = { workspace = true, default-features = false, features = ["clock"] } colorchoice.workspace = true cstr.workspace = true derive-getters.workspace = true +easy-ext.workspace = true futures.workspace = true itertools.workspace = true libc.workspace = true diff --git a/crates/voicevox_core_c_api/include/voicevox_core.h b/crates/voicevox_core_c_api/include/voicevox_core.h index 592ec8abb..da921c869 100644 --- a/crates/voicevox_core_c_api/include/voicevox_core.h +++ b/crates/voicevox_core_c_api/include/voicevox_core.h @@ -277,7 +277,7 @@ typedef struct VoicevoxInitializeOptions { /** * 音声モデルID。 */ -typedef const char *VoicevoxVoiceModelId; +typedef const uint8_t (*VoicevoxVoiceModelId)[16]; /** * スタイルID。 @@ -554,7 +554,7 @@ VoicevoxResultCode voicevox_synthesizer_load_voice_model(const struct VoicevoxSy * * \safety{ * - `synthesizer`は ::voicevox_synthesizer_new で得たものでなければならず、また ::voicevox_synthesizer_delete で解放されていてはいけない。 - * - `model_id`はヌル終端文字列を指し、かつ読み込みについて有効でなければならない。 + * - `model_id`は読み込みについて有効でなければならない。 * } */ #ifdef _WIN32 @@ -589,7 +589,7 @@ bool voicevox_synthesizer_is_gpu_mode(const struct VoicevoxSynthesizer *synthesi * * \safety{ * - `synthesizer`は ::voicevox_synthesizer_new で得たものでなければならず、また ::voicevox_synthesizer_delete で解放されていてはいけない。 - * - `model_id`はヌル終端文字列を指し、かつ読み込みについて有効でなければならない。 + * - `model_id`は読み込みについて有効でなければならない。 * } */ #ifdef _WIN32 diff --git a/crates/voicevox_core_c_api/src/c_impls.rs b/crates/voicevox_core_c_api/src/c_impls.rs index 4e73bf0fb..fe4afcf65 100644 --- a/crates/voicevox_core_c_api/src/c_impls.rs +++ b/crates/voicevox_core_c_api/src/c_impls.rs @@ -5,6 +5,9 @@ use voicevox_core::{InitializeOptions, Result, VoiceModelId}; use crate::{helpers::CApiResult, OpenJtalkRc, VoicevoxSynthesizer, VoicevoxVoiceModel}; +// FIXME: 中身(Rust API)を直接操作するかラッパーメソッド越しにするのかが混在していて、一貫性を +// 欠いている + impl OpenJtalkRc { pub(crate) fn new(open_jtalk_dic_dir: impl AsRef) -> Result { Ok(Self { @@ -28,7 +31,7 @@ impl VoicevoxSynthesizer { Ok(()) } - pub(crate) fn unload_voice_model(&self, model_id: &VoiceModelId) -> Result<()> { + pub(crate) fn unload_voice_model(&self, model_id: VoiceModelId) -> Result<()> { self.synthesizer.unload_voice_model(model_id)?; Ok(()) } @@ -42,8 +45,7 @@ impl VoicevoxSynthesizer { impl VoicevoxVoiceModel { pub(crate) fn from_path(path: impl AsRef) -> Result { let model = voicevox_core::blocking::VoiceModel::from_path(path)?; - let id = CString::new(model.id().raw_voice_model_id().as_str()).unwrap(); let metas = CString::new(serde_json::to_string(model.metas()).unwrap()).unwrap(); - Ok(Self { model, id, metas }) + Ok(Self { model, metas }) } } diff --git a/crates/voicevox_core_c_api/src/compatible_engine.rs b/crates/voicevox_core_c_api/src/compatible_engine.rs index 6755910f5..ae4d21a93 100644 --- a/crates/voicevox_core_c_api/src/compatible_engine.rs +++ b/crates/voicevox_core_c_api/src/compatible_engine.rs @@ -37,10 +37,7 @@ struct VoiceModelSet { static VOICE_MODEL_SET: Lazy = Lazy::new(|| { let all_vvms = get_all_models(); - let model_map: BTreeMap<_, _> = all_vvms - .iter() - .map(|vvm| (vvm.id().clone(), vvm.clone())) - .collect(); + let model_map: BTreeMap<_, _> = all_vvms.iter().map(|vvm| (vvm.id(), vvm.clone())).collect(); let metas = voicevox_core::__internal::interop::merge_metas( all_vvms.iter().flat_map(|vvm| vvm.metas()), ); @@ -48,7 +45,7 @@ static VOICE_MODEL_SET: Lazy = Lazy::new(|| { for vvm in all_vvms.iter() { for meta in vvm.metas().iter() { for style in meta.styles().iter() { - style_model_map.insert(*style.id(), vvm.id().clone()); + style_model_map.insert(*style.id(), vvm.id()); } } } diff --git a/crates/voicevox_core_c_api/src/helpers.rs b/crates/voicevox_core_c_api/src/helpers.rs index 1c163a0d0..ac0cab286 100644 --- a/crates/voicevox_core_c_api/src/helpers.rs +++ b/crates/voicevox_core_c_api/src/helpers.rs @@ -1,5 +1,7 @@ +use easy_ext::ext; use std::{error::Error as _, ffi::CStr, fmt::Debug, iter}; -use voicevox_core::{AudioQueryModel, UserDictWord}; +use uuid::Uuid; +use voicevox_core::{AudioQueryModel, UserDictWord, VoiceModelId}; use thiserror::Error; use tracing::error; @@ -163,6 +165,13 @@ impl Default for VoicevoxSynthesisOptions { } } +#[ext(UuidBytesExt)] +pub(crate) impl uuid::Bytes { + fn to_model_id(self) -> VoiceModelId { + Uuid::from_bytes(self).into() + } +} + impl VoicevoxUserDictWord { pub(crate) unsafe fn try_into_word(&self) -> CApiResult { Ok(UserDictWord::new( diff --git a/crates/voicevox_core_c_api/src/lib.rs b/crates/voicevox_core_c_api/src/lib.rs index fbb0bf6bf..08a54b11a 100644 --- a/crates/voicevox_core_c_api/src/lib.rs +++ b/crates/voicevox_core_c_api/src/lib.rs @@ -12,7 +12,7 @@ mod slice_owner; use self::drop_check::C_STRING_DROP_CHECKER; use self::helpers::{ accent_phrases_to_json, audio_query_model_to_json, ensure_utf8, into_result_code_with_error, - CApiError, + CApiError, UuidBytesExt as _, }; use self::result_code::VoicevoxResultCode; use self::slice_owner::U8_SLICE_OWNER; @@ -30,7 +30,8 @@ use std::sync::{Arc, Once}; use tracing_subscriber::fmt::format::Writer; use tracing_subscriber::EnvFilter; use uuid::Uuid; -use voicevox_core::{AccentPhraseModel, AudioQueryModel, TtsOptions, UserDictWord, VoiceModelId}; +use voicevox_core::__internal::interop::IdRef as _; +use voicevox_core::{AccentPhraseModel, AudioQueryModel, TtsOptions, UserDictWord}; use voicevox_core::{StyleId, SupportedDevices, SynthesisOptions}; fn init_logger_once() { @@ -238,12 +239,11 @@ pub extern "C" fn voicevox_get_version() -> *const c_char { #[derive(Getters)] pub struct VoicevoxVoiceModel { model: voicevox_core::blocking::VoiceModel, - id: CString, metas: CString, } /// 音声モデルID。 -pub type VoicevoxVoiceModelId = *const c_char; +pub type VoicevoxVoiceModelId<'a> = &'a [u8; 16]; /// スタイルID。 /// @@ -285,9 +285,9 @@ pub unsafe extern "C" fn voicevox_voice_model_new_from_path( /// - `model`は ::voicevox_voice_model_new_from_path で得たものでなければならず、また ::voicevox_voice_model_delete で解放されていてはいけない。 /// } #[no_mangle] -pub extern "C" fn voicevox_voice_model_id(model: &VoicevoxVoiceModel) -> VoicevoxVoiceModelId { +pub extern "C" fn voicevox_voice_model_id(model: &VoicevoxVoiceModel) -> VoicevoxVoiceModelId<'_> { init_logger_once(); - model.id().as_ptr() + model.model.id_ref().raw_voice_model_id().as_bytes() } /// ::VoicevoxVoiceModel からメタ情報を取得する。 @@ -399,20 +399,16 @@ pub extern "C" fn voicevox_synthesizer_load_voice_model( /// /// \safety{ /// - `synthesizer`は ::voicevox_synthesizer_new で得たものでなければならず、また ::voicevox_synthesizer_delete で解放されていてはいけない。 -/// - `model_id`はヌル終端文字列を指し、かつ読み込みについて有効でなければならない。 +/// - `model_id`は読み込みについて有効でなければならない。 /// } #[no_mangle] -pub unsafe extern "C" fn voicevox_synthesizer_unload_voice_model( +pub extern "C" fn voicevox_synthesizer_unload_voice_model( synthesizer: &VoicevoxSynthesizer, - model_id: VoicevoxVoiceModelId, + model_id: VoicevoxVoiceModelId<'_>, ) -> VoicevoxResultCode { init_logger_once(); - into_result_code_with_error((|| { - let raw_model_id = ensure_utf8(unsafe { CStr::from_ptr(model_id) })?; - synthesizer - .unload_voice_model(&VoiceModelId::new(raw_model_id.to_string())) - .map_err(Into::into) - })()) + let model_id = model_id.to_model_id(); + into_result_code_with_error(synthesizer.unload_voice_model(model_id).map_err(Into::into)) } /// ハードウェアアクセラレーションがGPUモードか判定する。 @@ -439,21 +435,16 @@ pub extern "C" fn voicevox_synthesizer_is_gpu_mode(synthesizer: &VoicevoxSynthes /// /// \safety{ /// - `synthesizer`は ::voicevox_synthesizer_new で得たものでなければならず、また ::voicevox_synthesizer_delete で解放されていてはいけない。 -/// - `model_id`はヌル終端文字列を指し、かつ読み込みについて有効でなければならない。 +/// - `model_id`は読み込みについて有効でなければならない。 /// } #[no_mangle] -pub unsafe extern "C" fn voicevox_synthesizer_is_loaded_voice_model( +pub extern "C" fn voicevox_synthesizer_is_loaded_voice_model( synthesizer: &VoicevoxSynthesizer, - model_id: VoicevoxVoiceModelId, + model_id: VoicevoxVoiceModelId<'_>, ) -> bool { init_logger_once(); - let Ok(raw_model_id) = ensure_utf8(unsafe { CStr::from_ptr(model_id) }) else { - // 与えられたIDがUTF-8ではない場合、それに対応する`VoicdModel`は確実に存在しない - return false; - }; - synthesizer - .synthesizer() - .is_loaded_voice_model(&VoiceModelId::new(raw_model_id.into())) + let model_id = model_id.to_model_id(); + synthesizer.synthesizer().is_loaded_voice_model(model_id) } /// 今読み込んでいる音声モデルのメタ情報を、JSONで取得する。 diff --git a/crates/voicevox_core_java_api/Cargo.toml b/crates/voicevox_core_java_api/Cargo.toml index 06b2af618..b39a98b2a 100644 --- a/crates/voicevox_core_java_api/Cargo.toml +++ b/crates/voicevox_core_java_api/Cargo.toml @@ -15,8 +15,11 @@ directml = ["voicevox_core/directml"] android_logger.workspace = true chrono = { workspace = true, default-features = false, features = ["clock"] } derive_more.workspace = true +easy-ext.workspace = true jni.workspace = true once_cell.workspace = true +pretty_assertions = "1.3.0" +rstest.workspace = true serde_json = { workspace = true, features = ["preserve_order"] } tracing = { workspace = true, features = ["log"] } tracing-subscriber = { workspace = true, features = ["env-filter"] } diff --git a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Synthesizer.java b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Synthesizer.java index a3fe0de6c..6ec6d9108 100644 --- a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Synthesizer.java +++ b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Synthesizer.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.UUID; import jp.hiroshiba.voicevoxcore.exceptions.InferenceFailedException; import jp.hiroshiba.voicevoxcore.exceptions.InvalidModelDataException; @@ -65,7 +66,7 @@ public void loadVoiceModel(VoiceModel voiceModel) throws InvalidModelDataExcepti * * @param voiceModelId 読み込みを解除する音声モデルのID。 */ - public void unloadVoiceModel(String voiceModelId) { + public void unloadVoiceModel(UUID voiceModelId) { rsUnloadVoiceModel(voiceModelId); } @@ -75,7 +76,7 @@ public void unloadVoiceModel(String voiceModelId) { * @param voiceModelId 音声モデルのID。 * @return 指定した音声モデルのIDが読み込まれているかどうか。 */ - public boolean isLoadedVoiceModel(String voiceModelId) { + public boolean isLoadedVoiceModel(UUID voiceModelId) { return rsIsLoadedVoiceModel(voiceModelId); } @@ -274,9 +275,9 @@ public TtsConfigurator tts(String text, int styleId) { private native void rsLoadVoiceModel(VoiceModel voiceModel) throws InvalidModelDataException; - private native void rsUnloadVoiceModel(String voiceModelId); + private native void rsUnloadVoiceModel(UUID voiceModelId); - private native boolean rsIsLoadedVoiceModel(String voiceModelId); + private native boolean rsIsLoadedVoiceModel(UUID voiceModelId); @Nonnull private native String rsAudioQueryFromKana(String kana, int styleId) diff --git a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/VoiceModel.java b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/VoiceModel.java index 576629515..d8c002f0f 100644 --- a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/VoiceModel.java +++ b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/VoiceModel.java @@ -5,13 +5,14 @@ import com.google.gson.annotations.SerializedName; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; +import java.util.UUID; /** 音声モデル。 */ public class VoiceModel extends Dll { private long handle; /** ID。 */ - @Nonnull public final String id; + @Nonnull public final UUID id; /** メタ情報。 */ @Nonnull public final SpeakerMeta[] metas; @@ -36,7 +37,7 @@ protected void finalize() throws Throwable { private native void rsFromPath(String modelPath); @Nonnull - private native String rsGetId(); + private native UUID rsGetId(); @Nonnull private native String rsGetMetasJson(); diff --git a/crates/voicevox_core_java_api/src/common.rs b/crates/voicevox_core_java_api/src/common.rs index bdb37f4ff..6e13cee89 100644 --- a/crates/voicevox_core_java_api/src/common.rs +++ b/crates/voicevox_core_java_api/src/common.rs @@ -1,7 +1,12 @@ use std::{error::Error as _, iter}; use derive_more::From; -use jni::{objects::JThrowable, JNIEnv}; +use easy_ext::ext; +use jni::{ + objects::{JObject, JThrowable}, + JNIEnv, +}; +use uuid::Uuid; #[macro_export] macro_rules! object { @@ -168,3 +173,51 @@ pub(crate) enum JavaApiError { DeJson(serde_json::Error), } + +#[ext(JNIEnvExt)] +pub(crate) impl JNIEnv<'_> { + fn new_uuid(&mut self, uuid: Uuid) -> jni::errors::Result> { + let (msbs, lsbs) = split_uuid(uuid); + self.new_object("java/util/UUID", "(JJ)V", &[msbs.into(), lsbs.into()]) + } + + fn get_uuid(&mut self, obj: &JObject<'_>) -> jni::errors::Result { + let mut get_bits = |method_name| self.call_method(obj, method_name, "()J", &[])?.j(); + let msbs = get_bits("getMostSignificantBits")?; + let lsbs = get_bits("getLeastSignificantBits")?; + Ok(construct_uuid(msbs, lsbs)) + } +} + +fn split_uuid(uuid: Uuid) -> (i64, i64) { + let uuid = uuid.as_u128(); + let msbs = (uuid >> 64) as _; + let lsbs = uuid as _; + (msbs, lsbs) +} + +fn construct_uuid(msbs: i64, lsbs: i64) -> Uuid { + return Uuid::from_u128((to_u128(msbs) << 64) + to_u128(lsbs)); + + fn to_u128(bits: i64) -> u128 { + (bits as u64).into() + } +} + +#[cfg(test)] +mod tests { + use pretty_assertions::assert_eq; + use rstest::rstest; + use uuid::{uuid, Uuid}; + + #[rstest] + #[case(uuid!("a1a2a3a4-b1b2-c1c2-d1d2-e1e2e3e4e5e6"))] + #[case(uuid!("00000000-0000-0000-0000-000000000000"))] + #[case(uuid!("00000000-0000-0000-ffff-ffffffffffff"))] + #[case(uuid!("ffffffff-ffff-ffff-0000-000000000000"))] + #[case(uuid!("ffffffff-ffff-ffff-ffff-ffffffffffff"))] + fn uuid_conversion_works(#[case] uuid: Uuid) { + let (msbs, lsbs) = super::split_uuid(uuid); + assert_eq!(uuid, super::construct_uuid(msbs, lsbs)); + } +} diff --git a/crates/voicevox_core_java_api/src/synthesizer.rs b/crates/voicevox_core_java_api/src/synthesizer.rs index fee5bc132..dc5dc971d 100644 --- a/crates/voicevox_core_java_api/src/synthesizer.rs +++ b/crates/voicevox_core_java_api/src/synthesizer.rs @@ -1,5 +1,5 @@ use crate::{ - common::{throw_if_err, JavaApiError}, + common::{throw_if_err, JNIEnvExt as _, JavaApiError}, enum_object, object, object_type, }; @@ -115,10 +115,10 @@ unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_Synthesizer_rsLoadVoice unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_Synthesizer_rsUnloadVoiceModel<'local>( env: JNIEnv<'local>, this: JObject<'local>, - model_id: JString<'local>, + model_id: JObject<'local>, ) { throw_if_err(env, (), |env| { - let model_id: String = env.get_string(&model_id)?.into(); + let model_id = env.get_uuid(&model_id)?.into(); let internal = env .get_rust_field::<_, _, Arc>>( @@ -126,7 +126,7 @@ unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_Synthesizer_rsUnloadVoi )? .clone(); - internal.unload_voice_model(&voicevox_core::VoiceModelId::new(model_id))?; + internal.unload_voice_model(model_id)?; Ok(()) }) @@ -138,10 +138,10 @@ unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_Synthesizer_rsIsLoadedV >( env: JNIEnv<'local>, this: JObject<'local>, - model_id: JString<'local>, + model_id: JObject<'local>, ) -> jboolean { throw_if_err(env, false, |env| { - let model_id: String = env.get_string(&model_id)?.into(); + let model_id = env.get_uuid(&model_id)?.into(); let internal = env .get_rust_field::<_, _, Arc>>( @@ -149,7 +149,7 @@ unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_Synthesizer_rsIsLoadedV )? .clone(); - let is_loaded = internal.is_loaded_voice_model(&voicevox_core::VoiceModelId::new(model_id)); + let is_loaded = internal.is_loaded_voice_model(model_id); Ok(is_loaded) }) diff --git a/crates/voicevox_core_java_api/src/voice_model.rs b/crates/voicevox_core_java_api/src/voice_model.rs index 42a20544a..546ab9bf6 100644 --- a/crates/voicevox_core_java_api/src/voice_model.rs +++ b/crates/voicevox_core_java_api/src/voice_model.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, sync::Arc}; -use crate::common::throw_if_err; +use crate::common::{throw_if_err, JNIEnvExt as _}; use jni::{ objects::{JObject, JString}, sys::jobject, @@ -35,9 +35,7 @@ unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_VoiceModel_rsGetId<'loc .get_rust_field::<_, _, Arc>(&this, "handle")? .clone(); - let id = internal.id().raw_voice_model_id(); - - let id = env.new_string(id)?; + let id = env.new_uuid(*internal.id().raw_voice_model_id())?; Ok(id.into_raw()) }) diff --git a/crates/voicevox_core_python_api/python/voicevox_core/_models.py b/crates/voicevox_core_python_api/python/voicevox_core/_models.py index 21a2016fe..f7929fae2 100644 --- a/crates/voicevox_core_python_api/python/voicevox_core/_models.py +++ b/crates/voicevox_core_python_api/python/voicevox_core/_models.py @@ -1,6 +1,7 @@ import dataclasses from enum import Enum from typing import List, NewType, Optional +from uuid import UUID import pydantic @@ -24,13 +25,13 @@ x : str """ -VoiceModelId = NewType("VoiceModelId", str) +VoiceModelId = NewType("VoiceModelId", UUID) """ 音声モデルID。 Parameters ---------- -x : str +x : UUID """ diff --git a/crates/voicevox_core_python_api/python/voicevox_core/_rust/asyncio.pyi b/crates/voicevox_core_python_api/python/voicevox_core/_rust/asyncio.pyi index d8e9f6fe2..468d885ee 100644 --- a/crates/voicevox_core_python_api/python/voicevox_core/_rust/asyncio.pyi +++ b/crates/voicevox_core_python_api/python/voicevox_core/_rust/asyncio.pyi @@ -109,7 +109,7 @@ class Synthesizer: 読み込むモデルのスタイルID。 """ ... - def unload_voice_model(self, voice_model_id: Union[VoiceModelId, str]) -> None: + def unload_voice_model(self, voice_model_id: Union[VoiceModelId, UUID]) -> None: """ 音声モデルの読み込みを解除する。 @@ -119,7 +119,7 @@ class Synthesizer: 音声モデルID。 """ ... - def is_loaded_voice_model(self, voice_model_id: Union[VoiceModelId, str]) -> bool: + def is_loaded_voice_model(self, voice_model_id: Union[VoiceModelId, UUID]) -> bool: """ 指定したvoice_model_idのモデルが読み込まれているか判定する。 diff --git a/crates/voicevox_core_python_api/python/voicevox_core/_rust/blocking.pyi b/crates/voicevox_core_python_api/python/voicevox_core/_rust/blocking.pyi index 5584d68bb..fd09eb8cd 100644 --- a/crates/voicevox_core_python_api/python/voicevox_core/_rust/blocking.pyi +++ b/crates/voicevox_core_python_api/python/voicevox_core/_rust/blocking.pyi @@ -104,7 +104,7 @@ class Synthesizer: 読み込むモデルのスタイルID。 """ ... - def unload_voice_model(self, voice_model_id: Union[VoiceModelId, str]) -> None: + def unload_voice_model(self, voice_model_id: Union[VoiceModelId, UUID]) -> None: """ 音声モデルの読み込みを解除する。 @@ -114,7 +114,7 @@ class Synthesizer: 音声モデルID。 """ ... - def is_loaded_voice_model(self, voice_model_id: Union[VoiceModelId, str]) -> bool: + def is_loaded_voice_model(self, voice_model_id: Union[VoiceModelId, UUID]) -> bool: """ 指定したvoice_model_idのモデルが読み込まれているか判定する。 diff --git a/crates/voicevox_core_python_api/src/convert.rs b/crates/voicevox_core_python_api/src/convert.rs index 3cee4186b..f40b2d449 100644 --- a/crates/voicevox_core_python_api/src/convert.rs +++ b/crates/voicevox_core_python_api/src/convert.rs @@ -149,6 +149,7 @@ pub(crate) fn to_rust_uuid(ob: &PyAny) -> PyResult { let uuid = ob.getattr("hex")?.extract::()?; uuid.parse::().into_py_value_result() } +// FIXME: `to_object`は必要無いのでは? pub(crate) fn to_py_uuid(py: Python<'_>, uuid: Uuid) -> PyResult { let uuid = uuid.hyphenated().to_string(); let uuid = py.import("uuid")?.call_method1("UUID", (uuid,))?; diff --git a/crates/voicevox_core_python_api/src/lib.rs b/crates/voicevox_core_python_api/src/lib.rs index 492d18f0e..e43fa4476 100644 --- a/crates/voicevox_core_python_api/src/lib.rs +++ b/crates/voicevox_core_python_api/src/lib.rs @@ -154,13 +154,13 @@ mod blocking { use camino::Utf8PathBuf; use pyo3::{ pyclass, pymethods, - types::{IntoPyDict as _, PyBytes, PyDict, PyList, PyString}, + types::{IntoPyDict as _, PyBytes, PyDict, PyList}, PyAny, PyObject, PyRef, PyResult, Python, }; use uuid::Uuid; use voicevox_core::{ AccelerationMode, AudioQueryModel, InitializeOptions, StyleId, SynthesisOptions, - TtsOptions, UserDictWord, VoiceModelId, + TtsOptions, UserDictWord, }; use crate::{convert::VoicevoxCoreResultExt as _, Closable}; @@ -180,8 +180,9 @@ mod blocking { } #[getter] - fn id(&self) -> &str { - self.model.id().raw_voice_model_id() + fn id(&self, py: Python<'_>) -> PyResult { + let id = *self.model.id().raw_voice_model_id(); + crate::convert::to_py_uuid(py, id) } #[getter] @@ -289,23 +290,25 @@ mod blocking { .into_py_result(py) } - fn unload_voice_model(&mut self, voice_model_id: &str, py: Python<'_>) -> PyResult<()> { + fn unload_voice_model( + &mut self, + #[pyo3(from_py_with = "crate::convert::to_rust_uuid")] voice_model_id: Uuid, + py: Python<'_>, + ) -> PyResult<()> { self.synthesizer .get()? - .unload_voice_model(&VoiceModelId::new(voice_model_id.to_string())) + .unload_voice_model(voice_model_id.into()) .into_py_result(py) } - // C APIの挙動と一貫性を持たせる。 - fn is_loaded_voice_model(&self, voice_model_id: &PyString) -> PyResult { - let Ok(voice_model_id) = voice_model_id.to_str() else { - // 与えられたIDがUTF-8ではない場合、それに対応する`VoicdModel`は確実に存在しない - return Ok(false); - }; + fn is_loaded_voice_model( + &self, + #[pyo3(from_py_with = "crate::convert::to_rust_uuid")] voice_model_id: Uuid, + ) -> PyResult { Ok(self .synthesizer .get()? - .is_loaded_voice_model(&VoiceModelId::new(voice_model_id.to_string()))) + .is_loaded_voice_model(voice_model_id.into())) } fn audio_query_from_kana<'py>( @@ -579,13 +582,13 @@ mod asyncio { use camino::Utf8PathBuf; use pyo3::{ pyclass, pymethods, - types::{IntoPyDict as _, PyBytes, PyDict, PyList, PyString}, + types::{IntoPyDict as _, PyBytes, PyDict, PyList}, PyAny, PyObject, PyRef, PyResult, Python, ToPyObject as _, }; use uuid::Uuid; use voicevox_core::{ AccelerationMode, AudioQueryModel, InitializeOptions, StyleId, SynthesisOptions, - TtsOptions, UserDictWord, VoiceModelId, + TtsOptions, UserDictWord, }; use crate::{convert::VoicevoxCoreResultExt as _, Closable}; @@ -608,8 +611,9 @@ mod asyncio { } #[getter] - fn id(&self) -> &str { - self.model.id().raw_voice_model_id() + fn id(&self, py: Python<'_>) -> PyResult { + let id = *self.model.id().raw_voice_model_id(); + crate::convert::to_py_uuid(py, id) } #[getter] @@ -725,23 +729,25 @@ mod asyncio { }) } - fn unload_voice_model(&mut self, voice_model_id: &str, py: Python<'_>) -> PyResult<()> { + fn unload_voice_model( + &mut self, + #[pyo3(from_py_with = "crate::convert::to_rust_uuid")] voice_model_id: Uuid, + py: Python<'_>, + ) -> PyResult<()> { self.synthesizer .get()? - .unload_voice_model(&VoiceModelId::new(voice_model_id.to_string())) + .unload_voice_model(voice_model_id.into()) .into_py_result(py) } - // C APIの挙動と一貫性を持たせる。 - fn is_loaded_voice_model(&self, voice_model_id: &PyString) -> PyResult { - let Ok(voice_model_id) = voice_model_id.to_str() else { - // 与えられたIDがUTF-8ではない場合、それに対応する`VoicdModel`は確実に存在しない - return Ok(false); - }; + fn is_loaded_voice_model( + &self, + #[pyo3(from_py_with = "crate::convert::to_rust_uuid")] voice_model_id: Uuid, + ) -> PyResult { Ok(self .synthesizer .get()? - .is_loaded_voice_model(&VoiceModelId::new(voice_model_id.to_string()))) + .is_loaded_voice_model(voice_model_id.into())) } fn audio_query_from_kana<'py>( diff --git a/model/sample.vvm/manifest.json b/model/sample.vvm/manifest.json index 0b82d0c3f..cdc765ec5 100644 --- a/model/sample.vvm/manifest.json +++ b/model/sample.vvm/manifest.json @@ -1,5 +1,6 @@ { "manifest_version": "0.0.0", + "id": "018fa5b1-146c-71e9-b523-6f6dabcf05fe", "metas_filename": "metas.json", "talk": { "predict_duration_filename": "predict_duration.onnx", From be8d0c065fb8a0da442062e605d3b7b583398ecc Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Sat, 25 May 2024 02:40:40 +0900 Subject: [PATCH 04/36] =?UTF-8?q?change:=20`manifest=5Fversion`=E3=82=92`v?= =?UTF-8?q?vm=5Fformat=5Fversion`=E3=81=AB=20(#794)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * change: `manifest_version`を`vvm_format_version`に * エラーメッセージを変更 * エラーメッセージを変更 * 文字列ではなく整数にする --- crates/voicevox_core/src/manifest.rs | 82 +++++++++++++++++++++++++--- model/sample.vvm/manifest.json | 2 +- 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/crates/voicevox_core/src/manifest.rs b/crates/voicevox_core/src/manifest.rs index 8464a48ab..0808a6414 100644 --- a/crates/voicevox_core/src/manifest.rs +++ b/crates/voicevox_core/src/manifest.rs @@ -1,20 +1,49 @@ -use std::{collections::BTreeMap, fmt::Display, sync::Arc}; +use std::{ + collections::BTreeMap, + fmt::{self, Display}, + sync::Arc, +}; use derive_getters::Getters; use derive_more::Deref; use derive_new::new; -use serde::{Deserialize, Serialize}; +use serde::{de, Deserialize, Deserializer, Serialize}; use serde_with::{serde_as, DisplayFromStr}; use crate::{StyleId, VoiceModelId}; -pub type RawManifestVersion = String; -#[derive(Deserialize, Clone, Debug, PartialEq, new)] -pub struct ManifestVersion(RawManifestVersion); +#[derive(Clone)] +struct FormatVersionV1; -impl Display for ManifestVersion { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) +impl<'de> Deserialize<'de> for FormatVersionV1 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + return deserializer.deserialize_any(Visitor); + + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = FormatVersionV1; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("an unsigned integer") + } + + fn visit_u64(self, v: u64) -> Result + where + E: de::Error, + { + match v { + 1 => Ok(FormatVersionV1), + v => Err(E::custom(format!( + "未知の形式です(`vvm_format_version={v}`)。新しいバージョンのVOICEVOX \ + COREであれば対応しているかもしれません", + ))), + } + } + } } } @@ -39,7 +68,7 @@ impl Display for InnerVoiceId { #[derive(Deserialize, Getters, Clone)] pub struct Manifest { #[allow(dead_code)] - manifest_version: ManifestVersion, + vvm_format_version: FormatVersionV1, pub(crate) id: VoiceModelId, metas_filename: String, #[serde(flatten)] @@ -66,3 +95,38 @@ pub(crate) struct TalkManifest { pub(crate) struct StyleIdToInnerVoiceId( #[serde_as(as = "Arc>")] Arc>, ); + +#[cfg(test)] +mod tests { + use std::ops::Deref; + + use rstest::rstest; + use serde::Deserialize; + + use super::FormatVersionV1; + + #[rstest] + #[case("{\"vvm_format_version\":1}", Ok(()))] + #[case( + "{\"vvm_format_version\":2}", + Err( + "未知の形式です(`vvm_format_version=2`)。新しいバージョンのVOICEVOX COREであれば対応\ + しているかもしれません at line 1 column 23", + ) + )] + fn vvm_format_version_works( + #[case] input: &str, + #[case] expected: Result<(), &str>, + ) -> anyhow::Result<()> { + let actual = serde_json::from_str::(input).map_err(|e| e.to_string()); + let actual = actual.as_ref().map(|_| ()).map_err(Deref::deref); + assert_eq!(expected, actual); + return Ok(()); + + #[derive(Deserialize)] + struct ManifestPart { + #[allow(dead_code)] + vvm_format_version: FormatVersionV1, + } + } +} diff --git a/model/sample.vvm/manifest.json b/model/sample.vvm/manifest.json index cdc765ec5..db2ca92c1 100644 --- a/model/sample.vvm/manifest.json +++ b/model/sample.vvm/manifest.json @@ -1,5 +1,5 @@ { - "manifest_version": "0.0.0", + "vvm_format_version": 1, "id": "018fa5b1-146c-71e9-b523-6f6dabcf05fe", "metas_filename": "metas.json", "talk": { From 4cd19cce53ae2e0ee14adeb93180f3c6e530ba3a Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Sat, 25 May 2024 22:18:28 +0900 Subject: [PATCH 05/36] =?UTF-8?q?chore:=20#796=20=E3=81=AB=E5=AF=BE?= =?UTF-8?q?=E3=81=99=E3=82=8BJava=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=20(#797)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: #796 に対するJavaのテストを追加 * テスト名を変更 --- .../voicevoxcore/VoiceModelTest.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/VoiceModelTest.java diff --git a/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/VoiceModelTest.java b/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/VoiceModelTest.java new file mode 100644 index 000000000..5a720b07f --- /dev/null +++ b/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/VoiceModelTest.java @@ -0,0 +1,36 @@ +package jp.hiroshiba.voicevoxcore; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.Gson; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import jakarta.annotation.Nonnull; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.UUID; +import org.junit.jupiter.api.Test; + +class VoiceModelTest extends TestUtils { + @Test + void idShouldBePreservedAsIs() throws IOException { + UUID expected = UUID.fromString(Manifest.readJson().id); + UUID actual = loadModel().id; + assertEquals(expected, actual); + } + + private static class Manifest { + @SerializedName("id") + @Expose + @Nonnull + String id; + + static Manifest readJson() throws IOException { + Path path = new File("../../../model/sample.vvm/manifest.json").toPath(); + String json = new String(Files.readAllBytes(path)); + return new Gson().fromJson(json, Manifest.class); + } + } +} From 264460ddf810843a9d54fea5413499359265bdb7 Mon Sep 17 00:00:00 2001 From: Han Yeong-woo Date: Tue, 4 Jun 2024 22:08:49 +0900 Subject: [PATCH 06/36] Bump actions (#798) --- .github/workflows/build_and_deploy.yml | 34 +++++++++---------- .../workflows/build_and_deploy_downloader.yml | 4 +-- .github/workflows/cargo-deny.yml | 2 +- .github/workflows/download_test.yml | 2 +- .github/workflows/generate_document.yml | 10 +++--- .github/workflows/java_lint.yml | 2 +- .github/workflows/labeler.yml | 2 +- .github/workflows/python_lint.yml | 4 +-- .github/workflows/test.yml | 34 +++++++++---------- .github/workflows/typos.yml | 4 +-- _typos.toml | 5 +++ 11 files changed, 54 insertions(+), 49 deletions(-) diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index 01ddfc434..aecd7dca8 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -178,9 +178,9 @@ jobs: env: ASSET_NAME: voicevox_core-${{ matrix.artifact_name }}-${{ needs.config.outputs.version }} steps: - - uses: actions/checkout@v3 # 製品版ではない場合 + - uses: actions/checkout@v4 # 製品版ではない場合 if: ${{ !inputs.is_production }} - - uses: actions/checkout@v3 # 製品版の場合 + - uses: actions/checkout@v4 # 製品版の場合 if: inputs.is_production with: fetch-depth: 0 # 全履歴取得 @@ -196,7 +196,7 @@ jobs: ) > /dev/null 2>&1 - name: Set up Python 3.8 if: matrix.whl_local_version - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.8" architecture: ${{ contains(matrix.artifact_name,'x86') && 'x86' || 'x64' }} @@ -220,7 +220,7 @@ jobs: echo "AR_${{ matrix.target }}=llvm-ar" >> "$GITHUB_ENV" - name: Checkout VOICEVOX RESOURCE if: inputs.is_production - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: VOICEVOX/voicevox_resource ref: ${{ env.VOICEVOX_RESOURCE_VERSION }} @@ -316,7 +316,7 @@ jobs: 7z a "../${{ env.ASSET_NAME }}.zip" "${{ env.ASSET_NAME }}" - name: Upload to Release if: fromJson(needs.config.outputs.deploy) && !contains(matrix.target, 'ios') - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: prerelease: true tag_name: ${{ env.VERSION }} @@ -325,7 +325,7 @@ jobs: target_commitish: ${{ github.sha }} - name: Upload Python whl to Release if: fromJson(needs.config.outputs.deploy) && matrix.whl_local_version - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: prerelease: true tag_name: ${{ env.VERSION }} @@ -349,7 +349,7 @@ jobs: IOS_AARCH64_PATH: artifact/voicevox_core-aarch64-apple-ios ASSET_NAME: voicevox_core-ios-xcframework-cpu-${{ needs.config.outputs.version }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: voicevox_core-x86_64-apple-ios @@ -363,10 +363,10 @@ jobs: name: voicevox_core-aarch64-apple-ios path: ${{ env.IOS_AARCH64_PATH }} - name: Create xcframework - id: create-xcframework + id: create-xcframework run: | build_util/make_ios_xcframework.bash - echo "output_asset_path=${OUTPUT_ASSET_PATH}" >> "$GITHUB_OUTPUT" + echo "output_asset_path=${OUTPUT_ASSET_PATH}" >> "$GITHUB_OUTPUT" env: OUTPUT_ASSET_PATH: artifact/voicevox_core-ios-xcframework-cpu - name: Archive artifact @@ -375,7 +375,7 @@ jobs: 7z a "../../${{ env.ASSET_NAME }}.zip" "voicevox_core.xcframework" - name: Upload to Release if: fromJson(needs.config.outputs.deploy) - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: prerelease: true tag_name: ${{ env.VERSION }} @@ -389,10 +389,10 @@ jobs: env: ASSET_NAME: model-${{ needs.config.outputs.version }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Checkout VOICEVOX FAT RESOURCE if: inputs.is_production - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: VOICEVOX/voicevox_fat_resource ref: ${{ env.VOICEVOX_FAT_RESOURCE_VERSION }} @@ -412,7 +412,7 @@ jobs: 7z a "../${{ env.ASSET_NAME }}.zip" "${{ env.ASSET_NAME }}" - name: Upload to Release if: fromJson(needs.config.outputs.deploy) - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: prerelease: true tag_name: ${{ env.VERSION }} @@ -425,7 +425,7 @@ jobs: if: ${{ !(github.event_name != 'release' && github.event_name != 'workflow_dispatch') }} # !env.IS_SIMPLE_TEST と同じ needs: [config, build_and_deploy] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Rust uses: ./.github/actions/rust-toolchain-from-file - name: Set up Java @@ -446,13 +446,13 @@ jobs: cargo set-version "$VERSION" -p voicevox_core_java_api - name: "Download artifact (android-arm64-cpu)" - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: voicevox_core_java_api-android-arm64-cpu path: artifact/android-arm64-cpu - name: "Download artifact (android-x86_64-cpu)" - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: voicevox_core_java_api-android-x86_64-cpu path: artifact/android-x86_64-cpu @@ -486,7 +486,7 @@ jobs: - name: Upload to Release if: fromJson(needs.config.outputs.deploy) - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: prerelease: true tag_name: ${{ env.VERSION }} diff --git a/.github/workflows/build_and_deploy_downloader.yml b/.github/workflows/build_and_deploy_downloader.yml index 9daeadea4..d1b9f4a21 100644 --- a/.github/workflows/build_and_deploy_downloader.yml +++ b/.github/workflows/build_and_deploy_downloader.yml @@ -69,7 +69,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install cross compiler for aarch64-unknown-linux-gnu if: matrix.target == 'aarch64-unknown-linux-gnu' @@ -104,7 +104,7 @@ jobs: - name: Upload to Release if: env.VERSION != '0.0.0' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: prerelease: true tag_name: ${{ env.VERSION }} diff --git a/.github/workflows/cargo-deny.yml b/.github/workflows/cargo-deny.yml index cf600f535..cf2ae2e3d 100644 --- a/.github/workflows/cargo-deny.yml +++ b/.github/workflows/cargo-deny.yml @@ -9,7 +9,7 @@ jobs: cargo-deny: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install cargo-binstall uses: taiki-e/install-action@cargo-binstall - name: Install cargo-deny diff --git a/.github/workflows/download_test.yml b/.github/workflows/download_test.yml index 2a1190e40..9f953534a 100644 --- a/.github/workflows/download_test.yml +++ b/.github/workflows/download_test.yml @@ -184,7 +184,7 @@ jobs: runs-on: ${{ matrix.os }} name: ${{ matrix.name }}-${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Rust uses: ./.github/actions/rust-toolchain-from-file - name: Get prerelease latest version diff --git a/.github/workflows/generate_document.yml b/.github/workflows/generate_document.yml index 37c39a1dd..531e800c3 100644 --- a/.github/workflows/generate_document.yml +++ b/.github/workflows/generate_document.yml @@ -11,13 +11,13 @@ jobs: generate_api_document: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true - name: Set up Rust uses: ./.github/actions/rust-toolchain-from-file - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.8" - name: Setup Java @@ -42,7 +42,7 @@ jobs: - name: cp crates/voicevox_core_c_api/include/voicevox_core.h run: cp crates/voicevox_core_c_api/include/voicevox_core.h docs/apis/c_api/doxygen/ - name: Generate doxygen document - uses: mattnotmitt/doxygen-action@v1.9.4 + uses: mattnotmitt/doxygen-action@v1.9.8 with: working-directory: "docs/apis/c_api/doxygen" - name: Build voicevox_core_python_api @@ -60,7 +60,7 @@ jobs: mkdir -p public/apis/java_api cp -r crates/voicevox_core_java_api/lib/build/docs/javadoc/* public/apis/java_api - name: Uplaod api document - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 if: ${{ github.ref_name == 'main' }} with: path: public @@ -78,4 +78,4 @@ jobs: steps: - name: Deploy to GitHub pages id: api - uses: actions/deploy-pages@v1 + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/java_lint.yml b/.github/workflows/java_lint.yml index ad408e3a7..6b7ff9e46 100644 --- a/.github/workflows/java_lint.yml +++ b/.github/workflows/java_lint.yml @@ -21,7 +21,7 @@ jobs: java-lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: java-version: "11" diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 25d78ec6a..3299f1586 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -10,7 +10,7 @@ jobs: triage: runs-on: ubuntu-latest steps: - - uses: github/issue-labeler@v2.5 + - uses: github/issue-labeler@v3.4 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" configuration-path: .github/labeler.yml diff --git a/.github/workflows/python_lint.yml b/.github/workflows/python_lint.yml index 22449c391..486119e2c 100644 --- a/.github/workflows/python_lint.yml +++ b/.github/workflows/python_lint.yml @@ -21,8 +21,8 @@ jobs: python: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: "3.8" - name: Install Poetry diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d0639d045..cace94823 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: shellcheck: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Update ShellCheck run: | sudo apt-get update @@ -29,7 +29,7 @@ jobs: actionlint: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # ShellCheckとPyflakesをインストールしておくと、shell: bashとshell: pythonのコードを検査してくれるようになる # # 参考: @@ -40,7 +40,7 @@ jobs: sudo apt-get update sudo apt-get install -y shellcheck - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.8" - name: Install Pyflakes @@ -53,7 +53,7 @@ jobs: validate-cargo-lock: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Rust uses: ./.github/actions/rust-toolchain-from-file - name: Validate Cargo.lock @@ -62,13 +62,13 @@ jobs: rust-lint: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Rust uses: ./.github/actions/rust-toolchain-from-file with: components: clippy,rustfmt - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.8" - uses: Swatinem/rust-cache@v2 @@ -79,7 +79,7 @@ jobs: rust-unit-test: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Rust uses: ./.github/actions/rust-toolchain-from-file - uses: Swatinem/rust-cache@v2 @@ -124,9 +124,9 @@ jobs: include: ${{ fromJson(needs.rust-integration-test-strategy-matrix.outputs.includes) }} runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.8" - name: Set up Rust @@ -140,7 +140,7 @@ jobs: c-header: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Rust uses: ./.github/actions/rust-toolchain-from-file - name: Install cargo-binstall @@ -187,7 +187,7 @@ jobs: artifact_name: linux-x64-cpu-cpp-shared runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Rust uses: ./.github/actions/rust-toolchain-from-file - name: Install cargo-binstall @@ -203,7 +203,7 @@ jobs: cp -v target/debug/libonnxruntime.*.dylib example/cpp/unix/voicevox_core/ || true - if: startsWith(matrix.os, 'mac') - uses: jwlawson/actions-setup-cmake@v1.13 + uses: jwlawson/actions-setup-cmake@v2 - name: Install build dependencies if: startsWith(matrix.os, 'ubuntu') run: | @@ -229,7 +229,7 @@ jobs: shell: pwsh steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Rust uses: ./.github/actions/rust-toolchain-from-file - name: Install cargo-binstall @@ -243,7 +243,7 @@ jobs: cp target/debug/voicevox_core.dll.lib example/cpp/windows/simple_tts/lib/x64/voicevox_core.lib - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@v2 - name: Restore NuGet packages working-directory: ${{env.GITHUB_WORKSPACE}} run: nuget restore ${{env.SOLUTION_FILE_PATH}} @@ -266,9 +266,9 @@ jobs: shell: bash working-directory: ./crates/voicevox_core_python_api steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.8" - name: Set up Rust @@ -301,7 +301,7 @@ jobs: - os: ubuntu-latest runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Rust uses: ./.github/actions/rust-toolchain-from-file - name: Set up Java diff --git a/.github/workflows/typos.yml b/.github/workflows/typos.yml index a42e05dcf..f7ac45241 100644 --- a/.github/workflows/typos.yml +++ b/.github/workflows/typos.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: typos-action - uses: crate-ci/typos@v1.12.12 + uses: crate-ci/typos@v1.21.0 diff --git a/_typos.toml b/_typos.toml index b70ddc608..4c3b0f48b 100644 --- a/_typos.toml +++ b/_typos.toml @@ -1,6 +1,11 @@ # Files for typos # Instruction: https://github.com/marketplace/actions/typos-action#getting-started +[default] +extend-ignore-identifiers-re = [ + "\\bPNGs\\b", # https://github.com/crate-ci/typos/issues/745 +] + [default.extend-identifiers] NdArray="NdArray" # onnxruntime::session::NdArray From 7cdea5e9150a33d98163150503eb00244adf627e Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Sat, 15 Jun 2024 23:46:11 +0900 Subject: [PATCH 07/36] =?UTF-8?q?fix:=20`voicevox=5Fuser=5Fdict=5Fadd=5Fwo?= =?UTF-8?q?rd`=E3=81=8C=E3=82=B9=E3=82=BF=E3=83=83=E3=82=AF=E3=82=92?= =?UTF-8?q?=E7=A0=B4=E5=A3=8A=E3=81=99=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20(#800)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/voicevox_core_c_api/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/voicevox_core_c_api/src/lib.rs b/crates/voicevox_core_c_api/src/lib.rs index 08a54b11a..eb2e4698e 100644 --- a/crates/voicevox_core_c_api/src/lib.rs +++ b/crates/voicevox_core_c_api/src/lib.rs @@ -1161,7 +1161,7 @@ pub unsafe extern "C" fn voicevox_user_dict_add_word( into_result_code_with_error((|| { let word = word.read_unaligned().try_into_word()?; let uuid = user_dict.dict.add_word(word)?; - output_word_uuid.as_ptr().copy_from(uuid.as_bytes(), 16); + output_word_uuid.as_ptr().write_unaligned(uuid.into_bytes()); Ok(()) })()) From ca1a982f3d0257cfb9f431abe8f230fcd37abab4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 16 Jun 2024 00:56:03 +0900 Subject: [PATCH 08/36] chore(deps): update dependency rust to v1.79.0 (#799) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 54227249d..b3a8c61e6 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.78.0 +1.79.0 From d3b559cc0dc56b298c4a4e67453c8af9449e935c Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Tue, 25 Jun 2024 04:17:02 +0900 Subject: [PATCH 09/36] =?UTF-8?q?change:=20macOS=2011=E3=81=AE=E3=82=B5?= =?UTF-8?q?=E3=83=9D=E3=83=BC=E3=83=88=E3=82=92=E5=88=87=E3=82=8B=20(#801)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build_and_deploy.yml | 4 ++-- .github/workflows/build_and_deploy_downloader.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index aecd7dca8..8ac31c592 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -123,7 +123,7 @@ jobs: "can_skip_in_simple_test": true }, { - "os": "macos-11", + "os": "macos-12", "features": "", "target": "aarch64-apple-darwin", "artifact_name": "osx-arm64-cpu", @@ -131,7 +131,7 @@ jobs: "can_skip_in_simple_test": false }, { - "os": "macos-11", + "os": "macos-12", "features": "", "target": "x86_64-apple-darwin", "artifact_name": "osx-x64-cpu", diff --git a/.github/workflows/build_and_deploy_downloader.yml b/.github/workflows/build_and_deploy_downloader.yml index d1b9f4a21..20fd4d63a 100644 --- a/.github/workflows/build_and_deploy_downloader.yml +++ b/.github/workflows/build_and_deploy_downloader.yml @@ -60,11 +60,11 @@ jobs: - name: download-osx-x64 target: x86_64-apple-darwin - os: macos-11 + os: macos-12 - name: download-osx-arm64 target: aarch64-apple-darwin - os: macos-11 + os: macos-12 runs-on: ${{ matrix.os }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cace94823..14bfbddd0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -103,8 +103,8 @@ jobs: { "os": "windows-2022", "features": "", "can_skip_in_simple_test": true }, { "os": "windows-2019", "features": "directml", "can_skip_in_simple_test": false }, { "os": "windows-2022", "features": "directml", "can_skip_in_simple_test": true }, - { "os": "macos-11", "features": "", "can_skip_in_simple_test": false }, - { "os": "macos-12", "features": "", "can_skip_in_simple_test": true }, + { "os": "macos-12", "features": "", "can_skip_in_simple_test": false }, + { "os": "macos-13", "features": "", "can_skip_in_simple_test": true }, { "os": "ubuntu-20.04", "features": "", "can_skip_in_simple_test": false }, { "os": "ubuntu-22.04", "features": "", "can_skip_in_simple_test": true } ]' From a728d7eb6dcb975a8fa4f8b1b14ce693d7815213 Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Thu, 4 Jul 2024 16:31:36 +0900 Subject: [PATCH 10/36] =?UTF-8?q?add:=20Rust=20API=E3=81=AEAPI=E3=83=89?= =?UTF-8?q?=E3=82=AD=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=88=E3=82=92=E3=83=87?= =?UTF-8?q?=E3=83=97=E3=83=AD=E3=82=A4=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B=20(#803)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/generate_document.yml | 22 ++++++++++++++++++++-- docs/apis/index.html | 1 + 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generate_document.yml b/.github/workflows/generate_document.yml index 531e800c3..039800df1 100644 --- a/.github/workflows/generate_document.yml +++ b/.github/workflows/generate_document.yml @@ -16,6 +16,20 @@ jobs: submodules: true - name: Set up Rust uses: ./.github/actions/rust-toolchain-from-file + - name: Extract the Rust version used on Docs.rs + id: docsrs-rust-version + run: | + page=$(curl -f https://docs.rs/about/builds) + date=$( + sed -n \ + 's:^.*rustc 1\.[0-9]\+\.[0-9]\+-nightly ([0-9a-f]\{9\} \([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\)).*$:\1:p' \ + <<< "$page" + ) + echo "rust-toolchain=nightly-$date" >> "$GITHUB_OUTPUT" + - name: Set up nightly Rust + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ steps.docsrs-rust-version.outputs.rust-toolchain }} - name: Set up Python uses: actions/setup-python@v5 with: @@ -25,8 +39,8 @@ jobs: with: java-version: "11" distribution: "adopt" - - name: Install cargo-binstall - uses: taiki-e/install-action@cargo-binstall + - name: Install cargo-docs-rs + uses: dtolnay/install@cargo-docs-rs - name: Create a venv uses: ./.github/actions/create-venv - name: Install python dependencies @@ -39,6 +53,10 @@ jobs: run: mkdir -p public/apis/c_api - name: cp docs/apis/index.html run: cp docs/apis/index.html public/apis/ + - name: Generate rustdoc + run: | + cargo +${{ steps.docsrs-rust-version.outputs.rust-toolchain }} docs-rs -p voicevox_core + mv target/x86_64-unknown-linux-gnu/doc public/apis/rust_api - name: cp crates/voicevox_core_c_api/include/voicevox_core.h run: cp crates/voicevox_core_c_api/include/voicevox_core.h docs/apis/c_api/doxygen/ - name: Generate doxygen document diff --git a/docs/apis/index.html b/docs/apis/index.html index 639602f9f..d1f5d09b2 100644 --- a/docs/apis/index.html +++ b/docs/apis/index.html @@ -6,6 +6,7 @@