Files
open-xiaoai/examples/xiaozhi/xiaozhi/xiaoai.py
T
2025-05-21 19:29:28 +08:00

116 lines
3.9 KiB
Python

import argparse
import asyncio
import threading
import numpy as np
import open_xiaoai_server
from xiaozhi.event import EventManager
from xiaozhi.ref import get_speaker, set_xiaoai
from xiaozhi.services.audio.stream import GlobalStream
from xiaozhi.services.speaker import SpeakerManager
from xiaozhi.utils.base import json_decode
ASCII_BANNER = """
▄▖ ▖▖▘ ▄▖▄▖
▌▌▛▌█▌▛▌▚▘▌▀▌▛▌▌▌▐
▙▌▙▌▙▖▌▌▌▌▌█▌▙▌▛▌▟▖
v1.0.0 by: https://del.wang
"""
class XiaoAI:
mode = "xiaoai"
speaker = SpeakerManager()
async_loop: asyncio.AbstractEventLoop = None
@classmethod
def setup_mode(cls):
set_xiaoai(cls)
parser = argparse.ArgumentParser(
description="小爱音箱接入小智 AI | by: https://del.wang"
)
parser.add_argument(
"--mode",
type=str,
choices=["xiaoai", "xiaozhi"],
default="xiaoai",
help="运行模式:【xiaoai】使用小爱音箱的输入输出音频(默认)、【xiaozhi】使用本地电脑的输入输出音频",
)
args = parser.parse_args()
if args.mode == "xiaozhi":
cls.mode = "xiaozhi"
@classmethod
def on_input_data(cls, data: bytes):
audio_array = np.frombuffer(data, dtype=np.uint16)
GlobalStream.input(audio_array.tobytes())
@classmethod
def on_output_data(cls, data: bytes):
async def on_output_data_async(data: bytes):
return await open_xiaoai_server.on_output_data(data)
asyncio.run_coroutine_threadsafe(
on_output_data_async(data),
cls.async_loop,
)
@classmethod
async def run_shell(cls, script: str, timeout: float = 10 * 1000):
return await open_xiaoai_server.run_shell(script, timeout)
@classmethod
async def on_event(cls, event: str):
event_json = json_decode(event) or {}
event_data = event_json.get("data", {})
event_type = event_json.get("event")
if not event_json.get("event"):
return
if event_type == "instruction" and event_data.get("NewLine"):
line = json_decode(event_data.get("NewLine"))
if (
line
and line.get("header", {}).get("namespace") == "SpeechRecognizer"
and line.get("header", {}).get("name") == "RecognizeResult"
):
text = line.get("payload", {}).get("results")[0].get("text")
if not text and not line.get("payload", {}).get("is_vad_begin"):
print("🔥 唤醒小爱")
EventManager.on_interrupt()
elif text and line.get("payload", {}).get("is_final"):
print(f"🔥 收到指令: {text}")
await EventManager.wakeup(text, "xiaoai")
elif event_type == "playing":
get_speaker().status = event_data.lower()
@classmethod
def __init_background_event_loop(cls):
def run_event_loop():
cls.async_loop = asyncio.new_event_loop()
asyncio.set_event_loop(cls.async_loop)
cls.async_loop.run_forever()
thread = threading.Thread(target=run_event_loop, daemon=True)
thread.start()
@classmethod
def __on_event(cls, event: str):
asyncio.run_coroutine_threadsafe(
cls.on_event(event),
cls.async_loop,
)
@classmethod
async def init_xiaoai(cls):
GlobalStream.on_output_data = cls.on_output_data
open_xiaoai_server.register_fn("on_input_data", cls.on_input_data)
open_xiaoai_server.register_fn("on_event", cls.__on_event)
cls.__init_background_event_loop()
print(ASCII_BANNER)
await open_xiaoai_server.start_server()