aout.c 23.2 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * aout.c: Windows DirectX audio output method
 *****************************************************************************
 * Copyright (C) 2001 VideoLAN
Gildas Bazin's avatar
 
Gildas Bazin committed
5
 * $Id: aout.c,v 1.2 2002/08/10 18:17:06 gbazin Exp $
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
 *
 * Authors: Gildas Bazin <gbazin@netcourrier.com>
 *
 * 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 <errno.h>                                                 /* ENOMEM */
#include <fcntl.h>                                       /* open(), O_WRONLY */
#include <string.h>                                            /* strerror() */

#include <stdlib.h>                            /* calloc(), malloc(), free() */

#include <vlc/vlc.h>
#include <vlc/aout.h>
Gildas Bazin's avatar
 
Gildas Bazin committed
35
#include "aout_internal.h"
36 37 38 39

#include <mmsystem.h>
#include <dsound.h>

Gildas Bazin's avatar
 
Gildas Bazin committed
40 41
#define FRAME_SIZE 2048              /* The size is in samples, not in bytes */

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
/*****************************************************************************
 * DirectSound GUIDs.
 * Defining them here allows us to get rid of the dxguid library during
 * the linking stage.
 *****************************************************************************/
#include <initguid.h>
DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);

/*****************************************************************************
 * notification_thread_t: DirectX event thread
 *****************************************************************************/
typedef struct notification_thread_t
{
    VLC_COMMON_MEMBERS

Gildas Bazin's avatar
 
Gildas Bazin committed
57
    aout_instance_t * p_aout;
58
    DSBPOSITIONNOTIFY p_events[2];               /* play notification events */
Gildas Bazin's avatar
 
Gildas Bazin committed
59
    int i_buffer_size;                         /* Size in bytes of one frame */
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

} notification_thread_t;

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

struct aout_sys_t
{
    LPDIRECTSOUND       p_dsobject;              /* main Direct Sound object */

    LPDIRECTSOUNDBUFFER p_dsbuffer_primary;     /* the actual sound card buffer
                                                   (not used directly) */

    LPDIRECTSOUNDBUFFER p_dsbuffer;   /* the sound buffer we use (direct sound
                                       * takes care of mixing all the
                                       * secondary buffers into the primary) */

    LPDIRECTSOUNDNOTIFY p_dsnotify;         /* the position notify interface */

    HINSTANCE           hdsound_dll;      /* handle of the opened dsound dll */

    vlc_mutex_t buffer_lock;                            /* audio buffer lock */

    notification_thread_t * p_notif;                 /* DirectSoundThread id */
Gildas Bazin's avatar
 
Gildas Bazin committed
88

89 90
};

Gildas Bazin's avatar
 
Gildas Bazin committed
91 92 93 94 95
/*****************************************************************************
 * Prototypes.
 *****************************************************************************/
void E_(CloseAudio) ( vlc_object_t *p_this );

96 97 98
/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
99 100
static int  SetFormat ( aout_instance_t * );
static void Play      ( aout_instance_t *, aout_buffer_t * );
101 102

/* local functions */
Gildas Bazin's avatar
 
Gildas Bazin committed
103 104 105
static int  DirectxCreateSecondaryBuffer ( aout_instance_t * );
static void DirectxDestroySecondaryBuffer( aout_instance_t * );
static int  DirectxInitDSound            ( aout_instance_t * );
106 107 108 109 110 111 112 113 114
static void DirectSoundThread            ( notification_thread_t * );

/*****************************************************************************
 * OpenAudio: open the audio device
 *****************************************************************************
 * This function opens and setups Direct Sound.
 *****************************************************************************/
int E_(OpenAudio) ( vlc_object_t *p_this )
{
Gildas Bazin's avatar
 
Gildas Bazin committed
115
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
116 117 118 119 120 121
    HRESULT dsresult;
    DSBUFFERDESC dsbuffer_desc;

    msg_Dbg( p_aout, "Open" );

   /* Allocate structure */
Gildas Bazin's avatar
 
Gildas Bazin committed
122 123
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->output.p_sys == NULL )
124 125
    {
        msg_Err( p_aout, "out of memory" );
Gildas Bazin's avatar
 
Gildas Bazin committed
126
        return 1;
127 128 129
    }

    /* Initialize some variables */
Gildas Bazin's avatar
 
Gildas Bazin committed
130 131 132 133 134 135 136 137 138
    p_aout->output.p_sys->p_dsobject = NULL;
    p_aout->output.p_sys->p_dsbuffer_primary = NULL;
    p_aout->output.p_sys->p_dsbuffer = NULL;
    p_aout->output.p_sys->p_dsnotify = NULL;
    p_aout->output.p_sys->p_notif = NULL;
    vlc_mutex_init( p_aout, &p_aout->output.p_sys->buffer_lock );

    p_aout->output.pf_setformat = SetFormat;
    p_aout->output.pf_play = Play;
139 140 141 142

    /* Initialise DirectSound */
    if( DirectxInitDSound( p_aout ) )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
143 144
        msg_Err( p_aout, "cannot initialize DirectSound" );
        goto error;
145 146 147 148 149 150 151
    }

    /* Obtain (not create) Direct Sound primary buffer */
    memset( &dsbuffer_desc, 0, sizeof(DSBUFFERDESC) );
    dsbuffer_desc.dwSize = sizeof(DSBUFFERDESC);
    dsbuffer_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
    msg_Warn( p_aout, "create direct sound primary buffer" );
Gildas Bazin's avatar
 
Gildas Bazin committed
152 153 154 155
    dsresult = IDirectSound_CreateSoundBuffer(p_aout->output.p_sys->p_dsobject,
                                     &dsbuffer_desc,
                                     &p_aout->output.p_sys->p_dsbuffer_primary,
                                     NULL);
156 157
    if( dsresult != DS_OK )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
158 159
        msg_Err( p_aout, "cannot create direct sound primary buffer" );
        goto error;
160 161 162 163
    }


    /* Now we need to setup DirectSound play notification */
Gildas Bazin's avatar
 
Gildas Bazin committed
164 165 166
    p_aout->output.p_sys->p_notif =
        vlc_object_create( p_aout, sizeof(notification_thread_t) );
    p_aout->output.p_sys->p_notif->p_aout = p_aout;
167 168

    /* first we need to create the notification events */
Gildas Bazin's avatar
 
Gildas Bazin committed
169
    p_aout->output.p_sys->p_notif->p_events[0].hEventNotify =
170
        CreateEvent( NULL, FALSE, FALSE, NULL );
Gildas Bazin's avatar
 
Gildas Bazin committed
171
    p_aout->output.p_sys->p_notif->p_events[1].hEventNotify =
172 173 174 175
        CreateEvent( NULL, FALSE, FALSE, NULL );

    /* then launch the notification thread */
    msg_Dbg( p_aout, "creating DirectSoundThread" );
Gildas Bazin's avatar
 
Gildas Bazin committed
176 177 178
    if( vlc_thread_create( p_aout->output.p_sys->p_notif,
                           "DirectSound Notification Thread",
                           DirectSoundThread, 1 ) )
179 180
    {
        msg_Err( p_aout, "cannot create DirectSoundThread" );
Gildas Bazin's avatar
 
Gildas Bazin committed
181
        goto error;
182 183
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
184 185 186
    vlc_object_attach( p_aout->output.p_sys->p_notif, p_aout );

    return 0;
187

Gildas Bazin's avatar
 
Gildas Bazin committed
188 189 190
 error:
    E_(CloseAudio)( VLC_OBJECT(p_aout) );
    return 1;
191 192 193 194 195 196 197 198 199
}

/*****************************************************************************
 * SetFormat: reset the audio device and sets its format
 *****************************************************************************
 * This functions set a new audio format.
 * For this we need to close the current secondary buffer and create another
 * one with the desired format.
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
200
static int SetFormat( aout_instance_t *p_aout )
201
{
Gildas Bazin's avatar
 
Gildas Bazin committed
202
    HRESULT dsresult;
203 204

    msg_Dbg( p_aout, "SetFormat" );
Gildas Bazin's avatar
 
Gildas Bazin committed
205
    vlc_mutex_lock( &p_aout->output.p_sys->buffer_lock );
206 207 208 209

    /* first release the current secondary buffer */
    DirectxDestroySecondaryBuffer( p_aout );

Gildas Bazin's avatar
 
Gildas Bazin committed
210 211 212 213
    /* calculate the frame size in bytes */
    p_aout->output.p_sys->p_notif->i_buffer_size = FRAME_SIZE * sizeof(s16)
                                            * p_aout->output.output.i_channels;

214 215 216
    /* then create a new secondary buffer */
    if( DirectxCreateSecondaryBuffer( p_aout ) )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
