dsh-cover:把一首歌翻唱到你自己音色上,一條命令跑完

前言

做過 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 有三種形態,優先級從高到低:

  1. 本地文件存在 → 直接採納,不復制、不轉碼。
  2. http(s) 鏈接 → yt-dlp 下載;域名含 bilibili.com 記作 bilibili。
  3. 歌名關鍵詞 → 按 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
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