Files
AlgerMusicPlayer/src/renderer/services/eqService.ts
T

187 lines
5.0 KiB
TypeScript

import { Howl, Howler } from 'howler';
import Tuna from 'tunajs';
// 类型定义扩展
interface HowlSound {
_sounds: Array<{
_node: HTMLMediaElement & {
destination?: MediaElementAudioSourceNode;
};
}>;
}
export interface EQSettings {
[key: string]: number;
}
export class EQService {
private context: AudioContext | null = null;
private tuna: any = null;
private equalizer: any = null;
private source: MediaElementAudioSourceNode | null = null;
private gainNode: GainNode | null = null;
private bypass = false;
// 预设频率
private readonly frequencies = [31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000];
// 默认EQ设置
private defaultEQSettings: EQSettings = Object.fromEntries(
this.frequencies.map((f) => [f.toString(), 0])
);
constructor() {
this.loadSavedSettings();
this.bypass = localStorage.getItem('eqBypass') === 'true';
this.initializeUserGestureHandler();
}
// 初始化用户手势处理
private initializeUserGestureHandler() {
const handler = async () => {
if (this.context?.state === 'suspended') {
await this.context.resume();
}
document.removeEventListener('click', handler);
};
document.addEventListener('click', handler);
}
// 初始化音频上下文
public async setupAudioContext(howl: Howl) {
try {
// 使用Howler的现有上下文
this.context = (Howler.ctx as AudioContext) || new AudioContext();
// 初始化Howler的音频系统(如果需要)
if (!Howler.ctx) {
Howler.ctx = this.context;
Howler.masterGain = this.context.createGain();
Howler.masterGain.connect(this.context.destination);
}
// 确保上下文处于运行状态
if (this.context.state === 'suspended') {
await this.context.resume();
}
const sound = (howl as unknown as HowlSound)._sounds[0];
if (!sound?._node) throw new Error('无法获取音频节点');
// 清理现有资源
await this.dispose();
// 创建新的处理链
this.tuna = new Tuna(this.context);
// 创建/复用源节点
if (!sound._node.destination) {
this.source = this.context.createMediaElementSource(sound._node);
sound._node.destination = this.source;
} else {
this.source = sound._node.destination;
}
// 创建效果节点
this.gainNode = this.context.createGain();
this.equalizer = new this.tuna.Equalizer({
frequencies: this.frequencies,
gains: this.frequencies.map((f) => this.getSavedGain(f.toString())),
bypass: this.bypass
});
// 连接节点链
this.source!.connect(this.equalizer.input).connect(this.gainNode).connect(Howler.masterGain);
// 恢复音量设置
const volume = localStorage.getItem('volume');
this.gainNode.gain.value = volume ? parseFloat(volume) : 1;
} catch (error) {
console.error('音频上下文初始化失败:', error);
await this.dispose();
throw error;
}
}
// EQ功能开关
public setEnabled(enabled: boolean) {
this.bypass = !enabled;
localStorage.setItem('eqBypass', JSON.stringify(this.bypass));
if (this.equalizer) this.equalizer.bypass = this.bypass;
}
public isEnabled(): boolean {
return !this.bypass;
}
// 调整频率增益
public setFrequencyGain(frequency: string, gain: number) {
const index = this.frequencies.findIndex((f) => f.toString() === frequency);
if (index !== -1 && this.equalizer) {
this.equalizer.setGain(index, gain);
this.saveSettings(frequency, gain);
}
}
// 重置EQ设置
public resetEQ() {
this.frequencies.forEach((f) => {
this.setFrequencyGain(f.toString(), 0);
});
localStorage.removeItem('eqSettings');
}
// 获取当前设置
public getAllSettings(): EQSettings {
return this.loadSavedSettings();
}
// 保存/加载设置
private saveSettings(frequency: string, gain: number) {
const settings = this.loadSavedSettings();
settings[frequency] = gain;
localStorage.setItem('eqSettings', JSON.stringify(settings));
}
private loadSavedSettings(): EQSettings {
const saved = localStorage.getItem('eqSettings');
return saved ? JSON.parse(saved) : { ...this.defaultEQSettings };
}
private getSavedGain(frequency: string): number {
return this.loadSavedSettings()[frequency] || 0;
}
// 清理资源
public async dispose() {
try {
[this.source, this.equalizer, this.gainNode].forEach((node) => {
if (node) {
node.disconnect();
// 特殊清理Tuna节点
if (node instanceof Tuna.Equalizer) node.destroy();
}
});
if (this.context && this.context !== Howler.ctx) {
await this.context.close();
}
this.context = null;
this.tuna = null;
this.source = null;
this.equalizer = null;
this.gainNode = null;
} catch (error) {
console.error('资源清理失败:', error);
}
}
}
export const eqService = new EQService();