]> git.sesse.net Git - vlc/blob - plugins/directx/aout_directx.c
95deadd41125de3c1df0529f9d5b0363c61c1a8c
[vlc] / plugins / directx / aout_directx.c
1 /*****************************************************************************
2  * aout_directx.c: Windows DirectX audio output method
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: aout_directx.c,v 1.22 2002/06/01 12:31:58 sam Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <errno.h>                                                 /* ENOMEM */
28 #include <fcntl.h>                                       /* open(), O_WRONLY */
29 #include <string.h>                                            /* strerror() */
30
31 #include <stdlib.h>                            /* calloc(), malloc(), free() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/aout.h>
35
36 #include <mmsystem.h>
37 #include <dsound.h>
38
39 /*****************************************************************************
40  * DirectSound GUIDs.
41  * Defining them here allows us to get rid of the dxguid library during
42  * the linking stage.
43  *****************************************************************************/
44 #include <initguid.h>
45 DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
46
47 /*****************************************************************************
48  * aout_sys_t: directx audio output method descriptor
49  *****************************************************************************
50  * This structure is part of the audio output thread descriptor.
51  * It describes the direct sound specific properties of an audio device.
52  *****************************************************************************/
53
54 struct aout_sys_s
55 {
56     LPDIRECTSOUND       p_dsobject;              /* main Direct Sound object */
57
58     LPDIRECTSOUNDBUFFER p_dsbuffer_primary;     /* the actual sound card buffer
59                                                    (not used directly) */
60
61     LPDIRECTSOUNDBUFFER p_dsbuffer;   /* the sound buffer we use (direct sound
62                                        * takes care of mixing all the
63                                        * secondary buffers into the primary) */
64
65     LPDIRECTSOUNDNOTIFY p_dsnotify;         /* the position notify interface */
66
67     HINSTANCE           hdsound_dll;      /* handle of the opened dsound dll */
68
69     long l_buffer_size;                       /* secondary sound buffer size */
70     long l_write_position;             /* next write position for the buffer */
71
72     volatile vlc_bool_t b_buffer_underflown;   /* buffer underflow detection */
73     volatile long l_data_played_from_beginning;   /* for underflow detection */
74     volatile long l_data_written_from_beginning;  /* for underflow detection */
75
76     vlc_mutex_t buffer_lock;                            /* audio buffer lock */
77
78     vlc_thread_t notification_thread_id;             /* DirectSoundThread id */
79
80     DSBPOSITIONNOTIFY notification_events[2];    /* play notification events */
81
82     vlc_bool_t b_notification_thread_die;         /* flag to kill the thread */
83 };
84
85 /*****************************************************************************
86  * Local prototypes.
87  *****************************************************************************/
88 static int     aout_Open        ( aout_thread_t *p_aout );
89 static int     aout_SetFormat   ( aout_thread_t *p_aout );
90 static int     aout_GetBufInfo  ( aout_thread_t *p_aout, int i_buffer_info );
91 static void    aout_Play        ( aout_thread_t *p_aout,
92                                   byte_t *buffer, int i_size );
93 static void    aout_Close       ( aout_thread_t *p_aout );
94
95 /* local functions */
96 static int  DirectxCreateSecondaryBuffer ( aout_thread_t *p_aout );
97 static void DirectxDestroySecondaryBuffer( aout_thread_t *p_aout );
98 static int  DirectxInitDSound            ( aout_thread_t *p_aout );
99 static void DirectSoundThread            ( aout_thread_t *p_aout );
100
101 /*****************************************************************************
102  * Functions exported as capabilities. They are declared as static so that
103  * we don't pollute the namespace too much.
104  *****************************************************************************/
105 void _M( aout_getfunctions )( function_list_t * p_function_list )
106 {
107     p_function_list->functions.aout.pf_open = aout_Open;
108     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
109     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
110     p_function_list->functions.aout.pf_play = aout_Play;
111     p_function_list->functions.aout.pf_close = aout_Close;
112 }
113
114 /*****************************************************************************
115  * aout_Open: open the audio device
116  *****************************************************************************
117  * This function opens and setups Direct Sound.
118  *****************************************************************************/
119 static int aout_Open( aout_thread_t *p_aout )
120 {
121     HRESULT dsresult;
122     DSBUFFERDESC dsbuffer_desc;
123
124     msg_Dbg( p_aout, "aout_Open" );
125
126    /* Allocate structure */
127     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
128
129     if( p_aout->p_sys == NULL )
130     {
131         msg_Err( p_aout, "out of memory" );
132         return( 1 );
133     }
134
135     /* Initialize some variables */
136     p_aout->p_sys->p_dsobject = NULL;
137     p_aout->p_sys->p_dsbuffer_primary = NULL;
138     p_aout->p_sys->p_dsbuffer = NULL;
139     p_aout->p_sys->p_dsnotify = NULL;
140     p_aout->p_sys->b_notification_thread_die = 0;
141     p_aout->p_sys->l_data_written_from_beginning = 0;
142     p_aout->p_sys->l_data_played_from_beginning = 0;
143     vlc_mutex_init( &p_aout->p_sys->buffer_lock );
144
145
146     /* Initialise DirectSound */
147     if( DirectxInitDSound( p_aout ) )
148     {
149         msg_Warn( p_aout, "cannot initialize DirectSound" );
150         return( 1 );
151     }
152
153     /* Obtain (not create) Direct Sound primary buffer */
154     memset( &dsbuffer_desc, 0, sizeof(DSBUFFERDESC) );
155     dsbuffer_desc.dwSize = sizeof(DSBUFFERDESC);
156     dsbuffer_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
157     msg_Warn( p_aout, "create direct sound primary buffer" );
158     dsresult = IDirectSound_CreateSoundBuffer(p_aout->p_sys->p_dsobject,
159                                             &dsbuffer_desc,
160                                             &p_aout->p_sys->p_dsbuffer_primary,
161                                             NULL);
162     if( dsresult != DS_OK )
163     {
164         msg_Warn( p_aout, "cannot create direct sound primary buffer" );
165         IDirectSound_Release( p_aout->p_sys->p_dsobject );
166         p_aout->p_sys->p_dsobject = NULL;
167         p_aout->p_sys->p_dsbuffer_primary = NULL;
168         return( 1 );
169     }
170
171
172     /* Now we need to setup DirectSound play notification */
173
174     /* first we need to create the notification events */
175     p_aout->p_sys->notification_events[0].hEventNotify =
176         CreateEvent( NULL, FALSE, FALSE, NULL );
177     p_aout->p_sys->notification_events[1].hEventNotify =
178         CreateEvent( NULL, FALSE, FALSE, NULL );
179
180     /* then launch the notification thread */
181     msg_Dbg( p_aout, "creating DirectSoundThread" );
182     if( vlc_thread_create( p_aout, &p_aout->p_sys->notification_thread_id,
183                            "DirectSound Notification Thread",
184                            (void *) DirectSoundThread, (void *) p_aout) )
185     {
186         msg_Err( p_aout, "cannot create DirectSoundThread" );
187         /* Let's go on anyway */
188     }
189
190     return( 0 );
191 }
192
193 /*****************************************************************************
194  * aout_SetFormat: reset the audio device and sets its format
195  *****************************************************************************
196  * This functions set a new audio format.
197  * For this we need to close the current secondary buffer and create another
198  * one with the desired format.
199  *****************************************************************************/
200 static int aout_SetFormat( aout_thread_t *p_aout )
201 {
202     HRESULT       dsresult;
203     WAVEFORMATEX  *p_waveformat;
204     unsigned long i_size_struct;
205
206     msg_Dbg( p_aout, "aout_SetFormat" );
207
208     /* Set the format of Direct Sound primary buffer */
209
210     /* first we need to know the current format */
211     dsresult = IDirectSoundBuffer_GetFormat( p_aout->p_sys->p_dsbuffer_primary,
212                                              NULL, 0, &i_size_struct );
213     if( dsresult == DS_OK )
214     {
215         p_waveformat = malloc( i_size_struct );
216         dsresult = IDirectSoundBuffer_GetFormat(
217                                              p_aout->p_sys->p_dsbuffer_primary,
218                                              p_waveformat, i_size_struct,
219                                              NULL );
220     }
221
222     if( dsresult == DS_OK )
223     {
224         /* Here we'll change the format */
225         p_waveformat->nChannels        = 2; 
226         p_waveformat->nSamplesPerSec   = (p_aout->i_rate < 44100) ? 44100
227                                              : p_aout->i_rate; 
228         p_waveformat->wBitsPerSample   = 16; 
229         p_waveformat->nBlockAlign      = p_waveformat->wBitsPerSample / 8 *
230                                              p_waveformat->nChannels;
231         p_waveformat->nAvgBytesPerSec  = p_waveformat->nSamplesPerSec *
232                                              p_waveformat->nBlockAlign;
233
234         dsresult = IDirectSoundBuffer_SetFormat(
235                                              p_aout->p_sys->p_dsbuffer_primary,
236                                              p_waveformat );
237     }
238     else msg_Warn( p_aout, "cannot get primary buffer format" );
239
240     if( dsresult != DS_OK )
241         msg_Warn( p_aout, "cannot set primary buffer format" );
242
243
244     /* Now we need to take care of Direct Sound secondary buffer */
245
246     vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
247
248     /* first release the current secondary buffer */
249     DirectxDestroySecondaryBuffer( p_aout );
250
251     /* then create a new secondary buffer */
252     if( DirectxCreateSecondaryBuffer( p_aout ) )
253     {
254         msg_Warn( p_aout, "cannot create buffer" );
255         vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
256         return( 1 );
257     }
258
259     vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
260
261     return( 0 );
262 }
263
264 /*****************************************************************************
265  * aout_GetBufInfo: buffer status query
266  *****************************************************************************
267  * returns the number of bytes in the audio buffer that have not yet been
268  * sent to the sound device.
269  *****************************************************************************/
270 static int aout_GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
271 {
272     long l_play_position, l_notused, l_result;
273     HRESULT dsresult;
274
275     if( p_aout->p_sys->b_buffer_underflown )
276     {
277         msg_Warn( p_aout, "aout_GetBufInfo underflow" );
278         return( i_buffer_limit );
279     }
280
281     dsresult = IDirectSoundBuffer_GetCurrentPosition(p_aout->p_sys->p_dsbuffer,
282                                                  &l_play_position, &l_notused);
283     if( dsresult != DS_OK )
284     {
285         msg_Warn( p_aout, "aout_GetBufInfo cannot get current pos" );
286         return( i_buffer_limit );
287     }
288
289     l_result = (p_aout->p_sys->l_write_position >= l_play_position) ?
290       (p_aout->p_sys->l_write_position - l_play_position)
291                : (p_aout->p_sys->l_buffer_size - l_play_position
292                   + p_aout->p_sys->l_write_position);
293
294 #if 0
295     msg_Dbg( p_aout, "aout_GetBufInfo: %i", i_result);
296 #endif
297     return l_result;
298 }
299
300 /*****************************************************************************
301  * aout_Play: play a sound buffer
302  *****************************************************************************
303  * This function writes a buffer of i_length bytes
304  * Don't forget that DirectSound buffers are circular buffers.
305  *****************************************************************************/
306 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
307 {
308     VOID            *p_write_position, *p_start_buffer;
309     long            l_bytes1, l_bytes2, l_play_position;
310     HRESULT         dsresult;
311
312     /* protect buffer access (because of DirectSoundThread) */
313     vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
314
315     if( p_aout->p_sys->b_buffer_underflown )
316     {
317         /*  there has been an underflow so we need to play the new sample
318          *  as soon as possible. This is why we query the play position */
319         dsresult = IDirectSoundBuffer_GetCurrentPosition(
320                                             p_aout->p_sys->p_dsbuffer,
321                                             &l_play_position,
322                                             &p_aout->p_sys->l_write_position );
323         if( dsresult != DS_OK )
324         {
325             msg_Warn( p_aout, "cannot get buffer position" );
326             p_aout->p_sys->l_write_position = 0; 
327         }
328
329         msg_Warn( p_aout, "aout_Play underflow" );
330         /* reinitialise the underflow detection counters */
331         p_aout->p_sys->b_buffer_underflown = 0;
332         p_aout->p_sys->l_data_written_from_beginning = 0;
333
334 #define WRITE_P  p_aout->p_sys->l_write_position
335 #define PLAY_P   l_play_position
336 #define BUF_SIZE p_aout->p_sys->l_buffer_size
337         p_aout->p_sys->l_data_played_from_beginning = -(WRITE_P %(BUF_SIZE/2));
338         if( PLAY_P < BUF_SIZE/2 && WRITE_P > BUF_SIZE/2 )
339         {
340             p_aout->p_sys->l_data_played_from_beginning -= (BUF_SIZE/2);
341         }
342         if( PLAY_P > BUF_SIZE/2 && WRITE_P < BUF_SIZE/2 )
343         {
344             p_aout->p_sys->l_data_played_from_beginning -= (BUF_SIZE/2);
345         }        
346 #undef WRITE_P
347 #undef PLAY_P
348 #undef BUF_SIZE
349     }
350
351     /* Before copying anything, we have to lock the buffer */
352     dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
353                    p_aout->p_sys->l_write_position,  /* Offset of lock start */
354                    i_size,                        /* Number of bytes to lock */
355                    &p_write_position,               /* Address of lock start */
356                    &l_bytes1,    /* Count of bytes locked before wrap around */
357                    &p_start_buffer,        /* Buffer adress (if wrap around) */
358                    &l_bytes2,            /* Count of bytes after wrap around */
359                    0);                                              /* Flags */
360     if( dsresult == DSERR_BUFFERLOST )
361     {
362         IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
363         dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
364                                             p_aout->p_sys->l_write_position,
365                                             i_size,
366                                             &p_write_position,
367                                             &l_bytes1,
368                                             &p_start_buffer,
369                                             &l_bytes2,
370                                             0);
371
372     }
373     if( dsresult != DS_OK )
374     {
375         msg_Warn( p_aout, "aout_Play cannot lock buffer" );
376         vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
377         return;
378     }
379
380     /* Now do the actual memcpy (two memcpy because the buffer is circular) */
381     memcpy( p_write_position, buffer, l_bytes1 );
382     if( p_start_buffer != NULL )
383     {
384         memcpy( p_start_buffer, buffer + l_bytes1, l_bytes2 );
385     }
386
387     /* Now the data has been copied, unlock the buffer */
388     IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer, 
389             p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
390
391     /* Update the write position index of the buffer*/
392     p_aout->p_sys->l_write_position += i_size;
393     p_aout->p_sys->l_write_position %= p_aout->p_sys->l_buffer_size;
394     p_aout->p_sys->l_data_written_from_beginning += i_size;
395
396     vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
397
398     /* The play function has no effect if the buffer is already playing */
399     dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
400                                         0,                         /* Unused */
401                                         0,                         /* Unused */
402                                         DSBPLAY_LOOPING );          /* Flags */
403     if( dsresult == DSERR_BUFFERLOST )
404     {
405         IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
406         dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
407                                             0,                     /* Unused */
408                                             0,                     /* Unused */
409                                             DSBPLAY_LOOPING );      /* Flags */
410     }
411     if( dsresult != DS_OK )
412     {
413         msg_Warn( p_aout, "aout_Play cannot play buffer" );
414         return;
415     }
416
417 }
418
419 /*****************************************************************************
420  * aout_Close: close the audio device
421  *****************************************************************************/
422 static void aout_Close( aout_thread_t *p_aout )
423 {
424
425     msg_Dbg( p_aout, "aout_Close" );
426
427     /* kill the position notification thread */
428     p_aout->p_sys->b_notification_thread_die = 1;
429     SetEvent( p_aout->p_sys->notification_events[0].hEventNotify );
430     vlc_thread_join( p_aout, p_aout->p_sys->notification_thread_id );
431     vlc_mutex_destroy( &p_aout->p_sys->buffer_lock );
432
433     /* release the secondary buffer */
434     DirectxDestroySecondaryBuffer( p_aout );
435
436     /* then release the primary buffer */
437     if( p_aout->p_sys->p_dsbuffer_primary != NULL )
438     {
439         IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer_primary );
440         p_aout->p_sys->p_dsbuffer_primary = NULL;
441     }  
442
443     /* finally release the DirectSound object */
444     if( p_aout->p_sys->p_dsobject != NULL )
445     {
446         IDirectSound_Release( p_aout->p_sys->p_dsobject );
447         p_aout->p_sys->p_dsobject = NULL;
448     }  
449     
450     /* free DSOUND.DLL */
451     if( p_aout->p_sys->hdsound_dll != NULL )
452     {
453        FreeLibrary( p_aout->p_sys->hdsound_dll );
454        p_aout->p_sys->hdsound_dll = NULL;
455     }
456
457     /* Close the Output. */
458     if ( p_aout->p_sys != NULL )
459     { 
460         free( p_aout->p_sys );
461         p_aout->p_sys = NULL;
462     }
463 }
464
465 /*****************************************************************************
466  * DirectxInitDSound
467  *****************************************************************************
468  *****************************************************************************/
469 static int DirectxInitDSound( aout_thread_t *p_aout )
470 {
471     HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
472
473     p_aout->p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
474     if( p_aout->p_sys->hdsound_dll == NULL )
475     {
476       msg_Warn( p_aout, "cannot open DSOUND.DLL" );
477       return( 1 );
478     }
479
480     OurDirectSoundCreate = (void *)GetProcAddress( p_aout->p_sys->hdsound_dll,
481                                                    "DirectSoundCreate" );
482
483     if( OurDirectSoundCreate == NULL )
484     {
485       msg_Warn( p_aout, "GetProcAddress FAILED" );
486       FreeLibrary( p_aout->p_sys->hdsound_dll );
487       p_aout->p_sys->hdsound_dll = NULL;
488       return( 1 );
489     }
490
491     /* Create the direct sound object */
492     if( OurDirectSoundCreate(NULL, &p_aout->p_sys->p_dsobject, NULL) != DS_OK )
493     {
494         msg_Warn( p_aout, "cannot create a direct sound device" );
495         p_aout->p_sys->p_dsobject = NULL;
496         FreeLibrary( p_aout->p_sys->hdsound_dll );
497         p_aout->p_sys->hdsound_dll = NULL;
498         return( 1 );
499     }
500
501     /* Set DirectSound Cooperative level, ie what control we want over Windows
502      * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
503      * settings of the primary buffer, but also that only the sound of our
504      * application will be hearable when it will have the focus.
505      * !!! (this is not really working as intended yet because to set the
506      * cooperative level you need the window handle of your application, and
507      * I don't know of any easy way to get it. Especially since we might play
508      * sound without any video, and so what window handle should we use ???
509      * The hack for now is to use the Desktop window handle - it seems to be
510      * working */
511     if( IDirectSound_SetCooperativeLevel(p_aout->p_sys->p_dsobject,
512                                          GetDesktopWindow(),
513                                          DSSCL_EXCLUSIVE) )
514     {
515         msg_Warn( p_aout, "cannot set direct sound cooperative level" );
516     }
517
518     return( 0 );
519 }
520
521 /*****************************************************************************
522  * DirectxCreateSecondaryBuffer
523  *****************************************************************************
524  * This function creates the buffer we'll use to play audio.
525  * In DirectSound there are two kinds of buffers:
526  * - the primary buffer: which is the actual buffer that the soundcard plays
527  * - the secondary buffer(s): these buffers are the one actually used by
528  *    applications and DirectSound takes care of mixing them into the primary.
529  *
530  * Once you create a secondary buffer, you cannot change its format anymore so
531  * you have to release the current and create another one.
532  *****************************************************************************/
533 static int DirectxCreateSecondaryBuffer( aout_thread_t *p_aout )
534 {
535     WAVEFORMATEX         waveformat;
536     DSBUFFERDESC         dsbdesc;
537     DSBCAPS              dsbcaps;
538
539     /* First set the buffer format */
540     memset(&waveformat, 0, sizeof(WAVEFORMATEX)); 
541     waveformat.wFormatTag      = WAVE_FORMAT_PCM; 
542     waveformat.nChannels       = p_aout->i_channels; 
543     waveformat.nSamplesPerSec  = p_aout->i_rate; 
544     waveformat.wBitsPerSample  = 16; 
545     waveformat.nBlockAlign     = waveformat.wBitsPerSample / 8 *
546                                  waveformat.nChannels;
547     waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec *
548                                      waveformat.nBlockAlign;
549
550     /* Then fill in the descriptor */
551     memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 
552     dsbdesc.dwSize = sizeof(DSBUFFERDESC); 
553     dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
554                     | DSBCAPS_CTRLPOSITIONNOTIFY     /* We need notification */
555                     | DSBCAPS_GLOBALFOCUS;      /* Allows background playing */
556     dsbdesc.dwBufferBytes = waveformat.nAvgBytesPerSec * 2;  /* 2 sec buffer */
557     dsbdesc.lpwfxFormat = &waveformat; 
558  
559     if( IDirectSound_CreateSoundBuffer( p_aout->p_sys->p_dsobject,
560                                         &dsbdesc,
561                                         &p_aout->p_sys->p_dsbuffer,
562                                         NULL) != DS_OK )
563     {
564         msg_Warn( p_aout, "cannot create direct sound secondary buffer" );
565         p_aout->p_sys->p_dsbuffer = NULL;
566         return( 1 );
567     }
568
569     /* backup the size of the secondary sound buffer */
570     memset(&dsbcaps, 0, sizeof(DSBCAPS)); 
571     dsbcaps.dwSize = sizeof(DSBCAPS);
572     IDirectSoundBuffer_GetCaps( p_aout->p_sys->p_dsbuffer, &dsbcaps  );
573     p_aout->p_sys->l_buffer_size = dsbcaps.dwBufferBytes;
574     p_aout->p_sys->l_write_position = 0;
575
576     msg_Dbg( p_aout, "DirectxCreateSecondaryBuffer: %li",
577                      p_aout->p_sys->l_buffer_size );
578
579     /* Now the secondary buffer is created, we need to setup its position
580      * notification */
581     p_aout->p_sys->notification_events[0].dwOffset = 0;    /* notif position */
582     p_aout->p_sys->notification_events[1].dwOffset = dsbcaps.dwBufferBytes / 2;
583
584     /* Get the IDirectSoundNotify interface */
585     if FAILED( IDirectSoundBuffer_QueryInterface( p_aout->p_sys->p_dsbuffer,
586                                                   &IID_IDirectSoundNotify,
587                                        (LPVOID *)&p_aout->p_sys->p_dsnotify ) )
588     {
589         msg_Warn( p_aout, "cannot get Notify interface" );
590         /* Go on anyway */
591         p_aout->p_sys->p_dsnotify = NULL;
592         return( 0 );
593     }
594         
595     if FAILED( IDirectSoundNotify_SetNotificationPositions(
596                                         p_aout->p_sys->p_dsnotify,
597                                         2,
598                                         p_aout->p_sys->notification_events ) )
599     {
600         msg_Warn( p_aout, "cannot set position Notification" );
601         /* Go on anyway */
602     }
603
604     return( 0 );
605 }
606
607 /*****************************************************************************
608  * DirectxCreateSecondaryBuffer
609  *****************************************************************************
610  * This function destroy the secondary buffer.
611  *****************************************************************************/
612 static void DirectxDestroySecondaryBuffer( aout_thread_t *p_aout )
613 {
614     /* make sure the buffer isn't playing */
615     if( p_aout->p_sys->p_dsbuffer != NULL )
616     {
617         IDirectSoundBuffer_Stop( p_aout->p_sys->p_dsbuffer );
618     }
619
620     if( p_aout->p_sys->p_dsnotify != NULL )
621     {
622         IDirectSoundNotify_Release( p_aout->p_sys->p_dsnotify );
623         p_aout->p_sys->p_dsnotify = NULL;
624     }
625
626     if( p_aout->p_sys->p_dsbuffer != NULL )
627     {
628         IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer );
629         p_aout->p_sys->p_dsbuffer = NULL;
630     }
631 }
632
633 /*****************************************************************************
634  * DirectSoundThread: this thread will capture play notification events. 
635  *****************************************************************************
636  * As Direct Sound uses circular buffers, we need to use event notification to
637  * manage them.
638  * Using event notification implies blocking the thread until the event is
639  * signaled so we really need to run this in a separate thread.
640  *****************************************************************************/
641 static void DirectSoundThread( aout_thread_t *p_aout )
642 {
643     HANDLE  notification_events[2];
644     VOID    *p_write_position, *p_start_buffer;
645     long    l_bytes1, l_bytes2;
646     HRESULT dsresult;
647     long    l_buffer_size, l_play_position, l_data_in_buffer;
648
649     notification_events[0]=p_aout->p_sys->notification_events[0].hEventNotify;
650     notification_events[1]=p_aout->p_sys->notification_events[1].hEventNotify;
651
652     /* this thread must be high-priority */
653     if( !SetThreadPriority( GetCurrentThread(),
654                             THREAD_PRIORITY_ABOVE_NORMAL ) )
655     {
656         msg_Warn( p_aout, "DirectSoundThread could not renice itself" );
657     }
658
659     msg_Dbg( p_aout, "DirectSoundThread ready" );
660
661     while( !p_aout->p_sys->b_notification_thread_die )
662     {
663         /* wait for the position notification */
664         l_play_position = WaitForMultipleObjects( 2, notification_events,
665                                                   0, INFINITE ); 
666         vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
667
668         if( p_aout->p_sys->b_notification_thread_die )
669         {
670             break;
671         }
672
673         /* check for buffer underflow (bodge for wrap around) */
674         l_buffer_size = p_aout->p_sys->l_buffer_size;
675         l_play_position = (l_play_position - WAIT_OBJECT_0) * l_buffer_size/2;
676         p_aout->p_sys->l_data_played_from_beginning += (l_buffer_size/2);
677         l_data_in_buffer = p_aout->p_sys->l_data_written_from_beginning -
678                                p_aout->p_sys->l_data_played_from_beginning; 
679
680         /* detect wrap-around */
681         if( l_data_in_buffer < (-l_buffer_size/2) )
682         {
683             msg_Dbg( p_aout, "DirectSoundThread wrap around: %li",
684                              l_data_in_buffer );
685             l_data_in_buffer += l_buffer_size;
686         }
687
688         /* detect underflow */
689         if( l_data_in_buffer <= 0 )
690         {
691             msg_Warn( p_aout,
692                       "DirectSoundThread underflow: %li", l_data_in_buffer );
693             p_aout->p_sys->b_buffer_underflown = 1;
694             p_aout->p_sys->l_write_position =
695                   (l_play_position + l_buffer_size/2) % l_buffer_size;
696             l_data_in_buffer = l_buffer_size / 2;
697             p_aout->p_sys->l_data_played_from_beginning -= (l_buffer_size/2);
698         }
699
700
701         /* Clear the data which has already been played */
702
703         /* Before copying anything, we have to lock the buffer */
704         dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
705                    p_aout->p_sys->l_write_position,  /* Offset of lock start */
706                    l_buffer_size - l_data_in_buffer,      /* Number of bytes */
707                    &p_write_position,               /* Address of lock start */
708                    &l_bytes1,    /* Count of bytes locked before wrap around */
709                    &p_start_buffer,        /* Buffer adress (if wrap around) */
710                    &l_bytes2,            /* Count of bytes after wrap around */
711                    0);                                              /* Flags */
712         if( dsresult == DSERR_BUFFERLOST )
713         {
714             IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
715             dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
716                                           p_aout->p_sys->l_write_position,
717                                           l_buffer_size - l_data_in_buffer,
718                                           &p_write_position,
719                                           &l_bytes1,
720                                           &p_start_buffer,
721                                           &l_bytes2,
722                                           0);
723         }
724         if( dsresult != DS_OK )
725         {
726             msg_Warn( p_aout, "aout_Play cannot lock buffer" );
727             vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
728             return;
729         }
730
731         /* Now do the actual memcpy (two because the buffer is circular) */
732         memset( p_write_position, 0, l_bytes1 );
733         if( p_start_buffer != NULL )
734         {
735             memset( p_start_buffer, 0, l_bytes2 );
736         }
737
738         /* Now the data has been copied, unlock the buffer */
739         IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer, 
740                         p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
741
742         vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
743
744     }
745
746     /* free the events */
747     CloseHandle( notification_events[0] );
748     CloseHandle( notification_events[1] );
749
750     msg_Dbg( p_aout, "DirectSoundThread exiting" );
751
752 }