diff --git a/doubao_mic.cpp b/doubao_mic.cpp index d846b0000..cbd777a89 100644 --- a/doubao_mic.cpp +++ b/doubao_mic.cpp @@ -15,19 +15,20 @@ #include #include #include -#include // 用于STDIN_FILENO和read +#include +#include +#include // 全局原子变量(线程安全) std::atomic is_recording(false); std::atomic exit_program(false); -std::atomic recorded_seconds(0); // 实时录制时长 +std::atomic recorded_seconds(0); // 音频缓冲区(加锁保护) std::vector audio_buffer; std::mutex buffer_mutex; -// 配置常量(可自定义) +// 配置常量 const int RECORD_TIMEOUT = 30; // 超时时间(秒) -const int RECORD_FINISH_WAIT_MS = 5000; // 停止后收尾等待时间 -const bool AUTO_RECOGNIZE_ON_TIMEOUT = true; // 超时自动识别 +const int FINISH_WAIT_MS = 2000; // 停止前收尾等待时间(毫秒) // 信号处理:Ctrl+C 优雅退出 void signal_handler(int sig) { @@ -35,34 +36,36 @@ void signal_handler(int sig) { printf("\n\n🛑 收到退出信号,正在清理资源...\n"); exit_program.store(true); is_recording.store(false); - // 等待收尾 - std::this_thread::sleep_for(std::chrono::milliseconds(RECORD_FINISH_WAIT_MS)); + // 给回调线程一点时间清理最后数据 + std::this_thread::sleep_for(std::chrono::milliseconds(100)); exit(0); } } -// 非阻塞检查输入(解决超时后需按回车问题) -bool check_input_non_blocking() { +// 非阻塞检查输入(核心修复:解决死锁) +bool check_input_non_blocking(int timeout_ms = 100) { fd_set fds; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); struct timeval tv; tv.tv_sec = 0; - tv.tv_usec = 100000; // 100ms超时 + tv.tv_usec = timeout_ms * 1000; // 转换为微秒 return select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv) > 0; } -// 清空输入缓冲区(避免残留回车) +// 清空输入缓冲区(避免残留) void clear_input_buffer() { - while (check_input_non_blocking()) { + // 使用非阻塞读取清空缓冲区 + while (check_input_non_blocking(10)) { char c; - read(STDIN_FILENO, &c, 1); + ssize_t ret = read(STDIN_FILENO, &c, 1); + (void)ret; } } -// 音频回调(取消30秒帧上限) +// 音频回调(确保完整接收音频帧) void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { if (!is_recording.load() || pInput == NULL) return; @@ -70,32 +73,26 @@ void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uin if (pInputFloat == NULL) return; std::lock_guard lock(buffer_mutex); - // 取消固定帧上限,仅保留内存保护(可选) - const size_t max_memory = 16000 * 120; // 最多120秒(约200MB内存) + // 安全保护:最多录制35秒(超时+5秒缓冲) + const size_t max_memory = 16000 * (RECORD_TIMEOUT + 5); if (audio_buffer.size() < max_memory) { audio_buffer.insert(audio_buffer.end(), pInputFloat, pInputFloat + frameCount); - // 更新实时录制时长 - recorded_seconds.store(audio_buffer.size() / 16000); + // 更新实时时长(精确到0.1秒) + recorded_seconds.store(static_cast(audio_buffer.size() / 16000.0)); } } -// 静音检测(裁剪无效音频,减少识别量) +// 静音检测(仅裁剪开头,保留末尾所有内容) int trim_silence(const float* audio_data, int audio_len, float threshold = 0.001f) { - // 跳过开头静音 int start = 0; while (start < audio_len && fabs(audio_data[start]) < threshold) { start++; } - // 跳过结尾静音 - int end = audio_len - 1; - while (end > start && fabs(audio_data[end]) < threshold) { - end--; - } - // 返回有效音频长度(至少保留1秒) - return std::max(end - start + 1, 16000); + // 关键:不裁剪末尾,确保最后几个字完整 + return std::max(audio_len - start, 16000); // 至少保留1秒 } -// 列出系统音频设备 +// 列出系统音频设备(修复参数类型:第三个参数为引用) void list_audio_devices(ma_context& context, ma_device_info** pCaptureInfos, ma_uint32& captureCount) { printf("\n📜 系统可用麦克风设备列表:\n"); printf("=============================================\n"); @@ -119,12 +116,12 @@ void list_audio_devices(ma_context& context, ma_device_info** pCaptureInfos, ma_ // 提示信息 void print_usage() { printf("=============================================\n"); - printf("🎤 语音识别程序(CPU优化版)\n"); + printf("🎤 语音识别程序(终极稳定版)\n"); printf("操作说明:\n"); printf(" 1. 按下【回车键】开始录制\n"); - printf(" 2. 说话完成后按回车停止录制并识别\n"); + printf(" 2. 说话完成后按回车停止(会自动收尾)\n"); printf(" 3. 录制超过%d秒自动停止并识别\n", RECORD_TIMEOUT); - printf(" 4. 录制中实时显示时长:【录制中... X秒】\n"); + printf(" 4. 录制中实时显示时长\n"); printf(" 5. Ctrl+C 退出程序\n"); printf("=============================================\n"); } @@ -133,21 +130,20 @@ void print_usage() { void print_cpu_optimize_tips() { printf("⚡ CPU优化配置说明:\n"); printf(" ✅ 已启用多线程识别(自动适配CPU核心数)\n"); - printf(" ✅ 已启用静音裁剪(减少无效音频识别)\n"); - printf(" ✅ 已使用贪心采样(最快的识别策略)\n"); + printf(" ✅ 停止前预留2秒缓冲,不丢最后音频\n"); + printf(" ✅ 修复线程死锁,手动停止立即响应\n"); printf(" 📌 模型优化:推荐使用 ggml-medium-q4_0.bin(量化版)\n"); printf(" 📌 编译优化:已用 -O3 最高级优化\n"); printf("=============================================\n"); } -// 核心识别函数(抽离复用) +// 核心识别函数 void recognize_audio(struct whisper_context* ctx, const std::vector& audio_data) { if (audio_data.empty()) { printf("⚠️ 未采集到音频数据,跳过识别\n"); return; } - // 静音裁剪 int valid_len = trim_silence(audio_data.data(), audio_data.size()); float valid_seconds = (float)valid_len / 16000; printf("🔍 正在识别(有效音频长度:%.2f秒,原始:%.2f秒)...\n", @@ -155,7 +151,6 @@ void recognize_audio(struct whisper_context* ctx, const std::vector& audi auto recognize_start = std::chrono::steady_clock::now(); - // CPU最优识别参数 whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY); wparams.language = "zh"; wparams.n_threads = std::max(2, (int)std::thread::hardware_concurrency()); @@ -169,13 +164,11 @@ void recognize_audio(struct whisper_context* ctx, const std::vector& audi wparams.print_special = false; wparams.token_timestamps = false; - // 执行识别 if (whisper_full(ctx, wparams, audio_data.data(), valid_len) != 0) { fprintf(stderr, "❌ 识别失败\n"); return; } - // 输出结果 auto recognize_duration = std::chrono::duration_cast( std::chrono::steady_clock::now() - recognize_start).count(); float speed = valid_seconds / (recognize_duration / 1000.0); @@ -210,10 +203,10 @@ int main(int argc, char** argv) { return 1; } - // 2. 枚举麦克风设备 + // 2. 枚举麦克风设备(修复参数传递:直接传变量,而非指针) ma_device_info* pCaptureInfos = NULL; ma_uint32 captureCount = 0; - list_audio_devices(context, &pCaptureInfos, captureCount); + list_audio_devices(context, &pCaptureInfos, captureCount); // 这里直接传captureCount(引用) // 3. 选择麦克风设备 ma_uint32 device_id = 0; @@ -223,7 +216,7 @@ int main(int argc, char** argv) { fprintf(stderr, "❌ 输入无效,使用默认设备ID 0\n"); device_id = 0; } - clear_input_buffer(); // 清空输入缓冲区 + clear_input_buffer(); // 清空缓冲区 } // 4. 初始化 Whisper 模型 @@ -238,7 +231,6 @@ int main(int argc, char** argv) { return 1; } - // 显示CPU优化提示 print_cpu_optimize_tips(); printf("✅ 模型加载成功!\n"); @@ -275,13 +267,26 @@ int main(int argc, char** argv) { print_usage(); - // 主循环 + // 主循环(彻底修复死锁逻辑) while (!exit_program.load()) { printf("\n👉 按下回车键开始录制...\n"); - getchar(); - clear_input_buffer(); // 清空残留输入 + + // 阻塞等待用户回车(确保由用户控制开始) + char input_char = 0; + while (!check_input_non_blocking() && !exit_program.load()) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + if (exit_program.load()) break; + + ssize_t ret1 = read(STDIN_FILENO, &input_char, 1); + (void)ret1; + clear_input_buffer(); // 清空其他残留输入 if (exit_program.load()) break; + if (input_char != '\n') { + printf("⚠️ 请按回车键触发录制!\n"); + continue; + } // 重置录制状态 is_recording.store(true); @@ -303,39 +308,47 @@ int main(int argc, char** argv) { bool is_timeout = false; auto start_time = std::chrono::steady_clock::now(); + bool manual_stop = false; - // 非阻塞监听输入 + 超时检测(核心修复) + // 核心循环 - 修复死锁:在设flag前等待,不阻塞主线程 while (is_recording.load() && !exit_program.load()) { - // 检查是否有回车输入(手动停止) - if (check_input_non_blocking()) { - char c; - read(STDIN_FILENO, &c, 1); - if (c == '\n') { // 只响应回车 - is_recording.store(false); - printf("\n🛑 已手动停止录制\n"); - break; - } - } - // 检查超时 auto duration = std::chrono::duration_cast( std::chrono::steady_clock::now() - start_time).count(); if (duration >= RECORD_TIMEOUT) { + printf("\n⏱️ 录制超时(%d秒),正在收尾...", RECORD_TIMEOUT); + fflush(stdout); + // 关键:先等2秒让数据写完,再停标志 + std::this_thread::sleep_for(std::chrono::milliseconds(FINISH_WAIT_MS)); is_recording.store(false); is_timeout = true; - printf("\n⏱️ 录制超时(%d秒),自动停止\n", RECORD_TIMEOUT); + printf("完成\n"); break; } + // 检查手动输入(非阻塞) + if (check_input_non_blocking(100)) { + char c; + ssize_t ret2 = read(STDIN_FILENO, &c, 1); + (void)ret2; + if (c == '\n') { + printf("\n🛑 已手动停止录制,正在收尾..."); + fflush(stdout); + // 关键:先sleep,让音频写完,再停标志 + std::this_thread::sleep_for(std::chrono::milliseconds(FINISH_WAIT_MS)); + is_recording.store(false); + manual_stop = true; + printf("完成\n"); + break; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - // 等待录制收尾 + // 等待进度线程退出(此时线程应该已经自然退出) progress_thread.join(); - printf("\n⏳ 正在收尾音频数据..."); - std::this_thread::sleep_for(std::chrono::milliseconds(RECORD_FINISH_WAIT_MS)); - printf("完成\n"); if (exit_program.load()) break; @@ -346,7 +359,7 @@ int main(int argc, char** argv) { captured_audio = audio_buffer; } - // 执行识别(无论手动/超时,自动识别) + // 执行识别 recognize_audio(ctx, captured_audio); }