📄 wavethread.cpp
字号:
m_pWaveHeaderArray = NULL;
m_dwActiveThreadPriority = THREAD_PRIORITY_NORMAL;
m_wBufferCount = 0;
m_dwSingleBufferSize = 0;
m_dwBufferSize = 0;
m_dwCopyBufferBytes = 0;
m_pCopyBufferPos = NULL;
m_bPause = FALSE;
m_dwUsedWaveOutBuffers = 0;
m_wNextCopyBuffer = 0;
ZeroMemory(&m_wfx, sizeof(m_wfx));
InitializeCriticalSection(&m_critSecRtp);
m_hEventKill = CreateEvent(NULL, FALSE, FALSE, NULL);
}
CWaveOutThread::~CWaveOutThread()
{
// Stop the thread processing
StopThread();
// Delete the critical section
DeleteCriticalSection(&m_critSecRtp);
// Destroy events
CloseHandle(m_hEventKill);
}
// Output initialization
BOOL CWaveOutThread::Init(cbWaveOut pcbWaveOut, // Callback function for event notifications, NULL = No callback!
DWORD dwActiveThreadPriority, // Thread Priority for this thread, if OUTPUT is active
WORD wBufferCount, // Count of buffers to use
DWORD dwSingleBufferSize, // Size of a single buffer in bytes (eg. 44100Hz Mono 16bit: 10ms = 882bytes)
WORD wChannels, // Mono = 1; Stereo = 2
DWORD dwSamplesPerSec, // 8000Hz/11025Hz/22050Hz/44100Hz
WORD wBitsPerSample) // 8bit/16bit
{
if (
( m_bInitialized
) ||
( dwActiveThreadPriority > THREAD_PRIORITY_IDLE
) ||
( wBufferCount < INTERNAL_WAVEOUT_BUFFER_COUNT
) ||
( dwSingleBufferSize < 80
) ||
( wChannels < 1
) ||
( wChannels > 2
) ||
(
( wBitsPerSample != 8
) &&
( wBitsPerSample != 16
)
) ||
(
( dwSamplesPerSec != 8000
) &&
( dwSamplesPerSec != 11025
) &&
( dwSamplesPerSec != 22050
) &&
( dwSamplesPerSec != 44100
)
)
)
{
return FALSE;
}
m_pcbWaveOut = pcbWaveOut;
m_dwActiveThreadPriority = dwActiveThreadPriority;
m_wBufferCount = wBufferCount;
m_dwSingleBufferSize = dwSingleBufferSize;
m_wfx.cbSize = 0;
m_wfx.wFormatTag = WAVE_FORMAT_PCM;
m_wfx.nChannels = wChannels;
m_wfx.nSamplesPerSec = dwSamplesPerSec;
m_wfx.nBlockAlign = (wBitsPerSample * wChannels) / 8;
m_wfx.nAvgBytesPerSec = dwSamplesPerSec * m_wfx.nBlockAlign;
m_wfx.wBitsPerSample = wBitsPerSample;
m_dwBufferSize = wBufferCount * dwSingleBufferSize;
m_bInitialized = TRUE;
return TRUE;
}
// Start sound output
BOOL CWaveOutThread::StartThread()
{
UINT id;
WORD i;
if ((m_thread) || (!m_bInitialized))
{
return FALSE;
}
m_dwUsedWaveOutBuffers = 0;
m_wNextCopyBuffer = 0;
// Start the thread
m_thread = CreateThread(NULL,
0,
ThreadProc,
(void*)this,
0,
&m_threadId);
if (m_thread)
{
DWORD dwExitCode;
if (GetExitCodeThread(m_thread, &dwExitCode))
{
if (dwExitCode != STILL_ACTIVE)
{
return FALSE;
}
}
if (m_hWaveOut == NULL)
{
m_pWaveOutBuffer = new unsigned char[m_dwSingleBufferSize * INTERNAL_WAVEOUT_BUFFER_COUNT];
if (m_pWaveOutBuffer)
{
ZeroMemory(m_pWaveOutBuffer, m_dwSingleBufferSize * INTERNAL_WAVEOUT_BUFFER_COUNT);
m_pWaveCopyBuffer = new unsigned char[m_dwBufferSize];
if (m_pWaveCopyBuffer)
{
ZeroMemory(m_pWaveCopyBuffer, m_dwBufferSize);
m_pWaveHeaderArray = new WAVEHDR[INTERNAL_WAVEOUT_BUFFER_COUNT];
if (m_pWaveHeaderArray)
{
ZeroMemory(m_pWaveHeaderArray, INTERNAL_WAVEOUT_BUFFER_COUNT * sizeof(WAVEHDR));
for (id = 0; id < waveOutGetNumDevs(); id++)
{
if (waveOutOpen(&m_hWaveOut, id, &m_wfx, m_threadId, (DWORD)this, CALLBACK_THREAD) == MMSYSERR_NOERROR)
{
waveOutPause(m_hWaveOut);
for (i=0 ; i<INTERNAL_WAVEOUT_BUFFER_COUNT ; i++)
{
m_dwUsedWaveOutBuffers |= (1 << i);
}
ResetBuffer();
m_bPause = TRUE;
return TRUE;
}
}
}
delete m_pWaveCopyBuffer;
m_pWaveCopyBuffer = NULL;
}
delete m_pWaveOutBuffer;
m_pWaveOutBuffer = NULL;
}
}
}
StopThread();
return FALSE;
}
// Stop sound output
BOOL CWaveOutThread::StopThread()
{
WORD i;
if (m_thread == NULL)
{
return FALSE;
}
SetThreadPriority(m_thread, THREAD_PRIORITY_NORMAL);
SetEvent(m_hEventKill);
if (m_hWaveOut)
{
waveOutPause(m_hWaveOut);
}
// This is ugly, wait for 10 seconds, then kill the thread
DWORD res = WaitForSingleObject(m_thread, 10000);
if (res == WAIT_TIMEOUT)
{
if (m_thread)
{
// OK, do it the hard way
TerminateThread(m_thread, 0);
CloseHandle(m_thread);
m_thread = NULL;
}
}
if (m_hWaveOut)
{
waveOutReset(m_hWaveOut);
if (m_pWaveHeaderArray)
{
for (i=0 ; i<m_wBufferCount ; i++)
{
waveOutUnprepareHeader(m_hWaveOut, &m_pWaveHeaderArray[i], sizeof(WAVEHDR));
}
}
waveOutClose(m_hWaveOut);
m_hWaveOut = NULL;
}
if (m_pWaveOutBuffer)
{
delete m_pWaveOutBuffer;
m_pWaveOutBuffer = NULL;
}
if (m_pWaveCopyBuffer)
{
delete m_pWaveCopyBuffer;
m_pWaveCopyBuffer = NULL;
}
if (m_pWaveHeaderArray)
{
delete m_pWaveHeaderArray;
m_pWaveHeaderArray = NULL;
}
return TRUE;
}
// Clear the buffer
void CWaveOutThread::ResetBuffer()
{
EnterCriticalSection(&m_critSecRtp);
m_pCopyBufferPos = m_pWaveCopyBuffer;
m_dwCopyBufferBytes = 0;
m_wNextCopyBuffer = 0;
LeaveCriticalSection(&m_critSecRtp);
}
// Get the size of the needed buffer for one second of data
DWORD CWaveOutThread::GetBytesPerSecond()
{
if (m_bInitialized)
{
return m_wfx.nAvgBytesPerSec;
}
return 0;
}
// Get the current length of data in the buffer
DWORD CWaveOutThread::GetDataLen()
{
DWORD dwBytes;
EnterCriticalSection(&m_critSecRtp);
dwBytes = m_dwCopyBufferBytes;
LeaveCriticalSection(&m_critSecRtp);
return dwBytes;
}
// Play sound data
BOOL CWaveOutThread::WriteData(unsigned char* pBuffer, // Data will be copied from this buffer
DWORD dwCount) // Count of bytes to copy
{
int i, iLastIndex;
EnterCriticalSection(&m_critSecRtp);
if (
( pBuffer == NULL
) ||
( m_dwCopyBufferBytes + dwCount > m_dwBufferSize
)
)
{
LeaveCriticalSection(&m_critSecRtp);
return FALSE;
}
if (m_pCopyBufferPos + dwCount > m_pWaveCopyBuffer + m_dwBufferSize)
{
memcpy(m_pCopyBufferPos,
pBuffer,
m_pWaveCopyBuffer + m_dwBufferSize - m_pCopyBufferPos);
pBuffer += m_pWaveCopyBuffer + m_dwBufferSize - m_pCopyBufferPos;
memcpy(m_pWaveCopyBuffer,
pBuffer,
m_pCopyBufferPos + dwCount - (m_pWaveCopyBuffer + m_dwBufferSize));
m_pCopyBufferPos = m_pWaveCopyBuffer + ((m_pCopyBufferPos + dwCount) - (m_pWaveCopyBuffer + m_dwBufferSize));
}
else
{
memcpy(m_pCopyBufferPos, pBuffer, dwCount);
m_pCopyBufferPos += dwCount;
}
m_dwCopyBufferBytes += dwCount;
while (m_dwCopyBufferBytes >= m_dwSingleBufferSize)
{
iLastIndex = -1;
for (i=INTERNAL_WAVEOUT_BUFFER_COUNT-1 ; i>=0; i--)
{
if (m_dwUsedWaveOutBuffers & (1 << i))
{
iLastIndex = i;
}
else
{
if (iLastIndex != -1)
{
break;
}
}
}
if (iLastIndex != -1)
{
memcpy(m_pWaveOutBuffer + iLastIndex * m_dwSingleBufferSize,
m_pWaveCopyBuffer + m_wNextCopyBuffer * m_dwSingleBufferSize,
m_dwSingleBufferSize);
m_pWaveHeaderArray[iLastIndex].dwBufferLength = m_dwSingleBufferSize;
m_pWaveHeaderArray[iLastIndex].lpData = (char*)m_pWaveOutBuffer + iLastIndex * m_dwSingleBufferSize;
m_pWaveHeaderArray[iLastIndex].dwUser = iLastIndex;
waveOutPrepareHeader(m_hWaveOut, &m_pWaveHeaderArray[iLastIndex], sizeof(WAVEHDR));
waveOutWrite(m_hWaveOut, &m_pWaveHeaderArray[iLastIndex], sizeof(WAVEHDR));
m_dwCopyBufferBytes -= m_dwSingleBufferSize;
m_wNextCopyBuffer++;
m_wNextCopyBuffer %= m_wBufferCount;
m_dwUsedWaveOutBuffers ^= (1 << iLastIndex);
if ((m_bPause) && (m_dwUsedWaveOutBuffers == 0))
{
SetThreadPriority(m_thread, m_dwActiveThreadPriority);
waveOutRestart(m_hWaveOut);
m_bPause = FALSE;
}
}
else
{
break;
}
}
LeaveCriticalSection(&m_critSecRtp);
return TRUE;
}
// Handles MM-API for sound output
DWORD WINAPI CWaveOutThread::ThreadProc(LPVOID lpParameter)
{
CWaveOutThread* pWaveOut = (CWaveOutThread*)lpParameter;
MSG msg;
DWORD dwWait = 0;
DWORD dwUser = 0;
WAVEHDR *pWaveHdr = NULL;
WAVE_OUT_EVENT event;
if (pWaveOut == NULL)
{
return -1;
}
while ((dwWait = WaitForSingleObject(pWaveOut->m_hEventKill, 5)) != 0)
{
// Handle MMAPI wave capturing thread messages
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
switch (msg.message)
{
case MM_WOM_DONE:
EnterCriticalSection(&pWaveOut->m_critSecRtp);
event = WAVE_OUT_EVENT_BUFFER_PLAYED;
pWaveHdr = (WAVEHDR*)msg.lParam;
// Unprepare header, initialize with the next buffer position, prepare the header
// again and add it to the wave capture device.
if (pWaveHdr)
{
waveOutUnprepareHeader(pWaveOut->m_hWaveOut, pWaveHdr, sizeof(WAVEHDR));
dwUser = pWaveHdr->dwUser;
ZeroMemory(pWaveHdr, sizeof(WAVEHDR));
if (pWaveOut->m_dwCopyBufferBytes >= pWaveOut->m_dwBufferSize)
{
memcpy(pWaveOut->m_pWaveOutBuffer + dwUser * pWaveOut->m_dwSingleBufferSize,
pWaveOut->m_pWaveCopyBuffer + pWaveOut->m_wNextCopyBuffer * pWaveOut->m_dwSingleBufferSize,
pWaveOut->m_dwSingleBufferSize);
pWaveOut->m_pWaveHeaderArray[dwUser].dwBufferLength = pWaveOut->m_dwSingleBufferSize;
pWaveOut->m_pWaveHeaderArray[dwUser].lpData = (char*)pWaveOut->m_pWaveOutBuffer + dwUser * pWaveOut->m_dwSingleBufferSize;
pWaveOut->m_pWaveHeaderArray[dwUser].dwUser = dwUser;
waveOutPrepareHeader(pWaveOut->m_hWaveOut, &pWaveOut->m_pWaveHeaderArray[dwUser], sizeof(WAVEHDR));
waveOutWrite(pWaveOut->m_hWaveOut, &pWaveOut->m_pWaveHeaderArray[dwUser], sizeof(WAVEHDR));
pWaveOut->m_dwCopyBufferBytes -= pWaveOut->m_dwSingleBufferSize;
pWaveOut->m_wNextCopyBuffer++;
pWaveOut->m_wNextCopyBuffer %= pWaveOut->m_wBufferCount;
}
else
{
pWaveOut->m_dwUsedWaveOutBuffers |= (1 << dwUser);
if (0xffffffff >> (32 - INTERNAL_WAVEOUT_BUFFER_COUNT) == pWaveOut->m_dwUsedWaveOutBuffers)
{
SetThreadPriority(pWaveOut->m_thread, THREAD_PRIORITY_NORMAL);
waveOutPause(pWaveOut->m_hWaveOut);
pWaveOut->m_bPause = TRUE;
event = WAVE_OUT_EVENT_BUFFER_EMPTY;
}
}
}
LeaveCriticalSection(&pWaveOut->m_critSecRtp);
if ((pWaveOut->m_pcbWaveOut != NULL) && (event != WAVE_OUT_EVENT_NONE))
{
(pWaveOut->m_pcbWaveOut)(event);
}
break;
default:
break;
}
}
}
CloseHandle(pWaveOut->m_thread);
pWaveOut->m_thread = NULL;
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -