Celtia: Nós Project's Galician TTS Model

Model description

Celtia is a Galician TTS model developed by the Nós project. It was trained from scratch using the Coqui TTS Python library on the corpus Nos_Celtia-GL. This corpus comprises a total of 20,000 sentences recorded by a professional voice talent. Specifically, a subset of 13,000 sentences, corresponding to 15.5 hours of speech, was used to train the model.

The model was trained directly on grapheme inputs, so no phonetic transcription is required. The Cotovía tool can be used to normalize the input text.

You can test the model in our live inference demo (Nós-TTS) or in our spaces (Galician TTS).

Intended uses and limitations

You can use this model to generate synthetic speech in Galician.

Installation

Cotovía

For text normalization, you can use the front-end of Cotovía. This software is available for download on the SourceForge website. The required Debian packages are cotovia_0.5_amd64.deb and cotovia-lang-gl_0.5_all.deb, which can be installed using the following commands:

sudo dpkg -i cotovia_0.5_amd64.deb
sudo dpkg -i cotovia-lang-gl_0.5_all.deb

TTS library

To synthesize speech, you need to install the Coqui TTS library:

pip install TTS

How to use

Command-line usage

The following command normalizes and synthesizes the input text using the Celtia model:

echo "Son Celtia, unha voz creada con intelixencia artificial" | cotovia -p -n -S | iconv -f iso88591 -t utf8 | tts --text "$(cat -)" --model_path celtia.pth --config_path config.json --out_path celtia.wav

The output synthesized speech is saved to the specified audio file.

Python usage

Normalization and synthesis can also be performed within Python:

import argparse
import string
import subprocess
from TTS.utils.synthesizer import Synthesizer

def sanitize_filename(filename):
    """Remove or replace any characters that are not allowed in file names."""
    return ''.join(c for c in filename if c.isalnum() or c in (' ', '_', '-')).rstrip()

def to_cotovia(text):
    # Input and output Cotovía files
    COTOVIA_IN_TXT_PATH = res + '.txt'
    COTOVIA_IN_TXT_PATH_ISO = 'iso8859-1' + res + '.txt'
    COTOVIA_OUT_PRE_PATH = 'iso8859-1' + res + '.pre'
    COTOVIA_OUT_PRE_PATH_UTF8 = 'utf8' + res + '.pre'

    with open(COTOVIA_IN_TXT_PATH, 'w') as f:
        f.write(text + '\n')

    # UTF-8 to ISO8859-1
    subprocess.run(["iconv", "-f", "utf-8", "-t", "iso8859-1", COTOVIA_IN_TXT_PATH, "-o", COTOVIA_IN_TXT_PATH_ISO], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
    subprocess.run(["cotovia", "-i", COTOVIA_IN_TXT_PATH_ISO, "-p"], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
    subprocess.run(["iconv", "-f", "iso8859-1", "-t", "utf-8", COTOVIA_OUT_PRE_PATH, "-o", COTOVIA_OUT_PRE_PATH_UTF8], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)

    segs = []
    try:
        with open(COTOVIA_OUT_PRE_PATH_UTF8, 'r') as f:
            segs = [line.rstrip() for line in f]
    except:
        print("ERROR: Couldn't read cotovia output")

    subprocess.run(["rm", COTOVIA_IN_TXT_PATH, COTOVIA_IN_TXT_PATH_ISO, COTOVIA_OUT_PRE_PATH, COTOVIA_OUT_PRE_PATH_UTF8], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)

    return segs

def text_preprocess(text):
    cotovia_preproc_text = to_cotovia(text)

    # Convert list to string
    cotovia_preproc_text_res = ' '.join(cotovia_preproc_text)

    # Add final punctuation if missing
    if cotovia_preproc_text_res[-1] not in string.punctuation:
        cotovia_preproc_text_res += '.'

    return cotovia_preproc_text_res

def main():
    parser = argparse.ArgumentParser(description='Cotovía text normalisation')
    parser.add_argument('text', type=str, help='Text to synthetize')
    parser.add_argument('model_path', type=str, help='Absolute path to the model checkpoint.pth')
    parser.add_argument('config_path', type=str, help='Absolute path to the model config.json')

    args = parser.parse_args()

    print("Text before preprocessing: ", args.text)
    text = text_preprocess(args.text)
    print("Text after preprocessing: ", text)

    synthesizer = Synthesizer(
        args.model_path, args.config_path, None, None, None, None,
    )

    # Step 1: Extract the first word from the text
    first_word = args.text.split()[0] if args.text.split() else "audio"
    first_word = sanitize_filename(first_word)  # Sanitize to make it a valid filename

    # Step 2: Use synthesizer's built-in function to synthesize and save the audio
    wavs = synthesizer.tts(text)
    filename = f"{first_word}.wav"
    synthesizer.save_wav(wavs, filename)

    print(f"Audio file saved as: {filename}")

if __name__ == "__main__":
    main()

This Python code takes an input text, normalizes it using Cotovía’s front-end, synthesizes speech from the normalized text, and saves the synthetic output speech as a .wav file.

A more advanced version, including additional text preprocessing, can be found in the script synthesize.py, avaliable in this repository. You can use this script to synthesise speech from an input text as follows:

python synthesize.py text model_path config_path

Training

Hyperparameter

The model is based on VITS proposed by Kim et al. The following hyperparameters were set in the coqui framework.

Hyperparameter Value
Model vits
Batch Size 26
Eval Batch Size 16
Mixed Precision true
Window Length 1024
Hop Length 256
FTT size 1024
Num Mels 80
Phonemizer null
Phoneme Lenguage en-us
Text Cleaners multilingual_cleaners
Formatter nos_fonemas
Optimizer adam
Adam betas (0.8, 0.99)
Adam eps 1e-09
Adam weight decay 0.01
Learning Rate Gen 0.0002
Lr. schedurer Gen ExponentialLR
Lr. schedurer Gamma Gen 0.999875
Learning Rate Disc 0.0002
Lr. schedurer Disc ExponentialLR
Lr. schedurer Gamma Disc 0.999875

The model was trained for 457900 steps.

The nos_fonemas formatter is a modification of the LJSpeech formatter with one extra column for the normalized input (extended numbers and acronyms).

Additional information

Authors

Carmen Magariños

Contact information

For further information, send an email to proxecto.nos@usc.gal

Licensing Information

Apache License, Version 2.0

Funding

This research was funded by “The Nós project: Galician in the society and economy of Artificial Intelligence”, resulting from the agreement 2021-CP080 between the Xunta de Galicia and the University of Santiago de Compostela, and thanks to the Investigo program, within the National Recovery, Transformation and Resilience Plan, within the framework of the European Recovery Fund (NextGenerationEU).

Citation information

If you use this model, please cite as follows:

Magariños, Carmen. 2023. Nos_TTS-celtia-vits-graphemes. URL: https://huggingface.co/proxectonos/Nos_TTS-celtia-vits-graphemes

Downloads last month
15
Inference Examples
Inference API (serverless) does not yet support coqui models for this pipeline type.

Space using proxectonos/Nos_TTS-celtia-vits-graphemes 1

Collection including proxectonos/Nos_TTS-celtia-vits-graphemes