codec: add pre-encoded voice reference (--ref-spk / --ref-rvq)

qwen-codec --talker extracts the speaker embedding (.spk, raw f32)
and the ICL codes (.rvq) in one pass, encode truncated to the hop
boundary conforming to the --ref-wav path. qwen-tts loads them via
--ref-spk / --ref-rvq and skips the speaker encoder and codec encode
on every synthesis: TTFA 205 ms -> 89 ms. Extends qt_tts_params with
ABI v2 latent fields, adds qt_num_codebooks(), ships freeman.spk +
freeman.rvq and switches clone scripts to the latent path. Output is
bit-identical to the raw path at fixed seed.
This commit is contained in:
Pascal
2026-06-11 21:45:13 +02:00
parent e8e33629c1
commit 0bf4a18b22
11 changed files with 399 additions and 144 deletions
+22 -6
View File
@@ -218,6 +218,18 @@ void qt_tts_default_params(struct qt_tts_params * p) {
p->on_chunk_user_data = nullptr;
p->codec_chunk_sec = 24.0f;
p->codec_left_context_sec = 2.0f;
p->ref_spk_emb = nullptr;
p->ref_spk_dim = 0;
p->ref_codes = nullptr;
p->ref_T = 0;
}
int qt_num_codebooks(const struct qt_context * q) {
if (!q) {
qt_set_error("qt_num_codebooks: q is NULL");
return 0;
}
return q->pt.num_code_groups;
}
struct qt_context * qt_init(const struct qt_init_params * params) {
@@ -356,22 +368,26 @@ enum qt_status qt_synthesize(struct qt_context * q, const struct qt_tts_params *
}
return QT_STATUS_MODE_INVALID;
}
if (params->ref_audio_24k && mt != "base") {
qt_set_error("--ref-wav is only valid for base models (loaded: %s)", mt.c_str());
// ABI v2 latent reference fields, same gate as the pipeline.
const bool has_lat_spk = params->abi_version >= 2 && params->ref_spk_emb && params->ref_spk_dim > 0;
const bool has_lat_codes = params->abi_version >= 2 && params->ref_codes && params->ref_T > 0;
if ((params->ref_audio_24k || has_lat_spk) && mt != "base") {
qt_set_error("--ref-wav / --ref-spk is only valid for base models (loaded: %s)", mt.c_str());
if (out) {
qt_audio_free(out);
}
return QT_STATUS_MODE_INVALID;
}
if (params->speaker && params->ref_audio_24k) {
qt_set_error("--speaker and --ref-wav are mutually exclusive");
if (params->speaker && (params->ref_audio_24k || has_lat_spk)) {
qt_set_error("--speaker and --ref-wav / --ref-spk are mutually exclusive");
if (out) {
qt_audio_free(out);
}
return QT_STATUS_INVALID_PARAMS;
}
if (params->ref_text && !params->ref_audio_24k) {
qt_set_error("--ref-text requires --ref-wav");
if (params->ref_text && !params->ref_audio_24k && !has_lat_codes) {
qt_set_error("--ref-text requires --ref-wav or --ref-rvq");
if (out) {
qt_audio_free(out);
}