#include "precomp.h" #include "mixer.h" #include "agc.h" // this should be moved to the mixer class - but right // now we already have two instances of that class (one in NAC, the other in CONF) static BOOL GetVolume(CMixerDevice *pMixer, DWORD *pdwVol) { DWORD dwSub=0, dwMain=0; BOOL fSubAvail, fMainAvail; if (pMixer == NULL) { return FALSE; } fSubAvail = pMixer->GetSubVolume(&dwSub); fMainAvail = pMixer->GetMainVolume(&dwMain); if ((!fSubAvail) && (!fMainAvail)) { *pdwVol = 0; return FALSE; } if ((fSubAvail) && (fMainAvail)) { *pdwVol = ((dwSub + dwMain)/2); } else if (fSubAvail) { *pdwVol = dwSub; } else { *pdwVol = dwMain; } return TRUE; } // check to see if volume has changed since the last update of the mixer // if so, we update m_dsLastVolumeSetting and return TRUE BOOL AGC::HasVolumeChanged() { DWORD dwVol; if (m_pMixer) { if (GetVolume(m_pMixer, &dwVol)) { if (dwVol != m_dwLastVolumeSetting) { m_dwLastVolumeSetting = dwVol; return TRUE; } } } return FALSE; } // raise the volume my the increment amount inline BOOL AGC::RaiseVolume() { DWORD dwVol; if (m_pMixer) { if (GetVolume(m_pMixer, &dwVol)) { if (dwVol < (AGC_MAXVOL-AGC_INCREMENT)) { dwVol += AGC_INCREMENT; } else { dwVol = AGC_MAXVOL; } m_pMixer->SetVolume(dwVol); GetVolume(m_pMixer, &m_dwLastVolumeSetting); return TRUE; } else { return FALSE; } } return FALSE; } // lower the volume by the increment amount inline BOOL AGC::LowerVolume() { DWORD dwRet; if (m_pMixer) { if (GetVolume(m_pMixer, &dwRet)) { if (dwRet > (AGC_INCREMENT+AGC_INCREMENT/2)) m_dwLastVolumeSetting = dwRet - AGC_INCREMENT; else m_dwLastVolumeSetting = AGC_INCREMENT / 2; m_pMixer->SetVolume(m_dwLastVolumeSetting); GetVolume(m_pMixer, &m_dwLastVolumeSetting); return TRUE; } else { return FALSE; } } return FALSE; } AGC::AGC(CMixerDevice *pMixer) : m_cPeaks(0), m_wCurrentPeak(0), m_dwCollectionTime(0), m_pMixer(pMixer), m_wThreshStrength(AGC_DEFAULT_THRESH), m_dwLastVolumeSetting(0), m_nLastUpdateResult(AGC_NOUPDATE) {;} // resets all stats inside the AGC control except the mixer object void AGC::Reset() { m_cPeaks = 0; m_wCurrentPeak = 0; m_dwCollectionTime = 0; m_wThreshStrength = AGC_DEFAULT_THRESH; m_dwLastVolumeSetting = 0; m_nLastUpdateResult = AGC_NOUPDATE; } // initialize the AGC control with an instance of a mixer object // (you can also set the mixer in the constructor) void AGC::SetMixer(CMixerDevice *pMixer) { m_pMixer = pMixer; if (pMixer) { GetVolume(pMixer, &m_dwLastVolumeSetting); pMixer->SetVolume(m_dwLastVolumeSetting); } } // call this method for all recorded packets that // are begin sent. mixer will get raised/lowered as // appropriate. wPeakStrength can be any WORD that // represents a volume amount, but is designed to be // the highest sample value in a packet. int AGC::Update(WORD wPeakStrength, DWORD dwLengthMS) { int nIndex; DWORD dwTotal=0, dwMin=AGC_PEAKVOL, dwMax=0; DWORD dwAvg=0; BOOL nMaxPeaks=0; ASSERT (PEAKARRAYSIZE >= 2); if (wPeakStrength > m_wCurrentPeak) { m_wCurrentPeak = wPeakStrength; } m_dwCollectionTime += dwLengthMS; // have we exceeded one second worth of collections if (m_dwCollectionTime > 1000) { m_aPeaks[m_cPeaks++] = m_wCurrentPeak; m_dwCollectionTime = 0; m_wCurrentPeak = 0; } if (m_cPeaks >= 2) { // compute the average volume and number of clips that occurred for (nIndex = 0; nIndex < m_cPeaks; nIndex++) { dwTotal += m_aPeaks[nIndex]; if (m_aPeaks[nIndex] < dwMin) { dwMin = m_aPeaks[nIndex]; } else if (m_aPeaks[nIndex] > dwMax) { dwMax = m_aPeaks[nIndex]; } if (m_aPeaks[nIndex] >= AGC_PEAKVOL) { nMaxPeaks++; } } dwAvg = (dwTotal-dwMin) / (PEAKARRAYSIZE-1); // check for clipping every 2 seconds if (((nMaxPeaks >= 1) && (dwAvg > AGC_HIGHVOL)) || (nMaxPeaks >=2)) { // if the volume changed during (user manually adjusted sliders) // then allow those settings to stay in effect for this update if (HasVolumeChanged()) { m_nLastUpdateResult = AGC_NOUPDATE; } else { m_cPeaks = 0; LowerVolume(); m_nLastUpdateResult = AGC_UPDATE_LOWERVOL; } return m_nLastUpdateResult; } if (m_cPeaks >= PEAKARRAYSIZE) { m_cPeaks = 0; // if the volume changed during (user manually adjusted sliders) // then allow those settings to stay in effect for this update if (HasVolumeChanged()) { m_nLastUpdateResult = AGC_NOUPDATE; } // should we actually raise the volume ? // if we just lowered the volume, don't raise it again // prevents the system from appearing "jerky" // if we just raised the volume, then don't raise immediately // again... let silence detection catch up. else if ((dwAvg < m_wThreshStrength) && (m_nLastUpdateResult == AGC_NOUPDATE)) { RaiseVolume(); m_nLastUpdateResult = AGC_UPDATE_RAISEVOL; } else { m_nLastUpdateResult = AGC_NOUPDATE; } return m_nLastUpdateResult; } return AGC_NOUPDATE; } // return NOUPDATE, but don't set m_nLastUpdateResult since // there was no decision made. return AGC_NOUPDATE; }