2004-07-16 19:56:36 +00:00
/***
2006-06-19 21:53:48 +00:00
This file is part of PulseAudio .
2007-01-04 13:43:45 +00:00
2009-02-17 19:35:01 +02:00
Copyright 2004 - 2009 Lennart Poettering
2007-02-13 15:35:19 +00:00
Copyright 2006 Pierre Ossman < ossman @ cendio . se > for Cendio AB
2006-06-19 21:53:48 +00:00
PulseAudio is free software ; you can redistribute it and / or modify
2004-11-14 14:58:54 +00:00
it under the terms of the GNU Lesser General Public License as published
2009-03-03 20:23:02 +00:00
by the Free Software Foundation ; either version 2.1 of the License ,
2004-07-16 19:56:36 +00:00
or ( at your option ) any later version .
2007-01-04 13:43:45 +00:00
2006-06-19 21:53:48 +00:00
PulseAudio is distributed in the hope that it will be useful , but
2004-07-16 19:56:36 +00:00
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
2007-01-04 13:43:45 +00:00
2004-11-14 14:58:54 +00:00
You should have received a copy of the GNU Lesser General Public License
2006-06-19 21:53:48 +00:00
along with PulseAudio ; if not , write to the Free Software
2004-07-16 19:56:36 +00:00
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
* * */
2004-07-16 19:16:42 +00:00
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
2004-11-17 23:11:34 +00:00
# include <sys/types.h>
2008-05-15 23:34:41 +00:00
# include <limits.h>
2004-07-16 17:03:11 +00:00
# include <asoundlib.h>
2006-06-19 21:53:48 +00:00
# include <pulse/sample.h>
# include <pulse/xmalloc.h>
2008-10-21 20:00:36 +02:00
# include <pulse/timeval.h>
2009-01-30 02:24:40 +01:00
# include <pulse/util.h>
2009-02-20 01:18:37 +01:00
# include <pulse/i18n.h>
2009-04-19 19:22:51 +02:00
# include <pulse/utf8.h>
2006-05-17 16:34:18 +00:00
2006-06-19 21:53:48 +00:00
# include <pulsecore/log.h>
2007-10-28 19:13:50 +00:00
# include <pulsecore/macro.h>
2007-11-13 17:37:44 +00:00
# include <pulsecore/core-util.h>
2008-05-15 23:34:41 +00:00
# include <pulsecore/atomic.h>
2009-02-18 21:49:31 +01:00
# include <pulsecore/core-error.h>
2009-02-21 16:33:46 +01:00
# include <pulsecore/once.h>
2009-04-19 19:22:51 +02:00
# include <pulsecore/thread.h>
2009-06-17 03:45:14 +02:00
# include <pulsecore/conf-parser.h>
2006-02-16 22:08:06 +00:00
# include "alsa-util.h"
2009-06-17 03:45:14 +02:00
# include "alsa-mixer.h"
2004-07-16 17:03:11 +00:00
2009-01-24 01:25:11 +01:00
# ifdef HAVE_HAL
# include "hal-util.h"
# endif
2009-03-01 20:34:07 +01:00
# ifdef HAVE_UDEV
# include "udev-util.h"
# endif
2006-06-16 19:33:05 +00:00
static int set_format ( snd_pcm_t * pcm_handle , snd_pcm_hw_params_t * hwparams , pa_sample_format_t * f ) {
2004-07-16 17:03:11 +00:00
static const snd_pcm_format_t format_trans [ ] = {
[ PA_SAMPLE_U8 ] = SND_PCM_FORMAT_U8 ,
[ PA_SAMPLE_ALAW ] = SND_PCM_FORMAT_A_LAW ,
[ PA_SAMPLE_ULAW ] = SND_PCM_FORMAT_MU_LAW ,
[ PA_SAMPLE_S16LE ] = SND_PCM_FORMAT_S16_LE ,
[ PA_SAMPLE_S16BE ] = SND_PCM_FORMAT_S16_BE ,
[ PA_SAMPLE_FLOAT32LE ] = SND_PCM_FORMAT_FLOAT_LE ,
[ PA_SAMPLE_FLOAT32BE ] = SND_PCM_FORMAT_FLOAT_BE ,
2007-11-09 02:45:07 +00:00
[ PA_SAMPLE_S32LE ] = SND_PCM_FORMAT_S32_LE ,
[ PA_SAMPLE_S32BE ] = SND_PCM_FORMAT_S32_BE ,
2009-01-16 03:15:39 +01:00
[ PA_SAMPLE_S24LE ] = SND_PCM_FORMAT_S24_3LE ,
[ PA_SAMPLE_S24BE ] = SND_PCM_FORMAT_S24_3BE ,
2009-01-16 18:39:36 +01:00
[ PA_SAMPLE_S24_32LE ] = SND_PCM_FORMAT_S24_LE ,
[ PA_SAMPLE_S24_32BE ] = SND_PCM_FORMAT_S24_BE ,
2004-07-16 17:03:11 +00:00
} ;
2006-06-16 19:33:05 +00:00
static const pa_sample_format_t try_order [ ] = {
PA_SAMPLE_FLOAT32NE ,
PA_SAMPLE_FLOAT32RE ,
2007-11-09 02:45:07 +00:00
PA_SAMPLE_S32NE ,
PA_SAMPLE_S32RE ,
2009-01-16 18:39:36 +01:00
PA_SAMPLE_S24_32NE ,
PA_SAMPLE_S24_32RE ,
2009-01-16 03:15:39 +01:00
PA_SAMPLE_S24NE ,
PA_SAMPLE_S24RE ,
2007-11-09 02:45:07 +00:00
PA_SAMPLE_S16NE ,
PA_SAMPLE_S16RE ,
2006-06-16 19:33:05 +00:00
PA_SAMPLE_ALAW ,
2007-11-09 02:45:07 +00:00
PA_SAMPLE_ULAW ,
2009-06-17 03:45:14 +02:00
PA_SAMPLE_U8
2006-06-16 19:33:05 +00:00
} ;
2009-06-17 03:45:14 +02:00
unsigned i ;
int ret ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( pcm_handle ) ;
2009-09-09 04:28:52 +02:00
pa_assert ( hwparams ) ;
2007-10-28 19:13:50 +00:00
pa_assert ( f ) ;
2006-06-16 19:33:05 +00:00
if ( ( ret = snd_pcm_hw_params_set_format ( pcm_handle , hwparams , format_trans [ * f ] ) ) > = 0 )
return ret ;
2009-05-14 01:25:07 +02:00
pa_log_debug ( " snd_pcm_hw_params_set_format(%s) failed: %s " ,
snd_pcm_format_description ( format_trans [ * f ] ) ,
pa_alsa_strerror ( ret ) ) ;
2006-06-16 19:33:05 +00:00
if ( * f = = PA_SAMPLE_FLOAT32BE )
* f = PA_SAMPLE_FLOAT32LE ;
else if ( * f = = PA_SAMPLE_FLOAT32LE )
* f = PA_SAMPLE_FLOAT32BE ;
2009-01-16 03:15:39 +01:00
else if ( * f = = PA_SAMPLE_S24BE )
* f = PA_SAMPLE_S24LE ;
else if ( * f = = PA_SAMPLE_S24LE )
* f = PA_SAMPLE_S24BE ;
2009-01-16 18:39:36 +01:00
else if ( * f = = PA_SAMPLE_S24_32BE )
* f = PA_SAMPLE_S24_32LE ;
else if ( * f = = PA_SAMPLE_S24_32LE )
* f = PA_SAMPLE_S24_32BE ;
2006-06-16 19:33:05 +00:00
else if ( * f = = PA_SAMPLE_S16BE )
* f = PA_SAMPLE_S16LE ;
else if ( * f = = PA_SAMPLE_S16LE )
* f = PA_SAMPLE_S16BE ;
2007-11-09 02:45:07 +00:00
else if ( * f = = PA_SAMPLE_S32BE )
* f = PA_SAMPLE_S32LE ;
else if ( * f = = PA_SAMPLE_S32LE )
* f = PA_SAMPLE_S32BE ;
2006-06-16 19:33:05 +00:00
else
goto try_auto ;
if ( ( ret = snd_pcm_hw_params_set_format ( pcm_handle , hwparams , format_trans [ * f ] ) ) > = 0 )
return ret ;
2007-01-04 13:43:45 +00:00
2009-05-14 01:25:07 +02:00
pa_log_debug ( " snd_pcm_hw_params_set_format(%s) failed: %s " ,
snd_pcm_format_description ( format_trans [ * f ] ) ,
pa_alsa_strerror ( ret ) ) ;
2006-06-16 19:33:05 +00:00
try_auto :
2009-06-17 03:45:14 +02:00
for ( i = 0 ; i < PA_ELEMENTSOF ( try_order ) ; i + + ) {
2006-06-16 19:33:05 +00:00
* f = try_order [ i ] ;
2007-01-04 13:43:45 +00:00
2006-06-16 19:33:05 +00:00
if ( ( ret = snd_pcm_hw_params_set_format ( pcm_handle , hwparams , format_trans [ * f ] ) ) > = 0 )
return ret ;
2009-05-14 01:25:07 +02:00
pa_log_debug ( " snd_pcm_hw_params_set_format(%s) failed: %s " ,
snd_pcm_format_description ( format_trans [ * f ] ) ,
pa_alsa_strerror ( ret ) ) ;
2006-06-16 19:33:05 +00:00
}
return - 1 ;
}
2009-09-09 04:28:52 +02:00
static int set_period_size ( snd_pcm_t * pcm_handle , snd_pcm_hw_params_t * hwparams , snd_pcm_uframes_t size ) {
snd_pcm_uframes_t s ;
int d , ret ;
pa_assert ( pcm_handle ) ;
pa_assert ( hwparams ) ;
s = size ;
d = 0 ;
if ( snd_pcm_hw_params_set_period_size_near ( pcm_handle , hwparams , & s , & d ) < 0 ) {
s = size ;
d = - 1 ;
if ( snd_pcm_hw_params_set_period_size_near ( pcm_handle , hwparams , & s , & d ) < 0 ) {
s = size ;
d = 1 ;
if ( ( ret = snd_pcm_hw_params_set_period_size_near ( pcm_handle , hwparams , & s , & d ) ) < 0 ) {
pa_log_info ( " snd_pcm_hw_params_set_period_size_near() failed: %s " , pa_alsa_strerror ( ret ) ) ;
return ret ;
}
}
}
return 0 ;
}
static int set_buffer_size ( snd_pcm_t * pcm_handle , snd_pcm_hw_params_t * hwparams , snd_pcm_uframes_t size ) {
int ret ;
pa_assert ( pcm_handle ) ;
pa_assert ( hwparams ) ;
if ( ( ret = snd_pcm_hw_params_set_buffer_size_near ( pcm_handle , hwparams , & size ) ) < 0 ) {
pa_log_info ( " snd_pcm_hw_params_set_buffer_size_near() failed: %s " , pa_alsa_strerror ( ret ) ) ;
return ret ;
}
return 0 ;
}
2006-06-16 19:33:05 +00:00
/* Set the hardware parameters of the given ALSA device. Returns the
2009-09-09 23:57:49 +02:00
* selected fragment settings in * buffer_size and * period_size . If tsched mode can be enabled */
2007-11-13 17:37:44 +00:00
int pa_alsa_set_hw_params (
snd_pcm_t * pcm_handle ,
pa_sample_spec * ss ,
snd_pcm_uframes_t * period_size ,
2009-09-09 04:28:52 +02:00
snd_pcm_uframes_t * buffer_size ,
2008-05-15 23:34:41 +00:00
snd_pcm_uframes_t tsched_size ,
2007-11-13 17:37:44 +00:00
pa_bool_t * use_mmap ,
2008-05-15 23:34:41 +00:00
pa_bool_t * use_tsched ,
2007-11-13 17:37:44 +00:00
pa_bool_t require_exact_channel_number ) {
2006-06-16 19:33:05 +00:00
int ret = - 1 ;
2009-09-09 04:28:52 +02:00
snd_pcm_hw_params_t * hwparams , * hwparams_copy ;
int dir ;
2009-01-16 23:33:15 +01:00
snd_pcm_uframes_t _period_size = period_size ? * period_size : 0 ;
2009-09-09 04:28:52 +02:00
snd_pcm_uframes_t _buffer_size = buffer_size ? * buffer_size : 0 ;
2007-11-13 17:37:44 +00:00
pa_bool_t _use_mmap = use_mmap & & * use_mmap ;
2008-05-15 23:34:41 +00:00
pa_bool_t _use_tsched = use_tsched & & * use_tsched ;
2009-09-09 04:28:52 +02:00
pa_sample_spec _ss = * ss ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( pcm_handle ) ;
pa_assert ( ss ) ;
snd_pcm_hw_params_alloca ( & hwparams ) ;
2009-09-09 04:28:52 +02:00
snd_pcm_hw_params_alloca ( & hwparams_copy ) ;
2006-03-05 18:37:13 +00:00
2009-05-14 01:25:07 +02:00
if ( ( ret = snd_pcm_hw_params_any ( pcm_handle , hwparams ) ) < 0 ) {
pa_log_debug ( " snd_pcm_hw_params_any() failed: %s " , pa_alsa_strerror ( ret ) ) ;
2007-11-13 23:42:15 +00:00
goto finish ;
2009-05-14 01:25:07 +02:00
}
2007-11-13 23:42:15 +00:00
2009-05-14 01:25:07 +02:00
if ( ( ret = snd_pcm_hw_params_set_rate_resample ( pcm_handle , hwparams , 0 ) ) < 0 ) {
pa_log_debug ( " snd_pcm_hw_params_set_rate_resample() failed: %s " , pa_alsa_strerror ( ret ) ) ;
2007-10-28 19:13:50 +00:00
goto finish ;
2009-05-14 01:25:07 +02:00
}
2007-10-28 19:13:50 +00:00
2007-11-13 17:37:44 +00:00
if ( _use_mmap ) {
2009-02-18 21:50:09 +01:00
if ( snd_pcm_hw_params_set_access ( pcm_handle , hwparams , SND_PCM_ACCESS_MMAP_INTERLEAVED ) < 0 ) {
2007-10-28 19:13:50 +00:00
/* mmap() didn't work, fall back to interleaved */
2009-05-14 01:25:07 +02:00
if ( ( ret = snd_pcm_hw_params_set_access ( pcm_handle , hwparams , SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 ) {
pa_log_debug ( " snd_pcm_hw_params_set_access() failed: %s " , pa_alsa_strerror ( ret ) ) ;
2007-10-28 19:13:50 +00:00
goto finish ;
2009-05-14 01:25:07 +02:00
}
2007-10-28 19:13:50 +00:00
2007-11-13 17:37:44 +00:00
_use_mmap = FALSE ;
2007-10-28 19:13:50 +00:00
}
2009-05-14 01:25:07 +02:00
} else if ( ( ret = snd_pcm_hw_params_set_access ( pcm_handle , hwparams , SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 ) {
pa_log_debug ( " snd_pcm_hw_params_set_access() failed: %s " , pa_alsa_strerror ( ret ) ) ;
2006-06-17 23:36:03 +00:00
goto finish ;
2009-05-14 01:25:07 +02:00
}
2006-06-17 23:36:03 +00:00
2008-05-15 23:34:41 +00:00
if ( ! _use_mmap )
_use_tsched = FALSE ;
2009-09-09 23:57:49 +02:00
if ( ! pa_alsa_pcm_is_hw ( pcm_handle ) )
_use_tsched = FALSE ;
2009-09-09 04:28:52 +02:00
if ( ( ret = set_format ( pcm_handle , hwparams , & _ss . format ) ) < 0 )
2006-06-17 23:36:03 +00:00
goto finish ;
2009-09-09 04:28:52 +02:00
if ( ( ret = snd_pcm_hw_params_set_rate_near ( pcm_handle , hwparams , & _ss . rate , NULL ) ) < 0 ) {
2009-05-14 01:25:07 +02:00
pa_log_debug ( " snd_pcm_hw_params_set_rate_near() failed: %s " , pa_alsa_strerror ( ret ) ) ;
2006-06-17 23:36:03 +00:00
goto finish ;
2009-05-14 01:25:07 +02:00
}
2006-06-17 23:36:03 +00:00
2007-11-13 17:37:44 +00:00
if ( require_exact_channel_number ) {
2009-09-09 04:28:52 +02:00
if ( ( ret = snd_pcm_hw_params_set_channels ( pcm_handle , hwparams , _ss . channels ) ) < 0 ) {
pa_log_debug ( " snd_pcm_hw_params_set_channels(%u) failed: %s " , _ss . channels , pa_alsa_strerror ( ret ) ) ;
2007-11-13 17:37:44 +00:00
goto finish ;
2009-05-14 01:25:07 +02:00
}
2007-11-13 17:37:44 +00:00
} else {
2009-09-09 04:28:52 +02:00
unsigned int c = _ss . channels ;
2009-05-14 01:25:07 +02:00
if ( ( ret = snd_pcm_hw_params_set_channels_near ( pcm_handle , hwparams , & c ) ) < 0 ) {
2009-09-09 04:28:52 +02:00
pa_log_debug ( " snd_pcm_hw_params_set_channels_near(%u) failed: %s " , _ss . channels , pa_alsa_strerror ( ret ) ) ;
2007-11-13 17:37:44 +00:00
goto finish ;
2009-05-14 01:25:07 +02:00
}
2006-06-17 23:36:03 +00:00
2009-09-09 04:28:52 +02:00
_ss . channels = c ;
2009-05-14 01:25:07 +02:00
}
2008-05-15 23:34:41 +00:00
2009-09-09 04:28:52 +02:00
if ( _use_tsched & & tsched_size > 0 ) {
_buffer_size = pa_convert_size ( tsched_size , ss , & _ss ) ;
_period_size = _buffer_size ;
} else {
_period_size = pa_convert_size ( _period_size , ss , & _ss ) ;
_buffer_size = pa_convert_size ( _buffer_size , ss , & _ss ) ;
}
2009-05-01 04:16:17 +02:00
2009-09-09 04:28:52 +02:00
if ( _buffer_size > 0 | | _period_size > 0 ) {
snd_pcm_uframes_t max_frames = 0 ;
2008-05-15 23:34:41 +00:00
2009-09-09 04:28:52 +02:00
if ( ( ret = snd_pcm_hw_params_get_buffer_size_max ( hwparams , & max_frames ) ) < 0 )
pa_log_warn ( " snd_pcm_hw_params_get_buffer_size_max() failed: %s " , pa_alsa_strerror ( ret ) ) ;
else
pa_log_debug ( " Maximum hw buffer size is %lu ms " , ( long unsigned ) max_frames * PA_MSEC_PER_SEC / _ss . rate ) ;
2008-05-15 23:34:41 +00:00
2009-09-09 04:28:52 +02:00
/* Some ALSA drivers really don't like if we set the buffer
* size first and the number of periods second . ( which would
* make a lot more sense to me ) So , try a few combinations
* before we give up . */
if ( _buffer_size > 0 & & _period_size > 0 ) {
snd_pcm_hw_params_copy ( hwparams_copy , hwparams ) ;
/* First try: set buffer size first, followed by period size */
if ( set_buffer_size ( pcm_handle , hwparams_copy , _buffer_size ) > = 0 & &
set_period_size ( pcm_handle , hwparams_copy , _period_size ) > = 0 & &
snd_pcm_hw_params ( pcm_handle , hwparams_copy ) > = 0 ) {
pa_log_debug ( " Set buffer size first, period size second. " ) ;
goto success ;
}
2009-05-01 04:16:17 +02:00
2009-09-09 04:28:52 +02:00
/* Second try: set period size first, followed by buffer size */
if ( set_period_size ( pcm_handle , hwparams_copy , _period_size ) > = 0 & &
set_buffer_size ( pcm_handle , hwparams_copy , _buffer_size ) > = 0 & &
snd_pcm_hw_params ( pcm_handle , hwparams_copy ) > = 0 ) {
pa_log_debug ( " Set period size first, buffer size second. " ) ;
goto success ;
}
2009-01-16 23:33:15 +01:00
}
2009-09-09 04:28:52 +02:00
if ( _buffer_size > 0 ) {
snd_pcm_hw_params_copy ( hwparams_copy , hwparams ) ;
/* Third try: set only buffer size */
if ( set_buffer_size ( pcm_handle , hwparams_copy , _buffer_size ) > = 0 & &
snd_pcm_hw_params ( pcm_handle , hwparams_copy ) > = 0 ) {
pa_log_debug ( " Set only buffer size second. " ) ;
goto success ;
2008-09-09 00:04:50 +03:00
}
2008-05-15 23:34:41 +00:00
}
2009-07-31 02:07:24 +02:00
2009-09-09 04:28:52 +02:00
if ( _period_size > 0 ) {
snd_pcm_hw_params_copy ( hwparams_copy , hwparams ) ;
/* Fourth try: set only period size */
if ( set_period_size ( pcm_handle , hwparams_copy , _period_size ) > = 0 & &
snd_pcm_hw_params ( pcm_handle , hwparams_copy ) > = 0 ) {
pa_log_debug ( " Set only period size second. " ) ;
goto success ;
}
}
2009-01-16 23:33:15 +01:00
}
2008-05-15 23:34:41 +00:00
2009-09-09 04:28:52 +02:00
pa_log_debug ( " Set neither period nor buffer size. " ) ;
/* Last chance, set nothing */
if ( ( ret = snd_pcm_hw_params ( pcm_handle , hwparams ) ) < 0 ) {
pa_log_info ( " snd_pcm_hw_params failed: %s " , pa_alsa_strerror ( ret ) ) ;
2004-11-20 22:17:31 +00:00
goto finish ;
2009-09-09 04:28:52 +02:00
}
success :
2006-02-20 23:47:46 +00:00
2009-09-09 04:28:52 +02:00
if ( ss - > rate ! = _ss . rate )
pa_log_info ( " Device %s doesn't support %u Hz, changed to %u Hz. " , snd_pcm_name ( pcm_handle ) , ss - > rate , _ss . rate ) ;
2006-05-30 22:05:07 +00:00
2009-09-09 04:28:52 +02:00
if ( ss - > channels ! = _ss . channels )
pa_log_info ( " Device %s doesn't support %u channels, changed to %u. " , snd_pcm_name ( pcm_handle ) , ss - > channels , _ss . channels ) ;
2006-06-16 19:33:05 +00:00
2009-09-09 04:28:52 +02:00
if ( ss - > format ! = _ss . format )
pa_log_info ( " Device %s doesn't support sample format %s, changed to %s. " , snd_pcm_name ( pcm_handle ) , pa_sample_format_to_string ( ss - > format ) , pa_sample_format_to_string ( _ss . format ) ) ;
2007-01-04 13:43:45 +00:00
2009-06-17 03:45:14 +02:00
if ( ( ret = snd_pcm_prepare ( pcm_handle ) ) < 0 ) {
pa_log_info ( " snd_pcm_prepare() failed: %s " , pa_alsa_strerror ( ret ) ) ;
2004-11-20 22:17:31 +00:00
goto finish ;
2009-06-17 03:45:14 +02:00
}
2004-11-20 22:17:31 +00:00
2009-09-09 04:28:52 +02:00
if ( ( ret = snd_pcm_hw_params_current ( pcm_handle , hwparams ) ) < 0 ) {
pa_log_info ( " snd_pcm_hw_params_current() failed: %s " , pa_alsa_strerror ( ret ) ) ;
goto finish ;
}
2008-05-15 23:34:41 +00:00
if ( ( ret = snd_pcm_hw_params_get_period_size ( hwparams , & _period_size , & dir ) ) < 0 | |
2009-09-09 04:28:52 +02:00
( ret = snd_pcm_hw_params_get_buffer_size ( hwparams , & _buffer_size ) ) < 0 ) {
pa_log_info ( " snd_pcm_hw_params_get_{period|buffer}_size() failed: %s " , pa_alsa_strerror ( ret ) ) ;
2004-11-20 22:17:31 +00:00
goto finish ;
2009-06-17 03:45:14 +02:00
}
2007-01-04 13:43:45 +00:00
2007-11-13 17:37:44 +00:00
/* If the sample rate deviates too much, we need to resample */
2009-09-09 04:28:52 +02:00
if ( _ss . rate < ss - > rate * .95 | | _ss . rate > ss - > rate * 1.05 )
ss - > rate = _ss . rate ;
ss - > channels = _ss . channels ;
ss - > format = _ss . format ;
2007-11-13 17:37:44 +00:00
2008-05-15 23:34:41 +00:00
pa_assert ( _period_size > 0 ) ;
2009-09-09 04:28:52 +02:00
pa_assert ( _buffer_size > 0 ) ;
2008-05-15 23:34:41 +00:00
2009-09-09 04:28:52 +02:00
if ( buffer_size )
* buffer_size = _buffer_size ;
2009-01-16 23:33:15 +01:00
if ( period_size )
* period_size = _period_size ;
2007-01-04 13:43:45 +00:00
2007-11-13 17:37:44 +00:00
if ( use_mmap )
* use_mmap = _use_mmap ;
2008-05-15 23:34:41 +00:00
if ( use_tsched )
* use_tsched = _use_tsched ;
2004-11-20 22:17:31 +00:00
ret = 0 ;
2007-01-04 13:43:45 +00:00
2008-08-31 16:25:37 +02:00
snd_pcm_nonblock ( pcm_handle , 1 ) ;
2004-11-20 22:17:31 +00:00
finish :
2007-01-04 13:43:45 +00:00
2004-07-16 17:03:11 +00:00
return ret ;
}
2008-05-15 23:34:41 +00:00
int pa_alsa_set_sw_params ( snd_pcm_t * pcm , snd_pcm_uframes_t avail_min ) {
2007-10-28 19:13:50 +00:00
snd_pcm_sw_params_t * swparams ;
2009-02-20 03:21:17 +01:00
snd_pcm_uframes_t boundary ;
2007-10-28 19:13:50 +00:00
int err ;
pa_assert ( pcm ) ;
snd_pcm_sw_params_alloca ( & swparams ) ;
if ( ( err = snd_pcm_sw_params_current ( pcm , swparams ) < 0 ) ) {
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Unable to determine current swparams: %s \n " , pa_alsa_strerror ( err ) ) ;
2007-10-28 19:13:50 +00:00
return err ;
}
2009-02-20 03:21:17 +01:00
if ( ( err = snd_pcm_sw_params_set_period_event ( pcm , swparams , 0 ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Unable to disable period event: %s \n " , pa_alsa_strerror ( err ) ) ;
2009-02-20 03:21:17 +01:00
return err ;
}
if ( ( err = snd_pcm_sw_params_set_tstamp_mode ( pcm , swparams , SND_PCM_TSTAMP_ENABLE ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Unable to enable time stamping: %s \n " , pa_alsa_strerror ( err ) ) ;
2009-02-20 03:21:17 +01:00
return err ;
}
if ( ( err = snd_pcm_sw_params_get_boundary ( swparams , & boundary ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Unable to get boundary: %s \n " , pa_alsa_strerror ( err ) ) ;
2009-02-20 03:21:17 +01:00
return err ;
}
if ( ( err = snd_pcm_sw_params_set_stop_threshold ( pcm , swparams , boundary ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Unable to set stop threshold: %s \n " , pa_alsa_strerror ( err ) ) ;
2007-10-28 19:13:50 +00:00
return err ;
}
if ( ( err = snd_pcm_sw_params_set_start_threshold ( pcm , swparams , ( snd_pcm_uframes_t ) - 1 ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Unable to set start threshold: %s \n " , pa_alsa_strerror ( err ) ) ;
2007-10-28 19:13:50 +00:00
return err ;
}
2008-05-15 23:34:41 +00:00
if ( ( err = snd_pcm_sw_params_set_avail_min ( pcm , swparams , avail_min ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_error ( " snd_pcm_sw_params_set_avail_min() failed: %s " , pa_alsa_strerror ( err ) ) ;
2008-05-15 23:34:41 +00:00
return err ;
}
2007-10-28 19:13:50 +00:00
if ( ( err = snd_pcm_sw_params ( pcm , swparams ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Unable to set sw params: %s \n " , pa_alsa_strerror ( err ) ) ;
2007-10-28 19:13:50 +00:00
return err ;
}
return 0 ;
}
2009-01-20 21:36:57 +01:00
snd_pcm_t * pa_alsa_open_by_device_id_auto (
2007-11-13 17:37:44 +00:00
const char * dev_id ,
char * * dev ,
pa_sample_spec * ss ,
pa_channel_map * map ,
int mode ,
snd_pcm_uframes_t * period_size ,
2009-09-09 04:28:52 +02:00
snd_pcm_uframes_t * buffer_size ,
2008-05-15 23:34:41 +00:00
snd_pcm_uframes_t tsched_size ,
pa_bool_t * use_mmap ,
2009-01-15 23:46:42 +01:00
pa_bool_t * use_tsched ,
2009-06-17 03:45:14 +02:00
pa_alsa_profile_set * ps ,
pa_alsa_mapping * * mapping ) {
2007-11-13 17:37:44 +00:00
char * d ;
snd_pcm_t * pcm_handle ;
2009-06-17 03:45:14 +02:00
void * state ;
pa_alsa_mapping * m ;
2007-11-13 17:37:44 +00:00
pa_assert ( dev_id ) ;
pa_assert ( dev ) ;
pa_assert ( ss ) ;
pa_assert ( map ) ;
2009-06-17 03:45:14 +02:00
pa_assert ( ps ) ;
2007-11-13 17:37:44 +00:00
/* First we try to find a device string with a superset of the
2009-06-17 03:45:14 +02:00
* requested channel map . We iterate through our device table from
* top to bottom and take the first that matches . If we didn ' t
* find a working device that way , we iterate backwards , and check
* all devices that do not provide a superset of the requested
* channel map . */
2007-11-13 17:37:44 +00:00
2009-06-17 03:45:14 +02:00
PA_HASHMAP_FOREACH ( m , ps - > mappings , state ) {
if ( ! pa_channel_map_superset ( & m - > channel_map , map ) )
continue ;
2009-01-16 22:01:45 +01:00
2009-06-17 03:45:14 +02:00
pa_log_debug ( " Checking for superset %s (%s) " , m - > name , m - > device_strings [ 0 ] ) ;
2007-11-13 17:37:44 +00:00
2009-06-17 03:45:14 +02:00
pcm_handle = pa_alsa_open_by_device_id_mapping (
dev_id ,
dev ,
ss ,
map ,
mode ,
period_size ,
2009-09-09 04:28:52 +02:00
buffer_size ,
2009-06-17 03:45:14 +02:00
tsched_size ,
use_mmap ,
use_tsched ,
m ) ;
2009-04-29 01:58:18 +02:00
2009-06-17 03:45:14 +02:00
if ( pcm_handle ) {
if ( mapping )
* mapping = m ;
2008-08-31 16:25:37 +02:00
2009-06-17 03:45:14 +02:00
return pcm_handle ;
2007-11-13 17:37:44 +00:00
}
2009-06-17 03:45:14 +02:00
}
2007-11-13 17:37:44 +00:00
2009-06-17 03:45:14 +02:00
PA_HASHMAP_FOREACH_BACKWARDS ( m , ps - > mappings , state ) {
if ( pa_channel_map_superset ( & m - > channel_map , map ) )
continue ;
2009-01-15 23:46:42 +01:00
2009-06-17 03:45:14 +02:00
pa_log_debug ( " Checking for subset %s (%s) " , m - > name , m - > device_strings [ 0 ] ) ;
2009-01-15 23:46:42 +01:00
2009-06-17 03:45:14 +02:00
pcm_handle = pa_alsa_open_by_device_id_mapping (
dev_id ,
dev ,
ss ,
map ,
mode ,
period_size ,
2009-09-09 04:28:52 +02:00
buffer_size ,
2009-06-17 03:45:14 +02:00
tsched_size ,
use_mmap ,
use_tsched ,
m ) ;
2009-01-15 23:46:42 +01:00
2009-06-17 03:45:14 +02:00
if ( pcm_handle ) {
if ( mapping )
* mapping = m ;
2009-01-15 23:46:42 +01:00
2009-06-17 03:45:14 +02:00
return pcm_handle ;
2009-01-15 23:46:42 +01:00
}
2007-11-13 17:37:44 +00:00
}
2009-06-17 03:45:14 +02:00
/* OK, we didn't find any good device, so let's try the raw hw: stuff */
2008-08-31 16:25:37 +02:00
d = pa_sprintf_malloc ( " hw:%s " , dev_id ) ;
2007-11-13 17:37:44 +00:00
pa_log_debug ( " Trying %s as last resort... " , d ) ;
2009-09-09 04:28:52 +02:00
pcm_handle = pa_alsa_open_by_device_string (
d ,
dev ,
ss ,
map ,
mode ,
period_size ,
buffer_size ,
tsched_size ,
use_mmap ,
use_tsched ,
FALSE ) ;
2007-11-13 17:37:44 +00:00
pa_xfree ( d ) ;
2009-06-17 03:45:14 +02:00
if ( pcm_handle & & mapping )
* mapping = NULL ;
2009-01-20 21:36:57 +01:00
return pcm_handle ;
}
2009-06-17 03:45:14 +02:00
snd_pcm_t * pa_alsa_open_by_device_id_mapping (
2009-01-20 21:36:57 +01:00
const char * dev_id ,
char * * dev ,
pa_sample_spec * ss ,
pa_channel_map * map ,
int mode ,
snd_pcm_uframes_t * period_size ,
2009-09-09 04:28:52 +02:00
snd_pcm_uframes_t * buffer_size ,
2009-01-20 21:36:57 +01:00
snd_pcm_uframes_t tsched_size ,
pa_bool_t * use_mmap ,
pa_bool_t * use_tsched ,
2009-06-17 03:45:14 +02:00
pa_alsa_mapping * m ) {
2009-01-20 21:36:57 +01:00
snd_pcm_t * pcm_handle ;
pa_sample_spec try_ss ;
2009-06-17 03:45:14 +02:00
pa_channel_map try_map ;
2009-01-20 21:36:57 +01:00
pa_assert ( dev_id ) ;
pa_assert ( dev ) ;
pa_assert ( ss ) ;
pa_assert ( map ) ;
2009-06-17 03:45:14 +02:00
pa_assert ( m ) ;
2009-01-20 21:36:57 +01:00
2009-06-17 03:45:14 +02:00
try_ss . channels = m - > channel_map . channels ;
2009-01-20 21:36:57 +01:00
try_ss . rate = ss - > rate ;
try_ss . format = ss - > format ;
2009-06-17 03:45:14 +02:00
try_map = m - > channel_map ;
2009-01-20 21:36:57 +01:00
2009-06-17 04:17:25 +02:00
pcm_handle = pa_alsa_open_by_template (
2009-06-17 03:45:14 +02:00
m - > device_strings ,
2009-04-29 01:58:18 +02:00
dev_id ,
2009-01-20 21:36:57 +01:00
dev ,
& try_ss ,
2009-06-17 03:45:14 +02:00
& try_map ,
2009-01-20 21:36:57 +01:00
mode ,
period_size ,
2009-09-09 04:28:52 +02:00
buffer_size ,
2009-01-20 21:36:57 +01:00
tsched_size ,
use_mmap ,
use_tsched ,
TRUE ) ;
if ( ! pcm_handle )
return NULL ;
* ss = try_ss ;
2009-06-17 03:45:14 +02:00
* map = try_map ;
2009-01-20 21:36:57 +01:00
pa_assert ( map - > channels = = ss - > channels ) ;
2009-01-15 23:46:42 +01:00
2007-11-13 17:37:44 +00:00
return pcm_handle ;
}
snd_pcm_t * pa_alsa_open_by_device_string (
const char * device ,
char * * dev ,
pa_sample_spec * ss ,
pa_channel_map * map ,
int mode ,
snd_pcm_uframes_t * period_size ,
2009-09-09 04:28:52 +02:00
snd_pcm_uframes_t * buffer_size ,
2008-05-15 23:34:41 +00:00
snd_pcm_uframes_t tsched_size ,
pa_bool_t * use_mmap ,
2009-01-16 22:01:45 +01:00
pa_bool_t * use_tsched ,
pa_bool_t require_exact_channel_number ) {
2007-11-13 17:37:44 +00:00
int err ;
char * d ;
snd_pcm_t * pcm_handle ;
2008-12-23 15:14:28 +01:00
pa_bool_t reformat = FALSE ;
2007-11-13 17:37:44 +00:00
pa_assert ( device ) ;
pa_assert ( ss ) ;
pa_assert ( map ) ;
d = pa_xstrdup ( device ) ;
for ( ; ; ) {
2008-12-23 15:14:28 +01:00
pa_log_debug ( " Trying %s %s SND_PCM_NO_AUTO_FORMAT ... " , d , reformat ? " without " : " with " ) ;
2008-08-31 16:25:37 +02:00
if ( ( err = snd_pcm_open ( & pcm_handle , d , mode ,
2009-06-17 03:45:14 +02:00
SND_PCM_NONBLOCK |
2008-05-15 23:34:41 +00:00
SND_PCM_NO_AUTO_RESAMPLE |
SND_PCM_NO_AUTO_CHANNELS |
2008-12-23 15:14:28 +01:00
( reformat ? 0 : SND_PCM_NO_AUTO_FORMAT ) ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_info ( " Error opening PCM device %s: %s " , d , pa_alsa_strerror ( err ) ) ;
2009-02-18 21:50:09 +01:00
goto fail ;
2007-11-13 17:37:44 +00:00
}
2009-04-29 01:58:18 +02:00
pa_log_debug ( " Managed to open %s " , d ) ;
2009-09-09 04:28:52 +02:00
if ( ( err = pa_alsa_set_hw_params (
pcm_handle ,
ss ,
period_size ,
buffer_size ,
tsched_size ,
use_mmap ,
use_tsched ,
require_exact_channel_number ) ) < 0 ) {
2007-11-13 17:37:44 +00:00
2008-12-23 15:14:28 +01:00
if ( ! reformat ) {
reformat = TRUE ;
snd_pcm_close ( pcm_handle ) ;
continue ;
}
2008-08-31 16:25:37 +02:00
/* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */
if ( ! pa_startswith ( d , " plug: " ) & & ! pa_startswith ( d , " plughw: " ) ) {
char * t ;
2007-11-13 17:37:44 +00:00
2008-08-31 16:25:37 +02:00
t = pa_sprintf_malloc ( " plug:%s " , d ) ;
2007-11-13 17:37:44 +00:00
pa_xfree ( d ) ;
2008-08-31 16:25:37 +02:00
d = t ;
2008-12-23 15:14:28 +01:00
reformat = FALSE ;
2007-11-13 17:37:44 +00:00
snd_pcm_close ( pcm_handle ) ;
2008-08-31 16:25:37 +02:00
continue ;
2007-11-13 17:37:44 +00:00
}
2008-08-31 16:25:37 +02:00
2009-04-19 19:22:51 +02:00
pa_log_info ( " Failed to set hardware parameters on %s: %s " , d , pa_alsa_strerror ( err ) ) ;
2008-08-31 16:25:37 +02:00
snd_pcm_close ( pcm_handle ) ;
2009-02-18 21:50:09 +01:00
goto fail ;
2007-11-13 17:37:44 +00:00
}
2009-01-16 23:33:15 +01:00
if ( dev )
* dev = d ;
2009-01-22 00:24:28 +01:00
else
pa_xfree ( d ) ;
2007-11-13 17:37:44 +00:00
2008-06-18 23:44:26 +02:00
if ( ss - > channels ! = map - > channels )
pa_channel_map_init_extend ( map , ss - > channels , PA_CHANNEL_MAP_ALSA ) ;
2007-11-13 17:37:44 +00:00
return pcm_handle ;
}
2009-02-18 21:50:09 +01:00
fail :
pa_xfree ( d ) ;
return NULL ;
2007-11-13 17:37:44 +00:00
}
2009-06-17 04:17:25 +02:00
snd_pcm_t * pa_alsa_open_by_template (
char * * template ,
2009-01-16 23:33:15 +01:00
const char * dev_id ,
2009-06-17 03:45:14 +02:00
char * * dev ,
pa_sample_spec * ss ,
pa_channel_map * map ,
int mode ,
snd_pcm_uframes_t * period_size ,
2009-09-09 04:28:52 +02:00
snd_pcm_uframes_t * buffer_size ,
2009-06-17 03:45:14 +02:00
snd_pcm_uframes_t tsched_size ,
pa_bool_t * use_mmap ,
pa_bool_t * use_tsched ,
pa_bool_t require_exact_channel_number ) {
2007-11-13 23:42:15 +00:00
2009-06-17 03:45:14 +02:00
snd_pcm_t * pcm_handle ;
char * * i ;
2007-11-13 23:42:15 +00:00
2009-06-17 04:17:25 +02:00
for ( i = template ; * i ; i + + ) {
2009-06-17 03:45:14 +02:00
char * d ;
2007-11-13 23:42:15 +00:00
2009-06-17 04:17:25 +02:00
d = pa_replace ( * i , " %f " , dev_id ) ;
2007-11-13 23:42:15 +00:00
2009-06-17 03:45:14 +02:00
pcm_handle = pa_alsa_open_by_device_string (
d ,
dev ,
ss ,
map ,
mode ,
period_size ,
2009-09-09 04:28:52 +02:00
buffer_size ,
2009-06-17 03:45:14 +02:00
tsched_size ,
use_mmap ,
use_tsched ,
require_exact_channel_number ) ;
2007-11-13 23:42:15 +00:00
2009-06-17 03:45:14 +02:00
pa_xfree ( d ) ;
2007-11-13 23:42:15 +00:00
2009-06-17 03:45:14 +02:00
if ( pcm_handle )
return pcm_handle ;
2007-11-13 23:42:15 +00:00
}
2009-06-17 03:45:14 +02:00
return NULL ;
2007-11-13 23:42:15 +00:00
}
2008-05-15 23:34:41 +00:00
2009-04-10 01:58:39 +02:00
void pa_alsa_dump ( pa_log_level_t level , snd_pcm_t * pcm ) {
2008-05-15 23:34:41 +00:00
int err ;
snd_output_t * out ;
pa_assert ( pcm ) ;
pa_assert_se ( snd_output_buffer_open ( & out ) = = 0 ) ;
if ( ( err = snd_pcm_dump ( pcm , out ) ) < 0 )
2009-04-19 19:22:51 +02:00
pa_logl ( level , " snd_pcm_dump(): %s " , pa_alsa_strerror ( err ) ) ;
2008-05-15 23:34:41 +00:00
else {
char * s = NULL ;
snd_output_buffer_string ( out , & s ) ;
2009-04-10 01:58:39 +02:00
pa_logl ( level , " snd_pcm_dump(): \n %s " , pa_strnull ( s ) ) ;
2008-05-15 23:34:41 +00:00
}
pa_assert_se ( snd_output_close ( out ) = = 0 ) ;
}
void pa_alsa_dump_status ( snd_pcm_t * pcm ) {
int err ;
snd_output_t * out ;
snd_pcm_status_t * status ;
2009-06-17 03:45:14 +02:00
char * s = NULL ;
2008-05-15 23:34:41 +00:00
pa_assert ( pcm ) ;
snd_pcm_status_alloca ( & status ) ;
2009-06-17 03:45:14 +02:00
if ( ( err = snd_output_buffer_open ( & out ) ) < 0 ) {
pa_log_debug ( " snd_output_buffer_open() failed: %s " , pa_cstrerror ( err ) ) ;
return ;
}
2008-05-15 23:34:41 +00:00
2009-06-17 03:45:14 +02:00
if ( ( err = snd_pcm_status ( pcm , status ) ) < 0 ) {
pa_log_debug ( " snd_pcm_status() failed: %s " , pa_cstrerror ( err ) ) ;
goto finish ;
}
2008-05-15 23:34:41 +00:00
2009-06-17 03:45:14 +02:00
if ( ( err = snd_pcm_status_dump ( status , out ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_debug ( " snd_pcm_dump(): %s " , pa_alsa_strerror ( err ) ) ;
2009-06-17 03:45:14 +02:00
goto finish ;
2008-05-15 23:34:41 +00:00
}
2009-06-17 03:45:14 +02:00
snd_output_buffer_string ( out , & s ) ;
pa_log_debug ( " snd_pcm_dump(): \n %s " , pa_strnull ( s ) ) ;
finish :
snd_output_close ( out ) ;
2008-05-15 23:34:41 +00:00
}
static void alsa_error_handler ( const char * file , int line , const char * function , int err , const char * fmt , . . . ) {
va_list ap ;
2008-08-26 23:19:43 +02:00
char * alsa_file ;
alsa_file = pa_sprintf_malloc ( " (alsa-lib)%s " , file ) ;
2008-05-15 23:34:41 +00:00
va_start ( ap , fmt ) ;
2008-08-26 23:19:43 +02:00
pa_log_levelv_meta ( PA_LOG_INFO , alsa_file , line , function , fmt , ap ) ;
2008-05-15 23:34:41 +00:00
va_end ( ap ) ;
2008-08-26 23:19:43 +02:00
pa_xfree ( alsa_file ) ;
2008-05-15 23:34:41 +00:00
}
static pa_atomic_t n_error_handler_installed = PA_ATOMIC_INIT ( 0 ) ;
2009-06-18 21:27:02 +02:00
void pa_alsa_refcnt_inc ( void ) {
2008-05-15 23:34:41 +00:00
/* This is not really thread safe, but we do our best */
if ( pa_atomic_inc ( & n_error_handler_installed ) = = 0 )
snd_lib_error_set_handler ( alsa_error_handler ) ;
}
2009-06-18 21:27:02 +02:00
void pa_alsa_refcnt_dec ( void ) {
2008-05-15 23:34:41 +00:00
int r ;
pa_assert_se ( ( r = pa_atomic_dec ( & n_error_handler_installed ) ) > = 1 ) ;
2009-06-18 21:27:02 +02:00
if ( r = = 1 ) {
2008-05-15 23:34:41 +00:00
snd_lib_error_set_handler ( NULL ) ;
2009-06-18 21:27:02 +02:00
snd_config_update_free_global ( ) ;
}
2008-05-15 23:34:41 +00:00
}
2009-03-01 21:34:01 +01:00
pa_bool_t pa_alsa_init_description ( pa_proplist * p ) {
2009-06-18 04:38:59 +02:00
const char * d , * k ;
2009-03-01 20:34:07 +01:00
pa_assert ( p ) ;
2009-03-01 21:34:01 +01:00
if ( pa_device_init_description ( p ) )
return TRUE ;
2009-03-01 20:34:07 +01:00
2009-06-18 04:38:59 +02:00
if ( ! ( d = pa_proplist_gets ( p , " alsa.card_name " ) ) )
d = pa_proplist_gets ( p , " alsa.name " ) ;
2009-03-01 20:34:07 +01:00
2009-06-18 04:38:59 +02:00
if ( ! d )
return FALSE ;
k = pa_proplist_gets ( p , PA_PROP_DEVICE_PROFILE_DESCRIPTION ) ;
if ( d & & k )
pa_proplist_setf ( p , PA_PROP_DEVICE_DESCRIPTION , _ ( " %s %s " ) , d , k ) ;
else if ( d )
pa_proplist_sets ( p , PA_PROP_DEVICE_DESCRIPTION , d ) ;
2009-03-01 21:34:01 +01:00
return FALSE ;
2009-03-01 20:34:07 +01:00
}
2009-01-24 01:25:11 +01:00
void pa_alsa_init_proplist_card ( pa_core * c , pa_proplist * p , int card ) {
2009-01-30 02:24:58 +01:00
char * cn , * lcn , * dn ;
2009-01-17 02:00:57 +01:00
pa_assert ( p ) ;
pa_assert ( card > = 0 ) ;
pa_proplist_setf ( p , " alsa.card " , " %i " , card ) ;
if ( snd_card_get_name ( card , & cn ) > = 0 ) {
pa_proplist_sets ( p , " alsa.card_name " , cn ) ;
free ( cn ) ;
}
if ( snd_card_get_longname ( card , & lcn ) > = 0 ) {
pa_proplist_sets ( p , " alsa.long_card_name " , lcn ) ;
free ( lcn ) ;
}
2009-01-24 01:25:11 +01:00
2009-01-30 02:24:58 +01:00
if ( ( dn = pa_alsa_get_driver_name ( card ) ) ) {
pa_proplist_sets ( p , " alsa.driver_name " , dn ) ;
pa_xfree ( dn ) ;
}
2009-03-01 20:34:07 +01:00
# ifdef HAVE_UDEV
2009-06-17 03:45:14 +02:00
pa_udev_get_info ( card , p ) ;
2009-03-01 20:34:07 +01:00
# endif
2009-01-24 01:25:11 +01:00
# ifdef HAVE_HAL
pa_hal_get_info ( c , p , card ) ;
# endif
2009-01-17 02:00:57 +01:00
}
2009-02-18 21:49:31 +01:00
void pa_alsa_init_proplist_pcm_info ( pa_core * c , pa_proplist * p , snd_pcm_info_t * pcm_info ) {
2008-05-15 23:34:41 +00:00
static const char * const alsa_class_table [ SND_PCM_CLASS_LAST + 1 ] = {
[ SND_PCM_CLASS_GENERIC ] = " generic " ,
[ SND_PCM_CLASS_MULTI ] = " multi " ,
[ SND_PCM_CLASS_MODEM ] = " modem " ,
[ SND_PCM_CLASS_DIGITIZER ] = " digitizer "
} ;
static const char * const class_table [ SND_PCM_CLASS_LAST + 1 ] = {
[ SND_PCM_CLASS_GENERIC ] = " sound " ,
[ SND_PCM_CLASS_MULTI ] = NULL ,
[ SND_PCM_CLASS_MODEM ] = " modem " ,
[ SND_PCM_CLASS_DIGITIZER ] = NULL
} ;
static const char * const alsa_subclass_table [ SND_PCM_SUBCLASS_LAST + 1 ] = {
[ SND_PCM_SUBCLASS_GENERIC_MIX ] = " generic-mix " ,
[ SND_PCM_SUBCLASS_MULTI_MIX ] = " multi-mix "
} ;
snd_pcm_class_t class ;
snd_pcm_subclass_t subclass ;
2009-03-01 20:34:07 +01:00
const char * n , * id , * sdn ;
2008-05-15 23:34:41 +00:00
int card ;
pa_assert ( p ) ;
pa_assert ( pcm_info ) ;
pa_proplist_sets ( p , PA_PROP_DEVICE_API , " alsa " ) ;
2009-06-17 03:45:14 +02:00
if ( ( class = snd_pcm_info_get_class ( pcm_info ) ) < = SND_PCM_CLASS_LAST ) {
2008-05-15 23:34:41 +00:00
if ( class_table [ class ] )
pa_proplist_sets ( p , PA_PROP_DEVICE_CLASS , class_table [ class ] ) ;
if ( alsa_class_table [ class ] )
pa_proplist_sets ( p , " alsa.class " , alsa_class_table [ class ] ) ;
}
2009-03-01 20:34:07 +01:00
2009-06-17 03:45:14 +02:00
if ( ( subclass = snd_pcm_info_get_subclass ( pcm_info ) ) < = SND_PCM_SUBCLASS_LAST )
2008-05-15 23:34:41 +00:00
if ( alsa_subclass_table [ subclass ] )
pa_proplist_sets ( p , " alsa.subclass " , alsa_subclass_table [ subclass ] ) ;
if ( ( n = snd_pcm_info_get_name ( pcm_info ) ) )
pa_proplist_sets ( p , " alsa.name " , n ) ;
if ( ( id = snd_pcm_info_get_id ( pcm_info ) ) )
pa_proplist_sets ( p , " alsa.id " , id ) ;
pa_proplist_setf ( p , " alsa.subdevice " , " %u " , snd_pcm_info_get_subdevice ( pcm_info ) ) ;
if ( ( sdn = snd_pcm_info_get_subdevice_name ( pcm_info ) ) )
pa_proplist_sets ( p , " alsa.subdevice_name " , sdn ) ;
pa_proplist_setf ( p , " alsa.device " , " %u " , snd_pcm_info_get_device ( pcm_info ) ) ;
2009-03-01 20:34:07 +01:00
if ( ( card = snd_pcm_info_get_card ( pcm_info ) ) > = 0 )
2009-01-24 01:25:11 +01:00
pa_alsa_init_proplist_card ( c , p , card ) ;
2008-05-15 23:34:41 +00:00
}
2009-06-17 03:45:14 +02:00
void pa_alsa_init_proplist_pcm ( pa_core * c , pa_proplist * p , snd_pcm_t * pcm ) {
2009-02-18 21:49:31 +01:00
snd_pcm_hw_params_t * hwparams ;
snd_pcm_info_t * info ;
int bits , err ;
snd_pcm_hw_params_alloca ( & hwparams ) ;
snd_pcm_info_alloca ( & info ) ;
if ( ( err = snd_pcm_hw_params_current ( pcm , hwparams ) ) < 0 )
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Error fetching hardware parameter info: %s " , pa_alsa_strerror ( err ) ) ;
2009-02-18 21:49:31 +01:00
else {
if ( ( bits = snd_pcm_hw_params_get_sbits ( hwparams ) ) > = 0 )
pa_proplist_setf ( p , " alsa.resolution_bits " , " %i " , bits ) ;
}
if ( ( err = snd_pcm_info ( pcm , info ) ) < 0 )
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Error fetching PCM info: %s " , pa_alsa_strerror ( err ) ) ;
2009-02-18 21:49:31 +01:00
else
pa_alsa_init_proplist_pcm_info ( c , p , info ) ;
}
2009-05-12 22:55:50 +02:00
void pa_alsa_init_proplist_ctl ( pa_proplist * p , const char * name ) {
int err ;
snd_ctl_t * ctl ;
snd_ctl_card_info_t * info ;
const char * t ;
pa_assert ( p ) ;
snd_ctl_card_info_alloca ( & info ) ;
if ( ( err = snd_ctl_open ( & ctl , name , 0 ) ) < 0 ) {
2009-09-08 23:46:23 +02:00
pa_log_warn ( " Error opening low-level control device '%s': %s " , name , snd_strerror ( err ) ) ;
2009-05-12 22:55:50 +02:00
return ;
}
if ( ( err = snd_ctl_card_info ( ctl , info ) ) < 0 ) {
pa_log_warn ( " Control device %s card info: %s " , name , snd_strerror ( err ) ) ;
snd_ctl_close ( ctl ) ;
return ;
}
2009-06-17 03:45:14 +02:00
if ( ( t = snd_ctl_card_info_get_mixername ( info ) ) & & * t )
2009-05-12 22:55:50 +02:00
pa_proplist_sets ( p , " alsa.mixer_name " , t ) ;
2009-06-17 03:45:14 +02:00
if ( ( t = snd_ctl_card_info_get_components ( info ) ) & & * t )
2009-05-12 22:55:50 +02:00
pa_proplist_sets ( p , " alsa.components " , t ) ;
snd_ctl_close ( ctl ) ;
}
2008-05-15 23:34:41 +00:00
int pa_alsa_recover_from_poll ( snd_pcm_t * pcm , int revents ) {
snd_pcm_state_t state ;
int err ;
pa_assert ( pcm ) ;
if ( revents & POLLERR )
2008-12-17 22:58:17 +01:00
pa_log_debug ( " Got POLLERR from ALSA " ) ;
2008-05-15 23:34:41 +00:00
if ( revents & POLLNVAL )
pa_log_warn ( " Got POLLNVAL from ALSA " ) ;
if ( revents & POLLHUP )
pa_log_warn ( " Got POLLHUP from ALSA " ) ;
2008-10-22 23:55:52 +02:00
if ( revents & POLLPRI )
pa_log_warn ( " Got POLLPRI from ALSA " ) ;
if ( revents & POLLIN )
2008-12-17 22:58:17 +01:00
pa_log_debug ( " Got POLLIN from ALSA " ) ;
2008-10-22 23:55:52 +02:00
if ( revents & POLLOUT )
2008-12-17 22:58:17 +01:00
pa_log_debug ( " Got POLLOUT from ALSA " ) ;
2008-05-15 23:34:41 +00:00
state = snd_pcm_state ( pcm ) ;
2008-12-17 22:58:17 +01:00
pa_log_debug ( " PCM state is %s " , snd_pcm_state_name ( state ) ) ;
2008-05-15 23:34:41 +00:00
/* Try to recover from this error */
switch ( state ) {
case SND_PCM_STATE_XRUN :
if ( ( err = snd_pcm_recover ( pcm , - EPIPE , 1 ) ) ! = 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s " , pa_alsa_strerror ( err ) ) ;
2008-05-15 23:34:41 +00:00
return - 1 ;
}
break ;
case SND_PCM_STATE_SUSPENDED :
if ( ( err = snd_pcm_recover ( pcm , - ESTRPIPE , 1 ) ) ! = 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s " , pa_alsa_strerror ( err ) ) ;
2008-05-15 23:34:41 +00:00
return - 1 ;
}
break ;
default :
snd_pcm_drop ( pcm ) ;
if ( ( err = snd_pcm_prepare ( pcm ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log_warn ( " Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s " , pa_alsa_strerror ( err ) ) ;
2008-05-15 23:34:41 +00:00
return - 1 ;
}
break ;
}
return 0 ;
}
pa_rtpoll_item * pa_alsa_build_pollfd ( snd_pcm_t * pcm , pa_rtpoll * rtpoll ) {
int n , err ;
struct pollfd * pollfd ;
pa_rtpoll_item * item ;
pa_assert ( pcm ) ;
if ( ( n = snd_pcm_poll_descriptors_count ( pcm ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log ( " snd_pcm_poll_descriptors_count() failed: %s " , pa_alsa_strerror ( n ) ) ;
2008-05-15 23:34:41 +00:00
return NULL ;
}
2008-08-19 22:39:54 +02:00
item = pa_rtpoll_item_new ( rtpoll , PA_RTPOLL_NEVER , ( unsigned ) n ) ;
2008-05-15 23:34:41 +00:00
pollfd = pa_rtpoll_item_get_pollfd ( item , NULL ) ;
2008-08-19 22:39:54 +02:00
if ( ( err = snd_pcm_poll_descriptors ( pcm , pollfd , ( unsigned ) n ) ) < 0 ) {
2009-04-19 19:22:51 +02:00
pa_log ( " snd_pcm_poll_descriptors() failed: %s " , pa_alsa_strerror ( err ) ) ;
2008-05-15 23:34:41 +00:00
pa_rtpoll_item_free ( item ) ;
return NULL ;
}
return item ;
}
2008-10-21 20:00:36 +02:00
2009-02-20 03:21:17 +01:00
snd_pcm_sframes_t pa_alsa_safe_avail ( snd_pcm_t * pcm , size_t hwbuf_size , const pa_sample_spec * ss ) {
2008-10-21 20:00:36 +02:00
snd_pcm_sframes_t n ;
size_t k ;
pa_assert ( pcm ) ;
pa_assert ( hwbuf_size > 0 ) ;
pa_assert ( ss ) ;
/* Some ALSA driver expose weird bugs, let's inform the user about
* what is going on */
2009-02-20 03:21:17 +01:00
n = snd_pcm_avail ( pcm ) ;
2008-10-21 20:00:36 +02:00
if ( n < = 0 )
return n ;
k = ( size_t ) n * pa_frame_size ( ss ) ;
2009-02-24 06:06:04 +01:00
if ( k > = hwbuf_size * 5 | |
k > = pa_bytes_per_second ( ss ) * 10 ) {
2009-02-21 16:33:46 +01:00
PA_ONCE_BEGIN {
2009-02-23 22:56:09 +01:00
char * dn = pa_alsa_get_driver_name_by_pcm ( pcm ) ;
pa_log ( _ ( " snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms). \n "
" Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers. " ) ,
( unsigned long ) k ,
( unsigned long ) ( pa_bytes_to_usec ( k , ss ) / PA_USEC_PER_MSEC ) ,
pa_strnull ( dn ) ) ;
pa_xfree ( dn ) ;
2009-04-10 01:58:39 +02:00
pa_alsa_dump ( PA_LOG_ERROR , pcm ) ;
2009-02-21 16:33:46 +01:00
} PA_ONCE_END ;
2008-10-21 20:00:36 +02:00
2009-02-24 06:06:04 +01:00
/* Mhmm, let's try not to fail completely */
n = ( snd_pcm_sframes_t ) ( hwbuf_size / pa_frame_size ( ss ) ) ;
}
2008-10-21 20:00:36 +02:00
return n ;
}
2009-02-23 22:55:33 +01:00
int pa_alsa_safe_delay ( snd_pcm_t * pcm , snd_pcm_sframes_t * delay , size_t hwbuf_size , const pa_sample_spec * ss ) {
ssize_t k ;
size_t abs_k ;
int r ;
pa_assert ( pcm ) ;
pa_assert ( delay ) ;
pa_assert ( hwbuf_size > 0 ) ;
pa_assert ( ss ) ;
/* Some ALSA driver expose weird bugs, let's inform the user about
* what is going on */
if ( ( r = snd_pcm_delay ( pcm , delay ) ) < 0 )
return r ;
k = ( ssize_t ) * delay * ( ssize_t ) pa_frame_size ( ss ) ;
abs_k = k > = 0 ? ( size_t ) k : ( size_t ) - k ;
2009-02-24 06:06:04 +01:00
if ( abs_k > = hwbuf_size * 5 | |
abs_k > = pa_bytes_per_second ( ss ) * 10 ) {
2009-02-23 22:55:33 +01:00
PA_ONCE_BEGIN {
char * dn = pa_alsa_get_driver_name_by_pcm ( pcm ) ;
pa_log ( _ ( " snd_pcm_delay() returned a value that is exceptionally large: %li bytes (%s%lu ms). \n "
" Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers. " ) ,
( signed long ) k ,
k < 0 ? " - " : " " ,
( unsigned long ) ( pa_bytes_to_usec ( abs_k , ss ) / PA_USEC_PER_MSEC ) ,
pa_strnull ( dn ) ) ;
pa_xfree ( dn ) ;
2009-04-10 01:58:39 +02:00
pa_alsa_dump ( PA_LOG_ERROR , pcm ) ;
2009-02-23 22:55:33 +01:00
} PA_ONCE_END ;
2009-02-24 06:06:04 +01:00
/* Mhmm, let's try not to fail completely */
if ( k < 0 )
* delay = - ( snd_pcm_sframes_t ) ( hwbuf_size / pa_frame_size ( ss ) ) ;
else
* delay = ( snd_pcm_sframes_t ) ( hwbuf_size / pa_frame_size ( ss ) ) ;
}
2009-02-23 22:55:33 +01:00
return 0 ;
}
2008-10-21 20:00:36 +02:00
int pa_alsa_safe_mmap_begin ( snd_pcm_t * pcm , const snd_pcm_channel_area_t * * areas , snd_pcm_uframes_t * offset , snd_pcm_uframes_t * frames , size_t hwbuf_size , const pa_sample_spec * ss ) {
int r ;
snd_pcm_uframes_t before ;
size_t k ;
pa_assert ( pcm ) ;
pa_assert ( areas ) ;
pa_assert ( offset ) ;
pa_assert ( frames ) ;
pa_assert ( hwbuf_size > 0 ) ;
pa_assert ( ss ) ;
before = * frames ;
r = snd_pcm_mmap_begin ( pcm , areas , offset , frames ) ;
if ( r < 0 )
return r ;
k = ( size_t ) * frames * pa_frame_size ( ss ) ;
if ( * frames > before | |
k > = hwbuf_size * 3 | |
k > = pa_bytes_per_second ( ss ) * 10 )
2009-02-21 16:33:46 +01:00
PA_ONCE_BEGIN {
2009-02-23 22:56:09 +01:00
char * dn = pa_alsa_get_driver_name_by_pcm ( pcm ) ;
pa_log ( _ ( " snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms). \n "
" Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers. " ) ,
( unsigned long ) k ,
( unsigned long ) ( pa_bytes_to_usec ( k , ss ) / PA_USEC_PER_MSEC ) ,
pa_strnull ( dn ) ) ;
pa_xfree ( dn ) ;
2009-04-10 01:58:39 +02:00
pa_alsa_dump ( PA_LOG_ERROR , pcm ) ;
2009-02-21 16:33:46 +01:00
} PA_ONCE_END ;
2008-10-21 20:00:36 +02:00
return r ;
}
2009-01-30 02:24:40 +01:00
char * pa_alsa_get_driver_name ( int card ) {
char * t , * m , * n ;
pa_assert ( card > = 0 ) ;
t = pa_sprintf_malloc ( " /sys/class/sound/card%i/device/driver/module " , card ) ;
m = pa_readlink ( t ) ;
pa_xfree ( t ) ;
if ( ! m )
return NULL ;
n = pa_xstrdup ( pa_path_get_filename ( m ) ) ;
pa_xfree ( m ) ;
return n ;
}
2009-02-23 22:54:28 +01:00
char * pa_alsa_get_driver_name_by_pcm ( snd_pcm_t * pcm ) {
int card ;
snd_pcm_info_t * info ;
snd_pcm_info_alloca ( & info ) ;
2009-03-30 18:00:23 +02:00
pa_assert ( pcm ) ;
2009-02-23 22:54:28 +01:00
if ( snd_pcm_info ( pcm , info ) < 0 )
return NULL ;
if ( ( card = snd_pcm_info_get_card ( info ) ) < 0 )
return NULL ;
return pa_alsa_get_driver_name ( card ) ;
}
2009-02-24 06:13:39 +01:00
char * pa_alsa_get_reserve_name ( const char * device ) {
const char * t ;
int i ;
pa_assert ( device ) ;
if ( ( t = strchr ( device , ' : ' ) ) )
device = t + 1 ;
if ( ( i = snd_card_get_index ( device ) ) < 0 ) {
int32_t k ;
if ( pa_atoi ( device , & k ) < 0 )
return NULL ;
i = ( int ) k ;
}
return pa_sprintf_malloc ( " Audio%i " , i ) ;
}
2009-03-30 18:00:23 +02:00
pa_bool_t pa_alsa_pcm_is_hw ( snd_pcm_t * pcm ) {
snd_pcm_info_t * info ;
snd_pcm_info_alloca ( & info ) ;
pa_assert ( pcm ) ;
if ( snd_pcm_info ( pcm , info ) < 0 )
return FALSE ;
return snd_pcm_info_get_card ( info ) > = 0 ;
}
2009-04-04 04:12:42 +02:00
pa_bool_t pa_alsa_pcm_is_modem ( snd_pcm_t * pcm ) {
snd_pcm_info_t * info ;
snd_pcm_info_alloca ( & info ) ;
pa_assert ( pcm ) ;
if ( snd_pcm_info ( pcm , info ) < 0 )
return FALSE ;
return snd_pcm_info_get_class ( info ) = = SND_PCM_CLASS_MODEM ;
}
2009-04-19 19:22:51 +02:00
PA_STATIC_TLS_DECLARE ( cstrerror , pa_xfree ) ;
const char * pa_alsa_strerror ( int errnum ) {
const char * original = NULL ;
char * translated , * t ;
char errbuf [ 128 ] ;
if ( ( t = PA_STATIC_TLS_GET ( cstrerror ) ) )
pa_xfree ( t ) ;
original = snd_strerror ( errnum ) ;
if ( ! original ) {
pa_snprintf ( errbuf , sizeof ( errbuf ) , " Unknown error %i " , errnum ) ;
original = errbuf ;
}
if ( ! ( translated = pa_locale_to_utf8 ( original ) ) ) {
pa_log_warn ( " Unable to convert error string to locale, filtering. " ) ;
translated = pa_utf8_filter ( original ) ;
}
PA_STATIC_TLS_SET ( cstrerror , translated ) ;
return translated ;
}