directx.c 26.9 KB
Newer Older
1
/*****************************************************************************
2
 * directx.c: Windows DirectX audio output method
3
4
 *****************************************************************************
 * Copyright (C) 2001 VideoLAN
gbazin's avatar
   
gbazin committed
5
 * $Id: directx.c,v 1.8 2002/11/15 16:27:10 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>
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
98
99
100

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

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

gbazin's avatar
   
gbazin committed
102
    int b_playing;                                         /* playing status */
103
104
105
106
107
};

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
108
109
static int  OpenAudio  ( vlc_object_t * );
static void CloseAudio ( vlc_object_t * );
110

gbazin's avatar
   
gbazin committed
111
static void Play       ( aout_instance_t * );
112
113

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

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

131
132
133
134
135
/*****************************************************************************
 * OpenAudio: open the audio device
 *****************************************************************************
 * This function opens and setups Direct Sound.
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
136
static int OpenAudio( vlc_object_t *p_this )
137
{
gbazin's avatar
   
gbazin committed
138
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
139
140
    HRESULT dsresult;
    DSBUFFERDESC dsbuffer_desc;
gbazin's avatar
   
gbazin committed
141
    int i;
142
143
144
145

    msg_Dbg( p_aout, "Open" );

   /* Allocate structure */
gbazin's avatar
   
gbazin committed
146
147
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->output.p_sys == NULL )
148
149
    {
        msg_Err( p_aout, "out of memory" );
150
        return VLC_EGENERIC;
151
152
153
    }

    /* Initialize some variables */
gbazin's avatar
   
gbazin committed
154
155
156
157
158
    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;
gbazin's avatar
   
gbazin committed
159
    p_aout->output.p_sys->b_playing = 0;
gbazin's avatar
   
gbazin committed
160
161

    p_aout->output.pf_play = Play;
162
    aout_VolumeSoftInit( p_aout );
163
164
165
166

    /* Initialise DirectSound */
    if( DirectxInitDSound( p_aout ) )
    {
gbazin's avatar
   
gbazin committed
167
168
        msg_Err( p_aout, "cannot initialize DirectSound" );
        goto error;
169
170
171
172
173
174
175
    }

    /* 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" );
gbazin's avatar
   
gbazin committed
176
177
178
179
    dsresult = IDirectSound_CreateSoundBuffer(p_aout->output.p_sys->p_dsobject,
                                     &dsbuffer_desc,
                                     &p_aout->output.p_sys->p_dsbuffer_primary,
                                     NULL);
180
181
    if( dsresult != DS_OK )
    {
gbazin's avatar
   
gbazin committed
182
183
        msg_Err( p_aout, "cannot create direct sound primary buffer" );
        goto error;
184
185
186
    }

    /* Now we need to setup DirectSound play notification */
gbazin's avatar
   
gbazin committed
187
188
189
    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;
190
191

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

    /* then create a new secondary buffer */
gbazin's avatar
   
gbazin committed
197
    p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
198
199
    if( DirectxCreateSecondaryBuffer( p_aout ) )
    {
gbazin's avatar
   
gbazin committed
200
        msg_Err( p_aout, "cannot create WAVE_FORMAT_IEEE_FLOAT buffer" );
201

gbazin's avatar
   
gbazin committed
202
203
204
205
206
207
208
        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;
        }
    }
209

Christophe Massiot's avatar
Christophe Massiot committed
210
211
212
213
    /* 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
214
215
                           DirectSoundThread,
                           THREAD_PRIORITY_TIME_CRITICAL, 1 ) )
Christophe Massiot's avatar
Christophe Massiot committed
216
217
218
219
220
221
222
    {
        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
223
    return 0;
Christophe Massiot's avatar
Christophe Massiot committed
224
225

 error:
gbazin's avatar
   
gbazin committed
226
    CloseAudio( VLC_OBJECT(p_aout) );
227
    return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
228
229
230
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
231
232
233
 * 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
234
 *****************************************************************************/
235
static void Play( aout_instance_t *p_aout )
gbazin's avatar
   
gbazin committed
236
{
gbazin's avatar
   
gbazin committed
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
    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 );
    }
254
255
256
257
258
}

/*****************************************************************************
 * CloseAudio: close the audio device
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
259
static void CloseAudio( vlc_object_t *p_this )
260
{
gbazin's avatar
   
gbazin 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 */
gbazin's avatar
   
