📄 pa_win_wmme.c
字号:
return NULL;
}
/*************************************************************************
** Returns recommended device ID.
** On the PC, the recommended device can be specified by the user by
** setting an environment variable. For example, to use device #1.
**
** set PA_RECOMMENDED_OUTPUT_DEVICE=1
**
** The user should first determine the available device ID by using
** the supplied application "pa_devs".
*/
#define PA_ENV_BUF_SIZE (32)
#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE")
#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE")
static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName )
{
DWORD hresult;
char envbuf[PA_ENV_BUF_SIZE];
PaDeviceID recommendedID = paNoDevice;
/* Let user determine default device by setting environment variable. */
/* hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE ); */
/* GJG20020928 - Windows CE does not support environment variables */
hresult=0;
if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
{
recommendedID = atoi( envbuf );
}
// return recommendedID;
return -1; /* GJG20021013 */
}
static PaError Pa_MaybeQueryDevices( void )
{
if( sNumDevices == 0 )
{
return Pa_QueryDevices();
}
return 0;
}
/**********************************************************************
** Check for environment variable, else query devices and use result.
*/
PaDeviceID Pa_GetDefaultInputDeviceID( void )
{
PaError result;
result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
if( result < 0 )
{
result = Pa_MaybeQueryDevices();
if( result < 0 ) return result;
result = sDefaultInputDeviceID;
}
return result;
}
PaDeviceID Pa_GetDefaultOutputDeviceID( void )
{
PaError result;
result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
if( result < 0 )
{
result = Pa_MaybeQueryDevices();
if( result < 0 ) return result;
result = sDefaultOutputDeviceID;
}
return result;
}
/**********************************************************************
** Initialize Host dependant part of API.
*/
PaError PaHost_Init( void )
{
#if PA_SIMULATE_UNDERFLOW
PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
#endif
return Pa_MaybeQueryDevices();
}
/**********************************************************************
** Check WAVE buffers to see if they are done.
** Fill any available output buffers and use any available
** input buffers by calling user callback.
*/
static PaError Pa_TimeSlice( internalPortAudioStream *past )
{
PaError result = 0;
long bytesEmpty = 0;
long bytesFilled = 0;
long buffersEmpty = 0;
MMRESULT mresult;
char *inBufPtr;
char *outBufPtr;
int gotInput = 0;
int gotOutput = 0;
int i;
int buffersProcessed = 0;
int done = 0;
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return paInternalError;
past->past_NumCallbacks += 1;
#if PA_SIMULATE_UNDERFLOW
if(gUnderCallbackCounter++ == UNDER_SLEEP_AT)
{
Sleep(UNDER_SLEEP_FOR);
}
#endif
#if PA_TRACE_RUN
AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", past->past_NumCallbacks );
#endif
while(!done)
{
/* If we are using output, then we need an empty output buffer. */
gotOutput = 0;
outBufPtr = NULL;
if( past->past_NumOutputChannels > 0 )
{
if((pahsc->pahsc_OutputBuffers[ pahsc->pahsc_CurrentOutputBuffer ].dwFlags & WHDR_DONE) == 0)
{
break; /* If none empty then bail and try again later. */
}
else
{
outBufPtr = pahsc->pahsc_OutputBuffers[ pahsc->pahsc_CurrentOutputBuffer ].lpData;
gotOutput = 1;
}
}
/* Use an input buffer if one is available. */
gotInput = 0;
inBufPtr = NULL;
if( ( past->past_NumInputChannels > 0 ) &&
(pahsc->pahsc_InputBuffers[ pahsc->pahsc_CurrentInputBuffer ].dwFlags & WHDR_DONE) )
{
inBufPtr = pahsc->pahsc_InputBuffers[ pahsc->pahsc_CurrentInputBuffer ].lpData;
gotInput = 1;
#if PA_TRACE_RUN
AddTraceMessage("Pa_TimeSlice: got input buffer at ", (int)inBufPtr );
AddTraceMessage("Pa_TimeSlice: got input buffer # ", pahsc->pahsc_CurrentInputBuffer );
#endif
}
/* If we can't do anything then bail out. */
if( !gotInput && !gotOutput ) break;
buffersProcessed += 1;
/* Each Wave buffer contains multiple user buffers so do them all now. */
/* Base Usage on time it took to process one host buffer. */
Pa_StartUsageCalculation( past );
for( i=0; i<pahsc->pahsc_UserBuffersPerHostBuffer; i++ )
{
if( done )
{
if( gotOutput )
{
/* Clear remainder of wave buffer if we are waiting for stop. */
AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i );
memset( outBufPtr, 0, pahsc->pahsc_BytesPerUserOutputBuffer );
}
}
else
{
/* Convert 16 bit native data to user data and call user routine. */
result = Pa_CallConvertInt16( past, (short *) inBufPtr, (short *) outBufPtr );
if( result != 0) done = 1;
}
if( gotInput ) inBufPtr += pahsc->pahsc_BytesPerUserInputBuffer;
if( gotOutput) outBufPtr += pahsc->pahsc_BytesPerUserOutputBuffer;
}
Pa_EndUsageCalculation( past );
/* Send WAVE buffer to Wave Device to be refilled. */
if( gotInput )
{
mresult = waveInAddBuffer( pahsc->pahsc_HWaveIn,
&pahsc->pahsc_InputBuffers[ pahsc->pahsc_CurrentInputBuffer ],
sizeof(WAVEHDR) );
if( mresult != MMSYSERR_NOERROR )
{
sPaHostError = mresult;
result = paHostError;
break;
}
pahsc->pahsc_CurrentInputBuffer = (pahsc->pahsc_CurrentInputBuffer+1 >= pahsc->pahsc_NumHostBuffers) ?
0 : pahsc->pahsc_CurrentInputBuffer+1;
}
/* Write WAVE buffer to Wave Device. */
if( gotOutput )
{
#if PA_TRACE_START_STOP
AddTraceMessage( "Pa_TimeSlice: writing buffer ", pahsc->pahsc_CurrentOutputBuffer );
#endif
mresult = waveOutWrite( pahsc->pahsc_HWaveOut,
&pahsc->pahsc_OutputBuffers[ pahsc->pahsc_CurrentOutputBuffer ],
sizeof(WAVEHDR) );
if( mresult != MMSYSERR_NOERROR )
{
sPaHostError = mresult;
result = paHostError;
break;
}
pahsc->pahsc_CurrentOutputBuffer = (pahsc->pahsc_CurrentOutputBuffer+1 >= pahsc->pahsc_NumHostBuffers) ?
0 : pahsc->pahsc_CurrentOutputBuffer+1;
}
}
#if PA_TRACE_RUN
AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed );
#endif
return (result != 0) ? result : done;
}
/*******************************************************************/
static PaError PaHost_BackgroundManager( internalPortAudioStream *past )
{
PaError result = 0;
int i;
int numQueuedOutputBuffers = 0;
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
/* Has someone asked us to abort by calling Pa_AbortStream()? */
if( past->past_StopNow )
{
past->past_IsActive = 0; /* Will cause thread to return. */
}
/* Has someone asked us to stop by calling Pa_StopStream()
* OR has a user callback returned '1' to indicate finished.
*/
else if( past->past_StopSoon )
{
/* Poll buffer and when all have played then exit thread. */
/* Count how many output buffers are queued. */
numQueuedOutputBuffers = 0;
if( past->past_NumOutputChannels > 0 )
{
for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
{
if( !( pahsc->pahsc_OutputBuffers[ i ].dwFlags & WHDR_DONE) )
{
#if PA_TRACE_START_STOP
AddTraceMessage( "WinMMPa_OutputThreadProc: waiting for buffer ", i );
#endif
numQueuedOutputBuffers++;
}
}
}
#if PA_TRACE_START_STOP
AddTraceMessage( "WinMMPa_OutputThreadProc: numQueuedOutputBuffers ", numQueuedOutputBuffers );
#endif
if( numQueuedOutputBuffers == 0 )
{
past->past_IsActive = 0; /* Will cause thread to return. */
}
}
else
{
/* Process full input buffer and fill up empty output buffers. */
if( (result = Pa_TimeSlice( past )) != 0)
{
/* User callback has asked us to stop. */
#if PA_TRACE_START_STOP
AddTraceMessage( "WinMMPa_OutputThreadProc: TimeSlice() returned ", result );
#endif
past->past_StopSoon = 1; /* Request that audio play out then stop. */
result = paNoError;
}
}
PaHost_UpdateStreamTime( pahsc );
return result;
}
#if PA_USE_TIMER_CALLBACK
/*******************************************************************/
static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
internalPortAudioStream *past;
PaHostSoundControl *pahsc;
PaError result;
past = (internalPortAudioStream *) dwUser;
if( past == NULL ) return;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return;
if( pahsc->pahsc_IfInsideCallback )
{
if( pahsc->pahsc_TimerID != 0 )
{
timeKillEvent(pahsc->pahsc_TimerID); /* Stop callback timer. */
pahsc->pahsc_TimerID = 0;
}
return;
}
pahsc->pahsc_IfInsideCallback = 1;
/* Manage flags and audio processing. */
result = PaHost_BackgroundManager( past );
if( result != paNoError )
{
past->past_IsActive = 0;
}
pahsc->pahsc_IfInsideCallback = 0;
}
#else /* PA_USE_TIMER_CALLBACK */
/*******************************************************************/
static DWORD WINAPI WinMMPa_OutputThreadProc( void *pArg )
{
internalPortAudioStream *past;
PaHostSoundControl *pahsc;
void *inputBuffer=NULL;
HANDLE events[2];
int numEvents = 0;
DWORD result = 0;
DWORD waitResult;
DWORD numTimeouts = 0;
DWORD timeOut;
past = (internalPortAudioStream *) pArg;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
#if PA_TRACE_START_STOP
AddTraceMessage( "WinMMPa_OutputThreadProc: timeoutPeriod", timeoutPeriod );
AddTraceMessage( "WinMMPa_OutputThreadProc: past_NumUserBuffers", past->past_NumUserBuffers );
#endif
/* Calculate timeOut as half the time it would take to play all buffers. */
timeOut = (DWORD) (500.0 * PaHost_GetTotalBufferFrames( past ) / past->past_SampleRate);
/* Get event(s) ready for wait. */
events[numEvents++] = pahsc->pahsc_BufferEvent;
if( pahsc->pahsc_AbortEventInited ) events[numEvents++] = pahsc->pahsc_AbortEvent;
/* Stay in this thread as long as we are "active". */
while( past->past_IsActive )
{
/*******************************************************************/
/******** WAIT here for an event from WMME or PA *******************/
/*******************************************************************/
waitResult = WaitForMultipleObjects( numEvents, events, FALSE, timeOut );
/* Error? */
if( waitResult == WAIT_FAILED )
{
sPaHostError = GetLastError();
result = paHostError;
past->past_IsActive = 0;
}
/* Timeout? Don't stop. Just keep polling for DONE.*/
else if( waitResult == WAIT_TIMEOUT )
{
#if PA_TRACE_START_STOP
AddTraceMessage( "WinMMPa_OutputThreadProc: timed out ", numQueuedOutputBuffers );
#endif
numTimeouts += 1;
}
/* Manage flags and audio processing. */
result = PaHost_BackgroundManager( past );
if( result != paNoError )
{
past->past_IsActive = 0;
}
}
return result;
}
#endif
/*******************************************************************/
PaError PaHost_OpenInputStream( internalPortAudioStream *past )
{
MMRESULT mr;
PaError result = paNoError;
PaHostSoundControl *pahsc;
int i;
int inputMmId;
int bytesPerInputFrame;
WAVEFORMATEX wfx;
const PaDeviceInfo *pad;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID));
pad = Pa_GetDeviceInfo( past->past_InputDeviceID );
if( pad == NULL ) return paInternalError;
switch( pad->nativeSampleFormats )
{
case paInt32:
case paFloat32:
bytesPerInputFrame = sizeof(float) * past->past_NumInputChannels;
break;
default:
bytesPerInputFrame = sizeof(short) * past->past_NumInputChannels;
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -