981 lines
34 KiB
Python
981 lines
34 KiB
Python
"""
|
||
空间音频效果处理器
|
||
实现专业的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()
|
||
|