rein0421 commited on
Commit
7bc1736
·
verified ·
1 Parent(s): d8f8a02

Upload process.py

Browse files
Files changed (1) hide show
  1. process.py +387 -90
process.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
  import os
3
  import shutil
4
  import numpy as np
@@ -9,9 +8,10 @@ from pyannote.audio import Model, Inference
9
  from pydub import AudioSegment
10
  import base64
11
  import binascii
 
12
 
13
  class AudioProcessor():
14
- def __init__(self,cache_dir = "/tmp/hf_cache"):
15
  hf_token = os.environ.get("HF")
16
  if hf_token is None:
17
  raise ValueError("HUGGINGFACE_HUB_TOKEN が設定されていません。")
@@ -19,14 +19,145 @@ class AudioProcessor():
19
  # pyannote モデルの読み込み
20
  model = Model.from_pretrained("pyannote/embedding", use_auth_token=hf_token, cache_dir=cache_dir)
21
  self.inference = Inference(model)
 
 
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- def cosine_similarity(self,vec1, vec2):
25
- vec1 = vec1 / np.linalg.norm(vec1)
26
- vec2 = vec2 / np.linalg.norm(vec2)
27
- return np.dot(vec1, vec2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  def segment_audio(self, path, target_path='/tmp/setup_voice', seg_duration=1.0):
 
 
 
 
 
 
 
 
 
 
 
30
  # 出力先ディレクトリが存在していれば中身をクリアする
31
  if os.path.exists(target_path):
32
  for file in os.listdir(target_path):
@@ -52,91 +183,252 @@ class AudioProcessor():
52
 
53
  return target_path, duration_ms
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
- def calculate_similarity(self,path1, path2):
57
- embedding1 = self.inference(path1)
58
- embedding2 = self.inference(path2)
59
- return float(self.cosine_similarity(embedding1.data.flatten(), embedding2.data.flatten()))
60
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
-
63
  def process_audio(self, reference_path, input_path, output_folder='/tmp/data/matched_segments', seg_duration=1.0, threshold=0.5):
64
- # 出力先ディレクトリの中身をクリアする
65
- if os.path.exists(output_folder):
66
- for file in os.listdir(output_folder):
67
- file_path = os.path.join(output_folder, file)
68
- if os.path.isfile(file_path):
69
- os.remove(file_path)
70
- else:
71
- os.makedirs(output_folder, exist_ok=True)
72
-
73
- segmented_path, total_duration_ms = self.segment_audio(input_path, seg_duration=seg_duration)
74
 
75
- matched_time_ms = 0
76
- for file in sorted(os.listdir(segmented_path)):
77
- segment_file = os.path.join(segmented_path, file)
78
- similarity = self.calculate_similarity(segment_file, reference_path)
79
- if similarity > threshold:
80
- shutil.copy(segment_file, output_folder)
81
- matched_time_ms += len(AudioSegment.from_file(segment_file))
82
 
83
- unmatched_time_ms = total_duration_ms - matched_time_ms
84
- return matched_time_ms, unmatched_time_ms,output_folder
85
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
  def process_multi_audio(self, reference_pathes, input_path, output_folder='/tmp/data/matched_multi_segments', seg_duration=1.0, threshold=0.5):
88
- # 出力先ディレクトリの中身をクリアする
89
- if os.path.exists(output_folder):
90
- for file in os.listdir(output_folder):
91
- file_path = os.path.join(output_folder, file)
92
- if os.path.isfile(file_path):
93
- os.remove(file_path)
94
- else:
95
- os.makedirs(output_folder, exist_ok=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
- # 入力音声をセグメントに分割
98
- segmented_path, total_duration_ms = self.segment_audio(input_path, seg_duration=seg_duration)
99
- segment_files = sorted(os.listdir(segmented_path))
100
- num_segments = len(segment_files)
 
 
 
 
 
 
101
 
102
- # 各リファレンスごとにセグメントとの類似度を計算し、行列 (rows: reference, columns: segment) を作成
103
- similarity = []
104
- for reference_path in reference_pathes:
105
- ref_similarity = []
 
 
 
106
  for file in segment_files:
107
  segment_file = os.path.join(segmented_path, file)
108
- sim = self.calculate_similarity(segment_file, reference_path)
109
- ref_similarity.append(sim)
110
- similarity.append(ref_similarity)
111
-
112
- # 転置行列を作成 (rows: segment, columns: reference)
113
- similarity_transposed = []
114
- for seg_idx in range(num_segments):
115
- seg_sim = []
116
- for ref_idx in range(len(reference_pathes)):
117
- seg_sim.append(similarity[ref_idx][seg_idx])
118
- similarity_transposed.append(seg_sim)
119
-
120
- # 各セグメントについて、最も高い類似度のリファレンスを選択
121
- best_matches = []
122
- for seg_sim in similarity_transposed:
123
- best_ref = np.argmax(seg_sim) # 最も類似度の高いリファレンスのインデックス
124
- # 閾値チェック (必要に応じて)
125
- if seg_sim[best_ref] < threshold:
126
- best_matches.append(None) # 閾値未満の場合はマッチなしとする
127
- else:
128
- best_matches.append(best_ref)
129
 
130
- # 各リファレンスごとに一致時間を集計 (セグメントごとの長さ seg_duration を加算)
131
- matched_time = [0] * len(reference_pathes)
132
- for match in best_matches:
133
- if match is not None:
134
- matched_time[match] += seg_duration
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
- return matched_time, segmented_path
137
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
- def save_audio_from_base64(self,base64_audio,output_dir,output_filename,temp_format='webm'):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  try:
141
  # Base64デコードして音声バイナリを取得
142
  try:
@@ -145,26 +437,26 @@ class AudioProcessor():
145
  raise ValueError("Invalid Base64 input data")
146
 
147
  # 保存するディレクトリを作成
148
- os.makedirs(output_dir,exist_ok=True)
149
 
150
- # 一時ファイルに保存(実際の形式は WebM などと仮定)
151
- temp_audio_path = os.path.join(output_dir,"temp_audio")
152
  try:
153
- with open(temp_audio_path,'wb') as f:
154
  f.write(audio_binary)
155
 
156
  # pydub を使って一時ファイルを WAV に変換
157
- # ※ここでは WebM 形式と仮定していますが、実際の形式に合わせて format の指定を変更してください
158
  try:
159
- audio = AudioSegment.from_file(temp_audio_path,format=temp_format)
160
  except Exception as e:
161
- audio = AudioSegment.from_file(temp_audio_path) #形式が不明な場合は自動判別させる(ただし変換できない場合もあり)
 
162
 
163
  # 音声ファイルを保存
164
- wav_audio_path = os.path.join(output_dir,output_filename)
165
- audio.export(wav_audio_path,format="wav")
166
  finally:
167
- #一時ファイルを削除
168
  if os.path.exists(temp_audio_path):
169
  os.remove(temp_audio_path)
170
  return wav_audio_path
@@ -176,7 +468,13 @@ class AudioProcessor():
176
  print(f"Unexpected Error: {e}")
177
  return None
178
 
179
- def delete_files_in_directory(self,directory_path):
 
 
 
 
 
 
180
  try:
181
  # ディレクトリ内のすべてのファイルを取得
182
  for filename in os.listdir(directory_path):
@@ -186,5 +484,4 @@ class AudioProcessor():
186
  os.remove(file_path)
187
  print(f"{file_path} を削除しました")
188
  except Exception as e:
189
- print(f"エラーが発生しました: {e}")
190
-
 
 
1
  import os
2
  import shutil
3
  import numpy as np
 
8
  from pydub import AudioSegment
9
  import base64
10
  import binascii
11
+ import warnings
12
 
13
  class AudioProcessor():
14
+ def __init__(self, cache_dir="/tmp/hf_cache", standard_duration=5.0):
15
  hf_token = os.environ.get("HF")
16
  if hf_token is None:
17
  raise ValueError("HUGGINGFACE_HUB_TOKEN が設定されていません。")
 
19
  # pyannote モデルの読み込み
20
  model = Model.from_pretrained("pyannote/embedding", use_auth_token=hf_token, cache_dir=cache_dir)
21
  self.inference = Inference(model)
22
+ # 標準の音声長さ(秒)
23
+ self.standard_duration = standard_duration
24
 
25
+ def normalize_audio_duration(self, input_path, target_duration_seconds=None, output_path=None):
26
+ """
27
+ 音声ファイルの長さを指定された時間(秒)にそろえる関数
28
+ 短すぎる場合は無音を追加し、長すぎる場合は切り詰める
29
+
30
+ Parameters:
31
+ input_path (str): 入力音声ファイルのパス
32
+ target_duration_seconds (float, optional): 目標となる音声の長さ(秒)。Noneの場合はself.standard_durationを使用
33
+ output_path (str, optional): 出力先のパス。Noneの場合は一時ファイルを生成
34
+
35
+ Returns:
36
+ str: 処理された音声ファイルのパス
37
+ """
38
+ try:
39
+ # デフォルト値の設定
40
+ if target_duration_seconds is None:
41
+ target_duration_seconds = self.standard_duration
42
+
43
+ # 音声ファイルを読み込む
44
+ audio = AudioSegment.from_file(input_path)
45
+
46
+ # 現在の長さ(ミリ秒)
47
+ current_duration_ms = len(audio)
48
+ target_duration_ms = int(target_duration_seconds * 1000)
49
+
50
+ # 出力パスが指定されていない場合は一時ファイルを生成
51
+ if output_path is None:
52
+ random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))
53
+ timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
54
+ output_dir = os.path.dirname(input_path) if os.path.dirname(input_path) else '/tmp'
55
+ output_path = os.path.join(output_dir, f"normalized_{timestamp}_{random_str}.wav")
56
+
57
+ # 長さの調整
58
+ if current_duration_ms < target_duration_ms:
59
+ # 短い場合は無音を追加
60
+ silence_duration = target_duration_ms - current_duration_ms
61
+ silence = AudioSegment.silent(duration=silence_duration)
62
+ normalized_audio = audio + silence
63
+ else:
64
+ # 長い場合は切り詰め
65
+ normalized_audio = audio[:target_duration_ms]
66
+
67
+ # ファイルに保存
68
+ normalized_audio.export(output_path, format="wav")
69
+
70
+ return output_path
71
+
72
+ except Exception as e:
73
+ print(f"音声の長さをそろえる処理でエラーが発生しました: {e}")
74
+ return None
75
+
76
+ def batch_normalize_audio_duration(self, input_directory, target_duration_seconds=None, output_directory=None):
77
+ """
78
+ ディレクトリ内の全音声ファイルの長さをそろえる関数
79
+
80
+ Parameters:
81
+ input_directory (str): 入力音声ファイルが格納されているディレクトリ
82
+ target_duration_seconds (float, optional): 目標となる音声の長さ(秒)。Noneの場合はself.standard_durationを使用
83
+ output_directory (str, optional): 出力先のディレクトリ。Noneの場合は入力と同じディレクトリに処理結果を保存
84
+
85
+ Returns:
86
+ list: 処理された音声ファイルのパスのリスト
87
+ """
88
+ try:
89
+ # デフォルト値の設定
90
+ if target_duration_seconds is None:
91
+ target_duration_seconds = self.standard_duration
92
+
93
+ # 出力ディレクトリが指定されていない場合は入力ディレクトリを使用
94
+ if output_directory is None:
95
+ output_directory = input_directory
96
+ else:
97
+ os.makedirs(output_directory, exist_ok=True)
98
+
99
+ output_files = []
100
+
101
+ # ディレクトリ内の全ファイルを処理
102
+ for filename in os.listdir(input_directory):
103
+ if filename.lower().endswith(('.wav', '.mp3', '.webm', '.ogg', '.flac')):
104
+ input_path = os.path.join(input_directory, filename)
105
+ output_filename = f"normalized_{filename}"
106
+ output_path = os.path.join(output_directory, output_filename)
107
+
108
+ # 音声の長さをそろえる
109
+ processed_file = self.normalize_audio_duration(
110
+ input_path,
111
+ target_duration_seconds,
112
+ output_path
113
+ )
114
+
115
+ if processed_file:
116
+ output_files.append(processed_file)
117
+
118
+ return output_files
119
+
120
+ except Exception as e:
121
+ print(f"バッチ処理でエラーが発生しました: {e}")
122
+ return []
123
 
