]> git.sesse.net Git - vlc/blob - plugins/qnx/aout_qnx.c
* QNX RTOS plug-in by Jon Lech Johansen.
[vlc] / plugins / qnx / aout_qnx.c
1 /*****************************************************************************
2  * aout_qnx.c : Alsa functions library
3  *****************************************************************************
4  * Copyright (C) 2000 VideoLAN
5  *
6  * Authors: Henri Fallon <henri@videolan.org>
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  * 
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
21  *****************************************************************************/
22
23 #define MODULE_NAME qnx 
24 #include "modules_inner.h"
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29
30 #include "defs.h"
31
32 #include <errno.h>                                                 /* ENOMEM */
33 #include <string.h>                                            /* strerror() */
34 #include <stdio.h>                                           /* "intf_msg.h" */
35 #include <stdlib.h>                            /* calloc(), malloc(), free() */
36
37 #include <sys/asoundlib.h>
38
39 #include "config.h"
40 #include "common.h"                                     /* boolean_t, byte_t */
41 #include "threads.h"
42 #include "mtime.h"
43 #include "tests.h"
44
45 #include "audio_output.h"                                   /* aout_thread_t */
46
47 #include "intf_msg.h"                        /* intf_DbgMsg(), intf_ErrMsg() */
48 #include "main.h"
49
50 #include "modules.h"
51 #include "modules_export.h"
52
53 typedef struct alsa_device_s
54 {
55     int i_num;
56 } alsa_device_t;
57
58 typedef struct alsa_card_s
59 {
60     int i_num;
61 } alsa_card_t;
62
63 /* here we store plugin dependant informations */
64
65 typedef struct aout_sys_s
66 {
67     snd_pcm_t                * p_alsa_handle;
68     alsa_device_t              s_alsa_device;
69     alsa_card_t                s_alsa_card;
70     snd_pcm_channel_params_t   s_alsa_channel_params;
71     snd_pcm_format_t           s_alsa_format;
72 } aout_sys_t;
73
74 /*****************************************************************************
75  * Local prototypes
76  *****************************************************************************/
77 static int     aout_Probe       ( probedata_t *p_data );
78 static int     aout_Open        ( aout_thread_t *p_aout );
79 static int     aout_SetFormat   ( aout_thread_t *p_aout );
80 static long    aout_GetBufInfo  ( aout_thread_t *p_aout, long l_buffer_info );
81 static void    aout_Play        ( aout_thread_t *p_aout,
82                                   byte_t *buffer, int i_size );
83 static void    aout_Close       ( aout_thread_t *p_aout );
84
85 /*****************************************************************************
86  * Functions exported as capabilities. They are declared as static so that
87  * we don't pollute the namespace too much.
88  *****************************************************************************/
89 void _M( aout_getfunctions )( function_list_t * p_function_list )
90 {
91     p_function_list->pf_probe = aout_Probe;
92     p_function_list->functions.aout.pf_open = aout_Open;
93     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
94     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
95     p_function_list->functions.aout.pf_play = aout_Play;
96     p_function_list->functions.aout.pf_close = aout_Close;
97 }
98
99 /*****************************************************************************
100  * aout_Probe: probes the audio device and return a score
101  *****************************************************************************
102  * This function tries to open the dps and returns a score to the plugin
103  * manager so that it can make its choice.
104  *****************************************************************************/
105 static int aout_Probe( probedata_t *p_data )
106 {
107     int i_open_return, i_close_return;
108     aout_sys_t local_sys;
109     /* This is the same as the beginning of the aout_Open */
110     
111     /* Initialize  */
112     local_sys.s_alsa_device.i_num = 0;
113     local_sys.s_alsa_card.i_num = 0;
114
115     /* Open device */
116     i_open_return = snd_pcm_open( &(local_sys.p_alsa_handle),
117                      local_sys.s_alsa_card.i_num,
118                      local_sys.s_alsa_device.i_num,
119                      SND_PCM_OPEN_PLAYBACK );
120     if( i_open_return )
121     {
122         intf_WarnMsg( 2, "aout info: could not probe ALSA device (%s)",
123               snd_strerror( i_open_return ) );
124         return ( 0 );
125     }
126
127     /* Close it */
128     i_close_return = snd_pcm_close( local_sys.p_alsa_handle );
129     
130     if( i_close_return )
131     {
132         intf_ErrMsg( "aout error: could not close ALSA device (%s)",
133                      snd_strerror( i_close_return ) );
134         return( 0 );
135     }
136     
137     if( TestMethod( AOUT_METHOD_VAR, "qnx" ) )
138     {
139         return( 999 );
140     }
141
142     /* And return score */
143     return( 50 );
144 }    
145
146 /*****************************************************************************
147  * aout_Open : creates a handle and opens an alsa device
148  *****************************************************************************
149  * This function opens an alsa device, through the alsa API
150  *****************************************************************************/
151 static int aout_Open( aout_thread_t *p_aout )
152 {
153     int i_open_returns;
154     
155     /* Allocate structures */
156     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
157     if( p_aout->p_sys == NULL )
158     {
159         intf_ErrMsg( "aout error: failed allocating memory for ALSA (%s)",
160                      strerror(ENOMEM) );
161         return( 1 );
162     }
163
164     /* Initialize  */
165     p_aout->p_sys->s_alsa_device.i_num = 0;
166     p_aout->p_sys->s_alsa_card.i_num = 0;
167     /* FIXME : why not other format ? */
168     p_aout->i_format = AOUT_FMT_S16_LE;   
169     /* FIXME : why always 2 channels ?*/
170     p_aout->i_channels = 2;
171     p_aout->l_rate = main_GetIntVariable( AOUT_RATE_VAR, AOUT_RATE_DEFAULT );
172     
173     /* Open device */
174     if( ( i_open_returns = snd_pcm_open( &(p_aout->p_sys->p_alsa_handle),
175                 p_aout->p_sys->s_alsa_card.i_num,
176                 p_aout->p_sys->s_alsa_device.i_num,
177                 SND_PCM_OPEN_PLAYBACK ) ) )
178     {
179         intf_ErrMsg( "aout error: could not open ALSA device (%s)",
180              snd_strerror(i_open_returns) );
181         return( -1 );
182     }
183
184     intf_DbgMsg( "aout info: ALSA device successfully opened" );
185     return( 0 );
186 }
187
188
189 /*****************************************************************************
190  * aout_SetFormat : sets the alsa output format
191  *****************************************************************************
192  * This function prepares the device, sets the rate, format, the mode
193  * ("play as soon as you have data"), and buffer information.
194  *****************************************************************************/
195 static int aout_SetFormat( aout_thread_t *p_aout )
196 {
197     
198     int i_set_param_returns;
199     int i_prepare_playback_returns;
200     int i_playback_go_returns;
201
202     /* Fill with zeros */
203     memset( &p_aout->p_sys->s_alsa_channel_params, 0,
204             sizeof( p_aout->p_sys->s_alsa_channel_params ) );
205     
206     /* Fill the s_alsa_channel_params structure */
207
208     /* Tranfer mode and direction*/    
209     p_aout->p_sys->s_alsa_channel_params.channel = SND_PCM_CHANNEL_PLAYBACK ;
210     p_aout->p_sys->s_alsa_channel_params.mode = SND_PCM_MODE_STREAM;
211     
212     /* Format and rate */
213     p_aout->p_sys->s_alsa_channel_params.format.interleave = 1;
214     if( p_aout->i_format == AOUT_FMT_S16_LE )
215     {
216         p_aout->p_sys->s_alsa_channel_params.format.format = 
217             SND_PCM_SFMT_S16_LE;
218     }
219     else
220     {
221         p_aout->p_sys->s_alsa_channel_params.format.format = 
222             SND_PCM_SFMT_S16_BE;
223     }
224     p_aout->p_sys->s_alsa_channel_params.format.rate = p_aout->l_rate;
225     p_aout->p_sys->s_alsa_channel_params.format.voices = p_aout->i_channels ;
226     
227     /* When to start playing and when to stop */
228     p_aout->p_sys->s_alsa_channel_params.start_mode = SND_PCM_START_DATA;
229     p_aout->p_sys->s_alsa_channel_params.stop_mode = SND_PCM_STOP_STOP;
230
231     /* Buffer information . I have chosen the stream mode here
232      * instead of the block mode. I don't know whether i'm wrong 
233      * but it seemed more logical */
234     /* TODO : find the best value to put here. Probably depending
235      * on many parameters */
236     p_aout->p_sys->s_alsa_channel_params.buf.stream.queue_size = 131072; 
237     
238     p_aout->p_sys->s_alsa_channel_params.buf.stream.fill = SND_PCM_FILL_NONE ;
239     p_aout->p_sys->s_alsa_channel_params.buf.stream.max_fill = 0 ; 
240   
241     /* Now we pass this to the driver */
242     i_set_param_returns = snd_pcm_channel_params( 
243             p_aout->p_sys->p_alsa_handle, 
244             &(p_aout->p_sys->s_alsa_channel_params) );
245     
246     if( i_set_param_returns )
247     {
248         intf_ErrMsg( "aout error: unable to set parameters (%s)",
249                      snd_strerror( i_set_param_returns ) );
250         return( -1 );
251     }
252
253     /* we shall now prepare the channel */
254     i_prepare_playback_returns = 
255         snd_pcm_playback_prepare( p_aout->p_sys->p_alsa_handle );
256
257     if( i_prepare_playback_returns )
258     {
259         intf_ErrMsg( "aout error: unable to prepare channel (%s)",
260                       snd_strerror( i_set_param_returns ) );
261         return( -1 );
262     }
263     
264    /* then we may go */
265    i_playback_go_returns =
266        snd_pcm_playback_go( p_aout->p_sys->p_alsa_handle );
267     if( i_playback_go_returns )
268     {
269         intf_ErrMsg( "aout error: unable to prepare channel (bis) (%s)",
270                      snd_strerror( i_set_param_returns ) );
271         return( -1 );
272     }
273
274     p_aout->i_latency = 0;
275
276     return( 0 );
277 }
278
279 /*****************************************************************************
280  * aout_BufInfo: buffer status query
281  *****************************************************************************
282  * This function returns the number of used byte in the queue.
283  * It also deals with errors : indeed if the device comes to run out
284  * of data to play, it switches to the "underrun" status. It has to
285  * be flushed and re-prepared
286  *****************************************************************************/
287 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
288 {
289     snd_pcm_channel_status_t alsa_channel_status;
290     int i_alsa_get_status_returns;
291     
292     memset( &alsa_channel_status, 0, sizeof( alsa_channel_status ) );
293    
294     i_alsa_get_status_returns = snd_pcm_channel_status( 
295             p_aout->p_sys->p_alsa_handle, &alsa_channel_status );
296
297     if( i_alsa_get_status_returns )
298     {
299         intf_ErrMsg( "aout error: failed getting alsa buffer info (%s)",
300                      snd_strerror ( i_alsa_get_status_returns ) );
301         return( -1 );
302     }
303
304     switch( alsa_channel_status.status )
305     {
306         case SND_PCM_STATUS_NOTREADY:
307         { 
308             intf_ErrMsg( "aout error: status NOT READY" );
309             break;
310         }
311     
312         case SND_PCM_STATUS_UNDERRUN:
313         {
314             int i_prepare_returns;
315             intf_ErrMsg( "aout error: status UNDERRUN ... resetting queue ");
316             i_prepare_returns = snd_pcm_playback_prepare( 
317                              p_aout->p_sys->p_alsa_handle );
318             if ( i_prepare_returns )
319             {
320                 intf_ErrMsg( "aout error: could not flush (%s)", 
321                              snd_strerror(i_prepare_returns) );
322             }
323             break;
324         }
325     } 
326
327     return( alsa_channel_status.count );
328 }
329
330 /*****************************************************************************
331  * aout_Play : plays a sample
332  *****************************************************************************
333  * Plays a sample using the snd_pcm_write function from the alsa API
334  *****************************************************************************/
335 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
336 {
337     int i_write_returns;
338
339     i_write_returns = (int) snd_pcm_write (
340             p_aout->p_sys->p_alsa_handle, (void *) buffer, (size_t) i_size );
341
342     if( i_write_returns <= 0 )
343     {
344         intf_ErrMsg( "aout error: writing blocks failed (%s)",
345                      snd_strerror( i_write_returns ) );
346     }
347 }
348
349 /*****************************************************************************
350  * aout_Close : close the Alsa device
351  *****************************************************************************/
352 static void aout_Close( aout_thread_t *p_aout )
353 {
354     int i_close_returns;
355
356     i_close_returns = snd_pcm_close( p_aout->p_sys->p_alsa_handle );
357
358     if( i_close_returns )
359     {
360         intf_ErrMsg( "aout error: failed closing ALSA device (%s)",
361                      snd_strerror( i_close_returns ) );
362     }
363     free( p_aout->p_sys );
364
365     intf_DbgMsg( "aout: ALSA device closed" );    
366 }
367