aout_sdl.c 12.3 KB
Newer Older
1 2 3
/*****************************************************************************
 * aout_sdl.c : audio sdl functions library
 *****************************************************************************
4 5
 * Copyright (C) 1999-2001 VideoLAN
 * $Id: aout_sdl.c,v 1.19 2001/11/28 15:08:05 massiot Exp $
6 7 8 9 10 11 12 13 14
 *
 * 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.
Sam Hocevar's avatar
 
Sam Hocevar committed
15
 *
16 17 18 19 20 21 22 23 24 25
 * 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.
 *****************************************************************************/

Sam Hocevar's avatar
 
Sam Hocevar committed
26 27 28
#define MODULE_NAME sdl
#include "modules_inner.h"

29 30 31 32 33 34 35 36 37 38 39 40
/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include "defs.h"

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

41
#include SDL_INCLUDE_FILE
42 43 44

#include "config.h"
#include "common.h"                                     /* boolean_t, byte_t */
45
#include "intf_msg.h"                        /* intf_DbgMsg(), intf_ErrMsg() */
46 47
#include "threads.h"
#include "mtime.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
48
#include "tests.h"
49 50 51 52 53 54

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

#include "main.h"

#include "modules.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
55
#include "modules_export.h"
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

/*****************************************************************************
 * 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;
Sam Hocevar's avatar
 
Sam Hocevar committed
71

Pierre Baillet's avatar
Pierre Baillet committed
72
    boolean_t b_active;
73

Sam Hocevar's avatar
 
Sam Hocevar committed
74
} aout_sys_t;
75 76 77 78 79 80 81 82 83 84 85

/*****************************************************************************
 * 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 );
Sam Hocevar's avatar
 
Sam Hocevar committed
86 87

static void    aout_SDLCallback ( void *userdata, Uint8 *stream, int len );
88 89 90 91 92

/*****************************************************************************
 * Functions exported as capabilities. They are declared as static so that
 * we don't pollute the namespace too much.
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
93
void _M( aout_getfunctions )( function_list_t * p_function_list )
94 95 96 97 98 99 100 101 102 103
{
    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;
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
104
 * aout_Probe: probe the audio device and return a score
105
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
106 107
 * This function tries to initialize SDL audio and returns a score to the
 * plugin manager so that it can select the best plugin.
108 109 110
 *****************************************************************************/
static int aout_Probe( probedata_t *p_data )
{
Gildas Bazin's avatar
 
Gildas Bazin committed
111
#if 0
Sam Hocevar's avatar
 
Sam Hocevar committed
112
    SDL_AudioSpec desired, obtained;
113 114

    /* Start AudioSDL */
115
    if( SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE) != 0 )
Pierre Baillet's avatar
Pierre Baillet committed
116
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
117
        intf_DbgMsg( "aout: SDL_Init failed (%s)", SDL_GetError() );
Pierre Baillet's avatar
Pierre Baillet committed
118 119
        return( 0 );
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
120 121 122 123

    desired.freq       = 11025;                                 /* frequency */
    desired.format     = AUDIO_U8;                        /* unsigned 8 bits */
    desired.channels   = 2;                                          /* mono */
Sam Hocevar's avatar
 
Sam Hocevar committed
124
    desired.callback   = aout_SDLCallback;    /* callback function mandatory */
Sam Hocevar's avatar
 
Sam Hocevar committed
125 126 127
    desired.userdata   = NULL;                     /* null parm for callback */
    desired.samples    = 4096;

128 129
    /* If we were unable to open the device, there is no way we can use
     * the plugin. Return a score of 0. */
Sam Hocevar's avatar
 
Sam Hocevar committed
130
    if( SDL_OpenAudio( &desired, &obtained ) < 0 )
131
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
132 133
        intf_DbgMsg( "aout: SDL_OpenAudio failed (%s)", SDL_GetError() );
        return( 0 );
134 135 136
    }

    /* Otherwise, there are good chances we can use this plugin, return 100. */
Sam Hocevar's avatar
 
Sam Hocevar committed
137
    intf_DbgMsg( "aout: SDL_OpenAudio successfully run" );
138
    SDL_CloseAudio();
Gildas Bazin's avatar
 
Gildas Bazin committed
139
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
140 141 142 143 144 145

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

Sam Hocevar's avatar
 
Sam Hocevar committed
146
    return( 40 );
147 148 149
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
150
 * aout_Open: open the audio device
151 152 153 154 155 156
 *****************************************************************************
 * 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 )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
157 158
    SDL_AudioSpec desired;
    int i_channels = p_aout->b_stereo ? 2 : 1;
159

Sam Hocevar's avatar
 
Sam Hocevar committed
160
   /* Allocate structure */
161
    p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
Sam Hocevar's avatar
 
Sam Hocevar committed
162

163 164
    if( p_aout->p_sys == NULL )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
165
        intf_ErrMsg( "aout error: %s", strerror(ENOMEM) );
166 167
        return( 1 );
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
168

169
    p_aout->p_sys->i_audio_end = 0;
170
    p_aout->p_sys->audio_buf = malloc( OVERFLOWLIMIT );
Sam Hocevar's avatar
 
Sam Hocevar committed
171

172 173 174 175 176 177 178 179
    /* 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 );

Sam Hocevar's avatar
 
Sam Hocevar committed
180 181 182
    desired.freq =     p_aout->l_rate;

    /* TODO: write conversion beetween AOUT_FORMAT_DEFAULT
183
     * AND AUDIO* from SDL. */