124
+ def cosine_similarity(self, vec1, vec2):
125
+ """
126
+ 2つのベクトル間のコサイン類似度を計算する
127
+ 次元数が異なる場合はエラーを発生させる
128
+
129
+ Parameters:
130
+ vec1, vec2: 比較する2つのベクトル
131
+
132
+ Returns:
133
+ float: コサイン類似度 (-1 から 1 の範囲)
134
+ """
135
+ try:
136
+ # 次元数チェック
137
+ if vec1.shape != vec2.shape:
138
+ raise ValueError(f"ベクトルの次元数が一致しません: {vec1.shape} vs {vec2.shape}")
139
+
140
+ # 正規化
141
+ vec1 = vec1 / np.linalg.norm(vec1)
142
+ vec2 = vec2 / np.linalg.norm(vec2)
143
+
144
+ return np.dot(vec1, vec2)
145
+ except Exception as e:
146
+ print(f"コサイン類似度計算でエラーが発生しました: {e}")
147
+ return None
148
 
149
  def segment_audio(self, path, target_path='/tmp/setup_voice', seg_duration=1.0):
150
+ """
151
+ 音声ファイルを一定の長さのセグメントに分割する
152
+
153
+ Parameters:
154
+ path (str): 入力音声ファイルのパス
155
+ target_path (str): 分割されたセグメントを保存するディレクトリ
156
+ seg_duration (float): 各セグメントの長さ(秒)
157
+
158
+ Returns:
159
+ tuple: (セグメントが保存されたディレクトリのパス, 元の音声の総時間(ミリ秒))
160
+ """
161
  # 出力先ディレクトリが存在していれば中身をクリアする
