| |
| |
| |
| |
| |
| |
| |
| #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"; |
|
|
| |
| 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; |
| } |
|
|