gbazin committed
266
    if( p_aout->output.p_sys->p_notif )
267
    {
268
        vlc_object_detach( p_aout->output.p_sys->p_notif );
gbazin's avatar
   
gbazin committed
269
270
271
        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
272
273
274
275
276
277

            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
278
279
280
            vlc_thread_join( p_aout->output.p_sys->p_notif );
        }
        vlc_object_destroy( p_aout->output.p_sys->p_notif );
281
282
283
284
285
286
    }

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

    /* then release the primary buffer */
gbazin's avatar
   
gbazin committed
287
288
    if( p_aout->output.p_sys->p_dsbuffer_primary )
        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer_primary );
289
290

    /* finally release the DirectSound object */
gbazin's avatar
   
gbazin committed
291
292
    if( p_aout->output.p_sys->p_dsobject )
        IDirectSound_Release( p_aout->output.p_sys->p_dsobject );
293
294
    
    /* free DSOUND.DLL */
gbazin's avatar
   
gbazin committed
295
296
    if( p_aout->output.p_sys->hdsound_dll )
       FreeLibrary( p_aout->output.p_sys->hdsound_dll );
297

298
    free( p_aout->output.p_sys );
299
300
301
}

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

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

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

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

    /* 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
342
343
344
    if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
                                          GetDesktopWindow(),
                                          DSSCL_EXCLUSIVE) )
345
346
347
348
    {
        msg_Warn( p_aout, "cannot set direct sound cooperative level" );
    }

gbazin's avatar
   
gbazin committed
349
350
351
352
353
354
355
356
357
358
359
    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;

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

/*****************************************************************************
 * 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
374
static int DirectxCreateSecondaryBuffer( aout_instance_t *p_aout )
375
376
377
378
{
    WAVEFORMATEX         waveformat;
    DSBUFFERDESC         dsbdesc;
    DSBCAPS              dsbcaps;
gbazin's avatar
   
gbazin committed
379
    int                  i_nb_channels, i;
380

381
    i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
gbazin's avatar
   
gbazin committed
382
    if ( i_nb_channels >= 2 )
383
384
    {
        i_nb_channels = 2;
gbazin's avatar
   
gbazin committed
385
386
387
388
389
390
391
392
        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;
393
    }
gbazin's avatar
   
gbazin committed
394

395
    /* First set the buffer format */
gbazin's avatar
   
gbazin committed
396
    memset( &waveformat, 0, sizeof(WAVEFORMATEX) );
gbazin's avatar
   
gbazin committed
397
398
399
400
401
402
403
404
405
406
407
    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;
    }
408
    waveformat.nChannels       = i_nb_channels;
gbazin's avatar
   
gbazin committed
409
    waveformat.nSamplesPerSec  = p_aout->output.output.i_rate;
410
411
412
413
414
    waveformat.nBlockAlign     = waveformat.wBitsPerSample / 8 *
                                 waveformat.nChannels;
    waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec *
                                     waveformat.nBlockAlign;

gbazin's avatar
   
gbazin committed
415
416
    aout_FormatPrepare( &p_aout->output.output );

417
    /* Then fill in the descriptor */
gbazin's avatar
   
gbazin committed
418
419
    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
    dsbdesc.dwSize = sizeof(DSBUFFERDESC);
420
421
422
    dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
                    | DSBCAPS_CTRLPOSITIONNOTIFY     /* We need notification */
                    | DSBCAPS_GLOBALFOCUS;      /* Allows background playing */
gbazin's avatar
   
gbazin committed
423
    dsbdesc.dwBufferBytes = FRAME_SIZE * FRAMES_NUM           /* buffer size */
gbazin's avatar
   
gbazin committed
424
                            * p_aout->output.output.i_bytes_per_frame;
gbazin's avatar
   
gbazin committed
425
    dsbdesc.lpwfxFormat = &waveformat;
426
 
gbazin's avatar
   
gbazin committed
427
    if( IDirectSound_CreateSoundBuffer( p_aout->output.p_sys->p_dsobject,
428
                                        &dsbdesc,
gbazin's avatar
   
gbazin committed
429
                                        &p_aout->output.p_sys->p_dsbuffer,
430
431
432
                                        NULL) != DS_OK )
    {
        msg_Warn( p_aout, "cannot create direct sound secondary buffer" );
gbazin's avatar
   
gbazin committed
433
        goto error;
434
435
    }

