189 lines
6.5 KiB
C++
189 lines
6.5 KiB
C++
#pragma once
|
|
// pipeline-tts.h: full TTS pipeline composition (Talker LM + code
|
|
// predictor MTP head + optional speaker encoder + 12Hz codec decoder).
|
|
//
|
|
// pipeline_tts_load opens the talker GGUF and the codec GGUF, parses
|
|
// every typed metadata block (specials, languages, speakers,
|
|
// generation defaults), loads every weight tensor on the shared
|
|
// backend and initialises both KV caches. pipeline_tts_synthesize
|
|
// runs the prompt assembly, the autoregressive frame loop and the
|
|
// codec decode in one pass; it fills the public qt_audio struct
|
|
// directly so the facade in qwen.cpp stays a thin wrapper.
|
|
|
|
#include "backend.h"
|
|
#include "code-predictor-weights.h"
|
|
#include "ggml-backend.h"
|
|
#include "gguf-weights.h"
|
|
#include "kv-cache.h"
|
|
#include "pipeline-codec.h"
|
|
#include "qwen.h"
|
|
#include "speaker-encoder-weights.h"
|
|
#include "talker-weights.h"
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
struct CodecSpecials {
|
|
int pad_id;
|
|
int bos_id;
|
|
int eos_id;
|
|
int think_id;
|
|
int nothink_id;
|
|
int think_bos_id;
|
|
int think_eos_id;
|
|
};
|
|
|
|
struct TextSpecials {
|
|
int im_start_id;
|
|
int im_end_id;
|
|
int tts_pad_id;
|
|
int tts_bos_id;
|
|
int tts_eos_id;
|
|
};
|
|
|
|
struct LanguageEntry {
|
|
std::string name;
|
|
int id;
|
|
};
|
|
|
|
// Speaker entry for CustomVoice variants. id is the codec embedding row id
|
|
// inserted in the talker prefix, dialect is empty unless the speaker
|
|
// overrides the user supplied language with a dialect lang_id (eric ->
|
|
// sichuan_dialect, dylan -> beijing_dialect on the upstream checkpoint).
|
|
struct SpeakerEntry {
|
|
std::string name;
|
|
int id;
|
|
std::string dialect;
|
|
};
|
|
|
|
struct GenerationDefaults {
|
|
bool do_sample;
|
|
int top_k;
|
|
float top_p;
|
|
float temperature;
|
|
float repetition_penalty;
|
|
bool subtalker_do_sample;
|
|
int subtalker_top_k;
|
|
float subtalker_top_p;
|
|
float subtalker_temperature;
|
|
int max_new_tokens;
|
|
};
|
|
|
|
struct PromptTextProjection {
|
|
int in_dim;
|
|
int hid_dim;
|
|
int out_dim;
|
|
std::vector<float> fc1_w;
|
|
std::vector<float> fc1_b;
|
|
std::vector<float> fc2_w;
|
|
std::vector<float> fc2_b;
|
|
};
|
|
|
|
struct PromptPrefixCacheEntry {
|
|
std::string key;
|
|
int rows;
|
|
std::vector<float> input_embed_prefix;
|
|
};
|
|
|
|
struct PromptCache {
|
|
bool initialized;
|
|
PromptTextProjection text_projection;
|
|
std::vector<float> tts_bos_emb;
|
|
std::vector<float> tts_eos_emb;
|
|
std::vector<float> tts_pad_emb;
|
|
std::vector<float> codec_pad_emb;
|
|
std::vector<float> codec_bos_emb;
|
|
std::vector<PromptPrefixCacheEntry> prefix_entries;
|
|
size_t max_prefix_entries;
|
|
};
|
|
|
|
struct PipelineTTS {
|
|
GGUFModel gguf_talker;
|
|
TalkerWeights talker;
|
|
CodePredictorWeights code_predictor;
|
|
SpeakerEncoderWeights speaker_encoder;
|
|
bool has_speaker_encoder;
|
|
|
|
PipelineCodec codec;
|
|
|
|
std::string tokenizer_type;
|
|
std::string model_size;
|
|
std::string model_type;
|
|
int num_code_groups;
|
|
|
|
CodecSpecials codec_specials;
|
|
TextSpecials text_specials;
|
|
std::vector<LanguageEntry> languages;
|
|
std::vector<SpeakerEntry> speakers;
|
|
GenerationDefaults gen_defaults;
|
|
PromptCache prompt_cache;
|
|
|
|
BackendPair bp;
|
|
ggml_backend_t backend;
|
|
ggml_backend_sched_t sched;
|
|
|
|
// Attention path config, set at load and forwarded to every
|
|
// talker / code predictor forward. use_flash_attn collapses to
|
|
// false on CPU only backends (fused FA needs a GPU kernel).
|
|
// clamp_fp16 inserts ggml_clamp on V before attention and on the
|
|
// residual stream between blocks to guard FP16 matmul accumulation
|
|
// on sub Ampere CUDA targets.
|
|
bool use_flash_attn;
|
|
bool clamp_fp16;
|
|
|
|
// Persistent KV caches: the talker holds the LM context, the
|
|
// predictor holds one frame's 16 sub-steps and gets reset every
|
|
// frame in code_predictor_step.
|
|
KVCache talker_kv;
|
|
KVCache code_predictor_kv;
|
|
};
|
|
|
|
// Open the talker GGUF and the codec GGUF, load every module on the
|
|
// shared backend. Aborts with a logged error on any missing tensor or
|
|
// invalid metadata. use_fa is gated on bp.has_gpu inside the load:
|
|
// CPU only runs always use the manual F32 attention chain. clamp_fp16
|
|
// is forwarded as is. Caller frees with pipeline_tts_free.
|
|
bool pipeline_tts_load(PipelineTTS * pt,
|
|
const char * talker_gguf_path,
|
|
const char * codec_gguf_path,
|
|
BackendPair bp,
|
|
bool use_fa,
|
|
bool clamp_fp16);
|
|
|
|
void pipeline_tts_free(PipelineTTS * pt);
|
|
|
|
struct BPETokenizer;
|
|
|
|
// Run the full TTS pipeline: prompt assembly, prefill, frame loop with
|
|
// sampling, codec decode, fill qt_audio. Reads every knob (text,
|
|
// references, sampling, cancel, on_chunk, ...) straight from the
|
|
// public qt_tts_params struct so the facade in qwen.cpp can hand it
|
|
// off verbatim after the mode validation and seed resolve.
|
|
//
|
|
// Returns QT_STATUS_OK on success. On any failure returns a negative
|
|
// qt_status with a diagnostic already routed through qt_log /
|
|
// qt_set_error and leaves `out` empty. QT_STATUS_CANCELLED is returned
|
|
// when params->cancel or params->on_chunk returns true / false
|
|
// respectively during the AR loop.
|
|
//
|
|
// In buffered mode (params->on_chunk == NULL) the synthesised waveform
|
|
// is malloc allocated into out->samples; the caller releases it with
|
|
// qt_audio_free. In streaming mode (params->on_chunk != NULL) audio is
|
|
// emitted through the callback as decoded chunks and out->samples
|
|
// stays NULL on success.
|
|
//
|
|
// resolved_seed is the seed actually used for sampling: qt_synthesize
|
|
// hands over the same value it logged so dump traces and replays line
|
|
// up across runs even when params->seed was -1.
|
|
qt_status pipeline_tts_synthesize(PipelineTTS * pt,
|
|
BPETokenizer * tok,
|
|
const struct qt_tts_params * params,
|
|
int64_t resolved_seed,
|
|
struct qt_audio * out);
|
|
|
|
// Convert a duration in seconds to a frame count at the codec frame
|
|
// rate (24000 / TOKENIZER_HOP_LENGTH). Clamps to a
|
|
// minimum of one frame.
|
|
int pipeline_tts_duration_sec_to_tokens(const PipelineTTS * pt, float duration_sec);
|