📄 sound.cpp
字号:
// If this value is Zero, the soundcard is opened and
// then closed. This is useful for checking the
// soundcard.
// SampleLimit = limit on number of samples to be written. 0 signifies
// continuous output stream.
// card = Which soundcard to use(0 to n-1) (-1 lets Windows decide)
// returns:
// 0 if opened OK
// ErrorCode if not
//////////////////////////////////////////////////////////////////////
UINT CSound::OutOpen( WAVEFORMATEX* pWFX, DWORD BufSize, DWORD SampleLimit, INT card)
{
m_OutWaiting = FALSE;
m_OutUnderflow = FALSE;
m_OutHeadPtr = 0;
m_OutTailPtr = 0;
m_OutSampleLimit = SampleLimit;
m_OutSamplesWritten = 0;
m_OutBufPosition = 0;
m_OutFormat = *pWFX;
m_OutBytesPerSample = (m_OutFormat.wBitsPerSample/8)*m_OutFormat.nChannels;
m_OutBufferSize = BufSize * m_OutBytesPerSample;
// Event for callback function to signal next buffer is free
m_OutEventHandle = CreateEvent(NULL, FALSE,FALSE,NULL);
if( (m_ErrorCode = waveOutOpen( &m_hwvout, card , pWFX,
(DWORD)&WaveOutCallback, (DWORD)this, CALLBACK_FUNCTION ) )
!= MMSYSERR_NOERROR )
{
OutClose(); // m_ErrorCode = MMSYSERR_xxx
return m_ErrorCode;
}
if( BufSize == 0 ) // see if just a soundcard check
{
InClose(); // if so close the soundcard input
return 0;
}
// start out paused so don't let output begin until some buffers are filled.
if( (m_ErrorCode = waveOutPause( m_hwvout ))!= MMSYSERR_NOERROR )
{
OutClose(); // m_ErrorCode = MMSYSERR_xxx
return m_ErrorCode;
}
// allocate and prepare all the output buffers
for(INT i=0; i<NUM_OUTPUT_SNDBUFFERS; i++ )
{
// allocate output buffer headers
m_pOutputBuffer[i] = NULL;
if( (m_pOutputBuffer[i] = new WAVEHDR[ sizeof(WAVEHDR)] ) == NULL )
{
OutClose();
m_ErrorCode = MEMORY_ERROR;
return m_ErrorCode;
}
// allocate output data buffers
m_pOutputBuffer[i]->dwBufferLength = m_OutBufferSize;
m_pOutputBuffer[i]->dwFlags = 0;
m_pOutputBuffer[i]->dwUser = NULL;
m_pOutputBuffer[i]->dwLoops = NULL;
if( (m_pOutputBuffer[i]->lpData = new char[m_OutBufferSize] ) == NULL )
{
OutClose();
m_ErrorCode = MEMORY_ERROR;
return m_ErrorCode;
}
if( (m_ErrorCode = waveOutPrepareHeader(m_hwvout, m_pOutputBuffer[i],
sizeof *m_pOutputBuffer[i]) ) != MMSYSERR_NOERROR )
{
OutClose(); // m_ErrorCode = MMSYSERR_xxx
return m_ErrorCode;
}
}
m_OutputOpen = TRUE;
m_fOutputHoldoff = TRUE;
return(0);
}
///////////////////////////////////////////////////////////////////////
// Writes 'Length' double's to the soundcard output.
// parameters:
// pData = pointer to block of 'Length' double's to output.
// Length = Number of samples to write from pData. If is zero
// then the sound output is flushed and closed.
// Returns:
// 'Length' if data was succesfully placed in the output buffers.
// 0 if output has finished( reached the specified sample limit )
// -1 if error ( use GetError() to retrieve error )
///////////////////////////////////////////////////////////////////////
LONG CSound::OutWrite( double* pData, INT Length )
{
INT i;
if( !m_OutputOpen ) // return -1 if output not running.
{
m_ErrorCode = SOUNDOUT_ERR_NOTOPEN;
return -1;
}
if( m_OutUnderflow )
{
m_ErrorCode = SOUNDOUT_ERR_UNDERFLOW;
OutClose();
return -1; // return -1 if underflow
}
if( Length == 0 ) // need to flush partially filled buffer
{ // and exit
OutClose();
if(m_ErrorCode == NO_ERRORS )
return Length;
else
return -1;
}else // here to add new data to soundcard buffer queue
{
if(m_OutFormat.wBitsPerSample == 16)
{
for( i=0; i < (Length*m_OutFormat.nChannels); i++ )
{
m_usTemp.both = (SHORT)(*(pData + i));
*(m_pOutputBuffer[m_OutHeadPtr]->lpData
+ (m_OutBufPosition++) ) = m_usTemp.bytes.lsb;
*(m_pOutputBuffer[m_OutHeadPtr]->lpData
+ (m_OutBufPosition++) ) = m_usTemp.bytes.msb;
}
}
else
{
for( i=0; i < (Length*m_OutFormat.nChannels); i++ )
{
m_usTemp.both = (SHORT)(*(pData + i) + 32768.0);
*( m_pOutputBuffer[m_OutHeadPtr]->lpData + (m_OutBufPosition++) )
= m_usTemp.bytes.msb;
}
}
if( m_OutBufPosition >= m_OutBufferSize ) //send it if full
{
waveOutWrite( m_hwvout, m_pOutputBuffer[m_OutHeadPtr],
sizeof *m_pOutputBuffer[m_OutHeadPtr] );
m_OutBufPosition = 0;
if( m_fOutputHoldoff ) // holdoff logic doesn't start sound
{ // until half the buffers are full
if( m_OutHeadPtr >= NUM_OUTPUT_SNDBUFFERS/2 )
{
m_fOutputHoldoff = FALSE;
waveOutRestart( m_hwvout );
}
}
EnterCriticalSection(&m_CriticalSection);
if( ++m_OutHeadPtr >= NUM_OUTPUT_SNDBUFFERS) // handle wrap around
m_OutHeadPtr = 0;
if( m_OutHeadPtr == m_OutTailPtr) //if all buffers full then need to wait
{
// wait for mmsystem to free up a new buffer
m_OutWaiting = TRUE;
LeaveCriticalSection(&m_CriticalSection);
if( WaitForSingleObject( m_OutEventHandle,TIMELIMIT )
!= WAIT_OBJECT_0 )
{
m_ErrorCode = SOUNDOUT_ERR_TIMEOUT;
OutClose();
return -1;
}
}
else
LeaveCriticalSection(&m_CriticalSection);
}
m_OutSamplesWritten += Length;
if( (m_OutSampleLimit != 0) && (m_OutSamplesWritten >= m_OutSampleLimit) )
{
OutClose();
if(m_ErrorCode == NO_ERRORS )
return 0;
else
return -1;
}
return Length; // return number Samples accepted OK
}
}
//////////////////////////////////////////////////////////////////////
// Private function that waits for all the output buffers to finish
// outputing. Returns zero if OK else error code.
//////////////////////////////////////////////////////////////////////
UINT CSound::WaitForFlush()
{
EnterCriticalSection(&m_CriticalSection);
while( m_OutHeadPtr != m_OutTailPtr )
{ // wait for all buffers to finish
// wait for mmsystem to free up a new buffer
m_OutWaiting = TRUE;
LeaveCriticalSection(&m_CriticalSection);
if( WaitForSingleObject( m_OutEventHandle,TIMELIMIT )
!= WAIT_OBJECT_0 )
{
m_ErrorCode = SOUNDOUT_ERR_TIMEOUT;
return m_ErrorCode;
}
EnterCriticalSection(&m_CriticalSection);
}
LeaveCriticalSection(&m_CriticalSection);
return m_ErrorCode;
}
//////////////////////////////////////////////////////////////////////
// Closes the Soundcard Output if open.
//////////////////////////////////////////////////////////////////////
void CSound::OutClose()
{
m_OutputOpen = FALSE;
if(m_hwvout != NULL)
{
if( !m_ErrorCode && (m_OutBufPosition > 0) )
{ // flush out all remaining data in local buffers
waveOutRestart( m_hwvout ); //make sure soundcard is running
m_pOutputBuffer[m_OutHeadPtr]->dwBufferLength = m_OutBufPosition;
waveOutWrite( m_hwvout, m_pOutputBuffer[m_OutHeadPtr],
sizeof *m_pOutputBuffer[m_OutHeadPtr] );
if( ++m_OutHeadPtr >= NUM_OUTPUT_SNDBUFFERS) // handle wrap around
m_OutHeadPtr = 0;
}
m_ErrorCode = WaitForFlush(); //wait to finish
Sleep(50);
waveOutReset(m_hwvout);
Sleep(50);
for(INT i=0; i<NUM_OUTPUT_SNDBUFFERS; i++ )
{
if( m_pOutputBuffer[i] )
{
if( m_pOutputBuffer[i]->lpData != NULL )
{
if( m_pOutputBuffer[i]->dwFlags&WHDR_PREPARED )
{
waveOutUnprepareHeader(m_hwvout, m_pOutputBuffer[i],
sizeof *m_pOutputBuffer[i]);
}
if( m_pOutputBuffer[i]->lpData )
{
delete m_pOutputBuffer[i]->lpData;
m_pOutputBuffer[i]->lpData = NULL;
}
}
if( m_pOutputBuffer[i] )
{
delete m_pOutputBuffer[i];
m_pOutputBuffer[i] = NULL;
}
}
}
waveOutClose(m_hwvout);
Sleep(50);
m_hwvout = NULL;
}
if(m_OutEventHandle)
{
CloseHandle(m_OutEventHandle);
m_OutEventHandle = NULL;
}
}
////////////////////////////////////////////////////////////
// callback Routine called by mmsystem when a buffer becomes full.
// no system calls except SetEvent may be called from here.
// ( not a member function of CSound Class )
////////////////////////////////////////////////////////////
void CALLBACK WaveInCallback( HWAVEIN m_hwvin, UINT uMsg, CSound* pCSound,
DWORD dwParam1, DWORD dwParam2 )
{
if( uMsg == MM_WIM_DATA ) // ignore all but buff full messages
{
EnterCriticalSection(&pCSound->m_CriticalSection);
if( ++pCSound->m_InHeadPtr >= NUM_INPUT_SNDBUFFERS) //inc ptr
pCSound->m_InHeadPtr = 0; // handle wrap around
if( pCSound->m_InHeadPtr == pCSound->m_InTailPtr ) //chk for overflo
pCSound->m_InOverflow = TRUE;
if(pCSound->m_InWaiting) //if user thread is waiting for buffer
{
pCSound->m_InWaiting = FALSE;
SetEvent( pCSound->m_InEventHandle); //signal it
}
LeaveCriticalSection(&pCSound->m_CriticalSection);
}
}
////////////////////////////////////////////////////////////
// callback Routine called by mmsystem when a buffer becomes empty.
// no system calls except SetEvent may be called from here.
// ( not a member function of CSound Class )
////////////////////////////////////////////////////////////
void CALLBACK WaveOutCallback( HWAVEOUT m_hwvout, UINT uMsg, CSound* pCSound,
DWORD dwParam1, DWORD dwParam2 )
{
if( uMsg == WOM_DONE ) // ignore all but buffer empty messages
{
EnterCriticalSection(&pCSound->m_CriticalSection);
if( ++pCSound->m_OutTailPtr >= NUM_INPUT_SNDBUFFERS) //inc ptr
pCSound->m_OutTailPtr = 0; // handle wrap around
if( pCSound->m_OutHeadPtr == pCSound->m_OutTailPtr ) //chk for underflo
pCSound->m_OutUnderflow = TRUE;
if(pCSound->m_OutWaiting) //if user thread is waiting for buffer
{
pCSound->m_OutWaiting = FALSE;
SetEvent( pCSound->m_OutEventHandle);
}
LeaveCriticalSection(&pCSound->m_CriticalSection);
}
}
///////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -