]> git.sesse.net Git - vlc/blob - modules/audio_output/directsound.c
mmdevice: avoid incorrect assertion
[vlc] / modules / audio_output / directsound.c
1 /*****************************************************************************
2  * directsound.c: DirectSound audio output plugin for VLC
3  *****************************************************************************
4  * Copyright (C) 2001-2009 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation, Inc.,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <math.h>
33
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_aout.h>
37 #include <vlc_charset.h>
38
39 #include "audio_output/windows_audio_common.h"
40 #include "audio_output/mmdevice.h"
41 #include <mmdeviceapi.h>
42
43 #define DS_BUF_SIZE (6*1024*1024)
44
45 static int  Open( vlc_object_t * );
46 static void Close( vlc_object_t * );
47 static HRESULT StreamStart( aout_stream_t *, audio_sample_format_t *,
48                             const GUID * );
49 static HRESULT StreamStop( aout_stream_t * );
50 static int ReloadDirectXDevices( vlc_object_t *, const char *,
51                                  char ***, char *** );
52
53 /* Speaker setup override options list */
54 static const char *const speaker_list[] = { "Windows default", "Mono", "Stereo",
55                                             "Quad", "5.1", "7.1" };
56
57 /*****************************************************************************
58  * Module descriptor
59  *****************************************************************************/
60 #define DEVICE_TEXT N_("Output device")
61 #define DEVICE_LONGTEXT N_("Select your audio output device")
62
63 #define SPEAKER_TEXT N_("Speaker configuration")
64 #define SPEAKER_LONGTEXT N_("Select speaker configuration you want to use. " \
65     "This option doesn't upmix! So NO e.g. Stereo -> 5.1 conversion." )
66
67 #define VOLUME_TEXT N_("Audio volume")
68 #define VOLUME_LONGTEXT N_("Audio volume in hundredths of decibels (dB).")
69
70 vlc_module_begin ()
71     set_description( N_("DirectX audio output") )
72     set_shortname( "DirectX" )
73     set_capability( "audio output", 100 )
74     set_category( CAT_AUDIO )
75     set_subcategory( SUBCAT_AUDIO_AOUT )
76     add_shortcut( "directx", "aout_directx" )
77
78     add_string( "directx-audio-device", NULL,
79              DEVICE_TEXT, DEVICE_LONGTEXT, false )
80         change_string_cb( ReloadDirectXDevices )
81     add_obsolete_string( "directx-audio-device-name")
82     add_bool( "directx-audio-float32", true, FLOAT_TEXT,
83               FLOAT_LONGTEXT, true )
84     add_string( "directx-audio-speaker", "Windows default",
85                  SPEAKER_TEXT, SPEAKER_LONGTEXT, true )
86         change_string_list( speaker_list, speaker_list )
87     add_float( "directx-volume", 1.0f,
88                  VOLUME_TEXT, VOLUME_LONGTEXT, true )
89         change_integer_range( DSBVOLUME_MIN, DSBVOLUME_MAX )
90
91     set_callbacks( Open, Close )
92
93     add_submodule()
94         set_capability( "aout stream", 30 )
95         set_callbacks( StreamStart, StreamStop )
96 vlc_module_end ()
97
98 typedef struct aout_stream_sys
99 {
100     LPDIRECTSOUND       p_dsobject; /*< main Direct Sound object */
101     LPDIRECTSOUNDBUFFER p_dsbuffer; /*< the sound buffer we use (direct sound
102                                         takes care of mixing all the secondary
103                                         buffers into the primary) */
104     LPDIRECTSOUNDNOTIFY p_notify;
105
106     int      i_bytes_per_sample;    /*< Size in bytes of one frame */
107     int      i_rate;                /*< Sample rate */
108
109     uint8_t  chans_to_reorder;      /*< Do we need channel reordering? */
110     uint8_t  chan_table[AOUT_CHAN_MAX];
111     uint32_t i_channel_mask;
112     vlc_fourcc_t format;
113
114     size_t  i_write;
115 } aout_stream_sys_t;
116
117 /**
118  * DirectSound audio output method descriptor
119  *
120  * This structure is part of the audio output thread descriptor.
121  * It describes the direct sound specific properties of an audio device.
122  */
123 struct aout_sys_t
124 {
125     aout_stream_sys_t s;
126     struct
127     {
128         float         volume;
129         LONG          mb;
130         bool          mute;
131     } volume;
132     HINSTANCE         hdsound_dll; /*< handle of the opened dsound DLL */
133 };
134
135 static HRESULT TimeGet( aout_stream_sys_t *sys, mtime_t *delay )
136 {
137     DWORD read;
138     HRESULT hr;
139     mtime_t size;
140
141     hr = IDirectSoundBuffer_GetCurrentPosition( sys->p_dsbuffer, &read, NULL );
142     if( hr != DS_OK )
143         return hr;
144
145     read %= DS_BUF_SIZE;
146
147     size = (mtime_t)sys->i_write - (mtime_t) read;
148     if( size < 0 )
149         size += DS_BUF_SIZE;
150
151     *delay = ( size / sys->i_bytes_per_sample ) * CLOCK_FREQ / sys->i_rate;
152     return DS_OK;
153 }
154
155 static HRESULT StreamTimeGet( aout_stream_t *s, mtime_t *delay )
156 {
157     return TimeGet( s->sys, delay );
158 }
159
160 static int OutputTimeGet( audio_output_t *aout, mtime_t *delay )
161 {
162     return (TimeGet( &aout->sys->s, delay ) == DS_OK) ? 0 : -1;
163 }
164
165 /**
166  * Fills in one of the DirectSound frame buffers.
167  *
168  * @return VLC_SUCCESS on success.
169  */
170 static HRESULT FillBuffer( vlc_object_t *obj, aout_stream_sys_t *p_sys,
171                            block_t *p_buffer )
172 {
173     size_t towrite = (p_buffer != NULL) ? p_buffer->i_buffer : DS_BUF_SIZE;
174     void *p_write_position, *p_wrap_around;
175     unsigned long l_bytes1, l_bytes2;
176     DWORD i_read;
177     size_t i_size;
178     mtime_t i_buf;
179     HRESULT dsresult;
180
181     size_t toerase = p_sys->i_bytes_per_sample * p_sys->i_rate / 4;
182     mtime_t max = towrite;
183
184     dsresult = IDirectSoundBuffer_GetCurrentPosition( p_sys->p_dsbuffer,
185                                                       &i_read, NULL );
186     if( dsresult == DS_OK )
187     {
188         max = (mtime_t)i_read - (mtime_t)p_sys->i_write;
189         if( max <= 0 )
190             max += DS_BUF_SIZE;
191     }
192
193     if( towrite + toerase <= max )
194         i_buf = towrite + toerase;
195     else
196         i_buf = towrite;
197
198     /* Before copying anything, we have to lock the buffer */
199     dsresult = IDirectSoundBuffer_Lock(
200            p_sys->p_dsbuffer,    /* DS buffer */
201            p_sys->i_write,       /* Start offset */
202            i_buf,                /* Number of bytes */
203            &p_write_position,    /* Address of lock start */
204            &l_bytes1,            /* Count of bytes locked before wrap around */
205            &p_wrap_around,       /* Buffer address (if wrap around) */
206            &l_bytes2,            /* Count of bytes after wrap around */
207            0 );                  /* Flags: DSBLOCK_FROMWRITECURSOR is buggy */
208     if( dsresult == DSERR_BUFFERLOST )
209     {
210         IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
211         dsresult = IDirectSoundBuffer_Lock(
212                                p_sys->p_dsbuffer,
213                                p_sys->i_write,
214                                i_buf,
215                                &p_write_position,
216                                &l_bytes1,
217                                &p_wrap_around,
218                                &l_bytes2,
219                                0 );
220     }
221     if( dsresult != DS_OK )
222     {
223         msg_Warn( obj, "cannot lock buffer" );
224         if( p_buffer != NULL )
225             block_Release( p_buffer );
226         return dsresult;
227     }
228
229     if( p_buffer == NULL )
230     {
231         memset( p_write_position, 0, l_bytes1 );
232         memset( p_wrap_around, 0, l_bytes2 );
233     }
234     else
235     {
236         if( p_sys->chans_to_reorder ) /* Do the channel reordering here */
237             aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer,
238                                  p_sys->chans_to_reorder, p_sys->chan_table,
239                                  p_sys->format );
240
241
242         i_size = ( p_buffer->i_buffer < l_bytes1 ) ? p_buffer->i_buffer : l_bytes1;
243         memcpy( p_write_position, p_buffer->p_buffer, i_size );
244         memset( (uint8_t*) p_write_position + i_size, 0, l_bytes1 - i_size );
245
246         if( l_bytes1 < p_buffer->i_buffer)
247         {   /* Compute the remaining buffer space to be written */
248             i_buf = i_buf - i_size - (l_bytes1 - i_size);
249             i_size = ( p_buffer->i_buffer - l_bytes1 < l_bytes2 ) ? p_buffer->i_buffer - l_bytes1 : l_bytes2;
250             memcpy( p_wrap_around, p_buffer->p_buffer + l_bytes1, i_size );
251             memset( (uint8_t*) p_wrap_around + i_size, 0, i_buf - i_size );
252         }
253         block_Release( p_buffer );
254     }
255
256     /* Now the data has been copied, unlock the buffer */
257     IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
258                                p_wrap_around, l_bytes2 );
259
260     p_sys->i_write += towrite;
261     p_sys->i_write %= DS_BUF_SIZE;
262
263     return DS_OK;
264 }
265
266 static HRESULT Play( vlc_object_t *obj, aout_stream_sys_t *sys,
267                      block_t *p_buffer )
268 {
269     HRESULT dsresult;
270
271     dsresult = FillBuffer( obj, sys, p_buffer );
272     if( dsresult != DS_OK )
273         return dsresult;
274
275     /* start playing the buffer */
276     dsresult = IDirectSoundBuffer_Play( sys->p_dsbuffer, 0, 0,
277                                         DSBPLAY_LOOPING );
278     if( dsresult == DSERR_BUFFERLOST )
279     {
280         IDirectSoundBuffer_Restore( sys->p_dsbuffer );
281         dsresult = IDirectSoundBuffer_Play( sys->p_dsbuffer,
282                                             0, 0, DSBPLAY_LOOPING );
283     }
284     if( dsresult != DS_OK )
285         msg_Err( obj, "cannot start playing buffer" );
286     return dsresult;
287 }
288
289 static HRESULT StreamPlay( aout_stream_t *s, block_t *block )
290 {
291     return Play( VLC_OBJECT(s), s->sys, block );
292 }
293
294 static void OutputPlay( audio_output_t *aout, block_t *block )
295 {
296     Play( VLC_OBJECT(aout), &aout->sys->s, block );
297 }
298
299 static HRESULT Pause( aout_stream_sys_t *sys, bool pause )
300 {
301     HRESULT hr;
302
303     if( pause )
304         hr = IDirectSoundBuffer_Stop( sys->p_dsbuffer );
305     else
306         hr = IDirectSoundBuffer_Play( sys->p_dsbuffer, 0, 0, DSBPLAY_LOOPING );
307     return hr;
308 }
309
310 static HRESULT StreamPause( aout_stream_t *s, bool pause )
311 {
312     return Pause( s->sys, pause );
313 }
314
315 static void OutputPause( audio_output_t *aout, bool pause, mtime_t date )
316 {
317     Pause( &aout->sys->s, pause );
318     (void) date;
319 }
320
321 static HRESULT Flush( aout_stream_sys_t *sys )
322 {
323     return IDirectSoundBuffer_Stop( sys->p_dsbuffer );
324 }
325
326 static HRESULT StreamFlush( aout_stream_t *s )
327 {
328     return Flush( s->sys );
329 }
330
331 static void OutputFlush( audio_output_t *aout, bool drain )
332 {
333     aout_stream_sys_t *sys = &aout->sys->s;
334
335     Flush( sys );
336     if( !drain )
337         IDirectSoundBuffer_SetCurrentPosition( sys->p_dsbuffer, sys->i_write );
338 }
339
340 /**
341  * Creates a DirectSound buffer of the required format.
342  *
343  * This function creates the buffer we'll use to play audio.
344  * In DirectSound there are two kinds of buffers:
345  * - the primary buffer: which is the actual buffer that the soundcard plays
346  * - the secondary buffer(s): these buffers are the one actually used by
347  *    applications and DirectSound takes care of mixing them into the primary.
348  *
349  * Once you create a secondary buffer, you cannot change its format anymore so
350  * you have to release the current one and create another.
351  */
352 static HRESULT CreateDSBuffer( vlc_object_t *obj, aout_stream_sys_t *sys,
353                                int i_format, int i_channels, int i_nb_channels,
354                                int i_rate, bool b_probe )
355 {
356     WAVEFORMATEXTENSIBLE waveformat;
357     DSBUFFERDESC         dsbdesc;
358     HRESULT              hr;
359
360     /* First set the sound buffer format */
361     waveformat.dwChannelMask = 0;
362     for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ )
363         if( i_channels & pi_vlc_chan_order_wg4[i] )
364             waveformat.dwChannelMask |= pi_channels_in[i];
365
366     switch( i_format )
367     {
368     case VLC_CODEC_SPDIFL:
369         i_nb_channels = 2;
370         /* To prevent channel re-ordering */
371         waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
372         waveformat.Format.wBitsPerSample = 16;
373         waveformat.Samples.wValidBitsPerSample =
374             waveformat.Format.wBitsPerSample;
375         waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
376         waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
377         break;
378
379     case VLC_CODEC_FL32:
380         waveformat.Format.wBitsPerSample = sizeof(float) * 8;
381         waveformat.Samples.wValidBitsPerSample =
382             waveformat.Format.wBitsPerSample;
383         waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
384         waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
385         break;
386
387     case VLC_CODEC_S16N:
388         waveformat.Format.wBitsPerSample = 16;
389         waveformat.Samples.wValidBitsPerSample =
390             waveformat.Format.wBitsPerSample;
391         waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
392         waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
393         break;
394     }
395
396     waveformat.Format.nChannels = i_nb_channels;
397     waveformat.Format.nSamplesPerSec = i_rate;
398     waveformat.Format.nBlockAlign =
399         waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
400     waveformat.Format.nAvgBytesPerSec =
401         waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
402
403     sys->i_bytes_per_sample = waveformat.Format.nBlockAlign;
404     sys->format = i_format;
405
406     /* Then fill in the direct sound descriptor */
407     memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
408     dsbdesc.dwSize = sizeof(DSBUFFERDESC);
409     dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /* Better position accuracy */
410                     | DSBCAPS_GLOBALFOCUS         /* Allows background playing */
411                     | DSBCAPS_CTRLVOLUME          /* Allows volume control */
412                     | DSBCAPS_CTRLPOSITIONNOTIFY; /* Allow position notifications */
413
414     /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
415     if( i_nb_channels <= 2 )
416     {
417         waveformat.Format.cbSize = 0;
418     }
419     else
420     {
421         waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
422         waveformat.Format.cbSize =
423             sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
424
425         /* Needed for 5.1 on emu101k */
426         dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
427     }
428
429     dsbdesc.dwBufferBytes = DS_BUF_SIZE; /* buffer size */
430     dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;
431
432     /* CreateSoundBuffer doesn't allow volume control for non-PCM buffers */
433     if ( i_format == VLC_CODEC_SPDIFL )
434         dsbdesc.dwFlags &= ~DSBCAPS_CTRLVOLUME;
435
436     hr = IDirectSound_CreateSoundBuffer( sys->p_dsobject, &dsbdesc,
437                                          &sys->p_dsbuffer, NULL );
438     if( FAILED(hr) )
439     {
440         if( !(dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) )
441             return hr;
442
443         /* Try without DSBCAPS_LOCHARDWARE */
444         dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
445         hr = IDirectSound_CreateSoundBuffer( sys->p_dsobject, &dsbdesc,
446                                              &sys->p_dsbuffer, NULL );
447         if( FAILED(hr) )
448             return hr;
449         if( !b_probe )
450             msg_Dbg( obj, "couldn't use hardware sound buffer" );
451     }
452
453     /* Stop here if we were just probing */
454     if( b_probe )
455     {
456         IDirectSoundBuffer_Release( sys->p_dsbuffer );
457         sys->p_dsbuffer = NULL;
458         return DS_OK;
459     }
460
461     sys->i_rate = i_rate;
462     sys->i_channel_mask = waveformat.dwChannelMask;
463     sys->chans_to_reorder =
464         aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
465                                   waveformat.dwChannelMask, sys->chan_table );
466     if( sys->chans_to_reorder )
467         msg_Dbg( obj, "channel reordering needed" );
468
469     hr = IDirectSoundBuffer_QueryInterface( sys->p_dsbuffer,
470                                             &IID_IDirectSoundNotify,
471                                             (void **) &sys->p_notify );
472     if( hr != DS_OK )
473     {
474         msg_Err( obj, "Couldn't query IDirectSoundNotify" );
475         sys->p_notify = NULL;
476     }
477
478     FillBuffer( obj, sys, NULL );
479     return DS_OK;
480 }
481
482 /**
483  * Creates a PCM DirectSound buffer.
484  *
485  * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
486  * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
487  */
488 static HRESULT CreateDSBufferPCM( vlc_object_t *obj, aout_stream_sys_t *sys,
489                                   vlc_fourcc_t *i_format, int i_channels,
490                                   int i_rate, bool b_probe )
491 {
492     HRESULT hr;
493     unsigned i_nb_channels = popcount( i_channels );
494
495     if( var_GetBool( obj, "directx-audio-float32" ) )
496     {
497         hr = CreateDSBuffer( obj, sys, VLC_CODEC_FL32, i_channels,
498                              i_nb_channels, i_rate, b_probe );
499         if( hr == DS_OK )
500         {
501             *i_format = VLC_CODEC_FL32;
502             return DS_OK;
503         }
504     }
505
506     hr = CreateDSBuffer( obj, sys, VLC_CODEC_S16N, i_channels, i_nb_channels,
507                          i_rate, b_probe );
508     if( hr == DS_OK )
509     {
510         *i_format = VLC_CODEC_S16N;
511         return DS_OK;
512     }
513
514     return hr;
515 }
516
517 /**
518  * Closes the audio device.
519  */
520 static HRESULT Stop( aout_stream_sys_t *p_sys )
521 {
522     if( p_sys->p_notify != NULL )
523     {
524         IDirectSoundNotify_Release(p_sys->p_notify );
525         p_sys->p_notify = NULL;
526     }
527     if( p_sys->p_dsbuffer != NULL )
528     {
529         IDirectSoundBuffer_Stop( p_sys->p_dsbuffer );
530         IDirectSoundBuffer_Release( p_sys->p_dsbuffer );
531         p_sys->p_dsbuffer = NULL;
532     }
533     if( p_sys->p_dsobject != NULL )
534     {
535         IDirectSound_Release( p_sys->p_dsobject );
536         p_sys->p_dsobject = NULL;
537     }
538     return DS_OK;
539 }
540
541 static HRESULT StreamStop( aout_stream_t *s )
542 {
543     HRESULT hr;
544
545     hr = Stop( s->sys );
546     free( s->sys );
547     return hr;
548 }
549
550 static void OutputStop( audio_output_t *aout )
551 {
552     msg_Dbg( aout, "closing audio device" );
553     Stop( &aout->sys->s );
554 }
555
556 static HRESULT Start( vlc_object_t *obj, aout_stream_sys_t *sys,
557                       audio_sample_format_t *restrict fmt )
558 {
559 #if !VLC_WINSTORE_APP
560     /* Set DirectSound Cooperative level, ie what control we want over Windows
561      * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
562      * settings of the primary buffer, but also that only the sound of our
563      * application will be hearable when it will have the focus.
564      * !!! (this is not really working as intended yet because to set the
565      * cooperative level you need the window handle of your application, and
566      * I don't know of any easy way to get it. Especially since we might play
567      * sound without any video, and so what window handle should we use ???
568      * The hack for now is to use the Desktop window handle - it seems to be
569      * working */
570     if( IDirectSound_SetCooperativeLevel( sys->p_dsobject, GetDesktopWindow(),
571                                           DSSCL_EXCLUSIVE) )
572         msg_Warn( obj, "cannot set direct sound cooperative level" );
573 #endif
574
575     const char *const *ppsz_compare = speaker_list;
576     char *psz_speaker;
577     int i = 0;
578     HRESULT hr;
579
580     /* Retrieve config values */
581     var_Create( obj, "directx-audio-float32",
582                 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
583     psz_speaker = var_CreateGetString( obj, "directx-audio-speaker" );
584
585     while ( *ppsz_compare != NULL )
586     {
587         if ( !strncmp( *ppsz_compare, psz_speaker, strlen(*ppsz_compare) ) )
588         {
589             break;
590         }
591         ppsz_compare++; i++;
592     }
593
594     if ( *ppsz_compare == NULL )
595     {
596         msg_Err( obj, "(%s) isn't valid speaker setup option", psz_speaker );
597         msg_Err( obj, "Defaulting to Windows default speaker config");
598         i = 0;
599     }
600     free( psz_speaker );
601
602     if( AOUT_FMT_SPDIF( fmt ) && var_InheritBool( obj, "spdif" ) )
603     {
604         hr = CreateDSBuffer( obj, sys, VLC_CODEC_SPDIFL,
605                              fmt->i_physical_channels,
606                              aout_FormatNbChannels(fmt), fmt->i_rate, false );
607         if( hr == DS_OK )
608         {
609             msg_Dbg( obj, "using A/52 pass-through over S/PDIF" );
610             fmt->i_format = VLC_CODEC_SPDIFL;
611
612             /* Calculate the frame size in bytes */
613             fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
614             fmt->i_frame_length = A52_FRAME_NB;
615         }
616     }
617     else
618         hr = DSERR_UNSUPPORTED;
619
620     if( hr != DS_OK )
621     {
622         if( i == 0 )
623         {
624             DWORD ui_speaker_config;
625             int i_channels = 2; /* Default to stereo */
626             int i_orig_channels = aout_FormatNbChannels( fmt );
627
628             /* Check the speaker configuration to determine which channel
629              * config should be the default */
630             hr = IDirectSound_GetSpeakerConfig( sys->p_dsobject,
631                                                 &ui_speaker_config );
632             if( FAILED(hr) )
633             {
634                 ui_speaker_config = DSSPEAKER_STEREO;
635                 msg_Dbg( obj, "GetSpeakerConfig failed" );
636             }
637
638             const char *name = "Unknown";
639             switch( DSSPEAKER_CONFIG(ui_speaker_config) )
640             {
641                 case DSSPEAKER_7POINT1:
642                 case DSSPEAKER_7POINT1_SURROUND:
643                     name = "7.1";
644                     i_channels = 8;
645                     break;
646                 case DSSPEAKER_5POINT1:
647                 case DSSPEAKER_5POINT1_SURROUND:
648                     name = "5.1";
649                     i_channels = 6;
650                     break;
651                 case DSSPEAKER_QUAD:
652                     name = "Quad";
653                     i_channels = 4;
654                     break;
655 #if 0 /* Lots of people just get their settings wrong and complain that
656        * this is a problem with VLC so just don't ever set mono by default. */
657                 case DSSPEAKER_MONO:
658                     name = "Mono";
659                     i_channels = 1;
660                     break;
661 #endif
662                 case DSSPEAKER_SURROUND:
663                     name = "Surround";
664                     i_channels = 4;
665                     break;
666                 case DSSPEAKER_STEREO:
667                     name = "Stereo";
668                     i_channels = 2;
669                     break;
670             }
671
672             if( i_channels >= i_orig_channels )
673                 i_channels = i_orig_channels;
674
675             msg_Dbg( obj, "%s speaker config: %s and stream has "
676                      "%d channels, using %d channels", "Windows", name,
677                      i_orig_channels, i_channels );
678
679             switch( i_channels )
680             {
681                 case 8:
682                     fmt->i_physical_channels = AOUT_CHANS_7_1;
683                     break;
684                 case 7:
685                 case 6:
686                     fmt->i_physical_channels = AOUT_CHANS_5_1;
687                     break;
688                 case 5:
689                 case 4:
690                     fmt->i_physical_channels = AOUT_CHANS_4_0;
691                     break;
692                 default:
693                     fmt->i_physical_channels = AOUT_CHANS_2_0;
694                     break;
695             }
696         }
697         else
698         {   /* Overriden speaker configuration */
699             const char *name = "Non-existant";
700             switch( i )
701             {
702                 case 1: /* Mono */
703                     name = "Mono";
704                     fmt->i_physical_channels = AOUT_CHAN_CENTER;
705                     break;
706                 case 2: /* Stereo */
707                     name = "Stereo";
708                     fmt->i_physical_channels = AOUT_CHANS_2_0;
709                     break;
710                 case 3: /* Quad */
711                     name = "Quad";
712                     fmt->i_physical_channels = AOUT_CHANS_4_0;
713                     break;
714                 case 4: /* 5.1 */
715                     name = "5.1";
716                     fmt->i_physical_channels = AOUT_CHANS_5_1;
717                     break;
718                 case 5: /* 7.1 */
719                     name = "7.1";
720                     fmt->i_physical_channels = AOUT_CHANS_7_1;
721                     break;
722             }
723             msg_Dbg( obj, "%s speaker config: %s", "VLC", name );
724         }
725
726         /* Open the device */
727         aout_FormatPrepare( fmt );
728
729         hr = CreateDSBufferPCM( obj, sys, &fmt->i_format,
730                                 fmt->i_physical_channels, fmt->i_rate, false );
731         if( hr != DS_OK )
732         {
733             msg_Err( obj, "cannot open directx audio device" );
734             goto error;
735         }
736     }
737
738     sys->i_write = 0;
739     return DS_OK;
740
741 error:
742     Stop( sys );
743     return hr;
744 }
745
746 static HRESULT StreamStart( aout_stream_t *s,
747                             audio_sample_format_t *restrict fmt,
748                             const GUID *sid )
749 {
750     aout_stream_sys_t *sys = malloc( sizeof( *sys ) );
751     if( unlikely(sys == NULL) )
752         return E_OUTOFMEMORY;
753
754     DIRECTX_AUDIO_ACTIVATION_PARAMS params = {
755         .cbDirectXAudioActivationParams = sizeof( params ),
756         .guidAudioSession = *sid,
757         .dwAudioStreamFlags = 0,
758     };
759     PROPVARIANT prop;
760
761     PropVariantInit( &prop );
762     prop.vt = VT_BLOB;
763     prop.blob.cbSize = sizeof( params );
764     prop.blob.pBlobData = (BYTE *)&params;
765
766     void *pv;
767     HRESULT hr = aout_stream_Activate( s, &IID_IDirectSound, &prop, &pv );
768     if( FAILED(hr) )
769         goto error;
770
771     sys->p_dsobject = pv;
772
773     hr = Start( VLC_OBJECT(s), sys, fmt );
774     if( FAILED(hr) )
775     {
776         IDirectSound_Release( sys->p_dsobject );
777         goto error;
778     }
779
780     s->sys = sys;
781     s->time_get = StreamTimeGet;
782     s->play = StreamPlay;
783     s->pause = StreamPause;
784     s->flush = StreamFlush;
785     return S_OK;
786 error:
787     free( sys );
788     return hr;
789 }
790
791 /**
792  * Handles all the gory details of DirectSound initialization.
793  */
794 static int InitDirectSound( audio_output_t *p_aout )
795 {
796     aout_sys_t *sys = p_aout->sys;
797     GUID guid, *p_guid = NULL;
798     HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
799
800     OurDirectSoundCreate = (void *)
801         GetProcAddress( p_aout->sys->hdsound_dll,
802                         "DirectSoundCreate" );
803     if( OurDirectSoundCreate == NULL )
804     {
805         msg_Warn( p_aout, "GetProcAddress FAILED" );
806         goto error;
807     }
808
809     char *dev = var_GetNonEmptyString( p_aout, "directx-audio-device" );
810     if( dev != NULL )
811     {
812         LPOLESTR lpsz = ToWide( dev );
813         free( dev );
814
815         if( SUCCEEDED( IIDFromString( lpsz, &guid ) ) )
816             p_guid = &guid;
817         else
818             msg_Err( p_aout, "bad device GUID: %ls", lpsz );
819         free( lpsz );
820     }
821
822     /* Create the direct sound object */
823     if FAILED( OurDirectSoundCreate( p_guid, &sys->s.p_dsobject, NULL ) )
824     {
825         msg_Warn( p_aout, "cannot create a direct sound device" );
826         goto error;
827     }
828
829     return VLC_SUCCESS;
830
831 error:
832     sys->s.p_dsobject = NULL;
833     return VLC_EGENERIC;
834
835 }
836
837 static int VolumeSet( audio_output_t *p_aout, float volume )
838 {
839     aout_sys_t *sys = p_aout->sys;
840     int ret = 0;
841
842     /* Directsound doesn't support amplification, so we use software
843        gain if we need it and only for this */
844     float gain = volume > 1.f ? volume * volume * volume : 1.f;
845     aout_GainRequest( p_aout, gain );
846
847     /* millibels from linear amplification */
848     LONG mb = lroundf( 6000.f * log10f( __MIN( volume, 1.f ) ));
849
850     /* Clamp to allowed DirectSound range */
851     static_assert( DSBVOLUME_MIN < DSBVOLUME_MAX, "DSBVOLUME_* confused" );
852     if( mb > DSBVOLUME_MAX )
853     {
854         mb = DSBVOLUME_MAX;
855         ret = -1;
856     }
857     if( mb <= DSBVOLUME_MIN )
858         mb = DSBVOLUME_MIN;
859
860     sys->volume.mb = mb;
861     sys->volume.volume = volume;
862     if( !sys->volume.mute && sys->s.p_dsbuffer != NULL &&
863         IDirectSoundBuffer_SetVolume( sys->s.p_dsbuffer, mb ) != DS_OK )
864         return -1;
865     /* Convert back to UI volume */
866     aout_VolumeReport( p_aout, volume );
867
868     if( var_InheritBool( p_aout, "volume-save" ) )
869         config_PutFloat( p_aout, "directx-volume", volume );
870     return ret;
871 }
872
873 static int MuteSet( audio_output_t *p_aout, bool mute )
874 {
875     HRESULT res = DS_OK;
876     aout_sys_t *sys = p_aout->sys;
877
878     sys->volume.mute = mute;
879
880     if( sys->s.p_dsbuffer != NULL )
881         res = IDirectSoundBuffer_SetVolume( sys->s.p_dsbuffer,
882                                             mute? DSBVOLUME_MIN : sys->volume.mb );
883
884     aout_MuteReport( p_aout, mute );
885     return (res != DS_OK);
886 }
887
888 static int OutputStart( audio_output_t *p_aout,
889                         audio_sample_format_t *restrict fmt )
890 {
891     msg_Dbg( p_aout, "Opening DirectSound Audio Output" );
892
893     /* Initialise DirectSound */
894     if( InitDirectSound( p_aout ) )
895     {
896         msg_Err( p_aout, "cannot initialize DirectSound" );
897         return -1;
898     }
899
900     HRESULT hr = Start( VLC_OBJECT(p_aout), &p_aout->sys->s, fmt );
901     if( FAILED(hr) )
902         return -1;
903
904     /* Force volume update */
905     VolumeSet( p_aout, p_aout->sys->volume.volume );
906     MuteSet( p_aout, p_aout->sys->volume.mute );
907
908     /* then launch the notification thread */
909     p_aout->time_get = OutputTimeGet;
910     p_aout->play = OutputPlay;
911     p_aout->pause = OutputPause;
912     p_aout->flush = OutputFlush;
913
914     return 0;
915 }
916
917 typedef struct
918 {
919     unsigned count;
920     char **ids;
921     char **names;
922 } ds_list_t;
923
924 static int CALLBACK DeviceEnumCallback( LPGUID guid, LPCWSTR desc,
925                                         LPCWSTR mod, LPVOID data )
926 {
927     ds_list_t *list = data;
928     OLECHAR buf[48];
929
930     if( StringFromGUID2( guid, buf, 48 ) <= 0 )
931         return true;
932
933     list->count++;
934     list->ids = xrealloc( list->ids, list->count * sizeof(char *) );
935     list->names = xrealloc( list->names, list->count * sizeof(char *) );
936     list->ids[list->count - 1] = FromWide( buf );
937     list->names[list->count - 1] = FromWide( desc );
938     if( list->ids == NULL || list->names == NULL )
939         abort();
940
941     (void) mod;
942     return true;
943 }
944
945 /**
946  * Stores the list of devices in preferences
947  */
948 static int ReloadDirectXDevices( vlc_object_t *p_this, char const *psz_name,
949                                  char ***values, char ***descs )
950 {
951     ds_list_t list = {
952         .count = 1,
953         .ids = xmalloc(sizeof (char *)),
954         .names = xmalloc(sizeof (char *)),
955     };
956     list.ids[0] = xstrdup("");
957     list.names[0] = xstrdup(_("Default"));
958
959     (void) psz_name;
960
961     HANDLE hdsound_dll = LoadLibrary(_T("DSOUND.DLL"));
962     if( hdsound_dll == NULL )
963     {
964         msg_Warn( p_this, "cannot open DSOUND.DLL" );
965         goto out;
966     }
967
968     /* Get DirectSoundEnumerate */
969     HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKW, LPVOID) =
970             (void *)GetProcAddress( hdsound_dll, "DirectSoundEnumerateW" );
971     if( OurDirectSoundEnumerate != NULL )
972     {
973         OurDirectSoundEnumerate( DeviceEnumCallback, &list );
974         msg_Dbg( p_this, "found %u devices", list.count );
975     }
976     FreeLibrary(hdsound_dll);
977
978 out:
979     *values = list.ids;
980     *descs = list.names;
981     return list.count;
982 }
983
984 static int DeviceSelect (audio_output_t *aout, const char *id)
985 {
986     var_SetString(aout, "directx-audio-device", (id != NULL) ? id : "");
987     aout_DeviceReport (aout, id);
988     aout_RestartRequest (aout, AOUT_RESTART_OUTPUT);
989     return 0;
990 }
991
992 static int Open(vlc_object_t *obj)
993 {
994     audio_output_t *aout = (audio_output_t *)obj;
995
996     HINSTANCE hdsound_dll = LoadLibrary(_T("DSOUND.DLL"));
997     if (hdsound_dll == NULL)
998     {
999         msg_Warn(aout, "cannot open DSOUND.DLL");
1000         return VLC_EGENERIC;
1001     }
1002
1003     aout_sys_t *sys = calloc(1, sizeof (*sys));
1004     if (unlikely(sys == NULL))
1005         return VLC_ENOMEM;
1006
1007     sys->hdsound_dll = hdsound_dll;
1008
1009     aout->sys = sys;
1010     aout->start = OutputStart;
1011     aout->stop = OutputStop;
1012     aout->volume_set = VolumeSet;
1013     aout->mute_set = MuteSet;
1014     aout->device_select = DeviceSelect;
1015
1016     /* Volume */
1017     sys->volume.volume = var_InheritFloat(aout, "directx-volume");
1018     aout_VolumeReport(aout, sys->volume.volume );
1019     MuteSet(aout, var_InheritBool(aout, "mute"));
1020
1021     /* DirectSound does not support hot-plug events (unless with WASAPI) */
1022     char **ids, **names;
1023     int count = ReloadDirectXDevices(obj, NULL, &ids, &names);
1024     if (count >= 0)
1025     {
1026         for (int i = 0; i < count; i++)
1027         {
1028             aout_HotplugReport(aout, ids[i], names[i]);
1029             free(names[i]);
1030             free(ids[i]);
1031         }
1032         free(names);
1033         free(ids);
1034     }
1035
1036     char *dev = var_CreateGetNonEmptyString(aout, "directx-audio-device");
1037     aout_DeviceReport(aout, dev);
1038     free(dev);
1039
1040     return VLC_SUCCESS;
1041 }
1042
1043 static void Close(vlc_object_t *obj)
1044 {
1045     audio_output_t *aout = (audio_output_t *)obj;
1046     aout_sys_t *sys = aout->sys;
1047
1048     var_Destroy(aout, "directx-audio-device");
1049     FreeLibrary(sys->hdsound_dll); /* free DSOUND.DLL */
1050     free(sys);
1051 }