neverlib 0.2.3__py3-none-any.whl → 0.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. neverlib/.history/Docs/audio_aug/test_snr_20250806011311.py +0 -0
  2. neverlib/.history/Docs/audio_aug/test_snr_20250806011331.py +75 -0
  3. neverlib/.history/Docs/audio_aug/test_snr_20250806011342.py +57 -0
  4. neverlib/.history/Docs/audio_aug/test_snr_20250806011352.py +57 -0
  5. neverlib/.history/Docs/audio_aug/test_snr_20250806011403.py +57 -0
  6. neverlib/.history/Docs/audio_aug/test_snr_20250806011413.py +57 -0
  7. neverlib/.history/Docs/audio_aug/test_snr_20250806011435.py +55 -0
  8. neverlib/.history/Docs/vad/1_20250810032405.py +0 -0
  9. neverlib/.history/Docs/vad/1_20250810032417.py +39 -0
  10. neverlib/.history/audio_aug/audio_aug_20250806010451.py +125 -0
  11. neverlib/.history/audio_aug/audio_aug_20250806010750.py +138 -0
  12. neverlib/.history/audio_aug/audio_aug_20250806010759.py +140 -0
  13. neverlib/.history/audio_aug/audio_aug_20250806010803.py +140 -0
  14. neverlib/.history/audio_aug/audio_aug_20250806010809.py +140 -0
  15. neverlib/.history/audio_aug/audio_aug_20250806011108.py +140 -0
  16. neverlib/.history/dataAnalyze/__init___20250806204125.py +14 -0
  17. neverlib/.history/dataAnalyze/__init___20250806204139.py +14 -0
  18. neverlib/.history/dataAnalyze/__init___20250806204159.py +14 -0
  19. neverlib/.history/filter/__init___20250820103351.py +70 -0
  20. neverlib/.history/filter/__init___20250821102348.py +70 -0
  21. neverlib/.history/filter/__init___20250821102405.py +14 -0
  22. neverlib/.history/filter/auto_eq/__init___20250819213121.py +36 -0
  23. neverlib/.history/filter/auto_eq/__init___20250821102241.py +36 -0
  24. neverlib/.history/filter/auto_eq/__init___20250821102259.py +36 -0
  25. neverlib/.history/filter/auto_eq/__init___20250821102307.py +36 -0
  26. neverlib/.history/filter/auto_eq/__init___20250821102310.py +36 -0
  27. neverlib/.history/filter/auto_eq/__init___20250821102318.py +36 -0
  28. neverlib/.history/filter/auto_eq/__init___20250821102507.py +36 -0
  29. neverlib/{filter/AudoEQ/auto_eq_de.py → .history/filter/auto_eq/de_eq_20250820103848.py} +1 -1
  30. neverlib/.history/filter/auto_eq/de_eq_20250821102422.py +360 -0
  31. neverlib/.history/filter/auto_eq/freq_eq_20250820140732.py +75 -0
  32. neverlib/.history/filter/auto_eq/freq_eq_20250820140745.py +75 -0
  33. neverlib/.history/filter/auto_eq/freq_eq_20250820140816.py +75 -0
  34. neverlib/.history/filter/auto_eq/freq_eq_20250820140938.py +77 -0
  35. neverlib/.history/filter/auto_eq/freq_eq_20250820141003.py +77 -0
  36. neverlib/.history/filter/auto_eq/freq_eq_20250820141006.py +77 -0
  37. neverlib/.history/filter/auto_eq/freq_eq_20250820141019.py +77 -0
  38. neverlib/.history/filter/auto_eq/freq_eq_20250820141049.py +77 -0
  39. neverlib/.history/filter/auto_eq/freq_eq_20250820141211.py +77 -0
  40. neverlib/.history/filter/auto_eq/freq_eq_20250820141227.py +77 -0
  41. neverlib/.history/filter/auto_eq/freq_eq_20250820141311.py +78 -0
  42. neverlib/.history/filter/auto_eq/freq_eq_20250820141340.py +78 -0
  43. neverlib/.history/filter/auto_eq/freq_eq_20250820141712.py +78 -0
  44. neverlib/.history/filter/auto_eq/freq_eq_20250820141733.py +78 -0
  45. neverlib/.history/filter/auto_eq/freq_eq_20250820141755.py +78 -0
  46. neverlib/.history/filter/auto_eq/freq_eq_20250821102434.py +76 -0
  47. neverlib/.history/filter/auto_eq/freq_eq_20250821102500.py +76 -0
  48. neverlib/.history/filter/auto_eq/freq_eq_20250821102502.py +76 -0
  49. neverlib/{filter/AudoEQ/auto_eq_ga_basic.py → .history/filter/auto_eq/ga_eq_basic_20250820102957.py} +1 -1
  50. neverlib/.history/filter/auto_eq/ga_eq_basic_20250820113054.py +380 -0
  51. neverlib/.history/filter/auto_eq/ga_eq_basic_20250820113150.py +380 -0
  52. neverlib/.history/filter/auto_eq/ga_eq_basic_20250820113520.py +385 -0
  53. neverlib/.history/filter/auto_eq/ga_eq_basic_20250820113525.py +385 -0
  54. neverlib/.history/filter/auto_eq/ga_eq_basic_20250821102212.py +385 -0
  55. neverlib/.history/metrics/dnsmos_20250806001612.py +160 -0
  56. neverlib/.history/metrics/dnsmos_20250815180659.py +160 -0
  57. neverlib/.history/metrics/dnsmos_20250815180701.py +158 -0
  58. neverlib/.history/metrics/dnsmos_20250815181321.py +154 -0
  59. neverlib/.history/metrics/dnsmos_20250815181327.py +154 -0
  60. neverlib/.history/metrics/dnsmos_20250815181331.py +154 -0
  61. neverlib/.history/metrics/dnsmos_20250815181620.py +154 -0
  62. neverlib/.history/metrics/dnsmos_20250815181631.py +154 -0
  63. neverlib/.history/metrics/dnsmos_20250815181742.py +154 -0
  64. neverlib/.history/metrics/dnsmos_20250815181824.py +153 -0
  65. neverlib/.history/metrics/dnsmos_20250815181834.py +153 -0
  66. neverlib/.history/metrics/dnsmos_20250815181922.py +153 -0
  67. neverlib/.history/metrics/dnsmos_20250815182011.py +147 -0
  68. neverlib/.history/metrics/dnsmos_20250815182036.py +144 -0
  69. neverlib/.history/metrics/dnsmos_20250815182936.py +143 -0
  70. neverlib/.history/metrics/dnsmos_20250815182942.py +143 -0
  71. neverlib/.history/metrics/dnsmos_20250815183032.py +137 -0
  72. neverlib/.history/metrics/dnsmos_20250815183101.py +144 -0
  73. neverlib/.history/metrics/dnsmos_20250815183121.py +144 -0
  74. neverlib/.history/metrics/dnsmos_20250815183123.py +143 -0
  75. neverlib/.history/metrics/dnsmos_20250815183214.py +143 -0
  76. neverlib/.history/metrics/dnsmos_20250815183240.py +143 -0
  77. neverlib/.history/metrics/dnsmos_20250815183248.py +144 -0
  78. neverlib/.history/metrics/dnsmos_20250815183407.py +142 -0
  79. neverlib/.history/metrics/dnsmos_20250815183409.py +142 -0
  80. neverlib/.history/metrics/dnsmos_20250815183431.py +142 -0
  81. neverlib/.history/metrics/dnsmos_20250815183507.py +140 -0
  82. neverlib/.history/metrics/dnsmos_20250815183513.py +139 -0
  83. neverlib/.history/metrics/dnsmos_20250815183618.py +139 -0
  84. neverlib/.history/metrics/dnsmos_20250815183709.py +140 -0
  85. neverlib/.history/metrics/dnsmos_20250815183756.py +137 -0
  86. neverlib/.history/metrics/dnsmos_20250815183815.py +128 -0
  87. neverlib/.history/metrics/dnsmos_20250815183827.py +129 -0
  88. neverlib/.history/metrics/dnsmos_20250815183913.py +117 -0
  89. neverlib/.history/metrics/dnsmos_20250815183914.py +117 -0
  90. neverlib/.history/metrics/dnsmos_20250815184003.py +118 -0
  91. neverlib/.history/metrics/dnsmos_20250815184040.py +118 -0
  92. neverlib/.history/metrics/dnsmos_20250815184049.py +118 -0
  93. neverlib/.history/metrics/dnsmos_20250815184104.py +117 -0
  94. neverlib/.history/metrics/dnsmos_20250815184200.py +117 -0
  95. neverlib/.history/metrics/lpc_lsp_metric_20250816015944.py +128 -0
  96. neverlib/.history/metrics/lpc_lsp_metric_20250816020142.py +128 -0
  97. neverlib/.history/metrics/lpc_lsp_metric_20250816020156.py +128 -0
  98. neverlib/.history/metrics/lpc_lsp_metric_20250816020554.py +130 -0
  99. neverlib/.history/metrics/lpc_lsp_metric_20250816020600.py +125 -0
  100. neverlib/.history/metrics/lpc_lsp_metric_20250816020631.py +120 -0
  101. neverlib/.history/metrics/lpc_lsp_metric_20250816020746.py +118 -0
  102. neverlib/.history/metrics/lpc_me_20250816013111.py +0 -0
  103. neverlib/.history/metrics/lpc_me_20250816013129.py +121 -0
  104. neverlib/.history/metrics/lpc_me_20250816015430.py +103 -0
  105. neverlib/.history/metrics/lpc_me_20250816015535.py +96 -0
  106. neverlib/.history/metrics/lpc_me_20250816015542.py +96 -0
  107. neverlib/.history/metrics/lpc_me_20250816015636.py +97 -0
  108. neverlib/.history/metrics/lpc_me_20250816015658.py +104 -0
  109. neverlib/.history/metrics/lpc_me_20250816015703.py +100 -0
  110. neverlib/.history/metrics/lpc_me_20250816015945.py +128 -0
  111. neverlib/.history/metrics/snr_20250806010538.py +177 -0
  112. neverlib/.history/metrics/snr_20250806211634.py +184 -0
  113. neverlib/.history/metrics/spec_20250805234209.py +45 -0
  114. neverlib/.history/metrics/spec_20250816135530.py +11 -0
  115. neverlib/.history/metrics/spec_20250816135654.py +16 -0
  116. neverlib/.history/metrics/spec_20250816135736.py +68 -0
  117. neverlib/.history/metrics/spec_20250816135904.py +75 -0
  118. neverlib/.history/metrics/spec_20250816135921.py +82 -0
  119. neverlib/.history/metrics/spec_20250816140111.py +82 -0
  120. neverlib/.history/metrics/spec_20250816140543.py +136 -0
  121. neverlib/.history/metrics/spec_20250816140559.py +172 -0
  122. neverlib/.history/metrics/spec_20250816140602.py +172 -0
  123. neverlib/.history/metrics/spec_20250816140608.py +172 -0
  124. neverlib/.history/metrics/spec_20250816140654.py +148 -0
  125. neverlib/.history/metrics/spec_20250816140705.py +144 -0
  126. neverlib/.history/metrics/spec_20250816140755.py +138 -0
  127. neverlib/.history/metrics/spec_20250816140823.py +170 -0
  128. neverlib/.history/metrics/spec_20250816140832.py +170 -0
  129. neverlib/.history/metrics/spec_20250816140833.py +170 -0
  130. neverlib/.history/metrics/spec_20250816140922.py +147 -0
  131. neverlib/.history/metrics/spec_20250816141148.py +107 -0
  132. neverlib/.history/metrics/spec_20250816141219.py +123 -0
  133. neverlib/.history/metrics/spec_20250816141732.py +178 -0
  134. neverlib/.history/metrics/spec_20250816141740.py +178 -0
  135. neverlib/.history/metrics/spec_20250816142030.py +178 -0
  136. neverlib/.history/metrics/spec_20250816142107.py +135 -0
  137. neverlib/.history/metrics/spec_20250816142126.py +135 -0
  138. neverlib/.history/metrics/spec_20250816142410.py +135 -0
  139. neverlib/.history/metrics/spec_20250816142415.py +136 -0
  140. neverlib/.history/metrics/spec_metric_20250816135156.py +0 -0
  141. neverlib/.history/metrics/spec_metric_20250816135226.py +5 -0
  142. neverlib/.history/metrics/spec_metric_20250816135227.py +10 -0
  143. neverlib/.history/metrics/spec_metric_20250816135306.py +15 -0
  144. neverlib/.history/metrics/spec_metric_20250816135442.py +31 -0
  145. neverlib/.history/metrics/spec_metric_20250816135448.py +31 -0
  146. neverlib/.history/metrics/spec_metric_20250816135520.py +29 -0
  147. neverlib/.history/metrics/spec_metric_20250816135537.py +63 -0
  148. neverlib/.history/metrics/spec_metric_20250816135653.py +65 -0
  149. neverlib/.history/vad/PreProcess_20250805234211.py +63 -0
  150. neverlib/.history/vad/PreProcess_20250809232455.py +63 -0
  151. neverlib/.history/vad/PreProcess_20250816020725.py +66 -0
  152. neverlib/.history/vad/VAD_Silero_20250805234211.py +50 -0
  153. neverlib/.history/vad/VAD_Silero_20250809232456.py +50 -0
  154. neverlib/.history/vad/VAD_WebRTC_20250805234211.py +61 -0
  155. neverlib/.history/vad/VAD_WebRTC_20250809232456.py +61 -0
  156. neverlib/.history/vad/VAD_funasr_20250805234211.py +54 -0
  157. neverlib/.history/vad/VAD_funasr_20250809232456.py +54 -0
  158. neverlib/.history/vad/VAD_vadlib_20250805234211.py +70 -0
  159. neverlib/.history/vad/VAD_vadlib_20250809232455.py +70 -0
  160. neverlib/.history/vad/VAD_whisper_20250805234211.py +55 -0
  161. neverlib/.history/vad/VAD_whisper_20250809232456.py +55 -0
  162. neverlib/.specstory/.what-is-this.md +69 -0
  163. neverlib/.specstory/history/2025-08-05_17-06Z-/350/277/231/344/270/200/346/255/245/347/232/204/347/233/256/347/232/204/346/230/257/344/273/200/344/271/210.md +424 -0
  164. neverlib/Docs/audio_aug/test_snr.py +55 -0
  165. neverlib/audio_aug/HarmonicDistortion.py +79 -0
  166. neverlib/audio_aug/TFDrop.py +41 -0
  167. neverlib/audio_aug/TFMask.py +56 -0
  168. neverlib/audio_aug/audio_aug.py +16 -1
  169. neverlib/audio_aug/clip_aug.py +41 -0
  170. neverlib/audio_aug/coder_aug.py +209 -0
  171. neverlib/audio_aug/coder_aug2.py +118 -0
  172. neverlib/audio_aug/loss_packet_aug.py +103 -0
  173. neverlib/audio_aug/quant_aug.py +78 -0
  174. neverlib/data_analyze/__init__.py +14 -0
  175. neverlib/filter/auto_eq/__init__.py +36 -0
  176. neverlib/filter/auto_eq/de_eq.py +360 -0
  177. neverlib/filter/auto_eq/freq_eq.py +76 -0
  178. neverlib/filter/{AudoEQ/auto_eq_ga_advanced.py → auto_eq/ga_eq_advanced.py} +1 -1
  179. neverlib/filter/auto_eq/ga_eq_basic.py +385 -0
  180. neverlib/metrics/dnsmos.py +58 -101
  181. neverlib/metrics/lpc_lsp.py +118 -0
  182. neverlib/metrics/snr.py +11 -4
  183. neverlib/metrics/spec.py +136 -45
  184. neverlib/utils/utils.py +17 -14
  185. neverlib/vad/PreProcess.py +5 -2
  186. neverlib/vad/VAD_Silero.py +1 -1
  187. neverlib/vad/VAD_WebRTC.py +1 -1
  188. neverlib/vad/VAD_funasr.py +1 -1
  189. neverlib/vad/VAD_vadlib.py +1 -1
  190. neverlib/vad/VAD_whisper.py +1 -1
  191. {neverlib-0.2.3.dist-info → neverlib-0.2.4.dist-info}/METADATA +1 -1
  192. neverlib-0.2.4.dist-info/RECORD +229 -0
  193. neverlib-0.2.3.dist-info/RECORD +0 -53
  194. /neverlib/{dataAnalyze/__init__.py → .history/dataAnalyze/__init___20250805234204.py} +0 -0
  195. /neverlib/{filter/AudoEQ/auto_eq_spectral_direct.py → .history/filter/auto_eq/freq_eq_20250805234206.py} +0 -0
  196. /neverlib/{dataAnalyze → data_analyze}/README.md +0 -0
  197. /neverlib/{dataAnalyze → data_analyze}/dataset_analyzer.py +0 -0
  198. /neverlib/{dataAnalyze → data_analyze}/quality_metrics.py +0 -0
  199. /neverlib/{dataAnalyze → data_analyze}/rms_distrubution.py +0 -0
  200. /neverlib/{dataAnalyze → data_analyze}/spectral_analysis.py +0 -0
  201. /neverlib/{dataAnalyze → data_analyze}/statistics.py +0 -0
  202. /neverlib/{dataAnalyze → data_analyze}/temporal_features.py +0 -0
  203. /neverlib/{dataAnalyze → data_analyze}/visualization.py +0 -0
  204. /neverlib/filter/{AudoEQ → auto_eq}/README.md +0 -0
  205. {neverlib-0.2.3.dist-info → neverlib-0.2.4.dist-info}/WHEEL +0 -0
  206. {neverlib-0.2.3.dist-info → neverlib-0.2.4.dist-info}/licenses/LICENSE +0 -0
  207. {neverlib-0.2.3.dist-info → neverlib-0.2.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,360 @@
1
+ # -*- coding:utf-8 -*-
2
+ # Author: AI Assistant based on User's Demand
3
+ # Date: 2023-10-27 (Using Differential Evolution)
4
+ # Modified: 2025-01-05 (Adapted for new filters.py structure)
5
+ import sys
6
+ sys.path.append("..")
7
+ import numpy as np
8
+ import librosa
9
+ import soundfile as sf
10
+ from scipy import signal as sp_signal
11
+ from scipy import optimize as sp_optimize # Keep for potential 'polish' if not using internal
12
+ from scipy.optimize import differential_evolution # Import differential_evolution
13
+ import warnings
14
+ import matplotlib.pyplot as plt
15
+ from neverlib.filter import EQFilter
16
+
17
+
18
+ def get_filter_function(filter_type, fs):
19
+ """获取滤波器函数, 返回配置好采样率的EQFilter实例的方法"""
20
+ eq_filter = EQFilter(fs=fs)
21
+ filter_func_map = {
22
+ 'peak': eq_filter.PeakingFilter,
23
+ 'low_shelf': eq_filter.LowshelfFilter,
24
+ 'high_shelf': eq_filter.HighshelfFilter,
25
+ 'low_pass': eq_filter.LowpassFilter,
26
+ 'high_pass': eq_filter.HighpassFilter,
27
+ }
28
+ return filter_func_map.get(filter_type)
29
+
30
+
31
+ def _calculate_spectrum(audio_data, target_sr, n_fft, hop_length):
32
+ S = librosa.stft(audio_data, n_fft=n_fft, hop_length=hop_length, win_length=n_fft)
33
+ mag = np.mean(np.abs(S), axis=1)
34
+ epsilon = 1e-9 # IMPORTANT: Add epsilon to avoid log(0)
35
+ spec_db = 20 * np.log10(mag + epsilon)
36
+ freq_axis = librosa.fft_frequencies(sr=target_sr, n_fft=n_fft)
37
+ return spec_db, freq_axis
38
+
39
+
40
+ def _load_audio_data(audio_path, target_sr):
41
+ data, sr_orig = sf.read(audio_path, dtype='float32')
42
+ if data.ndim > 1:
43
+ data = np.mean(data, axis=1)
44
+ if sr_orig != target_sr:
45
+ data = librosa.resample(data, orig_sr=sr_orig, target_sr=target_sr)
46
+ return data
47
+
48
+
49
+ def _apply_eq_cascade(audio_data, eq_params_list, fs):
50
+ if not eq_params_list:
51
+ return audio_data
52
+ processed_audio = audio_data.copy()
53
+
54
+ for params in eq_params_list:
55
+ filter_type, fc, Q, db_gain = params['filter_type'], params['fc'], params['Q'], params.get('dBgain')
56
+ filter_func = get_filter_function(filter_type, fs)
57
+
58
+ if filter_func is None:
59
+ warnings.warn(f"Unknown filter type: {filter_type}")
60
+ continue
61
+
62
+ # 根据滤波器类型调用相应的方法
63
+ if db_gain is not None:
64
+ b, a = filter_func(fc=fc, Q=Q, dBgain=db_gain)
65
+ else:
66
+ b, a = filter_func(fc=fc, Q=Q)
67
+
68
+ if not np.issubdtype(processed_audio.dtype, np.floating):
69
+ processed_audio = processed_audio.astype(np.float32)
70
+ processed_audio = sp_signal.lfilter(b, a, processed_audio)
71
+ return processed_audio
72
+
73
+
74
+ def _objective_function(flat_params, band_definitions, target_response_db, freq_axis, fs, n_fft):
75
+ current_cascade_response_db = np.zeros_like(freq_axis)
76
+ param_idx_counter = 0
77
+
78
+ for band_def in band_definitions:
79
+ band_type = band_def['type']
80
+ # Safety check for parameter length (can happen if bounds are wrong for DE)
81
+ if param_idx_counter + 1 >= len(flat_params):
82
+ warnings.warn(
83
+ f"Parameter array too short in objective function. Expected at least {param_idx_counter + 2} elements, got {len(flat_params)}")
84
+ return np.finfo(np.float64).max # Return large error
85
+
86
+ fc, q_val = flat_params[param_idx_counter], flat_params[param_idx_counter + 1]
87
+ param_idx_counter += 2
88
+
89
+ filter_func = get_filter_function(band_type, fs)
90
+ if filter_func is None:
91
+ warnings.warn(f"Unknown filter type: {band_type}")
92
+ return np.finfo(np.float64).max
93
+
94
+ try:
95
+ if band_type in ['peak', 'low_shelf', 'high_shelf']:
96
+ if param_idx_counter >= len(flat_params):
97
+ warnings.warn(f"Parameter array too short for gain parameter in objective function.")
98
+ return np.finfo(np.float64).max
99
+ db_gain = flat_params[param_idx_counter]
100
+ param_idx_counter += 1
101
+ b, a = filter_func(fc=fc, Q=q_val, dBgain=db_gain)
102
+ else:
103
+ b, a = filter_func(fc=fc, Q=q_val)
104
+
105
+ w, h = sp_signal.freqz(b, a, worN=freq_axis, fs=fs)
106
+ # Add epsilon to avoid log(0) which results in -inf and can break mean calculation
107
+ h_abs = np.abs(h)
108
+ h_db = 20 * np.log10(h_abs + 1e-9)
109
+ current_cascade_response_db += h_db
110
+
111
+ except Exception as e:
112
+ warnings.warn(f"Error computing filter response for {band_type}: {e}")
113
+ return np.finfo(np.float64).max
114
+
115
+ error = np.mean((current_cascade_response_db - target_response_db)**2)
116
+ if np.isnan(error) or np.isinf(error): # Handle potential nan/inf from objective
117
+ # This might happen if parameters lead to unstable filters or extreme responses
118
+ # print(f"Objective function returned NaN/Inf. Current error: {error}")
119
+ # print(f"Params (first few): {flat_params[:6]}")
120
+ return np.finfo(np.float64).max # Return a very large number
121
+ return error
122
+
123
+
124
+ def _get_initial_params_and_bounds(band_definitions, fs, target_response_db, freq_axis):
125
+ x0, bounds_list = [], [] # Changed bounds to bounds_list
126
+ min_fc, max_fc = 20.0, fs / 2.0 * 0.98
127
+ num_gain_filters = sum(1 for bd in band_definitions if bd['type'] in ['peak', 'low_shelf', 'high_shelf'])
128
+ log_fcs = np.logspace(np.log10(max(min_fc, 30)), np.log10(min(max_fc, fs / 2.1)), num_gain_filters, endpoint=True) if num_gain_filters > 0 else []
129
+ gain_filter_idx = 0
130
+ for band_def in band_definitions:
131
+ band_type = band_def['type']
132
+ initial_fc = band_def.get('initial_fc')
133
+ if initial_fc is None:
134
+ if band_type in ['peak', 'low_shelf', 'high_shelf'] and gain_filter_idx < len(log_fcs):
135
+ initial_fc = log_fcs[gain_filter_idx]
136
+ elif band_type == 'low_shelf':
137
+ initial_fc = np.clip(80, min_fc, max_fc)
138
+ elif band_type == 'high_shelf':
139
+ initial_fc = np.clip(8000, min_fc, max_fc)
140
+ elif band_type == 'low_pass':
141
+ initial_fc = np.clip(fs / 2.2, min_fc, max_fc)
142
+ elif band_type == 'high_pass':
143
+ initial_fc = np.clip(40, min_fc, max_fc)
144
+ else:
145
+ initial_fc = (min_fc + max_fc) / 2
146
+ x0.append(np.clip(initial_fc, min_fc, max_fc))
147
+ bounds_list.append((min_fc, max_fc))
148
+ initial_q = band_def.get('initial_Q', 1.0 if band_type == 'peak' else 0.707)
149
+ x0.append(initial_q)
150
+ bounds_list.append((0.1, 20.0))
151
+ if band_type in ['peak', 'low_shelf', 'high_shelf']:
152
+ fc_idx = np.argmin(np.abs(freq_axis - initial_fc))
153
+ initial_gain_default = target_response_db[fc_idx] if len(target_response_db) > 0 and fc_idx < len(target_response_db) else 0.0
154
+ initial_gain = band_def.get('initial_dBgain', initial_gain_default)
155
+ x0.append(np.clip(initial_gain, -20.0, 20.0))
156
+ bounds_list.append((-30.0, 30.0))
157
+ gain_filter_idx += 1
158
+ return np.array(x0), bounds_list # Return bounds_list for differential_evolution
159
+
160
+
161
+ def plot_spectra_comparison(spectra_data, freq_axis, title="Spectra Comparison"):
162
+ plt.figure(figsize=(12, 7))
163
+ for label, spec_db in spectra_data.items():
164
+ plt.plot(freq_axis, spec_db, label=label, alpha=0.8)
165
+ plt.xscale('log') # Re-enabled log scale for frequency axis
166
+ plt.xlabel("Frequency (Hz)")
167
+ plt.ylabel("Magnitude (dB)")
168
+ plt.title(title)
169
+ plt.legend()
170
+ plt.grid(True, which="both", ls="-", alpha=0.5) # Added which="both" for log grid
171
+ if len(freq_axis) > 0:
172
+ plt.xlim([20, freq_axis[-1]])
173
+ valid_spectra = [s[np.isfinite(s)] for s in spectra_data.values() if s is not None and len(s[np.isfinite(s)]) > 0]
174
+ if valid_spectra:
175
+ min_y = min(np.min(s) for s in valid_spectra) - 10
176
+ max_y = max(np.max(s) for s in valid_spectra) + 10
177
+ if np.isfinite(min_y) and np.isfinite(max_y):
178
+ plt.ylim([min_y, max_y])
179
+ plt.tight_layout()
180
+ try:
181
+ clean_title = "".join(c if c.isalnum() or c in [' ', '_', '-'] else '_' for c in title) # Sanitize more robustly
182
+ plt.savefig(f"{clean_title.replace(' ', '_')}.png")
183
+ # if plt.isinteractive():
184
+ # plt.show()
185
+ plt.close()
186
+ except Exception as e:
187
+ print(f"Error saving/showing plot: {e}")
188
+ finally:
189
+ plt.close()
190
+
191
+
192
+ def match_frequency_response(
193
+ source_audio_path: str,
194
+ target_audio_path: str,
195
+ output_eq_audio_path: str = "source_eq_matched.wav",
196
+ num_eq_bands: int = 10,
197
+ sampling_rate: int = 16000,
198
+ fft_size: int = 1024,
199
+ hop_length_ratio: float = 0.25,
200
+ eq_band_config_list: list = None,
201
+ optimizer_options: dict = None, # For DE, e.g., {'popsize': 20, 'maxiter': 500, 'workers': -1}
202
+ plot_results: bool = True,
203
+ verbose: bool = False
204
+ ):
205
+ hop_length = int(fft_size * hop_length_ratio)
206
+ if verbose:
207
+ print(f"SR={sampling_rate}, FFT={fft_size}, Hop={hop_length}")
208
+ print("Spectrum smoothing is DISABLED.")
209
+
210
+ source_data = _load_audio_data(source_audio_path, sampling_rate)
211
+ target_data = _load_audio_data(target_audio_path, sampling_rate)
212
+
213
+ source_spec_db, freq_axis = _calculate_spectrum(source_data, sampling_rate, fft_size, hop_length)
214
+ target_spec_db, _ = _calculate_spectrum(target_data, sampling_rate, fft_size, hop_length)
215
+
216
+ target_eq_overall_response_db = target_spec_db - source_spec_db
217
+
218
+ # _get_initial_params_and_bounds returns x0 and a list of (min,max) tuples for bounds
219
+ actual_num_bands = len(eq_band_config_list)
220
+ _, de_bounds = _get_initial_params_and_bounds(eq_band_config_list, sampling_rate, target_eq_overall_response_db, freq_axis)
221
+
222
+ if verbose:
223
+ print(f"EQ bands: {len(eq_band_config_list)}, Total params: {len(de_bounds)}")
224
+
225
+ # Default options for differential_evolution
226
+ # Note: popsize is often set to N_params * 10 or 15.
227
+ # maxiter might need to be lower than for L-BFGS-B for similar runtime, or higher for better solution.
228
+ num_params_to_optimize = len(de_bounds)
229
+ default_de_options = {
230
+ 'strategy': 'best1bin',
231
+ 'maxiter': 200 * actual_num_bands if actual_num_bands > 0 else 200, # Max generations
232
+ 'popsize': 15, # Population size per generation (popsize * num_params_to_optimize evaluations per generation)
233
+ 'tol': 0.01,
234
+ 'mutation': (0.5, 1),
235
+ 'recombination': 0.7,
236
+ 'disp': verbose,
237
+ 'polish': True, # Apply a local minimizer (L-BFGS-B) at the end
238
+ 'updating': 'deferred', # For parallel processing
239
+ 'workers': -1 # Use all available CPU cores
240
+ }
241
+ if optimizer_options:
242
+ default_de_options.update(optimizer_options)
243
+
244
+ obj_args = (eq_band_config_list, target_eq_overall_response_db, freq_axis, sampling_rate, fft_size)
245
+
246
+ if verbose:
247
+ print(f"Starting Differential Evolution, options: {default_de_options} (Smoothing DISABLED)...")
248
+
249
+ result = differential_evolution(
250
+ _objective_function,
251
+ bounds=de_bounds, # Pass the list of (min, max) tuples
252
+ args=obj_args,
253
+ **default_de_options # Pass all other options as keyword arguments
254
+ )
255
+
256
+ if verbose:
257
+ print(f"DE Optimization: Success={result.success}, Msg='{result.message}', NFEV={result.nfev},nit={result.nit}, FunVal={result.fun:.4e}")
258
+
259
+ optimized_params_flat = result.x
260
+ # ... (Rest of the function: formatting parameters, applying EQ, plotting - remains the same) ...
261
+ optimized_eq_parameters_list = []
262
+ current_param_idx = 0
263
+ for i, band_def in enumerate(eq_band_config_list):
264
+ params = {'filter_type': band_def['type'], 'fs': float(sampling_rate)}
265
+ params['fc'] = float(optimized_params_flat[current_param_idx])
266
+ params['Q'] = float(optimized_params_flat[current_param_idx + 1])
267
+ current_param_idx += 2
268
+ if params['filter_type'] in ['peak', 'low_shelf', 'high_shelf']:
269
+ params['dBgain'] = float(optimized_params_flat[current_param_idx])
270
+ current_param_idx += 1
271
+ else:
272
+ params['dBgain'] = None
273
+ optimized_eq_parameters_list.append(params)
274
+
275
+ eq_audio_data = None
276
+ if output_eq_audio_path:
277
+ eq_audio_data = _apply_eq_cascade(source_data, optimized_eq_parameters_list, sampling_rate)
278
+ max_val = np.max(np.abs(eq_audio_data))
279
+ if max_val > 1.0:
280
+ eq_audio_data /= max_val
281
+ warnings.warn(f"EQ'd audio clipped (max: {max_val:.2f}), scaled.")
282
+ elif max_val == 0 and len(eq_audio_data) > 0:
283
+ warnings.warn(f"EQ'd audio is all zeros.")
284
+ sf.write(output_eq_audio_path, eq_audio_data, sampling_rate, subtype='FLOAT')
285
+ if verbose:
286
+ print(f"EQ'd audio saved: {output_eq_audio_path}")
287
+
288
+ if plot_results:
289
+ spectra_to_plot = {"Source": source_spec_db, "Target": target_spec_db}
290
+ plot_title_main = f"Spectra (DE) - {len(eq_band_config_list)} bands - No Smoothing"
291
+ eq_spec_db, _ = _calculate_spectrum(eq_audio_data, sampling_rate, fft_size, hop_length)
292
+ spectra_to_plot["EQ'd Source"] = eq_spec_db
293
+ plot_spectra_comparison(spectra_to_plot, freq_axis, title=plot_title_main)
294
+ return optimized_eq_parameters_list, eq_audio_data
295
+
296
+
297
+ # --- Example Usage ---
298
+ if __name__ == '__main__':
299
+ source_file = "../data/white.wav"
300
+ target_file = "../data/white_EQ.wav"
301
+ output_eq_file = "../data/white_EQ_matched_DE.wav"
302
+
303
+ SR = 16000
304
+ NFFT = 1024
305
+
306
+ custom_band_config = [
307
+ {'type': 'high_pass', 'initial_fc': 40, 'initial_Q': 0.7},
308
+ {'type': 'low_shelf', 'initial_fc': 150, 'initial_Q': 0.7},
309
+ {'type': 'peak', 'initial_fc': 250},
310
+ {'type': 'peak', 'initial_fc': 500},
311
+ {'type': 'peak', 'initial_fc': 750},
312
+ {'type': 'peak', 'initial_fc': 1000},
313
+ {'type': 'peak', 'initial_fc': 1500},
314
+ {'type': 'peak', 'initial_fc': 2500},
315
+ {'type': 'peak', 'initial_fc': 3500},
316
+ {'type': 'peak', 'initial_fc': 5000},
317
+ {'type': 'peak', 'initial_fc': 6500},
318
+ {'type': 'high_shelf', 'initial_fc': 7000, 'initial_Q': 0.7},
319
+ ] # 12 bands
320
+
321
+ # Differential Evolution optimizer options
322
+ # popsize * (maxiter+1) * N_params = total evaluations (approx, due to strategy)
323
+ # For 12 bands, ~34 params. popsize=15*34=510 is very large.
324
+ # Let's try popsize = 15 (relative to num_params, so DEAP default like), or fixed like 50-100.
325
+ # maxiter for DE is number of generations.
326
+ de_opt_options = {
327
+ 'maxiter': 300, # Number of generations. Start smaller, e.g., 100-500.
328
+ 'popsize': 20, # Population size multiplier. popsize * N_params individuals.
329
+ # For ~34 params, 20*34=680 individuals per generation. This is large.
330
+ # Let's set popsize directly to a number like 100 for now.
331
+ # 'popsize': 100, # Try a fixed population size
332
+ 'mutation': (0.5, 1.0),
333
+ 'recombination': 0.7,
334
+ 'workers': -1, # Use all CPU cores for parallel fitness evaluation
335
+ 'polish': True, # Recommended: polish the best solution with L-BFGS-B
336
+ 'disp': True # Show progress
337
+ }
338
+ # Recalculate popsize for verbose message if using multiplier:
339
+ # num_total_params = 0
340
+ # for band in custom_band_config:
341
+ # num_total_params +=2 # fc, Q
342
+ # if band['type'] in ['peak', 'low_shelf', 'high_shelf']: num_total_params +=1
343
+ # print(f"Total parameters to optimize: {num_total_params}")
344
+ # de_opt_options['popsize'] = 10 * num_total_params # Example: 10 times the number of parameters
345
+
346
+ optimized_parameters, eq_processed_audio = match_frequency_response(
347
+ source_audio_path=source_file, target_audio_path=target_file,
348
+ output_eq_audio_path=output_eq_file, eq_band_config_list=custom_band_config,
349
+ sampling_rate=SR, fft_size=NFFT,
350
+ optimizer_options=de_opt_options, # Pass DE options
351
+ plot_results=True, verbose=True
352
+ )
353
+
354
+ if optimized_parameters:
355
+ print("\n优化后的EQ参数 (差分进化, 未平滑):")
356
+ for i, params in enumerate(optimized_parameters):
357
+ print(f" 频段 {i + 1}: 类型={params['filter_type']}, Fc={params['fc']:.1f}, Q={params['Q']:.2f}" +
358
+ (f", 增益={params['dBgain']:.2f}" if params['dBgain'] is not None else ""))
359
+ else:
360
+ print("未生成EQ参数或处理中发生错误。")
@@ -0,0 +1,76 @@
1
+ '''
2
+ Author: 凌逆战 | Never
3
+ Date: 2025-08-04 21:49:05
4
+ Description: 自动EQ补偿
5
+ '''
6
+ import numpy as np
7
+ import librosa
8
+ import soundfile as sf
9
+ import matplotlib.pyplot as plt
10
+
11
+ np.set_printoptions(precision=8)
12
+ np.set_printoptions(suppress=True) # 打印不使用科学计数法
13
+
14
+
15
+ def get_freq_eq(reference_audio, target_audio, sample_rate, fft_size, window_size, plot_results=False):
16
+ freq_bins = np.fft.rfftfreq(fft_size, d=1.0 / sample_rate) # [0, 31.25, 62.5,.....]
17
+
18
+ stft_reference = librosa.stft(reference_audio, n_fft=fft_size, hop_length=window_size // 2, win_length=window_size, window="hann")
19
+ stft_target = librosa.stft(target_audio, n_fft=fft_size, hop_length=window_size // 2, win_length=window_size, window="hann")
20
+ magnitude_reference, magnitude_target = np.abs(stft_reference), np.abs(stft_target) # (F,T)
21
+ # 求时间平均, 频响曲线 Frequency_Response_curve
22
+ reference_response = np.mean(magnitude_reference, axis=1)
23
+ target_response = np.mean(magnitude_target, axis=1)
24
+
25
+ reference_response_db = 20 * np.log10(reference_response) # 取对数幅度谱, 以便更好地可视化
26
+ target_response_db = 20 * np.log10(target_response) # 取对数幅度谱, 以便更好地可视化
27
+
28
+ eq_curve = target_response_db - reference_response_db # 补偿曲线 (28208, 1)
29
+ # print("补偿EQ曲线: ", len(eq_curve), np.array2string(np.power(10, eq_curve / 20), separator=', '))
30
+
31
+ if plot_results:
32
+ plt.figure(figsize=(10, 5))
33
+ # plt.plot(freq_bins, target_response_db, label="Target Response")
34
+ plt.plot(freq_bins, eq_curve, label="EQ Curve")
35
+ # compensated_response = reference_response_db + eq_curve # 补偿后的曲线
36
+ # plt.plot(freq_bins, compensated_response, label="Compensated Response")
37
+ plt.xlabel('Frequency (Hz)')
38
+ plt.ylabel('Amplitude (dB)')
39
+ plt.title('Frequency Response Compensation')
40
+ plt.grid(True)
41
+ plt.legend()
42
+ plt.xscale('log')
43
+ plt.grid(True, ls="--", alpha=0.4)
44
+ plt.tight_layout()
45
+ # plt.show()
46
+ plt.savefig(f"./frequency_eq_fft{window_size}.png")
47
+
48
+ # 拿到EQ之后我们对音频进行EQ补偿
49
+ reference_phase = np.angle(stft_reference) # (F,T)
50
+ for freq_idx in range(magnitude_reference.shape[0]):
51
+ magnitude_reference[freq_idx, :] *= np.power(10, eq_curve[freq_idx] / 20)
52
+ compensated_spectrum = magnitude_reference * np.exp(1.0j * reference_phase)
53
+ compensated_audio = librosa.istft(compensated_spectrum, hop_length=window_size // 2, win_length=window_size, n_fft=fft_size, window="hann")
54
+
55
+ return eq_curve, compensated_audio
56
+
57
+
58
+ if __name__ == "__main__":
59
+ SAMPLE_RATE = 16000
60
+ WINDOW_SIZE = FFT_SIZE = 512
61
+ # reference_audio_path = "../../data/white.wav"
62
+ # target_audio_path = "../../data/white_EQ.wav"
63
+ # print(os.path.exists(reference_audio_path))
64
+
65
+ # 读取音频文件
66
+ # reference_audio, _ = sf.read(reference_audio_path, dtype='float32')
67
+ # target_audio, _ = sf.read(target_audio_path, dtype='float32')
68
+ wav_3956, sr = sf.read("../../data/3956_speech.wav")
69
+ reference_audio = wav_3956[:, 1]
70
+ target_audio = wav_3956[:, 0]
71
+ eq_curve, compensated_audio = compute_frequency_eq(
72
+ reference_audio, target_audio,
73
+ SAMPLE_RATE, FFT_SIZE, WINDOW_SIZE,
74
+ plot_results=True
75
+ )
76
+ sf.write("../../data/frequency_eq.wav", compensated_audio, SAMPLE_RATE)
@@ -8,7 +8,7 @@ import scipy.signal as signal
8
8
  from scipy.signal import lfilter, freqz
9
9
  import matplotlib.pyplot as plt
10
10
  from deap import base, creator, tools, algorithms
11
- from filter import EQFilter
11
+ from neverlib.filter import EQFilter
12
12
  import logging
13
13
  import pickle
14
14
  import yaml