import os import random import sys import pandas as pd import torchaudio from pathlib import Path from tqdm import tqdm workspace_dir = Path(__file__).parent.parent.parent sys.path.append(str(workspace_dir.joinpath('src'))) # sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from handle.text_handle import process_text from tokenizer import ASRTokenizer # 读取 TSV # df = pd.read_csv(workspace_dir / '.data/ug/train.tsv', sep='\t') # # 计算每个音频的时长(秒) # durations = [] # for audio_path in tqdm(workspace_dir / '.data/ug/clips' / df['path'], desc="计算音频时长"): # try: # # 获取音频信息(不加载整个文件,速度快) # info = torchaudio.info(audio_path) # duration = info.num_frames / info.sample_rate # durations.append(duration) # except Exception as e: # print(f"读取失败: {audio_path}, 错误: {e}") # durations.append(None) # # 添加到 DataFrame # df['duration'] = durations # # 统计 # print(df['duration'].describe()) # print(f"超过20秒的样本: {(df['duration'] > 20).sum()}") # 保存结果(可选) # df.to_csv('data/audio/train_with_duration.tsv', sep='\t', index=False) # audio_path = workspace_dir / '.data/ug/clips/common_voice_ug_40252722.mp3' # info = torchaudio.info(audio_path) # print("info:", info) import pandas as pd import torch import torchaudio import numpy as np from pathlib import Path from tqdm import tqdm def analyze_audio_quality(audio_path, sample_rate_target=16000): """ 评估音频质量,返回多个指标 """ try: # 加载音频 waveform, sr = torchaudio.load(str(audio_path)) # 重采样 + 单声道 if sr != sample_rate_target: resampler = torchaudio.transforms.Resample(sr, sample_rate_target) waveform = resampler(waveform) if waveform.shape[0] > 1: waveform = waveform.mean(dim=0, keepdim=True) # 归一化 max_val = waveform.abs().max() if max_val > 0: waveform = waveform / max_val # ===== 指标 1: 静音比例 ===== silence_threshold = 0.01 is_silence = waveform.abs() < silence_threshold silence_ratio = is_silence.float().mean().item() # ===== 指标 2: 动态范围 ===== frame_len = int(0.025 * sample_rate_target) # 25ms hop_len = int(0.010 * sample_rate_target) # 10ms num_frames = (waveform.shape[1] - frame_len) // hop_len + 1 if num_frames <= 0: return None energies = [] for i in range(num_frames): start = i * hop_len frame = waveform[:, start:start + frame_len] rms = frame.pow(2).mean().sqrt().item() energies.append(rms) energies = np.array(energies) energies = np.clip(energies, 1e-10, None) log_energies = 20 * np.log10(energies) dynamic_range = log_energies.max() - log_energies.min() # ===== 指标 3: 语音活跃度 ===== energy_threshold = np.percentile(energies, 30) voice_ratio = (energies > energy_threshold).mean() # ===== 指标 4: 频谱中心频率 ===== spec_transform = torchaudio.transforms.Spectrogram(n_fft=512) spec = spec_transform(waveform) # [1, freq, time] # 用 torch 计算,避免 numpy axis 问题 freqs = torch.fft.rfftfreq(512, d=1.0/sample_rate_target) spec_mean = spec.mean(dim=-1).squeeze() # [freq_bins] torch 张量 # 确保类型一致,用 torch 计算 spectral_centroid = (freqs * spec_mean).sum() / (spec_mean.sum() + 1e-10) spectral_centroid = spectral_centroid.item() # ===== 指标 5: 频谱通量 ===== spec_np = spec.squeeze().numpy() # [freq, time] flux = np.abs(np.diff(spec_np, axis=1)).mean() return { 'duration': waveform.shape[1] / sample_rate_target, 'silence_ratio': silence_ratio, 'dynamic_range_db': float(dynamic_range), 'voice_ratio': float(voice_ratio), 'spectral_centroid': spectral_centroid, 'spectral_flux': float(flux), 'energy_std': float(energies.std()), } except Exception as e: print(f"失败: {audio_path}, {e}") return None # ===== 批量处理 ===== # df = pd.read_csv(workspace_dir / '.data/ug/train.tsv', sep='\t') # results = [] # for _, row in tqdm(df.iterrows(), total=len(df), desc="评估音频质量"): # audio_path = workspace_dir / '.data/ug/clips' / row['path'] # metrics = analyze_audio_quality(audio_path) # if metrics: # metrics['path'] = row['path'] # results.append(metrics) # # 汇总 # quality_df = pd.DataFrame(results) # print(quality_df.describe()) # # ===== 质量评分 ===== # def score_quality(row): # score = 100 # if row['silence_ratio'] > 0.5: # score -= 30 # elif row['silence_ratio'] > 0.3: # score -= 15 # if row['dynamic_range_db'] < 10: # score -= 25 # elif row['dynamic_range_db'] < 20: # score -= 10 # if row['voice_ratio'] < 0.3: # score -= 20 # if row['spectral_centroid'] > 4000: # score -= 15 # return max(0, score) # quality_df['quality_score'] = quality_df.apply(score_quality, axis=1) # quality_df['grade'] = pd.cut( # quality_df['quality_score'], # bins=[0, 40, 60, 80, 100], # labels=['差(弃用)', '较差', '一般', '良好'] # ) # print("\n质量分级统计:") # print(quality_df['grade'].value_counts()) # # 保存结果 # quality_df.to_csv('audio_quality_analysis.csv', index=False) # print("\n结果已保存到 audio_quality_analysis.csv") # Test # text = "مەن مەكتەپكە باردىم" # text = "ئاۋۋال كومپىيوتىرنى بىر كۆرسەم شۇنىڭغا قاراپ ماسلاشتۇرسام بوللاتتى" text = "قاپاقنى پۇلغا ئالماي، باراڭنى پۇلغا ئاپتۇ" # text = "ئامېىقى جاھان گۈرلىكىە قارشتۇرۇپ چوكشەڭگە ياردەم بېرىش ئۇرۇشىنىڭ ئلۇغ غەلبىسى جۇڭگو خەلقى ئورندىن دەستۇرغاندىكىن، دۇنيانىڭ شەرقىدە قەت كۆتۈرگەنلكىنى خىتاب لامىسى." # text = "ئاۋسترالىيە" text = " ھەر قانداق ئىشتا كىشىلەر بىلەن كېڭىشىش كېرەك. لېكىن كۆڭۈل تارتقان ئىشنى قىلىۋېرىش كېرەك." text = "\" 15 يىل بۇرۇن مەن بىلەن سەي چۇڭشىن (ئالى بابانىڭ قۇرغۇچىسىدىن بىرى) ئامېرىكىغا بېرىپ 30 نەچچە مەبلەغ سالغۇچى شىركەتلەر بىلەن كۆرۈشكىنىمىزدە ئۇلار بىزنى رەت قىلىپ ئىشىك سىرتىدا قالدۇرغاندى. ھېچكىم بىزگە ۋە كەلگۈسىمىزگە ئىشەنمىگەن ئىدى، ھېچكىممۇ بىزنىڭ ھازىر 300 مىليارد سودىنى تاماملىيالايدىغانلىقىمىزنى ئويلاپ خىيالىغىمۇ كەلتۈرمىگەن ئىدى.\" ئالى بابانىنىڭ ئورگىنى تەرىپىدىن ئىشلەنگەن ئىگىلىك تىكلەش ھۆججەتلىك فىلىمى \" بۇ چۈش ئەمەس\" نىڭ باش قىسمىدا مۇنداق بىر داڭلىق سۆز چىقىدۇ:\" مەن تاغدىن ئۆتكەن ۋاقتىمدا تاغ ماڭا گەپ قىلمىدى، مەن دېڭىزنى كېچىپ ئۆتكىنىمدە دېڭىز ماڭا گەپ قىلمىدى.\" بۇ فىلىمنىڭ قوشۇمچە ئىسمى بولسا \" مايۈن ۋە ئۇنىڭ مەڭگۈلۈك ' ياش ئالى'سى\" بولۇپ، ھەرخىل خەتەر ۋە قىيىنلىقنى بىرمۇ بىر يەڭگەن مايۈن ۋە ئۇنىڭ ئالى بابا قوشۇنىدىكى ھەمكارلاشقۇچىلىرىنىڭ قەيسەر كەچمىشى جانلىق بايان قىلىنغان." # text = "يۆ جيەنتاۋ يېقىنقى بىر مەزگىلدە، خىزمەتداشلىرى بىلەن نۇرغۇن قىيىنچىلىققا ئۇچرىغان شوپۇرغا ياردەم قىلغانلىقىنى، شۇنداقلا يېمەكلىك ۋە سۇ يەتكۈزۈپ بەرگەنلىكىنى، ئەمما ئاياغ سوۋغا قىلىشى تۇنجى قېتىم ئىكەنلىكىنى، بۈگۈنكىسى 20 يىللىق ساقچىلىق جەريانىدا تۇنجى قېتىم شوپۇرغا ئاياغ سوۋغا قىلىشى ئىكەنلىكىنى ئېيتتى." text = "يۆ جۇڭمىڭ مۇنداق دېدى: 2019- يىلى 12-ئايدا، مەملىكەتلىك خەلق قۇرۇلتىيى دائىمىي كومىتېتىنىڭ 44- قېتىملىق كومىتېت باشلىقلىرى يىغىنى مەملىكەتلىك خەلق قۇرۇلتىيى دائىمىي كومىتېتىنىڭ 2020-يىللىق قانۇن چىقىرىش خىزمىتى پىلانىنى پىرىنسىپ جەھەتتىن ماقۇللىدى، خىزمەت تەرتىپى بويىچە، 13-نۆۋەتلىك مەملىكەتلىك خەلق قۇرۇلتىيى 3-يىغىنىنىڭ روھى ۋە ۋەكىللەرنىڭ تەكلىپ-تەۋسىيەلىرىگە ئاساسەن پىلاننى تەڭشەش كېرەك. بۇ يىل 6-ئاينىڭ 1-كۈنى، 58-قېتىملىق كومىتېت باشلىقلىرى يىغىنى تەڭشەلگەندىن كېيىنكى يىللىق قانۇن چىقىرىش خىزمىتى پىلانىنى قاراپ چىقىپ ماقۇللىدى." text = " ئاشقازان-ئۈچەينىڭ لۆمۈلدىشىنى ئىلگىرى سۈرىدىغان دورىنى تاماقتىن بۇرۇن ئىستېمال قىلىش كېرەك." # text = "مەن مەكتەپكە باردىم" # text = "غەرىپئەللىرى" # text = "ئىكەنلىكىنى، بۈگۈنكىسى 20 يىللىق ساقچىلىق جەريانىدا تۇنجى" # text = " يېزىدىكى كەڭ، ئازادە ئۆي، باغلىرىنى تاشلاپ، خەقنىڭ ھويلىسىدا قورۇنۇپ-ئەيمىنىپ يەر دەسسەپ يۈردى. " # text = "بۇ يىللىق ئەمگەكچىلەر بايرىمىدا، 1-مايدىن 5-مايغىچە جەمئىي بەش كۈن دەم ئېلىشقا قويۇپ بېرىلىدۇ. 9-ماي (شەنبە) نورمال خىزمەت قىلىنىدۇ. شۇنىڭ بىلەن بىر ۋاقىتتا، ئۈرۈمچى شەھىرى تىيانشان رايونى، سايباغ رايونى، داۋانچىڭ رايونى، ئۈرۈمچى ناھىيەسىدىكى ئوتتۇرا، باشلانغۇچ مەكتەپلەر 29-ئاپرېلدىن 30-ئاپرېلغىچە ئەتىيازلىق دەم ئېلىشقا قويۇپ بېرىدۇ؛ سانجى ئوبلاستىدىكى ئوتتۇرا، باشلانغۇچ مەكتەپلەرنىڭ ئەتىيازلىق دەم ئېلىشى ئۈچ كۈن بولۇپ، بۇنىڭ ئىچىدە ئىككى كۈن 29-، 30-ئاپرېلغا ئورۇنلاشتۇرۇلىدۇ، قالغان بىر كۈن 1-ئىيۇنغا ئورۇنلاشتۇرۇلىدۇ، قۇربان ھېيتلىق دەم ئېلىش بىلەن بىرلەشتۈرۈلۈپ، ئۇدا ئالتە كۈن دەم ئېلىنىدۇ." # text = "ئەڭ يېڭى دەم ئېلىشقا قويۇپ بېرىش ئۇقتۇرۇشى! تەقەززالىق بىلەن كۈتكەن يەنە بىر دەم ئېلىش كېلەي دەپ قالدى! گوۋۇيۈەن بەنگۇڭتىڭىنىڭ «2025-يىللىق قىسمەن بايرام، دەم ئېلىش كۈنلىرىنى ئورۇنلاشتۇرۇش توغرىسىدىكى ئۇقتۇرۇشى»غا ئاساسەن، دۈەنۋۇ بايرىمىلىق دەم ئېلىشقا قويۇپ بېرىش ئورۇنلاشتۇرۇلۇشى تۆۋەندىكىچە: 5-ئاينىڭ 31-كۈنى (شەنبە)دىن 6-ئاينىڭ 2-كۈنىگىچە جەمئىي ئۈچ كۈن دەم ئېلىشقا قويۇپ بېرىلىدۇ! بۇ قېتىمقى دۈەنۋۇ بايرىمىلىق دەم ئېلىش ۋاقتى تەڭشەلمەيدۇ!" # text = "مەن شۇ چاغدا چۈشۈمدىمۇ كۆرۈپ باقمىغان مۇنچىۋالا جىق قېرىنداشلىرىم، جىگەرلىرىمنىڭ بارلىقىدىن سۆيۈندۈم." # text = "ئامېرىكا جاھان گىرلىكىگە قارشى تۇرۇپ، چاۋشەنگە ياردەم بىرىش ئۇرۇشىنىڭ ئۇلۇغ غەلبىسى، جۇڭگو خەلقى ئورۇندىن دەس تۇرغاندىنكىن دۇنيانىڭ شەرقىدە قەد كۆتۈرگەنلىكىنىڭ خىتاپنامىس" # result = export_syllabize(text) result = process_text(text) print(f"Original: {text}") print(f"Syllables: {result}") tokenizer = ASRTokenizer(vocab_path=workspace_dir / 'config/asr_vocab.json') print(text) ids = tokenizer.encode(text=text) # print(ids) print('|'.join(tokenizer.decode([id]) for id in ids))