📄 streamoutput.cpp
字号:
}
// Get next WAVE buffer
PBYTE StreamOutput::stGetNextBuffer(void)
{
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)
{
stReturnBuffer(lpOldHdr);
}
return m_lpCurrData;
}
// Return a buffer to the ACM
void StreamOutput::stReturnBuffer(LPWAVEHDR lpWaveHdr)
{
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
lpWaveHdr->dwFlags |= WHDR_DONE;
stDoCallbackReturnBuffer(lpWaveHdr);
}
// Break a playback loop
DWORD StreamOutput::stBreakLoop(void)
{
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_lpWaveHdrHead = NULL;
}
stReturnBuffer(lpHdr);
}
}
return MMSYSERR_NOERROR;
}
// Return current byte playback count
DWORD StreamOutput::stGetByteCount(void)
{
return m_dwByteCount;
}
// Get data position
DWORD StreamOutput::stGetPos(PMMTIME pmmt)
{
switch (pmmt->wType)
{
case TIME_SAMPLES:
pmmt->u.sample = (m_dwByteCount * 8) /
(m_WaveFormat.nChannels * m_WaveFormat.wBitsPerSample);
break;
case TIME_MS:
if (m_WaveFormat.nAvgBytesPerSec != 0)
{
pmmt->u.ms = (m_dwByteCount * 1000) / m_WaveFormat.nAvgBytesPerSec;
break;
}
// If we don't know avg bytes per sec, fall through to TIME_BYTES
default:
// Anything else, return TIME_BYTES instead.
pmmt->wType = TIME_BYTES;
// Fall through to TIME_BYTES
case TIME_BYTES:
pmmt->u.cb = m_dwByteCount;
}
return MMSYSERR_NOERROR;
}
// Stop DMA
DWORD StreamOutput::stStop(void)
{
m_bRunning = FALSE;
return MMSYSERR_NOERROR;
}
// Start DMA
DWORD StreamOutput::stStart(void)
{
if (m_lpCurrData)
{
m_bRunning = TRUE;
SetEvent(m_stEventDma);
}
return MMSYSERR_NOERROR;
}
// Reset stream
DWORD StreamOutput::stReset(void)
{
// Stop stream for now.
stStop();
m_lpWaveHdrCurrent = NULL;
m_lpCurrData = NULL;
m_lpCurrDataEnd = NULL;
m_dwByteCount = 0;
m_dwLoopCount = 0;
m_buffsUsed = 0;
LPWAVEHDR lpHdr;
while (m_lpWaveHdrHead)
{
lpHdr = m_lpWaveHdrHead;
m_lpWaveHdrHead = lpHdr->lpNext;
if (m_lpWaveHdrHead == NULL)
{
m_lpWaveHdrTail = NULL;
}
stReturnBuffer(lpHdr);
}
dmaListReset(m_stdmaCtl);
return MMSYSERR_NOERROR;
}
// Output control thread
void StreamOutput::InterruptThread(void)
{
DWORD bytes, evid, queueroom;
PBYTE pCurData;
int nextfillidx;
RETAILMSG(1, (TEXT("OutputStreamContext::InterruptThread started!!!!\r\n")));
// TBD thread priority
SetThreadPriority(m_stThread, THREAD_PRIORITY_HIGHEST); // TBD
pCurData = NULL;
nextfillidx = 0;
ResetEvent(m_stEventDma);
while (m_stThreadStop == 0)
{
// Wait for event
evid = WaitForSingleObject(m_stEventDma, INFINITE);
if (evid != WAIT_OBJECT_0)
{
RETAILMSG(1, (TEXT("OutputStreamContext::InterruptThread event failure!!!!\r\n")));
m_stThreadStop = 1;
}
if (m_stThreadStop == 0)
{
// Pop any previously completed buffers
bytes = dmaCleanUp(m_stdmaCtl);
while (bytes > 0)
{
m_dwByteCount += bytes;
bytes = dmaCleanUp(m_stdmaCtl);
m_buffsUsed--;
}
// Data fill DMA loop
queueroom = 1;
while ((queueroom == 1) && (m_buffsUsed < 2))
{
// Is there more data to play
if (pCurData == NULL)
{
pCurData = stGetNextBuffer();
}
// Pop any previously completed buffers
bytes = dmaCleanUp(m_stdmaCtl);
while (bytes > 0)
{
m_dwByteCount += bytes;
bytes = dmaCleanUp(m_stdmaCtl);
m_buffsUsed--;
}
// Compute number of bytes to transfer
bytes = (DWORD) (m_lpCurrDataEnd - pCurData);
if (bytes > 2048)
{
bytes = 2048;
}
// Copy data to DMA buffer
memcpy(m_buffVirt [nextfillidx], pCurData, bytes);
// Submit buffer to hardware
if (dmaListEntry(m_stdmaCtl, (DWORD) m_buffPhy [nextfillidx],
(I2S1_BASE + 8), bytes) == 0)
{
// Hardware cannot accept more yet
queueroom = 0;
}
else
{
// Toggle buffer space
nextfillidx = 1 - nextfillidx;
m_buffsUsed++;
pCurData += bytes;
if (pCurData >= m_lpCurrDataEnd)
{
pCurData = NULL;
}
}
}
}
if (dmaIsOn(m_stdmaCtl) == 0)
{
// Stalled, will need DMA restart later
m_bRunning = FALSE;
}
// Re-enable DMA interrupts
InterruptDone(m_stsysIntrDMA);
}
// Thread is exited
m_stThreadDone = 1;
}
//********************************************************************
// Misc functions
//********************************************************************
// Returns supported status of a stream open
DWORD StreamOutput::stIsSupportedFormat(LPWAVEFORMATEX pWaveFormat)
{
DWORD supported = 1;
// Check supported audio formats
if (pWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
{
supported = 0;
}
// Check supported channels
if ((pWaveFormat->nChannels != 1) && (pWaveFormat->nChannels != 2))
{
supported = 0;
}
// Check supported sample sizes
if ((pWaveFormat->wBitsPerSample != 8) &&
(pWaveFormat->wBitsPerSample != 16))
{
supported = 0;
}
return supported;
}
// Open, close, and buffer return callback function
void StreamOutput::stDoDriverCallback(UINT msg,
DWORD dwParam1,
DWORD dwParam2)
{
m_pfnCallback(m_hWave, msg, m_dwInstance, dwParam1, dwParam2);
}
// Buffer return callback function
void StreamOutput::stDoCallbackReturnBuffer(LPWAVEHDR lpHdr)
{
stDoDriverCallback(WOM_DONE, (DWORD)lpHdr, 0);
}
// Stream open callback function
void StreamOutput::stDoCallbackStreamOpened(void)
{
stDoDriverCallback(WOM_OPEN, 0, 0);
}
// Stream close callback function
void StreamOutput::stDoCallbackStreamClosed(void)
{
stDoDriverCallback(WOM_CLOSE, 0, 0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -