slslslrhfem commited on
Commit
ab69d12
·
1 Parent(s): 8618140

change download mechanism

Browse files
Files changed (1) hide show
  1. app.py +143 -210
app.py CHANGED
@@ -1,38 +1,25 @@
1
  import spaces
2
  import gradio as gr
3
- import torch
4
  import librosa
5
- import numpy as np
6
- import subprocess
7
- import sys
8
  import os
9
  import glob
10
  from pathlib import Path
11
  from huggingface_hub import snapshot_download
12
- import tempfile
13
- import uuid
 
 
14
 
15
- token = os.getenv("HF_TOKEN")
 
 
 
 
 
 
16
 
17
- # Install dependencies
18
- def install_dependencies():
19
- dependencies = [
20
- ("madmom", "git+https://github.com/CPJKU/madmom"),
21
- ("soundfile", "soundfile")
22
- ]
23
-
24
- for name, package in dependencies:
25
- try:
26
- __import__(name.replace('-', '_'))
27
- print(f"{name} already installed")
28
- except ImportError:
29
- print(f"Installing {name}...")
30
- subprocess.check_call([
31
- sys.executable, "-m", "pip", "install",
32
- package, "--no-cache-dir"
33
- ])
34
-
35
- install_dependencies()
36
 
37
  # Add current directory to Python path for ml_models
38
  sys.path.insert(0, '.')
@@ -49,7 +36,6 @@ def download_data_from_hub():
49
  folders_to_check = ["covers80", "ml_models"]
50
  downloaded_folders = {}
51
 
52
- # Check LFS file
53
  lfs_file = base_dir / "1005_e_4"
54
  print(f"Checking LFS file: {lfs_file}")
55
  if lfs_file.exists():
@@ -60,27 +46,13 @@ def download_data_from_hub():
60
  print("LFS file not found")
61
  downloaded_folders["1005_e_4"] = None
62
 
63
- # Check existing folders
64
  print("=== CHECKING EXISTING FOLDERS ===")
65
- for folder in folders_to_check:
66
- folder_path = base_dir / folder
67
- print(f"Checking {folder} at {folder_path}")
68
- if folder_path.exists():
69
- if any(folder_path.iterdir()):
70
- print(f" {folder} exists and has content")
71
- else:
72
- print(f" {folder} exists but is empty")
73
- else:
74
- print(f" {folder} does not exist")
75
-
76
  all_folders_exist = all((base_dir / folder).exists() and any((base_dir / folder).iterdir())
77
  for folder in folders_to_check)
78
  print(f"All folders exist: {all_folders_exist}")
79
 
80
  if not all_folders_exist:
81
  print("=== STARTING DOWNLOAD ===")
82
-
83
- # Download to a temporary directory first
84
  temp_dir = base_dir / "temp_download"
85
  print(f"Creating temp directory: {temp_dir}")
86
  temp_dir.mkdir(exist_ok=True)
@@ -91,44 +63,29 @@ def download_data_from_hub():
91
  repo_type="dataset",
92
  local_dir=str(temp_dir),
93
  local_dir_use_symlinks=False,
94
- token=token,
95
  ignore_patterns=["*.md", "*.txt", ".gitattributes", "README.md"]
96
  )
97
 
98
  print(f"Download completed to: {downloaded_path}")
99
 
100
- # Check what was downloaded
101
  print("=== CHECKING TEMP DOWNLOAD CONTENTS ===")
102
  print(f"Temp directory contents:")
103
  for item in temp_dir.iterdir():
104
  item_type = "DIR" if item.is_dir() else "FILE"
105
  print(f" {item.name} ({item_type})")
106
- if item.is_dir():
107
- file_count = len([f for f in item.rglob("*") if f.is_file()])
108
- print(f" Contains {file_count} files")
109
 
110
- # Move folders from temp to current directory
111
  print("=== MOVING FOLDERS ===")
112
  for folder_name in folders_to_check:
113
  temp_folder_path = temp_dir / folder_name
114
  target_folder_path = base_dir / folder_name
115
 
116
- print(f"Processing {folder_name}:")
117
- print(f" Source: {temp_folder_path}")
118
- print(f" Target: {target_folder_path}")
119
- print(f" Source exists: {temp_folder_path.exists()}")
120
-
121
  if temp_folder_path.exists():
