aout_sdl.c 12.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
/*****************************************************************************
 * aout_sdl.c : audio sdl functions library
 *****************************************************************************
 * Copyright (C) 1999, 2000 VideoLAN
 *
 * Authors: Michel Kaempf <maxx@via.ecp.fr>
 *          Samuel Hocevar <sam@zoy.org>
 *          Pierre Baillet <oct@zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include "defs.h"

#include <errno.h>                                                 /* ENOMEM */
#include <fcntl.h>                                       /* open(), O_WRONLY */
#include <sys/ioctl.h>                                            /* ioctl() */
#include <string.h>                                            /* strerror() */
#include <unistd.h>                                      /* write(), close() */
#include <stdio.h>                                           /* "intf_msg.h" */
#include <stdlib.h>                            /* calloc(), malloc(), free() */


#include "SDL/SDL.h"                                         /* SDL base include */

#include "config.h"
#include "common.h"                                     /* boolean_t, byte_t */
#include "threads.h"
#include "mtime.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
45
#include "tests.h"
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

#include "audio_output.h"                                   /* aout_thread_t */

#include "intf_msg.h"                        /* intf_DbgMsg(), intf_ErrMsg() */
#include "main.h"

#include "modules.h"


/*****************************************************************************
 * aout_sys_t: dsp audio output method descriptor
 *****************************************************************************
 * This structure is part of the audio output thread descriptor.
 * It describes the dsp specific properties of an audio device.
 *****************************************************************************/

/* the overflow limit is used to prevent the fifo from growing too big */
#define OVERFLOWLIMIT 100000

typedef struct aout_sys_s
{
    byte_t  * audio_buf;
    int i_audio_end;
Pierre Baillet's avatar
Pierre Baillet committed
69
    boolean_t b_active;
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
} aout_sys_t;


/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
static int     aout_Probe       ( probedata_t *p_data );
static int     aout_Open        ( aout_thread_t *p_aout );
static int     aout_SetFormat   ( aout_thread_t *p_aout );
static long    aout_GetBufInfo  ( aout_thread_t *p_aout, long l_buffer_info );
static void    aout_Play        ( aout_thread_t *p_aout,
                                  byte_t *buffer, int i_size );
static void    aout_Close       ( aout_thread_t *p_aout );
static void    SDL_aout_callback(void *userdata, Uint8 *stream, int len);

/*****************************************************************************
 * Functions exported as capabilities. They are declared as static so that
 * we don't pollute the namespace too much.
 *****************************************************************************/
void aout_getfunctions( function_list_t * p_function_list )
{
    p_function_list->pf_probe = aout_Probe;
    p_function_list->functions.aout.pf_open = aout_Open;
    p_function_list->functions.aout.pf_setformat = aout_SetFormat;
    p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
    p_function_list->functions.aout.pf_play = aout_Play;
    p_function_list->functions.aout.pf_close = aout_Close;
}

/*****************************************************************************
 * aout_Probe: probes the audio device and return a score
 *****************************************************************************
 * This function tries to open the dps and returns a score to the plugin
 * manager so that it can select the best plugin.
 *****************************************************************************/
static int aout_Probe( probedata_t *p_data )
{
    SDL_AudioSpec *desired, *obtained;

    /* Start AudioSDL */
    if( SDL_Init(SDL_INIT_AUDIO) != 0)
Pierre Baillet's avatar
Pierre Baillet committed
111 112 113 114
    {
        intf_DbgMsg( "aout_Probe: SDL init error: %s", SDL_GetError() );
        return( 0 );
    }
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
     
    /* asks for a minimum audio spec so that we are sure the dsp exists */
    desired = (SDL_AudioSpec *)malloc( sizeof(SDL_AudioSpec) );
    obtained = (SDL_AudioSpec *)malloc( sizeof(SDL_AudioSpec) );

    desired->freq       = 11025;                                /* frequency */
    desired->format     = AUDIO_U8;                       /* unsigned 8 bits */
    desired->channels   = 2;                                         /* mono */
    desired->callback   = SDL_aout_callback;     /* no callback function yet */
    desired->userdata   = NULL;                    /* null parm for callback */
    desired->samples    = 4096;
    
    
    /* If we were unable to open the device, there is no way we can use
     * the plugin. Return a score of 0. */
    if(SDL_OpenAudio( desired, obtained ) < 0)
    {
Pierre Baillet's avatar
Pierre Baillet committed
132 133 134
        free( desired );
        free( obtained );
        intf_DbgMsg( "aout_Probe: aout sdl error : %s", SDL_GetError() );
135 136
         return( 0 );
    }
Pierre Baillet's avatar
Pierre Baillet committed
137 138
    free( desired );
    free( obtained );
139 140 141

    /* Otherwise, there are good chances we can use this plugin, return 100. */
    SDL_CloseAudio();
Sam Hocevar's avatar
 
Sam Hocevar committed
142 143 144 145 146 147

    if( TestMethod( AOUT_METHOD_VAR, "sdl" ) )
    {
        return( 999 );
    }

Pierre Baillet's avatar
Pierre Baillet committed
148
    return( 50 );
149 150 151 152 153 154 155 156 157 158 159
}

