648 lines
19 KiB
Python
648 lines
19 KiB
Python
"""
|
||
3D空间音效插件主文件
|
||
提供基于Panda3D的3D音频处理功能,支持空间音效、环境音效和音频效果处理
|
||
"""
|
||
|
||
import math
|
||
from typing import Dict, List, Optional, Any
|
||
from plugins.plugin_manager import BasePlugin
|
||
from panda3d.core import AudioSound, NodePath
|
||
from direct.showbase.AudioManager import AudioManager
|
||
|
||
class Plugin(BasePlugin):
|
||
"""
|
||
3D空间音效插件
|
||
提供基于Panda3D的3D音频处理功能
|
||
"""
|
||
|
||
def __init__(self, plugin_manager, name):
|
||
super().__init__(plugin_manager, name)
|
||
self.config = {
|
||
"version": "1.0.0",
|
||
"author": "EG Team",
|
||
"description": "3D空间音效插件,支持3D定位音频、环境音效和音频效果处理"
|
||
}
|
||
|
||
# 核心组件
|
||
self.audio_manager = None
|
||
self.sound_sources = {} # 音频源管理
|
||
self.listener = None # 听者节点
|
||
self.audio_effects = {} # 音频效果管理
|
||
self.environment_zones = {} # 环境区域管理
|
||
|
||
# GUI元素
|
||
self.gui_elements = []
|
||
|
||
# 事件处理器
|
||
self.event_handlers = {}
|
||
|
||
# 插件状态
|
||
self.is_initialized = False
|
||
self.is_enabled = False
|
||
|
||
# 性能监控
|
||
self.performance_stats = {
|
||
'active_sounds': 0,
|
||
'total_sounds': 0,
|
||
'effects_applied': 0
|
||
}
|
||
|
||
def initialize(self) -> bool:
|
||
"""
|
||
初始化插件
|
||
"""
|
||
try:
|
||
if self.is_initialized:
|
||
return True
|
||
|
||
print(f"初始化3D空间音效插件: {self.name}")
|
||
|
||
# 初始化音频管理器
|
||
self.audio_manager = self.plugin_manager.world.audio_manager
|
||
|
||
# 初始化听者
|
||
self.listener = self.plugin_manager.world.camera
|
||
|
||
# 初始化音频源管理器
|
||
from .core.audio_source_manager import AudioSourceManager
|
||
self.audio_source_manager = AudioSourceManager(self)
|
||
|
||
# 初始化音频效果处理器
|
||
from .effects.audio_effect_processor import AudioEffectProcessor
|
||
self.audio_effect_processor = AudioEffectProcessor(self)
|
||
|
||
# 初始化环境音频管理器
|
||
from .core.environment_audio import EnvironmentAudioManager
|
||
self.environment_audio_manager = EnvironmentAudioManager(self)
|
||
|
||
self.is_initialized = True
|
||
print("✓ 3D空间音效插件初始化完成")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 3D空间音效插件初始化失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def enable(self) -> bool:
|
||
"""
|
||
启用插件
|
||
"""
|
||
if not super().enable():
|
||
return False
|
||
|
||
if self.is_enabled:
|
||
return True
|
||
|
||
try:
|
||
print(f"启用3D空间音效插件: {self.name}")
|
||
|
||
# 注册事件处理器
|
||
self._register_event_handlers()
|
||
|
||
# 创建GUI界面
|
||
self._create_gui()
|
||
|
||
self.is_enabled = True
|
||
print("✓ 3D空间音效插件启用成功")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 3D空间音效插件启用失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def disable(self) -> bool:
|
||
"""
|
||
禁用插件
|
||
"""
|
||
if not super().disable():
|
||
return False
|
||
|
||
if not self.is_enabled:
|
||
return True
|
||
|
||
try:
|
||
print(f"禁用3D空间音效插件: {self.name}")
|
||
|
||
# 清理GUI界面
|
||
self._cleanup_gui()
|
||
|
||
# 移除事件处理器
|
||
self._unregister_event_handlers()
|
||
|
||
# 停止所有音频
|
||
self.stop_all_sounds()
|
||
|
||
self.is_enabled = False
|
||
print("✓ 3D空间音效插件禁用成功")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"✗ 3D空间音效插件禁用失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def finalize(self):
|
||
"""
|
||
清理插件资源
|
||
"""
|
||
print(f"清理3D空间音效插件资源: {self.name}")
|
||
|
||
# 停止所有音频
|
||
self.stop_all_sounds()
|
||
|
||
# 清理所有组件
|
||
if self.audio_source_manager:
|
||
self.audio_source_manager.cleanup()
|
||
self.audio_source_manager = None
|
||
|
||
if self.audio_effect_processor:
|
||
self.audio_effect_processor.cleanup()
|
||
self.audio_effect_processor = None
|
||
|
||
if self.environment_audio_manager:
|
||
self.environment_audio_manager.cleanup()
|
||
self.environment_audio_manager = None
|
||
|
||
self.audio_manager = None
|
||
self.listener = None
|
||
self.sound_sources.clear()
|
||
self.audio_effects.clear()
|
||
self.environment_zones.clear()
|
||
|
||
self.is_initialized = False
|
||
self.is_enabled = False
|
||
|
||
def _register_event_handlers(self):
|
||
"""
|
||
注册事件处理器
|
||
"""
|
||
world = self.plugin_manager.world
|
||
|
||
# 3D音频系统控制快捷键
|
||
self.event_handlers['f1'] = self.toggle_audio_debug
|
||
self.event_handlers['f2'] = self.toggle_spatial_audio
|
||
self.event_handlers['f3'] = self.play_test_sound
|
||
self.event_handlers['f4'] = self.stop_all_sounds
|
||
self.event_handlers['f5'] = self.show_audio_stats
|
||
self.event_handlers['f6'] = self.toggle_environment_audio
|
||
self.event_handlers['f7'] = self.apply_reverb_effect
|
||
self.event_handlers['f8'] = self.apply_echo_effect
|
||
|
||
# 注册事件
|
||
for event, handler in self.event_handlers.items():
|
||
world.accept(event, handler)
|
||
world.accept(event.upper(), handler) # 同时注册大写事件
|
||
|
||
def _unregister_event_handlers(self):
|
||
"""
|
||
移除事件处理器
|
||
"""
|
||
world = self.plugin_manager.world
|
||
|
||
for event in self.event_handlers.keys():
|
||
world.ignore(event)
|
||
world.ignore(event.upper())
|
||
|
||
self.event_handlers.clear()
|
||
|
||
def _create_gui(self):
|
||
"""
|
||
创建GUI界面
|
||
"""
|
||
try:
|
||
world = self.plugin_manager.world
|
||
|
||
# 检查是否有GUI管理器
|
||
if not hasattr(world, 'gui_manager') or not world.gui_manager:
|
||
print("⚠ GUI管理器不可用,跳过GUI创建")
|
||
return
|
||
|
||
gui_manager = world.gui_manager
|
||
|
||
# 创建3D音频控制面板
|
||
panel_button = gui_manager.createGUIButton(
|
||
pos=(0.02, 0, 0.35),
|
||
text="3D音频",
|
||
size=0.06
|
||
)
|
||
self.gui_elements.append(panel_button)
|
||
|
||
# 创建功能按钮
|
||
functions = [
|
||
("音频调试 [F1]", self.toggle_audio_debug),
|
||
("空间音频 [F2]", self.toggle_spatial_audio),
|
||
("测试音效 [F3]", self.play_test_sound),
|
||
("停止音频 [F4]", self.stop_all_sounds),
|
||
("音频统计 [F5]", self.show_audio_stats),
|
||
("环境音频 [F6]", self.toggle_environment_audio),
|
||
("混响效果 [F7]", self.apply_reverb_effect),
|
||
("回声效果 [F8]", self.apply_echo_effect)
|
||
]
|
||
|
||
for i, (name, callback) in enumerate(functions):
|
||
button = gui_manager.createGUIButton(
|
||
pos=(0.02, 0, 0.28 - i * 0.055),
|
||
text=name,
|
||
size=0.045
|
||
)
|
||
self.gui_elements.append(button)
|
||
world.accept(f"audio_function_{name}", callback)
|
||
|
||
except Exception as e:
|
||
print(f"⚠ GUI创建失败: {e}")
|
||
|
||
def _cleanup_gui(self):
|
||
"""
|
||
清理GUI界面
|
||
"""
|
||
try:
|
||
world = self.plugin_manager.world
|
||
|
||
if hasattr(world, 'gui_manager') and world.gui_manager:
|
||
gui_manager = world.gui_manager
|
||
|
||
# 删除所有GUI元素
|
||
for element in self.gui_elements:
|
||
gui_manager.deleteGUIElement(element)
|
||
|
||
self.gui_elements.clear()
|
||
|
||
except Exception as e:
|
||
print(f"⚠ GUI清理失败: {e}")
|
||
|
||
# 事件处理方法
|
||
def toggle_audio_debug(self):
|
||
"""切换音频调试模式"""
|
||
print("✓ 音频调试模式切换")
|
||
|
||
def toggle_spatial_audio(self):
|
||
"""切换空间音频"""
|
||
print("✓ 空间音频切换")
|
||
|
||
def play_test_sound(self):
|
||
"""播放测试音效"""
|
||
print("✓ 播放测试音效")
|
||
|
||
def stop_all_sounds(self):
|
||
"""停止所有音频"""
|
||
if self.audio_source_manager:
|
||
self.audio_source_manager.stop_all_sounds()
|
||
print("✓ 停止所有音频")
|
||
|
||
def show_audio_stats(self):
|
||
"""显示音频统计信息"""
|
||
stats = self.get_stats()
|
||
print("=== 3D音频统计信息 ===")
|
||
for key, value in stats.items():
|
||
print(f" {key}: {value}")
|
||
|
||
def toggle_environment_audio(self):
|
||
"""切换环境音频"""
|
||
if self.environment_audio_manager:
|
||
self.environment_audio_manager.toggle_enabled()
|
||
print("✓ 环境音频切换")
|
||
|
||
def apply_reverb_effect(self):
|
||
"""应用混响效果"""
|
||
print("✓ 应用混响效果")
|
||
|
||
def apply_echo_effect(self):
|
||
"""应用回声效果"""
|
||
print("✓ 应用回声效果")
|
||
|
||
def get_stats(self) -> Dict[str, Any]:
|
||
"""
|
||
获取插件统计信息
|
||
|
||
Returns:
|
||
统计信息字典
|
||
"""
|
||
stats = self.performance_stats.copy()
|
||
if self.audio_source_manager:
|
||
stats.update(self.audio_source_manager.get_stats())
|
||
if self.audio_effect_processor:
|
||
stats.update(self.audio_effect_processor.get_stats())
|
||
if self.environment_audio_manager:
|
||
stats.update(self.environment_audio_manager.get_stats())
|
||
return stats
|
||
|
||
# 公共接口方法
|
||
def create_3d_sound(self, filepath: str, position: tuple = (0, 0, 0),
|
||
loop: bool = False, volume: float = 1.0) -> Optional[str]:
|
||
"""
|
||
创建3D音效
|
||
|
||
Args:
|
||
filepath: 音频文件路径
|
||
position: 音源位置 (x, y, z)
|
||
loop: 是否循环播放
|
||
volume: 音量 (0.0-1.0)
|
||
|
||
Returns:
|
||
音源ID,如果创建失败则返回None
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.create_3d_sound(filepath, position, loop, volume)
|
||
return None
|
||
|
||
def create_2d_sound(self, filepath: str, loop: bool = False, volume: float = 1.0) -> Optional[str]:
|
||
"""
|
||
创建2D音效
|
||
|
||
Args:
|
||
filepath: 音频文件路径
|
||
loop: 是否循环播放
|
||
volume: 音量 (0.0-1.0)
|
||
|
||
Returns:
|
||
音源ID,如果创建失败则返回None
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.create_2d_sound(filepath, loop, volume)
|
||
return None
|
||
|
||
def play_sound(self, sound_id: str) -> bool:
|
||
"""
|
||
播放音效
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
|
||
Returns:
|
||
是否播放成功
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.play_sound(sound_id)
|
||
return False
|
||
|
||
def stop_sound(self, sound_id: str) -> bool:
|
||
"""
|
||
停止音效
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
|
||
Returns:
|
||
是否停止成功
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.stop_sound(sound_id)
|
||
return False
|
||
|
||
def pause_sound(self, sound_id: str) -> bool:
|
||
"""
|
||
暂停音效
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
|
||
Returns:
|
||
是否暂停成功
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.pause_sound(sound_id)
|
||
return False
|
||
|
||
def resume_sound(self, sound_id: str) -> bool:
|
||
"""
|
||
恢复音效
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
|
||
Returns:
|
||
是否恢复成功
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.resume_sound(sound_id)
|
||
return False
|
||
|
||
def set_sound_position(self, sound_id: str, position: tuple) -> bool:
|
||
"""
|
||
设置音源位置
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
position: 位置 (x, y, z)
|
||
|
||
Returns:
|
||
是否设置成功
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.set_sound_position(sound_id, position)
|
||
return False
|
||
|
||
def set_sound_volume(self, sound_id: str, volume: float) -> bool:
|
||
"""
|
||
设置音源音量
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
volume: 音量 (0.0-1.0)
|
||
|
||
Returns:
|
||
是否设置成功
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.set_sound_volume(sound_id, volume)
|
||
return False
|
||
|
||
def set_sound_loop(self, sound_id: str, loop: bool) -> bool:
|
||
"""
|
||
设置音源循环模式
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
loop: 是否循环
|
||
|
||
Returns:
|
||
是否设置成功
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.set_sound_loop(sound_id, loop)
|
||
return False
|
||
|
||
def is_sound_playing(self, sound_id: str) -> bool:
|
||
"""
|
||
检查音效是否正在播放
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
|
||
Returns:
|
||
是否正在播放
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.is_sound_playing(sound_id)
|
||
return False
|
||
|
||
def get_sound_duration(self, sound_id: str) -> float:
|
||
"""
|
||
获取音效时长
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
|
||
Returns:
|
||
音效时长(秒)
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.get_sound_duration(sound_id)
|
||
return 0.0
|
||
|
||
def get_sound_position(self, sound_id: str) -> tuple:
|
||
"""
|
||
获取音源位置
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
|
||
Returns:
|
||
音源位置 (x, y, z)
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.get_sound_position(sound_id)
|
||
return (0, 0, 0)
|
||
|
||
def delete_sound(self, sound_id: str) -> bool:
|
||
"""
|
||
删除音效
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
|
||
Returns:
|
||
是否删除成功
|
||
"""
|
||
if self.audio_source_manager:
|
||
return self.audio_source_manager.delete_sound(sound_id)
|
||
return False
|
||
|
||
def create_audio_effect(self, effect_type: str, parameters: Dict[str, Any]) -> str:
|
||
"""
|
||
创建音频效果
|
||
|
||
Args:
|
||
effect_type: 效果类型
|
||
parameters: 效果参数
|
||
|
||
Returns:
|
||
效果ID
|
||
"""
|
||
if self.audio_effect_processor:
|
||
return self.audio_effect_processor.create_effect(effect_type, parameters)
|
||
return ""
|
||
|
||
def apply_effect_to_sound(self, sound_id: str, effect_id: str) -> bool:
|
||
"""
|
||
应用效果到音效
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
effect_id: 效果ID
|
||
|
||
Returns:
|
||
是否应用成功
|
||
"""
|
||
if self.audio_effect_processor:
|
||
return self.audio_effect_processor.apply_effect_to_sound(sound_id, effect_id)
|
||
return False
|
||
|
||
def remove_effect_from_sound(self, sound_id: str, effect_id: str) -> bool:
|
||
"""
|
||
从音效移除效果
|
||
|
||
Args:
|
||
sound_id: 音源ID
|
||
effect_id: 效果ID
|
||
|
||
Returns:
|
||
是否移除成功
|
||
"""
|
||
if self.audio_effect_processor:
|
||
return self.audio_effect_processor.remove_effect_from_sound(sound_id, effect_id)
|
||
return False
|
||
|
||
def set_listener_position(self, position: tuple) -> bool:
|
||
"""
|
||
设置听者位置
|
||
|
||
Args:
|
||
position: 位置 (x, y, z)
|
||
|
||
Returns:
|
||
是否设置成功
|
||
"""
|
||
if self.listener:
|
||
self.listener.setPos(position[0], position[1], position[2])
|
||
return True
|
||
return False
|
||
|
||
def set_listener_orientation(self, orientation: tuple) -> bool:
|
||
"""
|
||
设置听者方向
|
||
|
||
Args:
|
||
orientation: 方向 (heading, pitch, roll)
|
||
|
||
Returns:
|
||
是否设置成功
|
||
"""
|
||
if self.listener:
|
||
self.listener.setHpr(orientation[0], orientation[1], orientation[2])
|
||
return True
|
||
return False
|
||
|
||
def create_environment_zone(self, zone_id: str, position: tuple,
|
||
radius: float, properties: Dict[str, Any]) -> bool:
|
||
"""
|
||
创建环境区域
|
||
|
||
Args:
|
||
zone_id: 区域ID
|
||
position: 位置 (x, y, z)
|
||
radius: 半径
|
||
properties: 区域属性
|
||
|
||
Returns:
|
||
是否创建成功
|
||
"""
|
||
if self.environment_audio_manager:
|
||
return self.environment_audio_manager.create_zone(zone_id, position, radius, properties)
|
||
return False
|
||
|
||
def set_zone_property(self, zone_id: str, property_name: str, value: Any) -> bool:
|
||
"""
|
||
设置区域属性
|
||
|
||
Args:
|
||
zone_id: 区域ID
|
||
property_name: 属性名
|
||
value: 属性值
|
||
|
||
Returns:
|
||
是否设置成功
|
||
"""
|
||
if self.environment_audio_manager:
|
||
return self.environment_audio_manager.set_zone_property(zone_id, property_name, value)
|
||
return False
|
||
|
||
def update(self, dt: float):
|
||
"""
|
||
更新插件状态
|
||
|
||
Args:
|
||
dt: 时间增量
|
||
"""
|
||
# 更新音频源管理器
|
||
if self.audio_source_manager:
|
||
self.audio_source_manager.update(dt)
|
||
|
||
# 更新环境音频管理器
|
||
if self.environment_audio_manager:
|
||
self.environment_audio_manager.update(dt) |