前言¶
做過 DSH 插件的開發者大多會遇到一類需求:Agent 已經能理解意圖,但沒法自己產出一段音頻成品——尤其是翻唱這種鏈路又長、又依賴本地模型和顯卡的活。常見做法是自己拼 yt-dlp、UVR 分離、RVC 換聲、對齊、混音,腳本散着放,參數和中間產物沒人管,中途崩了還得從頭再跑。
下面介紹的 dsh-cover 就是針對這件事的:一個 DSH Host 插件,把取源、分軌、定調、移調、換聲、對齊、混音、驗收、導出串成九階段流水線,用自然語言觸發,產物和質檢結果落盤到固定目錄。維護者是 lol9706262-source,倉庫許可爲 MIT,版本 1.0.0。
這是什麼¶
一句話定位:你用一句話給出歌名(或鏈接、本地文件),加音色 id,插件跑完整條翻唱流水線,把 WAV 和 320k MP3 交給 Agent 發給你。
幾個關鍵事實:
- 純 Host 插件,運行時零 npm 依賴,所有 DSP 在
runtime/*.py,由 Applio 的 Python 解釋器執行。 - 裝完需要在 DSH 主進程啓動時加載,所以裝完要重啓 DSH 主進程,不是開新會話。
- 三個工具:
cover_doctor(環境自檢)、cover_make(全流程)、cover_status(讀 run.json 看進度與閘門結果)。
安裝與啓用¶
安裝命令按倉庫原文:
dsh plugin --profile desktop add github:lol9706262-source/dsh-cover
或者從本地目錄裝(改了源碼、想斷網跑時用):
dsh plugin --profile desktop add file:C:/path/to/dsh-cover
裝完執行一次環境自檢,這一步不改任何文件:
cover_doctor
它會檢查 Python、core.py、分離器、ffmpeg、yt-dlp、音色表、顯卡、磁盤。有問題先修這個,別直接跑正式流程。
卸載:
dsh plugin --profile desktop remove dsh-cover
典型用法¶
三個入口形態,直接說就行:
用我的音色翻唱一首「山水之間」
用 myvoice 翻唱 https://www.bilibili.com/video/BV1Vb421B7XQ
用 myvoice 翻唱 D:\音樂\山水之間.mp3
Agent 收到後會調 cover_make,取源、分軌、定調、移調、換聲、對齊、混音、驗收、導出九步順序跑完,最後把成品 WAV 和 320k MP3 發回來。
取源怎麼走的¶
source 有三種形態,優先級從高到低:
- 本地文件存在 → 直接採納,不復制、不轉碼。
- http(s) 鏈接 → yt-dlp 下載;域名含
bilibili.com記作bilibili。 - 歌名關鍵詞 → 按
fetchOrder(默認inmp3,bilibili,youtube)依次試。
三種關鍵詞取源裏,inmp3 命中率最高且不需要 cookie,但碼率只有 128k 或 96~98k AAC;bilibili 音質最好(30280 音軌常見 184~263k);youtube 的 ytsearch1: 兜底,目前會被 bot 校驗擋住。碼率擇優規則是:inmp3 拿到的碼率低於 fetchMinKbps(默認 160)時,自動再試一次 B 站,只有當 B 站那份高出 8 kbps 以上才替換,否則保留 inmp3。全過程記在 fallback_log 裏。
一個容易踩的點:關鍵詞取源時目錄名用關鍵詞本身(如 晴天 周杰倫),不用 B 站標題——B 站標題常帶「【Hi-Res無損】」這類裝飾,會把目錄名弄亂。真正的命中標題在 run.json 的 fetch.picked.title 裏。
三個工具怎麼分工¶
| 工具 | 作用 |
|---|---|
cover_doctor |
環境自檢,檢查 Python/core.py/分離器/ffmpeg/yt-dlp/音色表/顯卡/磁盤。不改文件。 |
cover_make |
全流程出成品:取源 → 分軌 → 定調 → 移調 → 換聲 → 對齊 → 混音 → 驗收 → 導出 |
cover_status |
讀 run.json,看進度、參數、閘門結果、成品路徑 |
排查問題時先看 cover_status,它直接從 run.json 摘當前階段和耗時,不用自己翻日誌。
產物與續跑¶
<coversRoot>\<歌名>\ 下的結構:
run.json 每階段狀態與耗時,斷了可以 resume
src\ 取源(URL → WAV)
sep\wav\ UVR-MDX 分出來的 Vocals / Instrumental
sep\instrumental_shiftN.wav 降調後的伴奏(|pitch|≥1.5 且非 0 才生成)
conv\vocal_<voiceId>.wav 換聲結果(44.1 kHz 單聲道)
mix\report.json 三檔變體的參數與 QC 實測值
mix\gates.json 閘門逐項判定
out\*_標準版.wav 成品 WAV(三檔 + 幹聲)
out\*.mp3 320k MP3
out\provenance.json 模型 hash / 定調依據 / 對齊結果 / 閘門結論
設計上做了兩點收斂:音頻只寫 out/,mix/ 只留 report.json 和 gates.json,同一份 WAV 不會存兩遍;run.json 每階段帶 inputs 指紋(文件大小 + mtime,換聲那步另帶 pitch/音色/分塊參數),指紋對不上就重算,不會拿舊結論套新產物。老 run.json 沒有 inputs 字段的一律視爲過期。
定調怎麼做的¶
沒有靠先跑一遍推理猜,而是直接對比兩個 f0 中位:
半音數 = 12 × log2(音色中位 / 原唱中位)
音色中位來自 _voices/<voiceId>.json 的 f0_median_hz(訓練集 f0 中位,作爲「你能舒服唱到哪」的基準),原唱中位從原聲軌裏用 pyin 提取。高位超出音色上限 3.5 半音以上 → 降調;低位不夠 → 升調;否則保持 0。--pitch 可以硬覆蓋。
然後按絕對半音數分檔:
| |pitch| | 模式 | 做法 |
|—|—|—|
| < 1.5 | none | 取 0,不動 |
| ≥ 1.5 | both | 人聲 --pitch + 伴奏 asetrate/atempo 一起移 |
沒有「只動人聲」這檔。人聲一旦移調、伴奏留在原調,整首歌必然跑調。早期版本留過 vocal-only,實測就是錯的,已刪除。這是這套流水線裏很關鍵的一條取捨:寧可不移,也不要只移一半。
顯存約束下的長歌處理¶
convert.py 默認按 --chunk-seconds 120 切塊,切點吸附到 ±8 秒窗口裏能量最低的幀(換氣或間奏),塊間 0.3 秒重疊做等功率交叉淡化;單塊失敗自動對半再切,下限 20 秒。
必須切的原因很具體:整首 4 分半的人聲一次喂進 RVC,cuDNN 會拋 RuntimeError: GET was unable to find an engine to execute this computation——那是 CUDA 側 ALLOC_FAILED 被 torch 翻譯成的說法。倉庫裏 1050 Ti 4 GB 的實測是 4m12s 能過、4m30s 必崩,就是顯存峯值貼着上限。
第二個坑是採樣率。Applio 輸出的是音色模型自己的採樣率(常見 40000 或 48000 Hz),不是輸入文件的採樣率。早期版本把 40000 Hz 的樣本當 44100 Hz 用,等於每塊都被壓快 0.907 倍再補零,拼起來整首歌是碎的,表現就是「對齊偏差 1800ms、相關 0.36」。現在讀回來先 librosa.resample 到目標採樣率再拼。這條已經修過,屬於真實踩過的坑。
混音與閘門¶
混音出三檔,默認參數如下,倉庫裏說實測可復現(峯值 −0.58 / RMS −13.1 / crest 12.5):
| 變體 | 人聲增益 | 伴奏增益 | 混響 wet |
|---|---|---|---|
| standard 標準版 | +4.0 dB | −2.0 dB | 0.11 |
| forward 人聲靠前版 | +6.5 dB | −3.0 dB | 0.09 |
| blend 融合版 | +3.0 dB | −1.5 dB | 0.15 |
母帶順序固定:先提增益到 −13 dBFS RMS,再 tanh 軟限幅(ceiling 0.94 / knee 0.65)。反了會把動態壓平。
成品閘門:
| 項 | 限 |
|---|---|
| peak | ≤ −0.3 dBFS |
| rms | −16.0 ~ −10.0 dBFS |
| crest | ≥ 10.0 dB |
| clipped | = 0 |
| stereo 相關 | 0.10 ~ 0.995(≥0.99 視爲近單聲道,放行) |
4.5–9.5 kHz 的窄帶哨音指標不參與判定,只作參考值輸出。閘門不過默認不導出,force: true 可強制;幹聲軌不參與成品閘門。
參數速查¶
cover_make 常用參數:
| 參數 | 默認 | 說明 |
|---|---|---|
source |
必填 | URL / 本地路徑 / 歌名關鍵詞(走 ytsearch) |
voice |
_voices/ 裏第一個音色 |
音色 id |
pitch |
自動 | 強制移調半音數 |
variants |
三檔全出 | standard / forward / blend |
preset |
default | default / slow_ballad / fast_pop |
target_rms |
−13.0 | 母帶目標 |
index_rate |
0.75 | 發飄往上調,貼原唱往下調 |
protect |
0.33 | 齒音糊往上調到 0.45 |
resume |
true | 沿用已有階段(產物指紋沒變才沿用) |
chunk_seconds |
120 | 換聲分塊秒數,4 GB 顯卡別超過 150 |
force |
false | 閘門不過也導出 |
音色表怎麼配¶
<voicesDir>\<voiceId>.json,一個音色一個文件,key 就是音色 id:
{
"myvoice": {
"pth": "<applioRoot>\\logs\\myvoice\\myvoice_300e_27900s.pth",
"index": "<applioRoot>\\logs\\myvoice\\myvoice.index",
"sample_rate": 40000,
"f0_median_hz": 172.89,
"f0_p95_hz": 258.75,
"f0_min_hz": 53.63,
"f0_max_hz": 319.34,
"defaults": { "index_rate": 0.75, "protect": 0.33, "volume_envelope": 1.0, "f0_method": "rmvpe" }
}
}
兩點容易配錯:sample_rate 必須是 RVC 模型你自己的採樣率(看訓練配置),不是輸入音頻的採樣率;f0_median_hz 是定調的基準,把它填成你訓練集 f0 中位,插件才能判斷這首歌你唱着是舒服還是夠不着。往這個目錄塞新的 json 後,cover_doctor 會認出來。
實測臺賬¶
以下是倉庫裏帶的實測記錄,供評估耗時參考(1050 Ti 4 GB):
| 歌 | 時長 | 定調 | 全過程 | 結果 |
|---|---|---|---|---|
| 山水之間 許嵩 | 276.06 s(本地 48k 單聲道) | 220.33 Hz → keep 0 | 374 s | 三檔全過 |
| 夜空中最亮的星 逃跑計劃 | 252.25 s(inmp3 128k) | 185.27 Hz → pitch −5 / both | 397 s | 三檔全過 |
| 晴天 周杰倫 | 269.50 s(B 站 184k) | 238.89 Hz → keep 0 | 299 s | 三檔全過 |
| 平凡之路 朴樹 | 303.25 s(B 站 211k) | 142.04 Hz → keep 0 | 483 s | 三檔全過 |
對應「山水之間 276.06s」的分階段耗時:取源 0.2 s / 分軌 62 s / 定調 36 s / 換聲 223 s / 對齊 5.6 s / 混音 36 s / 驗收 0.1 s / 導出 12 s,合計 374 s。成品的 QC 是 peak −0.609、RMS −13.032、crest 12.42、削波 0,三檔全過閘門。
依賴邊界與已知限制¶
依賴邊界:只需要 Applio env 裏已有的 numpy / soundfile / librosa / scipy;不引入 pyloudnorm / pedalboard / pyrubberband;ffmpeg 用 <applioRoot>\ffmpeg.exe(該版本 amix 沒有 normalize,所以混音走 Python 逐樣本);移調用 asetrate + aresample + atempo,時長不變。
已知邊界也寫得直白:
- 只有 2-stem UVR 分離,做不了主唱/和聲分離。
- 分離階段沒有分塊機制,長歌的分軌顯存峯值取決於 audio-separator 自身。
- 5m03s(平凡之路)分軌 62 秒、換聲 4m56s,全程 483 秒。
- yt-dlp 走 YouTube,國內網絡需要自己解決,直鏈能下、搜索常被 bot 校驗擋住;真要搜 YouTube 可加
cookiesFromBrowser: "chrome"或直接貼鏈接。
適用場景與注意¶
適合這類場景:已經有 Applio + RVC 音色模型,想讓 Agent 在對話裏直接出成品翻唱的開發者;尤其適合顯存只有 4 GB 級、需要分塊機制才跑得動的顯卡環境,以及想用一個固定目錄約定管住中間產物、不留散裝腳本的人。
注意兩點。其一,這是 Host 插件,以當前 DSH 主進程的權限運行,安裝前應先讀一遍源碼和許可證(MIT)再裝,確認和你現有 Applio/RVC 環境兼容。其二,它不解決取源和版權問題:用 yt-dlp 抓 B 站或 YouTube 的伴奏,以及用它翻唱並傳播成品,都由使用者自己判斷和負責——這個插件本身只是把取源、分離、換聲和質檢串起來。
結尾¶
dsh-cover 的價值在於把翻唱這條鏈路壓成一句自然語言加一個音色 id,中間的取源擇優、分塊顯存控制、隨機器約束的定調、混音閘門由插件在九步裏做完,產出統一落在 out/。它不解決分離粒度和版權,但把「跑得動、斷得明白、能復現」這三件事做到了一致的水位。
- 目錄頁:https://www.skillhub.cn/plugins/lol9706262-source/dsh-cover
- GitHub:https://github.com/lol9706262-source/dsh-cover