?? hxaudstr.cpp
字號:
// ///////////////////////////////////////////////////////////// // If no resampling, mix stream data directly into the player // buffer. if ( !m_pResampler) { pSourceBuffer = pPlayerBuf; ulMaxBytes = m_ulInputBytesPerGran; /* For only those sound cards which do not * support stereo - a RARE (non-existent) case */ if (m_AudioFmt.uChannels == 2 && m_DeviceFmt.uChannels == 1) { /* We should never reach here since this case * should be handled by the Resampler * Temporary ASSERT... */ HX_ASSERT(FALSE); } /* Mono->Stereo conversion*/ else if (m_bChannelConvert) { HX_ASSERT(ulMaxBytes <= ulBufSize/2); /* Avoid GPF in retail builds! */ if (ulMaxBytes > ulBufSize/2) { ulMaxBytes = ulBufSize/2; } } else { HX_ASSERT(ulMaxBytes <= ulBufSize); /* Avoid GPF in retail builds! */ if (ulMaxBytes > ulBufSize) { ulMaxBytes = ulBufSize; } } }#if defined(HELIX_FEATURE_RESAMPLER) // ///////////////////////////////////////////////////////////// // If resampling, mix stream data into a tmp buffer. Then // resample this buffer and mix the final resampled buffer into // the player buffer. else { bMonoToStereoMayBeConverted = FALSE;// memset(m_pTmpResBuf, 0, HX_SAFESIZE_T(m_ulMaxBlockSize)); pSourceBuffer = m_pTmpResBuf; /* * Audio Session will always ask for m_ulOutputBytesPerGran bytes to be mixed * in MixIntoBuffer() call. So we need to produce these many number of bytes. * If there is any mono-stereo conversion that happens in the mixing, number * of output bytes required from the resampler are half the number of * m_ulOutputBytesPerGran bytes. */ if (m_pResampler) { ulMaxFramesOut = m_ulOutputBytesPerGran/(m_DeviceFmt.uBitsPerSample==8 ? 1 : 2); if (m_DeviceFmt.uChannels == 2) { ulMaxFramesOut /= 2; } ulMaxFramesIn = m_pResampler->Requires(ulMaxFramesOut); ulMaxBytes = ulMaxFramesIn * ((m_AudioFmt.uBitsPerSample==8)? 1 : 2) * m_AudioFmt.uChannels; } else { ulMaxBytes = m_ulInputBytesPerGran; } HX_ASSERT(ulMaxBytes <= m_ulMaxBlockSize); }#endif /* HELIX_FEATURE_RESAMPLER */ // ///////////////////////////////////////////////////////////// // Mix n bytes of data into buffer ulNumBytesMixed = MixData(pSourceBuffer, ulMaxBytes, bMonoToStereoMayBeConverted, (!m_pResampler) ? bIsMixBufferDirty : bResampleBufferDirty);/* if (ulNumBytesMixed > 0) { ::fwrite(pSourceBuffer, ulNumBytesMixed, 1, fdbefore); }*/#if defined(HELIX_FEATURE_RESAMPLER) // ///////////////////////////////////////////////////////////// // If we need to resample , then do this and then mix data into // the player buffer. // Only resample and mix if volume is *not* zero and there // are some bytes to mix. if (m_pResampler && ulNumBytesMixed > 0 && m_uVolume > 0 && !m_bMute) { if(ulNumBytesMixed < ulMaxBytes && 8==m_AudioFmt.uBitsPerSample) { //fill remainder with 128's (-1), silence: UCHAR* pTmp = &pSourceBuffer[ulNumBytesMixed]; ULONG32 ulNumBytesLeftToSilence = ulMaxBytes-ulNumBytesMixed; do { *pTmp = 128; pTmp++; } while(--ulNumBytesLeftToSilence); } ULONG32 ulOutBytes = 0; if (m_pResampler) { ulOutBytes = m_pResampler->Resample((UINT16*)pSourceBuffer, ulMaxBytes, (UINT16*)m_pResampleBuf);/* FILE* fp = fopen("c:\\temp\\audio.txt", "w+"); ::fwrite(m_pResampleBuf, ulOutBytes, 1, fp); fclose(fp);*/ /* Resampler will do stereo to mono conversion for us.*/ HX_ASSERT(ulMaxFramesOut == (ulOutBytes / 2 / (m_AudioFmt.uChannels == 2 && m_DeviceFmt.uChannels == 1 ? 1 : m_AudioFmt.uChannels))) ; } if (m_bChannelConvert) { HX_ASSERT(ulOutBytes*2 <= ulBufSize); if ( ulOutBytes > ulBufSize/2 ) { ulOutBytes = ulBufSize/2; } } else { HX_ASSERT(ulOutBytes <= ulBufSize); ulOutBytes = ulBufSize; } BOOL bBeforeMixBufferDirty = bIsMixBufferDirty; UINT32 ulMixedNumBytes = 0;#if defined(HELIX_FEATURE_MIXER) ulMixedNumBytes = CHXMixer::MixBuffer( m_pResampleBuf, pPlayerBuf, ulOutBytes, m_bChannelConvert, m_uVolume, m_DeviceFmt.uBitsPerSample, bIsMixBufferDirty);#else ::memcpy(pPlayerBuf, m_pResampleBuf, ulOutBytes); /* Flawfinder: ignore */ bIsMixBufferDirty = TRUE; ulMixedNumBytes = ulOutBytes;#endif /* HELIX_FEATURE_MIXER */ // If we mixed only a partial buffer, make the remaining buffer silent if (!bBeforeMixBufferDirty && ulMixedNumBytes < ulBufSize) { ::memset(pPlayerBuf+ulMixedNumBytes, 0, ulBufSize-ulMixedNumBytes); } }afterMixing:#endif /* HELIX_FEATURE_RESAMPLER */#ifdef _TESTING if ( g_log > 0 ) { write( g_log, pPlayerBuf, ulNumBytesMixed); }#endif /* This is for *FROM* stream */ if (bGetCrossFadeData) { m_bFadeAlreadyDone = TRUE; } /* If we are cross-fading, we have data from this stream in pPlayerBuf * Now get data to be cross-faded from *From* stream in * m_pCrossFadeBuffer */#if defined(HELIX_FEATURE_CROSSFADE) && defined(HELIX_FEATURE_MIXER) else if (bCrossFadeThisTime) { /* Allocate CrossFade Buffer */ if (!m_pCrossFadeBuffer) { m_ulCrossFadeBufferSize = ulBufSize; m_pCrossFadeBuffer = new UCHAR[m_ulCrossFadeBufferSize]; } memset(m_pCrossFadeBuffer, 0, HX_SAFESIZE_T(m_ulCrossFadeBufferSize)); UINT32 ulCrossFadeLen = m_ulCrossFadeBufferSize; UINT32 ulTmpBufTime = 0; BOOL bIsDrity = FALSE; m_pCrossFadeStream->MixIntoBuffer(m_pCrossFadeBuffer, ulCrossFadeLen, ulTmpBufTime, bIsDrity, TRUE); /* Now it is time to perform cross-fading between * pPlayerBuf and m_pCrossFadeBuffer */ UINT32 ulStartByteToFade = 0; UINT32 ulNumMsInThisBuffer = CalcDeviceMs(ulBufSize); UINT32 ulNumBytesToBeCrossFaded = ulBufSize; UINT32 ulSampleSize = ((m_DeviceFmt.uBitsPerSample==8)? 1 : 2) * m_DeviceFmt.uChannels; /* Make sure we have integral number of samples */ HX_ASSERT(ulBufSize == (ulBufSize/ulSampleSize) * ulSampleSize); /* Only partial buffer needs to be cross-faded. * ------------- * ~~_____________| * * ----------- * |___________~~ * * * ----- * |_____| <-- Granularity size block that is mixed. * * <--> Only partial block needs to be faded */ if (ulTimeActuallyFaded < ulNumMsInThisBuffer) { ulNumBytesToBeCrossFaded = (UINT32) (ulBufSize * (ulTimeActuallyFaded*1./ulNumMsInThisBuffer)) ; UINT32 ulOutOfPhase = ulNumBytesToBeCrossFaded % ulSampleSize; if (ulOutOfPhase > 0) { ulNumBytesToBeCrossFaded = ulNumBytesToBeCrossFaded - ulOutOfPhase; } ulStartByteToFade = ulBufSize - ulNumBytesToBeCrossFaded; } if (ulTimeActuallyFaded > m_ulCrossFadeDuration) { ulNumBytesToBeCrossFaded = (UINT32) (ulBufSize * (m_ulCrossFadeDuration*1./ulNumMsInThisBuffer)) ; UINT32 ulOutOfPhase = ulNumBytesToBeCrossFaded % ulSampleSize; if (ulOutOfPhase > 0) { ulNumBytesToBeCrossFaded = ulNumBytesToBeCrossFaded - ulOutOfPhase; } } UINT16 uNumSamples = (UINT16) (ulNumBytesToBeCrossFaded/ ulSampleSize); BOOL bWasDirty = bIsMixBufferDirty; /* Mix the initial bytes that are not cross-faded*/ if (ulStartByteToFade > 0) { CHXMixer::MixBuffer( m_pCrossFadeBuffer, pPlayerBuf, ulStartByteToFade, FALSE, 100, 8, bWasDirty); } m_pCrossFader->CrossFade((INT16*) (m_pCrossFadeBuffer+ulStartByteToFade), (INT16*) (pPlayerBuf+ulStartByteToFade), uNumSamples); /* make sure we have silence in bytes that were not touched */ if (!bIsMixBufferDirty && (ulStartByteToFade + (uNumSamples*ulSampleSize)) < ulBufSize) { ::memset(pPlayerBuf+ulStartByteToFade + (uNumSamples*ulSampleSize), 0, ulBufSize - (ulStartByteToFade + (uNumSamples*ulSampleSize))); } bIsMixBufferDirty = TRUE; } if (bGetCrossFadeData || bCrossFadeThisTime) { if (bGetCrossFadeData) { HX_ASSERT(m_llLastWriteTime >= m_llCrossFadeStartTime); if (m_llLastWriteTime >= m_llCrossFadeStartTime) { HX_ASSERT(m_llLastWriteTime - m_llCrossFadeStartTime < MAX_TIMESTAMP_GAP); ulTimeActuallyFaded = INT64_TO_UINT32(m_llLastWriteTime - m_llCrossFadeStartTime); } } if (ulTimeActuallyFaded >= m_ulCrossFadeDuration) { m_bCrossFadingToBeDone = FALSE; HX_RELEASE(m_pCrossFadeStream); /* We should release any extra buffers if it is a * *from* stream */ if (!m_bFadeToThisStream) { /* Do not remove any instantaenous buffers */ FlushBuffers(FALSE); } } else { m_ulCrossFadeDuration -= ulTimeActuallyFaded; m_llCrossFadeStartTime += CAST_TO_INT64 ulTimeActuallyFaded; } } else if (m_bCrossFadingToBeDone && !m_bFadeToThisStream) { m_pCrossFadeStream->SyncStream(m_llLastWriteTime); }#endif /* HELIX_FEATURE_CROSSFADE && HELIX_FEATURE_MIXER *///{FILE* f1 = ::fopen("e:\\MixIntoBuffer.txt", "a+"); ::fprintf(f1, "%lu\t%p\t%lu\n", HX_GET_BETTERTICKCOUNT(), this, (UINT32)m_llLastWriteTime);::fclose(f1);} return HXR_OK;}/************************************************************************ * Method: * CHXAudioStream::MixData * Purpose: * Mix all valid data in my auxilliary list into the buffer. * * Thoughts: * while there are buffers available * if (buffertime is more than endtime) break; * if (any part of buffer is >startime and < endtime) * we are in business. * mix that part of the buffer, update offset, * update max of num bytes written in this round. * Looks like we need to keep LastWrite time and offsets for all buffers * that get written in one pass. * Consider this scenario: * * ----------------- ----------------- |________________| |_______________| -> Skew 1 ________ __________ |_______| |_________| ->Skew 2 5 ____________________________________ |__________________|_|_______________| Skew in opposite direction 3 _______ |_______| 4 ____________ |____________| <- Buffer to be mixed currently Order of buffer processing will be in the order of numbers on the right. Since all the theree buffers have fudge within fudge limit, they need to be written one after the other. This is possible only if we keep last write times and last written offsets in mixer buffer for each one of them. Is this extra processsing on every write worthed OR do we place limitations on the data that can be written. * Hmmm... We are now going with STREAMED/INSTANTANEOUS/TIMED model since the * above case is shows that users can really screw things up and it would be * very difficult to handle this case. So instead, we do not support over-lapped * buffers any more. i.e. If a renderer wants to have streamed and instantaneous * behavior, it needs to use two audio streams. */ULONG32 CHXAudioStream::MixData( UCHAR* pDestBuf, ULONG32 ulBufLen, BOOL bMonoToStereoMayBeConverted, BOOL& bIsMixBufferDirty){ HXAudioInfo* pInfo = 0; ULONG32 ulNumBytes = 0; LISTPOSITION lp = 0; LISTPOSITION lastlp = 0; LISTPOSITION lpList = 0; LISTPOSITION lastlpList = 0; ULONG32 ulNumBytesWritten = 0; BOOL bLastWriteTimeToBeUpdated = TRUE; ULONG32 ulDestinationOffset = 0; BOOL bWasMixBufferDirty = bIsMixBufferDirty; // ///////////////////////////////////////////////////////////// // Go thru the buffer list and mix in valid buffers. /* First all instantaneous buffers * All the instantaneous buffers get written at the start of * the destination buffer. */ lpList = lastlpList = 0; lpList = m_pInstantaneousList->GetHeadPosition(); while(lpList) { lastlpList = lpList; CHXSimpleList* pList =
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -