neverlib 0.2.1__py3-none-any.whl → 0.2.3__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.
- neverlib/__init__.py +2 -2
- neverlib/audio_aug/__init__.py +1 -1
- neverlib/audio_aug/audio_aug.py +4 -5
- neverlib/dataAnalyze/README.md +234 -0
- neverlib/dataAnalyze/__init__.py +87 -0
- neverlib/dataAnalyze/dataset_analyzer.py +590 -0
- neverlib/dataAnalyze/quality_metrics.py +364 -0
- neverlib/dataAnalyze/rms_distrubution.py +62 -0
- neverlib/dataAnalyze/spectral_analysis.py +218 -0
- neverlib/dataAnalyze/statistics.py +406 -0
- neverlib/dataAnalyze/temporal_features.py +126 -0
- neverlib/dataAnalyze/visualization.py +468 -0
- neverlib/filter/AudoEQ/README.md +165 -0
- neverlib/filter/AudoEQ/auto_eq_de.py +361 -0
- neverlib/filter/AudoEQ/auto_eq_ga_advanced.py +577 -0
- neverlib/filter/AudoEQ/auto_eq_ga_basic.py +380 -0
- neverlib/filter/AudoEQ/auto_eq_spectral_direct.py +75 -0
- neverlib/filter/README.md +101 -0
- neverlib/filter/__init__.py +7 -0
- neverlib/filter/biquad.py +45 -0
- neverlib/filter/common.py +5 -6
- neverlib/filter/core.py +339 -0
- neverlib/metrics/dnsmos.py +160 -0
- neverlib/metrics/snr.py +177 -0
- neverlib/metrics/spec.py +45 -0
- neverlib/metrics/test_pesq.py +35 -0
- neverlib/metrics/time.py +68 -0
- neverlib/tests/test_vad.py +21 -0
- neverlib/utils/audio_split.py +5 -3
- neverlib/utils/message.py +4 -4
- neverlib/utils/utils.py +32 -15
- neverlib/vad/PreProcess.py +1 -1
- neverlib/vad/README.md +10 -10
- neverlib/vad/VAD_Energy.py +1 -1
- neverlib/vad/VAD_Silero.py +1 -1
- neverlib/vad/VAD_WebRTC.py +1 -1
- neverlib/vad/VAD_funasr.py +1 -1
- neverlib/vad/VAD_statistics.py +3 -3
- neverlib/vad/VAD_vadlib.py +2 -2
- neverlib/vad/VAD_whisper.py +1 -1
- neverlib/vad/__init__.py +1 -1
- neverlib/vad/class_get_speech.py +4 -4
- neverlib/vad/class_vad.py +1 -1
- neverlib/vad/utils.py +47 -5
- {neverlib-0.2.1.dist-info → neverlib-0.2.3.dist-info}/METADATA +120 -120
- neverlib-0.2.3.dist-info/RECORD +53 -0
- {neverlib-0.2.1.dist-info → neverlib-0.2.3.dist-info}/WHEEL +1 -1
- neverlib/Documents/vad/VAD_Energy.ipynb +0 -159
- neverlib/Documents/vad/VAD_Silero.ipynb +0 -305
- neverlib/Documents/vad/VAD_WebRTC.ipynb +0 -183
- neverlib/Documents/vad/VAD_funasr.ipynb +0 -179
- neverlib/Documents/vad/VAD_ppasr.ipynb +0 -175
- neverlib/Documents/vad/VAD_statistics.ipynb +0 -522
- neverlib/Documents/vad/VAD_vadlib.ipynb +0 -184
- neverlib/Documents/vad/VAD_whisper.ipynb +0 -430
- neverlib/utils/waveform_analyzer.py +0 -51
- neverlib/wav_data/000_short.wav +0 -0
- neverlib-0.2.1.dist-info/RECORD +0 -40
- {neverlib-0.2.1.dist-info → neverlib-0.2.3.dist-info}/licenses/LICENSE +0 -0
- {neverlib-0.2.1.dist-info → neverlib-0.2.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,361 @@
|
|
|
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
|
+
import os
|
|
16
|
+
from filter import EQFilter
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_filter_function(filter_type, fs):
|
|
20
|
+
"""获取滤波器函数, 返回配置好采样率的EQFilter实例的方法"""
|
|
21
|
+
eq_filter = EQFilter(fs=fs)
|
|
22
|
+
filter_func_map = {
|
|
23
|
+
'peak': eq_filter.PeakingFilter,
|
|
24
|
+
'low_shelf': eq_filter.LowshelfFilter,
|
|
25
|
+
'high_shelf': eq_filter.HighshelfFilter,
|
|
26
|
+
'low_pass': eq_filter.LowpassFilter,
|
|
27
|
+
'high_pass': eq_filter.HighpassFilter,
|
|
28
|
+
}
|
|
29
|
+
return filter_func_map.get(filter_type)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _calculate_spectrum(audio_data, target_sr, n_fft, hop_length):
|
|
33
|
+
S = librosa.stft(audio_data, n_fft=n_fft, hop_length=hop_length, win_length=n_fft)
|
|
34
|
+
mag = np.mean(np.abs(S), axis=1)
|
|
35
|
+
epsilon = 1e-9 # IMPORTANT: Add epsilon to avoid log(0)
|
|
36
|
+
spec_db = 20 * np.log10(mag + epsilon)
|
|
37
|
+
freq_axis = librosa.fft_frequencies(sr=target_sr, n_fft=n_fft)
|
|
38
|
+
return spec_db, freq_axis
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _load_audio_data(audio_path, target_sr):
|
|
42
|
+
data, sr_orig = sf.read(audio_path, dtype='float32')
|
|
43
|
+
if data.ndim > 1:
|
|
44
|
+
data = np.mean(data, axis=1)
|
|
45
|
+
if sr_orig != target_sr:
|
|
46
|
+
data = librosa.resample(data, orig_sr=sr_orig, target_sr=target_sr)
|
|
47
|
+
return data
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _apply_eq_cascade(audio_data, eq_params_list, fs):
|
|
51
|
+
if not eq_params_list:
|
|
52
|
+
return audio_data
|
|
53
|
+
processed_audio = audio_data.copy()
|
|
54
|
+
|
|
55
|
+
for params in eq_params_list:
|
|
56
|
+
filter_type, fc, Q, db_gain = params['filter_type'], params['fc'], params['Q'], params.get('dBgain')
|
|
57
|
+
filter_func = get_filter_function(filter_type, fs)
|
|
58
|
+
|
|
59
|
+
if filter_func is None:
|
|
60
|
+
warnings.warn(f"Unknown filter type: {filter_type}")
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
# 根据滤波器类型调用相应的方法
|
|
64
|
+
if db_gain is not None:
|
|
65
|
+
b, a = filter_func(fc=fc, Q=Q, dBgain=db_gain)
|
|
66
|
+
else:
|
|
67
|
+
b, a = filter_func(fc=fc, Q=Q)
|
|
68
|
+
|
|
69
|
+
if not np.issubdtype(processed_audio.dtype, np.floating):
|
|
70
|
+
processed_audio = processed_audio.astype(np.float32)
|
|
71
|
+
processed_audio = sp_signal.lfilter(b, a, processed_audio)
|
|
72
|
+
return processed_audio
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _objective_function(flat_params, band_definitions, target_response_db, freq_axis, fs, n_fft):
|
|
76
|
+
current_cascade_response_db = np.zeros_like(freq_axis)
|
|
77
|
+
param_idx_counter = 0
|
|
78
|
+
|
|
79
|
+
for band_def in band_definitions:
|
|
80
|
+
band_type = band_def['type']
|
|
81
|
+
# Safety check for parameter length (can happen if bounds are wrong for DE)
|
|
82
|
+
if param_idx_counter + 1 >= len(flat_params):
|
|
83
|
+
warnings.warn(
|
|
84
|
+
f"Parameter array too short in objective function. Expected at least {param_idx_counter + 2} elements, got {len(flat_params)}")
|
|
85
|
+
return np.finfo(np.float64).max # Return large error
|
|
86
|
+
|
|
87
|
+
fc, q_val = flat_params[param_idx_counter], flat_params[param_idx_counter + 1]
|
|
88
|
+
param_idx_counter += 2
|
|
89
|
+
|
|
90
|
+
filter_func = get_filter_function(band_type, fs)
|
|
91
|
+
if filter_func is None:
|
|
92
|
+
warnings.warn(f"Unknown filter type: {band_type}")
|
|
93
|
+
return np.finfo(np.float64).max
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
if band_type in ['peak', 'low_shelf', 'high_shelf']:
|
|
97
|
+
if param_idx_counter >= len(flat_params):
|
|
98
|
+
warnings.warn(f"Parameter array too short for gain parameter in objective function.")
|
|
99
|
+
return np.finfo(np.float64).max
|
|
100
|
+
db_gain = flat_params[param_idx_counter]
|
|
101
|
+
param_idx_counter += 1
|
|
102
|
+
b, a = filter_func(fc=fc, Q=q_val, dBgain=db_gain)
|
|
103
|
+
else:
|
|
104
|
+
b, a = filter_func(fc=fc, Q=q_val)
|
|
105
|
+
|
|
106
|
+
w, h = sp_signal.freqz(b, a, worN=freq_axis, fs=fs)
|
|
107
|
+
# Add epsilon to avoid log(0) which results in -inf and can break mean calculation
|
|
108
|
+
h_abs = np.abs(h)
|
|
109
|
+
h_db = 20 * np.log10(h_abs + 1e-9)
|
|
110
|
+
current_cascade_response_db += h_db
|
|
111
|
+
|
|
112
|
+
except Exception as e:
|
|
113
|
+
warnings.warn(f"Error computing filter response for {band_type}: {e}")
|
|
114
|
+
return np.finfo(np.float64).max
|
|
115
|
+
|
|
116
|
+
error = np.mean((current_cascade_response_db - target_response_db)**2)
|
|
117
|
+
if np.isnan(error) or np.isinf(error): # Handle potential nan/inf from objective
|
|
118
|
+
# This might happen if parameters lead to unstable filters or extreme responses
|
|
119
|
+
# print(f"Objective function returned NaN/Inf. Current error: {error}")
|
|
120
|
+
# print(f"Params (first few): {flat_params[:6]}")
|
|
121
|
+
return np.finfo(np.float64).max # Return a very large number
|
|
122
|
+
return error
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _get_initial_params_and_bounds(band_definitions, fs, target_response_db, freq_axis):
|
|
126
|
+
x0, bounds_list = [], [] # Changed bounds to bounds_list
|
|
127
|
+
min_fc, max_fc = 20.0, fs / 2.0 * 0.98
|
|
128
|
+
num_gain_filters = sum(1 for bd in band_definitions if bd['type'] in ['peak', 'low_shelf', 'high_shelf'])
|
|
129
|
+
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 []
|
|
130
|
+
gain_filter_idx = 0
|
|
131
|
+
for band_def in band_definitions:
|
|
132
|
+
band_type = band_def['type']
|
|
133
|
+
initial_fc = band_def.get('initial_fc')
|
|
134
|
+
if initial_fc is None:
|
|
135
|
+
if band_type in ['peak', 'low_shelf', 'high_shelf'] and gain_filter_idx < len(log_fcs):
|
|
136
|
+
initial_fc = log_fcs[gain_filter_idx]
|
|
137
|
+
elif band_type == 'low_shelf':
|
|
138
|
+
initial_fc = np.clip(80, min_fc, max_fc)
|
|
139
|
+
elif band_type == 'high_shelf':
|
|
140
|
+
initial_fc = np.clip(8000, min_fc, max_fc)
|
|
141
|
+
elif band_type == 'low_pass':
|
|
142
|
+
initial_fc = np.clip(fs / 2.2, min_fc, max_fc)
|
|
143
|
+
elif band_type == 'high_pass':
|
|
144
|
+
initial_fc = np.clip(40, min_fc, max_fc)
|
|
145
|
+
else:
|
|
146
|
+
initial_fc = (min_fc + max_fc) / 2
|
|
147
|
+
x0.append(np.clip(initial_fc, min_fc, max_fc))
|
|
148
|
+
bounds_list.append((min_fc, max_fc))
|
|
149
|
+
initial_q = band_def.get('initial_Q', 1.0 if band_type == 'peak' else 0.707)
|
|
150
|
+
x0.append(initial_q)
|
|
151
|
+
bounds_list.append((0.1, 20.0))
|
|
152
|
+
if band_type in ['peak', 'low_shelf', 'high_shelf']:
|
|
153
|
+
fc_idx = np.argmin(np.abs(freq_axis - initial_fc))
|
|
154
|
+
initial_gain_default = target_response_db[fc_idx] if len(target_response_db) > 0 and fc_idx < len(target_response_db) else 0.0
|
|
155
|
+
initial_gain = band_def.get('initial_dBgain', initial_gain_default)
|
|
156
|
+
x0.append(np.clip(initial_gain, -20.0, 20.0))
|
|
157
|
+
bounds_list.append((-30.0, 30.0))
|
|
158
|
+
gain_filter_idx += 1
|
|
159
|
+
return np.array(x0), bounds_list # Return bounds_list for differential_evolution
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def plot_spectra_comparison(spectra_data, freq_axis, title="Spectra Comparison"):
|
|
163
|
+
plt.figure(figsize=(12, 7))
|
|
164
|
+
for label, spec_db in spectra_data.items():
|
|
165
|
+
plt.plot(freq_axis, spec_db, label=label, alpha=0.8)
|
|
166
|
+
plt.xscale('log') # Re-enabled log scale for frequency axis
|
|
167
|
+
plt.xlabel("Frequency (Hz)")
|
|
168
|
+
plt.ylabel("Magnitude (dB)")
|
|
169
|
+
plt.title(title)
|
|
170
|
+
plt.legend()
|
|
171
|
+
plt.grid(True, which="both", ls="-", alpha=0.5) # Added which="both" for log grid
|
|
172
|
+
if len(freq_axis) > 0:
|
|
173
|
+
plt.xlim([20, freq_axis[-1]])
|
|
174
|
+
valid_spectra = [s[np.isfinite(s)] for s in spectra_data.values() if s is not None and len(s[np.isfinite(s)]) > 0]
|
|
175
|
+
if valid_spectra:
|
|
176
|
+
min_y = min(np.min(s) for s in valid_spectra) - 10
|
|
177
|
+
max_y = max(np.max(s) for s in valid_spectra) + 10
|
|
178
|
+
if np.isfinite(min_y) and np.isfinite(max_y):
|
|
179
|
+
plt.ylim([min_y, max_y])
|
|
180
|
+
plt.tight_layout()
|
|
181
|
+
try:
|
|
182
|
+
clean_title = "".join(c if c.isalnum() or c in [' ', '_', '-'] else '_' for c in title) # Sanitize more robustly
|
|
183
|
+
plt.savefig(f"{clean_title.replace(' ', '_')}.png")
|
|
184
|
+
# if plt.isinteractive():
|
|
185
|
+
# plt.show()
|
|
186
|
+
plt.close()
|
|
187
|
+
except Exception as e:
|
|
188
|
+
print(f"Error saving/showing plot: {e}")
|
|
189
|
+
finally:
|
|
190
|
+
plt.close()
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def match_frequency_response(
|
|
194
|
+
source_audio_path: str,
|
|
195
|
+
target_audio_path: str,
|
|
196
|
+
output_eq_audio_path: str = "source_eq_matched.wav",
|
|
197
|
+
num_eq_bands: int = 10,
|
|
198
|
+
sampling_rate: int = 16000,
|
|
199
|
+
fft_size: int = 1024,
|
|
200
|
+
hop_length_ratio: float = 0.25,
|
|
201
|
+
eq_band_config_list: list = None,
|
|
202
|
+
optimizer_options: dict = None, # For DE, e.g., {'popsize': 20, 'maxiter': 500, 'workers': -1}
|
|
203
|
+
plot_results: bool = True,
|
|
204
|
+
verbose: bool = False
|
|
205
|
+
):
|
|
206
|
+
hop_length = int(fft_size * hop_length_ratio)
|
|
207
|
+
if verbose:
|
|
208
|
+
print(f"SR={sampling_rate}, FFT={fft_size}, Hop={hop_length}")
|
|
209
|
+
print("Spectrum smoothing is DISABLED.")
|
|
210
|
+
|
|
211
|
+
source_data = _load_audio_data(source_audio_path, sampling_rate)
|
|
212
|
+
target_data = _load_audio_data(target_audio_path, sampling_rate)
|
|
213
|
+
|
|
214
|
+
source_spec_db, freq_axis = _calculate_spectrum(source_data, sampling_rate, fft_size, hop_length)
|
|
215
|
+
target_spec_db, _ = _calculate_spectrum(target_data, sampling_rate, fft_size, hop_length)
|
|
216
|
+
|
|
217
|
+
target_eq_overall_response_db = target_spec_db - source_spec_db
|
|
218
|
+
|
|
219
|
+
# _get_initial_params_and_bounds returns x0 and a list of (min,max) tuples for bounds
|
|
220
|
+
actual_num_bands = len(eq_band_config_list)
|
|
221
|
+
_, de_bounds = _get_initial_params_and_bounds(eq_band_config_list, sampling_rate, target_eq_overall_response_db, freq_axis)
|
|
222
|
+
|
|
223
|
+
if verbose:
|
|
224
|
+
print(f"EQ bands: {len(eq_band_config_list)}, Total params: {len(de_bounds)}")
|
|
225
|
+
|
|
226
|
+
# Default options for differential_evolution
|
|
227
|
+
# Note: popsize is often set to N_params * 10 or 15.
|
|
228
|
+
# maxiter might need to be lower than for L-BFGS-B for similar runtime, or higher for better solution.
|
|
229
|
+
num_params_to_optimize = len(de_bounds)
|
|
230
|
+
default_de_options = {
|
|
231
|
+
'strategy': 'best1bin',
|
|
232
|
+
'maxiter': 200 * actual_num_bands if actual_num_bands > 0 else 200, # Max generations
|
|
233
|
+
'popsize': 15, # Population size per generation (popsize * num_params_to_optimize evaluations per generation)
|
|
234
|
+
'tol': 0.01,
|
|
235
|
+
'mutation': (0.5, 1),
|
|
236
|
+
'recombination': 0.7,
|
|
237
|
+
'disp': verbose,
|
|
238
|
+
'polish': True, # Apply a local minimizer (L-BFGS-B) at the end
|
|
239
|
+
'updating': 'deferred', # For parallel processing
|
|
240
|
+
'workers': -1 # Use all available CPU cores
|
|
241
|
+
}
|
|
242
|
+
if optimizer_options:
|
|
243
|
+
default_de_options.update(optimizer_options)
|
|
244
|
+
|
|
245
|
+
obj_args = (eq_band_config_list, target_eq_overall_response_db, freq_axis, sampling_rate, fft_size)
|
|
246
|
+
|
|
247
|
+
if verbose:
|
|
248
|
+
print(f"Starting Differential Evolution, options: {default_de_options} (Smoothing DISABLED)...")
|
|
249
|
+
|
|
250
|
+
result = differential_evolution(
|
|
251
|
+
_objective_function,
|
|
252
|
+
bounds=de_bounds, # Pass the list of (min, max) tuples
|
|
253
|
+
args=obj_args,
|
|
254
|
+
**default_de_options # Pass all other options as keyword arguments
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
if verbose:
|
|
258
|
+
print(f"DE Optimization: Success={result.success}, Msg='{result.message}', NFEV={result.nfev},nit={result.nit}, FunVal={result.fun:.4e}")
|
|
259
|
+
|
|
260
|
+
optimized_params_flat = result.x
|
|
261
|
+
# ... (Rest of the function: formatting parameters, applying EQ, plotting - remains the same) ...
|
|
262
|
+
optimized_eq_parameters_list = []
|
|
263
|
+
current_param_idx = 0
|
|
264
|
+
for i, band_def in enumerate(eq_band_config_list):
|
|
265
|
+
params = {'filter_type': band_def['type'], 'fs': float(sampling_rate)}
|
|
266
|
+
params['fc'] = float(optimized_params_flat[current_param_idx])
|
|
267
|
+
params['Q'] = float(optimized_params_flat[current_param_idx + 1])
|
|
268
|
+
current_param_idx += 2
|
|
269
|
+
if params['filter_type'] in ['peak', 'low_shelf', 'high_shelf']:
|
|
270
|
+
params['dBgain'] = float(optimized_params_flat[current_param_idx])
|
|
271
|
+
current_param_idx += 1
|
|
272
|
+
else:
|
|
273
|
+
params['dBgain'] = None
|
|
274
|
+
optimized_eq_parameters_list.append(params)
|
|
275
|
+
|
|
276
|
+
eq_audio_data = None
|
|
277
|
+
if output_eq_audio_path:
|
|
278
|
+
eq_audio_data = _apply_eq_cascade(source_data, optimized_eq_parameters_list, sampling_rate)
|
|
279
|
+
max_val = np.max(np.abs(eq_audio_data))
|
|
280
|
+
if max_val > 1.0:
|
|
281
|
+
eq_audio_data /= max_val
|
|
282
|
+
warnings.warn(f"EQ'd audio clipped (max: {max_val:.2f}), scaled.")
|
|
283
|
+
elif max_val == 0 and len(eq_audio_data) > 0:
|
|
284
|
+
warnings.warn(f"EQ'd audio is all zeros.")
|
|
285
|
+
sf.write(output_eq_audio_path, eq_audio_data, sampling_rate, subtype='FLOAT')
|
|
286
|
+
if verbose:
|
|
287
|
+
print(f"EQ'd audio saved: {output_eq_audio_path}")
|
|
288
|
+
|
|
289
|
+
if plot_results:
|
|
290
|
+
spectra_to_plot = {"Source": source_spec_db, "Target": target_spec_db}
|
|
291
|
+
plot_title_main = f"Spectra (DE) - {len(eq_band_config_list)} bands - No Smoothing"
|
|
292
|
+
eq_spec_db, _ = _calculate_spectrum(eq_audio_data, sampling_rate, fft_size, hop_length)
|
|
293
|
+
spectra_to_plot["EQ'd Source"] = eq_spec_db
|
|
294
|
+
plot_spectra_comparison(spectra_to_plot, freq_axis, title=plot_title_main)
|
|
295
|
+
return optimized_eq_parameters_list, eq_audio_data
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# --- Example Usage ---
|
|
299
|
+
if __name__ == '__main__':
|
|
300
|
+
source_file = "../data/white.wav"
|
|
301
|
+
target_file = "../data/white_EQ.wav"
|
|
302
|
+
output_eq_file = "../data/white_EQ_matched_DE.wav"
|
|
303
|
+
|
|
304
|
+
SR = 16000
|
|
305
|
+
NFFT = 1024
|
|
306
|
+
|
|
307
|
+
custom_band_config = [
|
|
308
|
+
{'type': 'high_pass', 'initial_fc': 40, 'initial_Q': 0.7},
|
|
309
|
+
{'type': 'low_shelf', 'initial_fc': 150, 'initial_Q': 0.7},
|
|
310
|
+
{'type': 'peak', 'initial_fc': 250},
|
|
311
|
+
{'type': 'peak', 'initial_fc': 500},
|
|
312
|
+
{'type': 'peak', 'initial_fc': 750},
|
|
313
|
+
{'type': 'peak', 'initial_fc': 1000},
|
|
314
|
+
{'type': 'peak', 'initial_fc': 1500},
|
|
315
|
+
{'type': 'peak', 'initial_fc': 2500},
|
|
316
|
+
{'type': 'peak', 'initial_fc': 3500},
|
|
317
|
+
{'type': 'peak', 'initial_fc': 5000},
|
|
318
|
+
{'type': 'peak', 'initial_fc': 6500},
|
|
319
|
+
{'type': 'high_shelf', 'initial_fc': 7000, 'initial_Q': 0.7},
|
|
320
|
+
] # 12 bands
|
|
321
|
+
|
|
322
|
+
# Differential Evolution optimizer options
|
|
323
|
+
# popsize * (maxiter+1) * N_params = total evaluations (approx, due to strategy)
|
|
324
|
+
# For 12 bands, ~34 params. popsize=15*34=510 is very large.
|
|
325
|
+
# Let's try popsize = 15 (relative to num_params, so DEAP default like), or fixed like 50-100.
|
|
326
|
+
# maxiter for DE is number of generations.
|
|
327
|
+
de_opt_options = {
|
|
328
|
+
'maxiter': 300, # Number of generations. Start smaller, e.g., 100-500.
|
|
329
|
+
'popsize': 20, # Population size multiplier. popsize * N_params individuals.
|
|
330
|
+
# For ~34 params, 20*34=680 individuals per generation. This is large.
|
|
331
|
+
# Let's set popsize directly to a number like 100 for now.
|
|
332
|
+
# 'popsize': 100, # Try a fixed population size
|
|
333
|
+
'mutation': (0.5, 1.0),
|
|
334
|
+
'recombination': 0.7,
|
|
335
|
+
'workers': -1, # Use all CPU cores for parallel fitness evaluation
|
|
336
|
+
'polish': True, # Recommended: polish the best solution with L-BFGS-B
|
|
337
|
+
'disp': True # Show progress
|
|
338
|
+
}
|
|
339
|
+
# Recalculate popsize for verbose message if using multiplier:
|
|
340
|
+
# num_total_params = 0
|
|
341
|
+
# for band in custom_band_config:
|
|
342
|
+
# num_total_params +=2 # fc, Q
|
|
343
|
+
# if band['type'] in ['peak', 'low_shelf', 'high_shelf']: num_total_params +=1
|
|
344
|
+
# print(f"Total parameters to optimize: {num_total_params}")
|
|
345
|
+
# de_opt_options['popsize'] = 10 * num_total_params # Example: 10 times the number of parameters
|
|
346
|
+
|
|
347
|
+
optimized_parameters, eq_processed_audio = match_frequency_response(
|
|
348
|
+
source_audio_path=source_file, target_audio_path=target_file,
|
|
349
|
+
output_eq_audio_path=output_eq_file, eq_band_config_list=custom_band_config,
|
|
350
|
+
sampling_rate=SR, fft_size=NFFT,
|
|
351
|
+
optimizer_options=de_opt_options, # Pass DE options
|
|
352
|
+
plot_results=True, verbose=True
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
if optimized_parameters:
|
|
356
|
+
print("\n优化后的EQ参数 (差分进化, 未平滑):")
|
|
357
|
+
for i, params in enumerate(optimized_parameters):
|
|
358
|
+
print(f" 频段 {i + 1}: 类型={params['filter_type']}, Fc={params['fc']:.1f}, Q={params['Q']:.2f}" +
|
|
359
|
+
(f", 增益={params['dBgain']:.2f}" if params['dBgain'] is not None else ""))
|
|
360
|
+
else:
|
|
361
|
+
print("未生成EQ参数或处理中发生错误。")
|