directx.c 25.8 KB
Newer Older
1
/*****************************************************************************
2
 * directx.c: Windows DirectX audio output method
3 4
 *****************************************************************************
 * Copyright (C) 2001 VideoLAN
5
 * $Id: directx.c,v 1.11 2003/02/14 17:00:02 ipkiss 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>
gbazin's avatar
 
gbazin committed
35
#include "aout_internal.h"
36

gbazin's avatar
 
gbazin committed
37
#include <windows.h>
38 39 40
#include <mmsystem.h>
#include <dsound.h>

gbazin's avatar
 
gbazin committed
41
#define FRAME_SIZE 2048              /* The size is in samples, not in bytes */
gbazin's avatar
 
gbazin committed
42 43 44 45 46
#define FRAMES_NUM 4

/* frame buffer status */
#define FRAME_QUEUED 0
#define FRAME_EMPTY 1
gbazin's avatar
 
gbazin committed
47

48 49 50 51 52 53 54 55
/*****************************************************************************
 * 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);

gbazin's avatar
 
gbazin committed
56 57 58 59 60 61 62
/*****************************************************************************
 * Useful macros
 *****************************************************************************/
#ifndef WAVE_FORMAT_IEEE_FLOAT
#   define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif

63 64 65 66 67 68 69
/*****************************************************************************
 * notification_thread_t: DirectX event thread
 *****************************************************************************/
typedef struct notification_thread_t
{
    VLC_COMMON_MEMBERS

gbazin's avatar
 
gbazin committed
70
    aout_instance_t * p_aout;
gbazin's avatar
 
gbazin committed
71 72 73 74 75
    int i_frame_status[FRAMES_NUM];           /* status of each frame buffer */
    DSBPOSITIONNOTIFY p_events[FRAMES_NUM];      /* play notification events */
    int i_frame_size;                         /* Size in bytes of one frame */

    mtime_t start_date;
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

} 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;   /* 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 */

    notification_thread_t * p_notif;                 /* DirectSoundThread id */
gbazin's avatar
 
gbazin committed
98

gbazin's avatar
 
gbazin committed
99
    int b_playing;                                         /* playing status */
100 101 102 103 104
};

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
105 106
static int  OpenAudio  ( vlc_object_t * );
static void CloseAudio ( vlc_object_t * );
107

gbazin's avatar
 
gbazin committed
108
static void Play       ( aout_instance_t * );
109 110

/* local functions */
gbazin's avatar
 
gbazin committed
111 112 113
static int  DirectxCreateSecondaryBuffer ( aout_instance_t * );
static void DirectxDestroySecondaryBuffer( aout_instance_t * );
static int  DirectxInitDSound            ( aout_instance_t * );
114
static void DirectSoundThread            ( notification_thread_t * );
gbazin's avatar
 
gbazin committed
115 116
static int  DirectxFillBuffer            ( aout_instance_t *, int,
                                           aout_buffer_t * );
117

gbazin's avatar
 
gbazin committed
118 119 120 121 122
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
vlc_module_begin();
    set_description( _("DirectX audio module") );
123
    set_capability( "audio output", 50 );
gbazin's avatar
 
gbazin committed
124 125 126 127
    add_shortcut( "directx" );
    set_callbacks( OpenAudio, CloseAudio );
vlc_module_end();

128 129 130 131 132
/*****************************************************************************
 * OpenAudio: open the audio device
 *****************************************************************************
 * This function opens and setups Direct Sound.
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
133
static int OpenAudio( vlc_object_t *p_this )
134
{
gbazin's avatar
 
gbazin committed
135
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
gbazin's avatar
 
gbazin committed
136
    int i;
137 138 139 140

    msg_Dbg( p_aout, "Open" );

   /* Allocate structure */
gbazin's avatar
 
gbazin committed
141 142
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->output.p_sys == NULL )
143 144
    {
        msg_Err( p_aout, "out of memory" );
145
        return VLC_EGENERIC;
146 147 148
    }

    /* Initialize some variables */
gbazin's avatar
 
gbazin committed
149 150 151 152
    p_aout->output.p_sys->p_dsobject = 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;
gbazin's avatar
 
gbazin committed
153
    p_aout->output.p_sys->b_playing = 0;
gbazin's avatar
 
gbazin committed
154 155

    p_aout->output.pf_play = Play;
156
    aout_VolumeSoftInit( p_aout );
