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