162
  if os.path.exists(target_path):
163
  for file in os.listdir(target_path):
 
183
 
184
  return target_path, duration_ms
185
 
186
+ def calculate_embedding(self, audio_path):
187
+ """
188
+ 音声ファイルからエンベディングを計算する
189
+ 必要に応じて音声の長さを標準化する
190
+
191
+ Parameters:
192
+ audio_path (str): 音声ファイルのパス
193
+
194
+ Returns:
195
+ numpy.ndarray: 計算されたエンベディング
196
+ """
197
+ try:
198
+ # 一時的に長さを標準化した音声ファイルを作成
199
+ normalized_path = self.normalize_audio_duration(audio_path)
200
+ if normalized_path is None:
201
+ raise ValueError("音声の長さの標準化に失敗しました")
202
+
203
+ # エンベディングを計算
204
+ embedding = self.inference(normalized_path)
205
+
206
+ # 一時ファイルを削除(必要に応じて)
207
+ if normalized_path != audio_path:
208
+ try:
209
+ os.remove(normalized_path)
210
+ except Exception as e:
211
+ warnings.warn(f"一時ファイルの削除に失敗しました: {e}")
212
+
213
+ return embedding.data.flatten()
214
+
215
+ except Exception as e:
216
+ print(f"エンベディング計算でエラーが発生しました: {e}")
217
+ return None
218
 