157 158 159 160

    /* Initialise DirectSound */
    if( DirectxInitDSound( p_aout ) )
    {
gbazin's avatar
 
gbazin committed
161 162
        msg_Err( p_aout, "cannot initialize DirectSound" );
        goto error;
163 164 165
    }

    /* Now we need to setup DirectSound play notification */
gbazin's avatar
 
gbazin committed
166 167 168
    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;
169 170

    /* first we need to create the notification events */
gbazin's avatar
 
gbazin committed
171 172 173
    for( i = 0; i < FRAMES_NUM; i++ )
        p_aout->output.p_sys->p_notif->p_events[i].hEventNotify =
            CreateEvent( NULL, FALSE, FALSE, NULL );
174 175

    /* then create a new secondary buffer */
gbazin's avatar
 
gbazin committed
176
    p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
177 178
    if( DirectxCreateSecondaryBuffer( p_aout ) )
    {
gbazin's avatar
 
gbazin committed
179
        msg_Dbg( p_aout, "cannot create WAVE_FORMAT_IEEE_FLOAT buffer" );
180

gbazin's avatar
 
gbazin committed
181 182 183 184 185 186 187
        p_aout->output.output.i_format = VLC_FOURCC('s','1','6','l');
        if( DirectxCreateSecondaryBuffer( p_aout ) )
        {
            msg_Err( p_aout, "cannot create WAVE_FORMAT_PCM buffer" );
            return 1;
        }
    }
188

Christophe Massiot's avatar
Christophe Massiot committed
189 190 191 192
    /* then launch the notification thread */
    msg_Dbg( p_aout, "creating DirectSoundThread" );
    if( vlc_thread_create( p_aout->output.p_sys->p_notif,
                           "DirectSound Notification Thread",
gbazin's avatar
 
gbazin committed
193
                           DirectSoundThread,
gbazin's avatar
 
gbazin committed
194
                           VLC_THREAD_PRIORITY_HIGHEST, 1 ) )
Christophe Massiot's avatar
Christophe Massiot committed
195 196 197 198 199 200 201
    {
        msg_Err( p_aout, "cannot create DirectSoundThread" );
        goto error;
    }

    vlc_object_attach( p_aout->output.p_sys->p_notif, p_aout );

gbazin's avatar
 
gbazin committed
202
    return 0;
Christophe Massiot's avatar
Christophe Massiot committed
203 204

 error:
gbazin's avatar
 
gbazin committed
205
    CloseAudio( VLC_OBJECT(p_aout) );
206
    return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
207 208 209
}

/*****************************************************************************
gbazin's avatar
 
gbazin committed
210 211 212
 * Play: we'll start playing the directsound buffer here because at least here
 *       we know the first buffer has been put in the aout fifo and we also
 *       know its date.
gbazin's avatar
 
gbazin committed
213
 *****************************************************************************/
214
static void Play( aout_instance_t *p_aout )
gbazin's avatar
 
gbazin committed
215
{
gbazin's avatar
 
gbazin committed
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
    if( !p_aout->output.p_sys->b_playing )
    {
        aout_buffer_t *p_buffer;

        p_aout->output.p_sys->b_playing = 1;

        /* get the playing date of the first aout buffer */
        p_aout->output.p_sys->p_notif->start_date =
            aout_FifoFirstDate( p_aout, &p_aout->output.fifo );

        /* fill in the first samples */
        p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
        DirectxFillBuffer( p_aout, 0, p_buffer );

        /* wake up the audio output thread */
        SetEvent( p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );
    }
233 234 235 236 237
}

/*****************************************************************************
 * CloseAudio: close the audio device
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
238
static void CloseAudio( vlc_object_t *p_this )
239
{
gbazin's avatar
 
gbazin committed
240
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
241 242 243 244

    msg_Dbg( p_aout, "Close" );

    /* kill the position notification thread, if any */
gbazin's avatar
 
gbazin committed
245
    if( p_aout->output.p_sys->p_notif )
246
    {
247
        vlc_object_detach( p_aout->output.p_sys->p_notif );
gbazin's avatar
 
gbazin committed
248 249 250
        if( p_aout->output.p_sys->p_notif->b_thread )
        {
            p_aout->output.p_sys->p_notif->b_die = 1;
gbazin's avatar
 
gbazin committed
251 252 253 254 255 256

            if( !p_aout->output.p_sys->b_playing )
                /* wake up the audio thread */
                SetEvent(
                    p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );

gbazin's avatar
 
gbazin committed
257 258 259
            vlc_thread_join( p_aout->output.p_sys->p_notif );
        }
        vlc_object_destroy( p_aout->output.p_sys->p_notif );
260 261 262 263 264 265
    }

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

    /* finally release the DirectSound object */
