Răsfoiți Sursa

优化均衡器效果

onecold 4 luni în urmă
părinte
comite
f28d6844e9

+ 50 - 27
entry/src/main/ets/common/util/EqualizerManager.ets

@@ -21,6 +21,11 @@ export interface EqualizerPreset {
   isCustom: boolean;     // 是否为自定义预设
 }
 
+export interface EqualizerGainStats {
+  maxPositiveGain: number;
+  positiveGainSum: number;
+}
+
 export class EqualizerManager {
   private static instance: EqualizerManager | null = null;
   private static readonly TAG: string = 'heanup EqualizerManager';
@@ -45,18 +50,18 @@ export class EqualizerManager {
     { frequency: 16000, label: '16kHz', gain: 0 }
   ];
 
-  // 内置预设 - 增益值增大以获得更明显的效果
+  // 内置预设 - 以音色校正为主,避免多频段同时大幅正增益导致失真
   public static readonly BUILT_IN_PRESETS: EqualizerPreset[] = [
     { id: 'normal', name: '正常', gains: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], isCustom: false },
-    { id: 'pop', name: '流行', gains: [-2, 3, 6, 6, 3, 0, -2, -2, -2, -2], isCustom: false },
-    { id: 'rock', name: '摇滚', gains: [6, 5, 2, 0, -2, 0, 3, 5, 6, 6], isCustom: false },
-    { id: 'jazz', name: '爵士', gains: [5, 3, 2, 3, -3, -3, 0, 2, 3, 5], isCustom: false },
-    { id: 'classical', name: '古典', gains: [6, 5, 3, 2, -2, -2, 0, 3, 5, 6], isCustom: false },
-    { id: 'bass_boost', name: '低音增强', gains: [10, 8, 6, 3, 0, 0, 0, 0, 0, 0], isCustom: false },
-    { id: 'treble_boost', name: '高音增强', gains: [0, 0, 0, 0, 0, 2, 4, 6, 8, 10], isCustom: false },
-    { id: 'vocal', name: '人声增强', gains: [-4, -2, 0, 4, 6, 6, 4, 0, -2, -4], isCustom: false },
-    { id: 'dance', name: '舞曲', gains: [8, 6, 3, 0, 0, -3, -2, 3, 5, 6], isCustom: false },
-    { id: 'acoustic', name: '原声', gains: [5, 4, 2, 2, 3, 3, 3, 5, 4, 2], isCustom: false }
+    { id: 'pop', name: '流行', gains: [-1.5, -0.5, 1.5, 2.5, 1.5, 0.5, -0.5, -1, -1, -1.5], isCustom: false },
+    { id: 'rock', name: '摇滚', gains: [2.5, 1.5, 0.5, -0.5, -1, 0, 1.5, 2.5, 3, 2.5], isCustom: false },
+    { id: 'jazz', name: '爵士', gains: [1.5, 1, 0.5, 1, -0.5, -1, 0.5, 1.5, 2, 1.5], isCustom: false },
+    { id: 'classical', name: '古典', gains: [0.5, 0, -0.5, -1, -1.5, 0.5, 1.5, 2.5, 2, 1], isCustom: false },
+    { id: 'bass_boost', name: '低音增强', gains: [4.5, 3.5, 2.5, 1.5, 0.5, -0.5, -1, -1, -1, -1], isCustom: false },
+    { id: 'treble_boost', name: '高音增强', gains: [-1, -1, -0.5, 0, 0.5, 1, 2, 3, 4, 4.5], isCustom: false },
+    { id: 'vocal', name: '人声增强', gains: [-2, -1, -0.5, 1, 2.5, 3, 2.5, 1, -0.5, -1.5], isCustom: false },
+    { id: 'dance', name: '舞曲', gains: [3.5, 2.5, 1.5, 0, -0.5, 0.5, 1.5, 2.5, 2, 1], isCustom: false },
+    { id: 'acoustic', name: '原声', gains: [1, 0.5, 0, 0.5, 1, 1.5, 1.5, 1, 0.5, 0], isCustom: false }
   ];
 
   // 当前状态
@@ -170,6 +175,20 @@ export class EqualizerManager {
     return this._customGains.slice();
   }
 
