EG/plugins/user/spatial_audio/plugin.py
2025-12-12 16:16:15 +08:00

648 lines
20 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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)