📄 strmctxt.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
// Portions Copyright (c) Texas Instruments. All rights reserved.
//
//------------------------------------------------------------------------------
//
#include "wavemain.h"
//------------------------------------------------------------------------------
//
// Function: Open
//
//
HRESULT
StreamContext::Open(DeviceContext *pDeviceContext, LPWAVEOPENDESC lpWOD, DWORD dwFlags)
{
m_RefCount = 1;
m_pDeviceContext = pDeviceContext;
m_pfnCallback = (DRVCALLBACK *)lpWOD->dwCallback;
m_dwInstance = lpWOD->dwInstance;
m_hWave = lpWOD->hWave;
m_dwFlags = dwFlags;
m_bRunning = FALSE;
m_bForceSpeaker = FALSE;
// If it's a PCMWAVEFORMAT struct, it's smaller than a WAVEFORMATEX struct (it doesn't have the cbSize field),
// so don't copy too much or we risk a fault if the structure is located on the end of a page.
// All other non-PCM wave formats share the WAVEFORMATEX base structure
// Note: I don't keep around anything after the cbSize of the WAVEFORMATEX struct so that I don't need to
// worry about allocating additional space. If we need to keep this info around in the future, we can either
// allocate it dynamically here, or keep the information in any derived format-specific classes.
DWORD dwSize;
WAVEFORMATEX *pwfx = lpWOD->lpFormat;
if (pwfx->wFormatTag == WAVE_FORMAT_PCM)
{
dwSize = sizeof(PCMWAVEFORMAT);
m_WaveFormat.cbSize = 0;
}
else
{
dwSize = sizeof(WAVEFORMATEX);
}
memcpy(&m_WaveFormat,pwfx,dwSize);
m_lpWaveHdrHead = NULL;
m_lpWaveHdrTail = NULL;
m_lpWaveHdrCurrent = NULL;
m_lpCurrData = NULL;
m_lpCurrDataEnd = NULL;
m_dwByteCount = 0;
m_dwLoopCount = 0;
m_SecondaryGainClass=0;
SetGain(pDeviceContext->GetDefaultStreamGain()); // Set gain to default value
DEBUGMSG(ZONE_MDD, (TEXT("Opening stream 0x%x\r\n"),this));
// Add stream to list. This will start playback.
pDeviceContext->NewStream(this);
DoCallbackStreamOpened();
return S_OK;
}
//------------------------------------------------------------------------------
//
// Function: Close
//
//
DWORD
StreamContext::Close()
{
if (StillPlaying())
{
return WAVERR_STILLPLAYING;
}
// Be sure to turn off speaker if we turned it on.
ForceSpeaker(FALSE);
DEBUGMSG(ZONE_MDD, (TEXT("Closing stream 0x%x\r\n"),this));
DoCallbackStreamClosed();
return MMSYSERR_NOERROR;
}
//------------------------------------------------------------------------------
//
// Function: AddRef
//
// Assumes lock is taken
//
LONG
StreamContext::AddRef()
{
LONG RefCount = ++m_RefCount;
DEBUGMSG(ZONE_MDD, (TEXT("AddRef stream 0x%x, RefCount=%d\r\n"),this,RefCount));
return RefCount;
}
//------------------------------------------------------------------------------
//
// Function: Release
//
// Assumes lock is taken
//
LONG
StreamContext::Release()
{
LONG RefCount = --m_RefCount;
// DEBUGMSG(ZONE_MDD, (TEXT("Releasing stream 0x%x, RefCount=%d\r\n"),this,RefCount));
if (RefCount==0)
{
DEBUGMSG(ZONE_MDD, (TEXT("Deleting stream 0x%x\r\n"),this));
// Only remove stream from list when all refcounts are gone.
m_pDeviceContext->DeleteStream(this);
delete this;
}
return RefCount;
}
//------------------------------------------------------------------------------
//
// Function: QueueBuffer
//
// append wave buffer to queue
//
DWORD
StreamContext::QueueBuffer(LPWAVEHDR lpWaveHdr)
{
if (!(lpWaveHdr->dwFlags & WHDR_PREPARED))
{
return WAVERR_UNPREPARED;
}
lpWaveHdr->dwFlags |= WHDR_INQUEUE;
lpWaveHdr->dwFlags &= ~WHDR_DONE;
lpWaveHdr->lpNext=NULL;
lpWaveHdr->dwBytesRecorded=0;
if (!m_lpWaveHdrHead)
{
m_lpWaveHdrHead = lpWaveHdr;
}
else
{
m_lpWaveHdrTail->lpNext=lpWaveHdr;
}
m_lpWaveHdrTail=lpWaveHdr;
// Note: Even if head & tail are valid, current may be NULL if we're in the middle of
// a loop and ran out of data. So, we need to check specifically against current to
// decide if we need to initialize it.
if (!m_lpWaveHdrCurrent)
{
m_lpWaveHdrCurrent = lpWaveHdr;
m_lpCurrData = (PBYTE)lpWaveHdr->lpData;
m_lpCurrDataEnd = (PBYTE)lpWaveHdr->lpData + lpWaveHdr->dwBufferLength;
if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) // if this is the start of a loop block
{
m_dwLoopCount = lpWaveHdr->dwLoops; // save # of loops
}
}
if (m_bRunning)
{
m_pDeviceContext->StreamReadyToRender(this);
}
return MMSYSERR_NOERROR;
}
//------------------------------------------------------------------------------
//
// Function: GetNextBuffer
//
// get next wave buffer from queue and return used wave buffers to caller
//
// Note: I've found that when we return used buffers, the wave manager may
// call back into the wave driver in the same thread context to close the stream when
// we return the last buffer.
// If it wasn't the last buffer, the close call will return MMSYSERR_STILLPLAYING.
// However, if it was the last buffer, the close will proceed, and the
// stream may be deleted out from under us. Note that a Lock won't help us here,
// since we're in the same thread which already owns the lock.
// The solution to this is the AddRef/Release use on the stream context, which keeps it
// around if we're acessing it, even if it's closed.
//
// Assumes lock is taken
//
PBYTE
StreamContext::GetNextBuffer()
{
LPWAVEHDR lpOldHdr;
LPWAVEHDR lpNewHdr;
LPSTR pNewBuf=NULL;
// Get a pointer to the current buffer which is now done being processed
lpOldHdr=m_lpWaveHdrCurrent;
if (!lpOldHdr)
{
return NULL;
}
// Are we in a loop
// Note: a loopcount of 1 means we're not really in a loop
if (m_dwLoopCount>1)
{
// We're in a loop!
if (lpOldHdr->dwFlags & WHDR_ENDLOOP)
{
// In loop, last buffer
// If dwLoopCount was set to INFINITE, loop forever
// (Note: this is not explicitly in the wave driver API spec)
if (m_dwLoopCount!=INFINITE)
{
m_dwLoopCount--; // decrement loop count
}
lpNewHdr=m_lpWaveHdrHead; // go back to start of loop
}
else
{
// In loop, intermediate buffer
lpNewHdr=lpOldHdr->lpNext; // just go to next buffer in loop block
}
lpOldHdr=NULL;
}
else
{
// Not in a loop; return old buffer and get new buffer
lpNewHdr=lpOldHdr->lpNext;
m_lpWaveHdrHead = lpNewHdr; // reset list head
if (!lpNewHdr)
{
m_lpWaveHdrTail=NULL; // no new buffer, reset tail to NULL
}
else if (lpNewHdr->dwFlags & WHDR_BEGINLOOP) // if new buffer is start of a loop block
{
m_dwLoopCount=lpNewHdr->dwLoops; // save # of loops
}
}
m_lpWaveHdrCurrent=lpNewHdr; // save current buffer pointer
if (lpNewHdr)
{
m_lpCurrData = (PBYTE)lpNewHdr->lpData; // reinitialize data pointer
m_lpCurrDataEnd = m_lpCurrData + lpNewHdr->dwBufferLength;
}
else
{
m_lpCurrData = NULL;
m_lpCurrDataEnd = NULL;
}
// Return the old buffer
// This may cause the stream to be destroyed, so make sure that any calls to this function
// are within an AddRef/Release block
if (lpOldHdr)
{
ReturnBuffer(lpOldHdr);
}
return m_lpCurrData;
}
//------------------------------------------------------------------------------
//
// Function: BreakLoop
//
// break loop condition by retunring all wave headers
//
DWORD
StreamContext::BreakLoop()
{
AddRef();
if (m_dwLoopCount>0)
{
m_dwLoopCount = 0;
LPWAVEHDR lpHdr;
while (m_lpWaveHdrHead!=m_lpWaveHdrCurrent)
{
lpHdr = m_lpWaveHdrHead;
m_lpWaveHdrHead = lpHdr->lpNext;
if (m_lpWaveHdrHead==NULL)
{
m_lpWaveHdrTail=NULL;
}
ReturnBuffer(lpHdr);
}
}
Release();
return MMSYSERR_NOERROR;
}
// GainMap in 16.16 format
// Sample code to generate GainMap using VC++
/*
#include "stdafx.h"
#include "math.h"
const int NumEntries = 200;
const double fdBMax = -0.5;
const double fdBMin = -100.0;
int _tmain(int argc, _TCHAR* argv[])
{
const double fNumEntries = ((double)(NumEntries-1));
for (int i=0;i<NumEntries;i++)
{
double fVol = fdBMax - ( (fdBMax - fdBMin) * ( ((double)(i)) / fNumEntries) );
double fMulVal = pow(10.0,fVol/20);
unsigned long MulVal = (unsigned long)(fMulVal * (double)0x10000);
printf("0x%04x, // %d: %2.2f dB\n",MulVal,i,fVol);
}
return 0;
}
*/
const WORD GainMap[] =
{
0xf1ad, // 0: -0.50 dB
0xe429, // 1: -1.00 dB
0xd765, // 2: -1.50 dB
0xcb59, // 3: -2.00 dB
0xbff9, // 4: -2.50 dB
0xb53b, // 5: -3.00 dB
0xab18, // 6: -3.50 dB
0xa186, // 7: -4.00 dB
0x987d, // 8: -4.50 dB
0x8ff5, // 9: -5.00 dB
0x87e8, // 10: -5.50 dB
0x804d, // 11: -6.00 dB
0x7920, // 12: -6.50 dB
0x7259, // 13: -7.00 dB
0x6bf4, // 14: -7.50 dB
0x65ea, // 15: -8.00 dB
0x6036, // 16: -8.50 dB
0x5ad5, // 17: -9.00 dB
0x55c0, // 18: -9.50 dB
0x50f4, // 19: -10.00 dB
0x4c6d, // 20: -10.50 dB
0x4826, // 21: -11.00 dB
0x441d, // 22: -11.50 dB
0x404d, // 23: -12.00 dB
0x3cb5, // 24: -12.50 dB
0x394f, // 25: -13.00 dB
0x361a, // 26: -13.50 dB
0x3314, // 27: -14.00 dB
0x3038, // 28: -14.50 dB
0x2d86, // 29: -15.00 dB
0x2afa, // 30: -15.50 dB
0x2892, // 31: -16.00 dB
0x264d, // 32: -16.50 dB
0x2429, // 33: -17.00 dB
0x2223, // 34: -17.50 dB
0x203a, // 35: -18.00 dB
0x1e6c, // 36: -18.50 dB
0x1cb9, // 37: -19.00 dB
0x1b1d, // 38: -19.50 dB
0x1999, // 39: -20.00 dB
0x182a, // 40: -20.50 dB
0x16d0, // 41: -21.00 dB
0x158a, // 42: -21.50 dB
0x1455, // 43: -22.00 dB
0x1332, // 44: -22.50 dB
0x121f, // 45: -23.00 dB
0x111c, // 46: -23.50 dB
0x1027, // 47: -24.00 dB
0x0f3f, // 48: -24.50 dB
0x0e65, // 49: -25.00 dB
0x0d97, // 50: -25.50 dB
0x0cd4, // 51: -26.00 dB
0x0c1c, // 52: -26.50 dB
0x0b6f, // 53: -27.00 dB
0x0acb, // 54: -27.50 dB
0x0a31, // 55: -28.00 dB
0x099f, // 56: -28.50 dB
0x0915, // 57: -29.00 dB
0x0893, // 58: -29.50 dB
0x0818, // 59: -30.00 dB
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -