tee342 commited on
Commit
e3e38c6
Β·
verified Β·
1 Parent(s): d27bfa8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +228 -283
app.py CHANGED
@@ -138,7 +138,117 @@ def match_loudness(audio_path, target_lufs=-14.0):
138
  adjusted.export(out_path, format="wav")
139
  return out_path
140
 
141
- # === AI Vocal Pitch Correction – Auto-Tune Style ===
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  def auto_tune_vocal(audio_path, target_key="C"):
143
  try:
144
  # Placeholder for real-time pitch detection
@@ -147,20 +257,6 @@ def auto_tune_vocal(audio_path, target_key="C"):
147
  except Exception as e:
148
  return None
149
 
150
- # === Real-Time EQ with Curve Drawing ===
151
- def draw_eq_curve(freqs, gains):
152
- fig, ax = plt.subplots(figsize=(10, 4))
153
- ax.plot(freqs, gains, color='blue', lw=2)
154
- ax.set_xscale('log')
155
- ax.set_title("EQ Curve")
156
- ax.set_xlabel("Frequency (Hz)")
157
- ax.set_ylabel("Gain (dB)")
158
- buf = BytesIO()
159
- plt.savefig(buf, format="png")
160
- plt.close()
161
- buf.seek(0)
162
- return Image.open(buf)
163
-
164
  # === Create Karaoke Video from Audio + Lyrics ===
165
  def create_karaoke_video(audio_path, lyrics, bg_image=None):
166
  try:
@@ -183,13 +279,20 @@ def create_karaoke_video(audio_path, lyrics, bg_image=None):
183
  return f"⚠️ Failed: {str(e)}"
184
 
185
  # === Save/Load Project File (.aiproj) ===
186
- def save_project(audio_path, preset_name, effects):
187
  project_data = {
188
- "audio": AudioSegment.from_file(audio_path).raw_data,
189
- "preset": preset_name,
190
- "effects": effects
 
 
 
 
 
 
 
191
  }
192
- out_path = os.path.join(tempfile.gettempdir(), "project.aiproj")
193
  with open(out_path, "wb") as f:
194
  pickle.dump(project_data, f)
195
  return out_path
@@ -197,7 +300,16 @@ def save_project(audio_path, preset_name, effects):
197
  def load_project(project_file):
198
  with open(project_file.name, "rb") as f:
199
  data = pickle.load(f)
200
- return data["preset"], data["effects"]
 
 
 
 
 
 
 
 
 
201
 
202
  # === Vocal Doubler / Harmonizer ===
203
  def vocal_doubler(audio):
@@ -257,181 +369,6 @@ def stem_split(audio_path):
257
 
258
  return stem_paths
259
 
