]> git.sesse.net Git - vlc/blob - plugins/alsa/aout_alsa.c
Added : alsa support
[vlc] / plugins / alsa / aout_alsa.c
1 /*****************************************************************************
2  * aout_alsa.c : Alsa functions library
3  *****************************************************************************
4  * Copyright (C) 2000 VideoLAN
5  *
6  * Authors:
7  *  Henri Fallon <henri@videolan.org>
8  * 
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #include "defs.h"
29
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <fcntl.h>                                       /* open(), O_WRONLY */
32 #include <sys/ioctl.h>                                            /* ioctl() */
33 #include <string.h>                                            /* strerror() */
34 #include <unistd.h>                                      /* write(), close() */
35 #include <stdio.h>                                           /* "intf_msg.h" */
36 #include <stdlib.h>                            /* calloc(), malloc(), free() */
37
38 #include <sys/asoundlib.h>
39 #include <linux/asound.h>
40
41 #include "config.h"
42 #include "common.h"                                     /* boolean_t, byte_t */
43 #include "threads.h"
44 #include "mtime.h"
45 #include "plugins.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
53
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     alsa_device_t * p_alsa_device;
71     alsa_card_t * p_alsa_card;
72     snd_pcm_channel_params_t s_alsa_channel_params;
73     snd_pcm_format_t s_alsa_format;
74 } aout_sys_t;
75
76
77
78 /*****************************************************************************
79  * aout_AlsaOpen : creates a handle and opens an alsa device
80  *****************************************************************************/
81
82 int aout_AlsaOpen( aout_thread_t *p_aout )
83 {
84
85     int i_open_returns;
86     
87     /* Allocate structures */
88     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
89     if( p_aout->p_sys == NULL )
90     {
91         intf_ErrMsg("error: %s\n", strerror(ENOMEM) );
92         return( 1 );
93     }
94
95     p_aout->p_sys->p_alsa_device = malloc( sizeof( alsa_device_t) );
96     p_aout->p_sys->p_alsa_card = malloc( sizeof( alsa_device_t ) );
97     if( ( p_aout->p_sys->p_alsa_device == NULL ) || 
98             ( p_aout->p_sys->p_alsa_card == NULL ) )
99     {
100         intf_ErrMsg ( "error: %s\n", strerror(ENOMEM) );
101         return ( 1 );
102     }
103
104     /* Initialize  */
105     p_aout->p_sys->p_alsa_device->i_num = 0;
106     p_aout->p_sys->p_alsa_card->i_num = 0;
107     /* FIXME : why not other format ? */
108     p_aout->i_format = AOUT_FMT_S16_LE;   
109     /* FIXME : why always 2 channels ?*/
110     p_aout->i_channels = 2;
111     p_aout->l_rate = main_GetIntVariable( AOUT_RATE_VAR, AOUT_RATE_DEFAULT );
112     
113     /* Open device */
114     if ( ( i_open_returns = snd_pcm_open( &(p_aout->p_sys->p_alsa_handle),
115                 p_aout->p_sys->p_alsa_card->i_num,
116                 p_aout->p_sys->p_alsa_device->i_num,
117                 SND_PCM_OPEN_PLAYBACK ) ) )
118     {
119         intf_ErrMsg ( "could not open alsa device; error code : %i\n",
120                 i_open_returns );
121         return ( 1 );
122     }
123
124     return ( 0 );
125
126     intf_ErrMsg("Alsa device open \n\n");
127 }
128
129
130 /*****************************************************************************
131  * aout_AlsaSetFormat : sets the alsa output format
132  *****************************************************************************/
133
134 int aout_AlsaSetFormat ( aout_thread_t *p_aout )
135 {
136     
137     int i_set_param_returns;
138     int i_prepare_playback_returns;
139     int i_playback_go_returns;
140
141     /* Fill with zeros */
142     memset(&p_aout->p_sys->s_alsa_channel_params,0,
143             sizeof(p_aout->p_sys->s_alsa_channel_params));
144     
145     /* Fill the s_alsa_channel_params structure */
146
147     /* Tranfer mode and direction*/    
148     p_aout->p_sys->s_alsa_channel_params.channel = SND_PCM_CHANNEL_PLAYBACK ;
149     p_aout->p_sys->s_alsa_channel_params.mode = SND_PCM_MODE_STREAM;
150     
151     /* Format and rate */
152     p_aout->p_sys->s_alsa_channel_params.format.interleave = 1;
153     if ( p_aout->i_format == AOUT_FMT_S16_LE )
154         p_aout->p_sys->s_alsa_channel_params.format.format = 
155             SND_PCM_SFMT_S16_LE;
156     else
157         p_aout->p_sys->s_alsa_channel_params.format.format = 
158             SND_PCM_SFMT_S16_BE;
159
160     p_aout->p_sys->s_alsa_channel_params.format.rate = p_aout->l_rate;
161     p_aout->p_sys->s_alsa_channel_params.format.voices = p_aout->i_channels ;
162     
163     /* When to start playing and when to stop */
164     p_aout->p_sys->s_alsa_channel_params.start_mode = SND_PCM_START_DATA;
165     p_aout->p_sys->s_alsa_channel_params.stop_mode = SND_PCM_STOP_STOP;
166
167     /* Buffer information . I have chosen the stream mode here
168      * instead of the block mode. I don't know whether i'm wrong 
169      * but it seemed more logical */
170     p_aout->p_sys->s_alsa_channel_params.buf.stream.queue_size = 131072; 
171     /* Fill with silence */
172     p_aout->p_sys->s_alsa_channel_params.buf.stream.fill = SND_PCM_FILL_NONE ;
173     p_aout->p_sys->s_alsa_channel_params.buf.stream.max_fill = 0 ; 
174   
175     /* Now we pass this to the driver */
176     i_set_param_returns = snd_pcm_channel_params ( 
177             p_aout->p_sys->p_alsa_handle, 
178             &(p_aout->p_sys->s_alsa_channel_params) );
179     
180     if ( i_set_param_returns )
181     {
182         intf_ErrMsg ( "ALSA_PLUGIN : Unable to set parameters; exit = %i\n",
183                 i_set_param_returns );
184         intf_ErrMsg( "This means : %s\n\n",
185                 snd_strerror( i_set_param_returns ) );
186         return ( 1 );
187     }
188
189     /* we shall now prepare the channel */
190     i_prepare_playback_returns = 
191         snd_pcm_playback_prepare ( p_aout->p_sys->p_alsa_handle );
192
193     if ( i_prepare_playback_returns )
194     {
195         intf_ErrMsg ( "ALSA_PLUGIN : Unable to prepare channel : exit = %i\n",
196                 i_prepare_playback_returns );
197         intf_ErrMsg( "This means : %s\n\n",
198                 snd_strerror( i_set_param_returns ) );
199
200         return ( 1 );
201     }
202     
203     /* then we may go */
204    i_playback_go_returns =
205        snd_pcm_playback_go ( p_aout->p_sys->p_alsa_handle );
206     if ( i_playback_go_returns )
207     {
208         intf_ErrMsg ( "ALSA_PLUGIN : Unable to prepare channel (bis) : 
209                 exit  = %i\n", i_playback_go_returns );
210         intf_ErrMsg( "This means : %s\n\n",
211                 snd_strerror( i_set_param_returns ) );
212         return ( 1 );
213     }
214     return ( 0 );
215 }
216
217 /*****************************************************************************
218  * aout_AlsaReset: resets the dsp
219  *****************************************************************************/
220 int aout_AlsaReset ( aout_thread_t *p_aout )
221 {
222     /* TODO : put something in here, such as close and open again 
223      * or check status, drain, flush, .... */ 
224     return ( 0 );
225 }
226
227 /*****************************************************************************
228  * aout_AlsaSetChannels: sets mono, stereo and other modes
229  *****************************************************************************/
230 int aout_AlsaSetChannels ( aout_thread_t *p_aout )
231 {
232     /* TODO : normally, nothing
233      * everything should be done in the AlsaSetFormat, as far a I understand
234      * the alsa documentation
235      */
236     return ( 0 );
237 }
238
239 /*****************************************************************************
240  * aout_AlsaSetRate: sets the audio output rate
241  *****************************************************************************
242  * As in the previous function, the rate is supposed to be set in the
243  * AlsaSetFormat function
244  *****************************************************************************/
245 int aout_AlsaSetRate ( aout_thread_t *p_aout )
246 {
247     return ( 0 );
248 }
249
250 /*****************************************************************************
251  * aout_AlsaGetBufInfo: buffer status query
252  *****************************************************************************/
253 long aout_AlsaGetBufInfo ( aout_thread_t *p_aout, long l_buffer_limit )
254 {
255     snd_pcm_channel_status_t alsa_channel_status;
256     int i_alsa_get_status_returns;
257     
258     memset (&alsa_channel_status, 0, sizeof(alsa_channel_status));
259     i_alsa_get_status_returns = snd_pcm_channel_status ( 
260             p_aout->p_sys->p_alsa_handle, &alsa_channel_status );
261
262     if ( i_alsa_get_status_returns )
263     {
264         intf_ErrMsg ( "Error getting alsa buffer info; exit=%i\n",
265                 i_alsa_get_status_returns );
266         intf_ErrMsg ( "This means : %s \n\n",
267                 snd_strerror ( i_alsa_get_status_returns ) );
268         return ( 1 );
269     }
270
271     switch (alsa_channel_status.status)
272     {
273         case SND_PCM_STATUS_NOTREADY : intf_ErrMsg("Status NOT READY \n \n");
274                                        break;
275         case SND_PCM_STATUS_UNDERRUN : {
276                                        int i_drain_returns;
277                                        intf_ErrMsg(
278                                   "Status UNDERRUN ... draining \n \n");
279                                        i_drain_returns = 
280                                            snd_pcm_playback_prepare(
281                                                p_aout->p_sys->p_alsa_handle );
282                                        if ( i_drain_returns )
283                                        {
284                                            intf_ErrMsg(
285                                   "Error : could not flush : %i\n",
286                                   i_drain_returns);
287                                            intf_ErrMsg(
288                                   "This means : %s\n",
289                                   snd_strerror(i_drain_returns));
290                                        }
291                                        break;
292                                        }
293     } 
294     return (  alsa_channel_status.count );
295 }
296
297 /*****************************************************************************
298  * aout_AlsaPlaySamples
299  *****************************************************************************/
300 void aout_AlsaPlaySamples ( aout_thread_t *p_aout, byte_t *buffer, int i_size )
301 {
302     int i_write_returns;
303
304     i_write_returns = (int) snd_pcm_write (
305             p_aout->p_sys->p_alsa_handle, (void *)buffer, (size_t) i_size );
306
307     if ( i_write_returns <= 0 )
308     {
309         intf_ErrMsg ( "Error writing blocks; exit=%i\n", i_write_returns );
310         intf_ErrMsg ( "This means : %s\n", snd_strerror( i_write_returns ) );
311     }
312 }
313
314 /*****************************************************************************
315  * aout_AlsaClose : close the Alsa device
316  *****************************************************************************/
317 void aout_AlsaClose ( aout_thread_t *p_aout )
318 {
319     int i_close_returns;
320
321     i_close_returns = snd_pcm_close ( p_aout->p_sys->p_alsa_handle );
322
323     if ( i_close_returns )
324     {
325         intf_ErrMsg( "Error closing alsa device; exit=%i\n",i_close_returns );
326         intf_ErrMsg( "This means : %s\n\n",snd_strerror( i_close_returns ) );
327     }
328 }