gbazin's avatar
 
gbazin committed
266 267
    if( p_aout->output.p_sys->p_dsobject )
        IDirectSound_Release( p_aout->output.p_sys->p_dsobject );
268 269
    
    /* free DSOUND.DLL */
gbazin's avatar
 
gbazin committed
270 271
    if( p_aout->output.p_sys->hdsound_dll )
       FreeLibrary( p_aout->output.p_sys->hdsound_dll );
272

273
    free( p_aout->output.p_sys );
274 275 276
}

/*****************************************************************************
gbazin's avatar
 
gbazin committed
277
 * DirectxInitDSound: handle all the gory details of DirectSound initialisation
278
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
279
static int DirectxInitDSound( aout_instance_t *p_aout )
280 281 282
{
    HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);

gbazin's avatar
 
gbazin committed
283 284
    p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
    if( p_aout->output.p_sys->hdsound_dll == NULL )
285
    {
gbazin's avatar
 
gbazin committed
286 287
        msg_Warn( p_aout, "cannot open DSOUND.DLL" );
        goto error;
288 289
    }

gbazin's avatar
 
gbazin committed
290 291 292
    OurDirectSoundCreate = (void *)GetProcAddress(
                                           p_aout->output.p_sys->hdsound_dll,
                                           "DirectSoundCreate" );
293 294
    if( OurDirectSoundCreate == NULL )
    {
gbazin's avatar
 
gbazin committed
295 296
        msg_Warn( p_aout, "GetProcAddress FAILED" );
        goto error;
297 298 299
    }

    /* Create the direct sound object */
gbazin's avatar
 
gbazin committed
300 301
    if( OurDirectSoundCreate( NULL, &p_aout->output.p_sys->p_dsobject, NULL )
        != DS_OK )
302 303
    {
        msg_Warn( p_aout, "cannot create a direct sound device" );
gbazin's avatar
 
gbazin committed
304
        goto error;
305 306 307 308 309 310 311 312 313 314 315 316
    }

    /* 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 */
gbazin's avatar
 
gbazin committed
317 318 319
    if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
                                          GetDesktopWindow(),
                                          DSSCL_EXCLUSIVE) )
320 321 322 323
    {
        msg_Warn( p_aout, "cannot set direct sound cooperative level" );
    }

gbazin's avatar
 
gbazin committed
324 325 326 327 328 329 330 331 332 333 334
    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;

335 336 337 338 339 340 341 342 343 344 345 346 347 348
}

/*****************************************************************************
 * 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.
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
349
static int DirectxCreateSecondaryBuffer( aout_instance_t *p_aout )
350 351 352 353
{
    WAVEFORMATEX         waveformat;
    DSBUFFERDESC         dsbdesc;
    DSBCAPS              dsbcaps;
gbazin's avatar
 
gbazin committed
354
    int                  i_nb_channels, i;
355

356
    i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
gbazin's avatar
 
gbazin committed
357
    if ( i_nb_channels >= 2 )
358 359
    {
        i_nb_channels = 2;
gbazin's avatar
 
gbazin committed
360 361 362 363 364 365 366 367
        p_aout->output.output.i_physical_channels =
            AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
    }
    else
    {
        i_nb_channels = 1;
        p_aout->output.output.i_physical_channels =
            AOUT_CHAN_CENTER;
368
    }
gbazin's avatar
 
gbazin committed
369

370
    /* First set the buffer format */
gbazin's avatar
 
gbazin committed
371
    memset( &waveformat, 0, sizeof(WAVEFORMATEX) );
gbazin's avatar
 
gbazin committed
372 373 374 375 376 377 378 379 380 381 382
    switch( p_aout->output.output.i_format )
    {
    case VLC_FOURCC('s','1','6','l'):
        waveformat.wFormatTag     = WAVE_FORMAT_PCM;
        waveformat.wBitsPerSample = 16;
        break;
    case VLC_FOURCC('f','l','3','2'):
        waveformat.wFormatTag     = WAVE_FORMAT_IEEE_FLOAT;
        waveformat.wBitsPerSample = sizeof(float) * 8;
        break;
    }
