""" 语音识别器模块 负责实时语音识别功能 """ 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(' 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}")