From 9431e00132bad0be0c047641014b43ccb8345a08 Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Sat, 25 Jan 2025 23:04:22 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat!:=20`VoiceModelFile::close`=E5=BE=8C?= =?UTF-8?q?=E3=82=82`id`=E3=81=A8`metas`=E3=81=B8=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=BB=E3=82=B9=E3=82=92=E4=BF=9D=E8=A8=BC=20(#937)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Rust APIでは、`VoiceModelFile::close`の返り値を`()`から `(VoiceModelId, Vec)`に 2. C APIでは、 `voicevox_voice_model_file_close`を `voicevox_voice_model_file_delete`に改名 3. Python APIとJava APIでは、`__(a)exit__`後も`id`と`metas`にアクセス可 能であることをドキュメント上で約束 BREAKING-CHANGE: 上記の1.と2.。 --- crates/voicevox_core/src/voice_model.rs | 12 ++++++++++-- crates/voicevox_core_c_api/include/voicevox_core.h | 6 +++--- crates/voicevox_core_c_api/src/lib.rs | 6 +++--- .../e2e/testcases/double_delete_voice_model_file.rs | 6 +++--- .../tests/e2e/testcases/simple_tts.rs | 2 +- .../tests/e2e/testcases/tts_via_audio_query.rs | 2 +- .../tests/e2e/testcases/user_dict_load.rs | 2 +- .../voicevoxcore/blocking/VoiceModelFile.java | 12 ++++++++++-- .../python/voicevox_core/_rust/asyncio.pyi | 8 +++++++- .../python/voicevox_core/_rust/blocking.pyi | 8 +++++++- docs/guide/dev/api-design.md | 1 + example/cpp/unix/simple_tts.cpp | 2 +- example/cpp/windows/simple_tts/simple_tts.cpp | 2 +- 13 files changed, 49 insertions(+), 20 deletions(-) diff --git a/crates/voicevox_core/src/voice_model.rs b/crates/voicevox_core/src/voice_model.rs index a52b266da..9a1a24169 100644 --- a/crates/voicevox_core/src/voice_model.rs +++ b/crates/voicevox_core/src/voice_model.rs @@ -600,6 +600,12 @@ pub(crate) mod blocking { Inner::open(path).block_on().map(Self) } + /// VVMファイルを閉じる。 + pub fn close(self) -> (VoiceModelId, VoiceModelMeta) { + let heads = self.0.into_heads(); + (*heads.header.manifest.id(), heads.header.metas.clone()) + } + pub(crate) fn inner(&self) -> &Inner { &self.0 } @@ -642,8 +648,10 @@ pub(crate) mod nonblocking { } /// VVMファイルを閉じる。 - pub async fn close(self) { - self.0.into_heads().zip.into_inner().close().await; + pub async fn close(self) -> (VoiceModelId, VoiceModelMeta) { + let heads = self.0.into_heads(); + heads.zip.into_inner().close().await; + (*heads.header.manifest.id(), heads.header.metas.clone()) } pub(crate) fn inner(&self) -> &Inner { diff --git a/crates/voicevox_core_c_api/include/voicevox_core.h b/crates/voicevox_core_c_api/include/voicevox_core.h index 979712565..0644d4dba 100644 --- a/crates/voicevox_core_c_api/include/voicevox_core.h +++ b/crates/voicevox_core_c_api/include/voicevox_core.h @@ -298,7 +298,7 @@ typedef struct VoicevoxUserDict VoicevoxUserDict; * 音声モデルファイル。 * * VVMファイルと対応する。 - * 構築(_construction_)は ::voicevox_voice_model_file_open で行い、破棄(_destruction_)は ::voicevox_voice_model_file_close で行う。 + * 構築(_construction_)は ::voicevox_voice_model_file_open で行い、破棄(_destruction_)は ::voicevox_voice_model_file_delete で行う。 */ typedef struct VoicevoxVoiceModelFile VoicevoxVoiceModelFile; @@ -636,7 +636,7 @@ __declspec(dllimport) char *voicevox_voice_model_file_create_metas_json(const struct VoicevoxVoiceModelFile *model); /** - * ::VoicevoxVoiceModelFile を、所有しているファイルディスクリプタを閉じた上で破棄(_destruct_)する。 + * ::VoicevoxVoiceModelFile を、所有しているファイルディスクリプタを閉じた上で破棄(_destruct_)する。ファイルの削除(_delete_)ではない。 * * 破棄対象への他スレッドでのアクセスが存在する場合、それらがすべて終わるのを待ってから破棄する。 * @@ -647,7 +647,7 @@ char *voicevox_voice_model_file_create_metas_json(const struct VoicevoxVoiceMode #ifdef _WIN32 __declspec(dllimport) #endif -void voicevox_voice_model_file_close(struct VoicevoxVoiceModelFile *model); +void voicevox_voice_model_file_delete(struct VoicevoxVoiceModelFile *model); /** * ::VoicevoxSynthesizer を構築(_construct_)する。 diff --git a/crates/voicevox_core_c_api/src/lib.rs b/crates/voicevox_core_c_api/src/lib.rs index ffdff9e8d..e684d9a27 100644 --- a/crates/voicevox_core_c_api/src/lib.rs +++ b/crates/voicevox_core_c_api/src/lib.rs @@ -416,7 +416,7 @@ pub extern "C" fn voicevox_get_version() -> *const c_char { /// 音声モデルファイル。 /// /// VVMファイルと対応する。 -/// 構築(_construction_)は ::voicevox_voice_model_file_open で行い、破棄(_destruction_)は ::voicevox_voice_model_file_close で行う。 +/// 構築(_construction_)は ::voicevox_voice_model_file_open で行い、破棄(_destruction_)は ::voicevox_voice_model_file_delete で行う。 #[derive(Debug, Educe)] #[educe(Default(expression = "Self { _padding: MaybeUninit::uninit() }"))] pub struct VoicevoxVoiceModelFile { @@ -497,7 +497,7 @@ pub extern "C" fn voicevox_voice_model_file_create_metas_json( // TODO: cbindgenが`#[unsafe(no_mangle)]`に対応したら`#[no_mangle]`を置き換える // SAFETY: voicevox_core_c_apiを構成するライブラリの中に、これと同名のシンボルは存在しない -/// ::VoicevoxVoiceModelFile を、所有しているファイルディスクリプタを閉じた上で破棄(_destruct_)する。 +/// ::VoicevoxVoiceModelFile を、所有しているファイルディスクリプタを閉じた上で破棄(_destruct_)する。ファイルの削除(_delete_)ではない。 /// /// 破棄対象への他スレッドでのアクセスが存在する場合、それらがすべて終わるのを待ってから破棄する。 /// @@ -505,7 +505,7 @@ pub extern "C" fn voicevox_voice_model_file_create_metas_json( /// /// @param [in] model 破棄対象 #[no_mangle] -pub extern "C" fn voicevox_voice_model_file_close(model: *mut VoicevoxVoiceModelFile) { +pub extern "C" fn voicevox_voice_model_file_delete(model: *mut VoicevoxVoiceModelFile) { init_logger_once(); model.drop_body(); } diff --git a/crates/voicevox_core_c_api/tests/e2e/testcases/double_delete_voice_model_file.rs b/crates/voicevox_core_c_api/tests/e2e/testcases/double_delete_voice_model_file.rs index 1ea6bdc19..fd7b40c7b 100644 --- a/crates/voicevox_core_c_api/tests/e2e/testcases/double_delete_voice_model_file.rs +++ b/crates/voicevox_core_c_api/tests/e2e/testcases/double_delete_voice_model_file.rs @@ -1,4 +1,4 @@ -//! `voicevox_voice_model_file_close`を二度呼ぶとクラッシュすることを確認する。 +//! `voicevox_voice_model_file_delete`を二度呼ぶとクラッシュすることを確認する。 use std::{mem::MaybeUninit, sync::LazyLock}; @@ -32,8 +32,8 @@ impl assert_cdylib::TestCase for TestCase { model.assume_init() }; - lib.voicevox_voice_model_file_close(model); - lib.voicevox_voice_model_file_close(model); + lib.voicevox_voice_model_file_delete(model); + lib.voicevox_voice_model_file_delete(model); unreachable!(); fn assert_ok(result_code: VoicevoxResultCode) { diff --git a/crates/voicevox_core_c_api/tests/e2e/testcases/simple_tts.rs b/crates/voicevox_core_c_api/tests/e2e/testcases/simple_tts.rs index 28e734ea4..750bf4d23 100644 --- a/crates/voicevox_core_c_api/tests/e2e/testcases/simple_tts.rs +++ b/crates/voicevox_core_c_api/tests/e2e/testcases/simple_tts.rs @@ -111,7 +111,7 @@ impl assert_cdylib::TestCase for TestCase { std::assert_eq!(SNAPSHOTS.output[&self.text].wav_length, wav_length); - lib.voicevox_voice_model_file_close(model); + lib.voicevox_voice_model_file_delete(model); lib.voicevox_open_jtalk_rc_delete(openjtalk); lib.voicevox_synthesizer_delete(synthesizer); lib.voicevox_wav_free(wav); diff --git a/crates/voicevox_core_c_api/tests/e2e/testcases/tts_via_audio_query.rs b/crates/voicevox_core_c_api/tests/e2e/testcases/tts_via_audio_query.rs index 32fabe3f4..f9b71308c 100644 --- a/crates/voicevox_core_c_api/tests/e2e/testcases/tts_via_audio_query.rs +++ b/crates/voicevox_core_c_api/tests/e2e/testcases/tts_via_audio_query.rs @@ -122,7 +122,7 @@ impl assert_cdylib::TestCase for TestCase { std::assert_eq!(SNAPSHOTS.output[&self.text].wav_length, wav_length); - lib.voicevox_voice_model_file_close(model); + lib.voicevox_voice_model_file_delete(model); lib.voicevox_open_jtalk_rc_delete(openjtalk); lib.voicevox_synthesizer_delete(synthesizer); lib.voicevox_json_free(audio_query); diff --git a/crates/voicevox_core_c_api/tests/e2e/testcases/user_dict_load.rs b/crates/voicevox_core_c_api/tests/e2e/testcases/user_dict_load.rs index a6e234eaa..3051369ed 100644 --- a/crates/voicevox_core_c_api/tests/e2e/testcases/user_dict_load.rs +++ b/crates/voicevox_core_c_api/tests/e2e/testcases/user_dict_load.rs @@ -136,7 +136,7 @@ impl assert_cdylib::TestCase for TestCase { audio_query_with_dict.get("kana") ); - lib.voicevox_voice_model_file_close(model); + lib.voicevox_voice_model_file_delete(model); lib.voicevox_open_jtalk_rc_delete(openjtalk); lib.voicevox_synthesizer_delete(synthesizer); lib.voicevox_user_dict_delete(dict); diff --git a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/blocking/VoiceModelFile.java b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/blocking/VoiceModelFile.java index 4c1c265b2..15410f7d6 100644 --- a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/blocking/VoiceModelFile.java +++ b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/blocking/VoiceModelFile.java @@ -15,10 +15,18 @@ public class VoiceModelFile implements Closeable { private long handle; - /** ID。 */ + /** + * ID。 + * + *

{@link #close}の後でも利用可能。 + */ @Nonnull public final UUID id; - /** メタ情報。 */ + /** + * メタ情報。 + * + *

{@link #close}の後でも利用可能。 + */ @Nonnull public final SpeakerMeta[] metas; public VoiceModelFile(String modelPath) { 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 bdee4e1ee..7ae067119 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 @@ -39,7 +39,11 @@ class VoiceModelFile: ... @property def id(self) -> VoiceModelId: - """ID。""" + """ + ID。 + + :attr:`close` および :attr:`__aexit__` の後でも利用可能。 + """ ... @property def metas(self) -> list[SpeakerMeta]: @@ -47,6 +51,8 @@ class VoiceModelFile: メタ情報。 この中身を書き換えても、 ``VoiceModelFile`` としての動作には影響しない。 + + :attr:`close` および :attr:`__aexit__` の後でも利用可能。 """ ... async def __aenter__(self) -> "VoiceModelFile": ... 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 58851e40a..d2719e7d1 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 @@ -39,7 +39,11 @@ class VoiceModelFile: ... @property def id(self) -> VoiceModelId: - """ID。""" + """ + ID。 + + :attr:`close` および :attr:`__exit__` の後でも利用可能。 + """ ... @property def metas(self) -> list[SpeakerMeta]: @@ -47,6 +51,8 @@ class VoiceModelFile: メタ情報。 この中身を書き換えても、 ``VoiceModelFile`` としての動作には影響しない。 + + :attr:`close` および :attr:`__exit__` の後でも利用可能。 """ ... def __enter__(self) -> "VoiceModelFile": ... diff --git a/docs/guide/dev/api-design.md b/docs/guide/dev/api-design.md index 6ea140a53..c3da8b588 100644 --- a/docs/guide/dev/api-design.md +++ b/docs/guide/dev/api-design.md @@ -11,6 +11,7 @@ VOICEVOX CORE の主要機能は Rust で実装されることを前提として * [`StyleId`](https://voicevox.github.io/voicevox_core/apis/rust_api/voicevox_core/struct.StyleId.html)といった[newtype](https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html)は、そのままnewtypeとして表現するべきです。 * 例えばPythonなら[`typing.NewType`](https://docs.python.org/ja/3/library/typing.html#newtype)で表現します。 * オプショナルな引数は、キーワード引数がある言語であればキーワード引数で、ビルダースタイルが一般的な言語であればビルダースタイルで表現すべきです。 +* [`VoiceModelFile`](https://voicevox.github.io/voicevox_core/apis/rust_api/voicevox_core/nonblocking/struct.VoiceModelFile.html)の"close"後でも`id`と`metas`は利用可能であるべきです。ただしRustにおける"close"だけは、`VoiceModelFile`を`id`と`metas`に分解するような形にします。 * `Synthesizer::render`は`range: std::ops::Range`を引数に取っています。`Range`にあたる型が標準で存在し、かつそれが配列の範囲指定として用いられるようなものであれば、それを使うべきです。 * ただし例えばPythonでは、`slice`を引数に取るのは慣習にそぐわないため`start: int, stop: int`のようにすべきです。 * もし`Range`にあたる型が標準で無く、かつRustの"start"/"end"やPythonの"start"/"stop"にあたる明確な言葉が無いのであれば、誤解が生じないよう"end_exclusive"のように命名するべきです。 diff --git a/example/cpp/unix/simple_tts.cpp b/example/cpp/unix/simple_tts.cpp index 210df1549..35b3352c8 100644 --- a/example/cpp/unix/simple_tts.cpp +++ b/example/cpp/unix/simple_tts.cpp @@ -58,7 +58,7 @@ int main(int argc, char *argv[]) { std::cerr << voicevox_error_result_to_message(result) << std::endl; return 0; } - voicevox_voice_model_file_close(model); + voicevox_voice_model_file_delete(model); } std::cout << "音声生成中..." << std::endl; diff --git a/example/cpp/windows/simple_tts/simple_tts.cpp b/example/cpp/windows/simple_tts/simple_tts.cpp index 2bdc947c6..c47889dd4 100644 --- a/example/cpp/windows/simple_tts/simple_tts.cpp +++ b/example/cpp/windows/simple_tts/simple_tts.cpp @@ -70,7 +70,7 @@ int main() { OutErrorMessage(result); return 0; } - voicevox_voice_model_file_close(model); + voicevox_voice_model_file_delete(model); } std::wcout << L"音声生成中" << std::endl; From 20e62c1643a328d94206ff6bf7e07f4b5da338e6 Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Sat, 25 Jan 2025 23:08:52 +0900 Subject: [PATCH 2/2] feat!: [downloader] rename `core` to `c-api` (#942) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING-CHANGE: `core` → `c-api`。 BREAKING-CHANGE: `-v, --version` → `--c-api-version`。 BREAKING-CHANGE: `--core-repo` → `--c-api-repo`。 --- .github/workflows/download_test.yml | 2 +- crates/downloader/src/main.rs | 54 ++++++++++++++--------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/download_test.yml b/.github/workflows/download_test.yml index 3ff8f71dd..ede84e59d 100644 --- a/.github/workflows/download_test.yml +++ b/.github/workflows/download_test.yml @@ -198,7 +198,7 @@ jobs: ) echo "VERSION=$VERSION" >> "$GITHUB_ENV" - name: Execute download command - run: ${{ matrix.download_command }} --version ${{ env.VERSION }} --core-repo ${{ github.repository }} + run: ${{ matrix.download_command }} --c-api-version ${{ env.VERSION }} --c-api-repo ${{ github.repository }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Check downloaded version diff --git a/crates/downloader/src/main.rs b/crates/downloader/src/main.rs index 0e26705ef..d09494aa0 100644 --- a/crates/downloader/src/main.rs +++ b/crates/downloader/src/main.rs @@ -45,8 +45,8 @@ const DEFAULT_OUTPUT: &str = if cfg!(windows) { "./voicevox_core" }; -const LIB_NAME: &str = "voicevox_core"; -const DEFAULT_CORE_REPO: &str = "VOICEVOX/voicevox_core"; +const C_API_LIB_NAME: &str = "voicevox_core"; +const DEFAULT_C_API_REPO: &str = "VOICEVOX/voicevox_core"; const DEFAULT_ONNXRUNTIME_BUILDER_REPO: &str = "VOICEVOX/onnxruntime-builder"; const DEFAULT_ADDITIONAL_LIBRARIES_REPO: &str = "VOICEVOX/voicevox_additional_libraries"; const DEFAULT_MODELS_REPO: &str = "VOICEVOX/voicevox_vvm"; @@ -90,7 +90,7 @@ struct Args { #[arg(long, num_args(1..), value_name("TARGET"), conflicts_with("min"))] exclude: Vec, - /// `--only core`のエイリアス + /// `--only c-api`のエイリアス #[arg(long, conflicts_with("additional_libraries_version"))] min: bool, @@ -98,9 +98,9 @@ struct Args { #[arg(short, long, value_name("DIRECTORY"), default_value(DEFAULT_OUTPUT))] output: PathBuf, - /// ダウンロードするvoicevox_coreのバージョンの指定 - #[arg(short, long, value_name("GIT_TAG_OR_LATEST"), default_value("latest"))] - version: String, + /// ダウンロードするVOICEVOX CORE C APIのバージョンの指定 + #[arg(long, value_name("GIT_TAG_OR_LATEST"), default_value("latest"))] + c_api_version: String, /// ダウンロードするONNX Runtimeのバージョンの指定 #[arg(long, value_name("GIT_TAG_OR_LATEST"), default_value("latest"))] @@ -122,8 +122,8 @@ struct Args { #[arg(value_enum, long, default_value(Os::default_opt().map(<&str>::from)))] os: Os, - #[arg(long, value_name("REPOSITORY"), default_value(DEFAULT_CORE_REPO))] - core_repo: RepoName, + #[arg(long, value_name("REPOSITORY"), default_value(DEFAULT_C_API_REPO))] + c_api_repo: RepoName, #[arg( long, @@ -145,7 +145,7 @@ struct Args { #[derive(ValueEnum, Clone, Copy, PartialEq, Eq, Hash)] enum DownloadTarget { - Core, + CApi, Onnxruntime, AdditionalLibraries, Models, @@ -217,13 +217,13 @@ async fn main() -> anyhow::Result<()> { exclude, min, output, - version, + c_api_version, onnxruntime_version, additional_libraries_version, devices, cpu_arch, os, - core_repo, + c_api_repo, onnxruntime_builder_repo, models_repo, additional_libraries_repo, @@ -241,21 +241,21 @@ async fn main() -> anyhow::Result<()> { .filter(|t| !exclude.contains(t)) .collect() } else if min { - [DownloadTarget::Core].into() + [DownloadTarget::CApi].into() } else { DownloadTarget::value_variants().iter().copied().collect() }; - if !targets.contains(&DownloadTarget::Core) { - if version != "latest" { + if !targets.contains(&DownloadTarget::CApi) { + if c_api_version != "latest" { warn!( - "`--version={version}`が指定されていますが、`core`はダウンロード対象から\ + "`--c-api-version={c_api_version}`が指定されていますが、`c-api`はダウンロード対象から\ 除外されています", ); } - if core_repo.to_string() != DEFAULT_CORE_REPO { + if c_api_repo.to_string() != DEFAULT_C_API_REPO { warn!( - "`--core-repo={core_repo}`が指定されていますが、`core`はダウンロード対象\ + "`--c-api-repo={c_api_repo}`が指定されていますが、`c-api`はダウンロード対象\ から除外されています", ); } @@ -283,9 +283,9 @@ async fn main() -> anyhow::Result<()> { let octocrab = &octocrab()?; - let core = OptionFuture::from(targets.contains(&DownloadTarget::Core).then(|| { - find_gh_asset(octocrab, &core_repo, &version, |tag, _| { - Ok(format!("{LIB_NAME}-{os}-{cpu_arch}-{tag}.zip")) + let c_api = OptionFuture::from(targets.contains(&DownloadTarget::CApi).then(|| { + find_gh_asset(octocrab, &c_api_repo, &c_api_version, |tag, _| { + Ok(format!("{C_API_LIB_NAME}-{os}-{cpu_arch}-{tag}.zip")) }) })) .await @@ -344,8 +344,8 @@ async fn main() -> anyhow::Result<()> { "ダウンロードデバイスタイプ: {}", devices.iter().format(", "), ); - if let Some(GhAsset { tag, .. }) = &core { - info!("ダウンロード{LIB_NAME}バージョン: {tag}"); + if let Some(GhAsset { tag, .. }) = &c_api { + info!("ダウンロード{C_API_LIB_NAME}バージョン: {tag}"); } if let Some(GhAsset { tag, .. }) = &onnxruntime { info!("ダウンロードONNX Runtimeバージョン: {tag}"); @@ -367,9 +367,9 @@ async fn main() -> anyhow::Result<()> { let mut tasks = JoinSet::new(); - if let Some(core) = core { + if let Some(c_api) = c_api { tasks.spawn(download_and_extract_from_gh( - core, + c_api, Stripping::FirstDir, &output, &progresses, @@ -1045,9 +1045,9 @@ mod tests { use super::Args; #[rstest] - #[case(&["", "--only", "core", "--exclude", "models"])] - #[case(&["", "--min", "--only", "core"])] - #[case(&["", "--min", "--exclude", "core"])] + #[case(&["", "--only", "c-api", "--exclude", "models"])] + #[case(&["", "--min", "--only", "c-api"])] + #[case(&["", "--min", "--exclude", "c-api"])] fn it_denies_conflicting_options(#[case] args: &[&str]) { let result = Args::try_parse_from(args).map(|_| ()).map_err(|e| e.kind()); assert_eq!(Err(clap::error::ErrorKind::ArgumentConflict), result);