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