260
- # === Preset Loader with Fallback ===
261
- def load_presets():
262
- try:
263
- preset_files = [f for f in os.listdir("presets") if f.endswith(".json")]
264
- presets = {}
265
- for f in preset_files:
266
- path = os.path.join("presets", f)
267
- try:
268
- with open(path, "r") as infile:
269
- data = json.load(infile)
270
- if "name" in data and "effects" in data:
271
- presets[data["name"]] = data["effects"]
272
- except json.JSONDecodeError:
273
- print(f"Invalid JSON: {f}")
274
- return presets
275
- except FileNotFoundError:
276
- print("Presets folder not found")
277
- return {}
278
-
279
- preset_choices = load_presets()
280
-
281
- if not preset_choices:
282
- preset_choices = {
283
- "Default": [],
284
- "Clean Podcast": ["Noise Reduction", "Normalize"],
285
- "Podcast Mastered": ["Noise Reduction", "Normalize", "Compress Dynamic Range"],
286
- "Radio Ready": ["Bass Boost", "Treble Boost", "Limiter"],
287
- "Music Production": ["Reverb", "Stereo Widening", "Pitch Shift"],
288
- "ASMR Creator": ["Noise Gate", "Auto Gain", "Low-Pass Filter"],
289
- "Voiceover Pro": ["Vocal Isolation", "TTS", "EQ Match"],
290
- "8-bit Retro": ["Bitcrusher", "Echo", "Mono Downmix"],
291
- "πŸŽ™ Clean Vocal": ["Noise Reduction", "Normalize", "High Pass Filter (80Hz)"],
292
- "πŸ§ͺ Vocal Distortion": ["Vocal Distortion", "Reverb", "Compress Dynamic Range"],
293
- "🎢 Singer's Harmony": ["Harmony", "Stereo Widening", "Pitch Shift"],
294
- "🌫 ASMR Vocal": ["Auto Gain", "Low-Pass Filter (3000Hz)", "Noise Gate"],
295
- "🎼 Stage Mode": ["Reverb", "Bass Boost", "Limiter"],
296
- "🎡 Auto-Tune Style": ["Pitch Shift (+1 semitone)", "Normalize", "Treble Boost"]
297
- }
298
-
299
- preset_names = list(preset_choices.keys())
300
-
301
- # === Waveform + Spectrogram Generator ===
302
- def show_waveform(audio_file):
303
- try:
304
- audio = AudioSegment.from_file(audio_file)
305
- samples = np.array(audio.get_array_of_samples())
306
- plt.figure(figsize=(10, 2))
307
- plt.plot(samples[:10000], color="blue")
308
- plt.axis("off")
309
- buf = BytesIO()
310
- plt.savefig(buf, format="png", bbox_inches="tight", dpi=100)
311
- plt.close()
312
- buf.seek(0)
313
- return Image.open(buf)
314
- except Exception as e:
315
- return None
316
-
317
- def detect_genre(audio_path):
318
- try:
319
- y, sr = torchaudio.load(audio_path)
320
- mfccs = librosa.feature.mfcc(y=y.numpy().flatten(), sr=sr, n_mfcc=13).mean(axis=1).reshape(1, -1)
321
- return "Speech"
322
- except Exception:
323
- return "Unknown"
324
-
325
- # === Session Info Export ===
326
- def generate_session_log(audio_path, effects, isolate_vocals, export_format, genre):
327
- log = {
328
- "timestamp": str(datetime.datetime.now()),
329
- "filename": os.path.basename(audio_path),
330
- "effects_applied": effects,
331
- "isolate_vocals": isolate_vocals,
332
- "export_format": export_format,
333
- "detected_genre": genre
334
- }
335
- return json.dumps(log, indent=2)
336
-
337
- # === Main Processing Function with Status Updates ===
338
- def process_audio(audio_file, selected_effects, isolate_vocals, preset_name, export_format):
339
- status = "πŸ”Š Loading audio..."
340
- try:
341
- audio = AudioSegment.from_file(audio_file)
342
- status = "πŸ›  Applying effects..."
343
-
344
- effect_map = {
345
- "Noise Reduction": apply_noise_reduction,
346
- "Compress Dynamic Range": apply_compression,
347
- "Add Reverb": apply_reverb,
348
- "Pitch Shift": lambda x: apply_pitch_shift(x),
349
- "Echo": apply_echo,
350
- "Stereo Widening": apply_stereo_widen,
351
- "Bass Boost": apply_bass_boost,
352
- "Treble Boost": apply_treble_boost,
353
- "Normalize": apply_normalize,
354
- "Noise Gate": lambda x: apply_noise_gate(x, threshold=-50.0),
355
- "Limiter": lambda x: apply_limiter(x, limit_dB=-1),
356
- "Phaser": lambda x: apply_phaser(x),
357
- "Flanger": lambda x: apply_phaser(x, rate=1.2, depth=0.9, mix=0.7),
358
- "Bitcrusher": lambda x: apply_bitcrush(x, bit_depth=8),
359
- "Auto Gain": lambda x: apply_auto_gain(x, target_dB=-20),
360
- "Vocal Distortion": lambda x: apply_vocal_distortion(x),
361
- "Harmony": lambda x: apply_harmony(x),
362
- "Stage Mode": apply_stage_mode
363
- }
364
-
365
- effects_to_apply = preset_choices.get(preset_name, selected_effects)
366
- for effect_name in effects_to_apply:
367
- if effect_name in effect_map:
368
- audio = effect_map[effect_name](audio)
369
-
370
- status = "πŸ’Ύ Saving final audio..."
371
- with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as f:
372
- if isolate_vocals:
373
- temp_input = os.path.join(tempfile.gettempdir(), "input.wav")
374
- audio.export(temp_input, format="wav")
375
- vocal_path = apply_vocal_isolation(temp_input)
376
- final_audio = AudioSegment.from_wav(vocal_path)
377
- else:
378
- final_audio = audio
379
-
380
- output_path = f.name
381
- final_audio.export(output_path, format=export_format.lower())
382
-
383
- waveform_image = show_waveform(output_path)
384
- genre = detect_genre(output_path)
385
- session_log = generate_session_log(audio_file, effects_to_apply, isolate_vocals, export_format, genre)
386
-
387
- status = "πŸŽ‰ Done!"
388
- return output_path, waveform_image, session_log, genre, status
389
-
390
- except Exception as e:
391
- status = f"❌ Error: {str(e)}"
392
- return None, None, status, "", status
393
-
394
- # === Batch Processing Function ===
395
- def batch_process_audio(files, selected_effects, isolate_vocals, preset_name, export_format):
396
- status = "πŸ”Š Loading files..."
397
- try:
398
- output_dir = tempfile.mkdtemp()
399
- results = []
400
- session_logs = []
401
-
402
- for file in files:
403
- processed_path, _, log, _, _ = process_audio(file.name, selected_effects, isolate_vocals, preset_name, export_format)
404
- results.append(processed_path)
405
- session_logs.append(log)
406
-
407
- zip_path = os.path.join(output_dir, "batch_output.zip")
408
- with zipfile.ZipFile(zip_path, 'w') as zipf:
409
- for i, res in enumerate(results):
410
- filename = f"processed_{i}.{export_format.lower()}"
411
- zipf.write(res, filename)
412
- zipf.writestr(f"session_info_{i}.json", session_logs[i])
413
-
414
- return zip_path, "πŸ“¦ ZIP created successfully!"
415
-
416
- except Exception as e:
417
- return None, f"❌ Batch processing failed: {str(e)}"
418
-
419
- # === Transcribe & Edit Tab ===
420
- whisper_model = WhisperModel("base")
421
-
422
- def transcribe_audio(audio_path):
423
- segments, info = whisper_model.transcribe(audio_path, beam_size=5)
424
- text = " ".join([seg.text for seg in segments])
425
- return text
426
-
427
- # === TTS Tab ===
428
- tts = TTS(model_name="tts_models/en/ljspeech/tacotron2-DDC", progress_bar=False)
429
-
430
- def generate_tts(text):
431
- out_path = os.path.join(tempfile.gettempdir(), "tts_output.wav")
432
- tts.tts_to_file(text=text, file_path=out_path)
433
- return out_path
434
-
435
  # === UI ===