219
+ def calculate_similarity(self, path1, path2):
220
+ """
221
+ 2つの音声ファイル間の類似度を計算する
222
+ 音声の長さを標準化してからエンベディングを計算
223
+
224
+ Parameters:
225
+ path1, path2 (str): 比較する2つの音声ファイルのパス
226
+
227
+ Returns:
228
+ float: コサイン類似度 (-1 から 1 の範囲)、エラー時はNone
229
+ """
230
+ try:
231
+ # エンベディングを計算
232
+ embedding1 = self.calculate_embedding(path1)
233
+ embedding2 = self.calculate_embedding(path2)
234
+
235
+ if embedding1 is None or embedding2 is None:
236
+ raise ValueError("エンベディングの計算に失���しました")
237
+
238
+ # 次元数チェック(念のため)
239
+ if embedding1.shape != embedding2.shape:
240
+ raise ValueError(f"エンベディングの次元数が一致しません: {embedding1.shape} vs {embedding2.shape}")
241
+
242
+ # 類似度を計算
243
+ return float(self.cosine_similarity(embedding1, embedding2))
244
+ except Exception as e:
245
+ print(f"類似度計算でエラーが発生しました: {e}")
246
+ return None
247
 
 
248
  def process_audio(self, reference_path, input_path, output_folder='/tmp/data/matched_segments', seg_duration=1.0, threshold=0.5):
