audio_output.c 10.6 KB
Newer Older
1
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
2
 * audio_output.c : audio output thread
3
 *****************************************************************************
4
 * Copyright (C) 1999-2001 VideoLAN
Sam Hocevar's avatar
 
Sam Hocevar committed
5
 * $Id: audio_output.c,v 1.80 2002/02/27 22:57:10 sam Exp $
6
 *
Renaud Dartus's avatar
 
Renaud Dartus committed
7
 * Authors: Michel Kaempf <maxx@via.ecp.fr>
8
 *          Cyril Deguet <asmax@via.ecp.fr>
9 10 11 12 13
 *
 * 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.
14
 * 
15 16
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
19
 *
20 21 22
 * 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.
23
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
24

25
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
26
 * Preamble
27
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
28 29 30 31 32
#include <stdio.h>                                           /* "intf_msg.h" */
#include <stdlib.h>                            /* calloc(), malloc(), free() */
#include <string.h>

#include <videolan/vlc.h>
33

Sam Hocevar's avatar
 
Sam Hocevar committed
34
#ifdef HAVE_UNISTD_H
Sam Hocevar's avatar
 
Sam Hocevar committed
35
#   include <unistd.h>                                           /* getpid() */
Sam Hocevar's avatar
 
Sam Hocevar committed
36 37
#endif

Sam Hocevar's avatar
 
Sam Hocevar committed
38
#ifdef WIN32                   /* getpid() for win32 is located in process.h */
Sam Hocevar's avatar
 
Sam Hocevar committed
39
#   include <process.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
40
#endif
Michel Kaempf's avatar
Michel Kaempf committed
41 42

#include "audio_output.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
43 44 45

#include "aout_pcm.h"
#include "aout_spdif.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
46

47
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
48
 * Local prototypes
49
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
50
static int  aout_SpawnThread ( aout_thread_t * p_aout );
Vincent Seguin's avatar
Vincent Seguin committed
51

Sam Hocevar's avatar
 
Sam Hocevar committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
/*****************************************************************************
 * aout_InitBank: initialize the audio output bank.
 *****************************************************************************/
void aout_InitBank ( void )
{
    p_aout_bank->i_count = 0;

    vlc_mutex_init( &p_aout_bank->lock );
}

/*****************************************************************************
 * aout_EndBank: empty the audio output bank.
 *****************************************************************************
 * This function ends all unused audio outputs and empties the bank in
 * case of success.
 *****************************************************************************/
void aout_EndBank ( void )
{
    /* Ask all remaining audio outputs to die */
    while( p_aout_bank->i_count )
    {
        aout_DestroyThread(
                p_aout_bank->pp_aout[ --p_aout_bank->i_count ], NULL );
    }

    vlc_mutex_destroy( &p_aout_bank->lock );
}

80
/*****************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
81
 * aout_CreateThread: initialize audio thread
82
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
83
aout_thread_t *aout_CreateThread( int *pi_status, int i_channels, int i_rate )
Michel Kaempf's avatar
Michel Kaempf committed
84
{
85
    aout_thread_t * p_aout;                             /* thread descriptor */
86
#if 0
87
    int             i_status;                               /* thread status */
88
#endif
Gildas Bazin's avatar
 
Gildas Bazin committed
89
    char *psz_name;
90

