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

整理: Character 利用範囲を拡大 #1385

Merged
merged 13 commits into from
Jun 20, 2024
Merged
63 changes: 46 additions & 17 deletions test/unit/test_metas_store.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
import uuid

from voicevox_engine.metas.Metas import Speaker, SpeakerStyle, StyleId, StyleType
from voicevox_engine.metas.MetasStore import filter_speakers_and_styles
from voicevox_engine.metas.MetasStore import (
SING_STYLE_TYPES,
TALK_STYLE_TYPES,
Character,
filter_characters_and_styles,
)


def _speakers_to_characters(speakers: list[Speaker]) -> list[Character]:
"""Speaker 配列をキャラクター配列へキャストする。"""
characters: list[Character] = []
for speaker in speakers:
styles = speaker.styles
talk_styles = filter(lambda style: style.type in TALK_STYLE_TYPES, styles)
sing_styles = filter(lambda style: style.type in SING_STYLE_TYPES, styles)
characters.append(
Character(
name=speaker.name,
uuid=speaker.speaker_uuid,
talk_styles=list(talk_styles),
sing_styles=list(sing_styles),
version=speaker.version,
supported_features=speaker.supported_features,
)
)
return characters


def _gen_speaker(style_types: list[StyleType]) -> Speaker:
Expand Down Expand Up @@ -38,14 +63,16 @@ def test_filter_speakers_and_styles_with_speaker() -> None:
speaker_allstyle = _gen_speaker(["talk", "singing_teacher", "frame_decode", "sing"])