249
+ """
250
+ 入力音声からリファレンス音声に類似したセグメントを抽出する
 
 
 
 
 
 
 
 
251
 
252
+ Parameters:
253
+ reference_path (str): リファレンス音声のパス
254
+ input_path (str): 入力音声のパス
255
+ output_folder (str): 類似セグメントを保存するディレクトリ
256
+ seg_duration (float): セグメントの長さ(秒)
257
+ threshold (float): 類似度の閾値
 
258
 
259
+ Returns:
260
+ tuple: (マッチした時間(ミリ秒), マッチしなかった時間(ミリ秒), 出力フォルダのパス)
261
+ """
262
+ try:
263
+ # リファレンス音声のエンベディングを計算(長さを標準化)
264
+ reference_embedding = self.calculate_embedding(reference_path)
265
+ if reference_embedding is None:
266
+ raise ValueError("リファレンス音声のエンベディング計算に失敗しました")
267
+
268
+ # 出力先ディレクトリの中身をクリアする
269
+ if os.path.exists(output_folder):
270
+ for file in os.listdir(output_folder):
271
+ file_path = os.path.join(output_folder, file)
272
+ if os.path.isfile(file_path):
273
+ os.remove(file_path)
274
+ else:
275
+ os.makedirs(output_folder, exist_ok=True)
276
+
277
+ # 入力音声をセグメントに分割
278
+ segmented_path, total_duration_ms = self.segment_audio(input_path, seg_duration=seg_duration)
279
+
280
+ matched_time_ms = 0
281
+ for file in sorted(os.listdir(segmented_path)):
282
+ segment_file = os.path.join(segmented_path, file)
283
+
284
+ # セグメントのエンベディングを計算
285
+ segment_embedding = self.calculate_embedding(segment_file)
286
+ if segment_embedding is None:
287
+ print(f"警告: セグメント {file} のエンベディング計算に失敗しました。スキップします。")
288
+ continue
289
+
290
+ try:
291
+ # 類似度を計算
292
+ similarity = float(self.cosine_similarity(segment_embedding, reference_embedding))
293
+
294
+ if similarity > threshold:
295
+ shutil.copy(segment_file, output_folder)
296
+ matched_time_ms += len(AudioSegment.from_file(segment_file))
297
+ except Exception as e:
298
+ print(f"セグメント {file} の類似度計算でエラーが発生しました: {e}")
299
+
300
+ unmatched_time_ms = total_duration_ms - matched_time_ms
301
+ return matched_time_ms, unmatched_time_ms, output_folder
302
+
303
+ except Exception as e:
304
+ print(f"音声処理でエラーが発生しました: {e}")
305
+ return 0, 0, output_folder
306
 
