<think>我们将使用gTTS(Google Text-to-Speech)库来实现语音播报。gTTS是一个Python库,用于与Google Translate的文本转语音API交互。它可以将文本转换为语音,并保存为MP3文件,然后我们可以使用播放器来播放这个MP3文件。
步骤:
1. 安装gTTS和播放MP3的库(例如,playsound或pygame)
2. 创建一个函数,将文本转换为语音并播放
3. 注意:gTTS需要互联网连接,因为它使用Google的TTS服务
由于gTTS生成的是MP3文件,我们需要一个播放MP3的方法。这里我们使用`playsound`库来播放,因为它简单。但注意,`playsound`在播放时是阻塞的(除非使用线程)。另外,我们也可以使用`pygame.mixer`来播放,它支持非阻塞播放,但需要更多的设置。
我们将提供两种方式:
- 方式1:使用playsound(简单,阻塞播放)
- 方式2:使用pygame(支持非阻塞播放)
我们选择方式2来实现非阻塞播放,这样可以在播放时继续执行其他代码。
但是,请注意:gTTS每次调用都会生成一个MP3文件,我们可以将其保存为临时文件,播放后删除。
代码结构:
- 创建一个类`VoiceBroadcaster`
- 初始化时设置语言(默认中文:'zh-cn',英文:'en')
- 提供`speak`方法,接受文本并播放
- 使用线程来避免阻塞主程序
安装:
pip install gtts pygame
注意:pygame用于播放音频,gTTS用于生成音频。
由于gTTS生成文件需要时间,我们可以将生成和播放放在不同线程中,以避免界面卡顿。
我们将实现:
- 一个工作线程,用于生成语音文件并播放
- 使用队列来管理多个播报任务
但是,为了简化,我们每次播报都生成一个临时文件,并在播放后删除。
具体步骤:
1. 用户调用`speak(text)`方法,将文本放入队列,并启动后台线程处理。
2. 后台线程循环从队列中取出文本,调用gTTS生成MP3文件(保存为临时文件),然后使用pygame播放。
3. 播放完成后删除临时文件。
注意:pygame.mixer需要初始化,并且只能初始化一次。
代码实现:</think>下面我将使用 Google Text-to-Speech (gTTS) 库实现一个功能完整的语音播报系统。gTTS 利用 Google 的 TTS 服务生成高质量的语音,支持多种语言和语音风格:
```python
from gtts import gTTS
import pygame
import os
import tempfile
import threading
import queue
import time
import re
from pydub import AudioSegment
from pydub.playback import play
import logging
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('VoiceBroadcaster')
class VoiceBroadcaster:
def __init__(self, lang='zh-cn', tld='com', slow=False, volume=1.0, cache_dir=None):
"""
初始化语音播报器
:param lang: 语言代码 (zh-cn: 中文, en: 英文, ja: 日文)
:param tld: Google TTS 顶级域名 (com, co.uk, com.au 等)
:param slow: 是否慢速播放
:param volume: 音量 (0.0-1.0)
:param cache_dir: 语音缓存目录
"""
self.lang = lang
self.tld = tld
self.slow = slow
self.volume = volume
self.cache_dir = cache_dir or os.path.join(tempfile.gettempdir(), 'tts_cache')
# 创建缓存目录
os.makedirs(self.cache_dir, exist_ok=True)
# 初始化Pygame混音器
pygame.mixer.init()
# 消息队列
self.message_queue = queue.Queue()
self.is_running = False
self.playback_thread = None
# 回调函数
self.on_start = None
self.on_end = None
self.on_error = None
logger.info(f"语音播报器初始化完成,语言: {lang}, 缓存目录: {self.cache_dir}")
def _get_filename(self, text):
"""生成基于文本内容的唯一文件名"""
# 简化文本用于文件名
simplified = re.sub(r'[^a-zA-Z0-9\u4e00-\u9fff]', '', text)[:20]
return f"tts_{hash(text) & 0xFFFFFFFF}_{simplified}.mp3"
def _generate_speech(self, text):
"""生成语音文件(使用缓存)"""
filename = self._get_filename(text)
filepath = os.path.join(self.cache_dir, filename)
# 检查缓存
if os.path.exists(filepath):
logger.debug(f"使用缓存语音: {filepath}")
return filepath
try:
logger.info(f"生成语音: '{text[:30]}...'")
tts = gTTS(text=text, lang=self.lang, tld=self.tld, slow=self.slow)
tts.save(filepath)
logger.debug(f"语音保存到: {filepath}")
return filepath
except Exception as e:
logger.error(f"生成语音失败: {str(e)}")
if callable(self.on_error):
self.on_error(text, str(e))
return None
def _play_audio(self, filepath):
"""播放音频文件"""
if not filepath or not os.path.exists(filepath):
logger.error(f"音频文件不存在: {filepath}")
return False
try:
# 使用pydub加载音频(支持更多格式)
audio = AudioSegment.from_file(filepath)
# 调整音量
if self.volume != 1.0:
audio = audio + (self.volume * 20 - 20) # 音量调整公式
# 播放
play(audio)
return True
except Exception as e:
logger.error(f"播放失败: {str(e)}")
if callable(self.on_error):
self.on_error(filepath, str(e))
return False
def speak(self, text, block=False):
"""
播报文本
:param text: 要播报的文本
:param block: 是否阻塞直到播报完成
"""
if block:
self._process_text(text)
else:
self.message_queue.put(text)
def _process_text(self, text):
"""处理单个文本的播报"""
try:
if callable(self.on_start):
self.on_start(text)
filepath = self._generate_speech(text)
if filepath:
self._play_audio(filepath)
if callable(self.on_end):
self.on_end(text)
except Exception as e:
logger.error(f"处理文本失败: {str(e)}")
if callable(self.on_error):
self.on_error(text, str(e))
def _playback_worker(self):
"""后台线程:处理消息队列中的播报请求"""
logger.info("语音播报后台线程启动")
while self.is_running or not self.message_queue.empty():
try:
text = self.message_queue.get(timeout=0.5)
self._process_text(text)
self.message_queue.task_done()
except queue.Empty:
continue
logger.info("语音播报后台线程停止")
def start(self):
"""启动语音播报后台服务"""
if not self.is_running:
self.is_running = True
self.playback_thread = threading.Thread(
target=self._playback_worker,
daemon=True,
name="VoicePlaybackThread"
)
self.playback_thread.start()
logger.info("语音播报服务启动")
def stop(self):
"""停止语音播报服务"""
self.is_running = False
if self.playback_thread and self.playback_thread.is_alive():
self.playback_thread.join(timeout=5.0)
logger.info("语音播报服务停止")
def clear_cache(self):
"""清除所有缓存文件"""
for file in os.listdir(self.cache_dir):
if file.startswith("tts_"):
os.remove(os.path.join(self.cache_dir, file))
logger.info(f"已清除缓存: {self.cache_dir}")
def __del__(self):
self.stop()
# ================= 使用示例 =================
if __name__ == "__main__":
# 创建语音播报器(中文)
broadcaster = VoiceBroadcaster(
lang='zh-cn',
tld='com',
slow=False,
volume=0.9
)
# 设置回调函数
def on_start(text):
print(f"▶ 开始播报: '{text}'")
def on_end(text):
print(f"✓ 播报完成: '{text}'")
def on_error(text, error):
print(f"✗ 播报出错: '{text}' - 错误: {error}")
broadcaster.on_start = on_start
broadcaster.on_end = on_end
broadcaster.on_error = on_error
# 启动后台服务
broadcaster.start()
# 添加播报消息(非阻塞)
broadcaster.speak("系统初始化完成")
broadcaster.speak("当前时间:" + time.strftime("%Y年%m月%d日 %H点%M分"))
broadcaster.speak("温度25摄氏度,湿度60%")
# 添加阻塞式播报
broadcaster.speak("重要通知:请立即保存您的工作", block=True)
# 添加英文播报(切换语音)
broadcaster.speak("System alert: Critical temperature detected!", block=True)
# 添加更多消息
broadcaster.speak("系统将在10秒后关闭")
# 等待所有消息处理完成
print("等待队列清空...")
broadcaster.message_queue.join()
# 停止服务
broadcaster.stop()
print("语音播报服务已停止")
# 清理缓存(可选)
# broadcaster.clear_cache()
```
### 安装依赖:
```bash
pip install gtts pygame pydub
```
### 代码详细解释:
1. **核心组件**:
- `gTTS`: Google的文本转语音服务
- `pygame`: 用于音频播放
- `pydub`: 处理音频格式转换和音量调整
2. **主要功能**:
- **语音生成**:使用gTTS将文本转换为MP3文件
- **缓存机制**:避免重复生成相同内容的语音
- **后台播放**:使用线程和队列实现非阻塞播放
- **音量控制**:支持0.0-1.0的音量调整
- **多语言支持**:通过`lang`参数切换语言
3. **高级特性**:
- **域名选择**:`tld`参数可选择不同地区的Google服务
- **语速控制**:`slow`参数控制语速(True为慢速)
- **回调系统**:`on_start`, `on_end`, `on_error`回调
- **缓存管理**:`clear_cache()`方法清理生成的文件
### 使用说明:
1. **基本播报**:
```python
broadcaster = VoiceBroadcaster(lang='zh-cn')
broadcaster.speak("你好,世界!")
```
2. **切换语言**:
```python
# 中文
broadcaster.speak("你好", lang='zh-cn')
# 英文
broadcaster.speak("Hello", lang='en')
# 日文
broadcaster.speak("こんにちは", lang='ja')
```
3. **使用不同地区的语音**:
```python
# 使用英国英语
broadcaster.speak("Hello", lang='en', tld='co.uk')
# 使用澳大利亚英语
broadcaster.speak("Hello", lang='en', tld='com.au')
```
4. **调整语速和音量**:
```python
# 慢速播放
broadcaster.slow = True
# 降低音量
broadcaster.volume = 0.7
```
### 解决常见问题:
1. **网络连接问题**:
- gTTS需要互联网连接,添加重试机制:
```python
def _generate_speech(self, text, retries=3):
for i in range(retries):
try:
# 生成语音代码
return filepath
except Exception as e:
logger.warning(f"生成语音失败,重试 {i+1}/{retries}: {str(e)}")
time.sleep(1)
return None
```
2. **长文本处理**:
```python
def speak_long_text(self, text, max_length=200):
"""分割长文本为多个片段"""
# 按句子分割
sentences = re.split(r'(?<=[。!?])', text)
# 合并短句子
chunks = []
current = ""
for s in sentences:
if len(current) + len(s) <= max_length:
current += s
else:
if current:
chunks.append(current)
current = s
if current:
chunks.append(current)
# 播报每个片段
for chunk in chunks:
self.speak(chunk.strip())
```
3. **离线支持**:
```python
def pre_cache(self, phrases):
"""预生成常用短语的语音"""
for phrase in phrases:
self._generate_speech(phrase)
```
### 性能优化建议:
1. **预加载常用短语**:
```python
common_phrases = ["系统启动", "警告", "操作完成"]
broadcaster.pre_cache(common_phrases)
```
2. **使用内存播放**:
```python
def _play_from_memory(self, audio_data):
"""直接从内存播放音频"""
with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as tmp:
tmp.write(audio_data)
tmp.flush()
self._play_audio(tmp.name)
```
3. **多线程生成**:
```python
# 在初始化时添加生成线程池
self.generation_pool = ThreadPoolExecutor(max_workers=3)
```
4. **音频格式转换**:
```python
# 转换为WAV格式减少CPU占用
audio = AudioSegment.from_file(filepath).set_frame_rate(16000).set_channels(1)
```