📄 hxaudstr_new.cpp
字号:
}
/************************************************************************
* Method:
* IHXAudioStream::ResetStream
* Purpose:
*/
void CHXAudioStream::ResetStream()
{
m_bInited = FALSE;
m_bCanBeRewound = FALSE;
m_bSetupDone = FALSE;
m_bAudioFormatKnown = FALSE;
m_bIsResumed = FALSE;
UnRegister();
while (m_pAvailableBuffers && m_pAvailableBuffers->GetCount() > 0)
{
IHXBuffer* pBuffer = (IHXBuffer*) m_pAvailableBuffers->RemoveHead();
HX_RELEASE(pBuffer);
}
HX_DELETE(m_pAvailableBuffers);
// Delete all entries in the audio data list
FlushBuffers();
HX_DELETE(m_pDataList);
HX_DELETE(m_pInstantaneousList);
CleanupRAByToTs();
HX_DELETE(m_pRAByToTsInList);
HX_DELETE(m_pRAByToTsAdjustedList);
HX_DELETE(m_pMixEngine);
m_bGotHooks = FALSE;
m_llLastWriteTime = 0;
m_ulTSRollOver = 0;
HX_RELEASE(m_pValues);
#if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
// Delete all entries in the pre-mix hook list.
if ( m_PreMixHookMap.GetCount() > 0)
{
HXAudioHookInfo* h = 0;
CHXMapPtrToPtr::Iterator lIter = m_PreMixHookMap.Begin();
for (; lIter != m_PreMixHookMap.End(); ++lIter)
{
h = (HXAudioHookInfo*) (*lIter);
ProcessAudioHook(ACTION_REMOVE, h->pHook);
h->pHook->Release();
delete h;
}
m_PreMixHookMap.RemoveAll();
}
#endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
#ifdef HELIX_FEATURE_VOLUME
if( m_pStreamVolume )
{
m_pStreamVolume->RemoveAdviseSink(this);
m_pStreamVolume->Release();
m_pStreamVolume=NULL;
}
#endif
HX_DELETE(m_pInDataPtr);
HX_DELETE(m_pOutDataPtr);
if (m_DryNotificationMap && m_DryNotificationMap->GetCount() > 0)
{
IHXDryNotification* pDryNotification = 0;
CHXMapPtrToPtr::Iterator lIter = m_DryNotificationMap->Begin();
for (; lIter != m_DryNotificationMap->End(); ++lIter)
{
pDryNotification = (IHXDryNotification*) (*lIter);
pDryNotification->Release();
}
m_DryNotificationMap->RemoveAll();
}
HX_RELEASE(m_pCrossFadeStream);
HX_RELEASE(m_pCommonClassFactory);
#if defined(HELIX_FEATURE_PREFERENCES)
HX_RELEASE(m_pPreferences);
#endif /* HELIX_FEATURE_PREFERENCES */
HX_RELEASE(m_Owner);
return;
}
HX_RESULT
CHXAudioStream::ProcessAudioHook(PROCESS_ACTION action,
IHXAudioHook* pAudioHook)
{
return HXR_OK;
}
/************************************************************************
* Method:
* IHXAudioStream::InitHooks
* Purpose:
* Init any pre-mix hooks. Return TRUE if hooks exist else return
* FALSE.
*/
void CHXAudioStream::InitHooks()
{
#if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
/* Iterate thru the hook list and call the hook's OnInit().
* If any of the hooks have disabled write set to TRUE, then
* we will let this override any set to FALSE.
*/
if ( m_PreMixHookMap.GetCount() > 0 )
{
HXAudioHookInfo* h = 0;
CHXMapPtrToPtr::Iterator lIter = m_PreMixHookMap.Begin();
for (; lIter != m_PreMixHookMap.End(); ++lIter)
{
h = (HXAudioHookInfo*) (*lIter);
if (h->bIgnoreAudioData ||
HXR_OK == ProcessAudioHook(ACTION_CHECK, h->pHook))
{
h->pHook->OnInit( &m_AudioFmt );
}
}
}
#endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
m_bHooksInitialized = TRUE;
}
/************************************************************************
* Method:
* IHXAudioStream::ProcessHooks
* Purpose:
*/
HX_RESULT CHXAudioStream::ProcessHooks
(
HXAudioData* pInData,
HXAudioData* pOutData
)
{
HX_RESULT theErr = HXR_OK;
#if defined(HELIX_FEATURE_AUDIO_PREMIXHOOK)
m_pInDataPtr->pData = pInData->pData;
m_pInDataPtr->pData->AddRef();
m_pInDataPtr->ulAudioTime = pInData->ulAudioTime;
m_pOutDataPtr->pData = NULL;
m_pOutDataPtr->ulAudioTime = pInData->ulAudioTime;
m_pInDataPtr->uAudioStreamType = pInData->uAudioStreamType;
m_pOutDataPtr->uAudioStreamType = pInData->uAudioStreamType;
if ( m_PreMixHookMap.GetCount() > 0 )
{
HXAudioHookInfo* pPreMixHookInfo = 0;
CHXMapPtrToPtr::Iterator lIter = m_PreMixHookMap.Begin();
for (; !theErr && lIter != m_PreMixHookMap.End(); ++lIter)
{
pPreMixHookInfo = (HXAudioHookInfo*) (*lIter);
if (HXR_OK == ProcessAudioHook(ACTION_CHECK, pPreMixHookInfo->pHook))
{
// XXXHP, disable hooks when it doesn't support multi-channel
if (m_AudioFmt.uChannels <= 2 || pPreMixHookInfo->bMultiChannelSupport)
{
theErr = pPreMixHookInfo->pHook->OnBuffer( m_pInDataPtr, m_pOutDataPtr);
/* Check to see if renderer changed the buffer. If so, then
* make this output as input to the next Hook.
*/
if (!theErr && m_pOutDataPtr->pData)
{
m_pInDataPtr->pData->Release();
m_pInDataPtr->pData = m_pOutDataPtr->pData;
m_pInDataPtr->ulAudioTime = m_pOutDataPtr->ulAudioTime;
m_pOutDataPtr->pData = 0;
}
}
}
else if (pPreMixHookInfo->bIgnoreAudioData)
{
IHXBuffer* pTempBuf = m_pInDataPtr->pData;
m_pInDataPtr->pData = NULL;
theErr = pPreMixHookInfo->pHook->OnBuffer( m_pInDataPtr, m_pOutDataPtr);
m_pInDataPtr->pData = pTempBuf;
}
}
}
/* Final output is always in InDataPtr*/
pOutData->pData = m_pInDataPtr->pData;
pOutData->ulAudioTime = m_pInDataPtr->ulAudioTime;
pOutData->uAudioStreamType = m_pInDataPtr->uAudioStreamType;
#endif /* HELIX_FEATURE_AUDIO_PREMIXHOOK */
return theErr;
}
/************************************************************************
* Method:
* CHXAudioStream::MixIntoBuffer
* Purpose:
* Mix stream data into this pPlayerBuf.
*
*/
HX_RESULT CHXAudioStream::MixIntoBuffer
(
UCHAR* pPlayerBuf,
ULONG32 ulBufSize,
ULONG32& ulBufTime,
BOOL& bIsMixBufferDirty,
BOOL bGetCrossFadeData
)
{
HX_RESULT res = HXR_OK ;
if (!m_bInited)
{
return HXR_NOT_INITIALIZED;
}
// bGetCrossFadeData should now be a thing of the past.
HX_ASSERT(!bGetCrossFadeData) ;
//{FILE* f1 = ::fopen("c:\\temp\\rasync.txt", "a+"); ::fprintf(f1, "Call MixIntoBuffer: %lu\n", m_ulLastWriteTime);::fclose(f1);}
/* If this is a *FROM* stream, we may have already mixed
* data during cross-fade with *TO* stream
*/
// update the outside world's sense of time.
// XXX wschildbach: how to account for rollover?
INT64 llNextMixTime = m_pMixEngine->GetNextMixTimeMillis();
UINT32 ulLastWriteTime = INT64_TO_UINT32(llNextMixTime - CAST_TO_INT64(m_ulTSRollOver) * CAST_TO_INT64(MAX_UINT32));
// In a surestream situation, ulBufTime is 0 on the first stream, and a time corresponding
// to the granularity in the second stream. I don't know what this code tries to do.
// XXX wschildbach
if (ulBufTime < ulLastWriteTime)
{
ulBufTime = ulLastWriteTime;
}
/* If there are any DryNotifications and the data list is empty
* we need to notify them so that they can write more data.
*
* If EnoughDataAvailable() returns FALSE, it will have updated
* the input values to point at where the buffer needs to be filled.
*/
INT64 llStartMix, llEndMix;
m_pMixEngine->GetMixRange(ulBufSize, llStartMix, llEndMix) ;
UINT32 nSamplesNeeded = INT64_TO_UINT32(llEndMix - llStartMix) ; // always fits into UINT32
if (!EnoughDataAvailable(llStartMix, nSamplesNeeded))
{
// Check if the audio device is really empty ("ReallyNeedData") or if we are
// just over-eagerly filling up the pre-roll. If the latter is the case,
// return HXR_WOULD_BLOCK
if (!bIsMixBufferDirty && !m_Owner->m_Owner->ReallyNeedData())
{
return HXR_WOULD_BLOCK;
}
// renderer might pause playback if has no packets
if (m_DryNotificationMap->GetCount() > 0)
{
IHXDryNotification* pDryNotification = 0;
CHXMapPtrToPtr::Iterator lIter = m_DryNotificationMap->Begin();
for (; lIter != m_DryNotificationMap->End(); ++lIter)
{
pDryNotification = (IHXDryNotification*) (*lIter);
UINT64 streamTime = Samples2Ms(llStartMix, &m_AudioFmt) -
CAST_TO_INT64(m_ulTSRollOver)*CAST_TO_INT64(MAX_UINT32);
HX_RESULT theErr = pDryNotification->OnDryNotification( INT64_TO_UINT32(streamTime),
INT64_TO_UINT32(Samples2Ms(nSamplesNeeded, &m_AudioFmt))) ;
if( theErr == HXR_OUTOFMEMORY )
{
return theErr;
}
}
if (m_Owner->GetState() != E_PLAYING)
{
return HXR_OK;
}
}
}
// XXX wschildbach what does this do?
m_Owner->DataInAudioDevice(TRUE);
/*
{FILE *f1 = fopen("c:\\temp\\mix.txt","a+");
fprintf(f1,"\ncall MixIntoBuffer(%I64d, id=%ld)\n",(INT64)mixTimeInSamples,m_pMixEngine - (HXAudioSvcMixEngine*)0xdde198);
fclose(f1);}
*/
// this call does all the mixing.
res = m_pMixEngine->MixIntoBuffer(pPlayerBuf, ulBufSize, bIsMixBufferDirty) ;
if( m_wLastError == HXR_OUTOFMEMORY )
{
return m_wLastError;
}
if (FAILED(res))
return res ;
#if defined(HELIX_FEATURE_AUDIO_INACCURATESAMPLING)
if( m_bRealAudioStream )
MapFudgedTimestamps();
#endif
// update the inside world's sense of time.
m_llLastWriteTime = m_pMixEngine->GetNextMixTimeMillis() ;
#if 0
{FILE *f = fopen("c:\\temp\\incoming.txt","a+");
fprintf(f,"mix %ld %I64d\n",ulBufTime,m_llLastWriteTime);fclose(f);}
#endif
return HXR_OK;
}
/*
* This is the callback function that m_pMixEngine->MixIntoBuffer() will call to read
* new samples.
*/
BOOL CHXAudioStream::ConvertIntoBuffer(tAudioSample* buffer, UINT32 nSamples, INT64 llStartTimeInSamples)
{
HXAudioInfo* pInfo = 0;
LISTPOSITION lp = 0;
INT32 nBytesPerSample = m_AudioFmt.uBitsPerSample>>3 ;
BOOL didMix = FALSE ; // set to TRUE if we have packets in this time range
BOOL bPacketsAfterRange = FALSE;
/*
{FILE *f1 = fopen("c:\\temp\\mix.txt","a+");
fprintf(f1," ConvertIntoBuffer(%I64d - %I64d, len=%ld)\n",(INT64)llStartTimeInSamples,(INT64)llStartTimeInSamples+nSamples,nSamples);
fclose(f1);}
*/
// there are two lists of packets here: timed audio and instantaneous audio.
// We only look into the list for timed buffers -- Instantaneoue audio is ignored
// (it never properly worked anyway, so support is discontinued).
/* remove old packets. Old packets are packets that have an end time that is before
our current mix time. */
lp = m_pDataList->GetHeadPosition();
while( lp )
{
LISTPOSITION lastlp = lp;
pInfo = (HXAudioInfo*) m_pDataList->GetNext(lp);
if (pInfo->llEndTimeInSamples < llStartTimeInSamples)
{
/*
{FILE *f1 = fopen("c:\\temp\\mix.txt","a+");
fprintf(f1,"-- reaping packet (%I64d,%I64d)\n",
pInfo->llStartTimeInSamples,pInfo->llEndTimeInSamples);
fclose(f1);}
*/
FreeInfo(pInfo);
m_pDataList->RemoveAt(lastlp);
if( m_wLastError == HXR_OUTOFMEMORY )
{
return FALSE;
}
}
else // if monotonous and non-overlapping
break ;
}
// now go through the entire list of packets, and look for overlap with the
// convert buffer. Any packet with overlap will be at least partially converted
// into the buffer.
// If packets overlap, one packet will then take precedence over another -- not
// much we can do about that.
lp = m_pDataList->GetHeadPosition();
while( lp )
{
pInfo = (HXAudioInfo*) m_pDataList->GetNext(lp);
if (pInfo->llStartTimeInSamples < llStartTimeInSamples + nSamples &&
pInfo->llEndTimeInSamples > llStartTimeInSamples)
{
// This packet has some overlap with what we are converting.
// if this is the first packet to be mixed into this buffer,
// silence out the entire buffer. This is inefficient, but safe
if (!didMix)
CAudioSvcSampleConverter::silence(buffer, nSamples) ;
didMix = TRUE ;
INT32 nMixbufferOffset = 0;
INT32 pastPacketStart = INT64_TO_INT32(llStartTimeInSamples - pInfo->llStartTimeInSamples) ;
if (pastPacketStart < 0)
{
nMixbufferOffset = -pastPacketStart;
pastPacketStart = 0 ;
}
INT32 nn = Bytes2Samples(pInfo->pBuffer->GetSize(), &m_AudioFmt) ;
INT32 nSamplesToUse = nn - pastPacketStart;
if (nSamplesToUse > (INT32)(nSamples - nMixbufferOffset))
nSamplesToUse = nSamples - nMixbufferOffset;
const unsigned char *cvtin = pInfo->pBuffer->GetBuffer() + pastPacketStart * nBytesPerSample ;
tAudioSample *cvtout = buffer + nMixbufferOffset ;
/*
{FILE *f1 = fopen("c:\\temp\\mix.txt","a+");
fprintf(f1," mix packet (%I64d,%I64d,len=%ld) into buffer(%ld,%ld,len=%ld)\n",
pInfo->llStartTimeInSamples,pInfo->llEndTimeInSamples,
nn,
nMixbufferOffset,nMixbufferOffset+nSamplesToUse,nSamplesToUse);
fprintf(f1," pastPacketStart = %I64d, nMixbufferOffset = %ld\n",
pastPacketStart,nMixbufferOffset) ;
fclose(f1);}
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -