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

📄 strmctxt.cpp

📁 此代码为WCE5.0下声卡的源代码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
// -----------------------------------------------------------------------------
//
//      THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//      ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//      THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//      PARTICULAR PURPOSE.
//  
// -----------------------------------------------------------------------------

#include "wavdrv.h"
#include "decibels.h"


/////////////////////////////////////////////////////////////////////////////
//
// CStreamContext Methods
//
/////////////////////////////////////////////////////////////////////////////

CStreamContext::CStreamContext(BOOL fPlayback, BOOL fPaused)
    : m_fIsPlayback(fPlayback)
    , m_fPaused(fPaused)
{
    MYTHIS_CONSTRUCTOR;
    
    InitializeCriticalSection(&m_cs);

    m_dwTransferCursor = 0;
    m_dwPlaybackRate = 0x10000; // initial nominal multiplier value of 1.0
    m_fStarted = FALSE;
    m_lRefCount = 1;
    // initialize volume to max

}

COutputStreamContext::COutputStreamContext()
    : CStreamContext(TRUE, FALSE) // output streams are created in the unpaused state
{
}

CInputStreamContext::CInputStreamContext()
    : CStreamContext(FALSE, TRUE) // input streams are created in the paused state
{
}

CStreamContext::~CStreamContext()
{
    MYTHIS_CHECK;

    Stop();

    m_pDevice->FreeDMAChannel(m_ulDmaChannel);

    DeleteCriticalSection(&m_cs);
    MYTHIS_DESTRUCTOR;
}

void CStreamContext::AddRef(void)
{
    InterlockedIncrement(&m_lRefCount);
}

void CStreamContext::Release(void)
{
    if (InterlockedDecrement(&m_lRefCount) == 0) {
        delete this;
    }
}

MMRESULT
CStreamContext::Initialize (CES1371 * pDevice, ULONG ulChannelIndex, LPWAVEOPENDESC pWOD)
{
    m_pDevice      = pDevice;
    m_ulDmaChannel  = ulChannelIndex;

    m_wfx = *(pWOD->lpFormat);
    m_ucSilence = (m_wfx.wBitsPerSample == 8) ? 0x80 : 0; // fill buffer w/ this to get silence

    // set up interrupt interval: pick some lowish latency and convert to bytes
    const int interrupt_period = 10; // break this out to make next line less obscure
    DWORD dwSamplesPerInterrupt = (m_wfx.nSamplesPerSec * interrupt_period / 1000);
    m_dwInterruptInterval = m_wfx.nBlockAlign * dwSamplesPerInterrupt;
    m_dwBufferWrapTime = (m_dwBufferSize * 1000) / m_wfx.nAvgBytesPerSec;

    // set up the DMA channel

    m_pDevice->InitDMAChannel(ulChannelIndex, InterruptHandler, this);
    m_pDevice->GetDMABuffer(m_ulDmaChannel, &m_dwBufferSize, (PVOID *) &m_pBufferBits);
    m_pDevice->SetDMAChannelFormat(m_ulDmaChannel, m_wfx.nChannels, m_wfx.wBitsPerSample, m_wfx.nSamplesPerSec);

    SetVolume(0xFFFFFFFF);

    if (!m_HdrQ.Initialize(pWOD, m_fIsPlayback ? MM_WOM_DONE : MM_WIM_DATA)) {
        // something went wrong
        return MMSYSERR_ERROR;
    }

    return MMSYSERR_NOERROR;
}


MMRESULT 
CStreamContext::Close (void)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);

    EnterCriticalSection(&m_cs); // bypass the usual CAutoLock to avoid releasing the CS after deleting it.

    if (!m_HdrQ.IsDone()) {
        return WAVERR_STILLPLAYING;
    }

    // Use Release instead of the destructor, since we don't want to
    // delete the object if it's in use by the interrupt handler.
    // Also, Release will invoke the destructor, which deletes the critical section
    // so we exit the CS here, BEFORE we invoke release.
    LeaveCriticalSection(&m_cs);
    Release();
    return MMSYSERR_NOERROR;
}

MMRESULT 
CStreamContext::AddBuffer (LPWAVEHDR lpHeader)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);
    CAutoLock cal(&m_cs);
    if (!m_HdrQ.AddBuffer(lpHeader)) {
        DEBUGMSG(ZONE_ERROR, (TEXT("header already queued\r\n")));
        return MMSYSERR_INVALPARAM;
    }

    if (!m_fPaused ) {
        if (m_fStarted) {
            // if buffer is up and running, get this latest data
            // into the buffer: don't wait for the interrupt thread

            // We're trying to minimize the latency by transfering data into
            // the DMA buffer on AddBuffer, rather than wait for
            // the next interrupt. This is a worthy goal,
            // but one should worry about about the callback re-entrancy problem:
            // AdvanceCurrentPosition can trigger callbacks,
            // which in some twisted but legal cases could lead
            // to the stream being closed before AdvanceCurrentPosition returns.
            // However, the wave API guarantees that the Close will never
            // happen on this thread, and we're protected by the m_cs lock
            // we we're OK.
            HandleInterrupt();
        }
        else {
            // buffer was stopped: get it going.
            Start();
        }
    }

    return MMSYSERR_NOERROR;
}

