# 语音唤醒模块

基于 sherpa-onnx 实现的独立语音唤醒模块，从 py-xiaozhi 项目中提取而来，支持AEC回音消除。

## 功能特点

- ✅ 基于 sherpa-onnx 的高性能唤醒词检测
- ✅ 异步音频采集和处理
- ✅ 可配置的检测参数（阈值、灵敏度等）
- ✅ 防重复触发机制
- ✅ 简洁的 API 接口
- ✅ 完全独立，无外部依赖
- ✅ **支持AEC回音消除**（Windows/Linux）

## 安装依赖

```bash
pip install -r requirements.txt
```

## 模型文件准备

在使用前，需要准备 sherpa-onnx 模型文件。确保以下文件存在于 `models/` 目录中：

- `encoder.onnx` - 编码器模型
- `decoder.onnx` - 解码器模型
- `joiner.onnx` - 连接器模型
- `tokens.txt` - 词汇表文件
- `keywords.txt` - 唤醒词配置文件

## 快速开始

### 1. 基本使用（不启用AEC）

```python
import asyncio
from wake_word_module import WakeWordDetector, AudioCapture, WakeWordConfig

async def main():
    # 创建配置
    config = WakeWordConfig(
        model_path="models",
        sample_rate=16000,
        keywords_threshold=0.2,
    )

    # 创建音频采集器（不启用AEC）
    audio_capture = AudioCapture(sample_rate=16000, channels=1, enable_aec=False)

    # 创建检测器
    detector = WakeWordDetector(config)

    # 设置回调
    def on_detected(result, full_text):
        print(f"检测到唤醒词: {result}")

    detector.on_detected(on_detected)

    # 连接并启动
    audio_capture.add_audio_listener(detector)
    await audio_capture.start()
    await detector.start()

    # 保持运行
    while True:
        await asyncio.sleep(1)

asyncio.run(main())
```

### 2. 启用AEC回音消除

```python
import asyncio
from wake_word_module import WakeWordDetector, AudioCapture, WakeWordConfig

async def main():
    # 创建配置
    config = WakeWordConfig(
        model_path="models",
        sample_rate=16000,
    )

    # 创建音频采集器（启用AEC）
    audio_capture = AudioCapture(
        sample_rate=16000,
        channels=1,
        enable_aec=True,  # 启用AEC回音消除
    )

    # 创建检测器
    detector = WakeWordDetector(config)

    # 设置回调
    detector.on_detected(lambda r, t: print(f"检测到唤醒词: {r}"))

    # 连接并启动
    audio_capture.add_audio_listener(detector)
    await audio_capture.start()
    await detector.start()

    # 保持运行
    while True:
        await asyncio.sleep(1)

asyncio.run(main())
```

**注意**: Windows和Linux平台会自动使用系统级AEC，无需额外配置。详见 [AEC_GUIDE.md](AEC_GUIDE.md)

### 3. 运行示例

```bash
# 使用AEC运行示例
python example.py

# 不使用AEC运行示例
python example.py --no-aec
```

## API 文档

### WakeWordConfig

语音唤醒配置类。

**参数:**
- `model_path` (str): 模型文件目录路径
- `sample_rate` (int): 音频采样率，默认 16000
- `num_threads` (int): 线程数，默认 4
- `provider` (str): 计算提供者 (cpu/cuda)，默认 "cpu"
- `max_active_paths` (int): 最大激活路径数，默认 2
- `keywords_score` (float): 关键词分数，默认 1.8
- `keywords_threshold` (float): 关键词阈值，默认 0.2
- `num_trailing_blanks` (int): 尾部空白数，默认 1
- `detection_cooldown` (float): 检测冷却时间(秒)，默认 1.5

### AudioCapture

音频采集器。

**参数:**
- `sample_rate` (int): 采样率，默认 16000
- `channels` (int): 声道数，默认 1
- `device_id` (int): 设备ID，None表示自动选择
- `enable_aec` (bool): 是否启用AEC回音消除，默认 False

**方法:**
- `start()`: 启动音频采集
- `stop()`: 停止音频采集
- `add_audio_listener(listener)`: 添加音频监听器
- `remove_audio_listener(listener)`: 移除音频监听器
- `list_devices()`: 列出所有可用设备（静态方法）

**AEC支持:**
- Windows/Linux: 自动使用系统级AEC，无需额外配置
- 其他平台: 不支持AEC功能

### WakeWordDetector

唤醒词检测器。

**方法:**
- `start()`: 启动检测器
- `stop()`: 停止检测器
- `pause()`: 暂停检测
- `resume()`: 恢复检测
- `on_detected(callback)`: 设置检测回调
- `on_error(callback)`: 设置错误回调
- `on_audio_data(audio_data)`: 接收音频数据（AudioListener接口）

## 配置说明

### 检测阈值调整

- `keywords_threshold`: 控制检测灵敏度
  - 值越小，越容易触发（可能误报）
  - 值越大，越难触发（可能漏报）
  - 推荐范围: 0.1 - 0.5

- `keywords_score`: 控制检测分数
  - 值越小，响应越快（可能降低准确率）
  - 值越大，越准确（可能增加延迟）
  - 推荐范围: 1.0 - 3.0

### 冷却时间

- `detection_cooldown`: 防止重复触发的时间间隔
  - 默认 1.5 秒
  - 可根据需要调整

## 注意事项

1. **模型文件**: 确保所有模型文件完整且路径正确
2. **音频设备**: 使用 `AudioCapture.list_devices()` 查看可用设备
3. **性能优化**: 根据硬件调整 `num_threads` 参数
4. **环境噪音**: 在嘈杂环境中可能需要调整检测阈值
5. **AEC配置**:
   - Windows/Linux: 自动使用系统级AEC，无需额外配置
   - 其他平台: 不支持AEC功能
6. **AEC性能**: Windows/Linux平台启用AEC不会增加CPU占用和延迟

## 故障排除

### 问题: 模型文件不存在

```
FileNotFoundError: 模型文件不存在: models/encoder.onnx
```

**解决方法**: 确保所有模型文件存在于指定目录中

### 问题: 找不到音频设备

```
RuntimeError: 找不到可用的输入设备
```

**解决方法**: 检查麦克风连接，使用 `AudioCapture.list_devices()` 查看设备

### 问题: 检测不到唤醒词

**解决方法**:
1. 降低 `keywords_threshold` 值
2. 确保环境安静
3. 检查麦克风音量
4. 验证唤醒词配置文件

## 许可证

本项目从 py-xiaozhi 项目提取，遵循原项目许可证。