#!/usr/bin/env python
# coding=utf-8

# aeneas is a Python/C library and a set of tools
# to automagically synchronize audio and text (aka forced alignment)
#
# Copyright (C) 2012-2013, <NAME> (www.albertopettarin.it)
# Copyright (C) 2013-2015, ReadBeyond Srl   (www.readbeyond.it)
# Copyright (C) 2015-2017, <NAME> (www.albertopettarin.it)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
This module contains the following classes:

* :class:`~aeneas.ttswrappers.espeakttswrapper.ESPEAKTTSWrapper`,
  a wrapper for the ``eSpeak`` TTS engine.

Please refer to
http://espeak.sourceforge.net/
for further details.
"""

from __future__ import absolute_import
from __future__ import print_function

from aeneas.exacttiming import TimeValue
from aeneas.language import Language
from aeneas.runtimeconfiguration import RuntimeConfiguration
from aeneas.ttswrappers.basettswrapper import BaseTTSWrapper
import aeneas.globalfunctions as gf


class ESPEAKTTSWrapper(BaseTTSWrapper):
    """
    A wrapper for the ``eSpeak`` TTS engine.

    This wrapper is the default TTS engine for ``aeneas``.

    This wrapper supports calling the TTS engine
    via ``subprocess`` or via Python C extension.

    In abstract terms, it performs one or more calls like ::

        $ espeak -v voice_code -w /tmp/output_file.wav < text

    To use this TTS engine, specify ::

        "tts=espeak"

    in the ``RuntimeConfiguration`` object.
    (You can omit this, since eSpeak is the default TTS engine.)
    To execute from a non-default location: ::

        "tts=espeak|tts_path=/path/to/espeak"

    To run the ``cew`` Python C extension
    in a separate process via
    :class:`~aeneas.cewsubprocess.CEWSubprocess`, use ::

        "cew_subprocess_enabled=True|cew_subprocess_path=/path/to/python"

    in the ``rconf`` object.

    See :class:`~aeneas.ttswrappers.basettswrapper.BaseTTSWrapper`
    for the available functions.
    Below are listed the languages supported by this wrapper.

    :param rconf: a runtime configuration
    :type  rconf: :class:`~aeneas.runtimeconfiguration.RuntimeConfiguration`
    :param logger: the logger object
    :type  logger: :class:`~aeneas.logger.Logger`
    """

    AFR = Language.AFR
    """ Afrikaans """

    ARG = Language.ARG
    """ Aragonese (not tested) """

    BOS = Language.BOS
    """ Bosnian (not tested) """

    BUL = Language.BUL
    """ Bulgarian """

    CAT = Language.CAT
    """ Catalan """

    CES = Language.CES
    """ Czech """

    CMN = Language.CMN
    """ Mandarin Chinese (not tested) """

    CYM = Language.CYM
    """ Welsh """

    DAN = Language.DAN
    """ Danish """

    DEU = Language.DEU
    """ German """

    ELL = Language.ELL
    """ Greek (Modern) """

    ENG = Language.ENG
    """ English """

    EPO = Language.EPO
    """ Esperanto (not tested) """

    EST = Language.EST
    """ Estonian """

    FAS = Language.FAS
    """ Persian """

    FIN = Language.FIN
    """ Finnish """

    FRA = Language.FRA
    """ French """

    GLE = Language.GLE
    """ Irish """

    GRC = Language.GRC
    """ Greek (Ancient) """

    HIN = Language.HIN
    """ Hindi (not tested) """

    HRV = Language.HRV
    """ Croatian """

    HUN = Language.HUN
    """ Hungarian """

    HYE = Language.HYE
    """ Armenian (not tested) """

    IND = Language.IND
    """ Indonesian (not tested) """

    ISL = Language.ISL
    """ Icelandic """

    ITA = Language.ITA
    """ Italian """

    JBO = Language.JBO
    """ Lojban (not tested) """

    KAN = Language.KAN
    """ Kannada (not tested) """

    KAT = Language.KAT
    """ Georgian (not tested) """

    KUR = Language.KUR
    """ Kurdish (not tested) """

    LAT = Language.LAT
    """ Latin """

    LAV = Language.LAV
    """ Latvian """

    LFN = Language.LFN
    """ Lingua Franca Nova (not tested) """

    LIT = Language.LIT
    """ Lithuanian """

    MAL = Language.MAL
    """ Malayalam (not tested) """

    MKD = Language.MKD
    """ Macedonian (not tested) """

    MSA = Language.MSA
    """ Malay (not tested) """

    NEP = Language.NEP
    """ Nepali (not tested) """

    NLD = Language.NLD
    """ Dutch """

    NOR = Language.NOR
    """ Norwegian """

    PAN = Language.PAN
    """ Panjabi (not tested) """

    POL = Language.POL
    """ Polish """

    POR = Language.POR
    """ Portuguese """

    RON = Language.RON
    """ Romanian """

    RUS = Language.RUS
    """ Russian """

    SLK = Language.SLK
    """ Slovak """

    SPA = Language.SPA
    """ Spanish """

    SQI = Language.SQI
    """ Albanian (not tested) """

    SRP = Language.SRP
    """ Serbian """

    SWA = Language.SWA
    """ Swahili """

    SWE = Language.SWE
    """ Swedish """

    TAM = Language.TAM
    """ Tamil (not tested) """

    TUR = Language.TUR
    """ Turkish """

    UKR = Language.UKR
    """ Ukrainian """

    VIE = Language.VIE
    """ Vietnamese (not tested) """

    YUE = Language.YUE
    """ Yue Chinese (not tested) """

    ZHO = Language.ZHO
    """ Chinese (not tested) """

    ENG_GBR = "eng-GBR"
    """ English (GB) """

    ENG_SCT = "eng-SCT"
    """ English (Scotland) (not tested) """

    ENG_USA = "eng-USA"
    """ English (USA) """

    SPA_ESP = "spa-ESP"
    """ Spanish (Castillan) """

    FRA_BEL = "fra-BEL"
    """ French (Belgium) (not tested) """

    FRA_FRA = "fra-FRA"
    """ French (France) """

    POR_BRA = "por-bra"
    """ Portuguese (Brazil) (not tested) """

    POR_PRT = "por-prt"
    """ Portuguese (Portugal) """

    AF = "af"
    """ Afrikaans """

    AN = "an"
    """ Aragonese (not tested) """

    BG = "bg"
    """ Bulgarian """

    BS = "bs"
    """ Bosnian (not tested) """

    CA = "ca"
    """ Catalan """

    CS = "cs"
    """ Czech """

    CY = "cy"
    """ Welsh """

    DA = "da"
    """ Danish """

    DE = "de"
    """ German """

    EL = "el"
    """ Greek (Modern) """

    EN = "en"
    """ English """

    EN_GB = "en-gb"
    """ English (GB) """

    EN_SC = "en-sc"
    """ English (Scotland) (not tested) """

    EN_UK_NORTH = "en-uk-north"
    """ English (Northern) (not tested) """

    EN_UK_RP = "en-uk-rp"
    """ English (Received Pronunciation) (not tested) """

    EN_UK_WMIDS = "en-uk-wmids"
    """ English (Midlands) (not tested) """

    EN_US = "en-us"
    """ English (USA) """

    EN_WI = "en-wi"
    """ English (West Indies) (not tested) """

    EO = "eo"
    """ Esperanto (not tested) """

    ES = "es"
    """ Spanish (Castillan) """

    ES_LA = "es-la"
    """ Spanish (Latin America) (not tested) """

    ET = "et"
    """ Estonian """

    FA = "fa"
    """ Persian """

    FA_PIN = "fa-pin"
    """ Persian (Pinglish) """

    FI = "fi"
    """ Finnish """

    FR = "fr"
    """ French """

    FR_BE = "fr-be"
    """ French (Belgium) (not tested) """

    FR_FR = "fr-fr"
    """ French (France) """

    GA = "ga"
    """ Irish """

    # NOTE already defined
    # COMMENTED GRC = "grc"
    # COMMENTED """ Greek (Ancient) """

    HI = "hi"
    """ Hindi (not tested) """

    HR = "hr"
    """ Croatian """

    HU = "hu"
    """ Hungarian """

    HY = "hy"
    """ Armenian (not tested) """

    HY_WEST = "hy-west"
    """ Armenian (West) (not tested) """

    ID = "id"
    """ Indonesian (not tested) """

    IS = "is"
    """ Icelandic """

    IT = "it"
    """ Italian """

    # NOTE already defined
    # COMMENTED JBO = "jbo"
    # COMMENTED """ Lojban (not tested) """

    KA = "ka"
    """ Georgian (not tested) """

    KN = "kn"
    """ Kannada (not tested) """

    KU = "ku"
    """ Kurdish (not tested) """

    LA = "la"
    """ Latin """

    # NOTE already defined
    # COMMENTED LFN = "lfn"
    # COMMENTED """ Lingua Franca Nova (not tested) """

    LT = "lt"
    """ Lithuanian """

    LV = "lv"
    """ Latvian """

    MK = "mk"
    """ Macedonian (not tested) """

    ML = "ml"
    """ Malayalam (not tested) """

    MS = "ms"
    """ Malay (not tested) """

    NE = "ne"
    """ Nepali (not tested) """

    NL = "nl"
    """ Dutch """

    NO = "no"
    """ Norwegian """

    PA = "pa"
    """ Panjabi (not tested) """

    PL = "pl"
    """ Polish """

    PT = "pt"
    """ Portuguese """

    PT_BR = "pt-br"
    """ Portuguese (Brazil) (not tested) """

    PT_PT = "pt-pt"
    """ Portuguese (Portugal) """

    RO = "ro"
    """ Romanian """

    RU = "ru"
    """ Russian """

    SQ = "sq"
    """ Albanian (not tested) """

    SK = "sk"
    """ Slovak """

    SR = "sr"
    """ Serbian """

    SV = "sv"
    """ Swedish """

    SW = "sw"
    """ Swahili """

    TA = "ta"
    """ Tamil (not tested) """

    TR = "tr"
    """ Turkish """

    UK = "uk"
    """ Ukrainian """

    VI = "vi"
    """ Vietnamese (not tested) """

    VI_HUE = "vi-hue"
    """ Vietnamese (hue) (not tested) """

    VI_SGN = "vi-sgn"
    """ Vietnamese (sgn) (not tested) """

    ZH = "zh"
    """ Mandarin Chinese (not tested) """

    ZH_YUE = "zh-yue"
    """ Yue Chinese (not tested) """

    CODE_TO_HUMAN = {
        AFR: u"Afrikaans",
        ARG: u"Aragonese (not tested)",
        BOS: u"Bosnian (not tested)",
        BUL: u"Bulgarian",
        CAT: u"Catalan",
        CES: u"Czech",
        CMN: u"Mandarin Chinese (not tested)",
        CYM: u"Welsh",
        DAN: u"Danish",
        DEU: u"German",
        ELL: u"Greek (Modern)",
        ENG: u"English",
        EPO: u"Esperanto (not tested)",
        EST: u"Estonian",
        FAS: u"Persian",
        FIN: u"Finnish",
        FRA: u"French",
        GLE: u"Irish",
        GRC: u"Greek (Ancient)",
        HIN: u"Hindi (not tested)",
        HRV: u"Croatian",
        HUN: u"Hungarian",
        HYE: u"Armenian (not tested)",
        IND: u"Indonesian (not tested)",
        ISL: u"Icelandic",
        ITA: u"Italian",
        JBO: u"Lojban (not tested)",
        KAN: u"Kannada (not tested)",
        KAT: u"Georgian (not tested)",
        KUR: u"Kurdish (not tested)",
        LAT: u"Latin",
        LAV: u"Latvian",
        LFN: u"Lingua Franca Nova (not tested)",
        LIT: u"Lithuanian",
        MAL: u"Malayalam (not tested)",
        MKD: u"Macedonian (not tested)",
        MSA: u"Malay (not tested)",
        NEP: u"Nepali (not tested)",
        NLD: u"Dutch",
        NOR: u"Norwegian",
        PAN: u"Panjabi (not tested)",
        POL: u"Polish",
        POR: u"Portuguese",
        RON: u"Romanian",
        RUS: u"Russian",
        SLK: u"Slovak",
        SPA: u"Spanish",
        SQI: u"Albanian (not tested)",
        SRP: u"Serbian",
        SWA: u"Swahili",
        SWE: u"Swedish",
        TAM: u"Tamil (not tested)",
        TUR: u"Turkish",
        UKR: u"Ukrainian",
        VIE: u"Vietnamese (not tested)",
        YUE: u"Yue Chinese (not tested)",
        ZHO: u"Chinese (not tested)",
        ENG_GBR: u"English (GB)",
        ENG_SCT: u"English (Scotland) (not tested)",
        ENG_USA: u"English (USA)",
        SPA_ESP: u"Spanish (Castillan)",
        FRA_BEL: u"French (Belgium) (not tested)",
        FRA_FRA: u"French (France)",
        POR_BRA: u"Portuguese (Brazil) (not tested)",
        POR_PRT: u"Portuguese (Portugal)",
        AF: u"Afrikaans",
        AN: u"Aragonese (not tested)",
        BG: u"Bulgarian",
        BS: u"Bosnian (not tested)",
        CA: u"Catalan",
        CS: u"Czech",
        CY: u"Welsh",
        DA: u"Danish",
        DE: u"German",
        EL: u"Greek (Modern)",
        EN: u"English",
        EN_GB: u"English (GB)",
        EN_SC: u"English (Scotland) (not tested)",
        EN_UK_NORTH: u"English (Northern) (not tested)",
        EN_UK_RP: u"English (Received Pronunciation) (not tested)",
        EN_UK_WMIDS: u"English (Midlands) (not tested)",
        EN_US: u"English (USA)",
        EN_WI: u"English (West Indies) (not tested)",
        EO: u"Esperanto (not tested)",
        ES: u"Spanish (Castillan)",
        ES_LA: u"Spanish (Latin America) (not tested)",
        ET: u"Estonian",
        FA: u"Persian",
        FA_PIN: u"Persian (Pinglish)",
        FI: u"Finnish",
        FR: u"French",
        FR_BE: u"French (Belgium) (not tested)",
        FR_FR: u"French (France)",
        GA: u"Irish",
        HI: u"Hindi (not tested)",
        HR: u"Croatian",
        HU: u"Hungarian",
        HY: u"Armenian (not tested)",
        HY_WEST: u"Armenian (West) (not tested)",
        ID: u"Indonesian (not tested)",
        IS: u"Icelandic",
        IT: u"Italian",
        KA: u"Georgian (not tested)",
        KN: u"Kannada (not tested)",
        KU: u"Kurdish (not tested)",
        LA: u"Latin",
        LT: u"Lithuanian",
        LV: u"Latvian",
        MK: u"Macedonian (not tested)",
        ML: u"Malayalam (not tested)",
        MS: u"Malay (not tested)",
        NE: u"Nepali (not tested)",
        NL: u"Dutch",
        NO: u"Norwegian",
        PA: u"Panjabi (not tested)",
        PL: u"Polish",
        PT: u"Portuguese",
        PT_BR: u"Portuguese (Brazil) (not tested)",
        PT_PT: u"Portuguese (Portugal)",
        RO: u"Romanian",
        RU: u"Russian",
        SQ: u"Albanian (not tested)",
        SK: u"Slovak",
        SR: u"Serbian",
        SV: u"Swedish",
        SW: u"Swahili",
        TA: u"Tamil (not tested)",
        TR: u"Turkish",
        UK: u"Ukrainian",
        VI: u"Vietnamese (not tested)",
        VI_HUE: u"Vietnamese (hue) (not tested)",
        VI_SGN: u"Vietnamese (sgn) (not tested)",
        ZH: u"Mandarin Chinese (not tested)",
        ZH_YUE: u"Yue Chinese (not tested)",
    }

    CODE_TO_HUMAN_LIST = sorted([u"%s\t%s" % (k, v) for k, v in CODE_TO_HUMAN.items()])

    LANGUAGE_TO_VOICE_CODE = {
        AF: "af",
        AN: "an",
        BG: "bg",
        BS: "bs",
        CA: "ca",
        CS: "cs",
        CY: "cy",
        DA: "da",
        DE: "de",
        EL: "el",
        EN: "en",
        EN_GB: "en-gb",
        EN_SC: "en-sc",
        EN_UK_NORTH: "en-uk-north",
        EN_UK_RP: "en-uk-rp",
        EN_UK_WMIDS: "en-uk-wmids",
        EN_US: "en-us",
        EN_WI: "en-wi",
        EO: "eo",
        ES: "es",
        ES_LA: "es-la",
        ET: "et",
        FA: "fa",
        FA_PIN: "fa-pin",
        FI: "fi",
        FR: "fr",
        FR_BE: "fr-be",
        FR_FR: "fr-fr",
        GA: "ga",
        # COMMENTED GRC: "grc",
        HI: "hi",
        HR: "hr",
        HU: "hu",
        HY: "hy",
        HY_WEST: "hy-west",
        ID: "id",
        IS: "is",
        IT: "it",
        # COMMENTED JBO: "jbo",
        KA: "ka",
        KN: "kn",
        KU: "ku",
        LA: "la",
        # COMMENTED LFN: "lfn",
        LT: "lt",
        LV: "lv",
        MK: "mk",
        ML: "ml",
        MS: "ms",
        NE: "ne",
        NL: "nl",
        NO: "no",
        PA: "pa",
        PL: "pl",
        PT: "pt",
        PT_BR: "pt-br",
        PT_PT: "pt-pt",
        RO: "ro",
        RU: "ru",
        SQ: "sq",
        SK: "sk",
        SR: "sr",
        SV: "sv",
        SW: "sw",
        TA: "ta",
        TR: "tr",
        UK: "ru",   # NOTE mocking support for Ukrainian with Russian voice
        VI: "vi",
        VI_HUE: "vi-hue",
        VI_SGN: "vi-sgn",
        ZH: "zh",
        ZH_YUE: "zh-yue",
        AFR: "af",
        ARG: "an",
        BOS: "bs",
        BUL: "bg",
        CAT: "ca",
        CES: "cs",
        CMN: "zh",
        CYM: "cy",
        DAN: "da",
        DEU: "de",
        ELL: "el",
        ENG: "en",
        EPO: "eo",
        EST: "et",
        FAS: "fa",
        FIN: "fi",
        FRA: "fr",
        GLE: "ga",
        GRC: "grc",
        HIN: "hi",
        HRV: "hr",
        HUN: "hu",
        HYE: "hy",
        IND: "id",
        ISL: "is",
        ITA: "it",
        JBO: "jbo",
        KAN: "kn",
        KAT: "ka",
        KUR: "ku",
        LAT: "la",
        LAV: "lv",
        LFN: "lfn",
        LIT: "lt",
        MAL: "ml",
        MKD: "mk",
        MSA: "ms",
        NEP: "ne",
        NLD: "nl",
        NOR: "no",
        PAN: "pa",
        POL: "pl",
        POR: "pt",
        RON: "ro",
        RUS: "ru",
        SLK: "sk",
        SPA: "es",
        SQI: "sq",
        SRP: "sr",
        SWA: "sw",
        SWE: "sv",
        TAM: "ta",
        TUR: "tr",
        UKR: "ru",  # NOTE mocking support for Ukrainian with Russian voice
        VIE: "vi",
        YUE: "zh-yue",
        ZHO: "zh",
        ENG_GBR: "en-gb",
        ENG_SCT: "en-sc",
        ENG_USA: "en-us",
        SPA_ESP: "es-es",
        FRA_BEL: "fr-be",
        FRA_FRA: "fr-fr",
        POR_BRA: "pt-br",
        POR_PRT: "pt-pt"
    }
    DEFAULT_LANGUAGE = ENG

    DEFAULT_TTS_PATH = "espeak"

    OUTPUT_AUDIO_FORMAT = ("pcm_s16le", 1, 22050)

    HAS_SUBPROCESS_CALL = True

    HAS_C_EXTENSION_CALL = True

    C_EXTENSION_NAME = "cew"

    TAG = u"ESPEAKTTSWrapper"

    def __init__(self, rconf=None, logger=None):
        super(ESPEAKTTSWrapper, self).__init__(rconf=rconf, logger=logger)
        self.set_subprocess_arguments([
            self.tts_path,
            u"-v",
            self.CLI_PARAMETER_VOICE_CODE_STRING,
            u"-w",
            self.CLI_PARAMETER_WAVE_PATH,
            self.CLI_PARAMETER_TEXT_STDIN
        ])

    def _synthesize_multiple_c_extension(self, text_file, output_file_path, quit_after=None, backwards=False):
        """
        Synthesize multiple text fragments, using the cew extension.

        Return a tuple (anchors, total_time, num_chars).

        :rtype: (bool, (list, :class:`~aeneas.exacttiming.TimeValue`, int))
        """
        self.log(u"Synthesizing using C extension...")

        # convert parameters from Python values to C values
        try:
            c_quit_after = float(quit_after)
        except TypeError:
            c_quit_after = 0.0
        c_backwards = 0
        if backwards:
            c_backwards = 1
        self.log([u"output_file_path: %s", output_file_path])
        self.log([u"c_quit_after:     %.3f", c_quit_after])
        self.log([u"c_backwards:      %d", c_backwards])
        self.log(u"Preparing u_text...")
        u_text = []
        fragments = text_file.fragments
        for fragment in fragments:
            f_lang = fragment.language
            f_text = fragment.filtered_text
            if f_lang is None:
                f_lang = self.DEFAULT_LANGUAGE
            f_voice_code = self._language_to_voice_code(f_lang)
            if f_text is None:
                f_text = u""
            u_text.append((f_voice_code, f_text))
        self.log(u"Preparing u_text... done")

        # call C extension
        sr = None
        sf = None
        intervals = None
        if self.rconf[RuntimeConfiguration.CEW_SUBPROCESS_ENABLED]:
            self.log(u"Using cewsubprocess to call aeneas.cew")
            try:
                self.log(u"Importing aeneas.cewsubprocess...")
                from aeneas.cewsubprocess import CEWSubprocess
                self.log(u"Importing aeneas.cewsubprocess... done")
                self.log(u"Calling aeneas.cewsubprocess...")
                cewsub = CEWSubprocess(rconf=self.rconf, logger=self.logger)
                sr, sf, intervals = cewsub.synthesize_multiple(output_file_path, c_quit_after, c_backwards, u_text)
                self.log(u"Calling aeneas.cewsubprocess... done")
            except Exception as exc:
                self.log_exc(u"An unexpected error occurred while running cewsubprocess", exc, False, None)
                # NOTE not critical, try calling aeneas.cew directly
                # COMMENTED return (False, None)

        if sr is None:
            self.log(u"Preparing c_text...")
            if gf.PY2:
                # Python 2 => pass byte strings
                c_text = [(gf.safe_bytes(t[0]), gf.safe_bytes(t[1])) for t in u_text]
            else:
                # Python 3 => pass Unicode strings
                c_text = [(gf.safe_unicode(t[0]), gf.safe_unicode(t[1])) for t in u_text]
            self.log(u"Preparing c_text... done")

            self.log(u"Calling aeneas.cew directly")
            try:
                self.log(u"Importing aeneas.cew...")
                import aeneas.cew.cew
                self.log(u"Importing aeneas.cew... done")
                self.log(u"Calling aeneas.cew...")
                sr, sf, intervals = aeneas.cew.cew.synthesize_multiple(
                    output_file_path,
                    c_quit_after,
                    c_backwards,
                    c_text
                )
                self.log(u"Calling aeneas.cew... done")
            except Exception as exc:
                self.log_exc(u"An unexpected error occurred while running cew", exc, False, None)
                return (False, None)

        self.log([u"sr: %d", sr])
        self.log([u"sf: %d", sf])

        # create output
        anchors = []
        current_time = TimeValue("0.000")
        num_chars = 0
        if backwards:
            fragments = fragments[::-1]
        for i in range(sf):
            # get the correct fragment
            fragment = fragments[i]
            # store for later output
            anchors.append([
                TimeValue(intervals[i][0]),
                fragment.identifier,
                fragment.filtered_text
            ])
            # increase the character counter
            num_chars += fragment.characters
            # update current_time
            current_time = TimeValue(intervals[i][1])

        # return output
        # NOTE anchors do not make sense if backwards == True
        self.log([u"Returning %d time anchors", len(anchors)])
        self.log([u"Current time %.3f", current_time])
        self.log([u"Synthesized %d characters", num_chars])
        self.log(u"Synthesizing using C extension... done")
        return (True, (anchors, current_time, num_chars))

#!/usr/bin/env python
# coding=utf-8

# aeneas is a Python/C library and a set of tools
# to automagically synchronize audio and text (aka forced alignment)
#
# Copyright (C) 2012-2013, <NAME> (www.albertopettarin.it)
# Copyright (C) 2013-2015, ReadBeyond Srl   (www.readbeyond.it)
# Copyright (C) 2015-2017, <NAME> (www.albertopettarin.it)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
This module contains the following classes:

* :class:`~aeneas.ttswrappers.espeakttswrapper.ESPEAKTTSWrapper`,
  a wrapper for the ``eSpeak`` TTS engine.

Please refer to
http://espeak.sourceforge.net/
for further details.
"""

