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 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <errno.h> /* ENOMEM */
38 #include <vlc_interface.h>
43 Note: we use the new API which is available since 0.9.0beta10a. */
44 #define ALSA_PCM_NEW_HW_PARAMS_API
45 #define ALSA_PCM_NEW_SW_PARAMS_API
46 #include <alsa/asoundlib.h>
48 /*#define ALSA_DEBUG*/
50 /*****************************************************************************
51 * aout_sys_t: ALSA audio output method descriptor
52 *****************************************************************************
53 * This structure is part of the audio output thread descriptor.
54 * It describes the ALSA specific properties of an audio device.
55 *****************************************************************************/
58 snd_pcm_t * p_snd_pcm;
59 unsigned int i_period_time;
62 snd_output_t * p_snd_stderr;
65 int b_playing; /* playing status */
71 snd_pcm_status_t *p_status;
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 ( vlc_object_t * );
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 );
101 /*****************************************************************************
103 *****************************************************************************/
104 static const char *const ppsz_devices[] = { "default" };
105 static const char *const ppsz_devices_text[] = { N_("Default") };
107 set_shortname( "ALSA" );
108 set_description( N_("ALSA audio output") );
109 set_category( CAT_AUDIO );
110 set_subcategory( SUBCAT_AUDIO_AOUT );
111 add_string( "alsadev", DEFAULT_ALSA_DEVICE, aout_FindAndRestart,
112 N_("ALSA Device Name"), NULL, false );
113 change_string_list( ppsz_devices, ppsz_devices_text, FindDevicesCallback );
114 change_action_add( FindDevicesCallback, N_("Refresh list") );
116 set_capability( "audio output", 150 );
117 set_callbacks( Open, Close );
120 /*****************************************************************************
121 * Probe: probe the audio device for available formats and channels
122 *****************************************************************************/
123 static void Probe( aout_instance_t * p_aout,
124 const char * psz_device, const char * psz_iec_device,
125 int *pi_snd_pcm_format )
127 struct aout_sys_t * p_sys = p_aout->output.p_sys;
128 vlc_value_t val, text;
131 var_Create ( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
132 text.psz_string = _("Audio Device");
133 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
135 /* We'll open the audio device in non blocking mode so we can just exit
136 * when it is already in use, but for the real stuff we'll still use
137 * the blocking mode */
139 /* Now test linear PCM capabilities */
140 if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
141 SND_PCM_STREAM_PLAYBACK,
142 SND_PCM_NONBLOCK ) ) )
145 snd_pcm_hw_params_t * p_hw;
146 snd_pcm_hw_params_alloca (&p_hw);
148 if ( snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) < 0 )
150 msg_Warn( p_aout, "unable to retrieve initial hardware parameters"
151 ", disabling linear PCM audio" );
152 snd_pcm_close( p_sys->p_snd_pcm );
153 var_Destroy( p_aout, "audio-device" );
157 if ( snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
158 *pi_snd_pcm_format ) < 0 )
162 if( *pi_snd_pcm_format != SND_PCM_FORMAT_S16 )
164 *pi_snd_pcm_format = SND_PCM_FORMAT_S16;
165 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
166 p_hw, *pi_snd_pcm_format );
170 msg_Warn( p_aout, "unable to set stream sample size and "
171 "word order, disabling linear PCM audio" );
172 snd_pcm_close( p_sys->p_snd_pcm );
173 var_Destroy( p_aout, "audio-device" );
178 i_channels = aout_FormatNbChannels( &p_aout->output.output );
180 while ( i_channels > 0 )
182 if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw,
185 switch ( i_channels )
188 val.i_int = AOUT_VAR_MONO;
189 text.psz_string = (char*)N_("Mono");
190 var_Change( p_aout, "audio-device",
191 VLC_VAR_ADDCHOICE, &val, &text );
194 val.i_int = AOUT_VAR_STEREO;
195 text.psz_string = (char*)N_("Stereo");
196 var_Change( p_aout, "audio-device",
197 VLC_VAR_ADDCHOICE, &val, &text );
198 var_Set( p_aout, "audio-device", val );
201 val.i_int = AOUT_VAR_2F2R;
202 text.psz_string = (char*)N_("2 Front 2 Rear");
203 var_Change( p_aout, "audio-device",
204 VLC_VAR_ADDCHOICE, &val, &text );
207 val.i_int = AOUT_VAR_5_1;
208 text.psz_string = (char*)"5.1";
209 var_Change( p_aout, "audio-device",
210 VLC_VAR_ADDCHOICE, &val, &text );
218 /* Special case for mono on stereo only boards */
219 i_channels = aout_FormatNbChannels( &p_aout->output.output );
220 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
221 if( val.i_int <= 0 && i_channels == 1 )
223 if ( !snd_pcm_hw_params_test_channels( p_sys->p_snd_pcm, p_hw, 2 ))
225 val.i_int = AOUT_VAR_STEREO;
226 text.psz_string = (char*)N_("Stereo");
227 var_Change( p_aout, "audio-device",
228 VLC_VAR_ADDCHOICE, &val, &text );
229 var_Set( p_aout, "audio-device", val );
233 /* Close the previously opened device */
234 snd_pcm_close( p_sys->p_snd_pcm );
236 else if ( i_ret == -EBUSY )
238 msg_Warn( p_aout, "audio device: %s is already in use", psz_device );
241 /* Test for S/PDIF device if needed */
242 if ( psz_iec_device )
244 /* Opening the device should be enough */
245 if ( !(i_ret = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
246 SND_PCM_STREAM_PLAYBACK,
247 SND_PCM_NONBLOCK ) ) )
249 val.i_int = AOUT_VAR_SPDIF;
250 text.psz_string = (char*)N_("A/52 over S/PDIF");
251 var_Change( p_aout, "audio-device",
252 VLC_VAR_ADDCHOICE, &val, &text );
253 if( config_GetInt( p_aout, "spdif" ) )
254 var_Set( p_aout, "audio-device", val );
256 snd_pcm_close( p_sys->p_snd_pcm );
258 else if ( i_ret == -EBUSY )
260 msg_Warn( p_aout, "audio device: %s is already in use",
265 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
268 /* Probe() has failed. */
269 msg_Dbg( p_aout, "failed to find a useable alsa configuration" );
270 var_Destroy( p_aout, "audio-device" );
274 /* Add final settings to the variable */
275 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
277 var_Set( p_aout, "intf-change", val );
280 /*****************************************************************************
281 * Open: create a handle and open an alsa device
282 *****************************************************************************
283 * This function opens an alsa device, through the alsa API.
285 * Note: the only heap-allocated string is psz_device. All the other pointers
286 * are references to psz_device or to stack-allocated data.
287 *****************************************************************************/
288 static int Open( vlc_object_t *p_this )
290 aout_instance_t * p_aout = (aout_instance_t *)p_this;
291 struct aout_sys_t * p_sys;
294 char psz_default_iec_device[128]; /* Buffer used to store the default
296 char * psz_device, * psz_iec_device; /* device names for PCM and S/PDIF
299 int i_vlc_pcm_format; /* Audio format for VLC's data */
300 int i_snd_pcm_format; /* Audio format for ALSA's data */
302 snd_pcm_uframes_t i_buffer_size = 0;
303 snd_pcm_uframes_t i_period_size = 0;
306 snd_pcm_hw_params_t *p_hw;
307 snd_pcm_sw_params_t *p_sw;
310 unsigned int i_old_rate;
313 /* Allocate structures */
314 p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) );
317 p_sys->b_playing = false;
318 p_sys->start_date = 0;
319 vlc_cond_init( &p_sys->wait );
320 vlc_mutex_init( &p_sys->lock );
322 /* Get device name */
323 if( (psz_device = config_GetPsz( p_aout, "alsadev" )) == NULL )
325 msg_Err( p_aout, "no audio device given (maybe \"default\" ?)" );
326 intf_UserFatal( p_aout, false, _("No Audio Device"),
327 _("No audio device name was given. You might want to " \
328 "enter \"default\".") );
333 /* Choose the IEC device for S/PDIF output:
334 if the device is overriden by the user then it will be the one
335 otherwise we compute the default device based on the output format. */
336 if( AOUT_FMT_NON_LINEAR( &p_aout->output.output )
337 && !strcmp( psz_device, DEFAULT_ALSA_DEVICE ) )
339 snprintf( psz_default_iec_device, sizeof(psz_default_iec_device),
340 "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
341 IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
342 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
344 ( p_aout->output.output.i_rate == 48000 ?
345 IEC958_AES3_CON_FS_48000 :
346 ( p_aout->output.output.i_rate == 44100 ?
347 IEC958_AES3_CON_FS_44100 : IEC958_AES3_CON_FS_32000 ) ) );
348 psz_iec_device = psz_default_iec_device;
350 else if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
352 psz_iec_device = psz_device;
356 psz_iec_device = NULL;
359 /* Choose the linear PCM format (read the comment above about FPU
361 if( vlc_CPU() & CPU_CAPABILITY_FPU )
363 i_vlc_pcm_format = VLC_FOURCC('f','l','3','2');
364 i_snd_pcm_format = SND_PCM_FORMAT_FLOAT;
368 i_vlc_pcm_format = AOUT_FMT_S16_NE;
369 i_snd_pcm_format = SND_PCM_FORMAT_S16;
372 /* If the variable doesn't exist then it's the first time we're called
373 and we have to probe the available audio formats and channels */
374 if ( var_Type( p_aout, "audio-device" ) == 0 )
376 Probe( p_aout, psz_device, psz_iec_device, &i_snd_pcm_format );
379 if ( var_Get( p_aout, "audio-device", &val ) < 0 )
386 p_aout->output.output.i_format = i_vlc_pcm_format;
387 if ( val.i_int == AOUT_VAR_5_1 )
389 p_aout->output.output.i_physical_channels
390 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
391 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
394 psz_device = strdup( "surround51" );
396 else if ( val.i_int == AOUT_VAR_2F2R )
398 p_aout->output.output.i_physical_channels
399 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
400 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
402 psz_device = strdup( "surround40" );
404 else if ( val.i_int == AOUT_VAR_STEREO )
406 p_aout->output.output.i_physical_channels
407 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
409 else if ( val.i_int == AOUT_VAR_MONO )
411 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
413 else if( val.i_int != AOUT_VAR_SPDIF )
415 /* This should not happen ! */
416 msg_Err( p_aout, "internal: can't find audio-device (%i)", val.i_int );
423 snd_output_stdio_attach( &p_sys->p_snd_stderr, stderr, 0 );
426 /* Open the device */
427 if ( val.i_int == AOUT_VAR_SPDIF )
429 if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_iec_device,
430 SND_PCM_STREAM_PLAYBACK, 0 ) ) < 0 )
432 msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
433 psz_iec_device, snd_strerror( i_snd_rc ) );
434 intf_UserFatal( p_aout, false, _("Audio output failed"),
435 _("VLC could not open the ALSA device \"%s\" (%s)."),
436 psz_iec_device, snd_strerror( i_snd_rc ) );
441 i_buffer_size = ALSA_SPDIF_BUFFER_SIZE;
442 i_snd_pcm_format = SND_PCM_FORMAT_S16;
445 i_vlc_pcm_format = VLC_FOURCC('s','p','d','i');
446 p_aout->output.i_nb_samples = i_period_size = ALSA_SPDIF_PERIOD_SIZE;
447 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
448 p_aout->output.output.i_frame_length = A52_FRAME_NB;
450 aout_VolumeNoneInit( p_aout );
456 msg_Dbg( p_aout, "opening ALSA device `%s'", psz_device );
458 /* Since it seems snd_pcm_close hasn't really released the device at
459 the time it returns, probe if the device is available in loop for 1s.
460 We cannot use blocking mode since the we would wait indefinitely when
461 switching from a dmx device to surround51. */
463 for( i = 10; i >= 0; i-- )
465 if ( ( i_snd_rc = snd_pcm_open( &p_sys->p_snd_pcm, psz_device,
466 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) ) == -EBUSY )
468 if( i ) msleep( 100000 /* 100ms */ );
471 msg_Err( p_aout, "audio device: %s is already in use",
473 intf_UserFatal( p_aout, false, _("Audio output failed"),
474 _("The audio device \"%s\" is already in use."),
483 msg_Err( p_aout, "cannot open ALSA device `%s' (%s)",
484 psz_device, snd_strerror( i_snd_rc ) );
485 intf_UserFatal( p_aout, false, _("Audio output failed"),
486 _("VLC could not open the ALSA device \"%s\" (%s)."),
487 psz_device, snd_strerror( i_snd_rc ) );
493 /* We want blocking mode */
494 snd_pcm_nonblock( p_sys->p_snd_pcm, 0 );
496 i_buffer_size = ALSA_DEFAULT_BUFFER_SIZE;
497 i_channels = aout_FormatNbChannels( &p_aout->output.output );
499 p_aout->output.i_nb_samples = i_period_size = ALSA_DEFAULT_PERIOD_SIZE;
501 aout_VolumeSoftInit( p_aout );
504 /* Free psz_device so that all the remaining data is stack-allocated */
507 p_aout->output.pf_play = Play;
509 snd_pcm_hw_params_alloca(&p_hw);
510 snd_pcm_sw_params_alloca(&p_sw);
512 /* Due to some bugs in alsa with some drivers, we need to retry in s16l
513 if snd_pcm_hw_params fails in fl32 */
518 /* Get Initial hardware parameters */
519 if ( ( i_snd_rc = snd_pcm_hw_params_any( p_sys->p_snd_pcm, p_hw ) ) < 0 )
521 msg_Err( p_aout, "unable to retrieve initial hardware parameters (%s)",
522 snd_strerror( i_snd_rc ) );
527 if ( ( i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm, p_hw,
528 i_snd_pcm_format ) ) < 0 )
530 if( i_snd_pcm_format != SND_PCM_FORMAT_S16 )
532 i_snd_pcm_format = SND_PCM_FORMAT_S16;
533 i_snd_rc = snd_pcm_hw_params_set_format( p_sys->p_snd_pcm,
534 p_hw, i_snd_pcm_format );
538 msg_Err( p_aout, "unable to set stream sample size and "
539 "word order (%s)", snd_strerror( i_snd_rc ) );
543 if( i_vlc_pcm_format != VLC_FOURCC('s','p','d','i') )
544 switch( i_snd_pcm_format )
546 case SND_PCM_FORMAT_FLOAT:
547 i_vlc_pcm_format = VLC_FOURCC('f','l','3','2');
549 case SND_PCM_FORMAT_S16:
550 i_vlc_pcm_format = AOUT_FMT_S16_NE;
553 p_aout->output.output.i_format = i_vlc_pcm_format;
555 if ( ( i_snd_rc = snd_pcm_hw_params_set_access( p_sys->p_snd_pcm, p_hw,
556 SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
558 msg_Err( p_aout, "unable to set interleaved stream format (%s)",
559 snd_strerror( i_snd_rc ) );
564 if ( ( i_snd_rc = snd_pcm_hw_params_set_channels( p_sys->p_snd_pcm, p_hw,
567 msg_Err( p_aout, "unable to set number of output channels (%s)",
568 snd_strerror( i_snd_rc ) );
573 i_old_rate = p_aout->output.output.i_rate;
574 #ifdef HAVE_ALSA_NEW_API
575 i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
576 &p_aout->output.output.i_rate,
579 i_snd_rc = snd_pcm_hw_params_set_rate_near( p_sys->p_snd_pcm, p_hw,
580 p_aout->output.output.i_rate,
583 if( i_snd_rc < 0 || p_aout->output.output.i_rate != i_old_rate )
585 msg_Warn( p_aout, "The rate %d Hz is not supported by your " \
586 "hardware. Using %d Hz instead.\n", i_old_rate, \
587 p_aout->output.output.i_rate );
590 /* Set period size. */
591 #ifdef HAVE_ALSA_NEW_API
592 if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
593 p_hw, &i_period_size, NULL ) ) < 0 )
595 if ( ( i_snd_rc = snd_pcm_hw_params_set_period_size_near( p_sys->p_snd_pcm,
596 p_hw, i_period_size, NULL ) ) < 0 )
599 msg_Err( p_aout, "unable to set period size (%s)",
600 snd_strerror( i_snd_rc ) );
603 p_aout->output.i_nb_samples = i_period_size;
605 /* Set buffer size. */
606 #ifdef HAVE_ALSA_NEW_API
607 if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
608 p_hw, &i_buffer_size ) ) < 0 )
610 if ( ( i_snd_rc = snd_pcm_hw_params_set_buffer_size_near( p_sys->p_snd_pcm,
611 p_hw, i_buffer_size ) ) < 0 )
614 msg_Err( p_aout, "unable to set buffer size (%s)",
615 snd_strerror( i_snd_rc ) );
619 /* Commit hardware parameters. */
620 if ( ( i_snd_rc = snd_pcm_hw_params( p_sys->p_snd_pcm, p_hw ) ) < 0 )
622 if ( b_retry == false &&
623 i_snd_pcm_format == SND_PCM_FORMAT_FLOAT)
626 i_snd_pcm_format = SND_PCM_FORMAT_S16;
627 p_aout->output.output.i_format = AOUT_FMT_S16_NE;
628 msg_Warn( p_aout, "unable to commit hardware configuration "
629 "with fl32 samples. Retrying with s16l (%s)", snd_strerror( i_snd_rc ) );
633 msg_Err( p_aout, "unable to commit hardware configuration (%s)",
634 snd_strerror( i_snd_rc ) );
640 #ifdef HAVE_ALSA_NEW_API
641 if( ( i_snd_rc = snd_pcm_hw_params_get_period_time( p_hw,
642 &p_sys->i_period_time, NULL ) ) < 0 )
644 if( ( p_sys->i_period_time =
645 (int)snd_pcm_hw_params_get_period_time( p_hw, NULL ) ) < 0 )
648 msg_Err( p_aout, "unable to get period time (%s)",
649 snd_strerror( i_snd_rc ) );
653 /* Get Initial software parameters */
654 snd_pcm_sw_params_current( p_sys->p_snd_pcm, p_sw );
656 i_snd_rc = snd_pcm_sw_params_set_sleep_min( p_sys->p_snd_pcm, p_sw, 0 );
658 i_snd_rc = snd_pcm_sw_params_set_avail_min( p_sys->p_snd_pcm, p_sw,
659 p_aout->output.i_nb_samples );
660 /* start playing when one period has been written */
661 i_snd_rc = snd_pcm_sw_params_set_start_threshold( p_sys->p_snd_pcm, p_sw,
662 ALSA_DEFAULT_PERIOD_SIZE);
665 msg_Err( p_aout, "unable to set start threshold (%s)",
666 snd_strerror( i_snd_rc ) );
670 /* Commit software parameters. */
671 if ( snd_pcm_sw_params( p_sys->p_snd_pcm, p_sw ) < 0 )
673 msg_Err( p_aout, "unable to set software configuration" );
678 snd_output_printf( p_sys->p_snd_stderr, "\nALSA hardware setup:\n\n" );
679 snd_pcm_dump_hw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
680 snd_output_printf( p_sys->p_snd_stderr, "\nALSA software setup:\n\n" );
681 snd_pcm_dump_sw_setup( p_sys->p_snd_pcm, p_sys->p_snd_stderr );
682 snd_output_printf( p_sys->p_snd_stderr, "\n" );
685 /* Create ALSA thread and wait for its readiness. */
686 if( vlc_thread_create( p_aout, "aout", ALSAThread,
687 VLC_THREAD_PRIORITY_OUTPUT, false ) )
689 msg_Err( p_aout, "cannot create ALSA thread (%m)" );
696 snd_pcm_close( p_sys->p_snd_pcm );
698 snd_output_close( p_sys->p_snd_stderr );
704 /*****************************************************************************
705 * Play: nothing to do
706 *****************************************************************************/
707 static void Play( aout_instance_t *p_aout )
709 if( !p_aout->output.p_sys->b_playing )
711 p_aout->output.p_sys->b_playing = 1;
713 /* get the playing date of the first aout buffer */
714 p_aout->output.p_sys->start_date =
715 aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
717 /* wake up the audio output thread */
718 vlc_mutex_lock( &p_aout->output.p_sys->lock );
719 vlc_cond_signal( &p_aout->output.p_sys->wait );
720 vlc_mutex_unlock( &p_aout->output.p_sys->lock );
724 /*****************************************************************************
725 * Close: close the ALSA device
726 *****************************************************************************/
727 static void Close( vlc_object_t *p_this )
729 aout_instance_t *p_aout = (aout_instance_t *)p_this;
730 struct aout_sys_t * p_sys = p_aout->output.p_sys;
733 /* Make sure that the thread will stop once it is waken up */
734 vlc_object_kill( p_aout );
736 /* make sure the audio output thread is waken up */
737 vlc_mutex_lock( &p_aout->output.p_sys->lock );
738 vlc_cond_signal( &p_aout->output.p_sys->wait );
739 vlc_mutex_unlock( &p_aout->output.p_sys->lock );
742 vlc_thread_join( p_aout );
743 p_aout->b_die = false;
745 i_snd_rc = snd_pcm_close( p_sys->p_snd_pcm );
749 msg_Err( p_aout, "failed closing ALSA device (%s)",
750 snd_strerror( i_snd_rc ) );
754 snd_output_close( p_sys->p_snd_stderr );
760 /*****************************************************************************
761 * ALSAThread: asynchronous thread used to DMA the data to the device
762 *****************************************************************************/
763 static void* ALSAThread( vlc_object_t* p_this )
765 aout_instance_t * p_aout = (aout_instance_t*)p_this;
766 struct aout_sys_t * p_sys = p_aout->output.p_sys;
767 int canc = vlc_savecancel ();
768 p_sys->p_status = (snd_pcm_status_t *)malloc(snd_pcm_status_sizeof());
770 /* Wait for the exact time to start playing (avoids resampling) */
771 vlc_mutex_lock( &p_sys->lock );
772 while( !p_sys->start_date && vlc_object_alive (p_aout) )
773 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
774 vlc_mutex_unlock( &p_sys->lock );
776 if( !vlc_object_alive (p_aout) )
779 mwait( p_sys->start_date - AOUT_PTS_TOLERANCE / 4 );
781 while ( vlc_object_alive (p_aout) )
787 snd_pcm_drop( p_sys->p_snd_pcm );
788 free( p_aout->output.p_sys->p_status );
789 vlc_restorecancel (canc);
793 /*****************************************************************************
794 * ALSAFill: function used to fill the ALSA buffer as much as possible
795 *****************************************************************************/
796 static void ALSAFill( aout_instance_t * p_aout )
798 struct aout_sys_t * p_sys = p_aout->output.p_sys;
799 aout_buffer_t * p_buffer;
800 snd_pcm_status_t * p_status = p_sys->p_status;
804 /* Fill in the buffer until space or audio output buffer shortage */
807 i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
810 msg_Err( p_aout, "cannot get device status" );
814 /* Handle buffer underruns and get the status again */
815 if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN )
817 /* Prepare the device */
818 i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm );
822 msg_Err( p_aout, "cannot recover from buffer underrun" );
826 msg_Dbg( p_aout, "recovered from buffer underrun" );
828 /* Get the new status */
829 i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status );
832 msg_Err( p_aout, "cannot get device status after recovery" );
836 /* Underrun, try to recover as quickly as possible */
841 /* Here the device should be in RUNNING state, p_status is valid. */
842 snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status );
843 if( delay == 0 ) /* workaround buggy alsa drivers */
844 if( snd_pcm_delay( p_sys->p_snd_pcm, &delay ) < 0 )
845 delay = 0; /* FIXME: use a positive minimal delay */
846 int i_bytes = snd_pcm_frames_to_bytes( p_sys->p_snd_pcm, delay );
847 next_date = mdate() + ( (mtime_t)i_bytes * 1000000
848 / p_aout->output.output.i_bytes_per_frame
849 / p_aout->output.output.i_rate
850 * p_aout->output.output.i_frame_length );
853 snd_pcm_state_t state = snd_pcm_status_get_state( p_status );
854 if( state != SND_PCM_STATE_RUNNING )
855 msg_Err( p_aout, "pcm status (%d) != RUNNING", state );
857 msg_Dbg( p_aout, "Delay is %ld frames (%d bytes)", delay, i_bytes );
859 msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame );
860 msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate );
861 msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length );
863 msg_Dbg( p_aout, "Next date is in %d microseconds", (int)(next_date - mdate()) );
867 p_buffer = aout_OutputNextBuffer( p_aout, next_date,
868 (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i')) );
870 /* Audio output buffer shortage -> stop the fill process and wait */
871 if( p_buffer == NULL )
876 i_snd_rc = snd_pcm_writei( p_sys->p_snd_pcm, p_buffer->p_buffer,
877 p_buffer->i_nb_samples );
878 if( i_snd_rc != -ESTRPIPE )
881 /* a suspend event occurred
882 * (stream is suspended and waiting for an application recovery) */
883 msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." );
885 while( vlc_object_alive (p_aout) && vlc_object_alive (p_aout->p_libvlc) &&
886 ( i_snd_rc = snd_pcm_resume( p_sys->p_snd_pcm ) ) == -EAGAIN )
892 /* Device does not supprot resuming, restart it */
893 i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm );
898 msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) );
900 aout_BufferFree( p_buffer );
905 msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) );
906 msleep( p_sys->i_period_time >> 1 );
909 static void GetDevicesForCard( module_config_t *p_item, int i_card );
910 static void GetDevices( module_config_t *p_item );
912 /*****************************************************************************
913 * config variable callback
914 *****************************************************************************/
915 static int FindDevicesCallback( vlc_object_t *p_this, char const *psz_name,
916 vlc_value_t newval, vlc_value_t oldval, void *p_unused )
918 module_config_t *p_item;
924 p_item = config_FindConfig( p_this, psz_name );
925 if( !p_item ) return VLC_SUCCESS;
927 /* Clear-up the current list */
930 /* Keep the first entrie */
931 for( i = 1; i < p_item->i_list; i++ )
933 free( (char *)p_item->ppsz_list[i] );
934 free( (char *)p_item->ppsz_list_text[i] );
936 /* TODO: Remove when no more needed */
937 p_item->ppsz_list[i] = NULL;
938 p_item->ppsz_list_text[i] = NULL;
942 GetDevices( p_item );
944 /* Signal change to the interface */
945 p_item->b_dirty = true;
951 static void GetDevicesForCard( module_config_t *p_item, int i_card )
953 int i_pcm_device = -1;
955 snd_pcm_info_t *p_pcm_info;
960 sprintf( psz_dev, "hw:%i", i_card );
962 if( ( i_err = snd_ctl_open( &p_ctl, psz_dev, 0 ) ) < 0 )
965 if( ( i_err = snd_card_get_name( i_card, &psz_card_name ) ) != 0)
966 psz_card_name = _("Unknown soundcard");
968 snd_pcm_info_alloca( &p_pcm_info );
972 char *psz_device, *psz_descr;
973 if( ( i_err = snd_ctl_pcm_next_device( p_ctl, &i_pcm_device ) ) < 0 )
975 if( i_pcm_device < 0 )
978 snd_pcm_info_set_device( p_pcm_info, i_pcm_device );
979 snd_pcm_info_set_subdevice( p_pcm_info, 0 );
980 snd_pcm_info_set_stream( p_pcm_info, SND_PCM_STREAM_PLAYBACK );
982 if( ( i_err = snd_ctl_pcm_info( p_ctl, p_pcm_info ) ) < 0 )
984 if( i_err != -ENOENT )
986 /*printf( "get_devices_for_card(): "
987 "snd_ctl_pcm_info() "
988 "failed (%d:%d): %s.\n", i_card,
989 i_pcm_device, snd_strerror( -i_err ) );*/
994 if( asprintf( &psz_device, "hw:%d,%d", i_card, i_pcm_device ) == -1 )
996 if( asprintf( &psz_descr, "%s: %s (%s)", psz_card_name,
997 snd_pcm_info_get_name(p_pcm_info), psz_device ) == -1 )
1004 (char **)realloc( p_item->ppsz_list,
1005 (p_item->i_list + 2) * sizeof(char *) );
1006 p_item->ppsz_list_text =
1007 (char **)realloc( p_item->ppsz_list_text,
1008 (p_item->i_list + 2) * sizeof(char *) );
1009 p_item->ppsz_list[ p_item->i_list ] = psz_device;
1010 p_item->ppsz_list_text[ p_item->i_list ] = psz_descr;
1012 p_item->ppsz_list[ p_item->i_list ] = NULL;
1013 p_item->ppsz_list_text[ p_item->i_list ] = NULL;
1016 snd_ctl_close( p_ctl );
1021 static void GetDevices( module_config_t *p_item )
1026 if( ( i_err = snd_card_next( &i_card ) ) != 0 )
1028 /*printf( "snd_card_next() failed: %s", snd_strerror( -i_err ) );*/
1032 while( i_card > -1 )
1034 GetDevicesForCard( p_item, i_card );
1035 if( ( i_err = snd_card_next( &i_card ) ) != 0 )
1037 /*printf( "snd_card_next() failed: %s", snd_strerror( -i_err ) );*/