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><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><w>Hello</w><w>World</w></s></speak>'
# ssml = '<speak><s>Hello world</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") wav_file: wave.Wave_write = wave.open("out.wav", "wb")
params_set = False params_set = False

View file

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

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import csv
import itertools import itertools
import logging import logging
import time import time
@ -24,6 +25,7 @@ PHONEME_MAP_TYPE = typing.Dict[PHONEME_TYPE, typing.List[PHONEME_TYPE]]
SPEAKER_NAME_TYPE = str SPEAKER_NAME_TYPE = str
SPEAKER_ID_TYPE = int 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] SPEAKER_MAP_TYPE = typing.Dict[SPEAKER_NAME_TYPE, SPEAKER_ID_TYPE]
DEFAULT_LANGUAGE = "en_US" DEFAULT_LANGUAGE = "en_US"
@ -137,7 +139,19 @@ class Mimic3Voice(metaclass=ABCMeta):
speaker_id = 0 speaker_id = 0
if isinstance(speaker, SPEAKER_NAME_TYPE): if isinstance(speaker, SPEAKER_NAME_TYPE):
if self.speaker_map: 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: elif speaker is not None:
speaker_id = speaker speaker_id = speaker
@ -198,7 +212,18 @@ class Mimic3Voice(metaclass=ABCMeta):
with open(phoneme_map_path, "r", encoding="utf-8") as map_file: with open(phoneme_map_path, "r", encoding="utf-8") as map_file:
phoneme_map = phonemes2ids.utils.load_phoneme_map(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: if config.phonemizer == Phonemizer.GRUUT:
return GruutVoice( return GruutVoice(
@ -206,6 +231,7 @@ class Mimic3Voice(metaclass=ABCMeta):
onnx_model=onnx_model, onnx_model=onnx_model,
phoneme_to_id=phoneme_to_id, phoneme_to_id=phoneme_to_id,
phoneme_map=phoneme_map, phoneme_map=phoneme_map,
speaker_map=speaker_map,
) )
if config.phonemizer == Phonemizer.ESPEAK: if config.phonemizer == Phonemizer.ESPEAK:
@ -214,6 +240,7 @@ class Mimic3Voice(metaclass=ABCMeta):
onnx_model=onnx_model, onnx_model=onnx_model,
phoneme_to_id=phoneme_to_id, phoneme_to_id=phoneme_to_id,
phoneme_map=phoneme_map, phoneme_map=phoneme_map,
speaker_map=speaker_map,
) )
if config.phonemizer == Phonemizer.SYMBOLS: if config.phonemizer == Phonemizer.SYMBOLS:
return SymbolsVoice( return SymbolsVoice(
@ -221,6 +248,7 @@ class Mimic3Voice(metaclass=ABCMeta):
onnx_model=onnx_model, onnx_model=onnx_model,
phoneme_to_id=phoneme_to_id, phoneme_to_id=phoneme_to_id,
phoneme_map=phoneme_map, phoneme_map=phoneme_map,
speaker_map=speaker_map,
) )
raise ValueError(f"Unsupported phonemizer: {config.phonemizer}") raise ValueError(f"Unsupported phonemizer: {config.phonemizer}")