""" Android Speech/TTS 组件 — TextToSpeech, Voice, SynthesisCallback 等 方法签名完全对齐 Android SDK """ import os import sys import locale as _locale from typing import Any, Dict, List, Optional, Set, Tuple, Union from .._core import ( Stub, AndroidOnlyError, android_context, resolve_android_path, to_android_path, _sdcard_root, _app_data_dir, _package_name, ) # ═══════════════════════════════════════════════════════════════ # TextToSpeech.OnInitListener # ═══════════════════════════════════════════════════════════════ class TextToSpeechOnInitListener(Stub): """OnInitListener — 初始化监听器""" def onInit(self, status: int): pass # ═══════════════════════════════════════════════════════════════ # UtteranceProgressListener # ═══════════════════════════════════════════════════════════════ class UtteranceProgressListener(Stub): """UtteranceProgressListener — 发音进度监听器""" def onStart(self, utterance_id: str): pass def onDone(self, utterance_id: str): pass def onError(self, utterance_id: str, error_code: int = -1): pass def onStop(self, utterance_id: str, interrupted: bool = False): pass def onBeginSynthesis(self, utterance_id: str, sample_rate_in_hz: int, audio_format: int, channel_count: int): pass def onRangeStart(self, utterance_id: str, start: int, end: int, frame: int): pass class TextToSpeechOnUtteranceCompletedListener(Stub): """OnUtteranceCompletedListener — 旧版发音完成监听器""" def onUtteranceCompleted(self, utterance_id: str): pass # ═══════════════════════════════════════════════════════════════ # SynthesisRequest # ═══════════════════════════════════════════════════════════════ class SynthesisRequest(Stub): """SynthesisRequest — 合成请求""" def __init__(self): super().__init__() self._text: str = "" self._speech_rate: float = 1.0 self._voice: Optional["Voice"] = None self._calling_uid: int = 0 self._country: str = "" self._language: str = "" self._variant: str = "" self._params: Dict[str, str] = {} def getText(self) -> str: return self._text def getSpeechRate(self) -> float: return self._speech_rate def getVoice(self) -> Optional["Voice"]: return self._voice def getCallingUid(self) -> int: return self._calling_uid def getCountry(self) -> str: return self._country def getLanguage(self) -> str: return self._language def getVariant(self) -> str: return self._variant def getParams(self) -> Dict[str, str]: return dict(self._params) # ═══════════════════════════════════════════════════════════════ # SynthesisCallback # ═══════════════════════════════════════════════════════════════ class SynthesisCallback(Stub): """SynthesisCallback — 合成回调""" def start(self, sample_rate_in_hz: int, audio_format: int, channel_count: int) -> int: return 0 def getMaxBufferSize(self) -> int: return 8192 def getChannelCount(self) -> int: return 1 def getAudioFormat(self) -> int: return 1 def getSampleRate(self) -> int: return 22050 def availableBuffer(self, buffer_size: int) -> int: return buffer_size def audioAvailable(self, audio: bytes) -> int: return len(audio) def done(self) -> int: return 0 def error(self) -> int: return -1 def hasStarted(self) -> bool: return False def hasFinished(self) -> bool: return False # ═══════════════════════════════════════════════════════════════ # Voice # ═══════════════════════════════════════════════════════════════ class Voice(Stub): """Voice — 语音""" QUALITY_DEFAULT = -1 QUALITY_VERY_LOW = 100 QUALITY_LOW = 200 QUALITY_NORMAL = 300 QUALITY_HIGH = 400 QUALITY_VERY_HIGH = 500 LATENCY_DEFAULT = -1 LATENCY_VERY_LOW = 100 LATENCY_LOW = 200 LATENCY_NORMAL = 300 LATENCY_HIGH = 400 LATENCY_VERY_HIGH = 500 def __init__(self, name: str = "", locale: Any = None, quality: int = QUALITY_NORMAL, latency: int = LATENCY_NORMAL, requires_network: bool = False, features: Optional[List[str]] = None): super().__init__() self._name = name self._locale = locale or _locale.getdefaultlocale()[0] self._quality = quality self._latency = latency self._requires_network = requires_network self._features = features or [] def getName(self) -> str: return self._name def getLocale(self) -> Any: return self._locale def getQuality(self) -> int: return self._quality def getLatency(self) -> int: return self._latency def isNetworkConnectionRequired(self) -> bool: return self._requires_network def getFeatures(self) -> List[str]: return list(self._features) def toString(self) -> str: return f"Voice(name={self._name}, locale={self._locale})" # ═══════════════════════════════════════════════════════════════ # TextToSpeech.Engine # ═══════════════════════════════════════════════════════════════ class TextToSpeechEngine(Stub): """TextToSpeech.Engine — TTS 引擎元数据""" ACTION_CHECK_TTS_DATA = "android.speech.tts.engine.CHECK_TTS_DATA" ACTION_INSTALL_TTS_DATA = "android.speech.tts.engine.INSTALL_TTS_DATA" ACTION_GET_SAMPLE_TEXT = "android.speech.tts.engine.GET_SAMPLE_TEXT" KEY_PARAM_STREAM = "streamType" KEY_PARAM_VOLUME = "volume" KEY_PARAM_PAN = "pan" KEY_PARAM_UTTERANCE_ID = "utteranceId" KEY_PARAM_SESSION_ID = "sessionId" KEY_FEATURE_NETWORK_SYNTHESIS = "networkSynthesis" KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedSynthesis" KEY_FEATURE_NOT_INSTALLED = "notInstalled" DEFAULT_STREAM = 3 DEFAULT_LANG = "eng" DEFAULT_COUNTRY = "USA" DEFAULT_VARIANT = "" @staticmethod def getLocaleLang(lang: str) -> str: return lang[:3] if len(lang) >= 3 else lang @staticmethod def getLocaleCountry(lang: str) -> str: return lang[3:6] if len(lang) >= 6 else "" @staticmethod def getLocaleVariant(lang: str) -> str: return lang[6:] if len(lang) > 6 else "" Engine = TextToSpeechEngine # ═══════════════════════════════════════════════════════════════ # TextToSpeech # ═══════════════════════════════════════════════════════════════ class TextToSpeech(Stub): """TextToSpeech — 文字转语音""" SUCCESS = 0 ERROR = -1 STOPPED = -2 QUEUE_FLUSH = 0 QUEUE_ADD = 1 LANG_COUNTRY_AVAILABLE = 1 LANG_AVAILABLE = 0 LANG_COUNTRY_VAR_AVAILABLE = 2 LANG_MISSING_DATA = -1 LANG_NOT_SUPPORTED = -2 Engine = TextToSpeechEngine def __init__(self, context: Any = None, listener: Optional[TextToSpeechOnInitListener] = None): super().__init__() self._context = context self._listener = listener self._utterance_progress_listener: Optional[UtteranceProgressListener] = None self._language: str = "" self._country: str = "" self._pitch: float = 1.0 self._speech_rate: float = 1.0 self._voice: Optional[Voice] = None self._voices: List[Voice] = [ Voice("en-us-1", "en_US", Voice.QUALITY_HIGH, Voice.LATENCY_NORMAL), Voice("en-gb-1", "en_GB", Voice.QUALITY_NORMAL, Voice.LATENCY_NORMAL), Voice("zh-cn-1", "zh_CN", Voice.QUALITY_HIGH, Voice.LATENCY_NORMAL), ] self._engine_package: str = "" self._initialized = False if listener: try: listener.onInit(self.SUCCESS) except Exception: pass self._initialized = True def speak(self, text: str, queue_mode: int = QUEUE_FLUSH, params: Any = None, utterance_id: str = "") -> int: return self.SUCCESS def stop(self) -> int: return self.SUCCESS def shutdown(self): self._initialized = False def setLanguage(self, language: str, country: str = "", variant: str = "") -> int: self._language = language self._country = country return self.LANG_AVAILABLE def getLanguage(self) -> Tuple[str, str, str]: return (self._language, self._country, "") def isLanguageAvailable(self, language: str, country: str = "", variant: str = "") -> int: return self.LANG_AVAILABLE def setOnUtteranceProgressListener(self, listener: UtteranceProgressListener): self._utterance_progress_listener = listener def setOnUtteranceCompletedListener(self, listener: TextToSpeechOnUtteranceCompletedListener): pass def setPitch(self, pitch: float) -> int: self._pitch = pitch return self.SUCCESS def setSpeechRate(self, rate: float) -> int: self._speech_rate = rate return self.SUCCESS def getVoice(self) -> Optional[Voice]: return self._voice def setVoice(self, voice: Voice) -> int: self._voice = voice return self.SUCCESS def getVoices(self) -> Set[Voice]: return set(self._voices) def addSpeech(self, text: str, filename: str) -> int: return self.SUCCESS def addEyes(self, text: str, filename: str) -> int: return self.SUCCESS def synthesizeToFile(self, text: str, params: Any, filename: str) -> int: return self.SUCCESS def setEngineByPackageName(self, package_name: str): self._engine_package = package_name def getDefaultEngine(self) -> Optional[str]: return "com.android.tts" def getEngines(self) -> List["EngineInfo"]: return [] def getFeatures(self, locale: Any) -> Set[str]: return set() def areDefaultsEnforced(self) -> bool: return False def setQueueMode(self, queue_mode: int): pass def isSpeaking(self) -> bool: return False def toString(self) -> str: return f"TextToSpeech(lang={self._language})" # ═══════════════════════════════════════════════════════════════ # EngineInfo # ═══════════════════════════════════════════════════════════════ class EngineInfo(Stub): """EngineInfo — 引擎信息""" def __init__(self): super().__init__() self.name: str = "" self.label: str = "" self.icon: int = 0 self.priority: int = 0 self.system: bool = False # ═══════════════════════════════════════════════════════════════ # __all__ # ═══════════════════════════════════════════════════════════════ __all__ = [ "TextToSpeech", "TextToSpeechEngine", "TextToSpeechOnInitListener", "TextToSpeechOnUtteranceCompletedListener", "SynthesisCallback", "SynthesisRequest", "Voice", "UtteranceProgressListener", "Engine", "EngineInfo", ]