File size: 5,240 Bytes
92264aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e405f21
92264aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134b4c4
 
 
92264aa
134b4c4
92264aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134b4c4
92264aa
 
 
 
 
 
 
 
 
 
 
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
/**************************************************************************************************
 * ZipVoice AXERA C++ Port
 *
 * ZipVoiceEngine: the core TTS inference engine.
 *
 * Manages encoder + split-decoder4 axmodel inference, duration expansion,
 * and flow-matching sampling. Mirrors the Python Decoder4ZipVoiceBoardRuntime.
 *
 * Model pipeline:
 *   1. encoder.axmodel:  cat_tokens[n] β†’ encoded[n, feat_dim]
 *   2. Duration expand:  repeat tokens β†’ text_condition[1, seq_len, feat_dim]
 *   3. decoder_part0..3: flow-matching ODE solver (Euler, num_step steps)
 *      part0: t, x, text_cond, speech_cond, guidance_scale, padding_mask
 *             β†’ decoder_hidden_p0, time_hidden, padding_mask2, cfg_scale
 *      part1: decoder_hidden_p0, time_hidden, padding_mask2 β†’ decoder_hidden_p1
 *      part2: decoder_hidden_p1, time_hidden, padding_mask2 β†’ decoder_hidden_p2
 *      part3: decoder_hidden_p2, time_hidden, padding_mask2, cfg_scale β†’ v
 *      x = x + v * dt
 **************************************************************************************************/

#pragma once

#include <string>
#include <vector>
#include <map>
#include <memory>
#include <cstdint>

#include "EngineWrapper.hpp"

class ZipVoiceEngine {
public:
    struct Config {
        std::string model_dir;
        int max_feat_len = 1024;
        int max_tokens = 384;
        int feat_dim = 100;
        int sampling_rate = 24000;
        int hop_length = 256;
        int num_step = 10;
        float t_shift = 0.5f;
        float guidance_scale = 1.0f;
    };

    struct ModelInfo {
        std::string name;
        std::string file;
        std::vector<std::string> inputs;
        std::vector<std::string> outputs;
    };

    struct Timing {
        float encoder_time_sec = 0.0f;
        float duration_expand_time_sec = 0.0f;
        float decoder_time_sec = 0.0f;
        float total_time_sec = 0.0f;
        int generated_frames = 0;
        int features_len = 0;
    };

    ZipVoiceEngine();
    ~ZipVoiceEngine();

    /**
     * Initialize from a model directory.
     * @param model_dir   Path containing axmodel files and JSON configs
     * @param axclConfig  AXCL only: path to axcl.json (nullptr β†’ default). Ignored on AXERA.
     */
    int Init(const std::string& model_dir, const char* axclConfig = nullptr);

    /**
     * Run encoder: cat_tokens[1, max_tokens] β†’ encoded[1, max_tokens, feat_dim].
     * out_encoded should be pre-allocated with size: 1 * max_tokens * feat_dim floats.
     */
    int RunEncoder(const std::vector<int32_t>& cat_tokens, std::vector<float>& out_encoded);

    /**
     * Duration expand: token-level features β†’ frame-level features.
     */
    int DurationExpand(const std::vector<float>& encoded,
                       int prompt_tokens_len,
                       int text_tokens_len,
                       int prompt_features_len,
                       float speed,
                       std::vector<float>& out_text_condition,
                       int& out_features_len);

    /**
     * Full sampling: encoder β†’ duration_expand β†’ decoder flow-matching.
     *
     * @param cat_tokens          [max_tokens] int32 token IDs
     * @param prompt_tokens_len   number of prompt tokens
     * @param text_tokens_len     number of text tokens
     * @param prompt_features     [prompt_frames, feat_dim] mel features from prompt wav
     * @param prompt_features_len number of prompt feature frames
     * @param speed               speed factor (1.0 = normal)
     * @param guidance_scale      classifier-free guidance scale
     * @param seed                random seed
     * @param out_features        output mel features [generated_frames, feat_dim]
     * @param out_timing          timing breakdown
     */
    int Sample(const std::vector<int32_t>& cat_tokens,
               int prompt_tokens_len,
               int text_tokens_len,
               const std::vector<float>& prompt_features,
               int prompt_features_len,
               float speed,
               float guidance_scale,
               int seed,
               std::vector<float>& out_features,
               Timing& out_timing);

    const Config& GetConfig() const { return m_config; }
    bool HasInit() const { return m_has_init; }

private:
    Config m_config;
    bool m_has_init;

    ModelInfo m_encoder_info;
    std::vector<ModelInfo> m_decoder_parts;

    // Model sessions (one per axmodel)
    std::map<std::string, std::unique_ptr<EngineWrapper>> m_sessions;

    // Decoder metadata
    int m_decoder_seq_len;
    bool m_decoder_has_padding_mask;

    int LoadConfig(const std::string& model_dir);
    int LoadManifest(const std::string& model_dir);
    int LoadModels(const std::string& model_dir, const char* axclConfig = nullptr);
    int LoadDecoderMetadata();

    std::vector<float> GetTimesteps(int num_step, float t_shift) const;

    int RunDecoderPart(const ModelInfo& part,
                       std::map<std::string, std::vector<float>>& values,
                       const std::vector<uint8_t>* padding_mask_data = nullptr,
                       std::vector<uint8_t>* padding_mask2_out = nullptr);

    static double GetCurrentTimeMs();
};