SaltProphet commited on
Commit
b42ee2b
·
verified ·
1 Parent(s): 81f90ee

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +29 -24
app.py CHANGED
@@ -15,7 +15,6 @@ OUTPUT_DIR = Path("nightpulse_output")
15
  TEMP_DIR = Path("temp_processing")
16
 
17
  def process_track(audio_file, cover_art_image):
18
- # Initialize return variables
19
  zip_path = None
20
  video_path = None
21
 
@@ -29,19 +28,19 @@ def process_track(audio_file, cover_art_image):
29
  shutil.rmtree(OUTPUT_DIR)
30
  if TEMP_DIR.exists():
31
  shutil.rmtree(TEMP_DIR)
32
- OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
 
 
 
33
  TEMP_DIR.mkdir(parents=True, exist_ok=True)
34
 
35
  filename = Path(audio_file).stem
36
 
37
- # --- 2. Analyze BPM & Key (Librosa) ---
38
  print(f"Analyzing {filename}...")
39
  try:
40
- # Mono load for robust BPM detection
41
  y, sr = librosa.load(audio_file, duration=60, mono=True)
42
  tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
43
-
44
- # Handle different librosa return types (float vs array)
45
  if np.ndim(tempo) > 0:
46
  detected_bpm = int(round(tempo[0]))
47
  else:
@@ -49,12 +48,11 @@ def process_track(audio_file, cover_art_image):
49
  print(f"Detected BPM: {detected_bpm}")
50
  except Exception as e:
51
  print(f"BPM Warning: {e}")
52
- detected_bpm = 120 # Safe Fallback
53
 
54
  # --- 3. AI Stem Separation (Demucs) ---
55
  print("Separating stems...")
56
  try:
57
- # Call Demucs via subprocess to ensure clean execution
58
  subprocess.run([
59
  sys.executable, "-m", "demucs",
60
  "-n", "htdemucs",
@@ -62,22 +60,27 @@ def process_track(audio_file, cover_art_image):
62
  audio_file
63
  ], check=True, capture_output=True)
64
  except subprocess.CalledProcessError as e:
65
- # Catch and display the specific error from Demucs
66
  raise RuntimeError(f"Demucs failed: {e.stderr.decode()}")
67
 
68
- # Locate Stems (Robust Search)
69
  demucs_out = TEMP_DIR / "htdemucs"
70
- # Demucs might normalize filenames (spaces -> underscores), so we find the first folder
71
  track_folder = next(demucs_out.iterdir(), None)
72
 
73
- if not track_folder:
74
- raise FileNotFoundError("Demucs output folder missing.")
75
 
 
76
  drums_path = track_folder / "drums.wav"
77
  melody_path = track_folder / "other.wav"
78
  bass_path = track_folder / "bass.wav"
 
79
 
80
- # --- 4. Loop Logic ---
 
 
 
 
 
 
 
81
  if detected_bpm <= 0: detected_bpm = 120
82
  ms_per_beat = (60 / detected_bpm) * 1000
83
  eight_bars_ms = ms_per_beat * 4 * 8
@@ -86,7 +89,6 @@ def process_track(audio_file, cover_art_image):
86
  if not source_path.exists(): return None, None
87
 
88
  audio = AudioSegment.from_wav(str(source_path))
89
- # Grab middle 8 bars
90
  start = len(audio) // 3
91
  end = start + eight_bars_ms
92
 
@@ -95,7 +97,8 @@ def process_track(audio_file, cover_art_image):
95
  end = min(len(audio), eight_bars_ms)
96
 
97
  loop = audio[start:int(end)].fade_in(15).fade_out(15).normalize()
98
- out_name = OUTPUT_DIR / f"{detected_bpm}BPM_{output_name}.wav"
 
99
  loop.export(out_name, format="wav")
100
  return out_name, loop
101
 
@@ -103,15 +106,13 @@ def process_track(audio_file, cover_art_image):
103
  loop_melody, _ = create_loop(melody_path, "MelodyLoop")
104
  create_loop(bass_path, "BassLoop")
105
 
106
- # --- 5. Video Generation ---
107
  if cover_art_image and loop_melody:
108
  print("Rendering Video...")
109
  try:
110
  vid_out = OUTPUT_DIR / "Promo_Video.mp4"
111
  audio_clip = AudioFileClip(str(loop_melody))
112
  img_clip = ImageClip(cover_art_image)
113
-
114
- # Resize to 1080w (maintain aspect ratio)
115
  img_clip = img_clip.resize(width=1080)
116
  img_clip = img_clip.set_duration(audio_clip.duration)
117
  img_clip = img_clip.set_audio(audio_clip)
@@ -122,11 +123,15 @@ def process_track(audio_file, cover_art_image):
122
  except Exception as e:
123
  print(f"Video skipped: {e}")
124
 
125
- # --- 6. Zip Export ---
126
  zip_file = "NightPulse_Pack.zip"
127
  with zipfile.ZipFile(zip_file, 'w') as zf:
128
- for f in OUTPUT_DIR.iterdir():
129
- zf.write(f, f.name)
 
 
 
 
130
  zip_path = zip_file
131
 
132
  return zip_path, video_path
@@ -142,11 +147,11 @@ iface = gr.Interface(
142
  gr.Image(type="filepath", label="Upload Cover Art")
143
  ],
144
  outputs=[
145
- gr.File(label="Download ZIP"),
146
  gr.Video(label="Preview Video")
147
  ],
148
  title="Night Pulse Audio | Automator",
149
- description="Night Pulse Pipeline v1.2 (Stable)"
150
  )
151
 
152
  if __name__ == "__main__":
 
15
  TEMP_DIR = Path("temp_processing")