Vincent Seguin's avatar
Vincent Seguin committed
91 92 93
    /* Allocate descriptor */
    p_aout = (aout_thread_t *) malloc( sizeof(aout_thread_t) );
    if( p_aout == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
94
    {
Vincent Seguin's avatar
Vincent Seguin committed
95
        return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
96 97
    }

98
    p_aout->i_latency = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
99
    p_aout->i_rate = config_GetIntVariable( AOUT_RATE_VAR );
Gildas Bazin's avatar
 
Gildas Bazin committed
100
    p_aout->i_channels = config_GetIntVariable( AOUT_MONO_VAR ) ? 1 : 2;
101 102 103

    /* Maybe we should pass this setting in argument */
    p_aout->i_format = AOUT_FORMAT_DEFAULT;
Gildas Bazin's avatar
 
Gildas Bazin committed
104 105 106

    /* special setting for ac3 pass-through mode */
    /* FIXME is it necessary ? (cf ac3_adec.c) */
Gildas Bazin's avatar
 
Gildas Bazin committed
107
    if( config_GetIntVariable( AOUT_SPDIF_VAR ) && p_main->b_ac3 )
Gildas Bazin's avatar
 
Gildas Bazin committed
108 109 110 111
    {
        intf_WarnMsg( 4, "aout info: setting ac3 spdif" );
        p_aout->i_format = AOUT_FMT_AC3;
    }
112
    
Sam Hocevar's avatar
 
Sam Hocevar committed
113
    if( p_aout->i_rate == 0 )
Gildas Bazin's avatar
 
Gildas Bazin committed
114 115 116 117 118 119
    {
        intf_ErrMsg( "aout error: null sample rate" );
        free( p_aout );
        return( NULL );
    }

120
    /* Choose the best module */
Gildas Bazin's avatar
 
Gildas Bazin committed
121 122 123 124
    psz_name = config_GetPszVariable( AOUT_METHOD_VAR );
    p_aout->p_module = module_Need( MODULE_CAPABILITY_AOUT, psz_name,
                                    (void *)p_aout );
    if( psz_name ) free( psz_name );
125
    if( p_aout->p_module == NULL )
126
    {
127
        intf_ErrMsg( "aout error: no suitable aout module" );
128 129 130 131
        free( p_aout );
        return( NULL );
    }

132
#define aout_functions p_aout->p_module->p_functions->aout.functions.aout
Sam Hocevar's avatar
 
Sam Hocevar committed
133 134 135 136 137
    p_aout->pf_open       = aout_functions.pf_open;
    p_aout->pf_setformat  = aout_functions.pf_setformat;
    p_aout->pf_getbufinfo = aout_functions.pf_getbufinfo;
    p_aout->pf_play       = aout_functions.pf_play;
    p_aout->pf_close      = aout_functions.pf_close;
138
#undef aout_functions
139

140
    /*
141
     * Initialize audio device
Vincent Seguin's avatar
Vincent Seguin committed
142
     */
Sam Hocevar's avatar
 
Sam Hocevar committed
143
    if ( p_aout->pf_setformat( p_aout ) )
Michel Kaempf's avatar
Michel Kaempf committed
144
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
145
        p_aout->pf_close( p_aout );
Sam Hocevar's avatar
 
Sam Hocevar committed
146
        module_Unneed( p_aout->p_module );
147 148
        free( p_aout );
        return( NULL );
Michel Kaempf's avatar
Michel Kaempf committed
149
    }
150

Renaud Dartus's avatar
Renaud Dartus committed
151
    /* Initialize the volume level */
Gildas Bazin's avatar
 
Gildas Bazin committed
152
    p_aout->i_volume = config_GetIntVariable( AOUT_VOLUME_VAR );
Sam Hocevar's avatar
 
Sam Hocevar committed
153
    p_aout->i_savedvolume = 0;
Renaud Dartus's avatar
 
Renaud Dartus committed
154
    
155 156 157
    /* FIXME: maybe it would be cleaner to change SpawnThread prototype
     * see vout to handle status correctly ?? however, it is not critical since
     * this thread is only called in main and all calls are blocking */
Vincent Seguin's avatar
Vincent Seguin committed
158 159
    if( aout_SpawnThread( p_aout ) )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
160
        p_aout->pf_close( p_aout );
Sam Hocevar's avatar
 
Sam Hocevar committed
161
        module_Unneed( p_aout->p_module );
162 163
        free( p_aout );
        return( NULL );
Vincent Seguin's avatar
Vincent Seguin committed
164 165 166
    }

    return( p_aout );
Michel Kaempf's avatar
Michel Kaempf committed
167 168
}

169
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
170
 * aout_SpawnThread
171
 *****************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
