EG/plugins/user/voice_control/recognition/speech_recognizer.py
2025-12-12 16:16:15 +08:00

707 lines
24 KiB
Python

"""
语音识别器模块
负责实时语音识别功能
"""
import time
import threading
import queue
import math
from typing import Dict, Any, List, Optional
class SpeechRecognizer:
"""
语音识别器
负责实时语音识别功能
"""
def __init__(self, plugin):
"""
初始化语音识别器
Args:
plugin: 语音控制插件实例
"""
self.plugin = plugin
self.enabled = False
self.initialized = False
# 音频输入配置
self.audio_config = {
'sample_rate': 16000,
'channels': 1,
'format': 'int16',
'chunk_size': 1024,
'buffer_size': 4096
}
# 识别配置
self.recognition_config = {
'language': 'zh-CN',
'enable_keyword_spotting': True,
'enable_continuous_recognition': True,
'silence_threshold': 0.01,
'speech_threshold': 0.02,
'silence_duration': 1.0,
'max_recording_duration': 10.0
}
# 音频缓冲区
self.audio_buffer = queue.Queue(maxsize=100)
self.processed_buffer = queue.Queue(maxsize=10)
# 识别状态
self.recognition_state = {
'is_recording': False,
'is_processing': False,
'is_speech_detected': False,
'last_speech_time': 0.0,
'recording_start_time': 0.0,
'audio_level': 0.0
}
# 麦克风管理
self.microphone = None
self.audio_stream = None
# 线程管理
self.recording_thread = None
self.processing_thread = None
self.running = False
# 统计信息
self.stats = {
'audio_chunks_processed': 0,
'speech_segments_detected': 0,
'recognition_attempts': 0,
'successful_recognitions': 0,
'failed_recognitions': 0,
'processing_time_total': 0.0
}
# 回调函数
self.recognition_callbacks = {
'audio_chunk_received': [],
'speech_detected': [],
'silence_detected': [],
'recognition_result': [],
'recognition_error': []
}
# 时间戳记录
self.last_processing_time = 0.0
self.last_recognition_time = 0.0
print("✓ 语音识别器已创建")
def initialize(self) -> bool:
"""
初始化语音识别器
Returns:
是否初始化成功
"""
try:
# 初始化音频输入设备
self._initialize_audio_input()
self.initialized = True
print("✓ 语音识别器初始化完成")
return True
except Exception as e:
print(f"✗ 语音识别器初始化失败: {e}")
import traceback
traceback.print_exc()
return False
def enable(self) -> bool:
"""
启用语音识别器
Returns:
是否启用成功
"""
try:
if not self.initialized:
print("✗ 语音识别器未初始化")
return False
self.enabled = True
self.running = True
# 启动录音线程
self.recording_thread = threading.Thread(target=self._recording_loop, daemon=True)
self.recording_thread.start()
# 启动处理线程
self.processing_thread = threading.Thread(target=self._processing_loop, daemon=True)
self.processing_thread.start()
print("✓ 语音识别器已启用")
return True
except Exception as e:
print(f"✗ 语音识别器启用失败: {e}")
import traceback
traceback.print_exc()
return False
def disable(self):
"""禁用语音识别器"""
try:
self.enabled = False
self.running = False
# 等待线程结束
if self.recording_thread and self.recording_thread.is_alive():
self.recording_thread.join(timeout=2.0)
if self.processing_thread and self.processing_thread.is_alive():
self.processing_thread.join(timeout=2.0)
# 停止音频流
self._stop_audio_stream()
print("✓ 语音识别器已禁用")
except Exception as e:
print(f"✗ 语音识别器禁用失败: {e}")
import traceback
traceback.print_exc()
def finalize(self):
"""清理语音识别器资源"""
try:
self.disable()
self.recognition_callbacks.clear()
# 清理音频缓冲区
while not self.audio_buffer.empty():
try:
self.audio_buffer.get_nowait()
except queue.Empty:
break
while not self.processed_buffer.empty():
try:
self.processed_buffer.get_nowait()
except queue.Empty:
break
self.initialized = False
print("✓ 语音识别器资源已清理")
except Exception as e:
print(f"✗ 语音识别器资源清理失败: {e}")
import traceback
traceback.print_exc()
def update(self, dt: float):
"""
更新语音识别器状态
Args:
dt: 时间增量
"""
try:
if not self.enabled:
return
current_time = time.time()
self.last_processing_time = current_time
except Exception as e:
print(f"✗ 语音识别器更新失败: {e}")
import traceback
traceback.print_exc()
def _initialize_audio_input(self):
"""初始化音频输入设备"""
try:
# 这里应该初始化实际的音频输入设备
# 由于这是一个模拟实现,我们只打印信息
print("✓ 音频输入设备初始化完成")
print(f" 采样率: {self.audio_config['sample_rate']} Hz")
print(f" 声道数: {self.audio_config['channels']}")
print(f" 格式: {self.audio_config['format']}")
except Exception as e:
print(f"✗ 音频输入设备初始化失败: {e}")
def _start_audio_stream(self) -> bool:
"""
启动音频流
Returns:
是否启动成功
"""
try:
# 这里应该启动实际的音频流
# 由于这是一个模拟实现,我们只设置状态
self.audio_stream = "simulated_audio_stream"
print("✓ 音频流已启动")
return True
except Exception as e:
print(f"✗ 音频流启动失败: {e}")
return False
def _stop_audio_stream(self):
"""停止音频流"""
try:
# 这里应该停止实际的音频流
self.audio_stream = None
print("✓ 音频流已停止")
except Exception as e:
print(f"✗ 音频流停止失败: {e}")
def _recording_loop(self):
"""录音循环线程"""
try:
# 启动音频流
if not self._start_audio_stream():
return
chunk_duration = self.audio_config['chunk_size'] / self.audio_config['sample_rate']
while self.running and self.enabled:
try:
# 模拟读取音频数据
audio_chunk = self._read_audio_chunk()
if audio_chunk is not None:
# 计算音频级别
audio_level = self._calculate_audio_level(audio_chunk)
# 更新状态
self.recognition_state['audio_level'] = audio_level
# 检测语音活动
is_speech = audio_level > self.recognition_config['speech_threshold']
was_speech = self.recognition_state['is_speech_detected']
# 更新语音检测状态
self.recognition_state['is_speech_detected'] = is_speech
if is_speech:
self.recognition_state['last_speech_time'] = time.time()
# 触发回调
self._trigger_recognition_callback('audio_chunk_received', {
'audio_chunk': audio_chunk,
'audio_level': audio_level,
'is_speech': is_speech
})
# 检测语音/静音转换
if is_speech and not was_speech:
self._trigger_recognition_callback('speech_detected', {
'audio_level': audio_level
})
elif not is_speech and was_speech:
self._trigger_recognition_callback('silence_detected', {
'audio_level': audio_level
})
# 添加到缓冲区
try:
self.audio_buffer.put_nowait({
'data': audio_chunk,
'level': audio_level,
'timestamp': time.time()
})
self.stats['audio_chunks_processed'] += 1
except queue.Full:
# 缓冲区满,丢弃最旧的数据
try:
self.audio_buffer.get_nowait()
self.audio_buffer.put_nowait({
'data': audio_chunk,
'level': audio_level,
'timestamp': time.time()
})
except queue.Empty:
pass
# 控制录音频率
time.sleep(chunk_duration)
except Exception as e:
print(f"✗ 录音循环错误: {e}")
time.sleep(0.1)
except Exception as e:
print(f"✗ 录音循环失败: {e}")
def _processing_loop(self):
"""处理循环线程"""
try:
while self.running and self.enabled:
try:
# 从缓冲区获取音频数据
if not self.audio_buffer.empty():
audio_data = self.audio_buffer.get_nowait()
self._process_audio_chunk(audio_data)
# 控制处理频率
time.sleep(0.01)
except queue.Empty:
time.sleep(0.01)
except Exception as e:
print(f"✗ 处理循环错误: {e}")
time.sleep(0.1)
except Exception as e:
print(f"✗ 处理循环失败: {e}")
def _read_audio_chunk(self) -> Optional[bytes]:
"""
读取音频块
Returns:
音频数据或None
"""
try:
# 模拟生成音频数据
import random
import struct
chunk_size = self.audio_config['chunk_size']
audio_data = bytearray()
for _ in range(chunk_size):
# 生成模拟的音频样本
sample = int(random.randint(-32768, 32767) * 0.1)
audio_data.extend(struct.pack('<h', sample))
return bytes(audio_data)
except Exception as e:
print(f"✗ 音频块读取失败: {e}")
return None
def _calculate_audio_level(self, audio_chunk: bytes) -> float:
"""
计算音频级别
Args:
audio_chunk: 音频数据块
Returns:
音频级别(0.0-1.0)
"""
try:
import struct
# 解析音频数据
samples = struct.unpack(f'<{len(audio_chunk)//2}h', audio_chunk)
# 计算RMS
if len(samples) > 0:
rms = math.sqrt(sum(sample**2 for sample in samples) / len(samples))
# 归一化到0-1范围(假设16位音频)
level = rms / 32768.0
return min(1.0, level)
else:
return 0.0
except Exception as e:
print(f"✗ 音频级别计算失败: {e}")
return 0.0
def _process_audio_chunk(self, audio_data: Dict[str, Any]):
"""
处理音频块
Args:
audio_data: 音频数据字典
"""
try:
start_time = time.time()
# 模拟语音识别处理
processing_result = self._simulate_recognition(audio_data)
if processing_result:
# 添加到处理结果缓冲区
try:
self.processed_buffer.put_nowait(processing_result)
except queue.Full:
try:
self.processed_buffer.get_nowait()
self.processed_buffer.put_nowait(processing_result)
except queue.Empty:
pass
# 触发识别结果回调
self._trigger_recognition_callback('recognition_result', processing_result)
# 传递给语音管理器处理
if self.plugin.voice_manager:
self.plugin.voice_manager.process_speech(
processing_result['text'],
processing_result['confidence']
)
self.stats['successful_recognitions'] += 1
# 更新统计信息
processing_time = time.time() - start_time
self.stats['processing_time_total'] += processing_time
self.stats['recognition_attempts'] += 1
self.last_recognition_time = time.time()
except Exception as e:
print(f"✗ 音频块处理失败: {e}")
self.stats['failed_recognitions'] += 1
# 触发错误回调
self._trigger_recognition_callback('recognition_error', {
'error': str(e),
'timestamp': time.time()
})
def _simulate_recognition(self, audio_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""
模拟语音识别
Args:
audio_data: 音频数据
Returns:
识别结果或None
"""
try:
# 基于音频级别和时间模拟识别结果
audio_level = audio_data.get('level', 0.0)
timestamp = audio_data.get('timestamp', time.time())
# 只有在音频级别足够高时才模拟识别
if audio_level > self.recognition_config['speech_threshold']:
import random
# 模拟识别文本
sample_texts = [
"前进", "后退", "左转", "右转", "停止",
"打开灯光", "关闭灯光", "增加音量", "减少音量",
"播放音乐", "暂停播放", "下一首", "上一首",
"你好", "谢谢", "再见", "帮助"
]
# 根据音频级别设置置信度
confidence = min(1.0, audio_level * 2) # 简单映射
# 有一定概率生成识别结果
if random.random() < 0.1: # 10%概率
text = random.choice(sample_texts)
return {
'text': text,
'confidence': confidence,
'timestamp': timestamp,
'audio_level': audio_level
}
return None
except Exception as e:
print(f"✗ 识别模拟失败: {e}")
return None
def start_recording(self) -> bool:
"""
开始录音
Returns:
是否开始成功
"""
try:
if not self.enabled:
print("✗ 语音识别器未启用")
return False
self.recognition_state['is_recording'] = True
self.recognition_state['recording_start_time'] = time.time()
print("✓ 录音已开始")
return True
except Exception as e:
print(f"✗ 录音开始失败: {e}")
return False
def stop_recording(self) -> bool:
"""
停止录音
Returns:
是否停止成功
"""
try:
self.recognition_state['is_recording'] = False
print("✓ 录音已停止")
return True
except Exception as e:
print(f"✗ 录音停止失败: {e}")
return False
def is_recording(self) -> bool:
"""
检查是否正在录音
Returns:
是否正在录音
"""
return self.recognition_state['is_recording']
def is_speech_detected(self) -> bool:
"""
检查是否检测到语音
Returns:
是否检测到语音
"""
return self.recognition_state['is_speech_detected']
def get_audio_level(self) -> float:
"""
获取当前音频级别
Returns:
音频级别(0.0-1.0)
"""
return self.recognition_state['audio_level']
def set_recognition_config(self, config: Dict[str, Any]) -> bool:
"""
设置识别配置
Args:
config: 配置字典
Returns:
是否设置成功
"""
try:
self.recognition_config.update(config)
print(f"✓ 识别配置已更新: {self.recognition_config}")
return True
except Exception as e:
print(f"✗ 识别配置设置失败: {e}")
return False
def get_recognition_config(self) -> Dict[str, Any]:
"""
获取识别配置
Returns:
配置字典
"""
return self.recognition_config.copy()
def set_audio_config(self, config: Dict[str, Any]) -> bool:
"""
设置音频配置
Args:
config: 配置字典
Returns:
是否设置成功
"""
try:
self.audio_config.update(config)
print(f"✓ 音频配置已更新: {self.audio_config}")
return True
except Exception as e:
print(f"✗ 音频配置设置失败: {e}")
return False
def get_audio_config(self) -> Dict[str, Any]:
"""
获取音频配置
Returns:
配置字典
"""
return self.audio_config.copy()
def _trigger_recognition_callback(self, callback_type: str, data: Dict[str, Any]):
"""
触发识别回调
Args:
callback_type: 回调类型
data: 回调数据
"""
try:
if callback_type in self.recognition_callbacks:
for callback in self.recognition_callbacks[callback_type]:
try:
callback(data)
except Exception as e:
print(f"✗ 识别回调执行失败: {e}")
except Exception as e:
print(f"✗ 识别回调触发失败: {e}")
def register_recognition_callback(self, callback_type: str, callback: callable):
"""
注册识别回调
Args:
callback_type: 回调类型
callback: 回调函数
"""
try:
if callback_type in self.recognition_callbacks:
self.recognition_callbacks[callback_type].append(callback)
print(f"✓ 识别回调已注册: {callback_type}")
else:
print(f"✗ 无效的回调类型: {callback_type}")
except Exception as e:
print(f"✗ 识别回调注册失败: {e}")
def unregister_recognition_callback(self, callback_type: str, callback: callable):
"""
注销识别回调
Args:
callback_type: 回调类型
callback: 回调函数
"""
try:
if callback_type in self.recognition_callbacks:
if callback in self.recognition_callbacks[callback_type]:
self.recognition_callbacks[callback_type].remove(callback)
print(f"✓ 识别回调已注销: {callback_type}")
except Exception as e:
print(f"✗ 识别回调注销失败: {e}")
def get_stats(self) -> Dict[str, Any]:
"""
获取统计信息
Returns:
统计信息字典
"""
stats = self.stats.copy()
if self.stats['successful_recognitions'] > 0:
stats['average_processing_time'] = (
self.stats['processing_time_total'] / self.stats['successful_recognitions']
)
else:
stats['average_processing_time'] = 0.0
return stats
def reset_stats(self):
"""重置统计信息"""
try:
self.stats = {
'audio_chunks_processed': 0,
'speech_segments_detected': 0,
'recognition_attempts': 0,
'successful_recognitions': 0,
'failed_recognitions': 0,
'processing_time_total': 0.0
}
print("✓ 语音识别器统计信息已重置")
except Exception as e:
print(f"✗ 语音识别器统计信息重置失败: {e}")