這是什麼
把一首歌(MP3、YouTube 連結、MIDI,或別人寫好的 MML)自動改編成三條單音旋律線,
寫成《倩女幽魂》編曲介面吃得下的 MML,分別貼進 音軌A / 音軌B / 音軌C。
三軌會同時播放,是一起組成一首曲子,不是主旋律配伴奏。
全部在你自己的電腦上跑,網址是 127.0.0.1,別人連不到,音檔也不會上傳到任何地方。
處理流程
- 取得音訊 拖檔案、貼 YouTube 連結,用 yt-dlp 抓音訊,或直接給 MIDI/MML。
- 採譜 音檔 → MIDI。預設 Spotify Basic Pitch,什麼樂器都聽、速度快;
- 抓速度、對格線 從每個音的起音時間推算速度,再把每個音貼到節拍格線上。
- 分成三軌 每一刻最高的音給 A 軌、最低的給 C 軌、中間留一個給 B 軌, 各自整理成單音線條。長音會擋住底下的琶音,免得左手伴奏爬進主旋律。
- 副歌讓路 抓出主旋律衝到高音的段落,在那些段落裡做三件事: 讓路——離主旋律太遠的中聲部音整個讓掉,低音改成少走動的長音,兩軌音量各退一點; 撐住——主旋律底下空著的地方補一個低八度的支撐音,高音才不會單薄; 推上去——主旋律在這些段落鎖在最大音量。 量過實際的曲子:不做這些的話,副歌裡中聲部有八成以上的時間在響、比主旋律低兩個八度, 有的曲子中聲部甚至比主旋律還大聲;而主旋律副歌內外的音量幾乎一樣,整首沒有起伏。 這一步不改主旋律的任何一個音,只動它的音量。 貼上現成 MML 的時候整套都不套用——那是別人一個音一個音寫出來的,盡量保留原樣。
- 配音量 用整首的力度分佈正規化。採譜模型輸出的力度偏保守, 直接換算會整首擠在很小聲的區間,並以樂句為單位變化。
- 塞進字數 從最豐富的版本往下試,挑第一個塞得進上限的。 每軌各自挑,不會因為一軌超標就把三軌都簡化。
為什麼每個音都寫長度
這一系網易/天諭的樂譜解析器沒有 l(預設音長)指令:
社群整理的符號表沒有它、實際流傳的譜每個音都寫明長度、
社群的 MIDI→MML 轉換器解析器也只認 t o v > < : 和音名。
如果程式寫了
l16 然後用裸音符,而遊戲忽略 l16
把裸音符當成四分音符,結果就是音高全對、節奏全平 —— 整首變成一樣長的「咚咚咚」,
而且各軌長度不一致還會互相對不上。所以這裡每個音、每個休止符都標長度。速度 t 則寫在三軌各自的開頭,三軌都要有,只寫在一軌會讓其他兩軌用預設速度跑掉。
用到的資源
| 專案 | 用途 | 授權 |
|---|---|---|
| Spotify Basic Pitch | 預設採譜模型,跑套件內附的 ONNX 版,228 KB | Apache-2.0 |
| yt-dlp | YouTube 連結抓音訊 | Unlicense |
| FFmpeg · imageio-ffmpeg | 音訊轉檔 | LGPL / GPL |
| librosa | 音訊分析、鼓點偵測 | ISC |
| mido | 讀寫 MIDI | MIT |
| soundfile · libsndfile | 讀 MP3/WAV | BSD / LGPL |
| NumPy · ONNX Runtime | 數值運算、模型推論 | BSD / MIT |
網頁後端只用 Python 內建的 http.server,沒有其他框架。
試聽是瀏覽器的 Web Audio API 即時合成,沒有取樣音源。
檔案結構
| 檔案 | 做什麼 |
|---|---|
mabi3.py | 轉換核心:採譜、抓速度、分三軌、配音量、寫 MML。也可以單獨當指令用。 |
server.py | 網頁後端,Python 內建 http.server,跑在 127.0.0.1:8765。 |
index.html | 介面:鋼琴捲軸、試聽合成器、調整面板。 |
about.html | 這一頁。 |
安裝.bat | 第一次安裝:basic-pitch 要用 --no-deps,否則會拉 TensorFlow 並降 numpy。 |
啟動網頁.bat | 開伺服器並打開瀏覽器。 |
轉換.bat | 把檔案拖上去直接轉,不開網頁。 |
jobs/ | 每次轉換的暫存資料夾(原檔、採譜出來的 MIDI、結果)。 |
限制
- 三條線都是單音。同一時刻超過三個音,多的一定會被丟掉,這是三軌的硬限制。
- 轉換過程的檔案(上傳的音檔、採譜的 MIDI、產生的 MML)放在系統暫存區, 關掉程式就全部刪掉,不會留在硬碟上。所以重開之後就抓不回上一次的結果了。
- 網頁試聽只是簡單合成,跟遊戲音色不一樣,以遊戲裡聽到的為準。
- 採譜是機器聽的。人聲、鼓、破音多的原曲會亂;該樂器的獨奏 Cover 最準。
- 架子鼓是用起音偵測分頻帶抓的(不是 AI 模型),複雜的鼓組會誤判。
- 樂譜欄不收中文、中文標點和
¥ $ @ * \ [ ] | %,所以不要貼整段MML@…;,要用單軌的「複製」。