mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-28 01:53:53 +08:00
@@ -4,13 +4,14 @@ from config.settings import load_config, check_config_file
|
|||||||
from core.websocket_server import WebSocketServer
|
from core.websocket_server import WebSocketServer
|
||||||
from manager.http_server import WebUI
|
from manager.http_server import WebUI
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from core.utils.util import get_local_ip
|
from core.utils.util import get_local_ip, check_ffmpeg_installed
|
||||||
|
|
||||||
TAG = __name__
|
TAG = __name__
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
check_config_file()
|
check_config_file()
|
||||||
|
check_ffmpeg_installed()
|
||||||
logger = setup_logging()
|
logger = setup_logging()
|
||||||
config = load_config()
|
config = load_config()
|
||||||
|
|
||||||
|
|||||||
@@ -16,29 +16,37 @@ async def isLLMWantToFinish(last_text):
|
|||||||
|
|
||||||
|
|
||||||
async def sendAudioMessage(conn, audios, text):
|
async def sendAudioMessage(conn, audios, text):
|
||||||
# 发送 tts.start
|
# 发送句子开始消息
|
||||||
if text == conn.tts_first_text:
|
if text == conn.tts_first_text:
|
||||||
logger.bind(tag=TAG).info(f"发送第一段语音: {text}")
|
logger.bind(tag=TAG).info(f"发送第一段语音: {text}")
|
||||||
conn.tts_start_speak_time = time.time()
|
conn.tts_start_speak_time = time.perf_counter()
|
||||||
await send_tts_message(conn, "sentence_start", text)
|
await send_tts_message(conn, "sentence_start", text)
|
||||||
|
|
||||||
# 发送音频数据
|
# 初始化流控参数
|
||||||
frame_duration = 60 # 初始帧持续时间(毫秒)
|
frame_duration = 60 # 毫秒
|
||||||
start_time = time.time() # 记录开始时间
|
start_time = time.perf_counter() # 使用高精度计时器
|
||||||
for idx, opus_packet in enumerate(audios):
|
play_position = 0 # 已播放的时长(毫秒)
|
||||||
|
|
||||||
|
for opus_packet in audios:
|
||||||
if conn.client_abort:
|
if conn.client_abort:
|
||||||
return
|
return
|
||||||
|
|
||||||
# 计算当前包的预期发送时间
|
# 计算当前包的预期发送时间
|
||||||
expected_time = start_time + idx * (frame_duration / 1000)
|
expected_time = start_time + (play_position / 1000)
|
||||||
current_time = time.time()
|
current_time = time.perf_counter()
|
||||||
# 如果未到预期时间则等待差值
|
|
||||||
if current_time < expected_time:
|
# 等待直到预期时间
|
||||||
await asyncio.sleep(expected_time - current_time)
|
delay = expected_time - current_time
|
||||||
|
if delay > 0:
|
||||||
|
await asyncio.sleep(delay)
|
||||||
|
|
||||||
# 发送音频包
|
# 发送音频包
|
||||||
await conn.websocket.send(opus_packet)
|
await conn.websocket.send(opus_packet)
|
||||||
|
play_position += frame_duration # 更新播放位置
|
||||||
|
await send_tts_message(conn, "sentence_end", text)
|
||||||
|
# 发送结束消息(如果是最后一个文本)
|
||||||
if conn.llm_finish_task and text == conn.tts_last_text:
|
if conn.llm_finish_task and text == conn.tts_last_text:
|
||||||
await send_tts_message(conn, 'stop')
|
await send_tts_message(conn, 'stop', None)
|
||||||
if await isLLMWantToFinish(text):
|
if await isLLMWantToFinish(text):
|
||||||
await conn.close()
|
await conn.close()
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import re
|
|||||||
import json
|
import json
|
||||||
import yaml
|
import yaml
|
||||||
import socket
|
import socket
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
def get_project_dir():
|
def get_project_dir():
|
||||||
@@ -114,3 +115,29 @@ def check_password(password):
|
|||||||
|
|
||||||
# 如果满足所有条件,则返回True
|
# 如果满足所有条件,则返回True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def check_ffmpeg_installed():
|
||||||
|
ffmpeg_installed = False
|
||||||
|
try:
|
||||||
|
# 执行ffmpeg -version命令,并捕获输出
|
||||||
|
result = subprocess.run(
|
||||||
|
['ffmpeg', '-version'],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
check=True # 如果返回码非零则抛出异常
|
||||||
|
)
|
||||||
|
# 检查输出中是否包含版本信息(可选)
|
||||||
|
output = result.stdout + result.stderr
|
||||||
|
if 'ffmpeg version' in output.lower():
|
||||||
|
ffmpeg_installed = True
|
||||||
|
return False
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
# 命令执行失败或未找到
|
||||||
|
ffmpeg_installed = False
|
||||||
|
if not ffmpeg_installed:
|
||||||
|
error_msg = "您的电脑还没正确安装ffmpeg\n"
|
||||||
|
error_msg += "\n建议您:\n"
|
||||||
|
error_msg += "1、按照项目的安装文档,正确进入conda环境\n"
|
||||||
|
error_msg += "2、查阅安装文档,如何在conda环境中安装ffmpeg\n"
|
||||||
|
raise ValueError(error_msg)
|
||||||
+20
-3
@@ -159,14 +159,31 @@ poetry run python app.py
|
|||||||
本项目使用`conda`管理依赖环境。如果不方便安装`conda`,需要根据实际的操作系统安装好`libopus`和`ffmpeg`。
|
本项目使用`conda`管理依赖环境。如果不方便安装`conda`,需要根据实际的操作系统安装好`libopus`和`ffmpeg`。
|
||||||
如果确定使用`conda`,则安装好后,开始执行以下命令。
|
如果确定使用`conda`,则安装好后,开始执行以下命令。
|
||||||
|
|
||||||
|
重要提示!windows 用户,可以通过安装`Anaconda`来管理环境。安装好`Anaconda`后,在`开始`那里搜索`anaconda`相关的关键词,
|
||||||
|
找到`Anaconda Prpmpt`,使用管理员身份运行它。如下图。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
运行之后,如果你能看到命令行窗口前面有一个(base)字样,说明你成功进入了`conda`环境。那么你就可以执行以下命令了。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
```
|
```
|
||||||
conda remove -n xiaozhi-esp32-server --all -y
|
conda remove -n xiaozhi-esp32-server --all -y
|
||||||
conda create -n xiaozhi-esp32-server python=3.10 -y
|
conda create -n xiaozhi-esp32-server python=3.10 -y
|
||||||
conda activate xiaozhi-esp32-server
|
conda activate xiaozhi-esp32-server
|
||||||
conda install conda-forge::libopus -y
|
|
||||||
conda install conda-forge::ffmpeg -y
|
# 添加清华源通道
|
||||||
|
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
|
||||||
|
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free
|
||||||
|
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
|
||||||
|
|
||||||
|
conda install libopus -y
|
||||||
|
conda install ffmpeg -y
|
||||||
```
|
```
|
||||||
|
|
||||||
|
请注意,以上命令,不是一股脑执行就成功的,你需要一步步执行,每一步执行完后,都检查一下输出的日志,查看是否成功。
|
||||||
|
|
||||||
## 2.安装本项目依赖
|
## 2.安装本项目依赖
|
||||||
|
|
||||||
你先要下载本项目源码,源码可以通过`git clone`命令下载,如果你不熟悉`git clone`命令。
|
你先要下载本项目源码,源码可以通过`git clone`命令下载,如果你不熟悉`git clone`命令。
|
||||||
@@ -179,7 +196,7 @@ conda install conda-forge::ffmpeg -y
|
|||||||
你需要把它重命名成`xiaozhi-esp32-server`,好了请记住这个目录,我们暂且称它为`项目目录`。
|
你需要把它重命名成`xiaozhi-esp32-server`,好了请记住这个目录,我们暂且称它为`项目目录`。
|
||||||
|
|
||||||
```
|
```
|
||||||
# 使用dos或者终端,进入到你的项目目录,执行以下命令
|
# 继续使用conda环境,进入到你的项目目录,执行以下命令
|
||||||
conda activate xiaozhi-esp32-server
|
conda activate xiaozhi-esp32-server
|
||||||
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
|
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 781 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 588 KiB |
Reference in New Issue
Block a user