alsa.c 36.7 KB
Newer Older
1 2 3
/*****************************************************************************
 * alsa.c : alsa plugin for vlc
 *****************************************************************************
4
 * Copyright (C) 2000-2001 the VideoLAN team
5
 * $Id$
6 7 8 9
 *
 * Authors: Henri Fallon <henri@videolan.org> - Original Author
 *          Jeffrey Baker <jwbaker@acm.org> - Port to ALSA 1.0 API
 *          John Paul Lorenti <jpl31@columbia.edu> - Device selection
10
 *          Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr> - S/PDIF and aout3
11 12 13 14 15
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
16
 *
17 18 19 20 21 22 23
 * This program is distributed in the hope that it will be useful,
 * but 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 26 27 28 29
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
30 31 32 33
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

34 35
#include <assert.h>

36
#include <vlc_common.h>
37
#include <vlc_plugin.h>
Rémi Denis-Courmont's avatar
More  
Rémi Denis-Courmont committed
38

39
#include <errno.h>                                                 /* ENOMEM */
40
#include <vlc_dialog.h>
41

Clément Stenac's avatar
Clément Stenac committed
42
#include <vlc_aout.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
43
#include <vlc_cpu.h>
44

45
/* ALSA part
46
   Note: we use the new API which is available since 0.9.0beta10a. */
47 48
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
49
#include <alsa/asoundlib.h>
50
#include <alsa/version.h>
51

52 53
/*#define ALSA_DEBUG*/

54 55 56 57 58 59 60 61 62
/*****************************************************************************
 * aout_sys_t: ALSA audio output method descriptor
 *****************************************************************************
 * This structure is part of the audio output thread descriptor.
 * It describes the ALSA specific properties of an audio device.
 *****************************************************************************/
struct aout_sys_t
{
    snd_pcm_t         * p_snd_pcm;
63
    unsigned int                 i_period_time;
64

Gildas Bazin's avatar
 
Gildas Bazin committed
65
#ifdef ALSA_DEBUG
66 67
    snd_output_t      * p_snd_stderr;
#endif
Gildas Bazin's avatar
 
Gildas Bazin committed
68

69
    mtime_t      start_date;
70
    vlc_thread_t thread;
71
    vlc_sem_t    wait;
72 73
};

74 75
#define A52_FRAME_NB 1536

76
/* These values are in frames.
77
   To convert them to a number of bytes you have to multiply them by the
78
   number of channel(s) (eg. 2 for stereo) and the size of a sample (eg.
Sam Hocevar's avatar
Sam Hocevar committed
79
   2 for int16_t). */
Gildas Bazin's avatar
 
Gildas Bazin committed
80
#define ALSA_DEFAULT_PERIOD_SIZE        1024
Gildas Bazin's avatar
 
Gildas Bazin committed
81
#define ALSA_DEFAULT_BUFFER_SIZE        ( ALSA_DEFAULT_PERIOD_SIZE << 8 )
82
#define ALSA_SPDIF_PERIOD_SIZE          A52_FRAME_NB
83
#define ALSA_SPDIF_BUFFER_SIZE          ( ALSA_SPDIF_PERIOD_SIZE << 4 )
84
/* Why << 4 ? --Meuuh */
85
/* Why not ? --Bozo */
86
/* Right. --Meuuh */
87

88
#define DEFAULT_ALSA_DEVICE N_("default")
89

90 91 92
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
93 94 95
static int   Open         ( vlc_object_t * );
static void  Close        ( vlc_object_t * );
static void  Play         ( aout_instance_t * );
96
static void* ALSAThread   ( void * );
97
static void  ALSAFill     ( aout_instance_t * );
98 99
static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
                                vlc_value_t newval, vlc_value_t oldval, void *p_unused );
100 101
static void GetDevicesForCard( vlc_object_t *, module_config_t *, int card );
static void GetDevices( vlc_object_t *, module_config_t * );
102 103 104 105

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
106 107
static const char *const ppsz_devices[] = { "default" };
static const char *const ppsz_devices_text[] = { N_("Default") };
108 109 110 111 112
vlc_module_begin ()
    set_shortname( "ALSA" )
    set_description( N_("ALSA audio output") )
    set_category( CAT_AUDIO )
    set_subcategory( SUBCAT_AUDIO_AOUT )
113
    add_string( "alsa-audio-device", DEFAULT_ALSA_DEVICE, aout_FindAndRestart,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
114
                N_("ALSA Device Name"), NULL, false )
115
        add_deprecated_alias( "alsadev" )   /* deprecated since 0.9.3 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
116
        change_string_list( ppsz_devices, ppsz_devices_text, FindDevicesCallback )
117
        change_action_add( FindDevicesCallback, N_("Refresh list") )
118

119 120 121
    set_capability( "audio output", 150 )
    set_callbacks( Open, Close )
vlc_module_end ()
122

123 124 125 126 127
/*****************************************************************************
 * Probe: probe the audio device for available formats and channels
 *****************************************************************************/
static void Probe( aout_instance_t * p_aout,
                   const char * psz_device, const char * psz_iec_device,
Gildas Bazin's avatar
 
Gildas Bazin committed
128
                   int *pi_snd_pcm_format )
129 130
{
    struct aout_sys_t * p_sys = p_aout->output.p_sys;
Gildas Bazin's avatar
 
Gildas Bazin committed
131
    vlc_value_t val, text;
Gildas Bazin's avatar
 
Gildas Bazin committed
132
    int i_ret;
133

Gildas Bazin's avatar
 
Gildas Bazin committed
134
    var_Create ( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
135
    text.psz_string = _("Audio Device");
Gildas Bazin's avatar
 
Gildas Bazin committed
136
    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
137

Gildas Bazin's avatar
 
Gildas Bazin committed
138 139 140 141
    /* We'll open the audio device in non blocking mode so we can just exit
     * when it is already in use, but for the real stuff we'll still use
     * the blocking mode */

142
    /* Now test linear PCM capabilities */
Gildas Bazin's avatar
 
Gildas Bazin committed
143 144 145
    if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
                                 SND_PCM_STREAM_PLAYBACK,
                                 SND_PCM_NONBLOCK ) ) )
146 147 148 149 150 151 152 153 154 155
    {
        int i_channels;
        snd_pcm_hw_params_t * p_hw;
        snd_pcm_hw_params_alloca (&p_hw);

        if ( snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) < 0 )
        {
            msg_Warn( p_aout, "unable to retrieve initial hardware parameters"
                              ", disabling linear PCM audio" );
            snd_pcm_close( p_sys->p_snd_pcm );
Gildas Bazin's avatar
 
Gildas Bazin committed
156
            var_Destroy( p_aout, "audio-device" );
157 158 159
            return;
        }

160
        if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
Gildas Bazin's avatar
 
Gildas Bazin committed
161
                                           *pi_snd_pcm_format ) < 0 )
162
        {
163 164
            int i_snd_rc = -1;

Gildas Bazin's avatar
 
Gildas Bazin committed
165 166 167
            if( *pi_snd_pcm_format != SND_PCM_FORMAT_S16 )
            {
                *pi_snd_pcm_format = SND_PCM_FORMAT_S16;
168 169 170 171 172 173 174 175 176 177
                i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
                                                    p_hw, *pi_snd_pcm_format );
            }
            if ( i_snd_rc < 0 )
            {
                msg_Warn( p_aout, "unable to set stream sample size and "
                          "word order, disabling linear PCM audio" );
                snd_pcm_close( p_sys->p_snd_pcm );
                var_Destroy( p_aout, "audio-device" );
                return;
Gildas Bazin's avatar
 
Gildas Bazin committed
178
            }
179
        }
180 181 182

        i_channels = aout_FormatNbChannels( &p_aout->output.output );

183
        while ( i_channels > 0 )
184 185 186 187 188 189 190
        {
            if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw,
                                                   i_channels ) )
            {
                switch ( i_channels )
                {
                case 1:
Gildas Bazin's avatar
 
Gildas Bazin committed
191
                    val.i_int = AOUT_VAR_MONO;
192
                    text.psz_string = _("Mono");
193
                    var_Change( p_aout, "audio-device",
Gildas Bazin's avatar
 
Gildas Bazin committed
194
                                VLC_VAR_ADDCHOICE, &val, &text );
195 196
                    break;
                case 2:
Gildas Bazin's avatar
 
Gildas Bazin committed
197
                    val.i_int = AOUT_VAR_STEREO;
198
                    text.psz_string = _("Stereo");
199
                    var_Change( p_aout, "audio-device",
Gildas Bazin's avatar
 
Gildas Bazin committed
200
                                VLC_VAR_ADDCHOICE, &val, &text );
Gildas Bazin's avatar
 
Gildas Bazin committed
201
                    var_Set( p_aout, "audio-device", val );
202 203
                    break;
                case 4:
Gildas Bazin's avatar
 
Gildas Bazin committed
204
                    val.i_int = AOUT_VAR_2F2R;
205
                    text.psz_string = _("2 Front 2 Rear");
206
                    var_Change( p_aout, "audio-device",
Gildas Bazin's avatar
 
Gildas Bazin committed
207
                                VLC_VAR_ADDCHOICE, &val, &text );
208 209
                    break;
                case 6:
Gildas Bazin's avatar
 
Gildas Bazin committed
210
                    val.i_int = AOUT_VAR_5_1;
211
                    text.psz_string = "5.1";
212
                    var_Change( p_aout, "audio-device",
Gildas Bazin's avatar
 
Gildas Bazin committed
213
                                VLC_VAR_ADDCHOICE, &val, &text );
214 215 216 217 218 219 220
                    break;
                }
            }

            --i_channels;
        }

221
        /* Special case for mono on stereo only boards */
222
        i_channels = aout_FormatNbChannels( &p_aout->output.output );
223 224 225 226 227 228
        var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
        if( val.i_int <= 0 && i_channels == 1 )
        {
            if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw, 2 ))
            {
                val.i_int = AOUT_VAR_STEREO;
229
                text.psz_string = (char*)N_("Stereo");
230 231 232 233 234
                var_Change( p_aout, "audio-device",
                            VLC_VAR_ADDCHOICE, &val, &text );
                var_Set( p_aout, "audio-device", val );
            }
        }
Clément Stenac's avatar
Clément Stenac committed
235

236 237 238
        /* Close the previously opened device */
        snd_pcm_close( p_sys->p_snd_pcm );
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
239 240 241 242
    else if ( i_ret == -EBUSY )
    {
        msg_Warn( p_aout, "audio device: %s is already in use", psz_device );
    }
243

