diff --git a/.github/actions/cargo-binstall-cbindgen/action.yml b/.github/actions/cargo-binstall-cbindgen/action.yml deleted file mode 100644 index 0eb0f8f5d..000000000 --- a/.github/actions/cargo-binstall-cbindgen/action.yml +++ /dev/null @@ -1,23 +0,0 @@ -inputs: - version: - required: true - default: ^0.24 - -runs: - using: composite - steps: - - name: Install cbindgen - shell: bash - run: | - case "$OS" in - Windows) - cargo install cbindgen --version ${{ inputs.version }};; - macOS | Linux ) - cargo binstall \ - cbindgen@${{ inputs.version }} \ - --pkg-url 'https://github.com/alsuren/cargo-quickinstall/releases/download/{ name }-{ version }-{ target }/{ name }-{ version }-{ target }.tar.gz' \ - --pkg-fmt tgz \ - --bin-dir '{ bin }{ binary-ext }' \ - --no-confirm \ - --log-level debug;; - esac diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index 46a506446..5f48330e7 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -151,6 +151,7 @@ jobs: - name: build voicevox_core_c_api run: cargo build -p voicevox_core_c_api -vv --features ${{ matrix.features }}, --target ${{ matrix.target }} --release env: + RUSTFLAGS: -C panic=abort ORT_USE_CUDA: ${{ matrix.use_cuda }} - name: build voicevox_core_python_api if: matrix.whl_local_version diff --git a/.github/workflows/download_test.yml b/.github/workflows/download_test.yml index 16df68606..161c9d6b2 100644 --- a/.github/workflows/download_test.yml +++ b/.github/workflows/download_test.yml @@ -298,7 +298,9 @@ jobs: if: ${{ env.EXPECTED_VOICEVOX_CORE_VERSION == 'latest' }} shell: bash run: | - echo "EXPECTED_VOICEVOX_CORE_VERSION=$(curl -sSfI "https://github.com/VOICEVOX/voicevox_core/releases/latest"| grep "location:" | sed -e "s%location: https://github.com/VOICEVOX/voicevox_core/releases/tag/%%" | sed 's/\r//g')" >> "$GITHUB_ENV" + echo "EXPECTED_VOICEVOX_CORE_VERSION=$(gh release view --repo VOICEVOX/voicevox_core --json 'tagName' --jq '.tagName')" >> "$GITHUB_ENV" + env: + GITHUB_TOKEN: ${{ github.token }} - name: Check downloaded version shell: bash run: | diff --git a/.github/workflows/generate_document.yml b/.github/workflows/generate_document.yml index ad52529e3..05cc825de 100644 --- a/.github/workflows/generate_document.yml +++ b/.github/workflows/generate_document.yml @@ -19,18 +19,16 @@ jobs: python-version: "3.8" - name: Install cargo-binstall uses: taiki-e/install-action@cargo-binstall - - name: Install cbindgen - uses: ./.github/actions/cargo-binstall-cbindgen - name: Create a venv uses: ./.github/actions/create-venv - name: pip install run: pip install -r ./crates/voicevox_core_python_api/requirements.txt - - name: Generate C header file - run: cbindgen --crate voicevox_core_c_api -o ./docs/apis/c_api/doxygen/voicevox_core.h - name: mkdir public run: mkdir -p public/apis/c_api - name: cp docs/apis/index.html run: cp docs/apis/index.html public/apis/ + - 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 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ed26b05c3..4d9c53f04 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -113,8 +113,27 @@ jobs: uses: ./.github/actions/rust-toolchain-from-file - name: Install cargo-binstall uses: taiki-e/install-action@cargo-binstall - - name: Install cbindgen - uses: ./.github/actions/cargo-binstall-cbindgen + - name: Extract the version of cbindgen that xtask depends on + id: cbindgen-version + run: | + metadata=$(cargo metadata --format-version 1) + version=$( + jq -r ' + (.workspace_members[] | select(startswith("xtask "))) as $xtask + | (.resolve.nodes[] | select(.id == $xtask).deps[] | select(.name == "cbindgen").pkg) as $cbindgen + | .packages[] | select(.id == $cbindgen).version + ' <<< "$metadata" + ) + echo "version=$version" >> "$GITHUB_OUTPUT" + - name: Install cbindgen v${{ steps.cbindgen-version.outputs.version }} + run: | + cargo binstall \ + cbindgen@${{ steps.cbindgen-version.outputs.version }} \ + --pkg-url 'https://github.com/alsuren/cargo-quickinstall/releases/download/{ name }-{ version }-{ target }/{ name }-{ version }-{ target }.tar.gz' \ + --pkg-fmt tgz \ + --bin-dir '{ bin }{ binary-ext }' \ + --no-confirm \ + --log-level debug - name: Assert voicevox_core.h is up to date run: | cbindgen --crate voicevox_core_c_api -o /tmp/voicevox_core.h @@ -140,15 +159,12 @@ jobs: uses: ./.github/actions/rust-toolchain-from-file - name: Install cargo-binstall uses: taiki-e/install-action@cargo-binstall - - name: Install cbindgen - uses: ./.github/actions/cargo-binstall-cbindgen - name: build voicevox_core_c_api run: cargo build -p voicevox_core_c_api -vv - - name: voicevox_core.hを生成 - run: cbindgen --crate voicevox_core_c_api -o ./example/cpp/unix/voicevox_core/voicevox_core.h - name: 必要なfileをunix用exampleのディレクトリに移動させる run: | 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 @@ -184,15 +200,12 @@ jobs: uses: ./.github/actions/rust-toolchain-from-file - name: Install cargo-binstall uses: taiki-e/install-action@cargo-binstall - - name: Install cbindgen - uses: ./.github/actions/cargo-binstall-cbindgen - name: build voicevox_core_c_api run: cargo build -p voicevox_core_c_api -vv - - name: voicevox_core.hを生成 - run: cbindgen --crate voicevox_core_c_api -o ./example/cpp/windows/simple_tts/voicevox_core.h - name: 必要なfileをexampleのディレクトリに移動させる run: | mkdir example/cpp/windows/simple_tts/lib/x64 + cp -v crates/voicevox_core_c_api/include/voicevox_core.h example/cpp/windows/simple_tts/ cp target/debug/voicevox_core.dll.lib example/cpp/windows/simple_tts/lib/x64/voicevox_core.lib - name: Add MSBuild to PATH diff --git a/Cargo.lock b/Cargo.lock index e124e4639..d035072b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3945,7 +3945,6 @@ dependencies = [ "duct", "easy-ext", "inventory", - "is-terminal", "libc", "libloading", "libtest-mimic", diff --git a/Cargo.toml b/Cargo.toml index 6dff0d4a4..e5d45dea1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,13 @@ [workspace] members = [ - "crates/download", - "crates/test_util", - "crates/voicevox_core", - "crates/voicevox_core_c_api", - "crates/voicevox_core_python_api", - "crates/xtask", + "crates/download", + "crates/test_util", + "crates/voicevox_core", + "crates/voicevox_core_c_api", + "crates/voicevox_core_python_api", + "crates/xtask" ] -[workspace.package] -version = "0.0.0" -edition = "2021" -publish = false - [workspace.dependencies] anyhow = "1.0.65" async_zip = { version = "0.0.11", features = ["full"] } @@ -21,8 +16,6 @@ const-default = { version = "1.0.0", features = ["derive"] } easy-ext = "1.0.1" fs-err = { version = "2.9.0", features = ["tokio"] } once_cell = "1.15.0" -# FIXME: iOS対応のpull request(https://github.com/wesleywiser/process_path/pull/16)がマージされる見込みが無いため -process_path = { git = "https://github.com/VOICEVOX/process_path.git", rev = "de226a26e8e18edbdb1d6f986afe37bbbf35fbf4" } regex = "1.6.0" serde = { version = "1.0.145", features = ["derive"] } serde_json = { version = "1.0.85", features = ["preserve_order"] } @@ -35,11 +28,20 @@ voicevox_core = { path = "crates/voicevox_core" } tokio = { version = "1.25.0", features = ["rt", "rt-multi-thread", "macros", "sync"] } derive-getters = "0.2.0" +# FIXME: iOS対応のpull request(https://github.com/wesleywiser/process_path/pull/16)がマージされる見込みが無いため +[workspace.dependencies.process_path] +git = "https://github.com/VOICEVOX/process_path.git" +rev = "de226a26e8e18edbdb1d6f986afe37bbbf35fbf4" + +[workspace.package] +version = "0.0.0" +edition = "2021" +publish = false + # min-sized-rustを元にrelease buildのサイズが小さくなるようにした # https://github.com/johnthagen/min-sized-rust [profile.release] opt-level = "z" lto = true codegen-units = 1 -panic = "abort" strip = true diff --git a/README.md b/README.md index e341b2900..2caa76c48 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,7 @@ typos **[VOICEVOX ENGINE SHARP](https://github.com/yamachu/VoicevoxEngineSharp) [@yamachu](https://github.com/yamachu)** ・・・ VOICEVOX ENGINE の C# 実装 **[voicevoxcore4s](https://github.com/windymelt/voicevoxcore4s) [@windymelt](https://github.com/windymelt)** ・・・ VOICEVOX CORE の Scala(JVM) 向け FFI ラッパー **[voicevox_flutter](https://github.com/char5742/voicevox_flutter) [@char5742](https://github.com/char5742)** ・・・ VOICEVOX CORE の Flutter 向け FFI ラッパー +**[voicevoxcore.go](https://github.com/sh1ma/voicevoxcore.go) [@sh1ma](https://github.com/sh1ma)** ・・・ VOICEVOX CORE の Go 言語 向け FFI ラッパー ## ライセンス ソースコードのライセンスは [MIT LICENSE](./LICENSE) です。 diff --git a/crates/voicevox_core/Cargo.toml b/crates/voicevox_core/Cargo.toml index f64c7970f..1a399ff75 100644 --- a/crates/voicevox_core/Cargo.toml +++ b/crates/voicevox_core/Cargo.toml @@ -8,9 +8,9 @@ publish.workspace = true default = [] directml = ["onnxruntime/directml"] - [dependencies] anyhow.workspace = true +async_zip.workspace = true cfg-if = "1.0.0" const-default.workspace = true derive-getters.workspace = true @@ -18,27 +18,32 @@ derive-new = "0.5.9" duplicate = "1.0.0" easy-ext.workspace = true fs-err.workspace = true +futures = "0.3.26" +nanoid = "0.4.0" once_cell.workspace = true -onnxruntime = { git = "https://github.com/VOICEVOX/onnxruntime-rs.git", rev="ebb9dcb9b26ee681889b52b6db3b4f642b04a250" } process_path.workspace = true +regex.workspace = true serde.workspace = true serde_json.workspace = true strum.workspace = true thiserror.workspace = true +tokio.workspace = true tracing.workspace = true -open_jtalk = { git = "https://github.com/VOICEVOX/open_jtalk-rs.git", rev="d766a52bad4ccafe18597e57bd6842f59dca881e" } -regex.workspace = true -async_zip.workspace = true -futures = "0.3.26" -nanoid = "0.4.0" -tokio.workspace = true + +[dependencies.onnxruntime] +git = "https://github.com/VOICEVOX/onnxruntime-rs.git" +rev = "ebb9dcb9b26ee681889b52b6db3b4f642b04a250" + +[dependencies.open_jtalk] +git = "https://github.com/VOICEVOX/open_jtalk-rs.git" +rev = "d766a52bad4ccafe18597e57bd6842f59dca881e" [dev-dependencies] -rstest = "0.15.0" -pretty_assertions = "1.3.0" flate2 = "1.0.24" -tar = "0.4.38" heck = "0.4.0" +pretty_assertions = "1.3.0" +rstest = "0.15.0" +tar = "0.4.38" test_util.workspace = true [target."cfg(windows)".dependencies] diff --git a/crates/voicevox_core/build.rs b/crates/voicevox_core/build.rs deleted file mode 100644 index de616bca1..000000000 --- a/crates/voicevox_core/build.rs +++ /dev/null @@ -1,2 +0,0 @@ -// OUT_DIR環境変数はbuild.rsがないと定義されないらしく内部で参照してるOUT_DIRのために残している -fn main() {} diff --git a/crates/voicevox_core_c_api/Cargo.toml b/crates/voicevox_core_c_api/Cargo.toml index 1f239d9ca..81b65ff58 100644 --- a/crates/voicevox_core_c_api/Cargo.toml +++ b/crates/voicevox_core_c_api/Cargo.toml @@ -5,46 +5,49 @@ edition.workspace = true publish.workspace = true [lib] -name = "voicevox_core" crate-type = ["cdylib"] +name = "voicevox_core" [[test]] -name = "e2e" harness = false +name = "e2e" [features] directml = ["voicevox_core/directml"] [dependencies] -voicevox_core.workspace = true -chrono = { version = "0.4.23", default-features = false, features = ["clock"] } # https://github.com/chronotope/chrono/issues/602 const-default.workspace = true -is-terminal = "0.4.2" +derive-getters.workspace = true libc = "0.2.134" once_cell.workspace = true serde_json.workspace = true thiserror.workspace = true -tracing-subscriber.workspace = true tokio.workspace = true -derive-getters.workspace = true +tracing-subscriber.workspace = true +voicevox_core.workspace = true + +[dependencies.chrono] +version = "0.4.23" +default-features = false +features = ["clock"] [dev-dependencies] -pretty_assertions = "1.3.0" anyhow.workspace = true -rstest = "0.15.0" assert_cmd = { version = "2.0.8", features = ["color-auto"] } clap.workspace = true duct = "0.13.6" easy-ext.workspace = true +inventory = "0.3.4" libloading = "0.7.3" libtest-mimic = "0.6.0" ndarray = "0.15.6" ndarray-stats = "0.5.1" +pretty_assertions = "1.3.0" process_path.workspace = true regex.workspace = true +rstest = "0.15.0" serde.workspace = true strum.workspace = true test_util.workspace = true toml = "0.7.2" typetag = "0.2.5" -inventory = "0.3.4" diff --git a/crates/voicevox_core_c_api/cbindgen.toml b/crates/voicevox_core_c_api/cbindgen.toml index 54bb26e85..d038d038d 100644 --- a/crates/voicevox_core_c_api/cbindgen.toml +++ b/crates/voicevox_core_c_api/cbindgen.toml @@ -5,6 +5,7 @@ language = "C" header = "/// @file" include_guard = "VOICEVOX_CORE_INCLUDE_GUARD" no_includes = true +include_version = true cpp_compat = true after_includes = """ #ifdef __cplusplus @@ -24,8 +25,8 @@ documentation_style = "doxy" prefix = """ #ifdef _WIN32 __declspec(dllimport) -#endif -""" +#endif""" +args = "vertical" [enum] rename_variants = "ScreamingSnakeCase" diff --git a/crates/voicevox_core_c_api/include/voicevox_core.h b/crates/voicevox_core_c_api/include/voicevox_core.h index d53524da6..fb5b53277 100644 --- a/crates/voicevox_core_c_api/include/voicevox_core.h +++ b/crates/voicevox_core_c_api/include/voicevox_core.h @@ -3,6 +3,8 @@ #ifndef VOICEVOX_CORE_INCLUDE_GUARD #define VOICEVOX_CORE_INCLUDE_GUARD +/* Generated with cbindgen:0.24.3 */ + #ifdef __cplusplus #include #else // __cplusplus @@ -230,7 +232,6 @@ extern const struct VoicevoxTtsOptions voicevox_default_tts_options; #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_open_jtalk_rc_new(const char *open_jtalk_dic_dir, struct OpenJtalkRc **out_open_jtalk); @@ -244,7 +245,6 @@ VoicevoxResultCode voicevox_open_jtalk_rc_new(const char *open_jtalk_dic_dir, #ifdef _WIN32 __declspec(dllimport) #endif - void voicevox_open_jtalk_rc_delete(struct OpenJtalkRc *open_jtalk); /** @@ -260,7 +260,6 @@ void voicevox_open_jtalk_rc_delete(struct OpenJtalkRc *open_jtalk); #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_voice_model_new_from_path(const char *path, struct VoicevoxVoiceModel **out_model); @@ -275,7 +274,6 @@ VoicevoxResultCode voicevox_voice_model_new_from_path(const char *path, #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxVoiceModelId voicevox_voice_model_id(const struct VoicevoxVoiceModel *model); /** @@ -289,7 +287,6 @@ VoicevoxVoiceModelId voicevox_voice_model_id(const struct VoicevoxVoiceModel *mo #ifdef _WIN32 __declspec(dllimport) #endif - const char *voicevox_voice_model_get_metas_json(const struct VoicevoxVoiceModel *model); /** @@ -302,7 +299,6 @@ const char *voicevox_voice_model_get_metas_json(const struct VoicevoxVoiceModel #ifdef _WIN32 __declspec(dllimport) #endif - void voicevox_voice_model_delete(struct VoicevoxVoiceModel *model); /** @@ -318,7 +314,6 @@ void voicevox_voice_model_delete(struct VoicevoxVoiceModel *model); #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_synthesizer_new_with_initialize(const struct OpenJtalkRc *open_jtalk, struct VoicevoxInitializeOptions options, struct VoicevoxSynthesizer **out_synthesizer); @@ -333,7 +328,6 @@ VoicevoxResultCode voicevox_synthesizer_new_with_initialize(const struct OpenJta #ifdef _WIN32 __declspec(dllimport) #endif - void voicevox_synthesizer_delete(struct VoicevoxSynthesizer *synthesizer); /** @@ -349,7 +343,6 @@ void voicevox_synthesizer_delete(struct VoicevoxSynthesizer *synthesizer); #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_synthesizer_load_voice_model(struct VoicevoxSynthesizer *synthesizer, const struct VoicevoxVoiceModel *model); @@ -366,7 +359,6 @@ VoicevoxResultCode voicevox_synthesizer_load_voice_model(struct VoicevoxSynthesi #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_synthesizer_unload_voice_model(struct VoicevoxSynthesizer *synthesizer, VoicevoxVoiceModelId model_id); @@ -381,7 +373,6 @@ VoicevoxResultCode voicevox_synthesizer_unload_voice_model(struct VoicevoxSynthe #ifdef _WIN32 __declspec(dllimport) #endif - bool voicevox_synthesizer_is_gpu_mode(const struct VoicevoxSynthesizer *synthesizer); /** @@ -397,7 +388,6 @@ bool voicevox_synthesizer_is_gpu_mode(const struct VoicevoxSynthesizer *synthesi #ifdef _WIN32 __declspec(dllimport) #endif - bool voicevox_is_loaded_voice_model(const struct VoicevoxSynthesizer *synthesizer, VoicevoxVoiceModelId model_id); @@ -412,7 +402,6 @@ bool voicevox_is_loaded_voice_model(const struct VoicevoxSynthesizer *synthesize #ifdef _WIN32 __declspec(dllimport) #endif - const char *voicevox_synthesizer_get_metas_json(const struct VoicevoxSynthesizer *synthesizer); /** @@ -426,7 +415,6 @@ const char *voicevox_synthesizer_get_metas_json(const struct VoicevoxSynthesizer #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_create_supported_devices_json(char **output_supported_devices_json); /** @@ -445,7 +433,6 @@ VoicevoxResultCode voicevox_create_supported_devices_json(char **output_supporte #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_synthesizer_audio_query(const struct VoicevoxSynthesizer *synthesizer, const char *text, VoicevoxStyleId style_id, @@ -466,7 +453,6 @@ VoicevoxResultCode voicevox_synthesizer_audio_query(const struct VoicevoxSynthes #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_synthesizer_create_accent_phrases(const struct VoicevoxSynthesizer *synthesizer, const char *text, VoicevoxStyleId style_id, @@ -487,7 +473,6 @@ VoicevoxResultCode voicevox_synthesizer_create_accent_phrases(const struct Voice #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_synthesizer_replace_mora_data(const struct VoicevoxSynthesizer *synthesizer, const char *accent_phrases_json, VoicevoxStyleId style_id, @@ -507,7 +492,6 @@ VoicevoxResultCode voicevox_synthesizer_replace_mora_data(const struct VoicevoxS #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_synthesizer_replace_phoneme_length(const struct VoicevoxSynthesizer *synthesizer, const char *accent_phrases_json, VoicevoxStyleId style_id, @@ -527,7 +511,6 @@ VoicevoxResultCode voicevox_synthesizer_replace_phoneme_length(const struct Voic #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_synthesizer_replace_mora_pitch(const struct VoicevoxSynthesizer *synthesizer, const char *accent_phrases_json, VoicevoxStyleId style_id, @@ -550,7 +533,6 @@ VoicevoxResultCode voicevox_synthesizer_replace_mora_pitch(const struct Voicevox #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_synthesizer_synthesis(const struct VoicevoxSynthesizer *synthesizer, const char *audio_query_json, VoicevoxStyleId style_id, @@ -575,7 +557,6 @@ VoicevoxResultCode voicevox_synthesizer_synthesis(const struct VoicevoxSynthesiz #ifdef _WIN32 __declspec(dllimport) #endif - VoicevoxResultCode voicevox_synthesizer_tts(const struct VoicevoxSynthesizer *synthesizer, const char *text, VoicevoxStyleId style_id, @@ -593,7 +574,6 @@ VoicevoxResultCode voicevox_synthesizer_tts(const struct VoicevoxSynthesizer *sy #ifdef _WIN32 __declspec(dllimport) #endif - void voicevox_json_free(char *json); /** @@ -606,7 +586,6 @@ void voicevox_json_free(char *json); #ifdef _WIN32 __declspec(dllimport) #endif - void voicevox_wav_free(uint8_t *wav); /** @@ -617,7 +596,6 @@ void voicevox_wav_free(uint8_t *wav); #ifdef _WIN32 __declspec(dllimport) #endif - const char *voicevox_error_result_to_message(VoicevoxResultCode result_code); #ifdef __cplusplus diff --git a/crates/voicevox_core_c_api/src/helpers.rs b/crates/voicevox_core_c_api/src/helpers.rs index cbb7e3fd9..20c5d5142 100644 --- a/crates/voicevox_core_c_api/src/helpers.rs +++ b/crates/voicevox_core_c_api/src/helpers.rs @@ -1,5 +1,5 @@ use std::alloc::Layout; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Debug; use const_default::ConstDefault; @@ -181,25 +181,38 @@ impl ConstDefault for VoicevoxSynthesisOptions { // libcのmallocで追加のアロケーションを行うことなく、`Vec`や`Vec`の内容を直接Cの世界に貸し出す。 /// Rustの世界の`Box<[impl Copy]>`をCの世界に貸し出すため、アドレスとレイアウトを管理するもの。 -pub(crate) struct BufferManager { +/// +/// `Mutex`による内部可変性を持ち、すべての操作は`&self`から行うことができる。 +pub(crate) struct BufferManager(Mutex); + +struct BufferManagerInner { address_to_layout_table: BTreeMap, + owned_str_addrs: BTreeSet, + static_str_addrs: BTreeSet, } impl BufferManager { pub const fn new() -> Self { - Self { + Self(Mutex::new(BufferManagerInner { address_to_layout_table: BTreeMap::new(), - } + owned_str_addrs: BTreeSet::new(), + static_str_addrs: BTreeSet::new(), + })) } - pub fn vec_into_raw(&mut self, vec: Vec) -> (*mut T, usize) { + pub fn vec_into_raw(&self, vec: Vec) -> (*mut T, usize) { + let BufferManagerInner { + address_to_layout_table, + .. + } = &mut *self.0.lock().unwrap(); + let slice = Box::leak(vec.into_boxed_slice()); let layout = Layout::for_value(slice); let len = slice.len(); let ptr = slice.as_mut_ptr(); let addr = ptr as usize; - let not_occupied = self.address_to_layout_table.insert(addr, layout).is_none(); + let not_occupied = address_to_layout_table.insert(addr, layout).is_none(); assert!(not_occupied, "すでに値が入っている状態はおかしい"); @@ -211,9 +224,14 @@ impl BufferManager { /// # Safety /// /// - `buffer_ptr`は`vec_into_raw`で取得したものであること。 - pub unsafe fn dealloc_slice(&mut self, buffer_ptr: *const T) { + pub unsafe fn dealloc_slice(&self, buffer_ptr: *const T) { + let BufferManagerInner { + address_to_layout_table, + .. + } = &mut *self.0.lock().unwrap(); + let addr = buffer_ptr as usize; - let layout = self.address_to_layout_table.remove(&addr).expect( + let layout = address_to_layout_table.remove(&addr).expect( "解放しようとしたポインタはvoicevox_coreの管理下にありません。\ 誤ったポインタであるか、二重解放になっていることが考えられます", ); @@ -227,6 +245,54 @@ impl BufferManager { std::alloc::dealloc(addr as *mut u8, layout); } } + + pub fn c_string_into_raw(&self, s: CString) -> *mut c_char { + let BufferManagerInner { + owned_str_addrs, .. + } = &mut *self.0.lock().unwrap(); + + let ptr = s.into_raw(); + owned_str_addrs.insert(ptr as _); + ptr + } + + /// `c_string_into_raw`でC API利用側に貸し出したポインタに対し、デアロケートする。 + /// + /// # Safety + /// + /// - `ptr`は`c_string_into_raw`で取得したものであること。 + pub unsafe fn dealloc_c_string(&self, ptr: *mut c_char) { + let BufferManagerInner { + owned_str_addrs, + static_str_addrs, + .. + } = &mut *self.0.lock().unwrap(); + + if !owned_str_addrs.remove(&(ptr as _)) { + if static_str_addrs.contains(&(ptr as _)) { + panic!( + "解放しようとしたポインタはvoicevox_core管理下のものですが、\ + voicevox_coreがアンロードされるまで永続する文字列に対するものです。\ + 解放することはできません", + ) + } + panic!( + "解放しようとしたポインタはvoicevox_coreの管理下にありません。\ + 誤ったポインタであるか、二重解放になっていることが考えられます", + ); + } + drop(CString::from_raw(ptr)); + } + + #[allow(dead_code)] // FIXME: これの代わりに、定数全部をコンストラクタに与える形にする + pub fn memorize_static_str(&self, ptr: *const c_char) -> *const c_char { + let BufferManagerInner { + static_str_addrs, .. + } = &mut *self.0.lock().unwrap(); + + static_str_addrs.insert(ptr as _); + ptr + } } #[cfg(test)] @@ -269,11 +335,37 @@ mod tests { #[should_panic( expected = "解放しようとしたポインタはvoicevox_coreの管理下にありません。誤ったポインタであるか、二重解放になっていることが考えられます" )] - fn buffer_manager_denies_unknown_ptr() { - let mut buffer_manager = BufferManager::new(); + fn buffer_manager_denies_unknown_slice_ptr() { + let buffer_manager = BufferManager::new(); unsafe { let x = 42; buffer_manager.dealloc_slice(&x as *const i32); } } + + #[test] + #[should_panic( + expected = "解放しようとしたポインタはvoicevox_coreの管理下にありません。誤ったポインタであるか、二重解放になっていることが考えられます" + )] + fn buffer_manager_denies_unknown_char_ptr() { + let buffer_manager = BufferManager::new(); + unsafe { + let s = CStr::from_bytes_with_nul(b"\0").unwrap().to_owned(); + buffer_manager.dealloc_c_string(s.into_raw()); + } + } + + #[test] + #[should_panic( + expected = "解放しようとしたポインタはvoicevox_core管理下のものですが、voicevox_coreがアンロードされるまで永続する文字列に対するものです。解放することはできません" + )] + fn buffer_manager_denies_known_static_char_ptr() { + let buffer_manager = BufferManager::new(); + unsafe { + buffer_manager.memorize_static_str(STATIC.as_ptr() as _); + buffer_manager.dealloc_c_string(STATIC.as_ptr() as *mut c_char); + } + + static STATIC: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") }; + } } diff --git a/crates/voicevox_core_c_api/src/lib.rs b/crates/voicevox_core_c_api/src/lib.rs index 95acaa784..212b0ae28 100644 --- a/crates/voicevox_core_c_api/src/lib.rs +++ b/crates/voicevox_core_c_api/src/lib.rs @@ -6,12 +6,11 @@ use self::helpers::*; use chrono::SecondsFormat; use const_default::ConstDefault; use derive_getters::Getters; -use is_terminal::IsTerminal; use once_cell::sync::Lazy; use std::env; use std::ffi::{CStr, CString}; use std::fmt; -use std::io::{self, Write}; +use std::io::{self, IsTerminal, Write}; use std::mem::MaybeUninit; use std::os::raw::c_char; use std::sync::{Arc, Mutex, MutexGuard}; @@ -67,7 +66,7 @@ static RUNTIME: Lazy = Lazy::new(|| { }); // C_APIに渡すために,VecやCStringのサイズを記憶しながら生ポインタを得るためのマネージャ -static BUFFER_MANAGER: Mutex = Mutex::new(BufferManager::new()); +static BUFFER_MANAGER: BufferManager = BufferManager::new(); /* * Cの関数として公開するための型や関数を定義するこれらの実装はvoicevox_core/publish.rsに定義してある対応する関数にある @@ -357,7 +356,7 @@ pub extern "C" fn voicevox_create_supported_devices_json( into_result_code_with_error((|| { let supported_devices = CString::new(SupportedDevices::create()?.to_json().to_string()).unwrap(); - output_supported_devices_json.write(supported_devices.into_raw()); + output_supported_devices_json.write(BUFFER_MANAGER.c_string_into_raw(supported_devices)); Ok(()) })()) } @@ -402,7 +401,7 @@ pub unsafe extern "C" fn voicevox_synthesizer_audio_query( ))?; let audio_query = CString::new(audio_query_model_to_json(&audio_query)) .expect("should not contain '\\0'"); - output_audio_query_json.write(audio_query.into_raw()); + output_audio_query_json.write(BUFFER_MANAGER.c_string_into_raw(audio_query)); Ok(()) })()) } @@ -445,7 +444,7 @@ pub unsafe extern "C" fn voicevox_synthesizer_create_accent_phrases( ))?; let accent_phrases = CString::new(accent_phrases_to_json(&accent_phrases)) .expect("should not contain '\\0'"); - output_accent_phrases_json.write(accent_phrases.into_raw()); + output_accent_phrases_json.write(BUFFER_MANAGER.c_string_into_raw(accent_phrases)); Ok(()) })()) } @@ -477,7 +476,7 @@ pub unsafe extern "C" fn voicevox_synthesizer_replace_mora_data( )?; let accent_phrases = CString::new(accent_phrases_to_json(&accent_phrases)) .expect("should not contain '\\0'"); - output_accent_phrases_json.write(accent_phrases.into_raw()); + output_accent_phrases_json.write(BUFFER_MANAGER.c_string_into_raw(accent_phrases)); Ok(()) })()) } @@ -509,7 +508,7 @@ pub unsafe extern "C" fn voicevox_synthesizer_replace_phoneme_length( )?; let accent_phrases = CString::new(accent_phrases_to_json(&accent_phrases)) .expect("should not contain '\\0'"); - output_accent_phrases_json.write(accent_phrases.into_raw()); + output_accent_phrases_json.write(BUFFER_MANAGER.c_string_into_raw(accent_phrases)); Ok(()) })()) } @@ -541,7 +540,7 @@ pub unsafe extern "C" fn voicevox_synthesizer_replace_mora_pitch( )?; let accent_phrases = CString::new(accent_phrases_to_json(&accent_phrases)) .expect("should not contain '\\0'"); - output_accent_phrases_json.write(accent_phrases.into_raw()); + output_accent_phrases_json.write(BUFFER_MANAGER.c_string_into_raw(accent_phrases)); Ok(()) })()) } @@ -589,7 +588,7 @@ pub unsafe extern "C" fn voicevox_synthesizer_synthesis( StyleId::new(style_id), &SynthesisOptions::from(options), ))?; - let (ptr, len) = BUFFER_MANAGER.lock().unwrap().vec_into_raw(wav); + let (ptr, len) = BUFFER_MANAGER.vec_into_raw(wav); output_wav.write(ptr); output_wav_length.write(len); Ok(()) @@ -637,7 +636,7 @@ pub unsafe extern "C" fn voicevox_synthesizer_tts( StyleId::new(style_id), &TtsOptions::from(options), ))?; - let (ptr, size) = BUFFER_MANAGER.lock().unwrap().vec_into_raw(output); + let (ptr, size) = BUFFER_MANAGER.vec_into_raw(output); output_wav.write(ptr); output_wav_length.write(size); Ok(()) @@ -651,7 +650,7 @@ pub unsafe extern "C" fn voicevox_synthesizer_tts( /// @param voicevox_audio_query で確保されたポインタであり、かつ呼び出し側でバッファの変更を行われていないこと #[no_mangle] pub unsafe extern "C" fn voicevox_json_free(json: *mut c_char) { - drop(CString::from_raw(json)); + BUFFER_MANAGER.dealloc_c_string(json); } /// wav データのメモリを解放する @@ -661,7 +660,7 @@ pub unsafe extern "C" fn voicevox_json_free(json: *mut c_char) { /// @param wav voicevox_tts,voicevox_synthesis で確保されたポインタであり、かつ呼び出し側でバッファの変更を行われていないこと #[no_mangle] pub unsafe extern "C" fn voicevox_wav_free(wav: *mut u8) { - BUFFER_MANAGER.lock().unwrap().dealloc_slice(wav); + BUFFER_MANAGER.dealloc_slice(wav); } /// エラー結果をメッセージに変換する diff --git a/crates/voicevox_core_python_api/Cargo.toml b/crates/voicevox_core_python_api/Cargo.toml index f75f19db9..34d317e73 100644 --- a/crates/voicevox_core_python_api/Cargo.toml +++ b/crates/voicevox_core_python_api/Cargo.toml @@ -17,15 +17,15 @@ directml = ["voicevox_core/directml"] [dependencies] easy-ext.workspace = true log = "0.4.17" +once_cell.workspace = true pyo3 = { version = "0.18.0", features = ["abi3-py38", "extension-module"] } +pyo3-asyncio = { version = "0.18.0", features = ["tokio-runtime"] } pyo3-log = "0.8.0" serde.workspace = true serde_json.workspace = true +tokio.workspace = true tracing.workspace = true voicevox_core.workspace = true -pyo3-asyncio = { version = "0.18.0", features = ["tokio-runtime"] } -tokio.workspace = true -once_cell.workspace = true [build-dependencies] anyhow.workspace = true diff --git a/rust-toolchain b/rust-toolchain index 493498565..832e9afb6 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.69.0 +1.70.0