JusTalk / process.py
A-yum1's picture
merge fixTranscription
a49984b
raw
history blame
8.67 kB
import os
import shutil
import numpy as np
import string
import random
from datetime import datetime
from pyannote.audio import Model, Inference
from pydub import AudioSegment
import base64
import binascii
class AudioProcessor():
def __init__(self,cache_dir = "/tmp/hf_cache"):
hf_token = os.environ.get("HF")
if hf_token is None:
raise ValueError("HUGGINGFACE_HUB_TOKEN が設定されていません。")
os.makedirs(cache_dir, exist_ok=True)
# pyannote モデルの読み込み
model = Model.from_pretrained("pyannote/embedding", use_auth_token=hf_token, cache_dir=cache_dir)
self.inference = Inference(model)
def cosine_similarity(self,vec1, vec2):
vec1 = vec1 / np.linalg.norm(vec1)
vec2 = vec2 / np.linalg.norm(vec2)
return np.dot(vec1, vec2)
def segment_audio(self, path, target_path='/tmp/setup_voice', seg_duration=1.0):
# 出力先ディレクトリが存在していれば中身をクリアする
if os.path.exists(target_path):
for file in os.listdir(target_path):
file_path = os.path.join(target_path, file)
if os.path.isfile(file_path):
os.remove(file_path)
else:
os.makedirs(target_path, exist_ok=True)
base_sound = AudioSegment.from_file(path)
duration_ms = len(base_sound)
seg_duration_ms = int(seg_duration * 1000)
for i, start in enumerate(range(0, duration_ms, seg_duration_ms)):
end = min(start + seg_duration_ms, duration_ms)
segment = base_sound[start:end]
# セグメントが指定長さに満たない場合、無音でパディングする
if len(segment) < seg_duration_ms:
silence = AudioSegment.silent(duration=(seg_duration_ms - len(segment)))
segment = segment + silence
segment.export(os.path.join(target_path, f'{i}.wav'), format="wav")
return target_path, duration_ms
def calculate_similarity(self,path1, path2):
embedding1 = self.inference(path1)
embedding2 = self.inference(path2)
return float(self.cosine_similarity(embedding1.data.flatten(), embedding2.data.flatten()))
def process_audio(self, reference_path, input_path, output_folder='/tmp/data/matched_segments', seg_duration=1.0, threshold=0.5):
# 出力先ディレクトリの中身をクリアする
if os.path.exists(output_folder):
for file in os.listdir(output_folder):
file_path = os.path.join(output_folder, file)
if os.path.isfile(file_path):
os.remove(file_path)
else:
os.makedirs(output_folder, exist_ok=True)
segmented_path, total_duration_ms = self.segment_audio(input_path, seg_duration=seg_duration)
matched_time_ms = 0
for file in sorted(os.listdir(segmented_path)):
segment_file = os.path.join(segmented_path, file)
similarity = self.calculate_similarity(segment_file, reference_path)
if similarity > threshold:
shutil.copy(segment_file, output_folder)
matched_time_ms += len(AudioSegment.from_file(segment_file))
unmatched_time_ms = total_duration_ms - matched_time_ms
return matched_time_ms, unmatched_time_ms,output_folder
def process_multi_audio(self, reference_pathes, input_path, output_folder='/tmp/data/matched_multi_segments', seg_duration=1.0, threshold=0.5):
# 出力先ディレクトリの中身をクリアする
if os.path.exists(output_folder):
for file in os.listdir(output_folder):
file_path = os.path.join(output_folder, file)
if os.path.isfile(file_path):
os.remove(file_path)
else:
os.makedirs(output_folder, exist_ok=True)
# 入力音声をセグメントに分割
segmented_path, total_duration_ms = self.segment_audio(input_path, seg_duration=seg_duration)
segment_files = sorted(os.listdir(segmented_path))
num_segments = len(segment_files)
# 各リファレンスごとにセグメントとの類似度を計算し、行列 (rows: reference, columns: segment) を作成
similarity = []
for reference_path in reference_pathes:
ref_similarity = []
for file in segment_files:
segment_file = os.path.join(segmented_path, file)
sim = self.calculate_similarity(segment_file, reference_path)
ref_similarity.append(sim)
similarity.append(ref_similarity)
# 転置行列を作成 (rows: segment, columns: reference)
similarity_transposed = []
for seg_idx in range(num_segments):
seg_sim = []
for ref_idx in range(len(reference_pathes)):
seg_sim.append(similarity[ref_idx][seg_idx])
similarity_transposed.append(seg_sim)
# 各セグメントについて、最も高い類似度のリファレンスを選択
best_matches = []
for seg_sim in similarity_transposed:
best_ref = np.argmax(seg_sim) # 最も類似度の高いリファレンスのインデックス
# 閾値チェック (必要に応じて)
if seg_sim[best_ref] < threshold:
best_matches.append(None) # 閾値未満の場合はマッチなしとする
else:
best_matches.append(best_ref)
# 各リファレンスごとに一致時間を集計 (セグメントごとの長さ seg_duration を加算)
matched_time = [0] * len(reference_pathes)
for match in best_matches:
if match is not None:
matched_time[match] += seg_duration
return matched_time, segmented_path
def save_audio_from_base64(self,base64_audio,output_dir,output_filename,temp_format='webm'):
try:
# Base64デコードして音声バイナリを取得
try:
audio_binary = base64.b64decode(base64_audio)
except binascii.Error:
raise ValueError("Invalid Base64 input data")
# 保存するディレクトリを作成
os.makedirs(output_dir,exist_ok=True)
# 一時ファイルに保存(実際の形式は WebM などと仮定)
temp_audio_path = os.path.join(output_dir,"temp_audio")
try:
with open(temp_audio_path,'wb') as f:
f.write(audio_binary)
# pydub を使って一時ファイルを WAV に変換
# ※ここでは WebM 形式と仮定していますが、実際の形式に合わせて format の指定を変更してください
try:
audio = AudioSegment.from_file(temp_audio_path,format=temp_format)
except Exception as e:
audio = AudioSegment.from_file(temp_audio_path) #形式が不明な場合は自動判別させる(ただし変換できない場合もあり)
# 音声ファイルを保存
wav_audio_path = os.path.join(output_dir,output_filename)
audio.export(wav_audio_path,format="wav")
finally:
#一時ファイルを削除
if os.path.exists(temp_audio_path):
os.remove(temp_audio_path)
return wav_audio_path
except ValueError as e:
print(f"Value Error: {e}")
except FileNotFoundError as e:
print(f"File Not Found Error: {e}")
except Exception as e:
print(f"Unexpected Error: {e}")
return None
def delete_files_in_directory(self,directory_path):
try:
# ディレクトリ内のすべてのファイルを取得
for filename in os.listdir(directory_path):
file_path = os.path.join(directory_path, filename)
# ファイルのみ削除する
if os.path.isfile(file_path):
os.remove(file_path)
print(f"{file_path} を削除しました")
except Exception as e:
print(f"エラーが発生しました: {e}")