383
    waveformat.nChannels       = i_nb_channels;
gbazin's avatar
 
gbazin committed
384
    waveformat.nSamplesPerSec  = p_aout->output.output.i_rate;
385 386 387 388 389
    waveformat.nBlockAlign     = waveformat.wBitsPerSample / 8 *
                                 waveformat.nChannels;
    waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec *
                                     waveformat.nBlockAlign;

gbazin's avatar
 
gbazin committed
390 391
    aout_FormatPrepare( &p_aout->output.output );

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

gbazin's avatar
 
gbazin committed
411
    /* backup the size of a frame */
gbazin's avatar
 
gbazin committed
412
    p_aout->output.p_sys->p_notif->i_frame_size = FRAME_SIZE *
gbazin's avatar
 
gbazin committed
413
        p_aout->output.output.i_bytes_per_frame;
gbazin's avatar
 
gbazin committed
414 415

    memset(&dsbcaps, 0, sizeof(DSBCAPS));
416
    dsbcaps.dwSize = sizeof(DSBCAPS);
gbazin's avatar
 
gbazin committed
417
    IDirectSoundBuffer_GetCaps( p_aout->output.p_sys->p_dsbuffer, &dsbcaps  );
gbazin's avatar
 
gbazin committed
418
    msg_Dbg( p_aout, "requested %li bytes buffer and got %li bytes.",
gbazin's avatar
 
gbazin committed
419
             FRAMES_NUM * p_aout->output.p_sys->p_notif->i_frame_size,
gbazin's avatar
 
gbazin committed
420
             dsbcaps.dwBufferBytes );
421 422 423

    /* Now the secondary buffer is created, we need to setup its position
     * notification */
gbazin's avatar
 
gbazin committed
424 425 426 427 428 429 430
    for( i = 0; i < FRAMES_NUM; i++ )
    {
        p_aout->output.p_sys->p_notif->p_events[i].dwOffset = i *
            p_aout->output.p_sys->p_notif->i_frame_size;

        p_aout->output.p_sys->p_notif->i_frame_status[i] = FRAME_EMPTY;
    }
431 432

    /* Get the IDirectSoundNotify interface */
gbazin's avatar
 
gbazin committed
433 434 435 436
    if FAILED( IDirectSoundBuffer_QueryInterface(
                                p_aout->output.p_sys->p_dsbuffer,
                                &IID_IDirectSoundNotify,
                                (LPVOID *)&p_aout->output.p_sys->p_dsnotify ) )
437
    {
gbazin's avatar
 
gbazin committed
438
        msg_Err( p_aout, "cannot get Notify interface" );
gbazin's avatar
 
gbazin committed
439
        goto error;
440 441 442
    }
        
    if FAILED( IDirectSoundNotify_SetNotificationPositions(
gbazin's avatar
 
gbazin committed
443 444
                                    p_aout->output.p_sys->p_dsnotify,
                                    FRAMES_NUM,
gbazin's avatar
 
gbazin committed
445
                                    p_aout->output.p_sys->p_notif->p_events ) )
446
    {
gbazin's avatar
 
gbazin committed
447
        msg_Err( p_aout, "cannot set position Notification" );
gbazin's avatar
 
gbazin committed
448
        goto error;
449
    }
gbazin's avatar
 
gbazin committed
450

gbazin's avatar
 
gbazin committed
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
    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;
    }
466
    return VLC_EGENERIC;
467 468 469 470 471
}

/*****************************************************************************
 * DirectxCreateSecondaryBuffer
 *****************************************************************************
gbazin's avatar
 
gbazin committed
472
 * This function destroys the secondary buffer.
473
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
474
static void DirectxDestroySecondaryBuffer( aout_instance_t *p_aout )
475 476
{
    /* make sure the buffer isn't playing */
gbazin's avatar
 
gbazin committed
477 478
    if( p_aout->output.p_sys->p_dsbuffer )
        IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer );
479

gbazin's avatar
 
gbazin committed
480
    if( p_aout->output.p_sys->p_dsnotify )
481
    {
gbazin's avatar
 
gbazin committed
482 483
        IDirectSoundNotify_Release( p_aout->output.p_sys->p_dsnotify );
        p_aout->output.p_sys->p_dsnotify = NULL;
484 485
    }

