📄 pa_win_wmme.c
字号:
stream->past_StopNow = 0;
stream->past_IsActive = 1;
wmmeStreamData->framesPlayed = 0.0;
wmmeStreamData->lastPosition = 0;
#if PA_TRACE_START_STOP
AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result );
#endif
#if PA_USE_TIMER_CALLBACK
/* Create timer that will wake us up so we can fill the DSound buffer. */
bufsPerTimerCallback = wmmeStreamData->numHostBuffers/4;
if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1;
if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1;
msecPerBuffer = (1000 * bufsPerTimerCallback *
wmmeStreamData->userBuffersPerHostBuffer *
internalStream->past_FramesPerUserBuffer ) / (int) internalStream->past_SampleRate;
if( msecPerBuffer < 10 ) msecPerBuffer = 10;
else if( msecPerBuffer > 100 ) msecPerBuffer = 100;
resolution = msecPerBuffer/4;
wmmeStreamData->timerID = timeSetEvent( msecPerBuffer, resolution,
(LPTIMECALLBACK) Pa_TimerCallback,
(DWORD) stream, TIME_PERIODIC );
if( wmmeStreamData->timerID == 0 )
{
result = paHostError;
sPaHostError = GetLastError();;
goto error;
}
#else /* PA_USE_TIMER_CALLBACK */
ResetEvent( wmmeStreamData->abortEvent );
/* Create thread that waits for audio buffers to be ready for processing. */
wmmeStreamData->engineThread = CreateThread( 0, 0, WinMMPa_OutputThreadProc, stream, 0, &wmmeStreamData->engineThreadID );
if( wmmeStreamData->engineThread == NULL )
{
result = paHostError;
sPaHostError = GetLastError();;
goto error;
}
#if PA_TRACE_START_STOP
AddTraceMessage( "PaHost_StartEngine: thread ", (int) wmmeStreamData->engineThread );
#endif
/* I used to pass the thread which was failing. I now pass GetCurrentProcess().
* This fix could improve latency for some applications. It could also result in CPU
* starvation if the callback did too much processing.
* I also added result checks, so we might see more failures at initialization.
* Thanks to Alberto di Bene for spotting this.
*/
if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) ) /* PLB20010816 */
{
result = paHostError;
sPaHostError = GetLastError();;
goto error;
}
if( !SetThreadPriority( wmmeStreamData->engineThread, THREAD_PRIORITY_HIGHEST ) )
{
result = paHostError;
sPaHostError = GetLastError();;
goto error;
}
#endif
error:
return result;
}
/*************************************************************************/
PaError PaHost_StopEngine( internalPortAudioStream *internalStream, int abort )
{
int timeOut;
PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream );
if( wmmeStreamData == NULL ) return paNoError;
/* Tell background thread to stop generating more data and to let current data play out. */
internalStream->past_StopSoon = 1;
/* If aborting, tell background thread to stop NOW! */
if( abort ) internalStream->past_StopNow = 1;
/* Calculate timeOut longer than longest time it could take to play all buffers. */
timeOut = (DWORD) (1500.0 * PaHost_GetTotalBufferFrames( internalStream ) / internalStream->past_SampleRate);
if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC;
#if PA_USE_TIMER_CALLBACK
if( (internalStream->past_OutputDeviceID != paNoDevice) &&
internalStream->past_IsActive &&
(wmmeStreamData->timerID != 0) )
{
/* Wait for IsActive to drop. */
while( (internalStream->past_IsActive) && (timeOut > 0) )
{
Sleep(10);
timeOut -= 10;
}
timeKillEvent( wmmeStreamData->timerID ); /* Stop callback timer. */
wmmeStreamData->timerID = 0;
}
#else /* PA_USE_TIMER_CALLBACK */
#if PA_TRACE_START_STOP
AddTraceMessage( "PaHost_StopEngine: thread ", (int) wmmeStreamData->engineThread );
#endif
if( (internalStream->past_OutputDeviceID != paNoDevice) &&
(internalStream->past_IsActive) &&
(wmmeStreamData->engineThread != NULL) )
{
DWORD got;
/* Tell background thread to stop generating more data and to let current data play out. */
DBUG(("PaHost_StopEngine: waiting for background thread.\n"));
got = WaitForSingleObject( wmmeStreamData->engineThread, timeOut );
if( got == WAIT_TIMEOUT )
{
ERR_RPT(("PaHost_StopEngine: timed out while waiting for background thread to finish.\n"));
return paTimedOut;
}
CloseHandle( wmmeStreamData->engineThread );
wmmeStreamData->engineThread = NULL;
}
#endif /* PA_USE_TIMER_CALLBACK */
internalStream->past_IsActive = 0;
return paNoError;
}
/*************************************************************************/
PaError PaHost_StopInput( internalPortAudioStream *stream, int abort )
{
MMRESULT mmresult;
PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream );
if( wmmeStreamData == NULL ) return paNoError; /* FIXME: why return paNoError? */
(void) abort; /* unused parameter */
if( wmmeStreamData->hWaveIn != NULL )
{
mmresult = waveInReset( wmmeStreamData->hWaveIn );
if( mmresult != MMSYSERR_NOERROR )
{
sPaHostError = mmresult;
return paHostError;
}
}
return paNoError;
}
/*************************************************************************/
PaError PaHost_StopOutput( internalPortAudioStream *internalStream, int abort )
{
MMRESULT mmresult;
PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream );
if( wmmeStreamData == NULL ) return paNoError; /* FIXME: why return paNoError? */
(void) abort; /* unused parameter */
#if PA_TRACE_START_STOP
AddTraceMessage( "PaHost_StopOutput: hWaveOut ", (int) wmmeStreamData->hWaveOut );
#endif
if( wmmeStreamData->hWaveOut != NULL )
{
mmresult = waveOutReset( wmmeStreamData->hWaveOut );
if( mmresult != MMSYSERR_NOERROR )
{
sPaHostError = mmresult;
return paHostError;
}
}
return paNoError;
}
/*******************************************************************/
PaError PaHost_CloseStream( internalPortAudioStream *stream )
{
int i;
PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream );
if( stream == NULL ) return paBadStreamPtr;
if( wmmeStreamData == NULL ) return paNoError; /* FIXME: why return no error? */
#if PA_TRACE_START_STOP
AddTraceMessage( "PaHost_CloseStream: hWaveOut ", (int) wmmeStreamData->hWaveOut );
#endif
/* Free data and device for output. */
if( wmmeStreamData->hWaveOut )
{
if( wmmeStreamData->outputBuffers )
{
for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
{
waveOutUnprepareHeader( wmmeStreamData->hWaveOut, &wmmeStreamData->outputBuffers[i], sizeof(WAVEHDR) );
PaHost_FreeTrackedMemory( wmmeStreamData->outputBuffers[i].lpData ); /* MEM */
}
PaHost_FreeTrackedMemory( wmmeStreamData->outputBuffers ); /* MEM */
}
waveOutClose( wmmeStreamData->hWaveOut );
}
/* Free data and device for input. */
if( wmmeStreamData->hWaveIn )
{
if( wmmeStreamData->inputBuffers )
{
for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
{
waveInUnprepareHeader( wmmeStreamData->hWaveIn, &wmmeStreamData->inputBuffers[i], sizeof(WAVEHDR) );
PaHost_FreeTrackedMemory( wmmeStreamData->inputBuffers[i].lpData ); /* MEM */
}
PaHost_FreeTrackedMemory( wmmeStreamData->inputBuffers ); /* MEM */
}
waveInClose( wmmeStreamData->hWaveIn );
}
#if (PA_USE_TIMER_CALLBACK == 0)
if( wmmeStreamData->abortEventInited ) CloseHandle( wmmeStreamData->abortEvent );
if( wmmeStreamData->bufferEventInited ) CloseHandle( wmmeStreamData->bufferEvent );
#endif
if( wmmeStreamData->streamLockInited )
DeleteCriticalSection( &wmmeStreamData->streamLock );
PaHost_FreeWMMEStreamData( stream );
return paNoError;
}
/*************************************************************************
* Determine minimum number of buffers required for this host based
* on minimum latency. Latency can be optionally set by user by setting
* an environment variable. For example, to set latency to 200 msec, put:
*
* set PA_MIN_LATENCY_MSEC=200
*
* in the AUTOEXEC.BAT file and reboot.
* If the environment variable is not set, then the latency will be determined
* based on the OS. Windows NT has higher latency than Win95.
*/
#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
{
char envbuf[PA_ENV_BUF_SIZE];
DWORD hresult;
int minLatencyMsec = 0;
double msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate;
int minBuffers;
/* Let user determine minimal latency by setting environment variable. */
hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
{
minLatencyMsec = atoi( envbuf ); /* REVIEW: will we crash if the environment variable contains some nasty value? */
}
else
{
/* Set minimal latency based on whether NT or other OS.
* NT has higher latency.
*/
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof( osvi );
GetVersionEx( &osvi );
DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
/* Check for NT */
if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
{
minLatencyMsec = PA_WIN_NT_LATENCY;
}
else if(osvi.dwMajorVersion >= 5)
{
minLatencyMsec = PA_WIN_WDM_LATENCY;
}
else
{
minLatencyMsec = PA_WIN_9X_LATENCY;
}
#if PA_USE_HIGH_LATENCY
PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
#endif
}
DBUG(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer));
if( minBuffers < 2 ) minBuffers = 2;
return minBuffers;
}
/*************************************************************************
* Cleanup device info.
*/
PaError PaHost_Term( void )
{
int i;
if( sNumDevices > 0 )
{
if( sDevicePtrs != NULL )
{
for( i=0; i<sNumDevices; i++ )
{
if( sDevicePtrs[i] != NULL )
{
PaHost_FreeTrackedMemory( (char*)sDevicePtrs[i]->name ); /* MEM */
PaHost_FreeTrackedMemory( (void*)sDevicePtrs[i]->sampleRates ); /* MEM */
PaHost_FreeTrackedMemory( sDevicePtrs[i] ); /* MEM */
}
}
PaHost_FreeTrackedMemory( sDevicePtrs ); /* MEM */
sDevicePtrs = NULL;
}
sNumDevices = 0;
}
#if PA_TRACK_MEMORY
PRINT(("PaHost_Term: sNumAllocations = %d\n", sNumAllocations ));
#endif
return paNoError;
}
/*************************************************************************/
void Pa_Sleep( long msec )
{
Sleep( msec );
}
/*************************************************************************
FIXME: the following memory allocation routines should not be declared here
* Allocate memory that can be accessed in real-time.
* This may need to be held in physical memory so that it is not
* paged to virtual memory.
* This call MUST be balanced with a call to PaHost_FreeFastMemory().
* Memory will be set to zero.
*/
void *PaHost_AllocateFastMemory( long numBytes )
{
return PaHost_AllocateTrackedMemory( numBytes ); /* FIXME - do we need physical memory? Use VirtualLock() */ /* MEM */
}
/*************************************************************************
* Free memory that could be accessed in real-time.
* This call MUST be
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -