char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Xuan-Son Nguyenargs: add --video-* CLI arguments (#24318)f29551215

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