307
  def process_multi_audio(self, reference_pathes, input_path, output_folder='/tmp/data/matched_multi_segments', seg_duration=1.0, threshold=0.5):
308
+ """
309
+ 入力音声から複数のリファレンス音声に類似したセグメントを抽出する
310
+
311
+ Parameters:
312
+ reference_pathes (list): リファレンス音声のパスのリスト
313
+ input_path (str): 入力音声のパス
314
+ output_folder (str): 類似セグメントを保存するディレクトリ
315
+ seg_duration (float): セグメントの長さ(秒)
316
+ threshold (float): 類似度の閾値
317
+
318
+ Returns:
319
+ tuple: (各リファレンスごとのマッチした時間のリスト, セグメントが保存されたディレクトリのパス)
320
+ """
321
+ try:
322
+ # 出力先ディレクトリの中身をクリアする
323
+ if os.path.exists(output_folder):
324
+ for file in os.listdir(output_folder):
325
+ file_path = os.path.join(output_folder, file)
326
+ if os.path.isfile(file_path):
327
+ os.remove(file_path)
328
+ else:
329
+ os.makedirs(output_folder, exist_ok=True)
330
 
331
+ # リファレンス音声のエンベディングを事前計算
332
+ reference_embeddings = []
333
+ for ref_path in reference_pathes:
334
+ embedding = self.calculate_embedding(ref_path)
335
+ if embedding is None:
336
+ print(f"警告: リファレンス {ref_path} のエンベディング計算に失敗しました")
337
+ # ダミーエンベディングを挿入(後で処理をスキップ)
338
+ reference_embeddings.append(None)
339
+ else:
340
+ reference_embeddings.append(embedding)
341
 
342
+ # 入力音声をセグメントに分割
343
+ segmented_path, total_duration_ms = self.segment_audio(input_path, seg_duration=seg_duration)
344
+ segment_files = sorted(os.listdir(segmented_path))
345
+ num_segments = len(segment_files)
346
+
347
+ # 各セグメントのエンベディングを計算
348
+ segment_embeddings = []
349
  for file in segment_files:
350
  segment_file = os.path.join(segmented_path, file)
351
+ embedding = self.calculate_embedding(segment_file)
352
+ if embedding is None:
353
+ print(f"警告: セグメント {file} のエンベディング計算に失敗しました")
354
+ segment_embeddings.append(None)
355
+ else:
356
+ segment_embeddings.append(embedding)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
 
358
+ # 各リファレンスごとにセグメントとの類似度を計算
359
+ similarity = []
360
+ for ref_embedding in reference_embeddings:
361
+ if ref_embedding is None:
362
+ # リファレンスのエンベディングが計算できなかった場合
363
+ similarity.append([0.0] * num_segments)
364
+ continue
365
+
366
+ ref_similarity = []
367
+ for seg_embedding in segment_embeddings:
368
+ if seg_embedding is None:
369
+ # セグメントのエンベディングが計算できなかった場合
370
+ ref_similarity.append(0.0)
371
+ continue
372
+
373
+ try:
374
+ # 次元数チェック
375
+ if ref_embedding.shape != seg_embedding.shape:
376
+ print(f"警告: エンベディングの次元数が一致しません: {ref_embedding.shape} vs {seg_embedding.shape}")
377
+ ref_similarity.append(0.0)
378
+ continue
379
+
380
+ # 類似度を計算
381
+ sim = float(self.cosine_similarity(seg_embedding, ref_embedding))
382
+ ref_similarity.append(sim)
383
+ except Exception as e:
384
+ print(f"類似度計算でエラーが発生しました: {e}")
385
+ ref_similarity.append(0.0)
386
+
387
+ similarity.append(ref_similarity)
388
 
