前言¶
做过 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