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
2008-05-15 23:34:41 +00:00
Copyright 2004 - 2008 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
2004-07-16 19:56:36 +00:00
by the Free Software Foundation ; either version 2 of the License ,
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-07-16 17:03:11 +00:00
# include <stdio.h>
2006-01-10 17:51:06 +00:00
2004-07-16 17:03:11 +00:00
# include <asoundlib.h>
2008-08-18 19:55:55 +02:00
# ifdef HAVE_VALGRIND_MEMCHECK_H
# include <valgrind/memcheck.h>
# endif
2006-06-19 21:53:48 +00:00
# include <pulse/xmalloc.h>
2007-10-28 19:13:50 +00:00
# include <pulse/util.h>
2008-05-15 23:34:41 +00:00
# include <pulse/timeval.h>
2006-06-19 21:53:48 +00:00
# include <pulsecore/core-error.h>
# include <pulsecore/core.h>
# include <pulsecore/module.h>
# include <pulsecore/memchunk.h>
# include <pulsecore/sink.h>
# include <pulsecore/modargs.h>
# include <pulsecore/core-util.h>
# include <pulsecore/sample-util.h>
# include <pulsecore/log.h>
2007-10-28 19:13:50 +00:00
# include <pulsecore/macro.h>
# include <pulsecore/thread.h>
# include <pulsecore/core-error.h>
# include <pulsecore/thread-mq.h>
# include <pulsecore/rtpoll.h>
2008-05-15 23:34:41 +00:00
# include <pulsecore/time-smoother.h>
# include <pulsecore/rtclock.h>
2006-02-16 19:19:58 +00:00
2006-02-16 22:08:06 +00:00
# include "alsa-util.h"
2006-02-17 12:10:58 +00:00
# include "module-alsa-source-symdef.h"
2004-07-16 17:03:11 +00:00
2007-11-09 18:25:40 +00:00
PA_MODULE_AUTHOR ( " Lennart Poettering " ) ;
PA_MODULE_DESCRIPTION ( " ALSA Source " ) ;
PA_MODULE_VERSION ( PACKAGE_VERSION ) ;
PA_MODULE_LOAD_ONCE ( FALSE ) ;
2006-04-26 15:40:14 +00:00
PA_MODULE_USAGE (
" source_name=<name for the source> "
" device=<ALSA device> "
2008-05-15 23:34:41 +00:00
" device_id=<ALSA card index> "
2006-04-26 15:40:14 +00:00
" format=<sample format> "
" rate=<sample rate> "
2008-05-15 23:34:41 +00:00
" channels=<number of channels> "
" channel_map=<channel map> "
2006-04-26 15:40:14 +00:00
" fragments=<number of fragments> "
" fragment_size=<fragment size> "
2008-05-15 23:34:41 +00:00
" mmap=<enable memory mapping?> "
" tsched=<enable system timer based scheduling mode?> "
" tsched_buffer_size=<buffer size when using timer based scheduling> "
2008-08-13 13:59:06 +02:00
" tsched_buffer_watermark=<upper fill watermark> " ) ;
2008-05-15 23:34:41 +00:00
static const char * const valid_modargs [ ] = {
" source_name " ,
" device " ,
" device_id " ,
" format " ,
" rate " ,
" channels " ,
" channel_map " ,
" fragments " ,
" fragment_size " ,
" mmap " ,
" tsched " ,
" tsched_buffer_size " ,
" tsched_buffer_watermark " ,
NULL
} ;
2007-10-28 19:13:50 +00:00
# define DEFAULT_DEVICE "default"
2008-05-15 23:34:41 +00:00
# define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
# define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */
# define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
# define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */
2004-09-11 23:17:38 +00:00
2004-07-16 17:03:11 +00:00
struct userdata {
2007-10-28 19:13:50 +00:00
pa_core * core ;
pa_module * module ;
pa_source * source ;
pa_thread * thread ;
pa_thread_mq thread_mq ;
pa_rtpoll * rtpoll ;
2004-07-16 17:03:11 +00:00
snd_pcm_t * pcm_handle ;
2007-10-28 19:13:50 +00:00
pa_alsa_fdlist * mixer_fdl ;
2006-02-26 17:57:58 +00:00
snd_mixer_t * mixer_handle ;
snd_mixer_elem_t * mixer_elem ;
long hw_volume_max , hw_volume_min ;
2008-05-15 23:34:41 +00:00
long hw_dB_max , hw_dB_min ;
pa_bool_t hw_dB_supported ;
2008-08-13 13:59:06 +02:00
pa_bool_t mixer_seperate_channels ;
pa_cvolume hardware_volume ;
2004-07-16 17:03:11 +00:00
2008-05-15 23:34:41 +00:00
size_t frame_size , fragment_size , hwbuf_size , tsched_watermark ;
2007-10-28 19:13:50 +00:00
unsigned nfragments ;
char * device_name ;
2008-05-15 23:34:41 +00:00
pa_bool_t use_mmap , use_tsched ;
2007-10-28 19:13:50 +00:00
pa_rtpoll_item * alsa_rtpoll_item ;
2007-11-13 23:42:15 +00:00
snd_mixer_selem_channel_id_t mixer_map [ SND_MIXER_SCHN_LAST ] ;
2004-07-16 17:03:11 +00:00
2008-05-15 23:34:41 +00:00
pa_smoother * smoother ;
int64_t frame_index ;
snd_pcm_sframes_t hwbuf_unused_frames ;
2004-07-16 17:03:11 +00:00
} ;
2008-05-15 23:34:41 +00:00
static void fix_tsched_watermark ( struct userdata * u ) {
size_t max_use ;
size_t min_sleep , min_wakeup ;
pa_assert ( u ) ;
2008-08-19 22:39:54 +02:00
max_use = u - > hwbuf_size - ( size_t ) u - > hwbuf_unused_frames * u - > frame_size ;
2008-05-15 23:34:41 +00:00
min_sleep = pa_usec_to_bytes ( TSCHED_MIN_SLEEP_USEC , & u - > source - > sample_spec ) ;
min_wakeup = pa_usec_to_bytes ( TSCHED_MIN_WAKEUP_USEC , & u - > source - > sample_spec ) ;
if ( min_sleep > max_use / 2 )
min_sleep = pa_frame_align ( max_use / 2 , & u - > source - > sample_spec ) ;
if ( min_sleep < u - > frame_size )
min_sleep = u - > frame_size ;
if ( min_wakeup > max_use / 2 )
min_wakeup = pa_frame_align ( max_use / 2 , & u - > source - > sample_spec ) ;
if ( min_wakeup < u - > frame_size )
min_wakeup = u - > frame_size ;
if ( u - > tsched_watermark > max_use - min_sleep )
u - > tsched_watermark = max_use - min_sleep ;
if ( u - > tsched_watermark < min_wakeup )
u - > tsched_watermark = min_wakeup ;
}
static pa_usec_t hw_sleep_time ( struct userdata * u , pa_usec_t * sleep_usec , pa_usec_t * process_usec ) {
pa_usec_t wm , usec ;
pa_assert ( u ) ;
usec = pa_source_get_requested_latency_within_thread ( u - > source ) ;
if ( usec = = ( pa_usec_t ) - 1 )
usec = pa_bytes_to_usec ( u - > hwbuf_size , & u - > source - > sample_spec ) ;
/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */
wm = pa_bytes_to_usec ( u - > tsched_watermark , & u - > source - > sample_spec ) ;
if ( usec > = wm ) {
* sleep_usec = usec - wm ;
* process_usec = wm ;
} else
* process_usec = * sleep_usec = usec / = 2 ;
/* pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */
return usec ;
}
static int try_recover ( struct userdata * u , const char * call , int err ) {
pa_assert ( u ) ;
pa_assert ( call ) ;
pa_assert ( err < 0 ) ;
pa_log_debug ( " %s: %s " , call , snd_strerror ( err ) ) ;
pa_assert ( err ! = - EAGAIN ) ;
if ( err = = - EPIPE )
pa_log_debug ( " %s: Buffer overrun! " , call ) ;
if ( ( err = snd_pcm_recover ( u - > pcm_handle , err , 1 ) ) = = 0 ) {
snd_pcm_start ( u - > pcm_handle ) ;
return 0 ;
}
pa_log ( " %s: %s " , call , snd_strerror ( err ) ) ;
return - 1 ;
}
static size_t check_left_to_record ( struct userdata * u , snd_pcm_sframes_t n ) {
size_t left_to_record ;
2008-10-04 02:13:39 +02:00
size_t rec_space = u - > hwbuf_size - ( size_t ) u - > hwbuf_unused_frames * u - > frame_size ;
2008-05-15 23:34:41 +00:00
2008-10-04 02:13:39 +02:00
if ( ( size_t ) n * u - > frame_size < rec_space )
left_to_record = rec_space - ( ( size_t ) n * u - > frame_size ) ;
2008-05-15 23:34:41 +00:00
else
left_to_record = 0 ;
if ( left_to_record > 0 ) {
/* pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); */
} else {
pa_log_info ( " Overrun! " ) ;
if ( u - > use_tsched ) {
size_t old_watermark = u - > tsched_watermark ;
u - > tsched_watermark * = 2 ;
fix_tsched_watermark ( u ) ;
if ( old_watermark ! = u - > tsched_watermark )
pa_log_notice ( " Increasing wakeup watermark to %0.2f ms " ,
( double ) pa_bytes_to_usec ( u - > tsched_watermark , & u - > source - > sample_spec ) / PA_USEC_PER_MSEC ) ;
}
}
return left_to_record ;
}
2008-10-22 23:55:52 +02:00
static int mmap_read ( struct userdata * u , pa_usec_t * sleep_usec , pa_bool_t polled ) {
2007-10-28 19:13:50 +00:00
int work_done = 0 ;
2008-08-11 19:46:11 +02:00
pa_usec_t max_sleep_usec = 0 , process_usec = 0 ;
2008-05-15 23:34:41 +00:00
size_t left_to_record ;
2004-07-16 17:03:11 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( u ) ;
pa_source_assert_ref ( u - > source ) ;
2004-08-04 16:39:30 +00:00
2008-05-15 23:34:41 +00:00
if ( u - > use_tsched )
hw_sleep_time ( u , & max_sleep_usec , & process_usec ) ;
2007-10-28 19:13:50 +00:00
for ( ; ; ) {
snd_pcm_sframes_t n ;
2008-05-15 23:34:41 +00:00
int r ;
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
snd_pcm_hwsync ( u - > pcm_handle ) ;
2007-01-04 13:43:45 +00:00
2008-10-21 20:00:36 +02:00
if ( PA_UNLIKELY ( ( n = pa_alsa_safe_avail_update ( u - > pcm_handle , u - > hwbuf_size , & u - > source - > sample_spec ) ) < 0 ) ) {
2004-07-16 17:03:11 +00:00
2008-08-19 22:39:54 +02:00
if ( ( r = try_recover ( u , " snd_pcm_avail_update " , ( int ) n ) ) = = 0 )
2007-10-28 19:13:50 +00:00
continue ;
2006-08-11 15:08:09 +00:00
2008-05-15 23:34:41 +00:00
return r ;
2007-10-28 19:13:50 +00:00
}
2006-08-11 15:08:09 +00:00
2008-05-15 23:34:41 +00:00
left_to_record = check_left_to_record ( u , n ) ;
2006-08-11 15:08:09 +00:00
2008-05-15 23:34:41 +00:00
if ( u - > use_tsched )
2008-10-22 23:55:52 +02:00
if ( ! polled & &
pa_bytes_to_usec ( left_to_record , & u - > source - > sample_spec ) > process_usec + max_sleep_usec / 2 )
2008-05-15 23:34:41 +00:00
break ;
2007-01-04 13:43:45 +00:00
2008-10-22 23:55:52 +02:00
if ( PA_UNLIKELY ( n < = 0 ) ) {
if ( polled )
pa_log ( " ALSA woke us up to read new data from the device, but there was actually nothing to read! "
2009-01-15 19:16:55 +01:00
" Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
" We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0. " ) ;
2008-10-22 23:55:52 +02:00
2008-05-15 23:34:41 +00:00
break ;
2008-10-22 23:55:52 +02:00
}
polled = FALSE ;
2006-08-11 15:08:09 +00:00
2008-05-15 23:34:41 +00:00
for ( ; ; ) {
int err ;
const snd_pcm_channel_area_t * areas ;
snd_pcm_uframes_t offset , frames = ( snd_pcm_uframes_t ) n ;
pa_memchunk chunk ;
void * p ;
2008-08-19 22:39:54 +02:00
snd_pcm_sframes_t sframes ;
2006-08-11 15:08:09 +00:00
2008-05-15 23:34:41 +00:00
/* pa_log_debug("%lu frames to read", (unsigned long) frames); */
2006-08-11 15:08:09 +00:00
2008-10-21 20:00:36 +02:00
if ( PA_UNLIKELY ( ( err = pa_alsa_safe_mmap_begin ( u - > pcm_handle , & areas , & offset , & frames , u - > hwbuf_size , & u - > source - > sample_spec ) ) < 0 ) ) {
2004-07-16 17:03:11 +00:00
2008-05-15 23:34:41 +00:00
if ( ( r = try_recover ( u , " snd_pcm_mmap_begin " , err ) ) = = 0 )
continue ;
2007-03-02 09:20:54 +00:00
2008-05-15 23:34:41 +00:00
return r ;
}
2007-03-02 09:20:54 +00:00
2008-05-15 23:34:41 +00:00
/* Make sure that if these memblocks need to be copied they will fit into one slot */
if ( frames > pa_mempool_block_size_max ( u - > source - > core - > mempool ) / u - > frame_size )
frames = pa_mempool_block_size_max ( u - > source - > core - > mempool ) / u - > frame_size ;
2007-03-02 09:20:54 +00:00
2008-05-15 23:34:41 +00:00
/* Check these are multiples of 8 bit */
pa_assert ( ( areas [ 0 ] . first & 7 ) = = 0 ) ;
pa_assert ( ( areas [ 0 ] . step & 7 ) = = 0 ) ;
2007-03-02 09:20:54 +00:00
2008-05-15 23:34:41 +00:00
/* We assume a single interleaved memory buffer */
pa_assert ( ( areas [ 0 ] . first > > 3 ) = = 0 ) ;
pa_assert ( ( areas [ 0 ] . step > > 3 ) = = u - > frame_size ) ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
p = ( uint8_t * ) areas [ 0 ] . addr + ( offset * u - > frame_size ) ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
chunk . memblock = pa_memblock_new_fixed ( u - > core - > mempool , p , frames * u - > frame_size , TRUE ) ;
chunk . length = pa_memblock_get_length ( chunk . memblock ) ;
chunk . index = 0 ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
pa_source_post ( u - > source , & chunk ) ;
pa_memblock_unref_fixed ( chunk . memblock ) ;
2007-10-28 19:13:50 +00:00
2008-08-19 22:39:54 +02:00
if ( PA_UNLIKELY ( ( sframes = snd_pcm_mmap_commit ( u - > pcm_handle , offset , frames ) ) < 0 ) ) {
2007-10-28 19:13:50 +00:00
2008-08-19 22:39:54 +02:00
if ( ( r = try_recover ( u , " snd_pcm_mmap_commit " , ( int ) sframes ) ) = = 0 )
2008-05-15 23:34:41 +00:00
continue ;
2007-03-02 09:20:54 +00:00
2008-05-15 23:34:41 +00:00
return r ;
}
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
work_done = 1 ;
2007-10-28 19:13:50 +00:00
2008-08-19 22:39:54 +02:00
u - > frame_index + = ( int64_t ) frames ;
2008-05-15 23:34:41 +00:00
/* pa_log_debug("read %lu frames", (unsigned long) frames); */
2007-03-02 09:20:54 +00:00
2008-05-15 23:34:41 +00:00
if ( frames > = ( snd_pcm_uframes_t ) n )
break ;
2007-10-28 19:13:50 +00:00
2008-08-19 22:39:54 +02:00
n - = ( snd_pcm_sframes_t ) frames ;
2008-05-15 23:34:41 +00:00
}
2007-10-28 19:13:50 +00:00
}
2008-05-15 23:34:41 +00:00
* sleep_usec = pa_bytes_to_usec ( left_to_record , & u - > source - > sample_spec ) - process_usec ;
return work_done ;
2007-03-02 09:20:54 +00:00
}
2008-10-22 23:55:52 +02:00
static int unix_read ( struct userdata * u , pa_usec_t * sleep_usec , pa_bool_t polled ) {
2007-10-28 19:13:50 +00:00
int work_done = 0 ;
2008-08-11 19:46:11 +02:00
pa_usec_t max_sleep_usec = 0 , process_usec = 0 ;
2008-05-15 23:34:41 +00:00
size_t left_to_record ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( u ) ;
pa_source_assert_ref ( u - > source ) ;
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
if ( u - > use_tsched )
hw_sleep_time ( u , & max_sleep_usec , & process_usec ) ;
2007-10-28 19:13:50 +00:00
for ( ; ; ) {
2008-05-15 23:34:41 +00:00
snd_pcm_sframes_t n ;
int r ;
snd_pcm_hwsync ( u - > pcm_handle ) ;
2008-10-21 20:00:36 +02:00
if ( PA_UNLIKELY ( ( n = pa_alsa_safe_avail_update ( u - > pcm_handle , u - > hwbuf_size , & u - > source - > sample_spec ) ) < 0 ) ) {
2008-05-15 23:34:41 +00:00
2008-08-19 22:39:54 +02:00
if ( ( r = try_recover ( u , " snd_pcm_avail_update " , ( int ) n ) ) = = 0 )
2008-05-15 23:34:41 +00:00
continue ;
return r ;
2004-07-16 17:03:11 +00:00
}
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
left_to_record = check_left_to_record ( u , n ) ;
2004-07-16 17:03:11 +00:00
2008-05-15 23:34:41 +00:00
if ( u - > use_tsched )
2008-10-22 23:55:52 +02:00
if ( ! polled & &
pa_bytes_to_usec ( left_to_record , & u - > source - > sample_spec ) > process_usec + max_sleep_usec / 2 )
2008-05-15 23:34:41 +00:00
break ;
2007-01-04 13:43:45 +00:00
2008-10-22 23:55:52 +02:00
if ( PA_UNLIKELY ( n < = 0 ) ) {
if ( polled )
pa_log ( " ALSA woke us up to read new data from the device, but there was actually nothing to read! "
2009-01-15 19:16:55 +01:00
" Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
" We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0. " ) ;
2008-10-22 23:55:52 +02:00
2007-10-28 19:13:50 +00:00
return work_done ;
2008-10-22 23:55:52 +02:00
}
polled = FALSE ;
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
for ( ; ; ) {
void * p ;
snd_pcm_sframes_t frames ;
pa_memchunk chunk ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
chunk . memblock = pa_memblock_new ( u - > core - > mempool , ( size_t ) - 1 ) ;
2007-10-28 19:13:50 +00:00
2008-08-19 22:39:54 +02:00
frames = ( snd_pcm_sframes_t ) ( pa_memblock_get_length ( chunk . memblock ) / u - > frame_size ) ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
if ( frames > n )
frames = n ;
2004-07-16 17:03:11 +00:00
2008-05-15 23:34:41 +00:00
/* pa_log_debug("%lu frames to read", (unsigned long) n); */
2007-03-02 09:20:54 +00:00
2008-05-15 23:34:41 +00:00
p = pa_memblock_acquire ( chunk . memblock ) ;
2008-08-19 22:39:54 +02:00
frames = snd_pcm_readi ( u - > pcm_handle , ( uint8_t * ) p , ( snd_pcm_uframes_t ) frames ) ;
2008-05-15 23:34:41 +00:00
pa_memblock_release ( chunk . memblock ) ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
pa_assert ( frames ! = 0 ) ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
if ( PA_UNLIKELY ( frames < 0 ) ) {
pa_memblock_unref ( chunk . memblock ) ;
2007-10-28 19:13:50 +00:00
2008-08-19 22:39:54 +02:00
if ( ( r = try_recover ( u , " snd_pcm_readi " , ( int ) ( frames ) ) ) = = 0 )
2008-05-15 23:34:41 +00:00
continue ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
return r ;
2007-03-02 09:20:54 +00:00
}
2008-05-15 23:34:41 +00:00
chunk . index = 0 ;
2008-08-19 22:39:54 +02:00
chunk . length = ( size_t ) frames * u - > frame_size ;
2008-05-15 23:34:41 +00:00
pa_source_post ( u - > source , & chunk ) ;
pa_memblock_unref ( chunk . memblock ) ;
work_done = 1 ;
u - > frame_index + = frames ;
/* pa_log_debug("read %lu frames", (unsigned long) frames); */
if ( frames > = n )
break ;
n - = frames ;
2007-10-28 19:13:50 +00:00
}
2008-05-15 23:34:41 +00:00
}
2007-03-02 09:20:54 +00:00
2008-05-15 23:34:41 +00:00
* sleep_usec = pa_bytes_to_usec ( left_to_record , & u - > source - > sample_spec ) - process_usec ;
return work_done ;
}
2006-08-11 15:08:09 +00:00
2008-05-15 23:34:41 +00:00
static void update_smoother ( struct userdata * u ) {
snd_pcm_sframes_t delay = 0 ;
int64_t frames ;
int err ;
pa_usec_t now1 , now2 ;
2004-07-16 17:03:11 +00:00
2008-05-15 23:34:41 +00:00
pa_assert ( u ) ;
pa_assert ( u - > pcm_handle ) ;
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
/* Let's update the time smoother */
snd_pcm_hwsync ( u - > pcm_handle ) ;
snd_pcm_avail_update ( u - > pcm_handle ) ;
if ( PA_UNLIKELY ( ( err = snd_pcm_delay ( u - > pcm_handle , & delay ) ) < 0 ) ) {
pa_log_warn ( " Failed to get delay: %s " , snd_strerror ( err ) ) ;
return ;
2007-10-28 19:13:50 +00:00
}
2008-05-15 23:34:41 +00:00
frames = u - > frame_index + delay ;
now1 = pa_rtclock_usec ( ) ;
2008-08-19 22:39:54 +02:00
now2 = pa_bytes_to_usec ( ( uint64_t ) frames * u - > frame_size , & u - > source - > sample_spec ) ;
2008-05-15 23:34:41 +00:00
pa_smoother_put ( u - > smoother , now1 , now2 ) ;
2007-10-28 19:13:50 +00:00
}
2004-07-16 17:03:11 +00:00
2007-10-28 19:13:50 +00:00
static pa_usec_t source_get_latency ( struct userdata * u ) {
pa_usec_t r = 0 ;
2008-05-15 23:34:41 +00:00
int64_t delay ;
pa_usec_t now1 , now2 ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( u ) ;
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
now1 = pa_rtclock_usec ( ) ;
now2 = pa_smoother_get ( u - > smoother , now1 ) ;
2008-08-19 22:39:54 +02:00
delay = ( int64_t ) now2 - ( int64_t ) pa_bytes_to_usec ( ( uint64_t ) u - > frame_index * u - > frame_size , & u - > source - > sample_spec ) ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
if ( delay > 0 )
r = ( pa_usec_t ) delay ;
2007-10-28 19:13:50 +00:00
return r ;
}
static int build_pollfd ( struct userdata * u ) {
pa_assert ( u ) ;
pa_assert ( u - > pcm_handle ) ;
if ( u - > alsa_rtpoll_item )
pa_rtpoll_item_free ( u - > alsa_rtpoll_item ) ;
2008-05-15 23:34:41 +00:00
if ( ! ( u - > alsa_rtpoll_item = pa_alsa_build_pollfd ( u - > pcm_handle , u - > rtpoll ) ) )
2007-10-28 19:13:50 +00:00
return - 1 ;
return 0 ;
2004-07-16 17:03:11 +00:00
}
2007-10-28 19:13:50 +00:00
static int suspend ( struct userdata * u ) {
pa_assert ( u ) ;
pa_assert ( u - > pcm_handle ) ;
2004-07-16 17:03:11 +00:00
2008-05-15 23:34:41 +00:00
pa_smoother_pause ( u - > smoother , pa_rtclock_usec ( ) ) ;
2007-10-28 19:13:50 +00:00
/* Let's suspend */
snd_pcm_close ( u - > pcm_handle ) ;
u - > pcm_handle = NULL ;
2004-07-16 17:03:11 +00:00
2007-10-28 19:13:50 +00:00
if ( u - > alsa_rtpoll_item ) {
pa_rtpoll_item_free ( u - > alsa_rtpoll_item ) ;
u - > alsa_rtpoll_item = NULL ;
}
2007-03-02 09:20:54 +00:00
2007-10-28 19:13:50 +00:00
pa_log_info ( " Device suspended... " ) ;
return 0 ;
2004-07-16 17:03:11 +00:00
}
2008-05-15 23:34:41 +00:00
static int update_sw_params ( struct userdata * u ) {
snd_pcm_uframes_t avail_min ;
int err ;
pa_assert ( u ) ;
/* Use the full buffer if noone asked us for anything specific */
u - > hwbuf_unused_frames = 0 ;
if ( u - > use_tsched ) {
pa_usec_t latency ;
if ( ( latency = pa_source_get_requested_latency_within_thread ( u - > source ) ) ! = ( pa_usec_t ) - 1 ) {
size_t b ;
2008-10-04 02:13:39 +02:00
pa_log_debug ( " latency set to %0.2fms " , ( double ) latency / PA_USEC_PER_MSEC ) ;
2008-05-15 23:34:41 +00:00
b = pa_usec_to_bytes ( latency , & u - > source - > sample_spec ) ;
/* We need at least one sample in our buffer */
if ( PA_UNLIKELY ( b < u - > frame_size ) )
b = u - > frame_size ;
2008-08-19 22:39:54 +02:00
u - > hwbuf_unused_frames = ( snd_pcm_sframes_t )
( PA_LIKELY ( b < u - > hwbuf_size ) ?
( ( u - > hwbuf_size - b ) / u - > frame_size ) : 0 ) ;
2008-05-15 23:34:41 +00:00
}
2009-01-08 21:13:18 +01:00
fix_tsched_watermark ( u ) ;
2008-05-15 23:34:41 +00:00
}
pa_log_debug ( " hwbuf_unused_frames=%lu " , ( unsigned long ) u - > hwbuf_unused_frames ) ;
avail_min = 1 ;
if ( u - > use_tsched ) {
pa_usec_t sleep_usec , process_usec ;
hw_sleep_time ( u , & sleep_usec , & process_usec ) ;
2009-01-14 00:06:40 +01:00
avail_min + = pa_usec_to_bytes ( sleep_usec , & u - > source - > sample_spec ) / u - > frame_size ;
2008-05-15 23:34:41 +00:00
}
pa_log_debug ( " setting avail_min=%lu " , ( unsigned long ) avail_min ) ;
if ( ( err = pa_alsa_set_sw_params ( u - > pcm_handle , avail_min ) ) < 0 ) {
pa_log ( " Failed to set software parameters: %s " , snd_strerror ( err ) ) ;
return err ;
}
return 0 ;
}
2007-10-28 19:13:50 +00:00
static int unsuspend ( struct userdata * u ) {
pa_sample_spec ss ;
2007-11-13 17:37:44 +00:00
int err ;
2008-05-15 23:34:41 +00:00
pa_bool_t b , d ;
2007-10-28 19:13:50 +00:00
unsigned nfrags ;
snd_pcm_uframes_t period_size ;
2006-02-26 21:50:55 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( u ) ;
pa_assert ( ! u - > pcm_handle ) ;
2006-02-26 21:50:55 +00:00
2007-10-28 19:13:50 +00:00
pa_log_info ( " Trying resume... " ) ;
2006-04-28 07:29:32 +00:00
2007-10-28 19:13:50 +00:00
snd_config_update_free_global ( ) ;
2008-09-09 00:10:54 +03:00
if ( ( err = snd_pcm_open ( & u - > pcm_handle , u - > device_name , SND_PCM_STREAM_CAPTURE ,
/*SND_PCM_NONBLOCK|*/
SND_PCM_NO_AUTO_RESAMPLE |
SND_PCM_NO_AUTO_CHANNELS |
SND_PCM_NO_AUTO_FORMAT ) ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_log ( " Error opening PCM device %s: %s " , u - > device_name , snd_strerror ( err ) ) ;
goto fail ;
}
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
ss = u - > source - > sample_spec ;
nfrags = u - > nfragments ;
period_size = u - > fragment_size / u - > frame_size ;
b = u - > use_mmap ;
2008-05-15 23:34:41 +00:00
d = u - > use_tsched ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
if ( ( err = pa_alsa_set_hw_params ( u - > pcm_handle , & ss , & nfrags , & period_size , u - > hwbuf_size / u - > frame_size , & b , & d , TRUE ) ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_log ( " Failed to set hardware parameters: %s " , snd_strerror ( err ) ) ;
goto fail ;
2006-02-26 21:50:55 +00:00
}
2008-05-15 23:34:41 +00:00
if ( b ! = u - > use_mmap | | d ! = u - > use_tsched ) {
2007-10-28 19:13:50 +00:00
pa_log_warn ( " Resume failed, couldn't get original access mode. " ) ;
goto fail ;
}
if ( ! pa_sample_spec_equal ( & ss , & u - > source - > sample_spec ) ) {
pa_log_warn ( " Resume failed, couldn't restore original sample settings. " ) ;
goto fail ;
}
if ( nfrags ! = u - > nfragments | | period_size * u - > frame_size ! = u - > fragment_size ) {
2008-09-09 02:17:01 +03:00
pa_log_warn ( " Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu) " ,
( unsigned long ) u - > nfragments , ( unsigned long ) u - > fragment_size ,
( unsigned long ) nfrags , period_size * u - > frame_size ) ;
2007-10-28 19:13:50 +00:00
goto fail ;
}
2008-05-15 23:34:41 +00:00
if ( update_sw_params ( u ) < 0 )
2007-10-28 19:13:50 +00:00
goto fail ;
if ( build_pollfd ( u ) < 0 )
goto fail ;
/* FIXME: We need to reload the volume somehow */
2008-05-15 23:34:41 +00:00
snd_pcm_start ( u - > pcm_handle ) ;
pa_smoother_resume ( u - > smoother , pa_rtclock_usec ( ) ) ;
2007-10-28 19:13:50 +00:00
pa_log_info ( " Resumed successfully... " ) ;
2006-02-26 21:50:55 +00:00
return 0 ;
2007-10-28 19:13:50 +00:00
fail :
if ( u - > pcm_handle ) {
snd_pcm_close ( u - > pcm_handle ) ;
u - > pcm_handle = NULL ;
}
return - 1 ;
2006-02-26 21:50:55 +00:00
}
2007-10-28 19:13:50 +00:00
static int source_process_msg ( pa_msgobject * o , int code , void * data , int64_t offset , pa_memchunk * chunk ) {
struct userdata * u = PA_SOURCE ( o ) - > userdata ;
switch ( code ) {
case PA_SOURCE_MESSAGE_GET_LATENCY : {
pa_usec_t r = 0 ;
if ( u - > pcm_handle )
r = source_get_latency ( u ) ;
* ( ( pa_usec_t * ) data ) = r ;
return 0 ;
}
case PA_SOURCE_MESSAGE_SET_STATE :
switch ( ( pa_source_state_t ) PA_PTR_TO_UINT ( data ) ) {
case PA_SOURCE_SUSPENDED :
2008-05-15 23:34:41 +00:00
pa_assert ( PA_SOURCE_IS_OPENED ( u - > source - > thread_info . state ) ) ;
2007-10-28 19:13:50 +00:00
if ( suspend ( u ) < 0 )
return - 1 ;
break ;
case PA_SOURCE_IDLE :
case PA_SOURCE_RUNNING :
if ( u - > source - > thread_info . state = = PA_SOURCE_INIT ) {
if ( build_pollfd ( u ) < 0 )
return - 1 ;
snd_pcm_start ( u - > pcm_handle ) ;
}
if ( u - > source - > thread_info . state = = PA_SOURCE_SUSPENDED ) {
if ( unsuspend ( u ) < 0 )
return - 1 ;
}
break ;
2004-09-16 00:05:56 +00:00
2007-10-28 19:13:50 +00:00
case PA_SOURCE_UNLINKED :
case PA_SOURCE_INIT :
;
}
break ;
}
return pa_source_process_msg ( o , code , data , offset , chunk ) ;
}
static int mixer_callback ( snd_mixer_elem_t * elem , unsigned int mask ) {
struct userdata * u = snd_mixer_elem_get_callback_private ( elem ) ;
pa_assert ( u ) ;
pa_assert ( u - > mixer_handle ) ;
if ( mask = = SND_CTL_EVENT_MASK_REMOVE )
2004-09-16 00:05:56 +00:00
return 0 ;
2007-10-28 19:13:50 +00:00
if ( mask & SND_CTL_EVENT_MASK_VALUE ) {
2008-08-13 13:59:06 +02:00
pa_source_get_volume ( u - > source , TRUE ) ;
pa_source_get_mute ( u - > source , TRUE ) ;
2004-09-16 00:05:56 +00:00
}
2007-10-28 19:13:50 +00:00
return 0 ;
2004-09-16 00:05:56 +00:00
}
2008-10-01 03:26:45 +02:00
static pa_volume_t from_alsa_volume ( struct userdata * u , long alsa_vol ) {
return ( pa_volume_t ) round ( ( ( double ) ( alsa_vol - u - > hw_volume_min ) * PA_VOLUME_NORM ) /
( double ) ( u - > hw_volume_max - u - > hw_volume_min ) ) ;
}
static long to_alsa_volume ( struct userdata * u , pa_volume_t vol ) {
long alsa_vol ;
alsa_vol = ( long ) round ( ( ( double ) vol * ( double ) ( u - > hw_volume_max - u - > hw_volume_min ) )
/ PA_VOLUME_NORM ) + u - > hw_volume_min ;
return PA_CLAMP_UNLIKELY ( alsa_vol , u - > hw_volume_min , u - > hw_volume_max ) ;
}
2007-10-28 19:13:50 +00:00
static int source_get_volume_cb ( pa_source * s ) {
2006-02-26 17:57:58 +00:00
struct userdata * u = s - > userdata ;
int err ;
2008-08-13 13:59:06 +02:00
unsigned i ;
pa_cvolume r ;
char t [ PA_CVOLUME_SNPRINT_MAX ] ;
2006-02-26 17:57:58 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( u ) ;
pa_assert ( u - > mixer_elem ) ;
2006-02-26 17:57:58 +00:00
2008-08-13 13:59:06 +02:00
if ( u - > mixer_seperate_channels ) {
2007-01-04 13:43:45 +00:00
2008-08-13 13:59:06 +02:00
r . channels = s - > sample_spec . channels ;
2007-01-04 13:43:45 +00:00
2008-08-13 13:59:06 +02:00
for ( i = 0 ; i < s - > sample_spec . channels ; i + + ) {
long alsa_vol ;
2008-05-15 23:34:41 +00:00
2008-08-13 13:59:06 +02:00
if ( u - > hw_dB_supported ) {
if ( ( err = snd_mixer_selem_get_capture_dB ( u - > mixer_elem , u - > mixer_map [ i ] , & alsa_vol ) ) < 0 )
goto fail ;
2008-08-18 19:55:55 +02:00
# ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED ( & alsa_vol , sizeof ( alsa_vol ) ) ;
# endif
2008-10-08 04:02:10 +02:00
r . values [ i ] = pa_sw_volume_from_dB ( ( double ) ( alsa_vol - u - > hw_dB_max ) / 100.0 ) ;
2008-08-13 13:59:06 +02:00
} else {
if ( ( err = snd_mixer_selem_get_capture_volume ( u - > mixer_elem , u - > mixer_map [ i ] , & alsa_vol ) ) < 0 )
goto fail ;
2006-08-07 16:50:15 +00:00
2008-10-01 03:26:45 +02:00
r . values [ i ] = from_alsa_volume ( u , alsa_vol ) ;
2008-08-13 13:59:06 +02:00
}
2008-05-15 23:34:41 +00:00
}
2008-08-13 13:59:06 +02:00
} else {
long alsa_vol ;
2008-10-01 03:26:45 +02:00
if ( u - > hw_dB_supported ) {
2008-08-13 13:59:06 +02:00
2008-10-01 03:26:45 +02:00
if ( ( err = snd_mixer_selem_get_capture_dB ( u - > mixer_elem , SND_MIXER_SCHN_MONO , & alsa_vol ) ) < 0 )
goto fail ;
2007-01-04 13:43:45 +00:00
2008-08-18 19:55:55 +02:00
# ifdef HAVE_VALGRIND_MEMCHECK_H
2008-10-01 03:26:45 +02:00
VALGRIND_MAKE_MEM_DEFINED ( & alsa_vol , sizeof ( alsa_vol ) ) ;
2008-08-18 19:55:55 +02:00
# endif
2008-10-08 04:02:10 +02:00
pa_cvolume_set ( & r , s - > sample_spec . channels , pa_sw_volume_from_dB ( ( double ) ( alsa_vol - u - > hw_dB_max ) / 100.0 ) ) ;
2008-10-01 03:26:45 +02:00
} else {
if ( ( err = snd_mixer_selem_get_capture_volume ( u - > mixer_elem , SND_MIXER_SCHN_MONO , & alsa_vol ) ) < 0 )
goto fail ;
pa_cvolume_set ( & r , s - > sample_spec . channels , from_alsa_volume ( u , alsa_vol ) ) ;
}
2008-08-13 13:59:06 +02:00
}
pa_log_debug ( " Read hardware volume: %s " , pa_cvolume_snprint ( t , sizeof ( t ) , & r ) ) ;
if ( ! pa_cvolume_equal ( & u - > hardware_volume , & r ) ) {
u - > hardware_volume = s - > volume = r ;
if ( u - > hw_dB_supported ) {
pa_cvolume reset ;
/* Hmm, so the hardware volume changed, let's reset our software volume */
pa_cvolume_reset ( & reset , s - > sample_spec . channels ) ;
pa_source_set_soft_volume ( s , & reset ) ;
}
2006-02-26 17:57:58 +00:00
}
return 0 ;
fail :
2006-08-18 21:38:40 +00:00
pa_log_error ( " Unable to read volume: %s " , snd_strerror ( err ) ) ;
2007-10-28 19:13:50 +00:00
2006-02-26 17:57:58 +00:00
return - 1 ;
}
2007-10-28 19:13:50 +00:00
static int source_set_volume_cb ( pa_source * s ) {
2006-02-26 17:57:58 +00:00
struct userdata * u = s - > userdata ;
int err ;
2008-08-13 13:59:06 +02:00
unsigned i ;
pa_cvolume r ;
2006-02-26 17:57:58 +00:00
2007-10-28 19:13:50 +00:00
pa_assert ( u ) ;
pa_assert ( u - > mixer_elem ) ;
2006-02-26 17:57:58 +00:00
2008-10-01 03:26:45 +02:00
if ( u - > mixer_seperate_channels ) {
2006-04-25 07:54:49 +00:00
2008-08-13 13:59:06 +02:00
r . channels = s - > sample_spec . channels ;
2007-10-28 19:13:50 +00:00
2008-08-13 13:59:06 +02:00
for ( i = 0 ; i < s - > sample_spec . channels ; i + + ) {
long alsa_vol ;
pa_volume_t vol ;
2008-05-15 23:34:41 +00:00
2008-08-13 13:59:06 +02:00
vol = s - > volume . values [ i ] ;
2008-05-15 23:34:41 +00:00
2008-08-13 13:59:06 +02:00
if ( u - > hw_dB_supported ) {
2006-04-08 00:19:52 +00:00
2008-08-13 13:59:06 +02:00
alsa_vol = ( long ) ( pa_sw_volume_to_dB ( vol ) * 100 ) ;
2008-10-08 04:02:10 +02:00
alsa_vol + = u - > hw_dB_max ;
2008-08-13 13:59:06 +02:00
alsa_vol = PA_CLAMP_UNLIKELY ( alsa_vol , u - > hw_dB_min , u - > hw_dB_max ) ;
2008-05-15 23:34:41 +00:00
2008-08-13 13:59:06 +02:00
if ( ( err = snd_mixer_selem_set_capture_dB ( u - > mixer_elem , u - > mixer_map [ i ] , alsa_vol , 1 ) ) < 0 )
goto fail ;
2008-05-15 23:34:41 +00:00
2008-08-13 13:59:06 +02:00
if ( ( err = snd_mixer_selem_get_capture_dB ( u - > mixer_elem , u - > mixer_map [ i ] , & alsa_vol ) ) < 0 )
goto fail ;
2008-10-08 04:02:10 +02:00
r . values [ i ] = pa_sw_volume_from_dB ( ( double ) ( alsa_vol - u - > hw_dB_max ) / 100.0 ) ;
2008-05-15 23:34:41 +00:00
2008-10-01 03:26:45 +02:00
} else {
alsa_vol = to_alsa_volume ( u , vol ) ;
2008-08-13 13:59:06 +02:00
if ( ( err = snd_mixer_selem_set_capture_volume ( u - > mixer_elem , u - > mixer_map [ i ] , alsa_vol ) ) < 0 )
goto fail ;
if ( ( err = snd_mixer_selem_get_capture_volume ( u - > mixer_elem , u - > mixer_map [ i ] , & alsa_vol ) ) < 0 )
goto fail ;
2008-10-01 03:26:45 +02:00
r . values [ i ] = from_alsa_volume ( u , alsa_vol ) ;
2008-08-13 13:59:06 +02:00
}
2008-05-15 23:34:41 +00:00
}
2006-04-08 00:19:52 +00:00
2008-08-13 13:59:06 +02:00
} else {
pa_volume_t vol ;
long alsa_vol ;
vol = pa_cvolume_max ( & s - > volume ) ;
2006-08-07 16:50:15 +00:00
2008-10-01 03:26:45 +02:00
if ( u - > hw_dB_supported ) {
alsa_vol = ( long ) ( pa_sw_volume_to_dB ( vol ) * 100 ) ;
2008-10-08 04:02:10 +02:00
alsa_vol + = u - > hw_dB_max ;
2008-10-01 03:26:45 +02:00
alsa_vol = PA_CLAMP_UNLIKELY ( alsa_vol , u - > hw_dB_min , u - > hw_dB_max ) ;
2008-08-13 13:59:06 +02:00
2008-10-01 03:26:45 +02:00
if ( ( err = snd_mixer_selem_set_capture_dB_all ( u - > mixer_elem , alsa_vol , 1 ) ) < 0 )
goto fail ;
2008-08-13 13:59:06 +02:00
2008-10-01 03:26:45 +02:00
if ( ( err = snd_mixer_selem_get_capture_dB ( u - > mixer_elem , SND_MIXER_SCHN_MONO , & alsa_vol ) ) < 0 )
goto fail ;
2008-05-15 23:34:41 +00:00
2008-10-08 04:02:10 +02:00
pa_cvolume_set ( & r , s - > volume . channels , pa_sw_volume_from_dB ( ( double ) ( alsa_vol - u - > hw_dB_max ) / 100.0 ) ) ;
2008-10-01 03:26:45 +02:00
} else {
alsa_vol = to_alsa_volume ( u , vol ) ;
if ( ( err = snd_mixer_selem_set_capture_volume_all ( u - > mixer_elem , alsa_vol ) ) < 0 )
goto fail ;
if ( ( err = snd_mixer_selem_get_capture_volume ( u - > mixer_elem , SND_MIXER_SCHN_MONO , & alsa_vol ) ) < 0 )
goto fail ;
pa_cvolume_set ( & r , s - > sample_spec . channels , from_alsa_volume ( u , alsa_vol ) ) ;
}
2006-02-26 17:57:58 +00:00
}
2008-08-13 13:59:06 +02:00
u - > hardware_volume = r ;
if ( u - > hw_dB_supported ) {
char t [ PA_CVOLUME_SNPRINT_MAX ] ;
/* Match exactly what the user requested by software */
2008-10-04 01:48:13 +02:00
pa_sw_cvolume_divide ( & r , & s - > volume , & r ) ;
2008-08-13 13:59:06 +02:00
pa_source_set_soft_volume ( s , & r ) ;
pa_log_debug ( " Requested volume: %s " , pa_cvolume_snprint ( t , sizeof ( t ) , & s - > volume ) ) ;
pa_log_debug ( " Got hardware volume: %s " , pa_cvolume_snprint ( t , sizeof ( t ) , & u - > hardware_volume ) ) ;
pa_log_debug ( " Calculated software volume: %s " , pa_cvolume_snprint ( t , sizeof ( t ) , & r ) ) ;
} else
/* We can't match exactly what the user requested, hence let's
* at least tell the user about it */
s - > volume = r ;
2006-02-26 17:57:58 +00:00
return 0 ;
fail :
2006-08-18 21:38:40 +00:00
pa_log_error ( " Unable to set volume: %s " , snd_strerror ( err ) ) ;
2007-10-28 19:13:50 +00:00
2006-02-26 17:57:58 +00:00
return - 1 ;
}
2007-10-28 19:13:50 +00:00
static int source_get_mute_cb ( pa_source * s ) {
2006-02-26 17:57:58 +00:00
struct userdata * u = s - > userdata ;
int err , sw ;
2007-10-28 19:13:50 +00:00
pa_assert ( u ) ;
pa_assert ( u - > mixer_elem ) ;
2006-02-26 17:57:58 +00:00
2007-10-28 19:13:50 +00:00
if ( ( err = snd_mixer_selem_get_capture_switch ( u - > mixer_elem , 0 , & sw ) ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log_error ( " Unable to get switch: %s " , snd_strerror ( err ) ) ;
2006-02-26 17:57:58 +00:00
return - 1 ;
}
2007-10-28 19:13:50 +00:00
s - > muted = ! sw ;
2006-02-26 17:57:58 +00:00
return 0 ;
}
2007-10-28 19:13:50 +00:00
static int source_set_mute_cb ( pa_source * s ) {
2006-02-26 17:57:58 +00:00
struct userdata * u = s - > userdata ;
int err ;
2007-10-28 19:13:50 +00:00
pa_assert ( u ) ;
pa_assert ( u - > mixer_elem ) ;
2006-02-26 17:57:58 +00:00
2007-10-28 19:13:50 +00:00
if ( ( err = snd_mixer_selem_set_capture_switch_all ( u - > mixer_elem , ! s - > muted ) ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log_error ( " Unable to set switch: %s " , snd_strerror ( err ) ) ;
2006-02-26 17:57:58 +00:00
return - 1 ;
}
return 0 ;
}
2008-05-15 23:34:41 +00:00
static void source_update_requested_latency_cb ( pa_source * s ) {
struct userdata * u = s - > userdata ;
pa_assert ( u ) ;
if ( ! u - > pcm_handle )
return ;
update_sw_params ( u ) ;
}
2007-10-28 19:13:50 +00:00
static void thread_func ( void * userdata ) {
struct userdata * u = userdata ;
2008-10-22 23:55:52 +02:00
unsigned short revents = 0 ;
2007-10-28 19:13:50 +00:00
pa_assert ( u ) ;
pa_log_debug ( " Thread starting up " ) ;
2007-11-01 02:58:26 +00:00
if ( u - > core - > realtime_scheduling )
pa_make_realtime ( u - > core - > realtime_priority ) ;
2007-10-28 19:13:50 +00:00
pa_thread_mq_install ( & u - > thread_mq ) ;
pa_rtpoll_install ( u - > rtpoll ) ;
for ( ; ; ) {
int ret ;
2008-05-15 23:34:41 +00:00
/* pa_log_debug("loop"); */
2007-10-28 19:13:50 +00:00
/* Read some data and pass it to the sources */
2008-05-15 23:34:41 +00:00
if ( PA_SOURCE_IS_OPENED ( u - > source - > thread_info . state ) ) {
int work_done = 0 ;
2008-08-11 19:46:11 +02:00
pa_usec_t sleep_usec = 0 ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
if ( u - > use_mmap )
2008-10-22 23:55:52 +02:00
work_done = mmap_read ( u , & sleep_usec , revents & POLLIN ) ;
2008-05-15 23:34:41 +00:00
else
2008-10-22 23:55:52 +02:00
work_done = unix_read ( u , & sleep_usec , revents & POLLIN ) ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
if ( work_done < 0 )
goto fail ;
/* pa_log_debug("work_done = %i", work_done); */
if ( work_done )
update_smoother ( u ) ;
if ( u - > use_tsched ) {
pa_usec_t cusec ;
/* OK, the capture buffer is now empty, let's
* calculate when to wake up next */
/* pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */
/* Convert from the sound card time domain to the
* system time domain */
cusec = pa_smoother_translate ( u - > smoother , pa_rtclock_usec ( ) , sleep_usec ) ;
/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
/* We don't trust the conversion, so we wake up whatever comes first */
pa_rtpoll_set_timer_relative ( u - > rtpoll , PA_MIN ( sleep_usec , cusec ) ) ;
2007-10-28 19:13:50 +00:00
}
2008-05-15 23:34:41 +00:00
} else if ( u - > use_tsched )
/* OK, we're in an invalid state, let's disable our timers */
pa_rtpoll_set_timer_disabled ( u - > rtpoll ) ;
2007-10-28 19:13:50 +00:00
/* Hmm, nothing to do. Let's sleep */
2008-09-29 21:36:42 +02:00
if ( ( ret = pa_rtpoll_run ( u - > rtpoll , TRUE ) ) < 0 )
2007-10-28 19:13:50 +00:00
goto fail ;
if ( ret = = 0 )
goto finish ;
/* Tell ALSA about this and process its response */
2008-05-15 23:34:41 +00:00
if ( PA_SOURCE_IS_OPENED ( u - > source - > thread_info . state ) ) {
2007-10-28 19:13:50 +00:00
struct pollfd * pollfd ;
int err ;
unsigned n ;
pollfd = pa_rtpoll_item_get_pollfd ( u - > alsa_rtpoll_item , & n ) ;
if ( ( err = snd_pcm_poll_descriptors_revents ( u - > pcm_handle , pollfd , n , & revents ) ) < 0 ) {
pa_log ( " snd_pcm_poll_descriptors_revents() failed: %s " , snd_strerror ( err ) ) ;
goto fail ;
}
2008-10-22 23:55:52 +02:00
if ( revents & ( POLLOUT | POLLERR | POLLNVAL | POLLHUP | POLLPRI ) ) {
2008-05-15 23:34:41 +00:00
if ( pa_alsa_recover_from_poll ( u - > pcm_handle , revents ) < 0 )
goto fail ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
snd_pcm_start ( u - > pcm_handle ) ;
2007-10-28 19:13:50 +00:00
}
2008-05-15 23:34:41 +00:00
if ( revents & & u - > use_tsched )
2008-09-09 02:17:01 +03:00
pa_log_debug ( " Wakeup from ALSA!%s%s " , ( revents & POLLIN ) ? " INPUT " : " " , ( revents & POLLOUT ) ? " OUTPUT " : " " ) ;
2008-10-22 23:55:52 +02:00
} else
revents = 0 ;
2007-10-28 19:13:50 +00:00
}
fail :
/* If this was no regular exit from the loop we have to continue
* processing messages until we received PA_MESSAGE_SHUTDOWN */
pa_asyncmsgq_post ( u - > thread_mq . outq , PA_MSGOBJECT ( u - > core ) , PA_CORE_MESSAGE_UNLOAD_MODULE , u - > module , 0 , NULL , NULL ) ;
pa_asyncmsgq_wait_for ( u - > thread_mq . inq , PA_MESSAGE_SHUTDOWN ) ;
finish :
pa_log_debug ( " Thread shutting down " ) ;
}
int pa__init ( pa_module * m ) {
2006-01-11 01:17:39 +00:00
pa_modargs * ma = NULL ;
2004-07-16 17:03:11 +00:00
struct userdata * u = NULL ;
2007-11-13 17:37:44 +00:00
const char * dev_id ;
2006-01-11 01:17:39 +00:00
pa_sample_spec ss ;
2006-04-26 15:40:14 +00:00
pa_channel_map map ;
2008-05-15 23:34:41 +00:00
uint32_t nfrags , hwbuf_size , frag_size , tsched_size , tsched_watermark ;
snd_pcm_uframes_t period_frames , tsched_frames ;
2004-07-16 17:03:11 +00:00
size_t frame_size ;
2006-03-05 20:59:57 +00:00
snd_pcm_info_t * pcm_info = NULL ;
2006-02-16 01:15:31 +00:00
int err ;
2006-08-12 16:26:59 +00:00
const char * name ;
char * name_buf = NULL ;
2008-05-15 23:34:41 +00:00
pa_bool_t namereg_fail ;
2008-08-13 13:59:06 +02:00
pa_bool_t use_mmap = TRUE , b , use_tsched = TRUE , d ;
2008-05-15 23:34:41 +00:00
pa_source_new_data data ;
2007-10-28 19:13:50 +00:00
snd_pcm_info_alloca ( & pcm_info ) ;
pa_assert ( m ) ;
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
pa_alsa_redirect_errors_inc ( ) ;
2004-07-16 17:03:11 +00:00
if ( ! ( ma = pa_modargs_new ( m - > argument , valid_modargs ) ) ) {
2007-10-28 19:13:50 +00:00
pa_log ( " Failed to parse module arguments " ) ;
2004-07-16 17:03:11 +00:00
goto fail ;
}
2007-10-28 19:13:50 +00:00
ss = m - > core - > default_sample_spec ;
2006-05-16 23:47:38 +00:00
if ( pa_modargs_get_sample_spec_and_channel_map ( ma , & ss , & map , PA_CHANNEL_MAP_ALSA ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_log ( " Failed to parse sample specification " ) ;
2004-07-16 17:03:11 +00:00
goto fail ;
}
2006-05-13 20:29:32 +00:00
2004-08-03 19:26:56 +00:00
frame_size = pa_frame_size ( & ss ) ;
2006-05-13 21:20:34 +00:00
2007-10-28 19:13:50 +00:00
nfrags = m - > core - > default_n_fragments ;
2008-08-19 22:39:54 +02:00
frag_size = ( uint32_t ) pa_usec_to_bytes ( m - > core - > default_fragment_size_msec * PA_USEC_PER_MSEC , & ss ) ;
2007-10-28 19:13:50 +00:00
if ( frag_size < = 0 )
2008-08-19 22:39:54 +02:00
frag_size = ( uint32_t ) frame_size ;
tsched_size = ( uint32_t ) pa_usec_to_bytes ( DEFAULT_TSCHED_BUFFER_USEC , & ss ) ;
tsched_watermark = ( uint32_t ) pa_usec_to_bytes ( DEFAULT_TSCHED_WATERMARK_USEC , & ss ) ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
if ( pa_modargs_get_value_u32 ( ma , " fragments " , & nfrags ) < 0 | |
pa_modargs_get_value_u32 ( ma , " fragment_size " , & frag_size ) < 0 | |
pa_modargs_get_value_u32 ( ma , " tsched_buffer_size " , & tsched_size ) < 0 | |
pa_modargs_get_value_u32 ( ma , " tsched_buffer_watermark " , & tsched_watermark ) < 0 ) {
2007-10-28 19:13:50 +00:00
pa_log ( " Failed to parse buffer metrics " ) ;
goto fail ;
}
2008-05-15 23:34:41 +00:00
hwbuf_size = frag_size * nfrags ;
period_frames = frag_size / frame_size ;
tsched_frames = tsched_size / frame_size ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
if ( pa_modargs_get_value_boolean ( ma , " mmap " , & use_mmap ) < 0 ) {
pa_log ( " Failed to parse mmap argument. " ) ;
2004-07-16 17:03:11 +00:00
goto fail ;
}
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
if ( pa_modargs_get_value_boolean ( ma , " tsched " , & use_tsched ) < 0 ) {
pa_log ( " Failed to parse timer_scheduling argument. " ) ;
goto fail ;
}
if ( use_tsched & & ! pa_rtclock_hrtimer ( ) ) {
2008-09-05 00:38:52 +02:00
pa_log_notice ( " Disabling timer-based scheduling because high-resolution timers are not available from the kernel. " ) ;
2008-05-15 23:34:41 +00:00
use_tsched = FALSE ;
}
2006-06-20 21:23:10 +00:00
u = pa_xnew0 ( struct userdata , 1 ) ;
2007-10-28 19:13:50 +00:00
u - > core = m - > core ;
2004-08-04 16:39:30 +00:00
u - > module = m ;
2007-10-28 19:13:50 +00:00
m - > userdata = u ;
u - > use_mmap = use_mmap ;
2008-05-15 23:34:41 +00:00
u - > use_tsched = use_tsched ;
2007-10-28 19:13:50 +00:00
u - > rtpoll = pa_rtpoll_new ( ) ;
2008-05-15 23:34:41 +00:00
pa_thread_mq_init ( & u - > thread_mq , m - > core - > mainloop , u - > rtpoll ) ;
2007-10-28 19:13:50 +00:00
u - > alsa_rtpoll_item = NULL ;
2008-05-15 23:34:41 +00:00
u - > smoother = pa_smoother_new ( DEFAULT_TSCHED_WATERMARK_USEC , DEFAULT_TSCHED_WATERMARK_USEC , TRUE , 5 ) ;
pa_smoother_set_time_offset ( u - > smoother , pa_rtclock_usec ( ) ) ;
2007-01-04 13:43:45 +00:00
2005-09-16 00:11:48 +00:00
snd_config_update_free_global ( ) ;
2007-10-28 19:13:50 +00:00
2007-11-13 23:42:15 +00:00
b = use_mmap ;
2008-05-15 23:34:41 +00:00
d = use_tsched ;
2007-11-13 23:42:15 +00:00
2007-11-13 17:37:44 +00:00
if ( ( dev_id = pa_modargs_get_value ( ma , " device_id " , NULL ) ) ) {
2007-10-28 19:13:50 +00:00
2007-11-13 17:37:44 +00:00
if ( ! ( u - > pcm_handle = pa_alsa_open_by_device_id (
dev_id ,
& u - > device_name ,
& ss , & map ,
SND_PCM_STREAM_CAPTURE ,
2008-05-15 23:34:41 +00:00
& nfrags , & period_frames , tsched_frames ,
& b , & d ) ) )
2007-10-28 19:13:50 +00:00
goto fail ;
2007-11-13 17:37:44 +00:00
} else {
2007-10-28 19:13:50 +00:00
2007-11-13 17:37:44 +00:00
if ( ! ( u - > pcm_handle = pa_alsa_open_by_device_string (
pa_modargs_get_value ( ma , " device " , DEFAULT_DEVICE ) ,
& u - > device_name ,
& ss , & map ,
SND_PCM_STREAM_CAPTURE ,
2008-05-15 23:34:41 +00:00
& nfrags , & period_frames , tsched_frames ,
& b , & d ) ) )
2007-10-28 19:13:50 +00:00
goto fail ;
2004-07-16 17:03:11 +00:00
}
2007-11-13 17:37:44 +00:00
pa_assert ( u - > device_name ) ;
pa_log_info ( " Successfully opened device %s. " , u - > device_name ) ;
2007-10-28 19:13:50 +00:00
if ( use_mmap & & ! b ) {
pa_log_info ( " Device doesn't support mmap(), falling back to UNIX read/write mode. " ) ;
2008-05-15 23:34:41 +00:00
u - > use_mmap = use_mmap = FALSE ;
}
if ( use_tsched & & ( ! b | | ! d ) ) {
pa_log_info ( " Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling. " ) ;
u - > use_tsched = use_tsched = FALSE ;
2007-10-28 19:13:50 +00:00
}
if ( u - > use_mmap )
pa_log_info ( " Successfully enabled mmap() mode. " ) ;
2008-05-15 23:34:41 +00:00
if ( u - > use_tsched )
pa_log_info ( " Successfully enabled timer-based scheduling mode. " ) ;
2007-10-28 19:13:50 +00:00
if ( ( err = snd_pcm_info ( u - > pcm_handle , pcm_info ) ) < 0 ) {
2006-08-18 21:38:40 +00:00
pa_log ( " Error fetching PCM info: %s " , snd_strerror ( err ) ) ;
2006-03-05 20:59:57 +00:00
goto fail ;
}
2007-10-28 19:13:50 +00:00
/* ALSA might tweak the sample spec, so recalculate the frame size */
frame_size = pa_frame_size ( & ss ) ;
if ( ( err = snd_mixer_open ( & u - > mixer_handle , 0 ) ) < 0 )
2006-08-18 21:38:40 +00:00
pa_log ( " Error opening mixer: %s " , snd_strerror ( err ) ) ;
2007-10-28 19:13:50 +00:00
else {
2007-11-13 23:42:15 +00:00
pa_bool_t found = FALSE ;
if ( pa_alsa_prepare_mixer ( u - > mixer_handle , u - > device_name ) > = 0 )
found = TRUE ;
else {
2008-05-15 23:34:41 +00:00
snd_pcm_info_t * info ;
2007-11-13 23:42:15 +00:00
2008-05-15 23:34:41 +00:00
snd_pcm_info_alloca ( & info ) ;
2007-11-13 23:42:15 +00:00
2008-05-15 23:34:41 +00:00
if ( snd_pcm_info ( u - > pcm_handle , info ) > = 0 ) {
char * md ;
int card ;
if ( ( card = snd_pcm_info_get_card ( info ) ) > = 0 ) {
md = pa_sprintf_malloc ( " hw:%i " , card ) ;
if ( strcmp ( u - > device_name , md ) )
if ( pa_alsa_prepare_mixer ( u - > mixer_handle , md ) > = 0 )
found = TRUE ;
pa_xfree ( md ) ;
}
}
2007-11-13 23:42:15 +00:00
}
2006-02-26 17:57:58 +00:00
2007-11-13 23:42:15 +00:00
if ( found )
2009-01-08 01:03:42 +01:00
if ( ! ( u - > mixer_elem = pa_alsa_find_elem ( u - > mixer_handle , " Capture " , " Mic " , FALSE ) ) )
2007-11-13 23:42:15 +00:00
found = FALSE ;
if ( ! found ) {
2007-10-28 19:13:50 +00:00
snd_mixer_close ( u - > mixer_handle ) ;
u - > mixer_handle = NULL ;
}
2006-02-26 17:57:58 +00:00
}
2006-08-12 16:26:59 +00:00
if ( ( name = pa_modargs_get_value ( ma , " source_name " , NULL ) ) )
2008-05-15 23:34:41 +00:00
namereg_fail = TRUE ;
2006-08-12 16:26:59 +00:00
else {
2007-11-13 17:37:44 +00:00
name = name_buf = pa_sprintf_malloc ( " alsa_input.%s " , u - > device_name ) ;
2008-05-15 23:34:41 +00:00
namereg_fail = FALSE ;
2006-08-12 16:26:59 +00:00
}
2007-01-04 13:43:45 +00:00
2008-05-15 23:34:41 +00:00
pa_source_new_data_init ( & data ) ;
data . driver = __FILE__ ;
data . module = m ;
pa_source_new_data_set_name ( & data , name ) ;
data . namereg_fail = namereg_fail ;
pa_source_new_data_set_sample_spec ( & data , & ss ) ;
pa_source_new_data_set_channel_map ( & data , & map ) ;
pa_alsa_init_proplist ( data . proplist , pcm_info ) ;
pa_proplist_sets ( data . proplist , PA_PROP_DEVICE_STRING , u - > device_name ) ;
pa_proplist_setf ( data . proplist , PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE , " %lu " , ( unsigned long ) ( period_frames * frame_size * nfrags ) ) ;
pa_proplist_setf ( data . proplist , PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE , " %lu " , ( unsigned long ) ( period_frames * frame_size ) ) ;
pa_proplist_sets ( data . proplist , PA_PROP_DEVICE_ACCESS_MODE , u - > use_tsched ? " mmap+timer " : ( u - > use_mmap ? " mmap " : " serial " ) ) ;
u - > source = pa_source_new ( m - > core , & data , PA_SOURCE_HARDWARE | PA_SOURCE_LATENCY ) ;
pa_source_new_data_done ( & data ) ;
2007-10-28 19:13:50 +00:00
pa_xfree ( name_buf ) ;
if ( ! u - > source ) {
2006-08-18 21:38:40 +00:00
pa_log ( " Failed to create source object " ) ;
2006-05-30 22:05:07 +00:00
goto fail ;
}
2004-07-16 17:03:11 +00:00
2007-10-28 19:13:50 +00:00
u - > source - > parent . process_msg = source_process_msg ;
2008-05-15 23:34:41 +00:00
u - > source - > update_requested_latency = source_update_requested_latency_cb ;
2004-07-16 17:03:11 +00:00
u - > source - > userdata = u ;
2007-10-28 19:13:50 +00:00
pa_source_set_asyncmsgq ( u - > source , u - > thread_mq . inq ) ;
pa_source_set_rtpoll ( u - > source , u - > rtpoll ) ;
u - > frame_size = frame_size ;
2008-08-19 22:39:54 +02:00
u - > fragment_size = frag_size = ( uint32_t ) ( period_frames * frame_size ) ;
2007-10-28 19:13:50 +00:00
u - > nfragments = nfrags ;
u - > hwbuf_size = u - > fragment_size * nfrags ;
2008-05-15 23:34:41 +00:00
u - > hwbuf_unused_frames = 0 ;
u - > tsched_watermark = tsched_watermark ;
u - > frame_index = 0 ;
u - > hw_dB_supported = FALSE ;
u - > hw_dB_min = u - > hw_dB_max = 0 ;
u - > hw_volume_min = u - > hw_volume_max = 0 ;
2008-08-13 13:59:06 +02:00
u - > mixer_seperate_channels = FALSE ;
pa_cvolume_mute ( & u - > hardware_volume , u - > source - > sample_spec . channels ) ;
2008-05-15 23:34:41 +00:00
if ( use_tsched )
fix_tsched_watermark ( u ) ;
2008-05-17 09:57:45 +00:00
pa_source_set_latency_range ( u - > source ,
! use_tsched ? pa_bytes_to_usec ( u - > hwbuf_size , & ss ) : ( pa_usec_t ) - 1 ,
pa_bytes_to_usec ( u - > hwbuf_size , & ss ) ) ;
2008-05-15 23:34:41 +00:00
pa_log_info ( " Using %u fragments of size %lu bytes, buffer time is %0.2fms " ,
nfrags , ( long unsigned ) u - > fragment_size ,
( double ) pa_bytes_to_usec ( u - > hwbuf_size , & ss ) / PA_USEC_PER_MSEC ) ;
2007-10-28 19:13:50 +00:00
2008-05-15 23:34:41 +00:00
if ( use_tsched )
pa_log_info ( " Time scheduling watermark is %0.2fms " ,
( double ) pa_bytes_to_usec ( u - > tsched_watermark , & ss ) / PA_USEC_PER_MSEC ) ;
if ( update_sw_params ( u ) < 0 )
goto fail ;
2007-10-28 19:13:50 +00:00
2006-02-26 17:57:58 +00:00
if ( u - > mixer_handle ) {
2007-10-28 19:13:50 +00:00
pa_assert ( u - > mixer_elem ) ;
2008-08-13 13:59:06 +02:00
if ( snd_mixer_selem_has_capture_volume ( u - > mixer_elem ) ) {
2008-10-01 04:15:05 +02:00
pa_bool_t suitable = FALSE ;
2008-05-15 23:34:41 +00:00
2008-10-01 04:15:05 +02:00
if ( snd_mixer_selem_get_capture_volume_range ( u - > mixer_elem , & u - > hw_volume_min , & u - > hw_volume_max ) < 0 )
2008-08-13 13:59:06 +02:00
pa_log_info ( " Failed to get volume range. Falling back to software volume control. " ) ;
2008-10-01 04:15:05 +02:00
else if ( u - > hw_volume_min > = u - > hw_volume_max )
pa_log_warn ( " Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense. " , u - > hw_volume_min , u - > hw_volume_max ) ;
else {
2008-05-15 23:34:41 +00:00
pa_log_info ( " Volume ranges from %li to %li. " , u - > hw_volume_min , u - > hw_volume_max ) ;
2008-10-01 04:15:05 +02:00
suitable = TRUE ;
2008-08-13 13:59:06 +02:00
}
2008-05-15 23:34:41 +00:00
2008-12-24 00:57:37 +01:00
if ( suitable ) {
if ( snd_mixer_selem_get_capture_dB_range ( u - > mixer_elem , & u - > hw_dB_min , & u - > hw_dB_max ) < 0 )
pa_log_info ( " Mixer doesn't support dB information. " ) ;
else {
2008-08-18 19:55:55 +02:00
# ifdef HAVE_VALGRIND_MEMCHECK_H
2008-12-24 00:57:37 +01:00
VALGRIND_MAKE_MEM_DEFINED ( & u - > hw_dB_min , sizeof ( u - > hw_dB_min ) ) ;
VALGRIND_MAKE_MEM_DEFINED ( & u - > hw_dB_max , sizeof ( u - > hw_dB_max ) ) ;
2008-08-18 19:55:55 +02:00
# endif
2008-12-24 00:57:37 +01:00
if ( u - > hw_dB_min > = u - > hw_dB_max )
pa_log_warn ( " Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense. " , ( double ) u - > hw_dB_min / 100.0 , ( double ) u - > hw_dB_max / 100.0 ) ;
else {
pa_log_info ( " Volume ranges from %0.2f dB to %0.2f dB. " , ( double ) u - > hw_dB_min / 100.0 , ( double ) u - > hw_dB_max / 100.0 ) ;
u - > hw_dB_supported = TRUE ;
if ( u - > hw_dB_max > 0 ) {
u - > source - > base_volume = pa_sw_volume_from_dB ( - ( double ) u - > hw_dB_max / 100.0 ) ;
pa_log_info ( " Fixing base volume to %0.2f dB " , pa_sw_volume_to_dB ( u - > source - > base_volume ) ) ;
} else
pa_log_info ( " No particular base volume set, fixing to 0 dB " ) ;
}
2008-10-01 04:15:05 +02:00
}
2008-05-15 23:34:41 +00:00
2008-12-24 00:57:37 +01:00
if ( ! u - > hw_dB_supported & &
u - > hw_volume_max - u - > hw_volume_min < 3 ) {
2008-05-15 23:34:41 +00:00
2008-12-24 00:57:37 +01:00
pa_log_info ( " Device has less than 4 volume levels. Falling back to software volume control. " ) ;
suitable = FALSE ;
}
2008-08-13 13:59:06 +02:00
}
2008-05-15 23:34:41 +00:00
2008-08-13 13:59:06 +02:00
if ( suitable ) {
u - > mixer_seperate_channels = pa_alsa_calc_mixer_map ( u - > mixer_elem , & map , u - > mixer_map , FALSE ) > = 0 ;
2007-10-28 19:13:50 +00:00
2008-08-13 13:59:06 +02:00
u - > source - > get_volume = source_get_volume_cb ;
u - > source - > set_volume = source_set_volume_cb ;
u - > source - > flags | = PA_SOURCE_HW_VOLUME_CTRL | ( u - > hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0 ) ;
pa_log_info ( " Using hardware volume control. Hardware dB scale %s. " , u - > hw_dB_supported ? " supported " : " not supported " ) ;
} else
pa_log_info ( " Using software volume control. " ) ;
}
2008-05-15 23:34:41 +00:00
2006-02-26 17:57:58 +00:00
if ( snd_mixer_selem_has_capture_switch ( u - > mixer_elem ) ) {
2007-10-28 19:13:50 +00:00
u - > source - > get_mute = source_get_mute_cb ;
u - > source - > set_mute = source_set_mute_cb ;
2008-05-15 23:34:41 +00:00
u - > source - > flags | = PA_SOURCE_HW_MUTE_CTRL ;
2008-08-13 13:59:06 +02:00
} else
pa_log_info ( " Using software mute control. " ) ;
2004-07-16 17:03:11 +00:00
2006-02-26 21:50:55 +00:00
u - > mixer_fdl = pa_alsa_fdlist_new ( ) ;
2007-10-28 19:13:50 +00:00
if ( pa_alsa_fdlist_set_mixer ( u - > mixer_fdl , u - > mixer_handle , m - > core - > mainloop ) < 0 ) {
2007-11-13 23:42:15 +00:00
pa_log ( " Failed to initialize file descriptor monitoring " ) ;
2006-02-26 21:50:55 +00:00
goto fail ;
}
2007-10-28 19:13:50 +00:00
2006-02-26 21:50:55 +00:00
snd_mixer_elem_set_callback ( u - > mixer_elem , mixer_callback ) ;
snd_mixer_elem_set_callback_private ( u - > mixer_elem , u ) ;
2006-05-30 22:05:07 +00:00
} else
u - > mixer_fdl = NULL ;
2006-02-26 21:50:55 +00:00
2008-05-15 23:34:41 +00:00
pa_alsa_dump ( u - > pcm_handle ) ;
2007-10-28 19:13:50 +00:00
if ( ! ( u - > thread = pa_thread_new ( thread_func , u ) ) ) {
pa_log ( " Failed to create thread. " ) ;
goto fail ;
}
2006-02-26 17:57:58 +00:00
/* Get initial mixer settings */
2008-05-15 23:34:41 +00:00
if ( data . volume_is_set ) {
if ( u - > source - > set_volume )
u - > source - > set_volume ( u - > source ) ;
} else {
if ( u - > source - > get_volume )
u - > source - > get_volume ( u - > source ) ;
}
if ( data . muted_is_set ) {
if ( u - > source - > set_mute )
u - > source - > set_mute ( u - > source ) ;
} else {
if ( u - > source - > get_mute )
u - > source - > get_mute ( u - > source ) ;
}
2006-03-05 15:42:37 +00:00
2007-10-28 19:13:50 +00:00
pa_source_put ( u - > source ) ;
2006-03-05 20:59:57 +00:00
2007-10-28 19:13:50 +00:00
pa_modargs_free ( ma ) ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
return 0 ;
2004-07-16 17:03:11 +00:00
fail :
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
if ( ma )
pa_modargs_free ( ma ) ;
2004-07-16 17:03:11 +00:00
2007-10-28 19:13:50 +00:00
pa__done ( m ) ;
return - 1 ;
2004-07-16 17:03:11 +00:00
}
2009-01-15 20:49:12 +01:00
int pa__get_n_used ( pa_module * m ) {
struct userdata * u ;
pa_assert ( m ) ;
pa_assert_se ( u = m - > userdata ) ;
return pa_source_linked_by ( u - > source ) ;
}
2007-10-28 19:13:50 +00:00
void pa__done ( pa_module * m ) {
2004-07-16 17:03:11 +00:00
struct userdata * u ;
2007-10-28 19:13:50 +00:00
pa_assert ( m ) ;
2004-07-16 17:03:11 +00:00
2008-05-15 23:34:41 +00:00
if ( ! ( u = m - > userdata ) ) {
pa_alsa_redirect_errors_dec ( ) ;
2004-08-04 16:39:30 +00:00
return ;
2008-05-15 23:34:41 +00:00
}
2006-02-26 17:57:58 +00:00
2007-10-28 19:13:50 +00:00
if ( u - > source )
pa_source_unlink ( u - > source ) ;
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
if ( u - > thread ) {
pa_asyncmsgq_send ( u - > thread_mq . inq , NULL , PA_MESSAGE_SHUTDOWN , NULL , 0 , NULL ) ;
pa_thread_free ( u - > thread ) ;
}
2007-01-04 13:43:45 +00:00
2007-10-28 19:13:50 +00:00
pa_thread_mq_done ( & u - > thread_mq ) ;
if ( u - > source )
pa_source_unref ( u - > source ) ;
if ( u - > alsa_rtpoll_item )
pa_rtpoll_item_free ( u - > alsa_rtpoll_item ) ;
if ( u - > rtpoll )
pa_rtpoll_free ( u - > rtpoll ) ;
if ( u - > mixer_fdl )
pa_alsa_fdlist_free ( u - > mixer_fdl ) ;
if ( u - > mixer_handle )
snd_mixer_close ( u - > mixer_handle ) ;
if ( u - > pcm_handle ) {
snd_pcm_drop ( u - > pcm_handle ) ;
snd_pcm_close ( u - > pcm_handle ) ;
}
2008-05-15 23:34:41 +00:00
if ( u - > smoother )
pa_smoother_free ( u - > smoother ) ;
2007-10-28 19:13:50 +00:00
pa_xfree ( u - > device_name ) ;
2004-08-04 16:39:30 +00:00
pa_xfree ( u ) ;
2004-07-16 17:03:11 +00:00
2007-10-28 19:13:50 +00:00
snd_config_update_free_global ( ) ;
2008-05-15 23:34:41 +00:00
pa_alsa_redirect_errors_dec ( ) ;
2007-10-28 19:13:50 +00:00
}