217 218 219
        msg_Err( p_aout, "cannot create buffer" );
        vlc_mutex_unlock( &p_aout->output.p_sys->buffer_lock );
        return 1;
220 221
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
222
    vlc_mutex_unlock( &p_aout->output.p_sys->buffer_lock );
223

Gildas Bazin's avatar
 
Gildas Bazin committed
224 225
    /* start playing the buffer */
    dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer,
226 227 228 229 230
                                        0,                         /* Unused */
                                        0,                         /* Unused */
                                        DSBPLAY_LOOPING );          /* Flags */
    if( dsresult == DSERR_BUFFERLOST )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
231 232
        IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
        dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer,
233 234 235 236 237 238
                                            0,                     /* Unused */
                                            0,                     /* Unused */
                                            DSBPLAY_LOOPING );      /* Flags */
    }
    if( dsresult != DS_OK )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
239
        msg_Warn( p_aout, "cannot play buffer" );
240 241
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
242 243 244 245 246 247 248 249 250 251 252 253
    return 0;
}

/*****************************************************************************
 * Play: play a sound buffer
 *****************************************************************************
 * This doesn't actually play the buffer. This just stores the buffer so it
 * can be played by the callback thread.
 *****************************************************************************/
static void Play( aout_instance_t *p_aout, aout_buffer_t *p_buffer )
{
    aout_FifoPush( p_aout, &p_aout->output.fifo, p_buffer );
254 255 256 257 258 259 260
}

/*****************************************************************************
 * CloseAudio: close the audio device
 *****************************************************************************/
void E_(CloseAudio) ( vlc_object_t *p_this )
{
Gildas Bazin's avatar
 
Gildas Bazin committed
261
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
262 263 264 265

    msg_Dbg( p_aout, "Close" );

    /* kill the position notification thread, if any */
Gildas Bazin's avatar
 
Gildas Bazin committed
266
    if( p_aout->output.p_sys->p_notif )
267
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
268 269 270 271 272 273 274
        vlc_object_detach_all( p_aout->output.p_sys->p_notif );
        if( p_aout->output.p_sys->p_notif->b_thread )
        {
            p_aout->output.p_sys->p_notif->b_die = 1;
            vlc_thread_join( p_aout->output.p_sys->p_notif );
        }
        vlc_object_destroy( p_aout->output.p_sys->p_notif );
275 276 277 278 279 280
    }

    /* release the secondary buffer */
    DirectxDestroySecondaryBuffer( p_aout );

    /* then release the primary buffer */
Gildas Bazin's avatar
 
Gildas Bazin committed
281 282
    if( p_aout->output.p_sys->p_dsbuffer_primary )
        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer_primary );
283 284

    /* finally release the DirectSound object */
Gildas Bazin's avatar
 
Gildas Bazin committed
285 286
    if( p_aout->output.p_sys->p_dsobject )
        IDirectSound_Release( p_aout->output.p_sys->p_dsobject );
287 288
    
    /* free DSOUND.DLL */
Gildas Bazin's avatar
 
Gildas Bazin committed
289 290
    if( p_aout->output.p_sys->hdsound_dll )
       FreeLibrary( p_aout->output.p_sys->hdsound_dll );
291 292

    /* Close the Output. */
Gildas Bazin's avatar
 
Gildas Bazin committed
293
    if ( p_aout->output.p_sys  )
294
    { 
Gildas Bazin's avatar
 
Gildas Bazin committed
295 296
        free( p_aout->output.p_sys );
        p_aout->output.p_sys = NULL;
297 298 299 300
    }
}

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
301
 * DirectxInitDSound: handle all the gory details of DirectSound initialisation
302
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
303
static int DirectxInitDSound( aout_instance_t *p_aout )
304 305 306
{
    HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);

Gildas Bazin's avatar
 
Gildas Bazin committed
307 308
    p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
    if( p_aout->output.p_sys->hdsound_dll == NULL )
309
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
310 311
        msg_Warn( p_aout, "cannot open DSOUND.DLL" );
        goto error;
312 313
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
314 315 316
    OurDirectSoundCreate = (void *)GetProcAddress(
                                           p_aout->output.p_sys->hdsound_dll,
                                           "DirectSoundCreate" );
317 318
    if( OurDirectSoundCreate == NULL )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
319 320
        msg_Warn( p_aout, "GetProcAddress FAILED" );
        goto error;
