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

882 lines
28 KiB
Python

"""
工具类模块
提供语音控制插件所需的实用工具函数
"""
import time
import math
import os
import json
import threading
from typing import Dict, Any, List, Optional, Union
import hashlib
class VoiceUtils:
"""
语音工具类
提供语音控制插件所需的实用工具函数
"""
def __init__(self, plugin):
"""
初始化语音工具类
Args:
plugin: 语音控制插件实例
"""
self.plugin = plugin
self.enabled = False
self.initialized = False
# 性能监控
self.performance_metrics = {
"update_times": [],
"processing_times": [],
"memory_usage": 0,
"cpu_usage": 0
}
# 日志配置
self.log_config = {
"enable_logging": True,
"log_level": "INFO",
"log_file": "/home/hello/EG/plugins/user/voice_control/logs/voice.log",
"max_log_size": 10 * 1024 * 1024, # 10MB
"log_format": "[{timestamp}] {level}: {message}"
}
# 数据转换配置
self.conversion_config = {
"audio_level_curve": "linear", # linear, exponential, logarithmic
"confidence_mapping": "linear",
"duration_scaling": "linear"
}
# 数学工具
self.math_utils = MathUtils()
# 缓存管理
self.cache_manager = CacheManager()
# 调试工具
self.debug_tools = DebugTools()
# 文件管理
self.file_manager = FileManager()
# 统计信息
self.stats = {
"functions_called": 0,
"data_processed": 0,
"cache_hits": 0,
"cache_misses": 0,
"files_processed": 0
}
# 线程锁
self.utils_lock = threading.RLock()
print("✓ 语音工具类已创建")
def initialize(self) -> bool:
"""
初始化语音工具类
Returns:
是否初始化成功
"""
try:
# 创建日志目录
log_dir = "/home/hello/EG/plugins/user/voice_control/logs"
if not os.path.exists(log_dir):
os.makedirs(log_dir)
# 初始化子工具
self.math_utils.initialize()
self.cache_manager.initialize()
self.debug_tools.initialize()
self.file_manager.initialize()
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.math_utils.enable()
self.cache_manager.enable()
self.debug_tools.enable()
self.file_manager.enable()
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.math_utils.disable()
self.cache_manager.disable()
self.debug_tools.disable()
self.file_manager.disable()
print("✓ 语音工具类已禁用")
except Exception as e:
print(f"✗ 语音工具类禁用失败: {e}")
import traceback
traceback.print_exc()
def finalize(self):
"""清理语音工具类资源"""
try:
self.disable()
self.performance_metrics.clear()
self.cache_manager.clear()
self.debug_tools.clear()
self.file_manager.clear()
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
# 更新性能指标
self._update_performance_metrics(dt)
# 更新子工具
self.cache_manager.update(dt)
self.debug_tools.update(dt)
self.file_manager.update(dt)
except Exception as e:
print(f"✗ 语音工具类更新失败: {e}")
import traceback
traceback.print_exc()
def _update_performance_metrics(self, dt: float):
"""更新性能指标"""
try:
# 记录更新时间
self.performance_metrics["update_times"].append(dt)
# 保持历史记录大小
if len(self.performance_metrics["update_times"]) > 1000:
self.performance_metrics["update_times"] = self.performance_metrics["update_times"][-1000:]
except Exception as e:
print(f"✗ 性能指标更新失败: {e}")
def log_message(self, message: str, level: str = "INFO"):
"""
记录日志消息
Args:
message: 日志消息
level: 日志级别 (DEBUG, INFO, WARNING, ERROR)
"""
try:
if not self.log_config["enable_logging"]:
return
# 检查日志级别
level_priority = {"DEBUG": 0, "INFO": 1, "WARNING": 2, "ERROR": 3}
config_level = self.log_config["log_level"]
if level_priority.get(level, 1) < level_priority.get(config_level, 1):
return
# 格式化日志消息
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
formatted_message = self.log_config["log_format"].format(
timestamp=timestamp,
level=level,
message=message
)
# 输出到控制台
print(formatted_message)
# 写入日志文件
try:
with self.utils_lock:
# 检查文件大小
if os.path.exists(self.log_config["log_file"]):
if os.path.getsize(self.log_config["log_file"]) > self.log_config["max_log_size"]:
# 重命名旧日志文件
backup_file = self.log_config["log_file"] + ".old"
if os.path.exists(backup_file):
os.remove(backup_file)
os.rename(self.log_config["log_file"], backup_file)
with open(self.log_config["log_file"], "a", encoding="utf-8") as f:
f.write(formatted_message + "\n")
except Exception as e:
pass # 忽略文件写入错误
except Exception as e:
pass # 忽略日志记录错误
def measure_performance(self, func: callable, *args, **kwargs) -> tuple:
"""
测量函数性能
Args:
func: 要测量的函数
*args: 函数参数
**kwargs: 函数关键字参数
Returns:
(结果, 执行时间)
"""
try:
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
execution_time = end_time - start_time
# 记录到性能指标
metric_name = f"{func.__name__}_times"
if metric_name not in self.performance_metrics:
self.performance_metrics[metric_name] = []
self.performance_metrics[metric_name].append(execution_time)
# 保持历史记录大小
if len(self.performance_metrics[metric_name]) > 1000:
self.performance_metrics[metric_name] = self.performance_metrics[metric_name][-1000:]
self.stats["functions_called"] += 1
return (result, execution_time)
except Exception as e:
self.log_message(f"性能测量失败: {e}", "ERROR")
raise
def convert_audio_level(self, level: float, curve: str = None) -> float:
"""
转换音频级别值
Args:
level: 原始级别值 (0.0-1.0)
curve: 转换曲线类型
Returns:
转换后的级别值
"""
try:
curve_type = curve or self.conversion_config["audio_level_curve"]
level = max(0.0, min(1.0, level))
if curve_type == "exponential":
# 指数曲线
return math.pow(level, 2)
elif curve_type == "logarithmic":
# 对数曲线
return math.log(level + 1) / math.log(2)
else:
# 线性曲线
return level
except Exception as e:
self.log_message(f"音频级别转换失败: {e}", "ERROR")
return level
def convert_confidence(self, confidence: float, mapping: str = None) -> float:
"""
转换置信度值
Args:
confidence: 原始置信度值 (0.0-1.0)
mapping: 映射类型
Returns:
转换后的置信度值
"""
try:
mapping_type = mapping or self.conversion_config["confidence_mapping"]
confidence = max(0.0, min(1.0, confidence))
if mapping_type == "exponential":
# 指数映射
return math.pow(confidence, 2)
elif mapping_type == "logarithmic":
# 对数映射
return math.log(confidence * 9 + 1) / math.log(10)
else:
# 线性映射
return confidence
except Exception as e:
self.log_message(f"置信度转换失败: {e}", "ERROR")
return confidence
def scale_duration(self, duration: float, scale_factor: float = 1.0,
scaling_type: str = None) -> float:
"""
缩放持续时间
Args:
duration: 原始持续时间
scale_factor: 缩放因子
scaling_type: 缩放类型
Returns:
缩放后的持续时间
"""
try:
scaling = scaling_type or self.conversion_config["duration_scaling"]
duration = max(0.0, duration)
scale_factor = max(0.0, scale_factor)
if scaling == "exponential":
return duration * math.pow(scale_factor, 2)
elif scaling == "logarithmic":
return duration * math.log(scale_factor + 1)
else:
return duration * scale_factor
except Exception as e:
self.log_message(f"持续时间缩放失败: {e}", "ERROR")
return duration
def interpolate_values(self, start_value: float, end_value: float,
progress: float, interpolation_type: str = "linear") -> float:
"""
插值计算
Args:
start_value: 起始值
end_value: 结束值
progress: 进度 (0.0-1.0)
interpolation_type: 插值类型
Returns:
插值结果
"""
try:
progress = max(0.0, min(1.0, progress))
if interpolation_type == "ease_in":
progress = math.pow(progress, 2)
elif interpolation_type == "ease_out":
progress = 1.0 - math.pow(1.0 - progress, 2)
elif interpolation_type == "ease_in_out":
progress = 0.5 * (1 - math.cos(progress * math.pi))
# linear 为默认情况,无需特殊处理
return start_value + (end_value - start_value) * progress
except Exception as e:
self.log_message(f"插值计算失败: {e}", "ERROR")
return start_value
def calculate_similarity(self, text1: str, text2: str) -> float:
"""
计算文本相似度
Args:
text1: 文本1
text2: 文本2
Returns:
相似度值 (0.0-1.0)
"""
try:
# 使用简单的字符级相似度计算
set1 = set(text1)
set2 = set(text2)
intersection = len(set1.intersection(set2))
union = len(set1.union(set2))
if union == 0:
return 1.0 if text1 == text2 else 0.0
return intersection / union
except Exception as e:
self.log_message(f"相似度计算失败: {e}", "ERROR")
return 0.0
def normalize_text(self, text: str, language: str = "zh-CN") -> str:
"""
标准化文本
Args:
text: 原始文本
language: 语言代码
Returns:
标准化后的文本
"""
try:
# 移除多余空格
text = " ".join(text.split())
# 转换为小写(仅适用于英文)
if language == "en-US":
text = text.lower()
# 移除特殊字符(保留字母、数字和基本标点)
import re
if language == "zh-CN":
text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9\s\.,!?;:]", "", text)
else:
text = re.sub(r"[^a-zA-Z0-9\s\.,!?;:]", "", text)
return text.strip()
except Exception as e:
self.log_message(f"文本标准化失败: {e}", "ERROR")
return text
def generate_hash(self, data: Union[str, bytes]) -> str:
"""
生成数据哈希值
Args:
data: 要哈希的数据
Returns:
哈希值字符串
"""
try:
if isinstance(data, str):
data = data.encode("utf-8")
return hashlib.md5(data).hexdigest()
except Exception as e:
self.log_message(f"哈希生成失败: {e}", "ERROR")
return ""
def format_time(self, seconds: float) -> str:
"""
格式化时间显示
Args:
seconds: 秒数
Returns:
格式化的时间字符串
"""
try:
if seconds < 60:
return f"{seconds:.1f}"
elif seconds < 3600:
minutes = int(seconds // 60)
secs = seconds % 60
return f"{minutes}{secs:.1f}"
else:
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
secs = seconds % 60
return f"{hours}{minutes}{secs:.1f}"
except Exception as e:
self.log_message(f"时间格式化失败: {e}", "ERROR")
return f"{seconds:.1f}"
def get_performance_stats(self) -> Dict[str, Any]:
"""
获取性能统计信息
Returns:
性能统计字典
"""
try:
stats = {}
for key, times in self.performance_metrics.items():
if times:
stats[key] = {
"count": len(times),
"average": sum(times) / len(times),
"min": min(times),
"max": max(times),
"total": sum(times)
}
return stats
except Exception as e:
self.log_message(f"性能统计获取失败: {e}", "ERROR")
return {}
def get_system_info(self) -> Dict[str, Any]:
"""
获取系统信息
Returns:
系统信息字典
"""
try:
import platform
import psutil
return {
"platform": platform.system(),
"platform_version": platform.version(),
"processor": platform.processor(),
"cpu_count": psutil.cpu_count(),
"memory_total": psutil.virtual_memory().total,
"memory_available": psutil.virtual_memory().available,
"plugin_version": getattr(self.plugin, "version", "unknown")
}
except Exception as e:
self.log_message(f"系统信息获取失败: {e}", "ERROR")
return {}
def get_stats(self) -> Dict[str, int]:
"""
获取统计信息
Returns:
统计信息字典
"""
return self.stats.copy()
def reset_stats(self):
"""重置统计信息"""
try:
self.stats = {
"functions_called": 0,
"data_processed": 0,
"cache_hits": 0,
"cache_misses": 0,
"files_processed": 0
}
print("✓ 工具类统计信息已重置")
except Exception as e:
self.log_message(f"统计信息重置失败: {e}", "ERROR")
def set_log_config(self, config: Dict[str, Any]) -> bool:
"""
设置日志配置
Args:
config: 日志配置字典
Returns:
是否设置成功
"""
try:
self.log_config.update(config)
self.log_message(f"日志配置已更新: {self.log_config}", "INFO")
return True
except Exception as e:
self.log_message(f"日志配置设置失败: {e}", "ERROR")
return False
def set_conversion_config(self, config: Dict[str, Any]) -> bool:
"""
设置转换配置
Args:
config: 转换配置字典
Returns:
是否设置成功
"""
try:
self.conversion_config.update(config)
self.log_message(f"转换配置已更新: {self.conversion_config}", "INFO")
return True
except Exception as e:
self.log_message(f"转换配置设置失败: {e}", "ERROR")
return False
def get_cache_manager(self):
"""
获取缓存管理器
Returns:
缓存管理器实例
"""
return self.cache_manager
def get_debug_tools(self):
"""
获取调试工具
Returns:
调试工具实例
"""
return self.debug_tools
def get_file_manager(self):
"""
获取文件管理器
Returns:
文件管理器实例
"""
return self.file_manager
class MathUtils:
"""数学工具类"""
def __init__(self):
self.enabled = False
self.initialized = False
def initialize(self):
"""初始化数学工具"""
self.initialized = True
def enable(self):
"""启用数学工具"""
self.enabled = True
def disable(self):
"""禁用数学工具"""
self.enabled = False
@staticmethod
def clamp(value: float, min_value: float, max_value: float) -> float:
"""限制值在指定范围内"""
return max(min_value, min(max_value, value))
@staticmethod
def lerp(start: float, end: float, t: float) -> float:
"""线性插值"""
return start + (end - start) * t
@staticmethod
def smoothstep(edge0: float, edge1: float, x: float) -> float:
"""平滑插值"""
t = MathUtils.clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0)
return t * t * (3.0 - 2.0 * t)
@staticmethod
def distance(pos1: tuple, pos2: tuple) -> float:
"""计算两点间距离"""
try:
dx = pos2[0] - pos1[0]
dy = pos2[1] - pos1[1]
dz = pos2[2] - pos1[2] if len(pos1) > 2 and len(pos2) > 2 else 0
return math.sqrt(dx*dx + dy*dy + dz*dz)
except Exception:
return 0.0
class CacheManager:
"""缓存管理器"""
def __init__(self):
self.enabled = False
self.initialized = False
self.cache = {}
self.access_times = {}
self.max_size = 1000
def initialize(self):
"""初始化缓存管理器"""
self.initialized = True
def enable(self):
"""启用缓存管理器"""
self.enabled = True
def disable(self):
"""禁用缓存管理器"""
self.enabled = False
def update(self, dt: float):
"""更新缓存管理器"""
if self.enabled:
self._cleanup()
def get(self, key: str) -> Optional[Any]:
"""获取缓存值"""
if key in self.cache:
self.access_times[key] = time.time()
return self.cache[key]
return None
def set(self, key: str, value: Any, ttl: float = 300.0):
"""设置缓存值"""
self.cache[key] = value
self.access_times[key] = time.time()
# 检查缓存大小
if len(self.cache) > self.max_size:
self._cleanup()
def _cleanup(self):
"""清理过期缓存"""
current_time = time.time()
expired_keys = [
key for key, access_time in self.access_times.items()
if current_time - access_time > 300.0
]
for key in expired_keys:
del self.cache[key]
del self.access_times[key]
def clear(self):
"""清空缓存"""
self.cache.clear()
self.access_times.clear()
class DebugTools:
"""调试工具类"""
def __init__(self):
self.enabled = False
self.initialized = False
self.debug_enabled = False
self.debug_data = {}
def initialize(self):
"""初始化调试工具"""
self.initialized = True
def enable(self):
"""启用调试工具"""
self.enabled = True
def disable(self):
"""禁用调试工具"""
self.enabled = False
def update(self, dt: float):
"""更新调试工具"""
pass
def enable_debug(self):
"""启用调试模式"""
self.debug_enabled = True
def disable_debug(self):
"""禁用调试模式"""
self.debug_enabled = False
def log_debug_data(self, key: str, data: Any):
"""记录调试数据"""
if self.debug_enabled:
self.debug_data[key] = data
def get_debug_data(self, key: str = None) -> Union[Dict[str, Any], Any]:
"""获取调试数据"""
if key:
return self.debug_data.get(key)
return self.debug_data.copy()
def clear_debug_data(self):
"""清空调试数据"""
self.debug_data.clear()
def clear(self):
"""清理调试工具"""
self.debug_data.clear()
class FileManager:
"""文件管理器"""
def __init__(self):
self.enabled = False
self.initialized = False
self.watched_files = {}
def initialize(self):
"""初始化文件管理器"""
self.initialized = True
def enable(self):
"""启用文件管理器"""
self.enabled = True
def disable(self):
"""禁用文件管理器"""
self.enabled = False
def update(self, dt: float):
"""更新文件管理器"""
if self.enabled:
self._check_file_changes()
def _check_file_changes(self):
"""检查文件变化"""
try:
for file_path, last_modified in list(self.watched_files.items()):
if os.path.exists(file_path):
current_modified = os.path.getmtime(file_path)
if current_modified > last_modified:
self.watched_files[file_path] = current_modified
# 触发文件变化事件
except Exception as e:
pass
def watch_file(self, file_path: str):
"""监视文件变化"""
try:
if os.path.exists(file_path):
self.watched_files[file_path] = os.path.getmtime(file_path)
except Exception as e:
pass
def unwatch_file(self, file_path: str):
"""取消监视文件"""
if file_path in self.watched_files:
del self.watched_files[file_path]
def read_json_file(self, file_path: str) -> Optional[Dict[str, Any]]:
"""读取JSON文件"""
try:
with open(file_path, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
return None
def write_json_file(self, file_path: str, data: Dict[str, Any]) -> bool:
"""写入JSON文件"""
try:
with open(file_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return True
except Exception as e:
return False
def clear(self):
"""清理文件管理器"""
self.watched_files.clear()