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