Gildas Bazin's avatar
 
Gildas Bazin committed
244 245 246 247
    /* Test for S/PDIF device if needed */
    if ( psz_iec_device )
    {
        /* Opening the device should be enough */
Gildas Bazin's avatar
 
Gildas Bazin committed
248 249 250
        if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
                                     SND_PCM_STREAM_PLAYBACK,
                                     SND_PCM_NONBLOCK ) ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
251
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
252
            val.i_int = AOUT_VAR_SPDIF;
253
            text.psz_string = (char*)N_("A/52 over S/PDIF");
Gildas Bazin's avatar
 
Gildas Bazin committed
254 255
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
256
            if( var_InheritInteger( p_aout, "spdif" ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
257
                var_Set( p_aout, "audio-device", val );
Gildas Bazin's avatar
 
Gildas Bazin committed
258 259

            snd_pcm_close( p_sys->p_snd_pcm );
Gildas Bazin's avatar
 
Gildas Bazin committed
260
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
261 262 263 264 265
        else if ( i_ret == -EBUSY )
        {
            msg_Warn( p_aout, "audio device: %s is already in use",
                      psz_iec_device );
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
266 267
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
268
    var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
269 270 271 272 273 274 275
#if (SND_LIB_VERSION <= 0x010015)
# warning Please update alsa-lib to version > 1.0.21a.
    var_Create( p_aout->p_libvlc, "alsa-working", VLC_VAR_BOOL );
    if( val.i_int <= 0 )
    {
        if( var_GetBool( p_aout->p_libvlc, "alsa-working" ) )
            dialog_FatalWait( p_aout, "ALSA version problem",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
276
                "VLC failed to re-initialize your sound output device.\n"
277 278 279 280 281 282
                "Please update alsa-lib to version 1.0.22 or higher "
                "to fix this issue." );
    }
    else
        var_SetBool( p_aout->p_libvlc, "alsa-working", true );
#endif
Gildas Bazin's avatar
 
Gildas Bazin committed
283 284 285
    if( val.i_int <= 0 )
    {
        /* Probe() has failed. */
286 287 288 289 290 291 292 293 294 295 296 297
#if (SND_LIB_VERSION <= 0x010017)
# warning Please update alsa-lib to version > 1.0.23.
        var_Create( p_aout->p_libvlc, "alsa-broken", VLC_VAR_BOOL );
        if( !var_GetBool( p_aout->p_libvlc, "alsa-broken" ) )
        {
            var_SetBool( p_aout->p_libvlc, "alsa-broken", true );
            dialog_FatalWait( p_aout, "Potential ALSA version problem",
                "VLC failed to initialize your sound output device (if any).\n"
                "Please update alsa-lib to version 1.0.24 or higher "
                "to try to fix this issue." );
        }
#endif
298
        msg_Dbg( p_aout, "failed to find a usable ALSA configuration" );
Gildas Bazin's avatar
 
Gildas Bazin committed
299
        var_Destroy( p_aout, "audio-device" );
300
        GetDevices( VLC_OBJECT(p_aout), NULL );
Gildas Bazin's avatar
 
Gildas Bazin committed
301 302 303
        return;
    }

304 305
    /* Add final settings to the variable */
    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
306
    var_SetBool( p_aout, "intf-change", true );
307 308
}

309 310 311
/*****************************************************************************
 * Open: create a handle and open an alsa device
 *****************************************************************************
312 313 314 315
 * This function opens an alsa device, through the alsa API.
 *
 * Note: the only heap-allocated string is psz_device. All the other pointers
 * are references to psz_device or to stack-allocated data.
316 317 318
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
319 320
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
    struct aout_sys_t * p_sys;
321 322 323 324 325 326 327 328 329 330 331 332 333
    vlc_value_t val;

    char psz_default_iec_device[128]; /* Buffer used to store the default
                                         S/PDIF device */
    char * psz_device, * psz_iec_device; /* device names for PCM and S/PDIF
                                            output */

    int i_vlc_pcm_format; /* Audio format for VLC's data */
    int i_snd_pcm_format; /* Audio format for ALSA's data */

    snd_pcm_uframes_t i_buffer_size = 0;
    snd_pcm_uframes_t i_period_size = 0;
    int i_channels = 0;
334

335 336
    snd_pcm_hw_params_t *p_hw;
    snd_pcm_sw_params_t *p_sw;
Christophe Massiot's avatar
Christophe Massiot committed
337

338
    int i_snd_rc = -1;
339
    unsigned int i_old_rate;
340
    bool b_retry = true;
341

342
    /* Allocate structures */
343 344
    p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_sys == NULL )
345
        return VLC_ENOMEM;
346

347
    /* Get device name */
348
    if( (psz_device = var_InheritString( p_aout, "alsa-audio-device" )) == NULL )
349 350
    {
        msg_Err( p_aout, "no audio device given (maybe \"default\" ?)" );
351
        dialog_Fatal( p_aout, _("No Audio Device"), "%s",
352 353
                        _("No audio device name was given. You might want to " \
                          "enter \"default\".") );
354 355 356
        free( p_sys );
        return VLC_EGENERIC;
    }
357

358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
    /* Choose the IEC device for S/PDIF output:
       if the device is overriden by the user then it will be the one
       otherwise we compute the default device based on the output format. */
    if( AOUT_FMT_NON_LINEAR( &p_aout->output.output )
        && !strcmp( psz_device, DEFAULT_ALSA_DEVICE ) )
    {
        snprintf( psz_default_iec_device, sizeof(psz_default_iec_device),
                  "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
                  IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
                  IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
                  0,
                  ( p_aout->output.output.i_rate == 48000 ?
                    IEC958_AES3_CON_FS_48000 :
                    ( p_aout->output.output.i_rate == 44100 ?
                      IEC958_AES3_CON_FS_44100 : IEC958_AES3_CON_FS_32000 ) ) );
        psz_iec_device = psz_default_iec_device;
    }
    else if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
    {
        psz_iec_device = psz_device;
    }
    else
    {
        psz_iec_device = NULL;
    }

    /* Choose the linear PCM format (read the comment above about FPU
       and float32) */
386
    if( HAVE_FPU )
387
    {
388
        i_vlc_pcm_format = VLC_CODEC_FL32;
389 390
        i_snd_pcm_format = SND_PCM_FORMAT_FLOAT;
    }
391
    else
392
    {
393
        i_vlc_pcm_format = VLC_CODEC_S16N;
394 395 396 397 398 399 400
        i_snd_pcm_format = SND_PCM_FORMAT_S16;
    }

    /* If the variable doesn't exist then it's the first time we're called
       and we have to probe the available audio formats and channels */
    if ( var_Type( p_aout, "audio-device" ) == 0 )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
401
        Probe( p_aout, psz_device, psz_iec_device, &i_snd_pcm_format );
402 403 404 405 406 407 408 409 410
    }

    if ( var_Get( p_aout, "audio-device", &val ) < 0 )
    {
        free( p_sys );
        free( psz_device );
        return VLC_EGENERIC;
    }

