📄 winaudio.cpp
字号:
}
HX_RESULT CAudioOutWindows::_Imp_Close()
{
#ifdef _TESTING
if ( m_audfile > 0 )
close(m_audfile);
m_audfile = -1;
#endif
zm_bClosed = TRUE;
if (m_hWave)
{
// NOTE: Before we can close, we need to reset!
Reset();
m_pMutex->Lock();
if (m_pWaveHdrs)
{
UINT16 unWHIndex;
// Unprepare the individual headers
for (unWHIndex = 0; unWHIndex < m_unAllocedBufferCnt; ++unWHIndex)
{
CWaveHeader* pWaveHeader = &m_pWaveHdrs[unWHIndex];
if (pWaveHeader->m_pPrepared)
{
LPWAVEHDR lpwHeader = LPWAVEHDR(&pWaveHeader->m_WAVEHDR);
// By now all headers should be marked as done because they
// are back from the audio device or prepared because we
// prepared them but never used them
HX_ASSERT(lpwHeader->dwFlags & WHDR_DONE ||
lpwHeader->dwFlags & WHDR_PREPARED);
//OutputDebugString("BEFORE CALL TO:waveOutUnprepareHeader\r\n");
HX_VERIFY(MMSYSERR_NOERROR == waveOutUnprepareHeader(m_hWave,
LPWAVEHDR(&pWaveHeader->m_WAVEHDR), sizeof(WAVEHDR)));
//OutputDebugString("AFTER CALL TO:waveOutUnprepareHeader\r\n");
pWaveHeader->m_pPrepared = FALSE;
}
}
}
if (m_ppAllocedBuffers)
{
UINT16 unIndex;
for (unIndex = 0; unIndex < m_unAllocedBufferCnt; unIndex++)
{
delete[] m_ppAllocedBuffers[unIndex];
}
delete[] m_ppAllocedBuffers;
m_ppAllocedBuffers = NULL;
}
m_rAvailBuffers.FlushQueue();
m_UsedBuffersList.RemoveAll();
m_unAllocedBufferCnt = 0;
m_pMutex->Unlock();
for (int i = 0;
i < 5 && (WAVERR_STILLPLAYING == waveOutClose( m_hWave )); ++i)
{
waveOutReset( m_hWave );
}
m_hWave = NULL;
if (m_pWaveHdrs)
{
delete[] m_pWaveHdrs;
m_pWaveHdrs = NULL;
}
}
UnRegister();
m_bIsFirstPacket = TRUE;
m_bInitialized = FALSE;
return HXR_OK;
}
HX_RESULT CAudioOutWindows::_Imp_Write
(
const HXAudioData* pAudioOutHdr
)
{
BOOL bTemp;
CWaveHeader* pWaveHeader;
MMRESULT res;
ULONG32 ulBufLen = 0;
UCHAR* pBuffer = 0;
if (pAudioOutHdr->pData)
{
pBuffer = pAudioOutHdr->pData->GetBuffer();
ulBufLen = pAudioOutHdr->pData->GetSize();
}
if (!m_bInitialized)
{
m_bInitialized = AllocateBuffers(MAX_REASONABLE_BUFFS,(UINT16)ulBufLen);
}
if (!m_bInitialized) return HXR_FAILED;
m_pMutex->Lock();
pWaveHeader = (CWaveHeader*)m_rAvailBuffers.DeQueuePtr(bTemp);
m_pMutex->Unlock();
if (!bTemp)
{
return HXR_WOULD_BLOCK;
}
#ifdef _TESTING
if ( m_audfile > 0 )
write(m_audfile, pBuffer,ulBufLen);
#endif
UINT32 ulBytesToCopy = ulBufLen;
HX_ASSERT(ulBytesToCopy <= pWaveHeader->m_WAVEHDR.dwBufferLength);
if (ulBytesToCopy > pWaveHeader->m_WAVEHDR.dwBufferLength)
ulBytesToCopy = pWaveHeader->m_WAVEHDR.dwBufferLength;
pWaveHeader->m_bAvail = FALSE;
memcpy(pWaveHeader->m_WAVEHDR.lpData, pBuffer, HX_SAFESIZE_T(ulBytesToCopy)); /* Flawfinder: ignore */
pWaveHeader->m_ulTimeEnd = pAudioOutHdr->ulAudioTime;
/* This is needed for LIVE where audio packets may not start
* from timestamp 0
*/
if (m_bIsFirstPacket)
{
m_bIsFirstPacket = FALSE;
}
//OutputDebugString("BEFORE CALL TO:waveOutWrite\r\n");
res = waveOutWrite(m_hWave, LPWAVEHDR(&pWaveHeader->m_WAVEHDR), sizeof(WAVEHDR));
m_pMutex->Lock();
m_UsedBuffersList.AddTail(pWaveHeader);
m_pMutex->Unlock();
//OutputDebugString("AFTER CALL TO:waveOutWrite\r\n");
return HXR_OK;
}
HX_RESULT CAudioOutWindows::_Imp_Seek(ULONG32 ulSeekTime)
{
return HXR_OK;
}
HX_RESULT CAudioOutWindows::_Imp_Pause()
{
MMRESULT res;
if (m_hWave)
{
// OutputDebugString("BEFORE CALL TO:waveOutRestart\r\n");
res = waveOutPause(m_hWave);
}
return HXR_OK;
}
HX_RESULT CAudioOutWindows::_Imp_Resume()
{
MMRESULT res;
if (m_hWave)
{
// OutputDebugString("BEFORE CALL TO:waveOutRestart\r\n");
res = waveOutRestart(m_hWave);
//OutputDebugString("AFTER CALL TO:waveOutRestart\r\n");
}
OnTimeSync();
return HXR_OK;
}
HX_RESULT CAudioOutWindows::_Imp_Reset()
{
//{FILE* f1 = ::fopen("c:\\audio.txt", "a+"); ::fprintf(f1, "Reset: \n");::fclose(f1);}
MMRESULT res;
m_bResetting = TRUE;
if (m_hWave)
{
// OutputDebugString("BEFORE CALL TO:waveOutReset\r\n");
res = waveOutReset(m_hWave);
// Need to be turned on after RC2000 XXXRA
// commenting it out for safety reasons.
//_Imp_Pause();
//OutputDebugString("AFTER CALL TO:waveOutReset\r\n");
m_bIsFirstPacket = TRUE;
m_llDeviceBytesPlayed = 0;
m_llDeviceSamplesPlayed = 0;
m_ulLastDeviceBytesPlayed = 0;
m_ulLastDeviceSamplesPlayed = 0;
m_ulDevPosRollOver = 0;
}
// First return the unused buffers to the available queue
// Then pump out the wom_dones.
//
_NumberOfBlocksRemainingToPlay();
// Make sure we handle all the wave-done messages,
// but establish a time limit to cope with the
// unpredictable nature of Windows. Wait for up
// to 1 second.
DWORD start = HX_GET_TICKCOUNT();
if (m_hWnd)
{
do
{
MSG msg;
// Yield, and try to pump WOM_DONE's to the Async Window!
while (PeekMessage(&msg, m_hWnd, MM_WOM_DONE, MM_WOM_DONE, PM_REMOVE))
{
if(msg.message == WM_QUIT)
{
// When peeking WM_QUIT message in the main thread of an application
// we have to put it back into message queue to allow the application
// to exit correctly. SB
PostQuitMessage(0);
break;
}
else
{
DispatchMessage(&msg);
}
}
if (CALCULATE_ELAPSED_TICKS(start, HX_GET_TICKCOUNT()) >= 1000)
{
// Brute force, mark them as free... We probably
// could do something better here, like close and
// reopen the wave device... but this is what the
// old code did, so lets try it as well!
ULONG32 lTmp;
m_pMutex->Lock();
for (lTmp = 0; m_pWaveHdrs && (lTmp < m_unAllocedBufferCnt); ++lTmp)
{
CWaveHeader *pwhDone = &m_pWaveHdrs[lTmp];
if (pwhDone && pwhDone->m_bAvail == FALSE)
{
pwhDone->m_bAvail = TRUE;
m_rAvailBuffers.EnQueuePtr(pwhDone);
}
}
m_pMutex->Unlock();
//OutputDebugString("Bail WOM_DONE vacuum, passed timeout!\r\n");
break;
}
}
while (_NumberOfBlocksRemainingToPlay());
}
//OutputDebugString("END: WOM_DONE vacuum!\r\n");
m_bResetting = FALSE;
return HXR_OK;
}
HX_RESULT CAudioOutWindows::_Imp_Drain()
{
return HXR_OK;
}
HX_RESULT CAudioOutWindows::_Imp_CheckFormat
(
const HXAudioFormat* pFormat
)
{
#if defined(_WIN32)
LPWAVEFORMATEX wavePtr;
#elif defined( _WINDOWS )
LPWAVEFORMAT wavePtr;
#endif
m_WaveFormat.SetFormat(pFormat->ulSamplesPerSec, pFormat->uChannels, pFormat->uBitsPerSample);
// Get the Windows style wave format!
wavePtr = m_WaveFormat.GetWaveFormat();
MMRESULT wRet = waveOutOpen(&m_hWave,WAVE_MAPPER,wavePtr,
0, (DWORD)this,WAVE_FORMAT_QUERY);
switch (wRet)
{
case MMSYSERR_NOERROR: return( HXR_OK );
case MMSYSERR_ALLOCATED: return( HXR_AUDIO_DRIVER );
case MMSYSERR_NOMEM: return( HXR_OUTOFMEMORY );
case MMSYSERR_BADDEVICEID: return( HXR_AUDIO_DRIVER );
case WAVERR_BADFORMAT: return( HXR_AUDIO_DRIVER );
case WAVERR_SYNC: return( HXR_AUDIO_DRIVER );
case MMSYSERR_NODRIVER: return( HXR_AUDIO_DRIVER );
default: return( HXR_AUDIO_DRIVER );
}
}
BOOL CAudioOutWindows::AllocateBuffers(UINT16 unNumBuffers, UINT16 unBufSize)
{
UCHAR** ppWaveBuffers;
CWaveHeader* pWaveHdrs;
MMRESULT tRes;
UINT16 unWHIndex;
ppWaveBuffers = NULL;
pWaveHdrs = NULL;
// Allocate memory for wave block headers and for wave data.
if (!(pWaveHdrs = new CWaveHeader [unNumBuffers]))
{
goto OnError;
}
// This array holds the buffer pointers so we can free them later
// There NO other purpose for this array.
if (!(ppWaveBuffers = new UCHAR*[unNumBuffers]))
{
goto OnError;
}
memset( ppWaveBuffers, 0, unNumBuffers * sizeof( UCHAR* ) );
// Mark all wave block headers as available, and point them
// at their respective wave data blocks.
// AND enqueue them in the AvailableBuffers Queue.
// AND allocate their buffers!!!
for (unWHIndex = 0; unWHIndex < unNumBuffers; unWHIndex++)
{
UCHAR* pBuff;
// Try to allocate the individual audio buffers
pBuff = new UCHAR [unBufSize];
if (!pBuff)
{
goto OnError;
}
ppWaveBuffers[unWHIndex] = pBuff;
CWaveHeader* pWaveHeader = &pWaveHdrs[unWHIndex];
// Init some header data before Preparing the header
pWaveHeader->m_WAVEHDR.lpData = (char *)pBuff;
pWaveHeader->m_WAVEHDR.dwUser = DWORD(pWaveHeader);
pWaveHeader->m_WAVEHDR.dwFlags = 0L;
pWaveHeader->m_WAVEHDR.dwBufferLength = unBufSize;
pWaveHeader->m_WAVEHDR.dwLoops = 0L;
pWaveHeader->m_bAvail = TRUE;
pWaveHeader->m_pUser = this;
m_pMutex->Lock();
m_rAvailBuffers.EnQueuePtr(pWaveHeader);
m_pMutex->Unlock();
//OutputDebugString("BEFORE CALL TO:waveOutPrepareHeader\r\n");
tRes = waveOutPrepareHeader(m_hWave, LPWAVEHDR(&pWaveHeader->m_WAVEHDR), sizeof(WAVEHDR));
//OutputDebugString("AFTER CALL TO:waveOutPrepareHeader\r\n");
if (tRes != MMSYSERR_NOERROR)
{
goto OnError;
}
else
{
pWaveHeader->m_pPrepared = TRUE;
}
}
m_unAllocedBufferCnt = unNumBuffers;
m_unAllocedBuffSize = unBufSize;
m_pWaveHdrs = pWaveHdrs;
m_ppAllocedBuffers = ppWaveBuffers;
return TRUE;
OnError:
if (pWaveHdrs)
{
// Unprepare the individual headers
for (unWHIndex = 0; unWHIndex < unNumBuffers; ++unWHIndex)
{
CWaveHeader* pWaveHeader = &pWaveHdrs[unWHIndex];
if (pWaveHeader->m_pPrepared)
{
//OutputDebugString("BEFORE CALL TO:waveOutUnprepareHeader\r\n");
tRes = waveOutUnprepareHeader(m_hWave,
LPWAVEHDR(&pWaveHeader->m_WAVEHDR), sizeof(WAVEHDR));
//OutputDebugString("AFTER CALL TO:waveOutUnprepareHeader\r\n");
pWaveHeader->m_pPrepared = FALSE;
}
}
delete[] pWaveHdrs;
pWaveHdrs = NULL;
}
// ppWaveBuffers actually points at an array of pointers that need to be free'd
if (ppWaveBuffers)
{
// Free the individual buffers
for (unWHIndex = 0; unWHIndex < unNumBuffers; ++unWHIndex)
{
UCHAR* pBuff;
pBuff = ppWaveBuffers[unWHIndex];
if (pBuff)
{
delete[] pBuff;
}
}
// Now free ppWaveBuffers itself
delete[] ppWaveBuffers;
ppWaveBuffers = NULL;
}
return FALSE;
}
/************************************************************************
* Method:
* CAudioOutWindows::_Imp_GetCurrentTime
* Purpose:
* Get the current time from the audio device.
* We added this to support the clock available in the
* Window's audio driver.
*/
HX_RESULT CAudioOutWindows::_Imp_GetCurrentTime
(
ULONG32& ulCurrentTime
)
{
ULONG32 ulDeviceTime = 0;
ULONG32 ulDeviceBytesPlayed = 0;
UINT32 ulDeviceSamplesPlayed = 0;
WAVEFMTPTR pWFmt = m_WaveFormat.GetWaveFormat();
// Set the time to the playback time of the
// wave device since it's the most accurate.
MMTIME mmTime;
// we want MS if we can get them
mmTime.wType = TIME_MS;
MMRESULT res = waveOutGetPosition(m_hWave, &mmTime, sizeof(MMTIME));
if (MMSYSERR_NOERROR == res)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -