Update app.py
Browse files
app.py
CHANGED
@@ -1,131 +1,131 @@
|
|
1 |
-
# app.py (Final Version with Download Feature)
|
2 |
-
import gradio as gr
|
3 |
-
import google.generativeai as genai
|
4 |
-
import os
|
5 |
-
import time
|
6 |
-
from pydub import AudioSegment
|
7 |
-
|
8 |
-
# --- 0. 全局配置 / Global Configuration ---
|
9 |
-
# API Key will be loaded from Hugging Face Secrets
|
10 |
-
GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
|
11 |
-
if not GOOGLE_API_KEY:
|
12 |
-
raise ValueError("Hugging Face Secret 'GOOGLE_API_KEY' not found!")
|
13 |
-
|
14 |
-
genai.configure(api_key=GOOGLE_API_KEY)
|
15 |
-
gemini_model = genai.GenerativeModel('gemini-1.5-flash-latest')
|
16 |
-
|
17 |
-
EQ_BANDS_HZ = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000]
|
18 |
-
EQ_RANGE_DB = 12
|
19 |
-
|
20 |
-
# --- 1. 双语文本库 / Bilingual Text Library (已更新提示信息) ---
|
21 |
-
LANG = {
|
22 |
-
'zh': {
|
23 |
-
'title': "FeiMatrix 智能动态调音", 'subtitle': "上传一首歌,告诉我你想要的感觉,剩下的交给我!", 'lang_label': "语言 / Language",
|
24 |
-
'step1_header': "第一步:上传音频", 'upload_label': "点击或拖拽 MP3 文件到这里",
|
25 |
-
'step2_header': "第二步:告诉我你的感觉", 'custom_input_label': "用你自己的话描述想要的感觉(推荐):",
|
26 |
-
'custom_input_placeholder': "例如:我想要吉他声更清脆,鼓声更有力...", 'quick_choice_label': "或者,快速选择一个预设风格:",
|
27 |
-
'quick_choices': [ "我想要咚咚咚的重低音!", "让高音更亮、更清楚", "让唱歌的声音更突出", "我喜欢温暖、厚实的感觉", "给我一种现场演唱会的空间感", "保持原汁原味,但细节多一点", "无 (使用上面的输入框)", ],
|
28 |
-
'default_choice': "无 (使用上面的输入框)", 'step3_header': "第三步:开始调音", 'process_button': "开始智能调音!",
|
29 |
-
'log_header': "AI 调音师工作日志", 'log_initial': "`[系统]`:我准备好啦,等你上传文件哦~", 'result_header': "第四步:聆听并下载您的定制版",
|
30 |
-
'result_label': "这里会显示AI调音后的音频", 'accordion_header': "(高级)查看 AI 定制的 EQ 参数", 'err_no_file': "哎呀,忘了上传MP3文件啦!",
|
31 |
-
'info_no_pref': "您没有指定风格,将为您进行温和的细节优化。", 'status_analyzing': "`[AI分析师]`:收到!正在分析您的音频... ⏳",
|
32 |
-
'status_analysis_failed': "AI分析失败: {e}。将使用默认调音策略。", 'status_understanding': "`[AI分析师]`:分析完成!\n> {analysis}\n\n`[AI调音师]`:正在理解{choice}并调整EQ... 🔊",
|
33 |
-
'status_tuning': "`[AI调音师]`:好嘞!已经按您的要求调整好了!\n\n`[系统]`:正在生成音频... 🎶",
|
34 |
-
# --- 修改点 1: 更新完成提示 ---
|
35 |
-
'status_done': "`[系统]`:搞定!您的AI定制版音频已生成!🎉\n(点击播放器右侧的 **⋮** 即可下载)",
|
36 |
-
},
|
37 |
-
'en': {
|
38 |
-
'title': "FeiMatrix AI Dynamic Equalizer", 'subtitle': "Upload a song, tell me the vibe you want, and I'll handle the rest!", 'lang_label': "Language / 语言",
|
39 |
-
'step1_header': "Step 1: Upload Audio", 'upload_label': "Click or drag your MP3 file here",
|
40 |
-
'step2_header': "Step 2: Tell Me Your Vibe", 'custom_input_label': "Describe the feeling you want in your own words (Recommended):",
|
41 |
-
'custom_input_placeholder': "e.g., I want the guitar to be crisper and the drums more powerful...", 'quick_choice_label': "Or, quickly pick a preset style:",
|
42 |
-
'quick_choices': [ "I want that 'thump-thump' heavy bass!", "Make the treble brighter and clearer", "Make the vocals stand out more", "I like a warm and rich sound", "Give me a live concert feeling", "Keep it natural, just add more detail", "None (use the input box above)", ],
|
43 |
-
'default_choice': "None (use the input box above)", 'step3_header': "Step 3: Start Tuning", 'process_button': "Start AI Tuning!",
|
44 |
-
'log_header': "AI Tuning Engineer's Log", 'log_initial': "`[System]`: Ready when you are, just upload a file~", 'result_header': "Step 4: Listen & Download Your Custom Version",
|
45 |
-
'result_label': "Your AI-tuned audio will appear here", 'accordion_header': "(Advanced) View AI-Customized EQ Parameters", 'err_no_file': "Oops, you forgot to upload the MP3 file!",
|
46 |
-
'info_no_pref': "You didn't specify a style, so I'll perform a gentle detail enhancement.", 'status_analyzing': "`[AI Analyst]`: Roger that! Analyzing your audio... ⏳",
|
47 |
-
'status_analysis_failed': "AI analysis failed: {e}. Default tuning strategy will be used.", 'status_understanding': "`[AI Analyst]`: Analysis complete!\n> {analysis}\n\n`[AI Tuning Engineer]`: Understanding {choice} and adjusting EQ... 🔊",
|
48 |
-
'status_tuning': "`[AI Tuning Engineer]`: Alright! Tuned to your request!\n\n`[System]`: Generating audio... 🎶",
|
49 |
-
# --- 修改点 1: 更新完成提示 ---
|
50 |
-
'status_done': "`[System]`: All set! Your AI custom audio is ready! 🎉\n(Click the **⋮** icon on the right of the player to download)",
|
51 |
-
}
|
52 |
-
}
|
53 |
-
LANG_MAP = {"简体中文": "zh", "English": "en"}
|
54 |
-
|
55 |
-
def get_ai_tuned_eq_settings(audio_analysis_text, user_preference):
|
56 |
-
eq_values = [0.0] * len(EQ_BANDS_HZ); pref_lower = user_preference.lower()
|
57 |
-
if "电子舞曲" in audio_analysis_text or "EDM" in audio_analysis_text: eq_values = [4.0, 2.5, -1.5, -0.5, 1.0, 2.0, 3.5, 4.0, 2.5, 1.0]
|
58 |
-
elif "爵士乐" in audio_analysis_text or "warm" in audio_analysis_text: eq_values = [3.0, 1.5, -0.5, -1.0, 0.5, 1.5, 2.0, 3.0, 2.0, 0.5]
|
59 |
-
elif "人声" in audio_analysis_text or "vocal" in audio_analysis_text: eq_values[4] += 0.5; eq_values[5] += 1.0
|
60 |
-
if any(k in pref_lower for k in ["低音", "bass", "thump", "punch", "蹦迪"]): eq_values[0] += 3.0; eq_values[1] += 1.5
|
61 |
-
if any(k in pref_lower for k in ["高音", "treble", "bright", "clear", "crisp"]): eq_values[6]+=1.5; eq_values[7]+=2.0; eq_values[8]+=1.0
|
62 |
-
if any(k in pref_lower for k in ["人声", "vocal", "singing", "lyrics"]): eq_values[4]+=1.5; eq_values[5]+=1.0; eq_values[3]-=0.5
|
63 |
-
if any(k in pref_lower for k in ["温暖", "warm", "rich", "soft"]): eq_values[1]+=1.5; eq_values[2]+=1.0; eq_values[6]-=0.5
|
64 |
-
if any(k in pref_lower for k in ["空间", "space", "live", "concert"]): eq_values[6]+=1.0; eq_values[7]+=1.0; eq_values[4]-=0.5
|
65 |
-
if any(k in pref_lower for k in ["自然", "natural", "original"]): [v*0.5 for v in eq_values]; eq_values[7]+=0.5
|
66 |
-
eq_values=[max(-EQ_RANGE_DB,min(EQ_RANGE_DB,v)) for v in eq_values]; return {f'{f} Hz':v for f,v in zip(EQ_BANDS_HZ, eq_values)}
|
67 |
-
|
68 |
-
def apply_eq_to_audio(audio_path, eq_settings):
|
69 |
-
try: audio = AudioSegment.from_file(audio_path)
|
70 |
-
except Exception as e: print(f"Audio load error: {e}"); return None
|
71 |
-
q_factor=1.414; filter_parts=[]
|
72 |
-
for band, gain in eq_settings.items():
|
73 |
-
if gain != 0: filter_parts.append(f"equalizer=f={band.split(' ')[0]}:width_type=q:w={q_factor}:g={gain}")
|
74 |
-
if not filter_parts: return audio_path
|
75 |
-
output_path = f"{os.path.splitext(audio_path)[0]}_eq.mp3"
|
76 |
-
try:
|
77 |
-
audio.export(output_path, format="mp3", parameters=["-af", ",".join(filter_parts)])
|
78 |
-
return output_path
|
79 |
-
except Exception as e: print(f"EQ apply error: {e}"); raise gr.Error("Failed to apply EQ! This might be an issue with ffmpeg on the server.")
|
80 |
-
|
81 |
-
def process_and_tune(audio_file, quick_choice, custom_input, lang_choice):
|
82 |
-
lang_code = LANG_MAP[lang_choice]; L = LANG[lang_code]
|
83 |
-
if not audio_file: raise gr.Error(L['err_no_file'])
|
84 |
-
if custom_input and custom_input.strip(): final_preference = custom_input
|
85 |
-
elif L['default_choice'] not in quick_choice: final_preference = quick_choice
|
86 |
-
else: final_preference = L['quick_choices'][-2]; gr.Info(L['info_no_pref'])
|
87 |
-
slider_updates={s: gr.update(value=0) for s in eq_sliders}
|
88 |
-
yield {status_log_md: gr.update(value=L['status_analyzing']), processed_audio_output: gr.update(value=None), eq_accordion: gr.update(visible=True, open=False), **slider_updates}
|
89 |
-
try:
|
90 |
-
prompt = "Briefly analyze this audio's genre, mood, and key instruments."; response = gemini_model.generate_content([genai.upload_file(path=audio_file.name), prompt])
|
91 |
-
audio_analysis_text = response.text or "(AI did not provide a detailed analysis)"
|
92 |
-
except Exception as e: audio_analysis_text = L['status_analysis_failed'].format(e=e); gr.Warning(audio_analysis_text)
|
93 |
-
choice_desc = f"“{final_preference}”"
|
94 |
-
yield {status_log_md: gr.update(value=L['status_understanding'].format(analysis=audio_analysis_text, choice=choice_desc))}; time.sleep(1)
|
95 |
-
eq_settings = get_ai_tuned_eq_settings(audio_analysis_text, final_preference); slider_updates = {s: gr.update(value=v) for s, v in zip(eq_sliders, eq_settings.values())}
|
96 |
-
yield {status_log_md: gr.update(value=L['status_tuning']), **slider_updates}; time.sleep(1)
|
97 |
-
eq_audio_path = apply_eq_to_audio(audio_file.name, eq_settings)
|
98 |
-
if not eq_audio_path: raise gr.Error("Audio processing failed!")
|
99 |
-
yield {status_log_md: gr.update(value=L['status_done']), processed_audio_output: gr.update(value=eq_audio_path, label=L['result_label'], autoplay=True), eq_accordion: gr.update(open=False)}
|
100 |
-
|
101 |
-
def update_language(lang_choice):
|
102 |
-
L = LANG[LANG_MAP[lang_choice]]
|
103 |
-
return {
|
104 |
-
title_md: gr.update(value=f"# {L['title']}"), subtitle_md: gr.update(value=L['subtitle']), step1_header_md: gr.update(value=f"### **{L['step1_header']}**"),
|
105 |
-
audio_input: gr.update(label=L['upload_label']), step2_header_md: gr.update(value=f"### **{L['step2_header']}**"), custom_input: gr.update(label=L['custom_input_label'], placeholder=L['custom_input_placeholder']),
|
106 |
-
quick_choice: gr.update(label=L['quick_choice_label'], choices=L['quick_choices'], value=L['default_choice']), step3_header_md: gr.update(value=f"### **{L['step3_header']}**"),
|
107 |
-
process_button: gr.update(value=L['process_button']), log_header_md: gr.update(value=f"### **{L['log_header']}**"), status_log_md: gr.update(value=L['log_initial']),
|
108 |
-
result_header_md: gr.update(value=f"### **{L['result_header']}**"), processed_audio_output: gr.update(label=L['result_label']), eq_accordion: gr.update(label=L['accordion_header']),
|
109 |
-
}
|
110 |
-
|
111 |
-
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
112 |
-
lang_switcher = gr.Radio(choices=["简体中文", "English"], value="简体中文", label="Language / 语言", info="选择界面语言 / Select UI Language")
|
113 |
-
title_md = gr.Markdown("# FeiMatrix 智能动态调音")
|
114 |
-
subtitle_md = gr.Markdown("上传一首歌,告诉我你想要的感觉,剩下的交给我!")
|
115 |
-
with gr.Column():
|
116 |
-
step1_header_md = gr.Markdown("### **第一步:上传音频**"); audio_input = gr.File(label="点击或拖拽 MP3 文件到这里", type="filepath", file_types=[".mp3"])
|
117 |
-
step2_header_md = gr.Markdown("### **第二步:告诉我你的感觉**"); custom_input = gr.Textbox(label="用你自己的话描述想要的感觉(推荐):", placeholder="例如:我想要吉他声更清脆,鼓声更有力...", lines=2)
|
118 |
-
quick_choice = gr.Radio(label="或者,快速选择一个预设风格:", choices=LANG['zh']['quick_choices'], value=LANG['zh']['default_choice'])
|
119 |
-
step3_header_md = gr.Markdown("### **第三步:开始调音**"); process_button = gr.Button("开始智能调音!", variant="primary")
|
120 |
-
log_header_md = gr.Markdown("### **AI 调音师工作日志**"); status_log_md = gr.Markdown("`[系统]`:我准备好啦,等你上传文件哦~")
|
121 |
-
result_header_md = gr.Markdown("### **第四步:聆听并下载您的定制版**");
|
122 |
-
# --- 修改点 2: 添加 show_download_button=True ---
|
123 |
-
processed_audio_output = gr.Audio(label="这里会显示AI调音后的音频", type="filepath", interactive=True, show_download_button=True)
|
124 |
-
with gr.Accordion("(高级)查看 AI 定制的 EQ 参数", open=False, visible=False) as eq_accordion:
|
125 |
-
eq_sliders = [gr.Slider(minimum=-EQ_RANGE_DB, maximum=EQ_RANGE_DB, value=0, step=0.5, label=f"{f} Hz", interactive=False) for f in EQ_BANDS_HZ]
|
126 |
-
all_ui_outputs = [title_md, subtitle_md, step1_header_md, audio_input, step2_header_md, custom_input, quick_choice, step3_header_md, process_button, log_header_md, status_log_md, result_header_md, processed_audio_output, eq_accordion]
|
127 |
-
lang_switcher.change(fn=update_language, inputs=lang_switcher, outputs=all_ui_outputs, queue=False)
|
128 |
-
process_button.click(fn=process_and_tune, inputs=[audio_input, quick_choice, custom_input, lang_switcher], outputs=[status_log_md, processed_audio_output, eq_accordion, *eq_sliders])
|
129 |
-
|
130 |
-
if __name__ == "__main__":
|
131 |
-
demo.launch(
|
|
|
1 |
+
# app.py (Final Version with Download Feature)
|
2 |
+
import gradio as gr
|
3 |
+
import google.generativeai as genai
|
4 |
+
import os
|
5 |
+
import time
|
6 |
+
from pydub import AudioSegment
|
7 |
+
|
8 |
+
# --- 0. 全局配置 / Global Configuration ---
|
9 |
+
# API Key will be loaded from Hugging Face Secrets
|
10 |
+
GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
|
11 |
+
if not GOOGLE_API_KEY:
|
12 |
+
raise ValueError("Hugging Face Secret 'GOOGLE_API_KEY' not found!")
|
13 |
+
|
14 |
+
genai.configure(api_key=GOOGLE_API_KEY)
|
15 |
+
gemini_model = genai.GenerativeModel('gemini-1.5-flash-latest')
|
16 |
+
|
17 |
+
EQ_BANDS_HZ = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000]
|
18 |
+
EQ_RANGE_DB = 12
|
19 |
+
|
20 |
+
# --- 1. 双语文本库 / Bilingual Text Library (已更新提示信息) ---
|
21 |
+
LANG = {
|
22 |
+
'zh': {
|
23 |
+
'title': "FeiMatrix 智能动态调音", 'subtitle': "上传一首歌,告诉我你想要的感觉,剩下的交给我!", 'lang_label': "语言 / Language",
|
24 |
+
'step1_header': "第一步:上传音频", 'upload_label': "点击或拖拽 MP3 文件到这里",
|
25 |
+
'step2_header': "第二步:告诉我你的感觉", 'custom_input_label': "用你自己的话描述想要的感觉(推荐):",
|
26 |
+
'custom_input_placeholder': "例如:我想要吉他声更清脆,鼓声更有力...", 'quick_choice_label': "或者,快速选择一个预设风格:",
|
27 |
+
'quick_choices': [ "我想要咚咚咚的重低音!", "让高音更亮、更清楚", "让唱歌的声音更突出", "我喜欢温暖、厚实的感觉", "给我一种现场演唱会的空间感", "保持原汁原味,但细节多一点", "无 (使用上面的输入框)", ],
|
28 |
+
'default_choice': "无 (使用上面的输入框)", 'step3_header': "第三步:开始调音", 'process_button': "开始智能调音!",
|
29 |
+
'log_header': "AI 调音师工作日志", 'log_initial': "`[系统]`:我准备好啦,等你上传文件哦~", 'result_header': "第四步:聆听并下载您的定制版",
|
30 |
+
'result_label': "这里会显示AI调音后的音频", 'accordion_header': "(高级)查看 AI 定制的 EQ 参数", 'err_no_file': "哎呀,忘了上传MP3文件啦!",
|
31 |
+
'info_no_pref': "您没有指定风格,将为您进行温和的细节优化。", 'status_analyzing': "`[AI分析师]`:收到!正在分析您的音频... ⏳",
|
32 |
+
'status_analysis_failed': "AI分析失败: {e}。将使用默认调音策略。", 'status_understanding': "`[AI分析师]`:分析完成!\n> {analysis}\n\n`[AI调音师]`:正在理解{choice}并调整EQ... 🔊",
|
33 |
+
'status_tuning': "`[AI调音师]`:好嘞!已经按您的要求调整好了!\n\n`[系统]`:正在生成音频... 🎶",
|
34 |
+
# --- 修改点 1: 更新完成提示 ---
|
35 |
+
'status_done': "`[系统]`:搞定!您的AI定制版音频已生成!🎉\n(点击播放器右侧的 **⋮** 即可下载)",
|
36 |
+
},
|
37 |
+
'en': {
|
38 |
+
'title': "FeiMatrix AI Dynamic Equalizer", 'subtitle': "Upload a song, tell me the vibe you want, and I'll handle the rest!", 'lang_label': "Language / 语言",
|
39 |
+
'step1_header': "Step 1: Upload Audio", 'upload_label': "Click or drag your MP3 file here",
|
40 |
+
'step2_header': "Step 2: Tell Me Your Vibe", 'custom_input_label': "Describe the feeling you want in your own words (Recommended):",
|
41 |
+
'custom_input_placeholder': "e.g., I want the guitar to be crisper and the drums more powerful...", 'quick_choice_label': "Or, quickly pick a preset style:",
|
42 |
+
'quick_choices': [ "I want that 'thump-thump' heavy bass!", "Make the treble brighter and clearer", "Make the vocals stand out more", "I like a warm and rich sound", "Give me a live concert feeling", "Keep it natural, just add more detail", "None (use the input box above)", ],
|
43 |
+
'default_choice': "None (use the input box above)", 'step3_header': "Step 3: Start Tuning", 'process_button': "Start AI Tuning!",
|
44 |
+
'log_header': "AI Tuning Engineer's Log", 'log_initial': "`[System]`: Ready when you are, just upload a file~", 'result_header': "Step 4: Listen & Download Your Custom Version",
|
45 |
+
'result_label': "Your AI-tuned audio will appear here", 'accordion_header': "(Advanced) View AI-Customized EQ Parameters", 'err_no_file': "Oops, you forgot to upload the MP3 file!",
|
46 |
+
'info_no_pref': "You didn't specify a style, so I'll perform a gentle detail enhancement.", 'status_analyzing': "`[AI Analyst]`: Roger that! Analyzing your audio... ⏳",
|
47 |
+
'status_analysis_failed': "AI analysis failed: {e}. Default tuning strategy will be used.", 'status_understanding': "`[AI Analyst]`: Analysis complete!\n> {analysis}\n\n`[AI Tuning Engineer]`: Understanding {choice} and adjusting EQ... 🔊",
|
48 |
+
'status_tuning': "`[AI Tuning Engineer]`: Alright! Tuned to your request!\n\n`[System]`: Generating audio... 🎶",
|
49 |
+
# --- 修改点 1: 更新完成提示 ---
|
50 |
+
'status_done': "`[System]`: All set! Your AI custom audio is ready! 🎉\n(Click the **⋮** icon on the right of the player to download)",
|
51 |
+
}
|
52 |
+
}
|
53 |
+
LANG_MAP = {"简体中文": "zh", "English": "en"}
|
54 |
+
|
55 |
+
def get_ai_tuned_eq_settings(audio_analysis_text, user_preference):
|
56 |
+
eq_values = [0.0] * len(EQ_BANDS_HZ); pref_lower = user_preference.lower()
|
57 |
+
if "电子舞曲" in audio_analysis_text or "EDM" in audio_analysis_text: eq_values = [4.0, 2.5, -1.5, -0.5, 1.0, 2.0, 3.5, 4.0, 2.5, 1.0]
|
58 |
+
elif "爵士乐" in audio_analysis_text or "warm" in audio_analysis_text: eq_values = [3.0, 1.5, -0.5, -1.0, 0.5, 1.5, 2.0, 3.0, 2.0, 0.5]
|
59 |
+
elif "人声" in audio_analysis_text or "vocal" in audio_analysis_text: eq_values[4] += 0.5; eq_values[5] += 1.0
|
60 |
+
if any(k in pref_lower for k in ["低音", "bass", "thump", "punch", "蹦迪"]): eq_values[0] += 3.0; eq_values[1] += 1.5
|
61 |
+
if any(k in pref_lower for k in ["高音", "treble", "bright", "clear", "crisp"]): eq_values[6]+=1.5; eq_values[7]+=2.0; eq_values[8]+=1.0
|
62 |
+
if any(k in pref_lower for k in ["人声", "vocal", "singing", "lyrics"]): eq_values[4]+=1.5; eq_values[5]+=1.0; eq_values[3]-=0.5
|
63 |
+
if any(k in pref_lower for k in ["温暖", "warm", "rich", "soft"]): eq_values[1]+=1.5; eq_values[2]+=1.0; eq_values[6]-=0.5
|
64 |
+
if any(k in pref_lower for k in ["空间", "space", "live", "concert"]): eq_values[6]+=1.0; eq_values[7]+=1.0; eq_values[4]-=0.5
|
65 |
+
if any(k in pref_lower for k in ["自然", "natural", "original"]): [v*0.5 for v in eq_values]; eq_values[7]+=0.5
|
66 |
+
eq_values=[max(-EQ_RANGE_DB,min(EQ_RANGE_DB,v)) for v in eq_values]; return {f'{f} Hz':v for f,v in zip(EQ_BANDS_HZ, eq_values)}
|
67 |
+
|
68 |
+
def apply_eq_to_audio(audio_path, eq_settings):
|
69 |
+
try: audio = AudioSegment.from_file(audio_path)
|
70 |
+
except Exception as e: print(f"Audio load error: {e}"); return None
|
71 |
+
q_factor=1.414; filter_parts=[]
|
72 |
+
for band, gain in eq_settings.items():
|
73 |
+
if gain != 0: filter_parts.append(f"equalizer=f={band.split(' ')[0]}:width_type=q:w={q_factor}:g={gain}")
|
74 |
+
if not filter_parts: return audio_path
|
75 |
+
output_path = f"{os.path.splitext(audio_path)[0]}_eq.mp3"
|
76 |
+
try:
|
77 |
+
audio.export(output_path, format="mp3", parameters=["-af", ",".join(filter_parts)])
|
78 |
+
return output_path
|
79 |
+
except Exception as e: print(f"EQ apply error: {e}"); raise gr.Error("Failed to apply EQ! This might be an issue with ffmpeg on the server.")
|
80 |
+
|
81 |
+
def process_and_tune(audio_file, quick_choice, custom_input, lang_choice):
|
82 |
+
lang_code = LANG_MAP[lang_choice]; L = LANG[lang_code]
|
83 |
+
if not audio_file: raise gr.Error(L['err_no_file'])
|
84 |
+
if custom_input and custom_input.strip(): final_preference = custom_input
|
85 |
+
elif L['default_choice'] not in quick_choice: final_preference = quick_choice
|
86 |
+
else: final_preference = L['quick_choices'][-2]; gr.Info(L['info_no_pref'])
|
87 |
+
slider_updates={s: gr.update(value=0) for s in eq_sliders}
|
88 |
+
yield {status_log_md: gr.update(value=L['status_analyzing']), processed_audio_output: gr.update(value=None), eq_accordion: gr.update(visible=True, open=False), **slider_updates}
|
89 |
+
try:
|
90 |
+
prompt = "Briefly analyze this audio's genre, mood, and key instruments."; response = gemini_model.generate_content([genai.upload_file(path=audio_file.name), prompt])
|
91 |
+
audio_analysis_text = response.text or "(AI did not provide a detailed analysis)"
|
92 |
+
except Exception as e: audio_analysis_text = L['status_analysis_failed'].format(e=e); gr.Warning(audio_analysis_text)
|
93 |
+
choice_desc = f"“{final_preference}”"
|
94 |
+
yield {status_log_md: gr.update(value=L['status_understanding'].format(analysis=audio_analysis_text, choice=choice_desc))}; time.sleep(1)
|
95 |
+
eq_settings = get_ai_tuned_eq_settings(audio_analysis_text, final_preference); slider_updates = {s: gr.update(value=v) for s, v in zip(eq_sliders, eq_settings.values())}
|
96 |
+
yield {status_log_md: gr.update(value=L['status_tuning']), **slider_updates}; time.sleep(1)
|
97 |
+
eq_audio_path = apply_eq_to_audio(audio_file.name, eq_settings)
|
98 |
+
if not eq_audio_path: raise gr.Error("Audio processing failed!")
|
99 |
+
yield {status_log_md: gr.update(value=L['status_done']), processed_audio_output: gr.update(value=eq_audio_path, label=L['result_label'], autoplay=True), eq_accordion: gr.update(open=False)}
|
100 |
+
|
101 |
+
def update_language(lang_choice):
|
102 |
+
L = LANG[LANG_MAP[lang_choice]]
|
103 |
+
return {
|
104 |
+
title_md: gr.update(value=f"# {L['title']}"), subtitle_md: gr.update(value=L['subtitle']), step1_header_md: gr.update(value=f"### **{L['step1_header']}**"),
|
105 |
+
audio_input: gr.update(label=L['upload_label']), step2_header_md: gr.update(value=f"### **{L['step2_header']}**"), custom_input: gr.update(label=L['custom_input_label'], placeholder=L['custom_input_placeholder']),
|
106 |
+
quick_choice: gr.update(label=L['quick_choice_label'], choices=L['quick_choices'], value=L['default_choice']), step3_header_md: gr.update(value=f"### **{L['step3_header']}**"),
|
107 |
+
process_button: gr.update(value=L['process_button']), log_header_md: gr.update(value=f"### **{L['log_header']}**"), status_log_md: gr.update(value=L['log_initial']),
|
108 |
+
result_header_md: gr.update(value=f"### **{L['result_header']}**"), processed_audio_output: gr.update(label=L['result_label']), eq_accordion: gr.update(label=L['accordion_header']),
|
109 |
+
}
|
110 |
+
|
111 |
+
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
112 |
+
lang_switcher = gr.Radio(choices=["简体中文", "English"], value="简体中文", label="Language / 语言", info="选择界面语言 / Select UI Language")
|
113 |
+
title_md = gr.Markdown("# FeiMatrix 智能动态调音")
|
114 |
+
subtitle_md = gr.Markdown("上传一首歌,告诉我你想要的感觉,剩下的交给我!")
|
115 |
+
with gr.Column():
|
116 |
+
step1_header_md = gr.Markdown("### **第一步:上传音频**"); audio_input = gr.File(label="点击或拖拽 MP3 文件到这里", type="filepath", file_types=[".mp3"])
|
117 |
+
step2_header_md = gr.Markdown("### **第二步:告诉我你的感觉**"); custom_input = gr.Textbox(label="用你自己的话描述想要的感觉(推荐):", placeholder="例如:我想要吉他声更清脆,鼓声更有力...", lines=2)
|
118 |
+
quick_choice = gr.Radio(label="或者,快速选择一个预设风格:", choices=LANG['zh']['quick_choices'], value=LANG['zh']['default_choice'])
|
119 |
+
step3_header_md = gr.Markdown("### **第三步:开始调音**"); process_button = gr.Button("开始智能调音!", variant="primary")
|
120 |
+
log_header_md = gr.Markdown("### **AI 调音师工作日志**"); status_log_md = gr.Markdown("`[系统]`:我准备好啦,等你上传文件哦~")
|
121 |
+
result_header_md = gr.Markdown("### **第四步:聆听并下载您的定制版**");
|
122 |
+
# --- 修改点 2: 添加 show_download_button=True ---
|
123 |
+
processed_audio_output = gr.Audio(label="这里会显示AI调音后的音频", type="filepath", interactive=True, show_download_button=True)
|
124 |
+
with gr.Accordion("(高级)查看 AI 定制的 EQ 参数", open=False, visible=False) as eq_accordion:
|
125 |
+
eq_sliders = [gr.Slider(minimum=-EQ_RANGE_DB, maximum=EQ_RANGE_DB, value=0, step=0.5, label=f"{f} Hz", interactive=False) for f in EQ_BANDS_HZ]
|
126 |
+
all_ui_outputs = [title_md, subtitle_md, step1_header_md, audio_input, step2_header_md, custom_input, quick_choice, step3_header_md, process_button, log_header_md, status_log_md, result_header_md, processed_audio_output, eq_accordion]
|
127 |
+
lang_switcher.change(fn=update_language, inputs=lang_switcher, outputs=all_ui_outputs, queue=False)
|
128 |
+
process_button.click(fn=process_and_tune, inputs=[audio_input, quick_choice, custom_input, lang_switcher], outputs=[status_log_md, processed_audio_output, eq_accordion, *eq_sliders])
|
129 |
+
|
130 |
+
if __name__ == "__main__":
|
131 |
+
demo.launch(mcp_server=True)
|