python实现分离不同人声、wespeaker
文章目录
- 方案选型
- 步骤
- 2、代码
- 可用的几个模型
- 其他
- 如果遇到c++问题
- wespeaker下载安装步骤
- 其他
- 文档
剪映等工具都支持背景音和人声分离。
那么如果想分离不同人声呢?例如想把郭靖和黄蓉的声音分开。
方案选型
1、本地运行、无需上传 # 上传的不但慢,而且大多有限制
2、最好是国内的,如果需要注册hugging face,国内经常连不上
所以方案为:
ffmpeg
demucs(Meta/Facebook开发的-人声分离专家)
wespeaker(阿里达摩院-声纹识别专家)
代码流程:
你可以把我们的程序想象成一个音频加工厂,流程如下:1、原料入库(FFmpeg)输入:input.mp4(杂乱的视频文件)操作:提取音频流->转为标准 WAV 产出:input_temp.wav(纯净的音频原料)2、粗加工-分离人声(Demucs)输入:input_temp.wav 操作:AI 模型分析频谱->剥离背景音乐 产出:input_vocals.wav(只有人声的干音)3、精加工-声纹识别(Wespeaker)输入:input_vocals.wav 操作: 切片:检测哪里有人在说话(VAD)。 特征提取:给每段话打上“声纹指纹”。 聚类:把相同的指纹归为一类(比如“说话人 A”、“说话人 B”)。 产出:speaker_0.wav,speaker_1.wav(按人分好的文件)步骤
需要提前装好ffmpeg。
1、安装依赖
pip install torch torchaudio demucs scipy numpy pydub pip install s3prl pip install openai-whisper pip install pydub注:wespeaker国内镜像没有,先从git下载master包,然后再安装到本地即可。
2、代码
importosimporttorchimporttorchaudioimportnumpyasnpfromscipy.cluster.hierarchyimportfcluster,linkagefromscipy.spatial.distanceimportpdistimportwarningsimportsubprocess# 过滤警告warnings.filterwarnings("ignore",message=".*set_audio_backend.*")# ================= 配置区域 =================INPUT_FILE="input.mp4"# 你的原始 MP4 文件WESPEAKER_MODEL_PATH="wespeaker_vox1_en_resnet34.pt"OUTPUT_DIR="output_speakers"# ============================================print("🚀 正在启动音频分离与声纹识别程序...")# --- 核心修复:使用 Pydub/FFmpeg 预处理音频 ---defprepare_audio(input_file):""" 将任意格式音频转换为 WAV,绕过 torchaudio 读取 MP4 的 Bug """ifnotos.path.exists(input_file):print(f"❌ 找不到输入文件:{input_file}")returnNone# 如果已经是 wav,直接返回ifinput_file.lower().endswith('.wav'):returninput_file output_wav=input_file.rsplit('.',1)[0]+"_temp.wav"print(f"🔄 正在使用 FFmpeg 将{input_file}转换为 WAV...")# 调用系统 FFmpeg# 注意:这里直接调用 'ffmpeg',依赖系统环境变量cmd=['ffmpeg','-y','-i',input_file,'-vn',# 不处理视频'-acodec','pcm_s16le',# 编码为 PCM (WAV)'-ar','44100',# 采样率 44100 (Demucs 标准)'-ac','2',# 双声道output_wav]try:# 尝试直接运行 ffmpegsubprocess.run(cmd,check=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)print(f"✅ 转换成功:{output_wav}")returnoutput_wavexceptsubprocess.CalledProcessErrorase:print(f"❌ FFmpeg 转换失败:{e.stderr.decode()}")print("💡 请确保在终端输入 'ffmpeg' 能直接运行")returnNone# --- 步骤 1: 使用 Demucs 去除背景音乐 ---defstep1_separate_vocals(input_file):print("\n"+"="*30)print("🎬 [步骤 1] 正在使用 Demucs 去除背景音乐...")print("="*30)try:fromdemucs.pretrainedimportget_modelfromdemucs.applyimportapply_modelfromdemucs.audioimportsave_audio# 1. 加载模型model=get_model('htdemucs')# 2. 加载音频waveform,sample_rate=torchaudio.load(input_file)print(f"📥 音频加载完成:{waveform.shape}, 采样率:{sample_rate}")# 【关键修复】# demucs 需要 3 维输入: [Batch, Channels, Length]# torchaudio 输出 2 维: [Channels, Length]# 我们需要在第 0 维增加一个 Batch 维度ifwaveform.dim()==2:waveform=waveform.unsqueeze(0)print(f"🔄 调整后的音频形状:{waveform.shape}")# 3. 执行分离print("🔄 正在分离音轨 (这可能需要几分钟)...")# 现在传入 3 维数据,就不会报错了out=apply_model(model,waveform,device='cpu',split=True,overlap=0.25)# out 是 [Batch, Sources, Length],取第 0 个 batchsources=out[0]print(f"🎵 分离完成,共得到{sources.shape[0]}条音轨")# 4. 提取人声 (通常索引 0 是人声)vocals=sources[0]# 5. 保存人声vocals_file=input_file.rsplit('.',1)[0]+"_vocals.wav"save_audio(vocals,vocals_file,samplerate=44100)print(f"✅ 人声提取成功:{vocals_file}")returnvocals_fileexceptExceptionase:print(f"❌ 步骤 1 失败:{e}")importtraceback traceback.print_exc()returnNone# --- 步骤 2: 简单的语音活动检测 ---defsimple_energy_vad(waveform,sample_rate,threshold_ratio=0.02,min_duration=0.5):ifwaveform.shape[0]>1:mono=waveform.mean(dim=0)else:mono=waveform.squeeze()energy=torch.abs(mono)max_energy=energy.max()ifmax_energy==0:return[]threshold=max_energy*threshold_ratio mask=energy>threshold segments=[]start_sample=0in_speech=Falsefori,is_speechinenumerate(mask):ifis_speechandnotin_speech:start_sample=i in_speech=Trueelifnotis_speechandin_speech:end_sample=i duration=(end_sample-start_sample)/sample_rateifduration>min_duration:segments.append((start_sample,end_sample))in_speech=Falsereturnsegments# --- 步骤 3: 声纹聚类 ---defstep2_diarize_speakers(vocals_file,output_dir):print("\n"+"="*30)print("🧠 [步骤 2] 正在识别说话人...")print("="*30)ifnotos.path.exists(WESPEAKER_MODEL_PATH):print(f"❌ 找不到模型文件:{WESPEAKER_MODEL_PATH}")returnwaveform,sample_rate=torchaudio.load(vocals_file)ifwaveform.shape[0]>1:waveform=torch.mean(waveform,dim=0,keepdim=True)print(f"⏳ 正在加载 Wespeaker 模型...")try:importwespeaker speaker_model=wespeaker.load_model(WESPEAKER_MODEL_PATH)device='cuda'iftorch.cuda.is_available()else'cpu'speaker_model.set_device(device)print("✅ 模型加载成功")exceptExceptionase:print(f"❌ 模型加载失败:{e}")returnprint("🔪 正在检测语音片段...")segments=simple_energy_vad(waveform,sample_rate)print(f"🔍 检测到{len(segments)}个有效语音片段")iflen(segments)<2:print("⚠️ 语音片段太少,无法聚类")returnembeddings=[]valid_segments=[]print("📝 正在提取声纹特征...")forstart,endinsegments:segment_wave=waveform[:,start:end]embedding=speaker_model.extract_embedding(segment_wave.numpy())ifembeddingisnotNone:embeddings.append(embedding)valid_segments.append((start,end))iflen(embeddings)<2:print("❌ 无法提取足够的声纹特征")returnembeddings=np.array(embeddings)print("📊 正在进行聚类分析...")distances=pdist(embeddings,metric='cosine')Z=linkage(distances,'average')labels=fcluster(Z,t=0.5,criterion='distance')unique_labels=set(labels)print(f"✅ 识别出{len(unique_labels)}位说话人:{list(unique_labels)}")os.makedirs(output_dir,exist_ok=True)forlabelinunique_labels:indices=np.where(labels==label)[0]longest_segment=Nonemax_len=0foridxinindices:start,end=valid_segments[idx]duration=end-startifduration>max_len:max_len=duration longest_segment=waveform[:,start:end]output_path=os.path.join(output_dir,f"speaker_{label}.wav")torchaudio.save(output_path,longest_segment,sample_rate)print(f" - 保存:{output_path}(时长:{max_len/sample_rate:.2f}s)")# ================= 主程序 =================if__name__=="__main__":# 1. 预处理:MP4 -> WAVwav_file=prepare_audio(INPUT_FILE)ifwav_file:# 2. 分离vocals_path=step1_separate_vocals(wav_file)# 3. 聚类ifvocals_path:step2_diarize_speakers(vocals_path,OUTPUT_DIR)print("\n🎉 全部处理完成!")# 可选:清理临时文件# os.remove(wav_file)输出结果:
🚀 正在启动音频分离与声纹识别程序...🔄 正在使用 FFmpeg 将input.mp4 转换为 WAV...✅ 转换成功:input_temp.wav==============================🎬[步骤1]正在使用 Demucs 去除背景音乐...==============================Downloading:"https://dl.fbaipublicfiles.com/demucs/hybrid_transformer/955717e8-8726e21a.th"to C:\Users\user/.cache\torch\hub\checkpoints\955717e8-8726e21a.th100%|██████████|80.2M/80.2M[00:15<00:00,5.29MB/s]📥 音频加载完成:torch.Size([2,13218240]),采样率:44100🔄 调整后的音频形状:torch.Size([1,2,13218240])🔄 正在分离音轨(这可能需要几分钟)...🎵 分离完成,共得到4条音轨 ✅ 人声提取成功:input_temp_vocals.wav==============================🧠[步骤2]正在识别说话人...==============================❌ 找不到模型文件:wespeaker_vox1_en_resnet34.pt 🎉 全部处理完成!果然成功了,效果还不错,就是优点慢。
可用的几个模型
mdx_extra:效果最好,速度最慢(龟速)。htdemucs_ft:效果不错,速度中等(推荐)。htdemucs:效果一般,速度很快。
其他
如果遇到c++问题
安装visual studio装下
wespeaker下载安装步骤
1、下文git地址下载master分支zip包。
2、pycharm所在虚拟环境中,执行安装命令
D:/PycharmProjects/transformer_demo/.venv/Scripts/python.exe-m install"安装包路径"其他
文档
wespeaker官网git地址(这里下载的master分支,可以pip install成功):
https://github.com/wenet-e2e/wespeaker