gbazin's avatar
   
gbazin committed
436
    /* backup the size of a frame */
gbazin's avatar
   
gbazin committed
437
    p_aout->output.p_sys->p_notif->i_frame_size = FRAME_SIZE *
gbazin's avatar
   
gbazin committed
438
        p_aout->output.output.i_bytes_per_frame;
gbazin's avatar
   
gbazin committed
439
440

    memset(&dsbcaps, 0, sizeof(DSBCAPS));
441
    dsbcaps.dwSize = sizeof(DSBCAPS);
gbazin's avatar
   
gbazin committed
442
    IDirectSoundBuffer_GetCaps( p_aout->output.p_sys->p_dsbuffer, &dsbcaps  );
gbazin's avatar
   
gbazin committed
443
    msg_Dbg( p_aout, "requested %li bytes buffer and got %li bytes.",
gbazin's avatar
   
gbazin committed
444
             FRAMES_NUM * p_aout->output.p_sys->p_notif->i_frame_size,
gbazin's avatar
   
gbazin committed
445
             dsbcaps.dwBufferBytes );
446
447
448

    /* Now the secondary buffer is created, we need to setup its position
     * notification */
gbazin's avatar
   
gbazin committed
449
450
451
452
453
454
455
    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;
    }
456
457

    /* Get the IDirectSoundNotify interface */
gbazin's avatar
   
gbazin committed
458
459
460
461
    if FAILED( IDirectSoundBuffer_QueryInterface(
                                p_aout->output.p_sys->p_dsbuffer,
                                &IID_IDirectSoundNotify,
                                (LPVOID *)&p_aout->output.p_sys->p_dsnotify ) )
462
    {
gbazin's avatar
   
gbazin committed
463
        msg_Err( p_aout, "cannot get Notify interface" );
gbazin's avatar
   
gbazin committed
464
        goto error;
465
466
467
    }
        
    if FAILED( IDirectSoundNotify_SetNotificationPositions(
gbazin's avatar
   
gbazin committed
468
469
                                    p_aout->output.p_sys->p_dsnotify,
                                    FRAMES_NUM,
gbazin's avatar
   
gbazin committed
470
                                    p_aout->output.p_sys->p_notif->p_events ) )
471
    {
gbazin's avatar
   
gbazin committed
472
        msg_Err( p_aout, "cannot set position Notification" );
gbazin's avatar
   
gbazin committed
473
        goto error;
474
    }
gbazin's avatar
   
gbazin committed
475

gbazin's avatar
   
gbazin committed
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
    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;
    }
491
    return VLC_EGENERIC;
492
493
494
495
496
}

/*****************************************************************************
 * DirectxCreateSecondaryBuffer
 *****************************************************************************
gbazin's avatar
   
gbazin committed
497
 * This function destroys the secondary buffer.
498
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
499
static void DirectxDestroySecondaryBuffer( aout_instance_t *p_aout )
500
501
{
    /* make sure the buffer isn't playing */
gbazin's avatar
   
gbazin committed
502
503
    if( p_aout->output.p_sys->p_dsbuffer )
        IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer );
504

gbazin's avatar
   
gbazin committed
505
    if( p_aout->output.p_sys->p_dsnotify )
506
    {
gbazin's avatar
   
gbazin committed
507
508
        IDirectSoundNotify_Release( p_aout->output.p_sys->p_dsnotify );
        p_aout->output.p_sys->p_dsnotify = NULL;
509
510
    }

gbazin's avatar
   
gbazin committed
511
    if( p_aout->output.p_sys->p_dsbuffer )
512
    {
gbazin's avatar
   
gbazin committed
513
514
        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
        p_aout->output.p_sys->p_dsbuffer = NULL;
515
516
517
    }
}

gbazin's avatar
   
gbazin committed
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
/*****************************************************************************
 * 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
557
        if( p_buffer ) aout_BufferFree( p_buffer );
gbazin's avatar
   
gbazin committed
558
559
560
561
        return VLC_EGENERIC;
    }

    if ( p_buffer != NULL )
gbazin's avatar
   
gbazin committed
562
    {
gbazin's avatar
   
gbazin committed
563
564
        p_aout->p_vlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
                                  l_bytes1 );
gbazin's avatar
   
gbazin committed
565
566
        aout_BufferFree( p_buffer );
    }
gbazin's avatar
   
gbazin committed
567
568
569
570
571
572
573
574
575
576
577
    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;
}

578
579
580
/*****************************************************************************
 * DirectSoundThread: this thread will capture play notification events. 
 *****************************************************************************
gbazin's avatar
   
gbazin committed
581
582
 * We use this thread to emulate a callback mechanism. The thread probes for
 * event notification and fills up the DS secondary buffer when needed.
583
584
585
 *****************************************************************************/