gbazin's avatar
 
gbazin committed
486
    if( p_aout->output.p_sys->p_dsbuffer )
487
    {
gbazin's avatar
 
gbazin committed
488 489
        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
        p_aout->output.p_sys->p_dsbuffer = NULL;
490 491 492
    }
}

gbazin's avatar
 
gbazin committed
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
/*****************************************************************************
 * DirectxFillBuffer: Fill in one of the direct sound frame buffers.
 *****************************************************************************
 * Returns VLC_SUCCESS on success.
 *****************************************************************************/
static int DirectxFillBuffer( aout_instance_t *p_aout, int i_frame,
                              aout_buffer_t *p_buffer )
{
    notification_thread_t *p_notif = p_aout->output.p_sys->p_notif;
    void *p_write_position, *p_wrap_around;
    long l_bytes1, l_bytes2;
    HRESULT dsresult;

    /* Before copying anything, we have to lock the buffer */
    dsresult = IDirectSoundBuffer_Lock(
                p_aout->output.p_sys->p_dsbuffer,               /* DS buffer */
                i_frame * p_notif->i_frame_size,             /* Start offset */
                p_notif->i_frame_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 )
    {
        IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
        dsresult = IDirectSoundBuffer_Lock(
                               p_aout->output.p_sys->p_dsbuffer,
                               i_frame * p_notif->i_frame_size,
                               p_notif->i_frame_size,
                               &p_write_position,
                               &l_bytes1,
                               &p_wrap_around,
                               &l_bytes2,
                               0 );
    }
    if( dsresult != DS_OK )
    {
        msg_Warn( p_notif, "cannot lock buffer" );
gbazin's avatar
 
gbazin committed
532
        if( p_buffer ) aout_BufferFree( p_buffer );
gbazin's avatar
 
gbazin committed
533 534 535 536
        return VLC_EGENERIC;
    }

    if ( p_buffer != NULL )
gbazin's avatar
 
gbazin committed
537
    {
gbazin's avatar
 
gbazin committed
538 539
        p_aout->p_vlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
                                  l_bytes1 );
gbazin's avatar
 
gbazin committed
540 541
        aout_BufferFree( p_buffer );
    }
gbazin's avatar
 
gbazin committed
542 543 544 545 546 547 548 549 550 551 552
    else
        memset( p_write_position, 0, l_bytes1 );

    /* Now the data has been copied, unlock the buffer */
    IDirectSoundBuffer_Unlock( p_aout->output.p_sys->p_dsbuffer,
                               p_write_position, l_bytes1,
                               p_wrap_around, l_bytes2 );

    return VLC_SUCCESS;
}

553 554 555
/*****************************************************************************
 * DirectSoundThread: this thread will capture play notification events. 
 *****************************************************************************
gbazin's avatar
 
gbazin committed
556 557
 * We use this thread to emulate a callback mechanism. The thread probes for
 * event notification and fills up the DS secondary buffer when needed.
558 559 560
 *****************************************************************************/
static void DirectSoundThread( notification_thread_t *p_notif )
{
gbazin's avatar
 
gbazin committed
561
    HANDLE  notification_events[FRAMES_NUM];
562
    HRESULT dsresult;
gbazin's avatar
 
gbazin committed
563
    aout_instance_t *p_aout = p_notif->p_aout;
gbazin's avatar
 
gbazin committed
564 565
    int i, i_which_frame, i_last_frame, i_next_frame;
    mtime_t mtime;
566

gbazin's avatar
 
gbazin committed
567 568
    for( i = 0; i < FRAMES_NUM; i++ )
        notification_events[i] = p_notif->p_events[i].hEventNotify;
569 570 571 572 573 574

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

    msg_Dbg( p_notif, "DirectSoundThread ready" );

gbazin's avatar
 
gbazin committed
575 576
    /* Wait here until Play() is called */
    WaitForSingleObject( notification_events[0], INFINITE );
gbazin's avatar
 
gbazin committed
577

gbazin's avatar
 
gbazin committed
578 579 580
    if( !p_notif->b_die )
    {
        mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 );
581

gbazin's avatar
 
gbazin committed
582 583 584 585 586
        /* start playing the buffer */
        dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer,
                                        0,                         /* Unused */
                                        0,                         /* Unused */
                                        DSBPLAY_LOOPING );          /* Flags */
gbazin's avatar
 