411 412
    p_aout->output.output.i_format = i_vlc_pcm_format;
    if ( val.i_int == AOUT_VAR_5_1 )
413 414 415 416 417
    {
        p_aout->output.output.i_physical_channels
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
               | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
               | AOUT_CHAN_LFE;
Gildas Bazin's avatar
 
Gildas Bazin committed
418 419
        free( psz_device );
        psz_device = strdup( "surround51" );
420
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
421
    else if ( val.i_int == AOUT_VAR_2F2R )
422 423 424 425
    {
        p_aout->output.output.i_physical_channels
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
               | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
Gildas Bazin's avatar
 
Gildas Bazin committed
426 427
        free( psz_device );
        psz_device = strdup( "surround40" );
428
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
429
    else if ( val.i_int == AOUT_VAR_STEREO )
430 431 432 433
    {
        p_aout->output.output.i_physical_channels
            = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
434
    else if ( val.i_int == AOUT_VAR_MONO )
435 436 437
    {
        p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
    }
438
    else if( val.i_int != AOUT_VAR_SPDIF )
439 440
    {
        /* This should not happen ! */
Gildas Bazin's avatar
 
Gildas Bazin committed
441
        msg_Err( p_aout, "internal: can't find audio-device (%i)", val.i_int );
442
        free( p_sys );
443 444 445 446
        free( psz_device );
        return VLC_EGENERIC;
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
447
#ifdef ALSA_DEBUG
448 449
    snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 );
#endif
450

451
    /* Open the device */
452
    if ( val.i_int == AOUT_VAR_SPDIF )
453
    {
454 455
        if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
                            SND_PCM_STREAM_PLAYBACK, 0 ) ) < 0 )
456
        {
457 458
            msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
                             psz_iec_device, snd_strerror( i_snd_rc ) );
459
            dialog_Fatal( p_aout, _("Audio output failed"),
460 461
                            _("VLC could not open the ALSA device \"%s\" (%s)."),
                            psz_iec_device, snd_strerror( i_snd_rc ) );
462 463 464
            free( p_sys );
            free( psz_device );
            return VLC_EGENERIC;
465
        }
466 467 468
        i_buffer_size = ALSA_SPDIF_BUFFER_SIZE;
        i_snd_pcm_format = SND_PCM_FORMAT_S16;
        i_channels = 2;
469

470
        i_vlc_pcm_format = VLC_CODEC_SPDIFL;
471 472 473
        p_aout->output.i_nb_samples = i_period_size = ALSA_SPDIF_PERIOD_SIZE;
        p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
        p_aout->output.output.i_frame_length = A52_FRAME_NB;
474

475
        aout_VolumeNoneInit( p_aout );
476
    }
477
    else
478
    {
479 480
        int i;

Gildas Bazin's avatar
 
Gildas Bazin committed
481 482
        msg_Dbg( p_aout, "opening ALSA device `%s'", psz_device );

483
        /* Since it seems snd_pcm_close hasn't really released the device at
484 485 486 487 488 489 490 491 492 493
          the time it returns, probe if the device is available in loop for 1s.
          We cannot use blocking mode since the we would wait indefinitely when
          switching from a dmx device to surround51. */

        for( i = 10; i >= 0; i-- )
        {
            if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
                   SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) ) == -EBUSY )
            {
                if( i ) msleep( 100000 /* 100ms */ );
494 495 496
                else
                {
                    msg_Err( p_aout, "audio device: %s is already in use",
497
                              psz_device );
498
                    dialog_Fatal( p_aout, _("Audio output failed"),
499 500 501
                                    _("The audio device \"%s\" is already in use."),
                                    psz_device );
                }
502 503 504 505 506
                continue;
            }
            break;
        }
        if( i_snd_rc < 0 )
