Local-Voice/multiprocess_recorder.py
2025-09-22 02:06:00 +08:00

338 lines
11 KiB
Python
Raw 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
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角色切换功能')
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
# 启动系统自动校准但根据NFC状态决定是否启动监听
print("🚀 启动系统(包含自动校准)...")
control_system.start(auto_calibration=True, auto_monitoring=not nfc_enabled)
except KeyboardInterrupt:
print("\n👋 用户中断")
except Exception as e:
print(f"❌ 系统启动失败: {e}")
if args.verbose:
import traceback
traceback.print_exc()
finally:
print("👋 系统退出")
if __name__ == "__main__":
main()