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