]> git.sesse.net Git - vlc/blob - plugins/alsa/aout_alsa.c
* Made everything ready for a vlc-0.2.91 / libdvdcss-1.0.0 release.
[vlc] / plugins / alsa / aout_alsa.c
1 /*****************************************************************************
2  * aout_alsa.c : Alsa functions library
3  *****************************************************************************
4  * Copyright (C) 2000 VideoLAN
5  * $Id: aout_alsa.c,v 1.19 2001/11/12 20:16:33 sam Exp $
6  *
7  * Authors: Henri Fallon <henri@videolan.org> - Original Author
8  *          Jeffrey Baker <jwbaker@acm.org> - Port to ALSA 1.0 API
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 #define MODULE_NAME alsa
26 #include "modules_inner.h"
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31
32 #include "defs.h"
33
34 #include <errno.h>                                                 /* ENOMEM */
35 #include <string.h>                                            /* strerror() */
36 #include <stdio.h>                                           /* "intf_msg.h" */
37 #include <stdlib.h>                            /* calloc(), malloc(), free() */
38
39 #include <sys/asoundlib.h>
40
41 #include "config.h"
42 #include "common.h"                                     /* boolean_t, byte_t */
43 #include "threads.h"
44 #include "mtime.h"
45 #include "tests.h"
46
47 #include "audio_output.h"                                   /* aout_thread_t */
48
49 #include "intf_msg.h"                        /* intf_DbgMsg(), intf_ErrMsg() */
50 #include "main.h"
51
52 #include "modules.h"
53 #include "modules_export.h"
54
55 typedef struct alsa_device_s
56 {
57     int i_num;
58 } alsa_device_t;
59
60 typedef struct alsa_card_s
61 {
62     int i_num;
63 } alsa_card_t;
64
65 /* here we store plugin dependant informations */
66
67 typedef struct aout_sys_s
68 {
69     snd_pcm_t   * p_alsa_handle;
70     unsigned long buffer_time;
71     unsigned long period_time;
72     unsigned long chunk_size;
73     unsigned long buffer_size;
74     unsigned long rate;
75     unsigned int  bytes_per_sample;
76     unsigned int  samples_per_frame;
77     unsigned int  bytes_per_frame;
78 } aout_sys_t;
79
80
81 /*****************************************************************************
82  * aout_Probe: probes the audio device and return a score
83  *****************************************************************************
84  * This function tries to open the dps and returns a score to the plugin
85  * manager so that it can make its choice.
86  *****************************************************************************/
87 static int aout_Probe( probedata_t *p_data )
88 {
89     int i_open_return, i_close_return;
90     aout_sys_t local_sys;
91
92     printf("aout_probe\n");
93     /* Open device */
94     i_open_return = snd_pcm_open( &(local_sys.p_alsa_handle), "default",
95                                   SND_PCM_STREAM_PLAYBACK, 0 );
96     printf("grmbl\n");
97     if( i_open_return )
98     {
99         intf_WarnMsg( 2, "aout info: could not probe ALSA device (%s)",
100                       snd_strerror( i_open_return ) );
101         return ( 0 );
102     }
103
104     /* Close it */
105     i_close_return = snd_pcm_close( local_sys.p_alsa_handle );
106
107     if( i_close_return )
108     {
109         intf_ErrMsg( "aout error: could not close ALSA device (%s)",
110                      snd_strerror( i_close_return ) );
111         return( 0 );
112     }
113
114     if( TestMethod( AOUT_METHOD_VAR, "alsa" ) )
115     {
116         return( 999 );
117     }
118
119     /* And return score */
120     return( 100 );
121 }
122
123 /*****************************************************************************
124  * aout_Open : creates a handle and opens an alsa device
125  *****************************************************************************
126  * This function opens an alsa device, through the alsa API
127  *****************************************************************************/
128 static int aout_Open( aout_thread_t *p_aout )
129 {
130
131     int i_open_returns;
132
133     /* Allocate structures */
134     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
135     if( p_aout->p_sys == NULL )
136     {
137         intf_ErrMsg( "aout error: failed allocating memory for ALSA (%s)",
138                      strerror(ENOMEM) );
139         return( 1 );
140     }
141
142     p_aout->i_format   = AOUT_FORMAT_DEFAULT;
143     p_aout->i_channels = 1 + main_GetIntVariable( AOUT_STEREO_VAR,
144                                                   AOUT_STEREO_DEFAULT );
145     p_aout->l_rate     = main_GetIntVariable( AOUT_RATE_VAR,
146                                               AOUT_RATE_DEFAULT );
147
148     /* Open device */
149     if( ( i_open_returns = snd_pcm_open(&(p_aout->p_sys->p_alsa_handle),
150                                         "default",
151                                         SND_PCM_STREAM_PLAYBACK, 0) ) )
152     {
153         intf_ErrMsg( "aout error: could not open ALSA device (%s)",
154                      snd_strerror(i_open_returns) );
155         return( -1 );
156     }
157
158     intf_DbgMsg( "aout info: ALSA device successfully opened" );
159     return( 0 );
160 }
161
162
163 /*****************************************************************************
164  * aout_SetFormat : sets the alsa output format
165  *****************************************************************************
166  * This function prepares the device, sets the rate, format, the mode
167  * ( "play as soon as you have data" ), and buffer information.
168  *****************************************************************************/
169 static int aout_SetFormat( aout_thread_t *p_aout )
170 {
171
172     int i_rv;
173     int i_format;
174
175     snd_pcm_hw_params_t *p_hw;
176     snd_pcm_sw_params_t *p_sw;
177
178     snd_pcm_hw_params_alloca(&p_hw);
179     snd_pcm_sw_params_alloca(&p_sw);
180
181     switch (p_aout->i_format)
182     {
183         case AOUT_FMT_S16_LE:
184             i_format = SND_PCM_FORMAT_S16_LE;
185             p_aout->p_sys->bytes_per_sample = 2;
186             break;
187
188         default:
189             i_format = SND_PCM_FORMAT_S16_BE;
190             p_aout->p_sys->bytes_per_sample = 2;
191             break;
192     }
193
194     p_aout->p_sys->samples_per_frame = p_aout->i_channels;
195     p_aout->p_sys->bytes_per_frame = p_aout->p_sys->samples_per_frame *
196                                      p_aout->p_sys->bytes_per_sample;
197
198     i_rv = snd_pcm_hw_params_any( p_aout->p_sys->p_alsa_handle, p_hw );
199     if( i_rv < 0 )
200     {
201         intf_ErrMsg( "aout error: unable to retrieve initial parameters" );
202         return( -1 );
203     }
204
205     i_rv = snd_pcm_hw_params_set_access( p_aout->p_sys->p_alsa_handle, p_hw,
206                                          SND_PCM_ACCESS_RW_INTERLEAVED );
207     if( i_rv < 0 )
208     {
209         intf_ErrMsg( "aout error: unable to set interleaved stream format" );
210         return( -1 );
211     }
212
213     i_rv = snd_pcm_hw_params_set_format( p_aout->p_sys->p_alsa_handle,
214                                          p_hw, i_format );
215     if( i_rv < 0 )
216     {
217         intf_ErrMsg( "aout error: unable to set stream sample size and word"
218                      " order" );
219         return( -1 );
220     }
221
222     i_rv = snd_pcm_hw_params_set_channels( p_aout->p_sys->p_alsa_handle, p_hw,
223                                            p_aout->i_channels );
224     if( i_rv < 0 )
225     {
226         intf_ErrMsg( "aout error: unable to set number of output channels" );
227         return( -1 );
228     }
229
230     i_rv = snd_pcm_hw_params_set_rate_near( p_aout->p_sys->p_alsa_handle, p_hw,
231                                             p_aout->l_rate, 0 );
232     if( i_rv < 0 )
233     {
234         intf_ErrMsg( "aout error: unable to set sample rate" );
235         return( -1 );
236     }
237     p_aout->p_sys->rate = i_rv;
238
239     i_rv = snd_pcm_hw_params_set_buffer_time_near( p_aout->p_sys->p_alsa_handle,
240                                                    p_hw, AOUT_BUFFER_DURATION,
241                                                    0 );
242     if( i_rv < 0 )
243     {
244         intf_ErrMsg( "aout error: unable to set buffer time" );
245         return( -1 );
246     }
247     p_aout->p_sys->buffer_time = i_rv;
248
249     i_rv = snd_pcm_hw_params_set_period_time_near( p_aout->p_sys->p_alsa_handle,
250          p_hw, p_aout->p_sys->buffer_time / p_aout->p_sys->bytes_per_frame, 0 );
251     if( i_rv < 0 )
252     {
253         intf_ErrMsg( "aout error: unable to set period time" );
254         return( -1 );
255     }
256     p_aout->p_sys->period_time = i_rv;
257
258     i_rv = snd_pcm_hw_params(p_aout->p_sys->p_alsa_handle, p_hw);
259     if (i_rv < 0)
260     {
261         intf_ErrMsg( "aout error: unable to set hardware configuration" );
262         return( -1 );
263     }
264
265     p_aout->p_sys->chunk_size = snd_pcm_hw_params_get_period_size( p_hw, 0 );
266     p_aout->p_sys->buffer_size = snd_pcm_hw_params_get_buffer_size( p_hw );
267
268     snd_pcm_sw_params_current( p_aout->p_sys->p_alsa_handle, p_sw );
269     i_rv = snd_pcm_sw_params_set_sleep_min( p_aout->p_sys->p_alsa_handle, p_sw,
270                                             0 );
271
272     i_rv = snd_pcm_sw_params_set_avail_min( p_aout->p_sys->p_alsa_handle, p_sw,
273                                             p_aout->p_sys->chunk_size );
274
275     /* Worked with the CVS version but not with 0.9beta3
276     i_rv = snd_pcm_sw_params_set_start_threshold( p_aout->p_sys->p_alsa_handle,
277                                             p_sw, p_aout->p_sys->buffer_size );
278
279     i_rv = snd_pcm_sw_params_set_stop_threshold( p_aout->p_sys->p_alsa_handle,
280                                              p_sw, p_aout->p_sys->buffer_size);
281     */
282     i_rv = snd_pcm_sw_params( p_aout->p_sys->p_alsa_handle, p_sw );
283     if( i_rv < 0 )
284     {
285         intf_ErrMsg( "aout error: unable to set software configuration" );
286         return( -1 );
287     }
288
289     p_aout->i_latency = 0;
290     
291     return( 0 );
292 }
293
294 /*****************************************************************************
295  * aout_HandleXrun : reprepare the output
296  *****************************************************************************
297  * When buffer gets empty, the driver goes in "Xrun" state, where it needs
298  * to be reprepared before playing again
299  *****************************************************************************/
300 static void aout_HandleXrun(aout_thread_t *p_aout)
301 {
302     int i_rv;
303
304     intf_ErrMsg( "aout error: resetting output after buffer underrun" );
305
306     i_rv = snd_pcm_reset( p_aout->p_sys->p_alsa_handle );
307     i_rv = snd_pcm_prepare( p_aout->p_sys->p_alsa_handle );
308     if( i_rv < 0 )
309     {
310         intf_ErrMsg( "aout error: unable to recover from buffer underrun (%s)",
311                      snd_strerror( i_rv ) );
312     }
313 }
314
315
316 /*****************************************************************************
317  * aout_BufInfo: buffer status query
318  *****************************************************************************
319  * This function returns the number of used byte in the queue.
320  * It also deals with errors : indeed if the device comes to run out
321  * of data to play, it switches to the "underrun" status. It has to
322  * be flushed and re-prepared
323  *****************************************************************************/
324 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
325 {
326     snd_pcm_status_t *p_status;
327     int i_alsa_get_status_returns;
328
329     snd_pcm_status_alloca( &p_status );
330
331     i_alsa_get_status_returns = snd_pcm_status( p_aout->p_sys->p_alsa_handle,
332                                                 p_status );
333
334     if( i_alsa_get_status_returns )
335     {
336         intf_ErrMsg ( "aout error: failed getting alsa buffer info (%s)",
337                       snd_strerror ( i_alsa_get_status_returns ) );
338         return ( -1 );
339     }
340
341     switch( snd_pcm_status_get_state( p_status ) )
342     {
343         case SND_PCM_STATE_XRUN :
344             aout_HandleXrun( p_aout );
345             break;
346
347         case SND_PCM_STATE_OPEN:
348         case SND_PCM_STATE_PREPARED:
349         case SND_PCM_STATE_RUNNING:
350             break;
351
352         default:
353             intf_ErrMsg( "aout error: unhandled condition %i",
354                          snd_pcm_status_get_state( p_status ) );
355             break;
356     }
357
358     return( snd_pcm_status_get_avail(p_status) *
359             p_aout->p_sys->bytes_per_frame );
360 }
361
362 /*****************************************************************************
363  * aout_Play : plays a sample
364  *****************************************************************************
365  * Plays a sample using the snd_pcm_writei function from the alsa API
366  *****************************************************************************/
367 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
368 {
369     snd_pcm_uframes_t tot_frames;
370     snd_pcm_uframes_t frames_left;
371     snd_pcm_uframes_t rv;
372
373     tot_frames = i_size / p_aout->p_sys->bytes_per_frame;
374     frames_left = tot_frames;
375
376     while( frames_left > 0 )
377     {
378         rv = snd_pcm_writei( p_aout->p_sys->p_alsa_handle, buffer +
379                              (tot_frames - frames_left) *
380                              p_aout->p_sys->bytes_per_frame, frames_left );
381
382         if( (signed int) rv < 0 )
383         {
384             intf_ErrMsg( "aout error: failed writing to output (%s)",
385                          snd_strerror( rv ) );
386             return;
387         }
388
389         frames_left -= rv;
390     }
391 }
392
393 /*****************************************************************************
394  * aout_Close : close the Alsa device
395  *****************************************************************************/
396 static void aout_Close( aout_thread_t *p_aout )
397 {
398     int i_close_returns;
399
400     i_close_returns = snd_pcm_close( p_aout->p_sys->p_alsa_handle );
401
402     if( i_close_returns )
403     {
404         intf_ErrMsg( "aout error: failed closing ALSA device (%s)",
405                      i_close_returns, snd_strerror( i_close_returns ) );
406     }
407
408     free( p_aout->p_sys );
409
410     intf_DbgMsg( "aout: ALSA device closed" );
411 }
412
413 /*****************************************************************************
414  * Functions exported as capabilities. They are declared as static so that
415  * we don't pollute the namespace too much.
416  *****************************************************************************/
417 void _M( aout_getfunctions )( function_list_t * p_function_list )
418 {
419     p_function_list->pf_probe = aout_Probe;
420     p_function_list->functions.aout.pf_open = aout_Open;
421     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
422     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
423     p_function_list->functions.aout.pf_play = aout_Play;
424     p_function_list->functions.aout.pf_close = aout_Close;
425 }
426