File size: 3,178 Bytes
a44ca9d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
/**
 * Inflect-Nano-v2 TTS CLI
 *
 * Usage:  tts_cli --text "Hello world" --output output.wav [--speed 1.0]
 *
 * Build:  cmake .. && make tts_cli
 */
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <chrono>
#include <stdexcept>
#include "inflect_tts.hpp"

static const char* kUsage = R"(Usage: tts_cli --text <TEXT> [OPTIONS]

Options:
  --text <TEXT>         Text to synthesize (required)
  --output <PATH>       Output WAV file (default: output.wav)
  --encoder <PATH>      Encoder AXMODEL path (default: ../models/inflect_encoder.axmodel)
  --decoder <PATH>      Decoder AXMODEL path (default: ../models/inflect_decoder.axmodel)
  --speed <FLOAT>       Playback speed 0.5-2.0 (default: 1.0)
  --variation <FLOAT>   Voice variation 0.0-1.0 (default: 0.667)
  -h, --help            Show this help
)";

int main(int argc, char* argv[]) {
    std::string text;
    std::string output = "output.wav";
    std::string encoder_path = "../models/inflect_encoder.axmodel";
    std::string decoder_path = "../models/inflect_decoder.axmodel";
    float speed = 1.0f;
    float variation = 0.667f;

    for (int i = 1; i < argc; ++i) {
        std::string arg = argv[i];
        if ((arg == "--text" || arg == "-t") && i + 1 < argc)
            text = argv[++i];
        else if (arg == "--output" && i + 1 < argc)
            output = argv[++i];
        else if (arg == "--encoder" && i + 1 < argc)
            encoder_path = argv[++i];
        else if (arg == "--decoder" && i + 1 < argc)
            decoder_path = argv[++i];
        else if (arg == "--speed" && i + 1 < argc)
            speed = std::stof(argv[++i]);
        else if (arg == "--variation" && i + 1 < argc)
            variation = std::stof(argv[++i]);
        else if (arg == "--help" || arg == "-h") {
            std::cout << kUsage; return 0;
        }
    }

    if (text.empty()) {
        std::cerr << "Error: --text is required\n" << kUsage;
        return 1;
    }

    std::cout << "🎤 Text:   " << text << "\n";
    std::cout << "📁 Output: " << output << "\n";

    try {
        TTSEngine engine(encoder_path, decoder_path);

        auto t0 = std::chrono::steady_clock::now();
        auto waveform = engine.synthesize(text, speed, variation);
        auto t1 = std::chrono::steady_clock::now();

        double elapsed = std::chrono::duration<double>(t1 - t0).count();
        double duration = waveform.size() / 24000.0;
        std::cout << "⏱  " << elapsed << "s wall, "
                  << duration << "s audio, RTF="
                  << (duration > 0 ? elapsed / duration : 0) << "x\n";

        // Write raw float32 PCM (add WAV header with libsndfile in production)
        std::ofstream out(output, std::ios::binary);
        if (!out) {
            std::cerr << "Error: cannot open " << output << "\n";
            return 1;
        }
        out.write(reinterpret_cast<const char*>(waveform.data()),
                  waveform.size() * sizeof(float));
        std::cout << "✅ Done → " << output << "\n";

    } catch (const std::exception& e) {
        std::cerr << "❌ Error: " << e.what() << "\n";
        return 1;
    }

    return 0;
}