def __init__(self, config_string=None): if (config_string is not None) and (not gf.is_unicode(config_string)): raise TypeError(u"config_string is not a Unicode string") # set dictionaries up to keep the config data self.data = {} self.types = {} self.aliases = {} for (field, info) in self.FIELDS: (fdefault, ftype, faliases) = info self.data[field] = fdefault self.types[field] = ftype for alias in faliases: self.aliases[alias] = field if config_string is not None: # strip leading/trailing " or ' characters if (len(config_string) > 0) and (config_string[0] == config_string[-1]) and (config_string[0] in [u"\"", u"'"]): config_string = config_string[1:-1] # populate values from config_string, # ignoring keys not present in FIELDS properties = gf.config_string_to_dict(config_string) for key in set(properties.keys()) & set(self.data.keys()): self.data[key] = properties[key]
def test_is_unicode(self): tests = [ (None, False), (u"", True), (u"foo", True), (u"fox99", True), (b"foo", False), ([], False), ([u"foo"], False), ({u"foo": u"baz"}, False), ] if gf.PY2: tests.extend([ ("", False), ("foo", False), ("fox99", False), ]) else: tests.extend([ ("", True), ("foo", True), ("fox99", True), ]) for test in tests: self.assertEqual(gf.is_unicode(test[0]), test[1])
def __init__(self, config_string=None): if (config_string is not None) and (not gf.is_unicode(config_string)): raise TypeError(u"config_string is not a Unicode string") # set dictionaries up to keep the config data self.data = {} self.types = {} self.aliases = {} self.desc = {} for (field, info) in self.FIELDS: (fdefault, ftype, faliases, fdesc) = info self.data[field] = fdefault self.types[field] = ftype self.desc[field] = fdesc for alias in faliases: self.aliases[alias] = field if config_string is not None: # strip leading/trailing " or ' characters if ( (len(config_string) > 0) and (config_string[0] == config_string[-1]) and (config_string[0] in [u"\"", u"'"]) ): config_string = config_string[1:-1] # populate values from config_string, # ignoring keys not present in FIELDS properties = gf.config_string_to_dict(config_string) for key in set(properties.keys()) & set(self.data.keys()): self.data[key] = properties[key]
def test_is_unicode(self): tests = [ (None, False), (u"", True), (u"foo", True), (u"fox99", True), (b"foo", False), ([], False), ([u"foo"], False), ({ u"foo": u"baz" }, False), ] if gf.PY2: tests.extend([ ("", False), ("foo", False), ("fox99", False), ]) else: tests.extend([ ("", True), ("foo", True), ("fox99", True), ]) for test in tests: self.assertEqual(gf.is_unicode(test[0]), test[1])
def lines(self, lines): if lines is not None: if not isinstance(lines, list): raise TypeError(u"lines is not an instance of list") for line in lines: if not gf.is_unicode(line): raise TypeError(u"lines contains an element which is not a Unicode string") self.__lines = lines
def lines(self, lines): if lines is not None: if not isinstance(lines, list): raise TypeError(u"lines is not an instance of list") for line in lines: if not gf.is_unicode(line): raise TypeError( u"lines contains an element which is not a Unicode string" ) self.__lines = lines
def _sanitize(cls, message): """ Sanitize the given message, dealing with multiple arguments and/or string formatting. :param message: the log message to be sanitized :type message: string or list of strings :rtype: string """ if isinstance(message, list): if len(message) == 0: sanitized = u"Empty log message" elif len(message) == 1: sanitized = message[0] else: sanitized = message[0] % tuple(message[1:]) else: sanitized = message if not gf.is_unicode(sanitized): raise TypeError("The given log message is not a Unicode string") return sanitized
def identifier(self, identifier): if (identifier is not None) and (not gf.is_unicode(identifier)): raise TypeError(u"identifier is not a Unicode string") self.__identifier = identifier
def synthesize_multiple(self, text_file, output_file_path, quit_after=None, backwards=False): """ Synthesize the text contained in the given fragment list into a WAVE file. Return a tuple (anchors, total_time, num_chars). Concrete subclasses must implement at least one of the following private functions: 1. ``_synthesize_multiple_python()`` 2. ``_synthesize_multiple_c_extension()`` 3. ``_synthesize_multiple_subprocess()`` :param text_file: the text file to be synthesized :type text_file: :class:`~aeneas.textfile.TextFile` :param string output_file_path: the path to the output audio file :param quit_after: stop synthesizing as soon as reaching this many seconds :type quit_after: :class:`~aeneas.exacttiming.TimeValue` :param bool backwards: if > 0, synthesize from the end of the text file :rtype: tuple (anchors, total_time, num_chars) :raises: TypeError: if ``text_file`` is ``None`` or one of the text fragments is not a Unicode string :raises: ValueError: if ``self.rconf[RuntimeConfiguration.ALLOW_UNLISTED_LANGUAGES]`` is ``False`` and a fragment has a language code not supported by the TTS engine, or if ``text_file`` has no fragments or all its fragments are empty :raises: OSError: if output file cannot be written to ``output_file_path`` :raises: RuntimeError: if both the C extension and the pure Python code did not succeed. """ if text_file is None: self.log_exc(u"text_file is None", None, True, TypeError) if len(text_file) < 1: self.log_exc(u"The text file has no fragments", None, True, ValueError) if text_file.chars == 0: self.log_exc(u"All fragments in the text file are empty", None, True, ValueError) if not self.rconf[RuntimeConfiguration.ALLOW_UNLISTED_LANGUAGES]: for fragment in text_file.fragments: if fragment.language not in self.LANGUAGE_TO_VOICE_CODE: self.log_exc( u"Language '%s' is not supported by the selected TTS engine" % (fragment.language), None, True, ValueError) for fragment in text_file.fragments: for line in fragment.lines: if not gf.is_unicode(line): self.log_exc( u"The text file contain a line which is not a Unicode string", None, True, TypeError) # log parameters if quit_after is not None: self.log([u"Quit after reaching %.3f", quit_after]) if backwards: self.log(u"Synthesizing backwards") # check that output_file_path can be written if not gf.file_can_be_written(output_file_path): self.log_exc( u"Cannot write to output file '%s'" % (output_file_path), None, True, OSError) # first, call Python function _synthesize_multiple_python() if available if self.HAS_PYTHON_CALL: self.log(u"Calling TTS engine via Python") try: computed, result = self._synthesize_multiple_python( text_file, output_file_path, quit_after, backwards) if computed: self.log( u"The _synthesize_multiple_python call was successful, returning anchors" ) return result else: self.log(u"The _synthesize_multiple_python call failed") except Exception as exc: self.log_exc( u"An unexpected error occurred while calling _synthesize_multiple_python", exc, False, None) # call _synthesize_multiple_c_extension() or _synthesize_multiple_subprocess() self.log(u"Calling TTS engine via C extension or subprocess") c_extension_function = self._synthesize_multiple_c_extension if self.HAS_C_EXTENSION_CALL else None subprocess_function = self._synthesize_multiple_subprocess if self.HAS_SUBPROCESS_CALL else None return gf.run_c_extension_with_fallback( self.log, self.C_EXTENSION_NAME, c_extension_function, subprocess_function, (text_file, output_file_path, quit_after, backwards), rconf=self.rconf)
def synthesize_single(self, text, language, output_file_path): """ Create a mono WAVE audio file containing the synthesized text. The ``text`` must be a Unicode string encodable with UTF-8. Return the duration of the synthesized audio file, in seconds. Concrete subclasses can (but they are not required to) implement one of the following private functions: 1. ``_synthesize_single_python()`` 2. ``_synthesize_single_c_extension()`` 3. ``_synthesize_single_subprocess()`` :param string text: the text to synthesize :param language: the language to use :type language: :class:`~aeneas.language.Language` :param string output_file_path: the path of the output audio file :rtype: :class:`~aeneas.timevalue.TimeValue` :raises: TypeError: if ``text`` is ``None`` or it is not a Unicode string :raises: ValueError: if ``self.rconf[RuntimeConfiguration.ALLOW_UNLISTED_LANGUAGES]`` is ``False`` and ``language`` is not supported by the TTS engine :raises: OSError: if output file cannot be written to ``output_file_path`` :raises: RuntimeError: if both the C extension and the pure Python code did not succeed. """ # check that text_file is not None if text is None: self.log_exc(u"text is None", None, True, TypeError) # check that text has unicode type if not gf.is_unicode(text): self.log_exc(u"text is not a Unicode string", None, True, TypeError) # check that output_file_path can be written if not gf.file_can_be_written(output_file_path): self.log_exc(u"Cannot write to output file '%s'" % (output_file_path), None, True, OSError) # check that the requested language is listed in language.py if (language not in self.LANGUAGE_TO_VOICE_CODE) and (not self.rconf[RuntimeConfiguration.ALLOW_UNLISTED_LANGUAGES]): self.log_exc(u"Language '%s' is not supported by the selected TTS engine" % (language), None, True, ValueError) self.log([u"Synthesizing text: '%s'", text]) self.log([u"Synthesizing language: '%s'", language]) self.log([u"Synthesizing to file: '%s'", output_file_path]) # return zero if text is the empty string if len(text) == 0: self.log(u"len(text) is zero: returning 0.000") return TimeValue("0.000") # language to voice code voice_code = self._language_to_voice_code(language) self.log([u"Using voice code: '%s'", voice_code]) # first, call Python function _synthesize_single_python() if available if self.has_python_call: self.log(u"Calling TTS engine via Python") try: result = self._synthesize_single_python(text, voice_code, output_file_path) return result[0] except Exception as exc: self.log_exc(u"An unexpected error occurred while calling _synthesize_single_python", exc, False, None) # call _synthesize_single_c_extension() or _synthesize_single_subprocess() self.log(u"Calling TTS engine via C extension or subprocess") c_extension_function = self._synthesize_single_c_extension if self.has_c_extension_call else None subprocess_function = self._synthesize_single_subprocess if self.has_subprocess_call else None result = gf.run_c_extension_with_fallback( self.log, "cew", c_extension_function, subprocess_function, (text, voice_code, output_file_path), rconf=self.rconf ) return result[0]
def synthesize_multiple(self, text_file, output_file_path, quit_after=None, backwards=False): """ Synthesize the text contained in the given fragment list into a WAVE file. Return a tuple (anchors, total_time, num_chars). Concrete subclasses must implement at least one of the following private functions: 1. ``_synthesize_multiple_python()`` 2. ``_synthesize_multiple_c_extension()`` 3. ``_synthesize_multiple_subprocess()`` :param text_file: the text file to be synthesized :type text_file: :class:`~aeneas.textfile.TextFile` :param string output_file_path: the path to the output audio file :param quit_after: stop synthesizing as soon as reaching this many seconds :type quit_after: :class:`~aeneas.timevalue.TimeValue` :param bool backwards: if > 0, synthese from the end of the text file :rtype: tuple (anchors, total_time, num_chars) :raises: TypeError: if ``text_file`` is ``None`` or one of the text fragments is not a Unicode string :raises: ValueError: if ``self.rconf[RuntimeConfiguration.ALLOW_UNLISTED_LANGUAGES]`` is ``False`` and a fragment has a language code not supported by the TTS engine, or if ``text_file`` has no fragments :raises: OSError: if output file cannot be written to ``output_file_path`` :raises: RuntimeError: if both the C extension and the pure Python code did not succeed. """ if text_file is None: self.log_exc(u"text_file is None", None, True, TypeError) if len(text_file) < 1: self.log_exc(u"The text file has no fragments", None, True, ValueError) if not self.rconf[RuntimeConfiguration.ALLOW_UNLISTED_LANGUAGES]: for fragment in text_file.fragments: if fragment.language not in self.LANGUAGE_TO_VOICE_CODE: self.log_exc(u"Language '%s' is not supported by the selected TTS engine" % (fragment.language), None, True, ValueError) for fragment in text_file.fragments: for line in fragment.lines: if not gf.is_unicode(line): self.log_exc(u"The text file contain a line which is not a Unicode string", None, True, TypeError) # log parameters if quit_after is not None: self.log([u"Quit after reaching %.3f", quit_after]) if backwards: self.log(u"Synthesizing backwards") # check that output_file_path can be written if not gf.file_can_be_written(output_file_path): self.log_exc(u"Cannot write to output file '%s'" % (output_file_path), None, True, OSError) # first, call Python function _synthesize_multiple_python() if available if self.has_python_call: self.log(u"Calling TTS engine via Python") try: computed, result = self._synthesize_multiple_python(text_file, output_file_path, quit_after, backwards) if computed: self.log(u"The _synthesize_multiple_python call was successful, returning anchors") return result else: self.log(u"The _synthesize_multiple_python call failed") except Exception as exc: self.log_exc(u"An unexpected error occurred while calling _synthesize_multiple_python", exc, False, None) # call _synthesize_multiple_c_extension() or _synthesize_multiple_subprocess() self.log(u"Calling TTS engine via C extension or subprocess") c_extension_function = self._synthesize_multiple_c_extension if self.has_c_extension_call else None subprocess_function = self._synthesize_multiple_subprocess if self.has_subprocess_call else None return gf.run_c_extension_with_fallback( self.log, "cew", c_extension_function, subprocess_function, (text_file, output_file_path, quit_after, backwards), rconf=self.rconf )