# 安卓Termux玩转Python音频:3种方法解决pygame无声问题(附避坑指南)
在移动设备上运行Python脚本,尤其是涉及多媒体处理时,总会遇到一些意想不到的挑战。我最初在安卓手机上用Termux搭建Python环境,是为了能随时随地跑一些自动化脚本和数据抓取工具。但当我想把桌面端一个带语音提示的小工具移植过来时,问题就来了——那个在电脑上运行流畅、用`pygame.mixer`播放提示音的脚本,在Termux里直接哑火,只留下一行“找不到声卡”的错误提示。这让我意识到,移动端开发环境和传统的桌面或服务器环境有着本质区别,尤其是在音频这类需要直接访问硬件的功能上。
Termux虽然强大,但它本质上是一个**无头(headless)**的Linux模拟环境,没有图形界面,也没有直接访问安卓音频子系统的权限。`pygame`这类库在设计时,默认会去寻找ALSA或PulseAudio这类桌面Linux的音频服务,这在安卓的沙盒环境中自然是行不通的。但这并不意味着在Termux里玩转音频就是死路一条。经过一番摸索和踩坑,我找到了几条切实可行的路径,不仅能解决播放问题,还能根据不同的应用场景选择最合适的方案。这篇文章,我就把这些方法、背后的原理,以及我踩过的那些坑,系统地分享给你。
## 1. 理解Termux音频环境的本质与限制
在深入解决方案之前,我们必须先搞清楚为什么`pygame`在Termux上会“失声”。这不是一个简单的库安装问题,而是**环境隔离**和**权限模型**差异导致的根本性障碍。
安卓系统为了安全,对应用进行了严格的沙盒化。每个应用(包括Termux)都运行在自己的独立空间中,对硬件和外设的访问受到严格控制。Termux通过一个巧妙的“插件”机制,为我们提供了访问部分系统功能的桥梁,比如存储、传感器,以及我们这里关心的**音频**。但请注意,这种访问不是直接的,而是通过Termux提供的特定API或命令行工具间接实现的。
`pygame.mixer`在初始化时,会尝试调用底层的SDL音频库,而SDL则会去寻找系统默认的音频设备。在标准的Linux发行版上,这个设备可能是`/dev/snd/pcmC0D0p`,但在Termux的环境里,这个路径要么不存在,要么Termux进程没有权限访问。这就是抛出“找不到声卡”错误的根本原因。
> **注意**:不要尝试在Termux里安装完整的桌面音频服务(如PulseAudio)或修改系统音频配置。这不仅过程复杂、极易失败,还可能破坏Termux环境的稳定性。我们的思路应该是“借道而行”,利用Termux生态中已有的、专门为移动端设计的工具。
那么,Termux为我们提供了哪些“桥梁”呢?主要有三类,它们各自的特点和适用场景如下表所示:
| 工具/方法 | 核心原理 | 优点 | 缺点 | 典型应用场景 |
| :--- | :--- | :--- | :--- | :--- |
| **play-audio** | 调用Termux内部集成的简单音频播放器。 | 轻量、稳定、无需额外权限,支持WAV等基础格式。 | 功能单一,无法进行精细的播放控制(如暂停、音量调节)。 | 播放简单的提示音、音效,或不需要交互的后台音频。 |
| **Termux:API (TTS)** | 调用安卓系统的文本转语音(TTS)引擎。 | 无需准备音频文件,直接合成语音,支持多语言。 | 语音质量依赖手机TTS引擎,音色和自然度可能不佳;无法播放自定义音频文件。 | 需要动态生成语音播报的场景,如朗读新闻、播报状态。 |
| **Termux:API (Media Player)** | 调用安卓系统的媒体播放服务。 | 功能相对完整,支持播放、暂停、停止等控制,兼容常见音频格式。 | 需要安装额外的APK插件,播放控制为异步操作,在脚本中需要妥善处理。 | 播放音乐、播客等较长的音频文件,或需要用户交互控制的场景。 |
理解了这些限制和可用的工具,我们就能有的放矢地选择解决方案了。接下来,我们将逐一拆解这三种方法的具体实现和代码细节。
## 2. 方案一:使用play-audio工具进行基础播放
`play-audio`是Termux自带的一个命令行音频播放工具,它大概是解决无声问题最直接、最轻量的方案了。它本质上是一个简单的WAV文件播放器,通过调用安卓底层的音频接口工作,完全绕过了`pygame`对桌面音频服务的依赖。
### 2.1 安装与验证
首先,确保你的Termux已经更新到最新版本,然后安装`play-audio`:
```bash
pkg update && pkg upgrade
pkg install play-audio
```
安装完成后,可以立刻找一个WAV格式的音频文件测试一下。你可以从网上下载一个,或者用`ffmpeg`(如果已安装)转换一个。假设你有一个名为`beep.wav`的文件在当前目录,运行:
```bash
play-audio beep.wav
```
如果听到声音,恭喜你,通往音频世界的大门已经打开。如果没听到,请检查:
1. 手机媒体音量是否打开。
2. 音频文件格式是否为标准的PCM WAV(`play-audio`对编码格式比较挑剔)。
3. 文件路径是否正确。
### 2.2 在Python中集成调用
既然命令行能播放,那么在Python脚本中调用它就顺理成章了。我们使用Python的`subprocess`模块来执行外部命令。这里有一个最基本的示例:
```python
import subprocess
import os
def play_audio_with_playaudio(file_path):
"""
使用play-audio工具播放音频文件。
Args:
file_path (str): 音频文件的路径。
"""
if not os.path.exists(file_path):
print(f"错误:文件 {file_path} 不存在。")
return
# 构建命令
command = f"play-audio {file_path}"
try:
# subprocess.call会等待命令执行完毕,适用于短音效
subprocess.call(command, shell=True)
print(f"已播放: {file_path}")
except Exception as e:
print(f"播放失败: {e}")
```
这段代码简单直接,调用`subprocess.call`会**阻塞**当前Python进程,直到音频播放完毕。这对于播放一个简短的提示音是没问题的。但如果你需要**后台播放**,或者在播放的同时进行其他操作(比如等待用户输入),阻塞式的调用就不合适了。
### 2.3 实现非阻塞播放与进程控制
为了实现后台播放和精确控制(比如中途停止),我们需要使用`subprocess.Popen`,并妥善管理生成的子进程。下面是一个更健壮、功能更完整的类实现:
```python
import subprocess
import os
import signal
import time
class AudioPlayer:
def __init__(self):
self.process = None
def play(self, file_path):
"""非阻塞方式播放音频。"""
if self.process and self.process.poll() is None:
print("警告:已有音频正在播放,先停止它。")
self.stop()
if not os.path.exists(file_path):
print(f"错误:文件 {file_path} 不存在。")
return False
try:
# 使用Popen创建独立进程组,便于后续整体终止
# preexec_fn=os.setsid 在Unix系系统上为子进程设置新的进程组
self.process = subprocess.Popen(
['play-audio', file_path],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
preexec_fn=os.setsid # 关键:创建新进程组
)
print(f"开始在后台播放: {file_path} (PID: {self.process.pid})")
return True
except FileNotFoundError:
print("错误:未找到'play-audio'命令,请确保已安装 'pkg install play-audio'")
return False
except Exception as e:
print(f"启动播放进程失败: {e}")
return False
def stop(self):
"""停止当前播放的音频。"""
if self.process and self.process.poll() is None:
try:
# 向整个进程组发送终止信号
os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
self.process.wait(timeout=2) # 等待进程结束
print("音频播放已停止。")
except subprocess.TimeoutExpired:
os.killpg(os.getpgid(self.process.pid), signal.SIGKILL) # 强制杀死
print("音频进程被强制终止。")
except ProcessLookupError:
print("进程已结束。")
finally:
self.process = None
else:
print("没有正在播放的音频。")
def is_playing(self):
"""检查是否正在播放。"""
return self.process is not None and self.process.poll() is None
# 使用示例
if __name__ == "__main__":
player = AudioPlayer()
player.play("/data/data/com.termux/files/home/sounds/alert.wav")
# 此时音频在后台播放,主程序可以继续做其他事情
time.sleep(2) # 模拟其他工作
# 2秒后停止播放
player.stop()
```
这段代码有几个关键点:
- **`preexec_fn=os.setsid`**:这行代码让`play-audio`进程运行在一个新的进程组中。这样,当我们想停止它时,可以向整个进程组发送信号,确保所有相关进程都被终止,避免产生“僵尸进程”。
- **`os.killpg`**:用于向整个进程组发送信号,`SIGTERM`是礼貌的终止请求,`SIGKILL`是强制杀死。
- **进程状态管理**:通过`poll()`方法检查子进程是否已结束,来维护播放状态。
### 2.4 进阶技巧:连续播放与格式处理
`play-audio`支持一次性传入多个文件进行连续播放,这个特性可以加以利用:
```bash
play-audio intro.wav main.wav outro.wav
```
在Python中,你可以简单地拼接文件路径来实现。但需要注意的是,这种方式下你无法在播放序列中间进行干预。对于需要动态生成播放列表的场景,更好的办法是在Python层面进行队列管理,依次调用`play`方法。
另外,`play-audio`主要支持WAV格式。如果你的音频是MP3、OGG等格式,需要先进行转换。可以在Termux中安装`ffmpeg`:
```bash
pkg install ffmpeg
```
然后编写一个预处理函数:
```python
def convert_to_wav(input_path, output_path=None):
"""使用ffmpeg将音频文件转换为WAV格式。"""
if output_path is None:
output_path = os.path.splitext(input_path)[0] + '.wav'
cmd = ['ffmpeg', '-i', input_path, '-acodec', 'pcm_s16le', '-ar', '44100', '-ac', '2', output_path]
try:
subprocess.run(cmd, check=True, capture_output=True)
print(f"转换成功: {output_path}")
return output_path
except subprocess.CalledProcessError as e:
print(f"转换失败: {e.stderr.decode()}")
return None
```
**避坑指南**:
- **权限问题**:确保Termux有存储权限(`termux-setup-storage`),并且你的音频文件位于Termux可访问的目录(如`~/storage/shared/`或`~/`)。
- **资源释放**:使用`Popen`后,务必在脚本适当位置(如`finally`块或`__del__`方法中)调用`stop()`,防止后台进程残留。
- **性能考虑**:频繁地创建和销毁`play-audio`进程会有开销。对于需要极低延迟或高频播放音效的场景(如游戏),此方案可能不是最佳选择。
## 3. 方案二:利用Termux:API实现文本转语音(TTS)
如果你的应用场景不是播放预录制的音频文件,而是需要动态地将文字转换为语音播报出来,那么Termux:API的TTS功能将是你的绝佳选择。它直接调用你手机系统中已经存在的文本转语音引擎,无需处理音频文件。
### 3.1 环境搭建与授权
使用此方案需要两个步骤:
1. **安装Termux:API插件**:在Termux中安装命令行工具。
```bash
pkg install termux-api
```
2. **安装配套的安卓APP**:从F-Droid或Google Play商店搜索并安装“Termux:API”这个应用。这是必须的,因为命令行工具需要通过这个APP与安卓系统服务进行通信。
安装完成后,最好先授权Termux:API应用所需的所有权限(如通知、无障碍服务等,视安卓版本而定)。然后在Termux中运行一个测试命令:
```bash
termux-tts-speak "Hello from Termux"
```
如果手机用系统语音读出了这句话,说明环境配置成功。如果没有,请检查APP是否已安装并授予了必要的权限。
### 3.2 Python调用与基础封装
在Python中,我们同样通过`subprocess`来调用`termux-tts-speak`命令。一个简单的封装函数如下:
```python
import subprocess
import json
def speak_text(text, language="en-us", rate=1.0, pitch=1.0, engine=None):
"""
使用Termux TTS朗读文本。
Args:
text (str): 要朗读的文本。
language (str): 语言代码,如 'en-us', 'zh-cn'。
rate (float): 语速,0.5到2.0之间。
pitch (float): 音高,0.5到2.0之间。
engine (str): 指定TTS引擎包名,如'com.google.android.tts'。
"""
# 构建命令参数
cmd = ['termux-tts-speak']
# 添加可选参数
if language:
cmd.extend(['-l', language])
if rate != 1.0:
cmd.extend(['-r', str(rate)])
if pitch != 1.0:
cmd.extend(['-p', str(pitch)])
if engine:
cmd.extend(['-e', engine])
# 最后加上要朗读的文本
cmd.append(text)
try:
# 此命令也是阻塞的,会等待朗读完毕
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode != 0:
print(f"TTS失败: {result.stderr}")
return False
return True
except subprocess.TimeoutExpired:
print("TTS朗读超时。")
return False
except FileNotFoundError:
print("未找到'termux-tts-speak'命令,请确保已安装 'pkg install termux-api' 和对应的APP。")
return False
# 使用示例
speak_text("系统初始化完成,当前温度25摄氏度。", language="zh-cn", rate=1.2)
```
### 3.3 探索与切换TTS引擎
安卓系统可能预装了多个TTS引擎,其语音质量和支持的语言各不相同。你可以使用`termux-tts-engines`命令来查看可用的引擎:
```bash
termux-tts-engines
```
输出可能类似于:
```
com.google.android.tts: Google 文字转语音引擎
com.svox.classic: Pico TTS
com.iflytek.speechsuite: 讯飞语音合成
```
你可以通过`-e`参数指定引擎。例如,想使用讯飞引擎朗读中文:
```python
speak_text("你好,世界", language="zh-cn", engine="com.iflytek.speechsuite")
```
> **提示**:不同引擎对参数(如`rate`, `pitch`)的支持程度可能不同,效果也有差异,需要实际测试。Google TTS通常对多语言支持较好,而讯飞等引擎在中文合成上可能更自然。
### 3.4 实现异步TTS与回调
和`play-audio`一样,默认的`subprocess.run`调用是阻塞的。对于较长的文本,这会让你的程序卡住。我们可以结合`threading`模块实现异步朗读:
```python
import threading
def speak_text_async(text, callback=None, **kwargs):
"""异步执行TTS,朗读完成后可选执行回调函数。"""
def _tts_thread():
success = speak_text(text, **kwargs)
if callback:
callback(success)
thread = threading.Thread(target=_tts_thread, daemon=True)
thread.start()
return thread # 返回线程对象,便于后续管理(如等待)
# 使用示例:在朗读的同时打印日志
def on_tts_finished(success):
if success:
print("TTS朗读任务完成。")
else:
print("TTS朗读任务失败。")
print("开始准备数据...")
tts_thread = speak_text_async("正在处理您的请求,请稍候。",
callback=on_tts_finished,
language="zh-cn")
# 主线程可以继续执行其他任务
print("TTS已启动,主程序继续运行...")
# 如果需要等待TTS结束
# tts_thread.join()
```
**避坑指南**:
- **引擎兼容性**:不是所有引擎都支持所有语言和参数。如果指定引擎后无法朗读,请尝试不指定引擎或换用其他引擎。
- **网络依赖**:一些高质量的TTS引擎(如Google TTS的某些高质量语音)可能需要网络连接才能工作。
- **异步管理**:大量创建异步TTS线程而不加管理,可能会消耗较多资源。对于需要连续播报的场景,建议使用队列(`queue.Queue`)来管理TTS任务。
## 4. 方案三:使用Termux:API的媒体播放器进行高级控制
当你需要播放较长的音频文件(如音乐、播客),并且希望拥有播放、暂停、停止等控制能力时,`termux-media-player`就是为你准备的工具。它通过Termux:API调用安卓系统的媒体播放能力,功能比`play-audio`更全面。
### 4.1 安装与基本播放
确保你已经按照方案二安装了`termux-api`包和对应的APP。它的基本播放命令非常简单:
```bash
termux-media-player play /sdcard/Music/song.mp3
```
要暂停、恢复或停止播放,则使用:
```bash
termux-media-player pause
termux-media-player resume
termux-media-player stop
```
### 4.2 在Python中构建媒体播放控制器
我们可以构建一个类来封装这些操作,并更好地管理播放状态。一个关键点是,`termux-media-player`的播放是**完全异步**的,`play`命令一发出就立即返回,播放动作在后台由系统服务处理。
```python
import subprocess
import time
import threading
class TermuxMediaPlayer:
def __init__(self):
self._is_playing = False
self._current_file = None
self._position = 0 # 模拟播放位置,实际需要更复杂的方式获取
def play(self, file_path):
"""播放指定音频文件。"""
cmd = ['termux-media-player', 'play', file_path]
try:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
self._is_playing = True
self._current_file = file_path
print(f"开始播放: {file_path}")
# 启动一个线程来“模拟”监听播放状态(实际无法直接获取)
# 更可靠的做法是通过定时器或事件来更新状态
return True
else:
print(f"播放命令失败: {result.stderr}")
return False
except FileNotFoundError:
print("未找到'termux-media-player'命令,请确保已安装 'pkg install termux-api'。")
return False
def pause(self):
"""暂停播放。"""
if self._is_playing:
subprocess.run(['termux-media-player', 'pause'], capture_output=True)
self._is_playing = False
print("播放已暂停。")
return True
return False
def resume(self):
"""恢复播放。"""
if not self._is_playing and self._current_file:
# 注意:resume命令可能不总是有效,一种可靠的方法是重新play
# subprocess.run(['termux-media-player', 'resume'], capture_output=True)
# 更稳妥的方式是记录位置并重新播放(这里简化处理)
return self.play(self._current_file)
return False
def stop(self):
"""停止播放。"""
subprocess.run(['termux-media-player', 'stop'], capture_output=True)
self._is_playing = False
self._current_file = None
self._position = 0
print("播放已停止。")
return True
def get_info(self):
"""获取当前播放信息(模拟)。"""
# termux-media-player 没有直接查询状态的命令。
# 这是一个简化示例。更复杂的实现可能需要解析系统日志或使用其他方法。
return {
"playing": self._is_playing,
"file": self._current_file,
"position": self._position
}
# 使用示例
if __name__ == "__main__":
player = TermuxMediaPlayer()
player.play("/storage/emulated/0/Music/test.mp3")
time.sleep(5) # 播放5秒
player.pause()
time.sleep(2) # 暂停2秒
player.resume() # 注意:这里可能不是从暂停点继续,而是重新开始
time.sleep(3)
player.stop()
```
### 4.3 状态监听与进度模拟的挑战
`termux-media-player`最大的局限性在于**缺乏查询状态**的命令。我们无法直接知道一首歌是否播放完毕、当前的播放进度是多少。这对于需要精确控制的应用(如音频编辑器或带同步字幕的播放器)来说是个问题。
有几种变通的思路:
1. **基于时间的模拟**:在调用`play`时记录开始时间,根据音频文件的已知时长来估算当前播放位置和是否结束。这需要你事先知道音频的时长(可以用`pydub`或`mutagen`库在Python中解析)。
2. **使用`termux-media-player play`的特性**:当播放完成时,似乎不会触发特定的事件。但你可以尝试结合方案一的`subprocess.Popen`,并监控进程是否结束?不,`play`命令是立即返回的,所以不行。
3. **外部事件驱动**:如果你的应用有用户界面或与其他服务交互,可以由用户操作(如点击“下一首”)或外部事件来驱动状态变更,而不是依赖播放器反馈。
一个结合了时长估算的改进示例:
```python
from pydub import AudioSegment # 需要安装 pydub: `pip install pydub`
import threading
class SmartMediaPlayer(TermuxMediaPlayer):
def __init__(self):
super().__init__()
self._duration = 0 # 音频总时长(秒)
self._start_time = 0 # 开始播放的时间戳
self._timer = None
def play(self, file_path):
# 首先获取音频时长
try:
audio = AudioSegment.from_file(file_path)
self._duration = len(audio) / 1000.0 # 转换为秒
except Exception as e:
print(f"无法解析音频文件时长,将无法精确控制: {e}")
self._duration = 0
# 调用父类方法开始播放
success = super().play(file_path)
if success and self._duration > 0:
self._start_time = time.time()
# 启动一个定时器,在预计结束时触发“停止”或“下一首”事件
self._timer = threading.Timer(self._duration, self._on_playback_finished)
self._timer.start()
return success
def _on_playback_finished(self):
print(f"音频播放完毕: {self._current_file}")
self._is_playing = False
# 这里可以触发一个自定义事件,比如播放列表中的下一首
# self.play_next()
def stop(self):
if self._timer:
self._timer.cancel()
self._timer = None
return super().stop()
def get_progress(self):
"""获取估计的播放进度(0.0 到 1.0)。"""
if not self._is_playing or self._duration <= 0:
return 0.0
elapsed = time.time() - self._start_time
progress = min(elapsed / self._duration, 1.0)
return progress
```
**避坑指南**:
- **格式支持**:`termux-media-player`依赖安卓系统的媒体解码器,通常支持MP3、AAC、OGG、WAV等常见格式。但遇到冷门编码时可能无法播放。
- **后台播放**:即使Termux切换到后台或屏幕关闭,播放也可能继续(取决于手机系统省电策略)。但如果Termux进程被系统杀死,播放会停止。
- **资源竞争**:如果同时使用`termux-media-player`和其他音频应用(如音乐播放器),可能会产生冲突。通常系统会处理这种竞争,但行为可能不一致。
- **状态不可靠**:如前所述,没有官方API获取播放状态和进度,所有状态都是我们模拟维护的,可能与实际情况有出入。对于要求不高的场景(如背景音乐播放)足够,但对交互要求高的场景需谨慎设计。
## 5. 综合对比与实战场景选择
至此,我们已经详细探讨了三种解决Termux中Python音频播放问题的方案。每种方案都有其鲜明的特点和最佳适用场景。为了帮助你快速做出选择,我将它们的关键信息汇总在下表中:
| 特性维度 | **play-audio** | **Termux:API (TTS)** | **Termux:API (Media Player)** |
| :--- | :--- | :--- | :--- |
| **核心功能** | 播放WAV音频文件 | 文本转语音 | 播放多种格式音频文件 |
| **控制粒度** | 播放/停止(进程级) | 播放/停止(进程级) | 播放/暂停/停止 |
| **状态反馈** | 无(需自行管理进程) | 无(需自行管理进程) | 无(状态需模拟) |
| **格式支持** | 主要WAV | 无(输入为文本) | MP3, AAC, OGG, WAV等 |
| **额外依赖** | Termux自带 | Termux:API APP + 系统TTS引擎 | Termux:API APP |
| **适用场景** | 游戏音效、系统提示音 | 语音播报、朗读助手、无障碍应用 | 音乐播放器、播客客户端、背景音乐 |
| **复杂度** | 低 | 中 | 中高 |
| **可靠性** | 高 | 中(依赖外部引擎) | 高 |
**如何选择?我的个人经验是:**
- **如果你只是需要播放“叮咚”、“哔哔”这样的短促提示音**,`play-audio`是首选。它最轻量,没有外部依赖,问题最少。我那个需要后台播放提示音的工具最终就选用了它,配合进程管理,非常稳定。
- **如果你的应用核心是“说话”**,比如一个命令行新闻阅读器、一个任务完成语音提醒,那么TTS方案最合适。省去了准备和管理大量音频文件的麻烦。不过,记得在代码里处理好网络引擎的离线回退,避免没网时功能完全失效。
- **如果你正在构建一个真正的音频播放应用**,比如一个简单的命令行音乐播放器,那么`termux-media-player`提供的暂停功能是刚需。你需要接受它状态管理上的不便,用文件时长估算等方法来弥补。我曾经用它做过一个在Termux里随机播放白噪音助眠的小脚本,效果不错。
**一个融合案例:智能语音提醒脚本**
最后,让我分享一个结合了TTS和文件播放的小脚本。这个脚本监控一个任务日志文件,当发现错误时,先用TTS播报“发现错误”,然后播放一个急促的警报音效。
```python
import time
import subprocess
import threading
from pathlib import Path
class TaskMonitor:
def __init__(self, log_file):
self.log_file = Path(log_file)
self.alert_sound = "/data/data/com.termux/files/home/sounds/alert.wav"
self._running = False
def tts_alert(self, message):
"""用TTS播报警告信息。"""
def _speak():
subprocess.run(['termux-tts-speak', '-l', 'zh-cn', message],
capture_output=True)
thread = threading.Thread(target=_speak, daemon=True)
thread.start()
return thread
def play_sound(self):
"""播放警报音效。"""
subprocess.Popen(['play-audio', self.alert_sound],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
def check_log(self):
"""检查日志文件中是否有错误。"""
# 这里简化处理,实际可能需要解析日志格式
try:
content = self.log_file.read_text(encoding='utf-8', errors='ignore')
return "ERROR" in content or "FAIL" in content
except:
return False
def run(self):
"""主监控循环。"""
self._running = True
print(f"开始监控日志文件: {self.log_file}")
last_alert_time = 0
alert_cooldown = 30 # 30秒内不重复警报
while self._running:
if self.check_log():
current_time = time.time()
if current_time - last_alert_time > alert_cooldown:
print("检测到错误,触发警报!")
# 先语音播报
self.tts_alert("警告,系统检测到错误")
# 稍后播放音效
time.sleep(1.5)
self.play_sound()
last_alert_time = current_time
time.sleep(5) # 每5秒检查一次
def stop(self):
self._running = False
if __name__ == "__main__":
monitor = TaskMonitor("/data/data/com.termux/files/home/task.log")
try:
monitor.run()
except KeyboardInterrupt:
monitor.stop()
print("\n监控已停止。")
```
这个例子展示了如何将两种方案结合起来,TTS负责传递明确的语义信息,而`play-audio`负责提供一种更紧迫的听觉信号。在Termux这个独特的环境里,正是这种灵活的组合与变通,让我们能够突破限制,实现有趣的功能。希望这些方法和思路,能帮你顺利解决安卓上Python音频开发的难题。