Spaces:
Running
Running
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
|