321 322 323
    }

    /* Create the direct sound object */
Gildas Bazin's avatar
 
Gildas Bazin committed
324 325
    if( OurDirectSoundCreate( NULL, &p_aout->output.p_sys->p_dsobject, NULL )
        != DS_OK )
326 327
    {
        msg_Warn( p_aout, "cannot create a direct sound device" );
Gildas Bazin's avatar
 
Gildas Bazin committed
328
        goto error;
329 330 331 332 333 334 335 336 337 338 339 340
    }

    /* Set DirectSound Cooperative level, ie what control we want over Windows
     * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
     * settings of the primary buffer, but also that only the sound of our
     * application will be hearable when it will have the focus.
     * !!! (this is not really working as intended yet because to set the
     * cooperative level you need the window handle of your application, and
     * I don't know of any easy way to get it. Especially since we might play
     * sound without any video, and so what window handle should we use ???
     * The hack for now is to use the Desktop window handle - it seems to be
     * working */
Gildas Bazin's avatar
 
Gildas Bazin committed
341 342 343
    if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
                                          GetDesktopWindow(),
                                          DSSCL_EXCLUSIVE) )
344 345 346 347
    {
        msg_Warn( p_aout, "cannot set direct sound cooperative level" );
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
348 349 350 351 352 353 354 355 356 357 358
    return 0;

 error:
    p_aout->output.p_sys->p_dsobject = NULL;
    if( p_aout->output.p_sys->hdsound_dll )
    {
        FreeLibrary( p_aout->output.p_sys->hdsound_dll );
        p_aout->output.p_sys->hdsound_dll = NULL;
    }
    return 1;

359 360 361 362 363 364 365 366 367 368 369 370 371 372
}

/*****************************************************************************
 * DirectxCreateSecondaryBuffer
 *****************************************************************************
 * This function creates the buffer we'll use to play audio.
 * In DirectSound there are two kinds of buffers:
 * - the primary buffer: which is the actual buffer that the soundcard plays
 * - the secondary buffer(s): these buffers are the one actually used by
 *    applications and DirectSound takes care of mixing them into the primary.
 *
 * Once you create a secondary buffer, you cannot change its format anymore so
 * you have to release the current and create another one.
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
373
static int DirectxCreateSecondaryBuffer( aout_instance_t *p_aout )
374 375 376 377 378 379 380 381
{
    WAVEFORMATEX         waveformat;
    DSBUFFERDESC         dsbdesc;
    DSBCAPS              dsbcaps;

    /* First set the buffer format */
    memset(&waveformat, 0, sizeof(WAVEFORMATEX)); 
    waveformat.wFormatTag      = WAVE_FORMAT_PCM; 
Gildas Bazin's avatar
 
Gildas Bazin committed
382 383
    waveformat.nChannels       = p_aout->output.output.i_channels;
    waveformat.nSamplesPerSec  = p_aout->output.output.i_rate; 
384 385 386 387 388 389 390 391 392 393 394 395
    waveformat.wBitsPerSample  = 16; 
    waveformat.nBlockAlign     = waveformat.wBitsPerSample / 8 *
                                 waveformat.nChannels;
    waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec *
                                     waveformat.nBlockAlign;

    /* Then fill in the descriptor */
    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 
    dsbdesc.dwSize = sizeof(DSBUFFERDESC); 
    dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
                    | DSBCAPS_CTRLPOSITIONNOTIFY     /* We need notification */
                    | DSBCAPS_GLOBALFOCUS;      /* Allows background playing */
Gildas Bazin's avatar
 
Gildas Bazin committed
396 397
    dsbdesc.dwBufferBytes = FRAME_SIZE * 2 /* frames*/ *      /* buffer size */
                            sizeof(s16) * p_aout->output.output.i_channels;
398 399
    dsbdesc.lpwfxFormat = &waveformat; 
 
Gildas Bazin's avatar
 
Gildas Bazin committed
400
    if( IDirectSound_CreateSoundBuffer( p_aout->output.p_sys->p_dsobject,
401
                                        &dsbdesc,
Gildas Bazin's avatar
 
Gildas Bazin committed
402
                                        &p_aout->output.p_sys->p_dsbuffer,
403 404 405
                                        NULL) != DS_OK )
    {
        msg_Warn( p_aout, "cannot create direct sound secondary buffer" );
Gildas Bazin's avatar
 
Gildas Bazin committed
406
        goto error;
407 408 409 410 411
    }

    /* backup the size of the secondary sound buffer */
    memset(&dsbcaps, 0, sizeof(DSBCAPS)); 
    dsbcaps.dwSize = sizeof(DSBCAPS);
Gildas Bazin's avatar
 
Gildas Bazin committed
412
    IDirectSoundBuffer_GetCaps( p_aout->output.p_sys->p_dsbuffer, &dsbcaps  );
413 414

    msg_Dbg( p_aout, "DirectxCreateSecondaryBuffer: %li",
Gildas Bazin's avatar
 
Gildas Bazin committed
415
             dsbcaps.dwBufferBytes );
416 417 418

    /* Now the secondary buffer is created, we need to setup its position
     * notification */
Gildas Bazin's avatar
 
Gildas Bazin committed
419 420 421
    p_aout->output.p_sys->p_notif->p_events[0].dwOffset = 0;
    p_aout->output.p_sys->p_notif->p_events[1].dwOffset =
        p_aout->output.p_sys->p_notif->i_buffer_size;
422 423

    /* Get the IDirectSoundNotify interface */
Gildas Bazin's avatar
 
Gildas Bazin committed
424 425 426 427
    if FAILED( IDirectSoundBuffer_QueryInterface(
                                p_aout->output.p_sys->p_dsbuffer,
                                &IID_IDirectSoundNotify,
                                (LPVOID *)&p_aout->output.p_sys->p_dsnotify ) )
428 429
    {
        msg_Warn( p_aout, "cannot get Notify interface" );
Gildas Bazin's avatar
 
Gildas Bazin committed
430
        goto error;
431 432 433
    }
        
    if FAILED( IDirectSoundNotify_SetNotificationPositions(
Gildas Bazin's avatar
 
Gildas Bazin committed
434 435
                                    p_aout->output.p_sys->p_dsnotify, 2,
                                    p_aout->output.p_sys->p_notif->p_events ) )
436 437
    {
        msg_Warn( p_aout, "cannot set position Notification" );
Gildas Bazin's avatar
 
Gildas Bazin committed
438
        goto error;
439 440
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
    p_aout->output.output.i_format = AOUT_FMT_S16_NE;
    p_aout->output.i_nb_samples = FRAME_SIZE;

    return 0;

 error:
    if( p_aout->output.p_sys->p_dsbuffer )
    {
        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
        p_aout->output.p_sys->p_dsbuffer = NULL;
    }
    if( p_aout->output.p_sys->p_dsnotify )
    {
        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
        p_aout->output.p_sys->p_dsnotify = NULL;
    }
    return 1;
458 459 460 461 462
}

/*****************************************************************************
 * DirectxCreateSecondaryBuffer
 *****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
463
 * This function destroys the secondary buffer.
464
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
465
static void DirectxDestroySecondaryBuffer( aout_instance_t *p_aout )
466 467
{
    /* make sure the buffer isn't playing */
Gildas Bazin's avatar
 
Gildas Bazin committed
468 469
    if( p_aout->output.p_sys->p_dsbuffer )
        IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer );
470

Gildas Bazin's avatar
 
Gildas Bazin committed
471
    if( p_aout->output.p_sys->p_dsnotify )
472
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
473 474
        IDirectSoundNotify_Release( p_aout->output.p_sys->p_dsnotify );
        p_aout->output.p_sys->p_dsnotify = NULL;
475 476
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
477
    if( p_aout->output.p_sys->p_dsbuffer )
478
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
479 480
        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
        p_aout->output.p_sys->p_dsbuffer = NULL;
481 482 483 484 485 486
    }
}

/*****************************************************************************
 * DirectSoundThread: this thread will capture play notification events. 
 *****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
487 488
 * We use this thread to emulate a callback mechanism. The thread probes for
 * event notification and fills up the DS secondary buffer when needed.
489 490 491 492 493
 *****************************************************************************/