MMRESULT 
CStreamContext::Unpause (void)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);
    CAutoLock cal(&m_cs);
    if (m_fPaused) {
        ASSERT(!m_fStarted);
        if (! m_HdrQ.IsEmpty()) {
            Start();
        }
        m_fPaused = FALSE;
    }
    return MMSYSERR_NOERROR;
}

MMRESULT 
CStreamContext::Pause (void)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);
    CAutoLock cal(&m_cs);
    if (!m_fPaused) {
        Stop();
        m_fPaused = TRUE;
    }
    return MMSYSERR_NOERROR;
}

MMRESULT 
CStreamContext::Reset (void)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);
    CAutoLock cal(&m_cs);
    // Note that waveOutReset does not affect the paused state
    Stop();
    m_HdrQ.Reset();
    return MMSYSERR_NOERROR;
}

MMRESULT 
CStreamContext::GetPosition (LPMMTIME pmmt)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);
    CAutoLock cal(&m_cs);
    DWORD dwBytePosition = m_HdrQ.GetBytePosition();

    switch (pmmt->wType) {

        case TIME_BYTES:
            pmmt->u.cb = dwBytePosition;
            break;

        case TIME_SAMPLES:
            pmmt->u.sample = dwBytePosition / m_wfx.nBlockAlign;
            break;

        case TIME_MS:
            // caveat emptor: don't call SetPlaybackRate and expect time-based
            // positions to mean much. Maybe return not supported?
            if (m_wfx.nAvgBytesPerSec != 0 && m_dwPlaybackRate == 0x10000) {
                pmmt->u.ms = (dwBytePosition * 1000) / m_wfx.nAvgBytesPerSec;
                break;
            }

        case TIME_MIDI:
        case TIME_TICKS:
        case TIME_SMPTE:
            //
            // We don't support these, so return TIME_BYTES instead.
            //
            pmmt->wType = TIME_BYTES;
            pmmt->u.cb = dwBytePosition;
            break;
    }

    return MMSYSERR_NOERROR;
}

MMRESULT 
CStreamContext::BreakLoop (void)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);
    CAutoLock cal(&m_cs);
    m_HdrQ.BreakLoop();
    return MMSYSERR_NOERROR;
} 

MMRESULT 
CStreamContext::GetPlaybackRate (PULONG pulRate)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);

    CAutoLock cal(&m_cs);
    *pulRate = m_dwPlaybackRate;
    return MMSYSERR_NOERROR;
}


// waveOutSetPlaybackRate
MMRESULT 
CStreamContext::SetPlaybackRate (ULONG ulRate)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);

    CAutoLock cal(&m_cs);

    __int64 nFrequency = ((__int64) m_wfx.nSamplesPerSec * (__int64) ulRate) >> 16;
    DEBUGMSG(ZONE_INFO, (TEXT("SetPlaybackRate(%08x,%5d) = %5d\r\n"), ulRate, m_wfx.nSamplesPerSec, (DWORD) nFrequency));

    DWORD dwSampleRate = (DWORD) nFrequency;

    if (dwSampleRate > 48000) {
        // The ES1371 SRC only goes up to 48KHz
        return MMSYSERR_ERROR;
    }

    m_pDevice->SRCSetRate((UCHAR) m_ulDmaChannel, (USHORT) dwSampleRate);

    m_dwPlaybackRate = ulRate;
    // also update our expected buffer wrap time
    // Sharp changes in playback rate will cause our current wrap-detection logic to fail
    // We need to insert code here that updates the known cursor positions for the benefit of
    // the heartbeat thread.
    m_dwBufferWrapTime = (m_dwBufferSize * 1000) / (DWORD) nFrequency;
    return MMSYSERR_NOERROR;

}

MMRESULT 
CStreamContext::GetVolume (PULONG pulVolume)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);

    CAutoLock cal(&m_cs);
    *pulVolume = m_dwVolume;
    return MMSYSERR_NOERROR;
}

MMRESULT 
CStreamContext::SetVolume (ULONG ulVolume)
{
    _MYTHIS_CHECK(return MMSYSERR_INVALHANDLE);

    CAutoLock cal(&m_cs);
    m_dwVolume = ulVolume;

    // need to convert the waveOut form of volume to dsound form
    // wave has absolute attenuation for each channel in low/high words of ulVolume
    // dsound has relative pan in one LONG, overall volume in another.
    // both have logarithmic scales, though, se we can do simple linear scaling.

    LONG nVolumeLeft  = ulVolume & 0xFFFF;
    LONG nVolumeRight = (ulVolume >> 16) & 0xFFFF;

    const int WAVE_VOLUME_MAX = 0xFFFF;
    const int scale_factor = -10000; // SetGain uses a 0-100dB scale. Adjust accordingly.

    // Convert wave volumes to dB:
    
    LONG nGainLeft, nGainRight;
    if (nVolumeLeft > 0) {
        nGainLeft = scale_factor * (WAVE_VOLUME_MAX - nVolumeLeft ) / WAVE_VOLUME_MAX;
    }
    else {
        nGainLeft = VOLUME_MIN;
    }

⌨️ 快捷键说明

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