SaltProphet commited on
Commit
0bcb76d
·
verified ·
1 Parent(s): 9083d98

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -56
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # 1. Import libraries
2
  import gradio as gr
3
  import os
4
  import shutil
@@ -36,9 +36,11 @@ def update_output_visibility(choice):
36
  async def separate_stems(audio_file_path, stem_choice, progress=gr.Progress(track_tqdm=True)):
37
  # Separates the uploaded audio using Demucs
38
  if audio_file_path is None: raise gr.Error("No audio file uploaded!")
39
- progress(0, desc="Starting separation...")
40
  log_history = "Starting separation...\n"
41
- yield { status_log: log_history, progress_bar: progress(0, desc="Starting...") }
 
 
42
  try:
43
  progress(0.05, desc="Preparing audio file...")
44
  log_history += "Preparing audio file...\n"; yield { status_log: log_history, progress_bar: progress(0.05, desc="Preparing...") }
@@ -70,7 +72,7 @@ async def separate_stems(audio_file_path, stem_choice, progress=gr.Progress(trac
70
  # Find the model-specific subfolder created by Demucs
71
  subfolders = [f.name for f in os.scandir(output_dir) if f.is_dir()]
72
  if not subfolders: raise gr.Error("Demucs output folder structure not found!")
73
- model_folder_name = subfolders[0] # Assume the first subfolder is the correct one
74
  stems_path = os.path.join(output_dir, model_folder_name, stable_filename_base)
75
 
76
  if not os.path.exists(stems_path):
@@ -87,7 +89,7 @@ async def separate_stems(audio_file_path, stem_choice, progress=gr.Progress(trac
87
  log_history += "✅ Stem separation complete!\n";
88
  yield {
89
  status_log: log_history,
90
- progress_bar: progress(1, desc="Complete!"),
91
  vocals_output: gr.update(value=vocals_path),
92
  drums_output: gr.update(value=drums_path),
93
  bass_output: gr.update(value=bass_path),
@@ -95,7 +97,7 @@ async def separate_stems(audio_file_path, stem_choice, progress=gr.Progress(trac
95
  }
96
  except Exception as e:
97
  print(f"An error occurred during separation: {e}")
98
- yield { status_log: log_history + f"❌ ERROR: {e}" }
99
 
100
  def slice_stem_real(stem_audio_data, loop_choice, sensitivity, stem_name, progress_fn=None):
101
  # Slices a single stem into loops or one-shots
@@ -114,7 +116,7 @@ def slice_stem_real(stem_audio_data, loop_choice, sensitivity, stem_name, progre
114
  print(f"Detected BPM for {stem_name}: {bpm_int}")
115
 
116
  output_files = []
117
- loops_dir = tempfile.mkdtemp() # Use a unique temp dir for each run
118
 
119
  if "One-Shots" in loop_choice:
120
  if progress_fn: progress_fn(0.3, desc="Finding transients...")
@@ -162,8 +164,8 @@ def slice_stem_real(stem_audio_data, loop_choice, sensitivity, stem_name, progre
162
  async def slice_all_and_zip_real(vocals, drums, bass, other, loop_choice, sensitivity, progress=gr.Progress(track_tqdm=True)):
163
  # Slices all available stems and creates a zip pack
164
  log_history = "Starting batch slice...\n"
165
- yield { status_log: log_history, progress_bar: progress(0, desc="Starting...") }
166
- await asyncio.sleep(0.1) # Give Gradio time to update UI
167
 
168
  stems_to_process = {"vocals": vocals, "drums": drums, "bass": bass, "other": other}
169
  zip_path = "Loop_Architect_Pack.zip"
@@ -179,7 +181,6 @@ async def slice_all_and_zip_real(vocals, drums, bass, other, loop_choice, sensit
179
  if data is not None:
180
  log_history += f"--- Slicing {name} stem ---\n"; yield { status_log: log_history }
181
 
182
- # Define a function to update the main progress bar from within slice_stem_real
183
  def update_main_progress(p, desc=""):
184
  overall_progress = (processed_count + p) / num_stems
185
  progress(overall_progress, desc=f"Slicing {name}: {desc}")
@@ -188,20 +189,18 @@ async def slice_all_and_zip_real(vocals, drums, bass, other, loop_choice, sensit
188
 
189
  if sliced_files:
190
  log_history += f"Generated {len(sliced_files)} slices for {name}.\n"; yield { status_log: log_history }
191
- all_temp_dirs.append(temp_dir) # Keep track of temp dirs for cleanup
192
  for loop_file in sliced_files:
193
- arcname = os.path.join(name, os.path.basename(loop_file)) # Add to subfolder in zip
194
  zf.write(loop_file, arcname)
195
  else:
196
  log_history += f"No slices generated for {name}.\n"; yield { status_log: log_history }
197
 
198
  processed_count += 1
199
- # Yield intermediate progress after each stem is fully processed
200
  yield { status_log: log_history, progress_bar: progress(processed_count / num_stems, desc=f"Finished {name}") }
201
 
202
- log_history += "Packaging complete!\n"; yield { status_log: log_history, progress_bar: progress(1, desc="Pack Ready!") }
203
 
204
- # Clean up all temporary directories used for slicing
205
  for d in all_temp_dirs:
206
  if d and os.path.exists(d):
207
  shutil.rmtree(d)
@@ -219,7 +218,6 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="red"))
219
  stem_options = gr.Radio(["4 Stems (Vocals, Drums, Bass, Other)", "2 Stems (Vocals + Instrumental)"], label="Separation Type", value="4 Stems (Vocals, Drums, Bass, Other)")
220
  submit_button = gr.Button("Separate Stems")
221
 
222
- # --- SLICING OPTIONS ARE BACK ---
223
  with gr.Accordion("Slicing Options", open=True):
224
  loop_options_radio = gr.Radio(["One-Shots (All Transients)", "4 Bar Loops", "8 Bar Loops"], label="Slice Type", value="One-Shots (All Transients)")
225
  sensitivity_slider = gr.Slider(minimum=0.01, maximum=0.5, value=0.05, step=0.01, label="One-Shot Sensitivity", info="Lower values = more slices")
@@ -229,14 +227,15 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="red"))
229
  download_zip_file = gr.File(label="Download Your Loop Pack", visible=False)
230
 
231
  gr.Markdown("### Status")
232
- progress_bar = gr.Progress()
 
233
  status_log = gr.Textbox(label="Status Log", lines=10, interactive=False)
234
 
235
  with gr.Column(scale=2):
236
  with gr.Accordion("Separated Stems", open=True):
237
  with gr.Row():
238
  vocals_output = gr.Audio(label="Vocals", scale=4)
239
- slice_vocals_btn = gr.Button("Slice Vocals", scale=1) # Renamed for clarity
240
  with gr.Row():
241
  drums_output = gr.Audio(label="Drums", scale=4)
242
  slice_drums_btn = gr.Button("Slice Drums", scale=1)
@@ -249,48 +248,53 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="red"))
249
  gr.Markdown("### Sliced Loops / Samples (Preview)")
250
  loop_gallery = gr.Gallery(label="Generated Loops Preview", columns=8, object_fit="contain", height="auto", preview=True)
251
 
252
- # --- Define Event Listeners ---
253
- def slice_and_display(stem_data, loop_choice, sensitivity):
254
- # This wrapper remains the same
255
- log_history = f"Slicing...\n"
256
- yield { status_log: log_history, progress_bar: gr.update(visible=True) } # Show progress bar
257
-
258
- # Define how slice_stem_real updates the progress bar
259
- def update_single_progress(p, desc=""):
260
- progress_bar.update(value=p, label=desc, visible=True)
261
-
262
- files, temp_dir = slice_stem_real(stem_data, loop_choice, sensitivity, "stem", progress_fn=update_single_progress) # Pass stem_name placeholder
263
-
264
- if temp_dir and os.path.exists(temp_dir):
265
- shutil.rmtree(temp_dir)
266
-
267
- yield {
268
- loop_gallery: gr.update(value=files),
269
- status_log: log_history + f"✅ Sliced into {len(files) if files else 0} pieces.",
270
- progress_bar: gr.update(visible=False) # Hide progress bar when done
271
- }
272
-
273
 
274
- submit_event = submit_button.click(
275
- fn=separate_stems,
276
- inputs=[audio_input, stem_options],
277
- # Corrected outputs: progress_bar removed
278
- outputs=[vocals_output, drums_output, bass_output, other_output, status_log]
279
- )
 
 
 
 
280
 
281
- stem_options.change(fn=update_output_visibility, inputs=stem_options, outputs=[vocals_output, drums_output, bass_output, other_output])
282
 
283
- slice_vocals_btn.click(fn=slice_and_display, inputs=[vocals_output, loop_options_radio, sensitivity_slider, gr.Textbox("vocals", visible=False)], outputs=[loop_gallery, status_log, progress_bar])
284
- slice_drums_btn.click(fn=slice_and_display, inputs=[drums_output, loop_options_radio, sensitivity_slider, gr.Textbox("drums", visible=False)], outputs=[loop_gallery, status_log, progress_bar])
285
- slice_bass_btn.click(fn=slice_and_display, inputs=[bass_output, loop_options_radio, sensitivity_slider, gr.Textbox("bass", visible=False)], outputs=[loop_gallery, status_log, progress_bar])
286
- slice_other_btn.click(fn=slice_and_display, inputs=[other_output, loop_options_radio, sensitivity_slider, gr.Textbox("other", visible=False)], outputs=[loop_gallery, status_log, progress_bar])
 
 
287
 
288
- slice_all_event = slice_all_button.click(
289
- fn=slice_all_and_zip_real,
290
- inputs=[vocals_output, drums_output, bass_output, other_output, loop_options_radio, sensitivity_slider],
291
- # Corrected outputs: progress_bar removed
292
- outputs=[download_zip_file, download_zip_file, status_log]
293
- )
 
 
 
 
 
 
 
 
294
 
295
  # --- Launch the UI ---
296
  demo.launch()
 
1
+ # Import libraries
2
  import gradio as gr
3
  import os
4
  import shutil
 
36
  async def separate_stems(audio_file_path, stem_choice, progress=gr.Progress(track_tqdm=True)):
37
  # Separates the uploaded audio using Demucs
38
  if audio_file_path is None: raise gr.Error("No audio file uploaded!")
39
+
40
  log_history = "Starting separation...\n"
41
+ # Initial yield to update UI immediately
42
+ yield { status_log: log_history, progress_bar: progress(0, desc="Starting...", visible=True) }
43
+
44
  try:
45
  progress(0.05, desc="Preparing audio file...")
46
  log_history += "Preparing audio file...\n"; yield { status_log: log_history, progress_bar: progress(0.05, desc="Preparing...") }
 
72
  # Find the model-specific subfolder created by Demucs
73
  subfolders = [f.name for f in os.scandir(output_dir) if f.is_dir()]
74
  if not subfolders: raise gr.Error("Demucs output folder structure not found!")
75
+ model_folder_name = subfolders[0]
76
  stems_path = os.path.join(output_dir, model_folder_name, stable_filename_base)
77
 
78
  if not os.path.exists(stems_path):
 
89
  log_history += "✅ Stem separation complete!\n";
90
  yield {
91
  status_log: log_history,
92
+ progress_bar: progress(1, desc="Complete!", visible=False), # Hide progress bar when done
93
  vocals_output: gr.update(value=vocals_path),
94
  drums_output: gr.update(value=drums_path),
95
  bass_output: gr.update(value=bass_path),
 
97
  }
98
  except Exception as e:
99
  print(f"An error occurred during separation: {e}")
100
+ yield { status_log: log_history + f"❌ ERROR: {e}", progress_bar: gr.update(visible=False) }
101
 
102
  def slice_stem_real(stem_audio_data, loop_choice, sensitivity, stem_name, progress_fn=None):
103
  # Slices a single stem into loops or one-shots
 
116
  print(f"Detected BPM for {stem_name}: {bpm_int}")
117
 
118
  output_files = []
119
+ loops_dir = tempfile.mkdtemp()
120
 
121
  if "One-Shots" in loop_choice:
122
  if progress_fn: progress_fn(0.3, desc="Finding transients...")
 
164
  async def slice_all_and_zip_real(vocals, drums, bass, other, loop_choice, sensitivity, progress=gr.Progress(track_tqdm=True)):
165
  # Slices all available stems and creates a zip pack
166
  log_history = "Starting batch slice...\n"
167
+ yield { status_log: log_history, progress_bar: progress(0, desc="Starting...", visible=True) }
168
+ await asyncio.sleep(0.1)
169
 
170
  stems_to_process = {"vocals": vocals, "drums": drums, "bass": bass, "other": other}
171
  zip_path = "Loop_Architect_Pack.zip"
 
181
  if data is not None:
182
  log_history += f"--- Slicing {name} stem ---\n"; yield { status_log: log_history }
183
 
 
184
  def update_main_progress(p, desc=""):
185
  overall_progress = (processed_count + p) / num_stems
186
  progress(overall_progress, desc=f"Slicing {name}: {desc}")
 
189
 
190
  if sliced_files:
191
  log_history += f"Generated {len(sliced_files)} slices for {name}.\n"; yield { status_log: log_history }
192
+ all_temp_dirs.append(temp_dir)
193
  for loop_file in sliced_files:
194
+ arcname = os.path.join(name, os.path.basename(loop_file))
195
  zf.write(loop_file, arcname)
196
  else:
197
  log_history += f"No slices generated for {name}.\n"; yield { status_log: log_history }
198
 
199
  processed_count += 1
 
200
  yield { status_log: log_history, progress_bar: progress(processed_count / num_stems, desc=f"Finished {name}") }
201
 
202
+ log_history += "Packaging complete!\n"; yield { status_log: log_history, progress_bar: progress(1, desc="Pack Ready!", visible=False) }
203
 
 
204
  for d in all_temp_dirs:
205
  if d and os.path.exists(d):
206
  shutil.rmtree(d)
 
218
  stem_options = gr.Radio(["4 Stems (Vocals, Drums, Bass, Other)", "2 Stems (Vocals + Instrumental)"], label="Separation Type", value="4 Stems (Vocals, Drums, Bass, Other)")
219
  submit_button = gr.Button("Separate Stems")
220
 
 
221
  with gr.Accordion("Slicing Options", open=True):
222
  loop_options_radio = gr.Radio(["One-Shots (All Transients)", "4 Bar Loops", "8 Bar Loops"], label="Slice Type", value="One-Shots (All Transients)")
223
  sensitivity_slider = gr.Slider(minimum=0.01, maximum=0.5, value=0.05, step=0.01, label="One-Shot Sensitivity", info="Lower values = more slices")
 
227
  download_zip_file = gr.File(label="Download Your Loop Pack", visible=False)
228
 
229
  gr.Markdown("### Status")
230
+ # Removed label from gr.Progress
231
+ progress_bar = gr.Progress(visible=False)
232
  status_log = gr.Textbox(label="Status Log", lines=10, interactive=False)
233
 
234
  with gr.Column(scale=2):
235
  with gr.Accordion("Separated Stems", open=True):
236
  with gr.Row():
237
  vocals_output = gr.Audio(label="Vocals", scale=4)
238
+ slice_vocals_btn = gr.Button("Slice Vocals", scale=1)
239
  with gr.Row():
240
  drums_output = gr.Audio(label="Drums", scale=4)
241
  slice_drums_btn = gr.Button("Slice Drums", scale=1)
 
248
  gr.Markdown("### Sliced Loops / Samples (Preview)")
249
  loop_gallery = gr.Gallery(label="Generated Loops Preview", columns=8, object_fit="contain", height="auto", preview=True)
250
 
251
+ # --- Define Event Listeners ---
252
+ def slice_and_display(stem_data, loop_choice, sensitivity, stem_name):
253
+ # Wrapper to handle progress display for single slice buttons
254
+ log_history = f"Slicing {stem_name}...\n"
255
+ # Use a dictionary to update multiple components
256
+ yield {
257
+ status_log: log_history,
258
+ progress_bar: gr.update(value=0, visible=True, label=f"Slicing {stem_name}...")
259
+ }
260
+
261
+ # Define how slice_stem_real updates the progress bar
262
+ def update_single_progress(p, desc=""):
263
+ progress_bar.update(value=p, label=desc, visible=True)
 
 
 
 
 
 
 
 
264
 
265
+ files, temp_dir = slice_stem_real(stem_data, loop_choice, sensitivity, stem_name, progress_fn=update_single_progress)
266
+
267
+ if temp_dir and os.path.exists(temp_dir):
268
+ shutil.rmtree(temp_dir)
269
+
270
+ yield {
271
+ loop_gallery: gr.update(value=files),
272
+ status_log: log_history + f"✅ Sliced {stem_name} into {len(files) if files else 0} pieces.",
273
+ progress_bar: gr.update(visible=False) # Hide progress bar when done
274
+ }
275
 
 
276
 
277
+ submit_event = submit_button.click(
278
+ fn=separate_stems,
279
+ inputs=[audio_input, stem_options],
280
+ # Corrected outputs: progress_bar removed from here
281
+ outputs=[vocals_output, drums_output, bass_output, other_output, status_log]
282
+ )
283
 
284
+ stem_options.change(fn=update_output_visibility, inputs=stem_options, outputs=[vocals_output, drums_output, bass_output, other_output])
285
+
286
+ # Use the wrapper function for individual slice buttons
287
+ slice_vocals_btn.click(fn=slice_and_display, inputs=[vocals_output, loop_options_radio, sensitivity_slider, gr.Textbox("vocals", visible=False)], outputs=[loop_gallery, status_log, progress_bar])
288
+ slice_drums_btn.click(fn=slice_and_display, inputs=[drums_output, loop_options_radio, sensitivity_slider, gr.Textbox("drums", visible=False)], outputs=[loop_gallery, status_log, progress_bar])
289
+ slice_bass_btn.click(fn=slice_and_display, inputs=[bass_output, loop_options_radio, sensitivity_slider, gr.Textbox("bass", visible=False)], outputs=[loop_gallery, status_log, progress_bar])
290
+ slice_other_btn.click(fn=slice_and_display, inputs=[other_output, loop_options_radio, sensitivity_slider, gr.Textbox("other", visible=False)], outputs=[loop_gallery, status_log, progress_bar])
291
+
292
+ slice_all_event = slice_all_button.click(
293
+ fn=slice_all_and_zip_real,
294
+ inputs=[vocals_output, drums_output, bass_output, other_output, loop_options_radio, sensitivity_slider],
295
+ # Corrected outputs: progress_bar removed from here
296
+ outputs=[download_zip_file, download_zip_file, status_log]
297
+ )
298
 
299
  # --- Launch the UI ---
300
  demo.launch()