172
static int aout_SpawnThread( aout_thread_t * p_aout )
Michel Kaempf's avatar
Michel Kaempf committed
173
{
Sam Hocevar's avatar
 
Sam Hocevar committed
174
    int     i_index, i_bytes;
Sam Hocevar's avatar
 
Sam Hocevar committed
175
    void (* pf_aout_thread)( aout_thread_t * ) = NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
176
    char   *psz_format;
Michel Kaempf's avatar
Michel Kaempf committed
177 178 179

    /* We want the audio output thread to live */
    p_aout->b_die = 0;
Sam Hocevar's avatar
Sam Hocevar committed
180
    p_aout->b_active = 1;
Michel Kaempf's avatar
Michel Kaempf committed
181 182

    /* Initialize the fifos lock */
183
    vlc_mutex_init( &p_aout->fifos_lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
184

Michel Kaempf's avatar
Michel Kaempf committed
185
    /* Initialize audio fifos : set all fifos as empty and initialize locks */
Sam Hocevar's avatar
 
Sam Hocevar committed
186
    for ( i_index = 0; i_index < AOUT_MAX_FIFOS; i_index++ )
Michel Kaempf's avatar
Michel Kaempf committed
187
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
188 189 190
        p_aout->fifo[i_index].i_format = AOUT_FIFO_NONE;
        vlc_mutex_init( &p_aout->fifo[i_index].data_lock );
        vlc_cond_init( &p_aout->fifo[i_index].data_wait );
Michel Kaempf's avatar
Michel Kaempf committed
191 192 193 194 195
    }

    /* Compute the size (in audio units) of the audio output buffer. Although
     * AOUT_BUFFER_DURATION is given in microseconds, the output rate is given
     * in Hz, that's why we need to divide by 10^6 microseconds (1 second) */
Sam Hocevar's avatar
 
Sam Hocevar committed
196 197
    p_aout->i_units = (s64)p_aout->i_rate * AOUT_BUFFER_DURATION / 1000000;
    p_aout->i_msleep = AOUT_BUFFER_DURATION;
Michel Kaempf's avatar
Michel Kaempf committed
198

Sam Hocevar's avatar
 
Sam Hocevar committed
199
    /* Make pf_aout_thread point to the right thread function, and compute the
Michel Kaempf's avatar
Michel Kaempf committed
200
     * byte size of the audio output buffer */
Sam Hocevar's avatar
 
Sam Hocevar committed
201
    switch ( p_aout->i_format )
Michel Kaempf's avatar
Michel Kaempf committed
202
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
203
        case AOUT_FMT_U8:
Sam Hocevar's avatar
 
Sam Hocevar committed
204 205 206
            pf_aout_thread = aout_PCMThread;
            psz_format = "unsigned 8 bits";
            i_bytes = p_aout->i_units * p_aout->i_channels;
Sam Hocevar's avatar
 
Sam Hocevar committed
207
            break;
Michel Kaempf's avatar
Michel Kaempf committed
208

Sam Hocevar's avatar
 
Sam Hocevar committed
209
        case AOUT_FMT_S8:
Sam Hocevar's avatar
 
Sam Hocevar committed
210 211 212
            pf_aout_thread = aout_PCMThread;
            psz_format = "signed 8 bits";
            i_bytes = p_aout->i_units * p_aout->i_channels;
Sam Hocevar's avatar
 
Sam Hocevar committed
213
            break;
Michel Kaempf's avatar
Michel Kaempf committed
214

Sam Hocevar's avatar
 
Sam Hocevar committed
215 216
        case AOUT_FMT_U16_LE:
        case AOUT_FMT_U16_BE:
Sam Hocevar's avatar
 
Sam Hocevar committed
217 218 219
            pf_aout_thread = aout_PCMThread;
            psz_format = "unsigned 16 bits";
            i_bytes = 2 * p_aout->i_units * p_aout->i_channels;
Sam Hocevar's avatar
 
Sam Hocevar committed
220
            break;
Michel Kaempf's avatar
Michel Kaempf committed
221

Sam Hocevar's avatar
 
Sam Hocevar committed
222 223
        case AOUT_FMT_S16_LE:
        case AOUT_FMT_S16_BE:
Sam Hocevar's avatar
 
Sam Hocevar committed
224 225 226
            pf_aout_thread = aout_PCMThread;
            psz_format = "signed 16 bits";
            i_bytes = 2 * p_aout->i_units * p_aout->i_channels;
Michel Kaempf's avatar
Michel Kaempf committed
227
            break;
228 229 230

        case AOUT_FMT_AC3:
            pf_aout_thread = aout_SpdifThread;
Sam Hocevar's avatar
 
Sam Hocevar committed
231 232
            psz_format = "ac3 pass-through";
            i_bytes = SPDIF_FRAME_SIZE;
233 234
            break;

Michel Kaempf's avatar
Michel Kaempf committed
235
        default:
Sam Hocevar's avatar
 
Sam Hocevar committed
236 237
            intf_ErrMsg( "aout error: unknown audio output format %i",
                         p_aout->i_format );
Michel Kaempf's avatar
Michel Kaempf committed
238 239 240 241 242
            return( -1 );
    }

    /* Allocate the memory needed by the audio output buffers, and set to zero
     * the s32 buffer's memory */
Sam Hocevar's avatar
 
