C/C++ Windows SAPI實現(xiàn)文字轉語音功能
本文通過封裝Windows SAPI(Speech Application Programming Interface),提供了一個現(xiàn)代化的C++接口實現(xiàn)文字轉語音功能。主要特性包括支持同步/異步語音合成、可調節(jié)語速(-10到10)和音量控制(0-100%),同時支持將合成語音保存為WAV文件,并自動處理特殊字符轉義,設計上也確保了線程安全。該接口依賴于Windows系統(tǒng)(需.NET Framework支持)、PowerShell 5.1及以上版本,以及C++11或更高版本。完整代碼在文字末尾提供。
快速開始
基礎使用示例
#include "tts.hpp" int main() { TTS::TextToSpeech tts; // 設置語音參數(shù) tts.set_rate(5); // 加快語速 tts.set_volume(80); // 80%音量 // 同步朗讀 tts.speak_sync("Hello, welcome to the text-to-speech system."); // 異步朗讀 auto future = tts.speak_async("This is an async operation."); future.wait(); // 等待完成 // 保存到文件 std::string filename = tts.save_to_wav("Audio saved to file."); return 0; }
核心功能詳解
語音參數(shù)設置
語速控制 (set_rate()
)
void set_rate(int rate); // 范圍:-10 ~ 10
- 正值加快語速
- 負值減慢語速
- 自動鉗制在有效范圍內
音量控制 (set_volume()
)
void set_volume(int volume); // 范圍:0 ~ 100
- 0表示靜音
- 100表示最大音量
- 支持百分比精確控制
同步朗讀 (speak_sync()
)
bool speak_sync(const std::string& text);
- 阻塞當前線程直到朗讀完成
- 返回執(zhí)行狀態(tài)(true表示成功)
- 適合需要順序執(zhí)行的場景
示例:
if (!tts.speak_sync("Critical system alert!")) { // 錯誤處理 }
異步朗讀 (speak_async()
)
std::future<bool> speak_async(const std::string& text);
- 立即返回std::future對象
- 支持多種等待方式:
auto future = tts.speak_async("Processing completed"); // 方式1:阻塞等待 future.wait(); // 方式2:輪詢檢查 while (future.wait_for(100ms) != std::future_status::ready) { // 執(zhí)行其他任務 } // 獲取結果 bool success = future.get();
保存音頻文件 (save_to_wav()
)
std::string save_to_wav(const std::string& text, const std::string& filename = "");
- 自動生成臨時文件(當filename為空時)
- 返回最終文件路徑
- 文件保存位置規(guī)則:
- 指定filename:使用完整路徑
- 未指定:生成隨機文件名(系統(tǒng)臨時目錄)
示例:
// 自動生成臨時文件 auto auto_file = tts.save_to_wav("Automatic filename"); // 自定義路徑 std::string custom_path = R"(C:\audio\alert.wav)"; auto custom_file = tts.save_to_wav("Custom path", custom_path);
高級用法
批量語音生成
std::vector<std::future<bool>> batch_process() { TTS::TextToSpeech tts; std::vector<std::future<bool>> results; for (int i = 0; i < 10; ++i) { std::string text = "Message " + std::to_string(i); results.push_back(tts.speak_async(text)); } return results; }
實時進度跟蹤
void monitor_async() { auto future = tts.speak_async("Long running operation"); std::thread monitor([&future]{ while (future.wait_for(1s) != std::future_status::ready) { std::cout << "Synthesizing..." << std::endl; } std::cout << "Completed with status: " << future.get() << std::endl; }); monitor.detach(); }
注意事項與最佳實踐
字符處理
- 自動轉義XML特殊字符:
&
,<
,>
,"
,'
- 支持多語言文本(需系統(tǒng)語音包支持)
- 建議預處理用戶輸入:
std::string sanitize_input(const std::string& raw) { // 移除控制字符等 std::string filtered; std::copy_if(raw.begin(), raw.end(), std::back_inserter(filtered), [](char c){ return std::isprint(c); }); return filtered; }
性能優(yōu)化
- 復用TextToSpeech實例(避免重復初始化)
- 異步操作時注意生命周期管理:
// 錯誤示例(對象提前銷毀): auto future = TTS::TextToSpeech().speak_async("text"); // 正確做法: auto tts = std::make_shared<TTS::TextToSpeech>(); auto future = tts->speak_async("text");
錯誤處理
檢查返回值:
if (!tts.speak_sync("text")) { std::cerr << "Speech synthesis failed" << std::endl; }
常見錯誤原因:
- PowerShell訪問權限不足
- 無效的文件路徑
- 系統(tǒng)語音引擎故障
常見問題解答
Q:支持哪些音頻格式?
A:目前僅支持WAV格式,由系統(tǒng)API決定
Q:如何處理中文字符?
A:需確保:
- 系統(tǒng)已安裝中文語音包
- 代碼文件使用UTF-8編碼
- 控制臺支持Unicode(建議使用chcp 65001)
Q:為什么需要生成批處理文件?
A:為了解決:
- PowerShell直接執(zhí)行的編碼問題
- 長命令行參數(shù)限制
- 錯誤代碼捕獲需求
Q:最大支持文本長度?
A:由系統(tǒng)限制決定,建議分段處理超過1MB的文本
Q:如何實現(xiàn)語音中斷?
A:當前版本未實現(xiàn),但可以通過銷毀對象終止異步操作
TTS.hpp 源代碼
#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; } // 執(zhí)行命令并返回結果 bool execute_command(const std::string& command) const { // 創(chuàng)建并寫入批處理文件 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(); // 執(zhí)行批處理文件 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; } // 范圍限制函數(shù) 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
到此這篇關于C/C++ Windows SAPI實現(xiàn)文字轉語音功能的文章就介紹到這了,更多相關C++ Windows SAPI文字轉語音內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
stl常用算法(Algorithms)介紹(stl排序算法、非變序型隊列)
這篇文章主要介紹了stl常用算法(Algorithms)介紹(stl排序算法、非變序型隊列),需要的朋友可以參考下2014-05-05