Source code for pythaitts

# -*- coding: utf-8 -*-
"""
PyThaiTTS
"""
__version__ = "1.0.0"

from typing import Union, Iterable, Iterator, Optional, Dict, Any
from pathlib import Path

from pythaitts.preprocess import (
    preprocess_text,
    num_to_thai,
    expand_maiyamok,
    load_custom_dict,
)
from pythaitts.pretrained.makham import (
    MakHamTTS,
    MakHamTTSParadee,
)
from pythaitts.pretrained.fastthaig2p.en_g2p import (
    EnglishG2P,
    en_g2p,
    en_ipa_to_thai_ipa,
    en_to_ipa,
)
from pythaitts.realtime import (
    RealtimeTTS,
    FastThaiG2PEngine,
    PyThaiTTSEngine,
    RealtimeTTSEngine,
    chunk_text,
    stream_text_to_chunks,
)


[docs] class TTS:
[docs] def __init__( self, pretrained="fastthaig2p", mode="last_checkpoint", version="1.0", device: str = "cpu", custom_dict: Optional[Union[Dict[str, str], str, Path, Iterable[Any]]] = None, **kwargs, ) -> None: """ :param str pretrained: TTS pretrained (fastthaig2p, makham, makham_paradee, lunarlist_onnx, khanomtan, lunarlist, vachana) :param str mode: pretrained mode (fastthaig2p, makham, makham_paradee, lunarlist_onnx, and vachana don't support) :param str version: model version (default is 1.0 or 1.1) :param str device: device for running model. (fastthaig2p, makham, makham_paradee, lunarlist_onnx, and vachana support CPU only.) :param custom_dict: Optional custom word dictionary for custom Thai pronunciation in words **Options for mode** * *last_checkpoint* (default) - last checkpoint of model * *best_model* - Best model (best loss) You can see more about khanomtan tts at `https://github.com/wannaphong/KhanomTan-TTS-v1.0 <https://github.com/wannaphong/KhanomTan-TTS-v1.0>`_ and `https://github.com/wannaphong/KhanomTan-TTS-v1.1 <https://github.com/wannaphong/KhanomTan-TTS-v1.1>`_ If you want to use khanomtan tts, you must to install coqui-tts before use the model by pip install coqui-tts. For lunarlist tts model, you must to install nemo before use the model by pip install nemo_toolkit['tts']. You can see more about lunarlist tts at `https://link.medium.com/OpPjQis6wBb <https://link.medium.com/OpPjQis6wBb>`_ For lunarlist_onnx tts model, \ You can see more about lunarlist tts at `https://github.com/PyThaiNLP/thaitts-onnx <https://github.com/PyThaiNLP/thaitts-onnx>`_ For vachana tts model, you must install vachanatts before using the model by pip install vachanatts. You can see more about vachana tts at `https://github.com/VYNCX/VachanaTTS2 <https://github.com/VYNCX/VachanaTTS2>`_ For fastthaig2p tts model, \ You can see more about fastthaig2p at `https://github.com/awslabs/FastThaiG2P <https://github.com/awslabs/FastThaiG2P>`_ For makham and makham_paradee tts models, \ You can see more about MakHamTTS at `https://huggingface.co/pythainlp/MakHamTTS <https://huggingface.co/pythainlp/MakHamTTS>`_ """ self.pretrained = pretrained self.mode = mode self.device = device self.custom_dict = custom_dict if custom_dict is not None: kwargs["custom_dict"] = custom_dict self.load_pretrained(version=version, **kwargs)
[docs] def load_pretrained(self, version="1.0", **kwargs): """ Load pretrained """ if self.pretrained == "lunarlist_onnx": from pythaitts.pretrained.lunarlist_onnx import LunarlistONNX self.model = LunarlistONNX() elif self.pretrained == "khanomtan": from pythaitts.pretrained.khanomtan_tts import KhanomTan self.model = KhanomTan(mode=self.mode, version=version) elif self.pretrained == "lunarlist": from pythaitts.pretrained.lunarlist_model import LunarlistModel self.model = LunarlistModel(mode=self.mode, device=self.device) elif self.pretrained == "vachana": from pythaitts.pretrained.vachana_tts import VachanaTTS self.model = VachanaTTS() elif self.pretrained in ("fastthaig2p", "FastThaiG2P"): from pythaitts.pretrained.fastthaig2p import FastThaiG2P self.model = FastThaiG2P(device=self.device, **kwargs) elif self.pretrained in ("makham", "MakHamTTS"): from pythaitts.pretrained.makham import MakHamTTS self.model = MakHamTTS(**kwargs) elif self.pretrained in ("makham_paradee", "MakHamTTS_paradee"): from pythaitts.pretrained.makham import MakHamTTSParadee self.model = MakHamTTSParadee(**kwargs) else: raise NotImplementedError( "PyThaiTTS doesn't support %s pretrained." % self.pretrained )
[docs] def add_custom_dict( self, custom_dict: Union[Dict[str, str], str, Path, Iterable[Any]] ) -> None: """Add custom word pronunciations to the TTS model.""" loaded = load_custom_dict(custom_dict) if self.custom_dict is None: self.custom_dict = {} elif isinstance(self.custom_dict, (str, Path)): self.custom_dict = load_custom_dict(self.custom_dict) elif not isinstance(self.custom_dict, dict): self.custom_dict = load_custom_dict(self.custom_dict) self.custom_dict.update(loaded) if hasattr(self.model, "add_custom_dict"): self.model.add_custom_dict(loaded)
[docs] def set_custom_dict( self, custom_dict: Union[Dict[str, str], str, Path, Iterable[Any]] ) -> None: """Replace custom word pronunciations with new dictionary.""" self.custom_dict = custom_dict if hasattr(self.model, "set_custom_dict"): self.model.set_custom_dict(custom_dict)
[docs] def tts( self, text: str, speaker_idx: str = "thai_som", language_idx: str = "th-th", return_type: str = "file", filename: str = None, preprocess: bool = True, custom_dict: Optional[Union[Dict[str, str], str, Path, Iterable[Any]]] = None, **kwargs, ): """ speech synthesis :param str text: text :param str speaker_idx: speaker (default is thai_som for fastthaig2p, Linda for khanomtan, th_f_1 for vachana) :param str language_idx: language (default is th-th) :param str return_type: return type (default is file) :param str filename: path filename for save wav file if return_type is file. :param bool preprocess: whether to preprocess text (convert numbers to Thai text and expand ๆ). Default is True. :param custom_dict: Optional custom word dictionary for custom Thai pronunciation in words :param kwargs: Additional parameters passed to the underlying model. """ effective_custom_dict = ( custom_dict if custom_dict is not None else getattr(self, "custom_dict", None) ) # Preprocess text if requested if preprocess: from pythaitts.preprocess import preprocess_text if self.pretrained not in ("fastthaig2p", "FastThaiG2P") and effective_custom_dict: text = preprocess_text(text, custom_dict=effective_custom_dict) else: text = preprocess_text(text) elif self.pretrained not in ("fastthaig2p", "FastThaiG2P") and effective_custom_dict: from pythaitts.preprocess import preprocess_text text = preprocess_text( text, expand_numbers=False, expand_maiyamok_char=False, custom_dict=effective_custom_dict, ) if self.pretrained == "lunarlist" or self.pretrained == "lunarlist_onnx": return self.model(text=text, return_type=return_type, filename=filename) elif self.pretrained == "vachana": if speaker_idx in ("thai_som", "Linda", "newvoice_female", None): speaker_idx = "th_f_1" return self.model( text=text, speaker_idx=speaker_idx, return_type=return_type, filename=filename, **kwargs, ) elif self.pretrained in ("fastthaig2p", "FastThaiG2P"): if speaker_idx in ("Linda", "newvoice_female", None): speaker_idx = "thai_som" call_kwargs = dict(kwargs) if effective_custom_dict is not None: call_kwargs["custom_dict"] = effective_custom_dict return self.model( text=text, speaker_idx=speaker_idx, return_type=return_type, filename=filename, **call_kwargs, ) elif self.pretrained in ("makham", "MakHamTTS"): if speaker_idx in ("Linda", "thai_som", None): speaker_idx = "newvoice_female" call_kwargs = dict(kwargs) if effective_custom_dict is not None: call_kwargs["custom_dict"] = effective_custom_dict return self.model( text=text, speaker_idx=speaker_idx, return_type=return_type, filename=filename, **call_kwargs, ) elif self.pretrained in ("makham_paradee", "MakHamTTS_paradee"): call_kwargs = dict(kwargs) if effective_custom_dict is not None: call_kwargs["custom_dict"] = effective_custom_dict return self.model( text=text, speaker_idx=speaker_idx, return_type=return_type, filename=filename, **call_kwargs, ) if speaker_idx in ("thai_som", "newvoice_female", None): speaker_idx = "Linda" return self.model( text=text, speaker_idx=speaker_idx, language_idx=language_idx, return_type=return_type, filename=filename, )
[docs] def stream( self, text: Union[str, Iterable[str]], speaker_idx: str = "thai_som", return_type: str = "waveform", play: bool = False, preprocess: bool = True, max_phonemes: int = 400, custom_dict: Optional[Union[Dict[str, str], str, Path, Iterable[Any]]] = None, **kwargs, ): """ Stream speech synthesis in real-time. :param Union[str, Iterable[str]] text: Input text or stream of text tokens (e.g. from LLM) :param str speaker_idx: Voice to use (default: "thai_som" for fastthaig2p) :param str return_type: Return format ("waveform", "bytes", "raw", "file") :param bool play: Whether to play audio chunks in real-time to speakers :param bool preprocess: Whether to preprocess text (numbers to words, ๆ) :param int max_phonemes: Maximum phonemes per synthesized chunk :param custom_dict: Optional custom word dictionary for custom Thai pronunciation in words :param kwargs: Additional parameters passed to the model :return: Generator yielding audio chunks """ effective_custom_dict = ( custom_dict if custom_dict is not None else getattr(self, "custom_dict", None) ) if self.pretrained in ("fastthaig2p", "FastThaiG2P"): if speaker_idx in ("Linda", "newvoice_female", None): speaker_idx = "thai_som" return self.model.stream( text=text, speaker_idx=speaker_idx, return_type=return_type, play=play, preprocess=preprocess, max_phonemes=max_phonemes, custom_dict=effective_custom_dict, **kwargs, ) elif self.pretrained in ("makham", "MakHamTTS"): if speaker_idx in ("Linda", "thai_som", None): speaker_idx = "newvoice_female" return self.model.stream( text=text, speaker_idx=speaker_idx, return_type=return_type, play=play, preprocess=preprocess, max_phonemes=max_phonemes, custom_dict=effective_custom_dict, **kwargs, ) elif self.pretrained in ("makham_paradee", "MakHamTTS_paradee"): return self.model.stream( text=text, speaker_idx=speaker_idx, return_type=return_type, play=play, preprocess=preprocess, max_phonemes=max_phonemes, custom_dict=effective_custom_dict, **kwargs, ) else: from pythaitts.realtime import stream_text_to_chunks def _gen(): for chunk in stream_text_to_chunks( text, max_phonemes=max_phonemes, preprocess=preprocess, custom_dict=effective_custom_dict, ): yield self.tts( text=chunk, speaker_idx=speaker_idx, return_type=return_type, preprocess=False, custom_dict=effective_custom_dict, **kwargs, ) return _gen()
tts_stream = stream
__all__ = [ "TTS", "RealtimeTTS", "FastThaiG2PEngine", "PyThaiTTSEngine", "RealtimeTTSEngine", "EnglishG2P", "en_g2p", "en_to_ipa", "en_ipa_to_thai_ipa", "MakHamTTS", "MakHamTTSParadee", "preprocess_text", "load_custom_dict", "num_to_thai", "expand_maiyamok", "chunk_text", "stream_text_to_chunks", ]