1 /*****************************************************************************
2 * directx.c: Windows DirectX audio output method
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 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_charset.h>
38 #include <vlc_atomic.h>
40 #include "windows_audio_common.h"
42 /*****************************************************************************
43 * notification_thread_t: DirectX event thread
44 *****************************************************************************/
45 typedef struct notification_thread_t
47 int i_frame_size; /* size in bytes of one frame */
48 int i_write_slot; /* current write position in our circular buffer */
56 } notification_thread_t;
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 *****************************************************************************/
67 HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
69 LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
70 LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
71 * takes care of mixing all the
72 * secondary buffers into the primary) */
80 notification_thread_t notif; /* DirectSoundThread id */
82 int i_frame_size; /* Size in bytes of one frame */
84 int i_speaker_setup; /* Speaker setup override */
86 uint8_t chans_to_reorder; /* do we need channel reordering */
87 uint8_t chan_table[AOUT_CHAN_MAX];
88 uint32_t i_channel_mask;
92 /*****************************************************************************
94 *****************************************************************************/
95 static int Open( vlc_object_t * );
96 static void Close( vlc_object_t * );
97 static void Stop( audio_output_t * );
98 static void Play ( audio_output_t *, block_t * );
99 static int VolumeSet ( audio_output_t *, float );
100 static int MuteSet ( audio_output_t *, bool );
102 /* local functions */
103 static void Probe( audio_output_t *, const audio_sample_format_t * );
104 static int InitDirectSound ( audio_output_t * );
105 static int CreateDSBuffer ( audio_output_t *, int, int, int, int, int, bool );
106 static int CreateDSBufferPCM ( audio_output_t *, vlc_fourcc_t*, int, int, bool );
107 static void DestroyDSBuffer ( audio_output_t * );
108 static void* DirectSoundThread( void * );
109 static int FillBuffer ( audio_output_t *, int, block_t * );
111 static int ReloadDirectXDevices( vlc_object_t *, const char *,
112 char ***, char *** );
114 /* Speaker setup override options list */
115 static const char *const speaker_list[] = { "Windows default", "Mono", "Stereo",
116 "Quad", "5.1", "7.1" };
118 /*****************************************************************************
120 *****************************************************************************/
121 #define DEVICE_TEXT N_("Output device")
122 #define DEVICE_LONGTEXT N_("Select your audio output device")
124 #define SPEAKER_TEXT N_("Speaker configuration")
125 #define SPEAKER_LONGTEXT N_("Select speaker configuration you want to use. " \
126 "This option doesn't upmix! So NO e.g. Stereo -> 5.1 conversion." )
128 #define VOLUME_TEXT N_("Audio volume")
129 #define VOLUME_LONGTEXT N_("Audio volume in hundredths of decibels (dB).")
132 set_description( N_("DirectX audio output") )
133 set_shortname( "DirectX" )
134 set_capability( "audio output", 100 )
135 set_category( CAT_AUDIO )
136 set_subcategory( SUBCAT_AUDIO_AOUT )
137 add_shortcut( "directx", "aout_directx" )
139 add_string( "directx-audio-device", NULL,
140 DEVICE_TEXT, DEVICE_LONGTEXT, false )
141 change_string_cb( ReloadDirectXDevices )
142 add_obsolete_string( "directx-audio-device-name")
143 add_bool( "directx-audio-float32", false, FLOAT_TEXT,
144 FLOAT_LONGTEXT, true )
145 add_string( "directx-audio-speaker", "Windows default",
146 SPEAKER_TEXT, SPEAKER_LONGTEXT, true )
147 change_string_list( speaker_list, speaker_list )
148 add_integer( "directx-volume", DSBVOLUME_MAX,
149 VOLUME_TEXT, VOLUME_LONGTEXT, true )
150 change_integer_range( DSBVOLUME_MIN, DSBVOLUME_MAX )
152 set_callbacks( Open, Close )
155 /*****************************************************************************
156 * OpenAudio: open the audio device
157 *****************************************************************************
158 * This function opens and setups Direct Sound.
159 *****************************************************************************/
160 static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
166 const char * const * ppsz_compare = speaker_list;
168 msg_Dbg( p_aout, "Opening DirectSound Audio Output" );
170 /* Retrieve config values */
171 var_Create( p_aout, "directx-audio-float32",
172 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
173 psz_speaker = var_CreateGetString( p_aout, "directx-audio-speaker" );
175 while ( *ppsz_compare != NULL )
177 if ( !strncmp( *ppsz_compare, psz_speaker, strlen(*ppsz_compare) ) )
184 if ( *ppsz_compare == NULL )
186 msg_Err( p_aout, "(%s) isn't valid speaker setup option", psz_speaker );
187 msg_Err( p_aout, "Defaulting to Windows default speaker config");
191 p_aout->sys->i_speaker_setup = i;
193 /* Initialise DirectSound */
194 if( InitDirectSound( p_aout ) )
196 msg_Err( p_aout, "cannot initialize DirectSound" );
200 if( var_Type( p_aout, "audio-device" ) == 0 )
202 Probe( p_aout, fmt );
205 if( var_Get( p_aout, "audio-device", &val ) < 0 )
207 msg_Err( p_aout, "DirectSound Probe failed()" );
211 /* Open the device */
212 if( val.i_int == AOUT_VAR_SPDIF )
214 fmt->i_format = VLC_CODEC_SPDIFL;
216 /* Calculate the frame size in bytes */
217 fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
218 fmt->i_frame_length = A52_FRAME_NB;
219 p_aout->sys->i_frame_size = fmt->i_bytes_per_frame;
221 if( CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL,
222 fmt->i_physical_channels,
223 aout_FormatNbChannels( fmt ), fmt->i_rate,
224 p_aout->sys->i_frame_size, false )
227 msg_Err( p_aout, "cannot open directx audio device" );
231 aout_PacketInit( p_aout, &p_aout->sys->packet, A52_FRAME_NB, fmt );
235 if( val.i_int == AOUT_VAR_5_1 )
236 fmt->i_physical_channels = AOUT_CHANS_5_0;
237 else if( val.i_int == AOUT_VAR_7_1 )
238 fmt->i_physical_channels = AOUT_CHANS_7_1;
239 else if( val.i_int == AOUT_VAR_3F2R )
240 fmt->i_physical_channels = AOUT_CHANS_5_0;
241 else if( val.i_int == AOUT_VAR_2F2R )
242 fmt->i_physical_channels = AOUT_CHANS_4_0;
243 else if( val.i_int == AOUT_VAR_MONO )
244 fmt->i_physical_channels = AOUT_CHAN_CENTER;
246 fmt->i_physical_channels = AOUT_CHANS_2_0;
248 aout_FormatPrepare( fmt );
250 if( CreateDSBufferPCM( p_aout, &fmt->i_format,
251 fmt->i_physical_channels, fmt->i_rate, false )
254 msg_Err( p_aout, "cannot open directx audio device" );
258 /* Calculate the frame size in bytes */
259 aout_PacketInit( p_aout, &p_aout->sys->packet, fmt->i_rate / 20, fmt );
262 /* Force volume update in thread. TODO: use session volume on Vista+ */
263 p_aout->sys->volume.volume = p_aout->sys->volume.mb;
265 /* Now we need to setup our DirectSound play notification structure */
266 vlc_atomic_set(&p_aout->sys->notif.abort, 0);
267 p_aout->sys->notif.event = CreateEvent( 0, FALSE, FALSE, 0 );
268 if( unlikely(p_aout->sys->notif.event == NULL) )
270 p_aout->sys->notif.i_frame_size = p_aout->sys->i_frame_size;
272 /* then launch the notification thread */
273 msg_Dbg( p_aout, "creating DirectSoundThread" );
274 if( vlc_clone( &p_aout->sys->notif.thread, DirectSoundThread, p_aout,
275 VLC_THREAD_PRIORITY_HIGHEST ) )
277 msg_Err( p_aout, "cannot create DirectSoundThread" );
278 CloseHandle( p_aout->sys->notif.event );
279 p_aout->sys->notif.event = NULL;
280 aout_PacketDestroy( p_aout );
284 p_aout->time_get = aout_PacketTimeGet;
286 p_aout->pause = NULL;
287 p_aout->flush = aout_PacketFlush;
295 /*****************************************************************************
296 * Probe: probe the audio device for available formats and channels
297 *****************************************************************************/
298 static void Probe( audio_output_t * p_aout, const audio_sample_format_t *fmt )
300 vlc_value_t val, text;
301 vlc_fourcc_t i_format;
302 DWORD ui_speaker_config;
303 bool is_default_output_set = false;
305 var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
306 text.psz_string = _("Audio Device");
307 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
309 /* Test for 5.1 support */
310 if( fmt->i_physical_channels == AOUT_CHANS_5_1 )
312 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_5_1,
313 fmt->i_rate, true ) == VLC_SUCCESS )
315 val.i_int = AOUT_VAR_5_1;
316 text.psz_string = (char*) "5.1";
317 var_Change( p_aout, "audio-device",
318 VLC_VAR_ADDCHOICE, &val, &text );
319 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
320 is_default_output_set = true;
321 msg_Dbg( p_aout, "device supports 5.1 channels" );
325 /* Test for 7.1 support */
326 if( fmt->i_physical_channels == AOUT_CHANS_7_1 )
328 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_7_1,
329 fmt->i_rate, true ) == VLC_SUCCESS )
331 val.i_int = AOUT_VAR_7_1;
332 text.psz_string = (char*) "7.1";
333 var_Change( p_aout, "audio-device",
334 VLC_VAR_ADDCHOICE, &val, &text );
335 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
336 is_default_output_set = true;
337 msg_Dbg( p_aout, "device supports 7.1 channels" );
341 /* Test for 3 Front 2 Rear support */
342 if( fmt->i_physical_channels == AOUT_CHANS_5_0 )
344 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_5_0,
345 fmt->i_rate, true ) == VLC_SUCCESS )
347 val.i_int = AOUT_VAR_3F2R;
348 text.psz_string = _("3 Front 2 Rear");
349 var_Change( p_aout, "audio-device",
350 VLC_VAR_ADDCHOICE, &val, &text );
351 if(!is_default_output_set)
353 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
354 is_default_output_set = true;
356 msg_Dbg( p_aout, "device supports 5 channels" );
360 /* Test for 2 Front 2 Rear support */
361 if( ( fmt->i_physical_channels & AOUT_CHANS_4_0 ) == AOUT_CHANS_4_0 )
363 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_4_0,
364 fmt->i_rate, true ) == VLC_SUCCESS )
366 val.i_int = AOUT_VAR_2F2R;
367 text.psz_string = _("2 Front 2 Rear");
368 var_Change( p_aout, "audio-device",
369 VLC_VAR_ADDCHOICE, &val, &text );
370 if(!is_default_output_set)
372 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
373 is_default_output_set = true;
375 msg_Dbg( p_aout, "device supports 4 channels" );
379 /* Test for stereo support */
380 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_2_0,
381 fmt->i_rate, true ) == VLC_SUCCESS )
383 val.i_int = AOUT_VAR_STEREO;
384 text.psz_string = _("Stereo");
385 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
386 if(!is_default_output_set)
388 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
389 is_default_output_set = true;
390 msg_Dbg( p_aout, "device supports 2 channels (DEFAULT!)" );
392 else msg_Dbg( p_aout, "device supports 2 channels" );
395 /* Test for mono support */
396 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHAN_CENTER,
397 fmt->i_rate, true ) == VLC_SUCCESS )
399 val.i_int = AOUT_VAR_MONO;
400 text.psz_string = _("Mono");
401 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
402 msg_Dbg( p_aout, "device supports 1 channel" );
405 /* Check the speaker configuration to determine which channel config should
407 if FAILED( IDirectSound_GetSpeakerConfig( p_aout->sys->p_dsobject,
408 &ui_speaker_config ) )
410 ui_speaker_config = DSSPEAKER_STEREO;
411 msg_Dbg( p_aout, "GetSpeakerConfig failed" );
413 switch( DSSPEAKER_CONFIG(ui_speaker_config) )
415 case DSSPEAKER_7POINT1:
416 case DSSPEAKER_7POINT1_SURROUND:
417 msg_Dbg( p_aout, "Windows says your SpeakerConfig is 7.1" );
418 val.i_int = AOUT_VAR_7_1;
420 case DSSPEAKER_5POINT1:
421 case DSSPEAKER_5POINT1_SURROUND:
422 msg_Dbg( p_aout, "Windows says your SpeakerConfig is 5.1" );
423 val.i_int = AOUT_VAR_5_1;
426 msg_Dbg( p_aout, "Windows says your SpeakerConfig is Quad" );
427 val.i_int = AOUT_VAR_2F2R;
429 #if 0 /* Lots of people just get their settings wrong and complain that
430 * this is a problem with VLC so just don't ever set mono by default. */
432 val.i_int = AOUT_VAR_MONO;
435 case DSSPEAKER_SURROUND:
436 msg_Dbg( p_aout, "Windows says your SpeakerConfig is surround" );
437 case DSSPEAKER_STEREO:
438 msg_Dbg( p_aout, "Windows says your SpeakerConfig is stereo" );
440 /* If nothing else is found, choose stereo output */
441 val.i_int = AOUT_VAR_STEREO;
445 /* Check if we want to override speaker config */
446 switch( p_aout->sys->i_speaker_setup )
448 case 0: /* Default value aka Windows default speaker setup */
451 msg_Dbg( p_aout, "SpeakerConfig is forced to Mono" );
452 val.i_int = AOUT_VAR_MONO;
455 msg_Dbg( p_aout, "SpeakerConfig is forced to Stereo" );
456 val.i_int = AOUT_VAR_STEREO;
459 msg_Dbg( p_aout, "SpeakerConfig is forced to Quad" );
460 val.i_int = AOUT_VAR_2F2R;
463 msg_Dbg( p_aout, "SpeakerConfig is forced to 5.1" );
464 val.i_int = AOUT_VAR_5_1;
467 msg_Dbg( p_aout, "SpeakerConfig is forced to 7.1" );
468 val.i_int = AOUT_VAR_7_1;
471 msg_Dbg( p_aout, "SpeakerConfig is forced to non-existing value" );
475 var_Set( p_aout, "audio-device", val );
477 /* Test for SPDIF support */
478 if ( AOUT_FMT_SPDIF( fmt ) )
480 if( CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL,
481 fmt->i_physical_channels,
482 aout_FormatNbChannels( fmt ), fmt->i_rate,
483 AOUT_SPDIF_SIZE, true )
486 msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
487 val.i_int = AOUT_VAR_SPDIF;
488 text.psz_string = _("A/52 over S/PDIF");
489 var_Change( p_aout, "audio-device",
490 VLC_VAR_ADDCHOICE, &val, &text );
491 if( var_InheritBool( p_aout, "spdif" ) )
492 var_Set( p_aout, "audio-device", val );
496 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
499 /* Probe() has failed. */
500 var_Destroy( p_aout, "audio-device" );
504 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
507 /*****************************************************************************
508 * Play: we'll start playing the directsound buffer here because at least here
509 * we know the first buffer has been put in the aout fifo and we also
511 *****************************************************************************/
512 static void Play( audio_output_t *p_aout, block_t *p_buffer )
514 /* get the playing date of the first aout buffer */
515 p_aout->sys->notif.start_date = p_buffer->i_pts;
517 /* fill in the first samples (zeroes) */
518 FillBuffer( p_aout, 0, NULL );
520 /* wake up the audio output thread */
521 SetEvent( p_aout->sys->notif.event );
523 aout_PacketPlay( p_aout, p_buffer );
524 p_aout->play = aout_PacketPlay;
527 static int VolumeSet( audio_output_t *p_aout, float vol )
529 aout_sys_t *sys = p_aout->sys;
532 /* Convert UI volume to linear factor (cube) */
533 vol = vol * vol * vol;
535 /* millibels from linear amplification */
536 LONG mb = lroundf(2000.f * log10f(vol));
538 /* Clamp to allowed DirectSound range */
539 static_assert( DSBVOLUME_MIN < DSBVOLUME_MAX, "DSBVOLUME_* confused" );
540 if( mb > DSBVOLUME_MAX )
545 if( mb <= DSBVOLUME_MIN )
549 if (!sys->volume.mute)
550 InterlockedExchange(&sys->volume.volume, mb);
552 /* Convert back to UI volume */
553 vol = cbrtf(powf(10.f, ((float)mb) / 2000.f));
554 aout_VolumeReport( p_aout, vol );
556 if( var_InheritBool( p_aout, "volume-save" ) )
557 config_PutInt( p_aout, "directx-volume", mb );
561 static int MuteSet( audio_output_t *p_aout, bool mute )
563 aout_sys_t *sys = p_aout->sys;
565 sys->volume.mute = mute;
566 InterlockedExchange(&sys->volume.volume,
567 mute ? DSBVOLUME_MIN : sys->volume.mb);
569 aout_MuteReport( p_aout, mute );
573 /*****************************************************************************
574 * CloseAudio: close the audio device
575 *****************************************************************************/
576 static void Stop( audio_output_t *p_aout )
578 aout_sys_t *p_sys = p_aout->sys;
580 msg_Dbg( p_aout, "closing audio device" );
582 /* kill the position notification thread, if any */
583 if( p_sys->notif.event != NULL )
585 vlc_atomic_set(&p_aout->sys->notif.abort, 1);
586 /* wake up the audio thread if needed */
587 if( p_aout->play == Play )
588 SetEvent( p_sys->notif.event );
590 vlc_join( p_sys->notif.thread, NULL );
591 CloseHandle( p_sys->notif.event );
592 aout_PacketDestroy( p_aout );
595 /* release the secondary buffer */
596 DestroyDSBuffer( p_aout );
598 /* finally release the DirectSound object */
599 if( p_sys->p_dsobject ) IDirectSound_Release( p_sys->p_dsobject );
602 /*****************************************************************************
603 * InitDirectSound: handle all the gory details of DirectSound initialisation
604 *****************************************************************************/
605 static int InitDirectSound( audio_output_t *p_aout )
607 aout_sys_t *sys = p_aout->sys;
608 GUID guid, *p_guid = NULL;
609 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
611 OurDirectSoundCreate = (void *)
612 GetProcAddress( p_aout->sys->hdsound_dll,
613 "DirectSoundCreate" );
614 if( OurDirectSoundCreate == NULL )
616 msg_Warn( p_aout, "GetProcAddress FAILED" );
620 char *dev = var_InheritString( p_aout, "directx-audio-device" );
623 LPOLESTR lpsz = ToWide( dev );
625 if( SUCCEEDED( IIDFromString( lpsz, &guid ) ) )
628 msg_Err( p_aout, "bad device GUID: %ls", lpsz );
633 /* Create the direct sound object */
634 if FAILED( OurDirectSoundCreate( p_guid, &sys->p_dsobject, NULL ) )
636 msg_Warn( p_aout, "cannot create a direct sound device" );
640 /* Set DirectSound Cooperative level, ie what control we want over Windows
641 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
642 * settings of the primary buffer, but also that only the sound of our
643 * application will be hearable when it will have the focus.
644 * !!! (this is not really working as intended yet because to set the
645 * cooperative level you need the window handle of your application, and
646 * I don't know of any easy way to get it. Especially since we might play
647 * sound without any video, and so what window handle should we use ???
648 * The hack for now is to use the Desktop window handle - it seems to be
650 if( IDirectSound_SetCooperativeLevel( p_aout->sys->p_dsobject,
654 msg_Warn( p_aout, "cannot set direct sound cooperative level" );
659 sys->p_dsobject = NULL;
664 /*****************************************************************************
665 * CreateDSBuffer: Creates a direct sound buffer of the required format.
666 *****************************************************************************
667 * This function creates the buffer we'll use to play audio.
668 * In DirectSound there are two kinds of buffers:
669 * - the primary buffer: which is the actual buffer that the soundcard plays
670 * - the secondary buffer(s): these buffers are the one actually used by
671 * applications and DirectSound takes care of mixing them into the primary.
673 * Once you create a secondary buffer, you cannot change its format anymore so
674 * you have to release the current one and create another.
675 *****************************************************************************/
676 static int CreateDSBuffer( audio_output_t *p_aout, int i_format,
677 int i_channels, int i_nb_channels, int i_rate,
678 int i_bytes_per_frame, bool b_probe )
680 WAVEFORMATEXTENSIBLE waveformat;
681 DSBUFFERDESC dsbdesc;
683 /* First set the sound buffer format */
684 waveformat.dwChannelMask = 0;
685 for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ )
686 if( i_channels & pi_vlc_chan_order_wg4[i] )
687 waveformat.dwChannelMask |= pi_channels_in[i];
691 case VLC_CODEC_SPDIFL:
693 /* To prevent channel re-ordering */
694 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
695 waveformat.Format.wBitsPerSample = 16;
696 waveformat.Samples.wValidBitsPerSample =
697 waveformat.Format.wBitsPerSample;
698 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
699 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
703 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
704 waveformat.Samples.wValidBitsPerSample =
705 waveformat.Format.wBitsPerSample;
706 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
707 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
711 waveformat.Format.wBitsPerSample = 16;
712 waveformat.Samples.wValidBitsPerSample =
713 waveformat.Format.wBitsPerSample;
714 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
715 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
719 waveformat.Format.nChannels = i_nb_channels;
720 waveformat.Format.nSamplesPerSec = i_rate;
721 waveformat.Format.nBlockAlign =
722 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
723 waveformat.Format.nAvgBytesPerSec =
724 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
726 p_aout->sys->format = i_format;
728 /* Then fill in the direct sound descriptor */
729 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
730 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
731 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
732 | DSBCAPS_GLOBALFOCUS /* Allows background playing */
733 | DSBCAPS_CTRLVOLUME; /* Allows volume control */
735 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
736 if( i_nb_channels <= 2 )
738 waveformat.Format.cbSize = 0;
742 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
743 waveformat.Format.cbSize =
744 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
746 /* Needed for 5.1 on emu101k */
747 dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
750 dsbdesc.dwBufferBytes = FRAMES_NUM * i_bytes_per_frame; /* buffer size */
751 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;
753 if FAILED( IDirectSound_CreateSoundBuffer(
754 p_aout->sys->p_dsobject, &dsbdesc,
755 &p_aout->sys->p_dsbuffer, NULL) )
757 if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE )
759 /* Try without DSBCAPS_LOCHARDWARE */
760 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
761 if FAILED( IDirectSound_CreateSoundBuffer(
762 p_aout->sys->p_dsobject, &dsbdesc,
763 &p_aout->sys->p_dsbuffer, NULL) )
768 msg_Dbg( p_aout, "couldn't use hardware sound buffer" );
776 /* Stop here if we were just probing */
779 IDirectSoundBuffer_Release( p_aout->sys->p_dsbuffer );
780 p_aout->sys->p_dsbuffer = NULL;
784 p_aout->sys->i_frame_size = i_bytes_per_frame;
785 p_aout->sys->i_channel_mask = waveformat.dwChannelMask;
786 p_aout->sys->chans_to_reorder =
787 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
788 waveformat.dwChannelMask,
789 p_aout->sys->chan_table );
790 if( p_aout->sys->chans_to_reorder )
792 msg_Dbg( p_aout, "channel reordering needed" );
798 /*****************************************************************************
799 * CreateDSBufferPCM: creates a PCM direct sound buffer.
800 *****************************************************************************
801 * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
802 * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
803 ****************************************************************************/
804 static int CreateDSBufferPCM( audio_output_t *p_aout, vlc_fourcc_t *i_format,
805 int i_channels, int i_rate, bool b_probe )
807 unsigned i_nb_channels = popcount( i_channels );
809 /* Float32 audio samples are not supported for 5.1 output on the emu101k */
810 if( !var_GetBool( p_aout, "directx-audio-float32" ) ||
812 CreateDSBuffer( p_aout, VLC_CODEC_FL32,
813 i_channels, i_nb_channels, i_rate,
814 (i_rate / 20) * 4 * i_nb_channels, b_probe )
817 if ( CreateDSBuffer( p_aout, VLC_CODEC_S16N,
818 i_channels, i_nb_channels, i_rate,
819 (i_rate / 20) * 2 * i_nb_channels, b_probe )
826 *i_format = VLC_CODEC_S16N;
832 *i_format = VLC_CODEC_FL32;
837 /*****************************************************************************
839 *****************************************************************************
840 * This function destroys the secondary buffer.
841 *****************************************************************************/
842 static void DestroyDSBuffer( audio_output_t *p_aout )
844 if( p_aout->sys->p_dsbuffer )
846 IDirectSoundBuffer_Release( p_aout->sys->p_dsbuffer );
847 p_aout->sys->p_dsbuffer = NULL;
851 /*****************************************************************************
852 * FillBuffer: Fill in one of the direct sound frame buffers.
853 *****************************************************************************
854 * Returns VLC_SUCCESS on success.
855 *****************************************************************************/
856 static int FillBuffer( audio_output_t *p_aout, int i_frame, block_t *p_buffer )
858 aout_sys_t *p_sys = p_aout->sys;
859 notification_thread_t *p_notif = &p_sys->notif;
860 void *p_write_position, *p_wrap_around;
861 unsigned long l_bytes1, l_bytes2;
864 /* Before copying anything, we have to lock the buffer */
865 dsresult = IDirectSoundBuffer_Lock(
866 p_sys->p_dsbuffer, /* DS buffer */
867 i_frame * p_notif->i_frame_size, /* Start offset */
868 p_notif->i_frame_size, /* Number of bytes */
869 &p_write_position, /* Address of lock start */
870 &l_bytes1, /* Count of bytes locked before wrap around */
871 &p_wrap_around, /* Buffer address (if wrap around) */
872 &l_bytes2, /* Count of bytes after wrap around */
874 if( dsresult == DSERR_BUFFERLOST )
876 IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
877 dsresult = IDirectSoundBuffer_Lock(
879 i_frame * p_notif->i_frame_size,
880 p_notif->i_frame_size,
887 if( dsresult != DS_OK )
889 msg_Warn( p_aout, "cannot lock buffer" );
890 if( p_buffer ) block_Release( p_buffer );
894 if( p_buffer == NULL )
896 memset( p_write_position, 0, l_bytes1 );
900 if( p_sys->chans_to_reorder )
901 /* Do the channel reordering here */
902 aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer,
903 p_sys->chans_to_reorder, p_sys->chan_table,
906 memcpy( p_write_position, p_buffer->p_buffer, l_bytes1 );
907 block_Release( p_buffer );
910 /* Now the data has been copied, unlock the buffer */
911 IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
912 p_wrap_around, l_bytes2 );
914 p_notif->i_write_slot = (i_frame + 1) % FRAMES_NUM;
918 /*****************************************************************************
919 * DirectSoundThread: this thread will capture play notification events.
920 *****************************************************************************
921 * We use this thread to emulate a callback mechanism. The thread probes for
922 * event notification and fills up the DS secondary buffer when needed.
923 *****************************************************************************/
924 static void* DirectSoundThread( void *data )
926 audio_output_t *p_aout = (audio_output_t *)data;
927 notification_thread_t *p_notif = &p_aout->sys->notif;
930 msg_Dbg( p_aout, "DirectSoundThread ready" );
932 /* Wait here until Play() is called */
933 WaitForSingleObject( p_notif->event, INFINITE );
935 if( !vlc_atomic_get( &p_notif->abort) )
938 mwait( p_notif->start_date - AOUT_MAX_PTS_ADVANCE / 2 );
940 /* start playing the buffer */
941 dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer,
944 DSBPLAY_LOOPING ); /* Flags */
945 if( dsresult == DSERR_BUFFERLOST )
947 IDirectSoundBuffer_Restore( p_aout->sys->p_dsbuffer );
948 dsresult = IDirectSoundBuffer_Play(
949 p_aout->sys->p_dsbuffer,
952 DSBPLAY_LOOPING ); /* Flags */
954 if( dsresult != DS_OK )
956 msg_Err( p_aout, "cannot start playing buffer" );
961 while( !vlc_atomic_get( &p_notif->abort ) )
964 int l_queued = 0, l_free_slots;
965 unsigned i_frame_siz = p_aout->sys->packet.samples;
966 mtime_t mtime = mdate();
969 /* Update volume if required */
970 LONG volume = InterlockedExchange( &p_aout->sys->volume.volume, -1 );
971 if( unlikely(volume != -1) )
972 IDirectSoundBuffer_SetVolume( p_aout->sys->p_dsbuffer, volume );
975 * Fill in as much audio data as we can in our circular buffer
978 /* Find out current play position */
979 if FAILED( IDirectSoundBuffer_GetCurrentPosition(
980 p_aout->sys->p_dsbuffer, &l_read, NULL ) )
982 msg_Err( p_aout, "GetCurrentPosition() failed!" );
986 /* Detect underruns */
987 if( l_queued && mtime - last_time >
988 INT64_C(1000000) * l_queued / p_aout->sys->packet.format.i_rate )
990 msg_Dbg( p_aout, "detected underrun!" );
994 /* Try to fill in as many frame buffers as possible */
995 l_read /= (p_aout->sys->packet.format.i_bytes_per_frame /
996 p_aout->sys->packet.format.i_frame_length);
997 l_queued = p_notif->i_write_slot * i_frame_siz - l_read;
998 if( l_queued < 0 ) l_queued += (i_frame_siz * FRAMES_NUM);
999 l_free_slots = (FRAMES_NUM * i_frame_siz - l_queued) / i_frame_siz;
1001 for( i = 0; i < l_free_slots; i++ )
1003 block_t *p_buffer = aout_PacketNext( p_aout,
1004 mtime + INT64_C(1000000) * (i * i_frame_siz + l_queued) /
1005 p_aout->sys->packet.format.i_rate );
1007 /* If there is no audio data available and we have some buffered
1008 * already, then just wait for the next time */
1009 if( !p_buffer && (i || l_queued / i_frame_siz) ) break;
1011 if( FillBuffer( p_aout, p_notif->i_write_slot % FRAMES_NUM,
1012 p_buffer ) != VLC_SUCCESS ) break;
1015 /* Sleep a reasonable amount of time */
1016 l_queued += (i * i_frame_siz);
1017 msleep( INT64_C(1000000) * l_queued / p_aout->sys->packet.format.i_rate / 2 );
1020 /* make sure the buffer isn't playing */
1021 IDirectSoundBuffer_Stop( p_aout->sys->p_dsbuffer );
1023 msg_Dbg( p_aout, "DirectSoundThread exiting" );
1034 static int CALLBACK DeviceEnumCallback( LPGUID guid, LPCWSTR desc,
1035 LPCWSTR mod, LPVOID data )
1037 ds_list_t *list = data;
1040 StringFromGUID2( guid, buf, 48 );
1043 list->ids = xrealloc( list->ids, list->count * sizeof(char *) );
1044 list->names = xrealloc( list->names, list->count * sizeof(char *) );
1045 list->ids[list->count - 1] = FromWide( buf );
1046 list->names[list->count - 1] = FromWide( desc );
1047 if( list->ids == NULL || list->names == NULL )
1054 /*****************************************************************************
1055 * ReloadDirectXDevices: store the list of devices in preferences
1056 *****************************************************************************/
1057 static int ReloadDirectXDevices( vlc_object_t *p_this, char const *psz_name,
1058 char ***values, char ***descs )
1060 ds_list_t list = { 0, NULL, NULL };
1064 HANDLE hdsound_dll = LoadLibrary(_T("DSOUND.DLL"));
1065 if( hdsound_dll == NULL )
1067 msg_Warn( p_this, "cannot open DSOUND.DLL" );
1071 /* Get DirectSoundEnumerate */
1072 HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKW, LPVOID) =
1073 (void *)GetProcAddress( hdsound_dll, _T("DirectSoundEnumerateW") );
1074 if( OurDirectSoundEnumerate != NULL )
1076 OurDirectSoundEnumerate( DeviceEnumCallback, &list );
1077 msg_Dbg( p_this, "found %u devices", list.count );
1079 FreeLibrary(hdsound_dll);
1083 *descs = list.names;
1087 static int Open(vlc_object_t *obj)
1089 audio_output_t *aout = (audio_output_t *)obj;
1090 aout_sys_t *sys = calloc(1, sizeof (*sys));
1091 if (unlikely(sys == NULL))
1094 sys->hdsound_dll = LoadLibrary(_T("DSOUND.DLL"));
1095 if (sys->hdsound_dll == NULL)
1097 msg_Warn(aout, "cannot open DSOUND.DLL");
1099 return VLC_EGENERIC;
1103 aout->start = Start;
1105 aout->volume_set = VolumeSet;
1106 aout->mute_set = MuteSet;
1109 LONG mb = var_InheritInteger(aout, "directx-volume");
1110 sys->volume.mb = mb;
1111 aout_VolumeReport(aout, cbrtf(powf(10.f, ((float)mb) / 2000.f)));
1112 MuteSet(aout, var_InheritBool(aout, "mute"));
1117 static void Close(vlc_object_t *obj)
1119 audio_output_t *aout = (audio_output_t *)obj;
1120 aout_sys_t *sys = aout->sys;
1122 FreeLibrary(sys->hdsound_dll); /* free DSOUND.DLL */