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>
39 #include "windows_audio_common.h"
41 #define DS_BUF_SIZE (1024*1024)
43 /*****************************************************************************
44 * aout_sys_t: directx audio output method descriptor
45 *****************************************************************************
46 * This structure is part of the audio output thread descriptor.
47 * It describes the direct sound specific properties of an audio device.
48 *****************************************************************************/
51 HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
53 LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
54 LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
55 * takes care of mixing all the
56 * secondary buffers into the primary) */
58 LPDIRECTSOUNDNOTIFY p_notify;
67 int i_bytes_per_sample; /* Size in bytes of one frame */
68 int i_rate; /* Sample rate */
70 int i_speaker_setup; /* Speaker setup override */
72 uint8_t chans_to_reorder; /* do we need channel reordering */
73 uint8_t chan_table[AOUT_CHAN_MAX];
74 uint32_t i_channel_mask;
80 /*****************************************************************************
82 *****************************************************************************/
83 static int Open( vlc_object_t * );
84 static void Close( vlc_object_t * );
85 static void Stop( audio_output_t * );
86 static void Play( audio_output_t *, block_t * );
87 static int VolumeSet( audio_output_t *, float );
88 static int MuteSet( audio_output_t *, bool );
89 static void Flush( audio_output_t *, bool );
90 static void Pause( audio_output_t *, bool, mtime_t );
91 static int TimeGet( audio_output_t *, mtime_t *);
94 static void Probe( audio_output_t *, const audio_sample_format_t * );
95 static int InitDirectSound ( audio_output_t * );
96 static int CreateDSBuffer ( audio_output_t *, int, int, int, int, bool );
97 static int CreateDSBufferPCM ( audio_output_t *, vlc_fourcc_t*, int, int, bool );
98 static void DestroyDSBuffer ( audio_output_t * );
99 static int FillBuffer ( audio_output_t *, block_t * );
101 static int ReloadDirectXDevices( vlc_object_t *, const char *,
102 char ***, char *** );
104 /* Speaker setup override options list */
105 static const char *const speaker_list[] = { "Windows default", "Mono", "Stereo",
106 "Quad", "5.1", "7.1" };
108 /*****************************************************************************
110 *****************************************************************************/
111 #define DEVICE_TEXT N_("Output device")
112 #define DEVICE_LONGTEXT N_("Select your audio output device")
114 #define SPEAKER_TEXT N_("Speaker configuration")
115 #define SPEAKER_LONGTEXT N_("Select speaker configuration you want to use. " \
116 "This option doesn't upmix! So NO e.g. Stereo -> 5.1 conversion." )
118 #define VOLUME_TEXT N_("Audio volume")
119 #define VOLUME_LONGTEXT N_("Audio volume in hundredths of decibels (dB).")
122 set_description( N_("DirectX audio output") )
123 set_shortname( "DirectX" )
124 set_capability( "audio output", 100 )
125 set_category( CAT_AUDIO )
126 set_subcategory( SUBCAT_AUDIO_AOUT )
127 add_shortcut( "directx", "aout_directx" )
129 add_string( "directx-audio-device", NULL,
130 DEVICE_TEXT, DEVICE_LONGTEXT, false )
131 change_string_cb( ReloadDirectXDevices )
132 add_obsolete_string( "directx-audio-device-name")
133 add_bool( "directx-audio-float32", false, FLOAT_TEXT,
134 FLOAT_LONGTEXT, true )
135 add_string( "directx-audio-speaker", "Windows default",
136 SPEAKER_TEXT, SPEAKER_LONGTEXT, true )
137 change_string_list( speaker_list, speaker_list )
138 add_float( "directx-volume", 1.0f,
139 VOLUME_TEXT, VOLUME_LONGTEXT, true )
140 change_integer_range( DSBVOLUME_MIN, DSBVOLUME_MAX )
142 set_callbacks( Open, Close )
145 /*****************************************************************************
146 * OpenAudio: open the audio device
147 *****************************************************************************
148 * This function opens and setups Direct Sound.
149 *****************************************************************************/
150 static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
156 const char * const * ppsz_compare = speaker_list;
158 msg_Dbg( p_aout, "Opening DirectSound Audio Output" );
160 /* Retrieve config values */
161 var_Create( p_aout, "directx-audio-float32",
162 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
163 psz_speaker = var_CreateGetString( p_aout, "directx-audio-speaker" );
165 while ( *ppsz_compare != NULL )
167 if ( !strncmp( *ppsz_compare, psz_speaker, strlen(*ppsz_compare) ) )
174 if ( *ppsz_compare == NULL )
176 msg_Err( p_aout, "(%s) isn't valid speaker setup option", psz_speaker );
177 msg_Err( p_aout, "Defaulting to Windows default speaker config");
181 p_aout->sys->i_speaker_setup = i;
183 /* Initialise DirectSound */
184 if( InitDirectSound( p_aout ) )
186 msg_Err( p_aout, "cannot initialize DirectSound" );
190 if( var_Type( p_aout, "audio-device" ) == 0 )
192 Probe( p_aout, fmt );
195 if( var_Get( p_aout, "audio-device", &val ) < 0 )
197 msg_Err( p_aout, "DirectSound Probe failed()" );
201 /* Open the device */
202 if( val.i_int == AOUT_VAR_SPDIF )
204 fmt->i_format = VLC_CODEC_SPDIFL;
206 /* Calculate the frame size in bytes */
207 fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
208 fmt->i_frame_length = A52_FRAME_NB;
210 if( CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL,
211 fmt->i_physical_channels,
212 aout_FormatNbChannels( fmt ), fmt->i_rate, false )
215 msg_Err( p_aout, "cannot open directx audio device" );
221 if( val.i_int == AOUT_VAR_5_1 )
222 fmt->i_physical_channels = AOUT_CHANS_5_0;
223 else if( val.i_int == AOUT_VAR_7_1 )
224 fmt->i_physical_channels = AOUT_CHANS_7_1;
225 else if( val.i_int == AOUT_VAR_3F2R )
226 fmt->i_physical_channels = AOUT_CHANS_5_0;
227 else if( val.i_int == AOUT_VAR_2F2R )
228 fmt->i_physical_channels = AOUT_CHANS_4_0;
229 else if( val.i_int == AOUT_VAR_MONO )
230 fmt->i_physical_channels = AOUT_CHAN_CENTER;
232 fmt->i_physical_channels = AOUT_CHANS_2_0;
234 aout_FormatPrepare( fmt );
236 if( CreateDSBufferPCM( p_aout, &fmt->i_format,
237 fmt->i_physical_channels, fmt->i_rate, false )
240 msg_Err( p_aout, "cannot open directx audio device" );
244 p_aout->sys->i_write = 0;
246 /* Force volume update */
247 VolumeSet( p_aout, p_aout->sys->volume.volume );
248 MuteSet( p_aout, p_aout->sys->volume.mute );
250 /* then launch the notification thread */
251 p_aout->time_get = TimeGet;
253 p_aout->pause = Pause;
254 p_aout->flush = Flush;
263 /*****************************************************************************
264 * Probe: probe the audio device for available formats and channels
265 *****************************************************************************/
266 static void Probe( audio_output_t * p_aout, const audio_sample_format_t *fmt )
268 vlc_value_t val, text;
269 vlc_fourcc_t i_format;
270 DWORD ui_speaker_config;
271 bool is_default_output_set = false;
273 var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
274 text.psz_string = _("Audio Device");
275 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
277 /* Test for 5.1 support */
278 if( fmt->i_physical_channels == AOUT_CHANS_5_1 )
280 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_5_1,
281 fmt->i_rate, true ) == VLC_SUCCESS )
283 val.i_int = AOUT_VAR_5_1;
284 text.psz_string = (char*) "5.1";
285 var_Change( p_aout, "audio-device",
286 VLC_VAR_ADDCHOICE, &val, &text );
287 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
288 is_default_output_set = true;
289 msg_Dbg( p_aout, "device supports 5.1 channels" );
293 /* Test for 7.1 support */
294 if( fmt->i_physical_channels == AOUT_CHANS_7_1 )
296 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_7_1,
297 fmt->i_rate, true ) == VLC_SUCCESS )
299 val.i_int = AOUT_VAR_7_1;
300 text.psz_string = (char*) "7.1";
301 var_Change( p_aout, "audio-device",
302 VLC_VAR_ADDCHOICE, &val, &text );
303 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
304 is_default_output_set = true;
305 msg_Dbg( p_aout, "device supports 7.1 channels" );
309 /* Test for 3 Front 2 Rear support */
310 if( fmt->i_physical_channels == AOUT_CHANS_5_0 )
312 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_5_0,
313 fmt->i_rate, true ) == VLC_SUCCESS )
315 val.i_int = AOUT_VAR_3F2R;
316 text.psz_string = _("3 Front 2 Rear");
317 var_Change( p_aout, "audio-device",
318 VLC_VAR_ADDCHOICE, &val, &text );
319 if(!is_default_output_set)
321 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
322 is_default_output_set = true;
324 msg_Dbg( p_aout, "device supports 5 channels" );
328 /* Test for 2 Front 2 Rear support */
329 if( ( fmt->i_physical_channels & AOUT_CHANS_4_0 ) == AOUT_CHANS_4_0 )
331 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_4_0,
332 fmt->i_rate, true ) == VLC_SUCCESS )
334 val.i_int = AOUT_VAR_2F2R;
335 text.psz_string = _("2 Front 2 Rear");
336 var_Change( p_aout, "audio-device",
337 VLC_VAR_ADDCHOICE, &val, &text );
338 if(!is_default_output_set)
340 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
341 is_default_output_set = true;
343 msg_Dbg( p_aout, "device supports 4 channels" );
347 /* Test for stereo support */
348 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_2_0,
349 fmt->i_rate, true ) == VLC_SUCCESS )
351 val.i_int = AOUT_VAR_STEREO;
352 text.psz_string = _("Stereo");
353 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
354 if(!is_default_output_set)
356 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
357 is_default_output_set = true;
358 msg_Dbg( p_aout, "device supports 2 channels (DEFAULT!)" );
360 else msg_Dbg( p_aout, "device supports 2 channels" );
363 /* Test for mono support */
364 if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHAN_CENTER,
365 fmt->i_rate, true ) == VLC_SUCCESS )
367 val.i_int = AOUT_VAR_MONO;
368 text.psz_string = _("Mono");
369 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
370 msg_Dbg( p_aout, "device supports 1 channel" );
373 /* Check the speaker configuration to determine which channel config should
375 if FAILED( IDirectSound_GetSpeakerConfig( p_aout->sys->p_dsobject,
376 &ui_speaker_config ) )
378 ui_speaker_config = DSSPEAKER_STEREO;
379 msg_Dbg( p_aout, "GetSpeakerConfig failed" );
381 switch( DSSPEAKER_CONFIG(ui_speaker_config) )
383 case DSSPEAKER_7POINT1:
384 case DSSPEAKER_7POINT1_SURROUND:
385 msg_Dbg( p_aout, "Windows says your SpeakerConfig is 7.1" );
386 val.i_int = AOUT_VAR_7_1;
388 case DSSPEAKER_5POINT1:
389 case DSSPEAKER_5POINT1_SURROUND:
390 msg_Dbg( p_aout, "Windows says your SpeakerConfig is 5.1" );
391 val.i_int = AOUT_VAR_5_1;
394 msg_Dbg( p_aout, "Windows says your SpeakerConfig is Quad" );
395 val.i_int = AOUT_VAR_2F2R;
397 #if 0 /* Lots of people just get their settings wrong and complain that
398 * this is a problem with VLC so just don't ever set mono by default. */
400 val.i_int = AOUT_VAR_MONO;
403 case DSSPEAKER_SURROUND:
404 msg_Dbg( p_aout, "Windows says your SpeakerConfig is surround" );
405 case DSSPEAKER_STEREO:
406 msg_Dbg( p_aout, "Windows says your SpeakerConfig is stereo" );
408 /* If nothing else is found, choose stereo output */
409 val.i_int = AOUT_VAR_STEREO;
413 /* Check if we want to override speaker config */
414 switch( p_aout->sys->i_speaker_setup )
416 case 0: /* Default value aka Windows default speaker setup */
419 msg_Dbg( p_aout, "SpeakerConfig is forced to Mono" );
420 val.i_int = AOUT_VAR_MONO;
423 msg_Dbg( p_aout, "SpeakerConfig is forced to Stereo" );
424 val.i_int = AOUT_VAR_STEREO;
427 msg_Dbg( p_aout, "SpeakerConfig is forced to Quad" );
428 val.i_int = AOUT_VAR_2F2R;
431 msg_Dbg( p_aout, "SpeakerConfig is forced to 5.1" );
432 val.i_int = AOUT_VAR_5_1;
435 msg_Dbg( p_aout, "SpeakerConfig is forced to 7.1" );
436 val.i_int = AOUT_VAR_7_1;
439 msg_Dbg( p_aout, "SpeakerConfig is forced to non-existing value" );
443 var_Set( p_aout, "audio-device", val );
445 /* Test for SPDIF support */
446 if ( AOUT_FMT_SPDIF( fmt ) )
448 if( CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL,
449 fmt->i_physical_channels,
450 aout_FormatNbChannels( fmt ), fmt->i_rate, true )
453 msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
454 val.i_int = AOUT_VAR_SPDIF;
455 text.psz_string = _("A/52 over S/PDIF");
456 var_Change( p_aout, "audio-device",
457 VLC_VAR_ADDCHOICE, &val, &text );
458 if( var_InheritBool( p_aout, "spdif" ) )
459 var_Set( p_aout, "audio-device", val );
463 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
466 /* Probe() has failed. */
467 var_Destroy( p_aout, "audio-device" );
471 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
474 /*****************************************************************************
475 * Play: we'll start playing the directsound buffer here because at least here
476 * we know the first buffer has been put in the aout fifo and we also
478 *****************************************************************************/
479 static void Play( audio_output_t *p_aout, block_t *p_buffer )
481 if( FillBuffer( p_aout, p_buffer ) == VLC_SUCCESS )
483 /* start playing the buffer */
484 HRESULT dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer,
485 0, 0, DSBPLAY_LOOPING );
486 if( dsresult == DSERR_BUFFERLOST )
488 IDirectSoundBuffer_Restore( p_aout->sys->p_dsbuffer );
489 dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer,
490 0, 0, DSBPLAY_LOOPING );
492 if( dsresult != DS_OK )
493 msg_Err( p_aout, "cannot start playing buffer" );
497 static int VolumeSet( audio_output_t *p_aout, float volume )
499 aout_sys_t *sys = p_aout->sys;
502 /* Convert UI volume to linear factor (cube) */
503 float vol = volume * volume * volume;
505 /* millibels from linear amplification */
506 LONG mb = lroundf(2000.f * log10f(vol));
508 /* Clamp to allowed DirectSound range */
509 static_assert( DSBVOLUME_MIN < DSBVOLUME_MAX, "DSBVOLUME_* confused" );
510 if( mb > DSBVOLUME_MAX )
515 if( mb <= DSBVOLUME_MIN )
519 sys->volume.volume = volume;
520 if( !sys->volume.mute && sys->p_dsbuffer &&
521 IDirectSoundBuffer_SetVolume( sys->p_dsbuffer, mb ) != DS_OK )
523 /* Convert back to UI volume */
524 aout_VolumeReport( p_aout, volume );
526 if( var_InheritBool( p_aout, "volume-save" ) )
527 config_PutFloat( p_aout, "directx-volume", volume );
531 static int MuteSet( audio_output_t *p_aout, bool mute )
534 aout_sys_t *sys = p_aout->sys;
536 sys->volume.mute = mute;
538 if( sys->p_dsbuffer )
539 res = IDirectSoundBuffer_SetVolume( sys->p_dsbuffer,
540 mute? DSBVOLUME_MIN : sys->volume.mb );
542 aout_MuteReport( p_aout, mute );
543 return (res != DS_OK);
546 /*****************************************************************************
547 * CloseAudio: close the audio device
548 *****************************************************************************/
549 static void Stop( audio_output_t *p_aout )
551 aout_sys_t *p_sys = p_aout->sys;
552 msg_Dbg( p_aout, "closing audio device" );
554 if( p_sys->p_notify )
555 IDirectSoundNotify_Release(p_sys->p_notify );
556 p_sys->p_notify = NULL;
558 IDirectSoundBuffer_Stop( p_sys->p_dsbuffer );
559 /* release the secondary buffer */
560 DestroyDSBuffer( p_aout );
562 /* finally release the DirectSound object */
563 if( p_sys->p_dsobject )
564 IDirectSound_Release( p_sys->p_dsobject );
567 /*****************************************************************************
568 * InitDirectSound: handle all the gory details of DirectSound initialisation
569 *****************************************************************************/
570 static int InitDirectSound( audio_output_t *p_aout )
572 aout_sys_t *sys = p_aout->sys;
573 GUID guid, *p_guid = NULL;
574 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
576 OurDirectSoundCreate = (void *)
577 GetProcAddress( p_aout->sys->hdsound_dll,
578 "DirectSoundCreate" );
579 if( OurDirectSoundCreate == NULL )
581 msg_Warn( p_aout, "GetProcAddress FAILED" );
585 char *dev = var_InheritString( p_aout, "directx-audio-device" );
588 LPOLESTR lpsz = ToWide( dev );
590 if( SUCCEEDED( IIDFromString( lpsz, &guid ) ) )
593 msg_Err( p_aout, "bad device GUID: %ls", lpsz );
598 /* Create the direct sound object */
599 if FAILED( OurDirectSoundCreate( p_guid, &sys->p_dsobject, NULL ) )
601 msg_Warn( p_aout, "cannot create a direct sound device" );
605 /* Set DirectSound Cooperative level, ie what control we want over Windows
606 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
607 * settings of the primary buffer, but also that only the sound of our
608 * application will be hearable when it will have the focus.
609 * !!! (this is not really working as intended yet because to set the
610 * cooperative level you need the window handle of your application, and
611 * I don't know of any easy way to get it. Especially since we might play
612 * sound without any video, and so what window handle should we use ???
613 * The hack for now is to use the Desktop window handle - it seems to be
615 #ifndef VLC_WINSTORE_APP
616 if( IDirectSound_SetCooperativeLevel( p_aout->sys->p_dsobject,
620 msg_Warn( p_aout, "cannot set direct sound cooperative level" );
626 sys->p_dsobject = NULL;
631 /*****************************************************************************
632 * CreateDSBuffer: Creates a direct sound buffer of the required format.
633 *****************************************************************************
634 * This function creates the buffer we'll use to play audio.
635 * In DirectSound there are two kinds of buffers:
636 * - the primary buffer: which is the actual buffer that the soundcard plays
637 * - the secondary buffer(s): these buffers are the one actually used by
638 * applications and DirectSound takes care of mixing them into the primary.
640 * Once you create a secondary buffer, you cannot change its format anymore so
641 * you have to release the current one and create another.
642 *****************************************************************************/
643 static int CreateDSBuffer( audio_output_t *p_aout, int i_format,
644 int i_channels, int i_nb_channels, int i_rate,
647 WAVEFORMATEXTENSIBLE waveformat;
648 DSBUFFERDESC dsbdesc;
650 /* First set the sound buffer format */
651 waveformat.dwChannelMask = 0;
652 for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ )
653 if( i_channels & pi_vlc_chan_order_wg4[i] )
654 waveformat.dwChannelMask |= pi_channels_in[i];
658 case VLC_CODEC_SPDIFL:
660 /* To prevent channel re-ordering */
661 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
662 waveformat.Format.wBitsPerSample = 16;
663 waveformat.Samples.wValidBitsPerSample =
664 waveformat.Format.wBitsPerSample;
665 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
666 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
670 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
671 waveformat.Samples.wValidBitsPerSample =
672 waveformat.Format.wBitsPerSample;
673 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
674 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
678 waveformat.Format.wBitsPerSample = 16;
679 waveformat.Samples.wValidBitsPerSample =
680 waveformat.Format.wBitsPerSample;
681 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
682 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
686 waveformat.Format.nChannels = i_nb_channels;
687 waveformat.Format.nSamplesPerSec = i_rate;
688 waveformat.Format.nBlockAlign =
689 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
690 waveformat.Format.nAvgBytesPerSec =
691 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
693 p_aout->sys->i_bytes_per_sample = waveformat.Format.nBlockAlign;
694 p_aout->sys->format = i_format;
696 /* Then fill in the direct sound descriptor */
697 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
698 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
699 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /* Better position accuracy */
700 | DSBCAPS_GLOBALFOCUS /* Allows background playing */
701 | DSBCAPS_CTRLVOLUME /* Allows volume control */
702 | DSBCAPS_CTRLPOSITIONNOTIFY; /* Allow position notifications */
704 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
705 if( i_nb_channels <= 2 )
707 waveformat.Format.cbSize = 0;
711 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
712 waveformat.Format.cbSize =
713 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
715 /* Needed for 5.1 on emu101k */
716 dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
719 dsbdesc.dwBufferBytes = DS_BUF_SIZE; /* buffer size */
720 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;
722 if FAILED( IDirectSound_CreateSoundBuffer(
723 p_aout->sys->p_dsobject, &dsbdesc,
724 &p_aout->sys->p_dsbuffer, NULL) )
726 if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE )
728 /* Try without DSBCAPS_LOCHARDWARE */
729 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
730 if FAILED( IDirectSound_CreateSoundBuffer(
731 p_aout->sys->p_dsobject, &dsbdesc,
732 &p_aout->sys->p_dsbuffer, NULL) )
737 msg_Dbg( p_aout, "couldn't use hardware sound buffer" );
745 /* Stop here if we were just probing */
748 IDirectSoundBuffer_Release( p_aout->sys->p_dsbuffer );
749 p_aout->sys->p_dsbuffer = NULL;
753 p_aout->sys->i_rate = i_rate;
754 p_aout->sys->i_channel_mask = waveformat.dwChannelMask;
755 p_aout->sys->chans_to_reorder =
756 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
757 waveformat.dwChannelMask,
758 p_aout->sys->chan_table );
759 if( p_aout->sys->chans_to_reorder )
761 msg_Dbg( p_aout, "channel reordering needed" );
764 if( IDirectSoundBuffer_QueryInterface( p_aout->sys->p_dsbuffer,
765 &IID_IDirectSoundNotify,
766 (void **) &p_aout->sys->p_notify )
770 msg_Err(p_aout, "Couldn't query IDirectSoundNotify");
771 p_aout->sys->p_notify = NULL;
774 FillBuffer(p_aout,NULL);
779 /*****************************************************************************
780 * CreateDSBufferPCM: creates a PCM direct sound buffer.
781 *****************************************************************************
782 * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
783 * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
784 ****************************************************************************/
785 static int CreateDSBufferPCM( audio_output_t *p_aout, vlc_fourcc_t *i_format,
786 int i_channels, int i_rate, bool b_probe )
788 unsigned i_nb_channels = popcount( i_channels );
790 /* Float32 audio samples are not supported for 5.1 output on the emu101k */
791 if( !var_GetBool( p_aout, "directx-audio-float32" ) ||
793 CreateDSBuffer( p_aout, VLC_CODEC_FL32,
794 i_channels, i_nb_channels, i_rate, b_probe )
797 if ( CreateDSBuffer( p_aout, VLC_CODEC_S16N,
798 i_channels, i_nb_channels, i_rate, b_probe )
805 *i_format = VLC_CODEC_S16N;
811 *i_format = VLC_CODEC_FL32;
816 /*****************************************************************************
818 *****************************************************************************
819 * This function destroys the secondary buffer.
820 *****************************************************************************/
821 static void DestroyDSBuffer( audio_output_t *p_aout )
823 if( p_aout->sys->p_dsbuffer )
825 IDirectSoundBuffer_Release( p_aout->sys->p_dsbuffer );
826 p_aout->sys->p_dsbuffer = NULL;
830 /*****************************************************************************
831 * FillBuffer: Fill in one of the direct sound frame buffers.
832 *****************************************************************************
833 * Returns VLC_SUCCESS on success.
834 *****************************************************************************/
835 static int FillBuffer( audio_output_t *p_aout, block_t *p_buffer )
837 aout_sys_t *p_sys = p_aout->sys;
839 size_t towrite = (p_buffer)?p_buffer->i_buffer:DS_BUF_SIZE;
840 void *p_write_position, *p_wrap_around;
841 unsigned long l_bytes1, l_bytes2;
844 /* Before copying anything, we have to lock the buffer */
845 dsresult = IDirectSoundBuffer_Lock(
846 p_sys->p_dsbuffer, /* DS buffer */
847 p_aout->sys->i_write, /* Start offset */
848 towrite, /* Number of bytes */
849 &p_write_position, /* Address of lock start */
850 &l_bytes1, /* Count of bytes locked before wrap around */
851 &p_wrap_around, /* Buffer address (if wrap around) */
852 &l_bytes2, /* Count of bytes after wrap around */
853 0 ); /* Flags: DSBLOCK_FROMWRITECURSOR is buggy */
854 if( dsresult == DSERR_BUFFERLOST )
856 IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
857 dsresult = IDirectSoundBuffer_Lock(
859 p_aout->sys->i_write,
867 if( dsresult != DS_OK )
869 msg_Warn( p_aout, "cannot lock buffer" );
870 if( p_buffer ) block_Release( p_buffer );
874 if( p_buffer == NULL )
876 memset( p_write_position, 0, l_bytes1 );
880 if( p_sys->chans_to_reorder )
881 /* Do the channel reordering here */
882 aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer,
883 p_sys->chans_to_reorder, p_sys->chan_table,
886 memcpy( p_write_position, p_buffer->p_buffer, l_bytes1 );
887 if( l_bytes1 < p_buffer->i_buffer)
888 memcpy(p_wrap_around, p_buffer->p_buffer + l_bytes1, l_bytes2);
889 block_Release( p_buffer );
892 /* Now the data has been copied, unlock the buffer */
893 IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
894 p_wrap_around, l_bytes2 );
896 p_sys->i_write += towrite;
897 p_sys->i_write %= DS_BUF_SIZE;
909 static int CALLBACK DeviceEnumCallback( LPGUID guid, LPCWSTR desc,
910 LPCWSTR mod, LPVOID data )
912 ds_list_t *list = data;
915 StringFromGUID2( guid, buf, 48 );
918 list->ids = xrealloc( list->ids, list->count * sizeof(char *) );
919 list->names = xrealloc( list->names, list->count * sizeof(char *) );
920 list->ids[list->count - 1] = FromWide( buf );
921 list->names[list->count - 1] = FromWide( desc );
922 if( list->ids == NULL || list->names == NULL )
929 /*****************************************************************************
930 * ReloadDirectXDevices: store the list of devices in preferences
931 *****************************************************************************/
932 static int ReloadDirectXDevices( vlc_object_t *p_this, char const *psz_name,
933 char ***values, char ***descs )
935 ds_list_t list = { 0, NULL, NULL };
939 HANDLE hdsound_dll = LoadLibrary(_T("DSOUND.DLL"));
940 if( hdsound_dll == NULL )
942 msg_Warn( p_this, "cannot open DSOUND.DLL" );
946 /* Get DirectSoundEnumerate */
947 HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKW, LPVOID) =
948 (void *)GetProcAddress( hdsound_dll, "DirectSoundEnumerateW" );
949 if( OurDirectSoundEnumerate != NULL )
951 OurDirectSoundEnumerate( DeviceEnumCallback, &list );
952 msg_Dbg( p_this, "found %u devices", list.count );
954 FreeLibrary(hdsound_dll);
962 static int Open(vlc_object_t *obj)
964 audio_output_t *aout = (audio_output_t *)obj;
965 aout_sys_t *sys = calloc(1, sizeof (*sys));
966 if (unlikely(sys == NULL))
969 sys->hdsound_dll = LoadLibrary(_T("DSOUND.DLL"));
970 if (sys->hdsound_dll == NULL)
972 msg_Warn(aout, "cannot open DSOUND.DLL");
980 aout->volume_set = VolumeSet;
981 aout->mute_set = MuteSet;
984 sys->volume.volume = var_InheritFloat(aout, "directx-volume");
985 aout_VolumeReport(aout, sys->volume.volume );
986 MuteSet(aout, var_InheritBool(aout, "mute"));
988 sys->hnotify_evt = CreateEvent(NULL, FALSE, TRUE, NULL);
989 if( !sys->hnotify_evt )
991 msg_Err(aout, "cannot create Event");
992 FreeLibrary(sys->hdsound_dll);
1000 static void Close(vlc_object_t *obj)
1002 audio_output_t *aout = (audio_output_t *)obj;
1003 aout_sys_t *sys = aout->sys;
1005 CloseHandle(sys->hnotify_evt);
1006 FreeLibrary(sys->hdsound_dll); /* free DSOUND.DLL */
1010 static void Flush ( audio_output_t * aout, bool drain )
1012 aout_sys_t *sys = aout->sys;
1018 DSBPOSITIONNOTIFY notif = {.dwOffset = aout->sys->i_write, .hEventNotify = sys->hnotify_evt } ;
1019 if( IDirectSoundNotify_SetNotificationPositions( sys->p_notify, 1, ¬if ) == DS_OK )
1021 WaitForSingleObject( sys->hnotify_evt, INFINITE );
1022 IDirectSoundBuffer_Stop( aout->sys->p_dsbuffer );
1026 while( IDirectSoundBuffer_GetCurrentPosition( aout->sys->p_dsbuffer,(LPDWORD) &read, NULL) == DS_OK )
1028 read %= DS_BUF_SIZE;
1029 if( read == aout->sys->i_write )
1036 IDirectSoundBuffer_Stop( aout->sys->p_dsbuffer );
1037 IDirectSoundBuffer_SetCurrentPosition( aout->sys->p_dsbuffer,
1038 aout->sys->i_write );
1042 static void Pause( audio_output_t * aout, bool pause, mtime_t date )
1046 IDirectSoundBuffer_Stop( aout->sys->p_dsbuffer );
1048 IDirectSoundBuffer_Play( aout->sys->p_dsbuffer, 0, 0, DSBPLAY_LOOPING );
1052 static int TimeGet( audio_output_t * aout, mtime_t * delay )
1057 if( IDirectSoundBuffer_GetCurrentPosition( aout->sys->p_dsbuffer, (LPDWORD) &read, NULL) != DS_OK )
1060 read %= DS_BUF_SIZE;
1062 size = (mtime_t)aout->sys->i_write - (mtime_t) read;
1064 size += DS_BUF_SIZE;
1066 *delay = ( size / aout->sys->i_bytes_per_sample ) * CLOCK_FREQ / aout->sys->i_rate;