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