Sam Hocevar committed
243
    p_aout->buffer = malloc( i_bytes );
Sam Hocevar's avatar
 
Sam Hocevar committed
244
    if ( p_aout->buffer == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
245
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
246
        intf_ErrMsg( "aout error: cannot create output buffer" );
Michel Kaempf's avatar
Michel Kaempf committed
247 248
        return( -1 );
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
249

Sam Hocevar's avatar
 
Sam Hocevar committed
250 251
    p_aout->s32_buffer = (s32 *)calloc( p_aout->i_units,
                                        sizeof(s32) * p_aout->i_channels );
Sam Hocevar's avatar
 
Sam Hocevar committed
252
    if ( p_aout->s32_buffer == NULL )
Michel Kaempf's avatar
Michel Kaempf committed
253
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
254
        intf_ErrMsg( "aout error: cannot create the s32 output buffer" );
Michel Kaempf's avatar
Michel Kaempf committed
255 256 257 258
        free( p_aout->buffer );
        return( -1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
259
    /* Rough estimate of the playing date */
260
    p_aout->date = mdate() + p_main->i_desync;
Michel Kaempf's avatar
Michel Kaempf committed
261 262

    /* Launch the thread */
Sam Hocevar's avatar
 
Sam Hocevar committed
263
    if ( vlc_thread_create( &p_aout->thread_id, "audio output",
Sam Hocevar's avatar
 
Sam Hocevar committed
264
                            (vlc_thread_func_t)pf_aout_thread, p_aout ) )
Michel Kaempf's avatar
Michel Kaempf committed
265
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
266
        intf_ErrMsg( "aout error: cannot spawn audio output thread" );
Michel Kaempf's avatar
Michel Kaempf committed
267 268 269 270 271
        free( p_aout->buffer );
        free( p_aout->s32_buffer );
        return( -1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
272 273
    intf_WarnMsg( 2, "aout info: %s thread spawned, %i channels, rate %i",
                     psz_format, p_aout->i_channels, p_aout->i_rate );
Michel Kaempf's avatar
Michel Kaempf committed
274 275 276
    return( 0 );
}

277
/*****************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
278
 * aout_DestroyThread
279
 *****************************************************************************/
Vincent Seguin's avatar
Vincent Seguin committed
280
void aout_DestroyThread( aout_thread_t * p_aout, int *pi_status )
Michel Kaempf's avatar
Michel Kaempf committed
281
{
Sam Hocevar's avatar
 
Sam Hocevar committed
282
    int i_index;
Henri Fallon's avatar
 
Henri Fallon committed
283
    
284
    /* FIXME: pi_status is not handled correctly: check vout how to do!?? */
Vincent Seguin's avatar
Vincent Seguin committed
285

Michel Kaempf's avatar
Michel Kaempf committed
286 287
    /* Ask thread to kill itself and wait until it's done */
    p_aout->b_die = 1;
288
    vlc_thread_join( p_aout->thread_id ); /* only if pi_status is NULL */
Michel Kaempf's avatar
Michel Kaempf committed
289 290 291 292 293

    /* Free the allocated memory */
    free( p_aout->buffer );
    free( p_aout->s32_buffer );

Henri Fallon's avatar
 
Henri Fallon committed
294
    /* Destroy the condition and mutex locks */
Sam Hocevar's avatar
 
Sam Hocevar committed
295
    for ( i_index = 0; i_index < AOUT_MAX_FIFOS; i_index++ )
Henri Fallon's avatar
 
Henri Fallon committed
296
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
297 298
        vlc_mutex_destroy( &p_aout->fifo[i_index].data_lock );
        vlc_cond_destroy( &p_aout->fifo[i_index].data_wait );
Henri Fallon's avatar
 
Henri Fallon committed
299
    }
Henri Fallon's avatar
 
Henri Fallon committed
300
    vlc_mutex_destroy( &p_aout->fifos_lock );
Henri Fallon's avatar
 
Henri Fallon committed
301
    
Sam Hocevar's avatar
 
Sam Hocevar committed
302 303 304
    /* Free the plugin */
    p_aout->pf_close( p_aout );

305
    /* Release the aout module */
Sam Hocevar's avatar
 
Sam Hocevar committed
306
    module_Unneed( p_aout->p_module );
307

308
    /* Free structure */
Vincent Seguin's avatar
Vincent Seguin committed
309
    free( p_aout );
Michel Kaempf's avatar
Michel Kaempf committed
310 311
}