#include "CalculateAudio.h" #include "spdlog/spdlog.h" #include "AudioData.h" #include StAudioNum::StAudioNum() { nTotal = 0; nLDataNum = 0; nLUnDataNum = 0; nRDataNum = 0; nRUnDataNum = 0; } StAudioNum& StAudioNum::operator=(const StAudioNum& obj) { roadInfo = obj.roadInfo; nTotal = obj.nTotal; nLDataNum = obj.nLDataNum; nLUnDataNum = obj.nLUnDataNum; nRDataNum = obj.nRDataNum; nRUnDataNum = obj.nRUnDataNum; return *this; } bool StAudioNum::AudioIsError(int nDataNum, int nUnDataNum, bool bLeft) { std::string strRoadName = roadInfo.strSoundCardName.toStdString() + ":" + std::to_string(roadInfo.roadInfo.nRoadNum); if (nDataNum > 0 && nUnDataNum > 0 && pcmErrorPercent > 0) { int nMax = nDataNum > nUnDataNum ? nDataNum : nUnDataNum; int nMin = nDataNum < nUnDataNum ? nDataNum : nUnDataNum; nMin *= 100; int nPer = nMin / nMax; if (nPer < pcmErrorPercent ) { if (bLeft) { SPDLOG_INFO("通道{}: 左声道数据不对称,判断为噪音, Left: {}, {}", strRoadName, nLDataNum, nLUnDataNum); } else { SPDLOG_INFO("通道{}: 右声道数据不对称,判断为噪音, Right: {}, {}", strRoadName, nRDataNum, nRUnDataNum); } return true; } } return false; } // 有问题的音频 bool StAudioNum::IsErrorAudio() { std::string strRoadName = roadInfo.strSoundCardName.toStdString() + ":" + std::to_string(roadInfo.roadInfo.nRoadNum); const int nMute = 200; if (nLDataNum > nMute || nLUnDataNum > nMute || nRDataNum > nMute || nRUnDataNum > nMute) { if (pcmLessPercent > 0) { const int nMinTotal = nTotal / pcmLessPercent; if ((nLDataNum > 0 || nLUnDataNum > 0) && (nLDataNum < nMinTotal || nLUnDataNum < nMinTotal) ) { SPDLOG_INFO("通道{} : 左声道数据很少, Left: {}, {}, Right: {}, {}", strRoadName, nLDataNum, nLUnDataNum, nRDataNum, nRUnDataNum); // 不是静音,但数据很少,直接判断为有误 return true; } if ((nRDataNum > 0 || nRUnDataNum > 0) && (nRDataNum < nMinTotal || nRUnDataNum < nMinTotal)) { SPDLOG_INFO("通道{} : 右声道数据很少, Left: {}, {}, Right: {}, {}", strRoadName, nLDataNum, nLUnDataNum, nRDataNum, nRUnDataNum); // 不是静音,但数据很少,直接判断为有误 return true; } } } else { SPDLOG_INFO("通道 {} : 静音了, Left: {}, {}, Right: {}, {}", strRoadName, nLDataNum, nLUnDataNum, nRDataNum, nRUnDataNum); return false; // 静音了 } if (AudioIsError(nLDataNum, nLUnDataNum, true)) { return true; } if (AudioIsError(nRDataNum, nRUnDataNum, false)) { return true; } return false; } /*-------------------------------------------------------------------------- * CAudio2ChanCorrelator 成员函数 *--------------------------------------------------------------------------*/ CAudio2ChanCorrelator::CAudio2ChanCorrelator() : m_nDisplayCutOffLevel(128) // 以前是64,音量为-50时反相不准确,所以修改为128 20150508 jinhl , m_bSignA(false) , m_fCorrelationSum(0.0f) , m_nCorrelationValue(0) , m_fDampingFactor(0.3f) { } CAudio2ChanCorrelator::~CAudio2ChanCorrelator() { } /** * @brief correlate data contained in A & B buffers ( range -100 to 100 ) * * @param ptrBufferA * @param ptrBufferB * @param nBufferLength * @param pMaxA * @param pMaxB * @param pRMSA * @param pRMSB * @param audioInfo * @return int */ int CAudio2ChanCorrelator::CorrelateChunks(const short * ptrBufferA, const short * ptrBufferB, int nBufferLength, short* pMaxA, short* pMaxB, short* pRMSA, short* pRMSB, StAudioNum &audioInfo) { int64_t nTotalA = 0, nTotlB = 0; *pMaxA = *pMaxB = *pRMSA = *pRMSB = 0; m_fCorrelationSum = 0; // reset correlation sum int nSamplesProcessedCount = 0; if( ptrBufferB ) { for ( int i = 0; i < nBufferLength; i+=2)// run through the buffer { if( abs((int)*ptrBufferA) > *pMaxA ) { *pMaxA = abs((int)*ptrBufferA); } nTotalA += (*ptrBufferA)*(*ptrBufferA); if( abs((int)*ptrBufferB) > *pMaxB ) { *pMaxB = abs((int)*ptrBufferB); } nTotlB += (*ptrBufferB)*(*ptrBufferB); //test sample A against threshold and if above record it's sign if (*ptrBufferA < m_nDisplayCutOffLevel && *ptrBufferA > -m_nDisplayCutOffLevel) { ptrBufferA += 2;// increment buffer pointers for next sample in the loop ptrBufferB += 2; continue; //level A below display threshold, hence no more processing required } if (*ptrBufferA > m_nDisplayCutOffLevel ) { // 金海林添加 2022-11-17 统计音频数据正负数 ++audioInfo.nLDataNum; m_bSignA = true; // A sample is positive } else { // 金海林添加 2022-11-17 统计音频数据正负数 ++audioInfo.nLUnDataNum; m_bSignA =false; // A sample is negative } //test sample B against threshold if (*ptrBufferB < m_nDisplayCutOffLevel && *ptrBufferB > -m_nDisplayCutOffLevel) { ptrBufferA += 2;// increment buffer pointers for next sample in the loop ptrBufferB += 2; continue; //level B below display threshold, hence no more processing required } if (*ptrBufferB > m_nDisplayCutOffLevel ) //B sample is positive { // 金海林添加 2022-11-17 统计音频数据正负数 ++audioInfo.nRDataNum; if(m_bSignA == true) // A & B samples are positive m_fCorrelationSum++; else m_fCorrelationSum--; // A negative & B positive } else //B sample is negative { // 金海林添加 2022-11-17 统计音频数据正负数 ++audioInfo.nRUnDataNum; if(m_bSignA == false) // A & B samples are negative m_fCorrelationSum++; else m_fCorrelationSum--; // A positive & B negative } // increment the buffers for next sample in the for loop ptrBufferA += 2; ptrBufferB += 2; nSamplesProcessedCount++; }//end of for loop m_nCorrelationValue = (int)(100 * (1- m_fDampingFactor) *m_fCorrelationSum/nSamplesProcessedCount + 1) + (int)(m_nCorrelationValue * m_fDampingFactor); *pRMSA = sqrt((long double)nTotalA/nBufferLength); *pRMSB = sqrt((long double)nTotlB/nBufferLength); } else { for ( int i = 0; i < nBufferLength; i++)// run through the buffer { if( abs((int)*ptrBufferA) > *pMaxA ) { *pMaxA = abs((int)*ptrBufferA); } nTotalA += (*ptrBufferA)*(*ptrBufferA); // increment the buffers for next sample in the for loop ptrBufferA += 1; }//end of for loop m_nCorrelationValue = 100; *pRMSA = sqrt((long double)nTotalA/nBufferLength); } return m_nCorrelationValue; } int CAudio2ChanCorrelator::CorrelateChunks(const short * ptrBufferA, const short * ptrBufferB, int nBufferLength, short* pMaxA, short* pMaxB, short* pRMSA, short* pRMSB, short* pLRAvg) //L+R的平均值 { int64_t nTotalA = 0, nTotlB = 0; *pMaxA = *pMaxB = *pRMSA = *pRMSB = *pLRAvg = 0; m_fCorrelationSum = 0; // reset correlation sum int nSamplesProcessedCount = 0; if( ptrBufferB ) { for ( int i = 0; i < nBufferLength; i+=2)// run through the buffer { if( abs((int)*ptrBufferA) > *pMaxA ) { *pMaxA = abs((int)*ptrBufferA); } nTotalA += (*ptrBufferA)*(*ptrBufferA); if( abs((int)*ptrBufferB) > *pMaxB ) { *pMaxB = abs((int)*ptrBufferB); } nTotlB += (*ptrBufferB)*(*ptrBufferB); if( abs((int)*ptrBufferA+(int)*ptrBufferB)/2 > *pLRAvg ) { *pLRAvg = abs((int)*ptrBufferA+(int)*ptrBufferB)/2; } //test sample A against threshold and if above record it's sign if (*ptrBufferA < m_nDisplayCutOffLevel && *ptrBufferA > -m_nDisplayCutOffLevel) { ptrBufferA += 2;// increment buffer pointers for next sample in the loop ptrBufferB += 2; continue; //level A below display threshold, hence no more processing required } if (*ptrBufferA > m_nDisplayCutOffLevel ) m_bSignA = true; // A sample is positive else m_bSignA =false; // A sample is negative //test sample B against threshold if (*ptrBufferB < m_nDisplayCutOffLevel && *ptrBufferB > -m_nDisplayCutOffLevel) { ptrBufferA += 2;// increment buffer pointers for next sample in the loop ptrBufferB += 2; continue; //level B below display threshold, hence no more processing required } if (*ptrBufferB > m_nDisplayCutOffLevel ) //B sample is positive { if(m_bSignA == true) // A & B samples are positive m_fCorrelationSum++; else m_fCorrelationSum--; // A negative & B positive } else //B sample is negative { if(m_bSignA == false) // A & B samples are negative m_fCorrelationSum++; else m_fCorrelationSum--; // A positive & B negative } // increment the buffers for next sample in the for loop ptrBufferA += 2; ptrBufferB += 2; nSamplesProcessedCount++; }//end of for loop m_nCorrelationValue = (int)(100 * (1- m_fDampingFactor) *m_fCorrelationSum/nSamplesProcessedCount + 1) + (int)(m_nCorrelationValue * m_fDampingFactor); *pRMSA = sqrt((long double)nTotalA/nBufferLength); *pRMSB = sqrt((long double)nTotlB/nBufferLength); } else { for ( int i = 0; i < nBufferLength; i++)// run through the buffer { if( abs((int)*ptrBufferA) > *pMaxA ) { *pMaxA = abs((int)*ptrBufferA); } nTotalA += (*ptrBufferA)*(*ptrBufferA); // increment the buffers for next sample in the for loop ptrBufferA += 1; }//end of for loop m_nCorrelationValue = 100; *pRMSA = sqrt((long double)nTotalA/nBufferLength); } return m_nCorrelationValue; } /** * @brief returns current correlation value (between -100 to 100) * * @return int */ int CAudio2ChanCorrelator::GetCorrelationLevel(void) { return m_nCorrelationValue; } /*-------------------------------------------------------------------------- * 全局函数 *--------------------------------------------------------------------------*/ int calculateDB(short val) { if( val == 0 ) { return -90; } if( val < 0 ) { val = -val; } int iDB = ( 20*log10((long double)val) - 90.3 ); if(iDB > VOLUME_MAX_BD || iDB < VOLUME_MIN_BD) { iDB = VOLUME_MIN_BD; } return iDB; } /*-------------------------------------------------------------------------- * 计算音量的静音、过载、反相等功能的类 *--------------------------------------------------------------------------*/ CaculateDBData::CaculateDBData() { /* 初始化环形队列 */ ringQueue.setQueueCapacity(CACHE_DATA_SECOND_MAX); ringQueue.setDefaultValue(nullptr); } CaculateDBData::~CaculateDBData() { if(!ringQueue.isEmpty()) { OneSecondData* data = nullptr; while(!ringQueue.isEmpty()) { data = ringQueue.front_pop(); if(data) { delete data; // 释放内存 data = nullptr; } } ringQueue.clearQueue(); // 清空队列 } } /* 设置偏移值 */ void CaculateDBData::setOffset(long offset) { /* 将ms转换成秒 */ int offsetSeconds = offset / 1000; if(offsetSeconds < ringQueue.QueueSize()) { m_offset = offsetSeconds; } } /** * @brief 根据参数计算静音的起始和结束位置 * * @param param 这个频道的音量参数 * @param avgDBSeconds 计算静音的平均音量秒数 * @param startPos 开始位置 * @param endPos 结束位置 * @return true * @return false */ bool CaculateDBData::calculateSilent(const StVolumeParam& param, const int avgDBSeconds, int& startPos, int& endPos) { startPos = -1; endPos = -1; if(param.GetSilentSwitch()) { const int silentDuration = param.GetSilentDuration(); if (silentDuration >= CACHE_DATA_SECOND_MAX) { SPDLOG_WARN("静音持续时间({})不能大于等于最大缓存{}秒", silentDuration, CACHE_DATA_SECOND_MAX); return false; } if (ringQueue.QueueSize() < (silentDuration + m_offset) || silentDuration <= 0) { SPDLOG_INFO("队列中数据暂不满足静音持续时间检测({})的要求,当前队列大小为{}", silentDuration + m_offset, ringQueue.QueueSize()); return false; } if (silentDuration >= avgDBSeconds) { SPDLOG_WARN("静音持续时间({})不能大于等于{}秒", silentDuration, avgDBSeconds); return false; } int nNum = 0; /* 计算起始位置,获取最新的需要用到的silentDuration个数据,环形队列数据永远从0开始获取, * 如果需要获取最新的数据,那么需要从队列的尾部开始获取 * 如果有偏移,那么起始点就是如下计算方式 */ int queueSize = ringQueue.QueueSize(); int startIndex = queueSize - silentDuration - m_offset; for(int j = startIndex; j < queueSize; ++j) { OneSecondData *data = ringQueue[j]; if(data == nullptr) { continue; } if (data->CalcDBOneSecond(param)) { endPos = j; if (startPos < 0) { startPos = j; } ++nNum; } } nNum = nNum * 100; if (nNum >= param.GetSilentNum()) { return true; } } return false; } /** * @brief 计算过载 * * @param param 计算参数 * @param avgDBSeconds 计算音量值需要的秒数 * @param startPos 过载开始位置 * @param endPos 过载结束位置 * @param lastReversed 上次是否是过载 * @return true * @return false */ bool CaculateDBData::calculateOverload(const StVolumeParam& param, const int avgDBSeconds, int& startPos, int& endPos, bool bLastOverload) { startPos = -1; endPos = -1; if(!param.GetOverloadSwitch()) { return false; // 没有开启过载检测 } /* 获取需要计算的音量秒数 */ const int calculateDuration = param.GetOverloadDuration(); if (calculateDuration >= CACHE_DATA_SECOND_MAX) { SPDLOG_WARN("过载持续时间({})不能大于等于最大缓存{}秒", calculateDuration, CACHE_DATA_SECOND_MAX); return false; } /* 判断队列中数据数量是否满足计算需要的数目 */ if(ringQueue.QueueSize() < calculateDuration || calculateDuration <= 0) { SPDLOG_INFO("队列中数据暂不满足过载持续时间检测({})的要求,当前队列大小为{}", calculateDuration, ringQueue.QueueSize()); return false; } if (calculateDuration >= avgDBSeconds) { SPDLOG_WARN("过载持续时间({})不能大于等于{}秒", calculateDuration, avgDBSeconds); return false; } /* 计算起始位置,获取最新的需要用到的 overloadDuration 个数据,环形队列数据永远从0开始获取, * 如果需要获取最新的数据,那么需要从队列的尾部开始获取 */ int queueSize = ringQueue.QueueSize(); int startIndex = queueSize - calculateDuration; // 第一秒和最后1秒都是过载的,如果中间有一秒不是过载的,那么中间这秒也仍然认为是过载的 int nMidOverload = 0; int nOverloadNum = 0; for(int j = startIndex; j < queueSize; ++j) { OneSecondData *data = ringQueue[j]; if(data == nullptr) { continue; // 没有获取到最新数据,继续等待 } if (data->CalcOverloadOneSecond(param)) { // 记录过载的起始位置 if (startPos < 0) { startPos = j; } endPos = j; ++nOverloadNum; } else { if (j > 0 && j < calculateDuration - 1) { ++nMidOverload; } } //nOverloadNum += data->CalcOverload(param); } // 第一秒和最后1秒都是过载的,如果中间有一秒不是过载的,那么中间这秒也仍然认为是过载的 if (1 == nMidOverload && calculateDuration == nOverloadNum + 1) { nOverloadNum = calculateDuration; } // 如果上一次是过载,那么允许后面的过载少一个 if (bLastOverload && calculateDuration == nOverloadNum + 1) { nOverloadNum = calculateDuration; } nOverloadNum = nOverloadNum * 100; if (nOverloadNum >= param.GetOverloadNum()) { return true; } return false; } /* 计算反相 */ bool CaculateDBData::calculatePhase(const StVolumeParam& param, const int avgDBSeconds, int& startPos, int& endPos, bool isLastReversed) { startPos = -1; endPos = -1; if(!param.GetPhaseSwitch()) { return false; // 没有开启反相检测 } /* 获取需要计算的音量秒数 */ const int calculateDuration = param.GetPhaseDuration(); if (calculateDuration >= CACHE_DATA_SECOND_MAX) { SPDLOG_WARN("反相持续时间({})不能大于等于最大缓存{}秒", calculateDuration, CACHE_DATA_SECOND_MAX); return false; } /* 判断队列中数据数量是否满足计算需要的数目 */ if(ringQueue.QueueSize() < calculateDuration || calculateDuration <= 0) { SPDLOG_INFO("队列中数据暂不满足反相持续时间检测({})的要求,当前队列大小为{}", calculateDuration, ringQueue.QueueSize()); return false; } if (calculateDuration >= avgDBSeconds) { SPDLOG_WARN("反相持续时间({})不能大于等于{}秒", calculateDuration, avgDBSeconds); return false; } StPhase phaseData; //1、上一秒是否反相,如果是,当反相值比较小时也表示反相 bool bLastTest = isLastReversed; /* 计算起始位置,获取最新的需要用到的 overloadDuration 个数据,环形队列数据永远从0开始获取, * 如果需要获取最新的数据,那么需要从队列的尾部开始获取 */ int queueSize = ringQueue.QueueSize(); int startIndex = queueSize - calculateDuration; for(int j = startIndex; j < queueSize; ++j) { OneSecondData *data = ringQueue[j]; if(data == nullptr) { continue; // 没有获取到最新数据,继续等待 } if (data->CalcPhaseOneSecond(param, bLastTest)) { // 记录反相的起始位置 if (startPos < 0) { startPos = j; } endPos = j; ++phaseData.iReversedNum; bLastTest = true; } else { bLastTest = false; ++phaseData.iUnReversedNum; } //stPhase phase; //data->CalcPhase(param, phase, bLastReversed); //stTotal.iReversedNum += phase.iReversedNum; //stTotal.iUnReversedNum += phase.iUnReversedNum; } // 反相百分比 int iRate = phaseData.iReversedNum * 100; return iRate >= param.GetPhaseNum(); } /** * @brief 判断平均音量是否低于设置的值 * * @param minDBLongTime 最小声音时长 * @param minDB 最小声音 * @return true * @return false */ bool CaculateDBData::isAvgDBLessThan(const long minDBLongTime, const long minDB) { if (ringQueue.QueueSize() < minDBLongTime || minDBLongTime <= 0) { return false; } int nNum = 0; double dTotalLeftDB = 0; double dTotalRightDB = 0; /* 计算起始位置,获取最新的minDBLongTime个数据,这里不需要管偏移值 */ int startIndex = ringQueue.QueueSize() - minDBLongTime; for(int j = startIndex; j < minDBLongTime; ++j) { OneSecondData *data = ringQueue[j]; if(data == nullptr) { continue; } for(int i = 0; i < VOLUME_INFO_NUM; ++i) { dTotalLeftDB += data->aryLeftDB[i]; dTotalRightDB += data->aryRightDB[i]; ++nNum; } } if (nNum > 0) { // 求平均值 int leftDB = dTotalLeftDB / nNum; int rightDB = dTotalRightDB / nNum; if (leftDB <= minDB && rightDB <= minDB) { return true; } } return false; } /** * @brief 计算正弦波是否是噪音,正弦波几秒直接判断为噪音 * * @param sinSeconds 判断是否是正弦波的条件秒数 * @param calSeconds 自定义的计算描述 * @param leftDB * @param rightDB * @return true * @return false */ bool CaculateDBData::isSinDB(const int sinSeconds, const int calSeconds, int& leftDB, int& rightDB) { int overloadDuration = sinSeconds; if (ringQueue.QueueSize() < overloadDuration || overloadDuration <= 0) { return false; } if (overloadDuration >= calSeconds) { overloadDuration = calSeconds - 5; } /* 计算起始位置,获取最新的minDBLongTime个数据,这里不需要管偏移值 */ int startIndex = ringQueue.QueueSize() - overloadDuration; for(int j = startIndex; j < overloadDuration; ++j) { OneSecondData *data = ringQueue[j]; if(data == nullptr) { continue; } int nLeftDBTemp, nRightDBTmp; if (!data->isConstDB(nLeftDBTemp, nRightDBTmp)) { // 不是正弦波,固定音量值 return false; } if (0 == j) { leftDB = nLeftDBTemp; rightDB = nRightDBTmp; } // 虽然是固定音量值,但是音量值不相等 else if (leftDB != nLeftDBTemp || rightDB != nRightDBTmp) { return false; } } return true; }