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
羽毛球分组比赛记分
小程序二维码

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

小夜