void CBaseStreamControl::CancelStop() { ASSERT(CritCheckIn(&m_CritSec)); m_tStopTime = MAX_TIME; m_dwStopCookie = 0; m_StreamStateOnStop = STREAM_FLOWING; }
HRESULT MyPin::SetMediaType(const CMediaType* pmt) { assert(CritCheckIn(this)); ReturnIfFailed(CBaseInputPin::SetMediaType(pmt)); auto pFormat = reinterpret_cast<const WAVEFORMATEX*>(pmt->pbFormat); // No point in doing integrity checks, that was done in CheckMediaType(). assert(pFormat); assert(pmt->cbFormat == sizeof(WAVEFORMATEX) + pFormat->cbSize); m_live = CheckLive(m_Connected); try { m_renderer.SetFormat(CopyWaveFormat(*pFormat), m_live); } catch (std::bad_alloc&) { return E_OUTOFMEMORY; } return S_OK; }
STDMETHODIMP CBasePin::DisconnectInternal() { ASSERT(CritCheckIn(m_pLock)); if (m_Connected) { HRESULT hr = BreakConnect(); if( FAILED( hr ) ) { // There is usually a bug in the program if BreakConnect() fails. DbgBreak( "WARNING: BreakConnect() failed in CBasePin::Disconnect()." ); return hr; } m_Connected->Release(); m_Connected = NULL; return S_OK; } else { // no connection - not an error return S_FALSE; } }
DWORD_PTR CAMSchedule::AddAdvisePacket( CAdvisePacket * pPacket ) { ASSERT(pPacket->m_rtEventTime >= 0 && pPacket->m_rtEventTime < MAX_TIME); ASSERT(CritCheckIn(&m_Serialize)); CAdvisePacket * p_prev = &head; CAdvisePacket * p_n; const DWORD_PTR Result = pPacket->m_dwAdviseCookie = ++m_dwNextCookie; // This relies on the fact that z is a sentry with a maximal m_rtEventTime for(;;p_prev = p_n) { p_n = p_prev->m_next; if ( p_n->m_rtEventTime >= pPacket->m_rtEventTime ) break; } p_prev->InsertAfter( pPacket ); ++m_dwAdviseCount; DbgLog((LOG_TIMING, 2, TEXT("Added advise %lu, for thread 0x%02X, scheduled at %lu"), pPacket->m_dwAdviseCookie, GetCurrentThreadId(), (pPacket->m_rtEventTime / (UNITS / MILLISECONDS)) )); // If packet added at the head, then clock needs to re-evaluate wait time. if ( p_prev == &head ) SetEvent( m_ev ); return Result; }
HRESULT MyPin::CheckConnect(IPin* pPin) { assert(CritCheckIn(this)); ReturnIfFailed(CBaseInputPin::CheckConnect(pPin)); m_live = CheckLive(pPin); return S_OK; }
void CBaseStreamControl::ExecuteStart() { ASSERT(CritCheckIn(&m_CritSec)); m_StreamState = STREAM_FLOWING; if (m_dwStartCookie) { DbgLog((LOG_TRACE,2,TEXT("*sending EC_STREAM_CONTROL_STARTED (%d)"), m_dwStartCookie)); m_pSink->Notify(EC_STREAM_CONTROL_STARTED, (LONG_PTR)this, m_dwStartCookie); } CancelStart(); // This will do the tidy up }
// // If the format changes, we need to reconnect // void CSynthFilter::ReconnectWithNewFormat(void) { // The caller must hold the state lock because this // function calls IsConnected(). ASSERT(CritCheckIn(pStateLock())); // The synth filter's only has one pin. The pin is an output pin. CDynamicSourceStream* pOutputPin = (CDynamicSourceStream*)GetPin(0); if( pOutputPin->IsConnected() ) { pOutputPin->OutputPinNeedsToBeReconnected(); } }
HRESULT CAsyncIo::PutDoneItem(CAsyncRequest* pRequest) { ASSERT(CritCheckIn(&m_csLists)); if (m_listDone.AddTail(pRequest)) { m_evDone.Set(); return S_OK; } else { return E_OUTOFMEMORY; } }
// put an item on the done list - ok to do this when // flushing HRESULT CAsyncIo::PutDoneItem(CAsyncRequest* pRequest) { ASSERT(CritCheckIn(&m_csLists)); if (m_listDone.AddTail(pRequest)) { // event should now be in a set state - force this m_evDone.Set(); return S_OK; } else { return E_OUTOFMEMORY; } }
// // AllocWaveCache // // HRESULT CAudioSynth::AllocWaveCache(const WAVEFORMATEX& wfex) { // The caller should hold the state lock because this // function uses m_iWaveCacheCycles, m_iWaveCacheSize // m_iFrequency, m_bWaveCache and m_wWaveCache. The // function should also hold the state lock because // it calls CalcCache(). ASSERT(CritCheckIn(m_pStateLock)); m_iWaveCacheCycles = m_iFrequency; m_iWaveCacheSize = (int) wfex.nSamplesPerSec; if(m_bWaveCache) { delete[] m_bWaveCache; m_bWaveCache = NULL; } if(m_wWaveCache) { delete[] m_wWaveCache; m_wWaveCache = NULL; } // The wave cache always stores PCM audio data. if(wfex.wBitsPerSample == 8) { m_bWaveCache = new BYTE [m_iWaveCacheSize]; if(NULL == m_bWaveCache) { return E_OUTOFMEMORY; } } else { m_wWaveCache = new WORD [m_iWaveCacheSize]; if(NULL == m_wWaveCache) { return E_OUTOFMEMORY; } } CalcCache(wfex); return S_OK; }
// // FillAudioBuffer // // This actually fills it with the sin wave by copying it verbatim into the output... // void CAudioSynth::FillPCMAudioBuffer(const WAVEFORMATEX& wfex, BYTE pBuf[], int iSize) { BOOL fCalcCache = FALSE; // The caller should always hold the state lock because this // function uses m_iFrequency, m_iFrequencyLast, m_iWaveform // m_iWaveformLast, m_iAmplitude, m_iAmplitudeLast, m_iWaveCacheIndex // m_iWaveCacheSize, m_bWaveCache and m_wWaveCache. The caller should // also hold the state lock because this function calls CalcCache(). ASSERT(CritCheckIn(m_pStateLock)); // Only realloc the cache if the format has changed ! if(m_iFrequency != m_iFrequencyLast) { fCalcCache = TRUE; m_iFrequencyLast = m_iFrequency; } if(m_iWaveform != m_iWaveformLast) { fCalcCache = TRUE; m_iWaveformLast = m_iWaveform; } if(m_iAmplitude != m_iAmplitudeLast) { fCalcCache = TRUE; m_iAmplitudeLast = m_iAmplitude; } if(fCalcCache) { // recalculate the sin wave... CalcCache(wfex); } // sin wave (old way) // Copy cache to output buffers copyCacheToOutputBuffers(wfex, pBuf, iSize); }
void CAudioSynth::GetPCMFormatStructure(WAVEFORMATEX* pwfex) { ASSERT(pwfex); if (!pwfex) return; // The caller must hold the state lock because this function uses // m_wChannels, m_wBitsPerSample and m_dwSamplesPerSec. ASSERT(CritCheckIn(m_pStateLock)); // Check for valid input parametes. ASSERT((1 == m_wChannels) || (2 == m_wChannels)); ASSERT((8 == m_wBitsPerSample) || (16 == m_wBitsPerSample)); ASSERT((8000 == m_dwSamplesPerSec) || (11025 == m_dwSamplesPerSec) || (22050 == m_dwSamplesPerSec) || (44100 == m_dwSamplesPerSec)); pwfex->wFormatTag = WAVE_FORMAT_PCM; pwfex->nChannels = m_wChannels; pwfex->nSamplesPerSec = m_dwSamplesPerSec; pwfex->wBitsPerSample = m_wBitsPerSample; pwfex->nBlockAlign = (WORD)((pwfex->wBitsPerSample * pwfex->nChannels) / BITS_PER_BYTE); pwfex->nAvgBytesPerSec = pwfex->nBlockAlign * pwfex->nSamplesPerSec; pwfex->cbSize = 0; }
const AudioDevice* AudioRenderer::GetAudioDevice() { assert(CritCheckIn(this)); return m_device.get(); }
HRESULT CVideoTransformFilter::Receive(IMediaSample *pSample) { // If the next filter downstream is the video renderer, then it may // be able to operate in DirectDraw mode which saves copying the data // and gives higher performance. In that case the buffer which we // get from GetDeliveryBuffer will be a DirectDraw buffer, and // drawing into this buffer draws directly onto the display surface. // This means that any waiting for the correct time to draw occurs // during GetDeliveryBuffer, and that once the buffer is given to us // the video renderer will count it in its statistics as a frame drawn. // This means that any decision to drop the frame must be taken before // calling GetDeliveryBuffer. ASSERT(CritCheckIn(&m_csReceive)); AM_MEDIA_TYPE *pmtOut, *pmt; #ifdef _DEBUG FOURCCMap fccOut; #endif HRESULT hr; ASSERT(pSample); IMediaSample * pOutSample; // If no output pin to deliver to then no point sending us data ASSERT (m_pOutput != NULL) ; // The source filter may dynamically ask us to start transforming from a // different media type than the one we're using now. If we don't, we'll // draw garbage. (typically, this is a palette change in the movie, // but could be something more sinister like the compression type changing, // or even the video size changing) #define rcS1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcSource #define rcT1 ((VIDEOINFOHEADER *)(pmt->pbFormat))->rcTarget pSample->GetMediaType(&pmt); if (pmt != NULL && pmt->pbFormat != NULL) { // spew some debug output ASSERT(!IsEqualGUID(pmt->majortype, GUID_NULL)); #ifdef _DEBUG fccOut.SetFOURCC(&pmt->subtype); LONG lCompression = HEADER(pmt->pbFormat)->biCompression; LONG lBitCount = HEADER(pmt->pbFormat)->biBitCount; LONG lStride = (HEADER(pmt->pbFormat)->biWidth * lBitCount + 7) / 8; lStride = (lStride + 3) & ~3; DbgLog((LOG_TRACE,3,TEXT("*Changing input type on the fly to"))); DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"), fccOut.GetFOURCC(), lCompression, lBitCount)); DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"), HEADER(pmt->pbFormat)->biHeight, rcT1.left, rcT1.top, rcT1.right, rcT1.bottom)); DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"), rcS1.left, rcS1.top, rcS1.right, rcS1.bottom, lStride)); #endif // now switch to using the new format. I am assuming that the // derived filter will do the right thing when its media type is // switched and streaming is restarted. StopStreaming(); m_pInput->CurrentMediaType() = *pmt; DeleteMediaType(pmt); // if this fails, playback will stop, so signal an error hr = StartStreaming(); if (FAILED(hr)) { return AbortPlayback(hr); } } // Now that we have noticed any format changes on the input sample, it's // OK to discard it. if (ShouldSkipFrame(pSample)) { MSR_NOTE(m_idSkip); m_bSampleSkipped = TRUE; return NOERROR; } // Set up the output sample hr = InitializeOutputSample(pSample, &pOutSample); if (FAILED(hr)) { return hr; } m_bSampleSkipped = FALSE; // The renderer may ask us to on-the-fly to start transforming to a // different format. If we don't obey it, we'll draw garbage #define rcS ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcSource #define rcT ((VIDEOINFOHEADER *)(pmtOut->pbFormat))->rcTarget pOutSample->GetMediaType(&pmtOut); if (pmtOut != NULL && pmtOut->pbFormat != NULL) { // spew some debug output ASSERT(!IsEqualGUID(pmtOut->majortype, GUID_NULL)); #ifdef _DEBUG fccOut.SetFOURCC(&pmtOut->subtype); LONG lCompression = HEADER(pmtOut->pbFormat)->biCompression; LONG lBitCount = HEADER(pmtOut->pbFormat)->biBitCount; LONG lStride = (HEADER(pmtOut->pbFormat)->biWidth * lBitCount + 7) / 8; lStride = (lStride + 3) & ~3; DbgLog((LOG_TRACE,3,TEXT("*Changing output type on the fly to"))); DbgLog((LOG_TRACE,3,TEXT("FourCC: %lx Compression: %lx BitCount: %ld"), fccOut.GetFOURCC(), lCompression, lBitCount)); DbgLog((LOG_TRACE,3,TEXT("biHeight: %ld rcDst: (%ld, %ld, %ld, %ld)"), HEADER(pmtOut->pbFormat)->biHeight, rcT.left, rcT.top, rcT.right, rcT.bottom)); DbgLog((LOG_TRACE,3,TEXT("rcSrc: (%ld, %ld, %ld, %ld) Stride: %ld"), rcS.left, rcS.top, rcS.right, rcS.bottom, lStride)); #endif // now switch to using the new format. I am assuming that the // derived filter will do the right thing when its media type is // switched and streaming is restarted. StopStreaming(); m_pOutput->CurrentMediaType() = *pmtOut; DeleteMediaType(pmtOut); hr = StartStreaming(); if (SUCCEEDED(hr)) { // a new format, means a new empty buffer, so wait for a keyframe // before passing anything on to the renderer. // !!! a keyframe may never come, so give up after 30 frames DbgLog((LOG_TRACE,3,TEXT("Output format change means we must wait for a keyframe"))); m_nWaitForKey = 30; // if this fails, playback will stop, so signal an error } else { // Must release the sample before calling AbortPlayback // because we might be holding the win16 lock or // ddraw lock pOutSample->Release(); AbortPlayback(hr); return hr; } } // After a discontinuity, we need to wait for the next key frame if (pSample->IsDiscontinuity() == S_OK) { DbgLog((LOG_TRACE,3,TEXT("Non-key discontinuity - wait for keyframe"))); m_nWaitForKey = 30; } // Start timing the transform (and log it if PERF is defined) if (SUCCEEDED(hr)) { m_tDecodeStart = timeGetTime(); MSR_START(m_idTransform); // have the derived class transform the data hr = Transform(pSample, pOutSample); // Stop the clock (and log it if PERF is defined) MSR_STOP(m_idTransform); m_tDecodeStart = timeGetTime()-m_tDecodeStart; m_itrAvgDecode = m_tDecodeStart*(10000/16) + 15*(m_itrAvgDecode/16); // Maybe we're waiting for a keyframe still? if (m_nWaitForKey) m_nWaitForKey--; if (m_nWaitForKey && pSample->IsSyncPoint() == S_OK) m_nWaitForKey = FALSE; // if so, then we don't want to pass this on to the renderer if (m_nWaitForKey && hr == NOERROR) { DbgLog((LOG_TRACE,3,TEXT("still waiting for a keyframe"))); hr = S_FALSE; } } if (FAILED(hr)) { DbgLog((LOG_TRACE,1,TEXT("Error from video transform"))); } else { // the Transform() function can return S_FALSE to indicate that the // sample should not be delivered; we only deliver the sample if it's // really S_OK (same as NOERROR, of course.) // Try not to return S_FALSE to a direct draw buffer (it's wasteful) // Try to take the decision earlier - before you get it. if (hr == NOERROR) { hr = m_pOutput->Deliver(pOutSample); } else { // S_FALSE returned from Transform is a PRIVATE agreement // We should return NOERROR from Receive() in this case because returning S_FALSE // from Receive() means that this is the end of the stream and no more data should // be sent. if (S_FALSE == hr) { // We must Release() the sample before doing anything // like calling the filter graph because having the // sample means we may have the DirectDraw lock // (== win16 lock on some versions) pOutSample->Release(); m_bSampleSkipped = TRUE; if (!m_bQualityChanged) { m_bQualityChanged = TRUE; NotifyEvent(EC_QUALITY_CHANGE,0,0); } return NOERROR; } } } // release the output buffer. If the connected pin still needs it, // it will have addrefed it itself. pOutSample->Release(); ASSERT(CritCheckIn(&m_csReceive)); return hr; }
// given a specific media type, attempt a connection (includes // checking that the type is acceptable to this pin) HRESULT CBasePin::AttemptConnection( IPin* pReceivePin, // connect to this pin const CMediaType* pmt // using this type ) { // The caller should hold the filter lock becasue this function // uses m_Connected. The caller should also hold the filter lock // because this function calls SetMediaType(), IsStopped() and // CompleteConnect(). ASSERT(CritCheckIn(m_pLock)); // Check that the connection is valid -- need to do this for every // connect attempt since BreakConnect will undo it. HRESULT hr = CheckConnect(pReceivePin); if (FAILED(hr)) { DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("CheckConnect failed"))); // Since the procedure is already returning an error code, there // is nothing else this function can do to report the error. EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) ); return hr; } DisplayTypeInfo(pReceivePin, pmt); /* Check we will accept this media type */ hr = CheckMediaType(pmt); if (hr == NOERROR) { /* Make ourselves look connected otherwise ReceiveConnection may not be able to complete the connection */ m_Connected = pReceivePin; m_Connected->AddRef(); hr = SetMediaType(pmt); if (SUCCEEDED(hr)) { /* See if the other pin will accept this type */ hr = pReceivePin->ReceiveConnection((IPin *)this, pmt); if (SUCCEEDED(hr)) { /* Complete the connection */ hr = CompleteConnect(pReceivePin); if (SUCCEEDED(hr)) { return hr; } else { DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to complete connection"))); pReceivePin->Disconnect(); } } } } else { // we cannot use this media type // return a specific media type error if there is one // or map a general failure code to something more helpful // (in particular S_FALSE gets changed to an error code) if (SUCCEEDED(hr) || (hr == E_FAIL) || (hr == E_INVALIDARG)) { hr = VFW_E_TYPE_NOT_ACCEPTED; } } // BreakConnect and release any connection here in case CheckMediaType // failed, or if we set anything up during a call back during // ReceiveConnection. // Since the procedure is already returning an error code, there // is nothing else this function can do to report the error. EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) ); /* If failed then undo our state */ if (m_Connected) { m_Connected->Release(); m_Connected = NULL; } return hr; }
void CBaseStreamControl::CancelStart() { ASSERT(CritCheckIn(&m_CritSec)); m_tStartTime = MAX_TIME; m_dwStartCookie = 0; }
// // DecideBufferSize // // This will always be called after the format has been sucessfully // negotiated. So we have a look at m_mt to see what format we agreed to. // Then we can ask for buffers of the correct size to contain them. HRESULT CSynthStream::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProperties) { // The caller should always hold the shared state lock // before calling this function. This function must hold // the shared state lock because it uses m_hPCMToMSADPCMConversionStream // m_dwTempPCMBufferSize. ASSERT(CritCheckIn(&m_cSharedState)); CheckPointer(pAlloc,E_POINTER); CheckPointer(pProperties,E_POINTER); WAVEFORMATEX *pwfexCurrent = (WAVEFORMATEX*)m_mt.Format(); if(WAVE_FORMAT_PCM == pwfexCurrent->wFormatTag) { pProperties->cbBuffer = WaveBufferSize; } else { // This filter only supports two formats: PCM and ADPCM. ASSERT(WAVE_FORMAT_ADPCM == pwfexCurrent->wFormatTag); pProperties->cbBuffer = pwfexCurrent->nBlockAlign; MMRESULT mmr = acmStreamSize(m_hPCMToMSADPCMConversionStream, pwfexCurrent->nBlockAlign, &m_dwTempPCMBufferSize, ACM_STREAMSIZEF_DESTINATION); // acmStreamSize() returns 0 if no error occurs. if(0 != mmr) { return E_FAIL; } } int nBitsPerSample = pwfexCurrent->wBitsPerSample; int nSamplesPerSec = pwfexCurrent->nSamplesPerSec; int nChannels = pwfexCurrent->nChannels; pProperties->cBuffers = (nChannels * nSamplesPerSec * nBitsPerSample) / (pProperties->cbBuffer * BITS_PER_BYTE); // Get 1/2 second worth of buffers pProperties->cBuffers /= 2; if(pProperties->cBuffers < 1) pProperties->cBuffers = 1 ; // Ask the allocator to reserve us the memory ALLOCATOR_PROPERTIES Actual; HRESULT hr = pAlloc->SetProperties(pProperties,&Actual); if(FAILED(hr)) { return hr; } // Is this allocator unsuitable if(Actual.cbBuffer < pProperties->cbBuffer) { return E_FAIL; } return NOERROR; }
// // GetMediaType // HRESULT CSynthStream::GetMediaType(CMediaType *pmt) { CheckPointer(pmt,E_POINTER); // The caller must hold the state lock because this function // calls get_OutputFormat() and GetPCMFormatStructure(). // The function assumes that the state of the m_Synth // object does not change between the two calls. The // m_Synth object's state will not change if the // state lock is held. ASSERT(CritCheckIn(m_pParent->pStateLock())); WAVEFORMATEX *pwfex; SYNTH_OUTPUT_FORMAT ofCurrent; HRESULT hr = m_Synth->get_OutputFormat( &ofCurrent ); if(FAILED(hr)) { return hr; } if(SYNTH_OF_PCM == ofCurrent) { pwfex = (WAVEFORMATEX *) pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX)); if(NULL == pwfex) { return E_OUTOFMEMORY; } m_Synth->GetPCMFormatStructure(pwfex); } else if(SYNTH_OF_MS_ADPCM == ofCurrent) { DWORD dwMaxWAVEFORMATEXSize; MMRESULT mmr = acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, (void*)&dwMaxWAVEFORMATEXSize); // acmMetrics() returns 0 if no errors occur. if(0 != mmr) { return E_FAIL; } pwfex = (WAVEFORMATEX *) pmt->AllocFormatBuffer(dwMaxWAVEFORMATEXSize); if(NULL == pwfex) { return E_OUTOFMEMORY; } WAVEFORMATEX wfexSourceFormat; m_Synth->GetPCMFormatStructure(&wfexSourceFormat); ZeroMemory(pwfex, dwMaxWAVEFORMATEXSize); pwfex->wFormatTag = WAVE_FORMAT_ADPCM; pwfex->cbSize = (USHORT)(dwMaxWAVEFORMATEXSize - sizeof(WAVEFORMATEX)); pwfex->nChannels = wfexSourceFormat.nChannels; pwfex->nSamplesPerSec = wfexSourceFormat.nSamplesPerSec; mmr = acmFormatSuggest(NULL, &wfexSourceFormat, pwfex, dwMaxWAVEFORMATEXSize, ACM_FORMATSUGGESTF_WFORMATTAG | ACM_FORMATSUGGESTF_NSAMPLESPERSEC | ACM_FORMATSUGGESTF_NCHANNELS); // acmFormatSuggest() returns 0 if no errors occur. if(0 != mmr) { return E_FAIL; } } else { return E_UNEXPECTED; } return CreateAudioMediaType(pwfex, pmt, FALSE); }
// // FillAudioBuffer // // // void CAudioSynth::FillPCMAudioBuffer(const WAVEFORMATEX& wfex, BYTE pBuf[], int iSize) { BOOL fCalcCache = FALSE; // The caller should always hold the state lock because this // function uses m_iFrequency, m_iFrequencyLast, m_iWaveform // m_iWaveformLast, m_iAmplitude, m_iAmplitudeLast, m_iWaveCacheIndex // m_iWaveCacheSize, m_bWaveCache and m_wWaveCache. The caller should // also hold the state lock because this function calls CalcCache(). ASSERT(CritCheckIn(m_pStateLock)); // Only realloc the cache if the format has changed ! if(m_iFrequency != m_iFrequencyLast) { fCalcCache = TRUE; m_iFrequencyLast = m_iFrequency; } if(m_iWaveform != m_iWaveformLast) { fCalcCache = TRUE; m_iWaveformLast = m_iWaveform; } if(m_iAmplitude != m_iAmplitudeLast) { fCalcCache = TRUE; m_iAmplitudeLast = m_iAmplitude; } if(fCalcCache) { CalcCache(wfex); } // Copy cache to output buffers if(wfex.wBitsPerSample == 8 && wfex.nChannels == 1) { while(iSize--) { *pBuf++ = m_bWaveCache[m_iWaveCacheIndex++]; if(m_iWaveCacheIndex >= m_iWaveCacheSize) m_iWaveCacheIndex = 0; } } else if(wfex.wBitsPerSample == 8 && wfex.nChannels == 2) { iSize /= 2; while(iSize--) { *pBuf++ = m_bWaveCache[m_iWaveCacheIndex]; *pBuf++ = m_bWaveCache[m_iWaveCacheIndex++]; if(m_iWaveCacheIndex >= m_iWaveCacheSize) m_iWaveCacheIndex = 0; } } else if(wfex.wBitsPerSample == 16 && wfex.nChannels == 1) { WORD * pW = (WORD *) pBuf; iSize /= 2; while(iSize--) { *pW++ = m_wWaveCache[m_iWaveCacheIndex++]; if(m_iWaveCacheIndex >= m_iWaveCacheSize) m_iWaveCacheIndex = 0; } } else if(wfex.wBitsPerSample == 16 && wfex.nChannels == 2) { WORD * pW = (WORD *) pBuf; iSize /= 4; while(iSize--) { *pW++ = m_wWaveCache[m_iWaveCacheIndex]; *pW++ = m_wWaveCache[m_iWaveCacheIndex++]; if(m_iWaveCacheIndex >= m_iWaveCacheSize) m_iWaveCacheIndex = 0; } } }