mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-29 15:13:57 +08:00
update:流式播放
This commit is contained in:
@@ -4,6 +4,7 @@ import queue
|
|||||||
import asyncio
|
import asyncio
|
||||||
import requests
|
import requests
|
||||||
import traceback
|
import traceback
|
||||||
|
import aiohttp
|
||||||
from config.logger import setup_logging
|
from config.logger import setup_logging
|
||||||
from core.utils.tts import MarkdownCleaner
|
from core.utils.tts import MarkdownCleaner
|
||||||
from core.providers.tts.base import TTSProviderBase
|
from core.providers.tts.base import TTSProviderBase
|
||||||
@@ -122,46 +123,59 @@ class TTSProvider(TTSProviderBase):
|
|||||||
|
|
||||||
async def text_to_speak(self, text, _):
|
async def text_to_speak(self, text, _):
|
||||||
"""流式处理TTS音频,每句只推送一次音频列表"""
|
"""流式处理TTS音频,每句只推送一次音频列表"""
|
||||||
start_time = time.time()
|
await self._tts_request(text)
|
||||||
logger.info(f"TTS请求: {text}")
|
|
||||||
try:
|
|
||||||
params = {
|
|
||||||
"tts_text": text,
|
|
||||||
"spk_id": self.voice,
|
|
||||||
"frame_duration": 60,
|
|
||||||
"stream": True,
|
|
||||||
"target_sr": 16000,
|
|
||||||
"audio_format": self.audio_format,
|
|
||||||
}
|
|
||||||
headers = {"Authorization": f"Bearer {self.access_token}"}
|
|
||||||
|
|
||||||
with requests.get(
|
|
||||||
self.api_url, params=params, headers=headers, timeout=5
|
|
||||||
) as response:
|
|
||||||
if response.status_code != 200:
|
|
||||||
logger.error(
|
|
||||||
f"TTS请求失败: {response.status_code}, {response.text}"
|
|
||||||
)
|
|
||||||
# 推送空LAST,防止播放端卡死
|
|
||||||
self.tts_audio_queue.put((SentenceType.LAST, [], None))
|
|
||||||
return
|
|
||||||
logger.info(f"TTS请求成功: {text}, 耗时: {time.time() - start_time}秒")
|
|
||||||
opus_datas = self.wav_to_opus_data_audio_raw(response.content)
|
|
||||||
self.tts_audio_queue.put((SentenceType.MIDDLE, opus_datas, text))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"TTS流式处理异常:{str(e)}")
|
|
||||||
# 推送空LAST,防止播放端卡死
|
|
||||||
self.tts_audio_queue.put((SentenceType.LAST, [], None))
|
|
||||||
|
|
||||||
# 保持原有方法
|
|
||||||
def wav_to_opus_data_audio_raw(self, raw_data_var):
|
|
||||||
opus_datas = self.opus_encoder.encode_pcm_to_opus(
|
|
||||||
raw_data_var, end_of_stream=True
|
|
||||||
)
|
|
||||||
return opus_datas
|
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
"""资源清理"""
|
"""资源清理"""
|
||||||
await super().close()
|
await super().close()
|
||||||
if hasattr(self, "opus_encoder"):
|
if hasattr(self, "opus_encoder"):
|
||||||
self.opus_encoder.close()
|
self.opus_encoder.close()
|
||||||
|
|
||||||
|
async def _tts_request(self, text: str) -> None:
|
||||||
|
"""发送TTS请求"""
|
||||||
|
start_time = time.time()
|
||||||
|
params = {
|
||||||
|
"tts_text": text,
|
||||||
|
"spk_id": self.voice,
|
||||||
|
"frame_durition": 60,
|
||||||
|
"stream": "true",
|
||||||
|
"target_sr": 16000,
|
||||||
|
"audio_format": "pcm",
|
||||||
|
"instruct_text": "",
|
||||||
|
}
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {self.access_token}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(
|
||||||
|
self.api_url, params=params, headers=headers, timeout=10
|
||||||
|
) as response:
|
||||||
|
if response.status != 200:
|
||||||
|
logger.error(
|
||||||
|
f"TTS请求失败: {response.status}, {await response.text()}"
|
||||||
|
)
|
||||||
|
# 推送空LAST,防止播放端卡死
|
||||||
|
self.tts_audio_queue.put((SentenceType.LAST, [], None))
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"TTS请求成功: {text}, 耗时: {time.time() - start_time}秒"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 流式处理音频数据
|
||||||
|
async for chunk in response.content.iter_chunks():
|
||||||
|
if chunk[0]: # 确保数据不为空
|
||||||
|
opus_data = self.opus_encoder.encode_pcm_to_opus(
|
||||||
|
chunk[0], end_of_stream=True
|
||||||
|
)
|
||||||
|
if opus_data:
|
||||||
|
self.tts_audio_queue.put(
|
||||||
|
(SentenceType.MIDDLE, opus_data, text)
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"TTS请求异常: {str(e)}")
|
||||||
|
self.tts_audio_queue.put((SentenceType.LAST, [], None))
|
||||||
|
|||||||
Reference in New Issue
Block a user