Spaces:
Running
Running
File size: 5,245 Bytes
55c17c3 b1477ac 55c17c3 f04ed54 44c1e23 f04ed54 b1477ac f04ed54 b1477ac f04ed54 6f33f17 f04ed54 55c17c3 b1477ac 6f33f17 f04ed54 6f33f17 b1477ac f04ed54 6f33f17 f04ed54 b1477ac 6f33f17 f04ed54 b1477ac 6f33f17 f04ed54 b1477ac f04ed54 6f33f17 f04ed54 6f33f17 f04ed54 6f33f17 f04ed54 6f33f17 f04ed54 6f33f17 f04ed54 6f33f17 f04ed54 6f33f17 b1477ac 6f33f17 b1477ac 6f33f17 b1477ac 6f33f17 f04ed54 6f33f17 f04ed54 6f33f17 b1477ac 6f33f17 b1477ac 6f33f17 f04ed54 6f33f17 b1477ac 6f33f17 b1477ac f04ed54 44c1e23 f04ed54 6f33f17 f04ed54 6f33f17 f04ed54 6f33f17 f04ed54 6f33f17 f04ed54 44c1e23 f04ed54 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
import gradio as gr
import os
import shutil
import zipfile
import librosa
import numpy as np
from pydub import AudioSegment
from moviepy.editor import AudioFileClip, ImageClip
import subprocess
from pathlib import Path
import sys
# --- Configuration ---
OUTPUT_DIR = Path("nightpulse_output")
TEMP_DIR = Path("temp_processing")
def process_track(audio_file, cover_art_image):
# Initialize return variables
zip_path = None
video_path = None
try:
# --- 0. Input Validation ---
if not audio_file:
raise ValueError("No audio file provided.")
# --- 1. Setup Directories ---
if OUTPUT_DIR.exists():
shutil.rmtree(OUTPUT_DIR)
if TEMP_DIR.exists():
shutil.rmtree(TEMP_DIR)
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
TEMP_DIR.mkdir(parents=True, exist_ok=True)
filename = Path(audio_file).stem
# --- 2. Analyze BPM & Key (Librosa) ---
print(f"Analyzing {filename}...")
try:
# Mono load for robust BPM detection
y, sr = librosa.load(audio_file, duration=60, mono=True)
tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
# Handle different librosa return types (float vs array)
if np.ndim(tempo) > 0:
detected_bpm = int(round(tempo[0]))
else:
detected_bpm = int(round(tempo))
print(f"Detected BPM: {detected_bpm}")
except Exception as e:
print(f"BPM Warning: {e}")
detected_bpm = 120 # Safe Fallback
# --- 3. AI Stem Separation (Demucs) ---
print("Separating stems...")
try:
# Call Demucs via subprocess to ensure clean execution
subprocess.run([
sys.executable, "-m", "demucs",
"-n", "htdemucs",
"--out", str(TEMP_DIR),
audio_file
], check=True, capture_output=True)
except subprocess.CalledProcessError as e:
raise RuntimeError(f"Demucs failed: {e.stderr.decode()}")
# Locate Stems (Robust Search)
demucs_out = TEMP_DIR / "htdemucs"
track_folder = next(demucs_out.iterdir(), None)
if not track_folder:
raise FileNotFoundError("Demucs output folder missing.")
drums_path = track_folder / "drums.wav"
melody_path = track_folder / "other.wav"
bass_path = track_folder / "bass.wav"
# --- 4. Loop Logic ---
if detected_bpm <= 0: detected_bpm = 120
ms_per_beat = (60 / detected_bpm) * 1000
eight_bars_ms = ms_per_beat * 4 * 8
def create_loop(source_path, output_name):
if not source_path.exists(): return None, None
audio = AudioSegment.from_wav(str(source_path))
# Grab middle 8 bars
start = len(audio) // 3
end = start + eight_bars_ms
if len(audio) < end:
start = 0
end = min(len(audio), eight_bars_ms)
loop = audio[start:int(end)].fade_in(15).fade_out(15).normalize()
out_name = OUTPUT_DIR / f"{detected_bpm}BPM_{output_name}.wav"
loop.export(out_name, format="wav")
return out_name, loop
loop_drums, _ = create_loop(drums_path, "DrumLoop")
loop_melody, _ = create_loop(melody_path, "MelodyLoop")
create_loop(bass_path, "BassLoop")
# --- 5. Video Generation ---
if cover_art_image and loop_melody:
print("Rendering Video...")
try:
vid_out = OUTPUT_DIR / "Promo_Video.mp4"
audio_clip = AudioFileClip(str(loop_melody))
img_clip = ImageClip(cover_art_image)
# Resize to 1080w (maintain aspect ratio)
img_clip = img_clip.resize(width=1080)
img_clip = img_clip.set_duration(audio_clip.duration)
img_clip = img_clip.set_audio(audio_clip)
img_clip.fps = 24
img_clip.write_videofile(str(vid_out), codec="libx264", audio_codec="aac", logger=None)
video_path = str(vid_out)
except Exception as e:
print(f"Video skipped: {e}")
# --- 6. Zip Export ---
zip_file = "NightPulse_Pack.zip"
with zipfile.ZipFile(zip_file, 'w') as zf:
for f in OUTPUT_DIR.iterdir():
zf.write(f, f.name)
zip_path = zip_file
return zip_path, video_path
except Exception as e:
raise gr.Error(f"System Error: {str(e)}")
# --- UI Definition (Corrected) ---
iface = gr.Interface(
fn=process_track,
inputs=[
gr.Audio(type="filepath", label="Upload Suno Track"),
gr.Image(type="filepath", label="Upload Cover Art")
],
outputs=[
gr.File(label="Download ZIP"),
gr.Video(label="Preview Video")
],
title="Night Pulse Audio | Automator",
description="Night Pulse Pipeline v1.1 (Stable)"
)
if __name__ == "__main__":
iface.launch() |