gbazin committed
587
        if( dsresult == DSERR_BUFFERLOST )
588
        {
gbazin's avatar
 
gbazin committed
589
            IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
gbazin's avatar
 
gbazin committed
590 591 592 593 594
            dsresult = IDirectSoundBuffer_Play(
                                            p_aout->output.p_sys->p_dsbuffer,
                                            0,                     /* Unused */
                                            0,                     /* Unused */
                                            DSBPLAY_LOOPING );      /* Flags */
595
        }
gbazin's avatar
 
gbazin committed
596
        if( dsresult != DS_OK )
597
        {
gbazin's avatar
 
gbazin committed
598
            msg_Err( p_aout, "cannot start playing buffer" );
599
        }
gbazin's avatar
 
gbazin committed
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
    }

    while( !p_notif->b_die )
    {
        aout_buffer_t *p_buffer;
        long l_latency;

        /* wait for the position notification */
        i_which_frame = WaitForMultipleObjects( FRAMES_NUM,
                                                notification_events, 0,
                                                INFINITE ) - WAIT_OBJECT_0;

        if( p_notif->b_die )
            break;

        mtime = mdate();
616

gbazin's avatar
 
gbazin committed
617 618 619
        /* We take into account the current latency */
        if( IDirectSoundBuffer_GetCurrentPosition(
                p_aout->output.p_sys->p_dsbuffer,
gbazin's avatar
 
gbazin committed
620
                &l_latency, NULL ) == DS_OK )
gbazin's avatar
 
gbazin committed
621
        {
gbazin's avatar
 
gbazin committed
622 623
            if( l_latency > (i_which_frame * FRAME_SIZE)
                  && l_latency < ((i_which_frame+1) * FRAME_SIZE) )
gbazin's avatar
 
gbazin committed
624
            {
gbazin's avatar
 
gbazin committed
625 626 627
                l_latency = - ( l_latency /
                                p_aout->output.output.i_bytes_per_frame %
                                FRAME_SIZE );
gbazin's avatar
 
gbazin committed
628 629 630
            }
            else
            {
gbazin's avatar
 
gbazin committed
631
                l_latency = FRAME_SIZE - ( l_latency /
gbazin's avatar
 
gbazin committed
632 633 634 635 636 637
                                      p_aout->output.output.i_bytes_per_frame %
                                      FRAME_SIZE );
            }
        }
        else
        {
gbazin's avatar
 
gbazin committed
638
            l_latency = 0;
gbazin's avatar
 
gbazin committed
639 640
        }

gbazin's avatar
 
gbazin committed
641 642 643 644
        /* Mark last frame as empty */
        i_last_frame = (i_which_frame + FRAMES_NUM -1) % FRAMES_NUM;
        i_next_frame = (i_which_frame + 1) % FRAMES_NUM;
        p_notif->i_frame_status[i_last_frame] = FRAME_EMPTY;
645

gbazin's avatar
 
gbazin committed
646 647
        /* Try to fill in as many frame buffers as possible */
        for( i = i_next_frame; (i % FRAMES_NUM) != i_which_frame; i++ )
648 649
        {

gbazin's avatar
 
gbazin committed
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
            /* Check if frame buf is already filled */
            if( p_notif->i_frame_status[i % FRAMES_NUM] == FRAME_QUEUED )
                continue;

            p_buffer = aout_OutputNextBuffer( p_aout,
                mtime + 1000000 / p_aout->output.output.i_rate *
                ((i - i_next_frame + 1) * FRAME_SIZE + l_latency), VLC_FALSE );

            /* If there is no audio data available and we have some buffered
             * already, then just wait for the next time */
            if( !p_buffer && (i != i_next_frame) )
            {
                //msg_Err( p_aout, "only %i frame buffers filled!",
                //         i - i_next_frame );
                break;
            }

            if( DirectxFillBuffer( p_aout, (i%FRAMES_NUM), p_buffer )
                != VLC_SUCCESS )
                break;

            /* Mark the frame buffer as QUEUED */
            p_notif->i_frame_status[i%FRAMES_NUM] = FRAME_QUEUED;
        }
674 675 676 677

    }

    /* free the events */
gbazin's avatar
 
gbazin committed
678 679
    for( i = 0; i < FRAMES_NUM; i++ )
        CloseHandle( notification_events[i] );
680 681 682

    msg_Dbg( p_notif, "DirectSoundThread exiting" );
}