SaltProphet commited on
Commit
6b29282
·
verified ·
1 Parent(s): d245644

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -40
app.py CHANGED
@@ -112,8 +112,21 @@ def analyze_and_separate(file_input, url_input):
112
  except Exception as e:
113
  raise gr.Error(f"Process Failed: {str(e)}")
114
 
115
- def package_and_export(track_folder_str, bpm, start_offset_sec, cover_art):
116
- """Phase 2: Package & Export"""
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  try:
118
  track_folder = Path(track_folder_str)
119
  stems = {
@@ -125,64 +138,78 @@ def package_and_export(track_folder_str, bpm, start_offset_sec, cover_art):
125
  "Vocals": track_folder / "vocals.wav"
126
  }
127
 
128
- # 1. Save Full Stems
 
 
 
 
 
 
129
  for name, path in stems.items():
130
  if path.exists():
131
  shutil.copy(path, OUTPUT_DIR / "Stems" / f"{bpm}BPM_Full_{name}.wav")
132
 
133
- # 2. Create Loops
134
- ms_per_beat = (60 / bpm) * 1000
135
- eight_bars_ms = ms_per_beat * 4 * 8
136
- start_ms = start_offset_sec * 1000
137
-
138
- created_loops = {}
 
139
 
140
- def make_loop(src, name):
141
- if not src.exists(): return None
142
- audio = AudioSegment.from_wav(str(src))
143
- end_ms = start_ms + eight_bars_ms
144
- if len(audio) < end_ms:
145
- s = 0
146
- e = min(len(audio), eight_bars_ms)
147
- else:
148
- s, e = start_ms, end_ms
149
- loop = audio[s:int(e)].fade_in(15).fade_out(15).normalize()
150
- out_path = OUTPUT_DIR / "Loops" / f"{bpm}BPM_{name}.wav"
151
- loop.export(out_path, format="wav")
152
- return out_path
153
 
154
- created_loops['melody'] = make_loop(stems['Synths'], "SynthLoop")
155
- make_loop(stems['Drums'], "DrumLoop")
156
- make_loop(stems['Bass'], "BassLoop")
157
- make_loop(stems['Guitar'], "GuitarLoop")
158
- make_loop(stems['Piano'], "PianoLoop")
159
- make_loop(stems['Vocals'], "VocalChop")
160
-
161
- # 3. Generate Video
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  video_path = None
163
- if cover_art and created_loops['melody']:
164
  print("Rendering Video...")
165
  vid_out = OUTPUT_DIR / "Promo_Video.mp4"
166
- audio_clip = AudioFileClip(str(created_loops['melody']))
167
  duration = audio_clip.duration
168
-
169
  img = ImageClip(cover_art).resize(width=1080)
170
-
171
- # Zoom Animation (Full Color)
172
- img = img.resize(lambda t : 1 + 0.02*t)
173
  img = img.set_position(('center', 'center'))
174
  img = img.set_duration(duration)
175
  img = img.set_audio(audio_clip)
176
-
177
  final_clip = CompositeVideoClip([img], size=(1080, 1920))
178
  final_clip.duration = duration
179
  final_clip.audio = audio_clip
180
  final_clip.fps = 24
181
-
182
  final_clip.write_videofile(str(vid_out), codec="libx264", audio_codec="aac", logger=None)
183
  video_path = str(vid_out)
184
 
185
- # 4. Zip
186
  zip_file = "NightPulse_Pack.zip"
187
  with zipfile.ZipFile(zip_file, 'w') as zf:
188
  for root, dirs, files in os.walk(OUTPUT_DIR):
@@ -190,13 +217,14 @@ def package_and_export(track_folder_str, bpm, start_offset_sec, cover_art):
190
  file_path = Path(root) / file
191
  arcname = file_path.relative_to(OUTPUT_DIR)
192
  zf.write(file_path, arcname)
193
-
194
  return zip_file, video_path
195
 
196
  except Exception as e:
197
  raise gr.Error(f"Packaging Failed: {str(e)}")
198
 
199
 
 
200
  # --- GUI (Blocks) ---
201
  with gr.Blocks(title="Night Pulse | Studio Pro") as app:
202
  gr.Markdown("# 🎛️ Night Pulse | Studio Command Center")
 
112
  except Exception as e:
113
  raise gr.Error(f"Process Failed: {str(e)}")
114
 
115
+ def package_and_export(
116
+ track_folder_str,
117
+ bpm,
118
+ start_offset_sec, # kept for compatibility, but not used for loop starts anymore
119
+ cover_art,
120
+ # --- new loop engine params (you can wire these into UI later) ---
121
+ loops_per_stem=12,
122
+ bar_lengths=(1, 2, 4, 8),
123
+ hop_bars=1,
124
+ top_k=30,
125
+ fade_ms=12,
126
+ loudness_mode="none", # "none" | "peak" | "rms"
127
+ target_dbfs=-14.0
128
+ ):
129
+ """Phase 2: Package & Export (real loop engine)"""
130
  try:
131
  track_folder = Path(track_folder_str)
132
  stems = {
 
138
  "Vocals": track_folder / "vocals.wav"
139
  }
140
 
141
+ # Cleanup Output
142
+ if OUTPUT_DIR.exists():
143
+ shutil.rmtree(OUTPUT_DIR)
144
+ (OUTPUT_DIR / "Stems").mkdir(parents=True, exist_ok=True)
145
+ (OUTPUT_DIR / "Loops").mkdir(parents=True, exist_ok=True)
146
+
147
+ # 1) Save Full Stems
148
  for name, path in stems.items():
149
  if path.exists():
150
  shutil.copy(path, OUTPUT_DIR / "Stems" / f"{bpm}BPM_Full_{name}.wav")
151
 
152
+ # 2) Build bar grid from the *master beat grid source*
153
+ # Use drums if present (best for beat tracking), otherwise "other", otherwise any existing stem.
154
+ grid_source = stems["Drums"] if stems["Drums"].exists() else (
155
+ stems["Synths"] if stems["Synths"].exists() else next((p for p in stems.values() if p.exists()), None)
156
+ )
157
+ if grid_source is None:
158
+ raise FileNotFoundError("No stems found to build beat/bar grid.")
159
 
160
+ bar_starts_ms = detect_bar_grid(str(grid_source), bpm=bpm, max_seconds=240)
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
+ # 3) Create quantized multi-loops per stem
163
+ all_exported_loops = {}
164
+ loops_dir = OUTPUT_DIR / "Loops"
165
+
166
+ for stem_name, stem_path in stems.items():
167
+ exported = make_quantized_loops(
168
+ stem_path=stem_path,
169
+ stem_name=stem_name,
170
+ bpm=int(bpm),
171
+ bar_starts_ms=bar_starts_ms,
172
+ bar_lengths=list(bar_lengths),
173
+ hop_bars=int(hop_bars),
174
+ max_loops_per_stem=int(loops_per_stem),
175
+ top_k=int(top_k),
176
+ fade_ms=int(fade_ms),
177
+ loudness_mode=str(loudness_mode),
178
+ target_dbfs=float(target_dbfs),
179
+ out_dir=loops_dir
180
+ )
181
+ all_exported_loops[stem_name] = exported
182
+
183
+ # Pick a melody-ish loop for video (prefer Synths, fallback Piano/Guitar)
184
+ video_loop = None
185
+ for key in ("Synths", "Piano", "Guitar"):
186
+ if all_exported_loops.get(key):
187
+ video_loop = all_exported_loops[key][0]
188
+ break
189
+
190
+ # 4) Generate Video (optional)
191
  video_path = None
192
+ if cover_art and video_loop:
193
  print("Rendering Video...")
194
  vid_out = OUTPUT_DIR / "Promo_Video.mp4"
195
+ audio_clip = AudioFileClip(str(video_loop))
196
  duration = audio_clip.duration
197
+
198
  img = ImageClip(cover_art).resize(width=1080)
199
+ img = img.resize(lambda t: 1 + 0.02 * t)
 
 
200
  img = img.set_position(('center', 'center'))
201
  img = img.set_duration(duration)
202
  img = img.set_audio(audio_clip)
203
+
204
  final_clip = CompositeVideoClip([img], size=(1080, 1920))
205
  final_clip.duration = duration
206
  final_clip.audio = audio_clip
207
  final_clip.fps = 24
208
+
209
  final_clip.write_videofile(str(vid_out), codec="libx264", audio_codec="aac", logger=None)
210
  video_path = str(vid_out)
211
 
212
+ # 5) Zip
213
  zip_file = "NightPulse_Pack.zip"
214
  with zipfile.ZipFile(zip_file, 'w') as zf:
215
  for root, dirs, files in os.walk(OUTPUT_DIR):
 
217
  file_path = Path(root) / file
218
  arcname = file_path.relative_to(OUTPUT_DIR)
219
  zf.write(file_path, arcname)
220
+
221
  return zip_file, video_path
222
 
223
  except Exception as e:
224
  raise gr.Error(f"Packaging Failed: {str(e)}")
225
 
226
 
227
+
228
  # --- GUI (Blocks) ---
229
  with gr.Blocks(title="Night Pulse | Studio Pro") as app:
230
  gr.Markdown("# 🎛️ Night Pulse | Studio Command Center")