#pragma once
#include <string>
#include <sstream>
#include <cstdlib>
#include <random>
#include <atomic>
#include <thread>
#include <memory>
#include <system_error>
#include <future>
#include <fstream>
#include <cstdio>
#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
namespace TTS {
class TextToSpeech {
public:
static constexpr int MIN_RATE = -10;
static constexpr int MAX_RATE = 10;
static constexpr int MIN_VOLUME = 0;
static constexpr int MAX_VOLUME = 100;
explicit TextToSpeech() = default;
// 设置语音速率(-10~10)
void set_rate(int rate) {
rate_ = clamp(rate, MIN_RATE, MAX_RATE);
}
// 设置音量(0~100)
void set_volume(int volume) {
volume_ = clamp(volume, MIN_VOLUME, MAX_VOLUME);
}
// 同步朗读(阻塞直到完成)
bool speak_sync(const std::string& text) {
return execute_command(generate_ps_command(text));
}
// 异步朗读(立即返回)
std::future<bool> speak_async(const std::string& text) {
return std::async(std::launch::async, [this, text] { return this->speak_sync(text); });
}
// 生成临时WAV文件(返回文件路径)
std::string save_to_wav(const std::string& text, const std::string& filename = "") {
std::string full_path;
bool clean_up;
std::tie(full_path, clean_up) = generate_temp_path(filename, ".wav");
std::string command = generate_ps_command(text, full_path);
if (!execute_command(command)) {
if (clean_up) std::remove(full_path.c_str());
return "";
}
return full_path;
}
private:
int rate_ = 0; // 默认语速
int volume_ = 100; // 默认音量
std::atomic<bool> cancel_flag_{false};
// 生成PowerShell命令
std::string generate_ps_command(const std::string& text, const std::string& output_file = "") const {
std::ostringstream oss;
oss << "powershell -Command \"";
oss << "Add-Type -AssemblyName System.Speech; ";
oss << "$speech = New-Object System.Speech.Synthesis.SpeechSynthesizer; ";
oss << "$speech.Rate = " << rate_ << "; ";
oss << "$speech.Volume = " << volume_ << "; ";
if (!output_file.empty()) {
oss << "$speech.SetOutputToWaveFile('" << output_file << "'); ";
} else {
oss << "$speech.SetOutputToDefaultAudioDevice(); ";
}
oss << "$speech.Speak([System.Xml.XmlConvert]::VerifyXmlChars('"
<< escape_ps_string(escape_xml(text)) << "'));\"";
return oss.str();
}
// 转义 PowerShell 字符串
std::string escape_ps_string(const std::string& text) const {
std::string result;
result.reserve(text.size() * 2);
for (char c : text) {
result += (c == '\'') ? "''" : std::string(1, c);
}
return result;
}
// 执行命令并返回结果
bool execute_command(const std::string& command) const {
// 创建并写入批处理文件
std::string bat_path;
bool dummy;
std::tie(bat_path, dummy) = generate_temp_path("tts_", ".bat");
std::ofstream bat_file(bat_path);
if (!bat_file) return false;
bat_file << "@echo off\n"
<< "chcp 65001 > nul\n"
<< command << "\n"
<< "exit /b %ERRORLEVEL%";
bat_file.close();
// 执行批处理文件
std::string cmd = "cmd /c \"" + bat_path + "\"";
int result = std::system(cmd.c_str());
// 清理临时文件
std::remove(bat_path.c_str());
return (result == 0);
}
// 生成临时文件路径
std::tuple<std::string, bool> generate_temp_path(const std::string& prefix = "tts_", const std::string& extension = "") const {
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 15);
std::string full_path;
bool need_cleanup = false;
if (prefix.empty()) {
char tmp_name[L_tmpnam];
if (std::tmpnam(tmp_name)) {
full_path = tmp_name;
need_cleanup = true;
}
} else {
const std::string temp_dir = get_temp_directory();
do {
std::string unique_part;
for (int i = 0; i < 8; ++i) {
unique_part += "0123456789abcdef"[dis(gen) % 16];
}
full_path = temp_dir + "\\" + prefix + unique_part + extension;
} while (file_exists(full_path));
}
return {full_path, need_cleanup};
}
// XML 转义
static std::string escape_xml(std::string data) {
std::string buffer;
buffer.reserve(data.size());
for (char c : data) {
switch (c) {
case '&': buffer += "&"; break;
case '\"': buffer += """; break;
case '\'': buffer += "'"; break;
case '<': buffer += "<"; break;
case '>': buffer += ">"; break;
default: buffer += c; break;
}
}
return buffer;
}
// 范围限制函数
template <typename T>
static T clamp(T value, T min, T max) {
return (value < min) ? min : (value > max) ? max : value;
}
// 获取临时目录
static std::string get_temp_directory() {
const char* tmp = std::getenv("TEMP");
if (!tmp) tmp = std::getenv("TMP");
return tmp ? tmp : ".";
}
// 检查文件是否存在
static bool file_exists(const std::string& path) {
#ifdef _WIN32
return ::_access(path.c_str(), 0) == 0;
#else
return ::access(path.c_str(), F_OK) == 0;
#endif
}
};
} // namespace TTS
|