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