📄 pa_linux_alsa.c
字号:
* config time.
*/
ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError );
/* Have to reset hwParams, to set new buffer size */
ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError );
*minChannels = (int)minChans;
*maxChannels = (int)maxChans;
*defaultSampleRate = defaultSr;
*defaultLowLatency = (double) lowLatency / *defaultSampleRate;
*defaultHighLatency = (double) highLatency / *defaultSampleRate;
end:
snd_pcm_close( pcm );
return result;
error:
goto end;
}
/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate
* wether input/output is available) */
static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo )
{
deviceInfo->structVersion = -1;
deviceInfo->name = NULL;
deviceInfo->hostApi = -1;
deviceInfo->maxInputChannels = 0;
deviceInfo->maxOutputChannels = 0;
deviceInfo->defaultLowInputLatency = -1.;
deviceInfo->defaultLowOutputLatency = -1.;
deviceInfo->defaultHighInputLatency = -1.;
deviceInfo->defaultHighOutputLatency = -1.;
deviceInfo->defaultSampleRate = -1.;
}
/* Helper struct */
typedef struct
{
char *alsaName;
char *name;
int isPlug;
int hasPlayback;
int hasCapture;
} DeviceNames;
static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi,
char **dst,
const char *src)
{
PaError result = paNoError;
int len = strlen( src ) + 1;
/* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */
PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
paInsufficientMemory );
strncpy( *dst, src, len );
error:
return result;
}
/* Disregard standard plugins
* XXX: Might want to make the "default" plugin available, if we can make it work
*/
static int IgnorePlugin( const char *pluginId )
{
#define numIgnored 10
static const char *ignoredPlugins[numIgnored] = {"hw", "plughw", "plug", "default", "dsnoop", "dmix", "tee",
"file", "null", "shm"};
int i;
for( i = 0; i < numIgnored; ++i )
{
if( !strcmp( pluginId, ignoredPlugins[i] ) )
{
return 1;
}
}
return 0;
}
/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */
static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
{
PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep;
PaAlsaDeviceInfo *deviceInfoArray;
int cardIdx = -1, devIdx = 0;
snd_ctl_card_info_t *cardInfo;
PaError result = paNoError;
size_t numDeviceNames = 0, maxDeviceNames = 1, i;
DeviceNames *deviceNames = NULL;
snd_config_t *topNode = NULL;
snd_pcm_info_t *pcmInfo;
int res;
int blocking = SND_PCM_NONBLOCK;
char alsaCardName[50];
if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) )
blocking = 0;
/* These two will be set to the first working input and output device, respectively */
commonApi->info.defaultInputDevice = paNoDevice;
commonApi->info.defaultOutputDevice = paNoDevice;
/* count the devices by enumerating all the card numbers */
/* snd_card_next() modifies the integer passed to it to be:
* the index of the first card if the parameter is -1
* the index of the next card if the parameter is the index of a card
* -1 if there are no more cards
*
* The function itself returns 0 if it succeeded. */
cardIdx = -1;
snd_ctl_card_info_alloca( &cardInfo );
snd_pcm_info_alloca( &pcmInfo );
while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 )
{
char *cardName;
int devIdx = -1;
snd_ctl_t *ctl;
char buf[50];
snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx );
/* Acquire name of card */
if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 )
continue; /* Unable to open card :( */
snd_ctl_card_info( ctl, cardInfo );
PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) );
while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 )
{
char *alsaDeviceName, *deviceName;
size_t len;
int hasPlayback = 0, hasCapture = 0;
snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx );
/* Obtain info about this particular device */
snd_pcm_info_set_device( pcmInfo, devIdx );
snd_pcm_info_set_subdevice( pcmInfo, 0 );
snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE );
if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
hasCapture = 1;
snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK );
if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
hasPlayback = 1;
if( !hasPlayback && !hasCapture )
{
continue; /* Error */
}
/* The length of the string written by snprintf plus terminating 0 */
len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1;
PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
paInsufficientMemory );
snprintf( deviceName, len, "%s: %s (%s)", cardName,
snd_pcm_info_get_name( pcmInfo ), buf );
++numDeviceNames;
if( !deviceNames || numDeviceNames > maxDeviceNames )
{
maxDeviceNames *= 2;
PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
paInsufficientMemory );
}
PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) );
deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName;
deviceNames[ numDeviceNames - 1 ].name = deviceName;
deviceNames[ numDeviceNames - 1 ].isPlug = 0;
deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback;
deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture;
}
snd_ctl_close( ctl );
}
/* Iterate over plugin devices */
snd_config_update();
if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 )
{
snd_config_iterator_t i, next;
snd_config_for_each( i, next, topNode )
{
const char *tpStr = NULL, *idStr = NULL;
char *alsaDeviceName, *deviceName;
snd_config_t *n = snd_config_iterator_entry( i ), *tp = NULL;
if( snd_config_get_type( n ) != SND_CONFIG_TYPE_COMPOUND )
continue;
ENSURE_( snd_config_search( n, "type", &tp ), paUnanticipatedHostError );
ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError );
ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError );
if( IgnorePlugin( idStr ) )
{
PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr ));
continue;
}
PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr ));
PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
strlen(idStr) + 6 ), paInsufficientMemory );
strcpy( alsaDeviceName, idStr );
PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
strlen(idStr) + 1 ), paInsufficientMemory );
strcpy( deviceName, idStr );
++numDeviceNames;
if( !deviceNames || numDeviceNames > maxDeviceNames )
{
maxDeviceNames *= 2;
PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
paInsufficientMemory );
}
deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName;
deviceNames[numDeviceNames - 1].name = deviceName;
deviceNames[numDeviceNames - 1].isPlug = 1;
deviceNames[numDeviceNames - 1].hasPlayback = 1;
deviceNames[numDeviceNames - 1].hasCapture = 1;
}
}
else
PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) ));
/* allocate deviceInfo memory based on the number of devices */
PA_UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory );
/* allocate all device info structs in a contiguous block */
PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory(
alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory );
/* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name),
* it's ignored.
*/
/* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */
for( i = 0, devIdx = 0; i < numDeviceNames; ++i )
{
snd_pcm_t *pcm;
PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx];
PaDeviceInfo *commonDeviceInfo = &deviceInfo->commonDeviceInfo;
/* Zero fields */
InitializeDeviceInfo( commonDeviceInfo );
/* to determine device capabilities, we must open the device and query the
* hardware parameter configuration space */
/* Query capture */
if( deviceNames[i].hasCapture &&
snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 )
{
if( GropeDevice( pcm, &deviceInfo->minInputChannels, &commonDeviceInfo->maxInputChannels,
&commonDeviceInfo->defaultLowInputLatency, &commonDeviceInfo->defaultHighInputLatency,
&commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
continue; /* Error */
}
/* Query playback */
if( deviceNames[i].hasPlayback &&
snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 )
{
if( GropeDevice( pcm, &deviceInfo->minOutputChannels, &commonDeviceInfo->maxOutputChannels,
&commonDeviceInfo->defaultLowOutputLatency, &commonDeviceInfo->defaultHighOutputLatency,
&commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
continue; /* Error */
}
commonDeviceInfo->structVersion = 2;
commonDeviceInfo->hostApi = alsaApi->hostApiIndex;
commonDeviceInfo->name = deviceNames[i].name;
deviceInfo->alsaName = deviceNames[i].alsaName;
deviceInfo->isPlug = deviceNames[i].isPlug;
/* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object.
* Should now be safe to add device info, unless the device supports neither capture nor playback
*/
if( commonDeviceInfo->maxInputChannels > 0 || commonDeviceInfo->maxOutputChannels > 0 )
{
if( commonApi->info.defaultInputDevice == paNoDevice && commonDeviceInfo->maxInputChannels > 0 )
commonApi->info.defaultInputDevice = devIdx;
if( commonApi->info.defaultOutputDevice == paNoDevice && commonDeviceInfo->maxOutputChannels > 0 )
commonApi->info.defaultOutputDevice = devIdx;
commonApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo;
}
}
free( deviceNames );
commonApi->info.deviceCount = devIdx; /* Number of successfully queried devices */
end:
return result;
error:
goto end; /* No particular action */
}
/* Check against known device capabilities */
static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode )
{
PaError result = paNoError;
int maxChans;
const PaAlsaDeviceInfo *deviceInfo = NULL;
assert( parameters );
if( parameters->device != paUseHostApiSpecificDeviceSpecification )
{
assert( parameters->device < hostApi->info.deviceCount );
PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination );
deviceInfo = GetDeviceInfo( hostApi, parameters->device );
}
else
{
const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo;
PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice );
PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1,
paIncompatibleHostApiSpecificStreamInfo );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -