From 86123f2a0b1c21b813ccc0e81d1e0de22e00fb08 Mon Sep 17 00:00:00 2001 From: Kai Date: Mon, 23 Oct 2023 03:01:32 +0900 Subject: [PATCH] =?UTF-8?q?=E3=82=B9=E3=82=BF=E3=82=A4=E3=83=ABID=EF=BC=88?= =?UTF-8?q?style=5Fid=EF=BC=89=E3=81=AE=E3=81=93=E3=81=A8=E3=82=92?= =?UTF-8?q?=E8=A9=B1=E8=80=85ID=EF=BC=88speaker=5Fid=EF=BC=89=E3=81=A8?= =?UTF-8?q?=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E3=82=B3=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=92=E5=85=A8=E9=83=A8=E7=BD=AE=E3=81=8D=E6=8F=9B=E3=81=88?= =?UTF-8?q?=E3=82=8B=20(#741)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hiroshiba --- README.md | 92 ++++++------ build_util/check_release_build.py | 4 +- run.py | 136 +++++++++++++----- test/test_mock_synthesis_engine.py | 6 +- test/test_synthesis_engine.py | 28 ++-- test/test_synthesis_engine_base.py | 14 +- voicevox_engine/cancellable_engine.py | 10 +- voicevox_engine/dev/synthesis_engine/mock.py | 18 +-- voicevox_engine/model.py | 8 +- voicevox_engine/morphing.py | 14 +- .../synthesis_engine/core_wrapper.py | 22 +-- .../synthesis_engine/synthesis_engine.py | 46 +++--- .../synthesis_engine/synthesis_engine_base.py | 63 ++++---- 13 files changed, 272 insertions(+), 189 deletions(-) diff --git a/README.md b/README.md index 21c1c4480..875178412 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ echo -n "こんにちは、音声合成の世界へようこそ" >text.txt curl -s \ -X POST \ - "127.0.0.1:50021/audio_query?speaker=1"\ + "127.0.0.1:50021/audio_query?style_id=1"\ --get --data-urlencode text@text.txt \ > query.json @@ -45,7 +45,7 @@ curl -s \ -H "Content-Type: application/json" \ -X POST \ -d @query.json \ - "127.0.0.1:50021/synthesis?speaker=1" \ + "127.0.0.1:50021/synthesis?style_id=1" \ > audio.wav ``` @@ -70,7 +70,7 @@ echo -n "ディープラーニングは万能薬ではありません" >text.txt curl -s \ -X POST \ - "127.0.0.1:50021/audio_query?speaker=1" \ + "127.0.0.1:50021/audio_query?style_id=1" \ --get --data-urlencode text@text.txt \ > query.json @@ -82,7 +82,7 @@ cat query.json | grep -o -E "\"kana\":\".*\"" echo -n "ディイプラ'アニングワ/バンノ'オヤクデワ/アリマセ'ン" > kana.txt curl -s \ -X POST \ - "127.0.0.1:50021/accent_phrases?speaker=1&is_kana=true" \ + "127.0.0.1:50021/accent_phrases?style_id=1&is_kana=true" \ --get --data-urlencode text@kana.txt \ > newphrases.json @@ -93,17 +93,17 @@ curl -s \ -H "Content-Type: application/json" \ -X POST \ -d @newquery.json \ - "127.0.0.1:50021/synthesis?speaker=1" \ + "127.0.0.1:50021/synthesis?style_id=1" \ > audio.wav ``` ### ユーザー辞書機能について -APIからユーザー辞書の参照、単語の追加、編集、削除を行うことができます。 +API からユーザー辞書の参照、単語の追加、編集、削除を行うことができます。 #### 参照 -`/user_dict`にGETリクエストを投げることでユーザー辞書の一覧を取得することができます。 +`/user_dict`に GET リクエストを投げることでユーザー辞書の一覧を取得することができます。 ```bash curl -s -X GET "127.0.0.1:50021/user_dict" @@ -111,17 +111,18 @@ curl -s -X GET "127.0.0.1:50021/user_dict" #### 単語追加 -`/user_dict_word`にPOSTリクエストを投げる事でユーザー辞書に単語を追加することができます。 -URLパラメータとして、以下が必要です。 +`/user_dict_word`に POST リクエストを投げる事でユーザー辞書に単語を追加することができます。 +URL パラメータとして、以下が必要です。 + - surface (辞書に登録する単語) - pronunciation (カタカナでの読み方) - accent_type (アクセント核位置、整数) アクセント核位置については、こちらの文章が参考になるかと思います。 〇型となっている数字の部分がアクセント核位置になります。 -https://tdmelodic.readthedocs.io/ja/latest/pages/introduction.html +https://tdmelodic.readthedocs.io/ja/latest/pages/introduction.html -成功した場合の返り値は単語に割り当てられるUUIDの文字列になります。 +成功した場合の返り値は単語に割り当てられる UUID の文字列になります。 ```bash surface="test" @@ -137,13 +138,14 @@ curl -s -X POST "127.0.0.1:50021/user_dict_word" \ #### 単語修正 -`/user_dict_word/{word_uuid}`にPUTリクエストを投げる事でユーザー辞書の単語を修正することができます。 -URLパラメータとして、以下が必要です。 +`/user_dict_word/{word_uuid}`に PUT リクエストを投げる事でユーザー辞書の単語を修正することができます。 +URL パラメータとして、以下が必要です。 + - surface (辞書に登録するワード) - pronunciation (カタカナでの読み方) - accent_type (アクセント核位置、整数) -word_uuidは単語追加時に確認できるほか、ユーザー辞書を参照することでも確認できます。 +word_uuid は単語追加時に確認できるほか、ユーザー辞書を参照することでも確認できます。 成功した場合の返り値は`204 No Content`になります。 ```bash @@ -162,9 +164,9 @@ curl -s -X PUT "127.0.0.1:50021/user_dict_word/$word_uuid" \ #### 単語削除 -`/user_dict_word/{word_uuid}`にDELETEリクエストを投げる事でユーザー辞書の単語を削除することができます。 +`/user_dict_word/{word_uuid}`に DELETE リクエストを投げる事でユーザー辞書の単語を削除することができます。 -word_uuidは単語追加時に確認できるほか、ユーザー辞書を参照することでも確認できます。 +word_uuid は単語追加時に確認できるほか、ユーザー辞書を参照することでも確認できます。 成功した場合の返り値は`204 No Content`になります。 ```bash @@ -199,7 +201,7 @@ curl -s \ -H "Content-Type: application/json" \ -X POST \ -d @query.json \ - "127.0.0.1:50021/synthesis?speaker=$style_id" \ + "127.0.0.1:50021/synthesis?style_id=$style_id" \ > audio.wav ``` @@ -216,7 +218,7 @@ echo -n "モーフィングを利用することで、2つの声を混ぜる curl -s \ -X POST \ - "127.0.0.1:50021/audio_query?speaker=0"\ + "127.0.0.1:50021/audio_query?style_id=0"\ --get --data-urlencode text@text.txt \ > query.json @@ -225,7 +227,7 @@ curl -s \ -H "Content-Type: application/json" \ -X POST \ -d @query.json \ - "127.0.0.1:50021/synthesis?speaker=0" \ + "127.0.0.1:50021/synthesis?style_id=0" \ > audio.wav export MORPH_RATE=0.5 @@ -268,11 +270,11 @@ curl -s -X GET "127.0.0.1:50021/speaker_info?speaker_uuid=7ffcb7ce-00ec-4bdc-82c この API は実験的機能であり、エンジン起動時に引数で`--enable_cancellable_synthesis`を指定しないと有効化されません。 音声合成に必要なパラメータは`/synthesis`と同様です。 -### CORS設定 +### CORS 設定 -VOICEVOXではセキュリティ保護のため`localhost`・`127.0.0.1`・`app://`・Originなし以外のOriginからリクエストを受け入れないようになっています。 +VOICEVOX ではセキュリティ保護のため`localhost`・`127.0.0.1`・`app://`・Origin なし以外の Origin からリクエストを受け入れないようになっています。 そのため、一部のサードパーティアプリからのレスポンスを受け取れない可能性があります。 -これを回避する方法として、エンジンから設定できるUIを用意しています。 +これを回避する方法として、エンジンから設定できる UI を用意しています。 #### 設定方法 @@ -345,7 +347,8 @@ docker run --rm --gpus all -p '127.0.0.1:50021:50021' voicevox/voicevox_engine:n ``` #### トラブルシューティング -GPU版を利用する場合、環境によってエラーが発生することがあります。その場合、`--runtime=nvidia`を`docker run`につけて実行すると解決できることがあります。 + +GPU 版を利用する場合、環境によってエラーが発生することがあります。その場合、`--runtime=nvidia`を`docker run`につけて実行すると解決できることがあります。 ## 貢献者の方へ @@ -419,29 +422,38 @@ CPU スレッド数が未指定の場合は、論理コア数の半分か物理 ``` ### 過去のバージョンのコアを使う -VOICEVOX Core 0.5.4以降のコアを使用する事が可能です。 -Macでのlibtorch版コアのサポートはしていません。 + +VOICEVOX Core 0.5.4 以降のコアを使用する事が可能です。 +Mac での libtorch 版コアのサポートはしていません。 #### 過去のバイナリを指定する -製品版VOICEVOXもしくはコンパイル済みエンジンのディレクトリを`--voicevox_dir`引数で指定すると、そのバージョンのコアが使用されます。 + +製品版 VOICEVOX もしくはコンパイル済みエンジンのディレクトリを`--voicevox_dir`引数で指定すると、そのバージョンのコアが使用されます。 + ```bash python run.py --voicevox_dir="/path/to/voicevox" ``` -Macでは、`DYLD_LIBRARY_PATH`の指定が必要です。 + +Mac では、`DYLD_LIBRARY_PATH`の指定が必要です。 + ```bash DYLD_LIBRARY_PATH="/path/to/voicevox" python run.py --voicevox_dir="/path/to/voicevox" ``` #### 音声ライブラリを直接指定する -[VOICEVOX Coreのzipファイル](https://github.com/VOICEVOX/voicevox_core/releases)を解凍したディレクトリを`--voicelib_dir`引数で指定します。 + +[VOICEVOX Core の zip ファイル](https://github.com/VOICEVOX/voicevox_core/releases)を解凍したディレクトリを`--voicelib_dir`引数で指定します。 また、コアのバージョンに合わせて、[libtorch](https://pytorch.org/)や[onnxruntime](https://github.com/microsoft/onnxruntime)のディレクトリを`--runtime_dir`引数で指定します。 -ただし、システムの探索パス上にlibtorch、onnxruntimeがある場合、`--runtime_dir`引数の指定は不要です。 -`--voicelib_dir`引数、`--runtime_dir`引数は複数回使用可能です。 -APIエンドポイントでコアのバージョンを指定する場合は`core_version`引数を指定してください。(未指定の場合は最新のコアが使用されます) +ただし、システムの探索パス上に libtorch、onnxruntime がある場合、`--runtime_dir`引数の指定は不要です。 +`--voicelib_dir`引数、`--runtime_dir`引数は複数回使用可能です。 +API エンドポイントでコアのバージョンを指定する場合は`core_version`引数を指定してください。(未指定の場合は最新のコアが使用されます) + ```bash python run.py --voicelib_dir="/path/to/voicevox_core" --runtime_dir="/path/to/libtorch_or_onnx" ``` -Macでは、`--runtime_dir`引数の代わりに`DYLD_LIBRARY_PATH`の指定が必要です。 + +Mac では、`--runtime_dir`引数の代わりに`DYLD_LIBRARY_PATH`の指定が必要です。 + ```bash DYLD_LIBRARY_PATH="/path/to/onnx" python run.py --voicelib_dir="/path/to/voicevox_core" ``` @@ -493,7 +505,7 @@ python make_docs.py ## ビルド この方法でビルドしたものは、リリースで公開されているものとは異なります。 -また、GPUで利用するにはcuDNNやCUDA、DirectMLなどのライブラリが追加で必要となります。 +また、GPU で利用するには cuDNN や CUDA、DirectML などのライブラリが追加で必要となります。 ```bash python -m pip install -r requirements-dev.txt @@ -603,19 +615,19 @@ VOICEVOX エディターにうまく読み込ませられないときは、エ ### Variables -| name | description | -| :----------------- | :---------------------------------------------------------------------- | -| DOCKERHUB_USERNAME | Docker Hub ユーザ名 | +| name | description | +| :----------------- | :------------------ | +| DOCKERHUB_USERNAME | Docker Hub ユーザ名 | ### Secrets -| name | description | -| :----------------- | :---------------------------------------------------------------------- | -| DOCKERHUB_TOKEN | [Docker Hub アクセストークン](https://hub.docker.com/settings/security) | +| name | description | +| :-------------- | :---------------------------------------------------------------------- | +| DOCKERHUB_TOKEN | [Docker Hub アクセストークン](https://hub.docker.com/settings/security) | ## 事例紹介 -**[voicevox-client](https://github.com/tuna2134/voicevox-client) [@tuna2134](https://github.com/tuna2134)** ・・・ VOICEVOX ENGINE のためのPythonラッパー +**[voicevox-client](https://github.com/tuna2134/voicevox-client) [@tuna2134](https://github.com/tuna2134)** ・・・ VOICEVOX ENGINE のための Python ラッパー ## ライセンス diff --git a/build_util/check_release_build.py b/build_util/check_release_build.py index 71bf49c08..008f25548 100644 --- a/build_util/check_release_build.py +++ b/build_util/check_release_build.py @@ -34,14 +34,14 @@ def test_release_build(dist_dir: Path, skip_run_process: bool) -> None: # テキスト -> クエリ text = "こんにちは、音声合成の世界へようこそ" req = Request( - base_url + "audio_query?" + urlencode({"speaker": "1", "text": text}), + base_url + "audio_query?" + urlencode({"style_id": "1", "text": text}), method="POST", ) with urlopen(req) as res: query = json.loads(res.read().decode("utf-8")) # クエリ -> 音声 - req = Request(base_url + "synthesis?speaker=1", method="POST") + req = Request(base_url + "synthesis?style_id=1", method="POST") req.add_header("Content-Type", "application/json") req.data = json.dumps(query).encode("utf-8") with urlopen(req) as res: diff --git a/run.py b/run.py index f102aec02..4a8afa27b 100644 --- a/run.py +++ b/run.py @@ -7,6 +7,7 @@ import re import sys import traceback +import warnings import zipfile from functools import lru_cache from io import BytesIO, TextIOWrapper @@ -42,7 +43,7 @@ ParseKanaError, Speaker, SpeakerInfo, - SpeakerNotFoundError, + StyleIdNotFoundError, SupportedDevicesInfo, UserDictWord, VvlibManifest, @@ -83,6 +84,21 @@ ) +def get_style_id_from_deprecated(style_id: int | None, speaker_id: int | None) -> int: + """ + style_idとspeaker_id両方ともNoneかNoneでないかをチェックし、 + どちらか片方しかNoneが存在しなければstyle_idを返す + """ + if speaker_id is not None and style_id is None: + warnings.warn("speakerは非推奨です。style_idを利用してください。", stacklevel=1) + return speaker_id + elif style_id is not None and speaker_id is None: + return style_id + raise HTTPException( + status_code=400, detail="speakerとstyle_idが両方とも存在しないか、両方とも存在しています。" + ) + + def b64encode_str(s): return base64.b64encode(s).decode("utf-8") @@ -224,14 +240,16 @@ def get_engine(core_version: Optional[str]) -> SynthesisEngineBase: ) def audio_query( text: str, - speaker: int, + style_id: int | None = Query(default=None), # noqa: B008 + speaker: int | None = Query(default=None, deprecated=True), # noqa: B008 core_version: str | None = None, ) -> AudioQuery: """ クエリの初期値を得ます。ここで得られたクエリはそのまま音声合成に利用できます。各値の意味は`Schemas`を参照してください。 """ + style_id = get_style_id_from_deprecated(style_id=style_id, speaker_id=speaker) engine = get_engine(core_version) - accent_phrases = engine.create_accent_phrases(text, speaker_id=speaker) + accent_phrases = engine.create_accent_phrases(text, style_id=style_id) return AudioQuery( accent_phrases=accent_phrases, speedScale=1, @@ -272,7 +290,7 @@ def audio_query_from_preset( raise HTTPException(status_code=422, detail="該当するプリセットIDが見つかりません") accent_phrases = engine.create_accent_phrases( - text, speaker_id=selected_preset.style_id + text, style_id=selected_preset.style_id ) return AudioQuery( accent_phrases=accent_phrases, @@ -301,7 +319,8 @@ def audio_query_from_preset( ) def accent_phrases( text: str, - speaker: int, + style_id: int | None = Query(default=None), # noqa: B008 + speaker: int | None = Query(default=None, deprecated=True), # noqa: B008 is_kana: bool = False, core_version: str | None = None, ) -> list[AccentPhrase]: @@ -314,6 +333,7 @@ def accent_phrases( * アクセント位置を`'`で指定する。全てのアクセント句にはアクセント位置を1つ指定する必要がある。 * アクセント句末に`?`(全角)を入れることにより疑問文の発音ができる。 """ + style_id = get_style_id_from_deprecated(style_id=style_id, speaker_id=speaker) engine = get_engine(core_version) if is_kana: try: @@ -324,12 +344,12 @@ def accent_phrases( detail=ParseKanaBadRequest(err).dict(), ) accent_phrases = engine.replace_mora_data( - accent_phrases=accent_phrases, speaker_id=speaker + accent_phrases=accent_phrases, style_id=style_id ) return accent_phrases else: - return engine.create_accent_phrases(text, speaker_id=speaker) + return engine.create_accent_phrases(text, style_id=style_id) @app.post( "/mora_data", @@ -339,11 +359,13 @@ def accent_phrases( ) def mora_data( accent_phrases: list[AccentPhrase], - speaker: int, + style_id: int | None = Query(default=None), # noqa: B008 + speaker: int | None = Query(default=None, deprecated=True), # noqa: B008 core_version: str | None = None, ) -> list[AccentPhrase]: + style_id = get_style_id_from_deprecated(style_id=style_id, speaker_id=speaker) engine = get_engine(core_version) - return engine.replace_mora_data(accent_phrases, speaker_id=speaker) + return engine.replace_mora_data(accent_phrases, style_id=style_id) @app.post( "/mora_length", @@ -353,12 +375,14 @@ def mora_data( ) def mora_length( accent_phrases: list[AccentPhrase], - speaker: int, + style_id: int | None = Query(default=None), # noqa: B008 + speaker: int | None = Query(default=None, deprecated=True), # noqa: B008 core_version: str | None = None, ) -> list[AccentPhrase]: + style_id = get_style_id_from_deprecated(style_id=style_id, speaker_id=speaker) engine = get_engine(core_version) return engine.replace_phoneme_length( - accent_phrases=accent_phrases, speaker_id=speaker + accent_phrases=accent_phrases, style_id=style_id ) @app.post( @@ -369,12 +393,14 @@ def mora_length( ) def mora_pitch( accent_phrases: list[AccentPhrase], - speaker: int, + style_id: int | None = Query(default=None), # noqa: B008 + speaker: int | None = Query(default=None, deprecated=True), # noqa: B008 core_version: str | None = None, ) -> list[AccentPhrase]: + style_id = get_style_id_from_deprecated(style_id=style_id, speaker_id=speaker) engine = get_engine(core_version) return engine.replace_mora_pitch( - accent_phrases=accent_phrases, speaker_id=speaker + accent_phrases=accent_phrases, style_id=style_id ) @app.post( @@ -392,17 +418,19 @@ def mora_pitch( ) def synthesis( query: AudioQuery, - speaker: int, + style_id: int | None = Query(default=None), # noqa: B008 + speaker: int | None = Query(default=None, deprecated=True), # noqa: B008 enable_interrogative_upspeak: bool = Query( # noqa: B008 default=True, description="疑問系のテキストが与えられたら語尾を自動調整する", ), core_version: str | None = None, ) -> FileResponse: + style_id = get_style_id_from_deprecated(style_id=style_id, speaker_id=speaker) engine = get_engine(core_version) wave = engine.synthesis( query=query, - speaker_id=speaker, + style_id=style_id, enable_interrogative_upspeak=enable_interrogative_upspeak, ) @@ -432,10 +460,12 @@ def synthesis( ) def cancellable_synthesis( query: AudioQuery, - speaker: int, request: Request, + style_id: int | None = Query(default=None), # noqa: B008 + speaker: int | None = Query(default=None, deprecated=True), # noqa: B008 core_version: str | None = None, ) -> FileResponse: + style_id = get_style_id_from_deprecated(style_id=style_id, speaker_id=speaker) if cancellable_engine is None: raise HTTPException( status_code=404, @@ -443,7 +473,7 @@ def cancellable_synthesis( ) f_name = cancellable_engine._synthesis_impl( query=query, - speaker_id=speaker, + style_id=style_id, request=request, core_version=core_version, ) @@ -472,27 +502,25 @@ def cancellable_synthesis( summary="複数まとめて音声合成する", ) def multi_synthesis( - queries: list[AudioQuery], - speaker: int, + queries: list[AccentPhrase], + style_id: int | None = Query(default=None), # noqa: B008 + speaker: int | None = Query(default=None, deprecated=True), # noqa: B008 core_version: str | None = None, ) -> FileResponse: + style_id = get_style_id_from_deprecated(style_id=style_id, speaker_id=speaker) engine = get_engine(core_version) sampling_rate = queries[0].outputSamplingRate with NamedTemporaryFile(delete=False) as f: - with zipfile.ZipFile(f, mode="a") as zip_file: - for i in range(len(queries)): - if queries[i].outputSamplingRate != sampling_rate: raise HTTPException( status_code=422, detail="サンプリングレートが異なるクエリがあります" ) with TemporaryFile() as wav_file: - - wave = engine.synthesis(query=queries[i], speaker_id=speaker) + wave = engine.synthesis(query=queries[i], style_id=style_id) soundfile.write( file=wav_file, data=wave, @@ -536,9 +564,9 @@ def morphable_targets( {str(k): v for k, v in morphable_target.items()} for morphable_target in morphable_targets ] - except SpeakerNotFoundError as e: + except StyleIdNotFoundError as e: raise HTTPException( - status_code=404, detail=f"該当する話者(speaker={e.speaker})が見つかりません" + status_code=404, detail=f"該当するスタイル(style_id={e.style_id})が見つかりません" ) @app.post( @@ -578,9 +606,9 @@ def _synthesis_morphing( status_code=400, detail="指定された話者ペアでのモーフィングはできません", ) - except SpeakerNotFoundError as e: + except StyleIdNotFoundError as e: raise HTTPException( - status_code=404, detail=f"該当する話者(speaker={e.speaker})が見つかりません" + status_code=404, detail=f"該当するスタイル(style_id={e.style_id})が見つかりません" ) # 生成したパラメータはキャッシュされる @@ -895,7 +923,34 @@ def uninstall_library(library_uuid: str) -> Response: library_manager.uninstall_library(library_uuid) return Response(status_code=204) - @app.post("/initialize_speaker", status_code=204, tags=["その他"]) + @app.post("/initialize_style_id", status_code=204, tags=["その他"]) + def initialize_style_id( + style_id: int, + skip_reinit: bool = Query( # noqa: B008 + False, description="既に初期化済みのスタイルの再初期化をスキップするかどうか" + ), + core_version: str | None = None, + ) -> Response: + """ + 指定されたstyle_idのスタイルを初期化します。 + 実行しなくても他のAPIは使用できますが、初回実行時に時間がかかることがあります。 + """ + engine = get_engine(core_version) + engine.initialize_style_id_synthesis(style_id=style_id, skip_reinit=skip_reinit) + return Response(status_code=204) + + @app.get("/is_initialized_style_id", response_model=bool, tags=["その他"]) + def is_initialized_style_id( + style_id: int, + core_version: str | None = None, + ) -> bool: + """ + 指定されたstyle_idのスタイルが初期化されているかどうかを返します。 + """ + engine = get_engine(core_version) + return engine.is_initialized_style_id_synthesis(style_id) + + @app.post("/initialize_speaker", status_code=204, tags=["その他"], deprecated=True) def initialize_speaker( speaker: int, skip_reinit: bool = Query( # noqa: B008 @@ -904,23 +959,34 @@ def initialize_speaker( core_version: str | None = None, ) -> Response: """ + こちらのAPIは非推奨です。`initialize_style_id`を利用してください。\n 指定されたspeaker_idの話者を初期化します。 実行しなくても他のAPIは使用できますが、初回実行時に時間がかかることがあります。 """ - engine = get_engine(core_version) - engine.initialize_speaker_synthesis(speaker_id=speaker, skip_reinit=skip_reinit) - return Response(status_code=204) + warnings.warn( + "使用しているAPI(/initialize_speaker)は非推奨です。/initialized_style_idを利用してください。", + stacklevel=1, + ) + return initialize_style_id( + style_id=speaker, skip_reinit=skip_reinit, core_version=core_version + ) - @app.get("/is_initialized_speaker", response_model=bool, tags=["その他"]) + @app.get( + "/is_initialized_speaker", response_model=bool, tags=["その他"], deprecated=True + ) def is_initialized_speaker( speaker: int, core_version: str | None = None, ) -> bool: """ + こちらのAPIは非推奨です。`is_initialize_style_id`を利用してください。\n 指定されたspeaker_idの話者が初期化されているかどうかを返します。 """ - engine = get_engine(core_version) - return engine.is_initialized_speaker_synthesis(speaker) + warnings.warn( + "使用しているAPI(/is_initialize_speaker)は非推奨です。/is_initialized_style_idを利用してください。", + stacklevel=1, + ) + return is_initialized_style_id(style_id=speaker, core_version=core_version) @app.get("/user_dict", response_model=dict[str, UserDictWord], tags=["ユーザー辞書"]) def get_user_dict_words() -> dict[str, UserDictWord]: diff --git a/test/test_mock_synthesis_engine.py b/test/test_mock_synthesis_engine.py index c06a0504a..ce6c59825 100644 --- a/test/test_mock_synthesis_engine.py +++ b/test/test_mock_synthesis_engine.py @@ -108,7 +108,7 @@ def test_replace_phoneme_length(self): self.assertEqual( self.engine.replace_phoneme_length( accent_phrases=self.accent_phrases_hello_hiho, - speaker_id=0, + style_id=0, ), self.accent_phrases_hello_hiho, ) @@ -117,7 +117,7 @@ def test_replace_mora_pitch(self): self.assertEqual( self.engine.replace_mora_pitch( accent_phrases=self.accent_phrases_hello_hiho, - speaker_id=0, + style_id=0, ), self.accent_phrases_hello_hiho, ) @@ -136,5 +136,5 @@ def test_synthesis(self): outputStereo=False, kana=create_kana(self.accent_phrases_hello_hiho), ), - speaker_id=0, + style_id=0, ) diff --git a/test/test_synthesis_engine.py b/test/test_synthesis_engine.py index b1a217416..c7d992293 100644 --- a/test/test_synthesis_engine.py +++ b/test/test_synthesis_engine.py @@ -22,11 +22,11 @@ ) -def yukarin_s_mock(length: int, phoneme_list: numpy.ndarray, speaker_id: numpy.ndarray): +def yukarin_s_mock(length: int, phoneme_list: numpy.ndarray, style_id: numpy.ndarray): result = [] # mockとしての適当な処理、特に意味はない for i in range(length): - result.append(float(phoneme_list[i] * 0.5 + speaker_id)) + result.append(float(phoneme_list[i] * 0.5 + style_id)) return numpy.array(result) @@ -38,7 +38,7 @@ def yukarin_sa_mock( end_accent_list: numpy.ndarray, start_accent_phrase_list: numpy.ndarray, end_accent_phrase_list: numpy.ndarray, - speaker_id: numpy.ndarray, + style_id: numpy.ndarray, ): result = [] # mockとしての適当な処理、特に意味はない @@ -54,7 +54,7 @@ def yukarin_sa_mock( + end_accent_phrase_list[0][i] ) * 0.5 - + speaker_id + + style_id ) ) return numpy.array(result)[numpy.newaxis] @@ -65,7 +65,7 @@ def decode_mock( phoneme_size: int, f0: numpy.ndarray, phoneme: numpy.ndarray, - speaker_id: Union[numpy.ndarray, int], + style_id: Union[numpy.ndarray, int], ): result = [] # mockとしての適当な処理、特に意味はない @@ -75,7 +75,7 @@ def decode_mock( result.append( float( f0[i][0] * (numpy.where(phoneme[i] == 1)[0] / phoneme_size) - + speaker_id + + style_id ) ) return numpy.array(result) @@ -92,7 +92,7 @@ def metas(self): def supported_devices(self): return "" - def is_model_loaded(self, speaker_id): + def is_model_loaded(self, style_id): return True @@ -303,7 +303,7 @@ def test_pre_process(self): def test_replace_phoneme_length(self): result = self.synthesis_engine.replace_phoneme_length( - accent_phrases=deepcopy(self.accent_phrases_hello_hiho), speaker_id=1 + accent_phrases=deepcopy(self.accent_phrases_hello_hiho), style_id=1 ) # yukarin_sに渡される値の検証 @@ -340,7 +340,7 @@ def test_replace_phoneme_length(self): dtype=numpy.int64, ), ) - self.assertEqual(yukarin_s_args["speaker_id"], 1) + self.assertEqual(yukarin_s_args["style_id"], 1) # flatten_morasを使わずに愚直にaccent_phrasesにデータを反映させてみる true_result = deepcopy(self.accent_phrases_hello_hiho) @@ -368,13 +368,13 @@ def test_replace_mora_pitch(self): empty_accent_phrases = [] self.assertEqual( self.synthesis_engine.replace_mora_pitch( - accent_phrases=empty_accent_phrases, speaker_id=1 + accent_phrases=empty_accent_phrases, style_id=1 ), [], ) result = self.synthesis_engine.replace_mora_pitch( - accent_phrases=deepcopy(self.accent_phrases_hello_hiho), speaker_id=1 + accent_phrases=deepcopy(self.accent_phrases_hello_hiho), style_id=1 ) # yukarin_saに渡される値の検証 @@ -393,7 +393,7 @@ def test_replace_mora_pitch(self): self.assertEqual(list_length, len(end_accent_list)) self.assertEqual(list_length, len(start_accent_phrase_list)) self.assertEqual(list_length, len(end_accent_phrase_list)) - self.assertEqual(yukarin_sa_args["speaker_id"], 1) + self.assertEqual(yukarin_sa_args["style_id"], 1) numpy.testing.assert_array_equal( vowel_phoneme_list, @@ -512,7 +512,7 @@ def synthesis_test_base(self, audio_query: AudioQuery): for i in range(len(phoneme_length_list)): phoneme_length_list[i] /= audio_query.speedScale - result = self.synthesis_engine.synthesis(query=audio_query, speaker_id=1) + result = self.synthesis_engine.synthesis(query=audio_query, style_id=1) # decodeに渡される値の検証 decode_args = self.decode_mock.call_args[1] @@ -577,7 +577,7 @@ def synthesis_test_base(self, audio_query: AudioQuery): assert_true_count += bool(phoneme[i][j] == decode_phoneme[i][j]) assert_phoneme_count += assert_true_count == num_phoneme self.assertTrue(assert_phoneme_count >= int(len(decode_phoneme) / 5) * 4) - self.assertEqual(decode_args["speaker_id"], 1) + self.assertEqual(decode_args["style_id"], 1) # decode forwarderのmockを使う true_result = decode_mock(list_length, num_phoneme, f0, phoneme, 1) diff --git a/test/test_synthesis_engine_base.py b/test/test_synthesis_engine_base.py index 63f976a0e..8202891b7 100644 --- a/test/test_synthesis_engine_base.py +++ b/test/test_synthesis_engine_base.py @@ -8,11 +8,11 @@ from voicevox_engine.synthesis_engine import SynthesisEngine -def yukarin_s_mock(length: int, phoneme_list: numpy.ndarray, speaker_id: numpy.ndarray): +def yukarin_s_mock(length: int, phoneme_list: numpy.ndarray, style_id: numpy.ndarray): result = [] # mockとしての適当な処理、特に意味はない for i in range(length): - result.append(round(float(phoneme_list[i] * 0.0625 + speaker_id), 2)) + result.append(round(float(phoneme_list[i] * 0.0625 + style_id), 2)) return numpy.array(result) @@ -24,7 +24,7 @@ def yukarin_sa_mock( end_accent_list: numpy.ndarray, start_accent_phrase_list: numpy.ndarray, end_accent_phrase_list: numpy.ndarray, - speaker_id: numpy.ndarray, + style_id: numpy.ndarray, ): result = [] # mockとしての適当な処理、特に意味はない @@ -41,7 +41,7 @@ def yukarin_sa_mock( + end_accent_phrase_list[0][i] ) * 0.0625 - + speaker_id + + style_id ), 2, ) @@ -54,7 +54,7 @@ def decode_mock( phoneme_size: int, f0: numpy.ndarray, phoneme: numpy.ndarray, - speaker_id: Union[numpy.ndarray, int], + style_id: Union[numpy.ndarray, int], ): result = [] # mockとしての適当な処理、特に意味はない @@ -64,7 +64,7 @@ def decode_mock( result.append( float( f0[i][0] * (numpy.where(phoneme[i] == 1)[0] / phoneme_size) - + speaker_id + + style_id ) ) return numpy.array(result) @@ -179,7 +179,7 @@ def metas(self): def supported_devices(self): return "" - def is_model_loaded(self, speaker_id): + def is_model_loaded(self, style_id): return True diff --git a/voicevox_engine/cancellable_engine.py b/voicevox_engine/cancellable_engine.py index 174549b40..c473c3e4a 100644 --- a/voicevox_engine/cancellable_engine.py +++ b/voicevox_engine/cancellable_engine.py @@ -140,7 +140,7 @@ def finalize_con( def _synthesis_impl( self, query: AudioQuery, - speaker_id: int, + style_id: int, request: Request, core_version: str | None, ) -> str: @@ -152,7 +152,7 @@ def _synthesis_impl( Parameters ---------- query: AudioQuery - speaker_id: int + style_id: int request: fastapi.Request 接続確立時に受け取ったものをそのまま渡せばよい https://fastapi.tiangolo.com/advanced/using-request-directly/ @@ -166,7 +166,7 @@ def _synthesis_impl( proc, sub_proc_con1 = self.procs_and_cons.get() self.watch_con_list.append((request, proc)) try: - sub_proc_con1.send((query, speaker_id, core_version)) + sub_proc_con1.send((query, style_id, core_version)) f_name = sub_proc_con1.recv() except EOFError: raise HTTPException(status_code=422, detail="既にサブプロセスは終了されています") @@ -231,7 +231,7 @@ def start_synthesis_subprocess( latest_core_version = get_latest_core_version(versions=synthesis_engines.keys()) while True: try: - query, speaker_id, core_version = sub_proc_con.recv() + query, style_id, core_version = sub_proc_con.recv() if core_version is None: _engine = synthesis_engines[latest_core_version] elif core_version in synthesis_engines: @@ -240,7 +240,7 @@ def start_synthesis_subprocess( # バージョンが見つからないエラー sub_proc_con.send("") continue - wave = _engine._synthesis_impl(query, speaker_id) + wave = _engine._synthesis_impl(query, style_id) with NamedTemporaryFile(delete=False) as f: soundfile.write( file=f, data=wave, samplerate=query.outputSamplingRate, format="WAV" diff --git a/voicevox_engine/dev/synthesis_engine/mock.py b/voicevox_engine/dev/synthesis_engine/mock.py index a2b5182ae..9f49f5953 100644 --- a/voicevox_engine/dev/synthesis_engine/mock.py +++ b/voicevox_engine/dev/synthesis_engine/mock.py @@ -38,7 +38,7 @@ def supported_devices(self) -> Optional[str]: return self._supported_devices def replace_phoneme_length( - self, accent_phrases: List[AccentPhrase], speaker_id: int + self, accent_phrases: List[AccentPhrase], style_id: int ) -> List[AccentPhrase]: """ replace_phoneme_length 入力accent_phrasesを変更せずにそのまま返します [Mock] @@ -47,8 +47,8 @@ def replace_phoneme_length( ---------- accent_phrases : List[AccentPhrase] フレーズ句のリスト - speaker_id : int - 話者 + style_id : int + スタイルID Returns ------- @@ -58,7 +58,7 @@ def replace_phoneme_length( return accent_phrases def replace_mora_pitch( - self, accent_phrases: List[AccentPhrase], speaker_id: int + self, accent_phrases: List[AccentPhrase], style_id: int ) -> List[AccentPhrase]: """ replace_mora_pitch 入力accent_phrasesを変更せずにそのまま返します [Mock] @@ -67,8 +67,8 @@ def replace_mora_pitch( ---------- accent_phrases : List[AccentPhrase] フレーズ句のリスト - speaker_id : int - 話者 + style_id : int + スタイルID Returns ------- @@ -77,7 +77,7 @@ def replace_mora_pitch( """ return accent_phrases - def _synthesis_impl(self, query: AudioQuery, speaker_id: int) -> np.ndarray: + def _synthesis_impl(self, query: AudioQuery, style_id: int) -> np.ndarray: """ synthesis voicevox coreを使わずに、音声合成する [Mock] @@ -85,8 +85,8 @@ def _synthesis_impl(self, query: AudioQuery, speaker_id: int) -> np.ndarray: ---------- query : AudioQuery /audio_query APIで得たjson - speaker_id : int - 話者 + style_id : int + スタイルID Returns ------- diff --git a/voicevox_engine/model.py b/voicevox_engine/model.py index 89d9e0011..f2d3524ee 100644 --- a/voicevox_engine/model.py +++ b/voicevox_engine/model.py @@ -112,10 +112,10 @@ class MorphableTargetInfo(BaseModel): # reason: Optional[str] = Field(title="is_morphableがfalseである場合、その理由") -class SpeakerNotFoundError(LookupError): - def __init__(self, speaker: int, *args: object, **kywrds: object) -> None: - self.speaker = speaker - super().__init__(f"speaker {speaker} is not found.", *args, **kywrds) +class StyleIdNotFoundError(LookupError): + def __init__(self, style_id: int, *args: object, **kywrds: object) -> None: + self.style_id = style_id + super().__init__(f"style_id {style_id} is not found.", *args, **kywrds) class LibrarySpeaker(BaseModel): diff --git a/voicevox_engine/morphing.py b/voicevox_engine/morphing.py index 47c6de71e..74c82fb7d 100644 --- a/voicevox_engine/morphing.py +++ b/voicevox_engine/morphing.py @@ -9,7 +9,7 @@ from .metas.Metas import Speaker, SpeakerSupportPermittedSynthesisMorphing, StyleInfo from .metas.MetasStore import construct_lookup -from .model import AudioQuery, MorphableTargetInfo, SpeakerNotFoundError +from .model import AudioQuery, MorphableTargetInfo, StyleIdNotFoundError from .synthesis_engine import SynthesisEngine @@ -80,15 +80,15 @@ def is_synthesis_morphing_permitted( target_speaker: int, ) -> bool: """ - 指定されたspeakerがモーフィング可能かどうか返す - speakerが見つからない場合はSpeakerNotFoundErrorを送出する + 指定されたstyle_idがモーフィング可能かどうか返す + style_idが見つからない場合はStyleIdNotFoundErrorを送出する """ base_speaker_data = speaker_lookup[base_speaker] target_speaker_data = speaker_lookup[target_speaker] if base_speaker_data is None or target_speaker_data is None: - raise SpeakerNotFoundError( + raise StyleIdNotFoundError( base_speaker if base_speaker_data is None else target_speaker ) @@ -141,10 +141,8 @@ def synthesis_morphing_parameter( # WORLDに掛けるため合成はモノラルで行う query.outputStereo = False - base_wave = engine.synthesis(query=query, speaker_id=base_speaker).astype("float") - target_wave = engine.synthesis(query=query, speaker_id=target_speaker).astype( - "float" - ) + base_wave = engine.synthesis(query=query, style_id=base_speaker).astype("float") + target_wave = engine.synthesis(query=query, style_id=target_speaker).astype("float") return create_morphing_parameter( base_wave=base_wave, diff --git a/voicevox_engine/synthesis_engine/core_wrapper.py b/voicevox_engine/synthesis_engine/core_wrapper.py index fe8f97787..b3852133f 100644 --- a/voicevox_engine/synthesis_engine/core_wrapper.py +++ b/voicevox_engine/synthesis_engine/core_wrapper.py @@ -443,14 +443,14 @@ def yukarin_s_forward( self, length: int, phoneme_list: np.ndarray, - speaker_id: np.ndarray, + style_id: np.ndarray, ) -> np.ndarray: output = np.zeros((length,), dtype=np.float32) self.assert_core_success( self.core.yukarin_s_forward( c_int(length), phoneme_list.ctypes.data_as(POINTER(c_long)), - speaker_id.ctypes.data_as(POINTER(c_long)), + style_id.ctypes.data_as(POINTER(c_long)), output.ctypes.data_as(POINTER(c_float)), ) ) @@ -465,11 +465,11 @@ def yukarin_sa_forward( end_accent_list: np.ndarray, start_accent_phrase_list: np.ndarray, end_accent_phrase_list: np.ndarray, - speaker_id: np.ndarray, + style_id: np.ndarray, ) -> np.ndarray: output = np.empty( ( - len(speaker_id), + len(style_id), length, ), dtype=np.float32, @@ -483,7 +483,7 @@ def yukarin_sa_forward( end_accent_list.ctypes.data_as(POINTER(c_long)), start_accent_phrase_list.ctypes.data_as(POINTER(c_long)), end_accent_phrase_list.ctypes.data_as(POINTER(c_long)), - speaker_id.ctypes.data_as(POINTER(c_long)), + style_id.ctypes.data_as(POINTER(c_long)), output.ctypes.data_as(POINTER(c_float)), ) ) @@ -495,7 +495,7 @@ def decode_forward( phoneme_size: int, f0: np.ndarray, phoneme: np.ndarray, - speaker_id: np.ndarray, + style_id: np.ndarray, ) -> np.ndarray: output = np.empty((length * 256,), dtype=np.float32) self.assert_core_success( @@ -504,7 +504,7 @@ def decode_forward( c_int(phoneme_size), f0.ctypes.data_as(POINTER(c_float)), phoneme.ctypes.data_as(POINTER(c_float)), - speaker_id.ctypes.data_as(POINTER(c_long)), + style_id.ctypes.data_as(POINTER(c_long)), output.ctypes.data_as(POINTER(c_float)), ) ) @@ -521,14 +521,14 @@ def finalize(self) -> None: return raise OldCoreError - def load_model(self, speaker_id: int) -> None: + def load_model(self, style_id: int) -> None: if self.exist_load_model: - self.assert_core_success(self.core.load_model(c_long(speaker_id))) + self.assert_core_success(self.core.load_model(c_long(style_id))) raise OldCoreError - def is_model_loaded(self, speaker_id: int) -> bool: + def is_model_loaded(self, style_id: int) -> bool: if self.exist_is_model_loaded: - return self.core.is_model_loaded(c_long(speaker_id)) + return self.core.is_model_loaded(c_long(style_id)) raise OldCoreError def assert_core_success(self, result: bool) -> None: diff --git a/voicevox_engine/synthesis_engine/synthesis_engine.py b/voicevox_engine/synthesis_engine/synthesis_engine.py index eb4565b17..81cad3d44 100644 --- a/voicevox_engine/synthesis_engine/synthesis_engine.py +++ b/voicevox_engine/synthesis_engine/synthesis_engine.py @@ -136,7 +136,7 @@ def __init__( core.yukarin_s_forward: 音素列から、音素ごとの長さを求める関数 length: 音素列の長さ phoneme_list: 音素列 - speaker_id: 話者番号 + style_id: スタイル番号 return: 音素ごとの長さ core.yukarin_sa_forward: モーラごとの音素列とアクセント情報から、モーラごとの音高を求める関数 @@ -147,7 +147,7 @@ def __init__( end_accent_list: アクセントの終了位置 start_accent_phrase_list: アクセント句の開始位置 end_accent_phrase_list: アクセント句の終了位置 - speaker_id: 話者番号 + style_id: スタイル番号 return: モーラごとの音高 core.decode_forward: フレームごとの音素と音高から波形を求める関数 @@ -155,7 +155,7 @@ def __init__( phoneme_size: 音素の種類数 f0: フレームごとの音高 phoneme: フレームごとの音素 - speaker_id: 話者番号 + style_id: スタイル番号 return: 音声波形 speakers: coreから取得したspeakersに関するjsonデータの文字列 @@ -182,25 +182,25 @@ def speakers(self) -> str: def supported_devices(self) -> Optional[str]: return self._supported_devices - def initialize_speaker_synthesis(self, speaker_id: int, skip_reinit: bool): + def initialize_style_id_synthesis(self, style_id: int, skip_reinit: bool): try: with self.mutex: # 以下の条件のいずれかを満たす場合, 初期化を実行する # 1. 引数 skip_reinit が False の場合 # 2. 話者が初期化されていない場合 - if (not skip_reinit) or (not self.core.is_model_loaded(speaker_id)): - self.core.load_model(speaker_id) + if (not skip_reinit) or (not self.core.is_model_loaded(style_id)): + self.core.load_model(style_id) except OldCoreError: pass # コアが古い場合はどうしようもないので何もしない - def is_initialized_speaker_synthesis(self, speaker_id: int) -> bool: + def is_initialized_style_id_synthesis(self, style_id: int) -> bool: try: - return self.core.is_model_loaded(speaker_id) + return self.core.is_model_loaded(style_id) except OldCoreError: return True # コアが古い場合はどうしようもないのでTrueを返す def replace_phoneme_length( - self, accent_phrases: List[AccentPhrase], speaker_id: int + self, accent_phrases: List[AccentPhrase], style_id: int ) -> List[AccentPhrase]: """ accent_phrasesの母音・子音の長さを設定する @@ -208,15 +208,15 @@ def replace_phoneme_length( ---------- accent_phrases : List[AccentPhrase] アクセント句モデルのリスト - speaker_id : int - 話者ID + style_id : int + スタイルID Returns ------- accent_phrases : List[AccentPhrase] 母音・子音の長さが設定されたアクセント句モデルのリスト """ # モデルがロードされていない場合はロードする - self.initialize_speaker_synthesis(speaker_id, skip_reinit=True) + self.initialize_style_id_synthesis(style_id, skip_reinit=True) # phoneme # AccentPhraseをすべてMoraおよびOjtPhonemeの形に分解し、処理可能な形にする flatten_moras, phoneme_data_list = pre_process(accent_phrases) @@ -233,7 +233,7 @@ def replace_phoneme_length( phoneme_length = self.core.yukarin_s_forward( length=len(phoneme_list_s), phoneme_list=phoneme_list_s, - speaker_id=numpy.array(speaker_id, dtype=numpy.int64).reshape(-1), + style_id=numpy.array(style_id, dtype=numpy.int64).reshape(-1), ) # yukarin_s_forwarderの結果をaccent_phrasesに反映する @@ -249,7 +249,7 @@ def replace_phoneme_length( return accent_phrases def replace_mora_pitch( - self, accent_phrases: List[AccentPhrase], speaker_id: int + self, accent_phrases: List[AccentPhrase], style_id: int ) -> List[AccentPhrase]: """ accent_phrasesの音高(ピッチ)を設定する @@ -257,15 +257,15 @@ def replace_mora_pitch( ---------- accent_phrases : List[AccentPhrase] アクセント句モデルのリスト - speaker_id : int - 話者ID + style_id : int + スタイルID Returns ------- accent_phrases : List[AccentPhrase] 音高(ピッチ)が設定されたアクセント句モデルのリスト """ # モデルがロードされていない場合はロードする - self.initialize_speaker_synthesis(speaker_id, skip_reinit=True) + self.initialize_style_id_synthesis(style_id, skip_reinit=True) # numpy.concatenateが空リストだとエラーを返すのでチェック if len(accent_phrases) == 0: return [] @@ -375,7 +375,7 @@ def _create_one_hot(accent_phrase: AccentPhrase, position: int): end_accent_list=end_accent_list[numpy.newaxis], start_accent_phrase_list=start_accent_phrase_list[numpy.newaxis], end_accent_phrase_list=end_accent_phrase_list[numpy.newaxis], - speaker_id=numpy.array(speaker_id, dtype=numpy.int64).reshape(-1), + style_id=numpy.array(style_id, dtype=numpy.int64).reshape(-1), )[0] # 無声母音を含むMoraに関しては、音高(ピッチ)を0にする @@ -390,22 +390,22 @@ def _create_one_hot(accent_phrase: AccentPhrase, position: int): return accent_phrases - def _synthesis_impl(self, query: AudioQuery, speaker_id: int): + def _synthesis_impl(self, query: AudioQuery, style_id: int): """ 音声合成クエリから音声合成に必要な情報を構成し、実際に音声合成を行う Parameters ---------- query : AudioQuery 音声合成クエリ - speaker_id : int - 話者ID + style_id : int + スタイルID Returns ------- wave : numpy.ndarray 音声合成結果 """ # モデルがロードされていない場合はロードする - self.initialize_speaker_synthesis(speaker_id, skip_reinit=True) + self.initialize_style_id_synthesis(style_id, skip_reinit=True) # phoneme # AccentPhraseをすべてMoraおよびOjtPhonemeの形に分解し、処理可能な形にする flatten_moras, phoneme_data_list = pre_process(query.accent_phrases) @@ -481,7 +481,7 @@ def _synthesis_impl(self, query: AudioQuery, speaker_id: int): phoneme_size=phoneme.shape[1], f0=f0[:, numpy.newaxis], phoneme=phoneme, - speaker_id=numpy.array(speaker_id, dtype=numpy.int64).reshape(-1), + style_id=numpy.array(style_id, dtype=numpy.int64).reshape(-1), ) # volume: ゲイン適用 diff --git a/voicevox_engine/synthesis_engine/synthesis_engine_base.py b/voicevox_engine/synthesis_engine/synthesis_engine_base.py index aaf4fc4a1..f6f798a7c 100644 --- a/voicevox_engine/synthesis_engine/synthesis_engine_base.py +++ b/voicevox_engine/synthesis_engine/synthesis_engine_base.py @@ -91,29 +91,30 @@ def speakers(self) -> str: def supported_devices(self) -> Optional[str]: raise NotImplementedError - def initialize_speaker_synthesis( # noqa: B027 - self, speaker_id: int, skip_reinit: bool + def initialize_style_id_synthesis( # noqa: B027 + self, + style_id: int, + skip_reinit: bool, ): - """ - 指定した話者での音声合成を初期化する。何度も実行可能。 + 指定したスタイルでの音声合成を初期化する。何度も実行可能。 未実装の場合は何もしない Parameters ---------- - speaker_id : int - 話者ID + style_id : int + スタイルID skip_reinit : bool True の場合, 既に初期化済みの話者の再初期化をスキップします """ pass - def is_initialized_speaker_synthesis(self, speaker_id: int) -> bool: + def is_initialized_style_id_synthesis(self, style_id: int) -> bool: """ - 指定した話者での音声合成が初期化されているかどうかを返す + 指定したスタイルでの音声合成が初期化されているかどうかを返す Parameters ---------- - speaker_id : int - 話者ID + style_id : int + スタイルID Returns ------- bool @@ -123,7 +124,7 @@ def is_initialized_speaker_synthesis(self, speaker_id: int) -> bool: @abstractmethod def replace_phoneme_length( - self, accent_phrases: List[AccentPhrase], speaker_id: int + self, accent_phrases: List[AccentPhrase], style_id: int ) -> List[AccentPhrase]: """ accent_phrasesの母音・子音の長さを設定する @@ -131,8 +132,8 @@ def replace_phoneme_length( ---------- accent_phrases : List[AccentPhrase] アクセント句モデルのリスト - speaker_id : int - 話者ID + style_id : int + スタイルID Returns ------- accent_phrases : List[AccentPhrase] @@ -142,7 +143,9 @@ def replace_phoneme_length( @abstractmethod def replace_mora_pitch( - self, accent_phrases: List[AccentPhrase], speaker_id: int + self, + accent_phrases: List[AccentPhrase], + style_id: int, ) -> List[AccentPhrase]: """ accent_phrasesの音高(ピッチ)を設定する @@ -150,8 +153,8 @@ def replace_mora_pitch( ---------- accent_phrases : List[AccentPhrase] アクセント句モデルのリスト - speaker_id : int - 話者ID + style_id : int + スタイルID Returns ------- accent_phrases : List[AccentPhrase] @@ -162,17 +165,17 @@ def replace_mora_pitch( def replace_mora_data( self, accent_phrases: List[AccentPhrase], - speaker_id: int, + style_id: int, ) -> List[AccentPhrase]: return self.replace_mora_pitch( accent_phrases=self.replace_phoneme_length( accent_phrases=accent_phrases, - speaker_id=speaker_id, + style_id=style_id, ), - speaker_id=speaker_id, + style_id=style_id, ) - def create_accent_phrases(self, text: str, speaker_id: int) -> List[AccentPhrase]: + def create_accent_phrases(self, text: str, style_id: int) -> List[AccentPhrase]: if len(text.strip()) == 0: return [] @@ -207,14 +210,14 @@ def create_accent_phrases(self, text: str, speaker_id: int) -> List[AccentPhrase breath_group.accent_phrases ) ], - speaker_id=speaker_id, + style_id=style_id, ) return accent_phrases def synthesis( self, query: AudioQuery, - speaker_id: int, + style_id: int, enable_interrogative_upspeak: bool = True, ) -> np.ndarray: """ @@ -224,8 +227,8 @@ def synthesis( ---------- query : AudioQuery 音声合成クエリ - speaker_id : int - 話者ID + style_id : int + スタイルID enable_interrogative_upspeak : bool 疑問系のテキストの語尾を自動調整する機能を有効にするか Returns @@ -239,18 +242,22 @@ def synthesis( query.accent_phrases = adjust_interrogative_accent_phrases( query.accent_phrases ) - return self._synthesis_impl(query, speaker_id) + return self._synthesis_impl(query, style_id) @abstractmethod - def _synthesis_impl(self, query: AudioQuery, speaker_id: int) -> np.ndarray: + def _synthesis_impl( + self, + query: AudioQuery, + style_id: int, + ) -> np.ndarray: """ 音声合成クエリから音声合成に必要な情報を構成し、実際に音声合成を行う Parameters ---------- query : AudioQuery 音声合成クエリ - speaker_id : int - 話者ID + style_id : int + スタイルID Returns ------- wave : numpy.ndarray