122
- # Remove existing target if it exists
123
  if target_folder_path.exists():
124
- print(f" Removing existing target directory")
125
  shutil.rmtree(target_folder_path)
126
 
127
- # Move folder
128
- print(f" Moving folder...")
129
  shutil.move(str(temp_folder_path), str(target_folder_path))
130
 
131
- # Verify move
132
  if target_folder_path.exists():
133
  file_count = len([f for f in target_folder_path.rglob("*") if f.is_file()])
134
  print(f" SUCCESS: {folder_name} moved with {file_count:,} files")
@@ -140,7 +97,6 @@ def download_data_from_hub():
140
  print(f" ERROR: {folder_name} not found in temp download")
141
  downloaded_folders[folder_name] = None
142
 
143
- # Clean up temp directory
144
  print("=== CLEANING UP TEMP DIRECTORY ===")
145
  if temp_dir.exists():
146
  shutil.rmtree(temp_dir)
@@ -164,39 +120,21 @@ def download_data_from_hub():
164
  print("=== DOWNLOAD FUNCTION END ===")
165
  return downloaded_folders
166
 
167
- # Download data and check results
168
  print("Starting Music Plagiarism Detection App...")
169
  folders = download_data_from_hub()
170
 
171
- # Final verification
172
- print("=== FINAL VERIFICATION ===")
173
- current_dir = Path(".")
174
- print(f"Current directory contents after download:")
175
- for item in current_dir.iterdir():
176
- item_type = "DIR" if item.is_dir() else "FILE"
177
- print(f" {item.name} ({item_type})")
178
-
179
- # Check ml_models specifically
180
- ml_models_path = Path("ml_models")
181
- print(f"ml_models check:")
182
- print(f" Exists: {ml_models_path.exists()}")
183
- if ml_models_path.exists():
184
- print(f" Is directory: {ml_models_path.is_dir()}")
185
- print(f" Contents:")
186
- for item in ml_models_path.iterdir():
187
- print(f" {item.name}")
188
-
189
- # Import inference
190
  print("=== IMPORTING INFERENCE ===")
191
  from inference import inference
192
 
 
 
 
 
193
  def find_song_file_by_title(song_title):
194
  covers80_path = Path("covers80")
195
-
196
  if not covers80_path.exists():
197
  return None
198
 
