File size: 9,053 Bytes
71e354e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include <map>
#include <fstream>
#include <numeric>
#include <cmath>
#include <cstring>
#include <cstdio>

#include "tts/kokoro.hpp"
#include "frontend/frontend_interface.hpp"
#include "frontend/kokoro_frontend.hpp"
#include "utils/logger.h"
#include "utils/string_utils.hpp"
#include "ax_model_runner/ax_model_runner.hpp"
#include "onnxruntime_cxx_api.h"

#define MAX_SEQ_LEN     96
#define STYLE_DIM       256
#define T_PAD           192
#define F0_LEN          384
#define VOICE_HEAD_DIM  128

static std::vector<float> sigmoid_vec(const std::vector<float>& x) {
    std::vector<float> r(x.size());
    for (size_t i = 0; i < x.size(); i++) r[i] = 1.0f / (1.0f + expf(-x[i]));
    return r;
}
static void transpose(const float* A, float* At, int m, int k) {
    for (int i = 0; i < m; i++) for (int j = 0; j < k; j++) At[j*m+i] = A[i*k+j];
}
static void matmul(const float* A, const float* B, float* C, int m, int k, int n) {
    for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) { float s=0; for (int p=0;p<k;p++) s+=A[i*k+p]*B[p*n+j]; C[i*n+j]=s; }
}

class Kokoro::Impl {
public:
    ~Impl() { uninit(); }
    bool init(AX_TTS_TYPE_E, AX_TTS_INIT_CONFIG* cfg) {
        std::string mp(cfg->model_path); model_path_ = mp;
        return load_models_(mp);
    }
    void uninit() { enc_.unload_model(); f0n_.unload_model(); dec_.unload_model(); har_sess_.release(); istft_sess_.release(); }
    bool run(const std::vector<int>& input_ids, AX_TTS_RUN_CONFIG* cfg, AX_TTS_AUDIO** audio) {
        if (!cfg->voice) return false;
        std::string vn(cfg->voice);
        if (vn != voice_name_) { if (!load_voice_(model_path_, vn)) return false; voice_name_ = vn; }
        std::vector<float> ad;
        if (!run_models_(input_ids, cfg->speed, ad)) return false;
        *audio = (AX_TTS_AUDIO*)malloc(sizeof(AX_TTS_AUDIO)+sizeof(float)*ad.size());
        auto* a=*audio; a->channels=1; a->num_samples=(int)ad.size(); a->sample_rate=cfg->sample_rate;
        std::memcpy(a->data, ad.data(), sizeof(float)*ad.size());
        return true;
    }
private:
    AxModelRunner enc_,f0n_,dec_;
    Ort::Env ort_env_{ORT_LOGGING_LEVEL_WARNING,"Kokoro"};
    Ort::Session har_sess_{nullptr},istft_sess_{nullptr};
    std::string voice_name_,model_path_;
    std::vector<float> voice_tensor_,d_buf_,t_en_buf_,dur_buf_,f0_buf_,n_buf_,dec_buf_;
    std::vector<int> d_shape_,f0_shape_,dec_shape_;