507 508
        {
            msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
509
                             psz_device, snd_strerror( i_snd_rc ) );
510
            dialog_Fatal( p_aout, _("Audio output failed"),
511 512
                            _("VLC could not open the ALSA device \"%s\" (%s)."),
                            psz_device, snd_strerror( i_snd_rc ) );
513 514 515 516
            free( p_sys );
            free( psz_device );
            return VLC_EGENERIC;
        }
517 518 519 520

        /* We want blocking mode */
        snd_pcm_nonblock( p_sys->p_snd_pcm, 0 );

521
        i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE;
522 523 524
        i_channels = aout_FormatNbChannels( &p_aout->output.output );

        p_aout->output.i_nb_samples = i_period_size = ALSA_DEFAULT_PERIOD_SIZE;
525

526
        aout_VolumeSoftInit( p_aout );
527 528
    }

529
    /* Free psz_device so that all the remaining data is stack-allocated */
530
    free( psz_device );
531

532
    p_aout->output.pf_play = Play;
533

534 535 536
    snd_pcm_hw_params_alloca(&p_hw);
    snd_pcm_sw_params_alloca(&p_sw);

537 538 539
    /* Due to some bugs in alsa with some drivers, we need to retry in s16l
       if snd_pcm_hw_params fails in fl32 */
    while ( b_retry )
540
    {
541
        b_retry = false;
542 543 544 545 546

        /* Get Initial hardware parameters */
        if ( ( i_snd_rc = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) ) < 0 )
        {
            msg_Err( p_aout, "unable to retrieve initial hardware parameters (%s)",
547
                         snd_strerror( i_snd_rc ) );
548 549
            goto error;
        }
550

551 552
        /* Set format. */
        if ( ( i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
553
                                                    i_snd_pcm_format ) ) < 0 )
554
        {
555 556 557 558
            if( i_snd_pcm_format != SND_PCM_FORMAT_S16 )
            {
                i_snd_pcm_format = SND_PCM_FORMAT_S16;
                i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
559
                                                     p_hw, i_snd_pcm_format );
560 561 562 563 564 565 566
            }
            if ( i_snd_rc < 0 )
            {
                msg_Err( p_aout, "unable to set stream sample size and "
                     "word order (%s)", snd_strerror( i_snd_rc ) );
                goto error;
            }
567
        }
568
        if( i_vlc_pcm_format != VLC_CODEC_SPDIFL )
569
        switch( i_snd_pcm_format )
570
        {
571
        case SND_PCM_FORMAT_FLOAT:
572
            i_vlc_pcm_format = VLC_CODEC_FL32;
573 574
            break;
        case SND_PCM_FORMAT_S16:
575
            i_vlc_pcm_format = VLC_CODEC_S16N;
576
            break;
577
        }
578
        p_aout->output.output.i_format = i_vlc_pcm_format;
579

580
        if ( ( i_snd_rc = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw,
581
                                    SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
582 583 584 585 586
        {
            msg_Err( p_aout, "unable to set interleaved stream format (%s)",
                             snd_strerror( i_snd_rc ) );
            goto error;
        }
587

588 589
        /* Set channels. */
        if ( ( i_snd_rc = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw,
590
                                                      i_channels ) ) < 0 )
591 592 593 594 595
        {
            msg_Err( p_aout, "unable to set number of output channels (%s)",
                             snd_strerror( i_snd_rc ) );
            goto error;
        }
596

597 598 599
        /* Set rate. */
        i_old_rate = p_aout->output.output.i_rate;
        i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
600 601
                                                &p_aout->output.output.i_rate,
                                                NULL );
602 603
        if( i_snd_rc < 0 || p_aout->output.output.i_rate != i_old_rate )
        {
604 605 606
            msg_Warn( p_aout, "The rate %d Hz is not supported by your " \
                "hardware. Using %d Hz instead.\n", i_old_rate, \
                p_aout->output.output.i_rate );
607
        }
608

609 610 611
        /* Set period size. */
        if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
                                    p_hw, &i_period_size, NULL ) ) < 0 )
612
        {
613
            msg_Err( p_aout, "unable to set period size (%s)",
614
                         snd_strerror( i_snd_rc ) );
615 616
            goto error;
        }
617
        p_aout->output.i_nb_samples = i_period_size;
618

619 620 621
/* Set buffer size. */
        if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
                                    p_hw, &i_buffer_size ) ) < 0 )
622
        {
623
            msg_Err( p_aout, "unable to set buffer size (%s)",
624
                         snd_strerror( i_snd_rc ) );
625 626
            goto error;
        }
627

628 629 630
        /* Commit hardware parameters. */
        if ( ( i_snd_rc = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw ) ) < 0 )
        {
631
            if ( b_retry == false &&
632 633
                                i_snd_pcm_format == SND_PCM_FORMAT_FLOAT)
            {
634
                b_retry = true;
635
                i_snd_pcm_format = SND_PCM_FORMAT_S16;
636
                p_aout->output.output.i_format = VLC_CODEC_S16N;
637 638 639 640 641 642
                msg_Warn( p_aout, "unable to commit hardware configuration "
                                  "with fl32 samples. Retrying with s16l (%s)",                                     snd_strerror( i_snd_rc ) );
            }
            else
            {
                msg_Err( p_aout, "unable to commit hardware configuration (%s)",
643
                         snd_strerror( i_snd_rc ) );
644 645 646
                goto error;
            }
        }
