zsy814's picture
Initial EchoLoc code release
d8bfe4a verified
Raw
History Blame Contribute Delete
5.59 kB
import os
import glob
import torch
import torchaudio
import torchaudio.transforms as T
from tqdm import tqdm
def merge_audio_data(root_dir, output_wav_root, output_tsv_path):
"""
遍历 root_dir 下的 lst 文件,将 prompt 和 target 音频拼接。
输出音频到 output_wav_root,输出元数据到 output_tsv_path。
"""
# 1. 准备工作
sub_dirs = ['en', 'zh']
silence_duration = 0.1 # 拼接中间插入 0.3秒 静音
# 创建输出目录
os.makedirs(output_wav_root, exist_ok=True)
tsv_lines = []
print(f"Scanning root directory: {root_dir}")
# 2. 收集 .lst 文件
lst_files = []
for lang in sub_dirs:
lang_dir = os.path.join(root_dir, lang)
if not os.path.exists(lang_dir):
continue
curr_lst_files = glob.glob(os.path.join(lang_dir, "*.lst"))
lst_files.extend(curr_lst_files)
print(f"Found {len(lst_files)} .lst files.")
success_count = 0
for lst_path in tqdm(lst_files, desc="Merging Audios"):
# 获取当前是 en 还是 zh
# 假设路径结构是 .../en/xxx.lst,所以 dirname 是 .../en,再 basename 就是 en
lang_category = os.path.basename(os.path.dirname(lst_path))
# 对应的输出子目录
save_dir = os.path.join(output_wav_root, lang_category)
os.makedirs(save_dir, exist_ok=True)
base_dir = os.path.dirname(lst_path)
with open(lst_path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
parts = line.split('|')
if len(parts) < 4:
continue
utt_id = parts[0]
prompt_text = parts[1]
prompt_rel_path = parts[2]
target_text = parts[3]
# -------------------------------------------------
# ✅ 文本拼接逻辑修改
# -------------------------------------------------
if lang_category == 'zh':
# 中文模式:直接拼接,不加空格
full_text = f"{prompt_text}{target_text}"
else:
# 英文模式:中间加空格
full_text = f"{prompt_text} {target_text}"
# -------------------------------------------------
# 获取路径
# -------------------------------------------------
prompt_abs_path = os.path.join(base_dir, prompt_rel_path)
target_abs_path = os.path.join(base_dir, "wavs", f"{utt_id}.wav")
new_filename = f"{utt_id}_merged.wav"
new_file_path = os.path.join(save_dir, new_filename)
if not (os.path.exists(prompt_abs_path) and os.path.exists(target_abs_path)):
continue
try:
# -------------------------------------------------
# 音频处理
# -------------------------------------------------
# load 默认返回 float32 类型
wav_p, sr_p = torchaudio.load(prompt_abs_path)
wav_t, sr_t = torchaudio.load(target_abs_path)
# 统一通道数 (转为单声道)
if wav_p.shape[0] > 1: wav_p = torch.mean(wav_p, dim=0, keepdim=True)
if wav_t.shape[0] > 1: wav_t = torch.mean(wav_t, dim=0, keepdim=True)
# 统一采样率 (以 prompt 的 SR 为基准,通常是 16k, 22k 或 24k)
target_sr = sr_p
if sr_t != target_sr:
resampler = T.Resample(sr_t, target_sr)
wav_t = resampler(wav_t)
# 创建静音片段
silence_samples = int(silence_duration * target_sr)
silence_wav = torch.zeros(1, silence_samples)
# 拼接
merged_wav = torch.cat([wav_p, silence_wav, wav_t], dim=1)
# 保存
torchaudio.save(new_file_path, merged_wav, target_sr)
# 计算总时长
total_duration = merged_wav.shape[1] / target_sr
# 记录 (路径 \t 文本 \t 时长 \t 采样率)
tsv_lines.append(f"{new_file_path}\t{full_text}\t{total_duration:.4f}\t{target_sr}")
success_count += 1
except Exception as e:
print(f"[Error] Failed to process {utt_id}: {e}")
continue
# 4. 写入 TSV
print(f"Saving metadata to {output_tsv_path}...")
with open(output_tsv_path, 'w', encoding='utf-8') as f:
for line in tsv_lines:
f.write(line + "\n")
print(f"Done. Processed {success_count} files.")
if __name__ == "__main__":
# 配置你的路径
INPUT_ROOT = "/workspace/echoloc/datas/ntu_enzh_speech/"
OUTPUT_WAV_ROOT = "/workspace/echoloc/datas/ntu_enzh_speech/merged_reconstruction/wavs"
OUTPUT_TSV_PATH = "/workspace/echoloc/datas/ntu_enzh_speech/merged_reconstruction/test_merged.tsv"
merge_audio_data(INPUT_ROOT, OUTPUT_WAV_ROOT, OUTPUT_TSV_PATH)