    bool load_models_(const std::string& mp) {
        std::string ep=mp+"/kokoro_enc_axera.axmodel"; if(enc_.load_model(ep.c_str())!=0){ALOGE("enc:%s",ep.c_str());return false;}
        std::string fp=mp+"/kokoro_f0n.axmodel"; if(f0n_.load_model(fp.c_str())!=0){ALOGE("f0n:%s",fp.c_str());return false;}
        std::string dp=mp+"/kokoro_dec.axmodel"; if(dec_.load_model(dp.c_str())!=0){ALOGE("dec:%s",dp.c_str());return false;}
        Ort::SessionOptions so; so.SetIntraOpNumThreads(1);
        har_sess_=Ort::Session(ort_env_,(mp+"/kokoro_har_noup.onnx").c_str(),so);
        istft_sess_=Ort::Session(ort_env_,(mp+"/kokoro_istft.onnx").c_str(),so);
        d_buf_.resize(enc_.get_output_size(0)/sizeof(float)); t_en_buf_.resize(enc_.get_output_size(1)/sizeof(float));
        dur_buf_.resize(enc_.get_output_size(2)/sizeof(float)); f0_buf_.resize(f0n_.get_output_size(0)/sizeof(float));
        n_buf_.resize(f0n_.get_output_size(1)/sizeof(float)); dec_buf_.resize(dec_.get_output_size(0)/sizeof(float));
        d_shape_=enc_.get_output_shape(0); f0_shape_=f0n_.get_output_shape(0); dec_shape_=dec_.get_output_shape(0);
        return true;
    }
    bool load_voice_(const std::string& mp,const std::string& vn){
        std::string p=mp+"/voices/"+vn+".bin"; FILE* f=fopen(p.c_str(),"rb");
        if(!f){ALOGE("voice %s",p.c_str());return false;}
        voice_tensor_.resize(STYLE_DIM);
        if(fread(voice_tensor_.data(),sizeof(float),STYLE_DIM,f)!=STYLE_DIM){fclose(f);return false;}
        fclose(f); return true;
    }
    bool run_models_(std::vector<int> input_ids,float speed,std::vector<float>& audio){
        int al=(int)input_ids.size(); if(al>MAX_SEQ_LEN){input_ids.resize(MAX_SEQ_LEN);al=MAX_SEQ_LEN;}
        input_ids.resize(MAX_SEQ_LEN,0);
        std::vector<float> sh(voice_tensor_.begin(),voice_tensor_.begin()+VOICE_HEAD_DIM);
        std::vector<float> st(voice_tensor_.begin()+VOICE_HEAD_DIM,voice_tensor_.end());
        // 1. Encoder
        std::vector<void*> ei{(void*)input_ids.data(),(void*)st.data()};
        std::vector<void*> eo{(void*)d_buf_.data(),(void*)t_en_buf_.data(),(void*)dur_buf_.data()};
        enc_.set_inputs(ei); if(enc_.run()!=0)return false; enc_.get_outputs(eo);
        // 2. Duration+Align
        int tf; std::vector<int> pd; process_duration_(dur_buf_,al,speed,pd,tf);
        std::vector<float> dT(640*96); transpose(d_buf_.data(),dT.data(),96,640);
        std::vector<float> aln(MAX_SEQ_LEN*tf,0);
        for(int i=0,c=0;i<MAX_SEQ_LEN;i++)for(int r=0;r<pd[i];r++)if(c<tf)aln[i*tf+c++]=1;
        std::vector<float> en_raw(640*tf),en_buf(640*T_PAD,0);
        matmul(dT.data(),aln.data(),en_raw.data(),640,96,tf);
        for(int i=0;i<640;i++)std::memcpy(&en_buf[i*T_PAD],&en_raw[i*tf],tf*4);
        std::vector<float> asr_raw(512*tf),asr_buf(512*T_PAD,0);
        matmul(t_en_buf_.data(),aln.data(),asr_raw.data(),512,96,tf);
        for(int i=0;i<512;i++)std::memcpy(&asr_buf[i*T_PAD],&asr_raw[i*tf],tf*4);
        // 3. F0N
        std::vector<void*> fi{(void*)en_buf.data(),(void*)st.data()};
        std::vector<void*> fo{(void*)f0_buf_.data(),(void*)n_buf_.data()};
        f0n_.set_inputs(fi); if(f0n_.run()!=0)return false; f0n_.get_outputs(fo);
        // 4. HAR
        std::vector<float> f0_up(115200);
        for(int i=0;i<F0_LEN;i++)for(int j=0;j<300;j++)f0_up[i*300+j]=f0_buf_[i];
        int64_t fs[]={1,115200};
        auto mem=Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator,OrtMemTypeCPU);
        std::vector<Ort::Value> hi;
        hi.push_back(Ort::Value::CreateTensor<float>(mem,f0_up.data(),115200,fs,2));
        const char* hin[]={"f0_up"},*hout[]={"har"};
        auto ho=har_sess_.Run(Ort::RunOptions{nullptr},hin,hi.data(),1,hout,1);
        std::vector<float> har(ho.front().GetTensorTypeAndShapeInfo().GetElementCount());
        std::memcpy(har.data(),ho.front().GetTensorMutableData<float>(),har.size()*4);
        // 5. Decoder
        std::vector<void*> di{(void*)asr_buf.data(),(void*)f0_buf_.data(),(void*)n_buf_.data(),(void*)sh.data(),(void*)har.data()};
        dec_.set_inputs(di); if(dec_.run()!=0)return false; dec_.get_output(0,dec_buf_.data());
        // 6. ISTFT
        int64_t rs[]={1,(int64_t)dec_shape_[1],(int64_t)dec_shape_[2]};
        std::vector<Ort::Value> ii;
        ii.push_back(Ort::Value::CreateTensor<float>(mem,dec_buf_.data(),dec_buf_.size(),rs,3));
        const char* iin[]={"raw_x"},*iout[]={"waveform"};
        auto io=istft_sess_.Run(Ort::RunOptions{nullptr},iin,ii.data(),1,iout,1);
        audio.resize(io.front().GetTensorTypeAndShapeInfo().GetElementCount());
        std::memcpy(audio.data(),io.front().GetTensorMutableData<float>(),audio.size()*4);
        int ts=(int)(tf*300.0f*224.0f/151.0f);
        if(ts<(int)audio.size())audio.resize(ts);
        return true;
    }
    void process_duration_(const std::vector<float>& dur,int al,float speed,std::vector<int>& pd,int& tf){
        auto ds=sigmoid_vec(dur); int D50=50;
        pd.resize(MAX_SEQ_LEN,0); int total=0,fixed=MAX_SEQ_LEN*2;
        for(int i=0;i<al;i++){float s=0;for(int j=0;j<D50;j++)s+=ds[i*D50+j];pd[i]=std::max(1,(int)roundf(s/speed));total+=pd[i];}
        int diff=fixed-total,pl=MAX_SEQ_LEN-al;
        if(diff>0&&pl>0){int e=diff/pl,r=diff%pl;
            for(int i=al;i<MAX_SEQ_LEN;i++)pd[i]=e;
            for(int i=al;i<al+r;i++)pd[i]++;}
        else if(diff<0){for(int d=abs(diff);d>0;){int mi=0;float mv=-1;
            for(int i=0;i<al;i++)if(pd[i]>1&&(float)pd[i]>mv){mv=(float)pd[i];mi=i;}
            if(mv<=1||mi>=al)break;pd[mi]--;d--;}}
        tf=std::accumulate(pd.begin(),pd.end(),0);
    }
};

Kokoro::Kokoro():impl_(std::make_unique<Impl>()){}
Kokoro::~Kokoro(){uninit();}
void Kokoro::uninit(){impl_.reset();}

bool Kokoro::init(AX_TTS_TYPE_E tts_type,AX_TTS_INIT_CONFIG* cfg){
    std::string vp=std::string(cfg->model_path)+"/vocab.txt";
    FILE* f=fopen(vp.c_str(),"r"); if(!f){ALOGE("vocab %s",vp.c_str());return false;}
    char line[256];
    while(fgets(line,sizeof(line),f)){char* tab=strchr(line,'\t');if(tab){*tab=0;vocab_[line]=atoi(tab+1);}}
    fclose(f);
    // Init frontend for text-to-phoneme conversion
    frontend_ = std::make_shared<KokoroFrontend>();
    if (!frontend_->init(cfg)) { ALOGE("frontend init failed"); return false; }
    return impl_->init(tts_type,cfg);
}

bool Kokoro::run(const std::string& text,AX_TTS_RUN_CONFIG* cfg,AX_TTS_AUDIO** audio){
    int err=0;
    std::string lang(cfg->language);
    auto ids = frontend_->run(text,lang,vocab_,err);
    if(err!=0||ids.empty()){ALOGE("frontend run failed err=%d",err);return false;}
    return impl_->run(ids,cfg,audio);
}