647 648
    }

649 650 651 652 653 654 655
    if( ( i_snd_rc = snd_pcm_hw_params_get_period_time( p_hw,
                                    &p_sys->i_period_time, NULL ) ) < 0 )
    {
        msg_Err( p_aout, "unable to get period time (%s)",
                         snd_strerror( i_snd_rc ) );
        goto error;
    }
656

657
    /* Get Initial software parameters */
658
    snd_pcm_sw_params_current( p_sys->p_snd_pcm, p_sw );
659

660
    i_snd_rc = snd_pcm_sw_params_set_sleep_min( p_sys->p_snd_pcm, p_sw, 0 );
661

662 663
    i_snd_rc = snd_pcm_sw_params_set_avail_min( p_sys->p_snd_pcm, p_sw,
                                                p_aout->output.i_nb_samples );
664 665 666 667 668 669 670 671 672
    /* start playing when one period has been written */
    i_snd_rc = snd_pcm_sw_params_set_start_threshold( p_sys->p_snd_pcm, p_sw,
                                                      ALSA_DEFAULT_PERIOD_SIZE);
    if( i_snd_rc < 0 )
    {
        msg_Err( p_aout, "unable to set start threshold (%s)",
                          snd_strerror( i_snd_rc ) );
        goto error;
    }
673

674
    /* Commit software parameters. */
675
    if ( snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw ) < 0 )
676 677
    {
        msg_Err( p_aout, "unable to set software configuration" );
678
        goto error;
679 680
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
681
#ifdef ALSA_DEBUG
682 683 684 685 686 687 688
    snd_output_printf( p_sys->p_snd_stderr, "\nALSA hardware setup:\n\n" );
    snd_pcm_dump_hw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
    snd_output_printf( p_sys->p_snd_stderr, "\nALSA software setup:\n\n" );
    snd_pcm_dump_sw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
    snd_output_printf( p_sys->p_snd_stderr, "\n" );
#endif

689 690 691
    p_sys->start_date = 0;
    vlc_sem_init( &p_sys->wait, 0 );

Christophe Massiot's avatar
Christophe Massiot committed
692
    /* Create ALSA thread and wait for its readiness. */
693 694
    if( vlc_clone( &p_sys->thread, ALSAThread, p_aout,
                   VLC_THREAD_PRIORITY_OUTPUT ) )
Christophe Massiot's avatar
Christophe Massiot committed
695
    {
696
        msg_Err( p_aout, "cannot create ALSA thread (%m)" );
697
        vlc_sem_destroy( &p_sys->wait );
698
        goto error;
Christophe Massiot's avatar
Christophe Massiot committed
699
    }
700

701
    return 0;
702 703 704

error:
    snd_pcm_close( p_sys->p_snd_pcm );
Gildas Bazin's avatar
 
Gildas Bazin committed
705
#ifdef ALSA_DEBUG
706 707 708 709
    snd_output_close( p_sys->p_snd_stderr );
#endif
    free( p_sys );
    return VLC_EGENERIC;
710 711
}

712 713 714 715 716
static void PlayIgnore( aout_instance_t *p_aout )
{   /* Already playing - nothing to do */
    (void) p_aout;
}

717
/*****************************************************************************
718
 * Play: start playback
719
 *****************************************************************************/
720
static void Play( aout_instance_t *p_aout )
721
{
722
    p_aout->output.pf_play = PlayIgnore;
Gildas Bazin's avatar
 
Gildas Bazin committed
723

724 725 726
    /* get the playing date of the first aout buffer */
    p_aout->output.p_sys->start_date =
        aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
Gildas Bazin's avatar
 
Gildas Bazin committed
727

728 729
    /* wake up the audio output thread */
    sem_post( &p_aout->output.p_sys->wait );
730 731 732
}

/*****************************************************************************
733
 * Close: close the ALSA device
734
 *****************************************************************************/
735
static void Close( vlc_object_t *p_this )
736
{
737 738 739
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
    struct aout_sys_t * p_sys = p_aout->output.p_sys;
    int i_snd_rc;
740

741
    /* Make sure that the thread will stop once it is waken up */
742 743
    vlc_cancel( p_sys->thread );
    vlc_join( p_sys->thread, NULL );
744
    vlc_sem_destroy( &p_sys->wait );
Gildas Bazin's avatar
 
Gildas Bazin committed
745

746
    /* */
747
    i_snd_rc = snd_pcm_close( p_sys->p_snd_pcm );
748

749 750 751 752
    if( i_snd_rc > 0 )
    {
        msg_Err( p_aout, "failed closing ALSA device (%s)",
                         snd_strerror( i_snd_rc ) );
753 754
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
755
#ifdef ALSA_DEBUG
756 757 758
    snd_output_close( p_sys->p_snd_stderr );
#endif

759
    free( p_sys );
760 761
}

762 763 764 765 766
static void pcm_drop(void *pcm)
{
    snd_pcm_drop(pcm);
}

767
/*****************************************************************************
768
 * ALSAThread: asynchronous thread used to DMA the data to the device
769
 *****************************************************************************/
770
static void* ALSAThread( void *data )
771
{
772
    aout_instance_t * p_aout = data;
773
    struct aout_sys_t * p_sys = p_aout->output.p_sys;
774

Gildas Bazin's avatar
 
Gildas Bazin committed
775
    /* Wait for the exact time to start playing (avoids resampling) */
776
    vlc_sem_wait( &p_sys->wait );
777
    mwait( p_sys->start_date - AOUT_PTS_TOLERANCE / 4 );
778

779 780
    vlc_cleanup_push( pcm_drop, p_sys->p_snd_pcm );
    for(;;)
781 782
        ALSAFill( p_aout );

783 784
    assert(0);
    vlc_cleanup_pop();
785 786 787
}

/*****************************************************************************
788
 * ALSAFill: function used to fill the ALSA buffer as much as possible
789
 *****************************************************************************/
790
static void ALSAFill( aout_instance_t * p_aout )
791
{
792
    struct aout_sys_t * p_sys = p_aout->output.p_sys;
793
    snd_pcm_t *p_pcm = p_sys->p_snd_pcm;
794
    snd_pcm_status_t * p_status;
795
    int i_snd_rc;
796
    mtime_t next_date;
797

798
    int canc = vlc_savecancel();
799
    /* Fill in the buffer until space or audio output buffer shortage */
800 801

    /* Get the status */
802
    snd_pcm_status_alloca(&p_status);
803
    i_snd_rc = snd_pcm_status( p_pcm, p_status );
804
    if( i_snd_rc < 0 )
805
    {
806 807 808
        msg_Err( p_aout, "cannot get device status" );
        goto error;
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
809

810 811 812 813
    /* Handle buffer underruns and get the status again */
    if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
    {
        /* Prepare the device */
814
        i_snd_rc = snd_pcm_prepare( p_pcm );
815
        if( i_snd_rc )
816
        {
817 818 819
            msg_Err( p_aout, "cannot recover from buffer underrun" );
            goto error;
        }
820

821
        msg_Dbg( p_aout, "recovered from buffer underrun" );
822

823
        /* Get the new status */
824
        i_snd_rc = snd_pcm_status( p_pcm, p_status );
825 826 827 828 829
        if( i_snd_rc < 0 )
        {
            msg_Err( p_aout, "cannot get device status after recovery" );
            goto error;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
830

831 832 833 834 835
        /* Underrun, try to recover as quickly as possible */
        next_date = mdate();
    }
    else
    {
Rafaël Carré's avatar
Rafaël Carré committed
836
        /* Here the device should be in RUNNING state, p_status is valid. */
837
        snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status );
Rafaël Carré's avatar
Rafaël Carré committed
838
        if( delay == 0 ) /* workaround buggy alsa drivers */
839
            if( snd_pcm_delay( p_pcm, &delay ) < 0 )
Rafaël Carré's avatar
Rafaël Carré committed
840
                delay = 0; /* FIXME: use a positive minimal delay */
841

842
        size_t i_bytes = snd_pcm_frames_to_bytes( p_pcm, delay );
843
        mtime_t delay_us = CLOCK_FREQ * i_bytes
844 845
                / p_aout->output.output.i_bytes_per_frame
                / p_aout->output.output.i_rate
846
                * p_aout->output.output.i_frame_length;
847 848 849 850 851 852

#ifdef ALSA_DEBUG
        snd_pcm_state_t state = snd_pcm_status_get_state( p_status );
        if( state != SND_PCM_STATE_RUNNING )
            msg_Err( p_aout, "pcm status (%d) != RUNNING", state );

853
        msg_Dbg( p_aout, "Delay is %ld frames (%zu bytes)", delay, i_bytes );
854 855 856 857

        msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame );
        msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate );
        msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length );
858
        msg_Dbg( p_aout, "Next date: in %"PRId64" microseconds", delay_us );
859
#endif
860
        next_date = mdate() + delay_us;
861
    }
862

863
    block_t *p_buffer = aout_OutputNextBuffer( p_aout, next_date,
864
           (p_aout->output.output.i_format ==  VLC_CODEC_SPDIFL) );
865

866 867 868 869 870 871
    /* Audio output buffer shortage -> stop the fill process and wait */
    if( p_buffer == NULL )
        goto error;

    for (;;)
    {
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
        int n = snd_pcm_poll_descriptors_count(p_pcm);
        struct pollfd ufd[n];
        unsigned short revents;

        snd_pcm_poll_descriptors(p_pcm, ufd, n);
        do
        {
            vlc_restorecancel(canc);
            poll(ufd, n, -1);
            canc = vlc_savecancel();
            snd_pcm_poll_descriptors_revents(p_pcm, ufd, n, &revents);
        }
        while(!revents);

        if(revents & POLLOUT)
        {
            i_snd_rc = snd_pcm_writei( p_pcm, p_buffer->p_buffer,
                                       p_buffer->i_nb_samples );
            if( i_snd_rc != -ESTRPIPE )
                break;
        }
893

894 895 896 897
        /* a suspend event occurred
         * (stream is suspended and waiting for an application recovery) */
        msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." );

898
        while( ( i_snd_rc = snd_pcm_resume( p_pcm ) ) == -EAGAIN )
899
        {
900 901 902
            vlc_restorecancel(canc);
            msleep(CLOCK_FREQ); /* device still suspended, wait... */
            canc = vlc_savecancel();
903 904
        }

905
        if( i_snd_rc < 0 )
906 907
            /* Device does not support resuming, restart it */
            i_snd_rc = snd_pcm_prepare( p_pcm );
908

909
    }