from __future__ import absolute_import
from __future__ import print_function

from aeneas.exacttiming import TimeValue
from aeneas.language import Language
from aeneas.runtimeconfiguration import RuntimeConfiguration
from aeneas.ttswrappers.basettswrapper import BaseTTSWrapper
import aeneas.globalfunctions as gf


class ESPEAKTTSWrapper(BaseTTSWrapper):
    """
    A wrapper for the ``eSpeak`` TTS engine.

    This wrapper is the default TTS engine for ``aeneas``.

    This wrapper supports calling the TTS engine
    via ``subprocess`` or via Python C extension.

    In abstract terms, it performs one or more calls like ::

        $ espeak -v voice_code -w /tmp/output_file.wav < text

    To use this TTS engine, specify ::

        "tts=espeak"

    in the ``RuntimeConfiguration`` object.
    (You can omit this, since eSpeak is the default TTS engine.)
    To execute from a non-default location: ::

        "tts=espeak|tts_path=/path/to/espeak"

    To run the ``cew`` Python C extension
    in a separate process via
    :class:`~aeneas.cewsubprocess.CEWSubprocess`, use ::

        "cew_subprocess_enabled=True|cew_subprocess_path=/path/to/python"

    in the ``rconf`` object.

    See :class:`~aeneas.ttswrappers.basettswrapper.BaseTTSWrapper`
    for the available functions.
    Below are listed the languages supported by this wrapper.

    :param rconf: a runtime configuration
    :type  rconf: :class:`~aeneas.runtimeconfiguration.RuntimeConfiguration`
    :param logger: the logger object
    :type  logger: :class:`~aeneas.logger.Logger`
    """

    AFR = Language.AFR
    """ Afrikaans """

    ARG = Language.ARG
    """ Aragonese (not tested) """

    BOS = Language.BOS
    """ Bosnian (not tested) """

    BUL = Language.BUL
    """ Bulgarian """

    CAT = Language.CAT
    """ Catalan """

    CES = Language.CES
    """ Czech """

    CMN = Language.CMN
    """ Mandarin Chinese (not tested) """

    CYM = Language.CYM
    """ Welsh """

    DAN = Language.DAN
    """ Danish """

    DEU = Language.DEU
    """ German """

    ELL = Language.ELL
    """ Greek (Modern) """

    ENG = Language.ENG
    """ English """

    EPO = Language.EPO
    """ Esperanto (not tested) """

    EST = Language.EST
    """ Estonian """

    FAS = Language.FAS
    """ Persian """

    FIN = Language.FIN
    """ Finnish """

    FRA = Language.FRA
    """ French """

    GLE = Language.GLE
    """ Irish """

    GRC = Language.GRC
    """ Greek (Ancient) """

    HIN = Language.HIN
    """ Hindi (not tested) """

    HRV = Language.HRV
    """ Croatian """

    HUN = Language.HUN
    """ Hungarian """

    HYE = Language.HYE
    """ Armenian (not tested) """

    IND = Language.IND
    """ Indonesian (not tested) """

    ISL = Language.ISL
    """ Icelandic """

    ITA = Language.ITA
    """ Italian """

    JBO = Language.JBO
    """ Lojban (not tested) """

    KAN = Language.KAN
    """ Kannada (not tested) """

    KAT = Language.KAT
    """ Georgian (not tested) """

    KUR = Language.KUR
    """ Kurdish (not tested) """

    LAT = Language.LAT
    """ Latin """

    LAV = Language.LAV
    """ Latvian """

    LFN = Language.LFN
    """ Lingua Franca Nova (not tested) """

    LIT = Language.LIT
    """ Lithuanian """

    MAL = Language.MAL
    """ Malayalam (not tested) """

    MKD = Language.MKD
    """ Macedonian (not tested) """

    MSA = Language.MSA
    """ Malay (not tested) """

    NEP = Language.NEP
    """ Nepali (not tested) """

    NLD = Language.NLD
    """ Dutch """

    NOR = Language.NOR
    """ Norwegian """

    PAN = Language.PAN
    """ Panjabi (not tested) """

    POL = Language.POL
    """ Polish """

    POR = Language.POR
    """ Portuguese """

    RON = Language.RON
    """ Romanian """

    RUS = Language.RUS
    """ Russian """

    SLK = Language.SLK
    """ Slovak """

    SPA = Language.SPA
    """ Spanish """

    SQI = Language.SQI
    """ Albanian (not tested) """

    SRP = Language.SRP
    """ Serbian """

    SWA = Language.SWA
    """ Swahili """

    SWE = Language.SWE
    """ Swedish """

    TAM = Language.TAM
    """ Tamil (not tested) """

    TUR = Language.TUR
    """ Turkish """

    UKR = Language.UKR
    """ Ukrainian """

    VIE = Language.VIE
    """ Vietnamese (not tested) """

    YUE = Language.YUE
    """ Yue Chinese (not tested) """

    ZHO = Language.ZHO
    """ Chinese (not tested) """

    ENG_GBR = "eng-GBR"
    """ English (GB) """

    ENG_SCT = "eng-SCT"
    """ English (Scotland) (not tested) """

    ENG_USA = "eng-USA"
    """ English (USA) """

    SPA_ESP = "spa-ESP"
    """ Spanish (Castillan) """

    FRA_BEL = "fra-BEL"
    """ French (Belgium) (not tested) """

    FRA_FRA = "fra-FRA"
    """ French (France) """

    POR_BRA = "por-bra"
    """ Portuguese (Brazil) (not tested) """

    POR_PRT = "por-prt"
    """ Portuguese (Portugal) """

    AF = "af"
    """ Afrikaans """

    AN = "an"
    """ Aragonese (not tested) """

    BG = "bg"
    """ Bulgarian """

    BS = "bs"
    """ Bosnian (not tested) """

    CA = "ca"
    """ Catalan """

    CS = "cs"
    """ Czech """

    CY = "cy"
    """ Welsh """

    DA = "da"
    """ Danish """

    DE = "de"
    """ German """

    EL = "el"
    """ Greek (Modern) """

    EN = "en"
    """ English """

    EN_GB = "en-gb"
    """ English (GB) """

    EN_SC = "en-sc"
    """ English (Scotland) (not tested) """

    EN_UK_NORTH = "en-uk-north"
    """ English (Northern) (not tested) """

    EN_UK_RP = "en-uk-rp"
    """ English (Received Pronunciation) (not tested) """

    EN_UK_WMIDS = "en-uk-wmids"
    """ English (Midlands) (not tested) """

    EN_US = "en-us"
    """ English (USA) """

    EN_WI = "en-wi"
    """ English (West Indies) (not tested) """

    EO = "eo"
    """ Esperanto (not tested) """

    ES = "es"
    """ Spanish (Castillan) """

    ES_LA = "es-la"
    """ Spanish (Latin America) (not tested) """

    ET = "et"
    """ Estonian """

    FA = "fa"
    """ Persian """

    FA_PIN = "fa-pin"
    """ Persian (Pinglish) """

    FI = "fi"
    """ Finnish """

    FR = "fr"
    """ French """

    FR_BE = "fr-be"
    """ French (Belgium) (not tested) """

    FR_FR = "fr-fr"
    """ French (France) """

    GA = "ga"
    """ Irish """

    # NOTE already defined
    # COMMENTED GRC = "grc"
    # COMMENTED """ Greek (Ancient) """

    HI = "hi"
    """ Hindi (not tested) """

    HR = "hr"
    """ Croatian """

    HU = "hu"
    """ Hungarian """

    HY = "hy"
    """ Armenian (not tested) """

    HY_WEST = "hy-west"
    """ Armenian (West) (not tested) """

    ID = "id"
    """ Indonesian (not tested) """

    IS = "is"
    """ Icelandic """

    IT = "it"
    """ Italian """

    # NOTE already defined
    # COMMENTED JBO = "jbo"
    # COMMENTED """ Lojban (not tested) """

    KA = "ka"
    """ Georgian (not tested) """

    KN = "kn"
    """ Kannada (not tested) """

    KU = "ku"
    """ Kurdish (not tested) """

    LA = "la"
    """ Latin """

    # NOTE already defined
    # COMMENTED LFN = "lfn"
    # COMMENTED """ Lingua Franca Nova (not tested) """

    LT = "lt"
    """ Lithuanian """

    LV = "lv"
    """ Latvian """

    MK = "mk"
    """ Macedonian (not tested) """

    ML = "ml"
    """ Malayalam (not tested) """

    MS = "ms"
    """ Malay (not tested) """

    NE = "ne"
    """ Nepali (not tested) """

    NL = "nl"
    """ Dutch """

    NO = "no"
    """ Norwegian """

    PA = "pa"
    """ Panjabi (not tested) """

    PL = "pl"
    """ Polish """

    PT = "pt"
    """ Portuguese """

    PT_BR = "pt-br"
    """ Portuguese (Brazil) (not tested) """

    PT_PT = "pt-pt"
    """ Portuguese (Portugal) """

    RO = "ro"
    """ Romanian """

    RU = "ru"
    """ Russian """

    SQ = "sq"
    """ Albanian (not tested) """

    SK = "sk"
    """ Slovak """

    SR = "sr"
    """ Serbian """

    SV = "sv"
    """ Swedish """

    SW = "sw"
    """ Swahili """

    TA = "ta"
    """ Tamil (not tested) """

    TR = "tr"
    """ Turkish """

    UK = "uk"
    """ Ukrainian """

    VI = "vi"
    """ Vietnamese (not tested) """

    VI_HUE = "vi-hue"
    """ Vietnamese (hue) (not tested) """

    VI_SGN = "vi-sgn"
    """ Vietnamese (sgn) (not tested) """

    ZH = "zh"
    """ Mandarin Chinese (not tested) """

    ZH_YUE = "zh-yue"
    """ Yue Chinese (not tested) """

    CODE_TO_HUMAN = {
        AFR: u"Afrikaans",
        ARG: u"Aragonese (not tested)",
        BOS: u"Bosnian (not tested)",
        BUL: u"Bulgarian",
        CAT: u"Catalan",
        CES: u"Czech",
        CMN: u"Mandarin Chinese (not tested)",
        CYM: u"Welsh",
        DAN: u"Danish",
        DEU: u"German",
        ELL: u"Greek (Modern)",
        ENG: u"English",
        EPO: u"Esperanto (not tested)",
        EST: u"Estonian",
        FAS: u"Persian",
        FIN: u"Finnish",
        FRA: u"French",
        GLE: u"Irish",
        GRC: u"Greek (Ancient)",
        HIN: u"Hindi (not tested)",
        HRV: u"Croatian",
        HUN: u"Hungarian",
        HYE: u"Armenian (not tested)",
        IND: u"Indonesian (not tested)",
        ISL: u"Icelandic",
        ITA: u"Italian",
        JBO: u"Lojban (not tested)",
        KAN: u"Kannada (not tested)",
        KAT: u"Georgian (not tested)",
        KUR: u"Kurdish (not tested)",
        LAT: u"Latin",
        LAV: u"Latvian",
        LFN: u"Lingua Franca Nova (not tested)",
        LIT: u"Lithuanian",
        MAL: u"Malayalam (not tested)",
        MKD: u"Macedonian (not tested)",
        MSA: u"Malay (not tested)",
        NEP: u"Nepali (not tested)",
        NLD: u"Dutch",
        NOR: u"Norwegian",
        PAN: u"Panjabi (not tested)",
        POL: u"Polish",
        POR: u"Portuguese",
        RON: u"Romanian",
        RUS: u"Russian",
        SLK: u"Slovak",
        SPA: u"Spanish",
        SQI: u"Albanian (not tested)",
        SRP: u"Serbian",
        SWA: u"Swahili",
        SWE: u"Swedish",
        TAM: u"Tamil (not tested)",
        TUR: u"Turkish",
        UKR: u"Ukrainian",
        VIE: u"Vietnamese (not tested)",
        YUE: u"Yue Chinese (not tested)",
        ZHO: u"Chinese (not tested)",
        ENG_GBR: u"English (GB)",
        ENG_SCT: u"English (Scotland) (not tested)",
        ENG_USA: u"English (USA)",
        SPA_ESP: u"Spanish (Castillan)",
        FRA_BEL: u"French (Belgium) (not tested)",
        FRA_FRA: u"French (France)",
        POR_BRA: u"Portuguese (Brazil) (not tested)",
        POR_PRT: u"Portuguese (Portugal)",
        AF: u"Afrikaans",
        AN: u"Aragonese (not tested)",
        BG: u"Bulgarian",
        BS: u"Bosnian (not tested)",
        CA: u"Catalan",
        CS: u"Czech",
        CY: u"Welsh",
        DA: u"Danish",
        DE: u"German",
        EL: u"Greek (Modern)",
        EN: u"English",
        EN_GB: u"English (GB)",
        EN_SC: u"English (Scotland) (not tested)",
        EN_UK_NORTH: u"English (Northern) (not tested)",
        EN_UK_RP: u"English (Received Pronunciation) (not tested)",
        EN_UK_WMIDS: u"English (Midlands) (not tested)",
        EN_US: u"English (USA)",
        EN_WI: u"English (West Indies) (not tested)",
        EO: u"Esperanto (not tested)",
        ES: u"Spanish (Castillan)",
        ES_LA: u"Spanish (Latin America) (not tested)",
        ET: u"Estonian",
        FA: u"Persian",
        FA_PIN: u"Persian (Pinglish)",
        FI: u"Finnish",
        FR: u"French",
        FR_BE: u"French (Belgium) (not tested)",
        FR_FR: u"French (France)",
        GA: u"Irish",
        HI: u"Hindi (not tested)",
        HR: u"Croatian",
        HU: u"Hungarian",
        HY: u"Armenian (not tested)",
        HY_WEST: u"Armenian (West) (not tested)",
        ID: u"Indonesian (not tested)",
        IS: u"Icelandic",
        IT: u"Italian",
        KA: u"Georgian (not tested)",
        KN: u"Kannada (not tested)",
        KU: u"Kurdish (not tested)",
        LA: u"Latin",
        LT: u"Lithuanian",
        LV: u"Latvian",
        MK: u"Macedonian (not tested)",
        ML: u"Malayalam (not tested)",
        MS: u"Malay (not tested)",
        NE: u"Nepali (not tested)",
        NL: u"Dutch",
        NO: u"Norwegian",
        PA: u"Panjabi (not tested)",
        PL: u"Polish",
        PT: u"Portuguese",
        PT_BR: u"Portuguese (Brazil) (not tested)",
        PT_PT: u"Portuguese (Portugal)",
        RO: u"Romanian",
        RU: u"Russian",
        SQ: u"Albanian (not tested)",
        SK: u"Slovak",
        SR: u"Serbian",
        SV: u"Swedish",
        SW: u"Swahili",
        TA: u"Tamil (not tested)",
        TR: u"Turkish",
        UK: u"Ukrainian",
        VI: u"Vietnamese (not tested)",
        VI_HUE: u"Vietnamese (hue) (not tested)",
        VI_SGN: u"Vietnamese (sgn) (not tested)",
        ZH: u"Mandarin Chinese (not tested)",
        ZH_YUE: u"Yue Chinese (not tested)",
    }

    CODE_TO_HUMAN_LIST = sorted([u"%s\t%s" % (k, v) for k, v in CODE_TO_HUMAN.items()])

    LANGUAGE_TO_VOICE_CODE = {
        AF: "af",
        AN: "an",
        BG: "bg",
        BS: "bs",
        CA: "ca",
        CS: "cs",
        CY: "cy",
        DA: "da",
        DE: "de",
        EL: "el",
        EN: "en",
        EN_GB: "en-gb",
        EN_SC: "en-sc",
        EN_UK_NORTH: "en-uk-north",
        EN_UK_RP: "en-uk-rp",
        EN_UK_WMIDS: "en-uk-wmids",
        EN_US: "en-us",
        EN_WI: "en-wi",
        EO: "eo",
        ES: "es",
        ES_LA: "es-la",
        ET: "et",
        FA: "fa",
        FA_PIN: "fa-pin",
        FI: "fi",
        FR: "fr",
        FR_BE: "fr-be",
        FR_FR: "fr-fr",
        GA: "ga",
        # COMMENTED GRC: "grc",
        HI: "hi",
        HR: "hr",
        HU: "hu",
        HY: "hy",
        HY_WEST: "hy-west",
        ID: "id",
        IS: "is",
        IT: "it",
        # COMMENTED JBO: "jbo",
        KA: "ka",
        KN: "kn",
        KU: "ku",
        LA: "la",
        # COMMENTED LFN: "lfn",
        LT: "lt",
        LV: "lv",
        MK: "mk",
        ML: "ml",
        MS: "ms",
        NE: "ne",
        NL: "nl",
        NO: "no",
        PA: "pa",
        PL: "pl",
        PT: "pt",
        PT_BR: "pt-br",
        PT_PT: "pt-pt",
        RO: "ro",
        RU: "ru",
        SQ: "sq",
        SK: "sk",
        SR: "sr",
        SV: "sv",
        SW: "sw",
        TA: "ta",
        TR: "tr",
        UK: "ru",   # NOTE mocking support for Ukrainian with Russian voice
        VI: "vi",
        VI_HUE: "vi-hue",
        VI_SGN: "vi-sgn",
        ZH: "zh",
        ZH_YUE: "zh-yue",
        AFR: "af",
        ARG: "an",
        BOS: "bs",
        BUL: "bg",
        CAT: "ca",
        CES: "cs",
        CMN: "zh",
        CYM: "cy",
        DAN: "da",
        DEU: "de",
        ELL: "el",
        ENG: "en",
        EPO: "eo",
        EST: "et",
        FAS: "fa",
        FIN: "fi",
        FRA: "fr",
        GLE: "ga",
        GRC: "grc",
        HIN: "hi",
        HRV: "hr",
        HUN: "hu",
        HYE: "hy",
        IND: "id",
        ISL: "is",
        ITA: "it",
        JBO: "jbo",
        KAN: "kn",
        KAT: "ka",
        KUR: "ku",
        LAT: "la",
        LAV: "lv",
        LFN: "lfn",
        LIT: "lt",
        MAL: "ml",
        MKD: "mk",
        MSA: "ms",
        NEP: "ne",
        NLD: "nl",
        NOR: "no",
        PAN: "pa",
        POL: "pl",
        POR: "pt",
        RON: "ro",
        RUS: "ru",
        SLK: "sk",
        SPA: "es",
        SQI: "sq",
        SRP: "sr",
        SWA: "sw",
        SWE: "sv",
        TAM: "ta",
        TUR: "tr",
        UKR: "ru",  # NOTE mocking support for Ukrainian with Russian voice
        VIE: "vi",
        YUE: "zh-yue",
        ZHO: "zh",
        ENG_GBR: "en-gb",
        ENG_SCT: "en-sc",
        ENG_USA: "en-us",
        SPA_ESP: "es-es",
        FRA_BEL: "fr-be",
        FRA_FRA: "fr-fr",
        POR_BRA: "pt-br",
        POR_PRT: "pt-pt"
    }
    DEFAULT_LANGUAGE = ENG

    DEFAULT_TTS_PATH = "espeak"

    OUTPUT_AUDIO_FORMAT = ("pcm_s16le", 1, 22050)

    HAS_SUBPROCESS_CALL = True

    HAS_C_EXTENSION_CALL = True

    C_EXTENSION_NAME = "cew"

    TAG = u"ESPEAKTTSWrapper"

    def __init__(self, rconf=None, logger=None):
        super(ESPEAKTTSWrapper, self).__init__(rconf=rconf, logger=logger)
        self.set_subprocess_arguments([
            self.tts_path,
            u"-v",
            self.CLI_PARAMETER_VOICE_CODE_STRING,
            u"-w",
            self.CLI_PARAMETER_WAVE_PATH,
            self.CLI_PARAMETER_TEXT_STDIN
        ])

    def _synthesize_multiple_c_extension(self, text_file, output_file_path, quit_after=None, backwards=False):
        """
        Synthesize multiple text fragments, using the cew extension.

        Return a tuple (anchors, total_time, num_chars).

        :rtype: (bool, (list, :class:`~aeneas.exacttiming.TimeValue`, int))
        """
        self.log(u"Synthesizing using C extension...")

        # convert parameters from Python values to C values
        try:
            c_quit_after = float(quit_after)
        except TypeError:
            c_quit_after = 0.0
        c_backwards = 0
        if backwards:
            c_backwards = 1
        self.log([u"output_file_path: %s", output_file_path])
        self.log([u"c_quit_after:     %.3f", c_quit_after])
        self.log([u"c_backwards:      %d", c_backwards])
        self.log(u"Preparing u_text...")
        u_text = []
        fragments = text_file.fragments
        for fragment in fragments:
            f_lang = fragment.language
            f_text = fragment.filtered_text
            if f_lang is None:
                f_lang = self.DEFAULT_LANGUAGE
            f_voice_code = self._language_to_voice_code(f_lang)
            if f_text is None:
                f_text = u""
            u_text.append((f_voice_code, f_text))
        self.log(u"Preparing u_text... done")

        # call C extension
        sr = None
        sf = None
        intervals = None
        if self.rconf[RuntimeConfiguration.CEW_SUBPROCESS_ENABLED]:
            self.log(u"Using cewsubprocess to call aeneas.cew")
            try:
                self.log(u"Importing aeneas.cewsubprocess...")
                from aeneas.cewsubprocess import CEWSubprocess
                self.log(u"Importing aeneas.cewsubprocess... done")
                self.log(u"Calling aeneas.cewsubprocess...")
                cewsub = CEWSubprocess(rconf=self.rconf, logger=self.logger)
                sr, sf, intervals = cewsub.synthesize_multiple(output_file_path, c_quit_after, c_backwards, u_text)
                self.log(u"Calling aeneas.cewsubprocess... done")
            except Exception as exc:
                self.log_exc(u"An unexpected error occurred while running cewsubprocess", exc, False, None)
                # NOTE not critical, try calling aeneas.cew directly
                # COMMENTED return (False, None)

        if sr is None:
            self.log(u"Preparing c_text...")
            if gf.PY2:
                # Python 2 => pass byte strings
                c_text = [(gf.safe_bytes(t[0]), gf.safe_bytes(t[1])) for t in u_text]
            else:
                # Python 3 => pass Unicode strings
                c_text = [(gf.safe_unicode(t[0]), gf.safe_unicode(t[1])) for t in u_text]
            self.log(u"Preparing c_text... done")

            self.log(u"Calling aeneas.cew directly")
            try:
                self.log(u"Importing aeneas.cew...")
                import aeneas.cew.cew
                self.log(u"Importing aeneas.cew... done")
                self.log(u"Calling aeneas.cew...")
                sr, sf, intervals = aeneas.cew.cew.synthesize_multiple(
                    output_file_path,
                    c_quit_after,
                    c_backwards,
                    c_text
                )
                self.log(u"Calling aeneas.cew... done")
            except Exception as exc:
                self.log_exc(u"An unexpected error occurred while running cew", exc, False, None)
                return (False, None)

        self.log([u"sr: %d", sr])
        self.log([u"sf: %d", sf])

        # create output
        anchors = []
        current_time = TimeValue("0.000")
        num_chars = 0
        if backwards:
            fragments = fragments[::-1]
        for i in range(sf):
            # get the correct fragment
            fragment = fragments[i]
            # store for later output
            anchors.append([
                TimeValue(intervals[i][0]),
                fragment.identifier,
                fragment.filtered_text
            ])
            # increase the character counter
            num_chars += fragment.characters
            # update current_time
            current_time = TimeValue(intervals[i][1])

        # return output
        # NOTE anchors do not make sense if backwards == True
        self.log([u"Returning %d time anchors", len(anchors)])
        self.log([u"Current time %.3f", current_time])
        self.log([u"Synthesized %d characters", num_chars])
        self.log(u"Synthesizing using C extension... done")
        return (True, (anchors, current_time, num_chars))

