diff --git a/mimic3-tts/mimic3_tts/__main__.py b/mimic3-tts/mimic3_tts/__main__.py index 72ed425..f145da0 100644 --- a/mimic3-tts/mimic3_tts/__main__.py +++ b/mimic3-tts/mimic3_tts/__main__.py @@ -21,7 +21,23 @@ speaker = SSMLSpeaker(tts) # ssml = 'бажав' # ssml = 'HelloWorld' # ssml = 'Hello world' -ssml = '12' +# ssml = '12' +ssml = ''' + + + + Today is 1/2. + + + + + + + Soy el 1. + + +''' + wav_file: wave.Wave_write = wave.open("out.wav", "wb") params_set = False diff --git a/mimic3-tts/mimic3_tts/tts.py b/mimic3-tts/mimic3_tts/tts.py index 7fb3009..e26bb06 100644 --- a/mimic3-tts/mimic3_tts/tts.py +++ b/mimic3-tts/mimic3_tts/tts.py @@ -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, diff --git a/mimic3-tts/mimic3_tts/voice.py b/mimic3-tts/mimic3_tts/voice.py index 140e6ce..8483ab6 100644 --- a/mimic3-tts/mimic3_tts/voice.py +++ b/mimic3-tts/mimic3_tts/voice.py @@ -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}")