#include "precomp.h" #define ZONE_AP ZONE_DP #define _GetState() (m_dwState & DP_MASK_STATE) #define _SetState(s) (m_dwState = (m_dwState & ~DP_MASK_STATE) | (s & DP_MASK_STATE)) #define _GetPlatform() (m_dwState & DP_MASK_PLATFORM) #define _SetPlatform(s) (m_dwState = (m_dwState & ~DP_MASK_PLATFORM) | (s & DP_MASK_PLATFORM)) int g_wavein_prepare = 0; int g_waveout_prepare = 0; /////////////////////////////////////////////////////// // // Public methods // HRESULT AudioPacket::Initialize ( MEDIAPACKETINIT * p ) { HRESULT hr = DPR_SUCCESS; FX_ENTRY ("AdPckt::Init") if (p == NULL) { DEBUGMSG (ZONE_AP, ("%s: invalid parameter (null ptr)\r\n", _fx_)); return DPR_INVALID_PARAMETER; } hr = MediaPacket::Initialize( p); if (hr != DPR_SUCCESS) goto MyExit; // allocate conversion header only if m_pWaveData != m_pNetData if (m_pRawData != m_pNetData) { if (m_dwState & DP_FLAG_ACM) { m_pStrmConvHdr = MemAlloc (sizeof (ACMSTREAMHEADER)); if (m_pStrmConvHdr == NULL) { DEBUGMSG (ZONE_AP, ("%s: MemAlloc4 (%ld) failed\r\n", _fx_, (ULONG) sizeof (ACMSTREAMHEADER))); hr = DPR_OUT_OF_MEMORY; goto MyExit; } } else { DEBUGMSG (ZONE_AP, ("%s: invalid platform (acm)\r\n", _fx_)); hr = DPR_INVALID_PLATFORM; goto MyExit; } } else { m_pStrmConvHdr = NULL; } // allocate device header if (m_dwState & DP_FLAG_MMSYSTEM) { m_pDevHdr = MemAlloc (sizeof (WAVEHDR)); if (m_pDevHdr == NULL) { DEBUGMSG (ZONE_AP, ("%s: MemAlloc5 (%ld) failed\r\n", _fx_, (ULONG) sizeof (WAVEHDR))); hr = DPR_OUT_OF_MEMORY; goto MyExit; } } else { DEBUGMSG (ZONE_AP, ("%s: invalid platform (mm)\r\n", _fx_)); hr = DPR_INVALID_PLATFORM; goto MyExit; } MakeSilence (); MyExit: if (hr != DPR_SUCCESS) { m_fInitialized = FALSE; Release(); } return hr; } HRESULT AudioPacket::Play ( MMIODEST *pmmioDest, UINT uDataType ) { HRESULT hr = DPR_SUCCESS; DWORD dwState = _GetState (); MMRESULT mmr; FX_ENTRY ("AdPckt::Play") if (dwState != MP_STATE_DECODED && dwState != MP_STATE_RESET) { DEBUGMSG (ZONE_AP, ("%s: out of seq, state=0x%lX\r\n", _fx_, m_dwState)); return DPR_OUT_OF_SEQUENCE; } if (uDataType == MP_DATATYPE_SILENCE) { LOG((LOGMSG_PLAY_SILENT,m_index,GetTickCount())); MakeSilence (); } else { if (uDataType == MP_DATATYPE_INTERPOLATED) { if (dwState == MP_STATE_DECODED) { LOG((LOGMSG_PLAY_INTERPOLATED,m_index,GetTickCount())); } else { LOG((LOGMSG_PLAY_SILENT,m_index,GetTickCount())); MakeSilence (); } } else { LOG((LOGMSG_PLAY,m_index, GetTickCount())); } } if (m_hDev) { if (m_dwState & DP_FLAG_MMSYSTEM) { ((WAVEHDR *) m_pDevHdr)->lpData = (char *) m_pDevData->data; // ((WAVEHDR *) m_pDevHdr)->dwBufferLength = (dwState == MP_STATE_DECODED ? // ((ACMSTREAMHEADER *) m_pStrmConvHdr)->cbDstLengthUsed : // m_pDevData->length); ((WAVEHDR *) m_pDevHdr)->dwBufferLength = (dwState == MP_STATE_DECODED ? m_cbValidRawData : m_pDevData->length); ((WAVEHDR *) m_pDevHdr)->dwUser = (DWORD_PTR) this; ((WAVEHDR *) m_pDevHdr)->dwFlags &= ~(WHDR_DONE|WHDR_INQUEUE); ((WAVEHDR *) m_pDevHdr)->dwLoops = 0L; // feed this buffer to play mmr = waveOutWrite ((HWAVEOUT) m_hDev, (WAVEHDR *) m_pDevHdr, sizeof (WAVEHDR)); if (mmr != MMSYSERR_NOERROR) { DEBUGMSG (ZONE_AP, ("%s: waveOutWrite failed, mmr=%ld\r\n", _fx_, (ULONG) mmr)); hr = DPR_CANT_WRITE_WAVE_DEV; // this is an extremely rare error, but we've seen it // occur on some sound cards // in this case, just set the "done" bit, mark the // state to the "playing", but still return an error. ((WAVEHDR *) m_pDevHdr)->dwFlags |= WHDR_DONE; goto MyExit; } } else { DEBUGMSG (ZONE_AP, ("%s: invalid platform (mm)\r\n", _fx_)); hr = DPR_INVALID_PLATFORM; goto MyExit; } if (pmmioDest && pmmioDest->fRecordToFile && pmmioDest->hmmioDst) { // write this buffer to disk WriteToFile(pmmioDest); } } else { DEBUGMSG (ZONE_AP, ("%s: invalid handle\r\n", _fx_)); hr = DPR_INVALID_HANDLE; goto MyExit; } MyExit: if ((hr == DPR_SUCCESS) || (hr == DPR_CANT_WRITE_WAVE_DEV)) { _SetState (((uDataType == MP_DATATYPE_SILENCE) || (uDataType == MP_DATATYPE_INTERPOLATED))? MP_STATE_PLAYING_SILENCE : MP_STATE_PLAYING_BACK); } return hr; } HRESULT AudioPacket::Record ( void ) { HRESULT hr = DPR_SUCCESS; MMRESULT mmr; FX_ENTRY ("AdPckt::Record") LOG((LOGMSG_RECORD,m_index)); if (_GetState () != MP_STATE_RESET) { DEBUGMSG (ZONE_AP, ("%s: out of seq, state=0x%lX\r\n", _fx_, m_dwState)); return DPR_OUT_OF_SEQUENCE; } if (m_hDev) { if (m_dwState & DP_FLAG_MMSYSTEM) { ((WAVEHDR *) m_pDevHdr)->lpData = (char *) m_pDevData->data; ((WAVEHDR *) m_pDevHdr)->dwBufferLength = m_pDevData->length; ((WAVEHDR *) m_pDevHdr)->dwUser = (DWORD_PTR) this; ((WAVEHDR *) m_pDevHdr)->dwFlags |= WHDR_PREPARED; ((WAVEHDR *) m_pDevHdr)->dwLoops = 0L; // feed this buffer to record mmr = waveInAddBuffer ((HWAVEIN)m_hDev, (WAVEHDR *) m_pDevHdr, sizeof (WAVEHDR)); if (mmr != MMSYSERR_NOERROR) { DEBUGMSG (ZONE_AP, ("%s: waveInAddBuffer failed, mmr=%ld\r\n", _fx_, (ULONG) mmr)); hr = DPR_CANT_ADD_BUFFER; goto MyExit; } } else { DEBUGMSG (ZONE_AP, ("%s: invalid platform (mm)\r\n", _fx_)); hr = DPR_INVALID_PLATFORM; goto MyExit; } } else { DEBUGMSG (ZONE_AP, ("%s: invalid handle\r\n", _fx_)); hr = DPR_INVALID_HANDLE; goto MyExit; } MyExit: if (hr == DPR_SUCCESS) _SetState (MP_STATE_RECORDING); return hr; } BOOL AudioPacket::IsBufferDone ( void ) { FX_ENTRY ("AdPckt::IsBufferDone") if (m_hDev) { if (m_dwState & DP_FLAG_MMSYSTEM) { return (((WAVEHDR *) m_pDevHdr)->dwFlags & WHDR_DONE); } } return FALSE; } HRESULT AudioPacket::MakeSilence ( void ) { // create white noise!!! FX_ENTRY ("AdPckt::MakeSilence") if (m_pDevFmt) { if (m_pDevData) { FillSilenceBuf ((WAVEFORMATEX *) m_pDevFmt, (PBYTE) m_pDevData->data, (ULONG) m_pDevData->length); } #if 0 if (m_pRawData != m_pDevData) { if (m_pRawData) ZeroMemory (m_pRawData->data, m_pRawData->length); } if (m_pNetData != m_pRawData) { if (m_pNetData) ZeroMemory (m_pNetData->data, m_pNetData->length); } #endif } _SetState(MP_STATE_RESET); return DPR_SUCCESS; } /* Returns the max. peak-to-peak signal value scaled to the range [0,0xffff] Optional argument returns the peak value as well */ HRESULT AudioPacket::GetSignalStrength ( PDWORD pdwMaxStrength) { return ComputePower(pdwMaxStrength, NULL); } HRESULT AudioPacket::ComputePower(PDWORD pdwMaxStrength, PWORD pwPeakStrength) { BYTE bMax, bMin, *pb; short sMax, sMin, *ps; UINT cbSize; FX_ENTRY ("AdPckt::GetSignalStrength") if (((WAVEFORMATEX *) m_pDevFmt)->wFormatTag != WAVE_FORMAT_PCM) return FALSE; switch (((WAVEFORMATEX *) m_pDevFmt)->wBitsPerSample) { case 8: // unsigned char pb = (PBYTE) (m_pDevData->data); cbSize = m_pDevData->length; bMax = 0; bMin = 255; for ( ; cbSize; cbSize--, pb++) { if (*pb > bMax) bMax = *pb; if (*pb < bMin) bMin = *pb; } if (pdwMaxStrength) { // 2^9 <-- 2^16 / 2^7 *pdwMaxStrength = ((DWORD) (bMax - bMin)) << 8; } if (pwPeakStrength) { *pwPeakStrength = (bMax > bMin) ? bMax : (WORD)(-bMin); *pwPeakStrength = (*pwPeakStrength) << 8; } break; case 16: // (signed) short ps = (short *) (m_pDevData->data); cbSize = m_pDevData->length; sMax = sMin = 0; for (cbSize >>= 1; cbSize; cbSize--, ps++) { if (*ps > sMax) sMax = *ps; if (*ps < sMin) sMin = *ps; } if (pdwMaxStrength) { *pdwMaxStrength = (DWORD) (sMax - sMin); // drop sign bit } if (pwPeakStrength) { *pwPeakStrength = ((WORD)(sMax) > (WORD)(-sMin)) ? sMax : (WORD)(-sMin); } break; default: if (pdwMaxStrength) *pdwMaxStrength = 0; if (pwPeakStrength) *pwPeakStrength = 0; break; } //LOG((LOGMSG_SILENT,m_index,fResult)); return DPR_SUCCESS; } HRESULT AudioPacket::Interpolate ( MediaPacket * pPrev, MediaPacket * pNext) { HRESULT hr = DPR_SUCCESS; DPHANDLE hPrevDevAudio; NETBUF *pPrevDevData; PVOID pPrevDevHdr; WAVEFORMATEX *pPrevpwfDevAudio; WAVEFORMATEX *pNextpwfDevAudio; NETBUF *pNextDevData; PVOID pNextDevHdr; PCMSUB PCMSub; FX_ENTRY ("AdPckt::Interpolate") // Make sure this really is an empty packet, that the previous packet is not an // empty packet and is being played back. It is not that important that we get // a handle to the next packet. If the next packet is decoded, then it's cool, // we can do a good job at interpolating between previous and next packet. If it's // not, well, too bad, we'll just work with the previous packet. if ((_GetState() != MP_STATE_RESET) || (pPrev->GetState() != MP_STATE_PLAYING_BACK)) { // DEBUGMSG (ZONE_AP, ("%s: out of seq, state=0x%lX\r\n", _fx_, m_dwState)); hr = DPR_OUT_OF_SEQUENCE; goto MyExit; } // Get pointers to the member variables of interest in the previous and next // packet. Test the next packet to find out if we can use it in the interpolation // algorithm. pPrev->GetProp (MP_PROP_DEV_HANDLE, (PDWORD_PTR)&hPrevDevAudio); pPrev->GetProp (MP_PROP_DEV_DATA, (PDWORD_PTR)&pPrevDevData); pPrev->GetProp (MP_PROP_DEV_MEDIA_HDR, (PDWORD_PTR)&pPrevDevHdr); pPrev->GetProp (MP_PROP_DEV_MEDIA_FORMAT, (PDWORD_PTR)&pPrevpwfDevAudio); if (hPrevDevAudio && pPrevDevData && pPrevDevHdr && pPrevpwfDevAudio && (pPrevpwfDevAudio->wFormatTag == 1) && (pPrevpwfDevAudio->nSamplesPerSec == 8000) && (pPrevpwfDevAudio->wBitsPerSample == 16)) { PCMSub.pwWaSuBf = (short *)m_pDevData->data; PCMSub.dwBfSize = ((WAVEHDR *) pPrevDevHdr)->dwBufferLength >> 1; PCMSub.dwSaPeSe = (DWORD)pPrevpwfDevAudio->nSamplesPerSec; PCMSub.dwBiPeSa = (DWORD)pPrevpwfDevAudio->wBitsPerSample; PCMSub.pwPrBf = (short *)pPrevDevData->data; pNext->GetProp (MP_PROP_DEV_DATA, (PDWORD_PTR)&pNextDevData); pNext->GetProp (MP_PROP_DEV_MEDIA_HDR, (PDWORD_PTR)&pNextDevHdr); pNext->GetProp (MP_PROP_DEV_MEDIA_FORMAT, (PDWORD_PTR)&pNextpwfDevAudio); // Do a bit of checking if ((pNext->GetState() == MP_STATE_DECODED) && pNextDevData && pNextDevHdr && (PCMSub.dwBfSize == (((WAVEHDR *) pNextDevHdr)->dwBufferLength >> 1)) && pNextpwfDevAudio && (pNextpwfDevAudio->wFormatTag == 1) && (pNextpwfDevAudio->nSamplesPerSec == 8000) && (pNextpwfDevAudio->wBitsPerSample == 16)) { PCMSub.eTech = techPATT_MATCH_BOTH_SIGN_CC; //PCMSub.eTech = techDUPLICATE_PREV; PCMSub.pwNeBf = (short *)pNextDevData->data; PCMSub.fScal = TRUE; } else { PCMSub.eTech = techPATT_MATCH_PREV_SIGN_CC; //PCMSub.eTech = techDUPLICATE_PREV; PCMSub.pwNeBf = (short *)NULL; PCMSub.fScal = FALSE; } // Do the actual interpolation hr = PCMSubstitute(&PCMSub); ((ACMSTREAMHEADER *) m_pStrmConvHdr)->cbDstLengthUsed = ((WAVEHDR *) pPrevDevHdr)->dwBufferLength; } else { DEBUGMSG (ZONE_AP, ("%s: can't interpolate\r\n", _fx_)); hr = DPR_INVALID_HANDLE; goto MyExit; } LOG((LOGMSG_INTERPOLATED,m_index)); MyExit: if (hr == DPR_SUCCESS) _SetState (MP_STATE_DECODED); else _SetState (MP_STATE_RESET); return hr; } HRESULT AudioPacket::Open ( UINT uType, DPHANDLE hdl ) // called by RxStream or TxStream { HRESULT hr = DPR_SUCCESS; MMRESULT mmr; FX_ENTRY ("AdPckt::Open") switch (uType) { #ifdef PREP_HDR_PER_CONV case MP_TYPE_RECVSTRMCONV: m_hStrmConv = hdl; break; #endif case MP_TYPE_STREAMCONV: if ((m_hStrmConv = hdl) != NULL) { if (m_dwState & DP_FLAG_ACM) { // initialize the header ZeroMemory (m_pStrmConvHdr, sizeof (ACMSTREAMHEADER)); ((ACMSTREAMHEADER *) m_pStrmConvHdr)->cbStruct = sizeof (ACMSTREAMHEADER); ((ACMSTREAMHEADER *) m_pStrmConvHdr)->fdwStatus = 0; ((ACMSTREAMHEADER *) m_pStrmConvHdr)->dwUser = 0; ((ACMSTREAMHEADER *) m_pStrmConvHdr)->dwSrcUser = 0; ((ACMSTREAMHEADER *) m_pStrmConvHdr)->cbSrcLengthUsed = 0; ((ACMSTREAMHEADER *) m_pStrmConvHdr)->dwDstUser = 0; ((ACMSTREAMHEADER *) m_pStrmConvHdr)->cbDstLengthUsed = 0; if (m_dwState & DP_FLAG_SEND) { ((ACMSTREAMHEADER *) m_pStrmConvHdr)->pbSrc = m_pRawData->data; ((ACMSTREAMHEADER *) m_pStrmConvHdr)->cbSrcLength = m_pRawData->length; ((ACMSTREAMHEADER *) m_pStrmConvHdr)->pbDst = m_pNetData->data; ((ACMSTREAMHEADER *) m_pStrmConvHdr)->cbDstLength = m_pNetData->length; } else if (m_dwState & DP_FLAG_RECV) { ((ACMSTREAMHEADER *) m_pStrmConvHdr)->pbSrc = m_pNetData->data; ((ACMSTREAMHEADER *) m_pStrmConvHdr)->cbSrcLength = m_pNetData->length; ((ACMSTREAMHEADER *) m_pStrmConvHdr)->pbDst = m_pRawData->data; ((ACMSTREAMHEADER *) m_pStrmConvHdr)->cbDstLength = m_pRawData->length; } // prepare the header mmr = acmStreamPrepareHeader ((HACMSTREAM) m_hStrmConv, (ACMSTREAMHEADER *) m_pStrmConvHdr, 0); if (mmr != MMSYSERR_NOERROR) { DEBUGMSG (ZONE_AP, ("%s: acmStreamPrepareHeader failed, mmr=%ld\r\n", _fx_, (ULONG) mmr)); hr = DPR_CANT_PREPARE_HEADER; goto MyExit; } m_fStrmPrepared = TRUE; } else { hr = DPR_INVALID_PLATFORM; goto MyExit; } } break; case MP_TYPE_DEV: if ((m_hDev = hdl) != NULL) { if (m_dwState & DP_FLAG_MMSYSTEM) { // initialize the header ZeroMemory (m_pDevHdr, sizeof (WAVEHDR)); ((WAVEHDR *) m_pDevHdr)->lpData = (char *) m_pDevData->data; ((WAVEHDR *) m_pDevHdr)->dwBufferLength = m_pDevData->length; ((WAVEHDR *) m_pDevHdr)->dwUser = (DWORD_PTR) this; ((WAVEHDR *) m_pDevHdr)->dwFlags = 0L; ((WAVEHDR *) m_pDevHdr)->dwLoops = 0L; if (m_dwState & DP_FLAG_SEND) { g_wavein_prepare++; // prepare the header mmr = waveInPrepareHeader ((HWAVEIN) m_hDev, (WAVEHDR *) m_pDevHdr, sizeof (WAVEHDR)); if (mmr != MMSYSERR_NOERROR) { DEBUGMSG (ZONE_AP, ("%s: waveInPrepareHeader failed, mmr=%ld\r\n", _fx_, (ULONG) mmr)); hr = DPR_CANT_PREPARE_HEADER; goto MyExit; } } else if (m_dwState & DP_FLAG_RECV) { g_waveout_prepare++; // prepare header mmr = waveOutPrepareHeader ((HWAVEOUT) m_hDev, (WAVEHDR *) m_pDevHdr, sizeof (WAVEHDR)); if (mmr != MMSYSERR_NOERROR) { DEBUGMSG (ZONE_AP, ("%s: waveOutPrepareHeader failed, mmr=%ld\r\n", _fx_, (ULONG) mmr)); hr = DPR_CANT_PREPARE_HEADER; goto MyExit; } } else { hr = DPR_INVALID_PARAMETER; goto MyExit; } m_fDevPrepared = TRUE; } else { hr = DPR_INVALID_PLATFORM; goto MyExit; } } else { hr = DPR_INVALID_HANDLE; goto MyExit; } break; default: hr = DPR_INVALID_PARAMETER; goto MyExit; } MyExit: return hr; } HRESULT AudioPacket::Close ( UINT uType ) // called by RxStream or TxStream { HRESULT hr = DPR_SUCCESS; MMRESULT mmr; FX_ENTRY ("AdPckt::Close") switch (uType) { #ifdef PREP_HDR_PER_CONV case MP_TYPE_RECVSTRMCONV: #endif case MP_TYPE_STREAMCONV: if (m_hStrmConv) { if (m_dwState & DP_FLAG_ACM) { if (m_fStrmPrepared) { // unprepare the header if (m_dwState & DP_FLAG_RECV) { // Within acmStreamUnprepareHeader, there is a test that compares ((ACMSTREAMHEADER *)m_pStrmConvHdr)->cbSrcLength // to ((ACMSTREAMHEADER *)m_pStrmConvHdr)->cbPreparedSrcLength. If there isn't an exact match, MSACM32 will fail // this call. That test is Ok when the size of the input buffer is constant, but with the variable bit rate codecs, // we can receive packets with a size smaller than the max size we advertize when we prepare the buffers. In // order to make this call succeed, we fix up ((ACMSTREAMHEADER *)m_pStrmConvHdr)->cbSrcLength before the call. ((ACMSTREAMHEADER *)m_pStrmConvHdr)->cbSrcLength = ((ACMSTREAMHEADER *)m_pStrmConvHdr)->dwReservedDriver[7]; } mmr = acmStreamUnprepareHeader ((HACMSTREAM) m_hStrmConv, (ACMSTREAMHEADER *) m_pStrmConvHdr, 0); m_fStrmPrepared = FALSE; // don't care about any error if (mmr != MMSYSERR_NOERROR) { DEBUGMSG (ZONE_AP, ("%s: acmStreamUnprepareHeader failed, mmr=%ld\r\n", _fx_, (ULONG) mmr)); hr = DPR_CANT_UNPREPARE_HEADER; goto MyExit; } } } if (uType == MP_TYPE_STREAMCONV) m_hStrmConv = NULL; } break; case MP_TYPE_DEV: if (m_hDev) { if (m_fDevPrepared) { if (m_dwState & DP_FLAG_SEND) { g_wavein_prepare--; mmr = waveInUnprepareHeader ((HWAVEIN) m_hDev, (WAVEHDR *) m_pDevHdr, sizeof (WAVEHDR)); } else if (m_dwState & DP_FLAG_RECV) { g_waveout_prepare--; mmr = waveOutUnprepareHeader ((HWAVEOUT) m_hDev, (WAVEHDR *) m_pDevHdr, sizeof (WAVEHDR)); } else { hr = DPR_INVALID_PARAMETER; goto MyExit; } m_fDevPrepared = FALSE; // don't care about any error if (mmr != MMSYSERR_NOERROR) { DEBUGMSG (ZONE_AP, ("%s: Unprep hdr failed, mmr=0x%lX\r\n", _fx_, mmr)); hr = DPR_CANT_UNPREPARE_HEADER; goto MyExit; } } m_hDev = NULL; } else { hr = DPR_INVALID_HANDLE; goto MyExit; } break; default: hr = DPR_INVALID_PARAMETER; goto MyExit; } MyExit: return hr; } void AudioPacket::WriteToFile (MMIODEST *pmmioDest) { MMRESULT mmr; long dwDataLength; FX_ENTRY ("AdPckt::WriteToFile") AudioFile::WriteDestFile(pmmioDest, m_pDevData->data, m_pDevData->length); } void AudioPacket::ReadFromFile (MMIOSRC *pmmioSrc) { AudioFile::ReadSourceFile(pmmioSrc, (BYTE*)(((WAVEHDR*)m_pDevHdr)->lpData), ((WAVEHDR*)m_pDevHdr)->dwBytesRecorded); } BOOL AudioPacket::IsSameMediaFormat(PVOID fmt1,PVOID fmt2) { return IsSameWaveFormat(fmt1,fmt2); } /************************************************************************* Function: PCMSubstitute(PCMSUB *) Purpose : Fills up missing buffer with wave data. Returns : HRESULT. DPR_SUCCESS if everything is cool, some error code otherwise. Params : pPCMSub == Pointer to wave substitution structure Techniques: * Straight replication of the previous packet * Straight replication of the next packet * Replication of some part of the previous packet based on pattern matching * Replication of some part of the next packet based on pattern matching * Search window size need to be at least twice the size of the pattern!!! Comments: * The algorithm searches previous packets to find pPCMSub->dwBfSize samples that resemble the missing packet. To do so it uses as a template the M speech samples that came just before the missing packet. The algorithm scans a search window of duration N samples to find the M samples that best match the template. It then uses as a replacement packet the L samples that follow the best match. * Current code assumes all the packets (current, previous, and next) have the same size. * Current code only takes 8kHz data. * Current code only takes 16bit data. * Current code requires that the matching pattern be smaller than packet. History : Date Reason 04/16/95 Created - PhilF *************************************************************************/ HRESULT AudioPacket::PCMSubstitute(PCMSUB *pPCMSub) { DWORD dwPaSize; // Pattern size in samples DWORD dwSeWiSize; // Search window size in samples short *pwPa = (short *)NULL; // Pointer to the pattern short *pwPaSav = (short *)NULL; // Pointer to the pattern (copy) short *pwPrSeWi = (short *)NULL; // Pointer to the previous buffer (search window) short *pwPrSeWiSav = (short *)NULL; // Pointer to the previous buffer (search window) (copy) short *pwNeSeWi = (short *)NULL; // Pointer to the next buffer (search window) short *pwNeSeWiSav = (short *)NULL; // Pointer to the next buffer (search window) (copy) DWORD i, j; // Counters DWORD dwPrCCPosMax; // Sample position of the maximum cross-correlation between pattern and previous buffer DWORD dwNeCCPosMax; // Sample position of the maximum cross-correlation between pattern and previous buffer long lPrCCMax; // Max cross-correlation with previous buffer long lNeCCMax; // Max cross-correlation with next buffer long lCCNum; // Cross-correlation numerator DWORD dwNuSaToCopy; // Number of samples to copy in the missing buffer DWORD dwNuSaCopied; // Number of samples copied in the missing buffer long alSign[2] = {1,-1}; // Sign array DWORD dwPaAmp; // Amplitude of the pattern DWORD dwPaAmpExp; // Expected amplitude of the pattern DWORD dwNeSeWiAmp; // Amplitude of a segment of the window following the current window DWORD dwNumPaInSeWin; // Number of patterns in search window DWORD dwPrSeWiAmp; // Amplitude of a segment of the current window BOOL fPaInPr; // Pattern is at the end of previous buffer of at the beginning of next buffer // Test input parameters if ((!pPCMSub) || (!pPCMSub->pwWaSuBf) || (pPCMSub->dwBiPeSa != 16) || (pPCMSub->dwSaPeSe != 8000)) return DPR_INVALID_PARAMETER; // Check number of buffer available before and after missing packet // In case there are no packet before or after the missing packet, // just return; the packet will be filled with silence data later. if (!pPCMSub->pwPrBf && !pPCMSub->pwNeBf) return DPR_CANT_INTERPOLATE; // Just replicate previous packet if ((pPCMSub->eTech == techDUPLICATE_PREV) && pPCMSub->pwPrBf) CopyMemory(pPCMSub->pwWaSuBf, pPCMSub->pwPrBf, pPCMSub->dwBfSize << 1); else // Just replicate next packet if ((pPCMSub->eTech == techDUPLICATE_NEXT) && pPCMSub->pwNeBf) CopyMemory(pPCMSub->pwWaSuBf, pPCMSub->pwNeBf, pPCMSub->dwBfSize << 1); else if ((pPCMSub->eTech == techPATT_MATCH_PREV_SIGN_CC) || (pPCMSub->eTech == techPATT_MATCH_NEXT_SIGN_CC) || (pPCMSub->eTech == techPATT_MATCH_BOTH_SIGN_CC)) { // We use a search window with a size double the size of the matching pattern // Experimentation will tell if this is a reasonable size or not // Experimentation will also tell if 4ms size of the matching pattern is Ok dwPaSize = pPCMSub->dwSaPeSe / 1000 * PATTERN_SIZE; if (dwPaSize > (pPCMSub->dwBfSize/2)) dwPaSize = pPCMSub->dwBfSize/2; if (!dwPaSize) return DPR_CANT_INTERPOLATE; #if 1 // For now look up the whole previous frame dwSeWiSize = pPCMSub->dwBfSize; #else dwSeWiSize = min(pPCMSub->dwBfSize, pPCMSub->dwSaPeSe / 1000 * SEARCH_SIZE); #endif // In order to use pattern matching based techniques we need to have the // previous buffer when doing a backward search, the next buffer // when doing a forward search, the previous buffer and the next buffer // when doing a full search if (pPCMSub->pwPrBf && (pPCMSub->eTech == techPATT_MATCH_PREV_SIGN_CC)) { pwPa = pwPaSav = pPCMSub->pwPrBf + pPCMSub->dwBfSize - dwPaSize; pwPrSeWi = pwPrSeWiSav = pPCMSub->pwPrBf + pPCMSub->dwBfSize - dwSeWiSize; } else if (pPCMSub->pwNeBf && (pPCMSub->eTech == techPATT_MATCH_NEXT_SIGN_CC)) { pwPa = pwPaSav = pPCMSub->pwNeBf; pwNeSeWi = pwNeSeWiSav = pPCMSub->pwNeBf; } else if (pPCMSub->pwPrBf && pPCMSub->pwNeBf && (pPCMSub->eTech == techPATT_MATCH_BOTH_SIGN_CC)) { // Use the pattern with the highest amplitude pwPa = pwPaSav = pPCMSub->pwPrBf + pPCMSub->dwBfSize - dwPaSize; pwNeSeWi = pPCMSub->pwNeBf; pwPrSeWi = pwPrSeWiSav = pPCMSub->pwPrBf + pPCMSub->dwBfSize - dwSeWiSize; fPaInPr = TRUE; for (i=0, dwPaAmp = 0, dwNeSeWiAmp = 0; i dwPaAmp) { pwPaSav = pPCMSub->pwNeBf; fPaInPr = FALSE; } pwPa = pwPaSav; pwNeSeWi = pwNeSeWiSav = pPCMSub->pwNeBf + dwPaSize/2; } if (pwPa && (pwPrSeWi || pwNeSeWi)) { // Look for best match in previous packet dwPrCCPosMax = 0; lPrCCMax = -((long)dwPaSize+1); if (pwPrSeWi && ((pPCMSub->eTech == techPATT_MATCH_PREV_SIGN_CC) || ((fPaInPr) && (pPCMSub->eTech == techPATT_MATCH_BOTH_SIGN_CC)))) { // Look for the highest sign correlation between pattern and search window for (i=0; i<(dwSeWiSize-dwPaSize-dwPaSize/2+1); i++, pwPa = pwPaSav, pwPrSeWi = pwPrSeWiSav + i) { // Compute the sign correlation between pattern, and search window for (j=0, lCCNum = 0; j> 15 & 1]; // Save position and value of highest sign correlation if (lCCNum>lPrCCMax) { dwPrCCPosMax = i; lPrCCMax = lCCNum; } } } // Look for best match in next packet dwNeCCPosMax = dwPaSize/2; lNeCCMax = -((long)dwPaSize+1); if (pwNeSeWi && ((pPCMSub->eTech == techPATT_MATCH_NEXT_SIGN_CC) || ((!fPaInPr) && (pPCMSub->eTech == techPATT_MATCH_BOTH_SIGN_CC)))) { // Look for the highest sign correlation between pattern and search window for (i=dwPaSize/2; i<(dwSeWiSize-dwPaSize-dwPaSize/2+1); i++, pwPa = pwPaSav, pwNeSeWi = pwNeSeWiSav + i) { // Compute the sign correlation between pattern, and search window for (j=0, lCCNum = 0; j> 15 & 1]; // Save position and value of highest sign correlation if (lCCNum>lNeCCMax) { dwNeCCPosMax = i; lNeCCMax = lCCNum; } } } if ((pPCMSub->eTech == techPATT_MATCH_PREV_SIGN_CC) || (pwPrSeWiSav && fPaInPr && (pPCMSub->eTech == techPATT_MATCH_BOTH_SIGN_CC))) { // Copy matching samples from the previous frame in missing frame dwNuSaToCopy = pPCMSub->dwBfSize-dwPaSize-dwPrCCPosMax; CopyMemory(pPCMSub->pwWaSuBf, pwPrSeWiSav+dwPaSize+dwPrCCPosMax, dwNuSaToCopy << 1); // Do it until missing packet is full for (dwNuSaCopied = dwNuSaToCopy; dwNuSaCopieddwBfSize;dwNuSaCopied += dwNuSaToCopy) { dwNuSaToCopy = min(pPCMSub->dwBfSize-dwNuSaCopied, dwNuSaToCopy); CopyMemory(pPCMSub->pwWaSuBf + dwNuSaCopied, pwPrSeWiSav+dwPaSize+dwPrCCPosMax, dwNuSaToCopy << 1); } } else { // Copy matching samples from the next frame in missing frame dwNuSaToCopy = dwNeCCPosMax; CopyMemory(pPCMSub->pwWaSuBf + pPCMSub->dwBfSize - dwNuSaToCopy, pPCMSub->pwNeBf, dwNuSaToCopy << 1); // Do it until missing packet is full for (dwNuSaCopied = dwNuSaToCopy; dwNuSaCopieddwBfSize;dwNuSaCopied += dwNuSaToCopy) { dwNuSaToCopy = min(pPCMSub->dwBfSize-dwNuSaCopied, dwNuSaToCopy); CopyMemory(pPCMSub->pwWaSuBf + pPCMSub->dwBfSize - dwNuSaCopied - dwNuSaToCopy, pPCMSub->pwNeBf+dwNeCCPosMax-dwNuSaToCopy, dwNuSaToCopy << 1); } } if ((pPCMSub->eTech == techPATT_MATCH_BOTH_SIGN_CC) && pwNeSeWiSav && pwPrSeWiSav) { if (pPCMSub->fScal) { // Compute the amplitude of the pattern for (i=0, dwPrSeWiAmp = 0, dwNeSeWiAmp = 0, pwPrSeWi = pPCMSub->pwPrBf + pPCMSub->dwBfSize - dwPaSize, pwNeSeWi = pPCMSub->pwNeBf; idwBfSize/dwPaSize; for (i=0, pwPaSav = pPCMSub->pwWaSuBf; i 65536; dwPaAmpExp >>= 1, dwPaAmp >>= 1) ; if (dwPaAmp && (dwPaAmp != dwPaAmpExp)) for (j=0, pwPa = pwPaSav; jcbDstLengthUsed ; else if (m_pDevData) // return size of buffer cbData = m_pDevData->length; else cbData = 0; return cbData * 8/ ((WAVEFORMATEX *) m_pDevFmt)->wBitsPerSample; }