+  private clampGainValue(gain: number): number {
+    let clampedGain = gain;
+    if (!Number.isFinite(clampedGain)) {
+      clampedGain = 0;
+    }
+    if (clampedGain < -12) {
+      clampedGain = -12;
+    }
+    if (clampedGain > 12) {
+      clampedGain = 12;
+    }
+    return Math.round(clampedGain * 10) / 10;
+  }
+
   // 获取所有预设(内置 + 自定义)
   getAllPresets(): EqualizerPreset[] {
     const result: EqualizerPreset[] = [];
@@ -205,6 +224,25 @@ export class EqualizerManager {
     return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
   }
 
+  getCurrentGainStats(): EqualizerGainStats {
+    const gains = this.getCurrentGains();
+    let maxPositiveGain = 0;
+    let positiveGainSum = 0;
+    for (let i = 0; i < gains.length; i++) {
+      const gain = gains[i];
+      if (gain > 0) {
+        positiveGainSum += gain;
+        if (gain > maxPositiveGain) {
+          maxPositiveGain = gain;
+        }
+      }
+    }
+    return {
+      maxPositiveGain: Math.round(maxPositiveGain * 10) / 10,
+      positiveGainSum: Math.round(positiveGainSum * 10) / 10
+    };
+  }
+
   // 设置预设
   setPreset(presetId: string): void {
     this._currentPresetId = presetId;
@@ -217,15 +255,7 @@ export class EqualizerManager {
     if (bandIndex < 0 || bandIndex >= 10) {
       return;
     }
-    // 限制增益范围 -12 到 +12
-    let clampedGain = gain;
-    if (clampedGain < -12) {
-      clampedGain = -12;
-    }
-    if (clampedGain > 12) {
-      clampedGain = 12;
-    }
-    this._customGains[bandIndex] = clampedGain;
+    this._customGains[bandIndex] = this.clampGainValue(gain);
     this._currentPresetId = 'custom';
     this.saveState();
   }
@@ -236,14 +266,7 @@ export class EqualizerManager {
       return;
     }
     for (let i = 0; i < 10; i++) {
-      let g = gains[i];
-      if (g < -12) {
-        g = -12;
-      }
-      if (g > 12) {
-        g = 12;
-      }
-      this._customGains[i] = g;
+      this._customGains[i] = this.clampGainValue(gains[i]);
     }
     this._currentPresetId = 'custom';
     this.saveState();

+ 21 - 10
entry/src/main/ets/view/EqualizerView.ets

@@ -40,6 +40,17 @@ function clampEqGain(value: number): number {
   return Math.round(value * 10) / 10;
 }
 
+function snapEqGainToHalfStep(value: number): number {
+  return Math.round(clampEqGain(value) * 2) / 2;
+}
+
+function formatEqGainValue(value: number): string {
+  const normalized = Math.round(clampEqGain(value) * 10) / 10;
+  const hasDecimal = Math.abs(normalized - Math.round(normalized)) > 0.001;
+  const displayValue = hasDecimal ? normalized.toFixed(1) : Math.round(normalized).toString();
+  return `${normalized > 0 ? '+' : ''}${displayValue}`;
+}
+
 function parseEqNumberList(content: string): number[] {
   const matches = content.match(/[-+]?\d+(?:\.\d+)?/g);
   if (!matches || matches.length < 10) {
@@ -200,9 +211,9 @@ export struct EqualizerView {
 
   private onGainChange(bandIndex: number, gain: number): void {
     const newGains = this.gains.slice();
-    newGains[bandIndex] = gain;
+    newGains[bandIndex] = snapEqGainToHalfStep(gain);
     this.gains = newGains;
-    this.equalizerManager.setCustomGain(bandIndex, gain);
+    this.equalizerManager.setCustomGain(bandIndex, newGains[bandIndex]);
     this.currentPresetId = 'custom';
     this.selectedPresetIndex = -1;
     this.notifyEqualizerChanged();
@@ -328,7 +339,7 @@ export struct EqualizerView {
   private BandSlider(band: EqualizerBand, index: number): void {
     Column() {
       // 增益值显示
-      Text(`${this.gains[index] > 0 ? '+' : ''}${this.gains[index]}`)
+      Text(formatEqGainValue(this.gains[index]))
         .fontSize(11)
         .fontColor(this.themeColor)
         .fontWeight(500)
@@ -340,7 +351,7 @@ export struct EqualizerView {
           value: this.gains[index],
           min: -12,
           max: 12,
-          step: 1,
+          step: 0.5,
           style: SliderStyle.OutSet,
           direction: Axis.Vertical,
           reverse: true
@@ -353,7 +364,7 @@ export struct EqualizerView {
           .trackThickness(4)
           .blockSize({ width: 16, height: 16 })
           .onChange((value: number) => {
-            this.onGainChange(index, Math.round(value));
+            this.onGainChange(index, value);
           })
       }
       .height(130)
@@ -586,9 +597,9 @@ export struct EqualizerViewWithCallback {
 
   private onGainChange(bandIndex: number, gain: number): void {
     const newGains = this.gains.slice();
-    newGains[bandIndex] = gain;
+    newGains[bandIndex] = snapEqGainToHalfStep(gain);
     this.gains = newGains;
-    this.equalizerManager.setCustomGain(bandIndex, gain);
+    this.equalizerManager.setCustomGain(bandIndex, newGains[bandIndex]);
     this.currentPresetId = 'custom';
     this.selectedPresetIndex = -1;
     this.notifyChange();
@@ -981,7 +992,7 @@ export struct EqualizerViewWithCallback {
   @Builder
   private BandSlider(band: EqualizerBand, index: number): void {
     Column() {
-      Text(`${this.gains[index] > 0 ? '+' : ''}${this.gains[index]}`)
+      Text(formatEqGainValue(this.gains[index]))
         .fontSize(11)
         .fontColor(this.themeColor)
         .fontWeight(500)
@@ -992,7 +1003,7 @@ export struct EqualizerViewWithCallback {
           value: this.gains[index],
           min: -12,
           max: 12,
-          step: 1,
+          step: 0.5,
           style: SliderStyle.OutSet,
           direction: Axis.Vertical,
           reverse: true
@@ -1005,7 +1016,7 @@ export struct EqualizerViewWithCallback {
           .trackThickness(4)
           .blockSize({ width: 16, height: 16 })
           .onChange((value: number) => {
-            this.onGainChange(index, Math.round(value));
+            this.onGainChange(index, value);
           })
       }
       .height(150)

+ 28 - 4
entry/src/main/ets/view/LocalMusic.ets

@@ -16147,15 +16147,37 @@ export struct LocalMusic {
   @State isShowSpectrum: boolean = false//是否显示频谱
   @State spectrumModeIndex: number = 0 //选中的频谱特效索引
 
+  private getEqualizerVolumeBoostRisk(): number {
+    const equalizerManager = EqualizerManager.getInstance();
+    if (!equalizerManager.enabled) {
+      return 0;
+    }
+    const gainStats = equalizerManager.getCurrentGainStats();
+    if (gainStats.maxPositiveGain <= 0 || gainStats.positiveGainSum <= 0) {
+      return 0;
+    }
+    const peakRisk = (gainStats.maxPositiveGain / 12) * 0.65;
+    const spreadRisk = (gainStats.positiveGainSum / 24) * 0.35;
+    return Math.max(0, Math.min(peakRisk + spreadRisk, 1));
+  }
+
   private getSafeBoostPercent(): number {
     const rawBoostPercent = Math.max(0, Math.min(this.volumeBoostPercent, this.volumeBoostMaxPercent));
+    let compressedBoostPercent = rawBoostPercent;
     if (rawBoostPercent <= 150) {
-      return rawBoostPercent;
+      compressedBoostPercent = rawBoostPercent;
+    } else if (rawBoostPercent <= 300) {
+      compressedBoostPercent = 150 + (rawBoostPercent - 150) * 0.45;
+    } else {
+      compressedBoostPercent = 217.5 + (rawBoostPercent - 300) * 0.15;
     }
-    if (rawBoostPercent <= 300) {
-      return 150 + (rawBoostPercent - 150) * 0.45;
+    const equalizerRisk = this.getEqualizerVolumeBoostRisk();
+    if (equalizerRisk <= 0) {
+      return compressedBoostPercent;
     }
-    return 217.5 + (rawBoostPercent - 300) * 0.15;
+    const protectedBoostPercent = compressedBoostPercent * (1 - equalizerRisk * 0.45);
+    const cappedBoostPercent = 220 - equalizerRisk * 140;
+    return Math.max(0, Math.min(protectedBoostPercent, cappedBoostPercent));
   }
 
   private getVolumeBoostFactor(): number {
@@ -16344,6 +16366,7 @@ export struct LocalMusic {
     try {
       const equalizerManager = EqualizerManager.getInstance();
       equalizerManager.applyToPlayer(this.mIjkMediaPlayer);
+      this.applyCurrentVolume();
       this.markEqualizerApplied();
       ToastUtil.showToast('均衡器已实时生效');
     } catch (error) {
@@ -16395,6 +16418,7 @@ export struct LocalMusic {
             if (this.mIjkMediaPlayer) {
               const equalizerManager = EqualizerManager.getInstance();
               equalizerManager.applyToPlayer(this.mIjkMediaPlayer);
+              this.applyCurrentVolume();
               this.markEqualizerApplied();
             }
           }

+ 79 - 7
ijkplayer/src/main/cpp/ijksdl/audio/ijksdl_equalizer.c

@@ -31,6 +31,33 @@ static inline int16_t clamp_sample(float sample) {
     return (int16_t)sample;
 }
 
+static inline float clamp_unit(float sample) {
+    if (sample > 1.0f) return 1.0f;
+    if (sample < -1.0f) return -1.0f;
+    return sample;
+}
+
+static inline float soft_clip_unit(float sample) {
+    const float abs_sample = fabsf(sample);
+    const float knee_start = 0.92f;
+    if (abs_sample <= knee_start) {
+        return sample;
+    }
+
+    const float over = abs_sample - knee_start;
+    const float compressed = knee_start + (0.08f * over) / (0.08f + over);
+    return sample >= 0.0f ? compressed : -compressed;
+}
+
+static inline int16_t soft_clip_sample(float sample) {
+    if (!isfinite(sample)) {
+        return 0;
+    }
+    const float normalized = sample / 32768.0f;
+    const float limited = soft_clip_unit(normalized);
+    return clamp_sample(clamp_unit(limited) * 32767.0f);
+}
+
 /**
  * 计算 Peaking EQ Biquad 滤波器系数
  * 基于 Audio EQ Cookbook
@@ -77,6 +104,42 @@ static void calculate_peaking_eq_coefficients(BiquadFilter *filter, int sample_r
     filter->a2 = a2 / a0;
 }
 
+static void update_equalizer_headroom(Equalizer *eq) {
+    if (eq == NULL) {
+        return;
+    }
+
+    float max_positive_gain = 0.0f;
+    float positive_gain_sum = 0.0f;
+    int active_band_count = 0;
+    for (int i = 0; i < EQ_BAND_COUNT; ++i) {
+        const float gain_db = eq->bands[i].gain_db;
+        if (fabsf(gain_db) >= 0.1f) {
+            active_band_count++;
+        }
+        if (gain_db > 0.0f) {
+            positive_gain_sum += gain_db;
+            if (gain_db > max_positive_gain) {
+                max_positive_gain = gain_db;
+            }
+        }
+    }
+
+    eq->active_band_count = active_band_count;
+    eq->positive_gain_sum_db = positive_gain_sum;
+    eq->max_positive_gain_db = max_positive_gain;
+
+    float headroom_db = max_positive_gain * 0.60f + positive_gain_sum * 0.08f;
+    if (active_band_count > 0 && max_positive_gain > 0.0f && headroom_db < 0.75f) {
+        headroom_db = 0.75f;
+    }
+    if (headroom_db > 9.0f) {
+        headroom_db = 9.0f;
+    }
+
+    eq->preamp_linear = powf(10.0f, -headroom_db / 20.0f);
+}
+
 /**
  * 重置单个滤波器状态
  */
@@ -134,6 +197,10 @@ void equalizer_init(Equalizer *eq, int sample_rate, int channels) {
     eq->sample_rate = sample_rate;
     eq->channels = channels;
     eq->enabled = false;
+    eq->active_band_count = 0;
+    eq->preamp_linear = 1.0f;
+    eq->positive_gain_sum_db = 0.0f;
+    eq->max_positive_gain_db = 0.0f;
     eq->initialized = true;
     
     // 初始化每个频段
@@ -141,7 +208,7 @@ void equalizer_init(Equalizer *eq, int sample_rate, int channels) {
         BiquadFilter *filter = &eq->bands[i];
         filter->frequency = EQ_CENTER_FREQUENCIES[i];
         filter->gain_db = 0.0f;
-        filter->q = EQ_DEFAULT_Q;
+        filter->q = EQ_BAND_Q_VALUES[i];
         reset_filter_state(filter);
         calculate_peaking_eq_coefficients(filter, sample_rate);
     }
@@ -158,6 +225,7 @@ void equalizer_set_gains(Equalizer *eq, const float *gains) {
     for (int i = 0; i < EQ_BAND_COUNT; i++) {
         equalizer_set_band_gain(eq, i, gains[i]);
     }
+    update_equalizer_headroom(eq);
 }
 
 void equalizer_set_band_gain(Equalizer *eq, int band_index, float gain_db) {
@@ -171,6 +239,7 @@ void equalizer_set_band_gain(Equalizer *eq, int band_index, float gain_db) {
         filter->gain_db = clamped_gain;
         calculate_peaking_eq_coefficients(filter, eq->sample_rate);
     }
+    update_equalizer_headroom(eq);
 }
 
 void equalizer_process_s16(Equalizer *eq, int16_t *buffer, int buffer_size) {
@@ -184,12 +253,15 @@ void equalizer_process_s16(Equalizer *eq, int16_t *buffer, int buffer_size) {
 
     int sample_count = buffer_size / (sizeof(int16_t) * channels);
     if (sample_count <= 0) return;
+    if (eq->active_band_count <= 0) return;
+
+    const float preamp = eq->preamp_linear > 0.0f ? eq->preamp_linear : 1.0f;
 
     if (channels == 2) {
         // 立体声处理
         for (int i = 0; i < sample_count; i++) {
-            float left = (float)buffer[i * 2];
-            float right = (float)buffer[i * 2 + 1];
+            float left = (float)buffer[i * 2] * preamp;
+            float right = (float)buffer[i * 2 + 1] * preamp;
             
             // 通过所有频段滤波器
             for (int b = 0; b < EQ_BAND_COUNT; b++) {
@@ -201,13 +273,13 @@ void equalizer_process_s16(Equalizer *eq, int16_t *buffer, int buffer_size) {
                 right = process_sample_right(filter, right);
             }
             
-            buffer[i * 2] = clamp_sample(left);
-            buffer[i * 2 + 1] = clamp_sample(right);
+            buffer[i * 2] = soft_clip_sample(left);
+            buffer[i * 2 + 1] = soft_clip_sample(right);
         }
     } else {
         // 单声道处理
         for (int i = 0; i < sample_count; i++) {
-            float sample = (float)buffer[i];
+            float sample = (float)buffer[i] * preamp;
             
             for (int b = 0; b < EQ_BAND_COUNT; b++) {
                 BiquadFilter *filter = &eq->bands[b];
@@ -216,7 +288,7 @@ void equalizer_process_s16(Equalizer *eq, int16_t *buffer, int buffer_size) {
                 sample = process_sample_left(filter, sample);
             }
             
-            buffer[i] = clamp_sample(sample);
+            buffer[i] = soft_clip_sample(sample);
         }
     }
 }

+ 9 - 4
ijkplayer/src/main/cpp/ijksdl/audio/ijksdl_equalizer.h

@@ -25,10 +25,11 @@ static const float EQ_CENTER_FREQUENCIES[EQ_BAND_COUNT] = {
     1000.0f, 2000.0f, 4000.0f, 8000.0f, 16000.0f
 };
 
-// 滤波器带宽 (Q factor)
-// Q 值越小,每个频段影响的范围越宽
-// 对于 10 频段均衡器,推荐 0.707 ~ 1.0
-#define EQ_DEFAULT_Q 0.707f
+// 不同频段使用不同的 Q,低频更宽,中频更聚焦,高频适度放宽
+static const float EQ_BAND_Q_VALUES[EQ_BAND_COUNT] = {
+    0.78f, 0.82f, 0.90f, 0.92f, 0.95f,
+    1.05f, 1.12f, 1.08f, 0.98f, 0.92f
+};
 
 // 单个 Biquad 滤波器状态
 typedef struct BiquadFilter {
@@ -54,6 +55,10 @@ typedef struct Equalizer {
     bool enabled;
     int sample_rate;
     int channels;
+    int active_band_count;
+    float preamp_linear;
+    float positive_gain_sum_db;
+    float max_positive_gain_db;
     bool initialized;
 } Equalizer;