910 911 912 913

    if( i_snd_rc < 0 )
        msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) );

914 915
    vlc_restorecancel(canc);
    block_Release( p_buffer );
916 917 918 919 920
    return;

error:
    if( i_snd_rc < 0 )
        msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) );
921 922 923

    vlc_restorecancel(canc);
    msleep(p_sys->i_period_time / 2);
924
}
925 926 927 928 929 930 931 932

/*****************************************************************************
 * config variable callback
 *****************************************************************************/
static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
                               vlc_value_t newval, vlc_value_t oldval, void *p_unused )
{
    module_config_t *p_item;
933 934 935
    (void)newval;
    (void)oldval;
    (void)p_unused;
936 937 938 939 940 941 942

    p_item = config_FindConfig( p_this, psz_name );
    if( !p_item ) return VLC_SUCCESS;

    /* Clear-up the current list */
    if( p_item->i_list )
    {
943 944
        int i;

945 946 947
        /* Keep the first entrie */
        for( i = 1; i < p_item->i_list; i++ )
        {
948 949
            free( (char *)p_item->ppsz_list[i] );
            free( (char *)p_item->ppsz_list_text[i] );
950 951 952 953 954 955 956
        }
        /* TODO: Remove when no more needed */
        p_item->ppsz_list[i] = NULL;
        p_item->ppsz_list_text[i] = NULL;
    }
    p_item->i_list = 1;

957
    GetDevices( p_this, p_item );
958 959

    /* Signal change to the interface */
960
    p_item->b_dirty = true;
961 962 963 964 965

    return VLC_SUCCESS;
}


966 967
static void GetDevicesForCard( vlc_object_t *obj, module_config_t *p_item,
                               int i_card )
968 969 970 971 972 973 974
{
    int i_pcm_device = -1;
    int i_err = 0;
    snd_pcm_info_t *p_pcm_info;
    snd_ctl_t *p_ctl;
    char psz_dev[64];
    char *psz_card_name;
975 976 977 978

    sprintf( psz_dev, "hw:%i", i_card );

    if( ( i_err = snd_ctl_open( &p_ctl, psz_dev, 0 ) ) < 0 )
979
        return;
980 981

    if( ( i_err = snd_card_get_name( i_card, &psz_card_name ) ) != 0)
982 983
        psz_card_name = _("Unknown soundcard");

984
    snd_pcm_info_alloca( &p_pcm_info );
985 986 987 988

    for (;;)
    {
        char *psz_device, *psz_descr;
989
        if( ( i_err = snd_ctl_pcm_next_device( p_ctl, &i_pcm_device ) ) < 0 )
990
            i_pcm_device = -1;
991
        if( i_pcm_device < 0 )
992 993
            break;

994 995 996
        snd_pcm_info_set_device( p_pcm_info, i_pcm_device );
        snd_pcm_info_set_subdevice( p_pcm_info, 0 );
        snd_pcm_info_set_stream( p_pcm_info, SND_PCM_STREAM_PLAYBACK );
997

998
        if( ( i_err = snd_ctl_pcm_info( p_ctl, p_pcm_info ) ) < 0 )
999
        {
1000
            if( i_err != -ENOENT )
1001 1002
                msg_Err( obj, "cannot get PCM device %d:%d infos: %s", i_card,
                         i_pcm_device, snd_strerror( -i_err ) );
1003 1004 1005
            continue;
        }

1006 1007 1008 1009 1010 1011 1012 1013
        if( asprintf( &psz_device, "hw:%d,%d", i_card, i_pcm_device ) == -1 )
            break;
        if( asprintf( &psz_descr, "%s: %s (%s)", psz_card_name,
                  snd_pcm_info_get_name(p_pcm_info), psz_device ) == -1 )
        {
            free( psz_device );
            break;
        }
1014

1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
        msg_Dbg( obj, "  %s", psz_descr );

        if( p_item )
        {
            p_item->ppsz_list = xrealloc( p_item->ppsz_list,
                                  (p_item->i_list + 2) * sizeof(char *) );
            p_item->ppsz_list_text = xrealloc( p_item->ppsz_list_text,
                                  (p_item->i_list + 2) * sizeof(char *) );
            p_item->ppsz_list[ p_item->i_list ] = psz_device;
            p_item->ppsz_list_text[ p_item->i_list ] = psz_descr;
            p_item->i_list++;
            p_item->ppsz_list[ p_item->i_list ] = NULL;
            p_item->ppsz_list_text[ p_item->i_list ] = NULL;
        }
        else
        {
            free( psz_device );
            free( psz_descr );
        }
1034 1035 1036 1037 1038 1039
    }

    snd_ctl_close( p_ctl );
}


1040
static void GetDevices( vlc_object_t *obj, module_config_t *p_item )
1041 1042
{
    int i_card = -1;
1043
    int i_err;
1044

1045
    msg_Dbg( obj, "Available alsa output devices:" );
1046 1047
    while( (i_err = snd_card_next( &i_card )) == 0 && i_card > -1 )
        GetDevicesForCard( obj, p_item, i_card );
1048

1049 1050
    if( i_err )
        msg_Err( obj, "cannot enumerate cards: %s", snd_strerror( -i_err ) );
1051
}