2024-05-23 01:27:51 +08:00
|
|
|
|
# basic
|
|
|
|
|
import io
|
|
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
import time
|
|
|
|
|
import json
|
|
|
|
|
import random
|
|
|
|
|
from collections import deque
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
# log
|
|
|
|
|
import logging
|
|
|
|
|
import warnings
|
|
|
|
|
# multiprocessing
|
|
|
|
|
import queue
|
|
|
|
|
import threading
|
|
|
|
|
import multiprocessing
|
|
|
|
|
# web request
|
|
|
|
|
import requests
|
|
|
|
|
import pyaudio
|
|
|
|
|
# hot words detection
|
|
|
|
|
import pvporcupine
|
|
|
|
|
|
|
|
|
|
from takway.apps.data_struct import QueueIterator
|
|
|
|
|
from takway.common_utils import *
|
|
|
|
|
from takway.audio_utils import PicovoiceRecorder, HDRecorder
|
|
|
|
|
from takway.clients.client_utils import BaseWebSocketClient
|
|
|
|
|
from takway.audio_utils import AudioPlayer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WebSocketClinet:
|
|
|
|
|
def __init__(self,
|
|
|
|
|
board,
|
|
|
|
|
server_args,
|
|
|
|
|
recorder_args,
|
|
|
|
|
player_args,
|
|
|
|
|
log_args,
|
|
|
|
|
excute_args=None,
|
|
|
|
|
):
|
|
|
|
|
self.board = board
|
|
|
|
|
# server_args
|
|
|
|
|
self.server_args = server_args
|
2024-05-23 16:07:23 +08:00
|
|
|
|
# self.recorder_args
|
2024-05-23 01:27:51 +08:00
|
|
|
|
self.recorder_args = recorder_args
|
|
|
|
|
# player_args
|
|
|
|
|
self.player_args = player_args
|
|
|
|
|
# excute_args
|
|
|
|
|
self.excute_args = excute_args
|
|
|
|
|
# log_args
|
|
|
|
|
self.log_args = log_args
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_init(self):
|
|
|
|
|
# multiprocessing
|
|
|
|
|
manager = multiprocessing.Manager()
|
|
|
|
|
self.trigger_queue = manager.Queue()
|
|
|
|
|
self.client_queue = manager.Queue()
|
|
|
|
|
self.audio_play_queue = manager.Queue()
|
|
|
|
|
self.excute_queue = manager.Queue()
|
|
|
|
|
|
2024-05-23 16:07:23 +08:00
|
|
|
|
# 唤醒事件
|
|
|
|
|
self.wakeup_event = manager.Event()
|
2024-06-18 18:09:12 +08:00
|
|
|
|
self.sleep_event = manager.Event()
|
2024-05-23 16:07:23 +08:00
|
|
|
|
|
2024-06-18 17:24:37 +08:00
|
|
|
|
# 打断事件
|
|
|
|
|
self.interrupt_event = manager.Event()
|
|
|
|
|
|
2024-05-23 16:07:23 +08:00
|
|
|
|
# 监听事件
|
|
|
|
|
self.listening_event = manager.Event()
|
|
|
|
|
# 播放事件
|
|
|
|
|
self.speaking_event = manager.Event()
|
2024-05-23 01:27:51 +08:00
|
|
|
|
|
|
|
|
|
processes = [
|
|
|
|
|
multiprocessing.Process(target=self.audio_process),
|
|
|
|
|
multiprocessing.Process(target=self.web_socket_client_process),
|
|
|
|
|
multiprocessing.Process(target=self.audio_play_process),
|
|
|
|
|
]
|
|
|
|
|
if self.excute_args.get('enable', False):
|
|
|
|
|
processes.append(
|
|
|
|
|
multiprocessing.Process(target=self.excute_process),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for process in processes:
|
|
|
|
|
time.sleep(0.5)
|
2024-05-23 16:07:23 +08:00
|
|
|
|
# time.sleep(2)
|
2024-05-23 01:27:51 +08:00
|
|
|
|
process.start()
|
|
|
|
|
for process in processes:
|
|
|
|
|
process.join()
|
|
|
|
|
|
|
|
|
|
def audio_process(self):
|
|
|
|
|
"""audio_process
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
trigger_queue: multiprocessing.Queue, trigger queue
|
|
|
|
|
client_queue: multiprocessing.Queue, client queue
|
|
|
|
|
"""
|
|
|
|
|
min_stream_record_time = self.recorder_args.pop('min_stream_record_time')
|
|
|
|
|
voice_trigger = self.recorder_args.pop('voice_trigger')
|
|
|
|
|
max_slience_time = self.recorder_args.pop('max_slience_time')
|
|
|
|
|
if voice_trigger:
|
|
|
|
|
recorder = PicovoiceRecorder(**self.recorder_args)
|
|
|
|
|
else:
|
|
|
|
|
voice_keys = ['access_key', 'keywords', 'keyword_paths', 'model_path','sensitivities', 'library_path']
|
|
|
|
|
for key in voice_keys:
|
|
|
|
|
self.recorder_args.pop(key)
|
|
|
|
|
recorder = HDRecorder(**self.recorder_args)
|
|
|
|
|
recorder.min_stream_record_time = min_stream_record_time
|
|
|
|
|
recorder.max_slience_time = max_slience_time
|
2024-05-23 16:07:23 +08:00
|
|
|
|
|
|
|
|
|
# self.hardware = recorder.hardware
|
|
|
|
|
# self.long_power_status = recorder.hardware.long_power_status
|
|
|
|
|
|
|
|
|
|
print(f"recorder.min_act_time: {recorder.min_act_time}")
|
|
|
|
|
print(f"recorder.max_slience_time: {recorder.max_slience_time}")
|
2024-05-23 01:27:51 +08:00
|
|
|
|
|
|
|
|
|
print("Audio Process started.")
|
|
|
|
|
|
2024-05-23 16:07:23 +08:00
|
|
|
|
# create threads
|
|
|
|
|
threads = [threading.Thread(target=self.hardware_trigger_thread, args=(recorder,))]
|
|
|
|
|
if voice_trigger:
|
|
|
|
|
vioce_threads = [
|
|
|
|
|
threading.Thread(target=self.voice_trigger_thread, args=(recorder,)),
|
|
|
|
|
]
|
|
|
|
|
threads.extend(vioce_threads)
|
|
|
|
|
for thread in threads:
|
|
|
|
|
thread.start()
|
|
|
|
|
print("Audio Process started.")
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
for thread in threads:
|
|
|
|
|
thread.join()
|
|
|
|
|
print(f"audio process exit") ; exit()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def hardware_trigger_thread(self, recorder):
|
|
|
|
|
"""hardware_trigger_thread
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
recorder: takway.audio_utils.Recorder, recorder object
|
|
|
|
|
"""
|
|
|
|
|
print("Hardware trigger thread started.")
|
|
|
|
|
last_short_power_status = False
|
2024-05-23 20:53:40 +08:00
|
|
|
|
|
|
|
|
|
board = self.board
|
2024-05-23 16:07:23 +08:00
|
|
|
|
while True:
|
|
|
|
|
# 开关按键被按下
|
|
|
|
|
if recorder.hardware.long_power_status:
|
2024-05-23 17:25:22 +08:00
|
|
|
|
self.wakeup_event.set()
|
2024-05-23 16:07:23 +08:00
|
|
|
|
# 短时按键被按下,打断
|
2024-05-30 01:43:06 +08:00
|
|
|
|
if recorder.hardware.short_power_status and not last_short_power_status:
|
2024-06-18 17:24:37 +08:00
|
|
|
|
self.interrupt_event.set()
|
2024-05-30 01:43:06 +08:00
|
|
|
|
recorder.hardware.short_power_status = False
|
|
|
|
|
print("Interrupt conversation.")
|
|
|
|
|
last_short_power_status = recorder.hardware.short_power_status
|
2024-05-23 16:07:23 +08:00
|
|
|
|
else:
|
|
|
|
|
self.wakeup_event.clear()
|
2024-05-23 20:29:07 +08:00
|
|
|
|
time.sleep(0.01)
|
2024-05-23 16:07:23 +08:00
|
|
|
|
|
|
|
|
|
def voice_trigger_thread(self, recorder):
|
|
|
|
|
"""voice_trigger_thread
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
recorder: takway.audio_utils.Recorder, recorder object
|
|
|
|
|
"""
|
2024-05-23 16:15:18 +08:00
|
|
|
|
board = self.board
|
2024-05-23 01:27:51 +08:00
|
|
|
|
print("Waiting for wake up...")
|
2024-05-23 20:53:40 +08:00
|
|
|
|
|
2024-05-23 01:27:51 +08:00
|
|
|
|
while True:
|
|
|
|
|
data = recorder.record_chunk_voice(
|
|
|
|
|
CHUNK=recorder.porcupine.frame_length,
|
|
|
|
|
return_type=None,
|
|
|
|
|
exception_on_overflow=False,
|
|
|
|
|
queue=None)
|
|
|
|
|
|
|
|
|
|
record_chunk_size = recorder.vad_chunk_size
|
|
|
|
|
|
|
|
|
|
# 开关按键被按下或被关键词唤醒
|
2024-05-23 16:07:23 +08:00
|
|
|
|
if self.wakeup_event.is_set():
|
|
|
|
|
print(f"{datetime.now()}: Wake up by button.")
|
2024-05-23 01:27:51 +08:00
|
|
|
|
else:
|
2024-06-18 20:57:56 +08:00
|
|
|
|
if self.sleep_event.is_set():
|
|
|
|
|
print(f"{datetime.now()}: stay sleep mode.")
|
|
|
|
|
recorder.hardware.long_power_status = False
|
|
|
|
|
self.sleep_event.clear()
|
|
|
|
|
continue
|
2024-05-23 16:07:23 +08:00
|
|
|
|
if recorder.is_wakeup(data):
|
|
|
|
|
recorder.hardware.long_power_status = True
|
2024-05-24 20:59:32 +08:00
|
|
|
|
self.wakeup_event.set()
|
2024-05-23 16:07:23 +08:00
|
|
|
|
print(f"{datetime.now()}: wake up by voice.")
|
|
|
|
|
else:
|
2024-06-18 17:40:43 +08:00
|
|
|
|
recorder.hardware.long_power_status = False
|
2024-05-23 16:07:23 +08:00
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# 设置为倾听模式
|
|
|
|
|
self.listening_event.set()
|
|
|
|
|
self.speaking_event.clear()
|
|
|
|
|
|
2024-05-23 01:27:51 +08:00
|
|
|
|
# wake up
|
|
|
|
|
is_bgn = True
|
|
|
|
|
is_end = False
|
|
|
|
|
frames = []
|
|
|
|
|
# status buffer
|
2024-05-23 17:52:47 +08:00
|
|
|
|
single_chat_finish = False
|
2024-05-23 01:27:51 +08:00
|
|
|
|
slience_bgn_t = time.time()
|
|
|
|
|
slience_time = 0
|
|
|
|
|
print("Start recording...")
|
|
|
|
|
# 准备对话状态
|
|
|
|
|
while True:
|
2024-06-18 18:09:12 +08:00
|
|
|
|
if self.sleep_event.is_set():
|
|
|
|
|
print(f"{datetime.now()}: sleep mode.")
|
|
|
|
|
recorder.hardware.long_power_status = False
|
2024-06-18 18:10:22 +08:00
|
|
|
|
self.sleep_event.clear()
|
|
|
|
|
break
|
2024-06-18 18:09:12 +08:00
|
|
|
|
|
2024-05-23 16:07:23 +08:00
|
|
|
|
if not self.wakeup_event.is_set():
|
|
|
|
|
break
|
2024-05-23 01:27:51 +08:00
|
|
|
|
|
2024-05-23 16:07:23 +08:00
|
|
|
|
if self.listening_event.is_set():
|
|
|
|
|
# 语音活动检测
|
|
|
|
|
data = recorder.record_chunk_voice(
|
|
|
|
|
CHUNK=record_chunk_size,
|
|
|
|
|
return_type=None,
|
|
|
|
|
exception_on_overflow=False)
|
|
|
|
|
is_speech = recorder.is_speech(data)
|
|
|
|
|
|
|
|
|
|
# 判断状态
|
|
|
|
|
if is_speech:
|
2024-05-23 17:35:05 +08:00
|
|
|
|
# print(f"{datetime.now()}: valid voice")
|
2024-05-23 16:07:23 +08:00
|
|
|
|
slience_bgn_t = time.time()
|
2024-05-23 20:33:00 +08:00
|
|
|
|
frames.append(data)
|
2024-05-23 16:07:23 +08:00
|
|
|
|
|
2024-05-23 01:27:51 +08:00
|
|
|
|
slience_time = time.time() - slience_bgn_t
|
|
|
|
|
|
2024-05-23 16:07:23 +08:00
|
|
|
|
# 长时沉默关闭唤醒状态:如果唤醒后超过一定时间没有说话/关闭按键被按下,则认为是结束
|
|
|
|
|
if slience_time > recorder.max_slience_time:
|
|
|
|
|
recorder.hardware.long_power_status = False
|
2024-05-24 20:59:32 +08:00
|
|
|
|
self.wakeup_event.clear()
|
2024-05-23 16:07:23 +08:00
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# 短时沉默结束单次对话:沉默时间超过一定时间段(0.5s左右),则发送数据
|
|
|
|
|
if slience_time > recorder.min_act_time:
|
2024-05-23 17:52:47 +08:00
|
|
|
|
single_chat_finish = True
|
|
|
|
|
|
|
|
|
|
if single_chat_finish:
|
2024-05-23 16:07:23 +08:00
|
|
|
|
is_end = True
|
|
|
|
|
is_bgn = False
|
|
|
|
|
|
2024-05-23 20:29:07 +08:00
|
|
|
|
# print(f"{datetime.now()}: slience_time: {slience_time}")
|
2024-05-23 16:07:23 +08:00
|
|
|
|
|
|
|
|
|
# 流式发送数据
|
|
|
|
|
stream_reset_status = self.stream_record_process(
|
|
|
|
|
bytes_frames=recorder.write_wave_bytes(frames),
|
|
|
|
|
frames_size=len(frames),
|
|
|
|
|
record_chunk_size=record_chunk_size,
|
|
|
|
|
sample_rate=recorder.RATE,
|
|
|
|
|
min_stream_record_time=recorder.min_stream_record_time,
|
|
|
|
|
is_bgn=is_bgn,
|
|
|
|
|
is_end=is_end)
|
2024-05-23 20:29:07 +08:00
|
|
|
|
# print(f"stream_reset_status: {stream_reset_status}, is_bgn: {is_bgn}, is_end: {is_end}, frame: {len(frames)}")
|
2024-05-23 16:07:23 +08:00
|
|
|
|
if stream_reset_status:
|
|
|
|
|
frames.clear()
|
|
|
|
|
is_bgn = False
|
2024-05-23 17:52:47 +08:00
|
|
|
|
if single_chat_finish:
|
|
|
|
|
is_bgn = True
|
|
|
|
|
is_end = False
|
|
|
|
|
single_chat_finish = False
|
2024-05-23 20:29:07 +08:00
|
|
|
|
print(f"{datetime.now()}: single conversation finish, reset frames.")
|
2024-05-23 17:52:47 +08:00
|
|
|
|
|
2024-05-23 16:07:23 +08:00
|
|
|
|
elif self.speaking_event.is_set():
|
2024-06-18 17:48:59 +08:00
|
|
|
|
print(f"{datetime.now()}: wait for speaking close and listening start or sleep mode.")
|
2024-06-09 02:37:05 +08:00
|
|
|
|
if board == 'orangepi':
|
|
|
|
|
recorder.hardware.set_led_on("red")
|
2024-06-18 17:48:59 +08:00
|
|
|
|
while True:
|
2024-06-18 18:09:12 +08:00
|
|
|
|
# 睡眠 or 监听状态
|
|
|
|
|
if self.sleep_event.is_set() or self.listening_event.is_set():
|
2024-06-18 17:48:59 +08:00
|
|
|
|
break
|
2024-06-18 20:30:03 +08:00
|
|
|
|
if self.interrupt_event.is_set():
|
2024-06-18 20:32:30 +08:00
|
|
|
|
print(f"{datetime.now()}: button interrupt (input).")
|
2024-06-18 20:30:03 +08:00
|
|
|
|
break
|
2024-06-09 02:37:05 +08:00
|
|
|
|
if board == 'orangepi':
|
|
|
|
|
recorder.hardware.set_led_off("red")
|
2024-05-23 20:20:42 +08:00
|
|
|
|
# 重新计时
|
|
|
|
|
slience_bgn_t = time.time()
|
2024-06-18 20:50:36 +08:00
|
|
|
|
print(f"{datetime.now()}: restart listening.")
|
2024-05-23 01:27:51 +08:00
|
|
|
|
|
|
|
|
|
def stream_record_process(self,
|
|
|
|
|
bytes_frames: bytes,
|
|
|
|
|
frames_size: int,
|
|
|
|
|
record_chunk_size: int,
|
|
|
|
|
sample_rate: int,
|
|
|
|
|
min_stream_record_time: int,
|
|
|
|
|
is_bgn: bool,
|
|
|
|
|
is_end: bool):
|
|
|
|
|
'''
|
|
|
|
|
Args:
|
|
|
|
|
bytes_frames: bytes, audio data
|
|
|
|
|
frames_size: int, audio data size
|
|
|
|
|
record_chunk_size: int, audio data chunk size
|
|
|
|
|
is_bgn: bool, is begin of stream
|
|
|
|
|
is_end: bool, is end of stream
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool, if stream reset status
|
|
|
|
|
'''
|
|
|
|
|
if len(bytes_frames) == 0:
|
|
|
|
|
return False
|
|
|
|
|
if frames_size*record_chunk_size >= min_stream_record_time*sample_rate or is_end:
|
|
|
|
|
if is_bgn and is_end:
|
|
|
|
|
return False
|
|
|
|
|
stream_data = dict(
|
|
|
|
|
frames=bytes_frames,
|
|
|
|
|
frames_size=frames_size,
|
|
|
|
|
chunk_size=record_chunk_size,
|
|
|
|
|
is_bgn=is_bgn,
|
|
|
|
|
is_end=is_end)
|
|
|
|
|
self.client_queue.put(('audio', stream_data))
|
|
|
|
|
if is_end:
|
|
|
|
|
# print("put None to client queue.")
|
|
|
|
|
self.client_queue.put(None)
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def web_socket_client_process(self):
|
|
|
|
|
|
|
|
|
|
client = BaseWebSocketClient(self.server_args['server_url'], self.server_args['session_id'])
|
|
|
|
|
print("Web socket client process started.")
|
|
|
|
|
# print("Web socket client process started.")
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
if self.client_queue.empty():
|
|
|
|
|
continue
|
2024-05-23 16:07:23 +08:00
|
|
|
|
print(f"{datetime.now()}: start setup web socket connection.")
|
2024-05-23 17:09:57 +08:00
|
|
|
|
|
|
|
|
|
# 第一级:唤醒状态下,连接服务器
|
|
|
|
|
if self.wakeup_event.is_set():
|
2024-05-23 16:07:23 +08:00
|
|
|
|
client.wakeup_client()
|
|
|
|
|
else:
|
|
|
|
|
print(f"not wake up, skip setup web socket connection.")
|
|
|
|
|
self.client_queue.get(block=False)
|
|
|
|
|
continue
|
2024-05-23 01:27:51 +08:00
|
|
|
|
|
2024-05-23 18:03:09 +08:00
|
|
|
|
# 播放状态下,不连接服务器
|
|
|
|
|
if self.speaking_event.is_set():
|
|
|
|
|
print(f"speaking, skip setup web socket connection.")
|
|
|
|
|
self.client_queue.get(block=False)
|
|
|
|
|
continue
|
|
|
|
|
|
2024-05-23 01:27:51 +08:00
|
|
|
|
# 发送数据
|
|
|
|
|
for queue_data in QueueIterator(self.client_queue):
|
2024-05-23 16:07:23 +08:00
|
|
|
|
# 发送音频数据
|
2024-05-23 01:27:51 +08:00
|
|
|
|
if queue_data[0] == 'audio':
|
2024-05-23 17:52:47 +08:00
|
|
|
|
|
|
|
|
|
# 当唤醒状态被关闭时,退出循环
|
|
|
|
|
if not self.wakeup_event.is_set():
|
|
|
|
|
self.listening_event.clear()
|
|
|
|
|
self.speaking_event.clear()
|
|
|
|
|
client.close_client()
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# 播放时不得发送数据,默认废消息
|
|
|
|
|
if self.speaking_event.is_set() and not self.listening_event.is_set():
|
|
|
|
|
client.close_client()
|
|
|
|
|
break
|
|
|
|
|
|
2024-05-23 01:27:51 +08:00
|
|
|
|
audio_dict = queue_data[1]
|
|
|
|
|
|
|
|
|
|
client.send_per_data(
|
|
|
|
|
audio=audio_dict['frames'],
|
|
|
|
|
stream=True,
|
|
|
|
|
voice_synthesize=True,
|
|
|
|
|
is_end=audio_dict['is_end'],
|
|
|
|
|
encoding='base64',
|
|
|
|
|
)
|
2024-05-23 17:52:47 +08:00
|
|
|
|
# is_end后切换播放模式
|
|
|
|
|
if audio_dict['is_end']:
|
|
|
|
|
self.listening_event.clear()
|
|
|
|
|
self.speaking_event.set()
|
2024-05-23 01:27:51 +08:00
|
|
|
|
|
2024-05-23 17:09:57 +08:00
|
|
|
|
if not self.wakeup_event.is_set():
|
2024-05-23 16:07:23 +08:00
|
|
|
|
continue
|
|
|
|
|
|
2024-05-23 01:27:51 +08:00
|
|
|
|
# 接收数据
|
|
|
|
|
while True:
|
2024-05-23 17:09:57 +08:00
|
|
|
|
# 当唤醒状态被关闭时,退出循环
|
|
|
|
|
if not self.wakeup_event.is_set():
|
|
|
|
|
self.listening_event.clear()
|
|
|
|
|
self.speaking_event.clear()
|
|
|
|
|
client.close_client()
|
|
|
|
|
break
|
|
|
|
|
|
2024-06-18 17:31:00 +08:00
|
|
|
|
if self.interrupt_event.is_set():
|
2024-06-18 20:43:50 +08:00
|
|
|
|
self.listening_event.set()
|
|
|
|
|
self.speaking_event.clear()
|
|
|
|
|
self.interrupt_event.clear()
|
2024-06-18 17:31:00 +08:00
|
|
|
|
break
|
|
|
|
|
|
2024-05-23 01:27:51 +08:00
|
|
|
|
response, data_type = client.receive_per_data()
|
|
|
|
|
if data_type == dict:
|
2024-05-23 16:07:23 +08:00
|
|
|
|
print(f"{datetime.now()}: receive json data: {response}") # 打印接收到的消息
|
2024-06-18 17:03:00 +08:00
|
|
|
|
# 5xx: 结束错误码处理
|
|
|
|
|
if response['code'] in [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545]:
|
|
|
|
|
# 恢复录音状态
|
2024-05-23 17:15:25 +08:00
|
|
|
|
self.listening_event.set()
|
|
|
|
|
self.speaking_event.clear()
|
|
|
|
|
break
|
2024-06-18 17:03:00 +08:00
|
|
|
|
# 200: 正常结束
|
2024-05-23 17:15:25 +08:00
|
|
|
|
elif response['code'] == 200:
|
2024-06-18 17:12:02 +08:00
|
|
|
|
self.audio_play_queue.put(('audio_json', response))
|
2024-05-23 18:03:09 +08:00
|
|
|
|
if response['type'] == 'close':
|
|
|
|
|
break
|
2024-06-18 17:03:00 +08:00
|
|
|
|
# 201: 切换沉默模式
|
|
|
|
|
elif response['code'] == 201:
|
2024-06-18 17:42:07 +08:00
|
|
|
|
self.listening_event.clear()
|
2024-06-18 17:03:00 +08:00
|
|
|
|
self.speaking_event.clear()
|
2024-06-18 18:09:12 +08:00
|
|
|
|
self.sleep_event.set() # 沉默状态下,关闭唤醒状态
|
|
|
|
|
self.wakeup_event.clear()
|
2024-05-23 20:00:40 +08:00
|
|
|
|
elif data_type == list:
|
2024-06-18 17:03:00 +08:00
|
|
|
|
print(f"{datetime.now()}: receive text_audio_data")
|
|
|
|
|
# 切换播放模式
|
|
|
|
|
self.listening_event.clear()
|
|
|
|
|
self.speaking_event.set()
|
|
|
|
|
self.audio_play_queue.put(('text_audio_data', response))
|
2024-05-23 20:00:40 +08:00
|
|
|
|
|
2024-05-23 01:27:51 +08:00
|
|
|
|
elif data_type == bytes:
|
2024-05-23 16:07:23 +08:00
|
|
|
|
# 开始播放
|
2024-06-18 17:03:00 +08:00
|
|
|
|
print(f"{datetime.now()}: receive audio bytes")
|
2024-05-23 01:27:51 +08:00
|
|
|
|
self.audio_play_queue.put(('audio_bytes', response))
|
|
|
|
|
elif data_type == None:
|
2024-05-23 16:07:23 +08:00
|
|
|
|
print(f"{datetime.now()}: receive None data, break loop.")
|
2024-06-18 17:03:00 +08:00
|
|
|
|
# 切换录音模式
|
2024-06-18 16:10:11 +08:00
|
|
|
|
self.listening_event.set()
|
|
|
|
|
self.speaking_event.clear()
|
2024-05-23 20:00:40 +08:00
|
|
|
|
# print(f"listening_event: {self.listening_event.is_set()}, speaking_event: {self.speaking_event.is_set()}")
|
2024-05-23 01:27:51 +08:00
|
|
|
|
break # 如果没有接收到消息,则退出循环
|
2024-06-18 17:14:01 +08:00
|
|
|
|
client.close_client()
|
2024-06-18 20:30:03 +08:00
|
|
|
|
print(f"{datetime.now()}: web socket client closed.")
|
2024-05-23 01:27:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def audio_play_process(self):
|
|
|
|
|
'''
|
|
|
|
|
Args:
|
|
|
|
|
audio_play_queue: multiprocessing.Queue, audio play queue
|
|
|
|
|
share_time_dict: multiprocessing.Manager.dict, shared time dict
|
|
|
|
|
'''
|
|
|
|
|
audio_player = AudioPlayer(**self.player_args)
|
|
|
|
|
print("Audio play process started.")
|
2024-05-23 16:07:23 +08:00
|
|
|
|
audio_list = []
|
2024-05-23 01:27:51 +08:00
|
|
|
|
while True:
|
2024-06-18 17:45:59 +08:00
|
|
|
|
print("wait for audio data.")
|
2024-05-23 01:27:51 +08:00
|
|
|
|
item = self.audio_play_queue.get()
|
2024-05-23 16:07:23 +08:00
|
|
|
|
|
|
|
|
|
# 唤醒状态
|
|
|
|
|
if not self.wakeup_event.is_set():
|
|
|
|
|
continue
|
|
|
|
|
|
2024-06-18 17:03:00 +08:00
|
|
|
|
if item[0] == 'text_audio_data':
|
2024-05-23 20:00:40 +08:00
|
|
|
|
# TODO: 判断bytes是否是最后一个,如果是最后一个,则播放完毕,切换监听模式
|
2024-06-18 17:03:00 +08:00
|
|
|
|
audio_text, audio_data = item[1]
|
2024-05-23 16:07:23 +08:00
|
|
|
|
|
2024-05-23 20:00:40 +08:00
|
|
|
|
print(f"{datetime.now()}: start playing audio.")
|
2024-06-18 17:03:00 +08:00
|
|
|
|
print(f"{datetime.now()}: audio text: {audio_text}")
|
2024-05-23 16:07:23 +08:00
|
|
|
|
|
2024-05-23 20:00:40 +08:00
|
|
|
|
if self.listening_event.is_set():
|
2024-05-23 16:07:23 +08:00
|
|
|
|
continue
|
2024-05-23 20:00:40 +08:00
|
|
|
|
|
|
|
|
|
if not self.speaking_event.is_set():
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
audio_list.append(audio_data)
|
|
|
|
|
|
|
|
|
|
# 播放音频
|
|
|
|
|
try:
|
|
|
|
|
tts_audio = audio_list[0] # 取出第一个音频
|
|
|
|
|
tts_audio = audio_player.check_audio_type(tts_audio, return_type=None)
|
|
|
|
|
for i in range(0, len(tts_audio), audio_player.CHUNK):
|
|
|
|
|
audio_player.stream.write(tts_audio[i:i+audio_player.CHUNK])
|
2024-05-24 22:58:16 +08:00
|
|
|
|
# print("Playing {} data...{}/{}".format(item[0], i, len(tts_audio)))
|
2024-05-23 20:00:40 +08:00
|
|
|
|
|
|
|
|
|
# 关闭状态
|
|
|
|
|
if not self.wakeup_event.is_set():
|
2024-06-18 17:24:37 +08:00
|
|
|
|
print(f"{datetime.now()}: microphone and speaker close.")
|
2024-05-23 20:00:40 +08:00
|
|
|
|
self.listening_event.clear()
|
|
|
|
|
self.speaking_event.clear()
|
2024-06-18 20:50:36 +08:00
|
|
|
|
self.sleep_event.set() # 沉默状态下,关闭唤醒状态
|
2024-05-23 20:00:40 +08:00
|
|
|
|
break
|
2024-05-24 22:45:24 +08:00
|
|
|
|
# 按键打断播放
|
2024-06-18 17:24:37 +08:00
|
|
|
|
if self.interrupt_event.is_set():
|
2024-06-18 20:32:30 +08:00
|
|
|
|
print(f"{datetime.now()}: button interrupt (output).")
|
2024-06-18 17:45:19 +08:00
|
|
|
|
|
2024-06-18 20:16:57 +08:00
|
|
|
|
time.sleep(0.3) # 虽然程序到这里,但是扬声器不一定播放完毕,延迟用来避免问题
|
2024-06-18 17:45:19 +08:00
|
|
|
|
|
|
|
|
|
while not self.audio_play_queue.empty(): # 清空队列
|
2024-06-18 20:09:41 +08:00
|
|
|
|
self.audio_play_queue.get()
|
2024-06-18 17:24:37 +08:00
|
|
|
|
self.listening_event.set()
|
|
|
|
|
self.speaking_event.clear()
|
2024-06-18 17:38:01 +08:00
|
|
|
|
self.interrupt_event.clear()
|
2024-05-24 22:45:24 +08:00
|
|
|
|
break
|
2024-05-23 20:00:40 +08:00
|
|
|
|
|
2024-06-18 17:38:01 +08:00
|
|
|
|
if not self.speaking_event.is_set():
|
2024-05-23 20:00:40 +08:00
|
|
|
|
audio_list = [] # 清空音频列表
|
2024-06-18 17:38:01 +08:00
|
|
|
|
print(f"{datetime.now()}: audio data clear.")
|
2024-05-23 20:00:40 +08:00
|
|
|
|
continue
|
2024-06-18 17:38:01 +08:00
|
|
|
|
else:
|
|
|
|
|
# 播放最后一段音频
|
|
|
|
|
audio_player.stream.write(tts_audio[i+audio_player.CHUNK:])
|
2024-06-18 17:56:32 +08:00
|
|
|
|
audio_list = [] # 清空音频列表
|
|
|
|
|
# audio_list.pop(0) # 弹出已播放音频
|
2024-06-18 17:38:01 +08:00
|
|
|
|
print(f"{datetime.now()}: audio data played.")
|
2024-05-23 20:00:40 +08:00
|
|
|
|
except TypeError as e:
|
|
|
|
|
print(f"audio play error: {e}")
|
|
|
|
|
continue
|
2024-05-23 20:09:52 +08:00
|
|
|
|
|
|
|
|
|
elif item[0] == 'audio_json':
|
2024-05-23 20:13:39 +08:00
|
|
|
|
data_type = item[1]['type']
|
2024-05-23 20:07:40 +08:00
|
|
|
|
print(f"data_type: {data_type}")
|
2024-05-23 20:00:40 +08:00
|
|
|
|
if self.wakeup_event.is_set():
|
2024-05-23 20:09:52 +08:00
|
|
|
|
if data_type == 'text':
|
2024-06-18 17:56:32 +08:00
|
|
|
|
if self.wakeup_event.is_set():
|
|
|
|
|
# 切换播放模式
|
|
|
|
|
self.listening_event.clear()
|
|
|
|
|
self.speaking_event.set()
|
2024-05-23 20:09:52 +08:00
|
|
|
|
if data_type in ['close', 'end']:
|
2024-05-23 20:22:22 +08:00
|
|
|
|
# 虽然程序到这里,但是扬声器不一定播放完毕,延迟用来避免问题
|
2024-06-18 20:16:57 +08:00
|
|
|
|
time.sleep(0.8)
|
2024-06-18 17:56:32 +08:00
|
|
|
|
if self.wakeup_event.is_set():
|
|
|
|
|
# 启动监听状态
|
|
|
|
|
self.speaking_event.clear()
|
|
|
|
|
self.listening_event.set()
|
2024-05-23 16:07:23 +08:00
|
|
|
|
|
2024-05-23 01:27:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def excute_process(self):
|
|
|
|
|
'''
|
|
|
|
|
Args:
|
|
|
|
|
excute_queue: multiprocessing.Queue, excute display queue
|
|
|
|
|
'''
|
|
|
|
|
print("Excute process started.")
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
if self.excute_queue.empty():
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
if self.speaker_active_set.is_set():
|
|
|
|
|
instruct, content = self.excute_queue.get()
|
|
|
|
|
|
|
|
|
|
print(f"Got speaker info: {instruct, content}")
|
|
|
|
|
|
|
|
|
|
print(f"Playing {instruct} {content}...")
|
|
|
|
|
print(f"play {instruct} time: {datetime.now()}")
|
|
|
|
|
self.audio_play_queue.put((instruct, content))
|
|
|
|
|
|
|
|
|
|
self.speaker_active_set.clear()
|
|
|
|
|
|