Source code for opsoro.sound.tts

"""
This module defines the interface for communicating with the TTS libraries.

.. autoclass:: _TTS
   :members:
   :undoc-members:
   :show-inheritance:
"""

import hashlib
import os
import string
import subprocess
from functools import partial

from opsoro.preferences import Preferences

get_path = partial(os.path.join, os.path.abspath(os.path.dirname(__file__)))


[docs]class _TTS(object):
[docs] def __init__(self): """ TTS class, used to convert text to speech. """ self.engine = "espeak" # "espeak" self.language = "nl" self.gender = "f" # "m" self.delay = "5" self.speed = "180" self.cache_folder = "/tmp/opsorotts"
[docs] def create(self, text): """ Takes a string of text, converts it using the PicoTTS engine, and plays it. Wave files are buffered in /tmp/OnoTTS/<text>.wav. First call blocks while PicoTTS generates the .wav, this may take about a second. Subsequent calls of the same text return immediately. If you wish to avoid this, sound files can be generated on beforehand by using generate_only=True. :param string text: text to convert to speech :return: path to the sound file :rtype: string """ def format_filename(s): """ Take a string and return a valid filename constructed from the string. Uses a whitelist approach: any characters not present in valid_chars are removed. Also spaces are replaced with underscores. Note: this method may produce invalid filenames such as ``, `.` or `..` When I use this method I prepend a date string like '2009_01_15_19_46_32_' and append a file extension like '.txt', so I avoid the potential of using an invalid filename. Taken from: https://gist.github.com/seanh/93666 :param string s: text to convert to a valid filename :return: formatted filename :rtype: string """ valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits) filename = ''.join(c for c in s if c in valid_chars) filename = filename.replace( ' ', '_') # I don't like spaces in filenames. return filename if not os.path.exists(self.cache_folder): os.makedirs(self.cache_folder) filename = str(hashlib.sha1(format_filename(text))) # Max length of filename is 255 chars # if len(filename) >= 250: # filename = filename[:250] full_path = os.path.join(get_path(self.cache_folder), filename + ".wav") if os.path.isfile(full_path): # Sound file already exists return full_path self.engine = Preferences.get("audio", "tts_engine", self.engine) self.language = Preferences.get("audio", "tts_language", self.language) self.gender = Preferences.get("audio", "tts_gender", self.gender) if self.engine == "pico": self.create_pico(text, full_path) elif self.engine == "espeak": self.create_espeak(text, full_path, self.language, self.gender, self.delay, self.speed) return full_path
[docs] def create_pico(self, text, file_path): """ Convert text to speech using the pico2wave TTS library. :param string text: text to convert to speech :param string file_path: file path to store the speech soundfile """ subprocess.call(["pico2wave", "-w", file_path, text])
[docs] def create_espeak(self, text, file_path, language, gender, delay, speed): """ Convert text to speech using the espeak TTS library. :param string text: text to convert to speech :param string file_path: file path to store the speech soundfile :param string language: language initials :param string gender: specify gender (m for male, f for female) :param int delay: delay between words in ms :param int speed: speed in words-per-minute """ text = "\"" + text + "\"" subprocess.call(["espeak", "-v", language + "+" + gender + "3", "-g", delay, "-s", speed, "-w", file_path, text])
# Global instance that can be accessed by apps and scripts TTS = _TTS()