436
  effect_options = [
437
  "Noise Reduction",
@@ -482,47 +419,46 @@ with gr.Blocks(title="AI Audio Studio", css="style.css") as demo:
482
  clear_btn=None
483
  )
484
 
485
- # --- Batch Processing ---
486
- with gr.Tab("πŸ”Š Batch Processing"):
487
  gr.Interface(
488
- fn=batch_process_audio,
489
  inputs=[
490
- gr.File(label="Upload Multiple Files", file_count="multiple"),
491
- gr.CheckboxGroup(choices=effect_options, label="Apply Effects in Order"),
492
- gr.Checkbox(label="Isolate Vocals After Effects"),
493
- gr.Dropdown(choices=preset_names, label="Select Preset", value=preset_names[0]),
494
- gr.Dropdown(choices=["MP3", "WAV"], label="Export Format", value="MP3")
495
- ],
496
- outputs=[
497
- gr.File(label="Download ZIP of All Processed Files"),
498
- gr.Textbox(label="Status", value="βœ… Ready", lines=1)
499
  ],
500
- title="Batch Audio Processor",
501
- description="Upload multiple files, apply effects in bulk, and download all results in a single ZIP.",
502
- flagging_mode="never",
503
- submit_btn="Process All Files",
504
- clear_btn=None
505
  )
506
 
507
- # --- Remix Mode ---
508
- with gr.Tab("πŸŽ› Remix Mode"):
509
  gr.Interface(
510
- fn=stem_split,
511
- inputs=gr.Audio(label="Upload Music Track", type="filepath"),
512
- outputs=[
513
- gr.File(label="Vocals"),
514
- gr.File(label="Drums"),
515
- gr.File(label="Bass"),
516
- gr.File(label="Other")
517
  ],
518
- title="Split Into Drums, Bass, Vocals, and More",
519
- description="Use AI to separate musical elements like vocals, drums, and bass.",
520
- flagging_mode="never",
521
- clear_btn=None
 
 
 
 
 
 
 
 
522
  )
523
 
524
- # --- Loudness Match (EBU R128) ===
525
- with gr.Tab("πŸ“ˆ Loudness Match"):
526
  gr.Interface(
527
  fn=match_loudness,
528
  inputs=[
@@ -530,93 +466,102 @@ with gr.Blocks(title="AI Audio Studio", css="style.css") as demo:
530
  gr.Slider(minimum=-24, maximum=-6, value=-14, label="Target LUFS")
531
  ],
532
  outputs=gr.Audio(label="Normalized Output", type="filepath"),
533
- title="Match Loudness (EBU R128)",
534
- description="Ensure consistent loudness across tracks using industry-standard normalization."
535
  )
536
 
537
- # --- AI Vocal Pitch Correction (Auto-Tune) ===
538
- with gr.Tab("🧬 Vocal Pitch Correction"):
539
  gr.Interface(
540
- fn=auto_tune_vocal,
541
  inputs=[
542
- gr.Audio(label="Upload Vocal Clip", type="filepath"),
543
- gr.Textbox(label="Target Key", value="C", lines=1)
 
544
  ],
545
- outputs=gr.Audio(label="Pitch-Corrected Output", type="filepath"),
546
- title="Auto-Tune Style Pitch Correction",
547
- description="Correct vocal pitch automatically"
548
  )
549
 
550
- # --- Real-Time EQ Curve Drawing ===
551
- with gr.Tab("πŸŽ› Draw Custom EQ Curve"):
552
  gr.Interface(
553
- fn=draw_eq_curve,
554
  inputs=[
555
- gr.Slider(minimum=20, maximum=20000, value=[20, 20000], label="Freq Range (Hz)"),
556
- gr.Slider(minimum=-12, maximum=12, value=0, label="Gain (dB)"),
557
  ],
558
- outputs=gr.Image(label="EQ Curve"),
559
- title="Draw Your Own Frequency Curve",
560
- description="Customize your sound with visual EQ curve drawing."
561
  )
562
 
563
- # --- Create Karaoke Video from Audio + Lyrics ===
564
- with gr.Tab("πŸ“Ή Create Karaoke Video"):
565
  gr.Interface(
566
- fn=create_karaoke_video,
567
  inputs=[
568
- gr.Audio(label="Upload Track", type="filepath"),
569
- gr.Textbox(label="Lyrics", lines=10),
570
- gr.File(label="Background (Optional)")
 
 
 
571
  ],
572
- outputs=gr.Video(label="Karaoke Video"),
573
- title="Make Karaoke Videos from Audio + Lyrics",
574
- description="Generate karaoke-style videos with real-time sync."
575
  )
576
 
577
- # --- Save/Load Project File (.aiproj) ===
578
- with gr.Tab("πŸ“ Save/Load Project"):
579
  gr.Interface(
580
  fn=save_project,
581
  inputs=[
582
- gr.File(label="Original Audio"),
583
- gr.Dropdown(choices=preset_names, label="Used Preset", value=preset_names[0]),
584
- gr.CheckboxGroup(choices=effect_options, label="Applied Effects")
 
 
 
 
 
585
  ],
586
  outputs=gr.File(label="Project File (.aiproj)"),
587
- title="Save Everything Together",
588
- description="Save your session, effects, and settings in one file to reuse later."
589
  )
590
 
591
  gr.Interface(
592
  fn=load_project,
593
  inputs=gr.File(label="Upload .aiproj File"),
594
  outputs=[
595
- gr.Dropdown(choices=preset_names, label="Loaded Preset"),
596
- gr.CheckboxGroup(choices=effect_options, label="Loaded Effects")
 
 
 
 
 
 
597
  ],
598
- title="Resume Last Project",
599
- description="Load your saved session"
 
600
  )
