Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

aout.c

00001 /*****************************************************************************
00002  * aout.c : QNX audio output
00003  *****************************************************************************
00004  * Copyright (C) 2000, 2001 the VideoLAN team
00005  *
00006  * Authors: Henri Fallon <[email protected]>
00007  *          Jon Lech Johansen <[email protected]>
00008  *          Pascal Levesque <[email protected]>
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00023  *****************************************************************************/
00024 
00025 /*****************************************************************************
00026  * Preamble
00027  *****************************************************************************/
00028 #include <errno.h>                                                 /* ENOMEM */
00029 #include <string.h>                                            /* strerror() */
00030 #include <stdlib.h>                            /* calloc(), malloc(), free() */
00031 
00032 #include <vlc/vlc.h>
00033 
00034 #ifdef HAVE_ALLOCA_H
00035 #   include <alloca.h>
00036 #endif
00037 
00038 #include <vlc/aout.h>
00039 #include "aout_internal.h"
00040 
00041 #include <sys/asoundlib.h>
00042 
00043 struct aout_sys_t
00044 {
00045     snd_pcm_t  * p_pcm_handle;
00046     int          i_card;
00047     int          i_device;
00048 
00049     byte_t *     p_silent_buffer;
00050 };
00051 
00052 #define DEFAULT_FRAME_SIZE 2048
00053 
00054 /*****************************************************************************
00055  * Local prototypes
00056  *****************************************************************************/
00057 int            E_(OpenAudio)    ( vlc_object_t *p_this );
00058 void           E_(CloseAudio)   ( vlc_object_t *p_this );
00059 static int     GetBufInfo       ( aout_instance_t * );
00060 static void    Play             ( aout_instance_t * );
00061 static int     QNXaoutThread    ( aout_instance_t * );
00062 
00063 /*****************************************************************************
00064  * Open : creates a handle and opens an alsa device
00065  *****************************************************************************
00066  * This function opens an alsa device, through the alsa API
00067  *****************************************************************************/
00068 int E_(OpenAudio)( vlc_object_t *p_this )
00069 {
00070     aout_instance_t *p_aout = (aout_instance_t *)p_this;
00071     int i_ret;
00072     int i_bytes_per_sample;
00073     int i_nb_channels;
00074     snd_pcm_channel_info_t pi;
00075     snd_pcm_channel_params_t pp;
00076     aout_instance_t *p_aout = (aout_instance_t *)p_this;
00077 
00078     /* allocate structure */
00079     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
00080     if( p_aout->output.p_sys == NULL )
00081     {
00082         msg_Err( p_aout, "out of memory" );
00083         return -1;
00084     }
00085 
00086     /* open audio device */
00087     if( ( i_ret = snd_pcm_open_preferred( &p_aout->output.p_sys->p_pcm_handle,
00088                                           &p_aout->output.p_sys->i_card,
00089                                           &p_aout->output.p_sys->i_device,
00090                                           SND_PCM_OPEN_PLAYBACK ) ) < 0 )
00091     {
00092         msg_Err( p_aout, "unable to open audio device (%s)",
00093                          snd_strerror( i_ret ) );
00094         free( p_aout->output.p_sys );
00095         return -1;
00096     }
00097 
00098     /* disable mmap */
00099     if( ( i_ret = snd_pcm_plugin_set_disable( p_aout->output.p_sys->p_pcm_handle,
00100                                               PLUGIN_DISABLE_MMAP ) ) < 0 )
00101     {
00102         msg_Err( p_aout, "unable to disable mmap (%s)", snd_strerror(i_ret) );
00103         E_(CloseAudio)( p_this );
00104         free( p_aout->output.p_sys );
00105         return -1;
00106     }
00107 
00108     p_aout->output.p_sys->p_silent_buffer = malloc( DEFAULT_FRAME_SIZE * 4 );
00109     p_aout->output.pf_play = Play;
00110     aout_VolumeSoftInit( p_aout );
00111 
00112     memset( &pi, 0, sizeof(pi) );
00113     memset( &pp, 0, sizeof(pp) );
00114 
00115     pi.channel = SND_PCM_CHANNEL_PLAYBACK;
00116     if( ( i_ret = snd_pcm_plugin_info( p_aout->output.p_sys->p_pcm_handle,
00117                                        &pi ) ) < 0 )
00118     {
00119         msg_Err( p_aout, "unable to get plugin info (%s)",
00120                          snd_strerror( i_ret ) );
00121         E_(CloseAudio)( p_this );
00122         free( p_aout->output.p_sys );
00123         return -1;
00124     }
00125 
00126     pp.mode       = SND_PCM_MODE_BLOCK;
00127     pp.channel    = SND_PCM_CHANNEL_PLAYBACK;
00128     pp.start_mode = SND_PCM_START_FULL;
00129     pp.stop_mode  = SND_PCM_STOP_STOP;
00130 
00131     pp.buf.block.frags_max   = 3;
00132     pp.buf.block.frags_min   = 1;
00133 
00134     pp.format.interleave     = 1;
00135     pp.format.rate           = p_aout->output.output.i_rate;
00136 
00137     i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
00138     if ( i_nb_channels > 2 )
00139     {
00140         /* I don't know if QNX supports more than two channels. */
00141         i_nb_channels = 2;
00142         p_aout->output.output.i_channels = AOUT_CHAN_STEREO;
00143     }
00144     pp.format.voices         = i_nb_channels;
00145 
00146     p_aout->output.output.i_format = AOUT_FMT_S16_NE;
00147     p_aout->output.i_nb_samples = DEFAULT_FRAME_SIZE;
00148     pp.format.format = SND_PCM_SFMT_S16;
00149     i_bytes_per_sample = 2;
00150 
00151     pp.buf.block.frag_size = p_aout->output.i_nb_samples *
00152                             p_aout->output.output.i_channels *
00153                             i_bytes_per_sample;
00154 
00155     /* set parameters */
00156     if( ( i_ret = snd_pcm_plugin_params( p_aout->output.p_sys->p_pcm_handle,
00157                                          &pp ) ) < 0 )
00158     {
00159         msg_Err( p_aout, "unable to set parameters (%s)", snd_strerror(i_ret) );
00160         E_(CloseAudio)( p_this );
00161         free( p_aout->output.p_sys );
00162         return -1;
00163     }
00164 
00165     /* prepare channel */
00166     if( ( i_ret = snd_pcm_plugin_prepare( p_aout->output.p_sys->p_pcm_handle,
00167                                           SND_PCM_CHANNEL_PLAYBACK ) ) < 0 )
00168     {
00169         msg_Err( p_aout, "unable to prepare channel (%s)",
00170                          snd_strerror( i_ret ) );
00171         E_(CloseAudio)( p_this );
00172         free( p_aout->output.p_sys );
00173         return -1;
00174     }
00175 
00176     /* Create audio thread and wait for its readiness. */
00177     if( vlc_thread_create( p_aout, "aout", QNXaoutThread,
00178                            VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
00179     {
00180         msg_Err( p_aout, "cannot create QNX audio thread (%s)", strerror(errno) );
00181         E_(CloseAudio)( p_this );
00182         free( p_aout->output.p_sys );
00183         return -1;
00184     }
00185 
00186     return( 0 );
00187 }
00188 
00189 /*****************************************************************************
00190  * GetBufInfo: buffer status query
00191  *****************************************************************************
00192  * This function returns the number of used byte in the queue.
00193  * It also deals with errors : indeed if the device comes to run out
00194  * of data to play, it switches to the "underrun" status. It has to
00195  * be flushed and re-prepared
00196  *****************************************************************************/
00197 static int GetBufInfo( aout_instance_t *p_aout )
00198 {
00199     int i_ret;
00200     snd_pcm_channel_status_t status;
00201 
00202     /* get current pcm status */
00203     memset( &status, 0, sizeof(status) );
00204     if( ( i_ret = snd_pcm_plugin_status( p_aout->output.p_sys->p_pcm_handle,
00205                                          &status ) ) < 0 )
00206     {
00207         msg_Err( p_aout, "unable to get device status (%s)",
00208                          snd_strerror( i_ret ) );
00209         return( -1 );
00210     }
00211 
00212     /* check for underrun */
00213     switch( status.status )
00214     {
00215         case SND_PCM_STATUS_READY:
00216         case SND_PCM_STATUS_UNDERRUN:
00217             if( ( i_ret = snd_pcm_plugin_prepare( p_aout->output.p_sys->p_pcm_handle,
00218                                           SND_PCM_CHANNEL_PLAYBACK ) ) < 0 )
00219             {
00220                 msg_Err( p_aout, "unable to prepare channel (%s)",
00221                                  snd_strerror( i_ret ) );
00222             }
00223             break;
00224     }
00225 
00226     return( status.count );
00227 }
00228 
00229 /*****************************************************************************
00230  * Play : plays a sample
00231  *****************************************************************************
00232  * Plays a sample using the snd_pcm_write function from the alsa API
00233  *****************************************************************************/
00234 static void Play( aout_instance_t *p_aout )
00235 {
00236 }
00237 
00238 /*****************************************************************************
00239  * CloseAudio: close the audio device
00240  *****************************************************************************/
00241 void E_(CloseAudio) ( vlc_object_t *p_this )
00242 {
00243     aout_instance_t *p_aout = (aout_instance_t *)p_this;
00244     int i_ret;
00245 
00246     p_aout->b_die = 1;
00247     vlc_thread_join( p_aout );
00248 
00249     if( ( i_ret = snd_pcm_close( p_aout->output.p_sys->p_pcm_handle ) ) < 0 )
00250     {
00251         msg_Err( p_aout, "unable to close audio device (%s)",
00252                          snd_strerror( i_ret ) );
00253     }
00254 
00255     free( p_aout->output.p_sys->p_silent_buffer );
00256     free( p_aout->output.p_sys );
00257 }
00258 
00259 
00260 /*****************************************************************************
00261  * QNXaoutThread: asynchronous thread used to DMA the data to the device
00262  *****************************************************************************/
00263 static int QNXaoutThread( aout_instance_t * p_aout )
00264 {
00265     struct aout_sys_t * p_sys = p_aout->output.p_sys;
00266 
00267     while ( !p_aout->b_die )
00268     {
00269         aout_buffer_t * p_buffer;
00270         int i_tmp, i_size;
00271         byte_t * p_bytes;
00272 
00273         if ( p_aout->output.output.i_format != VLC_FOURCC('s','p','d','i') )
00274         {
00275             mtime_t next_date = 0;
00276 
00277             /* Get the presentation date of the next write() operation. It
00278              * is equal to the current date + duration of buffered samples.
00279              * Order is important here, since GetBufInfo is believed to take
00280              * more time than mdate(). */
00281             next_date = (mtime_t)GetBufInfo( p_aout ) * 1000000
00282                       / p_aout->output.output.i_bytes_per_frame
00283                       / p_aout->output.output.i_rate
00284                       * p_aout->output.output.i_frame_length;
00285             next_date += mdate();
00286 
00287             p_buffer = aout_OutputNextBuffer( p_aout, next_date, VLC_FALSE );
00288         }
00289         else
00290         {
00291             p_buffer = aout_OutputNextBuffer( p_aout, 0, VLC_TRUE );
00292         }
00293 
00294         if ( p_buffer != NULL )
00295         {
00296             p_bytes = p_buffer->p_buffer;
00297             i_size = p_buffer->i_nb_bytes;
00298         }
00299         else
00300         {
00301             i_size = DEFAULT_FRAME_SIZE / p_aout->output.output.i_frame_length
00302                       * p_aout->output.output.i_bytes_per_frame;
00303             p_bytes = p_aout->output.p_sys->p_silent_buffer;
00304             memset( p_bytes, 0, i_size );
00305         }
00306 
00307         i_tmp = snd_pcm_plugin_write( p_aout->output.p_sys->p_pcm_handle,
00308                                         (void *) p_bytes,
00309                                         (size_t) i_size );
00310 
00311         if( i_tmp < 0 )
00312         {
00313             msg_Err( p_aout, "write failed (%s)", strerror(errno) );
00314         }
00315 
00316         if ( p_buffer != NULL )
00317         {
00318             aout_BufferFree( p_buffer );
00319         }
00320     }
00321 
00322     return 0;
00323 }
00324 

Generated on Tue Dec 20 10:14:41 2005 for vlc-0.8.4a by  doxygen 1.4.2