neverlib 0.2.9__py3-none-any.whl → 0.3.0__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/Docs/audio_aug/test_volume.ipynb +8 -8
- neverlib/Docs/filter/biquad.ipynb +1 -1
- neverlib/Docs/filter/filter_family.ipynb +4 -4
- neverlib/Docs/vad/VAD_WebRTC.ipynb +4 -4
- neverlib/Docs/vad/VAD_whisper.ipynb +2 -2
- neverlib/LLM/__init__.py +37 -0
- neverlib/LLM/bailian.py +342 -0
- neverlib/LLM/image.py +73 -0
- neverlib/LLM/text.py +32 -0
- neverlib/QA/ImpactNoiseRejection.py +4 -4
- neverlib/QA/gen_init.py +13 -16
- neverlib/__init__.py +5 -5
- neverlib/audio_aug/HarmonicDistortion.py +11 -11
- neverlib/audio_aug/__init__.py +54 -0
- neverlib/audio_aug/audio_aug.py +18 -18
- neverlib/audio_aug/coder_aug.py +25 -25
- neverlib/audio_aug/coder_aug2.py +10 -10
- neverlib/audio_aug/loss_packet_aug.py +16 -16
- neverlib/audio_aug/quant_aug.py +7 -7
- neverlib/data_analyze/README.md +1 -1
- neverlib/data_analyze/__init__.py +44 -0
- neverlib/data_analyze/dataset_analyzer.py +2 -2
- neverlib/data_analyze/quality_metrics.py +12 -12
- neverlib/data_analyze/statistics.py +1 -1
- neverlib/data_analyze/visualization.py +1 -1
- neverlib/filter/README.md +3 -3
- neverlib/filter/__init__.py +23 -0
- neverlib/filter/auto_eq/README.md +2 -2
- neverlib/filter/auto_eq/__init__.py +36 -0
- neverlib/filter/auto_eq/de_eq.py +1 -1
- neverlib/filter/auto_eq/ga_eq_advanced.py +2 -2
- neverlib/filter/auto_eq/ga_eq_basic.py +1 -1
- neverlib/filter/biquad.py +1 -1
- neverlib/metrics/__init__.py +36 -0
- neverlib/metrics/dnsmos.py +2 -2
- neverlib/metrics/lpc_lsp.py +8 -8
- neverlib/metrics/snr.py +5 -5
- neverlib/metrics/spec.py +23 -23
- neverlib/metrics/test_pesq.py +3 -3
- neverlib/tests/__init__.py +17 -0
- neverlib/tests/test_imports.py +1 -1
- neverlib/utils/README.md +1 -1
- neverlib/utils/__init__.py +53 -1
- neverlib/utils/audio_split.py +1 -1
- neverlib/utils/checkGPU.py +2 -2
- neverlib/utils/floder.py +6 -6
- neverlib/utils/lazy_expose.py +1 -1
- neverlib/utils/lazy_module.py +6 -6
- neverlib/utils/message.py +2 -3
- neverlib/utils/utils.py +108 -2
- neverlib/vad/README.md +5 -5
- neverlib/vad/__init__.py +38 -0
- neverlib/vad/utils.py +1 -1
- {neverlib-0.2.9.dist-info → neverlib-0.3.0.dist-info}/METADATA +3 -3
- {neverlib-0.2.9.dist-info → neverlib-0.3.0.dist-info}/RECORD +58 -57
- neverlib/QA/impact_noise_rejection.png +0 -0
- neverlib/QA/out.pcm +0 -0
- neverlib/QA/out.wav +0 -0
- {neverlib-0.2.9.dist-info → neverlib-0.3.0.dist-info}/WHEEL +0 -0
- {neverlib-0.2.9.dist-info → neverlib-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {neverlib-0.2.9.dist-info → neverlib-0.3.0.dist-info}/top_level.txt +0 -0
neverlib/QA/gen_init.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 自动生成指定包目录下的 __init__.py
|
|
1
|
+
# 自动生成指定包目录下的 __init__.py(懒加载格式:lazy_loader.attach)
|
|
2
2
|
# 并且支持IDE友好的 __init__.py 版本
|
|
3
3
|
|
|
4
4
|
import ast
|
|
@@ -8,7 +8,7 @@ from typing import Dict, List, Set
|
|
|
8
8
|
|
|
9
9
|
def _extract_exports_from_module(py_file: Path) -> List[str]:
|
|
10
10
|
"""
|
|
11
|
-
优先读取模块内显式定义的 __all__
|
|
11
|
+
优先读取模块内显式定义的 __all__, 否则收集顶层的类与函数名(排除以下划线开头的)
|
|
12
12
|
"""
|
|
13
13
|
try:
|
|
14
14
|
source = py_file.read_text(encoding='utf-8')
|
|
@@ -46,8 +46,8 @@ def _extract_exports_from_module(py_file: Path) -> List[str]:
|
|
|
46
46
|
|
|
47
47
|
def generate_init_for_directory(package_dir: Path, ide_friendly: bool = False) -> Path:
|
|
48
48
|
"""
|
|
49
|
-
为指定目录生成懒加载版 __init__.py
|
|
50
|
-
-
|
|
49
|
+
为指定目录生成懒加载版 __init__.py(覆盖写入).
|
|
50
|
+
- 仅扫描一级子模块(同级 .py 文件), 忽略以下划线开头的模块与 __init__.py 本身.
|
|
51
51
|
- 生成结构:
|
|
52
52
|
from lazy_loader import attach
|
|
53
53
|
|
|
@@ -59,7 +59,7 @@ def generate_init_for_directory(package_dir: Path, ide_friendly: bool = False) -
|
|
|
59
59
|
|
|
60
60
|
Args:
|
|
61
61
|
package_dir: 包目录路径
|
|
62
|
-
ide_friendly: 是否生成IDE
|
|
62
|
+
ide_friendly: 是否生成IDE友好版本(使用TYPE_CHECKING)
|
|
63
63
|
"""
|
|
64
64
|
package_dir = package_dir.resolve()
|
|
65
65
|
if not package_dir.is_dir():
|
|
@@ -87,7 +87,7 @@ def generate_init_for_directory(package_dir: Path, ide_friendly: bool = False) -
|
|
|
87
87
|
lines.append('from typing import TYPE_CHECKING, Any')
|
|
88
88
|
lines.append('')
|
|
89
89
|
lines.append('if TYPE_CHECKING:')
|
|
90
|
-
lines.append(' #
|
|
90
|
+
lines.append(' # 仅在类型检查时导入, 提供IDE补全支持')
|
|
91
91
|
|
|
92
92
|
# 生成 TYPE_CHECKING 导入
|
|
93
93
|
for module_name in sorted(module_to_exports.keys()):
|
|
@@ -158,7 +158,7 @@ def generate_all_packages(root_dir: Path, ide_friendly: bool = False) -> List[Pa
|
|
|
158
158
|
if package_dir == root_dir or package_dir in processed_dirs:
|
|
159
159
|
continue
|
|
160
160
|
|
|
161
|
-
# 检查是否包含其他Python
|
|
161
|
+
# 检查是否包含其他Python文件(不是只有__init__.py)
|
|
162
162
|
has_other_py_files = any(
|
|
163
163
|
f.name != '__init__.py' and f.suffix == '.py'
|
|
164
164
|
for f in package_dir.iterdir()
|
|
@@ -181,13 +181,10 @@ def main():
|
|
|
181
181
|
|
|
182
182
|
parser = argparse.ArgumentParser(description='为指定包目录自动生成 __init__.py')
|
|
183
183
|
parser.add_argument('-p', '--package-dir', dest='package_dir',
|
|
184
|
-
help='
|
|
185
|
-
parser.add_argument('--all',
|
|
186
|
-
|
|
187
|
-
parser.add_argument('--
|
|
188
|
-
help='生成IDE友好版本(使用TYPE_CHECKING)')
|
|
189
|
-
parser.add_argument('--backup', action='store_true',
|
|
190
|
-
help='备份原始文件')
|
|
184
|
+
help='包目录路径, 比如 /path/to/pkg 或 ./neverlib/utils')
|
|
185
|
+
parser.add_argument('--all', default=True, help='为所有子包生成 __init__.py')
|
|
186
|
+
parser.add_argument('--ide-friendly', default=True, help='生成IDE友好版本(使用TYPE_CHECKING)')
|
|
187
|
+
parser.add_argument('--backup', default=False, help='备份原始文件')
|
|
191
188
|
args = parser.parse_args()
|
|
192
189
|
|
|
193
190
|
if args.all:
|
|
@@ -209,9 +206,9 @@ def main():
|
|
|
209
206
|
init_path = generate_init_for_directory(target_dir, ide_friendly=args.ide_friendly)
|
|
210
207
|
print(f'已生成: {init_path}')
|
|
211
208
|
if args.ide_friendly:
|
|
212
|
-
print('✅ 已生成IDE
|
|
209
|
+
print('✅ 已生成IDE友好版本(支持代码补全和类型检查)')
|
|
213
210
|
else:
|
|
214
|
-
print('ℹ️
|
|
211
|
+
print('ℹ️ 生成标准懒加载版本, 如需IDE支持请使用 --ide-friendly 参数')
|
|
215
212
|
|
|
216
213
|
|
|
217
214
|
if __name__ == '__main__':
|
neverlib/__init__.py
CHANGED
|
@@ -3,8 +3,8 @@ Author: 凌逆战 | Never
|
|
|
3
3
|
Date: 2025-09-07
|
|
4
4
|
Description: neverlib - 音频处理和VAD工具集
|
|
5
5
|
|
|
6
|
-
这是一个提供音频处理、增强、分析和语音活动检测(VAD)功能的Python
|
|
7
|
-
|
|
6
|
+
这是一个提供音频处理、增强、分析和语音活动检测(VAD)功能的Python库.
|
|
7
|
+
该库使用懒加载机制, 可以根据需要导入模块, 提高启动速度并减少内存占用.
|
|
8
8
|
|
|
9
9
|
主要功能模块:
|
|
10
10
|
- utils: 实用工具函数
|
|
@@ -14,7 +14,7 @@ Description: neverlib - 音频处理和VAD工具集
|
|
|
14
14
|
- data_analyze: 数据分析工具
|
|
15
15
|
- metrics: 音频质量评估指标
|
|
16
16
|
|
|
17
|
-
注意:
|
|
17
|
+
注意: 所有功能需要通过具体子模块导入, 例如:
|
|
18
18
|
from neverlib.audio_aug import limiter
|
|
19
19
|
from neverlib.vad import EnergyVad_C
|
|
20
20
|
from neverlib.filter import HPFilter
|
|
@@ -45,13 +45,13 @@ if TYPE_CHECKING:
|
|
|
45
45
|
from .filter import HPFilter
|
|
46
46
|
from .audio_aug import volume_norm
|
|
47
47
|
|
|
48
|
-
#
|
|
48
|
+
# 懒加载子包, 减少初始导入开销
|
|
49
49
|
from lazy_loader import attach
|
|
50
50
|
|
|
51
51
|
__getattr__, __dir__, __all__ = attach(
|
|
52
52
|
__name__,
|
|
53
53
|
submodules=["audio_aug", "data_analyze", "filter", "metrics", "utils", "vad", ],
|
|
54
|
-
#
|
|
54
|
+
# 只导出子模块, 不直接导出函数
|
|
55
55
|
submod_attrs={
|
|
56
56
|
"utils": ["get_path_list"],
|
|
57
57
|
"filter": ["HPFilter"],
|
|
@@ -9,22 +9,22 @@ import soundfile as sf
|
|
|
9
9
|
|
|
10
10
|
def apply_harmonic_distortion(wav, drive=1.0, mix=1.0):
|
|
11
11
|
"""
|
|
12
|
-
使用 tanh
|
|
12
|
+
使用 tanh 函数模拟简单的谐波失真(饱和效果).
|
|
13
13
|
|
|
14
14
|
参数:
|
|
15
|
-
wav (np.ndarray):
|
|
16
|
-
drive (float):
|
|
17
|
-
mix (float):
|
|
18
|
-
0.0 表示纯净原声, 1.0
|
|
15
|
+
wav (np.ndarray): 输入的音频波形.
|
|
16
|
+
drive (float): 驱动/输入增益. 建议范围 [1.0, 10.0]. 值越大失真越严重.
|
|
17
|
+
mix (float): 干/湿信号混合比例. 范围 [0.0, 1.0].
|
|
18
|
+
0.0 表示纯净原声, 1.0 表示完全失真的声音.
|
|
19
19
|
|
|
20
20
|
返回:
|
|
21
|
-
np.ndarray:
|
|
21
|
+
np.ndarray: 经过谐波失真的音频波形.
|
|
22
22
|
"""
|
|
23
23
|
# 确保 drive 和 mix 在合理范围
|
|
24
24
|
drive = max(1.0, drive)
|
|
25
25
|
mix = np.clip(mix, 0.0, 1.0)
|
|
26
26
|
|
|
27
|
-
# 1.
|
|
27
|
+
# 1. 归一化(可选但推荐), 以获得更可控的效果
|
|
28
28
|
peak = np.max(np.abs(wav))
|
|
29
29
|
if peak == 0:
|
|
30
30
|
return wav
|
|
@@ -45,12 +45,12 @@ def apply_harmonic_distortion(wav, drive=1.0, mix=1.0):
|
|
|
45
45
|
|
|
46
46
|
def apply_pedalboard_distortion(wav, sr, drive_db=15.0):
|
|
47
47
|
"""
|
|
48
|
-
使用 pedalboard
|
|
48
|
+
使用 pedalboard 库模拟高质量的谐波失真.
|
|
49
49
|
|
|
50
50
|
参数:
|
|
51
|
-
wav (np.ndarray):
|
|
52
|
-
sr (int):
|
|
53
|
-
drive_db (float): 驱动增益, 单位是分贝(dB)
|
|
51
|
+
wav (np.ndarray): 输入的音频波形.
|
|
52
|
+
sr (int): 采样率.
|
|
53
|
+
drive_db (float): 驱动增益, 单位是分贝(dB). 值越大失真越严重.
|
|
54
54
|
"""
|
|
55
55
|
try:
|
|
56
56
|
import pedalboard as pdb
|
neverlib/audio_aug/__init__.py
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# This file is auto-generated. Do NOT edit manually.
|
|
2
2
|
# Generated by neverlib.QA.gen_init
|
|
3
|
+
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
# 仅在类型检查时导入, 提供IDE补全支持
|
|
8
|
+
from .HarmonicDistortion import apply_harmonic_distortion, apply_pedalboard_distortion
|
|
9
|
+
from .TFMask import FreqMask, TimeMask
|
|
10
|
+
from .audio_aug import add_reverb, get_snr_use_vad, limiter, measure_loudness, snr_aug_Interpolation, snr_aug_changeClean, snr_aug_changeNoise, snr_aug_vad_Interpolation, snr_diff_changeClean, snr_diff_changeNoise, volume_aug, volume_aug_dbrms, volume_aug_linmax, volume_aug_lufs, volume_convert, volume_norm
|
|
11
|
+
from .clip_aug import clipping_aug
|
|
12
|
+
from .coder_aug import aac_aug_save, amr_nb_aug, amr_wb_aug, flac_aug, flac_encode_save, mp3_aug, opus_aug_save, vorbis_aug
|
|
13
|
+
from .coder_aug2 import apply_codec_distortion, check_codec_available
|
|
14
|
+
from .loss_packet_aug import simulate_packet_loss_vectorized
|
|
15
|
+
from .quant_aug import apply_mulaw_quantization, apply_uniform_quantization
|
|
16
|
+
|
|
17
|
+
# 运行时使用懒加载
|
|
3
18
|
from lazy_loader import attach
|
|
4
19
|
|
|
5
20
|
__getattr__, __dir__, __all__ = attach(
|
|
@@ -26,3 +41,42 @@ __getattr__, __dir__, __all__ = attach(
|
|
|
26
41
|
"quant_aug": ['apply_mulaw_quantization', 'apply_uniform_quantization'],
|
|
27
42
|
}
|
|
28
43
|
)
|
|
44
|
+
|
|
45
|
+
# 显式声明 __all__ 以便 IDE 识别
|
|
46
|
+
if TYPE_CHECKING:
|
|
47
|
+
__all__ = [
|
|
48
|
+
'apply_harmonic_distortion',
|
|
49
|
+
'apply_pedalboard_distortion',
|
|
50
|
+
'FreqMask',
|
|
51
|
+
'TimeMask',
|
|
52
|
+
'add_reverb',
|
|
53
|
+
'get_snr_use_vad',
|
|
54
|
+
'limiter',
|
|
55
|
+
'measure_loudness',
|
|
56
|
+
'snr_aug_Interpolation',
|
|
57
|
+
'snr_aug_changeClean',
|
|
58
|
+
'snr_aug_changeNoise',
|
|
59
|
+
'snr_aug_vad_Interpolation',
|
|
60
|
+
'snr_diff_changeClean',
|
|
61
|
+
'snr_diff_changeNoise',
|
|
62
|
+
'volume_aug',
|
|
63
|
+
'volume_aug_dbrms',
|
|
64
|
+
'volume_aug_linmax',
|
|
65
|
+
'volume_aug_lufs',
|
|
66
|
+
'volume_convert',
|
|
67
|
+
'volume_norm',
|
|
68
|
+
'clipping_aug',
|
|
69
|
+
'aac_aug_save',
|
|
70
|
+
'amr_nb_aug',
|
|
71
|
+
'amr_wb_aug',
|
|
72
|
+
'flac_aug',
|
|
73
|
+
'flac_encode_save',
|
|
74
|
+
'mp3_aug',
|
|
75
|
+
'opus_aug_save',
|
|
76
|
+
'vorbis_aug',
|
|
77
|
+
'apply_codec_distortion',
|
|
78
|
+
'check_codec_available',
|
|
79
|
+
'simulate_packet_loss_vectorized',
|
|
80
|
+
'apply_mulaw_quantization',
|
|
81
|
+
'apply_uniform_quantization',
|
|
82
|
+
]
|
neverlib/audio_aug/audio_aug.py
CHANGED
|
@@ -66,7 +66,7 @@ def snr_aug_changeNoise(clean, noise, target_snr, hpf=False, sr=16000, order=4,
|
|
|
66
66
|
|
|
67
67
|
def snr_aug_changeClean(clean, noise, target_snr, clip_check=True, hpf=False, sr=16000, order=4, cutoff=100):
|
|
68
68
|
"""
|
|
69
|
-
|
|
69
|
+
保持噪声不变, 改变纯净语音的幅度以达到目标信噪比
|
|
70
70
|
snr = 10 * log10(k*signal_power/ noise_power)
|
|
71
71
|
"""
|
|
72
72
|
assert clean.shape == noise.shape, "clean and noise must have the same shape"
|
|
@@ -258,7 +258,7 @@ def volume_aug(wav, range, rate, method="linmax"):
|
|
|
258
258
|
def volume_aug_dbrms(wav, target_level, hpf=False, sr=16000, order=4, cutoff=100):
|
|
259
259
|
"""
|
|
260
260
|
音量增强, 使用dbrms方法
|
|
261
|
-
|
|
261
|
+
为了避免有冲击响应影响了最大值, 所以使用dBRMS方法, 一定要选好范围, 不然容易削波
|
|
262
262
|
Args:
|
|
263
263
|
wav: 音频
|
|
264
264
|
target_level: 目标音量, 单位dB
|
|
@@ -303,12 +303,12 @@ def volume_aug_linmax(wav, target_level, hpf=False, sr=16000, order=4, cutoff=10
|
|
|
303
303
|
def volume_aug_lufs(wav, target_lufs, hpf=False, sr=16000, order=4, cutoff=100):
|
|
304
304
|
"""
|
|
305
305
|
音量增强, 使用lufs方法,
|
|
306
|
-
LUFS是“感知响度” →
|
|
306
|
+
LUFS是“感知响度” → 跟人耳听感对齐, 而且符合国际响度标准.
|
|
307
307
|
|
|
308
|
-
LUFS 使用
|
|
309
|
-
-
|
|
310
|
-
-
|
|
311
|
-
使用
|
|
308
|
+
LUFS 使用 感知加权(K-weighting)
|
|
309
|
+
- 高频增强(模拟人耳在 3~6kHz 的敏感)
|
|
310
|
+
- 低频衰减(降低 <100Hz 对响度的影响).
|
|
311
|
+
使用 短时块(400ms)能量 + 响度门限(-70 LUFS) 过滤极静音段.
|
|
312
312
|
|
|
313
313
|
Args:
|
|
314
314
|
wav: 音频
|
|
@@ -319,7 +319,7 @@ def volume_aug_lufs(wav, target_lufs, hpf=False, sr=16000, order=4, cutoff=100):
|
|
|
319
319
|
cutoff: 截止频率
|
|
320
320
|
|
|
321
321
|
补充信息:
|
|
322
|
-
## 推荐的 target_lufs
|
|
322
|
+
## 推荐的 target_lufs 值(行业参考)
|
|
323
323
|
平台 推荐目标 LUFS
|
|
324
324
|
YouTube / Spotify -14
|
|
325
325
|
Apple Music -16
|
|
@@ -336,7 +336,7 @@ def volume_aug_lufs(wav, target_lufs, hpf=False, sr=16000, order=4, cutoff=100):
|
|
|
336
336
|
if hpf:
|
|
337
337
|
wav_tmp = HPFilter(wav_tmp, sr=sr, order=4, cutoff=1000)
|
|
338
338
|
|
|
339
|
-
# Step2: 创建 LUFS
|
|
339
|
+
# Step2: 创建 LUFS 测量器(ITU-R BS.1770)
|
|
340
340
|
meter = pyln.Meter(sr, block_size=0.400) # block_size=400ms
|
|
341
341
|
|
|
342
342
|
# Step3: 测量当前 LUFS
|
|
@@ -353,18 +353,18 @@ def volume_aug_lufs(wav, target_lufs, hpf=False, sr=16000, order=4, cutoff=100):
|
|
|
353
353
|
|
|
354
354
|
def measure_loudness(wav, sr):
|
|
355
355
|
"""
|
|
356
|
-
测量音频的 Peak / RMS / LUFS
|
|
356
|
+
测量音频的 Peak / RMS / LUFS, 以及峰均比(Crest Factor)
|
|
357
357
|
|
|
358
358
|
参数:
|
|
359
|
-
wav: np.ndarray,
|
|
359
|
+
wav: np.ndarray, 音频波形(范围 [-1, 1])
|
|
360
360
|
sr: int, 采样率
|
|
361
361
|
|
|
362
362
|
返回:
|
|
363
363
|
dict:
|
|
364
364
|
- peak_dbfs: 峰值(dBFS)
|
|
365
365
|
- rms_dbfs: 均方根电平(dBFS)
|
|
366
|
-
- lufs:
|
|
367
|
-
- crest_factor_db: 峰均比(dB)
|
|
366
|
+
- lufs: 感知响度(LUFS, ITU-R BS.1770-4标准)
|
|
367
|
+
- crest_factor_db: 峰均比(dB), 峰值与RMS的差值
|
|
368
368
|
"""
|
|
369
369
|
EPS = 1e-9
|
|
370
370
|
|
|
@@ -399,23 +399,23 @@ def volume_convert(value,
|
|
|
399
399
|
|
|
400
400
|
参数:
|
|
401
401
|
value: float
|
|
402
|
-
|
|
402
|
+
输入值(可能是线性幅度、dBFS、LUFS)
|
|
403
403
|
from_unit: str
|
|
404
404
|
输入单位: "linear", "dBFS", "RMS_dBFS", "LUFS"
|
|
405
405
|
to_unit: str
|
|
406
406
|
输出单位: "linear", "dBFS", "RMS_dBFS", "LUFS"
|
|
407
407
|
crest_factor_db: float | None
|
|
408
|
-
|
|
408
|
+
峰均比(用于 Peak <-> RMS 的转换)
|
|
409
409
|
lufs_offset: float | None
|
|
410
|
-
LUFS 与 RMS
|
|
411
|
-
例如,
|
|
410
|
+
LUFS 与 RMS 的差值(用于 RMS <-> LUFS 转换)
|
|
411
|
+
例如, 对人声, LUFS ≈ RMS_dBFS - 1.5
|
|
412
412
|
|
|
413
413
|
返回:
|
|
414
414
|
float
|
|
415
415
|
"""
|
|
416
416
|
EPS = 1e-9
|
|
417
417
|
|
|
418
|
-
# Step 1:
|
|
418
|
+
# Step 1: 统一转换成线性幅度(以满刻度 1.0 为基准)
|
|
419
419
|
if from_unit == "linear":
|
|
420
420
|
lin_val = value
|
|
421
421
|
elif from_unit in ("dBFS", "Peak_dBFS"):
|
neverlib/audio_aug/coder_aug.py
CHANGED
|
@@ -6,19 +6,19 @@ Description:
|
|
|
6
6
|
"""
|
|
7
7
|
语音编码器数据增强
|
|
8
8
|
MP3 (MPEG-1 Audio Layer III)
|
|
9
|
-
-
|
|
10
|
-
- 压缩特性:在中低码率下, 高频部分可能会有“嗖嗖”声或模糊感 (swishing artifacts)
|
|
11
|
-
-
|
|
9
|
+
- 主要用途:音乐分发、播客. 互联网音频的“元老”和事实标准.
|
|
10
|
+
- 压缩特性:在中低码率下, 高频部分可能会有“嗖嗖”声或模糊感 (swishing artifacts).
|
|
11
|
+
- 数据增强目的:模拟通用网络音频压缩.
|
|
12
12
|
|
|
13
13
|
AAC (Advanced Audio Coding)
|
|
14
|
-
-
|
|
15
|
-
- 压缩特性:在同等码率下, 通常比 MP3 保留更多高频细节,
|
|
16
|
-
-
|
|
14
|
+
- 主要用途:流媒体、视频文件、现代设备. 被认为是 MP3 的继任者.
|
|
15
|
+
- 压缩特性:在同等码率下, 通常比 MP3 保留更多高频细节, 音质更好.
|
|
16
|
+
- 数据增强目的:模拟现代流媒体和移动设备上的音频压缩.
|
|
17
17
|
|
|
18
18
|
AMR (Adaptive Multi-Rate)
|
|
19
|
-
-
|
|
20
|
-
- 压缩特性:严格为语音设计,
|
|
21
|
-
- 数据增强目的:固定采样率:AMR-NB (窄带) 为 8kHz, AMR-WB (宽带) 为 16kHz
|
|
19
|
+
- 主要用途:语音通话、移动通信. 专为语音优化.
|
|
20
|
+
- 压缩特性:严格为语音设计, 会滤除大部分非语音频率(如音乐), 导致音乐听起来“电话音”效果.
|
|
21
|
+
- 数据增强目的:固定采样率:AMR-NB (窄带) 为 8kHz, AMR-WB (宽带) 为 16kHz. 这一点至关重要!
|
|
22
22
|
"""
|
|
23
23
|
import random
|
|
24
24
|
import numpy as np
|
|
@@ -48,8 +48,8 @@ def flac_aug(wav, sr):
|
|
|
48
48
|
|
|
49
49
|
def opus_aug_save(wav: np.ndarray, sr: int, output_filepath: str):
|
|
50
50
|
"""
|
|
51
|
-
对音频进行 Opus 压缩,
|
|
52
|
-
使用 PyAV 实现,
|
|
51
|
+
对音频进行 Opus 压缩, 并直接保存到文件.
|
|
52
|
+
使用 PyAV 实现, 比特率是随机的.
|
|
53
53
|
"""
|
|
54
54
|
try:
|
|
55
55
|
import av
|
|
@@ -91,8 +91,8 @@ def opus_aug_save(wav: np.ndarray, sr: int, output_filepath: str):
|
|
|
91
91
|
|
|
92
92
|
def aac_aug_save(wav: np.ndarray, sr: int, output_filepath: str):
|
|
93
93
|
"""
|
|
94
|
-
对音频进行 AAC 压缩,
|
|
95
|
-
使用 PyAV 实现,
|
|
94
|
+
对音频进行 AAC 压缩, 并直接保存到文件.
|
|
95
|
+
使用 PyAV 实现, 比特率是随机的.
|
|
96
96
|
"""
|
|
97
97
|
try:
|
|
98
98
|
import av
|
|
@@ -139,23 +139,23 @@ def flac_encode_save(wav: np.ndarray,
|
|
|
139
139
|
compression_level: int = 5,
|
|
140
140
|
bits_per_sample=None):
|
|
141
141
|
"""
|
|
142
|
-
使用 pyFLAC 将 NumPy 音频数组编码为 FLAC
|
|
142
|
+
使用 pyFLAC 将 NumPy 音频数组编码为 FLAC 文件并保存.
|
|
143
143
|
|
|
144
144
|
参数:
|
|
145
|
-
wav (np.ndarray):
|
|
146
|
-
或 int16/int32
|
|
147
|
-
sr (int):
|
|
148
|
-
output_filepath (str): 输出的 .flac
|
|
149
|
-
compression_level (int, optional): FLAC 压缩级别, 范围 0 (最快) 到 8 (最高压缩, 最慢)
|
|
150
|
-
默认为 5,
|
|
151
|
-
bits_per_sample (int, optional):
|
|
152
|
-
如果为 None, 函数会根据输入 wav 的 dtype
|
|
153
|
-
默认为 None
|
|
145
|
+
wav (np.ndarray): 输入的音频数据. 可以是 float 类型 (范围 -1.0 到 1.0)
|
|
146
|
+
或 int16/int32 类型.
|
|
147
|
+
sr (int): 音频的采样率.
|
|
148
|
+
output_filepath (str): 输出的 .flac 文件路径.
|
|
149
|
+
compression_level (int, optional): FLAC 压缩级别, 范围 0 (最快) 到 8 (最高压缩, 最慢).
|
|
150
|
+
默认为 5, 是一个很好的平衡点.
|
|
151
|
+
bits_per_sample (int, optional): 每个样本的位数. 通常是 16 或 24.
|
|
152
|
+
如果为 None, 函数会根据输入 wav 的 dtype 自动推断.
|
|
153
|
+
默认为 None.
|
|
154
154
|
"""
|
|
155
155
|
|
|
156
156
|
# --- 1. 数据类型和位深处理 ---
|
|
157
|
-
# pyFLAC 的 Encoder 需要 int16 或 int32 格式的 NumPy
|
|
158
|
-
#
|
|
157
|
+
# pyFLAC 的 Encoder 需要 int16 或 int32 格式的 NumPy 数组.
|
|
158
|
+
# 我们需要根据输入数据进行转换.
|
|
159
159
|
|
|
160
160
|
if bits_per_sample is None:
|
|
161
161
|
# 自动推断位深
|
neverlib/audio_aug/coder_aug2.py
CHANGED
|
@@ -22,17 +22,17 @@ def check_codec_available(codec):
|
|
|
22
22
|
|
|
23
23
|
def apply_codec_distortion(wav, sr, codec='libopus', bitrate='24k'):
|
|
24
24
|
"""
|
|
25
|
-
使用 FFmpeg 对音频应用指定的编解码器和码率,
|
|
25
|
+
使用 FFmpeg 对音频应用指定的编解码器和码率, 以模拟有损压缩失真.
|
|
26
26
|
|
|
27
27
|
参数:
|
|
28
|
-
wav (np.ndarray):
|
|
29
|
-
sr (int):
|
|
30
|
-
codec (str): FFmpeg
|
|
31
|
-
例如: 'aac', 'libopus', 'amr_nb', 'amr_wb', 'mp3'
|
|
32
|
-
bitrate (str): 目标码率, FFmpeg
|
|
28
|
+
wav (np.ndarray): 输入的音频波形.
|
|
29
|
+
sr (int): 采样率.
|
|
30
|
+
codec (str): FFmpeg 支持的编码器名称.
|
|
31
|
+
例如: 'aac', 'libopus', 'amr_nb', 'amr_wb', 'mp3'.
|
|
32
|
+
bitrate (str): 目标码率, FFmpeg 格式. 例如: '64k', '24k', '12.2k'.
|
|
33
33
|
|
|
34
34
|
返回:
|
|
35
|
-
np.ndarray:
|
|
35
|
+
np.ndarray: 经过编解码器失真的音频波形.
|
|
36
36
|
"""
|
|
37
37
|
# 检查编解码器是否可用
|
|
38
38
|
if not check_codec_available(codec):
|
|
@@ -108,17 +108,17 @@ if __name__ == "__main__":
|
|
|
108
108
|
wav_path = "/data/never/Desktop/kws_train/QA/wav_data/TIMIT.wav"
|
|
109
109
|
wav, wav_sr = sf.read(wav_path, always_2d=True)
|
|
110
110
|
|
|
111
|
-
# 1. 模拟 Opus
|
|
111
|
+
# 1. 模拟 Opus 编解码器(常用于VoIP, WebRTC)
|
|
112
112
|
print("应用 Opus 编解码器失真...")
|
|
113
113
|
opus_wav = apply_codec_distortion(wav, wav_sr, codec='libopus', bitrate='24k')
|
|
114
114
|
sf.write('augmented_opus.wav', opus_wav, wav_sr)
|
|
115
115
|
|
|
116
|
-
# 2. 模拟 AAC
|
|
116
|
+
# 2. 模拟 AAC 编解码器(常用于流媒体, Apple设备)
|
|
117
117
|
print("应用 AAC 编解码器失真...")
|
|
118
118
|
aac_wav = apply_codec_distortion(wav, wav_sr, codec='aac', bitrate='64k')
|
|
119
119
|
sf.write('augmented_aac.wav', aac_wav, wav_sr)
|
|
120
120
|
|
|
121
|
-
# 3. 模拟 AMR-NB
|
|
121
|
+
# 3. 模拟 AMR-NB 编解码器(常用于传统移动通信)
|
|
122
122
|
# AMR-NB 的码率是固定的几个值之一
|
|
123
123
|
amr_bitrates = [
|
|
124
124
|
'4.75k', '5.15k', '5.9k', '6.7k', '7.4k', '7.95k', '10.2k', '12.2k'
|
|
@@ -3,19 +3,19 @@ Author: 凌逆战 | Never
|
|
|
3
3
|
Date: 2025-07-29 16:28:23
|
|
4
4
|
Description: 丢包数据增强
|
|
5
5
|
“置零” vs “缺失”:两种不同的模拟思路
|
|
6
|
-
结论:对于音质修复, 强烈推荐使用“置零”法,
|
|
6
|
+
结论:对于音质修复, 强烈推荐使用“置零”法, 而不是“删除”法.
|
|
7
7
|
|
|
8
8
|
理由如下:
|
|
9
9
|
|
|
10
|
-
- 保持时序对齐 (Temporal Alignment): 在音质修复任务中,
|
|
11
|
-
如果使用“删除”法, 输入音频变短, 这种对齐关系就被破坏了, 模型无法学习 (损坏的t时刻) -> (修复的t时刻)
|
|
12
|
-
- 简化模型训练: 输入和输出的长度保持一致,
|
|
13
|
-
- 更贴近修复任务的本质: 音质修复, 特别是丢包补偿 (Packet Loss Concealment, PLC), 其任务本质是**“根据上下文,
|
|
10
|
+
- 保持时序对齐 (Temporal Alignment): 在音质修复任务中, 模型需要一个一一对应的关系. 输入 damaged_audio 的第 t 秒, 应该对应输出 repaired_audio 的第 t 秒, 也对应原始 original_audio 的第 t 秒.
|
|
11
|
+
如果使用“删除”法, 输入音频变短, 这种对齐关系就被破坏了, 模型无法学习 (损坏的t时刻) -> (修复的t时刻) 的映射.
|
|
12
|
+
- 简化模型训练: 输入和输出的长度保持一致, 意味着你可以直接使用标准的模型架构(如 U-Net), 而不需要处理复杂的可变长度序列问题.
|
|
13
|
+
- 更贴近修复任务的本质: 音质修复, 特别是丢包补偿 (Packet Loss Concealment, PLC), 其任务本质是**“根据上下文, 猜测并填充一段丢失的音频”**.
|
|
14
14
|
|
|
15
|
-
“置零”法完美地创造了这样一个场景:模型看到了上下文,
|
|
16
|
-
“删除”法则改变了问题的性质, 变成了“检测不连续点并试图将其平滑化”, 这与 PLC
|
|
15
|
+
“置零”法完美地创造了这样一个场景:模型看到了上下文, 也看到了一个明确的“空白”(零区域), 它的任务就是把这个空白填上.
|
|
16
|
+
“删除”法则改变了问题的性质, 变成了“检测不连续点并试图将其平滑化”, 这与 PLC 的目标不完全一致.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
“置零”是在深度学习框架下对“真正丢弃”问题的一种高效、可解的数学建模. 我们牺牲了一点点物理上的真实性, 换来了模型训练的可行性和高效性.
|
|
19
19
|
'''
|
|
20
20
|
import numpy as np
|
|
21
21
|
import soundfile as sf
|
|
@@ -29,20 +29,20 @@ def simulate_packet_loss_vectorized(
|
|
|
29
29
|
burst_prob: float = 0.2
|
|
30
30
|
) -> np.ndarray:
|
|
31
31
|
"""
|
|
32
|
-
|
|
33
|
-
使用 NumPy 的向量化操作以获得极高的性能, 避免在 Python 中使用 for
|
|
32
|
+
模拟带有突发性的网络丢包(向量化版本).
|
|
33
|
+
使用 NumPy 的向量化操作以获得极高的性能, 避免在 Python 中使用 for 循环.
|
|
34
34
|
|
|
35
35
|
参数:
|
|
36
|
-
- wav: 原始音频波形 (NumPy 数组)
|
|
37
|
-
- sample_rate:
|
|
38
|
-
- packet_duration_ms:
|
|
36
|
+
- wav: 原始音频波形 (NumPy 数组).
|
|
37
|
+
- sample_rate: 采样率.
|
|
38
|
+
- packet_duration_ms: 每个数据包的时长(毫秒).
|
|
39
39
|
packet_duration_ms_list= np.arange(10, 60, 5) # 包时长一般为10-60ms, 5ms间隔
|
|
40
40
|
packet_duration_ms = random.choice(packet_duration_ms_list)
|
|
41
|
-
- loss_rate:
|
|
42
|
-
- burst_prob:
|
|
41
|
+
- loss_rate: 基础丢包率.
|
|
42
|
+
- burst_prob: 突发丢包概率.
|
|
43
43
|
|
|
44
44
|
返回:
|
|
45
|
-
-
|
|
45
|
+
- 损坏后的音频波形(与原始长度相同).
|
|
46
46
|
"""
|
|
47
47
|
# 0. 复制数组, 避免修改原始输入
|
|
48
48
|
damaged_wav = wav.copy()
|
neverlib/audio_aug/quant_aug.py
CHANGED
|
@@ -9,14 +9,14 @@ import soundfile as sf
|
|
|
9
9
|
|
|
10
10
|
def apply_uniform_quantization(wav, bit_depth=8):
|
|
11
11
|
"""
|
|
12
|
-
对音频应用均匀量化,
|
|
12
|
+
对音频应用均匀量化, 模拟较低位深度的效果.
|
|
13
13
|
|
|
14
14
|
参数:
|
|
15
|
-
wav (np.ndarray): 输入的音频波形, 值应在 [-1.0, 1.0]
|
|
16
|
-
bit_depth (int):
|
|
15
|
+
wav (np.ndarray): 输入的音频波形, 值应在 [-1.0, 1.0] 范围内.
|
|
16
|
+
bit_depth (int): 目标模拟的位深度.
|
|
17
17
|
|
|
18
18
|
返回:
|
|
19
|
-
np.ndarray:
|
|
19
|
+
np.ndarray: 量化后的音频波形.
|
|
20
20
|
"""
|
|
21
21
|
# 计算量化级别数
|
|
22
22
|
num_levels = 2 ** bit_depth
|
|
@@ -36,11 +36,11 @@ def apply_uniform_quantization(wav, bit_depth=8):
|
|
|
36
36
|
|
|
37
37
|
def apply_mulaw_quantization(wav, bit_depth=8):
|
|
38
38
|
"""
|
|
39
|
-
【最终正确版】使用 mu_compress 和 mu_expand 模拟 μ-law
|
|
39
|
+
【最终正确版】使用 mu_compress 和 mu_expand 模拟 μ-law 量化失真.
|
|
40
40
|
|
|
41
41
|
参数:
|
|
42
|
-
wav (np.ndarray):
|
|
43
|
-
bit_depth (int):
|
|
42
|
+
wav (np.ndarray): 输入音频.
|
|
43
|
+
bit_depth (int): 目标模拟的位深度.
|
|
44
44
|
"""
|
|
45
45
|
try:
|
|
46
46
|
import librosa
|
neverlib/data_analyze/README.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# This file is auto-generated. Do NOT edit manually.
|
|
2
2
|
# Generated by neverlib.QA.gen_init
|
|
3
|
+
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
# 仅在类型检查时导入, 提供IDE补全支持
|
|
8
|
+
from .dataset_analyzer import AudioFileInfo, DatasetAnalyzer, analyze_audio_dataset
|
|
9
|
+
from .quality_metrics import QualityAnalyzer, audio_health_check, comprehensive_quality_assessment
|
|
10
|
+
from .rms_distrubution import get_rms_vad
|
|
11
|
+
from .spectral_analysis import SpectralAnalyzer, compute_spectral_features, frequency_domain_stats
|
|
12
|
+
from .statistics import AudioStatistics, compare_datasets, quick_audio_stats
|
|
13
|
+
from .temporal_features import dB, dc_offset, max_rms_amplitude, mean_rms_amplitude, min_rms_amplitude, peak_amplitude, rms_amplitude, short_time_energy, zero_crossing_rate
|
|
14
|
+
from .visualization import AudioVisualizer, create_analysis_dashboard, plot_dataset_overview
|
|
15
|
+
|
|
16
|
+
# 运行时使用懒加载
|
|
3
17
|
from lazy_loader import attach
|
|
4
18
|
|
|
5
19
|
__getattr__, __dir__, __all__ = attach(
|
|
@@ -23,3 +37,33 @@ __getattr__, __dir__, __all__ = attach(
|
|
|
23
37
|
"visualization": ['AudioVisualizer', 'create_analysis_dashboard', 'plot_dataset_overview'],
|
|
24
38
|
}
|
|
25
39
|
)
|
|
40
|
+
|
|
41
|
+
# 显式声明 __all__ 以便 IDE 识别
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
__all__ = [
|
|
44
|
+
'AudioFileInfo',
|
|
45
|
+
'DatasetAnalyzer',
|
|
46
|
+
'analyze_audio_dataset',
|
|
47
|
+
'QualityAnalyzer',
|
|
48
|
+
'audio_health_check',
|
|
49
|
+
'comprehensive_quality_assessment',
|
|
50
|
+
'get_rms_vad',
|
|
51
|
+
'SpectralAnalyzer',
|
|
52
|
+
'compute_spectral_features',
|
|
53
|
+
'frequency_domain_stats',
|
|
54
|
+
'AudioStatistics',
|
|
55
|
+
'compare_datasets',
|
|
56
|
+
'quick_audio_stats',
|
|
57
|
+
'dB',
|
|
58
|
+
'dc_offset',
|
|
59
|
+
'max_rms_amplitude',
|
|
60
|
+
'mean_rms_amplitude',
|
|
61
|
+
'min_rms_amplitude',
|
|
62
|
+
'peak_amplitude',
|
|
63
|
+
'rms_amplitude',
|
|
64
|
+
'short_time_energy',
|
|
65
|
+
'zero_crossing_rate',
|
|
66
|
+
'AudioVisualizer',
|
|
67
|
+
'create_analysis_dashboard',
|
|
68
|
+
'plot_dataset_overview',
|
|
69
|
+
]
|
|
@@ -130,7 +130,7 @@ class DatasetAnalyzer:
|
|
|
130
130
|
has_clipping = max_amplitude >= 0.99
|
|
131
131
|
is_silent = mean_amplitude < 1e-6
|
|
132
132
|
|
|
133
|
-
# SNR
|
|
133
|
+
# SNR估计(基于信号强度和噪声层)
|
|
134
134
|
snr_estimate = None
|
|
135
135
|
try:
|
|
136
136
|
if not is_silent:
|
|
@@ -548,7 +548,7 @@ def analyze_audio_dataset(directory: str, output_dir: str = None,
|
|
|
548
548
|
|
|
549
549
|
Args:
|
|
550
550
|
directory: 音频文件目录
|
|
551
|
-
output_dir:
|
|
551
|
+
output_dir: 输出目录(可选)
|
|
552
552
|
extensions: 支持的文件扩展名
|
|
553
553
|
sr: 目标采样率
|
|
554
554
|
n_jobs: 并行作业数
|