⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 strmctxt.cpp

📁 此代码为WCE5.0下声卡的源代码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
    if (nVolumeRight > 0) {
        nGainRight= scale_factor * (WAVE_VOLUME_MAX - nVolumeRight) / WAVE_VOLUME_MAX;
    }
    else {
        nGainRight = VOLUME_MIN;
    }

    DEBUGMSG(ZONE_VOLUME, (TEXT("CStreamContext::SetVolume(%08x) -> %5d, %5d\r\n"), ulVolume, nGainRight, nGainLeft));

    USHORT usLeftVal, usRightVal;
    // Convert Decibels to Amplification factor and scale to suit SRC register
    //   [0 - 0xFFFF] scaled to [0 - 0x1000]
    // The Ensoniq SRC volume registers represent a 4.12 fixed-point scale factor.
    // In our case, we never amplify, only attenuate, so the largest scale factor
    // we'll ever use is 0x1000, which corresponds to 1.0. 
    // A value of 0x800 corresponds to a scale factor of 0.5.
    usRightVal = (USHORT) (DBToAmpFactor(nGainRight) >> 4);
    usLeftVal =  (USHORT) (DBToAmpFactor(nGainLeft)  >> 4);

    DEBUGMSG(ZONE_VOLUME, (TEXT("CStreamContext::SetVolume(%08x) => %04x,%04x\r\n"), ulVolume, usLeftVal,usRightVal));
    m_pDevice->SetDMAVolume (m_ulDmaChannel, usLeftVal, usRightVal);

    return MMSYSERR_NOERROR;
}

MMRESULT 
CStreamContext::PrepareHeader (LPWAVEHDR lpHeader)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);
    // nothing to do
    return MMSYSERR_NOERROR;
}

MMRESULT 
CStreamContext::UnprepareHeader (LPWAVEHDR lpHeader)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);
    // nothing to do
    return MMSYSERR_NOERROR;
}

// transfers queued data into the directsound buffer
// advances the write cursor
// If we don't have enough data queued up, we pad with silence, but only advance the
// transfer cursor as far as the real data goes.
VOID
COutputStreamContext::DoTransfer (PBYTE pDstBuffer, DWORD dwDstBytes)
{ PBYTE pSrcBuffer;
  ULONG ulSrcBytes;


    while (dwDstBytes > 0 && m_HdrQ.GetNextBlock (dwDstBytes, &pSrcBuffer, &ulSrcBytes)) {
        ASSERT(ulSrcBytes <= dwDstBytes);
        memcpy (pDstBuffer, pSrcBuffer, ulSrcBytes);
        pDstBuffer += ulSrcBytes;
        dwDstBytes -= ulSrcBytes;
        m_dwTransferCursor += ulSrcBytes;
    }


    // check for fill cursor at end of buffer and wrap
    ASSERT(m_dwTransferCursor <= m_dwBufferSize);
    if (m_dwTransferCursor == m_dwBufferSize) {
        m_dwTransferCursor = 0;
    }
}

// the symmetric opposite of COutputStreamContext::DoTransfer
// Copies data FROM the buffer TO the Header Queue
void CInputStreamContext::DoTransfer (PBYTE pSrcBuffer, DWORD dwSrctBytes)
{ PBYTE pDstBuffer;
  ULONG ulDstBytes;

    while (dwSrctBytes > 0 && m_HdrQ.GetNextBlock (dwSrctBytes, &pDstBuffer, &ulDstBytes)) {
        memcpy (pDstBuffer, pSrcBuffer, ulDstBytes);
        pSrcBuffer += ulDstBytes;
        dwSrctBytes -= ulDstBytes;
        m_dwTransferCursor += ulDstBytes;
    }
    // when there is more capture data than fits in the Header Queue, we drop it immediately

    // check for fill cursor at end of buffer and wrap
    ASSERT(m_dwTransferCursor <= m_dwBufferSize);
    if (m_dwTransferCursor == m_dwBufferSize) {
        m_dwTransferCursor = 0;
    }

}


// Transfer data to/from the DMA buffer and the WAVEHDR buffer.
// Start at the current m_dwTransferCursor and transfer dwTransferSize bytes.
// Handle the wrap-around case using the buffer lock mechanism
// Uses the virtual DoTransfer function to handle both input and output transfers

