Local-Voice/multiprocess_recorder.py
2025-09-27 14:30:19 +08:00

401 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
多进程音频录音系统
基于进程隔离的音频处理架构
"""
import os
import sys
import argparse
import json
import time
import subprocess
from typing import Dict, Any
# 导入LED控制器
try:
from led_controller import get_led_controller, SystemState, start_led_effects, stop_led_effects, set_led_state
LED_AVAILABLE = True
except ImportError:
LED_AVAILABLE = False
print("⚠️ LED控制器模块未找到LED功能将被禁用")
def check_dependencies():
"""检查系统依赖"""
missing_deps = []
try:
import pyaudio
except ImportError:
missing_deps.append("pyaudio")
try:
import numpy
except ImportError:
missing_deps.append("numpy")
try:
import requests
except ImportError:
missing_deps.append("requests")
try:
import websockets
except ImportError:
missing_deps.append("websockets")
if missing_deps:
print("❌ 缺少以下依赖库:")
for dep in missing_deps:
print(f" - {dep}")
print("\n请运行以下命令安装:")
print(f"pip install {' '.join(missing_deps)}")
return False
return True
def check_nfc_dependencies():
"""检查NFC相关依赖"""
try:
# 检查nfc-list命令是否存在
result = subprocess.run(['which', 'nfc-list'], capture_output=True, text=True)
if result.returncode != 0:
print("❌ 未找到nfc-list命令")
print(" 请安装libnfc工具:")
print(" Ubuntu/Debian: sudo apt-get install libnfc-bin")
print(" macOS: brew install libnfc")
return False
print("✅ NFC工具已安装")
return True
except Exception as e:
print(f"❌ NFC依赖检查失败: {e}")
return False
def check_environment():
"""检查运行环境"""
print("🔍 检查运行环境...")
# 检查Python版本
python_version = sys.version_info
if python_version.major < 3 or (python_version.major == 3 and python_version.minor < 7):
print(f"❌ Python版本过低: {python_version.major}.{python_version.minor}")
print("需要Python 3.7或更高版本")
return False
print(f"✅ Python版本: {python_version.major}.{python_version.minor}.{python_version.micro}")
# 检查操作系统
import platform
system = platform.system().lower()
print(f"✅ 操作系统: {system}")
# 检查音频设备
try:
import pyaudio
audio = pyaudio.PyAudio()
device_count = audio.get_device_count()
print(f"✅ 音频设备数量: {device_count}")
if device_count == 0:
print("❌ 未检测到音频设备")
return False
audio.terminate()
except Exception as e:
print(f"❌ 音频设备检查失败: {e}")
return False
# 检查网络连接
try:
import requests
response = requests.get("https://www.baidu.com", timeout=5)
print("✅ 网络连接正常")
except:
print("⚠️ 网络连接可能有问题会影响在线AI功能")
# 检查API密钥
api_key = os.environ.get("ARK_API_KEY")
if api_key:
print("✅ ARK_API_KEY 已设置")
else:
print("⚠️ ARK_API_KEY 未设置,大语言模型功能将被禁用")
print(" 请运行: export ARK_API_KEY='your_api_key_here'")
return True
def list_characters():
"""列出可用角色"""
characters_dir = os.path.join(os.path.dirname(__file__), "characters")
if not os.path.exists(characters_dir):
print("❌ 角色目录不存在")
return
characters = []
for file in os.listdir(characters_dir):
if file.endswith('.json'):
character_name = file[:-5]
config_file = os.path.join(characters_dir, file)
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
name = config.get('name', character_name)
desc = config.get('description', '无描述')
characters.append(f"{character_name}: {name} - {desc}")
except:
characters.append(f"{character_name}: 配置文件读取失败")
if characters:
print("🎭 可用角色列表:")
for char in characters:
print(f" - {char}")
else:
print("❌ 未找到任何角色配置文件")
def create_sample_config():
"""创建示例配置文件"""
config = {
"system": {
"max_queue_size": 1000,
"process_timeout": 30,
"heartbeat_interval": 1.0,
"log_level": "INFO"
},
"audio": {
"sample_rate": 16000,
"channels": 1,
"chunk_size": 1024,
"format": "paInt16"
},
"recording": {
"min_duration": 2.0,
"max_duration": 30.0,
"silence_threshold": 3.0,
"pre_record_duration": 2.0
},
"processing": {
"enable_asr": True,
"enable_llm": True,
"enable_tts": True,
"character": "libai",
"max_tokens": 50
},
"detection": {
"zcr_min": 2400,
"zcr_max": 12000,
"consecutive_silence_count": 30,
"max_zcr_history": 30
},
"playback": {
"buffer_size": 1000,
"show_progress": True,
"progress_interval": 100,
"chunk_size": 512
}
}
config_file = "config.json"
try:
with open(config_file, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
print(f"✅ 示例配置文件已创建: {config_file}")
except Exception as e:
print(f"❌ 创建配置文件失败: {e}")
def main():
"""主函数"""
parser = argparse.ArgumentParser(
description='多进程音频录音系统',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例:
python multiprocess_recorder.py # 使用默认角色
python multiprocess_recorder.py -c zhubajie # 指定角色
python multiprocess_recorder.py -l # 列出角色
python multiprocess_recorder.py --create-config # 创建配置文件
python multiprocess_recorder.py --enable-nfc # 启用NFC角色切换
"""
)
parser.add_argument('--character', '-c', type=str, default='libai',
help='选择角色 (默认: libai)')
parser.add_argument('--list-characters', '-l', action='store_true',
help='列出所有可用角色')
parser.add_argument('--config', type=str,
help='配置文件路径')
parser.add_argument('--create-config', action='store_true',
help='创建示例配置文件')
parser.add_argument('--check-env', action='store_true',
help='检查运行环境')
parser.add_argument('--verbose', '-v', action='store_true',
help='详细输出')
parser.add_argument('--enable-nfc', action='store_true',
help='启用NFC角色切换功能')
parser.add_argument('--enable-led', action='store_true',
help='启用LED灯光效果')
parser.add_argument('--led-count', type=int, default=10,
help='LED数量 (默认: 10)')
parser.add_argument('--led-brightness', type=int, default=100,
help='LED亮度 (默认: 100)')
parser.add_argument('--led-pin', type=int, default=10,
help='LED GPIO引脚 (默认: 10)')
args = parser.parse_args()
# 显示欢迎信息
print("🚀 多进程音频录音系统")
print("=" * 60)
# 检查依赖
if not check_dependencies():
sys.exit(1)
# 创建配置文件
if args.create_config:
create_sample_config()
return
# 检查环境
if args.check_env:
check_environment()
return
# 列出角色
if args.list_characters:
list_characters()
return
# 检查characters目录
characters_dir = os.path.join(os.path.dirname(__file__), "characters")
if not os.path.exists(characters_dir):
print(f"⚠️ 角色目录不存在: {characters_dir}")
print("请确保characters目录存在并包含角色配置文件")
# 检查指定角色
character_file = os.path.join(characters_dir, f"{args.character}.json")
if not os.path.exists(character_file):
print(f"⚠️ 角色文件不存在: {character_file}")
print(f"可用角色:")
list_characters()
return
print(f"🎭 当前角色: {args.character}")
print("🎯 系统特点:")
print(" - 多进程架构:输入输出完全隔离")
print(" - 零切换延迟:无需音频设备重置")
print(" - 实时响应:并行处理录音和播放")
print(" - 智能检测基于ZCR的语音识别")
print(" - 流式TTS实时音频生成和播放")
print(" - 角色扮演支持多种AI角色")
print("=" * 60)
# 显示使用说明
print("📖 使用说明:")
print(" - 检测到语音自动开始录音")
print(" - 持续静音3秒自动结束录音")
print(" - 录音完成后自动处理和播放")
print(" - 按 Ctrl+C 退出")
print("=" * 60)
# 加载配置
config = None
if args.config:
try:
with open(args.config, 'r', encoding='utf-8') as f:
config = json.load(f)
print(f"📋 加载配置文件: {args.config}")
except Exception as e:
print(f"⚠️ 配置文件加载失败: {e}")
print("使用默认配置")
try:
# 导入控制系统
from control_system import ControlSystem
# 创建控制系统
control_system = ControlSystem(config)
# 设置角色
control_system.config['processing']['character'] = args.character
# 设置日志级别
if args.verbose:
control_system.config['system']['log_level'] = "DEBUG"
# 启用NFC功能如果指定- 在系统启动前启用确保校准完成后能立即开始NFC检测
nfc_enabled = False
if args.enable_nfc:
print("📱 检查NFC依赖...")
if not check_nfc_dependencies():
print("❌ NFC依赖检查失败无法启用NFC功能")
else:
print("📱 启用NFC角色切换功能...")
control_system.enable_nfc()
nfc_enabled = True
# 启用LED功能如果指定
led_enabled = False
if args.enable_led:
if LED_AVAILABLE:
print("💡 启用LED灯光效果...")
print(f" LED数量: {args.led_count}")
print(f" LED亮度: {args.led_brightness}")
print(f" LED引脚: GPIO{args.led_pin}")
try:
# 启动LED系统
success = start_led_effects(
led_count=args.led_count,
brightness=args.led_brightness,
led_pin=args.led_pin
)
if success:
led_enabled = True
print("✅ LED系统已启动")
# 设置初始状态为启动效果
set_led_state(SystemState.STARTUP)
else:
print("❌ LED系统启动失败")
except Exception as e:
print(f"❌ LED系统初始化失败: {e}")
else:
print("⚠️ LED功能不可用请检查硬件和驱动")
# 启动系统自动校准但根据NFC状态决定是否启动监听
print("🚀 启动系统(包含自动校准)...")
control_system.start(auto_calibration=True, auto_monitoring=not nfc_enabled)
# 如果启用了LED设置系统状态为等待NFC或空闲监听
if led_enabled:
if nfc_enabled:
set_led_state(SystemState.WAITING_NFC)
else:
set_led_state(SystemState.IDLE_MONITORING)
except KeyboardInterrupt:
print("\n👋 用户中断")
except Exception as e:
print(f"❌ 系统启动失败: {e}")
if args.verbose:
import traceback
traceback.print_exc()
finally:
# 清理LED系统
if LED_AVAILABLE and led_enabled:
try:
print("💡 关闭LED系统...")
set_led_state(SystemState.SHUTDOWN)
time.sleep(1) # 给LED一些时间显示关闭效果
stop_led_effects()
print("✅ LED系统已关闭")
except Exception as e:
print(f"❌ 关闭LED系统失败: {e}")
print("👋 系统退出")
if __name__ == "__main__":
main()