#!/usr/bin/env python # coding=utf-8 # aeneas is a Python/C library and a set of tools # to automagically synchronize audio and text (aka forced alignment) # # Copyright (C) 2012-2013, <NAME> (www.albertopettarin.it) # Copyright (C) 2013-2015, ReadBeyond Srl   (www.readbeyond.it) # Copyright (C) 2015-2017, <NAME> (www.albertopettarin.it) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program.  If not, see <http://www.gnu.org/licenses/>. This module contains the following classes:  * :class:`~aeneas.ttswrappers.espeakttswrapper.ESPEAKTTSWrapper`,   a wrapper for the ``eSpeak`` TTS engine.  Please refer to http://espeak.sourceforge.net/ for further details. A wrapper for the ``eSpeak`` TTS engine.      This wrapper is the default TTS engine for ``aeneas``.      This wrapper supports calling the TTS engine     via ``subprocess`` or via Python C extension.      In abstract terms, it performs one or more calls like ::          $ espeak -v voice_code -w /tmp/output_file.wav < text      To use this TTS engine, specify ::          "tts=espeak"      in the ``RuntimeConfiguration`` object.     (You can omit this, since eSpeak is the default TTS engine.)     To execute from a non-default location: ::          "tts=espeak|tts_path=/path/to/espeak"      To run the ``cew`` Python C extension     in a separate process via     :class:`~aeneas.cewsubprocess.CEWSubprocess`, use ::          "cew_subprocess_enabled=True|cew_subprocess_path=/path/to/python"      in the ``rconf`` object.      See :class:`~aeneas.ttswrappers.basettswrapper.BaseTTSWrapper`     for the available functions.     Below are listed the languages supported by this wrapper.      :param rconf: a runtime configuration     :type  rconf: :class:`~aeneas.runtimeconfiguration.RuntimeConfiguration`     :param logger: the logger object     :type  logger: :class:`~aeneas.logger.Logger` Afrikaans Aragonese (not tested) Bosnian (not tested) Bulgarian Catalan Czech Mandarin Chinese (not tested) Welsh Danish German Greek (Modern) English Esperanto (not tested) Estonian Persian Finnish French Irish Greek (Ancient) Hindi (not tested) Croatian Hungarian Armenian (not tested) Indonesian (not tested) Icelandic Italian Lojban (not tested) Kannada (not tested) Georgian (not tested) Kurdish (not tested) Latin Latvian Lingua Franca Nova (not tested) Lithuanian Malayalam (not tested) Macedonian (not tested) Malay (not tested) Nepali (not tested) Dutch Norwegian Panjabi (not tested) Polish Portuguese Romanian Russian Slovak Spanish Albanian (not tested) Serbian Swahili Swedish Tamil (not tested) Turkish Ukrainian Vietnamese (not tested) Yue Chinese (not tested) Chinese (not tested) English (GB) English (Scotland) (not tested) English (USA) Spanish (Castillan) French (Belgium) (not tested) French (France) Portuguese (Brazil) (not tested) Portuguese (Portugal) Afrikaans Aragonese (not tested) Bulgarian Bosnian (not tested) Catalan Czech Welsh Danish German Greek (Modern) English English (GB) English (Scotland) (not tested) English (Northern) (not tested) English (Received Pronunciation) (not tested) English (Midlands) (not tested) English (USA) English (West Indies) (not tested) Esperanto (not tested) Spanish (Castillan) Spanish (Latin America) (not tested) Estonian Persian Persian (Pinglish) Finnish French French (Belgium) (not tested) French (France) Irish # NOTE already defined # COMMENTED GRC = "grc" # COMMENTED """ Greek (Ancient) """ Hindi (not tested) Croatian Hungarian Armenian (not tested) Armenian (West) (not tested) Indonesian (not tested) Icelandic Italian # NOTE already defined # COMMENTED JBO = "jbo" # COMMENTED """ Lojban (not tested) """ Georgian (not tested) Kannada (not tested) Kurdish (not tested) Latin # NOTE already defined # COMMENTED LFN = "lfn" # COMMENTED """ Lingua Franca Nova (not tested) """ Lithuanian Latvian Macedonian (not tested) Malayalam (not tested) Malay (not tested) Nepali (not tested) Dutch Norwegian Panjabi (not tested) Polish Portuguese Portuguese (Brazil) (not tested) Portuguese (Portugal) Romanian Russian Albanian (not tested) Slovak Serbian Swedish Swahili Tamil (not tested) Turkish Ukrainian Vietnamese (not tested) Vietnamese (hue) (not tested) Vietnamese (sgn) (not tested) Mandarin Chinese (not tested) Yue Chinese (not tested) # COMMENTED GRC: "grc", # COMMENTED JBO: "jbo", # COMMENTED LFN: "lfn", # NOTE mocking support for Ukrainian with Russian voice # NOTE mocking support for Ukrainian with Russian voice Synthesize multiple text fragments, using the cew extension.          Return a tuple (anchors, total_time, num_chars).          :rtype: (bool, (list, :class:`~aeneas.exacttiming.TimeValue`, int)) # convert parameters from Python values to C values # call C extension # NOTE not critical, try calling aeneas.cew directly # COMMENTED return (False, None) # Python 2 => pass byte strings # Python 3 => pass Unicode strings # create output # get the correct fragment # store for later output # increase the character counter # update current_time # return output # NOTE anchors do not make sense if backwards == True