📄 strmctxt.cpp
字号:
//
// 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 + -