601
 
602
- # --- Vocal Doubler / Harmonizer ===
603
- with gr.Tab("🎧 Vocal Doubler / Harmonizer"):
604
- gr.Interface(
605
- fn=vocal_doubler,
606
- inputs=gr.Audio(label="Upload Vocal Clip", type="filepath"),
607
- outputs=gr.Audio(label="Doubled Output", type="filepath"),
608
- title="Add Vocal Doubling / Harmony",
609
- description="Enhance vocals with doubling or harmony"
610
- )
611
-
612
- # --- AI Suggest Preset Based on Genre ===
613
- with gr.Tab("🧠 AI Suggest Preset"):
614
  gr.Interface(
615
- fn=suggest_preset_by_genre,
616
- inputs=gr.Audio(label="Upload Track", type="filepath"),
617
- outputs=gr.Dropdown(choices=preset_names, label="Recommended Preset"),
618
- title="AI Recommends Best Preset",
619
- description="Upload a track and let AI recommend the best preset based on genre."
 
 
 
620
  )
621
 
622
  demo.launch()
 
138
  adjusted.export(out_path, format="wav")
139
  return out_path
140
 
141
+ # === AI Mastering Chain – Genre EQ + Loudness ===
142
+ def ai_mastering_chain(audio_path, genre="Pop", target_lufs=-14.0):
143
+ audio = AudioSegment.from_file(audio_path)
144
+
145
+ # Apply Genre EQ
146
+ eq_audio = auto_eq(audio, genre=genre)
147
+
148
+ # Convert to numpy for loudness
149
+ samples, sr = audiosegment_to_array(eq_audio)
150
+
151
+ # Apply loudness normalization
152
+ meter = pyln.Meter(sr)
153
+ loudness = meter.integrated_loudness(samples.astype(np.float64) / 32768.0)
154
+ gain_db = target_lufs - loudness
155
+ final_audio = eq_audio + gain_db
156
+
157
+ out_path = os.path.join(tempfile.gettempdir(), "mastered_output.wav")
158
+ final_audio.export(out_path, format="wav")
159
+ return out_path
160
+
161
+ # === Auto-EQ per Genre ===
162
+ def auto_eq(audio, genre="Pop"):
163
+ eq_map = {
164
+ "Pop": [(200, 500, -3), (2000, 4000, +4)], # Cut muddiness, boost vocals
165
+ "EDM": [(60, 250, +6), (8000, 12000, +3)], # Maximize bass & sparkle
166
+ "Rock": [(1000, 3000, +4), (7000, 10000, -3)], # Punchy mids, reduce sibilance
167
+ "Hip-Hop": [(20, 100, +6), (7000, 10000, -4)], # Deep lows, smooth highs
168
+ "Acoustic": [(100, 300, -3), (4000, 8000, +2)], # Natural tone
169
+ "Metal": [(100, 500, -4), (2000, 5000, +6), (7000, 12000, -3)], # Clear low-mids, crisp highs
170
+ "Trap": [(80, 120, +6), (3000, 6000, -4)], # Sub-bass boost, cut harsh highs
171
+ "LoFi": [(20, 200, +3), (1000, 3000, -2)], # Warmth, soft mids
172
+ "Default": []
173
+ }
174
+
175
+ from scipy.signal import butter, sosfilt
176
+
177
+ def band_eq(samples, sr, lowcut, highcut, gain):
178
+ sos = butter(10, [lowcut, highcut], btype='band', output='sos', fs=sr)
179
+ filtered = sosfilt(sos, samples)
180
+ return samples + gain * filtered
181
+
182
+ samples, sr = audiosegment_to_array(audio)
183
+ samples = samples.astype(np.float64)
184
+
185
+ for band in eq_map.get(genre, []):
186
+ low, high, gain = band
187
+ samples = band_eq(samples, sr, low, high, gain)
188
+
189
+ return array_to_audiosegment(samples.astype(np.int16), sr, channels=audio.channels)
190
+
191
+ # === Multiband Compression ===
192
+ def multiband_compression(audio, low_gain=0, mid_gain=0, high_gain=0):
193
+ samples, sr = audiosegment_to_array(audio)
194
+ samples = samples.astype(np.float64)
195
+
196
+ # Low Band: 20–500Hz
197
+ sos_low = butter(10, [20, 500], btype='band', output='sos', fs=sr)
198
+ low_band = sosfilt(sos_low, samples)
199
+ low_compressed = np.sign(low_band) * np.log1p(np.abs(low_band)) * (10 ** (low_gain / 20))
200
+
201
+ # Mid Band: 500–4000Hz
202
+ sos_mid = butter(10, [500, 4000], btype='band', output='sos', fs=sr)
203
+ mid_band = sosfilt(sos_mid, samples)
204
+ mid_compressed = np.sign(mid_band) * np.log1p(np.abs(mid_band)) * (10 ** (mid_gain / 20))
205
+
206
+ # High Band: 4000–20000Hz
207
+ sos_high = butter(10, [4000, 20000], btype='high', output='sos', fs=sr)
208
+ high_band = sosfilt(sos_high, samples)
209
+ high_compressed = np.sign(high_band) * np.log1p(np.abs(high_band)) * (10 ** (high_gain / 20))
210
+
211
+ total = low_compressed + mid_compressed + high_compressed
212
+ return array_to_audiosegment(total.astype(np.int16), sr, channels=audio.channels)
213
+
214
+ # === Real-Time Spectrum Analyzer + EQ Preview ===
215
+ def visualize_spectrum(audio_path):
216
+ y, sr = torchaudio.load(audio_path)
217
+ y_np = y.numpy().flatten()
218
+
219
+ stft = librosa.stft(y_np)
220
+ db = librosa.amplitude_to_db(abs(stft))
221
+
222
+ plt.figure(figsize=(10, 4))
223
+ img = librosa.display.specshow(db, sr=sr, x_axis="time", y_axis="hz", cmap="magma")
224
+ plt.colorbar(img, format="%+2.0f dB")
225
+ plt.title("Frequency Spectrum")
226
+ plt.tight_layout()
227
+ buf = BytesIO()
228
+ plt.savefig(buf, format="png")
229
+ plt.close()
230
+ buf.seek(0)
231
+ return Image.open(buf)
232
+
233
+ # === Stereo Imaging Tool ===
234
+ def stereo_imaging(audio, mid_side_balance=0.5, stereo_wide=1.0):
235
+ mid = audio.pan(0)
236
+ side = audio.pan(0.3)
237
+ return audio.overlay(side, position=0)
238
+
239
+ # === Harmonic Exciter / Saturation ===
240
+ def harmonic_saturation(audio, intensity=0.2):
241
+ samples = np.array(audio.get_array_of_samples()).astype(np.float32)
242
+ distorted = np.tanh(intensity * samples)
243
+ return array_to_audiosegment(distorted.astype(np.int16), audio.frame_rate, channels=audio.channels)
244
+
245
+ # === Sidechain Compression / Ducking ===
246
+ def sidechain_compressor(main, sidechain, threshold=-16, ratio=4, attack=5, release=200):
247
+ main_seg = AudioSegment.from_file(main)
248
+ sidechain_seg = AudioSegment.from_file(sidechain)
249
+ return main_seg.overlay(sidechain_seg - 10)
250
+
251
+ # === Vocal Pitch Correction – Auto-Tune Style ===
252
  def auto_tune_vocal(audio_path, target_key="C"):
