cli, facade: rename --ref-audio to --ref-wav, align with omnivoice convention

This commit is contained in:
Pascal
2026-05-14 15:46:46 +02:00
parent 6ad6a7db7a
commit b1e864cb8d
7 changed files with 26 additions and 27 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ set PATH=%~dp0..\build\Release;%PATH%
qwen-tts.exe ^
--model ..\models\qwen-talker-1.7b-base-Q8_0.gguf ^
--codec ..\models\qwen-tokenizer-12hz-Q8_0.gguf ^
--ref-audio freeman.wav ^
--ref-wav freeman.wav ^
--ref-text freeman.txt ^
--lang English ^
-o clone.wav < prompt.txt
+1 -1
View File
@@ -5,7 +5,7 @@ set -eu
../build/qwen-tts \
--model ../models/qwen-talker-1.7b-base-Q8_0.gguf \
--codec ../models/qwen-tokenizer-12hz-Q8_0.gguf \
--ref-audio freeman.wav \
--ref-wav freeman.wav \
--ref-text freeman.txt \
--lang English \
-o clone.wav < prompt.txt
+4 -5
View File
@@ -288,8 +288,7 @@ bool pipeline_tts_synthesize(PipelineTTS * pt,
const float * ref_spk_emb_ptr = NULL;
if (has_ref_audio) {
if (!pt->has_speaker_encoder) {
fprintf(stderr,
"[Pipeline] FATAL: --ref-audio requires a model with a loaded speaker encoder (Base only)\n");
fprintf(stderr, "[Pipeline] FATAL: --ref-wav requires a model with a loaded speaker encoder (Base only)\n");
return false;
}
if (!speaker_encoder_extract(&pt->speaker_encoder, pt->sched, params.ref_audio_24k, params.ref_n_samples,
@@ -313,13 +312,13 @@ bool pipeline_tts_synthesize(PipelineTTS * pt,
int ref_codes_T = 0;
if (!ref_text.empty()) {
if (!has_ref_audio) {
fprintf(stderr, "[Pipeline] FATAL: --ref-text requires --ref-audio\n");
fprintf(stderr, "[Pipeline] FATAL: --ref-text requires --ref-wav\n");
return false;
}
// The codec hop is 1920 samples at 24 kHz so n_samples must be
// a multiple of 1920. Truncate to the nearest hop boundary.
if (params.ref_n_samples < QWEN_TOKENIZER_HOP_LENGTH) {
fprintf(stderr, "[Pipeline] FATAL: ref_audio too short for ICL (%d samples)\n", params.ref_n_samples);
fprintf(stderr, "[Pipeline] FATAL: ref_wav too short for ICL (%d samples)\n", params.ref_n_samples);
return false;
}
int aligned_T = (params.ref_n_samples / QWEN_TOKENIZER_HOP_LENGTH) * QWEN_TOKENIZER_HOP_LENGTH;
@@ -347,7 +346,7 @@ bool pipeline_tts_synthesize(PipelineTTS * pt,
debug_dump_2d(&d, "trailing-text-hidden", prompt.trailing_text_hidden.data(), prompt.T_trailing, prompt.hidden);
debug_dump_1d(&d, "tts-pad-embed", prompt.tts_pad_embed.data(), prompt.hidden);
// Voice clone dumps : speaker-emb fires when ref_audio is set
// Voice clone dumps : speaker-emb fires when ref_wav is set
// (modes A and B), ref-codes fires only when ref_text is also set
// (mode B ICL). Both are no-ops in base / tts / customvoice modes,
// the dump files simply do not appear in those runs.
+1 -1
View File
@@ -215,7 +215,7 @@ bool prompt_builder_build(const PipelineTTS * pt,
// Mode B requires ref_spk_emb so the speaker slot is also filled.
const bool icl = !ref_text.empty() && ref_codes != NULL && ref_codes_T > 0;
if (icl && ref_spk_emb == NULL) {
fprintf(stderr, "[Prompt] FATAL: ICL mode requires ref_spk_emb (no --ref-audio?)\n");
fprintf(stderr, "[Prompt] FATAL: ICL mode requires ref_spk_emb (no --ref-wav?)\n");
return false;
}
+3 -3
View File
@@ -328,17 +328,17 @@ enum qwen_status qwen_synthesize(struct qwen_context * q,
return QWEN_STATUS_MODE_INVALID;
}
if (params->ref_audio_24k && mt != "base") {
qt_set_error("--ref-audio is only valid for base models (loaded: %s)", mt.c_str());
qt_set_error("--ref-wav is only valid for base models (loaded: %s)", mt.c_str());
qwen_audio_free(out);
return QWEN_STATUS_MODE_INVALID;
}
if (params->speaker && params->ref_audio_24k) {
qt_set_error("--speaker and --ref-audio are mutually exclusive");
qt_set_error("--speaker and --ref-wav are mutually exclusive");
qwen_audio_free(out);
return QWEN_STATUS_INVALID_PARAMS;
}
if (params->ref_text && !params->ref_audio_24k) {
qt_set_error("--ref-text requires --ref-audio");
qt_set_error("--ref-text requires --ref-wav");
qwen_audio_free(out);
return QWEN_STATUS_INVALID_PARAMS;
}
+7 -7
View File
@@ -15,7 +15,7 @@ Cote Python the speaker embedding is captured directly via
model.extract_speaker_embedding, and the reference codec frames via
model.speech_tokenizer.encode. Both intermediates land as speaker-emb.bin
and ref-codes.bin and are compared against the C++ side dumps emitted
by pipeline-tts.cpp when --ref-audio and --ref-text are set.
by pipeline-tts.cpp when --ref-wav and --ref-text are set.
Dumps land in cpp/clone/ (C++) and python/clone/ (Python).
"""
@@ -271,10 +271,10 @@ def dump_mel_mag_python(ref_wav, dump_dir):
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--prompt", default="../examples/prompt.txt")
ap.add_argument("--ref-audio", default=DEFAULT_REF_AUDIO,
ap.add_argument("--ref-wav", default=DEFAULT_REF_AUDIO,
help="reference WAV path for voice cloning")
ap.add_argument("--ref-text-file", default=DEFAULT_REF_TEXT,
help="path to a UTF-8 file with the transcript of ref-audio")
help="path to a UTF-8 file with the transcript of ref-wav")
ap.add_argument("--seed", type=int, default=42)
ap.add_argument("--lang", default="english")
ap.add_argument("--quant", default="F32",
@@ -300,7 +300,7 @@ def main():
with open(args.ref_text_file, "r", encoding="utf-8") as f:
ref_text = f.read().strip()
print(f"[Input] Prompt: {len(text)} chars: {text[:60]}{'...' if len(text) > 60 else ''}")
print(f"[Input] RefAudio: {args.ref_audio}")
print(f"[Input] RefAudio: {args.ref_wav}")
print(f"[Input] RefText: {len(ref_text)} chars: {ref_text[:60]}{'...' if len(ref_text) > 60 else ''}")
print(f"[Input] Lang: {args.lang} Seed: {args.seed} MaxNewTokens: {args.max_new_tokens}")
print(f"[Input] Mode: greedy ICL")
@@ -328,7 +328,7 @@ def main():
# Load reference WAV. Resample to 24 kHz if needed since both the speaker
# encoder and the codec tokenizer expect 24 kHz mono input.
ref_wav, ref_sr = sf.read(args.ref_audio, always_2d=False)
ref_wav, ref_sr = sf.read(args.ref_wav, always_2d=False)
if ref_wav.ndim > 1:
ref_wav = ref_wav[:, 0]
ref_wav = ref_wav.astype(np.float32)
@@ -459,7 +459,7 @@ def main():
"--codec", model_cdc,
"--seed", str(args.seed),
"--text", text,
"--ref-audio", args.ref_audio,
"--ref-wav", args.ref_wav,
"--ref-text", ref_text,
"--lang", args.lang,
"--max-new", str(args.max_new_tokens),
@@ -467,7 +467,7 @@ def main():
"-o", args.out_cpp,
"--greedy",
]
print(f"[GGML] Cmd: {' '.join(cmd[:6])} --text [...] --ref-audio {args.ref_audio} --ref-text [...] --lang {args.lang} --max-new {args.max_new_tokens} --dump {DUMP_CPP} -o {args.out_cpp} --greedy")
print(f"[GGML] Cmd: {' '.join(cmd[:6])} --text [...] --ref-wav {args.ref_wav} --ref-text [...] --lang {args.lang} --max-new {args.max_new_tokens} --dump {DUMP_CPP} -o {args.out_cpp} --greedy")
r = subprocess.run(cmd)
if r.returncode != 0:
sys.exit(r.returncode)
+9 -9
View File
@@ -24,7 +24,7 @@
#include <string>
// Tokenizer sample rate for the 12 Hz Qwen3-TTS codec : 24 kHz. Used
// by audio_read_mono to resample the optional --ref-audio file before
// by audio_read_mono to resample the optional --ref-wav file before
// handing it to the facade. The output sample rate is reported by
// qwen_audio.sample_rate after a successful synthesis.
static const int QWEN_TTS_SAMPLE_RATE = 24000;
@@ -44,12 +44,12 @@ static void print_usage(const char * prog) {
" --instruct <s> Style instruction. Required for VoiceDesign, optional\n"
" for CustomVoice. Rejected for Base.\n"
" --speaker <name> Speaker name. Only valid for CustomVoice.\n"
" --ref-audio <wav> Reference WAV path for voice clone (Base only). Mutually\n"
" --ref-wav <wav> Reference WAV path for voice clone (Base only). Mutually\n"
" exclusive with --speaker. Mode A (x_vector_only) extracts\n"
" a speaker embedding via the ECAPA-TDNN encoder.\n"
" --ref-text <path> Path to a UTF-8 text file containing the reference\n"
" transcript for voice clone ICL mode (Base only, requires\n"
" --ref-audio). Switches the prompt to ICL mode B where the\n"
" --ref-wav). Switches the prompt to ICL mode B where the\n"
" talker conditions on the reference codec codes.\n"
" --max-new <n> Max new audio frames (default: 2048)\n"
" --format <fmt> WAV output format: wav16, wav24, wav32 (default: wav16)\n\n"
@@ -75,7 +75,7 @@ struct Args {
const char * lang;
const char * instruct;
const char * speaker;
const char * ref_audio;
const char * ref_wav;
const char * ref_text_path;
const char * dump_dir;
const char * out_wav;
@@ -165,8 +165,8 @@ static bool parse_args(int argc, char ** argv, Args & a) {
a.instruct = argv[++i];
} else if (std::strcmp(arg, "--speaker") == 0 && i + 1 < argc) {
a.speaker = argv[++i];
} else if (std::strcmp(arg, "--ref-audio") == 0 && i + 1 < argc) {
a.ref_audio = argv[++i];
} else if (std::strcmp(arg, "--ref-wav") == 0 && i + 1 < argc) {
a.ref_wav = argv[++i];
} else if (std::strcmp(arg, "--ref-text") == 0 && i + 1 < argc) {
a.ref_text_path = argv[++i];
} else if (std::strcmp(arg, "--format") == 0 && i + 1 < argc) {
@@ -249,11 +249,11 @@ static int run(const Args & a) {
std::unique_ptr<float, void (*)(void *)> raw_holder(NULL, std::free);
const float * ref_audio_24k = NULL;
int ref_n_samples = 0;
if (a.ref_audio) {
if (a.ref_wav) {
int T_in = 0;
float * raw = audio_read_mono(a.ref_audio, QWEN_TTS_SAMPLE_RATE, &T_in);
float * raw = audio_read_mono(a.ref_wav, QWEN_TTS_SAMPLE_RATE, &T_in);
if (!raw || T_in <= 0) {
fprintf(stderr, "[CLI] ERROR: cannot read --ref-audio '%s'\n", a.ref_audio);
fprintf(stderr, "[CLI] ERROR: cannot read --ref-wav '%s'\n", a.ref_wav);
if (raw) {
std::free(raw);
}