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