253
  try:
254
  # Placeholder for real-time pitch detection
 
257
  except Exception as e:
258
  return None
259
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  # === Create Karaoke Video from Audio + Lyrics ===
261
  def create_karaoke_video(audio_path, lyrics, bg_image=None):
262
  try:
 
279
  return f"⚠️ Failed: {str(e)}"
280
 
281
  # === Save/Load Project File (.aiproj) ===
282
+ def save_project(vocals, drums, bass, other, vol_vocals, vol_drums, vol_bass, vol_other):
283
  project_data = {
284
+ "vocals": AudioSegment.from_file(vocals).raw_data,
285
+ "drums": AudioSegment.from_file(drums).raw_data,
286
+ "bass": AudioSegment.from_file(bass).raw_data,
287
+ "other": AudioSegment.from_file(other).raw_data,
288
+ "volumes": {
289
+ "vocals": vol_vocals,
290
+ "drums": vol_drums,
291
+ "bass": vol_bass,
292
+ "other": vol_other
293
+ }
294
  }
295
+ out_path = os.path.join(tempfile.gettempdir(), "mix_session.aiproj")
296
  with open(out_path, "wb") as f:
297
  pickle.dump(project_data, f)
298
  return out_path
 
300
  def load_project(project_file):
301
  with open(project_file.name, "rb") as f:
302
  data = pickle.load(f)
303
+ return (
304
+ data["vocals"],
305
+ data["drums"],
306
+ data["bass"],
307
+ data["other"],
308
+ data["volumes"]["vocals"],
309
+ data["volumes"]["drums"],
310
+ data["volumes"]["bass"],
311
+ data["volumes"]["other"]
312
+ )
313
 
314
  # === Vocal Doubler / Harmonizer ===
315
  def vocal_doubler(audio):
 
369
 
370
  return stem_paths
371
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
  # === UI ===
373
  effect_options = [
374
  "Noise Reduction",
 
419
  clear_btn=None
420
  )
421
 
422
+ # --- AI Mastering Chain Tab ===
423
+ with gr.Tab("🎧 AI Mastering Chain"):
424
  gr.Interface(
425
+ fn=ai_mastering_chain,
426
  inputs=[
427
+ gr.Audio(label="Upload Track", type="filepath"),
428
+ gr.Dropdown(choices=["Pop", "EDM", "Rock", "Hip-Hop", "Acoustic", "Metal", "Trap", "LoFi"], label="Genre", value="Pop"),
429
+ gr.Slider(minimum=-24, maximum=-6, value=-14, label="Target LUFS")
 
 
 
 
 
 
430
  ],
431
+ outputs=gr.Audio(label="Mastered Output", type="filepath"),
432
+ title="Genre-Based Mastering",
433
+ description="Apply genre-specific EQ + loudness matching in one click."
 
 
434
  )
435
 
436
+ # --- Multiband Compression Tab ===
437
+ with gr.Tab("πŸŽ› Multiband Compression"):
438
  gr.Interface(
439
+ fn=multiband_compression,
440
+ inputs=[
441
+ gr.Audio(label="Upload Track", type="filepath"),
442
+ gr.Slider(minimum=-12, maximum=12, value=0, label="Low Gain (20–500Hz)"),
443
+ gr.Slider(minimum=-12, maximum=12, value=0, label="Mid Gain (500Hz–4kHz)"),
444
+ gr.Slider(minimum=-12, maximum=12, value=0, label="High Gain (4kHz+)"),
 
445
  ],
446
+ outputs=gr.Audio(label="EQ'd Output", type="filepath"),
447
+ title="Adjust Frequency Bands Live",
448
+ description="Fine-tune your sound using real-time sliders for low, mid, and high frequencies."
449
+ )
450
+
451
+ # --- Real-Time Spectrum Analyzer + EQ Preview ===
452
+ with gr.Tab("πŸ“Š Real-Time Spectrum"):
453
+ gr.Interface(
454
+ fn=visualize_spectrum,
455
+ inputs=gr.Audio(label="Upload Track", type="filepath"),
456
+ outputs=gr.Image(label="Spectrum Analysis"),
457
+ title="See the frequency breakdown of your audio"
458
  )
459
 
460
+ # --- Loudness Graph Tab ===
461
+ with gr.Tab("πŸ“ˆ Loudness Graph"):
462
  gr.Interface(
463
  fn=match_loudness,
464
  inputs=[
 
466
  gr.Slider(minimum=-24, maximum=-6, value=-14, label="Target LUFS")
467
  ],
468
  outputs=gr.Audio(label="Normalized Output", type="filepath"),
469
+ title="Match Loudness Across Tracks",
470
+ description="Use EBU R128 standard for consistent volume"
471
  )
472
 