389
+ # 転置行列を作成 (rows: segment, columns: reference)
390
+ similarity_transposed = []
391
+ for seg_idx in range(num_segments):
392
+ seg_sim = []
393
+ for ref_idx in range(len(reference_pathes)):
394
+ seg_sim.append(similarity[ref_idx][seg_idx])
395
+ similarity_transposed.append(seg_sim)
396
+
397
+ # 各セグメントについて、最も高い類似度のリファレンスを選択
398
+ best_matches = []
399
+ for seg_sim in similarity_transposed:
400
+ best_ref = np.argmax(seg_sim) # 最も類似度の高いリファレンスのインデックス
401
+ # 閾値チェック
402
+ if seg_sim[best_ref] < threshold:
403
+ best_matches.append(None) # 閾値未満の場合はマッチなしとする
404
+ else:
405
+ best_matches.append(best_ref)
406
+
407
+ # 各リファレンスごとに一致時間を集計
408
+ matched_time = [0] * len(reference_pathes)
409
+ for match in best_matches:
410
+ if match is not None:
411
+ matched_time[match] += seg_duration
412
 
413
+ return matched_time, segmented_path
414
+
415
+ except Exception as e:
416
+ print(f"マルチ音声処理でエラーが発生しました: {e}")
417
+ return [0] * len(reference_pathes), None
418
+
419
+ def save_audio_from_base64(self, base64_audio, output_dir, output_filename, temp_format='webm'):
420
+ """
421
+ Base64エンコードされた音声データをデコードして保存する
422
+
423
+ Parameters:
424
+ base64_audio (str): Base64エンコードされた音声データ
425
+ output_dir (str): 出力先ディレクトリ
426
+ output_filename (str): 出力ファイル名
427
+ temp_format (str): 一時ファイルのフォーマット
428
+
429
+ Returns:
430
+ str: 保存された音声ファイルのパス、エラー時はNone
431
+ """
432
  try:
433
  # Base64デコードして音声バイナリを取得
434
  try:
 
437
  raise ValueError("Invalid Base64 input data")
438
 
439
  # 保存するディレクトリを作成
440
+ os.makedirs(output_dir, exist_ok=True)
441
 
442
+ # 一時ファイルに保存
443
+ temp_audio_path = os.path.join(output_dir, "temp_audio")
444
  try:
445
+ with open(temp_audio_path, 'wb') as f:
446
  f.write(audio_binary)
447
 
448
  # pydub を使って一時ファイルを WAV に変換
 
449
  try:
450
+ audio = AudioSegment.from_file(temp_audio_path, format=temp_format)
451
  except Exception as e:
452
+ # 形式が不明な場合は自動判別
453
+ audio = AudioSegment.from_file(temp_audio_path)
454
 
455
  # 音声ファイルを保存
456
+ wav_audio_path = os.path.join(output_dir, output_filename)
457
+ audio.export(wav_audio_path, format="wav")
458
  finally:
459
+ # 一時ファイルを削除
460
  if os.path.exists(temp_audio_path):
461
  os.remove(temp_audio_path)
462
  return wav_audio_path
 
468
  print(f"Unexpected Error: {e}")
469
  return None
470
 
471
+ def delete_files_in_directory(self, directory_path):
472
+ """
473
+ ディレクトリ内のすべてのファイルを削除する
474
+
475
+ Parameters:
476
+ directory_path (str): 削除対象のディレクトリパス
477
+ """
478
  try:
479
  # ディレクトリ内のすべてのファイルを取得
480
  for filename in os.listdir(directory_path):
 
484
  os.remove(file_path)
485
  print(f"{file_path} を削除しました")
486
  except Exception as e:
487
+ print(f"ファイル削除でエラーが発生しました: {e}")