VOID
CStreamContext::DoSplitTransfer(DWORD dwTransferSize)
{ DWORD dwCount;
  DWORD dwBytesLeft;
  PBYTE pBuffer;

    ASSERT(dwTransferSize <= m_dwBufferSize);

    // we want to transfer dwTransferSize, but we need to figure out the address within the DMA buffer
    dwBytesLeft = m_dwBufferSize - m_dwTransferCursor;
    pBuffer = m_pBufferBits + m_dwTransferCursor;
    dwCount = dwTransferSize;
    if (dwCount > dwBytesLeft) {
        dwCount -= dwBytesLeft;
        // transfer to the end of the DMA buffer, then
        // wrap around to the start of the buffer
        DoTransfer(pBuffer, dwBytesLeft);
        pBuffer = m_pBufferBits;
    }

    DoTransfer(pBuffer, dwCount);
}

VOID 
CStreamContext::Stop(void)
{
    if (m_fStarted) {
        m_pDevice->StopDMAChannel( m_ulDmaChannel );
        m_fStarted = FALSE;
    }
}

VOID 
CStreamContext::Start(void)
{
    ASSERT(!m_fStarted);
    ASSERT(!m_HdrQ.IsEmpty()); // not true for capture streams

    // initialize the current DMA position and window

    m_dwPreTime = GetTickCount();
    m_dwTransferCursor = 0;
    m_dwLastDMACursor = 0;

    if (m_fIsPlayback) {
        // output streams start off with a full-buffer transfer
        // input streams must wait for data to accumulate in DMA bufffer before first transfer
        DoSplitTransfer(m_dwBufferSize);
    }

    m_fStarted = TRUE;
    
    m_pDevice->SetDMAPosition(  m_ulDmaChannel, 0);
	m_pDevice->SetDMAChannelBuffer( m_ulDmaChannel, m_dwBufferSize, (USHORT) m_dwInterruptInterval/m_wfx.nBlockAlign);
    m_pDevice->StartDMAChannel(  m_ulDmaChannel );

}

VOID
COutputStreamContext::GetAdvance(DWORD & dwAdvance, DWORD & dwRoomInBuffer)
{
    // Reading the DMA position in a way that completely excludes the possibility
    // of race conditions is an exercise in surreal temporal mechanics.
    // Strictly speaking, one can never know for sure where the DMA position is, because
    // an arbitrary amount of time may have elapsed since we actually looked at the hardware.
    // By the same reasoning, it's impossible to know precisely what the current time is.
    // However, one can get a grip on reality by reading the time before and after
    // reading the DMA position, thereby ensuring that we at least have window
    // of time during which we know the DMA position was valid.

    DWORD dwPreTime = GetTickCount();
    DWORD dwDMACursor = m_pDevice->GetDMAPosition(m_ulDmaChannel);

    ASSERT(dwDMACursor <= m_dwBufferSize);

    DWORD dwPostTime = GetTickCount();

    // can't advance past the transfer cursor
    DWORD dwMaxAdvance;
    if (m_dwLastDMACursor < m_dwTransferCursor) {
        dwMaxAdvance = m_dwTransferCursor - m_dwLastDMACursor;
    }
    else {
        dwMaxAdvance = m_dwBufferSize + m_dwTransferCursor - m_dwLastDMACursor;
    }


    // determine how much the DMA position has advanced
    // first, compute the worst-case elapsed time since we last read the DMA position
    DWORD dwElapsed = dwPostTime - m_dwPreTime;  
    DWORD dwOldLast = m_dwLastDMACursor;

    // assume we're safe & that there hasn't been enough time to wrap.
    if (dwDMACursor >= m_dwLastDMACursor) {
        dwAdvance = dwDMACursor - m_dwLastDMACursor;
    }
    else {
        dwAdvance =  m_dwBufferSize + dwDMACursor- m_dwLastDMACursor;
    }
    if ((dwElapsed >= m_dwBufferWrapTime-1) || (dwAdvance > dwMaxAdvance)) {
        // we know for sure we've wrapped...
        // or that the dma cursor advanced past the transfer cursor.
        // either way, limit the advance to the end of cued data, mark the entire buffer as available
//        DEBUGMSG(ZONE_WARNING, (TEXT("GetAdvance: underflow %3d %3d\r\n"), dwElapsed, m_dwBufferWrapTime));
        dwAdvance = dwMaxAdvance;           // we consumed all their was to consume
        dwRoomInBuffer = m_dwBufferSize;    // we can next consume the entire buffer
        m_dwTransferCursor = dwDMACursor;  // but start at where the DMA cursor is *now*
    }
    else {
        // but how much room is there in the buffer?
        DWORD dwBytesBeforeUnderflow = dwMaxAdvance - dwAdvance;
        // if we're close to underflowing (i.e. no fresh data from stream source)
        // we may want to silence-pad beyond the transfer cursor to avoid playing garbage
        if (m_fIsPlayback && dwBytesBeforeUnderflow <= m_dwInterruptInterval) {
            // clear enough of the buffer with silence to get us through the next interrupt
            // worry about wrap-around, though
            if (m_dwTransferCursor + m_dwInterruptInterval <= m_dwBufferSize) {
                // safely do it on one piece
                memset(m_pBufferBits + m_dwTransferCursor, m_ucSilence, m_dwInterruptInterval);
            }
            else {
                // must wrap around the end of the buffer
                memset(m_pBufferBits + m_dwTransferCursor, m_ucSilence, m_dwBufferSize - m_dwTransferCursor);
                memset(m_pBufferBits                     , m_ucSilence, m_dwTransferCursor + m_dwInterruptInterval - m_dwBufferSize);
            }
        }

        // finally, compute the amount of room left in the buffer for fresh data
        dwRoomInBuffer = m_dwBufferSize - dwBytesBeforeUnderflow;
    }


    m_dwPreTime = dwPreTime;
    m_dwLastDMACursor = dwDMACursor;

    if (dwAdvance > m_dwBufferSize) {
        RETAILMSG(1, (TEXT("Ensoniq: Over-advance! %08x %08x %08x\r\n"), dwAdvance, dwDMACursor, dwOldLast));
    }

}

