Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

整理: フルコンテキストラベル関連コメント/docstring/型ヒント #880

Merged
merged 12 commits into from
Dec 16, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 84 additions & 76 deletions voicevox_engine/tts_pipeline/full_context_label.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import re
from dataclasses import dataclass
from itertools import chain
from typing import Dict, List, Optional
from typing import Self

import pyopenjtalk

Expand All @@ -14,11 +14,11 @@ class Phoneme:

Attributes
----------
contexts: Dict[str, str]
contexts: dict[str, str]
音素の元
"""

contexts: Dict[str, str]
contexts: dict[str, str]

@classmethod
def from_label(cls, label: str):
Expand Down Expand Up @@ -81,11 +81,11 @@ def label(self):
@property
def phoneme(self):
"""
音素クラスの中で、発声に必要な要素を返す
音素クラスの中で、発声に必要なcontextを返す
Returns
-------
phoneme : str
発声に必要な要素を返す
発声に必要なcontextを返す
"""
return self.contexts["p3"]

Expand All @@ -111,13 +111,13 @@ class Mora:

Attributes
----------
consonant : Optional[Phoneme]
consonant : Phoneme | None
子音
vowel : Phoneme
母音
"""

consonant: Optional[Phoneme]
consonant: Phoneme | None
vowel: Phoneme

def set_context(self, key: str, value: str):
Expand All @@ -141,7 +141,7 @@ def phonemes(self):
音素群を返す
Returns
-------
phonemes : List[Phoneme]
phonemes : list[Phoneme]
母音しかない場合は母音のみ、子音もある場合は子音、母音の順番でPhonemeのリストを返す
"""
if self.consonant is not None:
Expand All @@ -155,7 +155,7 @@ def labels(self):
ラベル群を返す
Returns
-------
labels : List[str]
labels : list[str]
Moraに含まれるすべてのラベルを返す
"""
return [p.label for p in self.phonemes]
Expand All @@ -168,62 +168,69 @@ class AccentPhrase:
同じアクセントのMoraを複数保持する
Attributes
----------
moras : List[Mora]
moras : list[Mora]
音韻のリスト
accent : int
アクセント
"""

moras: List[Mora]
moras: list[Mora]
accent: int
is_interrogative: bool

@classmethod
def from_phonemes(cls, phonemes: List[Phoneme]):
"""
PhonemeのリストからAccentPhraseクラスを作成する
Parameters
----------
phonemes : List[Phoneme]
phonemeのリストを渡す
def from_phonemes(cls, phonemes: list[Phoneme]) -> Self:
"""音素系列をcontextで区切りAccentPhraseインスタンスを生成する"""

