Phoenixak99 commited on
Commit
c1cc3ad
Β·
verified Β·
1 Parent(s): 9dc1144

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +680 -0
app.py ADDED
@@ -0,0 +1,680 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import base64
3
+ from datetime import datetime
4
+ from io import BytesIO
5
+ import numpy as np
6
+ import requests
7
+ import streamlit as st
8
+ import streamlit_vertical_slider as svs
9
+ from pydub import AudioSegment
10
+ from streamlit import session_state as st_state
11
+
12
+ # Add CSS to block download buttons in audio players
13
+ st.markdown("""
14
+ <style>
15
+ /* Hide download button in all audio players - comprehensive targeting */
16
+ audio::-webkit-media-controls-panel button[aria-label="Download"],
17
+ audio::-webkit-media-controls-enclosure button[aria-label="Download"],
18
+ audio::-internal-media-controls-download-button,
19
+ audio::-webkit-media-controls-download-button,
20
+ audio::-webkit-media-controls-overflow-button,
21
+ audio::-webkit-media-controls-mute-button ~ button,
22
+ audio::-webkit-media-controls-panel button:last-child,
23
+ audio::-webkit-media-controls-panel menu,
24
+ audio::-webkit-media-controls-panel menuitem[aria-label*="Download"] {
25
+ display: none !important;
26
+ opacity: 0 !important;
27
+ pointer-events: none !important;
28
+ visibility: hidden !important;
29
+ width: 0 !important;
30
+ height: 0 !important;
31
+ position: absolute !important;
32
+ z-index: -1000 !important;
33
+ clip: rect(0 0 0 0) !important;
34
+ clip-path: inset(50%) !important;
35
+ }
36
+
37
+ /* Make the audio player background opaque to hide any potential UI elements */
38
+ audio::-webkit-media-controls-panel {
39
+ background-color: rgba(35, 35, 35, 0.85) !important;
40
+ }
41
+
42
+ /* Prevent download through context menu but allow volume and play control */
43
+ audio {
44
+ -webkit-user-select: none !important;
45
+ user-select: none !important;
46
+ -webkit-touch-callout: none !important;
47
+ }
48
+ </style>
49
+ """, unsafe_allow_html=True)
50
+
51
+ # Add a background image
52
+ page_bg_img = '''
53
+ <style>
54
+ .stApp {
55
+ background-image: url("https://songlabai.com/wp-content/uploads/2024/03/4.png");
56
+ background-size: cover;
57
+ }
58
+ </style>
59
+ '''
60
+ st.markdown(page_bg_img, unsafe_allow_html=True)
61
+
62
+ # Initialize session state variables for audio processing
63
+ if "vocal_audio" not in st_state:
64
+ st_state.vocal_audio = None
65
+ if "vocal_sample_rate" not in st_state:
66
+ st_state.vocal_sample_rate = None
67
+ if "audio" not in st_state:
68
+ st_state.audio = None
69
+ if "audio_pydub" not in st_state:
70
+ st_state.audio_pydub = None
71
+ if "audio_sample_rate" not in st_state:
72
+ st_state.audio_sample_rate = None
73
+ if "augmented_audio" not in st_state:
74
+ st_state.augmented_audio = None
75
+ if "augmented_audio_pydub" not in st_state:
76
+ st_state.augmented_audio_pydub = None
77
+ if "augmented_audio_sample_rate" not in st_state:
78
+ st_state.augmented_audio_sample_rate = None
79
+ if 'audio_bytes' not in st.session_state:
80
+ st.session_state['audio_bytes'] = None
81
+ if 'playing_state' not in st.session_state:
82
+ st.session_state['playing_state'] = 'none' # 'none', 'generated', 'processed'
83
+ if 'audio_metadata' not in st.session_state:
84
+ st.session_state['audio_metadata'] = None
85
+
86
+ def secure_audio_player(audio_bytes, format="audio/wav"):
87
+ """
88
+ Enhanced secure audio player that completely blocks downloads
89
+
90
+ Parameters:
91
+ audio_bytes: Audio data as bytes
92
+ format: Audio format mimetype
93
+ """
94
+ if audio_bytes is None:
95
+ return
96
+
97
+ # Encode to base64
98
+ b64 = base64.b64encode(audio_bytes).decode()
99
+
100
+ # Custom HTML with additional JavaScript security
101
+ custom_html = f"""
102
+ <div style="pointer-events: auto;" oncontextmenu="return false;">
103
+ <audio controls
104
+ controlsList="nodownload nofullscreen noremoteplayback"
105
+ disableRemotePlayback
106
+ oncontextmenu="return false;"
107
+ src="data:{format};base64,{b64}">
108
+ </audio>
109
+ </div>
110
+ <script>
111
+ // Additional JavaScript protection
112
+ document.addEventListener('DOMContentLoaded', function() {{
113
+ const audioElements = document.querySelectorAll('audio');
114
+ audioElements.forEach(audio => {{
115
+ // Prevent context menu
116
+ audio.addEventListener('contextmenu', e => e.preventDefault());
117
+
118
+ // Monitor and disable download attempts
119
+ audio.addEventListener('enterpictureinpicture', e => e.preventDefault());
120
+
121
+ // Remove download option from any menus that might appear
122
+ const observer = new MutationObserver(mutations => {{
123
+ const menus = document.querySelectorAll('menu, menuitem');
124
+ menus.forEach(menu => {{
125
+ if (menu.textContent.toLowerCase().includes('download')) {{
126
+ menu.remove();
127
+ }}
128
+ }});
129
+ }});
130
+
131
+ observer.observe(document.body, {{
132
+ childList: true,
133
+ subtree: true
134
+ }});
135
+ }});
136
+ </script>
137
+ """
138
+
139
+ st.markdown(custom_html, unsafe_allow_html=True)
140
+
141
+ def convert_audio_segment_to_float_array(audio_pydub):
142
+ """
143
+ Convert a pydub AudioSegment to a NumPy array of type float32.
144
+
145
+ Args:
146
+ audio_pydub (AudioSegment): The AudioSegment object to be converted.
147
+
148
+ Returns:
149
+ np.ndarray: A NumPy array containing the audio data as float32.
150
+ """
151
+ # Get the raw audio data as a sequence of samples
152
+ samples = audio_pydub.get_array_of_samples()
153
+
154
+ # Convert the samples to a NumPy array and normalize to float32
155
+ audio_array = np.array(samples).astype(np.float32)
156
+
157
+ # Normalize the audio array to range between -1.0 and 1.0
158
+ max_val = 2**15 # Assuming 16-bit audio, modify this if using different bit depths
159
+ audio_array /= max_val
160
+ return audio_array
161
+
162
+ def get_wordpress_beats():
163
+ """
164
+ Get beats from WordPress using the custom plugin endpoint
165
+
166
+ Returns:
167
+ list: List of beat dictionaries with id, title, url, etc.
168
+ """
169
+ # Use the custom plugin's REST API endpoint
170
+ api_url = "https://songlabai.com/wp-json/songlab/v1/beats"
171
+
172
+ try:
173
+ response = requests.get(api_url)
174
+ print(f"WordPress API response status: {response.status_code}")
175
+
176
+ if response.status_code == 200:
177
+ beats = response.json()
178
+ print(f"Found {len(beats)} beats in WordPress")
179
+ return beats
180
+ else:
181
+ print(f"Error fetching beats: {response.status_code}")
182
+ return []
183
+
184
+ except Exception as e:
185
+ print(f"Exception in get_wordpress_beats: {str(e)}")
186
+ return []
187
+
188
+ def download_wordpress_beat(beat_url):
189
+ """
190
+ Download a beat from WordPress
191
+
192
+ Parameters:
193
+ beat_url: URL of the beat file
194
+
195
+ Returns:
196
+ BytesIO: Beat data as BytesIO object or None if failed
197
+ """
198
+ try:
199
+ response = requests.get(beat_url, stream=True)
200
+ if response.status_code == 200:
201
+ return BytesIO(response.content)
202
+ else:
203
+ print(f"Error downloading beat: {response.status_code}")
204
+ return None
205
+ except Exception as e:
206
+ print(f"Exception in download_wordpress_beat: {str(e)}")
207
+ return None
208
+
209
+ def load_wordpress_beat_for_processing(beat_url, beat_name):
210
+ """
211
+ Download and load a WordPress beat for processing
212
+
213
+ Parameters:
214
+ beat_url: URL of the beat file
215
+ beat_name: Name of the beat
216
+
217
+ Returns:
218
+ tuple: (success, message)
219
+ """
220
+ try:
221
+ print(f"Downloading beat from: {beat_url}")
222
+
223
+ # Download the beat
224
+ beat_buffer = download_wordpress_beat(beat_url)
225
+ if not beat_buffer:
226
+ return False, "Failed to download beat"
227
+
228
+ # Get file extension from beat URL
229
+ file_extension = beat_url.split('.')[-1].lower() if '.' in beat_url else "mp3"
230
+
231
+ # Load into AudioSegment
232
+ beat_segment = AudioSegment.from_file(beat_buffer, format=file_extension)
233
+
234
+ print(f"Loaded beat: {beat_name}, duration: {len(beat_segment)/1000:.2f}s")
235
+
236
+ # Convert to WAV if needed
237
+ if file_extension != 'wav':
238
+ wav_buffer = BytesIO()
239
+ beat_segment.export(wav_buffer, format='wav')
240
+ wav_buffer.seek(0)
241
+ beat_segment = AudioSegment.from_wav(wav_buffer)
242
+
243
+ # Store in session state
244
+ st_state.audio_pydub = beat_segment
245
+ st_state.audio_sample_rate = beat_segment.frame_rate
246
+ st_state.augmented_audio_pydub = beat_segment
247
+ st_state.augmented_audio_sample_rate = beat_segment.frame_rate
248
+
249
+ # Convert to bytes for player
250
+ wav_bytes = BytesIO()
251
+ beat_segment.export(wav_bytes, format='wav')
252
+ wav_bytes.seek(0)
253
+ st.session_state.audio_bytes = wav_bytes.getvalue()
254
+
255
+ # Set playing state
256
+ st.session_state.playing_state = 'generated'
257
+
258
+ # Set metadata
259
+ st.session_state.audio_metadata = {
260
+ 'genre': 'Beat',
261
+ 'energy_level': 'Unknown',
262
+ 'tempo': 'Unknown',
263
+ 'description': f'Selected beat: {beat_name}',
264
+ 'duration': len(beat_segment)/1000 # Convert ms to seconds
265
+ }
266
+
267
+ return True, "Beat loaded successfully"
268
+ except Exception as e:
269
+ import traceback
270
+ print(f"Error loading beat: {str(e)}")
271
+ print(traceback.format_exc())
272
+ return False, f"Error loading beat: {str(e)}"
273
+
274
+ def display_audio_player(is_final=False):
275
+ """
276
+ Display audio player for either original or processed audio
277
+ """
278
+ if is_final and st_state.augmented_audio_pydub is not None:
279
+ # Display processed audio
280
+ audio_bytes = st_state.augmented_audio_pydub.export(format="wav").read()
281
+ st.markdown("### 🎧 Final Processed Audio")
282
+ secure_audio_player(audio_bytes)
283
+
284
+ # Display download button
285
+ st.download_button(
286
+ label="⬇️ Download Processed Audio",
287
+ data=audio_bytes,
288
+ file_name=f"processed_audio_{datetime.now().strftime('%Y%m%d_%H%M%S')}.wav",
289
+ mime="audio/wav"
290
+ )
291
+
292
+ elif not is_final and st.session_state.get('audio_bytes') is not None:
293
+ st.markdown("### 🎡 Loaded Audio")
294
+ secure_audio_player(st.session_state.audio_bytes)
295
+
296
+ # Display download button for original audio
297
+ st.download_button(
298
+ label="⬇️ Download Original Audio",
299
+ data=st.session_state.audio_bytes,
300
+ file_name=f"original_audio_{datetime.now().strftime('%Y%m%d_%H%M%S')}.wav",
301
+ mime="audio/wav"
302
+ )
303
+
304
+ # Main application code
305
+ st.title("Music Mixer & Processor")
306
+ st.markdown("Load audio, add vocals, and apply professional-quality effects to your tracks")
307
+
308
+ # Add tabs to separate uploaded music and beat selection
309
+ tab1, tab2 = st.tabs(["Upload Your Music", "Choose from Available Beats"])
310
+
311
+ with tab1:
312
+ # File uploader UI
313
+ uploaded_music = st.file_uploader(
314
+ "Upload music file",
315
+ type=["wav", "mp3", "ogg", "flac"],
316
+ help="Upload any music file to process",
317
+ key="user_music_uploader"
318
+ )
319
+
320
+ if uploaded_music:
321
+ # Show a preview player for the uploaded music
322
+ st.audio(uploaded_music, format=f"audio/{uploaded_music.name.split('.')[-1]}")
323
+
324
+ # Process button
325
+ if st.button("Load for Processing", key="load_uploaded_music"):
326
+ try:
327
+ # Read the uploaded music into memory
328
+ with st.spinner("Loading audio..."):
329
+ # Convert the uploaded file to an AudioSegment
330
+ audio_data = uploaded_music.read()
331
+
332
+ # Create a temporary file-like object
333
+ audio_buffer = BytesIO(audio_data)
334
+ audio_buffer.seek(0)
335
+
336
+ # Load into AudioSegment based on file extension
337
+ file_extension = uploaded_music.name.split('.')[-1].lower()
338
+ audio_segment = AudioSegment.from_file(audio_buffer, format=file_extension)
339
+
340
+ # Convert to the same format used in your application
341
+ if file_extension != 'wav':
342
+ wav_buffer = BytesIO()
343
+ audio_segment.export(wav_buffer, format='wav')
344
+ wav_buffer.seek(0)
345
+ audio_segment = AudioSegment.from_wav(wav_buffer)
346
+
347
+ # Store in session state
348
+ st_state.audio_pydub = audio_segment
349
+ st_state.audio_sample_rate = audio_segment.frame_rate
350
+ st_state.augmented_audio_pydub = audio_segment
351
+ st_state.augmented_audio_sample_rate = audio_segment.frame_rate
352
+
353
+ # Convert to bytes for player
354
+ wav_bytes = BytesIO()
355
+ audio_segment.export(wav_bytes, format='wav')
356
+ wav_bytes.seek(0)
357
+ st.session_state.audio_bytes = wav_bytes.getvalue()
358
+
359
+ # Set playing state
360
+ st.session_state.playing_state = 'generated'
361
+
362
+ # Set metadata
363
+ st.session_state.audio_metadata = {
364
+ 'genre': 'Unknown (Uploaded)',
365
+ 'energy_level': 'Unknown',
366
+ 'tempo': 'Unknown',
367
+ 'description': f'Uploaded file: {uploaded_music.name}',
368
+ 'duration': len(audio_segment)/1000 # Convert ms to seconds
369
+ }
370
+
371
+ # Success message
372
+ st.success("πŸŽ‰ Music loaded successfully! Scroll down to use processing tools.")
373
+
374
+ # Force refresh to update the UI with the loaded audio
375
+ st.rerun()
376
+
377
+ except Exception as e:
378
+ st.error(f"❌ Error loading audio file: {str(e)}")
379
+ st.info("Please make sure the file is a valid audio file in one of the supported formats.")
380
+
381
+ with tab2:
382
+ # Show beat selection
383
+ st.markdown("### 🎡 Select a Beat")
384
+
385
+ # Fetch beats from WordPress
386
+ with st.spinner("Loading beats from WordPress..."):
387
+ available_beats = get_wordpress_beats()
388
+
389
+ if not available_beats:
390
+ st.info("No beats are currently available. Please upload your own music or try again later.")
391
+ else:
392
+ # Create a dictionary for easy lookup
393
+ beats_dict = {beat['title']: beat for beat in available_beats}
394
+
395
+ selected_beat_title = st.selectbox(
396
+ "Choose a beat to master and add vocals to",
397
+ options=["-- Select a beat --"] + list(beats_dict.keys()),
398
+ key="user_beat_selection"
399
+ )
400
+
401
+ if selected_beat_title and selected_beat_title != "-- Select a beat --":
402
+ # Get the selected beat info
403
+ selected_beat = beats_dict[selected_beat_title]
404
+
405
+ # Display information about the beat
406
+ st.markdown(f"#### Beat Preview")
407
+ st.markdown(f"**Beat:** {selected_beat_title}")
408
+
409
+ # Show additional metadata if available
410
+ if selected_beat.get('genre') or selected_beat.get('bpm') or selected_beat.get('key'):
411
+ meta_items = []
412
+ if selected_beat.get('genre'):
413
+ meta_items.append(f"Genre: {selected_beat['genre']}")
414
+ if selected_beat.get('bpm'):
415
+ meta_items.append(f"BPM: {selected_beat['bpm']}")
416
+ if selected_beat.get('key'):
417
+ meta_items.append(f"Key: {selected_beat['key']}")
418
+ if selected_beat.get('duration'):
419
+ meta_items.append(f"Duration: {selected_beat['duration']}s")
420
+
421
+ if meta_items:
422
+ st.caption(" | ".join(meta_items))
423
+
424
+ # Get file extension from URL
425
+ file_extension = selected_beat['url'].split('.')[-1].lower() if '.' in selected_beat['url'] else "mp3"
426
+
427
+ # Download and preview the beat
428
+ try:
429
+ with st.spinner("Loading beat preview..."):
430
+ beat_buffer = download_wordpress_beat(selected_beat['url'])
431
+ if beat_buffer:
432
+ # Display audio player for preview
433
+ st.audio(beat_buffer, format=f"audio/{file_extension}")
434
+
435
+ # Add button to load the beat for processing
436
+ if st.button("Load this Beat for Processing", key="load_selected_beat"):
437
+ with st.spinner("Loading beat for processing..."):
438
+ success, message = load_wordpress_beat_for_processing(
439
+ selected_beat['url'],
440
+ selected_beat_title
441
+ )
442
+
443
+ if success:
444
+ st.success("πŸŽ‰ Beat loaded successfully! Scroll down to use processing tools.")
445
+ st.rerun()
446
+ else:
447
+ st.error(f"❌ {message}")
448
+ else:
449
+ st.error("Failed to load beat preview")
450
+ except Exception as e:
451
+ st.error(f"Error previewing beat: {str(e)}")
452
+ import traceback
453
+ print(traceback.format_exc())
454
+
455
+ # Add this helper info section
456
+ with st.expander("ℹ️ About uploading music and using beats"):
457
+ st.markdown("""
458
+ ### Ways to use music in this app
459
+
460
+ You have two options for working with existing music:
461
+
462
+ 1. **Upload Your Own Music**
463
+ - Upload your own audio files to process with our tools
464
+ - Support for WAV, MP3, OGG, and FLAC formats
465
+
466
+ 2. **Use Available Beats**
467
+ - Select from pre-made beats provided by Songlab AI
468
+ - Perfect for adding vocals or creating remixes
469
+
470
+ ### What you can do with the music
471
+
472
+ - **Add vocals** to instrumental tracks
473
+ - **Apply different effects** to experiment with various sounds
474
+ - **Master** the audio with professional-quality tools
475
+ - **Adjust pitch and volume** to perfect your sound
476
+ """)
477
+
478
+ # This divider separates the upload section from post-processing options
479
+ st.divider()
480
+
481
+ # Post-processing options - only show if we have audio to process
482
+ if st_state.audio_pydub is not None:
483
+ st.header("Audio Processing Tools")
484
+
485
+ # Display the original audio
486
+ display_audio_player(is_final=False)
487
+
488
+ # Vocal upload and mixing
489
+ st.subheader("Vocal Processing")
490
+
491
+ vocal_file = st.file_uploader(
492
+ "Upload Vocal File", type=["mp3", "wav", "ogg", "flac", "aac"],
493
+ help="Upload vocals to mix with your instrumental track"
494
+ )
495
+
496
+ if vocal_file:
497
+ # Preview the vocals
498
+ st.audio(vocal_file)
499
+
500
+ try:
501
+ # Load the vocal file
502
+ vocal_data = vocal_file.read()
503
+ file_extension = vocal_file.name.split('.')[-1].lower()
504
+
505
+ # Create buffer and load into AudioSegment
506
+ vocal_buffer = BytesIO(vocal_data)
507
+ vocal_audio = AudioSegment.from_file(vocal_buffer, format=file_extension)
508
+
509
+ # Store in session state
510
+ st_state.vocal_audio = vocal_audio
511
+ st.success("βœ… Vocals loaded successfully")
512
+ except Exception as e:
513
+ st.error(f"Error loading vocal file: {str(e)}")
514
+
515
+ # Mixing options
516
+ if st_state.vocal_audio is not None:
517
+ st.subheader("Vocal Mixing")
518
+
519
+ # Vocal volume
520
+ vocal_volume = st.slider(
521
+ "Vocal Volume",
522
+ min_value=-10,
523
+ max_value=10,
524
+ value=0,
525
+ help="Adjust the volume of the vocals relative to the instrumental"
526
+ )
527
+
528
+ # Vocal position
529
+ vocal_position = st.slider(
530
+ "Vocal Position (ms)",
531
+ min_value=0,
532
+ max_value=5000,
533
+ value=0,
534
+ help="Adjust when the vocals start (in milliseconds)"
535
+ )
536
+
537
+ # Apply vocal mix
538
+ if st.button("Mix Vocals with Instrumental"):
539
+ try:
540
+ # Adjust vocal volume if needed
541
+ adjusted_vocals = st_state.vocal_audio + vocal_volume
542
+
543
+ # Mix vocals at the specified position
544
+ st_state.augmented_audio_pydub = st_state.audio_pydub.overlay(
545
+ adjusted_vocals, position=vocal_position
546
+ )
547
+
548
+ # Update derived variables
549
+ st_state.augmented_audio = convert_audio_segment_to_float_array(
550
+ st_state.augmented_audio_pydub
551
+ )
552
+ st_state.augmented_audio_sample_rate = st_state.augmented_audio_pydub.frame_rate
553
+ st.session_state.playing_state = 'processed'
554
+
555
+ st.success("βœ… Vocals mixed successfully!")
556
+ # We'll display the processed audio later
557
+ except Exception as e:
558
+ st.error(f"Error mixing vocals: {str(e)}")
559
+
560
+ # Audio Effects
561
+ st.subheader("Audio Effects & Mastering")
562
+
563
+ # Volume Balance, Pitch Shift, and Effect Buttons
564
+ vol_col, pitch_col, buttons_col = st.columns([2, 2, 2.5])
565
+
566
+ with buttons_col:
567
+ with st.container(height=371, border=True):
568
+ st.markdown("### Effect Controls")
569
+ apply_stereo = st.button("Apply Stereo Effect",
570
+ help="Creates a stereo widening effect")
571
+
572
+ reverse = st.button("Apply Audio Reverse",
573
+ help="Reverses the audio track")
574
+
575
+ # Low-pass filter
576
+ low_pass = st.button("Apply Low-Pass Filter",
577
+ help="Reduces high frequencies, creating a warmer sound")
578
+
579
+ # Add compression button
580
+ compress = st.button("Apply Compression",
581
+ help="Reduces dynamic range, making quieter parts louder")
582
+
583
+ reset_post_processing = st.button("Reset All Effects",
584
+ help="Remove all applied effects")
585
+
586
+ with vol_col:
587
+ with st.container(border=True):
588
+ volume_balance = svs.vertical_slider(
589
+ "Volume Balance",
590
+ min_value=-10.0,
591
+ max_value=10.0,
592
+ default_value=0.0,
593
+ step=0.1,
594
+ slider_color="green",
595
+ track_color="lightgray",
596
+ thumb_color="red",
597
+ thumb_shape="pill",
598
+ )
599
+ vol_button = st.button("Apply Volume Change")
600
+
601
+ # Pitch shifting
602
+ with pitch_col:
603
+ with st.container(border=True):
604
+ pitch_semitones = svs.vertical_slider(
605
+ label="Pitch (semitones)",
606
+ min_value=-12,
607
+ max_value=12,
608
+ default_value=0,
609
+ step=1,
610
+ slider_color="red",
611
+ track_color="lightgray",
612
+ thumb_color="red",
613
+ thumb_shape="pill",
614
+ )
615
+ pitch_shift_button = st.button("Apply Pitch Shift")
616
+
617
+ effect_applied = False
618
+
619
+ if st_state.augmented_audio_pydub is not None:
620
+ if vol_button:
621
+ st_state.augmented_audio_pydub = st_state.augmented_audio_pydub + volume_balance
622
+ effect_applied = True
623
+ st.session_state.playing_state = 'processed'
624
+
625
+ if apply_stereo:
626
+ st_state.augmented_audio_pydub = st_state.augmented_audio_pydub.pan(
627
+ -0.5
628
+ ).overlay(st_state.augmented_audio_pydub.pan(0.5))
629
+ effect_applied = True
630
+ st.session_state.playing_state = 'processed'
631
+
632
+ if reverse:
633
+ st_state.augmented_audio_pydub = st_state.augmented_audio_pydub.reverse()
634
+ effect_applied = True
635
+ st.session_state.playing_state = 'processed'
636
+
637
+ if pitch_shift_button:
638
+ st_state.augmented_audio_pydub = st_state.augmented_audio_pydub._spawn(
639
+ st_state.augmented_audio_pydub.raw_data,
640
+ overrides={
641
+ "frame_rate": int(
642
+ st_state.augmented_audio_pydub.frame_rate
643
+ * (2 ** (pitch_semitones / 12.0))
644
+ )
645
+ },
646
+ )
647
+ effect_applied = True
648
+ st.session_state.playing_state = 'processed'
649
+
650
+ if low_pass:
651
+ # Simple low-pass filter approximation using pydub
652
+ # For a better filter, you would use scipy or another DSP library
653
+ from pydub.effects import low_pass_filter
654
+ st_state.augmented_audio_pydub = low_pass_filter(st_state.augmented_audio_pydub, 1500)
655
+ effect_applied = True
656
+ st.session_state.playing_state = 'processed'
657
+
658
+ if compress:
659
+ # Apply compression using pydub
660
+ # This is a simple approximation of compression
661
+ from pydub.effects import normalize
662
+ st_state.augmented_audio_pydub = normalize(st_state.augmented_audio_pydub)
663
+ effect_applied = True
664
+ st.session_state.playing_state = 'processed'
665
+
666
+ if reset_post_processing and st_state.audio_pydub is not None:
667
+ st_state.augmented_audio_pydub = st_state.audio_pydub
668
+ st.session_state.playing_state = 'generated'
669
+ effect_applied = True
670
+
671
+ # Display the final audio if processed
672
+ if effect_applied or st.session_state.playing_state == 'processed':
673
+ display_audio_player(is_final=True)
674
+
675
+ # Force rerun if any effect was applied to show the updated audio
676
+ if effect_applied:
677
+ st.rerun()
678
+ else:
679
+ # Show a message prompting the user to load audio first
680
+ st.info("πŸ‘† Please upload a music file or select a beat from the options above to start processing.")