VOID
CInputStreamContext::GetAdvance(DWORD & dwAdvance)
{
    DWORD dwPreTime = GetTickCount();
    DWORD dwDMACursor = m_pDevice->GetDMAPosition(m_ulDmaChannel);

    ASSERT(dwDMACursor <= m_dwBufferSize);

    DWORD dwPostTime = GetTickCount();

    // determine how much the DMA position has advanced
    // first, compute the worst-case elapsed time since we last read the DMA position
    DWORD dwElapsed = dwPostTime - m_dwPreTime;  
    DWORD dwOldLast = m_dwLastDMACursor;

    if (dwElapsed >= m_dwBufferWrapTime-1) {
        // we know for sure we've wrapped...
        // or that the dma cursor advanced past the transfer cursor.
        // either way, limit the advance to the end of cued data, mark the entire buffer as available
        DEBUGMSG(ZONE_WARNING, (TEXT("GetAdvance: underflow %3d %3d\r\n"), dwElapsed, m_dwBufferWrapTime));
        dwAdvance = m_dwBufferSize;    // we can next consume the entire buffer
        m_dwTransferCursor = dwDMACursor;  // but start at where the DMA cursor is *now*
    }
    else {
        // we're safe & that there hasn't been enough time to wrap.
        if (dwDMACursor >= m_dwLastDMACursor) {
            dwAdvance = dwDMACursor - m_dwLastDMACursor;
        }
        else {
            dwAdvance =  m_dwBufferSize + dwDMACursor- m_dwLastDMACursor;
        }
    }


    m_dwPreTime = dwPreTime;
    m_dwLastDMACursor = dwDMACursor;

    if (dwAdvance > m_dwBufferSize) {
        RETAILMSG(1, (TEXT("Ensoniq: Over-advance! %08x %08x %08x\r\n"), dwAdvance, dwDMACursor, dwOldLast));
    }

}

VOID 
CStreamContext::InterruptHandler(PVOID pArg)
{ 
    CStreamContext * pthis = (CStreamContext *) pArg;
    // addref/release around interrupt handler to avoid having the 
    // object pulled out from under the interrupt thread
    pthis->AddRef();
    pthis->HandleInterrupt();
    pthis->Release();
}

void
COutputStreamContext::HandleInterrupt(void)
{
    CAutoLock cal(&m_cs);

    DWORD dwAdvance, dwRoomInBuffer;
    GetAdvance(dwAdvance, dwRoomInBuffer);

    // notify the Header Queue of the advance so it can (possibly) make callbacks.
    m_HdrQ.AdvanceCurrentPosition(dwAdvance);

    if (m_HdrQ.IsDone()) {
        // finally, if the header queue is played out and we've played past the last known fill cursor position,
        // we can stop the buffer and mark this stream as not running
        Stop();
    }
    else {
        DoSplitTransfer(dwRoomInBuffer); // transfer fresh data from the header queue into the DMA buffer
    }

}

void
CInputStreamContext::HandleInterrupt(void)
{
    CAutoLock cal(&m_cs);

    DWORD dwAdvance;
    GetAdvance(dwAdvance);

    DoSplitTransfer(dwAdvance); // transfer freshly captured data into WAVEHDR buffers

    // notify the Header Queue of the advance so it can (possibly) make callbacks.
    m_HdrQ.AdvanceCurrentPosition(dwAdvance);

    if (m_HdrQ.IsDone()) {
        // finally, if the header queue is played out and we've played past the last known fill cursor position,
        // we can stop the buffer and mark this stream as not running
        Stop();
    }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -