""" 空间音频效果处理器 实现专业的3D空间音频效果处理 """ import uuid import math import numpy as np from typing import Dict, List, Any, Optional, Tuple from panda3d.core import Vec3 class SpatialProcessor: """ 空间音频效果处理器 实现专业的3D空间音频效果处理 """ def __init__(self, plugin): """ 初始化空间音频效果处理器 Args: plugin: 音频效果插件实例 """ self.plugin = plugin self.effects: Dict[str, Dict[str, Any]] = {} self.stats = { 'total_effects': 0, 'active_effects': 0, 'effects_processed': 0, 'processing_time': 0.0, 'memory_usage': 0 } self.buffer_size = plugin.buffer_size if plugin else 4096 self.sample_rate = 44100 # 默认采样率 self.speed_of_sound = 343.0 # 声速 (米/秒) def create_effect(self, parameters: Dict[str, Any]) -> str: """ 创建空间音频效果 Args: parameters: 空间音频效果参数 Returns: 效果ID """ try: # 生成唯一ID effect_id = str(uuid.uuid4()) # 默认参数 default_params = { 'source_position': (0.0, 0.0, 0.0), # 音源位置 (x, y, z) 'listener_position': (0.0, 0.0, 0.0), # 听者位置 (x, y, z) 'listener_orientation': (0.0, 0.0, 0.0), # 听者方向 (heading, pitch, roll) 'min_distance': 1.0, # 最小距离 'max_distance': 100.0, # 最大距离 'rolloff_factor': 1.0, # 衰减因子 'cone_inner_angle': 360.0, # 内锥角 (度) 'cone_outer_angle': 360.0, # 外锥角 (度) 'cone_outer_gain': 1.0, # 外锥增益 'doppler_factor': 1.0, # 多普勒因子 'spread': 0.0, # 扩散角度 (度) 'air_absorption': 0.0, # 空气吸收 'room_rolloff': 0.0, # 房间衰减 'occlusion': 0.0, # 遮挡 (0.0-1.0) 'obstruction': 0.0, # 阻挡 (0.0-1.0) 'hrtf_enabled': False, # HRTF启用 'reverb_send': 0.0 # 混响发送 (0.0-1.0) } # 合并参数 effect_params = default_params.copy() effect_params.update(parameters) # 创建效果对象 effect = { 'id': effect_id, 'type': 'spatial', 'parameters': effect_params, 'active': True, 'buffers': {}, # 缓冲区 'hrtf_filters': {}, # HRTF滤波器 'distance': 0.0, # 当前距离 'azimuth': 0.0, # 方位角 'elevation': 0.0, # 仰角 'created_time': self._get_current_time(), 'process_count': 0 } # 初始化缓冲区 self._initialize_buffers(effect) # 初始化HRTF滤波器 if effect_params.get('hrtf_enabled', False): self._initialize_hrtf_filters(effect) # 存储效果 self.effects[effect_id] = effect # 更新统计信息 self.stats['total_effects'] += 1 self.stats['active_effects'] += 1 # 在插件中注册效果 if self.plugin: self.plugin.effects[effect_id] = effect.copy() print(f"✓ 空间音频效果创建成功: {effect_id}") return effect_id except Exception as e: print(f"✗ 创建空间音频效果失败: {e}") import traceback traceback.print_exc() return "" def _initialize_buffers(self, effect: Dict[str, Any]): """ 初始化缓冲区 Args: effect: 效果对象 """ effect_id = effect['id'] buffers = {} # 输入和输出缓冲区 buffers['input'] = np.zeros(self.buffer_size) buffers['output_left'] = np.zeros(self.buffer_size) buffers['output_right'] = np.zeros(self.buffer_size) # 延迟缓冲区(用于多普勒效应) buffers['delay_line'] = np.zeros(int(0.1 * self.sample_rate)) # 100ms延迟线 buffers['delay_index'] = 0 effect['buffers'] = buffers def _initialize_hrtf_filters(self, effect: Dict[str, Any]): """ 初始化HRTF滤波器 Args: effect: 效果对象 """ # 这里应该加载真实的HRTF数据 # 简化实现使用生成的滤波器 hrtf_filters = {} # 为不同的角度生成简化的HRTF滤波器 for azimuth in range(-180, 181, 15): # 每15度一个滤波器 for elevation in range(-90, 91, 15): # 生成简化的HRTF滤波器系数 left_filter = self._generate_simple_hrtf_filter(azimuth, elevation, 'left') right_filter = self._generate_simple_hrtf_filter(azimuth, elevation, 'right') hrtf_filters[(azimuth, elevation)] = { 'left': left_filter, 'right': right_filter } effect['hrtf_filters'] = hrtf_filters def _generate_simple_hrtf_filter(self, azimuth: int, elevation: int, channel: str) -> np.ndarray: """ 生成简化的HRTF滤波器 Args: azimuth: 方位角 (度) elevation: 仰角 (度) channel: 声道 ('left' 或 'right') Returns: 滤波器系数数组 """ # 简化的HRTF滤波器生成 # 实际应用中会使用真实的HRTF测量数据 # 滤波器长度 filter_length = 64 # 生成基于角度的简化的滤波器 filter_coeffs = np.zeros(filter_length) # 基于角度计算延迟和幅度 if channel == 'left': # 左耳滤波器 delay = max(0, int((1.0 - math.cos(math.radians(azimuth))) * 10)) amplitude = 0.5 + 0.5 * math.cos(math.radians(azimuth)) else: # 右耳滤波器 delay = max(0, int((1.0 - math.cos(math.radians(-azimuth))) * 10)) amplitude = 0.5 + 0.5 * math.cos(math.radians(-azimuth)) # 应用延迟和幅度 if delay < filter_length: filter_coeffs[delay] = amplitude return filter_coeffs def delete_effect(self, effect_id: str) -> bool: """ 删除空间音频效果 Args: effect_id: 效果ID Returns: 是否删除成功 """ if effect_id not in self.effects: print(f"✗ 空间音频效果不存在: {effect_id}") return False try: # 删除效果 del self.effects[effect_id] # 更新统计信息 self.stats['active_effects'] -= 1 # 从插件中移除效果 if self.plugin and effect_id in self.plugin.effects: del self.plugin.effects[effect_id] print(f"✓ 空间音频效果已删除: {effect_id}") return True except Exception as e: print(f"✗ 删除空间音频效果失败: {e}") return False def process(self, audio_data: Any, parameters: Dict[str, Any]) -> Any: """ 处理空间音频效果 Args: audio_data: 输入音频数据 parameters: 效果参数 Returns: 处理后的音频数据 """ try: import time process_start = time.time() # 如果输入是NumPy数组 if isinstance(audio_data, np.ndarray): processed_data = self._process_numpy_array(audio_data, parameters) else: # 对于其他类型的数据,返回原始数据 processed_data = audio_data # 更新统计信息 self.stats['effects_processed'] += 1 self.stats['processing_time'] += (time.time() - process_start) return processed_data except Exception as e: print(f"✗ 空间音频效果处理失败: {e}") return audio_data def _process_numpy_array(self, audio_data: np.ndarray, parameters: Dict[str, Any]) -> np.ndarray: """ 处理NumPy数组音频数据 Args: audio_data: 输入音频数据 parameters: 效果参数 Returns: 处理后的音频数据 """ # 获取参数 source_pos = Vec3(*parameters.get('source_position', (0.0, 0.0, 0.0))) listener_pos = Vec3(*parameters.get('listener_position', (0.0, 0.0, 0.0))) listener_orient = parameters.get('listener_orientation', (0.0, 0.0, 0.0)) min_distance = max(0.1, parameters.get('min_distance', 1.0)) max_distance = max(min_distance, parameters.get('max_distance', 100.0)) rolloff_factor = max(0.0, parameters.get('rolloff_factor', 1.0)) cone_inner_angle = max(0.0, min(360.0, parameters.get('cone_inner_angle', 360.0))) cone_outer_angle = max(cone_inner_angle, min(360.0, parameters.get('cone_outer_angle', 360.0))) cone_outer_gain = max(0.0, min(1.0, parameters.get('cone_outer_gain', 1.0))) doppler_factor = max(0.0, parameters.get('doppler_factor', 1.0)) spread = max(0.0, parameters.get('spread', 0.0)) air_absorption = max(0.0, parameters.get('air_absorption', 0.0)) occlusion = max(0.0, min(1.0, parameters.get('occlusion', 0.0))) obstruction = max(0.0, min(1.0, parameters.get('obstruction', 0.0))) hrtf_enabled = parameters.get('hrtf_enabled', False) reverb_send = max(0.0, min(1.0, parameters.get('reverb_send', 0.0))) # 计算距离 distance = (source_pos - listener_pos).length() # 计算方位角和仰角 azimuth, elevation = self._calculate_angles(source_pos, listener_pos, listener_orient) # 计算音量衰减 volume = self._calculate_volume_attenuation( distance, min_distance, max_distance, rolloff_factor) # 计算锥形效应 cone_gain = self._calculate_cone_gain( source_pos, listener_pos, listener_orient, cone_inner_angle, cone_outer_angle, cone_outer_gain) # 计算遮挡和阻挡 occlusion_gain = 1.0 - occlusion * 0.8 # 最多减少80%音量 obstruction_gain = 1.0 - obstruction * 0.5 # 最多减少50%音量 # 计算多普勒效应 doppler_shift = self._calculate_doppler_shift( source_pos, listener_pos, doppler_factor) # 计算空气吸收 air_absorption_gain = 1.0 - (distance / max_distance) * air_absorption * 0.5 # 综合增益 total_gain = (volume * cone_gain * occlusion_gain * obstruction_gain * air_absorption_gain) # 创建输出数组 if audio_data.ndim == 1: # 单声道输入,生成立体声输出 output_left = np.zeros_like(audio_data) output_right = np.zeros_like(audio_data) for i in range(len(audio_data)): sample = audio_data[i] # 应用增益 processed_sample = sample * total_gain # 应用HRTF或简单立体声处理 if hrtf_enabled: # 简化的HRTF应用 left_sample, right_sample = self._apply_hrtf( processed_sample, azimuth, elevation) else: # 简单的立体声平移 left_sample, right_sample = self._simple_stereo_pan( processed_sample, azimuth, spread) output_left[i] = left_sample output_right[i] = right_sample # 合并为立体声数组 output_data = np.array([output_left, output_right]) else: # 多声道处理 if audio_data.shape[0] >= 2: # 已经是立体声,应用空间效果 output_left = np.zeros_like(audio_data[0]) output_right = np.zeros_like(audio_data[1]) for i in range(audio_data.shape[1]): left_sample = audio_data[0, i] * total_gain right_sample = audio_data[1, i] * total_gain # 应用HRTF或简单立体声处理 if hrtf_enabled: # 简化的HRTF应用 new_left, new_right = self._apply_hrtf( (left_sample + right_sample) * 0.5, azimuth, elevation) output_left[i] = new_left output_right[i] = new_right else: # 简单的立体声平移 new_left, new_right = self._simple_stereo_pan( (left_sample + right_sample) * 0.5, azimuth, spread) output_left[i] = new_left output_right[i] = new_right output_data = np.array([output_left, output_right]) else: # 其他多声道情况,返回原始数据 output_data = audio_data # 确保输出在有效范围内 processed_data = np.clip(output_data, -1.0, 1.0) return processed_data def _calculate_angles(self, source_pos: Vec3, listener_pos: Vec3, listener_orient: Tuple[float, float, float]) -> Tuple[float, float]: """ 计算方位角和仰角 Args: source_pos: 音源位置 listener_pos: 听者位置 listener_orient: 听者方向 Returns: (方位角, 仰角) (度) """ # 计算相对位置 relative_pos = source_pos - listener_pos # 计算方位角 (水平角度) azimuth = math.degrees(math.atan2(relative_pos.x, -relative_pos.y)) # 计算仰角 (垂直角度) distance_xy = math.sqrt(relative_pos.x**2 + relative_pos.y**2) elevation = math.degrees(math.atan2(relative_pos.z, distance_xy)) return azimuth, elevation def _calculate_volume_attenuation(self, distance: float, min_distance: float, max_distance: float, rolloff_factor: float) -> float: """ 计算音量衰减 Args: distance: 距离 min_distance: 最小距离 max_distance: 最大距离 rolloff_factor: 衰减因子 Returns: 音量增益 (0.0-1.0) """ if distance <= min_distance: return 1.0 elif distance >= max_distance: return 0.0 else: # 反距离衰减模型 gain = min_distance / (min_distance + rolloff_factor * (distance - min_distance)) return max(0.0, min(1.0, gain)) def _calculate_cone_gain(self, source_pos: Vec3, listener_pos: Vec3, listener_orient: Tuple[float, float, float], inner_angle: float, outer_angle: float, outer_gain: float) -> float: """ 计算锥形效应增益 Args: source_pos: 音源位置 listener_pos: 听者位置 listener_orient: 听者方向 inner_angle: 内锥角 (度) outer_angle: 外锥角 (度) outer_gain: 外锥增益 Returns: 锥形增益 (0.0-1.0) """ # 简化的锥形效应计算 # 实际应用中需要考虑音源的朝向 if inner_angle >= 360.0 and outer_angle >= 360.0: return 1.0 # 计算听者相对于音源的角度 relative_pos = listener_pos - source_pos angle = math.degrees(math.acos(relative_pos.normalized().dot(Vec3(0, -1, 0)))) if angle <= inner_angle / 2.0: return 1.0 elif angle >= outer_angle / 2.0: return outer_gain else: # 线性插值 t = (angle - inner_angle / 2.0) / (outer_angle / 2.0 - inner_angle / 2.0) return 1.0 - t * (1.0 - outer_gain) def _calculate_doppler_shift(self, source_pos: Vec3, listener_pos: Vec3, doppler_factor: float) -> float: """ 计算多普勒频移 Args: source_pos: 音源位置 listener_pos: 听者位置 doppler_factor: 多普勒因子 Returns: 频移比率 """ # 简化的多普勒效应计算 # 实际应用中需要考虑音源和听者的速度 if doppler_factor <= 0.0: return 1.0 # 计算相对速度(简化为距离变化率) relative_velocity = (source_pos - listener_pos).length() / self.sample_rate ratio = (self.speed_of_sound - doppler_factor * relative_velocity) / self.speed_of_sound return max(0.5, min(2.0, ratio)) def _apply_hrtf(self, sample: float, azimuth: float, elevation: float) -> Tuple[float, float]: """ 应用HRTF Args: sample: 输入样本 azimuth: 方位角 (度) elevation: 仰角 (度) Returns: (左声道样本, 右声道样本) """ # 简化的HRTF应用 # 实际应用中会使用真实的HRTF滤波器 # 量化角度以匹配预生成的滤波器 azimuth_quantized = int(round(azimuth / 15.0)) * 15 elevation_quantized = int(round(elevation / 15.0)) * 15 # 限制角度范围 azimuth_quantized = max(-180, min(180, azimuth_quantized)) elevation_quantized = max(-90, min(90, elevation_quantized)) # 简化的HRTF效果 left_gain = 0.5 + 0.5 * math.cos(math.radians(azimuth)) right_gain = 0.5 + 0.5 * math.cos(math.radians(-azimuth)) # 应用仰角影响 elevation_factor = math.cos(math.radians(elevation)) left_gain *= elevation_factor right_gain *= elevation_factor return sample * left_gain, sample * right_gain def _simple_stereo_pan(self, sample: float, azimuth: float, spread: float) -> Tuple[float, float]: """ 简单的立体声平移 Args: sample: 输入样本 azimuth: 方位角 (度) spread: 扩散角度 Returns: (左声道样本, 右声道样本) """ # 将角度转换为-1到1的平移值 pan = math.sin(math.radians(azimuth)) # 应用扩散 if spread > 0: spread_factor = 1.0 - spread / 180.0 pan *= spread_factor # 计算左右声道增益 left_gain = 0.5 - 0.5 * pan right_gain = 0.5 + 0.5 * pan return sample * left_gain, sample * right_gain def set_parameter(self, effect_id: str, parameter: str, value: Any) -> bool: """ 设置效果参数 Args: effect_id: 效果ID parameter: 参数名 value: 参数值 Returns: 是否设置成功 """ if effect_id not in self.effects: print(f"✗ 空间音频效果不存在: {effect_id}") return False try: self.effects[effect_id]['parameters'][parameter] = value # 如果HRTF启用状态改变,重新初始化滤波器 if parameter == 'hrtf_enabled': if value: self._initialize_hrtf_filters(self.effects[effect_id]) else: self.effects[effect_id]['hrtf_filters'] = {} # 如果是插件中的效果,也更新插件中的副本 if self.plugin and effect_id in self.plugin.effects: self.plugin.effects[effect_id]['parameters'][parameter] = value if parameter == 'hrtf_enabled': self.plugin.effects[effect_id]['hrtf_filters'] = \ self.effects[effect_id]['hrtf_filters'].copy() print(f"✓ 空间音频效果参数已设置: {effect_id}.{parameter} = {value}") return True except Exception as e: print(f"✗ 设置空间音频效果参数失败: {e}") return False def get_parameters(self, effect_id: str) -> Dict[str, Any]: """ 获取效果参数 Args: effect_id: 效果ID Returns: 参数字典 """ if effect_id not in self.effects: return {} return self.effects[effect_id]['parameters'].copy() def enable_effect(self, effect_id: str) -> bool: """ 启用空间音频效果 Args: effect_id: 效果ID Returns: 是否启用成功 """ if effect_id not in self.effects: print(f"✗ 空间音频效果不存在: {effect_id}") return False try: self.effects[effect_id]['active'] = True self.stats['active_effects'] += 1 # 如果是插件中的效果,也更新插件中的副本 if self.plugin and effect_id in self.plugin.effects: self.plugin.effects[effect_id]['active'] = True print(f"✓ 空间音频效果已启用: {effect_id}") return True except Exception as e: print(f"✗ 启用空间音频效果失败: {e}") return False def disable_effect(self, effect_id: str) -> bool: """ 禁用空间音频效果 Args: effect_id: 效果ID Returns: 是否禁用成功 """ if effect_id not in self.effects: print(f"✗ 空间音频效果不存在: {effect_id}") return False try: self.effects[effect_id]['active'] = False self.stats['active_effects'] -= 1 # 如果是插件中的效果,也更新插件中的副本 if self.plugin and effect_id in self.plugin.effects: self.plugin.effects[effect_id]['active'] = False print(f"✓ 空间音频效果已禁用: {effect_id}") return True except Exception as e: print(f"✗ 禁用空间音频效果失败: {e}") return False def cleanup(self): """清理所有资源""" self.effects.clear() self.stats = { 'total_effects': 0, 'active_effects': 0, 'effects_processed': 0, 'processing_time': 0.0, 'memory_usage': 0 } print("✓ 空间音频效果处理器资源已清理") def get_stats(self) -> Dict[str, int]: """ 获取统计信息 Returns: 统计信息字典 """ return self.stats.copy() def _get_current_time(self) -> float: """ 获取当前时间(秒) Returns: 当前时间 """ if self.plugin and self.plugin.plugin_manager and self.plugin.plugin_manager.world: return self.plugin.plugin_manager.world.globalClock.getFrameTime() import time return time.time() def update(self, dt: float): """ 更新处理器状态 Args: dt: 时间增量 """ # 这里可以更新任何需要定期更新的状态 pass def set_sample_rate(self, sample_rate: int): """ 设置采样率 Args: sample_rate: 采样率 (Hz) """ self.sample_rate = sample_rate print(f"✓ 空间音频效果处理器采样率已设置: {sample_rate} Hz") def set_source_position(self, effect_id: str, position: Tuple[float, float, float]) -> bool: """ 设置音源位置 Args: effect_id: 效果ID position: 位置 (x, y, z) Returns: 是否设置成功 """ return self.set_parameter(effect_id, 'source_position', position) def set_listener_position(self, effect_id: str, position: Tuple[float, float, float]) -> bool: """ 设置听者位置 Args: effect_id: 效果ID position: 位置 (x, y, z) Returns: 是否设置成功 """ return self.set_parameter(effect_id, 'listener_position', position) def set_listener_orientation(self, effect_id: str, orientation: Tuple[float, float, float]) -> bool: """ 设置听者方向 Args: effect_id: 效果ID orientation: 方向 (heading, pitch, roll) Returns: 是否设置成功 """ return self.set_parameter(effect_id, 'listener_orientation', orientation) def set_distance_model(self, effect_id: str, min_distance: float, max_distance: float, rolloff_factor: float) -> bool: """ 设置距离模型参数 Args: effect_id: 效果ID min_distance: 最小距离 max_distance: 最大距离 rolloff_factor: 衰减因子 Returns: 是否设置成功 """ success = True success &= self.set_parameter(effect_id, 'min_distance', max(0.1, min_distance)) success &= self.set_parameter(effect_id, 'max_distance', max(min_distance, max_distance)) success &= self.set_parameter(effect_id, 'rolloff_factor', max(0.0, rolloff_factor)) return success def set_cone_effect(self, effect_id: str, inner_angle: float, outer_angle: float, outer_gain: float) -> bool: """ 设置锥形效应参数 Args: effect_id: 效果ID inner_angle: 内锥角 (度) outer_angle: 外锥角 (度) outer_gain: 外锥增益 Returns: 是否设置成功 """ success = True success &= self.set_parameter(effect_id, 'cone_inner_angle', max(0.0, min(360.0, inner_angle))) success &= self.set_parameter(effect_id, 'cone_outer_angle', max(inner_angle, min(360.0, outer_angle))) success &= self.set_parameter(effect_id, 'cone_outer_gain', max(0.0, min(1.0, outer_gain))) return success def set_doppler_factor(self, effect_id: str, factor: float) -> bool: """ 设置多普勒因子 Args: effect_id: 效果ID factor: 多普勒因子 Returns: 是否设置成功 """ return self.set_parameter(effect_id, 'doppler_factor', max(0.0, factor)) def set_spread(self, effect_id: str, spread: float) -> bool: """ 设置扩散角度 Args: effect_id: 效果ID spread: 扩散角度 (度) Returns: 是否设置成功 """ return self.set_parameter(effect_id, 'spread', max(0.0, spread)) def set_air_absorption(self, effect_id: str, absorption: float) -> bool: """ 设置空气吸收 Args: effect_id: 效果ID absorption: 空气吸收系数 Returns: 是否设置成功 """ return self.set_parameter(effect_id, 'air_absorption', max(0.0, min(1.0, absorption))) def set_occlusion(self, effect_id: str, occlusion: float) -> bool: """ 设置遮挡系数 Args: effect_id: 效果ID occlusion: 遮挡系数 (0.0-1.0) Returns: 是否设置成功 """ return self.set_parameter(effect_id, 'occlusion', max(0.0, min(1.0, occlusion))) def set_obstruction(self, effect_id: str, obstruction: float) -> bool: """ 设置阻挡系数 Args: effect_id: 效果ID obstruction: 阻挡系数 (0.0-1.0) Returns: 是否设置成功 """ return self.set_parameter(effect_id, 'obstruction', max(0.0, min(1.0, obstruction))) def enable_hrtf(self, effect_id: str, enable: bool) -> bool: """ 启用/禁用HRTF Args: effect_id: 效果ID enable: 是否启用 Returns: 是否设置成功 """ return self.set_parameter(effect_id, 'hrtf_enabled', enable) def set_reverb_send(self, effect_id: str, send: float) -> bool: """ 设置混响发送量 Args: effect_id: 效果ID send: 混响发送量 (0.0-1.0) Returns: 是否设置成功 """ return self.set_parameter(effect_id, 'reverb_send', max(0.0, min(1.0, send))) def get_distance(self, effect_id: str) -> float: """ 获取当前距离 Args: effect_id: 效果ID Returns: 距离值 """ if effect_id not in self.effects: return 0.0 return self.effects[effect_id].get('distance', 0.0) def get_angles(self, effect_id: str) -> Tuple[float, float]: """ 获取当前角度 Args: effect_id: 效果ID Returns: (方位角, 仰角) (度) """ if effect_id not in self.effects: return (0.0, 0.0) effect = self.effects[effect_id] return (effect.get('azimuth', 0.0), effect.get('elevation', 0.0)) def create_preset(self, preset_name: str) -> Dict[str, Any]: """ 创建空间音频效果预设 Args: preset_name: 预设名称 Returns: 预设参数字典 """ presets = { 'close': { 'min_distance': 0.5, 'max_distance': 20.0, 'rolloff_factor': 1.0, 'doppler_factor': 1.0, 'air_absorption': 0.0 }, 'medium': { 'min_distance': 1.0, 'max_distance': 50.0, 'rolloff_factor': 0.8, 'doppler_factor': 0.8, 'air_absorption': 0.1 }, 'far': { 'min_distance': 5.0, 'max_distance': 100.0, 'rolloff_factor': 0.5, 'doppler_factor': 0.5, 'air_absorption': 0.3 }, 'outdoor': { 'min_distance': 2.0, 'max_distance': 200.0, 'rolloff_factor': 0.3, 'doppler_factor': 1.0, 'air_absorption': 0.5 }, 'indoor': { 'min_distance': 0.5, 'max_distance': 30.0, 'rolloff_factor': 1.2, 'doppler_factor': 0.7, 'air_absorption': 0.0 }, 'underwater': { 'min_distance': 1.0, 'max_distance': 40.0, 'rolloff_factor': 0.2, 'doppler_factor': 0.3, 'air_absorption': 0.8, 'speed_of_sound': 1500.0 # 水中声速 } } return presets.get(preset_name, {}).copy()