latterworks commited on
Commit
2ac66e6
·
verified ·
1 Parent(s): 4d9af98

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +51 -24
app.py CHANGED
@@ -32,12 +32,20 @@ class AudioAnalyzer:
32
  """Initialize with a temporary directory for file storage."""
33
  self.temp_dir = Path(temp_dir or tempfile.mkdtemp())
34
  self.temp_dir.mkdir(exist_ok=True)
 
35
  logger.info(f"Initialized temporary directory: {self.temp_dir}")
36
 
37
  def cleanup(self) -> None:
38
- """Remove temporary directory and its contents."""
 
 
 
 
 
 
 
39
  if self.temp_dir.exists():
40
- shutil.rmtree(self.temp_dir)
41
  logger.info(f"Cleaned up temporary directory: {self.temp_dir}")
42
 
43
  def download_youtube_audio(self, video_url: str, progress=gr.Progress()) -> Tuple[Optional[str], str]:
@@ -73,6 +81,25 @@ class AudioAnalyzer:
73
  logger.error(f"Unexpected error during download: {str(e)}")
74
  return None, f"Error: {str(e)}"
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  def extract_basic_features(self, audio_path: str, sr: int = 16000, max_duration: float = 60.0,
77
  progress=gr.Progress()) -> Tuple[Optional[str], Optional[str], Optional[str]]:
78
  """Extract basic audio features and generate visualizations."""
@@ -100,7 +127,7 @@ class AudioAnalyzer:
100
  'zero_crossing_rate': librosa.feature.zero_crossing_rate(y)[0]
101
  }
102
 
103
- progress(0.5, desc="Computing Mel spectrogram...")
104
  hop_length = 512
105
  S_mel = librosa.feature.melspectrogram(y=y, sr=sr, hop_length=hop_length, n_mels=80)
106
  S_dB = librosa.power_to_db(S_mel, ref=np.max)
@@ -129,9 +156,9 @@ class AudioAnalyzer:
129
  axes[1, 1].legend()
130
 
131
  plt.tight_layout()
132
- plot_path = self.temp_dir / f"basic_features_{np.random.randint(10000)}.png"
133
- plt.savefig(plot_path, dpi=300, bbox_inches='tight')
134
- plt.close()
135
 
136
  # Validate feature shapes
137
  for key in ['mfcc', 'spectral_centroid', 'spectral_rolloff', 'zero_crossing_rate']:
@@ -154,7 +181,7 @@ class AudioAnalyzer:
154
  """
155
 
156
  progress(1.0, desc="Analysis complete!")
157
- return str(plot_path), summary, None
158
 
159
  except Exception as e:
160
  logger.error(f"Error processing audio: {str(e)}")
@@ -177,9 +204,9 @@ class AudioAnalyzer:
177
  y_harm = librosa.effects.harmonic(y=y, margin=8)
178
  chroma_harm = librosa.feature.chroma_cqt(y=y_harm, sr=sr)
179
  chroma_filter = np.minimum(chroma_harm,
180
- librosa.decompose.nn_filter(chroma_harm,
181
- aggregate=np.median,
182
- metric='cosine'))
183
  chroma_smooth = scipy.ndimage.median_filter(chroma_filter, size=(1, 9))
184
  chroma_stft = librosa.feature.chroma_stft(y=y, sr=sr)
185
  chroma_cens = librosa.feature.chroma_cens(y=y, sr=sr)
@@ -200,13 +227,13 @@ class AudioAnalyzer:
200
  axes[i].set_title(title)
201
 
202
  plt.tight_layout()
203
- plot_path = self.temp_dir / f"chroma_features_{np.random.randint(10000)}.png"
204
- plt.savefig(plot_path, dpi=300, bbox_inches='tight')
205
- plt.close()
206
 
207
  summary = "Chroma feature analysis complete! Visualizations show different chroma extraction methods for harmonic analysis."
208
  progress(1.0, desc="Chroma analysis complete!")
209
- return str(plot_path), summary, None
210
 
211
  except Exception as e:
212
  logger.error(f"Error processing chroma features: {str(e)}")
@@ -222,7 +249,7 @@ class AudioAnalyzer:
222
  progress(0.1, desc="Loading audio...")
223
  y, sr = librosa.load(audio_path, sr=sr)
224
 
225
- progress(0.3, desc="Computing Mel spectrogram...")
226
  hop_length = 512
227
  S_mel = librosa.feature.melspectrogram(y=y, sr=sr, hop_length=hop_length, n_mels=80)
228
  S_dB = librosa.power_to_db(S_mel, ref=np.max)
@@ -239,16 +266,16 @@ class AudioAnalyzer:
239
 
240
  for i in range(num_patches_to_show):
241
  librosa.display.specshow(patches[..., i], y_axis='mel', x_axis='time',
242
- ax=axes[i], sr=sr, hop_length=hop_length)
243
  axes[i].set_title(f'Patch {i+1}')
244
 
245
  for i in range(num_patches_to_show, len(axes)):
246
  axes[i].set_visible(False)
247
 
248
  plt.tight_layout()
249
- plot_path = self.temp_dir / f"patches_{np.random.randint(10000)}.png"
250
- plt.savefig(plot_path, dpi=300, bbox_inches='tight')
251
- plt.close()
252
 
253
  summary = f"""
