""" 音频工具类 提供音频处理相关的实用工具函数 """ import math from typing import Tuple, List, Dict, Any from panda3d.core import Vec3 def calculate_distance(pos1: Tuple[float, float, float], pos2: Tuple[float, float, float]) -> float: """ 计算两点间距离 Args: pos1: 第一个点 (x, y, z) pos2: 第二个点 (x, y, z) Returns: 距离 """ dx = pos1[0] - pos2[0] dy = pos1[1] - pos2[1] dz = pos1[2] - pos2[2] return math.sqrt(dx*dx + dy*dy + dz*dz) def calculate_volume_attenuation(distance: float, max_distance: float = 50.0, min_volume: float = 0.0, max_volume: float = 1.0) -> float: """ 计算基于距离的音量衰减 Args: distance: 距离 max_distance: 最大距离(在此距离外音量为最小值) min_volume: 最小音量 max_volume: 最大音量 Returns: 计算后的音量 """ if distance >= max_distance: return min_volume # 线性衰减 attenuation = 1.0 - (distance / max_distance) volume = min_volume + (max_volume - min_volume) * attenuation return max(min_volume, min(max_volume, volume)) def calculate_pan(position: Tuple[float, float, float], listener_position: Tuple[float, float, float]) -> float: """ 计算立体声平移(左/右声道平衡) Args: position: 音源位置 listener_position: 听者位置 Returns: 平移值 (-1.0 到 1.0,-1为左,1为右) """ # 计算相对位置 rel_x = position[0] - listener_position[0] rel_z = position[2] - listener_position[2] # 计算角度 angle = math.atan2(rel_x, rel_z) # 转换为平移值 pan = math.sin(angle) return max(-1.0, min(1.0, pan)) def interpolate_positions(start_pos: Tuple[float, float, float], end_pos: Tuple[float, float, float], steps: int) -> List[Tuple[float, float, float]]: """ 在两点间插值生成位置列表 Args: start_pos: 起始位置 end_pos: 结束位置 steps: 插值步数 Returns: 插值位置列表 """ if steps <= 1: return [start_pos, end_pos] positions = [] for i in range(steps): t = i / (steps - 1) x = start_pos[0] + (end_pos[0] - start_pos[0]) * t y = start_pos[1] + (end_pos[1] - start_pos[1]) * t z = start_pos[2] + (end_pos[2] - start_pos[2]) * t positions.append((x, y, z)) return positions def validate_audio_file(filepath: str) -> Dict[str, Any]: """ 验证音频文件 Args: filepath: 音频文件路径 Returns: 验证结果字典 """ import os result = { 'valid': False, 'exists': False, 'file_size': 0, 'extension': '', 'supported': False } # 检查文件是否存在 if not os.path.exists(filepath): result['error'] = '文件不存在' return result result['exists'] = True # 获取文件信息 result['file_size'] = os.path.getsize(filepath) _, extension = os.path.splitext(filepath) result['extension'] = extension.lower() # 检查是否为支持的格式 supported_formats = ['.wav', '.ogg', '.mp3'] result['supported'] = result['extension'] in supported_formats # 验证通过 if result['supported'] and result['file_size'] > 0: result['valid'] = True return result def generate_test_tone(frequency: float = 440.0, duration: float = 1.0, sample_rate: int = 44100) -> bytes: """ 生成测试音调(正弦波) Args: frequency: 频率 (Hz) duration: 持续时间 (秒) sample_rate: 采样率 Returns: 音频数据字节 """ import math import struct # 生成正弦波数据 num_samples = int(duration * sample_rate) audio_data = bytearray() for i in range(num_samples): # 生成正弦波样本 t = i / sample_rate sample = math.sin(2 * math.pi * frequency * t) # 转换为16位整数 sample_int = int(sample * 32767) audio_data.extend(struct.pack(' bytes: """ 将多声道音频转换为单声道 Args: audio_data: 音频数据 channels: 声道数 Returns: 单声道音频数据 """ if channels == 1: return audio_data # 假设16位音频数据 sample_size = 2 frame_size = sample_size * channels mono_data = bytearray() for i in range(0, len(audio_data), frame_size): # 取第一个声道的数据 mono_data.extend(audio_data[i:i+sample_size]) return bytes(mono_data) def apply_volume_multiplier(audio_data: bytes, volume: float) -> bytes: """ 应用音量乘数到音频数据 Args: audio_data: 音频数据 volume: 音量乘数 (0.0-1.0) Returns: 处理后的音频数据 """ import struct if volume == 1.0: return audio_data # 假设16位音频数据 sample_count = len(audio_data) // 2 processed_data = bytearray() for i in range(sample_count): # 解包16位样本 sample = struct.unpack_from(' float: """ 计算多普勒频移 Args: source_velocity: 音源速度向量 listener_velocity: 听者速度向量 sound_speed: 声速 (米/秒) Returns: 频率比率 (播放频率/原始频率) """ # 计算相对速度 rel_velocity_x = source_velocity[0] - listener_velocity[0] rel_velocity_z = source_velocity[2] - listener_velocity[2] # 计算速度大小 source_speed = math.sqrt(rel_velocity_x**2 + rel_velocity_z**2) # 简化的多普勒效应计算 if sound_speed > source_speed: ratio = (sound_speed - source_speed) / sound_speed return max(0.5, min(2.0, ratio)) # 限制在合理范围内 else: return 0.5 # 音源速度接近或超过声速时的处理 def create_hrtf_filter(angle: float, elevation: float) -> Dict[str, List[float]]: """ 创建HRTF(头部相关传递函数)滤波器 Args: angle: 水平角度 (度) elevation: 垂直角度 (度) Returns: 左右声道滤波器系数 """ # 这是一个简化的HRTF实现 # 实际应用中会使用真实的HRTF测量数据 # 简单的左右声道延迟差异 angle_rad = math.radians(angle) delay_diff = math.sin(angle_rad) * 0.001 # 最大1毫秒延迟差异 # 简单的左右声道幅度差异 left_amp = 1.0 - max(0, math.sin(angle_rad)) * 0.3 right_amp = 1.0 - max(0, -math.sin(angle_rad)) * 0.3 return { 'left': [left_amp, 0.0, 0.0], 'right': [right_amp, 0.0, delay_diff] } def calculate_reverb_parameters(room_size: Tuple[float, float, float], absorption: float = 0.5) -> Dict[str, float]: """ 根据房间尺寸计算混响参数 Args: room_size: 房间尺寸 (长, 宽, 高) absorption: 吸收系数 (0.0-1.0) Returns: 混响参数字典 """ length, width, height = room_size # 计算房间体积 volume = length * width * height # 计算表面积 surface_area = 2 * (length*width + length*height + width*height) # 简化的混响时间计算(Sabine公式) reverb_time = 0.161 * volume / (surface_area * absorption) # 限制在合理范围内 reverb_time = max(0.1, min(10.0, reverb_time)) return { 'reverb_time': reverb_time, 'early_reflections': reverb_time * 0.1, 'late_reverb': reverb_time * 0.9, 'density': min(1.0, volume / 1000.0), 'diffusion': 1.0 - absorption }