NoiseDetectThread.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. #include "NoiseDetectThread.h"
  2. #include <random>
  3. #include "CreateWAVThread.h"
  4. #include "CreateLongFileThread.h"
  5. #include "ThreadAlarmManager.h"
  6. #include "ThreadManager.h"
  7. #include "commonDefine.h"
  8. #include "GlobalInfo.h"
  9. #include "signalstats.h"
  10. #include "spdlog.h"
  11. NoiseDetectThread::NoiseDetectThread(CalculateThreadInfo_t& threadInfo)
  12. : BaseCalculateThread(threadInfo),
  13. m_leftRightData(0)
  14. {
  15. m_logger = spdlog::get("ACAServer");
  16. if(m_logger == nullptr)
  17. {
  18. fmt::print("NoiseDetectThread: ACAServer Logger not found.\n");
  19. return;
  20. }
  21. }
  22. NoiseDetectThread::~NoiseDetectThread()
  23. {
  24. }
  25. /* 开启对比项通道的噪音报警功能 */
  26. void NoiseDetectThread::startCompareItemNoiseAlarm(const int itemID, const QString strName, const CompareItemRoadInfo_t& compareItemRoadInfo)
  27. {
  28. std::lock_guard<std::mutex> lock(m_mutexAlarm);
  29. /* 如果已经存在这个对比项的报警信息,就不需要重复添加了 */
  30. if(m_mapAlarmInfo.find(itemID) != m_mapAlarmInfo.end())
  31. {
  32. SPDLOG_LOGGER_INFO(m_logger, "{} 对比项 {} 的噪音报警功能已经开启", m_logBase, strName.toStdString());
  33. return;
  34. }
  35. /* 添加对比项的噪音报警信息 */
  36. AlarmInfo_t alarmInfo;
  37. alarmInfo.CompareItemID = itemID;
  38. alarmInfo.strCompareItemName = strName.toStdString();
  39. alarmInfo.isAlarm = false; // 初始状态没有报警
  40. alarmInfo.RoadInfo = compareItemRoadInfo;
  41. alarmInfo.AlarmType = EAlarmType::EAT_Noise;
  42. m_mapAlarmInfo[itemID] = alarmInfo;
  43. }
  44. /* 关闭对比项通道的噪音报警功能 */
  45. void NoiseDetectThread::stopCompareItemNoiseAlarm(const int itemID, const QString strName, const CompareItemRoadInfo_t& compareItemRoadInfo)
  46. {
  47. std::lock_guard<std::mutex> lock(m_mutexAlarm);
  48. auto it = m_mapAlarmInfo.find(itemID);
  49. if(it != m_mapAlarmInfo.end())
  50. {
  51. SPDLOG_LOGGER_INFO(m_logger, "{} 对比项 {} 的噪音报警功能已关闭", m_logBase, strName.toStdString());
  52. m_mapAlarmInfo.erase(it); // 移除对比项的噪音报警信息
  53. }else {
  54. SPDLOG_LOGGER_WARN(m_logger, "{} 对比项 {} 的噪音报警功能未开启,无法关闭", m_logBase, strName.toStdString());
  55. }
  56. }
  57. /* 线程功能函数 */
  58. void NoiseDetectThread::task()
  59. {
  60. /* 初始化数据 */
  61. if(!initData())
  62. {
  63. SPDLOG_LOGGER_ERROR(m_logger, "{} 初始化数据失败", m_logBase);
  64. return;
  65. }
  66. SPDLOG_LOGGER_INFO(m_logger, " ★ {} 噪音检测线程开始运行 ", m_logBase);
  67. while(m_isRunning)
  68. {
  69. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  70. /*------------------------------------------------------------------------
  71. * 获取最新的左右声道数据
  72. *------------------------------------------------------------------------*/
  73. if(!m_pThreadWav->getLatestLeftRightData(m_leftRightData))
  74. {
  75. continue;
  76. }
  77. // SPDLOG_LOGGER_ERROR(m_logger, "{} 获取最新的左右声道数据成功,开始调用动态库计算噪音------------------------------------------ ", m_logBase);
  78. /*------------------------------------------------------------------------
  79. * 计算数据
  80. *------------------------------------------------------------------------*/
  81. if(!detectNoise())
  82. {
  83. continue;
  84. }
  85. /*------------------------------------------------------------------------
  86. * 处理结果,写噪音报警信息到数据库(这里不写数据库,只计算结果返回给对比项)
  87. * 上面那个函数已经把结果保存了
  88. *------------------------------------------------------------------------*/
  89. saveResult();
  90. }
  91. clearData(); // 清理数据
  92. SPDLOG_LOGGER_INFO(m_logger, " ★ {} 噪音检测线程结束 ", m_logBase);
  93. }
  94. /* 初始化数据 */
  95. bool NoiseDetectThread::initData()
  96. {
  97. auto begin = m_threadInfo.compareItemInfo.mapRoad.begin(); // 获取第一个通道的信息
  98. m_roadInfo = begin->scRoadInfo; // 录音通道信息
  99. m_roadName = fmt::format("{}:{}", m_roadInfo.strSoundCardID.toStdString(), m_roadInfo.roadInfo.nRoadNum);
  100. m_logBase = fmt::format("噪音检测通道 {}:", m_roadName);
  101. /* 获取线程 */
  102. auto now = std::chrono::steady_clock::now();
  103. while(true)
  104. {
  105. if(m_pThreadWav == nullptr)
  106. {
  107. m_pThreadWav = ThreadMan.getCreateWAVThread(m_roadInfo.nSoundCardNum, m_roadInfo.roadInfo.nRoadNum);
  108. }
  109. if(m_pThreadCreateAlarm == nullptr)
  110. {
  111. m_pThreadCreateAlarm = ThreadMan.getCreateLongFileThread(m_roadInfo.nSoundCardNum, m_roadInfo.roadInfo.nRoadNum);
  112. }
  113. if(m_pThreadWav != nullptr && m_pThreadCreateAlarm != nullptr)
  114. {
  115. break; // 获取到线程了
  116. }
  117. if(std::chrono::steady_clock::now() - now > std::chrono::seconds(10))
  118. {
  119. SPDLOG_LOGGER_ERROR(m_logger, "{} 获取数据线程超时", m_logBase);
  120. return false; // 超时了,获取线程失败
  121. }
  122. }
  123. auto sampleRate = GInfo.sampleRate();
  124. m_sample_rate = static_cast<double>(sampleRate);
  125. /* 获取噪音检测参数 */
  126. m_noiseDetectParam = SysConfig.getNoiseDetectParam();
  127. /* 设置噪音检测参数 */
  128. m_ringQueueIsNoise.setQueueCapacity(m_noiseDetectParam.nNoiseDetectContinueCount);
  129. return true;
  130. }
  131. /* 清理数据 */
  132. void NoiseDetectThread::clearData()
  133. {
  134. /* 释放资源 */
  135. // signalstats_wrapper::finalize();
  136. }
  137. /* 调用动态库检测噪音 */
  138. bool NoiseDetectThread::detectNoise()
  139. {
  140. // SPDLOG_LOGGER_TRACE(m_logger, "{} 开始调用动态库计算噪音", m_logBase);
  141. SPDLOG_LOGGER_INFO(m_logger, "{} 左声道数据大小: {}, 右声道数据大小: {}",
  142. m_logBase, m_leftRightData.vecLeftData.size(), m_leftRightData.vecRightData.size());
  143. auto startTime = std::chrono::steady_clock::now();
  144. bool isNoiseLeft = false; /* 是否检测到左声道噪音 */
  145. bool isNoiseRight = false; /* 是否检测到右声道噪音 */
  146. try
  147. {
  148. /*-------------------------- 先检测左声道 --------------------------*/
  149. nJson jsonOutput;
  150. auto ret = signalstats::detect_signal(
  151. jsonOutput, /* 返回结果,和jsonResult是一样的 */
  152. m_leftRightData.vecLeftData, /* 左声道数据 */
  153. m_sample_rate, /* 采样率(HZ) */
  154. m_silence_threshold, /* 静音阈值 */
  155. m_db_threshold, /* 分贝阈值 */
  156. m_cv_threshold, /* 变异系数阈值 */
  157. m_window_params, /* 窗函数参数 */
  158. m_nperseg, /* 每段样本数 */
  159. m_noverlap, /* 重叠样本数 */
  160. m_nfft, /* FFT点数 */
  161. true /* 是否输出调试信息, true表示输出调试信息, false表示不输出调试信息 */
  162. );
  163. isNoiseLeft = jsonOutput["noise"].is_null() ? false : jsonOutput["noise"].get<bool>();
  164. std::chrono::duration<double> duration = std::chrono::steady_clock::now() - startTime;
  165. std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
  166. SPDLOG_LOGGER_DEBUG(m_logger, "{} 计算左声道噪音耗时: {}ms", m_logBase, ms.count());
  167. startTime = std::chrono::steady_clock::now(); /* 重置开始时间 */
  168. /*-------------------------- 再检测右声道 --------------------------*/
  169. jsonOutput.clear(); /* 清空输出结果 */
  170. signalstats::detect_signal(
  171. jsonOutput, /* 返回结果,和jsonResult是一样的 */
  172. m_leftRightData.vecRightData, /* 右声道数据 */
  173. m_sample_rate, /* 采样率(HZ) */
  174. m_silence_threshold, /* 静音阈值 */
  175. m_db_threshold, /* 分贝阈值 */
  176. m_cv_threshold, /* 变异系数阈值 */
  177. m_window_params, /* 窗函数参数 */
  178. m_nperseg, /* 每段样本数 */
  179. m_noverlap, /* 重叠样本数 */
  180. m_nfft, /* FFT点数 */
  181. false /* 是否输出调试信息, true表示输出调试信息, false表示不输出调试信息 */
  182. );
  183. isNoiseRight = jsonOutput["noise"].is_null() ? false : jsonOutput["noise"].get<bool>();
  184. // SPDLOG_LOGGER_DEBUG(m_logger, "{} 右声道噪音检测结果: {}", m_logBase, jsonOutput.dump(4));
  185. }
  186. catch (const std::exception& e)
  187. {
  188. SPDLOG_LOGGER_ERROR(m_logger, "{} 调用动态库检测噪音失败: {}", m_logBase, e.what());
  189. return false;
  190. }
  191. std::chrono::duration<double> duration = std::chrono::steady_clock::now() - startTime;
  192. std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
  193. SPDLOG_LOGGER_DEBUG(m_logger, "{} 计算右声道噪音耗时: {}ms", m_logBase, ms.count());
  194. SPDLOG_LOGGER_DEBUG(m_logger, "{} 左声道噪音检测结果: {}, 右声道噪音检测结果: {}", m_logBase, isNoiseLeft, isNoiseRight);
  195. /* -------------------------- 和以往的结果对比 --------------------------*/
  196. m_currentIsNoise = (isNoiseLeft || isNoiseRight); /* 是否检测到噪音 */
  197. return true;
  198. }
  199. /* 保存结果 */
  200. void NoiseDetectThread::saveResult()
  201. {
  202. /* 将当前噪音检测结果保存到环形队列中 */
  203. m_ringQueueIsNoise.push(m_currentIsNoise);
  204. /* 计算出噪音个数和噪音所占百分比 */
  205. const int size = m_ringQueueIsNoise.QueueSize();
  206. int numNoise = 0; // 噪音的个数
  207. int numCountinueNoise = 0; // 连续噪音的个数
  208. double percentNoise = 0.0; // 噪音所占百分比
  209. for(int i = 0; i < size; ++i)
  210. {
  211. if(m_ringQueueIsNoise.at(i))
  212. {
  213. numNoise++;
  214. numCountinueNoise++;
  215. }else {
  216. numCountinueNoise = 0; // 如果不是噪音,连续噪音计数清零
  217. }
  218. }
  219. /* 判断是否是噪音预警 */
  220. if(numCountinueNoise >= m_noiseDetectParam.nNoiseContinueCountIsWarn)
  221. {
  222. m_isNoiseWarning.store(true);
  223. }
  224. percentNoise = numNoise * 100.0 / m_noiseDetectParam.nNoiseContinueCountPercent;
  225. /* 根据噪音所占的百分比判断是否是噪音 */
  226. if(percentNoise >= m_noiseDetectParam.nNoiseContinueCountPercent)
  227. {
  228. m_isNoise.store(true);
  229. }else {
  230. m_isNoise.store(false);
  231. }
  232. if(m_isNoise.load())
  233. {
  234. /* 判断上次是否是噪音 */
  235. if(!m_isNoiseLast)
  236. {
  237. /* 开始噪音报警 */
  238. m_isNoiseWarning = true;
  239. /* 通知对比项线程,开始噪音报警 */
  240. std::lock_guard<std::mutex> lock(m_mutexAlarm);
  241. for(auto& pair : m_mapAlarmInfo)
  242. {
  243. AlarmInfo_t& alarmInfo = pair.second;
  244. if(!alarmInfo.isAlarm) // 如果没有报警
  245. {
  246. alarmInfo.isAlarm = true; // 设置为报警状态
  247. /* 向前推算噪音开始时间,开始时间需要向前推 n次噪音检测的时间 x 单次噪音检测需要的时间,单位秒 */
  248. int nNoiseDetectTime = m_noiseDetectParam.nNoiseDetectContinueCount * m_noiseDetectParam.nNoiseDetectDuration;
  249. QDateTime startTime = m_leftRightData.startTime;
  250. startTime = startTime.addSecs(-nNoiseDetectTime);
  251. alarmInfo.StartTime = startTime;
  252. SPDLOG_LOGGER_WARN(m_logger, "{} 对比项-{} {} 开始噪音报警", m_logBase, alarmInfo.strCompareItemName,
  253. alarmInfo.RoadInfo.strCompareRoadName.toStdString());
  254. /* 这里可以添加通知录制报警文件的线程开始录音 */
  255. if(m_pThreadCreateAlarm != nullptr)
  256. {
  257. m_pThreadCreateAlarm->startRecordAlarmFile(alarmInfo);
  258. }
  259. }
  260. }
  261. }
  262. }else
  263. {
  264. /* 没有噪音,判断是否结束报警 */
  265. if(m_isNoiseLast)
  266. {
  267. /* 停止噪音报警 */
  268. m_isNoiseWarning = false;
  269. /* 通知对比项线程,结束噪音报警 */
  270. std::lock_guard<std::mutex> lock(m_mutexAlarm);
  271. for(auto& pair : m_mapAlarmInfo)
  272. {
  273. AlarmInfo_t& alarmInfo = pair.second;
  274. if(alarmInfo.isAlarm) // 如果正在报警
  275. {
  276. // 设置为非报警状态
  277. alarmInfo.isAlarm = false;
  278. /* 设置结束时间 */
  279. alarmInfo.EndTime = m_leftRightData.endTime;
  280. SPDLOG_LOGGER_WARN(m_logger, "{} 对比项-{} {} 结束噪音报警", m_logBase, alarmInfo.strCompareItemName,
  281. alarmInfo.RoadInfo.strCompareRoadName.toStdString());
  282. /* 这里可以添加通知录制报警文件的线程停止录音 */
  283. if(m_pThreadCreateAlarm != nullptr)
  284. {
  285. m_pThreadCreateAlarm->stopRecordAlarmFile(alarmInfo);
  286. }
  287. /* 写入数据库,并清空时间 */
  288. AlarmManager.addAlarmInfo(alarmInfo);
  289. alarmInfo.StartTime = QDateTime();
  290. alarmInfo.EndTime = QDateTime();
  291. }
  292. }
  293. }
  294. }
  295. }