Sam Hocevar's avatar
 
Sam Hocevar committed
184 185 186 187 188 189 190
    desired.format     = AUDIO_S16LSB;                     /* stereo 16 bits */
    desired.channels   = i_channels;
    desired.callback   = aout_SDLCallback;
    desired.userdata   = p_aout->p_sys;
    desired.samples    = 1024;

    /* Open the sound device
191 192 193 194
     * 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
     */
Sam Hocevar's avatar
 
Sam Hocevar committed
195
    if( SDL_OpenAudio( &desired, NULL ) < 0 )
196
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
197
        intf_ErrMsg( "aout error: SDL_OpenAudio failed (%s)", SDL_GetError() );
198 199
        return( -1 );
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
200

Pierre Baillet's avatar
Pierre Baillet committed
201
    p_aout->p_sys->b_active = 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
202
    SDL_PauseAudio( 0 );
203

Sam Hocevar's avatar
 
Sam Hocevar committed
204
    return( 0 );
205 206 207
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
208
 * aout_SetFormat: reset the audio device and sets its format
209
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
210
 * This functions resets the audio device, tries to initialize the output
211 212 213 214 215 216 217
 * 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 */
Sam Hocevar's avatar
 
Sam Hocevar committed
218 219 220 221 222 223 224 225 226 227 228
    SDL_AudioSpec desired;
    int i_stereo = p_aout->b_stereo ? 2 : 1;

    /*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   = aout_SDLCallback;
    desired.userdata   = p_aout->p_sys;
    desired.samples    = 2048;

229
    /* Open the sound device */
Sam Hocevar's avatar
 
Sam Hocevar committed
230
    SDL_PauseAudio( 1 );
231
    SDL_CloseAudio();
Sam Hocevar's avatar
 
Sam Hocevar committed
232 233

    if( SDL_OpenAudio( &desired, NULL ) < 0 )
Pierre Baillet's avatar
Pierre Baillet committed
234 235
    {
        p_aout->p_sys->b_active = 0;
236
        return( -1 );
Pierre Baillet's avatar
Pierre Baillet committed
237
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
238

Pierre Baillet's avatar
Pierre Baillet committed
239
    p_aout->p_sys->b_active = 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
240 241
    SDL_PauseAudio( 0 );

242 243
    p_aout->i_latency = 0;
    
Sam Hocevar's avatar
 
Sam Hocevar committed
244
    return( 0 );
245 246 247 248 249
}

/*****************************************************************************
 * aout_GetBufInfo: buffer status query
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
250
 * returns the number of bytes in the audio buffer compared to the size of
251 252 253 254
 * l_buffer_limit...
 *****************************************************************************/
static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
{
255 256 257 258 259
    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 );
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
260
    return( p_aout->p_sys->i_audio_end - l_buffer_limit);
261 262 263
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
264
 * aout_Play: play a sound samples buffer
265 266 267 268 269 270
 *****************************************************************************
 * 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;
Sam Hocevar's avatar
 
Sam Hocevar committed
271

272 273
    SDL_LockAudio();                                     /* Stop callbacking */

Sam Hocevar's avatar
 
Sam Hocevar committed
274 275 276 277 278 279 280
    p_aout->p_sys->audio_buf = realloc( audio_buf,
                                        p_aout->p_sys->i_audio_end + i_size);
    memcpy( p_aout->p_sys->audio_buf + p_aout->p_sys->i_audio_end,
            buffer, i_size);

    p_aout->p_sys->i_audio_end += i_size;

281 282 283 284
    SDL_UnlockAudio();                                  /* go on callbacking */
}

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
285
 * aout_Close: close the audio device
286 287 288
 *****************************************************************************/
static void aout_Close( aout_thread_t *p_aout )
{
Pierre Baillet's avatar
Pierre Baillet committed
289
    if( p_aout->p_sys->b_active )
290
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
291 292 293
        SDL_PauseAudio( 1 );                                  /* pause audio */

        if( p_aout->p_sys->audio_buf != NULL )  /* do we have a buffer now ? */
Pierre Baillet's avatar
Pierre Baillet committed
294
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
295
            free( p_aout->p_sys->audio_buf );
Pierre Baillet's avatar
Pierre Baillet committed
296
        }
297
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
298

299
    SDL_CloseAudio();
Sam Hocevar's avatar
 
Sam Hocevar committed
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328

    free( p_aout->p_sys );                              /* Close the Output. */
}

/*****************************************************************************
 * aout_SDLCallback: what to do once SDL has played sound samples
 *****************************************************************************/
static void aout_SDLCallback( void *userdata, byte_t *stream, int len )
{
    struct aout_sys_s * p_sys = userdata;

    if( p_sys->i_audio_end > OVERFLOWLIMIT )
    {
        intf_ErrMsg( "aout error: aout_SDLCallback overflowed" );

        free( p_sys->audio_buf );
        p_sys->audio_buf = NULL;

        p_sys->i_audio_end = 0;
        /* we've gone to slow, increase output freq */
    }

    /* if we are not in underrun */
    if( p_sys->i_audio_end > len )
    {
        p_sys->i_audio_end -= len;
        memcpy( stream, p_sys->audio_buf, len );
        memmove( p_sys->audio_buf, p_sys->audio_buf + len, p_sys->i_audio_end );
    }
329 330
}