199
- # Try exact match patterns
200
  exact_patterns = [
201
  f"{song_title}.mp3",
202
  f"*{song_title}.mp3",
@@ -208,7 +146,6 @@ def find_song_file_by_title(song_title):
208
  if matches:
209
  return str(matches[0])
210
 
211
- # Try partial matches
212
  song_parts = song_title.replace('_', ' ').split()
213
  for part in song_parts:
214
  if len(part) > 3:
@@ -218,29 +155,54 @@ def find_song_file_by_title(song_title):
218
 
219
  return None
220
 
221
- def format_time(seconds):
222
- """Convert seconds to MM:SS format"""
223
- if seconds is None or seconds < 0:
224
- return "0:00"
225
-
226
- minutes = int(seconds // 60)
227
- seconds = int(seconds % 60)
228
- return f"{minutes}:{seconds:02d}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
  @spaces.GPU(duration=300)
231
- def process_audio_for_matching(audio_file):
232
  if audio_file is None:
233
- return """
 
234
  <div style='text-align: center; color: #dc2626; padding: 20px; background: #fef2f2; border-radius: 8px;'>
235
  <h3>No Audio File</h3>
236
  <p>Please upload an audio file to get started!</p>
237
  </div>
238
  """
239
 
 
 
240
  result = inference(audio_file)
241
 
242
  if result.get('message') != 'success':
243
- return f"""
244
  <div style="text-align: center; padding: 20px; background: #fefce8; border-radius: 8px;">
245
  <h3 style="color: #a16207;">No Matches Found</h3>
246
  <p style="color: #a16207;">{result.get('message', 'Unknown error occurred')}</p>
@@ -249,135 +211,97 @@ def process_audio_for_matching(audio_file):
249
 
250
  matches = result.get('matches', [])
251
  if not matches:
252
- return """
253
  <div style="text-align: center; padding: 20px; background: #fefce8; border-radius: 8px;">
254
  <h3 style="color: #a16207;">No Matches Found</h3>
255
  <p style="color: #a16207;">No matching vocals found in the dataset.</p>
256
  </div>
257
  """
258
 
259
- # Create custom HTML audio players with timestamp functionality
260
- audio_players_html = ""
261
-
262
- # Input audio player - use Gradio's file serving
263
- audio_players_html += f"""
264
- <div style="margin-bottom: 20px;">
265
- <h4 style="color: #111827;">Your Uploaded Audio</h4>
266
- <audio id="inputAudio" controls style="width: 100%;">
267
- <source src="data:audio/wav;base64,{audio_file}" type="audio/wav">
268
- Your browser does not support the audio element.
269
- </audio>
270
- </div>
271
- """
272
-
273
- # Match audio players - 일단 오디오 플레이어는 제거하고 정보만
274
- match_files_info = []
275
- for i, match in enumerate(matches[:3]):
276
- song_title = match.get('song_title', 'Unknown Song')
277
- song_file_path = find_song_file_by_title(song_title)
278
-
279
- if song_file_path and os.path.exists(song_file_path):
280
- match_files_info.append({
281
- 'title': song_title,
282
- 'path': song_file_path,
283
- 'index': i+1
284
- })
285
- else:
286
- match_files_info.append({
287
- 'title': song_title,
288
- 'path': None,
289
- 'index': i+1
290
- })
291
-
292
- # Generate match results with clickable timestamps
293
- matches_html = ""
294
- for i, match in enumerate(matches[:3]):
295
- rank = match.get('rank', 0)
296
  song_title = match.get('song_title', 'Unknown Song')
297
- confidence = match.get('confidence', '0%')
298
- test_time = match.get('test_time', 0)
299
- library_time = match.get('library_time', 0)
 
300
 
301
- # Ranking colors
302
- rank_colors = {1: '#dc2626', 2: '#ea580c', 3: '#16a34a'}
303
- rank_color = rank_colors.get(rank, '#6b7280')
304
 
305
- matches_html += f"""
306
- <div style="background: #ffffff; border-radius: 8px; padding: 15px; margin: 10px 0;
307
- border-left: 4px solid {rank_color}; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
308
- <div style="display: flex; justify-content: space-between; align-items: center;">
309
- <div style="flex: 1;">
310
- <h4 style="color: #111827; margin: 0; font-size: 1.1em;">
311
- <span style="background: {rank_color}; color: white; padding: 2px 6px; border-radius: 10px; font-size: 0.8em; margin-right: 8px;">
312
- #{rank}
313
- </span>
314
- {song_title}
315
- </h4>
316
- </div>
317
- <div style="display: flex; gap: 15px; align-items: center;">
318
- <div style="text-align: center;">
319
- <small style="color: #6b7280;">Your Audio</small>
320
- <div style="color: #dc2626; font-weight: 600;">
321
- {format_time(test_time)}
322
- </div>
323
- <small style="color: #9ca3af;">@{test_time:.1f}s</small>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  </div>
325
- <div style="text-align: center;">
326
- <small style="color: #6b7280;">Matched At</small>
327
- <div style="color: #16a34a; font-weight: 600;">
328
- {format_time(library_time)}
 
 
329
  </div>
330
- <small style="color: #9ca3af;">@{library_time:.1f}s</small>
331
- </div>
332
- <div style="background: #f3f4f6; color: #111827; padding: 4px 10px; border-radius: 12px; font-weight: 600; font-size: 0.9em;">
333
- {confidence}
334
  </div>
335
  </div>
336
  </div>
337
- </div>
338
- """
339
-
340
- # Complete HTML with audio players and results
341
- complete_html = f"""
342
  <div style="background: #ffffff; border-radius: 12px; padding: 20px;
343
  box-shadow: 0 4px 15px rgba(0,0,0,0.08); border: 1px solid #e5e7eb;">
344
-
345
- <div style="text-align: center; margin-bottom: 30px;">
346
  <h3 style="color: #111827; margin: 0;">Vocal Matching Results</h3>
347
  <p style="color: #6b7280; margin: 5px 0;">Found {len(matches)} similar vocals in Covers80 dataset</p>
348
- <p style="color: #2563eb; margin: 5px 0; font-size: 0.9em;">🎵 Timestamps show exact matching points in both audio files</p>
349
- </div>
350
-
351
- <!-- Audio Players -->
352
- <div style="background: #f8fafc; padding: 20px; border-radius: 8px; margin-bottom: 20px;">
353
- <h3 style="color: #111827; margin-bottom: 15px;">Audio Information</h3>
354
- <p style="color: #6b7280; font-size: 0.9em; margin-bottom: 15px;">
355
- Due to Hugging Face file access limitations, audio players are not available in this demo.
356
- However, you can see the exact timestamps where matches were found.
357
- </p>
358
- {audio_players_html}
359
- </div>
360
-
361
- <!-- Match Results -->
362
- <div>
363
- <h3 style="color: #111827; margin-bottom: 15px;">Detailed Results</h3>
364
- {matches_html}
365
  </div>
366
-
367
- <script>
368
- // JavaScript removed due to compatibility issues
369
- console.log('Music Plagiarism Detection - Timestamp display only version');
370
- </script>
371
-
372
- <style>
373
- /* Removed interactive styles */
374
- </style>
375
  </div>
376
  """
377
 
378
- return complete_html
 
379
 
380
- # CSS styles
381
  custom_css = """
382
  .gradio-container {
383
  background: #f9fafb !important;
@@ -395,7 +319,6 @@ custom_css = """
395
  }
396
  """
397
 
398
- # Gradio interface
399
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft(), title="Music Plagiarism Detection") as demo:
400
 
401
  gr.Markdown("""
@@ -406,30 +329,40 @@ with gr.Blocks(css=custom_css, theme=gr.themes.Soft(), title="Music Plagiarism D
406
  Authors: Seonghyeon Go, Yumin Kim | MIPPIA Inc. | Submitted to ICASSP 2026
407
  </p>
408
  <hr style="border: none; border-top: 1px solid #e5e7eb; margin: 15px 0;">
409
- <p><strong>Demo Version Notice:</strong> This demo differs from the paper version and focuses exclusively on vocal segment transcription.</p>
410
- <p style="font-size: 0.9em; color: #6b7280; margin: 8px 0;">
411
- Structure analysis has been excluded for optimization. Results are derived from all downbeats,
412
- which may cause some timestamps to appear less precise than expected.
413
- </p>
414
  <p style="color: #dc2626; font-weight: 600;">Processing can take up to 2 minutes per file</p>
415
  </div>
416
  """, elem_classes=["main-container"])
417
 
418
- # Input section
419
  with gr.Row():
420
- audio_input = gr.Audio(type="filepath", label="Upload Your Audio File")
421
 
422
  with gr.Row():
423
  submit_btn = gr.Button("Analyze Audio", variant="primary", size="lg")
424
 
425
- # Output section
426
  with gr.Row():
427
- results = gr.HTML(label="Analysis Results")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
 
429
  submit_btn.click(
430
- fn=process_audio_for_matching,
431
  inputs=[audio_input],
432
- outputs=[results]
433
  )
434
 
435
  if __name__ == "__main__":
 
1
  import spaces
2
  import gradio as gr
 
3
  import librosa
4
+ import soundfile as sf
 
 
5
  import os
6
  import glob
7
  from pathlib import Path
8
  from huggingface_hub import snapshot_download
9
+ import shutil
10
+ import sys
11
+ import subprocess
12
+ import numpy as np
13
 
14
+ # Install madmom from GitHub
15
+ def install_madmom():
16
+ subprocess.check_call([
17
+ sys.executable, "-m", "pip", "install",
18
+ "git+https://github.com/CPJKU/madmom", "--no-cache-dir"
19
+ ])
20
+ print("madmom installed from GitHub")
21
 
22
+ install_madmom()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  # Add current directory to Python path for ml_models
25
  sys.path.insert(0, '.')
 
36
  folders_to_check = ["covers80", "ml_models"]
37
  downloaded_folders = {}
38
 
 
39
  lfs_file = base_dir / "1005_e_4"
40
  print(f"Checking LFS file: {lfs_file}")
41
  if lfs_file.exists():
 
46
  print("LFS file not found")
47
  downloaded_folders["1005_e_4"] = None
48
 
 
49
  print("=== CHECKING EXISTING FOLDERS ===")
 
 
 
 
 
 
 
 
 
 
 
50
  all_folders_exist = all((base_dir / folder).exists() and any((base_dir / folder).iterdir())
51
  for folder in folders_to_check)
52
  print(f"All folders exist: {all_folders_exist}")
53
 
54
  if not all_folders_exist:
55
  print("=== STARTING DOWNLOAD ===")
 
 
56
  temp_dir = base_dir / "temp_download"
57
  print(f"Creating temp directory: {temp_dir}")
58
  temp_dir.mkdir(exist_ok=True)
 
63
  repo_type="dataset",
64
  local_dir=str(temp_dir),
65
  local_dir_use_symlinks=False,
66
+ token=os.getenv("HF_TOKEN"),
67
  ignore_patterns=["*.md", "*.txt", ".gitattributes", "README.md"]
68
  )
69
 
70
  print(f"Download completed to: {downloaded_path}")
71
 
 
72
  print("=== CHECKING TEMP DOWNLOAD CONTENTS ===")
73
  print(f"Temp directory contents:")
74
  for item in temp_dir.iterdir():
75
  item_type = "DIR" if item.is_dir() else "FILE"
76
  print(f" {item.name} ({item_type})")
 
 
 
77
 
 
78
  print("=== MOVING FOLDERS ===")
79
  for folder_name in folders_to_check:
80
  temp_folder_path = temp_dir / folder_name
81
  target_folder_path = base_dir / folder_name
82
 
 
 
 
 
 
83
  if temp_folder_path.exists():
 
84
  if target_folder_path.exists():
 
85
  shutil.rmtree(target_folder_path)
86
 
 
 
87
  shutil.move(str(temp_folder_path), str(target_folder_path))
88
 
 
89
  if target_folder_path.exists():
90
  file_count = len([f for f in target_folder_path.rglob("*") if f.is_file()])
91
  print(f" SUCCESS: {folder_name} moved with {file_count:,} files")
 
97
  print(f" ERROR: {folder_name} not found in temp download")
98
  downloaded_folders[folder_name] = None
99
 
 
100
  print("=== CLEANING UP TEMP DIRECTORY ===")
101
  if temp_dir.exists():
102
  shutil.rmtree(temp_dir)
 
120
  print("=== DOWNLOAD FUNCTION END ===")
121
  return downloaded_folders
122
 
 
123
  print("Starting Music Plagiarism Detection App...")
124
  folders = download_data_from_hub()
125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  print("=== IMPORTING INFERENCE ===")
127
  from inference import inference
128
 
129
+ # 오디오 세그먼트를 저장할 임시 디렉토리
130
+ TEMP_AUDIO_DIR = Path("./temp_audio_segments")
131
+ TEMP_AUDIO_DIR.mkdir(exist_ok=True)
132
+
133
  def find_song_file_by_title(song_title):
134
  covers80_path = Path("covers80")
 
135
  if not covers80_path.exists():
136
  return None
137
 
 
138
  exact_patterns = [
139
  f"{song_title}.mp3",
140
  f"*{song_title}.mp3",
 
146
  if matches:
147
  return str(matches[0])
148
 
 
149
  song_parts = song_title.replace('_', ' ').split()
150
  for part in song_parts:
151
  if len(part) > 3:
 
155
 
156
  return None
157
 
158
+ def crop_audio_segment_librosa(input_path, start_time, end_time, output_path):
159
+ """
160
+ librosa와 soundfile을 사용하여 오디오 파일의 특정 구간을 자르는 함수.
161
+ """
162
+ try:
163
+ # 오디오 파일 로드
164
+ y, sr = librosa.load(input_path, sr=None)
165
+
166
+ # 시작 및 종료 시간(초)을 샘플 인덱스로 변환
167
+ start_sample = int(start_time * sr)
168
+ end_sample = int(end_time * sr)
169
+
170
+ # numpy 배열 슬라이싱으로 오디오 세그먼트 추출
171
+ cropped_audio = y[start_sample:end_sample]
172
+
173
+ # 잘린 오디오를 WAV 파일로 저장
174
+ sf.write(output_path, cropped_audio, sr)
175
+
176
+ print(f"Successfully cropped audio from {input_path} to {output_path} ({start_time}-{end_time}s) using librosa.")
177
+ return output_path
178
+ except Exception as e:
179
+ print(f"Error cropping audio with librosa: {e}")
180
+ return None
181
+
182
+ def clear_temp_segments():
183
+ """임시 오디오 세그먼트 디렉토리 정리"""
184
+ if TEMP_AUDIO_DIR.exists():
185
+ shutil.rmtree(TEMP_AUDIO_DIR)
186
+ TEMP_AUDIO_DIR.mkdir(exist_ok=True)
187
+ print("Temporary audio segments cleared.")
188
 
189
  @spaces.GPU(duration=300)
190
+ def process_audio_for_matching_and_crop(audio_file):
191
  if audio_file is None:
192
+ # 10개의 None과 에러 메시지 반환
193
+ return [None] * 10, """
194
  <div style='text-align: center; color: #dc2626; padding: 20px; background: #fef2f2; border-radius: 8px;'>
195
  <h3>No Audio File</h3>
196
  <p>Please upload an audio file to get started!</p>
197
  </div>
198
  """
199
 
200
+ clear_temp_segments()
201
+
202
  result = inference(audio_file)
203
 
204
  if result.get('message') != 'success':
205
+ return [None] * 10, f"""
206
  <div style="text-align: center; padding: 20px; background: #fefce8; border-radius: 8px;">
207
  <h3 style="color: #a16207;">No Matches Found</h3>
208
  <p style="color: #a16207;">{result.get('message', 'Unknown error occurred')}</p>
 
211
 
212
  matches = result.get('matches', [])
213
  if not matches:
214
+ return [None] * 10, """
215
  <div style="text-align: center; padding: 20px; background: #fefce8; border-radius: 8px;">
216
  <h3 style="color: #a16207;">No Matches Found</h3>
217
  <p style="color: #a16207;">No matching vocals found in the dataset.</p>
218
  </div>
219
  """
220
 
221
+ # 10개의 오디오 컴포넌트를 위한 배열 초기화
222
+ audio_outputs = [None] * 10
223
+ match_html = ""
224
+
225
+ # 원본 오디오를 위한 슬롯 할당 (첫 번째는 업로드된 파일)
226
+ audio_outputs[0] = audio_file
227
+
228
+ # 상위 3개 매치 세그먼트 및 상위 4개 원본 오디오 처리
229
+ for i, match in enumerate(matches[:4]): # 상위 4개 매치만 처리
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  song_title = match.get('song_title', 'Unknown Song')
231
+ library_file_path = find_song_file_by_title(song_title)
232
+
233
+ if not library_file_path:
234
+ continue
235
 
236
+ # 원본 라이브러리 오디오 할당 (1-3번 슬롯)
237
+ if i < 3:
238
+ audio_outputs[i + 1] = library_file_path
239
 
240
+ # 상위 3개 매치에 대해서만 세그먼트 생성 (입력 오디오 세그먼트 및 라이브러리 오디오 세그먼트)
241
+ if i < 3:
242
+ # 입력 오디오 세그먼트 생성
243
+ input_start = match.get('test_time', 0)
244
+ input_end = match.get('test_time2', input_start + 10)
245
+ input_segment_path = TEMP_AUDIO_DIR / f"input_seg_{i}.wav"
246
+ cropped_input_path = crop_audio_segment_librosa(audio_file, input_start, input_end, input_segment_path)
247
+
248
+ # 라이브러리 오디오 세그먼트 생성
249
+ library_start = match.get('library_time', 0)
250
+ library_end = match.get('library_time2', library_start + 10)
251
+ library_segment_path = TEMP_AUDIO_DIR / f"library_seg_{i}.wav"
252
+ cropped_library_path = crop_audio_segment_librosa(library_file_path, library_start, library_end, library_segment_path)
253
+
254
+ # 세그먼트 파일 할당 (4-9번 슬롯)
255
+ if cropped_input_path and (i*2 + 4) < 10:
256
+ audio_outputs[i*2 + 4] = cropped_input_path
257
+ if cropped_library_path and (i*2 + 5) < 10:
258
+ audio_outputs[i*2 + 5] = cropped_library_path
259
+
260
+ # HTML 결과 포맷팅 (상위 3개 매치에 대해서만)
261
+ if i < 3:
262
+ rank = match.get('rank', 0)
263
+ confidence = match.get('confidence', '0%')
264
+ rank_colors = {1: '#dc2626', 2: '#ea580c', 3: '#16a34a'}
265
+ rank_color = rank_colors.get(rank, '#6b7280')
266
+
267
+ match_html += f"""
268
+ <div style="background: #ffffff; border-radius: 8px; padding: 15px; margin: 10px 0;
269
+ border-left: 4px solid {rank_color}; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
270
+ <div style="display: flex; justify-content: space-between; align-items: center;">
271
+ <div style="flex: 1;">
272
+ <h4 style="color: #111827; margin: 0; font-size: 1.1em;">
273
+ <span style="background: {rank_color}; color: white; padding: 2px 6px; border-radius: 10px; font-size: 0.8em; margin-right: 8px;">
274
+ #{rank}
275
+ </span>
276
+ {song_title}
277
+ </h4>
278
  </div>
279
+ <div style="display: flex; gap: 15px; align-items: center;">
280
+ <div style="text-align: center;">
281
+ <small style="color: #6b7280;">Confidence</small>
282
+ <div style="background: #f3f4f6; color: #111827; padding: 4px 10px; border-radius: 12px; font-weight: 600; font-size: 0.9em;">
283
+ {confidence}
284
+ </div>
285
  </div>
 
 
 
 
286
  </div>
287
  </div>
288
  </div>
289
+ """
290
+
291
+ results_html = f"""
 
 
292
  <div style="background: #ffffff; border-radius: 12px; padding: 20px;
293
  box-shadow: 0 4px 15px rgba(0,0,0,0.08); border: 1px solid #e5e7eb;">
294
+ <div style="text-align: center; margin-bottom: 20px;">
 
295
  <h3 style="color: #111827; margin: 0;">Vocal Matching Results</h3>
296
  <p style="color: #6b7280; margin: 5px 0;">Found {len(matches)} similar vocals in Covers80 dataset</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  </div>
298
+ {match_html}
 
 
 
 
 
 
 
 
299
  </div>
300
  """
301
 
302
+ # 총 10개의 오디오 컴포넌트와 HTML 결과를 반환
303
+ return audio_outputs + [results_html]
304
 
 
305
  custom_css = """
306
  .gradio-container {
307
  background: #f9fafb !important;
 
319
  }
320
  """
321
 
 
322
  with gr.Blocks(css=custom_css, theme=gr.themes.Soft(), title="Music Plagiarism Detection") as demo:
323
 
324
  gr.Markdown("""
 
329
  Authors: Seonghyeon Go, Yumin Kim | MIPPIA Inc. | Submitted to ICASSP 2026
330
  </p>
331
  <hr style="border: none; border-top: 1px solid #e5e7eb; margin: 15px 0;">
332
+ <p><strong>Demo Version Notice:</strong> This demo provides cropped audio segments of matched parts, rather than clickable timestamps.</p>
 
 
 
 
333
  <p style="color: #dc2626; font-weight: 600;">Processing can take up to 2 minutes per file</p>
334
  </div>
335
  """, elem_classes=["main-container"])
336
 
 
337
  with gr.Row():
338
+ audio_input = gr.Audio(type="filepath", label="Upload Your Audio File", elem_id="audio_input")
339
 
340
  with gr.Row():
341
  submit_btn = gr.Button("Analyze Audio", variant="primary", size="lg")
342
 
343
+ audio_outputs = []
344
  with gr.Row():
345
+ # 원본 오디오들을 위한 컴포넌트
346
+ with gr.Column(scale=1):
347
+ audio_outputs.append(gr.Audio(label=f"Your Original Audio", show_label=True))
348
+ for i in range(3):
349
+ audio_outputs.append(gr.Audio(label=f"Library Original Audio (Rank #{i+1})", show_label=True))
350
+
351
+ # 매칭된 세그먼트들을 위한 컴포넌트
352
+ with gr.Column(scale=1):
353
+ for i in range(3):
354
+ audio_outputs.append(gr.Audio(label=f"Your Audio Segment (Rank #{i+1})", show_label=True))
355
+ for i in range(3):
356
+ audio_outputs.append(gr.Audio(label=f"Library Audio Segment (Rank #{i+1})", show_label=True))
357
+
358
+ results = gr.HTML(label="Analysis Results")
359
+
360
+ all_outputs = audio_outputs + [results]
361
 
362
  submit_btn.click(
363
+ fn=process_audio_for_matching_and_crop,
364
  inputs=[audio_input],
365
+ outputs=all_outputs
366
  )
367
 
368
  if __name__ == "__main__":