static void DirectSoundThread( notification_thread_t *p_notif )
{
gbazin's avatar
   
gbazin committed
586
    HANDLE  notification_events[FRAMES_NUM];
587
    HRESULT dsresult;
gbazin's avatar
   
gbazin committed
588
    aout_instance_t *p_aout = p_notif->p_aout;
gbazin's avatar
   
gbazin committed
589
590
    int i, i_which_frame, i_last_frame, i_next_frame;
    mtime_t mtime;
591

gbazin's avatar
   
gbazin committed
592
593
    for( i = 0; i < FRAMES_NUM; i++ )
        notification_events[i] = p_notif->p_events[i].hEventNotify;
594
595
596
597
598
599

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

    msg_Dbg( p_notif, "DirectSoundThread ready" );

gbazin's avatar
   
gbazin committed
600
601
    /* Wait here until Play() is called */
    WaitForSingleObject( notification_events[0], INFINITE );
gbazin's avatar
   
gbazin committed
602

gbazin's avatar
   
gbazin committed
603
604
605
    if( !p_notif->b_die )
    {
        mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 );
606

gbazin's avatar
   
gbazin committed
607
608
609
610
611
        /* 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
612
        if( dsresult == DSERR_BUFFERLOST )
613
        {
gbazin's avatar
   
gbazin committed
614
            IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
gbazin's avatar
   
gbazin committed
615
616
617
618
619
            dsresult = IDirectSoundBuffer_Play(
                                            p_aout->output.p_sys->p_dsbuffer,
                                            0,                     /* Unused */
                                            0,                     /* Unused */
                                            DSBPLAY_LOOPING );      /* Flags */
620
        }
gbazin's avatar
   
gbazin committed
621
        if( dsresult != DS_OK )
622
        {
gbazin's avatar
   
gbazin committed
623
            msg_Err( p_aout, "cannot start playing buffer" );
624
        }
gbazin's avatar
   
gbazin committed
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
    }

    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();
641

gbazin's avatar
   
gbazin committed
642
643
644
        /* We take into account the current latency */
        if( IDirectSoundBuffer_GetCurrentPosition(
                p_aout->output.p_sys->p_dsbuffer,
gbazin's avatar
   
gbazin committed
645
                &l_latency, NULL ) == DS_OK )
gbazin's avatar
   
gbazin committed
646
        {
gbazin's avatar
   
gbazin committed
647
648
            if( l_latency > (i_which_frame * FRAME_SIZE)
                  && l_latency < ((i_which_frame+1) * FRAME_SIZE) )
gbazin's avatar
   
gbazin committed
649
            {
gbazin's avatar
   
gbazin committed
650
651
652
                l_latency = - ( l_latency /
                                p_aout->output.output.i_bytes_per_frame %
                                FRAME_SIZE );
gbazin's avatar
   
gbazin committed
653
654
655
            }
            else
            {
gbazin's avatar
   
gbazin committed
656
                l_latency = FRAME_SIZE - ( l_latency /
gbazin's avatar
   
gbazin committed
657
658
659
660
661
662
                                      p_aout->output.output.i_bytes_per_frame %
                                      FRAME_SIZE );
            }
        }
        else
        {
gbazin's avatar
   
gbazin committed
663
            l_latency = 0;
gbazin's avatar
   
gbazin committed
664
665
        }

gbazin's avatar
   
gbazin committed
666
667
668
669
        /* 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;
670

gbazin's avatar
   
gbazin committed
671
672
        /* Try to fill in as many frame buffers as possible */
        for( i = i_next_frame; (i % FRAMES_NUM) != i_which_frame; i++ )
673
674
        {

gbazin's avatar
   
gbazin committed
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
            /* 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;
        }
699
700
701
702

    }

    /* free the events */
gbazin's avatar
   
gbazin committed
703
704
    for( i = 0; i < FRAMES_NUM; i++ )
        CloseHandle( notification_events[i] );
705
706
707

    msg_Dbg( p_notif, "DirectSoundThread exiting" );
}