]> git.sesse.net Git - vlc/blob - plugins/alsa/aout_alsa.c
* libdvdcss enhancements by Billy Biggs <vektor@dumbterm.net>. This breaks
[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.17 2001/07/11 02:01:03 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     /* Open device */
93     i_open_return = snd_pcm_open( &(local_sys.p_alsa_handle), "plug:0,0",
94                                   SND_PCM_STREAM_PLAYBACK, 0 );
95     if( i_open_return )
96     {
97         intf_WarnMsg( 2, "aout info: could not probe ALSA device (%s)",
98                       snd_strerror( i_open_return ) );
99         return ( 0 );
100     }
101
102     /* Close it */
103     i_close_return = snd_pcm_close( local_sys.p_alsa_handle );
104
105     if( i_close_return )
106     {
107         intf_ErrMsg( "aout error: could not close ALSA device (%s)",
108                      snd_strerror( i_close_return ) );
109         return( 0 );
110     }
111
112     if( TestMethod( AOUT_METHOD_VAR, "alsa" ) )
113     {
114         return( 999 );
115     }
116
117     /* And return score */
118     return( 100 );
119 }
120
121 /*****************************************************************************
122  * aout_Open : creates a handle and opens an alsa device
123  *****************************************************************************
124  * This function opens an alsa device, through the alsa API
125  *****************************************************************************/
126 static int aout_Open( aout_thread_t *p_aout )
127 {
128
129     int i_open_returns;
130
131     /* Allocate structures */
132     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
133     if( p_aout->p_sys == NULL )
134     {
135         intf_ErrMsg( "aout error: failed allocating memory for ALSA (%s)",
136                      strerror(ENOMEM) );
137         return( 1 );
138     }
139
140     p_aout->i_format   = AOUT_FORMAT_DEFAULT;
141     p_aout->i_channels = 1 + main_GetIntVariable( AOUT_STEREO_VAR,
142                                                   AOUT_STEREO_DEFAULT );
143     p_aout->l_rate     = main_GetIntVariable( AOUT_RATE_VAR,
144                                               AOUT_RATE_DEFAULT );
145
146     /* Open device */
147     if( ( i_open_returns = snd_pcm_open(&(p_aout->p_sys->p_alsa_handle),
148                                         "plug:0,0",
149                                         SND_PCM_STREAM_PLAYBACK, 0) ) )
150     {
151         intf_ErrMsg( "aout error: could not open ALSA device (%s)",
152                      snd_strerror(i_open_returns) );
153         return( -1 );
154     }
155
156     intf_DbgMsg( "aout info: ALSA device successfully opened" );
157     return( 0 );
158 }
159
160
161 /*****************************************************************************
162  * aout_SetFormat : sets the alsa output format
163  *****************************************************************************
164  * This function prepares the device, sets the rate, format, the mode
165  * ( "play as soon as you have data" ), and buffer information.
166  *****************************************************************************/
167 static int aout_SetFormat( aout_thread_t *p_aout )
168 {
169
170     int i_rv;
171     int i_format;
172
173     snd_pcm_hw_params_t *p_hw;
174     snd_pcm_sw_params_t *p_sw;
175
176     snd_pcm_hw_params_alloca(&p_hw);
177     snd_pcm_sw_params_alloca(&p_sw);
178
179     switch (p_aout->i_format)
180     {
181         case AOUT_FMT_S16_LE:
182             i_format = SND_PCM_FORMAT_S16_LE;
183             p_aout->p_sys->bytes_per_sample = 2;
184             break;
185
186         default:
187             i_format = SND_PCM_FORMAT_S16_BE;
188             p_aout->p_sys->bytes_per_sample = 2;
189             break;
190     }
191
192     p_aout->p_sys->samples_per_frame = p_aout->i_channels;
193     p_aout->p_sys->bytes_per_frame = p_aout->p_sys->samples_per_frame *
194                                      p_aout->p_sys->bytes_per_sample;
195
196     i_rv = snd_pcm_hw_params_any( p_aout->p_sys->p_alsa_handle, p_hw );
197     if( i_rv < 0 )
198     {
199         intf_ErrMsg( "aout error: unable to retrieve initial parameters" );
200         return( -1 );
201     }
202
203     i_rv = snd_pcm_hw_params_set_access( p_aout->p_sys->p_alsa_handle, p_hw,
204                                          SND_PCM_ACCESS_RW_INTERLEAVED );
205     if( i_rv < 0 )
206     {
207         intf_ErrMsg( "aout error: unable to set interleaved stream format" );
208         return( -1 );
209     }
210
211     i_rv = snd_pcm_hw_params_set_format( p_aout->p_sys->p_alsa_handle,
212                                          p_hw, i_format );
213     if( i_rv < 0 )
214     {
215         intf_ErrMsg( "aout error: unable to set stream sample size and word"
216                      " order" );
217         return( -1 );
218     }
219
220     i_rv = snd_pcm_hw_params_set_channels( p_aout->p_sys->p_alsa_handle, p_hw,
221                                            p_aout->i_channels );
222     if( i_rv < 0 )
223     {
224         intf_ErrMsg( "aout error: unable to set number of output channels" );
225         return( -1 );
226     }
227
228     i_rv = snd_pcm_hw_params_set_rate_near( p_aout->p_sys->p_alsa_handle, p_hw,
229                                             p_aout->l_rate, 0 );
230     if( i_rv < 0 )
231     {
232         intf_ErrMsg( "aout error: unable to set sample rate" );
233         return( -1 );
234     }
235     p_aout->p_sys->rate = i_rv;
236
237     i_rv = snd_pcm_hw_params_set_buffer_time_near( p_aout->p_sys->p_alsa_handle,
238                                                    p_hw, AOUT_BUFFER_DURATION,
239                                                    0 );
240     if( i_rv < 0 )
241     {
242         intf_ErrMsg( "aout error: unable to set buffer time" );
243         return( -1 );
244     }
245     p_aout->p_sys->buffer_time = i_rv;
246
247     i_rv = snd_pcm_hw_params_set_period_time_near( p_aout->p_sys->p_alsa_handle,
248          p_hw, p_aout->p_sys->buffer_time / p_aout->p_sys->bytes_per_frame, 0 );
249     if( i_rv < 0 )
250     {
251         intf_ErrMsg( "aout error: unable to set period time" );
252         return( -1 );
253     }
254     p_aout->p_sys->period_time = i_rv;
255
256     i_rv = snd_pcm_hw_params(p_aout->p_sys->p_alsa_handle, p_hw);
257     if (i_rv < 0)
258     {
259         intf_ErrMsg( "aout error: unable to set hardware configuration" );
260         return( -1 );
261     }
262
263     p_aout->p_sys->chunk_size = snd_pcm_hw_params_get_period_size( p_hw, 0 );
264     p_aout->p_sys->buffer_size = snd_pcm_hw_params_get_buffer_size( p_hw );
265
266     snd_pcm_sw_params_current( p_aout->p_sys->p_alsa_handle, p_sw );
267     i_rv = snd_pcm_sw_params_set_sleep_min( p_aout->p_sys->p_alsa_handle, p_sw,
268                                             0 );
269
270     i_rv = snd_pcm_sw_params_set_avail_min( p_aout->p_sys->p_alsa_handle, p_sw,
271                                             p_aout->p_sys->chunk_size );
272
273     /* Worked with the CVS version but not with 0.9beta3
274     i_rv = snd_pcm_sw_params_set_start_threshold( p_aout->p_sys->p_alsa_handle,
275                                             p_sw, p_aout->p_sys->buffer_size );
276
277     i_rv = snd_pcm_sw_params_set_stop_threshold( p_aout->p_sys->p_alsa_handle,
278                                              p_sw, p_aout->p_sys->buffer_size);
279     */
280     i_rv = snd_pcm_sw_params( p_aout->p_sys->p_alsa_handle, p_sw );
281     if( i_rv < 0 )
282     {
283         intf_ErrMsg( "aout error: unable to set software configuration" );
284         return( -1 );
285     }
286
287     return( 0 );
288 }
289
290 /*****************************************************************************
291  * aout_HandleXrun : reprepare the output
292  *****************************************************************************
293  * When buffer gets empty, the driver goes in "Xrun" state, where it needs
294  * to be reprepared before playing again
295  *****************************************************************************/
296 static void aout_HandleXrun(aout_thread_t *p_aout)
297 {
298     int i_rv;
299
300     intf_ErrMsg( "aout error: resetting output after buffer underrun" );
301
302     i_rv = snd_pcm_reset( p_aout->p_sys->p_alsa_handle );
303     i_rv = snd_pcm_prepare( p_aout->p_sys->p_alsa_handle );
304     if( i_rv < 0 )
305     {
306         intf_ErrMsg( "aout error: unable to recover from buffer underrun (%s)",
307                      snd_strerror( i_rv ) );
308     }
309 }
310
311
312 /*****************************************************************************
313  * aout_BufInfo: buffer status query
314  *****************************************************************************
315  * This function returns the number of used byte in the queue.
316  * It also deals with errors : indeed if the device comes to run out
317  * of data to play, it switches to the "underrun" status. It has to
318  * be flushed and re-prepared
319  *****************************************************************************/
320 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
321 {
322     snd_pcm_status_t *p_status;
323     int i_alsa_get_status_returns;
324
325     snd_pcm_status_alloca( &p_status );
326
327     i_alsa_get_status_returns = snd_pcm_status( p_aout->p_sys->p_alsa_handle,
328                                                 p_status );
329
330     if( i_alsa_get_status_returns )
331     {
332         intf_ErrMsg ( "aout error: failed getting alsa buffer info (%s)",
333                       snd_strerror ( i_alsa_get_status_returns ) );
334         return ( -1 );
335     }
336
337     switch( snd_pcm_status_get_state( p_status ) )
338     {
339         case SND_PCM_STATE_XRUN :
340             aout_HandleXrun( p_aout );
341             break;
342
343         case SND_PCM_STATE_OPEN:
344         case SND_PCM_STATE_PREPARED:
345         case SND_PCM_STATE_RUNNING:
346             break;
347
348         default:
349             intf_ErrMsg( "aout error: unhandled condition %i",
350                          snd_pcm_status_get_state( p_status ) );
351             break;
352     }
353
354     return( snd_pcm_status_get_avail(p_status) *
355             p_aout->p_sys->bytes_per_frame );
356 }
357
358 /*****************************************************************************
359  * aout_Play : plays a sample
360  *****************************************************************************
361  * Plays a sample using the snd_pcm_writei function from the alsa API
362  *****************************************************************************/
363 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
364 {
365     snd_pcm_uframes_t tot_frames;
366     snd_pcm_uframes_t frames_left;
367     snd_pcm_uframes_t rv;
368
369     tot_frames = i_size / p_aout->p_sys->bytes_per_frame;
370     frames_left = tot_frames;
371
372     while( frames_left > 0 )
373     {
374         rv = snd_pcm_writei( p_aout->p_sys->p_alsa_handle, buffer +
375                              (tot_frames - frames_left) *
376                              p_aout->p_sys->bytes_per_frame, frames_left );
377
378         if( (signed int) rv < 0 )
379         {
380             intf_ErrMsg( "aout error: failed writing to output (%s)",
381                          snd_strerror( rv ) );
382             return;
383         }
384
385         frames_left -= rv;
386     }
387 }
388
389 /*****************************************************************************
390  * aout_Close : close the Alsa device
391  *****************************************************************************/
392 static void aout_Close( aout_thread_t *p_aout )
393 {
394     int i_close_returns;
395
396     i_close_returns = snd_pcm_close( p_aout->p_sys->p_alsa_handle );
397
398     if( i_close_returns )
399     {
400         intf_ErrMsg( "aout error: failed closing ALSA device (%s)",
401                      i_close_returns, snd_strerror( i_close_returns ) );
402     }
403
404     free( p_aout->p_sys );
405
406     intf_DbgMsg( "aout: ALSA device closed" );
407 }
408
409 /*****************************************************************************
410  * Functions exported as capabilities. They are declared as static so that
411  * we don't pollute the namespace too much.
412  *****************************************************************************/
413 void _M( aout_getfunctions )( function_list_t * p_function_list )
414 {
415     p_function_list->pf_probe = aout_Probe;
416     p_function_list->functions.aout.pf_open = aout_Open;
417     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
418     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
419     p_function_list->functions.aout.pf_play = aout_Play;
420     p_function_list->functions.aout.pf_close = aout_Close;
421 }
422