]> git.sesse.net Git - vlc/blob - plugins/qnx/aout_qnx.c
-Fixed ac3_spdif which has been broken recently,
[vlc] / plugins / qnx / aout_qnx.c
1 /*****************************************************************************
2  * aout_qnx.c : QNX audio output 
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  *
6  * Authors: Henri Fallon <henri@videolan.org>
7  *          Jon Lech Johansen <jon-vl@nanocrew.net>
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 #include <errno.h>                                                 /* ENOMEM */
28 #include <string.h>                                            /* strerror() */
29 #include <stdio.h>                                           /* "intf_msg.h" */
30 #include <stdlib.h>                            /* calloc(), malloc(), free() */
31
32 #include <sys/asoundlib.h>
33
34 #include <videolan/vlc.h>
35
36 #include "audio_output.h"                                   /* aout_thread_t */
37
38 typedef struct aout_sys_s
39 {
40     snd_pcm_t  * p_pcm_handle;
41     int          i_card;
42     int          i_device;
43 } aout_sys_t;
44
45 /*****************************************************************************
46  * Local prototypes
47  *****************************************************************************/
48 static int     aout_Probe       ( probedata_t *p_data );
49 static int     aout_Open        ( aout_thread_t *p_aout );
50 static int     aout_SetFormat   ( aout_thread_t *p_aout );
51 static long    aout_GetBufInfo  ( aout_thread_t *p_aout, long l_buffer_info );
52 static void    aout_Play        ( aout_thread_t *p_aout,
53                                   byte_t *buffer, int i_size );
54 static void    aout_Close       ( aout_thread_t *p_aout );
55
56 /*****************************************************************************
57  * Functions exported as capabilities. They are declared as static so that
58  * we don't pollute the namespace too much.
59  *****************************************************************************/
60 void _M( aout_getfunctions )( function_list_t * p_function_list )
61 {
62     p_function_list->pf_probe = aout_Probe;
63     p_function_list->functions.aout.pf_open = aout_Open;
64     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
65     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
66     p_function_list->functions.aout.pf_play = aout_Play;
67     p_function_list->functions.aout.pf_close = aout_Close;
68 }
69
70 /*****************************************************************************
71  * aout_Probe: probes the audio device and return a score
72  *****************************************************************************
73  * This function tries to open the dps and returns a score to the plugin
74  * manager so that it can make its choice.
75  *****************************************************************************/
76 static int aout_Probe( probedata_t *p_data )
77 {
78     int i_ret;
79     aout_sys_t adev;
80
81     /* open audio device */
82     if( ( i_ret = snd_pcm_open_preferred( &adev.p_pcm_handle,
83                                           &adev.i_card, &adev.i_device,
84                                           SND_PCM_OPEN_PLAYBACK ) ) < 0 )
85     {
86         intf_WarnMsg( 2, "aout error: unable to open audio device (%s)",
87                       snd_strerror( i_ret ) );
88         return( 0 );
89     }
90
91     /* close audio device */
92     if( ( i_ret = snd_pcm_close( adev.p_pcm_handle ) ) < 0 )
93     {
94         intf_WarnMsg( 2, "aout error: unable to close audio device (%s)",
95                       snd_strerror( i_ret ) );
96         return( 0 );
97     }
98
99     /* return score */
100     return( 50 );
101 }    
102
103 /*****************************************************************************
104  * aout_Open : creates a handle and opens an alsa device
105  *****************************************************************************
106  * This function opens an alsa device, through the alsa API
107  *****************************************************************************/
108 static int aout_Open( aout_thread_t *p_aout )
109 {
110     int i_ret;
111
112     /* allocate structure */
113     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
114     if( p_aout->p_sys == NULL )
115     {
116         intf_ErrMsg( "aout error: unable to allocate memory (%s)",
117                      strerror( ENOMEM ) );
118         return( 1 );
119     }
120
121     /* open audio device */
122     if( ( i_ret = snd_pcm_open_preferred( &p_aout->p_sys->p_pcm_handle,
123                                           &p_aout->p_sys->i_card,
124                                           &p_aout->p_sys->i_device,
125                                           SND_PCM_OPEN_PLAYBACK ) ) < 0 )
126     {
127         intf_ErrMsg( "aout error: unable to open audio device (%s)",
128                       snd_strerror( i_ret ) );
129         return( 1 );
130     }
131
132     /* disable mmap */
133     if( ( i_ret = snd_pcm_plugin_set_disable( p_aout->p_sys->p_pcm_handle,
134                                               PLUGIN_DISABLE_MMAP ) ) < 0 )
135     {
136         intf_ErrMsg( "aout error: unable to disable mmap (%s)",
137                      snd_strerror( i_ret ) );
138         aout_Close( p_aout );
139         return( 1 );
140     }
141
142     return( 0 );
143 }
144
145
146 /*****************************************************************************
147  * aout_SetFormat : set the audio output format 
148  *****************************************************************************
149  * This function prepares the device, sets the rate, format, the mode
150  * ("play as soon as you have data"), and buffer information.
151  *****************************************************************************/
152 static int aout_SetFormat( aout_thread_t *p_aout )
153 {
154     int i_ret;
155     int i_bytes_per_sample;
156     snd_pcm_channel_info_t pi;
157     snd_pcm_channel_params_t pp;
158
159     memset( &pi, 0, sizeof(pi) );
160     memset( &pp, 0, sizeof(pp) );
161
162     pi.channel = SND_PCM_CHANNEL_PLAYBACK;
163     if( ( i_ret = snd_pcm_plugin_info( p_aout->p_sys->p_pcm_handle,
164                                        &pi ) ) < 0 )
165     {
166         intf_ErrMsg( "aout error: unable to get plugin info (%s)",
167                      snd_strerror( i_ret ) );
168         return( 1 );
169     }
170
171     pp.mode       = SND_PCM_MODE_BLOCK;
172     pp.channel    = SND_PCM_CHANNEL_PLAYBACK;
173     pp.start_mode = SND_PCM_START_FULL;
174     pp.stop_mode  = SND_PCM_STOP_STOP;
175
176     pp.buf.block.frags_max   = 1;
177     pp.buf.block.frags_min   = 1;
178     
179     pp.format.interleave     = 1;
180     pp.format.rate           = p_aout->l_rate;
181     pp.format.voices         = p_aout->i_channels;
182
183     switch( p_aout->i_format )
184     {
185         case AOUT_FMT_S16_LE:
186             pp.format.format = SND_PCM_SFMT_S16_LE;
187             i_bytes_per_sample = 2;
188             break;
189
190         default:
191             pp.format.format = SND_PCM_SFMT_S16_BE;
192             i_bytes_per_sample = 2;
193             break;
194     }
195
196     pp.buf.block.frag_size =
197         (((s64)p_aout->l_rate * AOUT_BUFFER_DURATION) / 1000000) *
198         p_aout->i_channels * i_bytes_per_sample;
199
200     /* set parameters */
201     if( ( i_ret = snd_pcm_plugin_params( p_aout->p_sys->p_pcm_handle,
202                                          &pp ) ) < 0 )
203     {
204         intf_ErrMsg( "aout error: unable to set parameters (%s)",
205                      snd_strerror( i_ret ) );
206         return( 1 );
207     }
208
209     /* prepare channel */
210     if( ( i_ret = snd_pcm_plugin_prepare( p_aout->p_sys->p_pcm_handle,
211                                           SND_PCM_CHANNEL_PLAYBACK ) ) < 0 )
212     {
213         intf_ErrMsg( "aout error: unable to prepare channel (%s)",
214                      snd_strerror( i_ret ) );
215         return( 1 );
216     }
217
218     return( 0 );
219 }
220
221 /*****************************************************************************
222  * aout_BufInfo: buffer status query
223  *****************************************************************************
224  * This function returns the number of used byte in the queue.
225  * It also deals with errors : indeed if the device comes to run out
226  * of data to play, it switches to the "underrun" status. It has to
227  * be flushed and re-prepared
228  *****************************************************************************/
229 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
230 {
231     int i_ret;
232     snd_pcm_channel_status_t status;
233
234     /* get current pcm status */
235     memset( &status, 0, sizeof(status) );
236     if( ( i_ret = snd_pcm_plugin_status( p_aout->p_sys->p_pcm_handle,
237                                          &status ) ) < 0 )
238     {
239         intf_ErrMsg( "aout error: unable to get device status (%s)",
240                      snd_strerror( i_ret ) );
241         return( -1 );
242     }
243
244     /* check for underrun */
245     switch( status.status )
246     {
247         case SND_PCM_STATUS_READY:
248         case SND_PCM_STATUS_UNDERRUN:
249             if( ( i_ret = snd_pcm_plugin_prepare( p_aout->p_sys->p_pcm_handle,
250                                           SND_PCM_CHANNEL_PLAYBACK ) ) < 0 )
251             {
252                 intf_ErrMsg( "aout error: unable to prepare channel (%s)",
253                              snd_strerror( i_ret ) );
254             }
255             break;
256     }
257
258     return( status.count );
259 }
260
261 /*****************************************************************************
262  * aout_Play : plays a sample
263  *****************************************************************************
264  * Plays a sample using the snd_pcm_write function from the alsa API
265  *****************************************************************************/
266 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
267 {
268     int i_ret;
269
270     if( ( i_ret = snd_pcm_plugin_write( p_aout->p_sys->p_pcm_handle,
271                                         (void *) buffer, 
272                                         (size_t) i_size ) ) <= 0 )
273     {
274         intf_ErrMsg( "aout error: unable to write data (%s)",
275                      snd_strerror( i_ret ) );
276     }
277 }
278
279 /*****************************************************************************
280  * aout_Close : close the audio device
281  *****************************************************************************/
282 static void aout_Close( aout_thread_t *p_aout )
283 {
284     int i_ret;
285
286     if( ( i_ret = snd_pcm_close( p_aout->p_sys->p_pcm_handle ) ) < 0 )
287     {
288         intf_ErrMsg( "aout error: unable to close audio device (%s)",
289                      snd_strerror( i_ret ) );
290     }
291
292     free( p_aout->p_sys );
293 }