Appearance
音效播放器 API
概述
音效播放器(SoundPlayer)是 Eagle2Ae AE 扩展的一个实用工具类,负责播放连接成功、断开连接和其他操作相关的音效。该播放器提供了预加载、音量控制、测试播放等功能,为用户提供更好的听觉反馈。
核心特性
音效预加载
- 自动预加载所有音效文件
- 支持音效文件错误处理
- 提供音效文件加载状态检查
音量控制
- 统一音量控制接口
- 支持0.0-1.0范围的音量设置
- 实时音量调整
多音效支持
- 连接成功音效
- 断开连接音效
- Eagle导入音效
- 自定义音效播放
技术实现
核心类结构
javascript
/**
* 音效播放器类
* 用于播放连接成功和断开连接的音效
*/
class SoundPlayer {
/**
* 构造函数
*/
constructor() {
this.soundsPath = this.getSoundsPath();
this.sounds = {
linked: 'linked.wav',
stop: 'stop.wav',
eagle: 'eagle.wav'
};
// 预加载音效文件
this.preloadSounds();
}
}音效路径管理
javascript
/**
* 获取音效文件路径
* @returns {string} 音效文件路径
*/
getSoundsPath() {
try {
// 获取扩展根目录
const extensionPath = this.getExtensionPath();
return extensionPath + '/public/sound/';
} catch (error) {
console.warn('获取音效路径失败:', error);
return './public/sound/';
}
}
/**
* 获取扩展路径
* @returns {string} 扩展路径
*/
getExtensionPath() {
try {
// 在CEP环境中获取扩展路径
if (typeof window !== 'undefined' && window.__adobe_cep__) {
const csInterface = new CSInterface();
return csInterface.getSystemPath(SystemPath.EXTENSION);
}
// 备用方法:通过当前脚本路径推断
const currentScript = document.currentScript || document.querySelector('script[src*="main.js"]');
if (currentScript && currentScript.src) {
const scriptPath = currentScript.src;
const extensionPath = scriptPath.substring(0, scriptPath.lastIndexOf('/js/'));
return extensionPath;
}
return '.';
} catch (error) {
console.warn('获取扩展路径失败:', error);
return '.';
}
}音效预加载
javascript
/**
* 预加载音效文件
*/
preloadSounds() {
try {
this.audioElements = {};
for (const [key, filename] of Object.entries(this.sounds)) {
const audio = new Audio();
audio.preload = 'auto';
audio.volume = 0.6; // 设置音量为60%
audio.src = this.soundsPath + filename;
// 添加错误处理
audio.addEventListener('error', (e) => {
console.warn(`音效文件加载失败: ${filename}`, e);
});
audio.addEventListener('canplaythrough', () => {
console.log(`音效文件已预加载: ${filename}`);
});
this.audioElements[key] = audio;
}
console.log('音效播放器初始化完成');
} catch (error) {
console.warn('预加载音效失败:', error);
}
}音效播放实现
javascript
/**
* 播放连接成功音效
*/
playLinkedSound() {
this.playSound('linked', '🔗 播放连接成功音效');
}
/**
* 播放断开连接音效
*/
playStopSound() {
this.playSound('stop', '🔌 播放断开连接音效');
}
/**
* 播放Eagle导入音效
*/
playEagleSound() {
this.playSound('eagle', '🦅 播放Eagle导入音效');
}
/**
* 播放指定音效
* @param {string} soundKey - 音效键名
* @param {string} logMessage - 日志消息
*/
playSound(soundKey, logMessage = '') {
try {
if (!this.audioElements || !this.audioElements[soundKey]) {
console.warn(`音效不存在: ${soundKey}`);
return;
}
const audio = this.audioElements[soundKey];
// 重置播放位置
audio.currentTime = 0;
// 播放音效
const playPromise = audio.play();
if (playPromise !== undefined) {
playPromise
.then(() => {
if (logMessage) {
console.log(logMessage);
}
})
.catch((error) => {
console.warn(`播放音效失败 (${soundKey}):`, error);
});
}
} catch (error) {
console.warn(`播放音效出错 (${soundKey}):`, error);
}
}API参考
构造函数
javascript
/**
* 音效播放器构造函数
*/
constructor()核心方法
playLinkedSound()
播放连接成功音效
javascript
/**
* 播放连接成功音效
*/
playLinkedSound()playStopSound()
播放断开连接音效
javascript
/**
* 播放断开连接音效
*/
playStopSound()playEagleSound()
播放Eagle导入音效
javascript
/**
* 播放Eagle导入音效
*/
playEagleSound()playSound()
播放指定音效
javascript
/**
* 播放指定音效
* @param {string} soundKey - 音效键名
* @param {string} logMessage - 日志消息
*/
playSound(soundKey, logMessage = '')setVolume()
设置音量
javascript
/**
* 设置音量 (0.0 - 1.0)
* @param {number} volume - 音量值
*/
setVolume(volume)testSounds()
测试音效播放
javascript
/**
* 测试音效播放
*/
testSounds()checkSoundFiles()
检查音效文件是否存在
javascript
/**
* 检查音效文件是否存在
* @returns {Object} 音效文件检查结果
*/
checkSoundFiles()使用示例
基本使用
javascript
// 创建音效播放器实例
const soundPlayer = new SoundPlayer();
// 播放连接成功音效
soundPlayer.playLinkedSound();
// 播放断开连接音效
soundPlayer.playStopSound();
// 播放Eagle导入音效
soundPlayer.playEagleSound();音量控制
javascript
// 创建音效播放器实例
const soundPlayer = new SoundPlayer();
// 设置音量为30%
soundPlayer.setVolume(0.3);
// 播放音效(将以30%音量播放)
soundPlayer.playLinkedSound();
// 调整音量为80%
soundPlayer.setVolume(0.8);
// 播放音效(将以80%音量播放)
soundPlayer.playStopSound();音效测试
javascript
// 创建音效播放器实例
const soundPlayer = new SoundPlayer();
// 测试所有音效
soundPlayer.testSounds();
// 检查音效文件加载状态
const soundFiles = soundPlayer.checkSoundFiles();
console.log('音效文件状态:', soundFiles);自定义音效播放
javascript
// 创建音效播放器实例
const soundPlayer = new SoundPlayer();
// 直接播放指定音效
soundPlayer.playSound('linked', '播放自定义消息');
// 在特定事件中播放音效
function onConnectionSuccess() {
soundPlayer.playLinkedSound();
console.log('连接成功');
}
function onConnectionLost() {
soundPlayer.playStopSound();
console.log('连接断开');
}
function onEagleImport() {
soundPlayer.playEagleSound();
console.log('Eagle导入完成');
}最佳实践
性能优化
预加载音效
javascript// 在应用初始化时预加载音效 const soundPlayer = new SoundPlayer(); // 自动预加载 // 检查音效是否已加载 const soundFiles = soundPlayer.checkSoundFiles(); if (soundFiles.linked.loaded) { console.log('连接音效已就绪'); }合理控制音量
javascript// 根据用户设置调整音量 const userVolume = getUserPreference('soundVolume') || 0.6; soundPlayer.setVolume(userVolume);避免频繁播放
javascript// 使用防抖避免频繁播放相同音效 let soundTimeout; function playSoundWithDebounce(soundPlayer, soundMethod) { if (soundTimeout) clearTimeout(soundTimeout); soundTimeout = setTimeout(() => { soundPlayer[soundMethod](); }, 100); } // 使用示例 playSoundWithDebounce(soundPlayer, 'playLinkedSound');
错误处理
音效文件缺失处理
javascript// 检查音效文件是否存在 const soundFiles = soundPlayer.checkSoundFiles(); // 为缺失的音效提供降级方案 if (!soundFiles.linked.loaded) { console.log('连接音效文件缺失,使用系统提示音'); // 使用系统提示音或其他替代方案 }播放失败处理
javascript// 在播放音效时捕获错误 try { soundPlayer.playLinkedSound(); } catch (error) { console.warn('播放音效失败:', error); // 提供视觉反馈作为替代 showVisualNotification('连接成功'); }
内存管理
及时清理资源
javascript// 在应用关闭时清理音频资源 window.addEventListener('beforeunload', () => { if (soundPlayer && soundPlayer.audioElements) { // 停止所有正在播放的音效 Object.values(soundPlayer.audioElements).forEach(audio => { if (!audio.paused) { audio.pause(); audio.currentTime = 0; } }); } });避免内存泄漏
javascript// 在组件销毁时清理事件监听器 function cleanupSoundPlayer(soundPlayer) { if (soundPlayer && soundPlayer.audioElements) { Object.values(soundPlayer.audioElements).forEach(audio => { // 移除事件监听器 audio.removeEventListener('error'); audio.removeEventListener('canplaythrough'); // 重置音频元素 audio.src = ''; audio.load(); }); } }
故障排除
常见问题
音效无法播放
- 症状:调用播放方法后无声音输出
- 解决:
- 检查音效文件是否存在
- 验证音频格式是否支持
- 确认系统音量设置
- 检查浏览器音频策略
音效文件加载失败
- 症状:控制台显示音效文件加载错误
- 解决:
- 检查音效文件路径是否正确
- 验证文件权限
- 确认文件格式是否正确
- 检查网络连接状态
音量控制无效
- 症状:调用setVolume后音量未改变
- 解决:
- 检查音量值范围是否正确(0.0-1.0)
- 验证音频元素是否正确引用
- 确认浏览器音量控制策略
调试技巧
启用详细日志
javascript
// 在控制台中启用详细日志
localStorage.setItem('debugLogLevel', '0');
// 监控音效播放状态
soundPlayer.playLinkedSound = function() {
console.log('准备播放连接音效');
// 原始实现
SoundPlayer.prototype.playLinkedSound.call(this);
};性能监控
javascript
// 监控音效播放性能
function monitorSoundPerformance(soundPlayer, methodName) {
const originalMethod = soundPlayer[methodName];
soundPlayer[methodName] = function(...args) {
const startTime = performance.now();
const result = originalMethod.apply(this, args);
const endTime = performance.now();
console.log(`${methodName} 执行耗时: ${endTime - startTime}ms`);
return result;
};
}
// 使用示例
monitorSoundPerformance(soundPlayer, 'playLinkedSound');内存使用监控
javascript
// 监控音频元素内存使用
function logAudioMemoryUsage(soundPlayer) {
if (soundPlayer.audioElements) {
const audioElements = Object.values(soundPlayer.audioElements);
console.log(`音频元素数量: ${audioElements.length}`);
// 检查每个音频元素的状态
audioElements.forEach((audio, index) => {
console.log(`音频元素 ${index}:`, {
src: audio.src,
duration: audio.duration,
currentTime: audio.currentTime,
paused: audio.paused,
readyState: audio.readyState
});
});
}
}扩展性
自定义音效
javascript
// 扩展音效播放器以支持更多音效
class ExtendedSoundPlayer extends SoundPlayer {
constructor() {
super();
// 添加自定义音效
this.sounds.custom = 'custom.wav';
this.sounds.alert = 'alert.wav';
// 重新预加载
this.preloadSounds();
}
/**
* 播放自定义音效
*/
playCustomSound() {
this.playSound('custom', '🎵 播放自定义音效');
}
/**
* 播放警告音效
*/
playAlertSound() {
this.playSound('alert', '⚠️ 播放警告音效');
}
}
// 使用扩展的音效播放器
const extendedSoundPlayer = new ExtendedSoundPlayer();
extendedSoundPlayer.playCustomSound();
extendedSoundPlayer.playAlertSound();音效事件系统
javascript
// 为音效播放器添加事件系统
class EventedSoundPlayer extends SoundPlayer {
constructor() {
super();
this.eventListeners = {};
}
/**
* 添加事件监听器
* @param {string} event - 事件名称
* @param {Function} callback - 回调函数
*/
on(event, callback) {
if (!this.eventListeners[event]) {
this.eventListeners[event] = [];
}
this.eventListeners[event].push(callback);
}
/**
* 触发事件
* @param {string} event - 事件名称
* @param {Object} data - 事件数据
*/
emit(event, data) {
if (this.eventListeners[event]) {
this.eventListeners[event].forEach(callback => {
try {
callback(data);
} catch (error) {
console.error('事件回调执行失败:', error);
}
});
}
}
/**
* 重写播放方法以触发事件
*/
playSound(soundKey, logMessage = '') {
// 触发播放开始事件
this.emit('sound:play:start', { soundKey, logMessage });
try {
super.playSound(soundKey, logMessage);
// 触发播放完成事件
this.emit('sound:play:complete', { soundKey, logMessage });
} catch (error) {
// 触发播放错误事件
this.emit('sound:play:error', { soundKey, error, logMessage });
}
}
}
// 使用带事件的音效播放器
const eventedSoundPlayer = new EventedSoundPlayer();
// 监听音效播放事件
eventedSoundPlayer.on('sound:play:start', (data) => {
console.log('开始播放音效:', data.soundKey);
});
eventedSoundPlayer.on('sound:play:complete', (data) => {
console.log('音效播放完成:', data.soundKey);
});
eventedSoundPlayer.on('sound:play:error', (data) => {
console.error('音效播放出错:', data.soundKey, data.error);
});
// 播放音效将触发相应事件
eventedSoundPlayer.playLinkedSound();