neverlib 0.2.2__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 (236) 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___20250805234204.py +87 -0
  17. neverlib/.history/dataAnalyze/__init___20250806204125.py +14 -0
  18. neverlib/.history/dataAnalyze/__init___20250806204139.py +14 -0
  19. neverlib/.history/dataAnalyze/__init___20250806204159.py +14 -0
  20. neverlib/.history/filter/__init___20250820103351.py +70 -0
  21. neverlib/.history/filter/__init___20250821102348.py +70 -0
  22. neverlib/.history/filter/__init___20250821102405.py +14 -0
  23. neverlib/.history/filter/auto_eq/__init___20250819213121.py +36 -0
  24. neverlib/.history/filter/auto_eq/__init___20250821102241.py +36 -0
  25. neverlib/.history/filter/auto_eq/__init___20250821102259.py +36 -0
  26. neverlib/.history/filter/auto_eq/__init___20250821102307.py +36 -0
  27. neverlib/.history/filter/auto_eq/__init___20250821102310.py +36 -0
  28. neverlib/.history/filter/auto_eq/__init___20250821102318.py +36 -0
  29. neverlib/.history/filter/auto_eq/__init___20250821102507.py +36 -0
  30. neverlib/.history/filter/auto_eq/de_eq_20250820103848.py +361 -0
  31. neverlib/.history/filter/auto_eq/de_eq_20250821102422.py +360 -0
  32. neverlib/.history/filter/auto_eq/freq_eq_20250805234206.py +75 -0
  33. neverlib/.history/filter/auto_eq/freq_eq_20250820140732.py +75 -0
  34. neverlib/.history/filter/auto_eq/freq_eq_20250820140745.py +75 -0
  35. neverlib/.history/filter/auto_eq/freq_eq_20250820140816.py +75 -0
  36. neverlib/.history/filter/auto_eq/freq_eq_20250820140938.py +77 -0
  37. neverlib/.history/filter/auto_eq/freq_eq_20250820141003.py +77 -0
  38. neverlib/.history/filter/auto_eq/freq_eq_20250820141006.py +77 -0
  39. neverlib/.history/filter/auto_eq/freq_eq_20250820141019.py +77 -0
  40. neverlib/.history/filter/auto_eq/freq_eq_20250820141049.py +77 -0
  41. neverlib/.history/filter/auto_eq/freq_eq_20250820141211.py +77 -0
  42. neverlib/.history/filter/auto_eq/freq_eq_20250820141227.py +77 -0
  43. neverlib/.history/filter/auto_eq/freq_eq_20250820141311.py +78 -0
  44. neverlib/.history/filter/auto_eq/freq_eq_20250820141340.py +78 -0
  45. neverlib/.history/filter/auto_eq/freq_eq_20250820141712.py +78 -0
  46. neverlib/.history/filter/auto_eq/freq_eq_20250820141733.py +78 -0
  47. neverlib/.history/filter/auto_eq/freq_eq_20250820141755.py +78 -0
  48. neverlib/.history/filter/auto_eq/freq_eq_20250821102434.py +76 -0
  49. neverlib/.history/filter/auto_eq/freq_eq_20250821102500.py +76 -0
  50. neverlib/.history/filter/auto_eq/freq_eq_20250821102502.py +76 -0
  51. neverlib/.history/filter/auto_eq/ga_eq_basic_20250820102957.py +380 -0
  52. neverlib/.history/filter/auto_eq/ga_eq_basic_20250820113054.py +380 -0
  53. neverlib/.history/filter/auto_eq/ga_eq_basic_20250820113150.py +380 -0
  54. neverlib/.history/filter/auto_eq/ga_eq_basic_20250820113520.py +385 -0
  55. neverlib/.history/filter/auto_eq/ga_eq_basic_20250820113525.py +385 -0
  56. neverlib/.history/filter/auto_eq/ga_eq_basic_20250821102212.py +385 -0
  57. neverlib/.history/metrics/dnsmos_20250806001612.py +160 -0
  58. neverlib/.history/metrics/dnsmos_20250815180659.py +160 -0
  59. neverlib/.history/metrics/dnsmos_20250815180701.py +158 -0
  60. neverlib/.history/metrics/dnsmos_20250815181321.py +154 -0
  61. neverlib/.history/metrics/dnsmos_20250815181327.py +154 -0
  62. neverlib/.history/metrics/dnsmos_20250815181331.py +154 -0
  63. neverlib/.history/metrics/dnsmos_20250815181620.py +154 -0
  64. neverlib/.history/metrics/dnsmos_20250815181631.py +154 -0
  65. neverlib/.history/metrics/dnsmos_20250815181742.py +154 -0
  66. neverlib/.history/metrics/dnsmos_20250815181824.py +153 -0
  67. neverlib/.history/metrics/dnsmos_20250815181834.py +153 -0
  68. neverlib/.history/metrics/dnsmos_20250815181922.py +153 -0
  69. neverlib/.history/metrics/dnsmos_20250815182011.py +147 -0
  70. neverlib/.history/metrics/dnsmos_20250815182036.py +144 -0
  71. neverlib/.history/metrics/dnsmos_20250815182936.py +143 -0
  72. neverlib/.history/metrics/dnsmos_20250815182942.py +143 -0
  73. neverlib/.history/metrics/dnsmos_20250815183032.py +137 -0
  74. neverlib/.history/metrics/dnsmos_20250815183101.py +144 -0
  75. neverlib/.history/metrics/dnsmos_20250815183121.py +144 -0
  76. neverlib/.history/metrics/dnsmos_20250815183123.py +143 -0
  77. neverlib/.history/metrics/dnsmos_20250815183214.py +143 -0
  78. neverlib/.history/metrics/dnsmos_20250815183240.py +143 -0
  79. neverlib/.history/metrics/dnsmos_20250815183248.py +144 -0
  80. neverlib/.history/metrics/dnsmos_20250815183407.py +142 -0
  81. neverlib/.history/metrics/dnsmos_20250815183409.py +142 -0
  82. neverlib/.history/metrics/dnsmos_20250815183431.py +142 -0
  83. neverlib/.history/metrics/dnsmos_20250815183507.py +140 -0
  84. neverlib/.history/metrics/dnsmos_20250815183513.py +139 -0
  85. neverlib/.history/metrics/dnsmos_20250815183618.py +139 -0
  86. neverlib/.history/metrics/dnsmos_20250815183709.py +140 -0
  87. neverlib/.history/metrics/dnsmos_20250815183756.py +137 -0
  88. neverlib/.history/metrics/dnsmos_20250815183815.py +128 -0
  89. neverlib/.history/metrics/dnsmos_20250815183827.py +129 -0
  90. neverlib/.history/metrics/dnsmos_20250815183913.py +117 -0
  91. neverlib/.history/metrics/dnsmos_20250815183914.py +117 -0
  92. neverlib/.history/metrics/dnsmos_20250815184003.py +118 -0
  93. neverlib/.history/metrics/dnsmos_20250815184040.py +118 -0
  94. neverlib/.history/metrics/dnsmos_20250815184049.py +118 -0
  95. neverlib/.history/metrics/dnsmos_20250815184104.py +117 -0
  96. neverlib/.history/metrics/dnsmos_20250815184200.py +117 -0
  97. neverlib/.history/metrics/lpc_lsp_metric_20250816015944.py +128 -0
  98. neverlib/.history/metrics/lpc_lsp_metric_20250816020142.py +128 -0
  99. neverlib/.history/metrics/lpc_lsp_metric_20250816020156.py +128 -0
  100. neverlib/.history/metrics/lpc_lsp_metric_20250816020554.py +130 -0
  101. neverlib/.history/metrics/lpc_lsp_metric_20250816020600.py +125 -0
  102. neverlib/.history/metrics/lpc_lsp_metric_20250816020631.py +120 -0
  103. neverlib/.history/metrics/lpc_lsp_metric_20250816020746.py +118 -0
  104. neverlib/.history/metrics/lpc_me_20250816013111.py +0 -0
  105. neverlib/.history/metrics/lpc_me_20250816013129.py +121 -0
  106. neverlib/.history/metrics/lpc_me_20250816015430.py +103 -0
  107. neverlib/.history/metrics/lpc_me_20250816015535.py +96 -0
  108. neverlib/.history/metrics/lpc_me_20250816015542.py +96 -0
  109. neverlib/.history/metrics/lpc_me_20250816015636.py +97 -0
  110. neverlib/.history/metrics/lpc_me_20250816015658.py +104 -0
  111. neverlib/.history/metrics/lpc_me_20250816015703.py +100 -0
  112. neverlib/.history/metrics/lpc_me_20250816015945.py +128 -0
  113. neverlib/.history/metrics/snr_20250806010538.py +177 -0
  114. neverlib/.history/metrics/snr_20250806211634.py +184 -0
  115. neverlib/.history/metrics/spec_20250805234209.py +45 -0
  116. neverlib/.history/metrics/spec_20250816135530.py +11 -0
  117. neverlib/.history/metrics/spec_20250816135654.py +16 -0
  118. neverlib/.history/metrics/spec_20250816135736.py +68 -0
  119. neverlib/.history/metrics/spec_20250816135904.py +75 -0
  120. neverlib/.history/metrics/spec_20250816135921.py +82 -0
  121. neverlib/.history/metrics/spec_20250816140111.py +82 -0
  122. neverlib/.history/metrics/spec_20250816140543.py +136 -0
  123. neverlib/.history/metrics/spec_20250816140559.py +172 -0
  124. neverlib/.history/metrics/spec_20250816140602.py +172 -0
  125. neverlib/.history/metrics/spec_20250816140608.py +172 -0
  126. neverlib/.history/metrics/spec_20250816140654.py +148 -0
  127. neverlib/.history/metrics/spec_20250816140705.py +144 -0
  128. neverlib/.history/metrics/spec_20250816140755.py +138 -0
  129. neverlib/.history/metrics/spec_20250816140823.py +170 -0
  130. neverlib/.history/metrics/spec_20250816140832.py +170 -0
  131. neverlib/.history/metrics/spec_20250816140833.py +170 -0
  132. neverlib/.history/metrics/spec_20250816140922.py +147 -0
  133. neverlib/.history/metrics/spec_20250816141148.py +107 -0
  134. neverlib/.history/metrics/spec_20250816141219.py +123 -0
  135. neverlib/.history/metrics/spec_20250816141732.py +178 -0
  136. neverlib/.history/metrics/spec_20250816141740.py +178 -0
  137. neverlib/.history/metrics/spec_20250816142030.py +178 -0
  138. neverlib/.history/metrics/spec_20250816142107.py +135 -0
  139. neverlib/.history/metrics/spec_20250816142126.py +135 -0
  140. neverlib/.history/metrics/spec_20250816142410.py +135 -0
  141. neverlib/.history/metrics/spec_20250816142415.py +136 -0
  142. neverlib/.history/metrics/spec_metric_20250816135156.py +0 -0
  143. neverlib/.history/metrics/spec_metric_20250816135226.py +5 -0
  144. neverlib/.history/metrics/spec_metric_20250816135227.py +10 -0
  145. neverlib/.history/metrics/spec_metric_20250816135306.py +15 -0
  146. neverlib/.history/metrics/spec_metric_20250816135442.py +31 -0
  147. neverlib/.history/metrics/spec_metric_20250816135448.py +31 -0
  148. neverlib/.history/metrics/spec_metric_20250816135520.py +29 -0
  149. neverlib/.history/metrics/spec_metric_20250816135537.py +63 -0
  150. neverlib/.history/metrics/spec_metric_20250816135653.py +65 -0
  151. neverlib/.history/vad/PreProcess_20250805234211.py +63 -0
  152. neverlib/.history/vad/PreProcess_20250809232455.py +63 -0
  153. neverlib/.history/vad/PreProcess_20250816020725.py +66 -0
  154. neverlib/.history/vad/VAD_Silero_20250805234211.py +50 -0
  155. neverlib/.history/vad/VAD_Silero_20250809232456.py +50 -0
  156. neverlib/.history/vad/VAD_WebRTC_20250805234211.py +61 -0
  157. neverlib/.history/vad/VAD_WebRTC_20250809232456.py +61 -0
  158. neverlib/.history/vad/VAD_funasr_20250805234211.py +54 -0
  159. neverlib/.history/vad/VAD_funasr_20250809232456.py +54 -0
  160. neverlib/.history/vad/VAD_vadlib_20250805234211.py +70 -0
  161. neverlib/.history/vad/VAD_vadlib_20250809232455.py +70 -0
  162. neverlib/.history/vad/VAD_whisper_20250805234211.py +55 -0
  163. neverlib/.history/vad/VAD_whisper_20250809232456.py +55 -0
  164. neverlib/.specstory/.what-is-this.md +69 -0
  165. 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
  166. neverlib/Docs/audio_aug/test_snr.py +55 -0
  167. neverlib/__init__.py +2 -2
  168. neverlib/audio_aug/HarmonicDistortion.py +79 -0
  169. neverlib/audio_aug/TFDrop.py +41 -0
  170. neverlib/audio_aug/TFMask.py +56 -0
  171. neverlib/audio_aug/__init__.py +1 -1
  172. neverlib/audio_aug/audio_aug.py +19 -5
  173. neverlib/audio_aug/clip_aug.py +41 -0
  174. neverlib/audio_aug/coder_aug.py +209 -0
  175. neverlib/audio_aug/coder_aug2.py +118 -0
  176. neverlib/audio_aug/loss_packet_aug.py +103 -0
  177. neverlib/audio_aug/quant_aug.py +78 -0
  178. neverlib/data_analyze/README.md +234 -0
  179. neverlib/data_analyze/__init__.py +14 -0
  180. neverlib/data_analyze/dataset_analyzer.py +590 -0
  181. neverlib/data_analyze/quality_metrics.py +364 -0
  182. neverlib/data_analyze/rms_distrubution.py +62 -0
  183. neverlib/data_analyze/spectral_analysis.py +218 -0
  184. neverlib/data_analyze/statistics.py +406 -0
  185. neverlib/data_analyze/temporal_features.py +126 -0
  186. neverlib/data_analyze/visualization.py +468 -0
  187. neverlib/filter/README.md +101 -0
  188. neverlib/filter/__init__.py +7 -0
  189. neverlib/filter/auto_eq/README.md +165 -0
  190. neverlib/filter/auto_eq/__init__.py +36 -0
  191. neverlib/filter/auto_eq/de_eq.py +360 -0
  192. neverlib/filter/auto_eq/freq_eq.py +76 -0
  193. neverlib/filter/auto_eq/ga_eq_advanced.py +577 -0
  194. neverlib/filter/auto_eq/ga_eq_basic.py +385 -0
  195. neverlib/filter/biquad.py +45 -0
  196. neverlib/filter/common.py +5 -6
  197. neverlib/filter/core.py +339 -0
  198. neverlib/metrics/dnsmos.py +117 -0
  199. neverlib/metrics/lpc_lsp.py +118 -0
  200. neverlib/metrics/snr.py +184 -0
  201. neverlib/metrics/spec.py +136 -0
  202. neverlib/metrics/test_pesq.py +35 -0
  203. neverlib/metrics/time.py +68 -0
  204. neverlib/tests/test_vad.py +21 -0
  205. neverlib/utils/audio_split.py +2 -1
  206. neverlib/utils/message.py +4 -4
  207. neverlib/utils/utils.py +36 -16
  208. neverlib/vad/PreProcess.py +6 -3
  209. neverlib/vad/README.md +10 -10
  210. neverlib/vad/VAD_Energy.py +1 -1
  211. neverlib/vad/VAD_Silero.py +2 -2
  212. neverlib/vad/VAD_WebRTC.py +2 -2
  213. neverlib/vad/VAD_funasr.py +2 -2
  214. neverlib/vad/VAD_statistics.py +3 -3
  215. neverlib/vad/VAD_vadlib.py +3 -3
  216. neverlib/vad/VAD_whisper.py +2 -2
  217. neverlib/vad/__init__.py +1 -1
  218. neverlib/vad/class_get_speech.py +4 -4
  219. neverlib/vad/class_vad.py +1 -1
  220. neverlib/vad/utils.py +47 -5
  221. {neverlib-0.2.2.dist-info → neverlib-0.2.4.dist-info}/METADATA +120 -120
  222. neverlib-0.2.4.dist-info/RECORD +229 -0
  223. {neverlib-0.2.2.dist-info → neverlib-0.2.4.dist-info}/WHEEL +1 -1
  224. neverlib/Documents/vad/VAD_Energy.ipynb +0 -159
  225. neverlib/Documents/vad/VAD_Silero.ipynb +0 -305
  226. neverlib/Documents/vad/VAD_WebRTC.ipynb +0 -183
  227. neverlib/Documents/vad/VAD_funasr.ipynb +0 -179
  228. neverlib/Documents/vad/VAD_ppasr.ipynb +0 -175
  229. neverlib/Documents/vad/VAD_statistics.ipynb +0 -522
  230. neverlib/Documents/vad/VAD_vadlib.ipynb +0 -184
  231. neverlib/Documents/vad/VAD_whisper.ipynb +0 -430
  232. neverlib/utils/waveform_analyzer.py +0 -51
  233. neverlib/wav_data/000_short.wav +0 -0
  234. neverlib-0.2.2.dist-info/RECORD +0 -40
  235. {neverlib-0.2.2.dist-info → neverlib-0.2.4.dist-info}/licenses/LICENSE +0 -0
  236. {neverlib-0.2.2.dist-info → neverlib-0.2.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,385 @@
1
+ import sys
2
+ sys.path.append("..")
3
+ import random
4
+ import numpy as np
5
+ import soundfile as sf
6
+ import scipy.signal as signal
7
+ from scipy.signal import lfilter, freqz
8
+ import matplotlib.pyplot as plt
9
+ from deap import base, creator, tools, algorithms
10
+ from neverlib.filter import EQFilter
11
+
12
+ # --- Configuration Parameters ---
13
+ SOURCE_AUDIO_PATH = "../../data/white.wav"
14
+ TARGET_AUDIO_PATH = "../../data/white_EQ.wav"
15
+ OUTPUT_MATCHED_AUDIO_PATH = "../../data/white_matched.wav"
16
+
17
+ SR = 16000
18
+ NFFT = 1024
19
+ FREQ_NUM = NFFT // 2 + 1
20
+
21
+ # --- GA Configuration - 需要重点调整这些参数 ---
22
+ MAX_FILTERS = 10 # 尝试增加或减少, 取决于EQ预期复杂度
23
+ POPULATION_SIZE = 200 # 建议增加 (例如 100-200)
24
+ MAX_GENERATIONS = 150 # 建议增加 (例如 100-300, 甚至更多)
25
+ CXPB = 0.7 # 交叉概率
26
+ MUTPB_IND = 0.4 # 个体变异概率, 可以适当增加以增强探索
27
+ MUTPB_GENE = 0.15 # 基因变异概率, 可以适当增加
28
+
29
+ # 复杂度惩罚因子 - 关键调整参数!
30
+ # 初始可以尝试较小的值, 如果滤波器过多, 再逐渐增大
31
+ COMPLEXITY_PENALTY_FACTOR = 0.01 # 尝试不同的值: 0.001, 0.005, 0.01, 0.05, 0.1 等
32
+
33
+ # Filter Type Definitions (整数编码)
34
+ FILTER_TYPE_PEAK = 0
35
+ FILTER_TYPE_LOW_SHELF = 1
36
+ FILTER_TYPE_HIGH_SHELF = 2
37
+ AVAILABLE_FILTER_TYPES = [FILTER_TYPE_PEAK, FILTER_TYPE_LOW_SHELF, FILTER_TYPE_HIGH_SHELF]
38
+
39
+ FILTER_TYPE_MAP_INT_TO_STR = {
40
+ FILTER_TYPE_PEAK: 'peak',
41
+ FILTER_TYPE_LOW_SHELF: 'low_shelf',
42
+ FILTER_TYPE_HIGH_SHELF: 'high_shelf',
43
+ }
44
+ # 创建EQFilter实例
45
+ eq_filter = EQFilter(fs=SR)
46
+
47
+ FILTER_TYPE_MAP_INT_TO_FUNC = {
48
+ FILTER_TYPE_PEAK: eq_filter.PeakingFilter,
49
+ FILTER_TYPE_LOW_SHELF: eq_filter.LowshelfFilter,
50
+ FILTER_TYPE_HIGH_SHELF: eq_filter.HighshelfFilter,
51
+ }
52
+
53
+ # Parameter Bounds
54
+ FC_MIN, FC_MAX = 20, SR / 2 - 50
55
+ Q_MIN_PEAK, Q_MAX_PEAK = 0.3, 10.0
56
+ Q_MIN_SHELF, Q_MAX_SHELF = 0.3, 2.0
57
+ DBGAIN_MIN, DBGAIN_MAX = -25.0, 25.0 # 略微扩大增益范围
58
+
59
+ Q_BOUNDS_PER_TYPE = {
60
+ FILTER_TYPE_PEAK: (Q_MIN_PEAK, Q_MAX_PEAK),
61
+ FILTER_TYPE_LOW_SHELF: (Q_MIN_SHELF, Q_MAX_SHELF),
62
+ FILTER_TYPE_HIGH_SHELF: (Q_MIN_SHELF, Q_MAX_SHELF),
63
+ }
64
+
65
+ GENES_PER_FILTER_BLOCK = 5
66
+
67
+ creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
68
+ creator.create("Individual", list, fitness=creator.FitnessMin)
69
+
70
+ toolbox = base.Toolbox()
71
+
72
+
73
+ def generate_active_gene(): return random.randint(0, 1)
74
+ def generate_type_gene(): return random.choice(AVAILABLE_FILTER_TYPES)
75
+ def generate_fc_gene(): return random.uniform(FC_MIN, FC_MAX)
76
+
77
+
78
+ def generate_q_gene(filter_type_int):
79
+ q_min, q_max = Q_BOUNDS_PER_TYPE[filter_type_int]
80
+ return random.uniform(q_min, q_max)
81
+
82
+
83
+ def generate_dbgain_gene(): return random.uniform(DBGAIN_MIN, DBGAIN_MAX)
84
+
85
+
86
+ attribute_generators = []
87
+ for i in range(MAX_FILTERS):
88
+ toolbox.register(f"active_{i}", generate_active_gene)
89
+ attribute_generators.append(toolbox.__getattribute__(f"active_{i}"))
90
+ toolbox.register(f"type_{i}", generate_type_gene)
91
+ attribute_generators.append(toolbox.__getattribute__(f"type_{i}"))
92
+ toolbox.register(f"fc_{i}", generate_fc_gene)
93
+ attribute_generators.append(toolbox.__getattribute__(f"fc_{i}"))
94
+ attribute_generators.append(None)
95
+ toolbox.register(f"dbgain_{i}", generate_dbgain_gene)
96
+ attribute_generators.append(toolbox.__getattribute__(f"dbgain_{i}"))
97
+
98
+
99
+ def individual_creator():
100
+ chromosome = []
101
+ for i in range(MAX_FILTERS):
102
+ active = generate_active_gene()
103
+ type_val = generate_type_gene()
104
+ fc = generate_fc_gene()
105
+ q = generate_q_gene(type_val)
106
+ dbgain = generate_dbgain_gene()
107
+ chromosome.extend([active, type_val, fc, q, dbgain])
108
+ return creator.Individual(chromosome)
109
+
110
+
111
+ toolbox.register("individual", individual_creator)
112
+ toolbox.register("population", tools.initRepeat, list, toolbox.individual)
113
+
114
+
115
+ def get_magnitude_spectrum_db(audio, sr, n_fft):
116
+ # 使用 spectrogram 进行频谱估计, 并对时间帧平均
117
+ f_spec, t_spec, Sxx_spec = signal.spectrogram(audio, fs=sr, nperseg=n_fft, noverlap=n_fft // 4, scaling='spectrum', mode='magnitude')
118
+ avg_magnitude_spectrum_spec = np.mean(Sxx_spec, axis=1)
119
+ db_spectrum = 20 * np.log10(avg_magnitude_spectrum_spec + 1e-12)
120
+ return f_spec, db_spectrum
121
+
122
+
123
+ def get_single_filter_freq_response_db_from_coeffs(filter_params, num_freq_points, fs_proc):
124
+ # 为每个滤波器创建新的EQFilter实例, 使用正确的采样率
125
+ eq_filter_instance = EQFilter(fs=fs_proc)
126
+ filter_type = filter_params['type_int']
127
+ if filter_type == FILTER_TYPE_PEAK:
128
+ filter_func = eq_filter_instance.PeakingFilter
129
+ elif filter_type == FILTER_TYPE_LOW_SHELF:
130
+ filter_func = eq_filter_instance.LowshelfFilter
131
+ else: # HIGH_SHELF
132
+ filter_func = eq_filter_instance.HighshelfFilter
133
+
134
+ b, a = filter_func(fc=filter_params['fc'], Q=filter_params['q'], dBgain=filter_params['dBgain'])
135
+ w_native, h_native = freqz(b, a, worN=num_freq_points, fs=fs_proc)
136
+ response_db_native = 20 * np.log10(np.abs(h_native) + 1e-12)
137
+ return w_native, response_db_native
138
+
139
+
140
+ def get_combined_eq_response_db(active_filters_list, num_points_calc, fs_proc, freq_axis_target):
141
+ num_target_freq_bins = len(freq_axis_target)
142
+ combined_response_db = np.zeros(num_target_freq_bins)
143
+ if not active_filters_list:
144
+ return combined_response_db
145
+
146
+ for p_dict in active_filters_list:
147
+ w_native, individual_response_db_native = get_single_filter_freq_response_db_from_coeffs(
148
+ p_dict, num_points_calc, fs_proc
149
+ )
150
+ individual_response_db_interp = np.interp(
151
+ freq_axis_target, w_native, individual_response_db_native
152
+ )
153
+ combined_response_db += individual_response_db_interp
154
+ return combined_response_db
155
+
156
+
157
+ target_eq_shape_db_global = None
158
+ objective_freq_axis_global = None
159
+
160
+
161
+ def evaluate_individual(individual_chromosome):
162
+ global target_eq_shape_db_global, objective_freq_axis_global
163
+ if target_eq_shape_db_global is None or objective_freq_axis_global is None:
164
+ raise ValueError("全局目标频谱未设置!") # 中文注释
165
+
166
+ active_filters_params_list = []
167
+ num_active_filters = 0
168
+
169
+ for i in range(MAX_FILTERS):
170
+ base_idx = i * GENES_PER_FILTER_BLOCK
171
+ is_active = individual_chromosome[base_idx]
172
+
173
+ if is_active == 1:
174
+ num_active_filters += 1
175
+ filter_type_int = individual_chromosome[base_idx + 1]
176
+ fc_val = individual_chromosome[base_idx + 2]
177
+ q_val = individual_chromosome[base_idx + 3]
178
+ dbgain_val = individual_chromosome[base_idx + 4]
179
+
180
+ fc_val = np.clip(fc_val, FC_MIN, FC_MAX)
181
+ q_min_type, q_max_type = Q_BOUNDS_PER_TYPE[filter_type_int]
182
+ q_val = np.clip(q_val, q_min_type, q_max_type)
183
+ dbgain_val = np.clip(dbgain_val, DBGAIN_MIN, DBGAIN_MAX)
184
+
185
+ active_filters_params_list.append({
186
+ 'type_int': filter_type_int,
187
+ 'fc': fc_val,
188
+ 'q': q_val,
189
+ 'dBgain': dbgain_val,
190
+ 'fs': SR
191
+ })
192
+
193
+ if not active_filters_params_list:
194
+ achieved_eq_response_db = np.zeros_like(target_eq_shape_db_global)
195
+ else:
196
+ achieved_eq_response_db = get_combined_eq_response_db(
197
+ active_filters_params_list,
198
+ FREQ_NUM,
199
+ SR,
200
+ objective_freq_axis_global
201
+ )
202
+
203
+ error = np.sum((achieved_eq_response_db - target_eq_shape_db_global)**2)
204
+
205
+ # 调整复杂度惩罚项的计算方式, 使其与误差的量级更相关
206
+ # 例如, 如果误差本身就很大, 那么滤波器的数量惩罚可以相对小一些
207
+ # 或者, 如果目标EQ形状本身就很复杂(变化剧烈), 那么多用几个滤波器也是合理的
208
+ # penalty_scale = 1 + np.mean(np.abs(target_eq_shape_db_global)) # 基于目标EQ形状的平均绝对值
209
+ penalty_scale = np.sum(target_eq_shape_db_global**2) / len(target_eq_shape_db_global) if len(target_eq_shape_db_global) > 0 else 1.0
210
+ if penalty_scale < 1e-3:
211
+ penalty_scale = 1.0 # 避免除以过小的值或0
212
+
213
+ complexity_cost = COMPLEXITY_PENALTY_FACTOR * num_active_filters * (1 + penalty_scale * 0.1)
214
+
215
+ total_cost = error + complexity_cost
216
+ return (total_cost,)
217
+
218
+
219
+ toolbox.register("evaluate", evaluate_individual)
220
+ toolbox.register("mate", tools.cxTwoPoint)
221
+
222
+
223
+ def custom_mutate(individual, indpb_gene):
224
+ for i in range(len(individual)):
225
+ if random.random() < indpb_gene:
226
+ block_index = i // GENES_PER_FILTER_BLOCK
227
+ gene_type_in_block = i % GENES_PER_FILTER_BLOCK
228
+
229
+ current_filter_type_gene_idx = block_index * GENES_PER_FILTER_BLOCK + 1
230
+ current_filter_type = individual[current_filter_type_gene_idx]
231
+
232
+ if gene_type_in_block == 0: # Active gene
233
+ individual[i] = 1 - individual[i]
234
+ elif gene_type_in_block == 1: # Type gene
235
+ new_type = random.choice([t for t in AVAILABLE_FILTER_TYPES if t != individual[i]])
236
+ individual[i] = new_type
237
+ q_gene_idx = block_index * GENES_PER_FILTER_BLOCK + 3
238
+ individual[q_gene_idx] = generate_q_gene(new_type) # 根据新类型更新Q
239
+ elif gene_type_in_block == 2: # Fc gene
240
+ individual[i] = generate_fc_gene()
241
+ elif gene_type_in_block == 3: # Q gene
242
+ individual[i] = generate_q_gene(current_filter_type) # Q依赖于当前块的Type
243
+ elif gene_type_in_block == 4: # dBGain gene
244
+ individual[i] = generate_dbgain_gene()
245
+ return individual,
246
+
247
+
248
+ toolbox.register("mutate", custom_mutate, indpb_gene=MUTPB_GENE)
249
+ toolbox.register("select", tools.selTournament, tournsize=3) # 锦标赛选择, tournsize可调整
250
+
251
+
252
+ def main_ga():
253
+ global target_eq_shape_db_global, objective_freq_axis_global
254
+
255
+ source_audio, sr = sf.read(SOURCE_AUDIO_PATH)
256
+ target_audio, sr = sf.read(TARGET_AUDIO_PATH)
257
+
258
+ # wav_3956, sr = sf.read("../../data/3956_speech.wav")
259
+ # source_audio = wav_3956[:, 1]
260
+ # target_audio = wav_3956[:, 0]
261
+
262
+ assert sr == SR, "采样率不匹配"
263
+ assert source_audio.ndim == 1, "源音频必须是单声道"
264
+
265
+ source_freq_axis, source_db_spectrum = get_magnitude_spectrum_db(
266
+ source_audio, SR, NFFT
267
+ )
268
+ target_freq_axis, target_db_spectrum = get_magnitude_spectrum_db(
269
+ target_audio, SR, NFFT
270
+ )
271
+ assert np.array_equal(source_freq_axis, target_freq_axis), "源频谱和目标频谱的频率轴不一致"
272
+
273
+ target_eq_shape_db_global = target_db_spectrum - source_db_spectrum
274
+ objective_freq_axis_global = source_freq_axis
275
+
276
+ print(f"运行遗传算法 (种群: {POPULATION_SIZE}, 迭代: {MAX_GENERATIONS}, 最大滤波器数: {MAX_FILTERS})...") # 中文注释
277
+ population = toolbox.population(n=POPULATION_SIZE)
278
+ hall_of_fame = tools.HallOfFame(1) # 只记录最好的一个
279
+
280
+ # 设置统计信息
281
+ stats = tools.Statistics(lambda ind: ind.fitness.values)
282
+ stats.register("avg", np.mean)
283
+ stats.register("std", np.std)
284
+ stats.register("min", np.min)
285
+ stats.register("max", np.max)
286
+
287
+ # 运行GA
288
+ final_pop, logbook = algorithms.eaSimple(
289
+ population,
290
+ toolbox,
291
+ cxpb=CXPB,
292
+ mutpb=MUTPB_IND, # 个体变异概率
293
+ ngen=MAX_GENERATIONS,
294
+ stats=stats,
295
+ halloffame=hall_of_fame,
296
+ verbose=True # 打印每代统计信息
297
+ )
298
+
299
+ best_individual_chromosome = hall_of_fame[0]
300
+ print(f"\n最优个体适应度 (误差+惩罚): {best_individual_chromosome.fitness.values[0]:.4f}") # 中文注释
301
+
302
+ # 解码最优个体
303
+ optimized_eq_params_list = []
304
+ num_active_found = 0
305
+ print("\n--- Decoded Optimal EQ Filter Parameters ---") # 英文输出标题
306
+ for i in range(MAX_FILTERS):
307
+ base_idx = i * GENES_PER_FILTER_BLOCK
308
+ is_active = best_individual_chromosome[base_idx]
309
+ if is_active == 1:
310
+ num_active_found += 1
311
+ filter_type_int = best_individual_chromosome[base_idx + 1]
312
+ fc_val = best_individual_chromosome[base_idx + 2]
313
+ q_val = best_individual_chromosome[base_idx + 3]
314
+ dbgain_val = best_individual_chromosome[base_idx + 4]
315
+
316
+ param_dict = {
317
+ 'type': FILTER_TYPE_MAP_INT_TO_STR[filter_type_int],
318
+ 'fc': round(fc_val, 2),
319
+ 'q': round(q_val, 3),
320
+ 'dBgain': round(dbgain_val, 2),
321
+ 'fs': SR
322
+ }
323
+ optimized_eq_params_list.append(param_dict)
324
+ print(param_dict) # 打印每个找到的滤波器参数
325
+
326
+ if not optimized_eq_params_list:
327
+ print("Warning: Genetic algorithm did not find any active filters.")
328
+
329
+ # 应用EQ并保存 (如果找到了滤波器)
330
+ if optimized_eq_params_list and OUTPUT_MATCHED_AUDIO_PATH:
331
+ print(f"\nApplying optimized EQ to source audio and saving to {OUTPUT_MATCHED_AUDIO_PATH}...")
332
+
333
+ def apply_eq_to_signal_structural(audio, eq_params_list_decoded, fs):
334
+ processed_audio = np.copy(audio)
335
+ eq_filter_instance = EQFilter(fs=fs)
336
+
337
+ for p_dict_decoded in eq_params_list_decoded:
338
+ if p_dict_decoded['type'] == 'peak':
339
+ filter_func = eq_filter_instance.PeakingFilter
340
+ elif p_dict_decoded['type'] == 'low_shelf':
341
+ filter_func = eq_filter_instance.LowshelfFilter
342
+ else: # high_shelf
343
+ filter_func = eq_filter_instance.HighshelfFilter
344
+
345
+ b, a = filter_func(fc=p_dict_decoded['fc'], Q=p_dict_decoded['q'], dBgain=p_dict_decoded['dBgain'])
346
+ processed_audio = lfilter(b, a, processed_audio)
347
+ return processed_audio
348
+
349
+ source_audio_matched = apply_eq_to_signal_structural(source_audio, optimized_eq_params_list, SR)
350
+ sf.write(OUTPUT_MATCHED_AUDIO_PATH, source_audio_matched, SR)
351
+
352
+ # 生成对比图
353
+ print("Generating comparison plot...") # 英文输出
354
+ decoded_active_filters_for_eval = []
355
+ for p_dict in optimized_eq_params_list:
356
+ type_str_to_int_map = {v: k for k, v in FILTER_TYPE_MAP_INT_TO_STR.items()}
357
+ decoded_active_filters_for_eval.append({
358
+ 'type_int': type_str_to_int_map[p_dict['type']],
359
+ 'fc': p_dict['fc'],
360
+ 'q': p_dict['q'],
361
+ 'dBgain': p_dict['dBgain']
362
+ })
363
+
364
+ achieved_eq_response_for_sum_db = get_combined_eq_response_db(
365
+ decoded_active_filters_for_eval, FREQ_NUM, SR, objective_freq_axis_global
366
+ )
367
+ source_plus_achieved_eq_db = source_db_spectrum + achieved_eq_response_for_sum_db
368
+
369
+ plt.figure(figsize=(12, 7))
370
+ plt.semilogx(objective_freq_axis_global, source_db_spectrum, label='Source Audio Spectrum', alpha=0.8, color='deepskyblue')
371
+ plt.semilogx(objective_freq_axis_global, target_db_spectrum, label='Target Audio Spectrum', alpha=0.8, color='coral')
372
+ plt.semilogx(objective_freq_axis_global, source_plus_achieved_eq_db, label='Source Spectrum + Matched EQ', alpha=0.8, color='limegreen')
373
+
374
+ plt.title(f'EQ Matching Result ({num_active_found} active filters) - {SR}Hz')
375
+ plt.xlabel('Frequency (Hz)')
376
+ plt.ylabel('Magnitude (dB)')
377
+ plt.legend(loc='best')
378
+ plt.xscale('log')
379
+ plt.grid(True, ls="--", alpha=0.4)
380
+ plt.tight_layout()
381
+ plt.savefig("eq_matching_plot_3curves.png")
382
+
383
+
384
+ if __name__ == '__main__':
385
+ main_ga()
@@ -0,0 +1,160 @@
1
+ '''
2
+ Author: 凌逆战 | Never
3
+ Date: 2025-08-06 10:00:00
4
+ Description:
5
+ 要计算个性化 MOS 分数(干扰说话者受到惩罚),请提供“-p”参数,例如:python dnsmos.py -t ./SampleClips -o sample.csv -p
6
+ 要计算常规 MOS 分数,请省略“-p”参数。例如:python dnsmos.py -t ./SampleClips -o sample.csv
7
+ '''
8
+ import argparse
9
+ import concurrent.futures
10
+ import glob
11
+ import os
12
+ import librosa
13
+ import numpy as np
14
+ import onnxruntime as ort
15
+ import pandas as pd
16
+ import soundfile as sf
17
+ from tqdm import tqdm
18
+
19
+ SAMPLING_RATE = 16000
20
+ INPUT_LENGTH = 9.01
21
+
22
+
23
+ class ComputeScore:
24
+ def __init__(self, primary_model_path, p808_model_path) -> None:
25
+ self.onnx_sess = ort.InferenceSession(primary_model_path)
26
+ self.p808_onnx_sess = ort.InferenceSession(p808_model_path)
27
+
28
+ def audio_melspec(self, audio, n_mels=120, frame_size=320, hop_length=160, sr=16000, to_db=True):
29
+ mel_spec = librosa.feature.melspectrogram(y=audio, sr=sr, n_fft=frame_size+1, hop_length=hop_length, n_mels=n_mels)
30
+ if to_db:
31
+ mel_spec = (librosa.power_to_db(mel_spec, ref=np.max)+40)/40
32
+ return mel_spec.T
33
+
34
+ def get_polyfit_val(self, sig, bak, ovr, is_personalized_MOS):
35
+ if is_personalized_MOS:
36
+ p_ovr = np.poly1d([-0.00533021, 0.005101, 1.18058466, -0.11236046])
37
+ p_sig = np.poly1d([-0.01019296, 0.02751166, 1.19576786, -0.24348726])
38
+ p_bak = np.poly1d([-0.04976499, 0.44276479, -0.1644611, 0.96883132])
39
+ else:
40
+ p_ovr = np.poly1d([-0.06766283, 1.11546468, 0.04602535])
41
+ p_sig = np.poly1d([-0.08397278, 1.22083953, 0.0052439])
42
+ p_bak = np.poly1d([-0.13166888, 1.60915514, -0.39604546])
43
+
44
+ sig_poly = p_sig(sig)
45
+ bak_poly = p_bak(bak)
46
+ ovr_poly = p_ovr(ovr)
47
+
48
+ return sig_poly, bak_poly, ovr_poly
49
+
50
+ def __call__(self, fpath, sampling_rate, is_personalized_MOS):
51
+ aud, input_fs = sf.read(fpath)
52
+ fs = sampling_rate
53
+ if input_fs != fs:
54
+ audio = librosa.resample(aud, input_fs, fs)
55
+ else:
56
+ audio = aud
57
+ actual_audio_len = len(audio)
58
+ len_samples = int(INPUT_LENGTH*fs)
59
+ while len(audio) < len_samples:
60
+ audio = np.append(audio, audio)
61
+
62
+ num_hops = int(np.floor(len(audio)/fs) - INPUT_LENGTH)+1
63
+ hop_len_samples = fs
64
+ predicted_mos_sig_seg_raw = []
65
+ predicted_mos_bak_seg_raw = []
66
+ predicted_mos_ovr_seg_raw = []
67
+ predicted_mos_sig_seg = []
68
+ predicted_mos_bak_seg = []
69
+ predicted_mos_ovr_seg = []
70
+ predicted_p808_mos = []
71
+
72
+ for idx in range(num_hops):
73
+ audio_seg = audio[int(idx*hop_len_samples): int((idx+INPUT_LENGTH)*hop_len_samples)]
74
+ if len(audio_seg) < len_samples:
75
+ continue
76
+
77
+ input_features = np.array(audio_seg).astype('float32')[np.newaxis, :]
78
+ p808_input_features = np.array(self.audio_melspec(audio=audio_seg[:-160])).astype('float32')[np.newaxis, :, :]
79
+ oi = {'input_1': input_features}
80
+ p808_oi = {'input_1': p808_input_features}
81
+ p808_mos = self.p808_onnx_sess.run(None, p808_oi)[0][0][0]
82
+ mos_sig_raw, mos_bak_raw, mos_ovr_raw = self.onnx_sess.run(None, oi)[0][0]
83
+ mos_sig, mos_bak, mos_ovr = self.get_polyfit_val(mos_sig_raw, mos_bak_raw, mos_ovr_raw, is_personalized_MOS)
84
+ predicted_mos_sig_seg_raw.append(mos_sig_raw)
85
+ predicted_mos_bak_seg_raw.append(mos_bak_raw)
86
+ predicted_mos_ovr_seg_raw.append(mos_ovr_raw)
87
+ predicted_mos_sig_seg.append(mos_sig)
88
+ predicted_mos_bak_seg.append(mos_bak)
89
+ predicted_mos_ovr_seg.append(mos_ovr)
90
+ predicted_p808_mos.append(p808_mos)
91
+
92
+ clip_dict = {'filename': fpath, 'len_in_sec': actual_audio_len/fs, 'sr': fs}
93
+ clip_dict['num_hops'] = num_hops
94
+ clip_dict['OVRL_raw'] = np.mean(predicted_mos_ovr_seg_raw)
95
+ clip_dict['SIG_raw'] = np.mean(predicted_mos_sig_seg_raw)
96
+ clip_dict['BAK_raw'] = np.mean(predicted_mos_bak_seg_raw)
97
+ clip_dict['OVRL'] = np.mean(predicted_mos_ovr_seg)
98
+ clip_dict['SIG'] = np.mean(predicted_mos_sig_seg)
99
+ clip_dict['BAK'] = np.mean(predicted_mos_bak_seg)
100
+ clip_dict['P808_MOS'] = np.mean(predicted_p808_mos)
101
+ return clip_dict
102
+
103
+
104
+ def main(args):
105
+ models = glob.glob(os.path.join(args.testset_dir, "*"))
106
+ audio_clips_list = []
107
+ p808_model_path = os.path.join('DNSMOS', 'model_v8.onnx')
108
+
109
+ if args.personalized_MOS:
110
+ primary_model_path = os.path.join('pDNSMOS', 'sig_bak_ovr.onnx')
111
+ else:
112
+ primary_model_path = os.path.join('DNSMOS', 'sig_bak_ovr.onnx')
113
+
114
+ compute_score = ComputeScore(primary_model_path, p808_model_path)
115
+
116
+ rows = []
117
+ clips = []
118
+ clips = glob.glob(os.path.join(args.testset_dir, "*.wav"))
119
+ is_personalized_eval = args.personalized_MOS
120
+ desired_fs = SAMPLING_RATE
121
+ for m in tqdm(models):
122
+ max_recursion_depth = 10
123
+ audio_path = os.path.join(args.testset_dir, m)
124
+ audio_clips_list = glob.glob(os.path.join(audio_path, "*.wav"))
125
+ while len(audio_clips_list) == 0 and max_recursion_depth > 0:
126
+ audio_path = os.path.join(audio_path, "**")
127
+ audio_clips_list = glob.glob(os.path.join(audio_path, "*.wav"))
128
+ max_recursion_depth -= 1
129
+ clips.extend(audio_clips_list)
130
+
131
+ with concurrent.futures.ThreadPoolExecutor() as executor:
132
+ future_to_url = {executor.submit(compute_score, clip, desired_fs, is_personalized_eval): clip for clip in clips}
133
+ for future in tqdm(concurrent.futures.as_completed(future_to_url)):
134
+ clip = future_to_url[future]
135
+ try:
136
+ data = future.result()
137
+ except Exception as exc:
138
+ print('%r generated an exception: %s' % (clip, exc))
139
+ else:
140
+ rows.append(data)
141
+
142
+ df = pd.DataFrame(rows)
143
+ if args.csv_path:
144
+ csv_path = args.csv_path
145
+ df.to_csv(csv_path)
146
+ else:
147
+ print(df.describe())
148
+
149
+
150
+ if __name__ == "__main__":
151
+ parser = argparse.ArgumentParser()
152
+ parser.add_argument('-t', "--testset_dir", default='.',
153
+ help='Path to the dir containing audio clips in .wav to be evaluated')
154
+ parser.add_argument('-o', "--csv_path", default=None, help='Dir to the csv that saves the results')
155
+ parser.add_argument('-p', "--personalized_MOS", action='store_true',
156
+ help='Flag to indicate if personalized MOS score is needed or regular')
157
+
158
+ args = parser.parse_args()
159
+
160
+ main(args)