16
 
17
  def process_track(audio_file, cover_art_image):
 
18
  zip_path = None
19
  video_path = None
20
 
 
28
  shutil.rmtree(OUTPUT_DIR)
29
  if TEMP_DIR.exists():
30
  shutil.rmtree(TEMP_DIR)
31
+
32
+ # Create subfolders for organization
33
+ (OUTPUT_DIR / "Stems").mkdir(parents=True, exist_ok=True)
34
+ (OUTPUT_DIR / "Loops").mkdir(parents=True, exist_ok=True)
35
  TEMP_DIR.mkdir(parents=True, exist_ok=True)
36
 
37
  filename = Path(audio_file).stem
38
 
39
+ # --- 2. Analyze BPM (Librosa) ---
40
  print(f"Analyzing {filename}...")
41
  try:
 
42
  y, sr = librosa.load(audio_file, duration=60, mono=True)
43
  tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
 
 
44
  if np.ndim(tempo) > 0:
45
  detected_bpm = int(round(tempo[0]))
46
  else:
 
48
  print(f"Detected BPM: {detected_bpm}")
49
  except Exception as e:
50
  print(f"BPM Warning: {e}")
51
+ detected_bpm = 120
52
 
53
  # --- 3. AI Stem Separation (Demucs) ---
54
  print("Separating stems...")
55
  try:
 
56
  subprocess.run([
57
  sys.executable, "-m", "demucs",
58
  "-n", "htdemucs",
 
60
  audio_file
61
  ], check=True, capture_output=True)
62
  except subprocess.CalledProcessError as e:
 
63
  raise RuntimeError(f"Demucs failed: {e.stderr.decode()}")
64
 
 
65
  demucs_out = TEMP_DIR / "htdemucs"
 
66
  track_folder = next(demucs_out.iterdir(), None)
67
 
68
+ if not track_folder: raise FileNotFoundError("Demucs output folder missing.")
 
69
 
70
+ # Map the raw stems
71
  drums_path = track_folder / "drums.wav"
72
  melody_path = track_folder / "other.wav"
73
  bass_path = track_folder / "bass.wav"
74
+ vocals_path = track_folder / "vocals.wav"
75
 
76
+ # --- 4. EXPORT FULL STEMS (New Step) ---
77
+ # Copy the full length files to the Stems folder
78
+ if drums_path.exists(): shutil.copy(drums_path, OUTPUT_DIR / "Stems" / f"{detected_bpm}BPM_Full_Drums.wav")
79
+ if melody_path.exists(): shutil.copy(melody_path, OUTPUT_DIR / "Stems" / f"{detected_bpm}BPM_Full_Melody.wav")
80
+ if bass_path.exists(): shutil.copy(bass_path, OUTPUT_DIR / "Stems" / f"{detected_bpm}BPM_Full_Bass.wav")
81
+ if vocals_path.exists(): shutil.copy(vocals_path, OUTPUT_DIR / "Stems" / f"{detected_bpm}BPM_Full_Vocals.wav")
82
+
83
+ # --- 5. Loop Logic (The Chop) ---
84
  if detected_bpm <= 0: detected_bpm = 120
85
  ms_per_beat = (60 / detected_bpm) * 1000
86
  eight_bars_ms = ms_per_beat * 4 * 8
 
89
  if not source_path.exists(): return None, None
90
 
91
  audio = AudioSegment.from_wav(str(source_path))
 
92
  start = len(audio) // 3
93
  end = start + eight_bars_ms
94
 
 
97
  end = min(len(audio), eight_bars_ms)
98
 
99
  loop = audio[start:int(end)].fade_in(15).fade_out(15).normalize()
100
+ # Save to Loops folder
101
+ out_name = OUTPUT_DIR / "Loops" / f"{detected_bpm}BPM_{output_name}.wav"
102
  loop.export(out_name, format="wav")
103
  return out_name, loop
104
 
 
106
  loop_melody, _ = create_loop(melody_path, "MelodyLoop")
107
  create_loop(bass_path, "BassLoop")
108
 
109
+ # --- 6. Video Generation ---
110
  if cover_art_image and loop_melody:
111
  print("Rendering Video...")
112
  try:
113
  vid_out = OUTPUT_DIR / "Promo_Video.mp4"
114
  audio_clip = AudioFileClip(str(loop_melody))
115
  img_clip = ImageClip(cover_art_image)
 
 
116
  img_clip = img_clip.resize(width=1080)
117
  img_clip = img_clip.set_duration(audio_clip.duration)
118
  img_clip = img_clip.set_audio(audio_clip)
 
123
  except Exception as e:
124
  print(f"Video skipped: {e}")
125
 
126
+ # --- 7. Zip Export ---
127
  zip_file = "NightPulse_Pack.zip"
128
  with zipfile.ZipFile(zip_file, 'w') as zf:
129
+ for root, dirs, files in os.walk(OUTPUT_DIR):
130
+ for file in files:
131
+ file_path = Path(root) / file
132
+ # Create structure inside zip (Stems/file.wav, Loops/file.wav)
133
+ arcname = file_path.relative_to(OUTPUT_DIR)
134
+ zf.write(file_path, arcname)
135
  zip_path = zip_file
136
 
137
  return zip_path, video_path
 
147
  gr.Image(type="filepath", label="Upload Cover Art")
148
  ],
149
  outputs=[
150
+ gr.File(label="Download Full Pack (ZIP)"),
151
  gr.Video(label="Preview Video")
152
  ],
153
  title="Night Pulse Audio | Automator",
154
+ description="Night Pulse Pipeline v1.3 (Full Stems Included)"
155
  )
156
 
157
  if __name__ == "__main__":