0.研究背景
在實際的開發中可能會遇到這樣的問題,老板讓你把音頻中的每個講話人的聲音分離成不同的音頻片段。你可以使用au等專業的音頻處理軟件手動分離。但是這樣效率太慢了,現在ai這么發達,我們能否借助ai之力來分離一條音頻中的不同的說話人呢?答案是肯定可以的。
這里將利用聲紋識別加上語音識別來對音頻中不同的說話人進行語者分離。
1.技術選擇
市面上開源的聲紋識別和語音識別項目有很多,比如funasr,cam++就是兩個不錯的選擇,并且funasr是國內大廠阿里巴巴旗下開源的一個集成了ASR和標點符號預測,聲紋識別,聲紋對比等眾多模型的一個工具框架。那么本次項目就是基于funasr進行編程開發的。
2.項目源碼
項目已經開源到我的代碼倉庫中,大家可以訪問https://github.com/lukeewin/AudioSeparationGUI
如果國內的小伙伴們不方便訪問github那么也可以訪問gitee,https://gitee.com/lukeewin/AudioSeparationGUI
3.項目功能
改項目支持對音頻中每個說話人進行分離,不限制說話人數量,比如你的音頻中存在10個說話人,也是可以進行分離的。
同時改項目還支持對分離后的音頻,把相同的說話人講的聲音合并在一個音頻文件中。
除了支持音頻的分離外,該項目還支持分隔視頻片段,通過聲音驅動分隔視頻,形成視頻片段。
4.項目部分核心功能代碼
這里這粘貼部分核心功能代碼,如果需要看詳細代碼,可以到上面提到的代碼倉庫中下載。
def trans():if len(selected_file_list) != 0 and save_path.get() != '' and save_path.get() is not None:for audio in selected_file_list:if os.path.exists(audio):audio_name = os.path.splitext(os.path.basename(audio))[0]_, audio_extension = os.path.splitext(audio)show_info_label.config(text=f'正在執行中,請勿關閉程序。{audio}')speaker_audios = {} # 每個說話人作為 key,value 為列表,列表中為當前說話人對應的每個音頻片段# 音頻預處理try:audio_bytes, _ = (ffmpeg.input(audio, threads=0, hwaccel='cuda').output("-", format="wav", acodec="pcm_s16le", ac=1, ar=16000).run(cmd=["ffmpeg", "-nostdin"], capture_stdout=True, capture_stderr=True))res = model.generate(input=audio_bytes, batch_size_s=300, is_final=True, sentence_timestamp=True)rec_result = res[0]asr_result_text = rec_result['text']if asr_result_text != '':sentences = []for sentence in rec_result["sentence_info"]:start = to_date(sentence["start"])end = to_date(sentence["end"])if sentences and sentence["spk"] == sentences[-1]["spk"]:sentences[-1]["text"] += "" + sentence["text"]sentences[-1]["end"] = endelse:sentences.append({"text": sentence["text"], "start": start, "end": end, "spk": sentence["spk"]})# 剪切音頻或視頻片段i = 0for stn in sentences:stn_txt = stn['text']start = stn['start']end = stn['end']# tmp_start = to_milliseconds(start)# tmp_end = to_milliseconds(end)# duration = round((tmp_end - tmp_start) / 1000, 3)spk = stn['spk']# 根據文件名和 spk 創建目錄date = datetime.now().strftime("%Y-%m-%d")final_save_path = os.path.join(save_path.get(), date, audio_name, str(spk))os.makedirs(final_save_path, exist_ok=True)# 獲取音視頻后綴file_ext = os.path.splitext(audio)[-1]final_save_file = os.path.join(final_save_path, str(i)+file_ext)spk_txt_path = os.path.join(save_path.get(), date, audio_name)spk_txt_file = os.path.join(spk_txt_path, f'spk{spk}.txt')spk_txt_queue.put({'spk_txt_file': spk_txt_file, 'spk_txt': stn_txt, 'start': start, 'end': end})i += 1try:if file_ext in support_audio_format:(ffmpeg.input(audio, threads=0, ss=start, to=end, hwaccel='cuda').output(final_save_file).run(cmd=["ffmpeg", "-nostdin"], overwrite_output=True, capture_stdout=True,capture_stderr=True))elif file_ext in support_video_format:final_save_file = os.path.join(final_save_path, str(i)+'.mp4')(ffmpeg.input(audio, threads=0, ss=start, to=end, hwaccel='cuda').output(final_save_file, vcodec='libx264', crf=23, acodec='aac', ab='128k').run(cmd=["ffmpeg", "-nostdin"], overwrite_output=True, capture_stdout=True,capture_stderr=True))else:print(f'{audio}不支持')except ffmpeg.Error as e:print(f"剪切音頻發生錯誤,錯誤信息:{e}")# 記錄說話人和對應的音頻片段,用于合并音頻片段if spk not in speaker_audios:speaker_audios[spk] = [] # 列表中存儲音頻片段speaker_audios[spk].append({'file': final_save_file, 'audio_name': audio_name})ret = {"text": asr_result_text, "sentences": sentences}print(f'{audio} 切分完成')result_queue.put(f'{audio} 切分完成')show_info_label.config(text=f'{audio} 切分完成')print(f'轉寫結果:{ret}')# 存入合并隊列audio_concat_queue.put(speaker_audios)else:print("沒有轉寫結果")except Exception as e:print(f"轉寫異常:{e}")else:print("輸入的文件不存在")messagebox.showinfo("提醒", "輸入的文件不存在")else:print("沒有填寫輸入輸出")messagebox.showinfo("提醒", "沒有填寫選擇文件或保存路徑")
5.運行效果
6.其它
該項目使用Python開發,這里推薦Python版本為3.8,同時該項目中還依賴于ffmpeg,因此你需要提前安裝好ffmpeg,并且配置好環境變量,這里需要注意,安裝的路徑中不要出現中文或者空格或者特殊字符。
如果你是小白,不懂如何運行這個項目,你也可以點擊這里。