diff --git a/voicevox_engine/core_adapter.py b/voicevox_engine/core_adapter.py index 1d9bc1ed6..9197e3876 100644 --- a/voicevox_engine/core_adapter.py +++ b/voicevox_engine/core_adapter.py @@ -1,7 +1,7 @@ import threading -import numpy -from numpy import ndarray +import numpy as np +from numpy.typing import NDArray from .core_wrapper import CoreWrapper, OldCoreError from .metas.Metas import StyleId @@ -67,55 +67,58 @@ def is_initialized_style_id_synthesis(self, style_id: StyleId) -> bool: return True # コアが古い場合はどうしようもないのでTrueを返す def safe_yukarin_s_forward( - self, phoneme_list_s: ndarray, style_id: StyleId - ) -> ndarray: + self, phoneme_list_s: NDArray[np.int64], style_id: StyleId + ) -> NDArray[np.float32]: # 「指定スタイルを初期化」「mutexによる安全性」「系列長・データ型に関するアダプター」を提供する self.initialize_style_id_synthesis(style_id, skip_reinit=True) with self.mutex: phoneme_length = self.core.yukarin_s_forward( length=len(phoneme_list_s), phoneme_list=phoneme_list_s, - style_id=numpy.array(style_id, dtype=numpy.int64).reshape(-1), + style_id=np.array(style_id, dtype=np.int64).reshape(-1), ) return phoneme_length def safe_yukarin_sa_forward( self, - vowel_phoneme_list: ndarray, - consonant_phoneme_list: ndarray, - start_accent_list: ndarray, - end_accent_list: ndarray, - start_accent_phrase_list: ndarray, - end_accent_phrase_list: ndarray, + vowel_phoneme_list: NDArray[np.int64], + consonant_phoneme_list: NDArray[np.int64], + start_accent_list: NDArray[np.int64], + end_accent_list: NDArray[np.int64], + start_accent_phrase_list: NDArray[np.int64], + end_accent_phrase_list: NDArray[np.int64], style_id: StyleId, - ) -> ndarray: + ) -> NDArray[np.float32]: # 「指定スタイルを初期化」「mutexによる安全性」「系列長・データ型に関するアダプター」を提供する self.initialize_style_id_synthesis(style_id, skip_reinit=True) with self.mutex: f0_list = self.core.yukarin_sa_forward( length=vowel_phoneme_list.shape[0], - vowel_phoneme_list=vowel_phoneme_list[numpy.newaxis], - consonant_phoneme_list=consonant_phoneme_list[numpy.newaxis], - start_accent_list=start_accent_list[numpy.newaxis], - 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], - style_id=numpy.array(style_id, dtype=numpy.int64).reshape(-1), + vowel_phoneme_list=vowel_phoneme_list[np.newaxis], + consonant_phoneme_list=consonant_phoneme_list[np.newaxis], + start_accent_list=start_accent_list[np.newaxis], + end_accent_list=end_accent_list[np.newaxis], + start_accent_phrase_list=start_accent_phrase_list[np.newaxis], + end_accent_phrase_list=end_accent_phrase_list[np.newaxis], + style_id=np.array(style_id, dtype=np.int64).reshape(-1), )[0] return f0_list def safe_decode_forward( - self, phoneme: ndarray, f0: ndarray, style_id: StyleId - ) -> tuple[ndarray, int]: + self, + phoneme: NDArray[np.float32], + f0: NDArray[np.float32], + style_id: StyleId, + ) -> tuple[NDArray[np.float32], int]: # 「指定スタイルを初期化」「mutexによる安全性」「系列長・データ型に関するアダプター」を提供する self.initialize_style_id_synthesis(style_id, skip_reinit=True) with self.mutex: wave = self.core.decode_forward( length=phoneme.shape[0], phoneme_size=phoneme.shape[1], - f0=f0[:, numpy.newaxis], + f0=f0[:, np.newaxis], phoneme=phoneme, - style_id=numpy.array(style_id, dtype=numpy.int64).reshape(-1), + style_id=np.array(style_id, dtype=np.int64).reshape(-1), ) sr_wave = self.default_sampling_rate return wave, sr_wave diff --git a/voicevox_engine/core_wrapper.py b/voicevox_engine/core_wrapper.py index 6ba4eeadc..83fa5f417 100644 --- a/voicevox_engine/core_wrapper.py +++ b/voicevox_engine/core_wrapper.py @@ -8,6 +8,7 @@ from typing import Literal import numpy as np +from numpy.typing import NDArray class OldCoreError(Exception): @@ -525,21 +526,24 @@ def metas(self) -> str: return self.core.metas().decode("utf-8") def yukarin_s_forward( - self, length: int, phoneme_list: np.ndarray, style_id: np.ndarray - ) -> np.ndarray: + self, + length: int, + phoneme_list: NDArray[np.int64], + style_id: NDArray[np.int64], + ) -> NDArray[np.float32]: """ 音素列から、音素ごとの長さを求める関数 Parameters ---------- length : int 音素列の長さ - phoneme_list : np.ndarray + phoneme_list : NDArray[np.int64] 音素列 - style_id : np.ndarray + style_id : NDArray[np.int64] スタイル番号 Returns ------- - output : np.ndarray + output : NDArray[np.float32] 音素ごとの長さ """ output = np.zeros((length,), dtype=np.float32) @@ -556,37 +560,37 @@ def yukarin_s_forward( def yukarin_sa_forward( self, length: int, - vowel_phoneme_list: np.ndarray, - consonant_phoneme_list: np.ndarray, - start_accent_list: np.ndarray, - end_accent_list: np.ndarray, - start_accent_phrase_list: np.ndarray, - end_accent_phrase_list: np.ndarray, - style_id: np.ndarray, - ) -> np.ndarray: + vowel_phoneme_list: NDArray[np.int64], + consonant_phoneme_list: NDArray[np.int64], + start_accent_list: NDArray[np.int64], + end_accent_list: NDArray[np.int64], + start_accent_phrase_list: NDArray[np.int64], + end_accent_phrase_list: NDArray[np.int64], + style_id: NDArray[np.int64], + ) -> NDArray[np.float32]: """ モーラごとの音素列とアクセント情報から、モーラごとの音高を求める関数 Parameters ---------- length : int モーラ列の長さ - vowel_phoneme_list : np.ndarray + vowel_phoneme_list : NDArray[np.int64] 母音の音素列 - consonant_phoneme_list : np.ndarray + consonant_phoneme_list : NDArray[np.int64] 子音の音素列 - start_accent_list : np.ndarray + start_accent_list : NDArray[np.int64] アクセントの開始位置 - end_accent_list : np.ndarray + end_accent_list : NDArray[np.int64] アクセントの終了位置 - start_accent_phrase_list : np.ndarray + start_accent_phrase_list : NDArray[np.int64] アクセント句の開始位置 - end_accent_phrase_list : np.ndarray + end_accent_phrase_list : NDArray[np.int64] アクセント句の終了位置 - style_id : np.ndarray + style_id : NDArray[np.int64] スタイル番号 Returns ------- - output : np.ndarray + output : NDArray[np.float32] モーラごとの音高 """ output = np.empty( @@ -615,10 +619,10 @@ def decode_forward( self, length: int, phoneme_size: int, - f0: np.ndarray, - phoneme: np.ndarray, - style_id: np.ndarray, - ) -> np.ndarray: + f0: NDArray[np.float32], + phoneme: NDArray[np.float32], + style_id: NDArray[np.int64], + ) -> NDArray[np.float32]: """ フレームごとの音素と音高から波形を求める関数 Parameters @@ -627,15 +631,15 @@ def decode_forward( フレームの長さ phoneme_size : int 音素の種類数 - f0 : np.ndarray + f0 : NDArray[np.float32] フレームごとの音高 - phoneme : np.ndarray + phoneme : NDArray[np.float32] フレームごとの音素 - style_id : np.ndarray + style_id : NDArray[np.int64] スタイル番号 Returns ------- - output : np.ndarray + output : NDArray[np.float32] 音声波形 """ diff --git a/voicevox_engine/dev/core/mock.py b/voicevox_engine/dev/core/mock.py index 31918e0f3..51b06db28 100644 --- a/voicevox_engine/dev/core/mock.py +++ b/voicevox_engine/dev/core/mock.py @@ -1,8 +1,9 @@ import json from pathlib import Path -import numpy +import numpy as np from numpy import ndarray +from numpy.typing import NDArray from ...core_wrapper import CoreWrapper @@ -65,13 +66,13 @@ def metas(self) -> str: def yukarin_s_forward( self, length: int, phoneme_list: ndarray, style_id: ndarray - ) -> ndarray: + ) -> NDArray[np.floating]: """音素系列サイズ・音素ID系列・スタイルIDから音素長系列を生成する""" result = [] # mockとしての適当な処理、特に意味はない for i in range(length): result.append(round((phoneme_list[i] * 0.0625 + style_id).item(), 2)) - return numpy.array(result) + return np.array(result) def yukarin_sa_forward( self, @@ -83,7 +84,7 @@ def yukarin_sa_forward( start_accent_phrase_list: ndarray, end_accent_phrase_list: ndarray, style_id: ndarray, - ) -> ndarray: + ) -> NDArray[np.floating]: """モーラ系列サイズ・母音系列・子音系列・アクセント位置・アクセント句区切り・スタイルIDからモーラ音高系列を生成する""" assert length > 1, "前後無音を必ず付与しなければならない" @@ -107,7 +108,7 @@ def yukarin_sa_forward( 2, ) ) - return numpy.array(result)[numpy.newaxis] + return np.array(result)[np.newaxis] def decode_forward( self, @@ -116,15 +117,15 @@ def decode_forward( f0: ndarray, phoneme: ndarray, style_id: ndarray, - ) -> ndarray: + ) -> NDArray[np.floating]: """フレーム長・音素種類数・フレーム音高・フレーム音素onehot・スタイルIDからダミー音声波形を生成する""" # 入力値を反映し、長さが 256 倍であるダミー配列を出力する result: list[ndarray] = [] for i in range(length): result += [ - (f0[i, 0] * (numpy.where(phoneme[i] == 1)[0] / phoneme_size) + style_id) + (f0[i, 0] * (np.where(phoneme[i] == 1)[0] / phoneme_size) + style_id) ] * 256 - return numpy.array(result) + return np.array(result) def supported_devices(self): return json.dumps( diff --git a/voicevox_engine/dev/tts_engine/mock.py b/voicevox_engine/dev/tts_engine/mock.py index c91e06aa6..e85c44aa5 100644 --- a/voicevox_engine/dev/tts_engine/mock.py +++ b/voicevox_engine/dev/tts_engine/mock.py @@ -1,8 +1,9 @@ import copy from logging import getLogger -from typing import Any, Dict +from typing import Any import numpy as np +from numpy.typing import NDArray from pyopenjtalk import tts from soxr import resample @@ -24,7 +25,7 @@ def synthesize_wave( query: AudioQuery, style_id: StyleId, enable_interrogative_upspeak: bool = True, - ) -> np.ndarray: + ) -> NDArray[np.float32]: """音声合成用のクエリに含まれる読み仮名に基づいてOpenJTalkで音声波形を生成する""" # モーフィング時などに同一参照のqueryで複数回呼ばれる可能性があるので、元の引数のqueryに破壊的変更を行わない query = copy.deepcopy(query) @@ -38,9 +39,9 @@ def synthesize_wave( # volume wave *= query.volumeScale - return wave.astype("int16") + return wave - def forward(self, text: str, **kwargs: Dict[str, Any]) -> np.ndarray: + def forward(self, text: str, **kwargs: dict[str, Any]) -> NDArray[np.float32]: """ forward tts via pyopenjtalk.tts() 参照→TTSEngine のdocstring [Mock] @@ -52,7 +53,7 @@ def forward(self, text: str, **kwargs: Dict[str, Any]) -> np.ndarray: Returns ------- - wave [npt.NDArray[np.int16]] + wave [NDArray[np.float32]] 音声波形データをNumPy配列で返します Note @@ -63,10 +64,11 @@ def forward(self, text: str, **kwargs: Dict[str, Any]) -> np.ndarray: dtype=np.float64, 16 bit, mono 48000 Hz # resampleの説明 - 非モック実装(decode_forward)と合わせるために、出力を24kHzに変換した。 + 非モック実装(decode_forward)と合わせるために、出力を24kHz、32bit浮動小数に変換した。 """ logger = getLogger("uvicorn") # FastAPI / Uvicorn 内からの利用のため logger.info("[Mock] input text: %s" % text) wave, sr = tts(text) + wave /= 2**15 wave = resample(wave, 48000, 24000) - return wave + return wave.astype(np.float32) diff --git a/voicevox_engine/morphing.py b/voicevox_engine/morphing.py index f94fbb061..e5d7cbd7e 100644 --- a/voicevox_engine/morphing.py +++ b/voicevox_engine/morphing.py @@ -1,10 +1,10 @@ from copy import deepcopy from dataclasses import dataclass from itertools import chain -from typing import Dict, List, Tuple import numpy as np import pyworld as pw +from numpy.typing import NDArray from soxr import resample from .core_adapter import CoreAdapter @@ -19,20 +19,19 @@ from .tts_pipeline import TTSEngine -# FIXME: ndarray type hint, https://github.com/JeremyCCHsu/Python-Wrapper-for-World-Vocoder/blob/2b64f86197573497c685c785c6e0e743f407b63e/pyworld/pyworld.pyx#L398 # noqa @dataclass(frozen=True) class MorphingParameter: fs: int frame_period: float - base_f0: np.ndarray - base_aperiodicity: np.ndarray - base_spectrogram: np.ndarray - target_spectrogram: np.ndarray + base_f0: NDArray[np.double] + base_aperiodicity: NDArray[np.double] + base_spectrogram: NDArray[np.double] + target_spectrogram: NDArray[np.double] def create_morphing_parameter( - base_wave: np.ndarray, - target_wave: np.ndarray, + base_wave: NDArray[np.double], + target_wave: NDArray[np.double], fs: int, ) -> MorphingParameter: frame_period = 1.0 @@ -55,9 +54,9 @@ def create_morphing_parameter( def get_morphable_targets( - speakers: List[Speaker], - base_style_ids: List[StyleId], -) -> List[Dict[StyleId, MorphableTargetInfo]]: + speakers: list[Speaker], + base_style_ids: list[StyleId], +) -> list[dict[StyleId, MorphableTargetInfo]]: """ speakers: 全話者の情報 base_speakers: モーフィング可能か判定したいベースのスタイルIDリスト @@ -81,7 +80,7 @@ def get_morphable_targets( def is_synthesis_morphing_permitted( - speaker_lookup: Dict[StyleId, Tuple[Speaker, SpeakerStyle]], + speaker_lookup: dict[StyleId, tuple[Speaker, SpeakerStyle]], base_style_id: StyleId, target_style_id: StyleId, ) -> bool: @@ -163,7 +162,7 @@ def synthesis_morphing( morph_rate: float, output_fs: int, output_stereo: bool = False, -) -> np.ndarray: +) -> NDArray[np.float64]: """ 指定した割合で、パラメータをもとにモーフィングした音声を生成します。 @@ -178,7 +177,7 @@ def synthesis_morphing( Returns ------- - generated : np.ndarray + generated : NDArray[np.float64] モーフィングした音声 Raises diff --git a/voicevox_engine/tts_pipeline/acoustic_feature_extractor.py b/voicevox_engine/tts_pipeline/acoustic_feature_extractor.py index a3e774ec4..6c861e16a 100644 --- a/voicevox_engine/tts_pipeline/acoustic_feature_extractor.py +++ b/voicevox_engine/tts_pipeline/acoustic_feature_extractor.py @@ -1,6 +1,7 @@ from typing import Literal -import numpy +import numpy as np +from numpy.typing import NDArray # NOTE: `Vowel` は母音 (a/i/u/e/o の有声・無声) + 無音 pau + 撥音 N ("ん") + 促音 cl ("っ") # NOTE: 型の名称は暫定的 @@ -121,8 +122,8 @@ def phoneme_id(self) -> int: return self._PHONEME_LIST.index(self.phoneme) @property - def onehot(self): + def onehot(self) -> NDArray[np.float32]: """音素onehotベクトルを取得する""" - vec = numpy.zeros(self._NUM_PHONEME, dtype=numpy.float32) + vec = np.zeros(self._NUM_PHONEME, dtype=np.float32) vec[self.phoneme_id] = 1.0 return vec diff --git a/voicevox_engine/tts_pipeline/tts_engine.py b/voicevox_engine/tts_pipeline/tts_engine.py index 1e12eddd6..69692ec40 100644 --- a/voicevox_engine/tts_pipeline/tts_engine.py +++ b/voicevox_engine/tts_pipeline/tts_engine.py @@ -1,8 +1,8 @@ import copy import math -import numpy -from numpy import ndarray +import numpy as np +from numpy.typing import NDArray from soxr import resample from ..core_adapter import CoreAdapter @@ -131,7 +131,9 @@ def apply_speed_scale(moras: list[Mora], query: AudioQuery) -> list[Mora]: return moras -def count_frame_per_unit(moras: list[Mora]) -> tuple[ndarray, ndarray]: +def count_frame_per_unit( + moras: list[Mora], +) -> tuple[NDArray[np.integer], NDArray[np.integer]]: """ 音素あたり・モーラあたりのフレーム長を算出する Parameters @@ -140,9 +142,9 @@ def count_frame_per_unit(moras: list[Mora]) -> tuple[ndarray, ndarray]: モーラ系列 Returns ------- - frame_per_phoneme : ndarray + frame_per_phoneme : NDArray[np.integer] 音素あたりのフレーム長。端数丸め。shape = (Phoneme,) - frame_per_mora : ndarray + frame_per_mora : NDArray[np.integer] モーラあたりのフレーム長。端数丸め。shape = (Mora,) """ frame_per_phoneme: list[int] = [] @@ -159,13 +161,13 @@ def count_frame_per_unit(moras: list[Mora]) -> tuple[ndarray, ndarray]: frame_per_phoneme += [vowel_frames] frame_per_mora += [mora_frames] - return numpy.array(frame_per_phoneme), numpy.array(frame_per_mora) + return np.array(frame_per_phoneme), np.array(frame_per_mora) def _to_frame(sec: float) -> int: FRAMERATE = 93.75 # 24000 / 256 [frame/sec] # NOTE: `round` は偶数丸め。移植時に取扱い注意。詳細は voicevox_engine#552 - return numpy.round(sec * FRAMERATE).astype(numpy.int32).item() + return np.round(sec * FRAMERATE).astype(np.int32).item() def apply_pitch_scale(moras: list[Mora], query: AudioQuery) -> list[Mora]: @@ -179,22 +181,21 @@ def apply_intonation_scale(moras: list[Mora], query: AudioQuery) -> list[Mora]: """モーラ系列へ音声合成用のクエリがもつ抑揚スケール(`intonationScale`)を適用する""" # 有声音素 (f0>0) の平均値に対する乖離度をスケール voiced = list(filter(lambda mora: mora.pitch > 0, moras)) - mean_f0 = numpy.mean(list(map(lambda mora: mora.pitch, voiced))).item() + mean_f0 = np.mean(list(map(lambda mora: mora.pitch, voiced))).item() if mean_f0 != math.nan: # 空リスト -> NaN for mora in voiced: mora.pitch = (mora.pitch - mean_f0) * query.intonationScale + mean_f0 return moras -def apply_volume_scale(wave: numpy.ndarray, query: AudioQuery) -> numpy.ndarray: +def apply_volume_scale(wave: np.ndarray, query: AudioQuery) -> NDArray[np.floating]: """音声波形へ音声合成用のクエリがもつ音量スケール(`volumeScale`)を適用する""" - wave *= query.volumeScale - return wave + return wave * query.volumeScale def apply_output_sampling_rate( - wave: ndarray, sr_wave: int, query: AudioQuery -) -> ndarray: + wave: NDArray[np.floating], sr_wave: float, query: AudioQuery +) -> NDArray[np.floating]: """音声波形へ音声合成用のクエリがもつ出力サンプリングレート(`outputSamplingRate`)を適用する""" # サンプリングレート一致のときはスルー if sr_wave == query.outputSamplingRate: @@ -203,14 +204,18 @@ def apply_output_sampling_rate( return wave -def apply_output_stereo(wave: ndarray, query: AudioQuery) -> ndarray: +def apply_output_stereo( + wave: NDArray[np.floating], query: AudioQuery +) -> NDArray[np.floating]: """音声波形へ音声合成用のクエリがもつステレオ出力設定(`outputStereo`)を適用する""" if query.outputStereo: - wave = numpy.array([wave, wave]).T + wave = np.array([wave, wave]).T return wave -def query_to_decoder_feature(query: AudioQuery) -> tuple[ndarray, ndarray]: +def query_to_decoder_feature( + query: AudioQuery, +) -> tuple[NDArray[np.float32], NDArray[np.float32]]: """音声合成用のクエリからフレームごとの音素 (shape=(フレーム長, 音素数)) と音高 (shape=(フレーム長,)) を得る""" moras = to_flatten_moras(query.accent_phrases) @@ -221,18 +226,20 @@ def query_to_decoder_feature(query: AudioQuery) -> tuple[ndarray, ndarray]: moras = apply_intonation_scale(moras, query) # 表現を変更する(音素クラス → 音素 onehot ベクトル、モーラクラス → 音高スカラ) - phoneme = numpy.stack([p.onehot for p in to_flatten_phonemes(moras)]) - f0 = numpy.array([mora.pitch for mora in moras], dtype=numpy.float32) + phoneme = np.stack([p.onehot for p in to_flatten_phonemes(moras)]) + f0 = np.array([mora.pitch for mora in moras], dtype=np.float32) # 時間スケールを変更する(音素・モーラ → フレーム) frame_per_phoneme, frame_per_mora = count_frame_per_unit(moras) - phoneme = numpy.repeat(phoneme, frame_per_phoneme, axis=0) - f0 = numpy.repeat(f0, frame_per_mora) + phoneme = np.repeat(phoneme, frame_per_phoneme, axis=0) + f0 = np.repeat(f0, frame_per_mora) return phoneme, f0 -def raw_wave_to_output_wave(query: AudioQuery, wave: ndarray, sr_wave: int) -> ndarray: +def raw_wave_to_output_wave( + query: AudioQuery, wave: np.ndarray, sr_wave: int +) -> NDArray[np.floating]: """生音声波形に音声合成用のクエリを適用して出力音声波形を生成する""" wave = apply_volume_scale(wave, query) wave = apply_output_sampling_rate(wave, sr_wave, query) @@ -260,7 +267,7 @@ def update_length( phonemes = [Phoneme("pau")] + phonemes + [Phoneme("pau")] # 音素クラスから音素IDスカラへ表現を変換する - phoneme_ids = numpy.array([p.phoneme_id for p in phonemes], dtype=numpy.int64) + phoneme_ids = np.array([p.phoneme_id for p in phonemes], dtype=np.int64) # コアを用いて音素長を生成する phoneme_lengths = self._core.safe_yukarin_s_forward(phoneme_ids, style_id) @@ -287,9 +294,11 @@ def update_pitch( return [] # accent - def _create_one_hot(accent_phrase: AccentPhrase, position: int) -> ndarray: + def _create_one_hot( + accent_phrase: AccentPhrase, position: int + ) -> NDArray[np.floating]: """ - 単位行列(numpy.eye)を応用し、accent_phrase内でone hotな配列(リスト)を作る + 単位行列(np.eye)を応用し、accent_phrase内でone hotな配列(リスト)を作る 例えば、accent_phraseのmorasの長さが12、positionが1なら [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] morasの長さが同じく12、positionが-1なら @@ -304,16 +313,16 @@ def _create_one_hot(accent_phrase: AccentPhrase, position: int) -> ndarray: one hotにするindex Returns ------- - one_hot : numpy.ndarray + one_hot : NDArray[np.floating] one hotな配列(リスト) """ - return numpy.r_[ - numpy.eye(len(accent_phrase.moras))[position], + return np.r_[ + np.eye(len(accent_phrase.moras))[position], (0 if accent_phrase.pause_mora is not None else []), ] # アクセントの開始/終了位置リストを作る - start_accent_list = numpy.concatenate( + start_accent_list = np.concatenate( [ # accentはプログラミング言語におけるindexのように0始まりではなく1始まりなので、 # accentが1の場合は0番目を指定している @@ -322,7 +331,7 @@ def _create_one_hot(accent_phrase: AccentPhrase, position: int) -> ndarray: for accent_phrase in accent_phrases ] ) - end_accent_list = numpy.concatenate( + end_accent_list = np.concatenate( [ # accentはプログラミング言語におけるindexのように0始まりではなく1始まりなので、1を引いている _create_one_hot(accent_phrase, accent_phrase.accent - 1) @@ -331,35 +340,33 @@ def _create_one_hot(accent_phrase: AccentPhrase, position: int) -> ndarray: ) # アクセント句の開始/終了位置リストを作る - start_accent_phrase_list = numpy.concatenate( + start_accent_phrase_list = np.concatenate( [_create_one_hot(accent_phrase, 0) for accent_phrase in accent_phrases] ) - end_accent_phrase_list = numpy.concatenate( + end_accent_phrase_list = np.concatenate( [_create_one_hot(accent_phrase, -1) for accent_phrase in accent_phrases] ) # 前後無音を付加する - start_accent_list = numpy.r_[0, start_accent_list, 0] - end_accent_list = numpy.r_[0, end_accent_list, 0] - start_accent_phrase_list = numpy.r_[0, start_accent_phrase_list, 0] - end_accent_phrase_list = numpy.r_[0, end_accent_phrase_list, 0] + start_accent_list = np.r_[0, start_accent_list, 0] + end_accent_list = np.r_[0, end_accent_list, 0] + start_accent_phrase_list = np.r_[0, start_accent_phrase_list, 0] + end_accent_phrase_list = np.r_[0, end_accent_phrase_list, 0] # キャスト - start_accent_list = numpy.array(start_accent_list, dtype=numpy.int64) - end_accent_list = numpy.array(end_accent_list, dtype=numpy.int64) - start_accent_phrase_list = numpy.array( - start_accent_phrase_list, dtype=numpy.int64 - ) - end_accent_phrase_list = numpy.array(end_accent_phrase_list, dtype=numpy.int64) + start_accent_list = np.array(start_accent_list, dtype=np.int64) + end_accent_list = np.array(end_accent_list, dtype=np.int64) + start_accent_phrase_list = np.array(start_accent_phrase_list, dtype=np.int64) + end_accent_phrase_list = np.array(end_accent_phrase_list, dtype=np.int64) # アクセント句系列から(前後の無音含まない)モーラ系列と(前後の無音含む)音素系列を抽出する moras, phonemes = pre_process(accent_phrases) # 前後無音付加済みの音素系列から子音ID系列・母音ID系列を抽出する consonants, vowels = split_mora(phonemes) - vowel_ids = numpy.array([p.phoneme_id for p in vowels], dtype=numpy.int64) - consonant_ids = numpy.array( - [p.phoneme_id if p else -1 for p in consonants], dtype=numpy.int64 + vowel_ids = np.array([p.phoneme_id for p in vowels], dtype=np.int64) + consonant_ids = np.array( + [p.phoneme_id if p else -1 for p in consonants], dtype=np.int64 ) # コアを用いてモーラ音高を生成する @@ -411,7 +418,7 @@ def synthesize_wave( query: AudioQuery, style_id: StyleId, enable_interrogative_upspeak: bool = True, - ) -> ndarray: + ) -> NDArray[np.floating]: """音声合成用のクエリ・スタイルID・疑問文語尾自動調整フラグに基づいて音声波形を生成する""" # モーフィング時などに同一参照のqueryで複数回呼ばれる可能性があるので、元の引数のqueryに破壊的変更を行わない query = copy.deepcopy(query) diff --git a/voicevox_engine/utility/connect_base64_waves.py b/voicevox_engine/utility/connect_base64_waves.py index 900211e3c..287e14078 100644 --- a/voicevox_engine/utility/connect_base64_waves.py +++ b/voicevox_engine/utility/connect_base64_waves.py @@ -1,9 +1,9 @@ import base64 import io -from typing import List, Tuple import numpy as np import soundfile +from numpy.typing import NDArray from soxr import resample @@ -12,7 +12,7 @@ def __init__(self, message: str): self.message = message -def decode_base64_waves(waves: List[str]) -> List[Tuple[np.ndarray, int]]: +def decode_base64_waves(waves: list[str]) -> list[tuple[NDArray[np.float64], int]]: """ base64エンコードされた複数のwavデータをデコードする Parameters @@ -21,7 +21,7 @@ def decode_base64_waves(waves: List[str]) -> List[Tuple[np.ndarray, int]]: base64エンコードされたwavデータのリスト Returns ------- - waves_nparray_sr: List[Tuple[np.ndarray, int]] + waves_nparray_sr: List[Tuple[NDArray[np.float64], int]] (NumPy配列の音声波形データ, サンプリングレート) 形式のタプルのリスト """ if len(waves) == 0: @@ -42,7 +42,7 @@ def decode_base64_waves(waves: List[str]) -> List[Tuple[np.ndarray, int]]: return waves_nparray_sr -def connect_base64_waves(waves: List[str]) -> Tuple[np.ndarray, int]: +def connect_base64_waves(waves: list[str]) -> tuple[NDArray[np.float64], int]: waves_nparray_sr = decode_base64_waves(waves) max_sampling_rate = max([sr for _, sr in waves_nparray_sr])