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>
Gildas Bazin's avatar
 
Gildas Bazin committed
35
#include "aout_internal.h"
36

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

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

/* frame buffer status */
#define FRAME_QUEUED 0
#define FRAME_EMPTY 1
Gildas Bazin's avatar
 
Gildas Bazin 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);

Gildas Bazin's avatar
 
Gildas Bazin 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

Gildas Bazin's avatar
 
Gildas Bazin committed
70
    aout_instance_t * p_aout;
Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin committed
98

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

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

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

/* local functions */
Gildas Bazin's avatar
 
Gildas Bazin 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 * );
Gildas Bazin's avatar
 
Gildas Bazin committed
115 116
static int  DirectxFillBuffer            ( aout_instance_t *, int,
                                           aout_buffer_t * );
117

Gildas Bazin's avatar
 
Gildas Bazin committed
118 119 120 121 122
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
vlc_module_begin();
    set_description( _("DirectX audio module") );
123
    set_capability( "audio output", 50 );
Gildas Bazin's avatar
 
Gildas Bazin 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.
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
133
static int OpenAudio( vlc_object_t *p_this )
134
{
Gildas Bazin's avatar
 
Gildas Bazin committed
135
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
Gildas Bazin's avatar
 
Gildas Bazin committed
136
    int i;
137 138 139 140

    msg_Dbg( p_aout, "Open" );

   /* Allocate structure */
Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin 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;
Gildas Bazin's avatar
 
Gildas Bazin committed
153
    p_aout->output.p_sys->b_playing = 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
154 155

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

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

    /* Now we need to setup DirectSound play notification */
Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin committed
176
    p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
177 178
    if( DirectxCreateSecondaryBuffer( p_aout ) )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
179
        msg_Dbg( p_aout, "cannot create WAVE_FORMAT_IEEE_FLOAT buffer" );
180

Gildas Bazin's avatar
 
Gildas Bazin 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",
Gildas Bazin's avatar
 
Gildas Bazin committed
193
                           DirectSoundThread,
Gildas Bazin's avatar
 
Gildas Bazin 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 );

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

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

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin 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.
Gildas Bazin's avatar
 
Gildas Bazin committed
213
 *****************************************************************************/
214
static void Play( aout_instance_t *p_aout )
Gildas Bazin's avatar
 
Gildas Bazin committed
215
{
Gildas Bazin's avatar
 
Gildas Bazin 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
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
238
static void CloseAudio( vlc_object_t *p_this )
239
{
Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin committed
245
    if( p_aout->output.p_sys->p_notif )
246
    {
247
        vlc_object_detach( p_aout->output.p_sys->p_notif );
Gildas Bazin's avatar
 
Gildas Bazin committed
248 249 250
        if( p_aout->output.p_sys->p_notif->b_thread )
        {
            p_aout->output.p_sys->p_notif->b_die = 1;
Gildas Bazin's avatar
 
Gildas Bazin 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 );

Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin 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
}

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

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

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

    /* Create the direct sound object */
Gildas Bazin's avatar
 
Gildas Bazin 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" );
Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin 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" );
    }

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

356
    i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
Gildas Bazin's avatar
 
Gildas Bazin committed
357
    if ( i_nb_channels >= 2 )
358 359
    {
        i_nb_channels = 2;
Gildas Bazin's avatar
 
Gildas Bazin 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
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
369

370
    /* First set the buffer format */
Gildas Bazin's avatar
 
Gildas Bazin committed
371
    memset( &waveformat, 0, sizeof(WAVEFORMATEX) );
Gildas Bazin's avatar
 
Gildas Bazin 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;
Gildas Bazin's avatar
 
Gildas Bazin 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;

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

392
    /* Then fill in the descriptor */
Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin committed
398
    dsbdesc.dwBufferBytes = FRAME_SIZE * FRAMES_NUM           /* buffer size */
Gildas Bazin's avatar
 
Gildas Bazin committed
399
                            * p_aout->output.output.i_bytes_per_frame;
Gildas Bazin's avatar
 
Gildas Bazin committed
400
    dsbdesc.lpwfxFormat = &waveformat;
401
 
Gildas Bazin's avatar
 
Gildas Bazin committed
402
    if( IDirectSound_CreateSoundBuffer( p_aout->output.p_sys->p_dsobject,
403
                                        &dsbdesc,
Gildas Bazin's avatar
 
Gildas Bazin 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" );
Gildas Bazin's avatar
 
Gildas Bazin committed
408
        goto error;
409 410
    }

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

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

    /* Now the secondary buffer is created, we need to setup its position
     * notification */
Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin 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
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
438
        msg_Err( p_aout, "cannot get Notify interface" );
Gildas Bazin's avatar
 
Gildas Bazin committed
439
        goto error;
440 441 442
    }
        
    if FAILED( IDirectSoundNotify_SetNotificationPositions(
Gildas Bazin's avatar
 
Gildas Bazin committed
443 444
                                    p_aout->output.p_sys->p_dsnotify,
                                    FRAMES_NUM,
Gildas Bazin's avatar
 
Gildas Bazin committed
445
                                    p_aout->output.p_sys->p_notif->p_events ) )
446
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
447
        msg_Err( p_aout, "cannot set position Notification" );
Gildas Bazin's avatar
 
Gildas Bazin committed
448
        goto error;
449
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
450

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

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

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

Gildas Bazin's avatar
 
Gildas Bazin 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" );
Gildas Bazin's avatar
 
Gildas Bazin committed
532
        if( p_buffer ) aout_BufferFree( p_buffer );
Gildas Bazin's avatar
 
Gildas Bazin committed
533 534 535 536
        return VLC_EGENERIC;
    }

    if ( p_buffer != NULL )
Gildas Bazin's avatar
 
Gildas Bazin committed
537
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
538 539
        p_aout->p_vlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
                                  l_bytes1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
540 541
        aout_BufferFree( p_buffer );
    }
Gildas Bazin's avatar
 
Gildas Bazin 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. 
 *****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin 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 )
{
Gildas Bazin's avatar
 
Gildas Bazin committed
561
    HANDLE  notification_events[FRAMES_NUM];
562
    HRESULT dsresult;
Gildas Bazin's avatar
 
Gildas Bazin committed
563
    aout_instance_t *p_aout = p_notif->p_aout;
Gildas Bazin's avatar
 
Gildas Bazin committed
564 565
    int i, i_which_frame, i_last_frame, i_next_frame;
    mtime_t mtime;
566

Gildas Bazin's avatar
 
Gildas Bazin 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" );

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

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

Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin committed
587
        if( dsresult == DSERR_BUFFERLOST )
588
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
589
            IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
Gildas Bazin's avatar
 
Gildas Bazin committed
590 591 592 593 594
            dsresult = IDirectSoundBuffer_Play(
                                            p_aout->output.p_sys->p_dsbuffer,
                                            0,                     /* Unused */
                                            0,                     /* Unused */
                                            DSBPLAY_LOOPING );      /* Flags */
595
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
596
        if( dsresult != DS_OK )
597
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
598
            msg_Err( p_aout, "cannot start playing buffer" );
599
        }
Gildas Bazin's avatar
 
Gildas Bazin 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

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

Gildas Bazin's avatar
 
Gildas Bazin 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

Gildas Bazin's avatar
 
Gildas Bazin 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
        {

Gildas Bazin's avatar
 
Gildas Bazin 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 */
Gildas Bazin's avatar
 
Gildas Bazin committed
678 679
    for( i = 0; i < FRAMES_NUM; i++ )
        CloseHandle( notification_events[i] );
680 681 682

    msg_Dbg( p_notif, "DirectSoundThread exiting" );
}