Spaces:
Running
Running
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}") | |