static void DirectSoundThread( notification_thread_t *p_notif )
{
    HANDLE  notification_events[2];
    HRESULT dsresult;
Gildas Bazin's avatar
 
Gildas Bazin committed
494
    aout_instance_t *p_aout = p_notif->p_aout;
495

Gildas Bazin's avatar
 
Gildas Bazin committed
496 497
    notification_events[0] = p_notif->p_events[0].hEventNotify;
    notification_events[1] = p_notif->p_events[1].hEventNotify;
498 499 500 501 502 503 504 505

    /* Tell the main thread that we are ready */
    vlc_thread_ready( p_notif );

    /* this thread must be high-priority */
    if( !SetThreadPriority( GetCurrentThread(),
                            THREAD_PRIORITY_ABOVE_NORMAL ) )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
506
        msg_Warn( p_notif, "DirectSoundThread could not raise its priority" );
507 508 509 510 511 512
    }

    msg_Dbg( p_notif, "DirectSoundThread ready" );

    while( !p_notif->b_die )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
513 514 515 516 517
        int i_which_event;
        void *p_write_position, *p_wrap_around;
        long l_bytes1, l_bytes2;
        aout_buffer_t * p_buffer;

518
        /* wait for the position notification */
Gildas Bazin's avatar
 
Gildas Bazin committed
519 520 521 522
        i_which_event = WaitForMultipleObjects( 2, notification_events, 0,
                                                INFINITE ) - WAIT_OBJECT_0;

        vlc_mutex_lock( &p_aout->output.p_sys->buffer_lock );
523 524 525

        if( p_notif->b_die )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
526
            vlc_mutex_unlock( &p_aout->output.p_sys->buffer_lock );
527 528 529
            break;
        }

Gildas Bazin's avatar
 
Gildas Bazin committed
530 531 532 533 534 535 536 537 538 539 540 541 542
        /* Before copying anything, we have to lock the buffer */
        dsresult = IDirectSoundBuffer_Lock(
                                                                /* DS buffer */
            p_aout->output.p_sys->p_dsbuffer,
                                                     /* Offset of lock start */
            i_which_event ? 0 : p_notif->i_buffer_size,
            p_notif->i_buffer_size,                 /* Number of bytes */
            &p_write_position,                      /* Address of lock start */
            &l_bytes1,           /* Count of bytes locked before wrap around */
            &p_wrap_around,                /* Buffer adress (if wrap around) */
            &l_bytes2,                   /* Count of bytes after wrap around */
            0 );                                                    /* Flags */
        if( dsresult == DSERR_BUFFERLOST )
543
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
544 545 546 547 548 549 550 551 552 553
            IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
            dsresult = IDirectSoundBuffer_Lock(
                           p_aout->output.p_sys->p_dsbuffer,
                           i_which_event ? 0 : p_notif->i_buffer_size,
                           p_notif->i_buffer_size,
                           &p_write_position,
                           &l_bytes1,
                           &p_wrap_around,
                           &l_bytes2,
                           0 );
554
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
555
        if( dsresult != DS_OK )
556
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
557 558 559
            msg_Warn( p_notif, "cannot lock buffer" );
            vlc_mutex_unlock( &p_aout->output.p_sys->buffer_lock );
            continue;
560 561
        }

Gildas Bazin's avatar
 
Gildas Bazin committed
562 563
        /* FIXME : take into account DirectSound latency instead of mdate() */
        p_buffer = aout_OutputNextBuffer( p_aout, mdate() );
564

Gildas Bazin's avatar
 
Gildas Bazin committed
565 566 567 568
        /* Now do the actual memcpy into the circular buffer */
        if ( l_bytes1 != p_notif->i_buffer_size )
            msg_Err( p_aout, "Wrong buffer size: %d, %d", l_bytes1,
                     p_notif->i_buffer_size );
569

Gildas Bazin's avatar
 
Gildas Bazin committed
570
        if ( p_buffer != NULL )
571
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
572 573 574
            p_aout->p_vlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
                                      l_bytes1 );
            aout_BufferFree( p_buffer );
575
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
576
        else
577
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
578
            memset( p_write_position, 0, l_bytes1 );
579 580 581
        }

        /* Now the data has been copied, unlock the buffer */
Gildas Bazin's avatar
 
Gildas Bazin committed
582 583
        IDirectSoundBuffer_Unlock( p_aout->output.p_sys->p_dsbuffer,
                        p_write_position, l_bytes1, p_wrap_around, l_bytes2 );
584

Gildas Bazin's avatar
 
Gildas Bazin committed
585
        vlc_mutex_unlock( &p_aout->output.p_sys->buffer_lock );
586 587 588 589 590 591 592 593 594 595

    }

    /* free the events */
    CloseHandle( notification_events[0] );
    CloseHandle( notification_events[1] );

    msg_Dbg( p_notif, "DirectSoundThread exiting" );

}