////////////////////////////////////////////////////////////////////// // iAVC -- integer Automatic Volume Control (on samples given it) // // Copyright (C) 2002 Vincent A. Busam // 15754 Adams Ridge // Los Gatos, CA 95033 // email: vince@busam.com // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // If you change the algorithm or find other sets of values that improve the // output results, please send me a copy so that I can incorporate them into // iAVC. Of course, you are not required to send me any updates under the // LGPL license, but please consider doing so under the spirit of open source // and in appreciation of being able to take advantage of my efforts expended // in developing iAVC. // This code implements a "poor man's" dynamic range compression algorithm // that was build on hueristics. It's purpose is to perform dynamic range // compression in real time using only integer arithmetic. Processing time // is more important than memory. It acts like an automatic volume control, // frequently adjusting the volume (gain) based on an average of the current // sound samples #if !defined(IAVC_INLINE) || ( !defined(IAVC_SETNEXTSAMPLE) && !defined(IAVC_GETNEXTSAMPLE) && !defined(IAVC_ADJUSTMULTIPLIER) ) #ifdef _WINDOWS //#include "stdafx.h" // don't use precompiled headers on this file #endif #ifndef __cplusplus // You really should consider using C++. // Granted it is not needed or even useful in all situations, but real // object oriented design and code (not faux OO code like in MFC) // has lots of benefits. #endif #include "iAVC.h" #if (defined ( _WINDOWS ) | defined ( _DEBUG )) #define c_WithDebug 1 // should be = 1 #else #define c_WithDebug 0 #endif #ifdef IDEBUGLOG #include "LogFlags.h" #include "../Logger/IDebugLog.h" #else #ifdef _DEBUG #ifdef _WINDOWS #define _MFC_OVERRIDES_NEW #include // user _RPTF0 to get file name and line in output #define log0(fn,lf,ulid,fmt) _RPT0(_CRT_WARN,fmt); #define log1(fn,lf,ulid,fmt,p1) _RPT1(_CRT_WARN,fmt,p1); #define log2(fn,lf,ulid,fmt,p1,p2) _RPT2(_CRT_WARN,fmt,p1,p2); #define log3(fn,lf,ulid,fmt,p1,p2,p3) _RPT3(_CRT_WARN,fmt,p1,p2,p3); #define log4(fn,lf,ulid,fmt,p1,p2,p3,p4) _RPT4(_CRT_WARN,fmt,p1,p2,p3,p4); #define log5(fn,lf,ulid,fmt,p1,p2,p3,p4,p5) _RPT5(_CRT_WARN,fmt,p1,p2,p3,p4,p5); #elif #define log0(fn,lf,ulid,fmt) fprintf(stderr,fmt); #define log1(fn,lf,ulid,fmt,p1) fprintf(stderr,fmt,p1); #define log2(fn,lf,ulid,fmt,p1,p2) fprintf(stderr,fmt,p1,p2); #define log3(fn,lf,ulid,fmt,p1,p2,p3) fprintf(stderr,fmt,p1,p2,p3); #define log4(fn,lf,ulid,fmt,p1,p2,p3,p4) fprintf(stderr,fmt,p1,p2,p3,p4); #define log5(fn,lf,ulid,fmt,p1,p2,p3,p4,p5) fprintf(stderr,fmt,p1,p2,p3,p4,p5); #endif // _WINDOWS #else #define log0(fn,lf,ulid,fmt) ; #define log1(fn,lf,ulid,fmt,p1) ; #define log2(fn,lf,ulid,fmt,p1,p2) ; #define log3(fn,lf,ulid,fmt,p1,p2,p3) ; #define log4(fn,lf,ulid,fmt,p1,p2,p3,p4) ; #define log5(fn,lf,ulid,fmt,p1,p2,p3,p4,p5) ; #endif // _DEBUG #endif /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // iAVC constructor // /////////////////////////////////////////////////////////////////////////////// AutoVolCtrl::AutoVolCtrl() { m_pSampleList = NULL; m_nSampleWindowSize = DEFAULT_SAMPLE_WINDOW_SIZE; m_nSamplesInAvg = DEFAULT_ADJUSTER_WINDOW_SIZE; m_nLookAheadWindowSize = DEFAULT_LOOKAHEAD_WINDOW_SIZE; m_nMinSamplesBeforeSwitch = DEFAULT_MINIMUM_SAMPLES_BEFORE_SWITCH; m_nNumTracks = DEFAULT_NUMBER_OF_TRACKS; m_nMaxChangePct = DEFAULT_MAX_PCT_CHANGE_AT_ONCE; m_nMaxSampleValue = DEFAULT_MAX_SAMPLE_VALUE; SetSampleWindowSize ( m_nSampleWindowSize, m_nSamplesInAvg, m_nLookAheadWindowSize ); Reset(); // set multipliers to a nil transform for ( int i = 0 ; i < MULTIPLY_PCT_ARRAY_SIZE ; ++i ) m_nMultiplyPct [ i ] = IAVCMULTIPLYPCT ( APPLY_MULTIPLY_FACTOR ( 1 ) ); // default to no transform } /////////////////////////////////////////////////////////////////////////////// // // iAVC destructor // /////////////////////////////////////////////////////////////////////////////// AutoVolCtrl::~AutoVolCtrl() { // Dump diagnostic information log1(CN_iAVC,LL_DEBUG,0, "Sample Window Size %d\n", m_nSampleWindowSize ); log1(CN_iAVC,LL_DEBUG,0, "Adjuster Window Size %d\n", m_nSamplesInAvg ); log1(CN_iAVC,LL_DEBUG,0, "Min Samples to Switch %d\n", m_nMinSamplesBeforeSwitch ); log1(CN_iAVC,LL_DEBUG,0, "Pct Change threshold %d\n", m_nMaxChangePct ); log1(CN_iAVC,LL_DEBUG,0, "Number of Samples = %d\n", m_nTotalSamples ); log1(CN_iAVC,LL_DEBUG,0, "Multiplier changes = %d\n", m_nNumMultiplerChanges ); log1(CN_iAVC,LL_DEBUG,0, "Number of clips = %d\n", m_nClips ); if ( m_pSampleList != NULL ) delete []m_pSampleList; } /////////////////////////////////////////////////////////////////////////////// // // Reset // /////////////////////////////////////////////////////////////////////////////// void AutoVolCtrl::Reset() { ZeroSampleWindow(); SetMinSamplesBeforeSwitch ( m_nMinSamplesBeforeSwitch ); SetMaxPctChangeAtOnce ( m_nMaxChangePct ); // e.g. 10% SetNumberTracks ( m_nNumTracks ); m_nMaxSampleValue = DEFAULT_MAX_SAMPLE_VALUE; //TODO: make a method so caller can set // set our internal data m_nSampleAvgSum = 0; m_nSamplesInSum = 0; m_nCurrentMultiplier = APPLY_MULTIPLY_FACTOR ( 1 ); m_nTotalSamples = 0; m_nNumMultiplerChanges = 0; m_nClips = 0; m_nLookaheadSum = 0; m_nSamplesInLookahead = 0; m_nNumSamplesBeforeNextSwitch = 0; // allows switch on first GetSample } /////////////////////////////////////////////////////////////////////////////// // // SetSampleWindowSize // /////////////////////////////////////////////////////////////////////////////// bool AutoVolCtrl::SetSampleWindowSize ( unsigned long nSampleWindowSize, unsigned long nAdjusterWindowSize, unsigned long nLookAheadWindowSize ) { if ( nSampleWindowSize > MAX_SAMPLE_WINDOW_SIZE ) return false; // sums may overflow and we use int for indicies if ( nSampleWindowSize < nAdjusterWindowSize + nLookAheadWindowSize ) return false; m_nSamplesInAvg = nAdjusterWindowSize; m_nLookAheadWindowSize = nLookAheadWindowSize; if ( m_nSampleWindowSize != nSampleWindowSize || m_pSampleList == NULL ) { // window size has changed m_nSampleWindowSize = nSampleWindowSize; if ( m_pSampleList ) delete m_pSampleList; m_pSampleList = new Sample [ m_nSampleWindowSize ]; } // initialize a circular list of samples for ( unsigned long j = 0 ; j < m_nSampleWindowSize ; ++j ) { m_pSampleList [ j ].m_pNext = &(m_pSampleList[j + 1]); m_pSampleList [ j ].m_nLeft = 0; m_pSampleList [ j ].m_nRight = 0; m_pSampleList [ j ].m_nSampleValid = 0; // false m_pSampleList [ j ].m_nSampleAbsAvg = 0; // set average partner m_pSampleList [ j ].m_pAvgPartner = ( j < m_nSamplesInAvg ) ? &(m_pSampleList [ m_nSampleWindowSize - m_nSamplesInAvg + j]) : &(m_pSampleList [ j - m_nSamplesInAvg ]) ; // set lookahead partner m_pSampleList [ j ].m_pLookaheadPartner = ( j < m_nLookAheadWindowSize ) ? &(m_pSampleList [ m_nSampleWindowSize - m_nLookAheadWindowSize + j]) : &(m_pSampleList [ j - m_nLookAheadWindowSize ]) ; } m_pSampleList [ m_nSampleWindowSize - 1 ].m_pNext = &(m_pSampleList[0]); // last points to first ZeroSampleWindow(); if ( c_WithDebug ) { //for ( j = 0 ; j < m_nSampleWindowSize ; ++j ) //{ // unsigned long nNext = ( m_pSampleList [ j ].m_pNext - m_pSampleList ); // unsigned long nAvgp = ( m_pSampleList [ j ].m_pAvgPartner - m_pSampleList ); // unsigned long nLkap = ( m_pSampleList [ j ].m_pLookaheadPartner - m_pSampleList ); // log4(CN_iAVC,LL_DEBUG,0, "this=%d, next=%d, AvgPartner=%d, LookAheadPartner = %d", // j, nNext, nAvgp, nLkap ); //} } return true; } /////////////////////////////////////////////////////////////////////////////// // // SetMinSamplesBeforeSwitch // /////////////////////////////////////////////////////////////////////////////// bool AutoVolCtrl::SetMinSamplesBeforeSwitch ( unsigned long nMinSamplesBeforeSwitch ) { if ( m_nSampleWindowSize < nMinSamplesBeforeSwitch || nMinSamplesBeforeSwitch < MIN_MINIMUM_SAMPLES_BEFORE_SWITCH ) return false; m_nMinSamplesBeforeSwitch = nMinSamplesBeforeSwitch; m_nNumSamplesBeforeNextSwitch = 0; return true; } /////////////////////////////////////////////////////////////////////////////// // // SetMaxPctChangeAtOnce // /////////////////////////////////////////////////////////////////////////////// void AutoVolCtrl::SetMaxPctChangeAtOnce ( IAVCMULTIPLYPCT nPctChange ) { m_nMaxChangePct = nPctChange; } /////////////////////////////////////////////////////////////////////////////// // // SetMultipliers // /////////////////////////////////////////////////////////////////////////////// void AutoVolCtrl::SetMultipliers ( unsigned short int nValueWanted [ MULTIPLY_PCT_ARRAY_SIZE ] ) { for ( int i = 1 ; i < MULTIPLY_PCT_ARRAY_SIZE ; ++i ) { m_nMultiplyPct [ i ] = APPLY_MULTIPLY_FACTOR ( nValueWanted [ i ] ) / IAVCMULTIPLYPCT ( i ); if ( ( i % 1000 ) == 0 ) log3(CN_iAVC,LL_DEBUG,0, "SetMultipliers at sample %d, =%d (0x%X)\n", i, MULTIPLY_FACTOR_TO_INT_X256(m_nMultiplyPct [ i ]), MULTIPLY_FACTOR_TO_INT_X256(m_nMultiplyPct [ i ]) ); } m_nMultiplyPct [ 0 ] = m_nMultiplyPct [ 1 ]; } /////////////////////////////////////////////////////////////////////////////// // // SetNumberTracks // /////////////////////////////////////////////////////////////////////////////// bool AutoVolCtrl::SetNumberTracks ( unsigned int nNumTracks ) { if ( nNumTracks > MAX_NUMBER_OF_TRACKS ) return false; m_nNumTracks = nNumTracks; return true; } /////////////////////////////////////////////////////////////////////////////// // // ZeroSampleWindow // /////////////////////////////////////////////////////////////////////////////// void AutoVolCtrl::ZeroSampleWindow() { // initialize a circular list of samples for ( unsigned long j = 0 ; j < m_nSampleWindowSize ; ++j ) { m_pSampleList [ j ].m_nLeft = 0; m_pSampleList [ j ].m_nRight = 0; m_pSampleList [ j ].m_nSampleValid = 0; // false m_pSampleList [ j ].m_nSampleAbsAvg = 0; } // set subscripts for where next data goes or comes from m_pNextSet = &m_pSampleList [ 0 ]; m_pNextGet = &m_pSampleList [ 0 ]; } /////////////////////////////////////////////////////////////////////////////// // // SetNextSample // /////////////////////////////////////////////////////////////////////////////// bool AutoVolCtrl::SetNextSample ( IAVCSAMPLETYPE left, IAVCSAMPLETYPE right ) { #endif // !defined... #if defined(IAVC_SETNEXTSAMPLE) // take out of our sum the sample m_nSamplesInAvg before the sample just before the lookahead window Sample* pAddToSum = m_pNextSet->m_pLookaheadPartner; // node just before lookahead window Sample* pRemoveFromSum = pAddToSum->m_pAvgPartner; // node to remove from sample sum //if ( m_nTotalSamples <= 2200 ) //{ // TEMP // log8(CN_iAVC,LL_DEBUG,0, // "# = %d, sum = %d," // ", nextSet=%d, AddToAvg=%d (%d), RemoveFromAvg=%d (%d), newAbsAvg=%d", // m_nSamplesInSum, // long(m_nSampleAvgSum), // m_pNextSet - m_pSampleList, // pAddToSum - m_pSampleList, long(pAddToSum->m_nSampleAbsAvg), // pRemoveFromSum - m_pSampleList, long(pRemoveFromSum->m_nSampleAbsAvg), // long( absVal ( left ) + absVal ( right ) ) / m_nNumTracks ); //} // take this sample out of the sample sum (if valid) m_nSampleAvgSum -= pRemoveFromSum->m_nSampleAbsAvg; m_nSamplesInSum -= pRemoveFromSum->m_nSampleValid; // form average value for this cell m_pNextSet->m_nSampleAbsAvg = ( absVal ( left ) + absVal ( right ) ) / m_nNumTracks; if ( m_pNextSet->m_nSampleAbsAvg > DEFAULT_MAX_SAMPLE_VALUE ) // 9/1/02 Safety code needed for Audacity m_pNextSet->m_nSampleAbsAvg = DEFAULT_MAX_SAMPLE_VALUE; // put in new sample m_pNextSet->m_nLeft = left; m_pNextSet->m_nRight = right; m_pNextSet->m_nSampleValid = 1; // true, node will now always have a valid sample in it // add a node's samples into the sample sum (if valid) m_nSampleAvgSum += pAddToSum->m_nSampleAbsAvg; m_nSamplesInSum += pAddToSum->m_nSampleValid; //NOTUSED - not using lookahead //if ( m_nLookAheadWindowSize > 0 ) //{ // Figure out lookahead average for our lookahead partner // Sample* pLookaheadPartner = pAddToSum; // take this nodes samples out of lookahead sum // // take this sample out of the sum (if valid) // m_nLookaheadSum -= pLookaheadPartner->m_nSampleAbsAvg; // m_nSamplesInLookahead -= pLookaheadPartner->m_nSampleValid; // // // add into the lookahead sum the new values // ++m_nSamplesInLookahead; // m_nLookaheadSum += m_pNextSet->m_nSampleAbsAvg; //} m_pNextSet = m_pNextSet->m_pNext; #endif // defined(IAVC_SETNEXTSAMPLE) #if !defined(IAVC_INLINE) || ( !defined(IAVC_SETNEXTSAMPLE) && !defined(IAVC_GETNEXTSAMPLE) && !defined(IAVC_ADJUSTMULTIPLIER) ) return true; } /////////////////////////////////////////////////////////////////////////////// // // GetNextSample // /////////////////////////////////////////////////////////////////////////////// bool AutoVolCtrl::GetNextSample ( IAVCSAMPLETYPE & left, IAVCSAMPLETYPE & right ) { #endif // !defined... #if defined(IAVC_GETNEXTSAMPLE) // Note: If Puts circle around before we get the samples, then we'll lose one // whole round of samples. int nClip; // not used unless c_WithDebug is true #if defined(IAVC_INLINE) #undef IAVC_GETNEXTSAMPLE #define IAVC_ADJUSTMULTIPLIER //#pragma message("inlining AdjustMultiplier 1st time") #include "iAVC.cpp" #define IAVC_GETNEXTSAMPLE #undef IAVC_ADJUSTMULTIPLIER #else if ( m_pNextGet == m_pNextSet ) { return false; // no sample to give } AdjustMultiplier(); #endif if ( c_WithDebug ) { ++m_nTotalSamples; if ( ( m_nTotalSamples % 10000 ) <= 1 ) { log4(CN_iAVC,LL_DEBUG,0, "Sample %d, Number of samples in sum = %d, sample sum = %d, sample avg = %d\n", m_nTotalSamples, m_nSamplesInSum, long ( AVG_TO_MULTIPLIER_SUBSCRIPT(m_nSampleAvgSum) ), long ( AVG_TO_MULTIPLIER_SUBSCRIPT(m_nSampleAvgSum/m_nSamplesInSum) ) ); } nClip = 0; if ( IF_CLIP ( left ) || IF_CLIP ( right ) || m_nTotalSamples == 55666 ) { log2(CN_iAVC,LL_ERROR,0,"ERROR: Sample out of range, left=%d, right=%d\n", AVG_TO_MULTIPLIER_SUBSCRIPT( m_pNextGet->m_nLeft ), AVG_TO_MULTIPLIER_SUBSCRIPT( m_pNextGet->m_nRight ) ); } } IAVCSUMTYPE nLeft; IAVCSUMTYPE nRight; nLeft = UNDO_MULTIPLY_FACTOR ( m_nCurrentMultiplier * m_pNextGet->m_nLeft ); nRight = UNDO_MULTIPLY_FACTOR ( m_nCurrentMultiplier * m_pNextGet->m_nRight ); if ( IF_CLIP ( nLeft ) || IF_CLIP ( nRight ) ) { // We had a clip, see if we can adjust multiplier down. // What do we do? If this is a momentary pop, like a pop on a record, we should // do nothing. But most audio today is probably from CDs and therefore // probably clean. So let's be bold and ASSUME that we're just moving into // a loud section from a softer section (which can be why we have a high // multiplier for this sample). In this case, let's just change the multiplier // now and not wait for the end of the next change window. To figure out the // new multiplier, we'll just use this sample. long nCurSampleAvgSubscript = AVG_TO_MULTIPLIER_SUBSCRIPT(m_pNextGet->m_nSampleAbsAvg); if ( nCurSampleAvgSubscript < 0 ) // Safety code, should not be needed nCurSampleAvgSubscript = 0; else if ( nCurSampleAvgSubscript >= MULTIPLY_PCT_ARRAY_SIZE ) nCurSampleAvgSubscript = MULTIPLY_PCT_ARRAY_SIZE - 1; m_nCurrentMultiplier = m_nMultiplyPct [ nCurSampleAvgSubscript ]; // always positive m_nNumSamplesBeforeNextSwitch = m_nMinSamplesBeforeSwitch; if ( c_WithDebug ) { nClip = 1; } // // This path will take extra time, but shouldn't occur very often. // m_nNumSamplesBeforeNextSwitch = 0; // for multiplier adjustment // ++m_nNumSamplesBeforeNextSwitch; // don't do this twice for a sample, already invoked AdjustMultiplier // //#if defined(IAVC_INLINE) //#undef IAVC_GETNEXTSAMPLE //#define IAVC_ADJUSTMULTIPLIER ////#pragma message("inlining AdjustMultiplier 2nd time") //#include "DynRangeComp.cpp" //#define IAVC_GETNEXTSAMPLE //#undef IAVC_ADJUSTMULTIPLIER //#else // AdjustMultiplier(); //#endif nLeft = UNDO_MULTIPLY_FACTOR ( m_nCurrentMultiplier * m_pNextGet->m_nLeft ); nRight = UNDO_MULTIPLY_FACTOR ( m_nCurrentMultiplier * m_pNextGet->m_nRight ); if ( IF_CLIP ( nLeft ) || IF_CLIP ( nRight ) ) { nLeft = m_pNextGet->m_nLeft; // don't clip, use original values instead nRight = m_pNextGet->m_nRight; // don't clip, use original values instead } } left = nLeft; right = nRight; if ( c_WithDebug ) { if ( nClip != 0 ) { m_nClips += nClip; if ( ( m_nClips % 1 ) == 0 ) { // m_nTotalSamples may be off if buffered (i.e. more put samples than get samples done) log4(CN_iAVC,LL_DEBUG,0, "Sample %d clipped, orig left=%d, right=%d, multiplier=0x%X\n", m_nTotalSamples, AVG_TO_MULTIPLIER_SUBSCRIPT(m_pNextGet->m_nLeft), AVG_TO_MULTIPLIER_SUBSCRIPT(m_pNextGet->m_nRight), MULTIPLY_FACTOR_TO_INT_X256(m_nCurrentMultiplier) ); } } if ( ( m_nTotalSamples % 5000 ) == 0 ) { log3(CN_iAVC,LL_DEBUG,0, "Sample %d, multiplier=%d (0x%X),...\n", m_nTotalSamples, MULTIPLY_FACTOR_TO_INT_X256(m_nCurrentMultiplier), MULTIPLY_FACTOR_TO_INT_X256(m_nCurrentMultiplier) ); log4(CN_iAVC,LL_DEBUG,0, " , Transformed %d->%d %d->%d\n", long(AVG_TO_MULTIPLIER_SUBSCRIPT(m_pNextGet->m_nLeft)), long(AVG_TO_MULTIPLIER_SUBSCRIPT(left)), long(AVG_TO_MULTIPLIER_SUBSCRIPT(m_pNextGet->m_nRight)), long(AVG_TO_MULTIPLIER_SUBSCRIPT(right)) ); } } m_pNextGet = m_pNextGet->m_pNext; #endif // defined(IAVC_GETNEXTSAMPLE) #if !defined(IAVC_INLINE) || ( !defined(IAVC_SETNEXTSAMPLE) && !defined(IAVC_GETNEXTSAMPLE) && !defined(IAVC_ADJUSTMULTIPLIER) ) return true; } /////////////////////////////////////////////////////////////////////////////// // // AdjustMultiplier // /////////////////////////////////////////////////////////////////////////////// void AutoVolCtrl::AdjustMultiplier() { // TEMPORARY DEBUG CODE if ( c_WithDebug && m_nTotalSamples >= 466930L && m_nTotalSamples <= 466973L ) { log3(CN_iAVC,LL_DEBUG,0, "DEBUG at sample %d, mul now=0x%X (%d)\n", m_nTotalSamples, long(m_nCurrentMultiplier), long(m_nCurrentMultiplier) ); long nLookaheadAvg; // needs to be long since used as a subscript if ( m_nSamplesInLookahead > 0 ) nLookaheadAvg= long ( m_nLookaheadSum/m_nSamplesInLookahead ); else nLookaheadAvg = 0; log4(CN_iAVC,LL_DEBUG,0, " sample max=%d, sample win avg=%d, lookahead avg=%d, avg multiplier=0x%X\n", long(maxVal(absVal(m_pNextGet->m_nLeft),absVal(m_pNextGet->m_nRight))), long(m_nSampleAvgSum / m_nSamplesInSum), nLookaheadAvg, long(m_nMultiplyPct [ nLookaheadAvg ]) ); } #endif // !defined... #if defined(IAVC_ADJUSTMULTIPLIER) //#pragma message("inlining AdjustMultiplier") --m_nNumSamplesBeforeNextSwitch; if ( m_nNumSamplesBeforeNextSwitch <= 0 ) { // long time since last change, see if it is time to change the multiplier long nCurSampleAvgSubscript = ( m_nSamplesInSum <= 0 ) ? 0 : ( AVG_TO_MULTIPLIER_SUBSCRIPT(m_nSampleAvgSum) / m_nSamplesInSum ); if ( nCurSampleAvgSubscript < 0 ) // Safety code, should not be needed nCurSampleAvgSubscript = 0; else if ( nCurSampleAvgSubscript >= MULTIPLY_PCT_ARRAY_SIZE ) nCurSampleAvgSubscript = MULTIPLY_PCT_ARRAY_SIZE - 1; IAVCMULTIPLYPCT nNewMultiplier = m_nMultiplyPct [ nCurSampleAvgSubscript ]; // always positive IAVCMULTIPLYPCT nMultiplierDiff = nNewMultiplier - m_nCurrentMultiplier; // positive or negative // if new multiplier is 1, force change to get to 1 (nChangeThreshold always positive) IAVCMULTIPLYPCT nChangeThreshold = ( nMultiplierDiff != 0 && nNewMultiplier == APPLY_MULTIPLY_FACTOR ( 1 ) ) ? nMultiplierDiff : IAVCMULTIPLYPCT ( m_nCurrentMultiplier * m_nMaxChangePct / 100 ); // % of current multiplier //NOTUSED - not using lookahead //unsigned long nLookaheadAvg; //if ( m_nSamplesInLookahead > 0 ) // nLookaheadAvg = m_nLookaheadSum/m_nSamplesInLookahead; //else // nLookaheadAvg = 0; //long nLookaheadMultiplier = m_nMultiplyPct [ nLookaheadAvg ]; if ( nMultiplierDiff >= nChangeThreshold ) { // adjust multiplier up log4(CN_iAVC,LL_DEBUG,0, "Multiplier UP old=%d, new=%d, diff=%d, threshold=%d\n", MULTIPLY_FACTOR_TO_INT_X256(m_nCurrentMultiplier), MULTIPLY_FACTOR_TO_INT_X256(nNewMultiplier), MULTIPLY_FACTOR_TO_INT_X256(nMultiplierDiff), MULTIPLY_FACTOR_TO_INT_X256(nChangeThreshold) ); m_nCurrentMultiplier = nNewMultiplier; // or m_nCurrentMultiplier += nChangeThreshold; m_nNumSamplesBeforeNextSwitch = m_nMinSamplesBeforeSwitch; if ( c_WithDebug ) { ++m_nNumMultiplerChanges; log4(CN_iAVC,LL_DEBUG,0, "Multiplier UP at sample %d, current avg=%d, now=%d (0x%X)\n", m_nTotalSamples, nCurSampleAvgSubscript, MULTIPLY_FACTOR_TO_INT_X256(m_nCurrentMultiplier), MULTIPLY_FACTOR_TO_INT_X256(m_nCurrentMultiplier) ); //NOTUSED - not using lookahead //log2(CN_iAVC,LL_DEBUG,0, " lookahead: avg=%d, avg multiplier=0x%X\n", // long(nLookaheadAvg), // long(nLookaheadMultiplier) ); } } else if ( nMultiplierDiff <= - nChangeThreshold ) { // adjust multiplier down log4(CN_iAVC,LL_DEBUG,0, "Multiplier DOWN old=%d, new=%d, diff=%d, threshold=%d\n", MULTIPLY_FACTOR_TO_INT_X256(m_nCurrentMultiplier), MULTIPLY_FACTOR_TO_INT_X256(nNewMultiplier), MULTIPLY_FACTOR_TO_INT_X256(nMultiplierDiff), MULTIPLY_FACTOR_TO_INT_X256(nChangeThreshold) ); m_nCurrentMultiplier = nNewMultiplier; // or m_nCurrentMultiplier -= nChangeThreshold; m_nNumSamplesBeforeNextSwitch = m_nMinSamplesBeforeSwitch; if ( c_WithDebug ) { ++m_nNumMultiplerChanges; log4(CN_iAVC,LL_DEBUG,0, "Multiplier DOWN at sample %d, current avg=%d, now=%d (0x%X)\n", m_nTotalSamples, nCurSampleAvgSubscript, MULTIPLY_FACTOR_TO_INT_X256(m_nCurrentMultiplier), MULTIPLY_FACTOR_TO_INT_X256(m_nCurrentMultiplier) ); //NOTUSED - not using lookahead //log2(CN_iAVC,LL_DEBUG,0, " lookahead: avg=%d, avg multiplier=0x%X\n", // long(nLookaheadAvg), // long(nLookaheadMultiplier) ); } } } #endif // defined(IAVC_ADJUSTMULTIPLIER) #if !defined(IAVC_INLINE) || ( !defined(IAVC_SETNEXTSAMPLE) && !defined(IAVC_GETNEXTSAMPLE) && !defined(IAVC_ADJUSTMULTIPLIER) ) return; } #endif // !defined...