From d915f193559c1a58a3765428757a85028380f04c Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Fri, 4 Oct 2024 22:59:02 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20Rust=20API=E3=81=AE`Synthesizer`?= =?UTF-8?q?=E3=81=AE=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=A8=82=E6=AD=A3=20(#847)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rust APIの`Synthesizer`について、以下のFIXMEを解消する。 ``` pub(crate) mod blocking { // FIXME: ここのdocのコードブロックはasync版のものなので、`nonblocking`モジュールの方に移した上で、 // (ブロッキング版をpublic APIにするならの話ではあるが)ブロッキング版はブロッキング版でコード例 // を用意する ``` --- .../src/__internal/doctest_fixtures.rs | 2 + crates/voicevox_core/src/synthesizer.rs | 222 +++++++++++++++--- 2 files changed, 191 insertions(+), 33 deletions(-) diff --git a/crates/voicevox_core/src/__internal/doctest_fixtures.rs b/crates/voicevox_core/src/__internal/doctest_fixtures.rs index 57c28c7a9..9f4f91d35 100644 --- a/crates/voicevox_core/src/__internal/doctest_fixtures.rs +++ b/crates/voicevox_core/src/__internal/doctest_fixtures.rs @@ -4,6 +4,8 @@ use camino::Utf8Path; use crate::{AccelerationMode, InitializeOptions}; +pub use crate::synthesizer::nonblocking::IntoBlocking; + pub async fn synthesizer_with_sample_voice_model( voice_model_path: impl AsRef, #[cfg_attr(feature = "link-onnxruntime", allow(unused_variables))] onnxruntime_dylib_path: impl Into< diff --git a/crates/voicevox_core/src/synthesizer.rs b/crates/voicevox_core/src/synthesizer.rs index ee960cd1a..c99352a7a 100644 --- a/crates/voicevox_core/src/synthesizer.rs +++ b/crates/voicevox_core/src/synthesizer.rs @@ -80,10 +80,6 @@ pub struct InitializeOptions { } pub(crate) mod blocking { - // FIXME: ここのdocのコードブロックはasync版のものなので、`nonblocking`モジュールの方に移した上で、 - // (ブロッキング版をpublic APIにするならの話ではあるが)ブロッキング版はブロッキング版でコード例 - // を用意する - use std::io::{Cursor, Write as _}; use enum_map::enum_map; @@ -126,8 +122,7 @@ pub(crate) mod blocking { /// #[cfg_attr(feature = "load-onnxruntime", doc = "```")] #[cfg_attr(not(feature = "load-onnxruntime"), doc = "```compile_fail")] - /// # #[pollster::main] - /// # async fn main() -> anyhow::Result<()> { + /// # fn main() -> anyhow::Result<()> { /// # use test_util::{ONNXRUNTIME_DYLIB_PATH, OPEN_JTALK_DIC_DIR}; /// # /// # const ACCELERATION_MODE: AccelerationMode = AccelerationMode::Cpu; @@ -135,7 +130,7 @@ pub(crate) mod blocking { /// use std::sync::Arc; /// /// use voicevox_core::{ - /// nonblocking::{Onnxruntime, OpenJtalk, Synthesizer}, + /// blocking::{Onnxruntime, OpenJtalk, Synthesizer}, /// AccelerationMode, InitializeOptions, /// }; /// @@ -146,8 +141,8 @@ pub(crate) mod blocking { /// # .exec()?; /// # } /// let mut syntesizer = Synthesizer::new( - /// Onnxruntime::load_once().exec().await?, - /// Arc::new(OpenJtalk::new(OPEN_JTALK_DIC_DIR).await.unwrap()), + /// Onnxruntime::load_once().exec()?, + /// Arc::new(OpenJtalk::new(OPEN_JTALK_DIC_DIR).unwrap()), /// &InitializeOptions { /// acceleration_mode: ACCELERATION_MODE, /// ..Default::default() @@ -479,21 +474,23 @@ pub(crate) mod blocking { /// # Example /// /// ``` - /// # #[pollster::main] - /// # async fn main() -> anyhow::Result<()> { + /// # fn main() -> anyhow::Result<()> { + /// # use pollster::FutureExt as _; + /// # use voicevox_core::__internal::doctest_fixtures::IntoBlocking as _; + /// # /// # let synthesizer = /// # voicevox_core::__internal::doctest_fixtures::synthesizer_with_sample_voice_model( /// # test_util::SAMPLE_VOICE_MODEL_FILE_PATH, /// # test_util::ONNXRUNTIME_DYLIB_PATH, /// # test_util::OPEN_JTALK_DIC_DIR, /// # ) - /// # .await?; + /// # .block_on()? + /// # .into_blocking(); /// # /// use voicevox_core::StyleId; /// /// let accent_phrases = synthesizer - /// .create_accent_phrases_from_kana("コンニチワ'", StyleId::new(302)) - /// .await?; + /// .create_accent_phrases_from_kana("コンニチワ'", StyleId::new(302))?; /// # /// # Ok(()) /// # } @@ -698,21 +695,22 @@ pub(crate) mod blocking { /// # Example /// /// ``` - /// # #[pollster::main] - /// # async fn main() -> anyhow::Result<()> { + /// # fn main() -> anyhow::Result<()> { + /// # use pollster::FutureExt as _; + /// # use voicevox_core::__internal::doctest_fixtures::IntoBlocking as _; + /// # /// # let synthesizer = /// # voicevox_core::__internal::doctest_fixtures::synthesizer_with_sample_voice_model( /// # test_util::SAMPLE_VOICE_MODEL_FILE_PATH, /// # test_util::ONNXRUNTIME_DYLIB_PATH, /// # test_util::OPEN_JTALK_DIC_DIR, /// # ) - /// # .await?; + /// # .block_on()? + /// # .into_blocking(); /// # /// use voicevox_core::StyleId; /// - /// let audio_query = synthesizer - /// .audio_query_from_kana("コンニチワ'", StyleId::new(302)) - /// .await?; + /// let audio_query = synthesizer.audio_query_from_kana("コンニチワ'", StyleId::new(302))?; /// # /// # Ok(()) /// # } @@ -742,21 +740,22 @@ pub(crate) mod blocking { /// # Example /// /// ``` - /// # #[pollster::main] - /// # async fn main() -> anyhow::Result<()> { + /// # fn main() -> anyhow::Result<()> { + /// # use pollster::FutureExt as _; + /// # use voicevox_core::__internal::doctest_fixtures::IntoBlocking as _; + /// # /// # let synthesizer = /// # voicevox_core::__internal::doctest_fixtures::synthesizer_with_sample_voice_model( /// # test_util::SAMPLE_VOICE_MODEL_FILE_PATH, /// # test_util::ONNXRUNTIME_DYLIB_PATH, /// # test_util::OPEN_JTALK_DIC_DIR, /// # ) - /// # .await?; + /// # .block_on()? + /// # .into_blocking(); /// # /// use voicevox_core::StyleId; /// - /// let accent_phrases = synthesizer - /// .create_accent_phrases("こんにちは", StyleId::new(302)) - /// .await?; + /// let accent_phrases = synthesizer.create_accent_phrases("こんにちは", StyleId::new(302))?; /// # /// # Ok(()) /// # } @@ -775,21 +774,22 @@ pub(crate) mod blocking { /// # Examples /// /// ``` - /// # #[pollster::main] - /// # async fn main() -> anyhow::Result<()> { + /// # fn main() -> anyhow::Result<()> { + /// # use pollster::FutureExt as _; + /// # use voicevox_core::__internal::doctest_fixtures::IntoBlocking as _; + /// # /// # let synthesizer = /// # voicevox_core::__internal::doctest_fixtures::synthesizer_with_sample_voice_model( /// # test_util::SAMPLE_VOICE_MODEL_FILE_PATH, /// # test_util::ONNXRUNTIME_DYLIB_PATH, /// # test_util::OPEN_JTALK_DIC_DIR, /// # ) - /// # .await?; + /// # .block_on()? + /// # .into_blocking(); /// # /// use voicevox_core::StyleId; /// - /// let audio_query = synthesizer - /// .audio_query("こんにちは", StyleId::new(302)) - /// .await?; + /// let audio_query = synthesizer.audio_query("こんにちは", StyleId::new(302))?; /// # /// # Ok(()) /// # } @@ -1144,6 +1144,8 @@ pub(crate) mod blocking { pub(crate) mod nonblocking { use std::sync::Arc; + use easy_ext::ext; + use crate::{ AccentPhrase, AudioQuery, FullcontextExtractor, Result, StyleId, SynthesisOptions, VoiceModelId, VoiceModelMeta, @@ -1162,8 +1164,44 @@ pub(crate) mod nonblocking { #[derive(Clone)] pub struct Synthesizer(pub(super) Arc>); - // FIXME: docを書く impl self::Synthesizer { + /// `Synthesizer`をコンストラクトする。 + /// + /// # Example + /// + #[cfg_attr(feature = "load-onnxruntime", doc = "```")] + #[cfg_attr(not(feature = "load-onnxruntime"), doc = "```compile_fail")] + /// # #[pollster::main] + /// # async fn main() -> anyhow::Result<()> { + /// # use test_util::{ONNXRUNTIME_DYLIB_PATH, OPEN_JTALK_DIC_DIR}; + /// # + /// # const ACCELERATION_MODE: AccelerationMode = AccelerationMode::Cpu; + /// # + /// use std::sync::Arc; + /// + /// use voicevox_core::{ + /// nonblocking::{Onnxruntime, OpenJtalk, Synthesizer}, + /// AccelerationMode, InitializeOptions, + /// }; + /// + /// # if cfg!(windows) { + /// # // Windows\System32\onnxruntime.dllを回避 + /// # voicevox_core::blocking::Onnxruntime::load_once() + /// # .filename(test_util::ONNXRUNTIME_DYLIB_PATH) + /// # .exec()?; + /// # } + /// let mut syntesizer = Synthesizer::new( + /// Onnxruntime::load_once().exec().await?, + /// Arc::new(OpenJtalk::new(OPEN_JTALK_DIC_DIR).await.unwrap()), + /// &InitializeOptions { + /// acceleration_mode: ACCELERATION_MODE, + /// ..Default::default() + /// }, + /// )?; + /// # + /// # Ok(()) + /// # } + /// ``` pub fn new( onnxruntime: &'static crate::nonblocking::Onnxruntime, open_jtalk: O, @@ -1178,10 +1216,12 @@ pub(crate) mod nonblocking { crate::nonblocking::Onnxruntime::from_blocking(self.0.onnxruntime()) } + /// ハードウェアアクセラレーションがGPUモードか判定する。 pub fn is_gpu_mode(&self) -> bool { self.0.is_gpu_mode() } + /// 音声モデルを読み込む。 pub async fn load_voice_model( &self, model: &crate::nonblocking::VoiceModelFile, @@ -1190,10 +1230,12 @@ pub(crate) mod nonblocking { self.0.status.insert_model(model.header(), model_bytes) } + /// 音声モデルの読み込みを解除する。 pub fn unload_voice_model(&self, voice_model_id: VoiceModelId) -> Result<()> { self.0.unload_voice_model(voice_model_id) } + /// 指定したIDの音声モデルが読み込まれているか判定する。 pub fn is_loaded_voice_model(&self, voice_model_id: VoiceModelId) -> bool { self.0.is_loaded_voice_model(voice_model_id) } @@ -1203,10 +1245,12 @@ pub(crate) mod nonblocking { self.0.is_loaded_model_by_style_id(style_id) } + /// 今読み込んでいる音声モデルのメタ情報を返す。 pub fn metas(&self) -> VoiceModelMeta { self.0.metas() } + /// AudioQueryから音声合成を行う。 pub async fn synthesis( &self, audio_query: &AudioQuery, @@ -1221,6 +1265,30 @@ pub(crate) mod nonblocking { .await } + /// AquesTalk風記法からAccentPhrase (アクセント句)の配列を生成する。 + /// + /// # Example + /// + /// ``` + /// # #[pollster::main] + /// # async fn main() -> anyhow::Result<()> { + /// # let synthesizer = + /// # voicevox_core::__internal::doctest_fixtures::synthesizer_with_sample_voice_model( + /// # test_util::SAMPLE_VOICE_MODEL_FILE_PATH, + /// # test_util::ONNXRUNTIME_DYLIB_PATH, + /// # test_util::OPEN_JTALK_DIC_DIR, + /// # ) + /// # .await?; + /// # + /// use voicevox_core::StyleId; + /// + /// let accent_phrases = synthesizer + /// .create_accent_phrases_from_kana("コンニチワ'", StyleId::new(302)) + /// .await?; + /// # + /// # Ok(()) + /// # } + /// ``` pub async fn create_accent_phrases_from_kana( &self, kana: &str, @@ -1233,6 +1301,7 @@ pub(crate) mod nonblocking { .await } + /// AccentPhraseの配列の音高・音素長を、特定の声で生成しなおす。 pub async fn replace_mora_data( &self, accent_phrases: &[AccentPhrase], @@ -1245,6 +1314,7 @@ pub(crate) mod nonblocking { .await } + /// AccentPhraseの配列の音素長を、特定の声で生成しなおす。 pub async fn replace_phoneme_length( &self, accent_phrases: &[AccentPhrase], @@ -1259,6 +1329,7 @@ pub(crate) mod nonblocking { .await } + /// AccentPhraseの配列の音高を、特定の声で生成しなおす。 pub async fn replace_mora_pitch( &self, accent_phrases: &[AccentPhrase], @@ -1271,6 +1342,32 @@ pub(crate) mod nonblocking { .await } + /// AquesTalk風記法から[AudioQuery]を生成する。 + /// + /// # Example + /// + /// ``` + /// # #[pollster::main] + /// # async fn main() -> anyhow::Result<()> { + /// # let synthesizer = + /// # voicevox_core::__internal::doctest_fixtures::synthesizer_with_sample_voice_model( + /// # test_util::SAMPLE_VOICE_MODEL_FILE_PATH, + /// # test_util::ONNXRUNTIME_DYLIB_PATH, + /// # test_util::OPEN_JTALK_DIC_DIR, + /// # ) + /// # .await?; + /// # + /// use voicevox_core::StyleId; + /// + /// let audio_query = synthesizer + /// .audio_query_from_kana("コンニチワ'", StyleId::new(302)) + /// .await?; + /// # + /// # Ok(()) + /// # } + /// ``` + /// + /// [AudioQuery]: crate::AudioQuery pub async fn audio_query_from_kana( &self, kana: &str, @@ -1282,6 +1379,7 @@ pub(crate) mod nonblocking { crate::task::asyncify(move || blocking.audio_query_from_kana(&kana, style_id)).await } + /// AquesTalk風記法から音声合成を行う。 pub async fn tts_from_kana( &self, kana: &str, @@ -1297,6 +1395,30 @@ pub(crate) mod nonblocking { } impl self::Synthesizer { + /// 日本語のテキストからAccentPhrase (アクセント句)の配列を生成する。 + /// + /// # Example + /// + /// ``` + /// # #[pollster::main] + /// # async fn main() -> anyhow::Result<()> { + /// # let synthesizer = + /// # voicevox_core::__internal::doctest_fixtures::synthesizer_with_sample_voice_model( + /// # test_util::SAMPLE_VOICE_MODEL_FILE_PATH, + /// # test_util::ONNXRUNTIME_DYLIB_PATH, + /// # test_util::OPEN_JTALK_DIC_DIR, + /// # ) + /// # .await?; + /// # + /// use voicevox_core::StyleId; + /// + /// let accent_phrases = synthesizer + /// .create_accent_phrases("こんにちは", StyleId::new(302)) + /// .await?; + /// # + /// # Ok(()) + /// # } + /// ``` pub async fn create_accent_phrases( &self, text: &str, @@ -1308,6 +1430,32 @@ pub(crate) mod nonblocking { crate::task::asyncify(move || blocking.create_accent_phrases(&text, style_id)).await } + /// 日本語のテキストから[AudioQuery]を生成する。 + /// + /// # Examples + /// + /// ``` + /// # #[pollster::main] + /// # async fn main() -> anyhow::Result<()> { + /// # let synthesizer = + /// # voicevox_core::__internal::doctest_fixtures::synthesizer_with_sample_voice_model( + /// # test_util::SAMPLE_VOICE_MODEL_FILE_PATH, + /// # test_util::ONNXRUNTIME_DYLIB_PATH, + /// # test_util::OPEN_JTALK_DIC_DIR, + /// # ) + /// # .await?; + /// # + /// use voicevox_core::StyleId; + /// + /// let audio_query = synthesizer + /// .audio_query("こんにちは", StyleId::new(302)) + /// .await?; + /// # + /// # Ok(()) + /// # } + /// ``` + /// + /// [AudioQuery]: crate::AudioQuery pub async fn audio_query(&self, text: &str, style_id: StyleId) -> Result { let blocking = self.0.clone(); let text = text.to_owned(); @@ -1315,6 +1463,7 @@ pub(crate) mod nonblocking { crate::task::asyncify(move || blocking.audio_query(&text, style_id)).await } + /// 日本語のテキストから音声合成を行う。 pub async fn tts( &self, text: &str, @@ -1328,6 +1477,13 @@ pub(crate) mod nonblocking { crate::task::asyncify(move || blocking.tts(&text, style_id, &options)).await } } + + #[ext(IntoBlocking)] + impl self::Synthesizer { + pub fn into_blocking(self) -> Arc> { + self.0 + } + } } #[cfg(test)]