mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-29 18:23:59 +08:00
update:优化音频播放
This commit is contained in:
+2
-1
@@ -146,7 +146,7 @@ tmp
|
|||||||
.history
|
.history
|
||||||
.DS_Store
|
.DS_Store
|
||||||
main/xiaozhi-server/data
|
main/xiaozhi-server/data
|
||||||
main/xiaozhi-server/config/assets/wakeup_words.*
|
|
||||||
main/manager-web/node_modules
|
main/manager-web/node_modules
|
||||||
.config.yaml
|
.config.yaml
|
||||||
.secrets.yaml
|
.secrets.yaml
|
||||||
@@ -163,6 +163,7 @@ main/xiaozhi-server/models/sherpa-onnx*
|
|||||||
/models/SenseVoiceSmall/model.pt
|
/models/SenseVoiceSmall/model.pt
|
||||||
my_wakeup_words.mp3
|
my_wakeup_words.mp3
|
||||||
!main/xiaozhi-server/config/assets/bind_code.wav
|
!main/xiaozhi-server/config/assets/bind_code.wav
|
||||||
|
!main/xiaozhi-server/config/assets/wakeup_words.wav
|
||||||
!main/xiaozhi-server/config/assets/bind_not_found.wav
|
!main/xiaozhi-server/config/assets/bind_not_found.wav
|
||||||
!main/xiaozhi-server/config/assets/bind_code/*.wav
|
!main/xiaozhi-server/config/assets/bind_code/*.wav
|
||||||
!main/xiaozhi-server/config/assets/max_output_size.wav
|
!main/xiaozhi-server/config/assets/max_output_size.wav
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ module_test:
|
|||||||
# 唤醒词,用于识别唤醒词还是讲话内容
|
# 唤醒词,用于识别唤醒词还是讲话内容
|
||||||
wakeup_words:
|
wakeup_words:
|
||||||
- "你好小智"
|
- "你好小智"
|
||||||
|
- "嘿你好呀"
|
||||||
- "你好小志"
|
- "你好小志"
|
||||||
- "小爱同学"
|
- "小爱同学"
|
||||||
- "你好小鑫"
|
- "你好小鑫"
|
||||||
|
|||||||
Binary file not shown.
@@ -1,24 +1,15 @@
|
|||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import uuid
|
import uuid
|
||||||
import time
|
import time
|
||||||
import queue
|
import queue
|
||||||
import asyncio
|
import asyncio
|
||||||
import traceback
|
|
||||||
import threading
|
import threading
|
||||||
|
import traceback
|
||||||
|
import subprocess
|
||||||
import websockets
|
import websockets
|
||||||
from typing import Dict, Any
|
|
||||||
from plugins_func.loadplugins import auto_import_modules
|
|
||||||
from config.logger import setup_logging
|
|
||||||
from config.config_loader import get_project_dir
|
|
||||||
from core.utils import p3
|
|
||||||
from core.utils.dialogue import Message, Dialogue
|
|
||||||
from core.providers.tts.dto.dto import ContentType, TTSMessageDTO, SentenceType
|
|
||||||
from core.providers.tts.default import DefaultTTS
|
|
||||||
from core.handle.textHandle import handleTextMessage
|
|
||||||
from core.utils.util import (
|
from core.utils.util import (
|
||||||
extract_json_from_string,
|
extract_json_from_string,
|
||||||
initialize_modules,
|
initialize_modules,
|
||||||
@@ -27,16 +18,23 @@ from core.utils.util import (
|
|||||||
filter_sensitive_info,
|
filter_sensitive_info,
|
||||||
initialize_tts,
|
initialize_tts,
|
||||||
)
|
)
|
||||||
|
from typing import Dict, Any
|
||||||
|
from config.logger import setup_logging
|
||||||
|
from core.mcp.manager import MCPManager
|
||||||
|
from core.handle.reportHandle import report
|
||||||
|
from core.providers.tts.default import DefaultTTS
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from core.handle.receiveAudioHandle import handleAudioMessage
|
from core.utils.dialogue import Message, Dialogue
|
||||||
|
from core.handle.textHandle import handleTextMessage
|
||||||
from core.handle.functionHandler import FunctionHandler
|
from core.handle.functionHandler import FunctionHandler
|
||||||
|
from plugins_func.loadplugins import auto_import_modules
|
||||||
from plugins_func.register import Action, ActionResponse
|
from plugins_func.register import Action, ActionResponse
|
||||||
from core.auth import AuthMiddleware, AuthenticationError
|
from core.auth import AuthMiddleware, AuthenticationError
|
||||||
from core.mcp.manager import MCPManager
|
|
||||||
from config.config_loader import get_private_config_from_api
|
from config.config_loader import get_private_config_from_api
|
||||||
|
from core.handle.receiveAudioHandle import handleAudioMessage
|
||||||
|
from core.providers.tts.dto.dto import ContentType, TTSMessageDTO, SentenceType
|
||||||
from config.manage_api_client import DeviceNotFoundException, DeviceBindException
|
from config.manage_api_client import DeviceNotFoundException, DeviceBindException
|
||||||
from core.utils.output_counter import add_device_output
|
|
||||||
from core.handle.reportHandle import report
|
|
||||||
|
|
||||||
TAG = __name__
|
TAG = __name__
|
||||||
|
|
||||||
@@ -860,23 +858,24 @@ class ConnectionHandler:
|
|||||||
|
|
||||||
def clear_queues(self):
|
def clear_queues(self):
|
||||||
"""清空所有任务队列"""
|
"""清空所有任务队列"""
|
||||||
self.logger.bind(tag=TAG).debug(
|
if self.tts:
|
||||||
f"开始清理: TTS队列大小={self.tts.tts_text_queue.qsize()}, 音频队列大小={self.tts.tts_audio_queue.qsize()}"
|
self.logger.bind(tag=TAG).debug(
|
||||||
)
|
f"开始清理: TTS队列大小={self.tts.tts_text_queue.qsize()}, 音频队列大小={self.tts.tts_audio_queue.qsize()}"
|
||||||
|
)
|
||||||
|
|
||||||
# 使用非阻塞方式清空队列
|
# 使用非阻塞方式清空队列
|
||||||
for q in [self.tts.tts_text_queue, self.tts.tts_audio_queue]:
|
for q in [self.tts.tts_text_queue, self.tts.tts_audio_queue]:
|
||||||
if not q:
|
if not q:
|
||||||
continue
|
continue
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
q.get_nowait()
|
q.get_nowait()
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
break
|
break
|
||||||
|
|
||||||
self.logger.bind(tag=TAG).debug(
|
self.logger.bind(tag=TAG).debug(
|
||||||
f"清理结束: TTS队列大小={self.tts.tts_text_queue.qsize()}, 音频队列大小={self.tts.tts_audio_queue.qsize()}"
|
f"清理结束: TTS队列大小={self.tts.tts_text_queue.qsize()}, 音频队列大小={self.tts.tts_audio_queue.qsize()}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def reset_vad_states(self):
|
def reset_vad_states(self):
|
||||||
self.client_audio_buffer = bytearray()
|
self.client_audio_buffer = bytearray()
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
|
import os
|
||||||
|
import time
|
||||||
import json
|
import json
|
||||||
from core.handle.sendAudioHandle import send_stt_message
|
import random
|
||||||
from core.utils.util import remove_punctuation_and_length
|
|
||||||
import shutil
|
import shutil
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
from core.providers.tts.dto.dto import ContentType
|
||||||
import random
|
from core.providers.tts.dto.dto import SentenceType
|
||||||
import time
|
from core.handle.sendAudioHandle import send_stt_message
|
||||||
|
from core.utils.util import remove_punctuation_and_length
|
||||||
|
|
||||||
|
|
||||||
TAG = __name__
|
TAG = __name__
|
||||||
|
|
||||||
@@ -49,11 +52,12 @@ async def checkWakeupWords(conn, text):
|
|||||||
if file is None:
|
if file is None:
|
||||||
asyncio.create_task(wakeupWordsResponse(conn))
|
asyncio.create_task(wakeupWordsResponse(conn))
|
||||||
return False
|
return False
|
||||||
opus_packets, _ = conn.tts.audio_to_opus_data(file)
|
|
||||||
text_hello = WAKEUP_CONFIG["text"]
|
text_hello = WAKEUP_CONFIG["text"]
|
||||||
if not text_hello:
|
if not text_hello:
|
||||||
text_hello = text
|
text_hello = text
|
||||||
conn.tts.tts_audio_queue.put((opus_packets, text_hello, 0))
|
conn.tts.tts_one_sentence(
|
||||||
|
conn, ContentType.FILE, content_file=file, content_detail=text_hello
|
||||||
|
)
|
||||||
if time.time() - WAKEUP_CONFIG["create_time"] > WAKEUP_CONFIG["refresh_time"]:
|
if time.time() - WAKEUP_CONFIG["create_time"] > WAKEUP_CONFIG["refresh_time"]:
|
||||||
asyncio.create_task(wakeupWordsResponse(conn))
|
asyncio.create_task(wakeupWordsResponse(conn))
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -47,9 +47,8 @@ async def handleAudioMessage(conn, audio):
|
|||||||
text_len, _ = remove_punctuation_and_length(raw_text)
|
text_len, _ = remove_punctuation_and_length(raw_text)
|
||||||
if text_len > 0:
|
if text_len > 0:
|
||||||
# 使用自定义模块进行上报
|
# 使用自定义模块进行上报
|
||||||
enqueue_asr_report(conn, raw_text, copy.deepcopy(conn.asr_audio))
|
|
||||||
|
|
||||||
await startToChat(conn, raw_text)
|
await startToChat(conn, raw_text)
|
||||||
|
enqueue_asr_report(conn, raw_text, copy.deepcopy(conn.asr_audio))
|
||||||
else:
|
else:
|
||||||
conn.asr_server_receive = True
|
conn.asr_server_receive = True
|
||||||
conn.asr_audio.clear()
|
conn.asr_audio.clear()
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ from abc import ABC, abstractmethod
|
|||||||
from config.logger import setup_logging
|
from config.logger import setup_logging
|
||||||
from core.utils.util import audio_to_data
|
from core.utils.util import audio_to_data
|
||||||
from core.utils.tts import MarkdownCleaner
|
from core.utils.tts import MarkdownCleaner
|
||||||
|
from core.utils.output_counter import add_device_output
|
||||||
|
from core.handle.reportHandle import enqueue_tts_report
|
||||||
from core.handle.sendAudioHandle import sendAudioMessage
|
from core.handle.sendAudioHandle import sendAudioMessage
|
||||||
from core.providers.tts.dto.dto import TTSMessageDTO, SentenceType, ContentType
|
from core.providers.tts.dto.dto import TTSMessageDTO, SentenceType, ContentType
|
||||||
|
|
||||||
@@ -226,6 +228,9 @@ class TTSProviderBase(ABC):
|
|||||||
self.conn.loop,
|
self.conn.loop,
|
||||||
)
|
)
|
||||||
future.result()
|
future.result()
|
||||||
|
if self.conn.max_output_size > 0 and text:
|
||||||
|
add_device_output(self.conn.headers.get("device-id"), len(text))
|
||||||
|
enqueue_tts_report(self.conn, text, audio_datas)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).error(
|
logger.bind(tag=TAG).error(
|
||||||
f"audio_play_priority priority_thread: {text} {e}"
|
f"audio_play_priority priority_thread: {text} {e}"
|
||||||
@@ -298,6 +303,14 @@ class TTSProviderBase(ABC):
|
|||||||
audio_datas, _ = self.audio_to_pcm_data(tts_file)
|
audio_datas, _ = self.audio_to_pcm_data(tts_file)
|
||||||
else:
|
else:
|
||||||
audio_datas, _ = self.audio_to_opus_data(tts_file)
|
audio_datas, _ = self.audio_to_opus_data(tts_file)
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.delete_audio_file
|
||||||
|
and tts_file is not None
|
||||||
|
and os.path.exists(tts_file)
|
||||||
|
and tts_file.startswith(self.output_file)
|
||||||
|
):
|
||||||
|
os.remove(tts_file)
|
||||||
return audio_datas
|
return audio_datas
|
||||||
|
|
||||||
def _process_remaining_text(self):
|
def _process_remaining_text(self):
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
import asyncio
|
import asyncio
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
@@ -149,7 +150,7 @@ class TTSProvider(TTSProviderBase):
|
|||||||
self.start_connection_flag = False
|
self.start_connection_flag = False
|
||||||
self.tts_text = ""
|
self.tts_text = ""
|
||||||
# 合成文字语音后,播放的音频文件列表
|
# 合成文字语音后,播放的音频文件列表
|
||||||
self.tts_audio_files = []
|
self.before_stop_play_files = []
|
||||||
self.opus_encoder = opus_encoder_utils.OpusEncoderUtils(
|
self.opus_encoder = opus_encoder_utils.OpusEncoderUtils(
|
||||||
sample_rate=16000, channels=1, frame_size_ms=60
|
sample_rate=16000, channels=1, frame_size_ms=60
|
||||||
)
|
)
|
||||||
@@ -188,6 +189,7 @@ class TTSProvider(TTSProviderBase):
|
|||||||
)
|
)
|
||||||
future.result()
|
future.result()
|
||||||
self.tts_audio_first_sentence = True
|
self.tts_audio_first_sentence = True
|
||||||
|
self.before_stop_play_files.clear()
|
||||||
elif ContentType.TEXT == message.content_type:
|
elif ContentType.TEXT == message.content_type:
|
||||||
if message.content_detail:
|
if message.content_detail:
|
||||||
future = asyncio.run_coroutine_threadsafe(
|
future = asyncio.run_coroutine_threadsafe(
|
||||||
@@ -196,9 +198,18 @@ class TTSProvider(TTSProviderBase):
|
|||||||
)
|
)
|
||||||
future.result()
|
future.result()
|
||||||
elif ContentType.FILE == message.content_type:
|
elif ContentType.FILE == message.content_type:
|
||||||
pass
|
self.before_stop_play_files.append(
|
||||||
|
(message.content_file, message.content_detail)
|
||||||
|
)
|
||||||
|
|
||||||
if message.sentence_type == SentenceType.LAST:
|
if message.sentence_type == SentenceType.LAST:
|
||||||
|
for tts_file, text in self.before_stop_play_files:
|
||||||
|
if tts_file and os.path.exists(tts_file):
|
||||||
|
audio_datas = self._process_audio_file(tts_file)
|
||||||
|
self.tts_audio_queue.put(
|
||||||
|
(message.sentence_type, audio_datas, text)
|
||||||
|
)
|
||||||
|
self.before_stop_play_files.clear()
|
||||||
future = asyncio.run_coroutine_threadsafe(
|
future = asyncio.run_coroutine_threadsafe(
|
||||||
self.finish_session(self.conn.sentence_id), loop=self.conn.loop
|
self.finish_session(self.conn.sentence_id), loop=self.conn.loop
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user