254
  **Patch Generation Summary:**
@@ -260,7 +287,7 @@ class AudioAnalyzer:
260
  """
261
 
262
  progress(1.0, desc="Patch generation complete!")
263
- return str(plot_path), summary, None
264
 
265
  except Exception as e:
266
  logger.error(f"Error generating patches: {str(e)}")
@@ -334,11 +361,11 @@ def create_gradio_interface() -> gr.Blocks:
334
 
335
  gr.Markdown("""
336
  ### ℹ️ Usage Tips
337
- - **Processing Limits**: 60s for basic features, 30s for chroma features to ensure fast response times
338
  - **YouTube Downloads**: Ensure URLs are valid and respect YouTube's terms of service
339
- - **Visualizations**: High-quality, suitable for research and educational use
340
- - **Storage**: Temporary files are automatically cleaned up when the interface closes
341
- - **Support**: For issues, check the [GitHub repository](https://github.com/your-repo) or contact the developer
342
  """)
343
 
344
  # Event handlers
 
32
  """Initialize with a temporary directory for file storage."""
33
  self.temp_dir = Path(temp_dir or tempfile.mkdtemp())
34
  self.temp_dir.mkdir(exist_ok=True)
35
+ self.plot_files = [] # Track plot files for cleanup
36
  logger.info(f"Initialized temporary directory: {self.temp_dir}")
37
 
38
  def cleanup(self) -> None:
39
+ """Remove temporary directory and plot files."""
40
+ for plot_file in self.plot_files:
41
+ if Path(plot_file).exists():
42
+ try:
43
+ Path(plot_file).unlink()
44
+ logger.info(f"Removed plot file: {plot_file}")
45
+ except Exception as e:
46
+ logger.warning(f"Failed to remove plot file {plot_file}: {str(e)}")
47
  if self.temp_dir.exists():
48
+ shutil.rmtree(self.temp_dir, ignore_errors=True)
49
  logger.info(f"Cleaned up temporary directory: {self.temp_dir}")
50
 
51
  def download_youtube_audio(self, video_url: str, progress=gr.Progress()) -> Tuple[Optional[str], str]:
 
81
  logger.error(f"Unexpected error during download: {str(e)}")
82
  return None, f"Error: {str(e)}"
83
 
84
+ def save_plot(self, fig, filename: str) -> Optional[str]:
85
+ """Save matplotlib figure to a temporary file and verify existence."""
86
+ try:
87
+ # Use NamedTemporaryFile to ensure persistence
88
+ with tempfile.NamedTemporaryFile(suffix='.png', delete=False, dir=self.temp_dir) as tmp_file:
89
+ plot_path = tmp_file.name
90
+ fig.savefig(plot_path, dpi=300, bbox_inches='tight', format='png')
91
+ plt.close(fig)
92
+ if not Path(plot_path).exists():
93
+ logger.error(f"Plot file not created: {plot_path}")
94
+ return None
95
+ self.plot_files.append(plot_path)
96
+ logger.info(f"Saved plot: {plot_path}")
97
+ return str(plot_path)
98
+ except Exception as e:
99
+ logger.error(f"Error saving plot {filename}: {str(e)}")
100
+ plt.close(fig)
101
+ return None
102
+
103
  def extract_basic_features(self, audio_path: str, sr: int = 16000, max_duration: float = 60.0,
104
  progress=gr.Progress()) -> Tuple[Optional[str], Optional[str], Optional[str]]:
105
  """Extract basic audio features and generate visualizations."""
 
127
  'zero_crossing_rate': librosa.feature.zero_crossing_rate(y)[0]
128
  }
129
 
130
+ progress(0.5, desc="Computing mel spectrogram...")
131
  hop_length = 512
132
  S_mel = librosa.feature.melspectrogram(y=y, sr=sr, hop_length=hop_length, n_mels=80)
133
  S_dB = librosa.power_to_db(S_mel, ref=np.max)
 
156
  axes[1, 1].legend()
157
 
158
  plt.tight_layout()
159
+ plot_path = self.save_plot(fig, "basic_features")
160
+ if not plot_path:
161
+ return None, None, "Failed to save feature visualizations"
162
 
163
  # Validate feature shapes
164
  for key in ['mfcc', 'spectral_centroid', 'spectral_rolloff', 'zero_crossing_rate']:
 
181
  """
182
 
183
  progress(1.0, desc="Analysis complete!")
184
+ return plot_path, summary, None
185
 
186
  except Exception as e:
187
  logger.error(f"Error processing audio: {str(e)}")
 
204
  y_harm = librosa.effects.harmonic(y=y, margin=8)
205
  chroma_harm = librosa.feature.chroma_cqt(y=y_harm, sr=sr)
206
  chroma_filter = np.minimum(chroma_harm,
207
+ librosa.decompose.nn_filter(chroma_harm,
208
+ aggregate=np.median,
209
+ metric='cosine'))
210
  chroma_smooth = scipy.ndimage.median_filter(chroma_filter, size=(1, 9))
211
  chroma_stft = librosa.feature.chroma_stft(y=y, sr=sr)
212
  chroma_cens = librosa.feature.chroma_cens(y=y, sr=sr)
 
227
  axes[i].set_title(title)
228
 
229
  plt.tight_layout()
230
+ plot_path = self.save_plot(fig, "chroma_features")
231
+ if not plot_path:
232
+ return None, None, "Failed to save chroma visualizations"
233
 
234
  summary = "Chroma feature analysis complete! Visualizations show different chroma extraction methods for harmonic analysis."
235
  progress(1.0, desc="Chroma analysis complete!")
236
+ return plot_path, summary, None
237
 
238
  except Exception as e:
239
  logger.error(f"Error processing chroma features: {str(e)}")
 
249
  progress(0.1, desc="Loading audio...")
250
  y, sr = librosa.load(audio_path, sr=sr)
251
 
252
+ progress(0.3, desc="Computing mel spectrogram...")
253
  hop_length = 512
254
  S_mel = librosa.feature.melspectrogram(y=y, sr=sr, hop_length=hop_length, n_mels=80)
255
  S_dB = librosa.power_to_db(S_mel, ref=np.max)
 
266
 
267
  for i in range(num_patches_to_show):
268
  librosa.display.specshow(patches[..., i], y_axis='mel', x_axis='time',
269
+ ax=axes[i], sr=sr, hop_length=hop_length)
270
  axes[i].set_title(f'Patch {i+1}')
271
 
272
  for i in range(num_patches_to_show, len(axes)):
273
  axes[i].set_visible(False)
274
 
275
  plt.tight_layout()
276
+ plot_path = self.save_plot(fig, "patches")
277
+ if not plot_path:
278
+ return None, None, "Failed to save patch visualizations"
279
 
280
  summary = f"""
281
  **Patch Generation Summary:**
 
287
  """
288
 
289
  progress(1.0, desc="Patch generation complete!")
290
+ return plot_path, summary, None
291
 
292
  except Exception as e:
293
  logger.error(f"Error generating patches: {str(e)}")
 
361
 
362
  gr.Markdown("""
363
  ### ℹ️ Usage Tips
364
+ - **Processing Limits**: 60s for basic features, 30s for chroma features for fast response
365
  - **YouTube Downloads**: Ensure URLs are valid and respect YouTube's terms of service
366
+ - **Visualizations**: High-quality, suitable for research and education
367
+ - **Storage**: Temporary files are cleaned up when the interface closes
368
+ - **Support**: For issues, check the [GitHub repository](https://github.com/your-repo)
369
  """)
370
 
371
  # Event handlers