| """ |
| ACE-Step v1.5 音樂生成器 |
| 基於 Gradio 的 Web UI,調用 ACE-Step 官方 API 進行音樂生成 |
| """ |
|
|
| import os |
| import base64 |
| import tempfile |
| import httpx |
| import gradio as gr |
| from dotenv import load_dotenv |
|
|
| |
| load_dotenv() |
|
|
| |
| API_BASE_URL = os.environ.get("ACEMUSIC_API_URL", "https://api.acemusic.ai") |
| API_KEY = os.environ.get("ACEMUSIC_API_KEY", "") |
|
|
|
|
| def generate_music( |
| prompt: str, |
| lyrics: str, |
| vocal_language: str, |
| duration: float, |
| bpm: int, |
| instrumental: bool, |
| sample_mode: bool, |
| use_format: bool, |
| progress=gr.Progress() |
| ): |
| """ |
| 調用 ACE-Step API 生成音樂 |
| |
| Args: |
| prompt: 音樂描述 |
| lyrics: 歌詞(選填) |
| vocal_language: 人聲語言 |
| duration: 音訊時長(秒) |
| bpm: 節拍速度 |
| instrumental: 是否為純音樂 |
| sample_mode: 是否使用自然語言模式 |
| use_format: 是否使用 LLM 格式化增強 |
| progress: Gradio 進度條 |
| |
| Returns: |
| tuple: (音訊檔案路徑, 元數據文字, 錯誤訊息) |
| """ |
| if not prompt.strip(): |
| return None, "", "請輸入音樂描述" |
| |
| if not API_KEY: |
| return None, "", "未設定 API Key,請在環境變數中設定 ACEMUSIC_API_KEY" |
| |
| progress(0.1, desc="準備請求...") |
| |
| |
| if lyrics.strip(): |
| |
| content = f"<prompt>{prompt}</prompt>\n<lyrics>{lyrics}</lyrics>" |
| elif sample_mode: |
| |
| content = prompt |
| else: |
| |
| content = f"<prompt>{prompt}</prompt>" |
| |
| |
| request_body = { |
| "messages": [ |
| {"role": "user", "content": content} |
| ], |
| "audio_config": { |
| "duration": duration, |
| "vocal_language": vocal_language, |
| }, |
| "sample_mode": sample_mode, |
| "use_format": use_format, |
| } |
| |
| |
| if bpm > 0: |
| request_body["audio_config"]["bpm"] = bpm |
| |
| if instrumental: |
| request_body["audio_config"]["instrumental"] = True |
| |
| progress(0.2, desc="發送請求到 API...") |
| |
| try: |
| |
| with httpx.Client(timeout=600) as client: |
| response = client.post( |
| f"{API_BASE_URL}/v1/chat/completions", |
| headers={ |
| "Content-Type": "application/json", |
| "Authorization": f"Bearer {API_KEY}" |
| }, |
| json=request_body |
| ) |
| |
| progress(0.8, desc="處理回應...") |
| |
| |
| if response.status_code == 401: |
| return None, "", "API Key 無效或未授權" |
| elif response.status_code == 429: |
| return None, "", "服務繁忙,請稍後再試" |
| elif response.status_code == 503: |
| return None, "", "模型尚未初始化,請稍後再試" |
| elif response.status_code == 504: |
| return None, "", "生成逾時,請嘗試縮短時長" |
| elif response.status_code != 200: |
| return None, "", f"API 錯誤: {response.status_code} - {response.text}" |
| |
| |
| result = response.json() |
| |
| |
| choices = result.get("choices", []) |
| if not choices: |
| return None, "", "API 回應格式錯誤:缺少 choices" |
| |
| message = choices[0].get("message", {}) |
| audio_list = message.get("audio", []) |
| |
| if not audio_list: |
| return None, "", "API 回應中沒有音訊資料" |
| |
| |
| audio_url = audio_list[0].get("audio_url", {}).get("url", "") |
| |
| if not audio_url: |
| return None, "", "音訊 URL 為空" |
| |
| |
| progress(0.9, desc="解碼音訊...") |
| |
| |
| if audio_url.startswith("data:"): |
| b64_data = audio_url.split(",", 1)[1] |
| else: |
| b64_data = audio_url |
| |
| audio_bytes = base64.b64decode(b64_data) |
| |
| |
| with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as f: |
| f.write(audio_bytes) |
| audio_path = f.name |
| |
| |
| content_text = message.get("content", "音樂生成成功") |
| |
| progress(1.0, desc="完成!") |
| |
| return audio_path, content_text, "" |
| |
| except httpx.TimeoutException: |
| return None, "", "請求逾時,請稍後再試" |
| except httpx.RequestError as e: |
| return None, "", f"網路錯誤: {str(e)}" |
| except Exception as e: |
| return None, "", f"發生錯誤: {str(e)}" |
|
|
|
|
| |
| def create_ui(): |
| """建立 Gradio UI""" |
| |
| with gr.Blocks( |
| title="ACE-Step 音樂生成器", |
| theme=gr.themes.Soft(), |
| css=""" |
| .container { max-width: 1200px; margin: auto; } |
| .header { text-align: center; margin-bottom: 20px; } |
| .output-audio { margin-top: 20px; } |
| """ |
| ) as demo: |
| |
| gr.HTML(""" |
| <div class="header"> |
| <h1>🎵 ACE-Step v1.5 音樂生成器</h1> |
| <p>使用 AI 生成高品質音樂 - 支援多種風格與語言</p> |
| </div> |
| """) |
| |
| with gr.Row(): |
| |
| with gr.Column(scale=1): |
| gr.Markdown("### 📝 輸入設定") |
| |
| prompt_input = gr.Textbox( |
| label="音樂描述", |
| placeholder="描述您想要的音樂風格、情緒、場景...\n例如:輕快的華語流行歌曲,鋼琴伴奏", |
| lines=3, |
| max_lines=5 |
| ) |
| |
| lyrics_input = gr.Textbox( |
| label="歌詞(選填)", |
| placeholder="輸入歌詞,支援結構標記...\n\n[Verse 1]\n第一段歌詞\n\n[Chorus]\n副歌歌詞", |
| lines=5, |
| max_lines=10 |
| ) |
| |
| with gr.Accordion("進階設定", open=False): |
| sample_mode_check = gr.Checkbox( |
| label="自然語言模式", |
| value=False, |
| info="啟用後,系統會自動生成提示詞和歌詞" |
| ) |
| |
| use_format_check = gr.Checkbox( |
| label="LLM 格式化增強", |
| value=False, |
| info="使用 LLM 增強提示詞和歌詞" |
| ) |
| |
| |
| with gr.Column(scale=1): |
| gr.Markdown("### ⚙️ 參數設定") |
| |
| language_dropdown = gr.Dropdown( |
| label="人聲語言", |
| choices=[ |
| ("中文", "zh"), |
| ("英文", "en"), |
| ("日文", "ja"), |
| ("韓文", "ko"), |
| ("自動", "auto") |
| ], |
| value="zh", |
| interactive=True |
| ) |
| |
| duration_slider = gr.Slider( |
| label="時長(秒)", |
| minimum=10, |
| maximum=120, |
| value=30, |
| step=5, |
| interactive=True |
| ) |
| |
| bpm_input = gr.Slider( |
| label="BPM(選填,0 表示自動決定)", |
| minimum=0, |
| maximum=200, |
| value=0, |
| step=1, |
| interactive=True |
| ) |
| |
| instrumental_check = gr.Checkbox( |
| label="純音樂(無人聲)", |
| value=False, |
| interactive=True |
| ) |
| |
| |
| generate_btn = gr.Button( |
| "🎵 生成音樂", |
| variant="primary", |
| size="lg" |
| ) |
| |
| |
| gr.Markdown("### 🎧 生成結果") |
| |
| with gr.Row(): |
| with gr.Column(scale=2): |
| audio_output = gr.Audio( |
| label="生成的音訊", |
| type="filepath", |
| interactive=False |
| ) |
| |
| with gr.Column(scale=1): |
| metadata_output = gr.Textbox( |
| label="元數據", |
| lines=10, |
| max_lines=15, |
| interactive=False |
| ) |
| |
| error_output = gr.Textbox( |
| label="錯誤訊息", |
| visible=False |
| ) |
| |
| |
| generate_btn.click( |
| fn=generate_music, |
| inputs=[ |
| prompt_input, |
| lyrics_input, |
| language_dropdown, |
| duration_slider, |
| bpm_input, |
| instrumental_check, |
| sample_mode_check, |
| use_format_check |
| ], |
| outputs=[ |
| audio_output, |
| metadata_output, |
| error_output |
| ] |
| ) |
| |
| |
| gr.Markdown(""" |
| --- |
| ### 📖 使用說明 |
| |
| **輸入模式:** |
| 1. **標籤模式**(推薦):直接輸入音樂描述,系統會自動識別 |
| 2. **歌詞模式**:輸入歌詞(支援 `[Verse]`、`[Chorus]` 等標記) |
| 3. **自然語言模式**:用自然語言描述,讓 AI 自動生成 |
| |
| **參數說明:** |
| - **時長**:生成音訊的長度(10-120 秒) |
| - **BPM**:節拍速度,留空由系統自動決定 |
| - **純音樂**:勾選後不會生成人聲 |
| |
| --- |
| Powered by [ACE-Step](https://acemusic.ai) | API: `api.acemusic.ai` |
| """) |
| |
| return demo |
|
|
|
|
| |
| if __name__ == "__main__": |
| demo = create_ui() |
| demo.launch( |
| server_name="0.0.0.0", |
| server_port=7860, |
| share=False |
| ) |
|
|