File size: 7,240 Bytes
2eaa44a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
import platform
import subprocess
import shutil
from pathlib import Path
import os
from typing import Optional, Tuple
from phonemizer.backend.espeak.wrapper import EspeakWrapper


class EspeakConfig:
    """Utility class for configuring espeak-ng library and binary."""

    @staticmethod
    def find_espeak_binary() -> tuple[bool, Optional[str]]:
        """
        Find espeak-ng binary using multiple methods.

        Returns:
            tuple: (bool indicating if espeak is available, path to espeak binary if found)
        """
        # Common binary names
        binary_names = ["espeak-ng", "espeak"]
        if platform.system() == "Windows":
            binary_names = ["espeak-ng.exe", "espeak.exe"]

        # Common installation directories for Linux
        linux_paths = [
            "/usr/bin",
            "/usr/local/bin",
            "/usr/lib/espeak-ng",
            "/usr/local/lib/espeak-ng",
            "/opt/espeak-ng/bin",
        ]

        # First check if it's in PATH
        for name in binary_names:
            espeak_path = shutil.which(name)
            if espeak_path:
                return True, espeak_path

        # For Linux, check common installation directories
        if platform.system() == "Linux":
            for directory in linux_paths:
                for name in binary_names:
                    path = Path(directory) / name
                    if path.exists():
                        return True, str(path)

        # Try running the command directly as a last resort
        try:
            subprocess.run(
                ["espeak-ng", "--version"],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                check=True,
            )
            return True, "espeak-ng"
        except (subprocess.SubprocessError, FileNotFoundError):
            pass

        return False, None

    @staticmethod
    def find_library_path() -> Optional[str]:
        """
        Find the espeak-ng library using multiple search methods.

        Returns:
            Optional[str]: Path to the library if found, None otherwise
        """
        system = platform.system()

        if system == "Linux":
            lib_names = ["libespeak-ng.so", "libespeak-ng.so.1"]
            common_paths = [
                # Debian/Ubuntu paths
                "/usr/lib/x86_64-linux-gnu",
                "/usr/lib/aarch64-linux-gnu",  # For ARM64
                "/usr/lib/arm-linux-gnueabihf",  # For ARM32
                "/usr/lib",
                "/usr/local/lib",
                # Fedora/RHEL paths
                "/usr/lib64",
                "/usr/lib32",
                # Common additional paths
                "/usr/lib/espeak-ng",
                "/usr/local/lib/espeak-ng",
                "/opt/espeak-ng/lib",
            ]

            # Check common locations first
            for path in common_paths:
                for lib_name in lib_names:
                    lib_path = Path(path) / lib_name
                    if lib_path.exists():
                        return str(lib_path)

            # Search system library paths
            try:
                # Use ldconfig to find the library
                result = subprocess.run(
                    ["ldconfig", "-p"], capture_output=True, text=True, check=True
                )
                for line in result.stdout.splitlines():
                    if "libespeak-ng.so" in line:
                        # Extract path from ldconfig output
                        return line.split("=>")[-1].strip()
            except (subprocess.SubprocessError, FileNotFoundError):
                pass

        elif system == "Darwin":  # macOS
            common_paths = [
                Path("/opt/homebrew/lib/libespeak-ng.dylib"),
                Path("/usr/local/lib/libespeak-ng.dylib"),
                *list(
                    Path("/opt/homebrew/Cellar/espeak-ng").glob(
                        "*/lib/libespeak-ng.dylib"
                    )
                ),
                *list(
                    Path("/usr/local/Cellar/espeak-ng").glob("*/lib/libespeak-ng.dylib")
                ),
            ]

            for path in common_paths:
                if path.exists():
                    return str(path)

        elif system == "Windows":
            common_paths = [
                Path(os.environ.get("PROGRAMFILES", "C:\\Program Files"))
                / "eSpeak NG"
                / "libespeak-ng.dll",
                Path(os.environ.get("PROGRAMFILES(X86)", "C:\\Program Files (x86)"))
                / "eSpeak NG"
                / "libespeak-ng.dll",
                *[
                    Path(p) / "libespeak-ng.dll"
                    for p in os.environ.get("PATH", "").split(os.pathsep)
                ],
            ]

            for path in common_paths:
                if path.exists():
                    return str(path)

        return None

    @classmethod
    def configure_espeak(cls) -> Tuple[bool, str]:
        """
        Configure espeak-ng for use with the phonemizer.

        Returns:
            Tuple[bool, str]: (Success status, Status message)
        """
        # First check if espeak binary is available
        espeak_available, espeak_path = cls.find_espeak_binary()
        if not espeak_available:
            raise FileNotFoundError(
                "Could not find espeak-ng binary. Please install espeak-ng:\n"
                "Ubuntu/Debian: sudo apt-get install espeak-ng espeak-ng-data\n"
                "Fedora: sudo dnf install espeak-ng\n"
                "Arch: sudo pacman -S espeak-ng\n"
                "MacOS: brew install espeak-ng\n"
                "Windows: Download from https://github.com/espeak-ng/espeak-ng/releases"
            )

        # Find the library
        library_path = cls.find_library_path()
        if not library_path:
            # On Linux, we might not need to explicitly set the library path
            if platform.system() == "Linux":
                return True, f"Using system espeak-ng installation at: {espeak_path}"
            else:
                raise FileNotFoundError(
                    "Could not find espeak-ng library. Please ensure espeak-ng is properly installed."
                )

        # Try to set the library path
        try:
            EspeakWrapper.set_library(library_path)
            return True, f"Successfully configured espeak-ng library at: {library_path}"
        except Exception as e:
            if platform.system() == "Linux":
                # On Linux, try to continue without explicit library path
                return True, f"Using system espeak-ng installation at: {espeak_path}"
            else:
                raise RuntimeError(f"Failed to configure espeak-ng library: {str(e)}")


def setup_espeak():
    """
    Set up espeak-ng for use with the phonemizer.
    Raises appropriate exceptions if setup fails.
    """
    try:
        success, message = EspeakConfig.configure_espeak()
        print(message)
    except Exception as e:
        print(f"Error configuring espeak-ng: {str(e)}")
        raise


# Replace the original set_espeak_library function with this
set_espeak_library = setup_espeak