qwen-tts: drop --text, align help on omnivoice style
This commit is contained in:
+34
-54
@@ -9,8 +9,7 @@
|
|||||||
// from GGUF metadata at qt_init time. The CLI surface mirrors the
|
// from GGUF metadata at qt_init time. The CLI surface mirrors the
|
||||||
// omnivoice.cpp tooling: kebab-case flags, --format wav16/wav24/wav32,
|
// omnivoice.cpp tooling: kebab-case flags, --format wav16/wav24/wav32,
|
||||||
// -o '-' streams to stdout, --seed -1 means non deterministic
|
// -o '-' streams to stdout, --seed -1 means non deterministic
|
||||||
// (resolved inside qt_synthesize), the utterance text comes from
|
// (resolved inside qt_synthesize), the utterance text is read from stdin.
|
||||||
// --text or stdin if --text is absent.
|
|
||||||
|
|
||||||
#include "audio-io.h"
|
#include "audio-io.h"
|
||||||
#include "qwen.h"
|
#include "qwen.h"
|
||||||
@@ -26,59 +25,44 @@
|
|||||||
static void print_usage(const char * prog) {
|
static void print_usage(const char * prog) {
|
||||||
fprintf(stderr, "qwentts.cpp %s\n\n", qt_version());
|
fprintf(stderr, "qwentts.cpp %s\n\n", qt_version());
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Usage: %s --model <gguf> --codec <gguf> [options] -o <out.wav>\n\n"
|
"Usage: %s --model <gguf> --codec <gguf> [options] -o <out.wav> < text.txt\n\n"
|
||||||
"Required:\n"
|
"Required:\n"
|
||||||
" --model <gguf> Talker LM GGUF (qwen-talker-*.gguf)\n"
|
" --model <gguf> Talker LM GGUF (qwen-talker-*.gguf)\n"
|
||||||
" --codec <gguf> Tokenizer GGUF (qwen-tokenizer-*.gguf)\n"
|
" --codec <gguf> Codec GGUF (qwen-tokenizer-*.gguf)\n"
|
||||||
" -o <path> Output WAV. '-' streams to stdout (pipe friendly).\n\n"
|
" -o <path> Output WAV (24 kHz mono). '-' streams to stdout (pipe friendly).\n\n"
|
||||||
"Input:\n"
|
"Input:\n"
|
||||||
" --text <s> Utterance text. If absent, stdin is read fully.\n\n"
|
" stdin Target text to synthesise. Read fully then synthesised in one shot.\n\n"
|
||||||
"Synthesis options:\n"
|
"Optional:\n"
|
||||||
" --lang <name> Language (auto, english, chinese, ...) (default: english)\n"
|
" --format <fmt> WAV output format: wav16, wav24, wav32 (default: wav16)\n"
|
||||||
" --instruct <s> Style instruction. Required for VoiceDesign, optional\n"
|
" --lang <name> Language label (default: english)\n"
|
||||||
" for CustomVoice. Rejected for Base.\n"
|
" --instruct <str> Style instruction. Required for VoiceDesign, optional for\n"
|
||||||
" --speaker <name> Speaker name. Only valid for CustomVoice.\n"
|
" CustomVoice, rejected for Base\n"
|
||||||
" --ref-wav <wav> Reference WAV path for voice clone (Base only). Mutually\n"
|
" --speaker <name> Speaker name (CustomVoice only)\n"
|
||||||
" exclusive with --speaker. Mode A (x_vector_only) extracts\n"
|
" --ref-wav <path> Reference WAV for voice cloning (Base only)\n"
|
||||||
" a speaker embedding via the ECAPA-TDNN encoder.\n"
|
" --ref-text <path> Transcript file for the reference (enables ICL clone mode)\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-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"
|
" --max-new <n> Max new audio frames (default: 2048)\n"
|
||||||
" --format <fmt> WAV output format: wav16, wav24, wav32 (default: wav16)\n\n"
|
" --codec-chunk-dur <f> Codec decode chunk duration in seconds (default: 24.0)\n"
|
||||||
"Sampling options:\n"
|
" --codec-left-dur <f> Codec decode left context duration in seconds (default: 2.0)\n\n"
|
||||||
" --seed <n> Sampling seed, -1 for random (default: -1)\n"
|
"Sampling:\n"
|
||||||
|
" --seed <int> Sampling seed (default: -1 for random)\n"
|
||||||
" --greedy Disable stochastic sampling on both stacks\n"
|
" --greedy Disable stochastic sampling on both stacks\n"
|
||||||
" --temp <f> Talker temperature (default: 0.9)\n"
|
" --temp <f> Talker temperature (default: 0.9)\n"
|
||||||
" --top-k <n> Talker top-k (default: 50, 0 = disabled)\n"
|
" --top-k <n> Talker top-k (default: 50, 0 disables)\n"
|
||||||
" --top-p <f> Talker top-p (default: 1.0)\n"
|
" --top-p <f> Talker top-p (default: 1.0)\n"
|
||||||
" --rep-pen <f> Talker repetition penalty (default: 1.05)\n"
|
" --rep-pen <f> Talker repetition penalty (default: 1.05)\n"
|
||||||
" --sub-temp <f> Sub-talker temperature (default: 0.9)\n"
|
" --sub-temp <f> Sub-talker temperature (default: 0.9)\n"
|
||||||
" --sub-top-k <n> Sub-talker top-k (default: 50)\n"
|
" --sub-top-k <n> Sub-talker top-k (default: 50)\n"
|
||||||
" --sub-top-p <f> Sub-talker top-p (default: 1.0)\n\n"
|
" --sub-top-p <f> Sub-talker top-p (default: 1.0)\n\n"
|
||||||
"Backend options:\n"
|
|
||||||
" --no-fa Disable flash attention (manual F32 attention chain)\n"
|
|
||||||
" --clamp-fp16 Clamp hidden states + V to FP16 range (sub Ampere CUDA)\n\n"
|
|
||||||
"Codec decode framing:\n"
|
|
||||||
" --codec-chunk-sec <f> Decode chunk size in seconds (default: 24.0,\n"
|
|
||||||
" = 300 frames at 12.5 Hz, matches the upstream\n"
|
|
||||||
" Qwen3-TTS 12 Hz tokenizer chunked_decode).\n"
|
|
||||||
" Lower values reduce streaming latency at the\n"
|
|
||||||
" cost of more frequent codec passes.\n"
|
|
||||||
" --codec-left-context-sec <f> Left context window in seconds (default: 2.0,\n"
|
|
||||||
" = 25 frames at 12.5 Hz, upstream default).\n"
|
|
||||||
" Re uses previously decoded frames to remove\n"
|
|
||||||
" edge artefacts at chunk boundaries.\n\n"
|
|
||||||
"Debug:\n"
|
"Debug:\n"
|
||||||
" --dump <dir> Dump intermediate tensors for cossim debug\n",
|
" --no-fa Disable flash attention\n"
|
||||||
|
" --clamp-fp16 Clamp hidden states + V to FP16 range\n"
|
||||||
|
" --dump <dir> Dump intermediate tensors (f32) to <dir>\n",
|
||||||
prog);
|
prog);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Args {
|
struct Args {
|
||||||
const char * model;
|
const char * model;
|
||||||
const char * codec;
|
const char * codec;
|
||||||
const char * text;
|
|
||||||
const char * lang;
|
const char * lang;
|
||||||
const char * instruct;
|
const char * instruct;
|
||||||
const char * speaker;
|
const char * speaker;
|
||||||
@@ -105,7 +89,7 @@ struct Args {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Read all of stdin into a string. Trims trailing newlines so a piped
|
// Read all of stdin into a string. Trims trailing newlines so a piped
|
||||||
// text file behaves like a clean --text argument.
|
// text file behaves like clean utterance input.
|
||||||
static std::string read_stdin_text() {
|
static std::string read_stdin_text() {
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << std::cin.rdbuf();
|
ss << std::cin.rdbuf();
|
||||||
@@ -172,8 +156,6 @@ static bool parse_args(int argc, char ** argv, Args & a) {
|
|||||||
a.model = argv[++i];
|
a.model = argv[++i];
|
||||||
} else if (std::strcmp(arg, "--codec") == 0 && i + 1 < argc) {
|
} else if (std::strcmp(arg, "--codec") == 0 && i + 1 < argc) {
|
||||||
a.codec = argv[++i];
|
a.codec = argv[++i];
|
||||||
} else if (std::strcmp(arg, "--text") == 0 && i + 1 < argc) {
|
|
||||||
a.text = argv[++i];
|
|
||||||
} else if (std::strcmp(arg, "--lang") == 0 && i + 1 < argc) {
|
} else if (std::strcmp(arg, "--lang") == 0 && i + 1 < argc) {
|
||||||
a.lang = argv[++i];
|
a.lang = argv[++i];
|
||||||
} else if (std::strcmp(arg, "--instruct") == 0 && i + 1 < argc) {
|
} else if (std::strcmp(arg, "--instruct") == 0 && i + 1 < argc) {
|
||||||
@@ -218,9 +200,9 @@ static bool parse_args(int argc, char ** argv, Args & a) {
|
|||||||
a.use_fa = false;
|
a.use_fa = false;
|
||||||
} else if (std::strcmp(arg, "--clamp-fp16") == 0) {
|
} else if (std::strcmp(arg, "--clamp-fp16") == 0) {
|
||||||
a.clamp_fp16 = true;
|
a.clamp_fp16 = true;
|
||||||
} else if (std::strcmp(arg, "--codec-chunk-sec") == 0 && i + 1 < argc) {
|
} else if (std::strcmp(arg, "--codec-chunk-dur") == 0 && i + 1 < argc) {
|
||||||
a.codec_chunk_sec = (float) std::atof(argv[++i]);
|
a.codec_chunk_sec = (float) std::atof(argv[++i]);
|
||||||
} else if (std::strcmp(arg, "--codec-left-context-sec") == 0 && i + 1 < argc) {
|
} else if (std::strcmp(arg, "--codec-left-dur") == 0 && i + 1 < argc) {
|
||||||
a.codec_left_context_sec = (float) std::atof(argv[++i]);
|
a.codec_left_context_sec = (float) std::atof(argv[++i]);
|
||||||
} else if (std::strcmp(arg, "-o") == 0 && i + 1 < argc) {
|
} else if (std::strcmp(arg, "-o") == 0 && i + 1 < argc) {
|
||||||
a.out_wav = argv[++i];
|
a.out_wav = argv[++i];
|
||||||
@@ -299,19 +281,17 @@ static int run(const Args & a) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve utterance text: explicit --text wins, otherwise read stdin
|
// Resolve utterance text: read stdin fully. Empty stdin triggers a
|
||||||
// fully. Empty stdin combined with no --text triggers a clean error.
|
// clean error. Inline text on the command line is intentionally not
|
||||||
std::string text_buf;
|
// supported: shell quoting and special characters belong in a file
|
||||||
const char * text = a.text;
|
// or piped input, never in argv.
|
||||||
if (!text) {
|
std::string text_buf = read_stdin_text();
|
||||||
text_buf = read_stdin_text();
|
if (text_buf.empty()) {
|
||||||
if (text_buf.empty()) {
|
fprintf(stderr, "[CLI] ERROR: stdin is empty, nothing to synthesise\n");
|
||||||
fprintf(stderr, "[CLI] ERROR: no --text and stdin is empty\n");
|
qt_free(q);
|
||||||
qt_free(q);
|
return 1;
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
text = text_buf.c_str();
|
|
||||||
}
|
}
|
||||||
|
const char * text = text_buf.c_str();
|
||||||
|
|
||||||
// Translate CLI args into the facade params. Seed -1 is forwarded
|
// Translate CLI args into the facade params. Seed -1 is forwarded
|
||||||
// verbatim and resolved by qt_synthesize via std::random_device.
|
// verbatim and resolved by qt_synthesize via std::random_device.
|
||||||
|
|||||||
Reference in New Issue
Block a user