You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1006 lines
29 KiB
1006 lines
29 KiB
|
|
#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<dwPaSize; i++, pwPa++, pwNeSeWi++)
|
|
{
|
|
dwPaAmp += abs(*pwPa);
|
|
dwNeSeWiAmp += abs(*pwNeSeWi);
|
|
}
|
|
if (dwNeSeWiAmp > 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<dwPaSize; j++, pwPa++, pwPrSeWi++)
|
|
lCCNum += alSign[(*pwPa ^ *pwPrSeWi)>> 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<dwPaSize; j++, pwPa++, pwNeSeWi++)
|
|
lCCNum += alSign[(*pwPa ^ *pwNeSeWi)>> 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; dwNuSaCopied<pPCMSub->dwBfSize;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; dwNuSaCopied<pPCMSub->dwBfSize;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; i<dwPaSize; i++, pwPrSeWi++, pwNeSeWi++)
|
|
{
|
|
dwPrSeWiAmp += abs(*pwPrSeWi);
|
|
dwNeSeWiAmp += abs(*pwNeSeWi);
|
|
}
|
|
// Scale data
|
|
dwNumPaInSeWin = pPCMSub->dwBfSize/dwPaSize;
|
|
for (i=0, pwPaSav = pPCMSub->pwWaSuBf; i<dwNumPaInSeWin; i++, pwPaSav += dwPaSize)
|
|
{
|
|
for (j=0, pwPa = pwPaSav, dwPaAmp = 0; j<dwPaSize; j++, pwPa++)
|
|
dwPaAmp += abs(*pwPa);
|
|
dwPaAmpExp = (dwPrSeWiAmp * (dwNumPaInSeWin - i) + dwNeSeWiAmp * (i + 1)) / (dwNumPaInSeWin + 1);
|
|
for (;dwPaAmpExp > 65536; dwPaAmpExp >>= 1, dwPaAmp >>= 1)
|
|
;
|
|
if (dwPaAmp && (dwPaAmp != dwPaAmpExp))
|
|
for (j=0, pwPa = pwPaSav; j<dwPaSize; j++, pwPa++)
|
|
*pwPa = (short)((long)*pwPa * (long)dwPaAmpExp / (long)dwPaAmp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return DPR_CANT_INTERPOLATE;
|
|
|
|
return DPR_SUCCESS;
|
|
|
|
}
|
|
|
|
// returns length of uncompressed PCM data in buffer
|
|
DWORD
|
|
AudioPacket::GetDevDataSamples()
|
|
{
|
|
DWORD dwState = _GetState();
|
|
DWORD cbData;
|
|
|
|
if (dwState == MP_STATE_DECODED)
|
|
// return actual length
|
|
cbData = ((ACMSTREAMHEADER *) m_pStrmConvHdr)->cbDstLengthUsed ;
|
|
else if (m_pDevData)
|
|
// return size of buffer
|
|
cbData = m_pDevData->length;
|
|
else
|
|
cbData = 0;
|
|
|
|
return cbData * 8/ ((WAVEFORMATEX *) m_pDevFmt)->wBitsPerSample;
|
|
|
|
}
|
|
|
|
|