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