3535#include < condition_variable>
3636#include < atomic>
3737#include < fstream>
38- #include < sys/time.h>
3938#include < iomanip>
4039#include < chrono>
41- #include < sys/stat.h>
42- #include < sys/types.h>
43- #include < sys/wait.h>
44- #include < unistd.h>
45- #include < dirent.h>
4640#include < cmath>
4741#include < sstream>
4842#include < random>
4943#include < cstdarg>
5044#include < signal.h>
5145
46+ #ifdef _WIN32
47+ #include < windows.h>
48+ #include < direct.h>
49+ #include < io.h>
50+ #include < process.h>
51+ #include < sys/stat.h>
52+ #include < sys/types.h>
53+ // Windows compatibility macros
54+ #define popen _popen
55+ #define pclose _pclose
56+ #define unlink _unlink
57+ #define stat _stat
58+ #define S_IFDIR _S_IFDIR
59+ #else
60+ #include < sys/time.h>
61+ #include < sys/stat.h>
62+ #include < sys/types.h>
63+ #include < sys/wait.h>
64+ #include < unistd.h>
65+ #include < dirent.h>
66+ #endif
67+
68+ // ============================================================
69+ // Cross-platform helper: recursive directory creation
70+ // Replaces "mkdir -p" shell command for Windows compatibility
71+ // ============================================================
72+ static bool cross_platform_mkdir_p (const std::string& path) {
73+ if (path.empty ()) return false ;
74+
75+ std::string normalized = path;
76+ #ifdef _WIN32
77+ for (char & c : normalized) {
78+ if (c == ' /' ) c = ' \\ ' ;
79+ }
80+ size_t pos = 0 ;
81+ if (normalized.size () >= 2 && normalized[1 ] == ' :' ) {
82+ pos = 2 ;
83+ if (normalized.size () > 2 && normalized[2 ] == ' \\ ' ) pos = 3 ;
84+ }
85+ while (pos < normalized.size () && normalized[pos] == ' \\ ' ) pos++;
86+
87+ while (pos < normalized.size ()) {
88+ pos = normalized.find (' \\ ' , pos);
89+ if (pos == std::string::npos) pos = normalized.size ();
90+ std::string sub = normalized.substr (0 , pos);
91+ if (!sub.empty ()) {
92+ struct _stat info;
93+ if (_stat (sub.c_str (), &info) != 0 ) {
94+ if (_mkdir (sub.c_str ()) != 0 && errno != EEXIST) {
95+ return false ;
96+ }
97+ }
98+ }
99+ if (pos < normalized.size ()) pos++;
100+ }
101+ return true ;
102+ #else
103+ for (char & c : normalized) {
104+ if (c == ' \\ ' ) c = ' /' ;
105+ }
106+ size_t pos = 0 ;
107+ if (!normalized.empty () && normalized[0 ] == ' /' ) pos = 1 ;
108+ while (pos < normalized.size ()) {
109+ pos = normalized.find (' /' , pos);
110+ if (pos == std::string::npos) pos = normalized.size ();
111+ std::string sub = normalized.substr (0 , pos);
112+ if (!sub.empty ()) {
113+ struct stat info;
114+ if (::stat (sub.c_str (), &info) != 0 ) {
115+ if (mkdir (sub.c_str (), 0755 ) != 0 && errno != EEXIST) {
116+ return false ;
117+ }
118+ }
119+ }
120+ if (pos < normalized.size ()) pos++;
121+ }
122+ return true ;
123+ #endif
124+ }
125+
52126// 前向声明:Python Token2Wav 服务函数(定义在文件后面)
53127static bool start_python_t2w_service (struct omni_context * ctx_omni);
54128static void stop_python_t2w_service (struct omni_context * ctx_omni);
@@ -5169,26 +5243,19 @@ static bool generate_audio_tokens_local(
51695243
51705244// Helper function to play WAV file
51715245static void play_wav_file (const std::string& wav_file_path) {
5246+ #ifndef _WIN32
51725247 // Play audio asynchronously using fork() to avoid blocking TTS thread
5173- // This is important because audio playback can take time and shouldn't delay
5174- // the next TTS request
5175-
51765248 pid_t pid = fork ();
51775249 if (pid == 0 ) {
5178- // Child process: execute playback command
51795250 #ifdef __APPLE__
51805251 execl (" /usr/bin/afplay" , " afplay" , wav_file_path.c_str (), (char *)NULL );
51815252 #else
51825253 execl (" /usr/bin/aplay" , " aplay" , wav_file_path.c_str (), (char *)NULL );
51835254 #endif
5184- // If execl fails, exit child process
51855255 _exit (1 );
51865256 } else if (pid > 0 ) {
51875257 // Parent process: continue without waiting
5188- // Detach child process to avoid zombie processes
5189- // Note: We don't wait for the child, allowing it to run independently
51905258 } else {
5191- // Fork failed, fallback to system() with & (less ideal but better than blocking)
51925259 std::string play_cmd;
51935260 #ifdef __APPLE__
51945261 play_cmd = " afplay \" " + wav_file_path + " \" &" ;
@@ -5198,6 +5265,8 @@ static void play_wav_file(const std::string& wav_file_path) {
51985265 LOG_WRN (" TTS: fork() failed, using system() fallback for audio playback\n " );
51995266 system (play_cmd.c_str ());
52005267 }
5268+ #endif
5269+ // Windows: no-op (audio playback handled by frontend)
52015270}
52025271
52035272
@@ -5227,14 +5296,11 @@ static void move_old_output_to_archive() {
52275296 struct stat info;
52285297 if (stat (dir_path.c_str (), &info) != 0 ) {
52295298 // Directory doesn't exist, try to create it
5230- std::string cmd = " mkdir -p " + dir_path;
5231- int ret = system (cmd.c_str ());
5232- if (ret != 0 ) {
5299+ if (!cross_platform_mkdir_p (dir_path)) {
52335300 LOG_ERR (" Failed to create output directory: %s\n " , dir_path.c_str ());
52345301 return false ;
5235- } else {
5236- return true ;
52375302 }
5303+ return true ;
52385304 } else if (!(info.st_mode & S_IFDIR)) {
52395305 LOG_ERR (" Output path exists but is not a directory: %s\n " , dir_path.c_str ());
52405306 return false ;
@@ -5245,8 +5311,7 @@ static void move_old_output_to_archive() {
52455311 // Helper function to find next available ID in old_output directory
52465312 auto get_next_output_id = [](const std::string& old_output_base) -> int {
52475313 // Ensure old_output base directory exists
5248- std::string mkdir_cmd = " mkdir -p " + old_output_base;
5249- system (mkdir_cmd.c_str ());
5314+ cross_platform_mkdir_p (old_output_base);
52505315
52515316 // Find maximum ID in old_output directory
52525317 int max_id = -1 ;
@@ -5410,17 +5475,8 @@ void tts_thread_func_duplex(struct omni_context * ctx_omni, common_params *param
54105475
54115476 // Helper function to create directory
54125477 auto create_dir = [](const std::string& dir_path) {
5413- struct stat info;
5414- if (stat (dir_path.c_str (), &info) != 0 ) {
5415- std::string cmd = " mkdir -p " + dir_path;
5416- int ret = system (cmd.c_str ());
5417- if (ret != 0 ) {
5418- LOG_ERR (" Failed to create output directory: %s\n " , dir_path.c_str ());
5419- return false ;
5420- }
5421- return true ;
5422- } else if (!(info.st_mode & S_IFDIR)) {
5423- LOG_ERR (" Output path exists but is not a directory: %s\n " , dir_path.c_str ());
5478+ if (!cross_platform_mkdir_p (dir_path)) {
5479+ LOG_ERR (" Failed to create output directory: %s\n " , dir_path.c_str ());
54245480 return false ;
54255481 }
54265482 return true ;
@@ -6018,14 +6074,11 @@ void tts_thread_func(struct omni_context * ctx_omni, common_params *params) {
60186074 struct stat info;
60196075 if (stat (dir_path.c_str (), &info) != 0 ) {
60206076 // Directory doesn't exist, try to create it
6021- std::string cmd = " mkdir -p " + dir_path;
6022- int ret = system (cmd.c_str ());
6023- if (ret != 0 ) {
6077+ if (!cross_platform_mkdir_p (dir_path)) {
60246078 LOG_ERR (" Failed to create output directory: %s\n " , dir_path.c_str ());
60256079 return false ;
6026- } else {
6027- return true ;
60286080 }
6081+ return true ;
60296082 } else if (!(info.st_mode & S_IFDIR)) {
60306083 LOG_ERR (" Output path exists but is not a directory: %s\n " , dir_path.c_str ());
60316084 return false ;
@@ -7872,9 +7925,7 @@ void t2w_thread_func_python(struct omni_context * ctx_omni, common_params *param
78727925
78737926 // 确保输出目录存在
78747927 {
7875- char mkdir_cmd[512 ];
7876- snprintf (mkdir_cmd, sizeof (mkdir_cmd), " mkdir -p %s" , tts_wav_output_dir.c_str ());
7877- system (mkdir_cmd);
7928+ cross_platform_mkdir_p (tts_wav_output_dir);
78787929 }
78797930
78807931 while (t2w_thread_running) {
@@ -7897,9 +7948,7 @@ void t2w_thread_func_python(struct omni_context * ctx_omni, common_params *param
78977948 if (!ctx_omni->duplex_mode && ctx_omni->simplex_round_idx != last_round_idx) {
78987949 last_round_idx = ctx_omni->simplex_round_idx ;
78997950 tts_wav_output_dir = get_wav_output_dir ();
7900- char mkdir_cmd[512 ];
7901- snprintf (mkdir_cmd, sizeof (mkdir_cmd), " mkdir -p %s" , tts_wav_output_dir.c_str ());
7902- system (mkdir_cmd);
7951+ cross_platform_mkdir_p (tts_wav_output_dir);
79037952 }
79047953
79057954 // 重置 Python 缓存
@@ -7951,9 +8000,7 @@ void t2w_thread_func_python(struct omni_context * ctx_omni, common_params *param
79518000 last_round_idx, received_round_idx);
79528001 last_round_idx = received_round_idx;
79538002 tts_wav_output_dir = get_wav_output_dir ();
7954- char mkdir_cmd[512 ];
7955- snprintf (mkdir_cmd, sizeof (mkdir_cmd), " mkdir -p %s" , tts_wav_output_dir.c_str ());
7956- system (mkdir_cmd);
8003+ cross_platform_mkdir_p (tts_wav_output_dir);
79578004 // 重置 wav 索引,因为是新的轮次
79588005 wav_idx = 0 ;
79598006 }
@@ -8071,9 +8118,7 @@ void t2w_thread_func_python(struct omni_context * ctx_omni, common_params *param
80718118 if (!ctx_omni->duplex_mode && ctx_omni->simplex_round_idx != last_round_idx) {
80728119 last_round_idx = ctx_omni->simplex_round_idx ;
80738120 tts_wav_output_dir = get_wav_output_dir ();
8074- char mkdir_cmd[512 ];
8075- snprintf (mkdir_cmd, sizeof (mkdir_cmd), " mkdir -p %s" , tts_wav_output_dir.c_str ());
8076- system (mkdir_cmd);
8121+ cross_platform_mkdir_p (tts_wav_output_dir);
80778122 }
80788123 }
80798124 break ;
@@ -8253,9 +8298,7 @@ void t2w_thread_func_cpp(struct omni_context * ctx_omni, common_params *params)
82538298 print_with_timestamp (" T2W线程(C++): 新输出目录 %s\n " , tts_wav_output_dir.c_str ());
82548299
82558300 // 确保目录存在
8256- char mkdir_cmd[512 ];
8257- snprintf (mkdir_cmd, sizeof (mkdir_cmd), " mkdir -p %s" , tts_wav_output_dir.c_str ());
8258- system (mkdir_cmd);
8301+ cross_platform_mkdir_p (tts_wav_output_dir);
82598302 }
82608303
82618304 // Add new tokens to buffer
@@ -8446,9 +8489,7 @@ void t2w_thread_func_cpp(struct omni_context * ctx_omni, common_params *params)
84468489 tts_wav_output_dir = get_wav_output_dir ();
84478490 print_with_timestamp (" T2W线程: 轮次结束后更新输出目录为 %s\n " , tts_wav_output_dir.c_str ());
84488491 // 确保目录存在
8449- char mkdir_cmd[512 ];
8450- snprintf (mkdir_cmd, sizeof (mkdir_cmd), " mkdir -p %s" , tts_wav_output_dir.c_str ());
8451- system (mkdir_cmd);
8492+ cross_platform_mkdir_p (tts_wav_output_dir);
84528493 }
84538494 }
84548495 // 注意:is_chunk_end 时不重置 buffer,剩余 tokens 保留给下一个 chunk
0 commit comments