File size: 8,674 Bytes
77591a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1804233
77591a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1804233
77591a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a49984b
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

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}")