1 /*****************************************************************************
2 * directsound.c: DirectSound audio output plugin for VLC
3 *****************************************************************************
4 * Copyright (C) 2001-2009 VLC authors and VideoLAN
7 * Authors: Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
38 #include <vlc_charset.h>
40 #include "audio_output/windows_audio_common.h"
41 #include "audio_output/mmdevice.h"
42 #include <mmdeviceapi.h>
44 #define DS_BUF_SIZE (6*1024*1024)
46 static int Open( vlc_object_t
* );
47 static void Close( vlc_object_t
* );
48 static HRESULT
StreamStart( aout_stream_t
*, audio_sample_format_t
*,
50 static HRESULT
StreamStop( aout_stream_t
* );
51 static int ReloadDirectXDevices( const char *, char ***, char *** );
52 static void * PlayedDataEraser( void * );
53 /* Speaker setup override options list */
54 static const char *const speaker_list
[] = { "Windows default", "Mono", "Stereo",
55 "Quad", "5.1", "7.1" };
57 /*****************************************************************************
59 *****************************************************************************/
60 #define DEVICE_TEXT N_("Output device")
61 #define DEVICE_LONGTEXT N_("Select your audio output device")
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." )
67 #define VOLUME_TEXT N_("Audio volume")
68 #define VOLUME_LONGTEXT N_("Audio volume in hundredths of decibels (dB).")
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", "directsound", "dsound" )
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_float_range( 0.f
, 2.f
)
91 set_callbacks( Open
, Close
)
94 set_capability( "aout stream", 30 )
95 set_callbacks( StreamStart
, StreamStop
)
98 typedef struct aout_stream_sys
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
;
106 int i_bytes_per_sample
; /*< Size in bytes of one frame */
107 int i_rate
; /*< Sample rate */
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
;
121 vlc_thread_t eraser_thread
;
125 * DirectSound audio output method descriptor
127 * This structure is part of the audio output thread descriptor.
128 * It describes the direct sound specific properties of an audio device.
141 static HRESULT
Flush( aout_stream_sys_t
*sys
, bool drain
);
142 static HRESULT
TimeGet( aout_stream_sys_t
*sys
, vlc_tick_t
*delay
)
148 hr
= IDirectSoundBuffer_GetStatus( sys
->p_dsbuffer
, &status
);
151 if( !(status
& DSBSTATUS_PLAYING
) )
152 return DSERR_INVALIDCALL
;
154 hr
= IDirectSoundBuffer_GetCurrentPosition( sys
->p_dsbuffer
, &read
, NULL
);
158 size
= (ssize_t
)read
- sys
->i_last_read
;
160 /* GetCurrentPosition cannot be trusted if the return doesn't change
161 * Just return an error */
163 return DSERR_GENERIC
;
168 sys
->i_last_read
= read
;
170 if( sys
->i_data
< 0 )
174 *delay
= ( sys
->i_data
/ sys
->i_bytes_per_sample
) * CLOCK_FREQ
/ sys
->i_rate
;
179 static HRESULT
StreamTimeGet( aout_stream_t
*s
, vlc_tick_t
*delay
)
181 return TimeGet( s
->sys
, delay
);
184 static int OutputTimeGet( audio_output_t
*aout
, vlc_tick_t
*delay
)
186 aout_sys_t
*sys
= aout
->sys
;
187 return (TimeGet( &sys
->s
, delay
) == DS_OK
) ? 0 : -1;
191 * Fills in one of the DirectSound frame buffers.
193 * @return VLC_SUCCESS on success.
195 static HRESULT
FillBuffer( vlc_object_t
*obj
, aout_stream_sys_t
*p_sys
,
198 size_t towrite
= (p_buffer
!= NULL
) ? p_buffer
->i_buffer
: DS_BUF_SIZE
;
199 void *p_write_position
, *p_wrap_around
;
200 unsigned long l_bytes1
, l_bytes2
;
203 vlc_mutex_lock( &p_sys
->lock
);
205 /* Before copying anything, we have to lock the buffer */
206 dsresult
= IDirectSoundBuffer_Lock(
207 p_sys
->p_dsbuffer
, /* DS buffer */
208 p_sys
->i_write
, /* Start offset */
209 towrite
, /* Number of bytes */
210 &p_write_position
, /* Address of lock start */
211 &l_bytes1
, /* Count of bytes locked before wrap around */
212 &p_wrap_around
, /* Buffer address (if wrap around) */
213 &l_bytes2
, /* Count of bytes after wrap around */
214 0 ); /* Flags: DSBLOCK_FROMWRITECURSOR is buggy */
215 if( dsresult
== DSERR_BUFFERLOST
)
217 IDirectSoundBuffer_Restore( p_sys
->p_dsbuffer
);
218 dsresult
= IDirectSoundBuffer_Lock(
228 if( dsresult
!= DS_OK
)
230 msg_Warn( obj
, "cannot lock buffer" );
231 if( p_buffer
!= NULL
)
232 block_Release( p_buffer
);
233 vlc_mutex_unlock( &p_sys
->lock
);
237 if( p_buffer
== NULL
)
239 memset( p_write_position
, 0, l_bytes1
);
240 memset( p_wrap_around
, 0, l_bytes2
);
244 if( p_sys
->chans_to_reorder
) /* Do the channel reordering here */
245 aout_ChannelReorder( p_buffer
->p_buffer
, p_buffer
->i_buffer
,
246 p_sys
->chans_to_reorder
, p_sys
->chan_table
,
249 memcpy( p_write_position
, p_buffer
->p_buffer
, l_bytes1
);
250 if( p_wrap_around
&& l_bytes2
)
251 memcpy( p_wrap_around
, p_buffer
->p_buffer
+ l_bytes1
, l_bytes2
);
253 if( unlikely( ( l_bytes1
+ l_bytes2
) < p_buffer
->i_buffer
) )
254 msg_Err( obj
, "Buffer overrun");
256 block_Release( p_buffer
);
259 /* Now the data has been copied, unlock the buffer */
260 IDirectSoundBuffer_Unlock( p_sys
->p_dsbuffer
, p_write_position
, l_bytes1
,
261 p_wrap_around
, l_bytes2
);
263 p_sys
->i_write
+= towrite
;
264 p_sys
->i_write
%= DS_BUF_SIZE
;
265 p_sys
->i_data
+= towrite
;
266 vlc_mutex_unlock( &p_sys
->lock
);
271 static HRESULT
Play( vlc_object_t
*obj
, aout_stream_sys_t
*sys
,
275 dsresult
= FillBuffer( obj
, sys
, p_buffer
);
276 if( dsresult
!= DS_OK
)
279 /* start playing the buffer */
280 dsresult
= IDirectSoundBuffer_Play( sys
->p_dsbuffer
, 0, 0,
282 if( dsresult
== DSERR_BUFFERLOST
)
284 IDirectSoundBuffer_Restore( sys
->p_dsbuffer
);
285 dsresult
= IDirectSoundBuffer_Play( sys
->p_dsbuffer
,
286 0, 0, DSBPLAY_LOOPING
);
288 if( dsresult
!= DS_OK
)
289 msg_Err( obj
, "cannot start playing buffer: (hr=0x%0lx)", dsresult
);
292 vlc_mutex_lock( &sys
->lock
);
293 sys
->b_playing
= true;
294 vlc_cond_signal(&sys
->cond
);
295 vlc_mutex_unlock( &sys
->lock
);
301 static HRESULT
StreamPlay( aout_stream_t
*s
, block_t
*block
)
303 return Play( VLC_OBJECT(s
), s
->sys
, block
);
306 static void OutputPlay( audio_output_t
*aout
, block_t
*block
, vlc_tick_t date
)
308 aout_sys_t
*sys
= aout
->sys
;
309 Play( VLC_OBJECT(aout
), &sys
->s
, block
);
313 static HRESULT
Pause( aout_stream_sys_t
*sys
, bool pause
)
318 hr
= IDirectSoundBuffer_Stop( sys
->p_dsbuffer
);
320 hr
= IDirectSoundBuffer_Play( sys
->p_dsbuffer
, 0, 0, DSBPLAY_LOOPING
);
323 vlc_mutex_lock( &sys
->lock
);
324 sys
->b_playing
= !pause
;
326 vlc_cond_signal( &sys
->cond
);
327 vlc_mutex_unlock( &sys
->lock
);
332 static HRESULT
StreamPause( aout_stream_t
*s
, bool pause
)
334 return Pause( s
->sys
, pause
);
337 static void OutputPause( audio_output_t
*aout
, bool pause
, vlc_tick_t date
)
339 aout_sys_t
*sys
= aout
->sys
;
340 Pause( &sys
->s
, pause
);
344 static HRESULT
Flush( aout_stream_sys_t
*sys
, bool drain
)
346 HRESULT ret
= IDirectSoundBuffer_Stop( sys
->p_dsbuffer
);
347 if( ret
== DS_OK
&& !drain
)
349 vlc_mutex_lock(&sys
->lock
);
351 sys
->i_last_read
= sys
->i_write
;
352 IDirectSoundBuffer_SetCurrentPosition( sys
->p_dsbuffer
, sys
->i_write
);
353 sys
->b_playing
= false;
354 vlc_mutex_unlock(&sys
->lock
);
359 static HRESULT
StreamFlush( aout_stream_t
*s
)
361 return Flush( s
->sys
, false );
364 static void OutputFlush( audio_output_t
*aout
, bool drain
)
366 aout_sys_t
*sys
= aout
->sys
;
367 Flush( &sys
->s
, drain
);
371 * Creates a DirectSound buffer of the required format.
373 * This function creates the buffer we'll use to play audio.
374 * In DirectSound there are two kinds of buffers:
375 * - the primary buffer: which is the actual buffer that the soundcard plays
376 * - the secondary buffer(s): these buffers are the one actually used by
377 * applications and DirectSound takes care of mixing them into the primary.
379 * Once you create a secondary buffer, you cannot change its format anymore so
380 * you have to release the current one and create another.
382 static HRESULT
CreateDSBuffer( vlc_object_t
*obj
, aout_stream_sys_t
*sys
,
383 int i_format
, int i_channels
, int i_nb_channels
,
384 int i_rate
, bool b_probe
)
386 WAVEFORMATEXTENSIBLE waveformat
;
387 DSBUFFERDESC dsbdesc
;
390 /* First set the sound buffer format */
391 waveformat
.dwChannelMask
= 0;
392 for( unsigned i
= 0; pi_vlc_chan_order_wg4
[i
]; i
++ )
393 if( i_channels
& pi_vlc_chan_order_wg4
[i
] )
394 waveformat
.dwChannelMask
|= pi_channels_in
[i
];
398 case VLC_CODEC_SPDIFL
:
400 /* To prevent channel re-ordering */
401 waveformat
.dwChannelMask
= SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
;
402 waveformat
.Format
.wBitsPerSample
= 16;
403 waveformat
.Samples
.wValidBitsPerSample
=
404 waveformat
.Format
.wBitsPerSample
;
405 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_DOLBY_AC3_SPDIF
;
406 waveformat
.SubFormat
= _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF
;
410 waveformat
.Format
.wBitsPerSample
= sizeof(float) * 8;
411 waveformat
.Samples
.wValidBitsPerSample
=
412 waveformat
.Format
.wBitsPerSample
;
413 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_IEEE_FLOAT
;
414 waveformat
.SubFormat
= _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
418 waveformat
.Format
.wBitsPerSample
= 16;
419 waveformat
.Samples
.wValidBitsPerSample
=
420 waveformat
.Format
.wBitsPerSample
;
421 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
422 waveformat
.SubFormat
= _KSDATAFORMAT_SUBTYPE_PCM
;
426 waveformat
.Format
.nChannels
= i_nb_channels
;
427 waveformat
.Format
.nSamplesPerSec
= i_rate
;
428 waveformat
.Format
.nBlockAlign
=
429 waveformat
.Format
.wBitsPerSample
/ 8 * i_nb_channels
;
430 waveformat
.Format
.nAvgBytesPerSec
=
431 waveformat
.Format
.nSamplesPerSec
* waveformat
.Format
.nBlockAlign
;
433 sys
->i_bytes_per_sample
= waveformat
.Format
.nBlockAlign
;
434 sys
->format
= i_format
;
436 /* Then fill in the direct sound descriptor */
437 memset(&dsbdesc
, 0, sizeof(DSBUFFERDESC
));
438 dsbdesc
.dwSize
= sizeof(DSBUFFERDESC
);
439 dsbdesc
.dwFlags
= DSBCAPS_GETCURRENTPOSITION2
/* Better position accuracy */
440 | DSBCAPS_GLOBALFOCUS
/* Allows background playing */
441 | DSBCAPS_CTRLVOLUME
/* Allows volume control */
442 | DSBCAPS_CTRLPOSITIONNOTIFY
; /* Allow position notifications */
444 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
445 if( i_nb_channels
<= 2 )
447 waveformat
.Format
.cbSize
= 0;
451 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
452 waveformat
.Format
.cbSize
=
453 sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
455 /* Needed for 5.1 on emu101k */
456 dsbdesc
.dwFlags
|= DSBCAPS_LOCHARDWARE
;
459 dsbdesc
.dwBufferBytes
= DS_BUF_SIZE
; /* buffer size */
460 dsbdesc
.lpwfxFormat
= (WAVEFORMATEX
*)&waveformat
;
462 /* CreateSoundBuffer doesn't allow volume control for non-PCM buffers */
463 if ( i_format
== VLC_CODEC_SPDIFL
)
464 dsbdesc
.dwFlags
&= ~DSBCAPS_CTRLVOLUME
;
466 hr
= IDirectSound_CreateSoundBuffer( sys
->p_dsobject
, &dsbdesc
,
467 &sys
->p_dsbuffer
, NULL
);
470 if( !(dsbdesc
.dwFlags
& DSBCAPS_LOCHARDWARE
) )
473 /* Try without DSBCAPS_LOCHARDWARE */
474 dsbdesc
.dwFlags
&= ~DSBCAPS_LOCHARDWARE
;
475 hr
= IDirectSound_CreateSoundBuffer( sys
->p_dsobject
, &dsbdesc
,
476 &sys
->p_dsbuffer
, NULL
);
480 msg_Dbg( obj
, "couldn't use hardware sound buffer" );
483 /* Stop here if we were just probing */
486 IDirectSoundBuffer_Release( sys
->p_dsbuffer
);
487 sys
->p_dsbuffer
= NULL
;
491 sys
->i_rate
= i_rate
;
492 sys
->i_channel_mask
= waveformat
.dwChannelMask
;
493 sys
->chans_to_reorder
=
494 aout_CheckChannelReorder( pi_channels_in
, pi_channels_out
,
495 waveformat
.dwChannelMask
, sys
->chan_table
);
496 if( sys
->chans_to_reorder
)
497 msg_Dbg( obj
, "channel reordering needed" );
499 hr
= IDirectSoundBuffer_QueryInterface( sys
->p_dsbuffer
,
500 &IID_IDirectSoundNotify
,
501 (void **) &sys
->p_notify
);
504 msg_Err( obj
, "Couldn't query IDirectSoundNotify" );
505 sys
->p_notify
= NULL
;
508 FillBuffer( obj
, sys
, NULL
);
513 * Creates a PCM DirectSound buffer.
515 * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
516 * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
518 static HRESULT
CreateDSBufferPCM( vlc_object_t
*obj
, aout_stream_sys_t
*sys
,
519 vlc_fourcc_t
*i_format
, int i_channels
,
520 int i_rate
, bool b_probe
)
523 unsigned i_nb_channels
= vlc_popcount( i_channels
);
525 if( var_GetBool( obj
, "directx-audio-float32" ) )
527 hr
= CreateDSBuffer( obj
, sys
, VLC_CODEC_FL32
, i_channels
,
528 i_nb_channels
, i_rate
, b_probe
);
531 *i_format
= VLC_CODEC_FL32
;
536 hr
= CreateDSBuffer( obj
, sys
, VLC_CODEC_S16N
, i_channels
, i_nb_channels
,
540 *i_format
= VLC_CODEC_S16N
;
548 * Closes the audio device.
550 static HRESULT
Stop( aout_stream_sys_t
*p_sys
)
552 vlc_mutex_lock( &p_sys
->lock
);
553 p_sys
->b_playing
= true;
554 vlc_cond_signal( &p_sys
->cond
);
555 vlc_mutex_unlock( &p_sys
->lock
);
556 vlc_cancel( p_sys
->eraser_thread
);
557 vlc_join( p_sys
->eraser_thread
, NULL
);
558 vlc_cond_destroy( &p_sys
->cond
);
559 vlc_mutex_destroy( &p_sys
->lock
);
561 if( p_sys
->p_notify
!= NULL
)
563 IDirectSoundNotify_Release(p_sys
->p_notify
);
564 p_sys
->p_notify
= NULL
;
566 if( p_sys
->p_dsbuffer
!= NULL
)
568 IDirectSoundBuffer_Stop( p_sys
->p_dsbuffer
);
569 IDirectSoundBuffer_Release( p_sys
->p_dsbuffer
);
570 p_sys
->p_dsbuffer
= NULL
;
572 if( p_sys
->p_dsobject
!= NULL
)
574 IDirectSound_Release( p_sys
->p_dsobject
);
575 p_sys
->p_dsobject
= NULL
;
580 static HRESULT
StreamStop( aout_stream_t
*s
)
589 static void OutputStop( audio_output_t
*aout
)
591 msg_Dbg( aout
, "closing audio device" );
592 aout_sys_t
*sys
= aout
->sys
;
596 static HRESULT
Start( vlc_object_t
*obj
, aout_stream_sys_t
*sys
,
597 audio_sample_format_t
*restrict pfmt
)
599 if( aout_FormatNbChannels( pfmt
) == 0 )
602 #if !VLC_WINSTORE_APP
603 /* Set DirectSound Cooperative level, ie what control we want over Windows
604 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
605 * settings of the primary buffer, but also that only the sound of our
606 * application will be hearable when it will have the focus.
607 * !!! (this is not really working as intended yet because to set the
608 * cooperative level you need the window handle of your application, and
609 * I don't know of any easy way to get it. Especially since we might play
610 * sound without any video, and so what window handle should we use ???
611 * The hack for now is to use the Desktop window handle - it seems to be
613 if( IDirectSound_SetCooperativeLevel( sys
->p_dsobject
, GetDesktopWindow(),
615 msg_Warn( obj
, "cannot set direct sound cooperative level" );
618 if( AOUT_FMT_HDMI( pfmt
) )
621 audio_sample_format_t fmt
= *pfmt
;
622 const char *const *ppsz_compare
= speaker_list
;
625 HRESULT hr
= DSERR_UNSUPPORTED
;
627 /* Retrieve config values */
628 var_Create( obj
, "directx-audio-float32",
629 VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
630 psz_speaker
= var_CreateGetString( obj
, "directx-audio-speaker" );
632 while ( *ppsz_compare
!= NULL
)
634 if ( !strncmp( *ppsz_compare
, psz_speaker
, strlen(*ppsz_compare
) ) )
641 if ( *ppsz_compare
== NULL
)
643 msg_Err( obj
, "(%s) isn't valid speaker setup option", psz_speaker
);
644 msg_Err( obj
, "Defaulting to Windows default speaker config");
649 vlc_mutex_init(&sys
->lock
);
650 vlc_cond_init(&sys
->cond
);
652 if( AOUT_FMT_SPDIF( &fmt
) )
654 if( var_InheritBool( obj
, "spdif" ) )
655 hr
= CreateDSBuffer( obj
, sys
, VLC_CODEC_SPDIFL
,
656 fmt
.i_physical_channels
,
657 aout_FormatNbChannels(&fmt
), fmt
.i_rate
, false );
661 msg_Dbg( obj
, "using A/52 pass-through over S/PDIF" );
662 fmt
.i_format
= VLC_CODEC_SPDIFL
;
664 /* Calculate the frame size in bytes */
665 fmt
.i_bytes_per_frame
= AOUT_SPDIF_SIZE
;
666 fmt
.i_frame_length
= A52_FRAME_NB
;
670 vlc_mutex_destroy(&sys
->lock
);
671 vlc_cond_destroy(&sys
->cond
);
680 DWORD ui_speaker_config
;
681 int i_channels
= 2; /* Default to stereo */
682 int i_orig_channels
= aout_FormatNbChannels( &fmt
);
684 /* Check the speaker configuration to determine which channel
685 * config should be the default */
686 hr
= IDirectSound_GetSpeakerConfig( sys
->p_dsobject
,
687 &ui_speaker_config
);
690 ui_speaker_config
= DSSPEAKER_STEREO
;
691 msg_Dbg( obj
, "GetSpeakerConfig failed" );
694 const char *name
= "Unknown";
695 switch( DSSPEAKER_CONFIG(ui_speaker_config
) )
697 case DSSPEAKER_7POINT1
:
698 case DSSPEAKER_7POINT1_SURROUND
:
702 case DSSPEAKER_5POINT1
:
703 case DSSPEAKER_5POINT1_SURROUND
:
711 #if 0 /* Lots of people just get their settings wrong and complain that
712 * this is a problem with VLC so just don't ever set mono by default. */
718 case DSSPEAKER_SURROUND
:
722 case DSSPEAKER_STEREO
:
728 if( i_channels
>= i_orig_channels
)
729 i_channels
= i_orig_channels
;
731 msg_Dbg( obj
, "%s speaker config: %s and stream has "
732 "%d channels, using %d channels", "Windows", name
,
733 i_orig_channels
, i_channels
);
738 fmt
.i_physical_channels
= AOUT_CHANS_7_1
;
742 fmt
.i_physical_channels
= AOUT_CHANS_5_1
;
746 fmt
.i_physical_channels
= AOUT_CHANS_4_0
;
749 fmt
.i_physical_channels
= AOUT_CHANS_2_0
;
754 { /* Overriden speaker configuration */
755 const char *name
= "Non-existant";
760 fmt
.i_physical_channels
= AOUT_CHAN_CENTER
;
764 fmt
.i_physical_channels
= AOUT_CHANS_2_0
;
768 fmt
.i_physical_channels
= AOUT_CHANS_4_0
;
772 fmt
.i_physical_channels
= AOUT_CHANS_5_1
;
776 fmt
.i_physical_channels
= AOUT_CHANS_7_1
;
779 msg_Dbg( obj
, "%s speaker config: %s", "VLC", name
);
782 /* Open the device */
783 aout_FormatPrepare( &fmt
);
785 hr
= CreateDSBufferPCM( obj
, sys
, &fmt
.i_format
,
786 fmt
.i_physical_channels
, fmt
.i_rate
, false );
789 msg_Err( obj
, "cannot open directx audio device" );
794 int ret
= vlc_clone(&sys
->eraser_thread
, PlayedDataEraser
, (void*) obj
,
795 VLC_THREAD_PRIORITY_LOW
);
796 if( unlikely( ret
) )
799 msg_Err( obj
, "Couldn't start eraser thread" );
801 vlc_cond_destroy(&sys
->cond
);
802 vlc_mutex_destroy(&sys
->lock
);
804 if( sys
->p_notify
!= NULL
)
806 IDirectSoundNotify_Release( sys
->p_notify
);
807 sys
->p_notify
= NULL
;
809 IDirectSoundBuffer_Release( sys
->p_dsbuffer
);
810 sys
->p_dsbuffer
= NULL
;
811 IDirectSound_Release( sys
->p_dsobject
);
812 sys
->p_dsobject
= NULL
;
816 fmt
.channel_type
= AUDIO_CHANNEL_TYPE_BITMAP
;
819 sys
->b_playing
= false;
821 sys
->i_last_read
= 0;
831 static HRESULT
StreamStart( aout_stream_t
*s
,
832 audio_sample_format_t
*restrict fmt
,
835 aout_stream_sys_t
*sys
= calloc( 1, sizeof( *sys
) );
836 if( unlikely(sys
== NULL
) )
837 return E_OUTOFMEMORY
;
843 DIRECTX_AUDIO_ACTIVATION_PARAMS params
= {
844 .cbDirectXAudioActivationParams
= sizeof( params
),
845 .guidAudioSession
= *sid
,
846 .dwAudioStreamFlags
= 0,
850 PropVariantInit( &prop
);
852 prop
.blob
.cbSize
= sizeof( params
);
853 prop
.blob
.pBlobData
= (BYTE
*)¶ms
;
855 hr
= aout_stream_Activate( s
, &IID_IDirectSound
, &prop
, &pv
);
858 hr
= aout_stream_Activate( s
, &IID_IDirectSound
, NULL
, &pv
);
862 sys
->p_dsobject
= pv
;
864 hr
= Start( VLC_OBJECT(s
), sys
, fmt
);
869 s
->time_get
= StreamTimeGet
;
870 s
->play
= StreamPlay
;
871 s
->pause
= StreamPause
;
872 s
->flush
= StreamFlush
;
880 * Handles all the gory details of DirectSound initialization.
882 static int InitDirectSound( audio_output_t
*p_aout
)
884 aout_sys_t
*sys
= p_aout
->sys
;
885 GUID guid
, *p_guid
= NULL
;
887 char *dev
= var_GetNonEmptyString( p_aout
, "directx-audio-device" );
890 LPOLESTR lpsz
= ToWide( dev
);
893 if( SUCCEEDED( IIDFromString( lpsz
, &guid
) ) )
896 msg_Err( p_aout
, "bad device GUID: %ls", lpsz
);
900 /* Create the direct sound object */
901 if FAILED( DirectSoundCreate( p_guid
, &sys
->s
.p_dsobject
, NULL
) )
903 msg_Warn( p_aout
, "cannot create a direct sound device" );
910 sys
->s
.p_dsobject
= NULL
;
915 static int VolumeSet( audio_output_t
*p_aout
, float volume
)
917 aout_sys_t
*sys
= p_aout
->sys
;
920 /* Directsound doesn't support amplification, so we use software
921 gain if we need it and only for this */
922 float gain
= volume
> 1.f
? volume
* volume
* volume
: 1.f
;
923 aout_GainRequest( p_aout
, gain
);
925 /* millibels from linear amplification */
926 LONG mb
= lroundf( 6000.f
* log10f( __MIN( volume
, 1.f
) ));
928 /* Clamp to allowed DirectSound range */
929 static_assert( DSBVOLUME_MIN
< DSBVOLUME_MAX
, "DSBVOLUME_* confused" );
930 if( mb
> DSBVOLUME_MAX
)
935 if( mb
<= DSBVOLUME_MIN
)
939 sys
->volume
.volume
= volume
;
940 if( !sys
->volume
.mute
&& sys
->s
.p_dsbuffer
!= NULL
&&
941 IDirectSoundBuffer_SetVolume( sys
->s
.p_dsbuffer
, mb
) != DS_OK
)
943 /* Convert back to UI volume */
944 aout_VolumeReport( p_aout
, volume
);
946 if( var_InheritBool( p_aout
, "volume-save" ) )
947 config_PutFloat( "directx-volume", volume
);
951 static int MuteSet( audio_output_t
*p_aout
, bool mute
)
954 aout_sys_t
*sys
= p_aout
->sys
;
956 sys
->volume
.mute
= mute
;
958 if( sys
->s
.p_dsbuffer
!= NULL
)
959 res
= IDirectSoundBuffer_SetVolume( sys
->s
.p_dsbuffer
,
960 mute
? DSBVOLUME_MIN
: sys
->volume
.mb
);
962 aout_MuteReport( p_aout
, mute
);
963 return (res
!= DS_OK
);
966 static int OutputStart( audio_output_t
*p_aout
,
967 audio_sample_format_t
*restrict fmt
)
969 msg_Dbg( p_aout
, "Opening DirectSound Audio Output" );
971 /* Initialise DirectSound */
972 if( InitDirectSound( p_aout
) )
974 msg_Err( p_aout
, "cannot initialize DirectSound" );
978 aout_sys_t
*sys
= p_aout
->sys
;
979 HRESULT hr
= Start( VLC_OBJECT(p_aout
), &sys
->s
, fmt
);
983 /* Force volume update */
984 VolumeSet( p_aout
, sys
->volume
.volume
);
985 MuteSet( p_aout
, sys
->volume
.mute
);
987 /* then launch the notification thread */
988 p_aout
->time_get
= OutputTimeGet
;
989 p_aout
->play
= OutputPlay
;
990 p_aout
->pause
= OutputPause
;
991 p_aout
->flush
= OutputFlush
;
1003 static int CALLBACK
DeviceEnumCallback( LPGUID guid
, LPCWSTR desc
,
1004 LPCWSTR mod
, LPVOID data
)
1006 ds_list_t
*list
= data
;
1009 if( StringFromGUID2( guid
, buf
, 48 ) <= 0 )
1013 list
->ids
= realloc_or_free( list
->ids
, list
->count
* sizeof(char *) );
1014 if( list
->ids
== NULL
)
1016 list
->names
= realloc_or_free( list
->names
, list
->count
* sizeof(char *) );
1017 if( list
->names
== NULL
)
1022 list
->ids
[list
->count
- 1] = FromWide( buf
);
1023 list
->names
[list
->count
- 1] = FromWide( desc
);
1030 * Stores the list of devices in preferences
1032 static int ReloadDirectXDevices( char const *psz_name
,
1033 char ***values
, char ***descs
)
1037 .ids
= xmalloc(sizeof (char *)),
1038 .names
= xmalloc(sizeof (char *)),
1040 list
.ids
[0] = xstrdup("");
1041 list
.names
[0] = xstrdup(_("Default"));
1045 DirectSoundEnumerate( DeviceEnumCallback
, &list
);
1048 *descs
= list
.names
;
1052 static int DeviceSelect (audio_output_t
*aout
, const char *id
)
1054 var_SetString(aout
, "directx-audio-device", (id
!= NULL
) ? id
: "");
1055 aout_DeviceReport (aout
, id
);
1056 aout_RestartRequest (aout
, AOUT_RESTART_OUTPUT
);
1060 static int Open(vlc_object_t
*obj
)
1062 audio_output_t
*aout
= (audio_output_t
*)obj
;
1063 aout_sys_t
*sys
= calloc(1, sizeof (*sys
));
1064 if (unlikely(sys
== NULL
))
1068 aout
->start
= OutputStart
;
1069 aout
->stop
= OutputStop
;
1070 aout
->volume_set
= VolumeSet
;
1071 aout
->mute_set
= MuteSet
;
1072 aout
->device_select
= DeviceSelect
;
1075 sys
->volume
.volume
= var_InheritFloat(aout
, "directx-volume");
1076 aout_VolumeReport(aout
, sys
->volume
.volume
);
1077 MuteSet(aout
, var_InheritBool(aout
, "mute"));
1079 /* DirectSound does not support hot-plug events (unless with WASAPI) */
1080 char **ids
, **names
;
1081 int count
= ReloadDirectXDevices(NULL
, &ids
, &names
);
1082 msg_Dbg(obj
, "found %d devices", count
);
1085 for (int i
= 0; i
< count
; i
++)
1087 aout_HotplugReport(aout
, ids
[i
], names
[i
]);
1095 char *dev
= var_CreateGetNonEmptyString(aout
, "directx-audio-device");
1096 aout_DeviceReport(aout
, dev
);
1102 static void Close(vlc_object_t
*obj
)
1104 audio_output_t
*aout
= (audio_output_t
*)obj
;
1105 aout_sys_t
*sys
= aout
->sys
;
1107 var_Destroy(aout
, "directx-audio-device");
1111 static void * PlayedDataEraser( void * data
)
1113 const audio_output_t
*aout
= (audio_output_t
*) data
;
1114 aout_sys_t
*aout_sys
= aout
->sys
;
1115 aout_stream_sys_t
*p_sys
= &aout_sys
->s
;
1116 void *p_write_position
, *p_wrap_around
;
1117 unsigned long l_bytes1
, l_bytes2
;
1119 int64_t toerase
, tosleep
;
1124 int canc
= vlc_savecancel();
1125 vlc_mutex_lock( &p_sys
->lock
);
1127 while( !p_sys
->b_playing
)
1128 vlc_cond_wait( &p_sys
->cond
, &p_sys
->lock
);
1133 dsresult
= IDirectSoundBuffer_GetCurrentPosition( p_sys
->p_dsbuffer
,
1135 if( dsresult
== DS_OK
)
1137 int64_t max
= (int64_t) i_read
- (int64_t) p_sys
->i_write
;
1142 tosleep
+= DS_BUF_SIZE
;
1144 tosleep
= ( tosleep
/ p_sys
->i_bytes_per_sample
) * CLOCK_FREQ
/ p_sys
->i_rate
;
1147 tosleep
= __MAX( tosleep
, VLC_TICK_FROM_MS(20) );
1148 dsresult
= IDirectSoundBuffer_Lock( p_sys
->p_dsbuffer
,
1156 if( dsresult
== DSERR_BUFFERLOST
)
1158 IDirectSoundBuffer_Restore( p_sys
->p_dsbuffer
);
1159 dsresult
= IDirectSoundBuffer_Lock( p_sys
->p_dsbuffer
,
1168 if( dsresult
!= DS_OK
)
1171 memset( p_write_position
, 0, l_bytes1
);
1172 memset( p_wrap_around
, 0, l_bytes2
);
1174 IDirectSoundBuffer_Unlock( p_sys
->p_dsbuffer
, p_write_position
, l_bytes1
,
1175 p_wrap_around
, l_bytes2
);
1177 vlc_mutex_unlock(&p_sys
->lock
);
1178 vlc_restorecancel(canc
);
1179 vlc_tick_sleep(tosleep
);