# Outputs
result = filter_speakers_and_styles(
[
speaker_talk_only,
speaker_singing_teacher_only,
speaker_frame_decode_only,
speaker_sing_only,
speaker_allstyle,
],
result = filter_characters_and_styles(
_speakers_to_characters(
[
speaker_talk_only,
speaker_singing_teacher_only,
speaker_frame_decode_only,
speaker_sing_only,
speaker_allstyle,
]
),
"speaker",
)

Expand All @@ -70,14 +97,16 @@ def test_filter_speakers_and_styles_with_singer() -> None:
speaker_allstyle = _gen_speaker(["talk", "singing_teacher", "frame_decode", "sing"])

# Outputs
result = filter_speakers_and_styles(
[
speaker_talk_only,
speaker_singing_teacher_only,
speaker_frame_decode_only,
speaker_sing_only,
speaker_allstyle,
],
result = filter_characters_and_styles(
_speakers_to_characters(
[
speaker_talk_only,
speaker_singing_teacher_only,
speaker_frame_decode_only,
speaker_sing_only,
speaker_allstyle,
]
),
"singer",
)

Expand Down
8 changes: 5 additions & 3 deletions voicevox_engine/app/routers/morphing.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from voicevox_engine.core.core_initializer import CoreManager
from voicevox_engine.metas.Metas import StyleId
from voicevox_engine.metas.MetasStore import MetasStore
from voicevox_engine.metas.MetasStore import MetasStore, characters_to_speakers
from voicevox_engine.model import AudioQuery
from voicevox_engine.morphing.model import MorphableTargetInfo
from voicevox_engine.morphing.morphing import (
Expand Down Expand Up @@ -57,7 +57,8 @@ def morphable_targets(
version = core_version or core_manager.latest_version()
core = core_manager.get_core(version)

speakers = metas_store.load_combined_metas(core.characters)
characters = metas_store.load_combined_metas(core.characters)
speakers = characters_to_speakers(characters)
try:
morphable_targets = get_morphable_targets(speakers, base_style_ids)
except StyleIdNotFoundError as e:
Expand Down Expand Up @@ -97,7 +98,8 @@ def _synthesis_morphing(
core = core_manager.get_core(version)

# モーフィングが許可されないキャラクターペアを拒否する
speakers = metas_store.load_combined_metas(core.characters)
characters = metas_store.load_combined_metas(core.characters)
speakers = characters_to_speakers(characters)
try:
morphable = is_morphable(speakers, base_style_id, target_style_id)
except StyleIdNotFoundError as e:
Expand Down
14 changes: 7 additions & 7 deletions voicevox_engine/app/routers/speaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from voicevox_engine.core.core_initializer import CoreManager
from voicevox_engine.metas.Metas import Speaker, SpeakerInfo
from voicevox_engine.metas.MetasStore import MetasStore, filter_speakers_and_styles
from voicevox_engine.metas.MetasStore import MetasStore, filter_characters_and_styles


def b64encode_str(s: bytes) -> str:
Expand All @@ -29,8 +29,8 @@ def speakers(core_version: str | SkipJsonSchema[None] = None) -> list[Speaker]:
"""話者情報の一覧を取得します。"""
version = core_version or core_manager.latest_version()
core = core_manager.get_core(version)
speakers = metas_store.load_combined_metas(core.characters)
return filter_speakers_and_styles(speakers, "speaker")
characters = metas_store.load_combined_metas(core.characters)
return filter_characters_and_styles(characters, "speaker")

@router.get("/speaker_info")
def speaker_info(
Expand Down Expand Up @@ -79,8 +79,8 @@ def _speaker_info(

# 該当話者を検索する
core_characters = core_manager.get_core(version).characters
speakers = metas_store.load_combined_metas(core_characters)
speakers = filter_speakers_and_styles(speakers, speaker_or_singer)
characters = metas_store.load_combined_metas(core_characters)
speakers = filter_characters_and_styles(characters, speaker_or_singer)
speaker = next(
filter(lambda spk: spk.speaker_uuid == speaker_uuid, speakers), None
)
Expand Down Expand Up @@ -143,8 +143,8 @@ def singers(core_version: str | SkipJsonSchema[None] = None) -> list[Speaker]:
"""歌手情報の一覧を取得します"""
version = core_version or core_manager.latest_version()
core = core_manager.get_core(version)
singers = metas_store.load_combined_metas(core.characters)
return filter_speakers_and_styles(singers, "singer")
characters = metas_store.load_combined_metas(core.characters)
return filter_characters_and_styles(characters, "singer")

@router.get("/singer_info")
def singer_info(
Expand Down
103 changes: 56 additions & 47 deletions voicevox_engine/metas/MetasStore.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,36 @@ def cast_styles(cores: list[CoreCharacterStyle]) -> list[SpeakerStyle]:
]


@dataclass
class Character:
"""キャラクター"""

name: str
uuid: str
talk_styles: list[SpeakerStyle]
sing_styles: list[SpeakerStyle]
version: str
supported_features: SpeakerSupportedFeatures


TALK_STYLE_TYPES: Final = ["talk"]
SING_STYLE_TYPES: Final = ["singing_teacher", "frame_decode", "sing"]


def characters_to_speakers(characters: list[Character]) -> list[Speaker]:
"""キャラクター配列を Speaker 配列へキャストする。"""
return [
Speaker(
name=character.name,
speaker_uuid=character.uuid,
styles=character.talk_styles + character.sing_styles,
version=character.version,
supported_features=character.supported_features,
)
for character in characters
]


class _EngineSpeaker(BaseModel):
"""
エンジンに含まれる話者情報
Expand Down Expand Up @@ -53,60 +83,39 @@ def __init__(self, engine_speakers_path: Path) -> None:
for folder in engine_speakers_path.iterdir()
}

def load_combined_metas(self, core_metas: list[CoreCharacter]) -> list[Speaker]:
def load_combined_metas(
self, core_characters: list[CoreCharacter]
) -> list[Character]:
"""コアとエンジンのメタ情報を統合する。"""
return [
Speaker(
supported_features=self._loaded_metas[
speaker_meta.speaker_uuid
].supported_features,
name=speaker_meta.name,
speaker_uuid=speaker_meta.speaker_uuid,
styles=cast_styles(speaker_meta.styles),
version=speaker_meta.version,
characters: list[Character] = []
for core_character in core_characters:
character_uuid = core_character.speaker_uuid
engine_character = self._loaded_metas[character_uuid]
styles = cast_styles(core_character.styles)
talk_styles = list(
filter(lambda style: style.type in TALK_STYLE_TYPES, styles)
)
for speaker_meta in core_metas
]


@dataclass
class Character:
"""キャラクター"""

name: str
uuid: str
talk_styles: list[SpeakerStyle]
sing_styles: list[SpeakerStyle]
version: str
supported_features: SpeakerSupportedFeatures


TALK_STYLE_TYPES: Final = ["talk"]
SING_STYLE_TYPES: Final = ["singing_teacher", "frame_decode", "sing"]
sing_styles = list(
filter(lambda style: style.type in SING_STYLE_TYPES, styles)
)
characters.append(
Character(
name=core_character.name,
uuid=character_uuid,
talk_styles=talk_styles,
sing_styles=sing_styles,
version=core_character.version,
supported_features=engine_character.supported_features,
)
)
return characters


def filter_speakers_and_styles(
speakers: list[Speaker],
def filter_characters_and_styles(
characters: list[Character],
speaker_or_singer: Literal["speaker", "singer"],
) -> list[Speaker]:
"""キャラクター内のスタイルをtalk系・sing系のみにする。スタイル数が0になったキャラクターは除外する。"""

characters = map(
lambda speaker: Character(
name=speaker.name,
uuid=speaker.speaker_uuid,
talk_styles=list(
filter(lambda style: style.type in TALK_STYLE_TYPES, speaker.styles)
),
sing_styles=list(
filter(lambda style: style.type in SING_STYLE_TYPES, speaker.styles)
),
version=speaker.version,
supported_features=speaker.supported_features,
),
speakers,
)

if speaker_or_singer == "speaker":
# talk 系スタイルを持たないキャラクターを除外する
talk_characters = filter(
Expand Down