Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,269 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
with gr.Blocks() as demo:
|
2 |
gr.HTML('<h3 style="text-align:center;">Where Your Audio Meets Intelligence</h3>')
|
3 |
gr.Markdown('### Upload, edit, export — powered by AI!')
|
4 |
|
5 |
-
# 1. Single File Studio
|
6 |
with gr.Tab("🎵 Single File Studio"):
|
7 |
with gr.Row():
|
8 |
with gr.Column():
|
9 |
input_audio = gr.Audio(label="Upload Audio", type="filepath")
|
10 |
-
effect_checkbox = gr.CheckboxGroup(
|
11 |
-
|
12 |
-
label="Apply Effects in Order"
|
13 |
-
)
|
14 |
-
preset_dropdown = gr.Dropdown(choices=preset_names, label="Select Preset", value=preset_names[0])
|
15 |
export_format = gr.Dropdown(choices=["WAV", "MP3"], label="Export Format", value="WAV")
|
16 |
isolate_vocals = gr.Checkbox(label="Isolate Vocals After Effects")
|
17 |
process_btn = gr.Button("Process Audio")
|
|
|
18 |
with gr.Column():
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
def
|
26 |
-
return preset_choices.get(
|
27 |
-
|
28 |
-
preset_dropdown.change(
|
29 |
-
|
30 |
-
def
|
31 |
effs = preset_choices.get(preset, []) if preset in preset_choices else effects
|
32 |
-
return process_audio(
|
33 |
-
|
34 |
-
process_btn.click(process_wrapper,
|
35 |
-
inputs=[input_audio, effect_checkbox, isolate_vocals, preset_dropdown, export_format],
|
36 |
-
outputs=[output_audio, waveform_img, session_log_out, genre_out, status_box])
|
37 |
-
|
38 |
-
# 2. Remix Mode
|
39 |
-
with gr.Tab("🎛 Remix Mode"):
|
40 |
-
with gr.Row():
|
41 |
-
with gr.Column():
|
42 |
-
remix_input = gr.Audio(label="Upload Music Track", type="filepath")
|
43 |
-
split_button = gr.Button("Split Into Drums, Bass, Vocals, etc.")
|
44 |
-
with gr.Column():
|
45 |
-
vocals_file = gr.File(label="Vocals")
|
46 |
-
drums_file = gr.File(label="Drums")
|
47 |
-
bass_file = gr.File(label="Bass")
|
48 |
-
other_file = gr.File(label="Other")
|
49 |
-
split_button.click(stem_split, inputs=remix_input, outputs=[vocals_file, drums_file, bass_file, other_file])
|
50 |
-
|
51 |
-
# 3. AI Remastering
|
52 |
-
with gr.Tab("🔮 AI Remastering"):
|
53 |
-
remaster_input = gr.Audio(label="Upload Low-Quality Recording", type="filepath")
|
54 |
-
remaster_output = gr.Audio(label="Studio-Grade Output", type="numpy")
|
55 |
-
remaster_status = gr.Textbox(label="Status", value="Ready", interactive=False)
|
56 |
-
remaster_btn = gr.Button("Remaster")
|
57 |
-
remaster_btn.click(ai_remaster, inputs=remaster_input, outputs=remaster_output)
|
58 |
-
remaster_btn.click(lambda _: "Done!", remaster_btn, remaster_status)
|
59 |
-
|
60 |
-
# 4. Harmonic Saturation
|
61 |
-
with gr.Tab("🧬 Harmonic Saturation"):
|
62 |
-
saturation_in = gr.Audio(label="Upload Track", type="filepath")
|
63 |
-
saturation_type = gr.Dropdown(choices=["Tube", "Tape", "Console", "Mix Bus"], label="Saturation Type", value="Tube")
|
64 |
-
saturation_intensity = gr.Slider(minimum=0.1, maximum=1.0, value=0.2, label="Intensity")
|
65 |
-
saturation_out = gr.Audio(label="Warm Output", type="numpy")
|
66 |
-
sat_btn = gr.Button("Apply Saturation")
|
67 |
-
sat_btn.click(harmonic_saturation,
|
68 |
-
inputs=[saturation_in, saturation_type, saturation_intensity],
|
69 |
-
outputs=saturation_out)
|
70 |
-
|
71 |
-
# 5. Vocal Doubler / Harmonizer
|
72 |
-
with gr.Tab("🎧 Vocal Doubler / Harmonizer"):
|
73 |
-
vocal_in = gr.Audio(label="Upload Vocal Clip", type="filepath")
|
74 |
-
vocal_out = gr.Audio(label="Doubled Output", type="numpy")
|
75 |
-
vocal_status = gr.Textbox(label="Status", interactive=False)
|
76 |
-
vocal_btn = gr.Button("Add Vocal Doubling / Harmony")
|
77 |
-
vocal_btn.click(run_harmony, inputs=vocal_in, outputs=[vocal_out, vocal_status])
|
78 |
-
|
79 |
-
# 6. Batch Processing
|
80 |
-
with gr.Tab("🔊 Batch Processing"):
|
81 |
-
batch_files = gr.File(label="Upload Multiple Files", file_count="multiple")
|
82 |
-
batch_effects = gr.CheckboxGroup(choices=list({e for effs in preset_choices.values() for e in effs}), label="Apply Effects in Order")
|
83 |
-
batch_isolate = gr.Checkbox(label="Isolate Vocals After Effects")
|
84 |
-
batch_preset = gr.Dropdown(choices=preset_names, label="Select Preset", value=preset_names[0])
|
85 |
-
batch_export = gr.Dropdown(choices=["MP3", "WAV"], label="Export Format", value="MP3")
|
86 |
-
batch_btn = gr.Button("Process All Files")
|
87 |
-
batch_zip = gr.File(label="Download All Processed Files (ZIP)")
|
88 |
-
batch_status = gr.Textbox(label="Status", interactive=False)
|
89 |
-
batch_btn.click(batch_process_audio,
|
90 |
-
inputs=[batch_files, batch_effects, batch_isolate, batch_preset, batch_export],
|
91 |
-
outputs=[batch_zip, batch_status])
|
92 |
-
|
93 |
-
# 7. AI Auto-Tune
|
94 |
-
with gr.Tab("🎤 AI Auto-Tune"):
|
95 |
-
autotune_file = gr.File(label="Source Voice Clip")
|
96 |
-
autotune_key = gr.Textbox(label="Target Key", value="C", lines=1)
|
97 |
-
autotune_out = gr.Audio(label="Pitch-Corrected Output", type="numpy")
|
98 |
-
autotune_btn = gr.Button("Apply Auto-Tune")
|
99 |
-
autotune_btn.click(auto_tune_vocal, inputs=[autotune_file, autotune_key], outputs=autotune_out)
|
100 |
-
|
101 |
-
# 8. Frequency Spectrum
|
102 |
-
with gr.Tab("📊 Frequency Spectrum"):
|
103 |
-
spectrum_in = gr.Audio(label="Upload Track", type="filepath")
|
104 |
-
spectrum_out = gr.Image(label="Frequency Spectrum")
|
105 |
-
spectrum_btn = gr.Button("Visualize Spectrum")
|
106 |
-
spectrum_btn.click(visualize_spectrum, inputs=spectrum_in, outputs=spectrum_out)
|
107 |
-
|
108 |
-
# 9. Loudness Graph
|
109 |
-
with gr.Tab("📈 Loudness Graph"):
|
110 |
-
loudness_in = gr.Audio(label="Upload Track", type="filepath")
|
111 |
-
loudness_target = gr.Slider(minimum=-24, maximum=-6, value=-14, label="Target LUFS")
|
112 |
-
loudness_out = gr.Audio(label="Normalized Output", type="numpy")
|
113 |
-
loudness_btn = gr.Button("Match Loudness")
|
114 |
-
loudness_btn.click(match_loudness, inputs=[loudness_in, loudness_target], outputs=loudness_out)
|
115 |
-
|
116 |
-
# 10. Save/Load Project
|
117 |
-
with gr.Tab("📁 Save/Load Project"):
|
118 |
-
with gr.Row():
|
119 |
-
with gr.Column():
|
120 |
-
project_audio = gr.File(label="Original Audio")
|
121 |
-
project_preset = gr.Dropdown(choices=preset_names, label="Used Preset", value=preset_names[0])
|
122 |
-
project_effects = gr.CheckboxGroup(choices=list({e for effs in preset_choices.values() for e in effs}), label="Applied Effects")
|
123 |
-
save_proj_btn = gr.Button("Save Project")
|
124 |
-
project_file = gr.File(label="Project File (.aiproj)")
|
125 |
-
with gr.Column():
|
126 |
-
load_proj_file = gr.File(label="Load .aiproj File")
|
127 |
-
loaded_preset = gr.Dropdown(choices=preset_names, label="Loaded Preset")
|
128 |
-
loaded_effects = gr.CheckboxGroup(choices=list({e for effs in preset_choices.values() for e in effs}), label="Loaded Effects")
|
129 |
-
load_proj_btn = gr.Button("Load Project")
|
130 |
-
save_proj_btn.click(save_project, inputs=[project_audio, project_preset, project_effects], outputs=project_file)
|
131 |
-
load_proj_btn.click(load_project, inputs=load_proj_file, outputs=[loaded_preset, loaded_effects])
|
132 |
-
|
133 |
-
# 11. Prompt-Based Editing
|
134 |
-
with gr.Tab("🧠 Prompt-Based Editing"):
|
135 |
-
prompt_audio = gr.File(label="Upload Audio", file_types=[".wav", ".mp3"])
|
136 |
-
prompt_text = gr.Textbox(label="Describe What You Want", lines=5)
|
137 |
-
prompt_out = gr.Audio(label="Edited Output", type="numpy")
|
138 |
-
prompt_btn = gr.Button("Process Prompt")
|
139 |
-
prompt_btn.click(process_prompt, inputs=[prompt_audio, prompt_text], outputs=prompt_out)
|
140 |
-
|
141 |
-
# 12. Custom EQ Editor
|
142 |
-
with gr.Tab("🎛 Custom EQ Editor"):
|
143 |
-
eq_audio = gr.Audio(label="Upload Track", type="filepath")
|
144 |
-
eq_genre = gr.Dropdown(choices=list(eq_map.keys()), value="Pop", label="Genre")
|
145 |
-
eq_output = gr.Audio(label="EQ-Enhanced Output", type="numpy")
|
146 |
-
eq_btn = gr.Button("Apply EQ")
|
147 |
-
eq_btn.click(auto_eq, inputs=[eq_audio, eq_genre], outputs=eq_output)
|
148 |
-
|
149 |
-
# 13. A/B Compare
|
150 |
-
with gr.Tab("🎯 A/B Compare"):
|
151 |
-
ab_track1 = gr.Audio(label="Version A", type="filepath")
|
152 |
-
ab_track2 = gr.Audio(label="Version B", type="filepath")
|
153 |
-
ab_out1 = gr.Audio(label="Version A", type="filepath")
|
154 |
-
ab_out2 = gr.Audio(label="Version B", type="filepath")
|
155 |
-
ab_btn = gr.Button("Compare")
|
156 |
-
ab_btn.click(compare_ab, inputs=[ab_track1, ab_track2], outputs=[ab_out1, ab_out2])
|
157 |
-
|
158 |
-
# 14. Loop Playback
|
159 |
-
with gr.Tab("🔁 Loop Playback"):
|
160 |
-
loop_audio = gr.Audio(label="Upload Track", type="filepath")
|
161 |
-
loop_start = gr.Slider(minimum=0, maximum=30000, step=100, value=5000, label="Start MS")
|
162 |
-
loop_end = gr.Slider(minimum=100, maximum=30000, step=100, value=10000, label="End MS")
|
163 |
-
loop_repeat = gr.Slider(minimum=1, maximum=10, value=2, label="Repeat Loops")
|
164 |
-
loop_out = gr.Audio(label="Looped Output", type="numpy")
|
165 |
-
loop_btn = gr.Button("Loop Section")
|
166 |
-
loop_btn.click(loop_section, inputs=[loop_audio, loop_start, loop_end, loop_repeat], outputs=loop_out)
|
167 |
-
|
168 |
-
# 15. Share Effect Chain Tab
|
169 |
-
with gr.Tab("🔗 Share Effect Chain"):
|
170 |
-
share_effects = gr.CheckboxGroup(choices=list({e for effs in preset_choices.values() for e in effs}), label="Select Effects")
|
171 |
-
share_code = gr.Textbox(label="Share Code", lines=2)
|
172 |
-
share_btn = gr.Button("Generate Share Code")
|
173 |
-
share_btn.click(lambda x: json.dumps(sorted(x)), inputs=share_effects, outputs=share_code)
|
174 |
-
|
175 |
-
# 16. Load Shared Chain Tab
|
176 |
-
with gr.Tab("📥 Load Shared Chain"):
|
177 |
-
load_code = gr.Textbox(label="Paste Shared Code", lines=2)
|
178 |
-
loaded_effects = gr.CheckboxGroup(choices=list({e for effs in preset_choices.values() for e in effs}), label="Loaded Effects")
|
179 |
-
load_code_btn = gr.Button("Load Effects")
|
180 |
-
load_code_btn.click(lambda code: json.loads(code) if code else [], inputs=load_code, outputs=loaded_effects)
|
181 |
-
|
182 |
-
# 17. Keyboard Shortcuts Tab
|
183 |
-
with gr.Tab("⌨ Keyboard Shortcuts"):
|
184 |
-
gr.Markdown("""
|
185 |
-
### Keyboard Controls
|
186 |
-
- `Ctrl + Z`: Undo last effect
|
187 |
-
- `Ctrl + Y`: Redo
|
188 |
-
- `Spacebar`: Play/Stop playback
|
189 |
-
- `Ctrl + S`: Save current session
|
190 |
-
- `Ctrl + O`: Open session
|
191 |
-
- `Ctrl + C`: Copy effect chain
|
192 |
-
- `Ctrl + V`: Paste effect chain
|
193 |
-
""")
|
194 |
-
|
195 |
-
# 18. Vocal Formant Correction Tab
|
196 |
-
with gr.Tab("🧑🎤 Vocal Formant Correction"):
|
197 |
-
formant_audio = gr.Audio(label="Upload Vocal Track", type="filepath")
|
198 |
-
formant_shift = gr.Slider(minimum=-2, maximum=2, value=1.0, step=0.1, label="Formant Shift")
|
199 |
-
formant_output = gr.Audio(label="Natural-Sounding Vocal", type="numpy")
|
200 |
-
formant_btn = gr.Button("Apply Correction")
|
201 |
-
formant_btn.click(
|
202 |
-
lambda audio, shift: array_to_audiosegment(
|
203 |
-
librosa.effects.pitch_shift(
|
204 |
-
np.array(AudioSegment.from_file(audio).get_array_of_samples()),
|
205 |
-
sr=AudioSegment.from_file(audio).frame_rate,
|
206 |
-
n_steps=shift
|
207 |
-
).astype(np.int16),
|
208 |
-
AudioSegment.from_file(audio).frame_rate,
|
209 |
-
channels=AudioSegment.from_file(audio).channels
|
210 |
-
),
|
211 |
-
inputs=[formant_audio, formant_shift],
|
212 |
-
outputs=formant_output
|
213 |
-
)
|
214 |
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
clone_output = gr.Audio(label="Converted Output", type="numpy")
|
220 |
-
clone_btn = gr.Button("Clone Voice")
|
221 |
-
|
222 |
-
def clone_func(source, ref):
|
223 |
-
s = AudioSegment.from_file(source.name)
|
224 |
-
r = AudioSegment.from_file(ref.name)
|
225 |
-
mixed = s.overlay(r - 10)
|
226 |
-
tmp = save_audiosegment_to_temp(mixed, ".wav")
|
227 |
-
return load_audiofile_to_numpy(tmp)
|
228 |
-
|
229 |
-
clone_btn.click(clone_func, inputs=[source_voice, reference_voice], outputs=clone_output)
|
230 |
-
|
231 |
-
# 20. DAW Template Export Tab
|
232 |
-
with gr.Tab("🎛 DAW Template Export"):
|
233 |
-
daw_stems = gr.File(label="Upload Stems", file_count="multiple")
|
234 |
-
daw_output = gr.File(label="DAW Template (.json/.als/.flp)")
|
235 |
-
daw_btn = gr.Button("Generate Template")
|
236 |
-
|
237 |
-
def generate_template(stems):
|
238 |
-
template = {
|
239 |
-
"format": "Ableton Live",
|
240 |
-
"stems": [os.path.basename(s.name) for s in stems],
|
241 |
-
"effects": ["Reverb", "EQ", "Compression"],
|
242 |
-
"tempo": 128,
|
243 |
-
"title": "Studio Pulse Project"
|
244 |
-
}
|
245 |
-
out_path = os.path.join(tempfile.gettempdir(), "ableton_template.json")
|
246 |
-
with open(out_path, "w") as f:
|
247 |
-
json.dump(template, f, indent=2)
|
248 |
-
return out_path
|
249 |
-
|
250 |
-
daw_btn.click(generate_template, inputs=daw_stems, outputs=daw_output)
|
251 |
-
|
252 |
-
# 21. Export Full Mix ZIP Tab
|
253 |
-
with gr.Tab("📁 Export Full Mix ZIP"):
|
254 |
-
stems_files = gr.File(label="Stems", file_count="multiple")
|
255 |
-
final_mix_file = gr.File(label="Final Mix")
|
256 |
-
full_zip_output = gr.File(label="Full Mix Archive (.zip)")
|
257 |
-
export_zip_btn = gr.Button("Export ZIP")
|
258 |
-
|
259 |
-
def export_zip(stems, final_mix):
|
260 |
-
zip_path = os.path.join(tempfile.gettempdir(), "full_export.zip")
|
261 |
-
with zipfile.ZipFile(zip_path, "w") as zipf:
|
262 |
-
for i, stem in enumerate(stems):
|
263 |
-
zipf.write(stem.name, f"stem_{i}.wav")
|
264 |
-
zipf.write(final_mix.name, "final_mix.wav")
|
265 |
-
return zip_path
|
266 |
-
|
267 |
-
export_zip_btn.click(export_zip, inputs=[stems_files, final_mix_file], outputs=full_zip_output)
|
268 |
|
269 |
demo.launch()
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from pydub import AudioSegment
|
3 |
+
import numpy as np
|
4 |
+
import tempfile
|
5 |
+
import os
|
6 |
+
import noisereduce as nr
|
7 |
+
import torch
|
8 |
+
from demucs import pretrained
|
9 |
+
from demucs.apply import apply_model
|
10 |
+
import torchaudio
|
11 |
+
from pathlib import Path
|
12 |
+
import matplotlib.pyplot as plt
|
13 |
+
from io import BytesIO
|
14 |
+
from PIL import Image
|
15 |
+
import zipfile
|
16 |
+
import datetime
|
17 |
+
import librosa
|
18 |
+
import warnings
|
19 |
+
import json
|
20 |
+
import pickle
|
21 |
+
import soundfile as sf
|
22 |
+
|
23 |
+
warnings.filterwarnings("ignore")
|
24 |
+
|
25 |
+
### Helper Functions ###
|
26 |
+
|
27 |
+
def audiosegment_to_array(audio):
|
28 |
+
return np.array(audio.get_array_of_samples()), audio.frame_rate
|
29 |
+
|
30 |
+
def array_to_audiosegment(samples, frame_rate, channels=1):
|
31 |
+
return AudioSegment(
|
32 |
+
samples.tobytes(),
|
33 |
+
frame_rate=int(frame_rate),
|
34 |
+
sample_width=samples.dtype.itemsize,
|
35 |
+
channels=channels
|
36 |
+
)
|
37 |
+
|
38 |
+
def save_audiosegment_to_temp(audio: AudioSegment, suffix=".wav"):
|
39 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as f:
|
40 |
+
audio.export(f.name, format=suffix.lstrip('.'))
|
41 |
+
return f.name
|
42 |
+
|
43 |
+
def load_audiofile_to_numpy(path):
|
44 |
+
samples, sr = sf.read(path, dtype="int16")
|
45 |
+
if samples.ndim > 1 and samples.shape[1] > 2:
|
46 |
+
samples = samples[:, :2]
|
47 |
+
return samples, sr
|
48 |
+
|
49 |
+
def show_waveform(audio_file):
|
50 |
+
try:
|
51 |
+
audio = AudioSegment.from_file(audio_file)
|
52 |
+
samples = np.array(audio.get_array_of_samples())
|
53 |
+
plt.figure(figsize=(10,2))
|
54 |
+
plt.plot(samples[:10000], color="skyblue")
|
55 |
+
plt.axis('off')
|
56 |
+
buf = BytesIO()
|
57 |
+
plt.savefig(buf, format='png', bbox_inches='tight')
|
58 |
+
plt.close()
|
59 |
+
buf.seek(0)
|
60 |
+
return Image.open(buf)
|
61 |
+
except Exception:
|
62 |
+
return None
|
63 |
+
|
64 |
+
### Effects ###
|
65 |
+
|
66 |
+
def apply_normalize(audio): return audio.normalize()
|
67 |
+
|
68 |
+
def apply_noise_reduction(audio):
|
69 |
+
samples, sr = audiosegment_to_array(audio)
|
70 |
+
reduced = nr.reduce_noise(y=samples, sr=sr)
|
71 |
+
return array_to_audiosegment(reduced, sr, audio.channels)
|
72 |
+
|
73 |
+
def apply_compression(audio): return audio.compress_dynamic_range()
|
74 |
+
|
75 |
+
def apply_reverb(audio):
|
76 |
+
reverb = audio - 10
|
77 |
+
return audio.overlay(reverb, position=1000)
|
78 |
+
|
79 |
+
def apply_pitch_shift(audio, semitones=-2):
|
80 |
+
new_fr = int(audio.frame_rate * (2 ** (semitones / 12)))
|
81 |
+
return audio._spawn(audio.raw_data, overrides={"frame_rate": new_fr}).set_frame_rate(audio.frame_rate)
|
82 |
+
|
83 |
+
def apply_echo(audio, delay_ms=500, decay=0.5):
|
84 |
+
echo = audio - 10
|
85 |
+
return audio.overlay(echo, position=delay_ms)
|
86 |
+
|
87 |
+
def apply_stereo_widen(audio, pan_amount=0.3):
|
88 |
+
left = audio.pan(-pan_amount)
|
89 |
+
right = audio.pan(pan_amount)
|
90 |
+
return AudioSegment.from_mono_audiosegments(left, right)
|
91 |
+
|
92 |
+
def apply_bass_boost(audio, gain=10): return audio.low_pass_filter(100).apply_gain(gain)
|
93 |
+
|
94 |
+
def apply_treble_boost(audio, gain=10): return audio.high_pass_filter(4000).apply_gain(gain)
|
95 |
+
|
96 |
+
def apply_limiter(audio, limit_dB=-1):
|
97 |
+
limiter = audio._spawn(audio.raw_data, overrides={"frame_rate": audio.frame_rate})
|
98 |
+
return limiter.apply_gain(limit_dB)
|
99 |
+
|
100 |
+
def apply_auto_gain(audio, target_dB=-20):
|
101 |
+
change = target_dB - audio.dBFS
|
102 |
+
return audio.apply_gain(change)
|
103 |
+
|
104 |
+
def apply_vocal_distortion(audio, intensity=0.3):
|
105 |
+
samples = np.array(audio.get_array_of_samples()).astype(np.float32)
|
106 |
+
distorted = samples + intensity * np.sin(samples * 2 * np.pi / 32768)
|
107 |
+
return array_to_audiosegment(distorted.astype(np.int16), audio.frame_rate, audio.channels)
|
108 |
+
|
109 |
+
def apply_harmony(audio, shift_semitones=4):
|
110 |
+
shifted_up = apply_pitch_shift(audio, shift_semitones)
|
111 |
+
shifted_down = apply_pitch_shift(audio, -shift_semitones)
|
112 |
+
return audio.overlay(shifted_up).overlay(shifted_down)
|
113 |
+
|
114 |
+
def apply_stage_mode(audio):
|
115 |
+
processed = apply_reverb(audio)
|
116 |
+
processed = apply_bass_boost(processed, gain=6)
|
117 |
+
return apply_limiter(processed, limit_dB=-2)
|
118 |
+
|
119 |
+
def apply_bitcrush(audio, bit_depth=8):
|
120 |
+
samples = np.array(audio.get_array_of_samples())
|
121 |
+
max_value = 2 ** bit_depth - 1
|
122 |
+
downsampled = np.round(samples / (32768 / max_value)).astype(np.int16)
|
123 |
+
return array_to_audiosegment(downsampled, audio.frame_rate // 2, audio.channels)
|
124 |
+
|
125 |
+
### Presets ###
|
126 |
+
|
127 |
+
preset_choices = {
|
128 |
+
"Default": [],
|
129 |
+
"Clean Podcast": ["Noise Reduction", "Normalize"],
|
130 |
+
"Podcast Mastered": ["Noise Reduction", "Normalize", "Compress Dynamic Range"],
|
131 |
+
"Radio Ready": ["Bass Boost", "Treble Boost", "Limiter"],
|
132 |
+
"Music Production": ["Reverb", "Stereo Widening", "Pitch Shift"],
|
133 |
+
"ASMR Creator": ["Noise Gate", "Auto Gain", "Low-Pass Filter"],
|
134 |
+
"Voiceover Pro": ["Vocal Isolation", "TTS", "EQ Match"],
|
135 |
+
"8-bit Retro": ["Bitcrusher", "Echo", "Mono Downmix"],
|
136 |
+
"🎙 Clean Vocal": ["Noise Reduction", "Normalize", "High Pass Filter (80Hz)"],
|
137 |
+
"🧪 Vocal Distortion": ["Vocal Distortion", "Reverb", "Compress Dynamic Range"],
|
138 |
+
"🎶 Singer's Harmony": ["Harmony", "Stereo Widening", "Pitch Shift"],
|
139 |
+
"🌫 ASMR Vocal": ["Auto Gain", "Low-Pass Filter (3000Hz)", "Noise Gate"],
|
140 |
+
"🎼 Stage Mode": ["Reverb", "Bass Boost", "Limiter"],
|
141 |
+
}
|
142 |
+
|
143 |
+
preset_names = list(preset_choices.keys())
|
144 |
+
|
145 |
+
### Main processing ###
|
146 |
+
|
147 |
+
def process_audio(audio_file, selected_effects, isolate_vocals, preset_name, export_format):
|
148 |
+
try:
|
149 |
+
audio = AudioSegment.from_file(audio_file)
|
150 |
+
effect_map = {
|
151 |
+
"Noise Reduction": apply_noise_reduction,
|
152 |
+
"Compress Dynamic Range": apply_compression,
|
153 |
+
"Add Reverb": apply_reverb,
|
154 |
+
"Pitch Shift": apply_pitch_shift,
|
155 |
+
"Echo": apply_echo,
|
156 |
+
"Stereo Widening": apply_stereo_widen,
|
157 |
+
"Bass Boost": apply_bass_boost,
|
158 |
+
"Treble Boost": apply_treble_boost,
|
159 |
+
"Normalize": apply_normalize,
|
160 |
+
"Limiter": lambda x: apply_limiter(x, limit_dB=-1),
|
161 |
+
"Auto Gain": lambda x: apply_auto_gain(x, target_dB=-20),
|
162 |
+
"Vocal Distortion": apply_vocal_distortion,
|
163 |
+
"Stage Mode": apply_stage_mode,
|
164 |
+
"Harmony": apply_harmony,
|
165 |
+
"Bitcrusher": apply_bitcrush,
|
166 |
+
}
|
167 |
+
for effect in selected_effects:
|
168 |
+
if effect in effect_map:
|
169 |
+
audio = effect_map[effect](audio)
|
170 |
+
if isolate_vocals:
|
171 |
+
temp_path = save_audiosegment_to_temp(audio, suffix=".wav")
|
172 |
+
vocal_path = apply_vocal_isolation(temp_path)
|
173 |
+
audio = AudioSegment.from_file(vocal_path)
|
174 |
+
output_path = save_audiosegment_to_temp(audio, suffix='.' + export_format.lower())
|
175 |
+
samples, sr = load_audiofile_to_numpy(output_path)
|
176 |
+
waveform = show_waveform(output_path)
|
177 |
+
session_log = json.dumps({
|
178 |
+
"timestamp": str(datetime.datetime.now()),
|
179 |
+
"filename": os.path.basename(audio_file),
|
180 |
+
"effects_applied": selected_effects,
|
181 |
+
"isolate_vocals": isolate_vocals,
|
182 |
+
"export_format": export_format,
|
183 |
+
"detected_genre": "Unknown"
|
184 |
+
}, indent=2)
|
185 |
+
return (samples, sr), waveform, session_log, "Unknown", "🎉 Done!"
|
186 |
+
except Exception as e:
|
187 |
+
return None, None, f"Error: {e}", "", f"Error: {e}"
|
188 |
+
|
189 |
+
### Other necessary functions (batch, AI remaster...) would follow similar patterns.
|
190 |
+
|
191 |
+
# ===================================================
|
192 |
+
# Now, the Gradio UI:
|
193 |
+
# Paste this after all function definitions above
|
194 |
+
# ===================================================
|
195 |
+
|
196 |
with gr.Blocks() as demo:
|
197 |
gr.HTML('<h3 style="text-align:center;">Where Your Audio Meets Intelligence</h3>')
|
198 |
gr.Markdown('### Upload, edit, export — powered by AI!')
|
199 |
|
|
|
200 |
with gr.Tab("🎵 Single File Studio"):
|
201 |
with gr.Row():
|
202 |
with gr.Column():
|
203 |
input_audio = gr.Audio(label="Upload Audio", type="filepath")
|
204 |
+
effect_checkbox = gr.CheckboxGroup(choices=list({e for effects in preset_choices.values() for e in effects}), label="Apply Effects in Order")
|
205 |
+
preset_dropdown = gr.Dropdown(choices=preset_names, label="Select Preset")
|
|
|
|
|
|
|
206 |
export_format = gr.Dropdown(choices=["WAV", "MP3"], label="Export Format", value="WAV")
|
207 |
isolate_vocals = gr.Checkbox(label="Isolate Vocals After Effects")
|
208 |
process_btn = gr.Button("Process Audio")
|
209 |
+
|
210 |
with gr.Column():
|
211 |
+
processed_audio = gr.Audio(label="Processed Audio", type="numpy")
|
212 |
+
waveform_image = gr.Image(label="Waveform Preview")
|
213 |
+
session_log = gr.Textbox(label="Session Log", lines=6)
|
214 |
+
detected_genre = gr.Textbox(label="Detected Genre")
|
215 |
+
status = gr.Textbox(label="Status", lines=1, value="Ready")
|
216 |
+
|
217 |
+
def update_effects(preset):
|
218 |
+
return preset_choices.get(preset, [])
|
219 |
+
|
220 |
+
preset_dropdown.change(update_effects, inputs=preset_dropdown, outputs=effect_checkbox)
|
221 |
+
|
222 |
+
def run_processing(audio, effects, isolate, preset, fmt):
|
223 |
effs = preset_choices.get(preset, []) if preset in preset_choices else effects
|
224 |
+
return process_audio(audio, effs, isolate, preset, fmt)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
225 |
|
226 |
+
process_btn.click(run_processing,
|
227 |
+
inputs=[input_audio, effect_checkbox, isolate_vocals, preset_dropdown, export_format],
|
228 |
+
outputs=[processed_audio, waveform_image, session_log, detected_genre, status]
|
229 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
|
231 |
demo.launch()
|