Returns
-------
accent_phrase : AccentPhrase
AccentPhraseクラスを返す
"""
moras: List[Mora] = []
# NOTE:「モーラごとの音素系列」は音素系列をcontextで区切り生成される。

moras: list[Mora] = [] # モーラ系列
mora_phonemes: list[Phoneme] = [] # モーラごとの音素系列を一時保存するコンテナ

mora_phonemes: List[Phoneme] = []
for phoneme, next_phoneme in zip(phonemes, phonemes[1:] + [None]):
# workaround for Hihosiba/voicevox_engine#57
# (py)openjtalk によるアクセント句内のモーラへの附番は 49 番目まで
# 49 番目のモーラについて、続く音素のモーラ番号を単一モーラの特定に使えない
# モーラ抽出を打ち切る(ワークアラウンド、VOICEVOX/voicevox_engine#57)
# context a2(モーラ番号)の最大値が 49 であるため、49番目以降のモーラでは音素のモーラ番号を区切りに使えない
if int(phoneme.contexts["a2"]) == 49:
break

# 区切りまで音素系列を一時保存する
mora_phonemes.append(phoneme)

# 一時的な音素系列を確定させて処理する
# a2はアクセント句内でのモーラ番号(1~49)
if (
next_phoneme is None
or phoneme.contexts["a2"] != next_phoneme.contexts["a2"]
):
# モーラごとの音素系列長に基づいて子音と母音を得る
if len(mora_phonemes) == 1:
consonant, vowel = None, mora_phonemes[0]
elif len(mora_phonemes) == 2:
consonant, vowel = mora_phonemes[0], mora_phonemes[1]
else:
raise ValueError(mora_phonemes)
# 子音と母音からモーラを生成して保存する
mora = Mora(consonant=consonant, vowel=vowel)
moras.append(mora)
# 次に向けてリセット
mora_phonemes = []

# アクセント位置を決定する
# f2はアクセント句のアクセント位置(1~49)
accent = int(moras[0].vowel.contexts["f2"])
# workaround for Hihosiba/voicevox_engine#55
# アクセント位置とするキー f2 の値がアクセント句内のモーラ数を超える場合がある
# f2 の値がアクセント句内のモーラ数を超える場合はクリップ(ワークアラウンド、VOICEVOX/voicevox_engine#55 を参照)
accent = accent if accent <= len(moras) else len(moras)

# 疑問文か否か判定する(末尾モーラ母音のcontextに基づく)
# f3はアクセント句が疑問文かどうか(1で疑問文)
is_interrogative = moras[-1].vowel.contexts["f3"] == "1"
return cls(moras=moras, accent=accent, is_interrogative=is_interrogative)

# AccentPhrase インスタンスを生成する
accent_phrase = cls(
moras=moras, accent=accent, is_interrogative=is_interrogative
)

return accent_phrase

def set_context(self, key: str, value: str):
"""
Expand All @@ -244,7 +251,7 @@ def phonemes(self):
音素群を返す
Returns
-------
phonemes : List[Phoneme]
phonemes : list[Phoneme]
AccentPhraseに間接的に含まれる全てのPhonemeを返す
"""
return list(chain.from_iterable(m.phonemes for m in self.moras))
Expand All @@ -255,7 +262,7 @@ def labels(self):
ラベル群を返す
Returns
-------
labels : List[str]
labels : list[str]
AccentPhraseに間接的に含まれる全てのラベルを返す
"""
return [p.label for p in self.phonemes]
Expand Down Expand Up @@ -288,41 +295,43 @@ class BreathGroup:
アクセントの異なるアクセント句を複数保持する
Attributes
----------
accent_phrases : List[AccentPhrase]
accent_phrases : list[AccentPhrase]
アクセント句のリスト
"""

accent_phrases: List[AccentPhrase]
accent_phrases: list[AccentPhrase]

@classmethod
def from_phonemes(cls, phonemes: List[Phoneme]):
"""
PhonemeのリストからBreathGroupクラスを作成する
Parameters
----------
phonemes : List[Phoneme]
phonemeのリストを渡す
def from_phonemes(cls, phonemes: list[Phoneme]) -> Self:
"""音素系列をcontextで区切りBreathGroupインスタンスを生成する"""

# NOTE:「アクセント句ごとの音素系列」は音素系列をcontextで区切り生成される。

accent_phrases: list[AccentPhrase] = [] # アクセント句系列
accent_phonemes: list[Phoneme] = [] # アクセント句ごとの音素系列を一時保存するコンテナ

Returns
-------
breath_group : BreathGroup
BreathGroupクラスを返す
"""
accent_phrases: List[AccentPhrase] = []
accent_phonemes: List[Phoneme] = []
for phoneme, next_phoneme in zip(phonemes, phonemes[1:] + [None]):
# 区切りまで音素系列を一時保存する
accent_phonemes.append(phoneme)

# 一時的な音素系列を確定させて処理する
# i3はBreathGroupの番号
# f5はBreathGroup内でのアクセント句の番号
if (
next_phoneme is None
or phoneme.contexts["i3"] != next_phoneme.contexts["i3"]
or phoneme.contexts["f5"] != next_phoneme.contexts["f5"]
):
# アクセント句を生成して保存する
accent_phrase = AccentPhrase.from_phonemes(accent_phonemes)
accent_phrases.append(accent_phrase)
# 次に向けてリセット
accent_phonemes = []

return cls(accent_phrases=accent_phrases)
# BreathGroup インスタンスを生成する
breath_group = cls(accent_phrases=accent_phrases)

return breath_group

def set_context(self, key: str, value: str):
"""
Expand All @@ -343,7 +352,7 @@ def phonemes(self):
音素群を返す
Returns
-------
phonemes : List[Phoneme]
phonemes : list[Phoneme]
BreathGroupに間接的に含まれる全てのPhonemeを返す
"""
return list(
Expand All @@ -358,7 +367,7 @@ def labels(self):
ラベル群を返す
Returns
-------
labels : List[str]
labels : list[str]
BreathGroupに間接的に含まれる全てのラベルを返す
"""
return [p.label for p in self.phonemes]
Expand All @@ -371,46 +380,45 @@ class Utterance:
発声の区切りと無音を複数保持する
Attributes
----------
breath_groups : List[BreathGroup]
breath_groups : list[BreathGroup]
発声の区切りのリスト
pauses : List[Phoneme]
pauses : list[Phoneme]
無音のリスト
"""

breath_groups: List[BreathGroup]
pauses: List[Phoneme]
breath_groups: list[BreathGroup]
pauses: list[Phoneme]

@classmethod
def from_phonemes(cls, phonemes: List[Phoneme]):
"""
Phonemeの完全なリストからUtteranceクラスを作成する
Parameters
----------
phonemes : List[Phoneme]
phonemeのリストを渡す
def from_phonemes(cls, phonemes: list[Phoneme]) -> Self:
"""音素系列をポーズで区切りUtteranceインスタンスを生成する"""

Returns
-------
utterance : Utterance
Utteranceクラスを返す
"""
pauses: List[Phoneme] = []
# NOTE:「BreathGroupごとの音素系列」は音素系列をポーズで区切り生成される。

pauses: list[Phoneme] = [] # ポーズ音素のリスト
breath_groups: list[BreathGroup] = [] # BreathGroup のリスト
group_phonemes: list[Phoneme] = [] # BreathGroupごとの音素系列を一時保存するコンテナ

breath_groups: List[BreathGroup] = []
group_phonemes: List[Phoneme] = []
for phoneme in phonemes:
# ポーズが出現するまで音素系列を一時保存する
if not phoneme.is_pause():
group_phonemes.append(phoneme)

# 一時的な音素系列を確定させて処理する
else:
# ポーズ音素を保存する
pauses.append(phoneme)

if len(group_phonemes) > 0:
# 音素系列からBreathGroupを生成して保存する
breath_group = BreathGroup.from_phonemes(group_phonemes)
breath_groups.append(breath_group)
# 次に向けてリセット
group_phonemes = []

return cls(breath_groups=breath_groups, pauses=pauses)
# Utteranceインスタンスを生成する
utterance = cls(breath_groups=breath_groups, pauses=pauses)

return utterance

def set_context(self, key: str, value: str):
"""
Expand All @@ -431,7 +439,7 @@ def phonemes(self):
音素群を返す
Returns
-------
phonemes : List[Phoneme]
phonemes : list[Phoneme]
Utteranceクラスに直接的・間接的に含まれる、全てのPhonemeを返す
"""
accent_phrases = list(
Expand Down Expand Up @@ -496,7 +504,7 @@ def phonemes(self):
),
)

phonemes: List[Phoneme] = []
phonemes: list[Phoneme] = []
for i in range(len(self.pauses)):
if self.pauses[i] is not None:
phonemes += [self.pauses[i]]
Expand All @@ -512,7 +520,7 @@ def labels(self):
ラベル群を返す
Returns
-------
labels : List[str]
labels : list[str]
Utteranceクラスに直接的・間接的に含まれる全てのラベルを返す
"""
return [p.label for p in self.phonemes]
Expand Down