📄 pa_dsound.c
字号:
if( sNumDevices <= 0 ) Pa_Initialize();
return sNumDevices;
}
static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id )
{
if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
return &sDevices[id];
}
/*************************************************************************/
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
{
internalPortAudioDevice *pad;
if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
pad = Pa_GetInternalDevice( id );
return &pad->pad_Info ;
}
static PaError Pa_MaybeQueryDevices( void )
{
if( sNumDevices == 0 )
{
return Pa_QueryDevices();
}
return 0;
}
/*************************************************************************
** 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 );
if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
{
recommendedID = atoi( envbuf );
}
return recommendedID;
}
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;
}
/**********************************************************************
** Make sure that we have queried the device capabilities.
*/
PaError PaHost_Init( void )
{
#if PA_SIMULATE_UNDERFLOW
PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
#endif
return Pa_MaybeQueryDevices();
}
static PaError Pa_TimeSlice( internalPortAudioStream *past )
{
PaError result = 0;
long bytesEmpty = 0;
long bytesFilled = 0;
long bytesToXfer = 0;
long numChunks;
HRESULT hresult;
PaHostSoundControl *pahsc;
short *nativeBufPtr;
past->past_NumCallbacks += 1;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return paInternalError;
/* How much input data is available? */
#if SUPPORT_AUDIO_CAPTURE
if( past->past_NumInputChannels > 0 )
{
DSW_QueryInputFilled( &pahsc->pahsc_DSoundWrapper, &bytesFilled );
bytesToXfer = bytesFilled;
}
#endif /* SUPPORT_AUDIO_CAPTURE */
/* How much output room is available? */
if( past->past_NumOutputChannels > 0 )
{
DSW_QueryOutputSpace( &pahsc->pahsc_DSoundWrapper, &bytesEmpty );
bytesToXfer = bytesEmpty;
}
AddTraceMessage( "bytesEmpty ", bytesEmpty );
/* Choose smallest value if both are active. */
if( (past->past_NumInputChannels > 0) && (past->past_NumOutputChannels > 0) )
{
bytesToXfer = ( bytesFilled < bytesEmpty ) ? bytesFilled : bytesEmpty;
}
/* printf("bytesFilled = %d, bytesEmpty = %d, bytesToXfer = %d\n",
bytesFilled, bytesEmpty, bytesToXfer);
*/
/* Quantize to multiples of a buffer. */
numChunks = bytesToXfer / pahsc->pahsc_BytesPerBuffer;
if( numChunks > (long)(past->past_NumUserBuffers/2) )
{
numChunks = (long)past->past_NumUserBuffers/2;
}
else if( numChunks < 0 )
{
numChunks = 0;
}
AddTraceMessage( "numChunks ", numChunks );
nativeBufPtr = pahsc->pahsc_NativeBuffer;
if( numChunks > 0 )
{
while( numChunks-- > 0 )
{
/* Measure usage based on time to process one user buffer. */
Pa_StartUsageCalculation( past );
#if SUPPORT_AUDIO_CAPTURE
/* Get native data from DirectSound. */
if( past->past_NumInputChannels > 0 )
{
hresult = DSW_ReadBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer );
if( hresult < 0 )
{
ERR_RPT(("DirectSound ReadBlock failed, hresult = 0x%x\n",hresult));
sPaHostError = hresult;
break;
}
}
#endif /* SUPPORT_AUDIO_CAPTURE */
/* Convert 16 bit native data to user data and call user routine. */
result = Pa_CallConvertInt16( past, nativeBufPtr, nativeBufPtr );
if( result != 0) break;
/* Pass native data to DirectSound. */
if( past->past_NumOutputChannels > 0 )
{
/* static short DEBUGHACK = 0;
DEBUGHACK += 0x0049;
nativeBufPtr[0] = DEBUGHACK; /* Make buzz to see if DirectSound still running. */
hresult = DSW_WriteBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer );
if( hresult < 0 )
{
ERR_RPT(("DirectSound WriteBlock failed, result = 0x%x\n",hresult));
sPaHostError = hresult;
break;
}
}
Pa_EndUsageCalculation( past );
}
}
return result;
}
/*******************************************************************/
static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
internalPortAudioStream *past;
PaHostSoundControl *pahsc;
#if PA_SIMULATE_UNDERFLOW
gUnderCallbackCounter++;
if( (gUnderCallbackCounter >= UNDER_START_GAP) &&
(gUnderCallbackCounter <= UNDER_STOP_GAP) )
{
if( gUnderCallbackCounter == UNDER_START_GAP)
{
AddTraceMessage("Begin stall: gUnderCallbackCounter =======", gUnderCallbackCounter );
}
if( gUnderCallbackCounter == UNDER_STOP_GAP)
{
AddTraceMessage("End stall: gUnderCallbackCounter =======", gUnderCallbackCounter );
}
return;
}
#endif
past = (internalPortAudioStream *) dwUser;
if( past == NULL ) return;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return;
if( !pahsc->pahsc_IfInsideCallback && past->past_IsActive )
{
if( past->past_StopNow )
{
past->past_IsActive = 0;
}
else if( past->past_StopSoon )
{
DSoundWrapper *dsw = &pahsc->pahsc_DSoundWrapper;
if( past->past_NumOutputChannels > 0 )
{
DSW_ZeroEmptySpace( dsw );
AddTraceMessage("Pa_TimerCallback: waiting - written ", (int) dsw->dsw_FramesWritten );
AddTraceMessage("Pa_TimerCallback: waiting - played ", (int) dsw->dsw_FramesPlayed );
/* clear past_IsActive when all sound played */
if( dsw->dsw_FramesPlayed >= past->past_FrameCount )
{
past->past_IsActive = 0;
}
}
else
{
past->past_IsActive = 0;
}
}
else
{
pahsc->pahsc_IfInsideCallback = 1;
if( Pa_TimeSlice( past ) != 0) /* Call time slice independant of timing method. */
{
past->past_StopSoon = 1;
}
pahsc->pahsc_IfInsideCallback = 0;
}
}
}
/*******************************************************************/
PaError PaHost_OpenStream( internalPortAudioStream *past )
{
HRESULT hr;
PaError result = paNoError;
PaHostSoundControl *pahsc;
int numBytes, maxChannels;
unsigned int minNumBuffers;
internalPortAudioDevice *pad;
DSoundWrapper *dsw;
/* Allocate and initialize host data. */
pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); /* MEM */
if( pahsc == NULL )
{
result = paInsufficientMemory;
goto error;
}
memset( pahsc, 0, sizeof(PaHostSoundControl) );
past->past_DeviceData = (void *) pahsc;
pahsc->pahsc_TimerID = 0;
dsw = &pahsc->pahsc_DSoundWrapper;
DSW_Init( dsw );
/* Allocate native buffer. */
maxChannels = ( past->past_NumOutputChannels > past->past_NumInputChannels ) ?
past->past_NumOutputChannels : past->past_NumInputChannels;
pahsc->pahsc_BytesPerBuffer = past->past_FramesPerUserBuffer * maxChannels * sizeof(short);
if( maxChannels > 0 )
{
pahsc->pahsc_NativeBuffer = (short *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerBuffer); /* MEM */
if( pahsc->pahsc_NativeBuffer == NULL )
{
result = paInsufficientMemory;
goto error;
}
}
else
{
result = paInvalidChannelCount;
goto error;
}
DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer ));
minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
numBytes = pahsc->pahsc_BytesPerBuffer * past->past_NumUserBuffers;
if( numBytes < DSBSIZE_MIN )
{
result = paBufferTooSmall;
goto error;
}
if( numBytes > DSBSIZE_MAX )
{
result = paBufferTooBig;
goto error;
}
pahsc->pahsc_FramesPerDSBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers;
{
int msecLatency = (int) ((pahsc->pahsc_FramesPerDSBuffer * 1000) / past->past_SampleRate);
PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", pahsc->pahsc_FramesPerDSBuffer, msecLatency ));
}
/* ------------------ OUTPUT */
if( (past->past_OutputDeviceID >= 0) && (past->past_NumOutputChannels > 0) )
{
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID));
pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound, NULL );
/* If this fails, then try each output device until we find one that works. */
if( hr != DS_OK )
{
int i;
ERR_RPT(("Creation of requested Audio Output device '%s' failed.\n",
((pad->pad_lpGUID == NULL) ? "Default" : pad->pad_Info.name) ));
for( i=0; i<Pa_CountDevices(); i++ )
{
pad = Pa_GetInternalDevice( i );
if( pad->pad_Info.maxOutputChannels >= past->past_NumOutputChannels )
{
DBUG(("Try device '%s' instead.\n", pad->pad_Info.name ));
hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound, NULL );
if( hr == DS_OK )
{
ERR_RPT(("Using device '%s' instead.\n", pad->pad_Info.name ));
break;
}
}
}
}
if( hr != DS_OK )
{
ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
result = paHostError;
sPaHostError = hr;
goto error;
}
hr = DSW_InitOutputBuffer( dsw,
(unsigned long) (past->past_SampleRate + 0.5),
past->past_NumOutputChannels, numBytes );
DBUG(("DSW_InitOutputBuffer() returns %x\n", hr));
if( hr != DS_OK )
{
result = paHostError;
sPaHostError = hr;
goto error;
}
past->past_FrameCount = pahsc->pahsc_DSoundWrapper.dsw_FramesWritten;
}
#if SUPPORT_AUDIO_CAPTURE
/* ------------------ INPUT */
if( (past->past_InputDeviceID >= 0) && (past->past_NumInputChannels > 0) )
{
pad = Pa_GetInternalDevice( past->past_InputDeviceID );
hr = DirectSoundCaptureCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
/* If this fails, then try each input device until we find one that works. */
if( hr != DS_OK )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -