From 1d24929ee5bba27f62e141fc6dd1132a0e7fb34f Mon Sep 17 00:00:00 2001 From: Ryo Yamashita Date: Sun, 1 Dec 2024 14:36:40 +0900 Subject: [PATCH] =?UTF-8?q?feat!:=20`render`=E3=81=AE=E5=BC=95=E6=95=B0?= =?UTF-8?q?=E3=81=AE=E7=AF=84=E5=9B=B2=E6=8C=87=E5=AE=9A=E9=83=A8=E5=88=86?= =?UTF-8?q?=E3=82=92=E5=90=84=E8=A8=80=E8=AA=9E=E3=81=AE=E6=85=A3=E7=BF=92?= =?UTF-8?q?=E3=81=AB=E5=90=88=E3=82=8F=E3=81=9B=E3=82=8B=20(#879)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Synthesizer::render`の引数の範囲指定部分を次のようにする。 Rust API: `range: Range` Python API: `start: int, stop: int` 以前の議論: https://github.com/VOICEVOX/voicevox_core/pull/870#discussion_r1835573095 --- crates/voicevox_core/src/synthesizer.rs | 21 ++++++++----------- .../python/voicevox_core/_rust/blocking.pyi | 2 +- crates/voicevox_core_python_api/src/lib.rs | 18 +++++++--------- docs/guide/dev/api-design.md | 5 +++-- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/crates/voicevox_core/src/synthesizer.rs b/crates/voicevox_core/src/synthesizer.rs index b985e2bf6..deea59b1d 100644 --- a/crates/voicevox_core/src/synthesizer.rs +++ b/crates/voicevox_core/src/synthesizer.rs @@ -468,15 +468,15 @@ mod inner { pub(super) async fn render( &self, audio: &AudioFeature, - start: usize, - end: usize, + range: Range, ) -> Result> { // TODO: 44.1kHzなどの対応 - if (start..end).is_empty() { + if range.is_empty() { + // FIXME: `start>end`に対してパニックせずに正常に空を返してしまうのでは? // 指定区間が空のときは早期リターン return Ok(vec![]); } - let spec_segment = crop_with_margin(audio, start..end); + let spec_segment = crop_with_margin(audio, range); let wave_with_margin = self .render_audio_segment(spec_segment.to_owned(), audio.style_id) .await?; @@ -524,7 +524,7 @@ mod inner { let audio = self .precompute_render(audio_query, style_id, options) .await?; - let pcm = self.render(&audio, 0, audio.frame_length).await?; + let pcm = self.render(&audio, 0..audio.frame_length).await?; Ok(wav_from_s16le( &pcm, audio_query.output_sampling_rate, @@ -1201,6 +1201,8 @@ mod inner { 形を考えると、ここの引数を構造体にまとめたりしても可読性に寄与しない" )] pub(crate) mod blocking { + use std::ops::Range; + use easy_ext::ext; use crate::{ @@ -1310,13 +1312,8 @@ pub(crate) mod blocking { } /// 中間表現から16bit PCMで音声波形を生成する。 - pub fn render( - &self, - audio: &AudioFeature, - start: usize, - end: usize, - ) -> crate::Result> { - self.0.render(audio, start, end).block_on() + pub fn render(&self, audio: &AudioFeature, range: Range) -> crate::Result> { + self.0.render(audio, range).block_on() } /// AudioQueryから直接WAVフォーマットで音声波形を生成する。 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 9d73c032b..63111acfe 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 @@ -367,7 +367,7 @@ class Synthesizer: self, audio: AudioFeature, start: int, - end: int, + stop: int, ) -> bytes: ... def synthesis( self, diff --git a/crates/voicevox_core_python_api/src/lib.rs b/crates/voicevox_core_python_api/src/lib.rs index 4873d7ccf..9f2a9e7e1 100644 --- a/crates/voicevox_core_python_api/src/lib.rs +++ b/crates/voicevox_core_python_api/src/lib.rs @@ -713,32 +713,28 @@ mod blocking { Ok(AudioFeature { audio }) } - #[pyo3(signature=(audio, start, end))] fn render<'py>( &self, audio: &AudioFeature, start: usize, - end: usize, + stop: usize, py: Python<'py>, ) -> PyResult<&'py PyBytes> { - if start > audio.frame_length() || end > audio.frame_length() { + if start > audio.frame_length() || stop > audio.frame_length() { return Err(PyIndexError::new_err(format!( - "({}, {}) is out of range for audio feature of length {}", - start, - end, - audio.frame_length(), + "({start}, {stop}) is out of range for audio feature of length {len}", + len = audio.frame_length(), ))); } - if start > end { + if start > stop { return Err(PyValueError::new_err(format!( - "({}, {}) is invalid range because start > end", - start, end, + "({start}, {stop}) is invalid range because start > end", ))); } let wav = &self .synthesizer .read()? - .render(&audio.audio, start, end) + .render(&audio.audio, start..stop) .into_py_result(py)?; Ok(PyBytes::new(py, wav)) } diff --git a/docs/guide/dev/api-design.md b/docs/guide/dev/api-design.md index 89df3e0dd..6ea140a53 100644 --- a/docs/guide/dev/api-design.md +++ b/docs/guide/dev/api-design.md @@ -11,5 +11,6 @@ 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)で表現します。 * オプショナルな引数は、キーワード引数がある言語であればキーワード引数で、ビルダースタイルが一般的な言語であればビルダースタイルで表現すべきです。 - - +* `Synthesizer::render`は`range: std::ops::Range`を引数に取っています。`Range`にあたる型が標準で存在し、かつそれが配列の範囲指定として用いられるようなものであれば、それを使うべきです。 + * ただし例えばPythonでは、`slice`を引数に取るのは慣習にそぐわないため`start: int, stop: int`のようにすべきです。 + * もし`Range`にあたる型が標準で無く、かつRustの"start"/"end"やPythonの"start"/"stop"にあたる明確な言葉が無いのであれば、誤解が生じないよう"end_exclusive"のように命名するべきです。