/*****************************************************************************
 * aout_Open: opens the audio device (the digital sound processor)
 *****************************************************************************
 * This function opens the dsp as a usual non-blocking write-only file, and
 * modifies the p_aout->i_fd with the file's descriptor.
 *****************************************************************************/
static int aout_Open( aout_thread_t *p_aout )
{
    SDL_AudioSpec *desired;
Pierre Baillet's avatar
Pierre Baillet committed
160
    int i_channels = p_aout->b_stereo?2:1;
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175

    /* asks for a minimum audio spec so that we are sure the dsp exists */
    desired = (SDL_AudioSpec *)malloc( sizeof(SDL_AudioSpec) );

       /* Allocate structure */
    
    p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->p_sys == NULL )
    {
        intf_ErrMsg("aout_Open error: %s", strerror(ENOMEM) );
        return( 1 );
    }
    
   
    p_aout->p_sys->i_audio_end = 0;
176
    p_aout->p_sys->audio_buf = malloc( OVERFLOWLIMIT );
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
    
    /* Initialize some variables */
    p_aout->psz_device = 0;
    p_aout->i_format   = AOUT_FORMAT_DEFAULT;
    p_aout->i_channels = 1 + main_GetIntVariable( AOUT_STEREO_VAR,
                                                  AOUT_STEREO_DEFAULT );
    p_aout->l_rate     = main_GetIntVariable( AOUT_RATE_VAR,
                                              AOUT_RATE_DEFAULT );
    
    
    desired->freq =     p_aout->l_rate;

    /* TODO: write conversion beetween AOUT_FORMAT_DEFAULT 
     * AND AUDIO* from SDL. */
    desired->format     = AUDIO_S16LSB;                    /* stereo 16 bits */
Pierre Baillet's avatar
Pierre Baillet committed
192
    desired->channels    = i_channels;
193 194 195 196 197 198 199 200 201 202 203
    desired->callback   = SDL_aout_callback;
    desired->userdata   = p_aout->p_sys; 
    desired->samples    = 2048;
     
    /* Open the sound device 
     * we just ask the SDL to wrap at the good frequency if the one we
     * ask for is unavailable. This is done by setting the second parar
     * to NULL
     */
    if( SDL_OpenAudio(desired,NULL) < 0 )
    {
Pierre Baillet's avatar
Pierre Baillet committed
204 205
        free( desired );
        intf_ErrMsg( "aout_Open error: can't open audio device: %s", 
206 207 208
                        SDL_GetError() );
        return( -1 );
    }
Pierre Baillet's avatar
Pierre Baillet committed
209 210
    p_aout->p_sys->b_active = 1;
    free( desired );
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
    SDL_PauseAudio(0);
    return( 0 );

}

/*****************************************************************************
 * aout_SetFormat: resets the dsp and sets its format
 *****************************************************************************
 * This functions resets the DSP device, tries to initialize the output
 * format with the value contained in the dsp structure, and if this value
 * could not be set, the default value returned by ioctl is set. It then
 * does the same for the stereo mode, and for the output rate.
 *****************************************************************************/
