]> git.sesse.net Git - vlc/blob - modules/audio_output/directx.c
* modules/audio_output/directx.c: use VLC_THREAD_PRIORITY_HIGHEST for
[vlc] / modules / audio_output / directx.c
1 /*****************************************************************************
2  * directx.c: Windows DirectX audio output method
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: directx.c,v 1.10 2003/01/05 13:39:32 gbazin Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <errno.h>                                                 /* ENOMEM */
28 #include <fcntl.h>                                       /* open(), O_WRONLY */
29 #include <string.h>                                            /* strerror() */
30
31 #include <stdlib.h>                            /* calloc(), malloc(), free() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/aout.h>
35 #include "aout_internal.h"
36
37 #include <windows.h>
38 #include <mmsystem.h>
39 #include <dsound.h>
40
41 #define FRAME_SIZE 2048              /* The size is in samples, not in bytes */
42 #define FRAMES_NUM 4
43
44 /* frame buffer status */
45 #define FRAME_QUEUED 0
46 #define FRAME_EMPTY 1
47
48 /*****************************************************************************
49  * DirectSound GUIDs.
50  * Defining them here allows us to get rid of the dxguid library during
51  * the linking stage.
52  *****************************************************************************/
53 #include <initguid.h>
54 DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
55
56 /*****************************************************************************
57  * Useful macros
58  *****************************************************************************/
59 #ifndef WAVE_FORMAT_IEEE_FLOAT
60 #   define WAVE_FORMAT_IEEE_FLOAT 0x0003
61 #endif
62
63 /*****************************************************************************
64  * notification_thread_t: DirectX event thread
65  *****************************************************************************/
66 typedef struct notification_thread_t
67 {
68     VLC_COMMON_MEMBERS
69
70     aout_instance_t * p_aout;
71     int i_frame_status[FRAMES_NUM];           /* status of each frame buffer */
72     DSBPOSITIONNOTIFY p_events[FRAMES_NUM];      /* play notification events */
73     int i_frame_size;                         /* Size in bytes of one frame */
74
75     mtime_t start_date;
76
77 } notification_thread_t;
78
79 /*****************************************************************************
80  * aout_sys_t: directx audio output method descriptor
81  *****************************************************************************
82  * This structure is part of the audio output thread descriptor.
83  * It describes the direct sound specific properties of an audio device.
84  *****************************************************************************/
85 struct aout_sys_t
86 {
87     LPDIRECTSOUND       p_dsobject;              /* main Direct Sound object */
88
89     LPDIRECTSOUNDBUFFER p_dsbuffer;   /* the sound buffer we use (direct sound
90                                        * takes care of mixing all the
91                                        * secondary buffers into the primary) */
92
93     LPDIRECTSOUNDNOTIFY p_dsnotify;         /* the position notify interface */
94
95     HINSTANCE           hdsound_dll;      /* handle of the opened dsound dll */
96
97     notification_thread_t * p_notif;                 /* DirectSoundThread id */
98
99     int b_playing;                                         /* playing status */
100 };
101
102 /*****************************************************************************
103  * Local prototypes.
104  *****************************************************************************/
105 static int  OpenAudio  ( vlc_object_t * );
106 static void CloseAudio ( vlc_object_t * );
107
108 static void Play       ( aout_instance_t * );
109
110 /* local functions */
111 static int  DirectxCreateSecondaryBuffer ( aout_instance_t * );
112 static void DirectxDestroySecondaryBuffer( aout_instance_t * );
113 static int  DirectxInitDSound            ( aout_instance_t * );
114 static void DirectSoundThread            ( notification_thread_t * );
115 static int  DirectxFillBuffer            ( aout_instance_t *, int,
116                                            aout_buffer_t * );
117
118 /*****************************************************************************
119  * Module descriptor
120  *****************************************************************************/
121 vlc_module_begin();
122     set_description( _("DirectX audio module") );
123     set_capability( "audio output", 100 );
124     add_shortcut( "directx" );
125     set_callbacks( OpenAudio, CloseAudio );
126 vlc_module_end();
127
128 /*****************************************************************************
129  * OpenAudio: open the audio device
130  *****************************************************************************
131  * This function opens and setups Direct Sound.
132  *****************************************************************************/
133 static int OpenAudio( vlc_object_t *p_this )
134 {
135     aout_instance_t * p_aout = (aout_instance_t *)p_this;
136     int i;
137
138     msg_Dbg( p_aout, "Open" );
139
140    /* Allocate structure */
141     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
142     if( p_aout->output.p_sys == NULL )
143     {
144         msg_Err( p_aout, "out of memory" );
145         return VLC_EGENERIC;
146     }
147
148     /* Initialize some variables */
149     p_aout->output.p_sys->p_dsobject = NULL;
150     p_aout->output.p_sys->p_dsbuffer = NULL;
151     p_aout->output.p_sys->p_dsnotify = NULL;
152     p_aout->output.p_sys->p_notif = NULL;
153     p_aout->output.p_sys->b_playing = 0;
154
155     p_aout->output.pf_play = Play;
156     aout_VolumeSoftInit( p_aout );
157
158     /* Initialise DirectSound */
159     if( DirectxInitDSound( p_aout ) )
160     {
161         msg_Err( p_aout, "cannot initialize DirectSound" );
162         goto error;
163     }
164
165     /* Now we need to setup DirectSound play notification */
166     p_aout->output.p_sys->p_notif =
167         vlc_object_create( p_aout, sizeof(notification_thread_t) );
168     p_aout->output.p_sys->p_notif->p_aout = p_aout;
169
170     /* first we need to create the notification events */
171     for( i = 0; i < FRAMES_NUM; i++ )
172         p_aout->output.p_sys->p_notif->p_events[i].hEventNotify =
173             CreateEvent( NULL, FALSE, FALSE, NULL );
174
175     /* then create a new secondary buffer */
176     p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
177     if( DirectxCreateSecondaryBuffer( p_aout ) )
178     {
179         msg_Dbg( p_aout, "cannot create WAVE_FORMAT_IEEE_FLOAT buffer" );
180
181         p_aout->output.output.i_format = VLC_FOURCC('s','1','6','l');
182         if( DirectxCreateSecondaryBuffer( p_aout ) )
183         {
184             msg_Err( p_aout, "cannot create WAVE_FORMAT_PCM buffer" );
185             return 1;
186         }
187     }
188
189     /* then launch the notification thread */
190     msg_Dbg( p_aout, "creating DirectSoundThread" );
191     if( vlc_thread_create( p_aout->output.p_sys->p_notif,
192                            "DirectSound Notification Thread",
193                            DirectSoundThread,
194                            VLC_THREAD_PRIORITY_HIGHEST, 1 ) )
195     {
196         msg_Err( p_aout, "cannot create DirectSoundThread" );
197         goto error;
198     }
199
200     vlc_object_attach( p_aout->output.p_sys->p_notif, p_aout );
201
202     return 0;
203
204  error:
205     CloseAudio( VLC_OBJECT(p_aout) );
206     return VLC_EGENERIC;
207 }
208
209 /*****************************************************************************
210  * Play: we'll start playing the directsound buffer here because at least here
211  *       we know the first buffer has been put in the aout fifo and we also
212  *       know its date.
213  *****************************************************************************/
214 static void Play( aout_instance_t *p_aout )
215 {
216     if( !p_aout->output.p_sys->b_playing )
217     {
218         aout_buffer_t *p_buffer;
219
220         p_aout->output.p_sys->b_playing = 1;
221
222         /* get the playing date of the first aout buffer */
223         p_aout->output.p_sys->p_notif->start_date =
224             aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
225
226         /* fill in the first samples */
227         p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
228         DirectxFillBuffer( p_aout, 0, p_buffer );
229
230         /* wake up the audio output thread */
231         SetEvent( p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );
232     }
233 }
234
235 /*****************************************************************************
236  * CloseAudio: close the audio device
237  *****************************************************************************/
238 static void CloseAudio( vlc_object_t *p_this )
239 {
240     aout_instance_t * p_aout = (aout_instance_t *)p_this;
241
242     msg_Dbg( p_aout, "Close" );
243
244     /* kill the position notification thread, if any */
245     if( p_aout->output.p_sys->p_notif )
246     {
247         vlc_object_detach( p_aout->output.p_sys->p_notif );
248         if( p_aout->output.p_sys->p_notif->b_thread )
249         {
250             p_aout->output.p_sys->p_notif->b_die = 1;
251
252             if( !p_aout->output.p_sys->b_playing )
253                 /* wake up the audio thread */
254                 SetEvent(
255                     p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );
256
257             vlc_thread_join( p_aout->output.p_sys->p_notif );
258         }
259         vlc_object_destroy( p_aout->output.p_sys->p_notif );
260     }
261
262     /* release the secondary buffer */
263     DirectxDestroySecondaryBuffer( p_aout );
264
265     /* finally release the DirectSound object */
266     if( p_aout->output.p_sys->p_dsobject )
267         IDirectSound_Release( p_aout->output.p_sys->p_dsobject );
268     
269     /* free DSOUND.DLL */
270     if( p_aout->output.p_sys->hdsound_dll )
271        FreeLibrary( p_aout->output.p_sys->hdsound_dll );
272
273     free( p_aout->output.p_sys );
274 }
275
276 /*****************************************************************************
277  * DirectxInitDSound: handle all the gory details of DirectSound initialisation
278  *****************************************************************************/
279 static int DirectxInitDSound( aout_instance_t *p_aout )
280 {
281     HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
282
283     p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
284     if( p_aout->output.p_sys->hdsound_dll == NULL )
285     {
286         msg_Warn( p_aout, "cannot open DSOUND.DLL" );
287         goto error;
288     }
289
290     OurDirectSoundCreate = (void *)GetProcAddress(
291                                            p_aout->output.p_sys->hdsound_dll,
292                                            "DirectSoundCreate" );
293     if( OurDirectSoundCreate == NULL )
294     {
295         msg_Warn( p_aout, "GetProcAddress FAILED" );
296         goto error;
297     }
298
299     /* Create the direct sound object */
300     if( OurDirectSoundCreate( NULL, &p_aout->output.p_sys->p_dsobject, NULL )
301         != DS_OK )
302     {
303         msg_Warn( p_aout, "cannot create a direct sound device" );
304         goto error;
305     }
306
307     /* Set DirectSound Cooperative level, ie what control we want over Windows
308      * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
309      * settings of the primary buffer, but also that only the sound of our
310      * application will be hearable when it will have the focus.
311      * !!! (this is not really working as intended yet because to set the
312      * cooperative level you need the window handle of your application, and
313      * I don't know of any easy way to get it. Especially since we might play
314      * sound without any video, and so what window handle should we use ???
315      * The hack for now is to use the Desktop window handle - it seems to be
316      * working */
317     if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
318                                           GetDesktopWindow(),
319                                           DSSCL_EXCLUSIVE) )
320     {
321         msg_Warn( p_aout, "cannot set direct sound cooperative level" );
322     }
323
324     return 0;
325
326  error:
327     p_aout->output.p_sys->p_dsobject = NULL;
328     if( p_aout->output.p_sys->hdsound_dll )
329     {
330         FreeLibrary( p_aout->output.p_sys->hdsound_dll );
331         p_aout->output.p_sys->hdsound_dll = NULL;
332     }
333     return 1;
334
335 }
336
337 /*****************************************************************************
338  * DirectxCreateSecondaryBuffer
339  *****************************************************************************
340  * This function creates the buffer we'll use to play audio.
341  * In DirectSound there are two kinds of buffers:
342  * - the primary buffer: which is the actual buffer that the soundcard plays
343  * - the secondary buffer(s): these buffers are the one actually used by
344  *    applications and DirectSound takes care of mixing them into the primary.
345  *
346  * Once you create a secondary buffer, you cannot change its format anymore so
347  * you have to release the current and create another one.
348  *****************************************************************************/
349 static int DirectxCreateSecondaryBuffer( aout_instance_t *p_aout )
350 {
351     WAVEFORMATEX         waveformat;
352     DSBUFFERDESC         dsbdesc;
353     DSBCAPS              dsbcaps;
354     int                  i_nb_channels, i;
355
356     i_nb_channels = aout_FormatNbChannels( &p_aout->output.output );
357     if ( i_nb_channels >= 2 )
358     {
359         i_nb_channels = 2;
360         p_aout->output.output.i_physical_channels =
361             AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
362     }
363     else
364     {
365         i_nb_channels = 1;
366         p_aout->output.output.i_physical_channels =
367             AOUT_CHAN_CENTER;
368     }
369
370     /* First set the buffer format */
371     memset( &waveformat, 0, sizeof(WAVEFORMATEX) );
372     switch( p_aout->output.output.i_format )
373     {
374     case VLC_FOURCC('s','1','6','l'):
375         waveformat.wFormatTag     = WAVE_FORMAT_PCM;
376         waveformat.wBitsPerSample = 16;
377         break;
378     case VLC_FOURCC('f','l','3','2'):
379         waveformat.wFormatTag     = WAVE_FORMAT_IEEE_FLOAT;
380         waveformat.wBitsPerSample = sizeof(float) * 8;
381         break;
382     }
383     waveformat.nChannels       = i_nb_channels;
384     waveformat.nSamplesPerSec  = p_aout->output.output.i_rate;
385     waveformat.nBlockAlign     = waveformat.wBitsPerSample / 8 *
386                                  waveformat.nChannels;
387     waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec *
388                                      waveformat.nBlockAlign;
389
390     aout_FormatPrepare( &p_aout->output.output );
391
392     /* Then fill in the descriptor */
393     memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
394     dsbdesc.dwSize = sizeof(DSBUFFERDESC);
395     dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
396                     | DSBCAPS_CTRLPOSITIONNOTIFY     /* We need notification */
397                     | DSBCAPS_GLOBALFOCUS;      /* Allows background playing */
398     dsbdesc.dwBufferBytes = FRAME_SIZE * FRAMES_NUM           /* buffer size */
399                             * p_aout->output.output.i_bytes_per_frame;
400     dsbdesc.lpwfxFormat = &waveformat;
401  
402     if( IDirectSound_CreateSoundBuffer( p_aout->output.p_sys->p_dsobject,
403                                         &dsbdesc,
404                                         &p_aout->output.p_sys->p_dsbuffer,
405                                         NULL) != DS_OK )
406     {
407         msg_Warn( p_aout, "cannot create direct sound secondary buffer" );
408         goto error;
409     }
410
411     /* backup the size of a frame */
412     p_aout->output.p_sys->p_notif->i_frame_size = FRAME_SIZE *
413         p_aout->output.output.i_bytes_per_frame;
414
415     memset(&dsbcaps, 0, sizeof(DSBCAPS));
416     dsbcaps.dwSize = sizeof(DSBCAPS);
417     IDirectSoundBuffer_GetCaps( p_aout->output.p_sys->p_dsbuffer, &dsbcaps  );
418     msg_Dbg( p_aout, "requested %li bytes buffer and got %li bytes.",
419              FRAMES_NUM * p_aout->output.p_sys->p_notif->i_frame_size,
420              dsbcaps.dwBufferBytes );
421
422     /* Now the secondary buffer is created, we need to setup its position
423      * notification */
424     for( i = 0; i < FRAMES_NUM; i++ )
425     {
426         p_aout->output.p_sys->p_notif->p_events[i].dwOffset = i *
427             p_aout->output.p_sys->p_notif->i_frame_size;
428
429         p_aout->output.p_sys->p_notif->i_frame_status[i] = FRAME_EMPTY;
430     }
431
432     /* Get the IDirectSoundNotify interface */
433     if FAILED( IDirectSoundBuffer_QueryInterface(
434                                 p_aout->output.p_sys->p_dsbuffer,
435                                 &IID_IDirectSoundNotify,
436                                 (LPVOID *)&p_aout->output.p_sys->p_dsnotify ) )
437     {
438         msg_Err( p_aout, "cannot get Notify interface" );
439         goto error;
440     }
441         
442     if FAILED( IDirectSoundNotify_SetNotificationPositions(
443                                     p_aout->output.p_sys->p_dsnotify,
444                                     FRAMES_NUM,
445                                     p_aout->output.p_sys->p_notif->p_events ) )
446     {
447         msg_Err( p_aout, "cannot set position Notification" );
448         goto error;
449     }
450
451     p_aout->output.i_nb_samples = FRAME_SIZE;
452
453     return 0;
454
455  error:
456     if( p_aout->output.p_sys->p_dsbuffer )
457     {
458         IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
459         p_aout->output.p_sys->p_dsbuffer = NULL;
460     }
461     if( p_aout->output.p_sys->p_dsnotify )
462     {
463         IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
464         p_aout->output.p_sys->p_dsnotify = NULL;
465     }
466     return VLC_EGENERIC;
467 }
468
469 /*****************************************************************************
470  * DirectxCreateSecondaryBuffer
471  *****************************************************************************
472  * This function destroys the secondary buffer.
473  *****************************************************************************/
474 static void DirectxDestroySecondaryBuffer( aout_instance_t *p_aout )
475 {
476     /* make sure the buffer isn't playing */
477     if( p_aout->output.p_sys->p_dsbuffer )
478         IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer );
479
480     if( p_aout->output.p_sys->p_dsnotify )
481     {
482         IDirectSoundNotify_Release( p_aout->output.p_sys->p_dsnotify );
483         p_aout->output.p_sys->p_dsnotify = NULL;
484     }
485
486     if( p_aout->output.p_sys->p_dsbuffer )
487     {
488         IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
489         p_aout->output.p_sys->p_dsbuffer = NULL;
490     }
491 }
492
493 /*****************************************************************************
494  * DirectxFillBuffer: Fill in one of the direct sound frame buffers.
495  *****************************************************************************
496  * Returns VLC_SUCCESS on success.
497  *****************************************************************************/
498 static int DirectxFillBuffer( aout_instance_t *p_aout, int i_frame,
499                               aout_buffer_t *p_buffer )
500 {
501     notification_thread_t *p_notif = p_aout->output.p_sys->p_notif;
502     void *p_write_position, *p_wrap_around;
503     long l_bytes1, l_bytes2;
504     HRESULT dsresult;
505
506     /* Before copying anything, we have to lock the buffer */
507     dsresult = IDirectSoundBuffer_Lock(
508                 p_aout->output.p_sys->p_dsbuffer,               /* DS buffer */
509                 i_frame * p_notif->i_frame_size,             /* Start offset */
510                 p_notif->i_frame_size,                    /* Number of bytes */
511                 &p_write_position,                  /* Address of lock start */
512                 &l_bytes1,       /* Count of bytes locked before wrap around */
513                 &p_wrap_around,            /* Buffer adress (if wrap around) */
514                 &l_bytes2,               /* Count of bytes after wrap around */
515                 0 );                                                /* Flags */
516     if( dsresult == DSERR_BUFFERLOST )
517     {
518         IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
519         dsresult = IDirectSoundBuffer_Lock(
520                                p_aout->output.p_sys->p_dsbuffer,
521                                i_frame * p_notif->i_frame_size,
522                                p_notif->i_frame_size,
523                                &p_write_position,
524                                &l_bytes1,
525                                &p_wrap_around,
526                                &l_bytes2,
527                                0 );
528     }
529     if( dsresult != DS_OK )
530     {
531         msg_Warn( p_notif, "cannot lock buffer" );
532         if( p_buffer ) aout_BufferFree( p_buffer );
533         return VLC_EGENERIC;
534     }
535
536     if ( p_buffer != NULL )
537     {
538         p_aout->p_vlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
539                                   l_bytes1 );
540         aout_BufferFree( p_buffer );
541     }
542     else
543         memset( p_write_position, 0, l_bytes1 );
544
545     /* Now the data has been copied, unlock the buffer */
546     IDirectSoundBuffer_Unlock( p_aout->output.p_sys->p_dsbuffer,
547                                p_write_position, l_bytes1,
548                                p_wrap_around, l_bytes2 );
549
550     return VLC_SUCCESS;
551 }
552
553 /*****************************************************************************
554  * DirectSoundThread: this thread will capture play notification events. 
555  *****************************************************************************
556  * We use this thread to emulate a callback mechanism. The thread probes for
557  * event notification and fills up the DS secondary buffer when needed.
558  *****************************************************************************/
559 static void DirectSoundThread( notification_thread_t *p_notif )
560 {
561     HANDLE  notification_events[FRAMES_NUM];
562     HRESULT dsresult;
563     aout_instance_t *p_aout = p_notif->p_aout;
564     int i, i_which_frame, i_last_frame, i_next_frame;
565     mtime_t mtime;
566
567     for( i = 0; i < FRAMES_NUM; i++ )
568         notification_events[i] = p_notif->p_events[i].hEventNotify;
569
570     /* Tell the main thread that we are ready */
571     vlc_thread_ready( p_notif );
572
573     msg_Dbg( p_notif, "DirectSoundThread ready" );
574
575     /* Wait here until Play() is called */
576     WaitForSingleObject( notification_events[0], INFINITE );
577
578     if( !p_notif->b_die )
579     {
580         mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 );
581
582         /* start playing the buffer */
583         dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer,
584                                         0,                         /* Unused */
585                                         0,                         /* Unused */
586                                         DSBPLAY_LOOPING );          /* Flags */
587         if( dsresult == DSERR_BUFFERLOST )
588         {
589             IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
590             dsresult = IDirectSoundBuffer_Play(
591                                             p_aout->output.p_sys->p_dsbuffer,
592                                             0,                     /* Unused */
593                                             0,                     /* Unused */
594                                             DSBPLAY_LOOPING );      /* Flags */
595         }
596         if( dsresult != DS_OK )
597         {
598             msg_Err( p_aout, "cannot start playing buffer" );
599         }
600     }
601
602     while( !p_notif->b_die )
603     {
604         aout_buffer_t *p_buffer;
605         long l_latency;
606
607         /* wait for the position notification */
608         i_which_frame = WaitForMultipleObjects( FRAMES_NUM,
609                                                 notification_events, 0,
610                                                 INFINITE ) - WAIT_OBJECT_0;
611
612         if( p_notif->b_die )
613             break;
614
615         mtime = mdate();
616
617         /* We take into account the current latency */
618         if( IDirectSoundBuffer_GetCurrentPosition(
619                 p_aout->output.p_sys->p_dsbuffer,
620                 &l_latency, NULL ) == DS_OK )
621         {
622             if( l_latency > (i_which_frame * FRAME_SIZE)
623                   && l_latency < ((i_which_frame+1) * FRAME_SIZE) )
624             {
625                 l_latency = - ( l_latency /
626                                 p_aout->output.output.i_bytes_per_frame %
627                                 FRAME_SIZE );
628             }
629             else
630             {
631                 l_latency = FRAME_SIZE - ( l_latency /
632                                       p_aout->output.output.i_bytes_per_frame %
633                                       FRAME_SIZE );
634             }
635         }
636         else
637         {
638             l_latency = 0;
639         }
640
641         /* Mark last frame as empty */
642         i_last_frame = (i_which_frame + FRAMES_NUM -1) % FRAMES_NUM;
643         i_next_frame = (i_which_frame + 1) % FRAMES_NUM;
644         p_notif->i_frame_status[i_last_frame] = FRAME_EMPTY;
645
646         /* Try to fill in as many frame buffers as possible */
647         for( i = i_next_frame; (i % FRAMES_NUM) != i_which_frame; i++ )
648         {
649
650             /* Check if frame buf is already filled */
651             if( p_notif->i_frame_status[i % FRAMES_NUM] == FRAME_QUEUED )
652                 continue;
653
654             p_buffer = aout_OutputNextBuffer( p_aout,
655                 mtime + 1000000 / p_aout->output.output.i_rate *
656                 ((i - i_next_frame + 1) * FRAME_SIZE + l_latency), VLC_FALSE );
657
658             /* If there is no audio data available and we have some buffered
659              * already, then just wait for the next time */
660             if( !p_buffer && (i != i_next_frame) )
661             {
662                 //msg_Err( p_aout, "only %i frame buffers filled!",
663                 //         i - i_next_frame );
664                 break;
665             }
666
667             if( DirectxFillBuffer( p_aout, (i%FRAMES_NUM), p_buffer )
668                 != VLC_SUCCESS )
669                 break;
670
671             /* Mark the frame buffer as QUEUED */
672             p_notif->i_frame_status[i%FRAMES_NUM] = FRAME_QUEUED;
673         }
674
675     }
676
677     /* free the events */
678     for( i = 0; i < FRAMES_NUM; i++ )
679         CloseHandle( notification_events[i] );
680
681     msg_Dbg( p_notif, "DirectSoundThread exiting" );
682 }