Use speaker names as well as ids

This commit is contained in:
Michael Hansen 2022-03-21 15:41:20 -04:00
commit 3b730bfd9a
3 changed files with 64 additions and 19 deletions

View file

@ -21,7 +21,23 @@ speaker = SSMLSpeaker(tts)
# ssml = '<speak><voice name="uk_UK/m-ailabs_low"><s><w>бажав</w></s></voice></speak>'
# ssml = '<speak><s><w>Hello</w><w>World</w></s></speak>'
# ssml = '<speak><s>Hello world</s></speak>'
ssml = '<speak><s><voice name="el_GR/rapunzelina_low"><say-as interpret-as="characters">12</say-as></voice></s></speak>'
# ssml = '<speak><s><voice name="el_GR/rapunzelina_low"><say-as interpret-as="characters">12</say-as></voice></s></speak>'
ssml = '''
<speak>
<voice name="en_US/amy_low">
<s>
Today is 1/2.
<break time="1s" />
</s>
</voice>
<voice name="es_ES/carlfm_low">
<s>Soy el <say-as interpret-as="number" format="ordinal">1</say-as>.</s>
</voice>
</speak>
'''
wav_file: wave.Wave_write = wave.open("out.wav", "wb")
params_set = False

View file

@ -27,7 +27,7 @@ from opentts_abc import (
from mimic3_tts.config import TrainingConfig
from mimic3_tts.utils import audio_float_to_int16
from mimic3_tts.voice import Mimic3Voice
from mimic3_tts.voice import Mimic3Voice, SPEAKER_TYPE
_DIR = Path(__file__).parent
@ -47,7 +47,7 @@ class Mimic3Settings:
voice: typing.Optional[str] = None
language: typing.Optional[str] = None
voices_directories: typing.Optional[typing.Iterable[typing.Union[str, Path]]] = None
speaker_id: typing.Optional[int] = None
speaker: typing.Optional[SPEAKER_TYPE] = None
length_scale: float = 1.0
noise_scale: float = 0.667
noise_w: float = 0.8
@ -81,26 +81,24 @@ class Mimic3TextToSpeechSystem(TextToSpeechSystem):
@voice.setter
def voice(self, new_voice: str):
if new_voice != self.settings.voice:
# Clear speaker id on voice change
self.speaker_id = None
# Clear speaker on voice change
self.speaker = None
self.settings.voice = new_voice
if "#" in self.settings.voice:
# Split
voice, speaker_id_str = self.settings.voice.split("#", maxsplit=1)
voice, speaker = self.settings.voice.split("#", maxsplit=1)
self.settings.voice = voice
# TODO: Use speaker map
self.speaker_id = int(speaker_id_str)
self.speaker = speaker
@property
def speaker_id(self) -> typing.Optional[int]:
return self.settings.speaker_id
def speaker(self) -> typing.Optional[SPEAKER_TYPE]:
return self.settings.speaker
@speaker_id.setter
def speaker_id(self, new_speaker_id: typing.Optional[int]):
self.settings.speaker_id = new_speaker_id
@speaker.setter
def speaker(self, new_speaker: typing.Optional[SPEAKER_TYPE]):
self.settings.speaker = new_speaker
@property
def language(self) -> str:
@ -154,12 +152,15 @@ class Mimic3TextToSpeechSystem(TextToSpeechSystem):
for sent_phonemes in voice.text_to_phonemes(text, text_language=text_language):
self._results.append(
Mimic3Phonemes(
current_settings=deepcopy(self.settings), phonemes=sent_phonemes,
current_settings=deepcopy(self.settings),
phonemes=sent_phonemes,
)
)
def _speak_sentence_phonemes(
self, sent_phonemes, settings: typing.Optional[Mimic3Settings] = None,
self,
sent_phonemes,
settings: typing.Optional[Mimic3Settings] = None,
) -> AudioResult:
settings = settings or self.settings
voice = self._get_or_load_voice(settings.voice or self.voice)
@ -169,7 +170,7 @@ class Mimic3TextToSpeechSystem(TextToSpeechSystem):
audio = voice.ids_to_audio(
sent_phoneme_ids,
speaker=self.speaker_id,
speaker=self.speaker,
length_scale=settings.length_scale,
noise_scale=settings.noise_scale,
noise_w=settings.noise_w,

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python3
import csv
import itertools
import logging
import time
@ -24,6 +25,7 @@ PHONEME_MAP_TYPE = typing.Dict[PHONEME_TYPE, typing.List[PHONEME_TYPE]]
SPEAKER_NAME_TYPE = str
SPEAKER_ID_TYPE = int
SPEAKER_TYPE = typing.Union[SPEAKER_NAME_TYPE, SPEAKER_ID_TYPE]
SPEAKER_MAP_TYPE = typing.Dict[SPEAKER_NAME_TYPE, SPEAKER_ID_TYPE]
DEFAULT_LANGUAGE = "en_US"
@ -137,7 +139,19 @@ class Mimic3Voice(metaclass=ABCMeta):
speaker_id = 0
if isinstance(speaker, SPEAKER_NAME_TYPE):
if self.speaker_map:
speaker_id = self.speaker_map.get(speaker, speaker_id)
maybe_speaker_id = self.speaker_map.get(speaker)
if maybe_speaker_id is None:
try:
# Interpret as speaker id
speaker_id = int(speaker)
except ValueError:
_LOGGER.warning(
"Unable to find a speaker with the name '%s'. Falling back to first speaker.",
speaker,
)
pass
else:
speaker_id = maybe_speaker_id
elif speaker is not None:
speaker_id = speaker
@ -198,7 +212,18 @@ class Mimic3Voice(metaclass=ABCMeta):
with open(phoneme_map_path, "r", encoding="utf-8") as map_file:
phoneme_map = phonemes2ids.utils.load_phoneme_map(map_file)
# TODO: Load speaker map
# id -> speaker | alias | alias ...
speaker_map: typing.Optional[SPEAKER_MAP_TYPE] = None
speaker_map_path = voice_dir / "speaker_map.csv"
if speaker_map_path.is_file():
_LOGGER.debug("Loading speaker map from %s", speaker_map_path)
with open(speaker_map_path, "r", encoding="utf-8") as map_file:
reader = csv.reader(map_file, delimiter="|")
speaker_map = {}
for row in reader:
speaker_id = int(row[0])
for alias in row[1:]:
speaker_map[alias] = speaker_id
if config.phonemizer == Phonemizer.GRUUT:
return GruutVoice(
@ -206,6 +231,7 @@ class Mimic3Voice(metaclass=ABCMeta):
onnx_model=onnx_model,
phoneme_to_id=phoneme_to_id,
phoneme_map=phoneme_map,
speaker_map=speaker_map,
)
if config.phonemizer == Phonemizer.ESPEAK:
@ -214,6 +240,7 @@ class Mimic3Voice(metaclass=ABCMeta):
onnx_model=onnx_model,
phoneme_to_id=phoneme_to_id,
phoneme_map=phoneme_map,
speaker_map=speaker_map,
)
if config.phonemizer == Phonemizer.SYMBOLS:
return SymbolsVoice(
@ -221,6 +248,7 @@ class Mimic3Voice(metaclass=ABCMeta):
onnx_model=onnx_model,
phoneme_to_id=phoneme_to_id,
phoneme_map=phoneme_map,
speaker_map=speaker_map,
)
raise ValueError(f"Unsupported phonemizer: {config.phonemizer}")