]> git.sesse.net Git - vlc/blob - modules/audio_output/directx.c
* modules/audio_output/directx.c: audio device selection based on a patch from Matthe...
[vlc] / modules / audio_output / directx.c
1 /*****************************************************************************
2  * directx.c: Windows DirectX audio output method
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
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 8
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 #ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
64 #   define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
65 #endif
66
67 #ifndef WAVE_FORMAT_EXTENSIBLE
68 #define  WAVE_FORMAT_EXTENSIBLE   0xFFFE
69 #endif
70
71 #ifndef SPEAKER_FRONT_LEFT
72 #   define SPEAKER_FRONT_LEFT             0x1
73 #   define SPEAKER_FRONT_RIGHT            0x2
74 #   define SPEAKER_FRONT_CENTER           0x4
75 #   define SPEAKER_LOW_FREQUENCY          0x8
76 #   define SPEAKER_BACK_LEFT              0x10
77 #   define SPEAKER_BACK_RIGHT             0x20
78 #   define SPEAKER_FRONT_LEFT_OF_CENTER   0x40
79 #   define SPEAKER_FRONT_RIGHT_OF_CENTER  0x80
80 #   define SPEAKER_BACK_CENTER            0x100
81 #   define SPEAKER_SIDE_LEFT              0x200
82 #   define SPEAKER_SIDE_RIGHT             0x400
83 #   define SPEAKER_TOP_CENTER             0x800
84 #   define SPEAKER_TOP_FRONT_LEFT         0x1000
85 #   define SPEAKER_TOP_FRONT_CENTER       0x2000
86 #   define SPEAKER_TOP_FRONT_RIGHT        0x4000
87 #   define SPEAKER_TOP_BACK_LEFT          0x8000
88 #   define SPEAKER_TOP_BACK_CENTER        0x10000
89 #   define SPEAKER_TOP_BACK_RIGHT         0x20000
90 #   define SPEAKER_RESERVED               0x80000000
91 #endif
92
93 #ifndef DSSPEAKER_HEADPHONE
94 #   define DSSPEAKER_HEADPHONE         0x00000001
95 #endif
96 #ifndef DSSPEAKER_MONO
97 #   define DSSPEAKER_MONO              0x00000002
98 #endif
99 #ifndef DSSPEAKER_QUAD
100 #   define DSSPEAKER_QUAD              0x00000003
101 #endif
102 #ifndef DSSPEAKER_STEREO
103 #   define DSSPEAKER_STEREO            0x00000004
104 #endif
105 #ifndef DSSPEAKER_SURROUND
106 #   define DSSPEAKER_SURROUND          0x00000005
107 #endif
108 #ifndef DSSPEAKER_5POINT1
109 #   define DSSPEAKER_5POINT1           0x00000006
110 #endif
111
112 #ifndef _WAVEFORMATEXTENSIBLE_
113 typedef struct {
114     WAVEFORMATEX    Format;
115     union {
116         WORD wValidBitsPerSample;       /* bits of precision  */
117         WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */
118         WORD wReserved;                 /* If neither applies, set to zero. */
119     } Samples;
120     DWORD           dwChannelMask;      /* which channels are */
121                                         /* present in stream  */
122     GUID            SubFormat;
123 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
124 #endif
125
126 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
127 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
128 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
129
130 /*****************************************************************************
131  * notification_thread_t: DirectX event thread
132  *****************************************************************************/
133 typedef struct notification_thread_t
134 {
135     VLC_COMMON_MEMBERS
136
137     aout_instance_t * p_aout;
138     int i_frame_status[FRAMES_NUM];           /* status of each frame buffer */
139     DSBPOSITIONNOTIFY p_events[FRAMES_NUM];      /* play notification events */
140     int i_frame_size;                         /* Size in bytes of one frame */
141
142     mtime_t start_date;
143
144 } notification_thread_t;
145
146 /*****************************************************************************
147  * aout_sys_t: directx audio output method descriptor
148  *****************************************************************************
149  * This structure is part of the audio output thread descriptor.
150  * It describes the direct sound specific properties of an audio device.
151  *****************************************************************************/
152 struct aout_sys_t
153 {
154     HINSTANCE           hdsound_dll;      /* handle of the opened dsound dll */
155
156     int                 i_device_id;                 /*  user defined device */
157     LPGUID              p_device_guid;
158
159     LPDIRECTSOUND       p_dsobject;              /* main Direct Sound object */
160     LPDIRECTSOUNDBUFFER p_dsbuffer;   /* the sound buffer we use (direct sound
161                                        * takes care of mixing all the
162                                        * secondary buffers into the primary) */
163
164     LPDIRECTSOUNDNOTIFY p_dsnotify;         /* the position notify interface */
165     notification_thread_t *p_notif;                  /* DirectSoundThread id */
166
167     int b_playing;                                         /* playing status */
168
169     int i_frame_size;                         /* Size in bytes of one frame */
170
171     vlc_bool_t b_chan_reorder;              /* do we need channel reordering */
172     int pi_chan_table[AOUT_CHAN_MAX];
173     uint32_t i_channel_mask;
174     uint32_t i_bits_per_sample;
175     uint32_t i_channels;
176 };
177
178 static const uint32_t pi_channels_src[] =
179     { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
180       AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
181       AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
182       AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 };
183 static const uint32_t pi_channels_in[] =
184     { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
185       SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT,
186       SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
187       SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, 0 };
188 static const uint32_t pi_channels_out[] =
189     { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
190       SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY,
191       SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
192       SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, 0 };
193
194 /*****************************************************************************
195  * Local prototypes.
196  *****************************************************************************/
197 static int  OpenAudio  ( vlc_object_t * );
198 static void CloseAudio ( vlc_object_t * );
199 static void Play       ( aout_instance_t * );
200
201 /* local functions */
202 static void Probe             ( aout_instance_t * );
203 static int  InitDirectSound   ( aout_instance_t * );
204 static int  CreateDSBuffer    ( aout_instance_t *, int, int, int, int, int, vlc_bool_t );
205 static int  CreateDSBufferPCM ( aout_instance_t *, int*, int, int, int, vlc_bool_t );
206 static void DestroyDSBuffer   ( aout_instance_t * );
207 static void DirectSoundThread ( notification_thread_t * );
208 static int  FillBuffer        ( aout_instance_t *, int, aout_buffer_t * );
209
210 /*****************************************************************************
211  * Module descriptor
212  *****************************************************************************/
213 #define DEVICE_TEXT N_("Output device")
214 #define DEVICE_LONGTEXT N_( \
215     "DirectX device number: 0 default device, 1..N device by number" \
216     "(Note that the default device appears as 0 AND another number)." )
217 #define FLOAT_TEXT N_("Use float32 output")
218 #define FLOAT_LONGTEXT N_( \
219     "The option allows you to enable or disable the high-quality float32 " \
220     "audio output mode (which is not well supported by some soundcards)." )
221
222 vlc_module_begin();
223     set_description( _("DirectX audio output") );
224     set_shortname( "DirectX" );
225     set_capability( "audio output", 100 );
226     set_category( CAT_AUDIO );
227     set_subcategory( SUBCAT_AUDIO_AOUT );
228     add_shortcut( "directx" );
229     add_integer( "directx-audio-device", 0, NULL, DEVICE_TEXT,
230                  DEVICE_LONGTEXT, VLC_TRUE );
231     add_bool( "directx-audio-float32", 1, 0, FLOAT_TEXT,
232               FLOAT_LONGTEXT, VLC_TRUE );
233     set_callbacks( OpenAudio, CloseAudio );
234 vlc_module_end();
235
236 /*****************************************************************************
237  * OpenAudio: open the audio device
238  *****************************************************************************
239  * This function opens and setups Direct Sound.
240  *****************************************************************************/
241 static int OpenAudio( vlc_object_t *p_this )
242 {
243     aout_instance_t * p_aout = (aout_instance_t *)p_this;
244     vlc_value_t val;
245     int i;
246
247     msg_Dbg( p_aout, "OpenAudio" );
248
249    /* Allocate structure */
250     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
251     if( p_aout->output.p_sys == NULL )
252     {
253         msg_Err( p_aout, "out of memory" );
254         return VLC_EGENERIC;
255     }
256
257     /* Initialize some variables */
258     p_aout->output.p_sys->p_dsobject = NULL;
259     p_aout->output.p_sys->p_dsbuffer = NULL;
260     p_aout->output.p_sys->p_dsnotify = NULL;
261     p_aout->output.p_sys->p_notif = NULL;
262     p_aout->output.p_sys->b_playing = 0;
263
264     p_aout->output.pf_play = Play;
265     aout_VolumeSoftInit( p_aout );
266
267     /* Retrieve config values */
268     var_Create( p_aout, "directx-audio-float32",
269                 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
270     var_Create( p_aout, "directx-audio-device",
271                 VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
272     var_Get( p_aout, "directx-audio-device", &val );
273     p_aout->output.p_sys->i_device_id = val.i_int;
274     p_aout->output.p_sys->p_device_guid = 0;
275
276     /* Initialise DirectSound */
277     if( InitDirectSound( p_aout ) )
278     {
279         msg_Err( p_aout, "cannot initialize DirectSound" );
280         goto error;
281     }
282
283     if( var_Type( p_aout, "audio-device" ) == 0 )
284     {
285         Probe( p_aout );
286     }
287
288     if( var_Get( p_aout, "audio-device", &val ) < 0 )
289     {
290         /* Probe() has failed. */
291         goto error;
292     }
293
294     /* Now we need to setup our DirectSound play notification structure */
295     p_aout->output.p_sys->p_notif =
296         vlc_object_create( p_aout, sizeof(notification_thread_t) );
297     p_aout->output.p_sys->p_notif->p_aout = p_aout;
298
299     /* Then create the notification events */
300     for( i = 0; i < FRAMES_NUM; i++ )
301         p_aout->output.p_sys->p_notif->p_events[i].hEventNotify =
302             CreateEvent( NULL, FALSE, FALSE, NULL );
303
304     /* Open the device */
305     if( val.i_int == AOUT_VAR_SPDIF )
306     {
307         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
308
309         /* Calculate the frame size in bytes */
310         p_aout->output.i_nb_samples = A52_FRAME_NB;
311         p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
312         p_aout->output.output.i_frame_length = A52_FRAME_NB;
313         p_aout->output.p_sys->i_frame_size =
314             p_aout->output.output.i_bytes_per_frame;
315
316         if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
317                             p_aout->output.output.i_physical_channels,
318                             aout_FormatNbChannels( &p_aout->output.output ),
319                             p_aout->output.output.i_rate,
320                             p_aout->output.p_sys->i_frame_size, VLC_FALSE )
321             != VLC_SUCCESS )
322         {
323             msg_Err( p_aout, "cannot open directx audio device" );
324             free( p_aout->output.p_sys );
325             return VLC_EGENERIC;
326         }
327
328         aout_VolumeNoneInit( p_aout );
329     }
330     else
331     {
332         if( val.i_int == AOUT_VAR_5_1 )
333         {
334             p_aout->output.output.i_physical_channels
335                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
336                    | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
337                    | AOUT_CHAN_LFE;
338         }
339         else if( val.i_int == AOUT_VAR_3F2R )
340         {
341             p_aout->output.output.i_physical_channels
342                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
343                    | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
344         }
345         else if( val.i_int == AOUT_VAR_2F2R )
346         {
347             p_aout->output.output.i_physical_channels
348                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
349                    | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
350         }
351         else if( val.i_int == AOUT_VAR_MONO )
352         {
353             p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
354         }
355         else
356         {
357             p_aout->output.output.i_physical_channels
358                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
359         }
360
361         if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format,
362                                p_aout->output.output.i_physical_channels,
363                                aout_FormatNbChannels( &p_aout->output.output ),
364                                p_aout->output.output.i_rate, VLC_FALSE )
365             != VLC_SUCCESS )
366         {
367             msg_Err( p_aout, "cannot open directx audio device" );
368             free( p_aout->output.p_sys );
369             return VLC_EGENERIC;
370         }
371
372         /* Calculate the frame size in bytes */
373         p_aout->output.i_nb_samples = FRAME_SIZE;
374         aout_FormatPrepare( &p_aout->output.output );
375         p_aout->output.p_sys->i_frame_size =
376             FRAME_SIZE * p_aout->output.output.i_bytes_per_frame;
377
378         aout_VolumeSoftInit( p_aout );
379     }
380
381     /* then launch the notification thread */
382     msg_Dbg( p_aout, "creating DirectSoundThread" );
383     if( vlc_thread_create( p_aout->output.p_sys->p_notif,
384                            "DirectSound Notification Thread",
385                            DirectSoundThread,
386                            VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
387     {
388         msg_Err( p_aout, "cannot create DirectSoundThread" );
389         goto error;
390     }
391
392     vlc_object_attach( p_aout->output.p_sys->p_notif, p_aout );
393
394     return VLC_SUCCESS;
395
396  error:
397     CloseAudio( VLC_OBJECT(p_aout) );
398     return VLC_EGENERIC;
399 }
400
401 /*****************************************************************************
402  * Probe: probe the audio device for available formats and channels
403  *****************************************************************************/
404 static void Probe( aout_instance_t * p_aout )
405 {
406     vlc_value_t val, text;
407     int i_format;
408     unsigned int i_physical_channels;
409     DWORD ui_speaker_config;
410
411     var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
412     text.psz_string = _("Audio Device");
413     var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
414
415     /* Test for 5.1 support */
416     i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
417                           AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
418                           AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
419     if( p_aout->output.output.i_physical_channels == i_physical_channels )
420     {
421         if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 6,
422                                p_aout->output.output.i_rate, VLC_TRUE )
423             == VLC_SUCCESS )
424         {
425             val.i_int = AOUT_VAR_5_1;
426             text.psz_string = N_("5.1");
427             var_Change( p_aout, "audio-device",
428                         VLC_VAR_ADDCHOICE, &val, &text );
429             msg_Dbg( p_aout, "device supports 5.1 channels" );
430         }
431     }
432
433     /* Test for 3 Front 2 Rear support */
434     i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
435                           AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
436                           AOUT_CHAN_REARRIGHT;
437     if( p_aout->output.output.i_physical_channels == i_physical_channels )
438     {
439         if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 5,
440                                p_aout->output.output.i_rate, VLC_TRUE )
441             == VLC_SUCCESS )
442         {
443             val.i_int = AOUT_VAR_3F2R;
444             text.psz_string = N_("3 Front 2 Rear");
445             var_Change( p_aout, "audio-device",
446                         VLC_VAR_ADDCHOICE, &val, &text );
447             msg_Dbg( p_aout, "device supports 5 channels" );
448         }
449     }
450
451     /* Test for 2 Front 2 Rear support */
452     i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
453                           AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
454     if( ( p_aout->output.output.i_physical_channels & i_physical_channels )
455         == i_physical_channels )
456     {
457         if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 4,
458                                p_aout->output.output.i_rate, VLC_TRUE )
459             == VLC_SUCCESS )
460         {
461             val.i_int = AOUT_VAR_2F2R;
462             text.psz_string = N_("2 Front 2 Rear");
463             var_Change( p_aout, "audio-device",
464                         VLC_VAR_ADDCHOICE, &val, &text );
465             msg_Dbg( p_aout, "device supports 4 channels" );
466         }
467     }
468
469     /* Test for stereo support */
470     i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
471     if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 2,
472                            p_aout->output.output.i_rate, VLC_TRUE )
473         == VLC_SUCCESS )
474     {
475         val.i_int = AOUT_VAR_STEREO;
476         text.psz_string = N_("Stereo");
477         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
478         var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
479         msg_Dbg( p_aout, "device supports 2 channels" );
480     }
481
482     /* Test for mono support */
483     i_physical_channels = AOUT_CHAN_CENTER;
484     if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 1,
485                            p_aout->output.output.i_rate, VLC_TRUE )
486         == VLC_SUCCESS )
487     {
488         val.i_int = AOUT_VAR_MONO;
489         text.psz_string = N_("Mono");
490         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
491         msg_Dbg( p_aout, "device supports 1 channel" );
492     }
493
494     /* Check the speaker configuration to determine which channel config should
495      * be the default */
496     if FAILED( IDirectSound_GetSpeakerConfig( p_aout->output.p_sys->p_dsobject,
497                                               &ui_speaker_config ) )
498     {
499         ui_speaker_config = DSSPEAKER_STEREO;
500     }
501     switch( DSSPEAKER_CONFIG(ui_speaker_config) )
502     {
503     case DSSPEAKER_5POINT1:
504         val.i_int = AOUT_VAR_5_1;
505         break;
506     case DSSPEAKER_QUAD:
507         val.i_int = AOUT_VAR_2F2R;
508         break;
509 #if 0 /* Lots of people just get their settings wrong and complain that
510        * this is a problem with VLC so just don't ever set mono by default. */
511     case DSSPEAKER_MONO:
512         val.i_int = AOUT_VAR_MONO;
513         break;
514 #endif
515     case DSSPEAKER_SURROUND:
516     case DSSPEAKER_STEREO:
517     default:
518         val.i_int = AOUT_VAR_STEREO;
519         break;
520     }
521     var_Set( p_aout, "audio-device", val );
522
523     /* Test for SPDIF support */
524     if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
525     {
526         if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
527                             p_aout->output.output.i_physical_channels,
528                             aout_FormatNbChannels( &p_aout->output.output ),
529                             p_aout->output.output.i_rate,
530                             AOUT_SPDIF_SIZE, VLC_TRUE )
531             == VLC_SUCCESS )
532         {
533             msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
534             val.i_int = AOUT_VAR_SPDIF;
535             text.psz_string = N_("A/52 over S/PDIF");
536             var_Change( p_aout, "audio-device",
537                         VLC_VAR_ADDCHOICE, &val, &text );
538             if( config_GetInt( p_aout, "spdif" ) )
539                 var_Set( p_aout, "audio-device", val );
540         }
541     }
542
543     var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
544     if( val.i_int <= 0 )
545     {
546         /* Probe() has failed. */
547         var_Destroy( p_aout, "audio-device" );
548         return;
549     }
550
551     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
552
553     val.b_bool = VLC_TRUE;
554     var_Set( p_aout, "intf-change", val );
555 }
556
557 /*****************************************************************************
558  * Play: we'll start playing the directsound buffer here because at least here
559  *       we know the first buffer has been put in the aout fifo and we also
560  *       know its date.
561  *****************************************************************************/
562 static void Play( aout_instance_t *p_aout )
563 {
564     if( !p_aout->output.p_sys->b_playing )
565     {
566         aout_buffer_t *p_buffer;
567
568         p_aout->output.p_sys->b_playing = 1;
569
570         /* get the playing date of the first aout buffer */
571         p_aout->output.p_sys->p_notif->start_date =
572             aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
573
574         /* fill in the first samples */
575         p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
576         FillBuffer( p_aout, 0, p_buffer );
577
578         /* wake up the audio output thread */
579         SetEvent( p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );
580     }
581 }
582
583 /*****************************************************************************
584  * CloseAudio: close the audio device
585  *****************************************************************************/
586 static void CloseAudio( vlc_object_t *p_this )
587 {
588     aout_instance_t * p_aout = (aout_instance_t *)p_this;
589     aout_sys_t *p_sys = p_aout->output.p_sys;
590
591     msg_Dbg( p_aout, "CloseAudio" );
592
593     /* kill the position notification thread, if any */
594     if( p_sys->p_notif )
595     {
596         vlc_object_detach( p_sys->p_notif );
597         if( p_sys->p_notif->b_thread )
598         {
599             p_sys->p_notif->b_die = 1;
600
601             if( !p_sys->b_playing )
602                 /* wake up the audio thread */
603                 SetEvent( p_sys->p_notif->p_events[0].hEventNotify );
604
605             vlc_thread_join( p_sys->p_notif );
606         }
607         vlc_object_destroy( p_sys->p_notif );
608     }
609
610     /* release the secondary buffer */
611     DestroyDSBuffer( p_aout );
612
613     /* finally release the DirectSound object */
614     if( p_sys->p_dsobject ) IDirectSound_Release( p_sys->p_dsobject );
615     
616     /* free DSOUND.DLL */
617     if( p_sys->hdsound_dll ) FreeLibrary( p_sys->hdsound_dll );
618
619     if( p_aout->output.p_sys->p_device_guid )
620         free( p_aout->output.p_sys->p_device_guid );
621
622     free( p_sys );
623 }
624
625 /*****************************************************************************
626  * CallBackDirectSoundEnum: callback to enumerate available devices
627  *****************************************************************************/
628 static int CALLBACK CallBackDirectSoundEnum( LPGUID p_guid, LPCSTR psz_desc,
629                                              LPCSTR psz_mod, LPVOID _p_aout )
630 {
631     aout_instance_t *p_aout = (aout_instance_t *)_p_aout;
632
633     msg_Dbg( p_aout, "found device: %s", psz_desc );
634
635     if( p_aout->output.p_sys->i_device_id == 0 && p_guid )
636     {
637         p_aout->output.p_sys->p_device_guid = malloc( sizeof( GUID ) );
638         *p_aout->output.p_sys->p_device_guid = *p_guid;
639         msg_Dbg( p_aout, "using device: %s", psz_desc );
640     }
641
642     p_aout->output.p_sys->i_device_id--;
643     return 1;
644 }
645
646 /*****************************************************************************
647  * InitDirectSound: handle all the gory details of DirectSound initialisation
648  *****************************************************************************/
649 static int InitDirectSound( aout_instance_t *p_aout )
650 {
651     HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
652     HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACK, LPVOID);
653
654     p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
655     if( p_aout->output.p_sys->hdsound_dll == NULL )
656     {
657         msg_Warn( p_aout, "cannot open DSOUND.DLL" );
658         goto error;
659     }
660
661     OurDirectSoundCreate = (void *)
662         GetProcAddress( p_aout->output.p_sys->hdsound_dll,
663                         "DirectSoundCreate" );
664     if( OurDirectSoundCreate == NULL )
665     {
666         msg_Warn( p_aout, "GetProcAddress FAILED" );
667         goto error;
668     }
669
670     /* Get DirectSoundEnumerate */
671     OurDirectSoundEnumerate = (void *)
672        GetProcAddress( p_aout->output.p_sys->hdsound_dll,
673                        "DirectSoundEnumerateA" );
674     if( OurDirectSoundEnumerate )
675     {
676         /* Attempt enumeration */
677         if( FAILED( OurDirectSoundEnumerate( CallBackDirectSoundEnum, 
678                                              p_aout ) ) )
679         {
680             msg_Dbg( p_aout, "enumeration of DirectSound devices failed" );
681         }
682     }
683
684     /* Create the direct sound object */
685     if FAILED( OurDirectSoundCreate( p_aout->output.p_sys->p_device_guid, 
686                                      &p_aout->output.p_sys->p_dsobject,
687                                      NULL ) )
688     {
689         msg_Warn( p_aout, "cannot create a direct sound device" );
690         goto error;
691     }
692
693     /* Set DirectSound Cooperative level, ie what control we want over Windows
694      * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
695      * settings of the primary buffer, but also that only the sound of our
696      * application will be hearable when it will have the focus.
697      * !!! (this is not really working as intended yet because to set the
698      * cooperative level you need the window handle of your application, and
699      * I don't know of any easy way to get it. Especially since we might play
700      * sound without any video, and so what window handle should we use ???
701      * The hack for now is to use the Desktop window handle - it seems to be
702      * working */
703     if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
704                                           GetDesktopWindow(),
705                                           DSSCL_EXCLUSIVE) )
706     {
707         msg_Warn( p_aout, "cannot set direct sound cooperative level" );
708     }
709
710     return VLC_SUCCESS;
711
712  error:
713     p_aout->output.p_sys->p_dsobject = NULL;
714     if( p_aout->output.p_sys->hdsound_dll )
715     {
716         FreeLibrary( p_aout->output.p_sys->hdsound_dll );
717         p_aout->output.p_sys->hdsound_dll = NULL;
718     }
719     return VLC_EGENERIC;
720
721 }
722
723 /*****************************************************************************
724  * CreateDSBuffer: Creates a direct sound buffer of the required format.
725  *****************************************************************************
726  * This function creates the buffer we'll use to play audio.
727  * In DirectSound there are two kinds of buffers:
728  * - the primary buffer: which is the actual buffer that the soundcard plays
729  * - the secondary buffer(s): these buffers are the one actually used by
730  *    applications and DirectSound takes care of mixing them into the primary.
731  *
732  * Once you create a secondary buffer, you cannot change its format anymore so
733  * you have to release the current one and create another.
734  *****************************************************************************/
735 static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
736                            int i_channels, int i_nb_channels, int i_rate,
737                            int i_bytes_per_frame, vlc_bool_t b_probe )
738 {
739     WAVEFORMATEXTENSIBLE waveformat;
740     DSBUFFERDESC         dsbdesc;
741     unsigned int         i;
742
743     /* First set the sound buffer format */
744     waveformat.dwChannelMask = 0;
745     for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ )
746     {
747         if( i_channels & pi_channels_src[i] )
748             waveformat.dwChannelMask |= pi_channels_in[i];
749     }
750
751     switch( i_format )
752     {
753     case VLC_FOURCC('s','p','d','i'):
754         i_nb_channels = 2;
755         /* To prevent channel re-ordering */
756         waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
757         waveformat.Format.wBitsPerSample = 16;
758         waveformat.Samples.wValidBitsPerSample =
759             waveformat.Format.wBitsPerSample;
760         waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
761         waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
762         break;
763
764     case VLC_FOURCC('f','l','3','2'):
765         waveformat.Format.wBitsPerSample = sizeof(float) * 8;
766         waveformat.Samples.wValidBitsPerSample =
767             waveformat.Format.wBitsPerSample;
768         waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
769         waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
770         break;
771
772     case VLC_FOURCC('s','1','6','l'):
773         waveformat.Format.wBitsPerSample = 16;
774         waveformat.Samples.wValidBitsPerSample =
775             waveformat.Format.wBitsPerSample;
776         waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
777         waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
778         break;
779     }
780
781     waveformat.Format.nChannels = i_nb_channels;
782     waveformat.Format.nSamplesPerSec = i_rate;
783     waveformat.Format.nBlockAlign =
784         waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
785     waveformat.Format.nAvgBytesPerSec =
786         waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
787
788     p_aout->output.p_sys->i_bits_per_sample = waveformat.Format.wBitsPerSample;
789     p_aout->output.p_sys->i_channels = i_nb_channels;
790
791     /* Then fill in the direct sound descriptor */
792     memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
793     dsbdesc.dwSize = sizeof(DSBUFFERDESC);
794     dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
795                     | DSBCAPS_CTRLPOSITIONNOTIFY     /* We need notification */
796                     | DSBCAPS_GLOBALFOCUS;      /* Allows background playing */
797
798     /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
799     if( i_nb_channels <= 2 )
800     {
801         waveformat.Format.cbSize = 0;
802     }
803     else
804     {
805         waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
806         waveformat.Format.cbSize =
807             sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
808
809         /* Needed for 5.1 on emu101k */
810         dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
811     }
812
813     dsbdesc.dwBufferBytes = FRAMES_NUM * i_bytes_per_frame;   /* buffer size */
814     dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;
815
816     if FAILED( IDirectSound_CreateSoundBuffer(
817                    p_aout->output.p_sys->p_dsobject, &dsbdesc,
818                    &p_aout->output.p_sys->p_dsbuffer, NULL) )
819     {
820         if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE )
821         {
822             /* Try without DSBCAPS_LOCHARDWARE */
823             dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
824             if FAILED( IDirectSound_CreateSoundBuffer(
825                    p_aout->output.p_sys->p_dsobject, &dsbdesc,
826                    &p_aout->output.p_sys->p_dsbuffer, NULL) )
827             {
828                 return VLC_EGENERIC;
829             }
830             if( !b_probe )
831                 msg_Dbg( p_aout, "couldn't use hardware sound buffer" );
832         }
833         else
834         {
835             return VLC_EGENERIC;
836         }
837     }
838
839     /* Stop here if we were just probing */
840     if( b_probe )
841     {
842         IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
843         p_aout->output.p_sys->p_dsbuffer = NULL;
844         return VLC_SUCCESS;
845     }
846
847     /* Backup the size of a frame */
848     p_aout->output.p_sys->p_notif->i_frame_size = i_bytes_per_frame;
849
850     /* Now the secondary buffer is created, we need to setup its position
851      * notification */
852     for( i = 0; i < FRAMES_NUM; i++ )
853     {
854         p_aout->output.p_sys->p_notif->p_events[i].dwOffset = i *
855             p_aout->output.p_sys->p_notif->i_frame_size;
856
857         p_aout->output.p_sys->p_notif->i_frame_status[i] = FRAME_EMPTY;
858     }
859
860     /* Get the IDirectSoundNotify interface */
861     if FAILED( IDirectSoundBuffer_QueryInterface(
862                                 p_aout->output.p_sys->p_dsbuffer,
863                                 &IID_IDirectSoundNotify,
864                                 (LPVOID *)&p_aout->output.p_sys->p_dsnotify ) )
865     {
866         msg_Err( p_aout, "cannot get IDirectSoundNotify interface" );
867         goto error;
868     }
869
870     if FAILED( IDirectSoundNotify_SetNotificationPositions(
871                                     p_aout->output.p_sys->p_dsnotify,
872                                     FRAMES_NUM,
873                                     p_aout->output.p_sys->p_notif->p_events ) )
874     {
875         msg_Err( p_aout, "cannot set position notification" );
876         goto error;
877     }
878
879     p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask;
880
881     p_aout->output.p_sys->b_chan_reorder =
882         aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
883                                   waveformat.dwChannelMask, i_nb_channels,
884                                   p_aout->output.p_sys->pi_chan_table );
885
886     if( p_aout->output.p_sys->b_chan_reorder )
887     {
888         msg_Dbg( p_aout, "channel reordering needed" );
889     }
890
891     return VLC_SUCCESS;
892
893  error:
894     DestroyDSBuffer( p_aout );
895     return VLC_EGENERIC;
896 }
897
898 /*****************************************************************************
899  * CreateDSBufferPCM: creates a PCM direct sound buffer.
900  *****************************************************************************
901  * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
902  * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
903  ****************************************************************************/
904 static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format,
905                               int i_channels, int i_nb_channels, int i_rate,
906                               vlc_bool_t b_probe )
907 {
908     vlc_value_t val;
909
910     var_Get( p_aout, "directx-audio-float32", &val );
911
912     /* Float32 audio samples are not supported for 5.1 output on the emu101k */
913
914     if( !val.b_bool || i_nb_channels > 2 ||
915         CreateDSBuffer( p_aout, VLC_FOURCC('f','l','3','2'),
916                         i_channels, i_nb_channels, i_rate,
917                         FRAME_SIZE * 4 * i_nb_channels, b_probe )
918         != VLC_SUCCESS )
919     {
920         if ( CreateDSBuffer( p_aout, VLC_FOURCC('s','1','6','l'),
921                              i_channels, i_nb_channels, i_rate,
922                              FRAME_SIZE * 2 * i_nb_channels, b_probe )
923              != VLC_SUCCESS )
924         {
925             return VLC_EGENERIC;
926         }
927         else
928         {
929             *i_format = VLC_FOURCC('s','1','6','l');
930             return VLC_SUCCESS;
931         }
932     }
933     else
934     {
935         *i_format = VLC_FOURCC('f','l','3','2');
936         return VLC_SUCCESS;
937     }
938 }
939
940 /*****************************************************************************
941  * DestroyDSBuffer
942  *****************************************************************************
943  * This function destroys the secondary buffer.
944  *****************************************************************************/
945 static void DestroyDSBuffer( aout_instance_t *p_aout )
946 {
947     if( p_aout->output.p_sys->p_dsnotify )
948     {
949         IDirectSoundNotify_Release( p_aout->output.p_sys->p_dsnotify );
950         p_aout->output.p_sys->p_dsnotify = NULL;
951     }
952
953     if( p_aout->output.p_sys->p_dsbuffer )
954     {
955         IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
956         p_aout->output.p_sys->p_dsbuffer = NULL;
957     }
958 }
959
960 /*****************************************************************************
961  * FillBuffer: Fill in one of the direct sound frame buffers.
962  *****************************************************************************
963  * Returns VLC_SUCCESS on success.
964  *****************************************************************************/
965 static int FillBuffer( aout_instance_t *p_aout, int i_frame,
966                        aout_buffer_t *p_buffer )
967 {
968     notification_thread_t *p_notif = p_aout->output.p_sys->p_notif;
969     aout_sys_t *p_sys = p_aout->output.p_sys;
970     void *p_write_position, *p_wrap_around;
971     long l_bytes1, l_bytes2;
972     HRESULT dsresult;
973
974     /* Before copying anything, we have to lock the buffer */
975     dsresult = IDirectSoundBuffer_Lock(
976                 p_sys->p_dsbuffer,                              /* DS buffer */
977                 i_frame * p_notif->i_frame_size,             /* Start offset */
978                 p_notif->i_frame_size,                    /* Number of bytes */
979                 &p_write_position,                  /* Address of lock start */
980                 &l_bytes1,       /* Count of bytes locked before wrap around */
981                 &p_wrap_around,            /* Buffer adress (if wrap around) */
982                 &l_bytes2,               /* Count of bytes after wrap around */
983                 0 );                                                /* Flags */
984     if( dsresult == DSERR_BUFFERLOST )
985     {
986         IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
987         dsresult = IDirectSoundBuffer_Lock(
988                                p_sys->p_dsbuffer,
989                                i_frame * p_notif->i_frame_size,
990                                p_notif->i_frame_size,
991                                &p_write_position,
992                                &l_bytes1,
993                                &p_wrap_around,
994                                &l_bytes2,
995                                0 );
996     }
997     if( dsresult != DS_OK )
998     {
999         msg_Warn( p_notif, "cannot lock buffer" );
1000         if( p_buffer ) aout_BufferFree( p_buffer );
1001         return VLC_EGENERIC;
1002     }
1003
1004     if( p_buffer == NULL )
1005     {
1006         memset( p_write_position, 0, l_bytes1 );
1007     }
1008     else
1009     {
1010         if( p_sys->b_chan_reorder )
1011         {
1012             /* Do the channel reordering here */
1013             aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_nb_bytes,
1014                                  p_sys->i_channels, p_sys->pi_chan_table,
1015                                  p_sys->i_bits_per_sample );
1016         }
1017
1018         p_aout->p_vlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
1019                                   l_bytes1 );
1020         aout_BufferFree( p_buffer );
1021     }
1022
1023     /* Now the data has been copied, unlock the buffer */
1024     IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
1025                                p_wrap_around, l_bytes2 );
1026
1027     return VLC_SUCCESS;
1028 }
1029
1030 /*****************************************************************************
1031  * DirectSoundThread: this thread will capture play notification events. 
1032  *****************************************************************************
1033  * We use this thread to emulate a callback mechanism. The thread probes for
1034  * event notification and fills up the DS secondary buffer when needed.
1035  *****************************************************************************/
1036 static void DirectSoundThread( notification_thread_t *p_notif )
1037 {
1038     HANDLE  notification_events[FRAMES_NUM];
1039     HRESULT dsresult;
1040     aout_instance_t *p_aout = p_notif->p_aout;
1041     int i, i_which_frame, i_last_frame, i_next_frame;
1042     mtime_t mtime;
1043     vlc_bool_t b_sleek;
1044
1045     for( i = 0; i < FRAMES_NUM; i++ )
1046         notification_events[i] = p_notif->p_events[i].hEventNotify;
1047
1048     /* We don't want any resampling when using S/PDIF output */
1049     b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i');
1050
1051     /* Tell the main thread that we are ready */
1052     vlc_thread_ready( p_notif );
1053
1054     msg_Dbg( p_notif, "DirectSoundThread ready" );
1055
1056     /* Wait here until Play() is called */
1057     WaitForSingleObject( notification_events[0], INFINITE );
1058
1059     if( !p_notif->b_die )
1060     {
1061         mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 );
1062
1063         /* start playing the buffer */
1064         dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer,
1065                                         0,                         /* Unused */
1066                                         0,                         /* Unused */
1067                                         DSBPLAY_LOOPING );          /* Flags */
1068         if( dsresult == DSERR_BUFFERLOST )
1069         {
1070             IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
1071             dsresult = IDirectSoundBuffer_Play(
1072                                             p_aout->output.p_sys->p_dsbuffer,
1073                                             0,                     /* Unused */
1074                                             0,                     /* Unused */
1075                                             DSBPLAY_LOOPING );      /* Flags */
1076         }
1077         if( dsresult != DS_OK )
1078         {
1079             msg_Err( p_aout, "cannot start playing buffer" );
1080         }
1081     }
1082
1083     while( !p_notif->b_die )
1084     {
1085         aout_buffer_t *p_buffer;
1086         long l_latency;
1087
1088         /* wait for the position notification */
1089         i_which_frame = WaitForMultipleObjects( FRAMES_NUM,
1090                                                 notification_events, 0,
1091                                                 INFINITE ) - WAIT_OBJECT_0;
1092
1093         if( p_notif->b_die )
1094             break;
1095
1096         mtime = mdate();
1097
1098         /* We take into account the current latency */
1099         if SUCCEEDED( IDirectSoundBuffer_GetCurrentPosition(
1100                         p_aout->output.p_sys->p_dsbuffer,
1101                         &l_latency, NULL ) )
1102         {
1103             if( l_latency > (i_which_frame * FRAME_SIZE)
1104                   && l_latency < ((i_which_frame+1) * FRAME_SIZE) )
1105             {
1106                 l_latency = - ( l_latency /
1107                                 p_aout->output.output.i_bytes_per_frame %
1108                                 FRAME_SIZE );
1109             }
1110             else
1111             {
1112                 l_latency = FRAME_SIZE - ( l_latency /
1113                                       p_aout->output.output.i_bytes_per_frame %
1114                                       FRAME_SIZE );
1115             }
1116         }
1117         else
1118         {
1119             l_latency = 0;
1120         }
1121
1122         /* Mark last frame as empty */
1123         i_last_frame = (i_which_frame + FRAMES_NUM -1) % FRAMES_NUM;
1124         i_next_frame = (i_which_frame + 1) % FRAMES_NUM;
1125         p_notif->i_frame_status[i_last_frame] = FRAME_EMPTY;
1126
1127         /* Try to fill in as many frame buffers as possible */
1128         for( i = i_next_frame; (i % FRAMES_NUM) != i_which_frame; i++ )
1129         {
1130
1131             /* Check if frame buf is already filled */
1132             if( p_notif->i_frame_status[i % FRAMES_NUM] == FRAME_QUEUED )
1133                 continue;
1134
1135             p_buffer = aout_OutputNextBuffer( p_aout,
1136                 mtime + 1000000 / p_aout->output.output.i_rate *
1137                 ((i - i_next_frame + 1) * FRAME_SIZE + l_latency), b_sleek );
1138
1139             /* If there is no audio data available and we have some buffered
1140              * already, then just wait for the next time */
1141             if( !p_buffer && (i != i_next_frame) )
1142             {
1143                 //msg_Err( p_aout, "only %i frame buffers filled!",
1144                 //         i - i_next_frame );
1145                 break;
1146             }
1147
1148             if( FillBuffer( p_aout, (i%FRAMES_NUM), p_buffer )
1149                 != VLC_SUCCESS )
1150                 break;
1151
1152             /* Mark the frame buffer as QUEUED */
1153             p_notif->i_frame_status[i%FRAMES_NUM] = FRAME_QUEUED;
1154         }
1155
1156     }
1157
1158     /* make sure the buffer isn't playing */
1159     IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer );
1160
1161     /* free the events */
1162     for( i = 0; i < FRAMES_NUM; i++ )
1163         CloseHandle( notification_events[i] );
1164
1165     msg_Dbg( p_notif, "DirectSoundThread exiting" );
1166 }