1 /*****************************************************************************
2 * alsa.c : alsa plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2000-2001 the VideoLAN team
7 * Authors: Henri Fallon <henri@videolan.org> - Original Author
8 * Jeffrey Baker <jwbaker@acm.org> - Port to ALSA 1.0 API
9 * John Paul Lorenti <jpl31@columbia.edu> - Device selection
10 * Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr> - S/PDIF and aout3
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
39 #include <errno.h> /* ENOMEM */
40 #include <vlc_dialog.h>
46 Note: we use the new API which is available since 0.9.0beta10a. */
47 #define ALSA_PCM_NEW_HW_PARAMS_API
48 #define ALSA_PCM_NEW_SW_PARAMS_API
49 #include <alsa/asoundlib.h>
50 #include <alsa/version.h>
52 /*#define ALSA_DEBUG*/
54 /*****************************************************************************
55 * aout_sys_t: ALSA audio output method descriptor
56 *****************************************************************************
57 * This structure is part of the audio output thread descriptor.
58 * It describes the ALSA specific properties of an audio device.
59 *****************************************************************************/
62 snd_pcm_t * p_snd_pcm;
63 unsigned int i_period_time;
66 snd_output_t * p_snd_stderr;
74 #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.
80 #define ALSA_DEFAULT_PERIOD_SIZE 1024
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 */
88 #define DEFAULT_ALSA_DEVICE N_("default")
90 /*****************************************************************************
92 *****************************************************************************/
93 static int Open ( vlc_object_t * );
94 static void Close ( vlc_object_t * );
95 static void Play ( aout_instance_t * );
96 static void* ALSAThread ( void * );
97 static void ALSAFill ( aout_instance_t * );
98 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
99 vlc_value_t newval, vlc_value_t oldval, void *p_unused );
100 static void GetDevicesForCard( vlc_object_t *, module_config_t *, int card );
101 static void GetDevices( vlc_object_t *, module_config_t * );
103 /*****************************************************************************
105 *****************************************************************************/
106 static const char *const ppsz_devices[] = { "default" };
107 static const char *const ppsz_devices_text[] = { N_("Default") };
109 set_shortname( "ALSA" )
110 set_description( N_("ALSA audio output") )
111 set_category( CAT_AUDIO )
112 set_subcategory( SUBCAT_AUDIO_AOUT )
113 add_string( "alsa-audio-device", DEFAULT_ALSA_DEVICE, aout_FindAndRestart,
114 N_("ALSA Device Name"), NULL, false )
115 add_deprecated_alias( "alsadev" ) /* deprecated since 0.9.3 */
116 change_string_list( ppsz_devices, ppsz_devices_text, FindDevicesCallback )
117 change_action_add( FindDevicesCallback, N_("Refresh list") )
119 set_capability( "audio output", 150 )
120 set_callbacks( Open, Close )
123 /*****************************************************************************
124 * Probe: probe the audio device for available formats and channels
125 *****************************************************************************/
126 static void Probe( aout_instance_t * p_aout,
127 const char * psz_device, const char * psz_iec_device,
128 int *pi_snd_pcm_format )
130 struct aout_sys_t * p_sys = p_aout->output.p_sys;
131 vlc_value_t val, text;
134 var_Create ( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
135 text.psz_string = _("Audio Device");
136 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
138 /* We'll open the audio device in non blocking mode so we can just exit
139 * when it is already in use, but for the real stuff we'll still use
140 * the blocking mode */
142 /* Now test linear PCM capabilities */
143 if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
144 SND_PCM_STREAM_PLAYBACK,
145 SND_PCM_NONBLOCK ) ) )
148 snd_pcm_hw_params_t * p_hw;
149 snd_pcm_hw_params_alloca (&p_hw);
151 if ( snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) < 0 )
153 msg_Warn( p_aout, "unable to retrieve initial hardware parameters"
154 ", disabling linear PCM audio" );
155 snd_pcm_close( p_sys->p_snd_pcm );
156 var_Destroy( p_aout, "audio-device" );
160 if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
161 *pi_snd_pcm_format ) < 0 )
165 if( *pi_snd_pcm_format != SND_PCM_FORMAT_S16 )
167 *pi_snd_pcm_format = SND_PCM_FORMAT_S16;
168 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
169 p_hw, *pi_snd_pcm_format );
173 msg_Warn( p_aout, "unable to set stream sample size and "
174 "word order, disabling linear PCM audio" );
175 snd_pcm_close( p_sys->p_snd_pcm );
176 var_Destroy( p_aout, "audio-device" );
181 i_channels = aout_FormatNbChannels( &p_aout->output.output );
183 while ( i_channels > 0 )
185 if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw,
188 switch ( i_channels )
191 val.i_int = AOUT_VAR_MONO;
192 text.psz_string = _("Mono");
193 var_Change( p_aout, "audio-device",
194 VLC_VAR_ADDCHOICE, &val, &text );
197 val.i_int = AOUT_VAR_STEREO;
198 text.psz_string = _("Stereo");
199 var_Change( p_aout, "audio-device",
200 VLC_VAR_ADDCHOICE, &val, &text );
201 var_Set( p_aout, "audio-device", val );
204 val.i_int = AOUT_VAR_2F2R;
205 text.psz_string = _("2 Front 2 Rear");
206 var_Change( p_aout, "audio-device",
207 VLC_VAR_ADDCHOICE, &val, &text );
210 val.i_int = AOUT_VAR_5_1;
211 text.psz_string = "5.1";
212 var_Change( p_aout, "audio-device",
213 VLC_VAR_ADDCHOICE, &val, &text );
221 /* Special case for mono on stereo only boards */
222 i_channels = aout_FormatNbChannels( &p_aout->output.output );
223 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
224 if( val.i_int <= 0 && i_channels == 1 )
226 if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw, 2 ))
228 val.i_int = AOUT_VAR_STEREO;
229 text.psz_string = (char*)N_("Stereo");
230 var_Change( p_aout, "audio-device",
231 VLC_VAR_ADDCHOICE, &val, &text );
232 var_Set( p_aout, "audio-device", val );
236 /* Close the previously opened device */
237 snd_pcm_close( p_sys->p_snd_pcm );
239 else if ( i_ret == -EBUSY )
241 msg_Warn( p_aout, "audio device: %s is already in use", psz_device );
244 /* Test for S/PDIF device if needed */
245 if ( psz_iec_device )
247 /* Opening the device should be enough */
248 if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
249 SND_PCM_STREAM_PLAYBACK,
250 SND_PCM_NONBLOCK ) ) )
252 val.i_int = AOUT_VAR_SPDIF;
253 text.psz_string = (char*)N_("A/52 over S/PDIF");
254 var_Change( p_aout, "audio-device",
255 VLC_VAR_ADDCHOICE, &val, &text );
256 if( var_InheritBool( p_aout, "spdif" ) )
257 var_Set( p_aout, "audio-device", val );
259 snd_pcm_close( p_sys->p_snd_pcm );
261 else if ( i_ret == -EBUSY )
263 msg_Warn( p_aout, "audio device: %s is already in use",
268 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
269 #if (SND_LIB_VERSION <= 0x010015)
270 # warning Please update alsa-lib to version > 1.0.21a.
271 var_Create( p_aout->p_libvlc, "alsa-working", VLC_VAR_BOOL );
274 if( var_GetBool( p_aout->p_libvlc, "alsa-working" ) )
275 dialog_Fatal( p_aout, "ALSA version problem",
276 "VLC failed to re-initialize your sound output device.\n"
277 "Please update alsa-lib to version 1.0.22 or higher "
278 "to fix this issue." );
281 var_SetBool( p_aout->p_libvlc, "alsa-working", true );
285 /* Probe() has failed. */
286 #if (SND_LIB_VERSION <= 0x010017)
287 # warning Please update alsa-lib to version > 1.0.23.
288 var_Create( p_aout->p_libvlc, "alsa-broken", VLC_VAR_BOOL );
289 if( !var_GetBool( p_aout->p_libvlc, "alsa-broken" ) )
291 var_SetBool( p_aout->p_libvlc, "alsa-broken", true );
292 dialog_Fatal( p_aout, "Potential ALSA version problem",
293 "VLC failed to initialize your sound output device (if any).\n"
294 "Please update alsa-lib to version 1.0.24 or higher "
295 "to try to fix this issue." );
298 msg_Dbg( p_aout, "failed to find a usable ALSA configuration" );
299 var_Destroy( p_aout, "audio-device" );
300 GetDevices( VLC_OBJECT(p_aout), NULL );
304 /* Add final settings to the variable */
305 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
306 var_SetBool( p_aout, "intf-change", true );
309 /*****************************************************************************
310 * Open: create a handle and open an alsa device
311 *****************************************************************************
312 * This function opens an alsa device, through the alsa API.
314 * Note: the only heap-allocated string is psz_device. All the other pointers
315 * are references to psz_device or to stack-allocated data.
316 *****************************************************************************/
317 static int Open( vlc_object_t *p_this )
319 aout_instance_t * p_aout = (aout_instance_t *)p_this;
320 struct aout_sys_t * p_sys;
323 char psz_default_iec_device[128]; /* Buffer used to store the default
325 char * psz_device, * psz_iec_device; /* device names for PCM and S/PDIF
328 int i_vlc_pcm_format; /* Audio format for VLC's data */
329 int i_snd_pcm_format; /* Audio format for ALSA's data */
331 snd_pcm_uframes_t i_buffer_size = 0;
332 snd_pcm_uframes_t i_period_size = 0;
335 snd_pcm_hw_params_t *p_hw;
336 snd_pcm_sw_params_t *p_sw;
339 unsigned int i_old_rate;
342 /* Allocate structures */
343 p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
347 /* Get device name */
348 if( (psz_device = var_InheritString( p_aout, "alsa-audio-device" )) == NULL )
350 msg_Err( p_aout, "no audio device given (maybe \"default\" ?)" );
351 dialog_Fatal( p_aout, _("No Audio Device"), "%s",
352 _("No audio device name was given. You might want to " \
353 "enter \"default\".") );
358 /* Choose the IEC device for S/PDIF output:
359 if the device is overriden by the user then it will be the one
360 otherwise we compute the default device based on the output format. */
361 if( AOUT_FMT_NON_LINEAR( &p_aout->output.output )
362 && !strcmp( psz_device, DEFAULT_ALSA_DEVICE ) )
364 snprintf( psz_default_iec_device, sizeof(psz_default_iec_device),
365 "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
366 IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
367 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
369 ( p_aout->output.output.i_rate == 48000 ?
370 IEC958_AES3_CON_FS_48000 :
371 ( p_aout->output.output.i_rate == 44100 ?
372 IEC958_AES3_CON_FS_44100 : IEC958_AES3_CON_FS_32000 ) ) );
373 psz_iec_device = psz_default_iec_device;
375 else if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
377 psz_iec_device = psz_device;
381 psz_iec_device = NULL;
384 /* Choose the linear PCM format (read the comment above about FPU
388 i_vlc_pcm_format = VLC_CODEC_FL32;
389 i_snd_pcm_format = SND_PCM_FORMAT_FLOAT;
393 i_vlc_pcm_format = VLC_CODEC_S16N;
394 i_snd_pcm_format = SND_PCM_FORMAT_S16;
397 /* If the variable doesn't exist then it's the first time we're called
398 and we have to probe the available audio formats and channels */
399 if ( var_Type( p_aout, "audio-device" ) == 0 )
401 Probe( p_aout, psz_device, psz_iec_device, &i_snd_pcm_format );
404 if ( var_Get( p_aout, "audio-device", &val ) < 0 )
411 p_aout->output.output.i_format = i_vlc_pcm_format;
412 if ( val.i_int == AOUT_VAR_5_1 )
414 p_aout->output.output.i_physical_channels
415 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
416 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
419 psz_device = strdup( "surround51" );
421 else if ( val.i_int == AOUT_VAR_2F2R )
423 p_aout->output.output.i_physical_channels
424 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
425 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
427 psz_device = strdup( "surround40" );
429 else if ( val.i_int == AOUT_VAR_STEREO )
431 p_aout->output.output.i_physical_channels
432 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
434 else if ( val.i_int == AOUT_VAR_MONO )
436 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
438 else if( val.i_int != AOUT_VAR_SPDIF )
440 /* This should not happen ! */
441 msg_Err( p_aout, "internal: can't find audio-device (%"PRId64")",
449 snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 );
452 /* Open the device */
453 if ( val.i_int == AOUT_VAR_SPDIF )
455 if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
456 SND_PCM_STREAM_PLAYBACK, 0 ) ) < 0 )
458 msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
459 psz_iec_device, snd_strerror( i_snd_rc ) );
460 dialog_Fatal( p_aout, _("Audio output failed"),
461 _("VLC could not open the ALSA device \"%s\" (%s)."),
462 psz_iec_device, snd_strerror( i_snd_rc ) );
467 i_buffer_size = ALSA_SPDIF_BUFFER_SIZE;
468 i_snd_pcm_format = SND_PCM_FORMAT_S16;
471 i_vlc_pcm_format = VLC_CODEC_SPDIFL;
472 p_aout->output.i_nb_samples = i_period_size = ALSA_SPDIF_PERIOD_SIZE;
473 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
474 p_aout->output.output.i_frame_length = A52_FRAME_NB;
476 aout_VolumeNoneInit( p_aout );
482 msg_Dbg( p_aout, "opening ALSA device `%s'", psz_device );
484 /* Since it seems snd_pcm_close hasn't really released the device at
485 the time it returns, probe if the device is available in loop for 1s.
486 We cannot use blocking mode since the we would wait indefinitely when
487 switching from a dmx device to surround51. */
489 for( i = 10; i >= 0; i-- )
491 if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
492 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) ) == -EBUSY )
494 if( i ) msleep( 100000 /* 100ms */ );
497 msg_Err( p_aout, "audio device: %s is already in use",
499 dialog_Fatal( p_aout, _("Audio output failed"),
500 _("The audio device \"%s\" is already in use."),
509 msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
510 psz_device, snd_strerror( i_snd_rc ) );
511 dialog_Fatal( p_aout, _("Audio output failed"),
512 _("VLC could not open the ALSA device \"%s\" (%s)."),
513 psz_device, snd_strerror( i_snd_rc ) );
519 /* We want blocking mode */
520 snd_pcm_nonblock( p_sys->p_snd_pcm, 0 );
522 i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE;
523 i_channels = aout_FormatNbChannels( &p_aout->output.output );
525 p_aout->output.i_nb_samples = i_period_size = ALSA_DEFAULT_PERIOD_SIZE;
527 aout_VolumeSoftInit( p_aout );
530 /* Free psz_device so that all the remaining data is stack-allocated */
533 p_aout->output.pf_play = Play;
535 snd_pcm_hw_params_alloca(&p_hw);
536 snd_pcm_sw_params_alloca(&p_sw);
538 /* Due to some bugs in alsa with some drivers, we need to retry in s16l
539 if snd_pcm_hw_params fails in fl32 */
544 /* Get Initial hardware parameters */
545 if ( ( i_snd_rc = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) ) < 0 )
547 msg_Err( p_aout, "unable to retrieve initial hardware parameters (%s)",
548 snd_strerror( i_snd_rc ) );
553 if ( ( i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
554 i_snd_pcm_format ) ) < 0 )
556 if( i_snd_pcm_format != SND_PCM_FORMAT_S16 )
558 i_snd_pcm_format = SND_PCM_FORMAT_S16;
559 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
560 p_hw, i_snd_pcm_format );
564 msg_Err( p_aout, "unable to set stream sample size and "
565 "word order (%s)", snd_strerror( i_snd_rc ) );
569 if( i_vlc_pcm_format != VLC_CODEC_SPDIFL )
570 switch( i_snd_pcm_format )
572 case SND_PCM_FORMAT_FLOAT:
573 i_vlc_pcm_format = VLC_CODEC_FL32;
575 case SND_PCM_FORMAT_S16:
576 i_vlc_pcm_format = VLC_CODEC_S16N;
579 p_aout->output.output.i_format = i_vlc_pcm_format;
581 if ( ( i_snd_rc = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw,
582 SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
584 msg_Err( p_aout, "unable to set interleaved stream format (%s)",
585 snd_strerror( i_snd_rc ) );
590 if ( ( i_snd_rc = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw,
593 msg_Err( p_aout, "unable to set number of output channels (%s)",
594 snd_strerror( i_snd_rc ) );
599 i_old_rate = p_aout->output.output.i_rate;
600 i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
601 &p_aout->output.output.i_rate,
603 if( i_snd_rc < 0 || p_aout->output.output.i_rate != i_old_rate )
605 msg_Warn( p_aout, "The rate %d Hz is not supported by your " \
606 "hardware. Using %d Hz instead.\n", i_old_rate, \
607 p_aout->output.output.i_rate );
610 /* Set period size. */
611 if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
612 p_hw, &i_period_size, NULL ) ) < 0 )
614 msg_Err( p_aout, "unable to set period size (%s)",
615 snd_strerror( i_snd_rc ) );
618 p_aout->output.i_nb_samples = i_period_size;
620 /* Set buffer size. */
621 if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
622 p_hw, &i_buffer_size ) ) < 0 )
624 msg_Err( p_aout, "unable to set buffer size (%s)",
625 snd_strerror( i_snd_rc ) );
629 /* Commit hardware parameters. */
630 if ( ( i_snd_rc = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw ) ) < 0 )
632 if ( b_retry == false &&
633 i_snd_pcm_format == SND_PCM_FORMAT_FLOAT)
636 i_snd_pcm_format = SND_PCM_FORMAT_S16;
637 p_aout->output.output.i_format = VLC_CODEC_S16N;
638 msg_Warn( p_aout, "unable to commit hardware configuration "
639 "with fl32 samples. Retrying with s16l (%s)", snd_strerror( i_snd_rc ) );
643 msg_Err( p_aout, "unable to commit hardware configuration (%s)",
644 snd_strerror( i_snd_rc ) );
650 if( ( i_snd_rc = snd_pcm_hw_params_get_period_time( p_hw,
651 &p_sys->i_period_time, NULL ) ) < 0 )
653 msg_Err( p_aout, "unable to get period time (%s)",
654 snd_strerror( i_snd_rc ) );
658 /* Get Initial software parameters */
659 snd_pcm_sw_params_current( p_sys->p_snd_pcm, p_sw );
661 i_snd_rc = snd_pcm_sw_params_set_sleep_min( p_sys->p_snd_pcm, p_sw, 0 );
663 i_snd_rc = snd_pcm_sw_params_set_avail_min( p_sys->p_snd_pcm, p_sw,
664 p_aout->output.i_nb_samples );
665 /* start playing when one period has been written */
666 i_snd_rc = snd_pcm_sw_params_set_start_threshold( p_sys->p_snd_pcm, p_sw,
667 ALSA_DEFAULT_PERIOD_SIZE);
670 msg_Err( p_aout, "unable to set start threshold (%s)",
671 snd_strerror( i_snd_rc ) );
675 /* Commit software parameters. */
676 if ( snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw ) < 0 )
678 msg_Err( p_aout, "unable to set software configuration" );
683 snd_output_printf( p_sys->p_snd_stderr, "\nALSA hardware setup:\n\n" );
684 snd_pcm_dump_hw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
685 snd_output_printf( p_sys->p_snd_stderr, "\nALSA software setup:\n\n" );
686 snd_pcm_dump_sw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
687 snd_output_printf( p_sys->p_snd_stderr, "\n" );
690 p_sys->start_date = 0;
691 vlc_sem_init( &p_sys->wait, 0 );
693 /* Create ALSA thread and wait for its readiness. */
694 if( vlc_clone( &p_sys->thread, ALSAThread, p_aout,
695 VLC_THREAD_PRIORITY_OUTPUT ) )
697 msg_Err( p_aout, "cannot create ALSA thread (%m)" );
698 vlc_sem_destroy( &p_sys->wait );
705 snd_pcm_close( p_sys->p_snd_pcm );
707 snd_output_close( p_sys->p_snd_stderr );
713 static void PlayIgnore( aout_instance_t *p_aout )
714 { /* Already playing - nothing to do */
718 /*****************************************************************************
719 * Play: start playback
720 *****************************************************************************/
721 static void Play( aout_instance_t *p_aout )
723 p_aout->output.pf_play = PlayIgnore;
725 /* get the playing date of the first aout buffer */
726 p_aout->output.p_sys->start_date =
727 aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
729 /* wake up the audio output thread */
730 sem_post( &p_aout->output.p_sys->wait );
733 /*****************************************************************************
734 * Close: close the ALSA device
735 *****************************************************************************/
736 static void Close( vlc_object_t *p_this )
738 aout_instance_t *p_aout = (aout_instance_t *)p_this;
739 struct aout_sys_t * p_sys = p_aout->output.p_sys;
742 /* Make sure that the thread will stop once it is waken up */
743 vlc_cancel( p_sys->thread );
744 vlc_join( p_sys->thread, NULL );
745 vlc_sem_destroy( &p_sys->wait );
748 i_snd_rc = snd_pcm_close( p_sys->p_snd_pcm );
752 msg_Err( p_aout, "failed closing ALSA device (%s)",
753 snd_strerror( i_snd_rc ) );
757 snd_output_close( p_sys->p_snd_stderr );
763 static void pcm_drop(void *pcm)
768 /*****************************************************************************
769 * ALSAThread: asynchronous thread used to DMA the data to the device
770 *****************************************************************************/
771 static void* ALSAThread( void *data )
773 aout_instance_t * p_aout = data;
774 struct aout_sys_t * p_sys = p_aout->output.p_sys;
776 /* Wait for the exact time to start playing (avoids resampling) */
777 vlc_sem_wait( &p_sys->wait );
778 mwait( p_sys->start_date - AOUT_PTS_TOLERANCE / 4 );
780 vlc_cleanup_push( pcm_drop, p_sys->p_snd_pcm );
788 /*****************************************************************************
789 * ALSAFill: function used to fill the ALSA buffer as much as possible
790 *****************************************************************************/
791 static void ALSAFill( aout_instance_t * p_aout )
793 struct aout_sys_t * p_sys = p_aout->output.p_sys;
794 snd_pcm_t *p_pcm = p_sys->p_snd_pcm;
795 snd_pcm_status_t * p_status;
799 int canc = vlc_savecancel();
800 /* Fill in the buffer until space or audio output buffer shortage */
803 snd_pcm_status_alloca(&p_status);
804 i_snd_rc = snd_pcm_status( p_pcm, p_status );
807 msg_Err( p_aout, "cannot get device status" );
811 /* Handle buffer underruns and get the status again */
812 if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
814 /* Prepare the device */
815 i_snd_rc = snd_pcm_prepare( p_pcm );
818 msg_Err( p_aout, "cannot recover from buffer underrun" );
822 msg_Dbg( p_aout, "recovered from buffer underrun" );
824 /* Get the new status */
825 i_snd_rc = snd_pcm_status( p_pcm, p_status );
828 msg_Err( p_aout, "cannot get device status after recovery" );
832 /* Underrun, try to recover as quickly as possible */
837 /* Here the device should be in RUNNING state, p_status is valid. */
838 snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status );
839 if( delay == 0 ) /* workaround buggy alsa drivers */
840 if( snd_pcm_delay( p_pcm, &delay ) < 0 )
841 delay = 0; /* FIXME: use a positive minimal delay */
843 size_t i_bytes = snd_pcm_frames_to_bytes( p_pcm, delay );
844 mtime_t delay_us = CLOCK_FREQ * i_bytes
845 / p_aout->output.output.i_bytes_per_frame
846 / p_aout->output.output.i_rate
847 * p_aout->output.output.i_frame_length;
850 snd_pcm_state_t state = snd_pcm_status_get_state( p_status );
851 if( state != SND_PCM_STATE_RUNNING )
852 msg_Err( p_aout, "pcm status (%d) != RUNNING", state );
854 msg_Dbg( p_aout, "Delay is %ld frames (%zu bytes)", delay, i_bytes );
856 msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame );
857 msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate );
858 msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length );
859 msg_Dbg( p_aout, "Next date: in %"PRId64" microseconds", delay_us );
861 next_date = mdate() + delay_us;
864 block_t *p_buffer = aout_OutputNextBuffer( p_aout, next_date,
865 (p_aout->output.output.i_format == VLC_CODEC_SPDIFL) );
867 /* Audio output buffer shortage -> stop the fill process and wait */
868 if( p_buffer == NULL )
871 block_cleanup_push( p_buffer );
874 int n = snd_pcm_poll_descriptors_count(p_pcm);
875 struct pollfd ufd[n];
876 unsigned short revents;
878 snd_pcm_poll_descriptors(p_pcm, ufd, n);
881 vlc_restorecancel(canc);
883 canc = vlc_savecancel();
884 snd_pcm_poll_descriptors_revents(p_pcm, ufd, n, &revents);
888 if(revents & POLLOUT)
890 i_snd_rc = snd_pcm_writei( p_pcm, p_buffer->p_buffer,
891 p_buffer->i_nb_samples );
892 if( i_snd_rc != -ESTRPIPE )
896 /* a suspend event occurred
897 * (stream is suspended and waiting for an application recovery) */
898 msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." );
900 while( ( i_snd_rc = snd_pcm_resume( p_pcm ) ) == -EAGAIN )
902 vlc_restorecancel(canc);
903 msleep(CLOCK_FREQ); /* device still suspended, wait... */
904 canc = vlc_savecancel();
908 /* Device does not support resuming, restart it */
909 i_snd_rc = snd_pcm_prepare( p_pcm );
914 msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) );
916 vlc_restorecancel(canc);
922 msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) );
924 vlc_restorecancel(canc);
925 msleep(p_sys->i_period_time / 2);
928 /*****************************************************************************
929 * config variable callback
930 *****************************************************************************/
931 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
932 vlc_value_t newval, vlc_value_t oldval, void *p_unused )
934 module_config_t *p_item;
939 p_item = config_FindConfig( p_this, psz_name );
940 if( !p_item ) return VLC_SUCCESS;
942 /* Clear-up the current list */
947 /* Keep the first entrie */
948 for( i = 1; i < p_item->i_list; i++ )
950 free( (char *)p_item->ppsz_list[i] );
951 free( (char *)p_item->ppsz_list_text[i] );
953 /* TODO: Remove when no more needed */
954 p_item->ppsz_list[i] = NULL;
955 p_item->ppsz_list_text[i] = NULL;
959 GetDevices( p_this, p_item );
961 /* Signal change to the interface */
962 p_item->b_dirty = true;
968 static void GetDevicesForCard( vlc_object_t *obj, module_config_t *p_item,
971 int i_pcm_device = -1;
973 snd_pcm_info_t *p_pcm_info;
978 sprintf( psz_dev, "hw:%i", i_card );
980 if( ( i_err = snd_ctl_open( &p_ctl, psz_dev, 0 ) ) < 0 )
983 if( ( i_err = snd_card_get_name( i_card, &psz_card_name ) ) != 0)
984 psz_card_name = _("Unknown soundcard");
986 snd_pcm_info_alloca( &p_pcm_info );
990 char *psz_device, *psz_descr;
991 if( ( i_err = snd_ctl_pcm_next_device( p_ctl, &i_pcm_device ) ) < 0 )
993 if( i_pcm_device < 0 )
996 snd_pcm_info_set_device( p_pcm_info, i_pcm_device );
997 snd_pcm_info_set_subdevice( p_pcm_info, 0 );
998 snd_pcm_info_set_stream( p_pcm_info, SND_PCM_STREAM_PLAYBACK );
1000 if( ( i_err = snd_ctl_pcm_info( p_ctl, p_pcm_info ) ) < 0 )
1002 if( i_err != -ENOENT )
1003 msg_Err( obj, "cannot get PCM device %d:%d infos: %s", i_card,
1004 i_pcm_device, snd_strerror( -i_err ) );
1008 if( asprintf( &psz_device, "hw:%d,%d", i_card, i_pcm_device ) == -1 )
1010 if( asprintf( &psz_descr, "%s: %s (%s)", psz_card_name,
1011 snd_pcm_info_get_name(p_pcm_info), psz_device ) == -1 )
1017 msg_Dbg( obj, " %s", psz_descr );
1021 p_item->ppsz_list = xrealloc( p_item->ppsz_list,
1022 (p_item->i_list + 2) * sizeof(char *) );
1023 p_item->ppsz_list_text = xrealloc( p_item->ppsz_list_text,
1024 (p_item->i_list + 2) * sizeof(char *) );
1025 p_item->ppsz_list[ p_item->i_list ] = psz_device;
1026 p_item->ppsz_list_text[ p_item->i_list ] = psz_descr;
1028 p_item->ppsz_list[ p_item->i_list ] = NULL;
1029 p_item->ppsz_list_text[ p_item->i_list ] = NULL;
1038 snd_ctl_close( p_ctl );
1042 static void GetDevices( vlc_object_t *obj, module_config_t *p_item )
1047 msg_Dbg( obj, "Available alsa output devices:" );
1048 while( (i_err = snd_card_next( &i_card )) == 0 && i_card > -1 )
1049 GetDevicesForCard( obj, p_item, i_card );
1052 msg_Err( obj, "cannot enumerate cards: %s", snd_strerror( -i_err ) );