Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

alsa.c

00001 /*****************************************************************************
00002  * alsa.c : alsa plugin for vlc
00003  *****************************************************************************
00004  * Copyright (C) 2000-2001 the VideoLAN team
00005  * $Id: alsa.c 13660 2005-12-10 15:36:07Z gbazin $
00006  *
00007  * Authors: Henri Fallon <[email protected]> - Original Author
00008  *          Jeffrey Baker <[email protected]> - Port to ALSA 1.0 API
00009  *          John Paul Lorenti <[email protected]> - Device selection
00010  *          Arnaud de Bossoreille de Ribou <[email protected]> - S/PDIF and aout3
00011  *
00012  * This program is free software; you can redistribute it and/or modify
00013  * it under the terms of the GNU General Public License as published by
00014  * the Free Software Foundation; either version 2 of the License, or
00015  * (at your option) any later version.
00016  *
00017  * This program is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020  * GNU General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU General Public License
00023  * along with this program; if not, write to the Free Software
00024  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00025  *****************************************************************************/
00026 
00027 /*****************************************************************************
00028  * Preamble
00029  *****************************************************************************/
00030 #include <errno.h>                                                 /* ENOMEM */
00031 #include <string.h>                                            /* strerror() */
00032 #include <stdlib.h>                            /* calloc(), malloc(), free() */
00033 
00034 #include <vlc/vlc.h>
00035 
00036 #include <vlc/aout.h>
00037 
00038 #include "aout_internal.h"
00039 
00040 /* ALSA part
00041    Note: we use the new API which is available since 0.9.0beta10a. */
00042 #define ALSA_PCM_NEW_HW_PARAMS_API
00043 #define ALSA_PCM_NEW_SW_PARAMS_API
00044 #include <alsa/asoundlib.h>
00045 
00046 /*****************************************************************************
00047  * aout_sys_t: ALSA audio output method descriptor
00048  *****************************************************************************
00049  * This structure is part of the audio output thread descriptor.
00050  * It describes the ALSA specific properties of an audio device.
00051  *****************************************************************************/
00052 struct aout_sys_t
00053 {
00054     snd_pcm_t         * p_snd_pcm;
00055     unsigned int                 i_period_time;
00056 
00057 #ifdef ALSA_DEBUG
00058     snd_output_t      * p_snd_stderr;
00059 #endif
00060 
00061     int b_playing;                                         /* playing status */
00062     mtime_t start_date;
00063 
00064     vlc_mutex_t lock;
00065     vlc_cond_t  wait ;
00066 
00067     snd_pcm_status_t *p_status;
00068 };
00069 
00070 #define A52_FRAME_NB 1536
00071 
00072 /* These values are in frames.
00073    To convert them to a number of bytes you have to multiply them by the
00074    number of channel(s) (eg. 2 for stereo) and the size of a sample (eg.
00075    2 for int16_t). */
00076 #define ALSA_DEFAULT_PERIOD_SIZE        1024
00077 #define ALSA_DEFAULT_BUFFER_SIZE        ( ALSA_DEFAULT_PERIOD_SIZE << 8 )
00078 #define ALSA_SPDIF_PERIOD_SIZE          A52_FRAME_NB
00079 #define ALSA_SPDIF_BUFFER_SIZE          ( ALSA_SPDIF_PERIOD_SIZE << 4 )
00080 /* Why << 4 ? --Meuuh */
00081 /* Why not ? --Bozo */
00082 /* Right. --Meuuh */
00083 
00084 #define DEFAULT_ALSA_DEVICE N_("default")
00085 
00086 /*****************************************************************************
00087  * Local prototypes
00088  *****************************************************************************/
00089 static int  Open         ( vlc_object_t * );
00090 static void Close        ( vlc_object_t * );
00091 static void Play         ( aout_instance_t * );
00092 static int  ALSAThread   ( aout_instance_t * );
00093 static void ALSAFill     ( aout_instance_t * );
00094 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
00095                                 vlc_value_t newval, vlc_value_t oldval, void *p_unused );
00096 
00097 /*****************************************************************************
00098  * Module descriptor
00099  *****************************************************************************/
00100 static char *ppsz_devices[] = { "default" };
00101 static char *ppsz_devices_text[] = { N_("Default") };
00102 vlc_module_begin();
00103     set_shortname( "ALSA" );
00104     set_description( _("ALSA audio output") );
00105     set_category( CAT_AUDIO );
00106     set_subcategory( SUBCAT_AUDIO_AOUT );
00107     add_string( "alsadev", DEFAULT_ALSA_DEVICE, aout_FindAndRestart,
00108                 N_("ALSA Device Name"), NULL, VLC_FALSE );
00109         change_string_list( ppsz_devices, ppsz_devices_text, FindDevicesCallback );
00110         change_action_add( FindDevicesCallback, N_("Refresh list") );
00111 
00112     set_capability( "audio output", 150 );
00113     set_callbacks( Open, Close );
00114 vlc_module_end();
00115 
00116 /*****************************************************************************
00117  * Probe: probe the audio device for available formats and channels
00118  *****************************************************************************/
00119 static void Probe( aout_instance_t * p_aout,
00120                    const char * psz_device, const char * psz_iec_device,
00121                    int *pi_snd_pcm_format )
00122 {
00123     struct aout_sys_t * p_sys = p_aout->output.p_sys;
00124     vlc_value_t val, text;
00125     int i_ret;
00126 
00127     var_Create ( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
00128     text.psz_string = _("Audio Device");
00129     var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
00130 
00131     /* We'll open the audio device in non blocking mode so we can just exit
00132      * when it is already in use, but for the real stuff we'll still use
00133      * the blocking mode */
00134 
00135     /* Now test linear PCM capabilities */
00136     if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
00137                                  SND_PCM_STREAM_PLAYBACK,
00138                                  SND_PCM_NONBLOCK ) ) )
00139     {
00140         int i_channels;
00141         snd_pcm_hw_params_t * p_hw;
00142         snd_pcm_hw_params_alloca (&p_hw);
00143 
00144         if ( snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) < 0 )
00145         {
00146             msg_Warn( p_aout, "unable to retrieve initial hardware parameters"
00147                               ", disabling linear PCM audio" );
00148             snd_pcm_close( p_sys->p_snd_pcm );
00149             var_Destroy( p_aout, "audio-device" );
00150             return;
00151         }
00152 
00153         if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
00154                                            *pi_snd_pcm_format ) < 0 )
00155         {
00156             int i_snd_rc = -1;
00157 
00158             if( *pi_snd_pcm_format != SND_PCM_FORMAT_S16 )
00159             {
00160                 *pi_snd_pcm_format = SND_PCM_FORMAT_S16;
00161                 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
00162                                                     p_hw, *pi_snd_pcm_format );
00163             }
00164             if ( i_snd_rc < 0 )
00165             {
00166                 msg_Warn( p_aout, "unable to set stream sample size and "
00167                           "word order, disabling linear PCM audio" );
00168                 snd_pcm_close( p_sys->p_snd_pcm );
00169                 var_Destroy( p_aout, "audio-device" );
00170                 return;
00171             }
00172         }
00173 
00174         i_channels = aout_FormatNbChannels( &p_aout->output.output );
00175 
00176         while ( i_channels > 0 )
00177         {
00178             if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw,
00179                                                    i_channels ) )
00180             {
00181                 switch ( i_channels )
00182                 {
00183                 case 1:
00184                     val.i_int = AOUT_VAR_MONO;
00185                     text.psz_string = N_("Mono");
00186                     var_Change( p_aout, "audio-device",
00187                                 VLC_VAR_ADDCHOICE, &val, &text );
00188                     break;
00189                 case 2:
00190                     val.i_int = AOUT_VAR_STEREO;
00191                     text.psz_string = N_("Stereo");
00192                     var_Change( p_aout, "audio-device",
00193                                 VLC_VAR_ADDCHOICE, &val, &text );
00194                     var_Set( p_aout, "audio-device", val );
00195                     break;
00196                 case 4:
00197                     val.i_int = AOUT_VAR_2F2R;
00198                     text.psz_string = N_("2 Front 2 Rear");
00199                     var_Change( p_aout, "audio-device",
00200                                 VLC_VAR_ADDCHOICE, &val, &text );
00201                     break;
00202                 case 6:
00203                     val.i_int = AOUT_VAR_5_1;
00204                     text.psz_string = N_("5.1");
00205                     var_Change( p_aout, "audio-device",
00206                                 VLC_VAR_ADDCHOICE, &val, &text );
00207                     break;
00208                 }
00209             }
00210 
00211             --i_channels;
00212         }
00213 
00214         /* Special case for mono on stereo only boards */
00215         i_channels = aout_FormatNbChannels( &p_aout->output.output );        
00216         var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
00217         if( val.i_int <= 0 && i_channels == 1 )
00218         {
00219             if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw, 2 ))
00220             {
00221                 val.i_int = AOUT_VAR_STEREO;
00222                 text.psz_string = N_("Stereo");
00223                 var_Change( p_aout, "audio-device",
00224                             VLC_VAR_ADDCHOICE, &val, &text );
00225                 var_Set( p_aout, "audio-device", val );
00226             }
00227         }
00228         
00229         /* Close the previously opened device */
00230         snd_pcm_close( p_sys->p_snd_pcm );
00231     }
00232     else if ( i_ret == -EBUSY )
00233     {
00234         msg_Warn( p_aout, "audio device: %s is already in use", psz_device );
00235     }
00236 
00237     /* Test for S/PDIF device if needed */
00238     if ( psz_iec_device )
00239     {
00240         /* Opening the device should be enough */
00241         if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
00242                                      SND_PCM_STREAM_PLAYBACK,
00243                                      SND_PCM_NONBLOCK ) ) )
00244         {
00245             val.i_int = AOUT_VAR_SPDIF;
00246             text.psz_string = N_("A/52 over S/PDIF");
00247             var_Change( p_aout, "audio-device",
00248                         VLC_VAR_ADDCHOICE, &val, &text );
00249             if( config_GetInt( p_aout, "spdif" ) )
00250                 var_Set( p_aout, "audio-device", val );
00251 
00252             snd_pcm_close( p_sys->p_snd_pcm );
00253         }
00254         else if ( i_ret == -EBUSY )
00255         {
00256             msg_Warn( p_aout, "audio device: %s is already in use",
00257                       psz_iec_device );
00258         }
00259     }
00260 
00261     var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
00262     if( val.i_int <= 0 )
00263     {
00264         /* Probe() has failed. */
00265         msg_Dbg( p_aout, "failed to find a useable alsa configuration" );
00266         var_Destroy( p_aout, "audio-device" );
00267         return;
00268     }
00269 
00270     /* Add final settings to the variable */
00271     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
00272     val.b_bool = VLC_TRUE;
00273     var_Set( p_aout, "intf-change", val );
00274 }
00275 
00276 /*****************************************************************************
00277  * Open: create a handle and open an alsa device
00278  *****************************************************************************
00279  * This function opens an alsa device, through the alsa API.
00280  *
00281  * Note: the only heap-allocated string is psz_device. All the other pointers
00282  * are references to psz_device or to stack-allocated data.
00283  *****************************************************************************/
00284 static int Open( vlc_object_t *p_this )
00285 {
00286     aout_instance_t * p_aout = (aout_instance_t *)p_this;
00287     struct aout_sys_t * p_sys;
00288     vlc_value_t val;
00289 
00290     char psz_default_iec_device[128]; /* Buffer used to store the default
00291                                          S/PDIF device */
00292     char * psz_device, * psz_iec_device; /* device names for PCM and S/PDIF
00293                                             output */
00294 
00295     int i_vlc_pcm_format; /* Audio format for VLC's data */
00296     int i_snd_pcm_format; /* Audio format for ALSA's data */
00297 
00298     snd_pcm_uframes_t i_buffer_size = 0;
00299     snd_pcm_uframes_t i_period_size = 0;
00300     int i_channels = 0;
00301 
00302     snd_pcm_hw_params_t *p_hw;
00303     snd_pcm_sw_params_t *p_sw;
00304 
00305     int i_snd_rc = -1;
00306     unsigned int i_old_rate;
00307     vlc_bool_t b_retry = VLC_TRUE;
00308 
00309     /* Allocate structures */
00310     p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
00311     if( p_sys == NULL )
00312     {
00313         msg_Err( p_aout, "out of memory" );
00314         return VLC_ENOMEM;
00315     }
00316     p_sys->b_playing = VLC_FALSE;
00317     p_sys->start_date = 0;
00318     vlc_cond_init( p_aout, &p_sys->wait );
00319     vlc_mutex_init( p_aout, &p_sys->lock );
00320 
00321     /* Get device name */
00322     if( (psz_device = config_GetPsz( p_aout, "alsadev" )) == NULL )
00323     {
00324         msg_Err( p_aout, "no audio device given (maybe \"default\" ?)" );
00325         free( p_sys );
00326         return VLC_EGENERIC;
00327     }
00328 
00329     /* Choose the IEC device for S/PDIF output:
00330        if the device is overriden by the user then it will be the one
00331        otherwise we compute the default device based on the output format. */
00332     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output )
00333         && !strcmp( psz_device, DEFAULT_ALSA_DEVICE ) )
00334     {
00335         snprintf( psz_default_iec_device, sizeof(psz_default_iec_device),
00336                   "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
00337                   IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
00338                   IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
00339                   0,
00340                   ( p_aout->output.output.i_rate == 48000 ?
00341                     IEC958_AES3_CON_FS_48000 :
00342                     ( p_aout->output.output.i_rate == 44100 ?
00343                       IEC958_AES3_CON_FS_44100 : IEC958_AES3_CON_FS_32000 ) ) );
00344         psz_iec_device = psz_default_iec_device;
00345     }
00346     else if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
00347     {
00348         psz_iec_device = psz_device;
00349     }
00350     else
00351     {
00352         psz_iec_device = NULL;
00353     }
00354 
00355     /* Choose the linear PCM format (read the comment above about FPU
00356        and float32) */
00357     if( p_aout->p_libvlc->i_cpu & CPU_CAPABILITY_FPU )
00358     {
00359         i_vlc_pcm_format = VLC_FOURCC('f','l','3','2');
00360         i_snd_pcm_format = SND_PCM_FORMAT_FLOAT;
00361     }
00362     else
00363     {
00364         i_vlc_pcm_format = AOUT_FMT_S16_NE;
00365         i_snd_pcm_format = SND_PCM_FORMAT_S16;
00366     }
00367 
00368     /* If the variable doesn't exist then it's the first time we're called
00369        and we have to probe the available audio formats and channels */
00370     if ( var_Type( p_aout, "audio-device" ) == 0 )
00371     {
00372         Probe( p_aout, psz_device, psz_iec_device, &i_snd_pcm_format );
00373     }
00374 
00375     if ( var_Get( p_aout, "audio-device", &val ) < 0 )
00376     {
00377         free( p_sys );
00378         free( psz_device );
00379         return VLC_EGENERIC;
00380     }
00381 
00382     p_aout->output.output.i_format = i_vlc_pcm_format;
00383     if ( val.i_int == AOUT_VAR_5_1 )
00384     {
00385         p_aout->output.output.i_physical_channels
00386             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
00387                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
00388                | AOUT_CHAN_LFE;
00389         free( psz_device );
00390         psz_device = strdup( "surround51" );
00391     }
00392     else if ( val.i_int == AOUT_VAR_2F2R )
00393     {
00394         p_aout->output.output.i_physical_channels
00395             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
00396                | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
00397         free( psz_device );
00398         psz_device = strdup( "surround40" );
00399     }
00400     else if ( val.i_int == AOUT_VAR_STEREO )
00401     {
00402         p_aout->output.output.i_physical_channels
00403             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
00404     }
00405     else if ( val.i_int == AOUT_VAR_MONO )
00406     {
00407         p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
00408     }
00409     else if( val.i_int != AOUT_VAR_SPDIF )
00410     {
00411         /* This should not happen ! */
00412         msg_Err( p_aout, "internal: can't find audio-device (%i)", val.i_int );
00413         free( p_sys );
00414         free( psz_device );
00415         return VLC_EGENERIC;
00416     }
00417 
00418 #ifdef ALSA_DEBUG
00419     snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 );
00420 #endif
00421 
00422     /* Open the device */
00423     if ( val.i_int == AOUT_VAR_SPDIF )
00424     {
00425         if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
00426                             SND_PCM_STREAM_PLAYBACK, 0 ) ) < 0 )
00427         {
00428             msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
00429                              psz_iec_device, snd_strerror( i_snd_rc ) );
00430             free( p_sys );
00431             free( psz_device );
00432             return VLC_EGENERIC;
00433         }
00434         i_buffer_size = ALSA_SPDIF_BUFFER_SIZE;
00435         i_snd_pcm_format = SND_PCM_FORMAT_S16;
00436         i_channels = 2;
00437 
00438         i_vlc_pcm_format = VLC_FOURCC('s','p','d','i');
00439         p_aout->output.i_nb_samples = i_period_size = ALSA_SPDIF_PERIOD_SIZE;
00440         p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
00441         p_aout->output.output.i_frame_length = A52_FRAME_NB;
00442 
00443         aout_VolumeNoneInit( p_aout );
00444     }
00445     else
00446     {
00447         int i;
00448 
00449         msg_Dbg( p_aout, "opening ALSA device `%s'", psz_device );
00450 
00451         /* Since it seems snd_pcm_close hasen't really released the device at
00452           the time it returns, probe if the device is available in loop for 1s.
00453           We cannot use blocking mode since the we would wait indefinitely when
00454           switching from a dmx device to surround51. */
00455 
00456         for( i = 10; i >= 0; i-- )
00457         {
00458             if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
00459                    SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) ) == -EBUSY )
00460             {
00461                 if( i ) msleep( 100000 /* 100ms */ );
00462                 else msg_Err( p_aout, "audio device: %s is already in use",
00463                               psz_device );
00464                 continue;
00465             }
00466             break;
00467         }
00468         if( i_snd_rc < 0 )
00469         {
00470             msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
00471                              psz_device, snd_strerror( i_snd_rc ) );
00472             free( p_sys );
00473             free( psz_device );
00474             return VLC_EGENERIC;
00475         }
00476 
00477         /* We want blocking mode */
00478         snd_pcm_nonblock( p_sys->p_snd_pcm, 0 );
00479 
00480         i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE;
00481         i_channels = aout_FormatNbChannels( &p_aout->output.output );
00482 
00483         p_aout->output.i_nb_samples = i_period_size = ALSA_DEFAULT_PERIOD_SIZE;
00484 
00485         aout_VolumeSoftInit( p_aout );
00486     }
00487 
00488     /* Free psz_device so that all the remaining data is stack-allocated */
00489     free( psz_device );
00490 
00491     p_aout->output.pf_play = Play;
00492 
00493     snd_pcm_hw_params_alloca(&p_hw);
00494     snd_pcm_sw_params_alloca(&p_sw);
00495 
00496     /* Due to some bugs in alsa with some drivers, we need to retry in s16l
00497        if snd_pcm_hw_params fails in fl32 */
00498     while ( b_retry )
00499     {
00500         b_retry = VLC_FALSE;
00501 
00502         /* Get Initial hardware parameters */
00503         if ( ( i_snd_rc = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) ) < 0 )
00504         {
00505             msg_Err( p_aout, "unable to retrieve initial hardware parameters (%s)",
00506                          snd_strerror( i_snd_rc ) );
00507             goto error;
00508         }
00509 
00510         /* Set format. */
00511         if ( ( i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
00512                                                     i_snd_pcm_format ) ) < 0 )
00513         {
00514             if( i_snd_pcm_format != SND_PCM_FORMAT_S16 )
00515             {
00516                 i_snd_pcm_format = SND_PCM_FORMAT_S16;
00517                 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
00518                                                      p_hw, i_snd_pcm_format );
00519             }
00520             if ( i_snd_rc < 0 )
00521             {
00522                 msg_Err( p_aout, "unable to set stream sample size and "
00523                      "word order (%s)", snd_strerror( i_snd_rc ) );
00524                 goto error;
00525             }
00526         }
00527         if( i_vlc_pcm_format != VLC_FOURCC('s','p','d','i') )
00528         switch( i_snd_pcm_format )
00529         {
00530         case SND_PCM_FORMAT_FLOAT:
00531             i_vlc_pcm_format = VLC_FOURCC('f','l','3','2');
00532             break;
00533         case SND_PCM_FORMAT_S16:
00534             i_vlc_pcm_format = AOUT_FMT_S16_NE;
00535             break;
00536         }
00537         p_aout->output.output.i_format = i_vlc_pcm_format;
00538 
00539         if ( ( i_snd_rc = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw,
00540                                     SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
00541         {
00542             msg_Err( p_aout, "unable to set interleaved stream format (%s)",
00543                              snd_strerror( i_snd_rc ) );
00544             goto error;
00545         }
00546 
00547         /* Set channels. */
00548         if ( ( i_snd_rc = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw,
00549                                                       i_channels ) ) < 0 )
00550         {
00551             msg_Err( p_aout, "unable to set number of output channels (%s)",
00552                              snd_strerror( i_snd_rc ) );
00553             goto error;
00554         }
00555 
00556         /* Set rate. */
00557         i_old_rate = p_aout->output.output.i_rate;
00558 #ifdef HAVE_ALSA_NEW_API
00559         i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
00560                                                 &p_aout->output.output.i_rate,
00561                                                 NULL );
00562 #else
00563         i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
00564                                                 p_aout->output.output.i_rate,
00565                                                 NULL );
00566 #endif
00567         if( i_snd_rc < 0 || p_aout->output.output.i_rate != i_old_rate )
00568         {
00569             msg_Warn( p_aout, "The rate %d Hz is not supported by your hardware. "
00570                   "Using %d Hz instead.\n", i_old_rate,
00571                   p_aout->output.output.i_rate );
00572         }
00573 
00574         /* Set buffer size. */
00575 #ifdef HAVE_ALSA_NEW_API
00576         if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
00577                                     p_hw, &i_buffer_size ) ) < 0 )
00578 #else
00579         if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
00580                                     p_hw, i_buffer_size ) ) < 0 )
00581 #endif
00582         {
00583             msg_Err( p_aout, "unable to set buffer size (%s)",
00584                          snd_strerror( i_snd_rc ) );
00585             goto error;
00586         }
00587 
00588         /* Set period size. */
00589 #ifdef HAVE_ALSA_NEW_API
00590         if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
00591                                     p_hw, &i_period_size, NULL ) ) < 0 )
00592 #else
00593         if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
00594                                     p_hw, i_period_size, NULL ) ) < 0 )
00595 #endif
00596         {
00597             msg_Err( p_aout, "unable to set period size (%s)",
00598                          snd_strerror( i_snd_rc ) );
00599             goto error;
00600         }
00601         p_aout->output.i_nb_samples = i_period_size;
00602 
00603         /* Commit hardware parameters. */
00604         if ( ( i_snd_rc = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw ) ) < 0 )
00605         {
00606             if ( b_retry == VLC_FALSE &&
00607                                 i_snd_pcm_format == SND_PCM_FORMAT_FLOAT)
00608             {
00609                 b_retry = VLC_TRUE;
00610                 i_snd_pcm_format = SND_PCM_FORMAT_S16;
00611                 p_aout->output.output.i_format = AOUT_FMT_S16_NE;
00612                 msg_Warn( p_aout, "unable to commit hardware configuration "
00613                                   "with fl32 samples. Retrying with s16l (%s)",                                     snd_strerror( i_snd_rc ) );
00614             }
00615             else
00616             {
00617                 msg_Err( p_aout, "unable to commit hardware configuration (%s)",
00618                          snd_strerror( i_snd_rc ) );
00619                 goto error;
00620             }
00621         }
00622     }
00623 
00624 #ifdef HAVE_ALSA_NEW_API
00625     if( ( i_snd_rc = snd_pcm_hw_params_get_period_time( p_hw,
00626                                     &p_sys->i_period_time, NULL ) ) < 0 )
00627 #else
00628     if( ( p_sys->i_period_time =
00629                   (int)snd_pcm_hw_params_get_period_time( p_hw, NULL ) ) < 0 )
00630 #endif
00631     {
00632         msg_Err( p_aout, "unable to get period time (%s)",
00633                          snd_strerror( i_snd_rc ) );
00634         goto error;
00635     }
00636 
00637     /* Get Initial software parameters */
00638     snd_pcm_sw_params_current( p_sys->p_snd_pcm, p_sw );
00639 
00640     i_snd_rc = snd_pcm_sw_params_set_sleep_min( p_sys->p_snd_pcm, p_sw, 0 );
00641 
00642     i_snd_rc = snd_pcm_sw_params_set_avail_min( p_sys->p_snd_pcm, p_sw,
00643                                                 p_aout->output.i_nb_samples );
00644 
00645     /* Commit software parameters. */
00646     if ( snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw ) < 0 )
00647     {
00648         msg_Err( p_aout, "unable to set software configuration" );
00649         goto error;
00650     }
00651 
00652 #ifdef ALSA_DEBUG
00653     snd_output_printf( p_sys->p_snd_stderr, "\nALSA hardware setup:\n\n" );
00654     snd_pcm_dump_hw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
00655     snd_output_printf( p_sys->p_snd_stderr, "\nALSA software setup:\n\n" );
00656     snd_pcm_dump_sw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
00657     snd_output_printf( p_sys->p_snd_stderr, "\n" );
00658 #endif
00659 
00660     /* Create ALSA thread and wait for its readiness. */
00661     if( vlc_thread_create( p_aout, "aout", ALSAThread,
00662                            VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
00663     {
00664         msg_Err( p_aout, "cannot create ALSA thread (%s)", strerror(errno) );
00665         goto error;
00666     }
00667 
00668     return 0;
00669 
00670 error:
00671     snd_pcm_close( p_sys->p_snd_pcm );
00672 #ifdef ALSA_DEBUG
00673     snd_output_close( p_sys->p_snd_stderr );
00674 #endif
00675     free( p_sys );
00676     return VLC_EGENERIC;
00677 }
00678 
00679 /*****************************************************************************
00680  * Play: nothing to do
00681  *****************************************************************************/
00682 static void Play( aout_instance_t *p_aout )
00683 {
00684     if( !p_aout->output.p_sys->b_playing )
00685     {
00686         p_aout->output.p_sys->b_playing = 1;
00687 
00688         /* get the playing date of the first aout buffer */
00689         p_aout->output.p_sys->start_date =
00690             aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
00691 
00692         /* wake up the audio output thread */
00693         vlc_mutex_lock( &p_aout->output.p_sys->lock );
00694         vlc_cond_signal( &p_aout->output.p_sys->wait );
00695         vlc_mutex_unlock( &p_aout->output.p_sys->lock );
00696     }
00697 }
00698 
00699 /*****************************************************************************
00700  * Close: close the ALSA device
00701  *****************************************************************************/
00702 static void Close( vlc_object_t *p_this )
00703 {
00704     aout_instance_t *p_aout = (aout_instance_t *)p_this;
00705     struct aout_sys_t * p_sys = p_aout->output.p_sys;
00706     int i_snd_rc;
00707 
00708     /* make sure the audio output thread is waken up */
00709     vlc_mutex_lock( &p_aout->output.p_sys->lock );
00710     vlc_cond_signal( &p_aout->output.p_sys->wait );
00711     vlc_mutex_unlock( &p_aout->output.p_sys->lock );
00712 
00713     p_aout->b_die = VLC_TRUE;
00714     vlc_thread_join( p_aout );
00715     p_aout->b_die = VLC_FALSE;
00716 
00717     i_snd_rc = snd_pcm_close( p_sys->p_snd_pcm );
00718 
00719     if( i_snd_rc > 0 )
00720     {
00721         msg_Err( p_aout, "failed closing ALSA device (%s)",
00722                          snd_strerror( i_snd_rc ) );
00723     }
00724 
00725 #ifdef ALSA_DEBUG
00726     snd_output_close( p_sys->p_snd_stderr );
00727 #endif
00728 
00729     free( p_sys );
00730 }
00731 
00732 /*****************************************************************************
00733  * ALSAThread: asynchronous thread used to DMA the data to the device
00734  *****************************************************************************/
00735 static int ALSAThread( aout_instance_t * p_aout )
00736 {
00737     p_aout->output.p_sys->p_status =
00738         (snd_pcm_status_t *)malloc(snd_pcm_status_sizeof());
00739 
00740     /* Wait for the exact time to start playing (avoids resampling) */
00741     vlc_mutex_lock( &p_aout->output.p_sys->lock );
00742     if( !p_aout->output.p_sys->start_date )
00743         vlc_cond_wait( &p_aout->output.p_sys->wait,
00744                        &p_aout->output.p_sys->lock );
00745     vlc_mutex_unlock( &p_aout->output.p_sys->lock );
00746 
00747     mwait( p_aout->output.p_sys->start_date - AOUT_PTS_TOLERANCE / 4 );
00748 
00749     while ( !p_aout->b_die )
00750     {
00751         ALSAFill( p_aout );
00752     }
00753 
00754     free( p_aout->output.p_sys->p_status );
00755     return 0;
00756 }
00757 
00758 /*****************************************************************************
00759  * ALSAFill: function used to fill the ALSA buffer as much as possible
00760  *****************************************************************************/
00761 static void ALSAFill( aout_instance_t * p_aout )
00762 {
00763     struct aout_sys_t * p_sys = p_aout->output.p_sys;
00764 
00765     aout_buffer_t * p_buffer;
00766     snd_pcm_status_t * p_status = p_sys->p_status;
00767     snd_timestamp_t ts_next;
00768     int i_snd_rc;
00769     mtime_t next_date;
00770 
00771     /* Fill in the buffer until space or audio output buffer shortage */
00772     {
00773         /* Get the status */
00774         i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
00775         if( i_snd_rc < 0 )
00776         {
00777             msg_Err( p_aout, "unable to get the device's status (%s)",
00778                              snd_strerror( i_snd_rc ) );
00779 
00780             msleep( p_sys->i_period_time >> 1 );
00781             return;
00782         }
00783 
00784         /* Handle buffer underruns and reget the status */
00785         if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
00786         {
00787             /* Prepare the device */
00788             i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm );
00789 
00790             if( i_snd_rc == 0 )
00791             {
00792                 msg_Warn( p_aout, "recovered from buffer underrun" );
00793 
00794                 /* Reget the status */
00795                 i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
00796                 if( i_snd_rc < 0 )
00797                 {
00798                     msg_Err( p_aout, "unable to get the device's status after "
00799                              "recovery (%s)", snd_strerror( i_snd_rc ) );
00800 
00801                     msleep( p_sys->i_period_time >> 1 );
00802                     return;
00803                 }
00804             }
00805             else
00806             {
00807                 msg_Err( p_aout, "unable to recover from buffer underrun" );
00808 
00809                 msleep( p_sys->i_period_time >> 1 );
00810                 return;
00811             }
00812 
00813             /* Underrun, try to recover as quickly as possible */
00814             next_date = mdate();
00815         }
00816         else
00817         {
00818             /* Here the device should be either in the RUNNING state.
00819              * p_status is valid. */
00820 
00821             snd_pcm_status_get_tstamp( p_status, &ts_next );
00822             next_date = (mtime_t)ts_next.tv_sec * 1000000 + ts_next.tv_usec;
00823             if( next_date )
00824             {
00825                 next_date += (mtime_t)snd_pcm_status_get_delay(p_status)
00826                         * 1000000 / p_aout->output.output.i_rate;
00827             }
00828             else
00829             {
00830                 /* With screwed ALSA drivers the timestamp is always zero;
00831                  * use another method then */
00832                 snd_pcm_sframes_t delay;
00833                 ssize_t i_bytes = 0;
00834 
00835                 if( !snd_pcm_delay( p_sys->p_snd_pcm, &delay ) )
00836                 {
00837                     i_bytes = snd_pcm_frames_to_bytes(p_sys->p_snd_pcm, delay);
00838                 }
00839                 next_date = mdate() + (mtime_t)i_bytes * 1000000
00840                         / p_aout->output.output.i_bytes_per_frame
00841                         / p_aout->output.output.i_rate
00842                         * p_aout->output.output.i_frame_length;
00843             }
00844         }
00845 
00846         p_buffer = aout_OutputNextBuffer( p_aout, next_date,
00847                         (p_aout->output.output.i_format ==
00848                          VLC_FOURCC('s','p','d','i')) );
00849 
00850         /* Audio output buffer shortage -> stop the fill process and wait */
00851         if( p_buffer == NULL )
00852         {
00853             msleep( p_sys->i_period_time >> 1 );
00854             return;
00855         }
00856 
00857         i_snd_rc = snd_pcm_writei( p_sys->p_snd_pcm, p_buffer->p_buffer,
00858                                    p_buffer->i_nb_samples );
00859 
00860         if( i_snd_rc < 0 )
00861         {
00862             msg_Err( p_aout, "write failed (%s)",
00863                              snd_strerror( i_snd_rc ) );
00864         }
00865 
00866         aout_BufferFree( p_buffer );
00867     }
00868 }
00869 
00870 static void GetDevicesForCard(module_config_t *p_item, int i_card);
00871 static void GetDevices( module_config_t *p_item );
00872 
00873 /*****************************************************************************
00874  * config variable callback
00875  *****************************************************************************/
00876 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
00877                                vlc_value_t newval, vlc_value_t oldval, void *p_unused )
00878 {
00879     module_config_t *p_item;
00880     int i;
00881 
00882     p_item = config_FindConfig( p_this, psz_name );
00883     if( !p_item ) return VLC_SUCCESS;
00884 
00885     /* Clear-up the current list */
00886     if( p_item->i_list )
00887     {
00888         /* Keep the first entrie */
00889         for( i = 1; i < p_item->i_list; i++ )
00890         {
00891             free( p_item->ppsz_list[i] );
00892             free( p_item->ppsz_list_text[i] );
00893         }
00894         /* TODO: Remove when no more needed */
00895         p_item->ppsz_list[i] = NULL;
00896         p_item->ppsz_list_text[i] = NULL;
00897     }
00898     p_item->i_list = 1;
00899 
00900     GetDevices( p_item );
00901 
00902     /* Signal change to the interface */
00903     p_item->b_dirty = VLC_TRUE;
00904 
00905     return VLC_SUCCESS;
00906 
00907 }
00908 
00909 
00910 static void GetDevicesForCard(module_config_t *p_item, int i_card)
00911 {
00912     int i_pcm_device = -1;
00913     int i_err = 0;
00914     snd_pcm_info_t *p_pcm_info;
00915     snd_ctl_t *p_ctl;
00916     char psz_dev[64];
00917     char *psz_card_name;
00918     
00919     sprintf(psz_dev, "hw:%i", i_card);
00920     
00921     if (( i_err = snd_ctl_open(&p_ctl, psz_dev, 0)) < 0 )
00922     {
00923         return;
00924     }
00925     
00926     if ((i_err = snd_card_get_name(i_card, &psz_card_name)) != 0)
00927     {
00928         psz_card_name = _("Unknown soundcard");
00929     }
00930 
00931     snd_pcm_info_alloca(&p_pcm_info);
00932 
00933     for (;;)
00934     {
00935         char *psz_device, *psz_descr;
00936         if ((i_err = snd_ctl_pcm_next_device(p_ctl, &i_pcm_device)) < 0)
00937         {
00938             i_pcm_device = -1;
00939         }
00940         if ( i_pcm_device < 0 )
00941             break;
00942 
00943         snd_pcm_info_set_device(p_pcm_info, i_pcm_device);
00944         snd_pcm_info_set_subdevice(p_pcm_info, 0);
00945         snd_pcm_info_set_stream(p_pcm_info, SND_PCM_STREAM_PLAYBACK);
00946 
00947         if ((i_err = snd_ctl_pcm_info(p_ctl, p_pcm_info)) < 0)
00948         {
00949             if (i_err != -ENOENT)
00950             {
00951 /*                printf("get_devices_for_card(): "
00952                          "snd_ctl_pcm_info() "
00953                          "failed (%d:%d): %s.\n", i_card,
00954                          i_pcm_device, snd_strerror(-i_err));*/
00955             }
00956             continue;
00957         }
00958 
00959         asprintf( &psz_device, "hw:%d,%d", i_card, i_pcm_device );
00960         asprintf( &psz_descr, "%s: %s (%s)", psz_card_name,
00961                   snd_pcm_info_get_name(p_pcm_info), psz_device );
00962 
00963         p_item->ppsz_list =
00964             (char **)realloc( p_item->ppsz_list,
00965                               (p_item->i_list + 2) * sizeof(char *) );
00966         p_item->ppsz_list_text =
00967             (char **)realloc( p_item->ppsz_list_text,
00968                               (p_item->i_list + 2) * sizeof(char *) );
00969         p_item->ppsz_list[ p_item->i_list ] = psz_device;
00970         p_item->ppsz_list_text[ p_item->i_list ] = psz_descr;
00971         p_item->i_list++;
00972         p_item->ppsz_list[ p_item->i_list ] = NULL;
00973         p_item->ppsz_list_text[ p_item->i_list ] = NULL;
00974                 
00975     }
00976 
00977     snd_ctl_close( p_ctl );
00978 }
00979 
00980 
00981 
00982 static void GetDevices( module_config_t *p_item )
00983 {
00984     int i_card = -1;
00985     int i_err = 0;
00986     
00987     if ((i_err = snd_card_next(&i_card)) != 0)
00988     {
00989 //        g_warning("snd_next_card() failed: %s", snd_strerror(-err));
00990         return;
00991     }
00992     
00993     while (i_card > -1)
00994     {
00995         GetDevicesForCard(p_item, i_card);
00996         if ((i_err = snd_card_next(&i_card)) != 0)
00997         {
00998 //            g_warning("snd_next_card() failed: %s",
00999 //                  snd_strerror(-err));
01000             break;
01001         }
01002     }
01003 }

Generated on Tue Dec 20 10:14:27 2005 for vlc-0.8.4a by  doxygen 1.4.2