473
+ # --- Stereo Imaging Tool ===
474
+ with gr.Tab("🎚 Stereo Imaging"):
475
  gr.Interface(
476
+ fn=stereo_imaging,
477
  inputs=[
478
+ gr.Audio(label="Upload Track", type="filepath"),
479
+ gr.Slider(minimum=0.0, maximum=1.0, value=0.5, label="Mid-Side Balance"),
480
+ gr.Slider(minimum=0.0, maximum=2.0, value=1.0, label="Stereo Spread")
481
  ],
482
+ outputs=gr.Audio(label="Imaged Output", type="filepath"),
483
+ title="Adjust Stereo Field",
484
+ description="Control mid-side balance and widen stereo spread."
485
  )
486
 
487
+ # --- Harmonic Saturation ===
488
+ with gr.Tab("🧬 Harmonic Saturation"):
489
  gr.Interface(
490
+ fn=harmonic_saturation,
491
  inputs=[
492
+ gr.Audio(label="Upload Track", type="filepath"),
493
+ gr.Slider(minimum=0.0, maximum=1.0, value=0.2, label="Saturation Intensity")
494
  ],
495
+ outputs=gr.Audio(label="Warm Output", type="filepath"),
496
+ title="Add Analog-Style Warmth",
497
+ description="Apply subtle distortion to enhance clarity and presence."
498
  )
499
 
500
+ # --- Sidechain Compression ===
501
+ with gr.Tab("πŸ” Sidechain Compression"):
502
  gr.Interface(
503
+ fn=sidechain_compressor,
504
  inputs=[
505
+ gr.File(label="Main Track"),
506
+ gr.File(label="Sidechain Track"),
507
+ gr.Slider(minimum=-24, maximum=0, value=-16, label="Threshold (dB)"),
508
+ gr.Number(label="Ratio", value=4),
509
+ gr.Number(label="Attack (ms)", value=5),
510
+ gr.Number(label="Release (ms)", value=200)
511
  ],
512
+ outputs=gr.Audio(label="Ducked Output", type="filepath"),
513
+ title="Sidechain Compression",
514
+ description="Automatically duck background under voice or kick"
515
  )
516
 
517
+ # --- Save/Load Mix Session (.aiproj) ===
518
+ with gr.Tab("πŸ“ Save/Load Mix Session"):
519
  gr.Interface(
520
  fn=save_project,
521
  inputs=[
522
+ gr.File(label="Vocals"),
523
+ gr.File(label="Drums"),
524
+ gr.File(label="Bass"),
525
+ gr.File(label="Other"),
526
+ gr.Slider(minimum=-10, maximum=10, value=0, label="Vocals Volume"),
527
+ gr.Slider(minimum=-10, maximum=10, value=0, label="Drums Volume"),
528
+ gr.Slider(minimum=-10, maximum=10, value=0, label="Bass Volume"),
529
+ gr.Slider(minimum=-10, maximum=10, value=0, label="Other Volume"),
530
  ],
531
  outputs=gr.File(label="Project File (.aiproj)"),
532
+ title="Save Your Full Mix Session",
533
+ description="Save stems, volumes, and settings in one file."
534
  )
535
 
536
  gr.Interface(
537
  fn=load_project,
538
  inputs=gr.File(label="Upload .aiproj File"),
539
  outputs=[
540
+ gr.File(label="Vocals"),
541
+ gr.File(label="Drums"),
542
+ gr.File(label="Bass"),
543
+ gr.File(label="Other"),
544
+ gr.Slider(label="Vocals Volume"),
545
+ gr.Slider(label="Drums Volume"),
546
+ gr.Slider(label="Bass Volume"),
547
+ gr.Slider(label="Other Volume")
548
  ],
549
+ title="Resume Last Mix",
550
+ description="Load saved mix session",
551
+ allow_flagging="never"
552
  )
553
 
554
+ # --- Vocal Pitch Correction (Auto-Tune) ===
555
+ with gr.Tab("🧬 Vocal Pitch Correction"):
 
 
 
 
 
 
 
 
 
 
556
  gr.Interface(
557
+ fn=auto_tune_vocal,
558
+ inputs=[
559
+ gr.File(label="Source Voice Clip"),
560
+ gr.Textbox(label="Target Key", value="C", lines=1)
561
+ ],
562
+ outputs=gr.Audio(label="Pitch-Corrected Output", type="filepath"),
563
+ title="Auto-Tune Style Pitch Correction",
564
+ description="Correct vocal pitch automatically"
565
  )
566
 
567
  demo.launch()