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