char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
f29551215
char/aliceai-t5-moe
1#include "arg.h" 2#include "common.h" 3#include "sampling.h" 4#include "log.h" 5#include "llama.h" 6#include "mtmd.h" 7#include "mtmd-helper.h" 8 9#include <cstdio> 10#include <cstring> 11#include <string> 12 13/** 14* Please note that this is NOT a production-ready binary. 15* It is a playground for trying TTS support in llama.cpp. 16* For contributors: please keep this code simple and easy to understand. Do not add unnecessary complexity. The goal is to have a simple CLI for testing TTS support. 17*/ 18 19struct tts_timings { 20int64_t t_start_us = ggml_time_us (); 21int64_t t_last_us = t_start_us ; 22 23void report (int n_frames ) { 24const int64_t t_now_us = ggml_time_us (); 25if (t_now_us - t_last_us < 2000000 ) { 26return ; 27 } 28t_last_us = t_now_us ; 29const double t_elapsed_s = (t_now_us - t_start_us ) /1e6 ; 30const double fps = t_elapsed_s > 0 ?n_frames /t_elapsed_s :0.0 ; 31LOG_INF ("frames generated: %d, speed: %.2f frames/s\n" ,n_frames ,fps ); 32 } 33}; 34 35static void print_usage (int ,char ** argv ) { 36LOG ("\nexample usage:\n" ); 37LOG ("\n %s -m backbone.gguf -mm mmproj.gguf -p \"text to speak\" -o output.wav" ,argv [0 ]); 38LOG ("\n %s -hf user/model -p \"text to speak\" -o output.wav\n" ,argv [0 ]); 39LOG ("\nnote: --tts-lang and --tts-speaker-file may not be supported in all models" ); 40LOG ("\n use -n to limit the output length" ); 41LOG ("\n see tts/README.md for per-model usage notes" ); 42LOG ("\n\n" ); 43} 44 45int main (int argc ,char ** argv ) { 46common_params params ; 47 48common_init (); 49 50if (!common_params_parse (argc ,argv ,params ,LLAMA_EXAMPLE_TTS ,print_usage )) { 51return 1 ; 52 } 53 54mtmd_helper_log_set (common_log_default_callback ,nullptr ); 55 56if (params .prompt .empty ()) { 57LOG_ERR ("no prompt provided, use -p \"text\"\n" ); 58return 1 ; 59 } 60if (params .mmproj .path .empty ()) { 61LOG_ERR ("no mmproj provided, use --mmproj\n" ); 62return 1 ; 63 } 64 65// important: keep this file as generic as possible 66// model-specific logic should be in mtmd-helper-gen or mtmd API 67 68// always enable embd, so that we can pass hidden states to the audio generation helper 69params .embedding = true; 70 71llama_backend_init (); 72llama_numa_init (params .numa ); 73 74// 75// load backbone model and mmproj 76// 77 78auto llama_init = common_init_from_params (params ); 79llama_model * model = llama_init -> model (); 80llama_context * lctx = llama_init -> context (); 81common_sampler * smpl = llama_init -> sampler (0 ); 82if (!model || !lctx ) { 83LOG_ERR ("failed to init model/context\n" ); 84return 1 ; 85 } 86 87mtmd_context_params mtmd_params = mtmd_context_params_default (); 88mtmd_params .use_gpu = params .mmproj_use_gpu ; 89mtmd_params .device = params .mmproj_device ; 90 mtmd::context_ptr mctx (mtmd_init_from_file (params .mmproj .path .c_str (),model ,mtmd_params )); 91if (!mctx ) { 92LOG_ERR ("failed to load mmproj %s\n" ,params .mmproj .path .c_str ()); 93return 1 ; 94 } 95if (mtmd_gen_audio_get_info (mctx .get ()).type == MTMD_GEN_AUDIO_TYPE_NONE ) { 96LOG_ERR ("mmproj does not support audio generation\n" ); 97return 1 ; 98 } 99 100// 101// stage 0: process speaker reference file, if any 102// 103 104 mtmd::bitmap_ptr speaker_bitmap ; 105if (!params .tts_speaker_file .empty ()) { 106auto wrapper = mtmd_helper_bitmap_init_from_file (mctx .get (),params .tts_speaker_file .c_str (), false,mtmd_helper_init_opt_default ()); 107if (!wrapper .bitmap ) { 108LOG_ERR ("failed to load speaker file %s\n" ,params .tts_speaker_file .c_str ()); 109return 1 ; 110 } 111speaker_bitmap .reset (wrapper .bitmap ); 112 } 113 114 mtmd_helper::gen_audio gen (lctx ,mctx .get ()); 115mtmd_helper_gen_audio_inp inp {}; 116inp .seq_id = 0 ; 117inp .prompt = params .prompt .c_str (); 118inp .prompt_len = params .prompt .size (); 119inp .speaker_ref = speaker_bitmap .get (); 120inp .lang = params .tts_lang .c_str (); 121inp .top_k = params .sampling .top_k ; 122inp .top_p = params .sampling .top_p ; 123inp .seed = params .sampling .seed ; 124inp .out_type = MTMD_HELPER_GEN_AUDIO_OUTTYPE_WAV ; 125 126// 127// stage 1: process prompt via backbone model, generate semantic representation 128// 129 130if (gen .set_input (& inp )!= 0 ) { 131LOG_ERR ("set_input failed\n" ); 132return 1 ; 133 } 134 135const int64_t t_prompt_start_us = ggml_time_us (); 136 137for (;;) { 138int32_t ret = gen .step_prompt (params .n_batch ); 139if (ret < 0 ) { 140LOG_ERR ("prompt processing failed\n" ); 141return 1 ; 142 } 143if (ret == 0 ) { 144break ; 145 } 146 } 147 148// note: some pipelines ignore this token and use the hidden state instead 149auto sample_semantic_code = [& ]()-> llama_token { 150llama_token t = common_sampler_sample (smpl ,lctx ,-1 ); 151common_sampler_accept (smpl ,t , true); 152return t ; 153 }; 154 155const int max_new = params .n_predict > 0 ?params .n_predict :512 ; 156int n_frames = 0 ; 157llama_token sampled = sample_semantic_code (); 158const float * h_state = llama_get_embeddings_ith (lctx ,-1 ); 159 160tts_timings timings ; 161const int64_t t_gen_start_us = ggml_time_us (); 162 163bool stop = false; 164while (!stop && n_frames < max_new ) { 165const float * h_next = nullptr ; 166 167// stage 2+3: semantic --> acoustic details --> audio waveform 168// step_gen() runs both stages and returns new h_state for next step 169if (gen .step_gen (sampled ,h_state ,& h_next ,& stop )!= 0 ) { 170LOG_ERR ("step_gen failed at frame %d\n" ,n_frames ); 171return 1 ; 172 } 173if (!h_next ) { 174break ;// stopped without generating a frame 175 } 176 177n_frames ++ ; 178h_state = h_next ; 179sampled = sample_semantic_code (); 180timings .report (n_frames ); 181 } 182const double t_gen_s = (ggml_time_us ()- t_gen_start_us ) /1e6 ; 183 184int32_t sample_rate = 0 ; 185const char * data = nullptr ; 186size_t data_len = 0 ; 187int64_t n_samples = 0 ; 188const int64_t t_wav_start_us = ggml_time_us (); 189if (gen .get_output (& sample_rate ,& data ,& data_len ,& n_samples )!= 0 ) { 190LOG_ERR ("get_output failed\n" ); 191return 1 ; 192 } 193const double t_wav_s = (ggml_time_us ()- t_wav_start_us ) /1e6 ; 194 195LOG_INF ("generated %d frames, %zu bytes of WAV audio (%d Hz)\n" ,n_frames ,data_len ,sample_rate ); 196 197const double t_prompt_s = (t_gen_start_us - t_prompt_start_us ) /1e6 ; 198const double t_total_s = t_prompt_s + t_gen_s + t_wav_s ; 199const double audio_s = sample_rate > 0 ? (double )n_samples /sample_rate :0.0 ; 200LOG_INF ("timings: prompt eval %.2fs + generation %.2fs + vocoder %.2fs = total %.2fs\n" , 201t_prompt_s ,t_gen_s ,t_wav_s ,t_total_s ); 202LOG_INF (" output audio = %.2fs (audio time = %.2fx process time)\n" ,audio_s ,t_total_s > 0 ?audio_s /t_total_s :0.0 ); 203FILE * f = fopen (params .out_file .c_str (),"wb" ); 204if (!f ) { 205LOG_ERR ("failed to open %s\n" ,params .out_file .c_str ()); 206return 1 ; 207 } 208fwrite (data ,1 ,data_len ,f ); 209fclose (f ); 210LOG_INF ("wrote %s\n" ,params .out_file .c_str ()); 211 212llama_backend_free (); 213return 0 ; 214}