static int aout_SetFormat( aout_thread_t *p_aout )
{
    /* TODO: finish and clean this */
    SDL_AudioSpec *desired;
    int i_stereo = p_aout->b_stereo?2:1;
    desired = (SDL_AudioSpec *)malloc( sizeof(SDL_AudioSpec) );

/*    i_format = p_aout->i_format;
*/
    desired->freq =     p_aout->l_rate;               /* Set the output rate */
    desired->format     = AUDIO_S16LSB;                    /* stereo 16 bits */
    desired->channels    = i_stereo;
    desired->callback   = SDL_aout_callback;
    desired->userdata   = p_aout->p_sys; 
    desired->samples    = 2048;
     
    /* Open the sound device */
    SDL_PauseAudio(1);
    SDL_CloseAudio();
    if( SDL_OpenAudio(desired,NULL) < 0 )
Pierre Baillet's avatar
Pierre Baillet committed
244 245 246
    {
        free( desired );
        p_aout->p_sys->b_active = 0;
247
        return( -1 );
Pierre Baillet's avatar
Pierre Baillet committed
248 249 250
    }
    p_aout->p_sys->b_active = 1;
    free( desired );
251 252 253 254 255 256 257 258 259 260 261 262
    SDL_PauseAudio(0);
    return(0);    
}

/*****************************************************************************
 * aout_GetBufInfo: buffer status query
 *****************************************************************************
 * returns the number of bytes in the audio buffer compared to the size of 
 * l_buffer_limit...
 *****************************************************************************/
static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
{
263 264 265 266 267
    if(l_buffer_limit > p_aout->p_sys->i_audio_end)
    {
        /* returning 0 here juste gives awful sound in the speakers :/ */
        return( l_buffer_limit );
    }
268 269 270 271 272 273 274 275 276 277 278
    return( p_aout->p_sys->i_audio_end-l_buffer_limit);
}


static void SDL_aout_callback(void *userdata, byte_t *stream, int len)
{
    struct aout_sys_s * p_sys = userdata;
    int end = p_sys->i_audio_end;
    
    if(end > OVERFLOWLIMIT)
    {
Pierre Baillet's avatar
Pierre Baillet committed
279
        intf_ErrMsg("aout SDL_aout_callback: Overflow.");
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
        free(p_sys->audio_buf);
        p_sys->audio_buf = NULL;
        p_sys->i_audio_end = 0;
        end = 0;
        // we've gone to slow, increase output freq
    }
       
    /* if we are not in underrun */
    if(end>len)                     
    {
        memcpy(stream, p_sys->audio_buf, len);
        memmove(p_sys->audio_buf, &(p_sys->audio_buf[len]), end-len);
        p_sys->i_audio_end -= len;
    }
}

/*****************************************************************************
 * aout_Play: plays a sound samples buffer
 *****************************************************************************
 * This function writes a buffer of i_length bytes in the dsp
 *****************************************************************************/
static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
{
    byte_t * audio_buf = p_aout->p_sys->audio_buf;
    
    SDL_LockAudio();                                     /* Stop callbacking */

    audio_buf = realloc(audio_buf, p_aout->p_sys->i_audio_end + i_size);
    memcpy(&(audio_buf[p_aout->p_sys->i_audio_end]), buffer, i_size);
    p_aout->p_sys->i_audio_end += i_size; 
    p_aout->p_sys->audio_buf = audio_buf;
    
    SDL_UnlockAudio();                                  /* go on callbacking */
}

/*****************************************************************************
 * aout_Close: closes the dsp audio device
 *****************************************************************************/
static void aout_Close( aout_thread_t *p_aout )
{
Pierre Baillet's avatar
Pierre Baillet committed
320
    if( p_aout->p_sys->b_active )
321
    {
Pierre Baillet's avatar
Pierre Baillet committed
322 323 324 325 326 327 328
        SDL_PauseAudio(1);                                    /* pause audio */
    
        if(p_aout->p_sys->audio_buf != NULL)    /* do we have a buffer now ? */
        {
            free(p_aout->p_sys->audio_buf);
        }
        p_aout->p_sys->b_active = 0;                         /* just for sam */
329 330 331 332 333
    }
    free(p_aout->p_sys);                                /* Close the Output. */
    SDL_CloseAudio();
}