Loop-Architect / app.py
SaltProphet's picture
Update app.py
ac29ba7 verified
raw
history blame
6.43 kB
# 1. Install necessary libraries
!pip install gradio "demucs>=4.0.0" librosa soundfile matplotlib
# 2. Import libraries
import gradio as gr
import os
import shutil
import asyncio
import librosa
import librosa.display
import soundfile as sf
import numpy as np
import time
import zipfile
import tempfile
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Agg')
# --- Helper/Processing Functions ---
def update_output_visibility(choice):
# Now handles 2, 4, and 6 stem visibility
is_6_stem = "6 Stems" in choice
is_4_stem = "4 Stems" in choice
is_2_stem = "2 Stems" in choice
return {
vocals_output: gr.update(visible=True),
drums_output: gr.update(visible=is_4_stem or is_6_stem),
bass_output: gr.update(visible=is_4_stem or is_6_stem),
guitar_output: gr.update(visible=is_6_stem), # Guitar visible only for 6 stems
piano_output: gr.update(visible=is_6_stem), # Piano visible only for 6 stems
other_output: gr.update(visible=True, label="Instrumental (No Vocals)" if is_2_stem else "Other")
}
async def separate_stems(audio_file_path, stem_choice, progress=gr.Progress(track_tqdm=True)):
if audio_file_path is None: raise gr.Error("No audio file uploaded!")
progress(0, desc="Starting...")
try:
progress(0.05, desc="Preparing audio file...")
original_filename_base = os.path.basename(audio_file_path).rsplit('.', 1)[0]
stable_input_path = f"stable_input_{original_filename_base}.wav"
shutil.copy(audio_file_path, stable_input_path)
# Determine Demucs model based on choice
model_name = "htdemucs" # Default to 4-stem model
model_arg = ""
if "2 Stems" in stem_choice:
model_arg = "--two-stems=vocals"
elif "6 Stems" in stem_choice:
model_name = "htdemucs_6s" # Use the 6-stem model
output_dir = "separated"
if os.path.exists(output_dir): shutil.rmtree(output_dir)
# Use -n flag to specify the model name
command = f"python3 -m demucs -n {model_name} {model_arg} -o \"{output_dir}\" \"{stable_input_path}\""
progress(0.2, desc=f"Running Demucs ({model_name})...")
process = await asyncio.create_subprocess_shell(command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
stdout, stderr = await process.communicate()
if process.returncode != 0:
raise gr.Error(f"Demucs failed. Error: {stderr.decode()[:500]}")
progress(0.8, desc="Locating separated stem files...")
stable_filename_base = os.path.basename(stable_input_path).rsplit('.', 1)[0]
# Demucs output folder now matches the model name
stems_path = os.path.join(output_dir, model_name, stable_filename_base)
if not os.path.exists(stems_path):
raise gr.Error(f"Demucs finished, but output directory '{stems_path}' not found!")
# Check for all possible stem files
vocals_path = os.path.join(stems_path, "vocals.wav") if os.path.exists(os.path.join(stems_path, "vocals.wav")) else None
drums_path = os.path.join(stems_path, "drums.wav") if os.path.exists(os.path.join(stems_path, "drums.wav")) else None
bass_path = os.path.join(stems_path, "bass.wav") if os.path.exists(os.path.join(stems_path, "bass.wav")) else None
guitar_path = os.path.join(stems_path, "guitar.wav") if os.path.exists(os.path.join(stems_path, "guitar.wav")) else None
piano_path = os.path.join(stems_path, "piano.wav") if os.path.exists(os.path.join(stems_path, "piano.wav")) else None
# Handle 'other' vs 'no_vocals' based on model
other_filename = "no_vocals.wav" if "2 Stems" in stem_choice else "other.wav"
other_path = os.path.join(stems_path, other_filename) if os.path.exists(os.path.join(stems_path, other_filename)) else None
os.remove(stable_input_path)
return vocals_path, drums_path, bass_path, guitar_path, piano_path, other_path
except Exception as e:
print(f"An error occurred: {e}")
raise gr.Error(str(e))
# --- Create the full Gradio Interface ---
with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", secondary_hue="red")) as demo:
gr.Markdown("# 🎵 Loop Architect")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 1. Separate Stems")
audio_input = gr.Audio(type="filepath", label="Upload a Track")
# Added 6-stem option
stem_options = gr.Radio(
["6 Stems (Vocals, Drums, Bass, Guitar, Piano, Other)",
"4 Stems (Vocals, Drums, Bass, Other)",
"2 Stems (Vocals + Instrumental)"],
label="Separation Type",
value="4 Stems (Vocals, Drums, Bass, Other)" # Default still 4 stems
)
submit_button = gr.Button("Separate Stems")
with gr.Column(scale=2):
with gr.Accordion("Separated Stems", open=True):
# Added players for guitar and piano, initially hidden
with gr.Row():
vocals_output = gr.Audio(label="Vocals", scale=4)
with gr.Row():
drums_output = gr.Audio(label="Drums", scale=4, visible=True)
with gr.Row():
bass_output = gr.Audio(label="Bass", scale=4, visible=True)
with gr.Row():
guitar_output = gr.Audio(label="Guitar", scale=4, visible=False)
with gr.Row():
piano_output = gr.Audio(label="Piano", scale=4, visible=False)
with gr.Row():
other_output = gr.Audio(label="Other / Instrumental", scale=4, visible=True)
# --- Define Event Listeners ---
submit_button.click(
fn=separate_stems,
inputs=[audio_input, stem_options],
# Update outputs to include new players
outputs=[vocals_output, drums_output, bass_output, guitar_output, piano_output, other_output]
)
stem_options.change(
fn=update_output_visibility,
inputs=stem_options,
# Update outputs to include new players
outputs=[vocals_output, drums_output, bass_output, guitar_output, piano_output, other_output]
)
# --- Launch the UI ---
demo.launch()