directx.c 25.8 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.10 2003/01/05 13:39:32 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

} 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
123
124
125
126
127
/*****************************************************************************
 * 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();

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" );
}