1 /*****************************************************************************
2 * directx.c: Windows DirectX audio output method
3 *****************************************************************************
4 * Copyright (C) 2001-2009 the VideoLAN team
7 * Authors: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
35 #include <vlc_charset.h>
37 #include "windows_audio_common.h"
41 #define FRAME_SIZE ((int)p_aout->output.output.i_rate/20) /* Size in samples */
43 /*****************************************************************************
44 * notification_thread_t: DirectX event thread
45 *****************************************************************************/
46 typedef struct notification_thread_t
50 aout_instance_t
*p_aout
;
51 int i_frame_size
; /* size in bytes of one frame */
52 int i_write_slot
; /* current write position in our circular buffer */
57 } notification_thread_t
;
59 /*****************************************************************************
60 * aout_sys_t: directx audio output method descriptor
61 *****************************************************************************
62 * This structure is part of the audio output thread descriptor.
63 * It describes the direct sound specific properties of an audio device.
64 *****************************************************************************/
67 HINSTANCE hdsound_dll
; /* handle of the opened dsound dll */
69 char * psz_device
; /* user defined device name */
72 LPDIRECTSOUND p_dsobject
; /* main Direct Sound object */
73 LPDIRECTSOUNDBUFFER p_dsbuffer
; /* the sound buffer we use (direct sound
74 * takes care of mixing all the
75 * secondary buffers into the primary) */
77 notification_thread_t
*p_notif
; /* DirectSoundThread id */
79 int b_playing
; /* playing status */
81 int i_frame_size
; /* Size in bytes of one frame */
83 int i_speaker_setup
; /* Speaker setup override */
85 bool b_chan_reorder
; /* do we need channel reordering */
86 int pi_chan_table
[AOUT_CHAN_MAX
];
87 uint32_t i_channel_mask
;
88 uint32_t i_bits_per_sample
;
92 /*****************************************************************************
94 *****************************************************************************/
95 static int OpenAudio ( vlc_object_t
* );
96 static void CloseAudio ( vlc_object_t
* );
97 static void Play ( aout_instance_t
* );
100 static void Probe ( aout_instance_t
* );
101 static int InitDirectSound ( aout_instance_t
* );
102 static int CreateDSBuffer ( aout_instance_t
*, int, int, int, int, int, bool );
103 static int CreateDSBufferPCM ( aout_instance_t
*, vlc_fourcc_t
*, int, int, int, bool );
104 static void DestroyDSBuffer ( aout_instance_t
* );
105 static void* DirectSoundThread( vlc_object_t
* );
106 static int FillBuffer ( aout_instance_t
*, int, aout_buffer_t
* );
108 static int ReloadDirectXDevices( vlc_object_t
*, char const *,
109 vlc_value_t
, vlc_value_t
, void * );
111 /* Speaker setup override options list */
112 static const char *const speaker_list
[] = { "Windows default", "Mono", "Stereo",
113 "Quad", "5.1", "7.1" };
114 static const char *const ppsz_adev
[] = {"default", };
115 static const char *const ppsz_adev_text
[] = {"default", };
117 /*****************************************************************************
119 *****************************************************************************/
120 #define DEVICE_TEXT N_("Output device")
121 #define DEVICE_LONGTEXT N_("Select your audio output device")
123 #define SPEAKER_TEXT N_("Speaker configuration")
124 #define SPEAKER_LONGTEXT N_("Select speaker configuration you want to use. " \
125 "This option doesn't upmix! So NO e.g. Stereo -> 5.1 conversion." )
128 set_description( N_("DirectX audio output") )
129 set_shortname( "DirectX" )
130 set_capability( "audio output", 100 )
131 set_category( CAT_AUDIO
)
132 set_subcategory( SUBCAT_AUDIO_AOUT
)
133 add_shortcut( "directx", "directsound" )
135 add_string( "directx-audio-device-name", "default", NULL
,
136 DEVICE_TEXT
, DEVICE_LONGTEXT
, false )
137 add_deprecated_alias( "directx-audio-device" ) /* Since 1.1.0 */
138 change_string_list( ppsz_adev
, ppsz_adev_text
, ReloadDirectXDevices
)
139 change_action_add( ReloadDirectXDevices
, N_("Refresh list") )
140 change_need_restart ()
141 add_bool( "directx-audio-float32", false, NULL
, FLOAT_TEXT
,
142 FLOAT_LONGTEXT
, true )
143 add_string( "directx-audio-speaker", "Windows default", NULL
,
144 SPEAKER_TEXT
, SPEAKER_LONGTEXT
, true )
145 change_string_list( speaker_list
, 0, 0 )
146 change_need_restart ()
148 set_callbacks( OpenAudio
, CloseAudio
)
151 /*****************************************************************************
152 * OpenAudio: open the audio device
153 *****************************************************************************
154 * This function opens and setups Direct Sound.
155 *****************************************************************************/
156 static int OpenAudio( vlc_object_t
*p_this
)
158 aout_instance_t
* p_aout
= (aout_instance_t
*)p_this
;
163 const char * const * ppsz_compare
= speaker_list
;
165 msg_Dbg( p_aout
, "Opening DirectSound Audio Output" );
167 /* Allocate structure */
168 p_aout
->output
.p_sys
= malloc( sizeof( aout_sys_t
) );
169 if( p_aout
->output
.p_sys
== NULL
)
172 /* Initialize some variables */
173 p_aout
->output
.p_sys
->p_dsobject
= NULL
;
174 p_aout
->output
.p_sys
->p_dsbuffer
= NULL
;
175 p_aout
->output
.p_sys
->p_notif
= NULL
;
176 p_aout
->output
.p_sys
->b_playing
= 0;
178 p_aout
->output
.pf_play
= Play
;
179 aout_VolumeSoftInit( p_aout
);
181 /* Retrieve config values */
182 var_Create( p_aout
, "directx-audio-float32",
183 VLC_VAR_BOOL
| VLC_VAR_DOINHERIT
);
184 psz_speaker
= var_CreateGetString( p_aout
, "directx-audio-speaker" );
186 while ( *ppsz_compare
!= NULL
)
188 if ( !strncmp( *ppsz_compare
, psz_speaker
, strlen(*ppsz_compare
) ) )
195 if ( *ppsz_compare
== NULL
)
197 msg_Err( p_aout
, "(%s) isn't valid speaker setup option", psz_speaker
);
198 msg_Err( p_aout
, "Defaulting to Windows default speaker config");
202 p_aout
->output
.p_sys
->i_speaker_setup
= i
;
204 p_aout
->output
.p_sys
->p_device_guid
= 0;
206 /* Initialise DirectSound */
207 if( InitDirectSound( p_aout
) )
209 msg_Err( p_aout
, "cannot initialize DirectSound" );
213 if( var_Type( p_aout
, "audio-device" ) == 0 )
218 if( var_Get( p_aout
, "audio-device", &val
) < 0 )
220 /* Probe() has failed. */
224 /* Open the device */
225 if( val
.i_int
== AOUT_VAR_SPDIF
)
227 p_aout
->output
.output
.i_format
= VLC_CODEC_SPDIFL
;
229 /* Calculate the frame size in bytes */
230 p_aout
->output
.i_nb_samples
= A52_FRAME_NB
;
231 p_aout
->output
.output
.i_bytes_per_frame
= AOUT_SPDIF_SIZE
;
232 p_aout
->output
.output
.i_frame_length
= A52_FRAME_NB
;
233 p_aout
->output
.p_sys
->i_frame_size
=
234 p_aout
->output
.output
.i_bytes_per_frame
;
236 if( CreateDSBuffer( p_aout
, VLC_CODEC_SPDIFL
,
237 p_aout
->output
.output
.i_physical_channels
,
238 aout_FormatNbChannels( &p_aout
->output
.output
),
239 p_aout
->output
.output
.i_rate
,
240 p_aout
->output
.p_sys
->i_frame_size
, false )
243 msg_Err( p_aout
, "cannot open directx audio device" );
244 free( p_aout
->output
.p_sys
);
248 aout_VolumeNoneInit( p_aout
);
252 if( val
.i_int
== AOUT_VAR_5_1
)
254 p_aout
->output
.output
.i_physical_channels
255 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_CENTER
256 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
259 else if( val
.i_int
== AOUT_VAR_7_1
)
261 p_aout
->output
.output
.i_physical_channels
262 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_CENTER
263 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
264 | AOUT_CHAN_MIDDLELEFT
| AOUT_CHAN_MIDDLERIGHT
267 else if( val
.i_int
== AOUT_VAR_3F2R
)
269 p_aout
->output
.output
.i_physical_channels
270 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_CENTER
271 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
;
273 else if( val
.i_int
== AOUT_VAR_2F2R
)
275 p_aout
->output
.output
.i_physical_channels
276 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
277 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
;
279 else if( val
.i_int
== AOUT_VAR_MONO
)
281 p_aout
->output
.output
.i_physical_channels
= AOUT_CHAN_CENTER
;
285 p_aout
->output
.output
.i_physical_channels
286 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
;
289 if( CreateDSBufferPCM( p_aout
, &p_aout
->output
.output
.i_format
,
290 p_aout
->output
.output
.i_physical_channels
,
291 aout_FormatNbChannels( &p_aout
->output
.output
),
292 p_aout
->output
.output
.i_rate
, false )
295 msg_Err( p_aout
, "cannot open directx audio device" );
296 free( p_aout
->output
.p_sys
);
300 /* Calculate the frame size in bytes */
301 p_aout
->output
.i_nb_samples
= FRAME_SIZE
;
302 aout_FormatPrepare( &p_aout
->output
.output
);
303 aout_VolumeSoftInit( p_aout
);
306 /* Now we need to setup our DirectSound play notification structure */
307 p_aout
->output
.p_sys
->p_notif
=
308 vlc_object_create( p_aout
, sizeof(notification_thread_t
) );
309 p_aout
->output
.p_sys
->p_notif
->p_aout
= p_aout
;
311 p_aout
->output
.p_sys
->p_notif
->event
= CreateEvent( 0, FALSE
, FALSE
, 0 );
312 p_aout
->output
.p_sys
->p_notif
->i_frame_size
=
313 p_aout
->output
.p_sys
->i_frame_size
;
315 /* then launch the notification thread */
316 msg_Dbg( p_aout
, "creating DirectSoundThread" );
317 if( vlc_thread_create( p_aout
->output
.p_sys
->p_notif
,
318 "DirectSound Notification Thread",
320 VLC_THREAD_PRIORITY_HIGHEST
) )
322 msg_Err( p_aout
, "cannot create DirectSoundThread" );
323 CloseHandle( p_aout
->output
.p_sys
->p_notif
->event
);
324 vlc_object_release( p_aout
->output
.p_sys
->p_notif
);
325 p_aout
->output
.p_sys
->p_notif
= NULL
;
329 vlc_object_attach( p_aout
->output
.p_sys
->p_notif
, p_aout
);
334 CloseAudio( VLC_OBJECT(p_aout
) );
338 /*****************************************************************************
339 * Probe: probe the audio device for available formats and channels
340 *****************************************************************************/
341 static void Probe( aout_instance_t
* p_aout
)
343 vlc_value_t val
, text
;
344 vlc_fourcc_t i_format
;
345 unsigned int i_physical_channels
;
346 DWORD ui_speaker_config
;
347 bool is_default_output_set
= false;
349 var_Create( p_aout
, "audio-device", VLC_VAR_INTEGER
| VLC_VAR_HASCHOICE
);
350 text
.psz_string
= _("Audio Device");
351 var_Change( p_aout
, "audio-device", VLC_VAR_SETTEXT
, &text
, NULL
);
353 /* Test for 5.1 support */
354 i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
|
355 AOUT_CHAN_CENTER
| AOUT_CHAN_REARLEFT
|
356 AOUT_CHAN_REARRIGHT
| AOUT_CHAN_LFE
;
357 if( p_aout
->output
.output
.i_physical_channels
== i_physical_channels
)
359 if( CreateDSBufferPCM( p_aout
, &i_format
, i_physical_channels
, 6,
360 p_aout
->output
.output
.i_rate
, true )
363 val
.i_int
= AOUT_VAR_5_1
;
364 text
.psz_string
= (char*) "5.1";
365 var_Change( p_aout
, "audio-device",
366 VLC_VAR_ADDCHOICE
, &val
, &text
);
367 var_Change( p_aout
, "audio-device", VLC_VAR_SETDEFAULT
, &val
, NULL
);
368 is_default_output_set
= true;
369 msg_Dbg( p_aout
, "device supports 5.1 channels" );
373 /* Test for 7.1 support */
374 i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
|
375 AOUT_CHAN_CENTER
| AOUT_CHAN_REARLEFT
|
376 AOUT_CHAN_MIDDLELEFT
| AOUT_CHAN_MIDDLERIGHT
|
377 AOUT_CHAN_REARRIGHT
| AOUT_CHAN_LFE
;
378 if( p_aout
->output
.output
.i_physical_channels
== i_physical_channels
)
380 if( CreateDSBufferPCM( p_aout
, &i_format
, i_physical_channels
, 8,
381 p_aout
->output
.output
.i_rate
, true )
384 val
.i_int
= AOUT_VAR_7_1
;
385 text
.psz_string
= (char*) "7.1";
386 var_Change( p_aout
, "audio-device",
387 VLC_VAR_ADDCHOICE
, &val
, &text
);
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 7.1 channels" );
394 /* Test for 3 Front 2 Rear support */
395 i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
|
396 AOUT_CHAN_CENTER
| AOUT_CHAN_REARLEFT
|
398 if( p_aout
->output
.output
.i_physical_channels
== i_physical_channels
)
400 if( CreateDSBufferPCM( p_aout
, &i_format
, i_physical_channels
, 5,
401 p_aout
->output
.output
.i_rate
, true )
404 val
.i_int
= AOUT_VAR_3F2R
;
405 text
.psz_string
= _("3 Front 2 Rear");
406 var_Change( p_aout
, "audio-device",
407 VLC_VAR_ADDCHOICE
, &val
, &text
);
408 if(!is_default_output_set
)
410 var_Change( p_aout
, "audio-device", VLC_VAR_SETDEFAULT
, &val
, NULL
);
411 is_default_output_set
= true;
413 msg_Dbg( p_aout
, "device supports 5 channels" );
417 /* Test for 2 Front 2 Rear support */
418 i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
|
419 AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
;
420 if( ( p_aout
->output
.output
.i_physical_channels
& i_physical_channels
)
421 == i_physical_channels
)
423 if( CreateDSBufferPCM( p_aout
, &i_format
, i_physical_channels
, 4,
424 p_aout
->output
.output
.i_rate
, true )
427 val
.i_int
= AOUT_VAR_2F2R
;
428 text
.psz_string
= _("2 Front 2 Rear");
429 var_Change( p_aout
, "audio-device",
430 VLC_VAR_ADDCHOICE
, &val
, &text
);
431 if(!is_default_output_set
)
433 var_Change( p_aout
, "audio-device", VLC_VAR_SETDEFAULT
, &val
, NULL
);
434 is_default_output_set
= true;
436 msg_Dbg( p_aout
, "device supports 4 channels" );
440 /* Test for stereo support */
441 i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
;
442 if( CreateDSBufferPCM( p_aout
, &i_format
, i_physical_channels
, 2,
443 p_aout
->output
.output
.i_rate
, true )
446 val
.i_int
= AOUT_VAR_STEREO
;
447 text
.psz_string
= _("Stereo");
448 var_Change( p_aout
, "audio-device", VLC_VAR_ADDCHOICE
, &val
, &text
);
449 if(!is_default_output_set
)
451 var_Change( p_aout
, "audio-device", VLC_VAR_SETDEFAULT
, &val
, NULL
);
452 is_default_output_set
= true;
453 msg_Dbg( p_aout
, "device supports 2 channels (DEFAULT!)" );
455 else msg_Dbg( p_aout
, "device supports 2 channels" );
458 /* Test for mono support */
459 i_physical_channels
= AOUT_CHAN_CENTER
;
460 if( CreateDSBufferPCM( p_aout
, &i_format
, i_physical_channels
, 1,
461 p_aout
->output
.output
.i_rate
, true )
464 val
.i_int
= AOUT_VAR_MONO
;
465 text
.psz_string
= _("Mono");
466 var_Change( p_aout
, "audio-device", VLC_VAR_ADDCHOICE
, &val
, &text
);
467 msg_Dbg( p_aout
, "device supports 1 channel" );
470 /* Check the speaker configuration to determine which channel config should
472 if FAILED( IDirectSound_GetSpeakerConfig( p_aout
->output
.p_sys
->p_dsobject
,
473 &ui_speaker_config
) )
475 ui_speaker_config
= DSSPEAKER_STEREO
;
476 msg_Dbg( p_aout
, "GetSpeakerConfig failed" );
478 switch( DSSPEAKER_CONFIG(ui_speaker_config
) )
480 case DSSPEAKER_7POINT1
:
481 msg_Dbg( p_aout
, "Windows says your SpeakerConfig is 7.1" );
482 val
.i_int
= AOUT_VAR_7_1
;
484 case DSSPEAKER_5POINT1
:
485 msg_Dbg( p_aout
, "Windows says your SpeakerConfig is 5.1" );
486 val
.i_int
= AOUT_VAR_5_1
;
489 msg_Dbg( p_aout
, "Windows says your SpeakerConfig is Quad" );
490 val
.i_int
= AOUT_VAR_2F2R
;
492 #if 0 /* Lots of people just get their settings wrong and complain that
493 * this is a problem with VLC so just don't ever set mono by default. */
495 val
.i_int
= AOUT_VAR_MONO
;
498 case DSSPEAKER_SURROUND
:
499 msg_Dbg( p_aout
, "Windows says your SpeakerConfig is surround" );
500 case DSSPEAKER_STEREO
:
501 msg_Dbg( p_aout
, "Windows says your SpeakerConfig is stereo" );
503 /* If nothing else is found, choose stereo output */
504 val
.i_int
= AOUT_VAR_STEREO
;
508 /* Check if we want to override speaker config */
509 switch( p_aout
->output
.p_sys
->i_speaker_setup
)
511 case 0: /* Default value aka Windows default speaker setup */
514 msg_Dbg( p_aout
, "SpeakerConfig is forced to Mono" );
515 val
.i_int
= AOUT_VAR_MONO
;
518 msg_Dbg( p_aout
, "SpeakerConfig is forced to Stereo" );
519 val
.i_int
= AOUT_VAR_STEREO
;
522 msg_Dbg( p_aout
, "SpeakerConfig is forced to Quad" );
523 val
.i_int
= AOUT_VAR_2F2R
;
526 msg_Dbg( p_aout
, "SpeakerConfig is forced to 5.1" );
527 val
.i_int
= AOUT_VAR_5_1
;
530 msg_Dbg( p_aout
, "SpeakerConfig is forced to 7.1" );
531 val
.i_int
= AOUT_VAR_7_1
;
534 msg_Dbg( p_aout
, "SpeakerConfig is forced to non-existing value" );
538 var_Set( p_aout
, "audio-device", val
);
540 /* Test for SPDIF support */
541 if ( AOUT_FMT_NON_LINEAR( &p_aout
->output
.output
) )
543 if( CreateDSBuffer( p_aout
, VLC_CODEC_SPDIFL
,
544 p_aout
->output
.output
.i_physical_channels
,
545 aout_FormatNbChannels( &p_aout
->output
.output
),
546 p_aout
->output
.output
.i_rate
,
547 AOUT_SPDIF_SIZE
, true )
550 msg_Dbg( p_aout
, "device supports A/52 over S/PDIF" );
551 val
.i_int
= AOUT_VAR_SPDIF
;
552 text
.psz_string
= _("A/52 over S/PDIF");
553 var_Change( p_aout
, "audio-device",
554 VLC_VAR_ADDCHOICE
, &val
, &text
);
555 if( var_InheritBool( p_aout
, "spdif" ) )
556 var_Set( p_aout
, "audio-device", val
);
560 var_Change( p_aout
, "audio-device", VLC_VAR_CHOICESCOUNT
, &val
, NULL
);
563 /* Probe() has failed. */
564 var_Destroy( p_aout
, "audio-device" );
568 var_AddCallback( p_aout
, "audio-device", aout_ChannelsRestart
, NULL
);
569 var_SetBool( p_aout
, "intf-change", true );
572 /*****************************************************************************
573 * Play: we'll start playing the directsound buffer here because at least here
574 * we know the first buffer has been put in the aout fifo and we also
576 *****************************************************************************/
577 static void Play( aout_instance_t
*p_aout
)
579 if( !p_aout
->output
.p_sys
->b_playing
)
581 aout_buffer_t
*p_buffer
;
583 p_aout
->output
.p_sys
->b_playing
= 1;
585 /* get the playing date of the first aout buffer */
586 p_aout
->output
.p_sys
->p_notif
->start_date
=
587 aout_FifoFirstDate( p_aout
, &p_aout
->output
.fifo
);
589 /* fill in the first samples */
590 for( int i
= 0; i
< FRAMES_NUM
; i
++ )
592 p_buffer
= aout_FifoPop( p_aout
, &p_aout
->output
.fifo
);
593 if( !p_buffer
) break;
594 FillBuffer( p_aout
, i
, p_buffer
);
597 /* wake up the audio output thread */
598 SetEvent( p_aout
->output
.p_sys
->p_notif
->event
);
602 /*****************************************************************************
603 * CloseAudio: close the audio device
604 *****************************************************************************/
605 static void CloseAudio( vlc_object_t
*p_this
)
607 aout_instance_t
* p_aout
= (aout_instance_t
*)p_this
;
608 aout_sys_t
*p_sys
= p_aout
->output
.p_sys
;
610 msg_Dbg( p_aout
, "closing audio device" );
612 /* kill the position notification thread, if any */
615 vlc_object_kill( p_sys
->p_notif
);
616 /* wake up the audio thread if needed */
617 if( !p_sys
->b_playing
) SetEvent( p_sys
->p_notif
->event
);
619 vlc_thread_join( p_sys
->p_notif
);
620 vlc_object_release( p_sys
->p_notif
);
623 /* release the secondary buffer */
624 DestroyDSBuffer( p_aout
);
626 /* finally release the DirectSound object */
627 if( p_sys
->p_dsobject
) IDirectSound_Release( p_sys
->p_dsobject
);
629 /* free DSOUND.DLL */
630 if( p_sys
->hdsound_dll
) FreeLibrary( p_sys
->hdsound_dll
);
632 free( p_aout
->output
.p_sys
->p_device_guid
);
636 /*****************************************************************************
637 * CallBackDirectSoundEnum: callback to enumerate available devices
638 *****************************************************************************/
639 static int CALLBACK
CallBackDirectSoundEnum( LPGUID p_guid
, LPCWSTR psz_desc
,
640 LPCWSTR psz_mod
, LPVOID _p_aout
)
642 VLC_UNUSED( psz_mod
);
644 aout_instance_t
*p_aout
= (aout_instance_t
*)_p_aout
;
646 char *psz_device
= FromWide( psz_desc
);
647 msg_Dbg( p_aout
, "found device: %s", psz_device
);
649 if( p_aout
->output
.p_sys
->psz_device
&&
650 !strcmp(p_aout
->output
.p_sys
->psz_device
, psz_device
) && p_guid
)
652 /* Use the device corresponding to psz_device */
653 p_aout
->output
.p_sys
->p_device_guid
= malloc( sizeof( GUID
) );
654 *p_aout
->output
.p_sys
->p_device_guid
= *p_guid
;
655 msg_Dbg( p_aout
, "using device: %s", psz_device
);
659 /* If no default device has been selected, chose the first one */
660 if( !p_aout
->output
.p_sys
->psz_device
&& p_guid
)
662 p_aout
->output
.p_sys
->psz_device
= strdup( psz_device
);
663 p_aout
->output
.p_sys
->p_device_guid
= malloc( sizeof( GUID
) );
664 *p_aout
->output
.p_sys
->p_device_guid
= *p_guid
;
665 msg_Dbg( p_aout
, "using device: %s", psz_device
);
673 /*****************************************************************************
674 * InitDirectSound: handle all the gory details of DirectSound initialisation
675 *****************************************************************************/
676 static int InitDirectSound( aout_instance_t
*p_aout
)
678 HRESULT (WINAPI
*OurDirectSoundCreate
)(LPGUID
, LPDIRECTSOUND
*, LPUNKNOWN
);
679 HRESULT (WINAPI
*OurDirectSoundEnumerate
)(LPDSENUMCALLBACKW
, LPVOID
);
681 p_aout
->output
.p_sys
->hdsound_dll
= LoadLibrary("DSOUND.DLL");
682 if( p_aout
->output
.p_sys
->hdsound_dll
== NULL
)
684 msg_Warn( p_aout
, "cannot open DSOUND.DLL" );
688 OurDirectSoundCreate
= (void *)
689 GetProcAddress( p_aout
->output
.p_sys
->hdsound_dll
,
690 "DirectSoundCreate" );
691 if( OurDirectSoundCreate
== NULL
)
693 msg_Warn( p_aout
, "GetProcAddress FAILED" );
697 /* Get DirectSoundEnumerate */
698 OurDirectSoundEnumerate
= (void *)
699 GetProcAddress( p_aout
->output
.p_sys
->hdsound_dll
,
700 "DirectSoundEnumerateW" );
701 if( OurDirectSoundEnumerate
)
703 p_aout
->output
.p_sys
->psz_device
= var_InheritString(p_aout
, "directx-audio-device-name");
704 /* Attempt enumeration */
705 if( FAILED( OurDirectSoundEnumerate( CallBackDirectSoundEnum
,
708 msg_Dbg( p_aout
, "enumeration of DirectSound devices failed" );
712 /* Create the direct sound object */
713 if FAILED( OurDirectSoundCreate( p_aout
->output
.p_sys
->p_device_guid
,
714 &p_aout
->output
.p_sys
->p_dsobject
,
717 msg_Warn( p_aout
, "cannot create a direct sound device" );
721 /* Set DirectSound Cooperative level, ie what control we want over Windows
722 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
723 * settings of the primary buffer, but also that only the sound of our
724 * application will be hearable when it will have the focus.
725 * !!! (this is not really working as intended yet because to set the
726 * cooperative level you need the window handle of your application, and
727 * I don't know of any easy way to get it. Especially since we might play
728 * sound without any video, and so what window handle should we use ???
729 * The hack for now is to use the Desktop window handle - it seems to be
731 if( IDirectSound_SetCooperativeLevel( p_aout
->output
.p_sys
->p_dsobject
,
735 msg_Warn( p_aout
, "cannot set direct sound cooperative level" );
741 p_aout
->output
.p_sys
->p_dsobject
= NULL
;
742 if( p_aout
->output
.p_sys
->hdsound_dll
)
744 FreeLibrary( p_aout
->output
.p_sys
->hdsound_dll
);
745 p_aout
->output
.p_sys
->hdsound_dll
= NULL
;
751 /*****************************************************************************
752 * CreateDSBuffer: Creates a direct sound buffer of the required format.
753 *****************************************************************************
754 * This function creates the buffer we'll use to play audio.
755 * In DirectSound there are two kinds of buffers:
756 * - the primary buffer: which is the actual buffer that the soundcard plays
757 * - the secondary buffer(s): these buffers are the one actually used by
758 * applications and DirectSound takes care of mixing them into the primary.
760 * Once you create a secondary buffer, you cannot change its format anymore so
761 * you have to release the current one and create another.
762 *****************************************************************************/
763 static int CreateDSBuffer( aout_instance_t
*p_aout
, int i_format
,
764 int i_channels
, int i_nb_channels
, int i_rate
,
765 int i_bytes_per_frame
, bool b_probe
)
767 WAVEFORMATEXTENSIBLE waveformat
;
768 DSBUFFERDESC dsbdesc
;
771 /* First set the sound buffer format */
772 waveformat
.dwChannelMask
= 0;
773 for( i
= 0; i
< sizeof(pi_channels_src
)/sizeof(uint32_t); i
++ )
775 if( i_channels
& pi_channels_src
[i
] )
776 waveformat
.dwChannelMask
|= pi_channels_in
[i
];
781 case VLC_CODEC_SPDIFL
:
783 /* To prevent channel re-ordering */
784 waveformat
.dwChannelMask
= SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
;
785 waveformat
.Format
.wBitsPerSample
= 16;
786 waveformat
.Samples
.wValidBitsPerSample
=
787 waveformat
.Format
.wBitsPerSample
;
788 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_DOLBY_AC3_SPDIF
;
789 waveformat
.SubFormat
= _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF
;
793 waveformat
.Format
.wBitsPerSample
= sizeof(float) * 8;
794 waveformat
.Samples
.wValidBitsPerSample
=
795 waveformat
.Format
.wBitsPerSample
;
796 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_IEEE_FLOAT
;
797 waveformat
.SubFormat
= _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
801 waveformat
.Format
.wBitsPerSample
= 16;
802 waveformat
.Samples
.wValidBitsPerSample
=
803 waveformat
.Format
.wBitsPerSample
;
804 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
805 waveformat
.SubFormat
= _KSDATAFORMAT_SUBTYPE_PCM
;
809 waveformat
.Format
.nChannels
= i_nb_channels
;
810 waveformat
.Format
.nSamplesPerSec
= i_rate
;
811 waveformat
.Format
.nBlockAlign
=
812 waveformat
.Format
.wBitsPerSample
/ 8 * i_nb_channels
;
813 waveformat
.Format
.nAvgBytesPerSec
=
814 waveformat
.Format
.nSamplesPerSec
* waveformat
.Format
.nBlockAlign
;
816 p_aout
->output
.p_sys
->i_bits_per_sample
= waveformat
.Format
.wBitsPerSample
;
817 p_aout
->output
.p_sys
->i_channels
= i_nb_channels
;
819 /* Then fill in the direct sound descriptor */
820 memset(&dsbdesc
, 0, sizeof(DSBUFFERDESC
));
821 dsbdesc
.dwSize
= sizeof(DSBUFFERDESC
);
822 dsbdesc
.dwFlags
= DSBCAPS_GETCURRENTPOSITION2
/* Better position accuracy */
823 | DSBCAPS_GLOBALFOCUS
; /* Allows background playing */
825 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
826 if( i_nb_channels
<= 2 )
828 waveformat
.Format
.cbSize
= 0;
832 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
833 waveformat
.Format
.cbSize
=
834 sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
836 /* Needed for 5.1 on emu101k */
837 dsbdesc
.dwFlags
|= DSBCAPS_LOCHARDWARE
;
840 dsbdesc
.dwBufferBytes
= FRAMES_NUM
* i_bytes_per_frame
; /* buffer size */
841 dsbdesc
.lpwfxFormat
= (WAVEFORMATEX
*)&waveformat
;
843 if FAILED( IDirectSound_CreateSoundBuffer(
844 p_aout
->output
.p_sys
->p_dsobject
, &dsbdesc
,
845 &p_aout
->output
.p_sys
->p_dsbuffer
, NULL
) )
847 if( dsbdesc
.dwFlags
& DSBCAPS_LOCHARDWARE
)
849 /* Try without DSBCAPS_LOCHARDWARE */
850 dsbdesc
.dwFlags
&= ~DSBCAPS_LOCHARDWARE
;
851 if FAILED( IDirectSound_CreateSoundBuffer(
852 p_aout
->output
.p_sys
->p_dsobject
, &dsbdesc
,
853 &p_aout
->output
.p_sys
->p_dsbuffer
, NULL
) )
858 msg_Dbg( p_aout
, "couldn't use hardware sound buffer" );
866 /* Stop here if we were just probing */
869 IDirectSoundBuffer_Release( p_aout
->output
.p_sys
->p_dsbuffer
);
870 p_aout
->output
.p_sys
->p_dsbuffer
= NULL
;
874 p_aout
->output
.p_sys
->i_frame_size
= i_bytes_per_frame
;
875 p_aout
->output
.p_sys
->i_channel_mask
= waveformat
.dwChannelMask
;
876 p_aout
->output
.p_sys
->b_chan_reorder
=
877 aout_CheckChannelReorder( pi_channels_in
, pi_channels_out
,
878 waveformat
.dwChannelMask
, i_nb_channels
,
879 p_aout
->output
.p_sys
->pi_chan_table
);
881 if( p_aout
->output
.p_sys
->b_chan_reorder
)
883 msg_Dbg( p_aout
, "channel reordering needed" );
889 /*****************************************************************************
890 * CreateDSBufferPCM: creates a PCM direct sound buffer.
891 *****************************************************************************
892 * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
893 * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
894 ****************************************************************************/
895 static int CreateDSBufferPCM( aout_instance_t
*p_aout
, vlc_fourcc_t
*i_format
,
896 int i_channels
, int i_nb_channels
, int i_rate
,
899 /* Float32 audio samples are not supported for 5.1 output on the emu101k */
900 if( !var_GetBool( p_aout
, "directx-audio-float32" ) ||
902 CreateDSBuffer( p_aout
, VLC_CODEC_FL32
,
903 i_channels
, i_nb_channels
, i_rate
,
904 FRAME_SIZE
* 4 * i_nb_channels
, b_probe
)
907 if ( CreateDSBuffer( p_aout
, VLC_CODEC_S16L
,
908 i_channels
, i_nb_channels
, i_rate
,
909 FRAME_SIZE
* 2 * i_nb_channels
, b_probe
)
916 *i_format
= VLC_CODEC_S16L
;
922 *i_format
= VLC_CODEC_FL32
;
927 /*****************************************************************************
929 *****************************************************************************
930 * This function destroys the secondary buffer.
931 *****************************************************************************/
932 static void DestroyDSBuffer( aout_instance_t
*p_aout
)
934 if( p_aout
->output
.p_sys
->p_dsbuffer
)
936 IDirectSoundBuffer_Release( p_aout
->output
.p_sys
->p_dsbuffer
);
937 p_aout
->output
.p_sys
->p_dsbuffer
= NULL
;
941 /*****************************************************************************
942 * FillBuffer: Fill in one of the direct sound frame buffers.
943 *****************************************************************************
944 * Returns VLC_SUCCESS on success.
945 *****************************************************************************/
946 static int FillBuffer( aout_instance_t
*p_aout
, int i_frame
,
947 aout_buffer_t
*p_buffer
)
949 notification_thread_t
*p_notif
= p_aout
->output
.p_sys
->p_notif
;
950 aout_sys_t
*p_sys
= p_aout
->output
.p_sys
;
951 void *p_write_position
, *p_wrap_around
;
952 unsigned long l_bytes1
, l_bytes2
;
955 /* Before copying anything, we have to lock the buffer */
956 dsresult
= IDirectSoundBuffer_Lock(
957 p_sys
->p_dsbuffer
, /* DS buffer */
958 i_frame
* p_notif
->i_frame_size
, /* Start offset */
959 p_notif
->i_frame_size
, /* Number of bytes */
960 &p_write_position
, /* Address of lock start */
961 &l_bytes1
, /* Count of bytes locked before wrap around */
962 &p_wrap_around
, /* Buffer address (if wrap around) */
963 &l_bytes2
, /* Count of bytes after wrap around */
965 if( dsresult
== DSERR_BUFFERLOST
)
967 IDirectSoundBuffer_Restore( p_sys
->p_dsbuffer
);
968 dsresult
= IDirectSoundBuffer_Lock(
970 i_frame
* p_notif
->i_frame_size
,
971 p_notif
->i_frame_size
,
978 if( dsresult
!= DS_OK
)
980 msg_Warn( p_notif
, "cannot lock buffer" );
981 if( p_buffer
) aout_BufferFree( p_buffer
);
985 if( p_buffer
== NULL
)
987 memset( p_write_position
, 0, l_bytes1
);
991 if( p_sys
->b_chan_reorder
)
993 /* Do the channel reordering here */
994 aout_ChannelReorder( p_buffer
->p_buffer
, p_buffer
->i_buffer
,
995 p_sys
->i_channels
, p_sys
->pi_chan_table
,
996 p_sys
->i_bits_per_sample
);
999 vlc_memcpy( p_write_position
, p_buffer
->p_buffer
, l_bytes1
);
1000 aout_BufferFree( p_buffer
);
1003 /* Now the data has been copied, unlock the buffer */
1004 IDirectSoundBuffer_Unlock( p_sys
->p_dsbuffer
, p_write_position
, l_bytes1
,
1005 p_wrap_around
, l_bytes2
);
1007 p_notif
->i_write_slot
= (i_frame
+ 1) % FRAMES_NUM
;
1011 /*****************************************************************************
1012 * DirectSoundThread: this thread will capture play notification events.
1013 *****************************************************************************
1014 * We use this thread to emulate a callback mechanism. The thread probes for
1015 * event notification and fills up the DS secondary buffer when needed.
1016 *****************************************************************************/
1017 static void* DirectSoundThread( vlc_object_t
*p_this
)
1019 notification_thread_t
*p_notif
= (notification_thread_t
*)p_this
;
1020 aout_instance_t
*p_aout
= p_notif
->p_aout
;
1022 int canc
= vlc_savecancel ();
1024 /* We don't want any resampling when using S/PDIF output */
1025 bool b_sleek
= (p_aout
->output
.output
.i_format
== VLC_CODEC_SPDIFL
);
1027 msg_Dbg( p_notif
, "DirectSoundThread ready" );
1029 /* Wait here until Play() is called */
1030 WaitForSingleObject( p_notif
->event
, INFINITE
);
1032 if( vlc_object_alive (p_notif
) )
1035 mwait( p_notif
->start_date
- AOUT_PTS_TOLERANCE
/ 2 );
1037 /* start playing the buffer */
1038 dsresult
= IDirectSoundBuffer_Play( p_aout
->output
.p_sys
->p_dsbuffer
,
1041 DSBPLAY_LOOPING
); /* Flags */
1042 if( dsresult
== DSERR_BUFFERLOST
)
1044 IDirectSoundBuffer_Restore( p_aout
->output
.p_sys
->p_dsbuffer
);
1045 dsresult
= IDirectSoundBuffer_Play(
1046 p_aout
->output
.p_sys
->p_dsbuffer
,
1049 DSBPLAY_LOOPING
); /* Flags */
1051 if( dsresult
!= DS_OK
)
1053 msg_Err( p_aout
, "cannot start playing buffer" );
1056 last_time
= mdate();
1058 while( vlc_object_alive (p_notif
) )
1061 int l_queued
= 0, l_free_slots
;
1062 unsigned i_frame_siz
= p_aout
->output
.i_nb_samples
;
1063 mtime_t mtime
= mdate();
1067 * Fill in as much audio data as we can in our circular buffer
1070 /* Find out current play position */
1071 if FAILED( IDirectSoundBuffer_GetCurrentPosition(
1072 p_aout
->output
.p_sys
->p_dsbuffer
, &l_read
, NULL
) )
1074 msg_Err( p_aout
, "GetCurrentPosition() failed!" );
1078 /* Detect underruns */
1079 if( l_queued
&& mtime
- last_time
>
1080 INT64_C(1000000) * l_queued
/ p_aout
->output
.output
.i_rate
)
1082 msg_Dbg( p_aout
, "detected underrun!" );
1086 /* Try to fill in as many frame buffers as possible */
1087 l_read
/= (p_aout
->output
.output
.i_bytes_per_frame
/
1088 p_aout
->output
.output
.i_frame_length
);
1089 l_queued
= p_notif
->i_write_slot
* i_frame_siz
- l_read
;
1090 if( l_queued
< 0 ) l_queued
+= (i_frame_siz
* FRAMES_NUM
);
1091 l_free_slots
= (FRAMES_NUM
* i_frame_siz
- l_queued
) / i_frame_siz
;
1093 for( i
= 0; i
< l_free_slots
; i
++ )
1095 aout_buffer_t
*p_buffer
= aout_OutputNextBuffer( p_aout
,
1096 mtime
+ INT64_C(1000000) * (i
* i_frame_siz
+ l_queued
) /
1097 p_aout
->output
.output
.i_rate
, b_sleek
);
1099 /* If there is no audio data available and we have some buffered
1100 * already, then just wait for the next time */
1101 if( !p_buffer
&& (i
|| l_queued
/ i_frame_siz
) ) break;
1103 if( FillBuffer( p_aout
, p_notif
->i_write_slot
% FRAMES_NUM
,
1104 p_buffer
) != VLC_SUCCESS
) break;
1107 /* Sleep a reasonable amount of time */
1108 l_queued
+= (i
* i_frame_siz
);
1109 msleep( INT64_C(1000000) * l_queued
/ p_aout
->output
.output
.i_rate
/ 2 );
1112 /* make sure the buffer isn't playing */
1113 IDirectSoundBuffer_Stop( p_aout
->output
.p_sys
->p_dsbuffer
);
1115 /* free the event */
1116 CloseHandle( p_notif
->event
);
1118 vlc_restorecancel (canc
);
1119 msg_Dbg( p_notif
, "DirectSoundThread exiting" );
1123 /*****************************************************************************
1124 * CallBackConfigNBEnum: callback to get the number of available devices
1125 *****************************************************************************/
1126 static int CALLBACK
CallBackConfigNBEnum( LPGUID p_guid
, LPCWSTR psz_desc
,
1127 LPCWSTR psz_mod
, LPVOID p_nb
)
1129 VLC_UNUSED( psz_mod
); VLC_UNUSED( psz_desc
); VLC_UNUSED( p_guid
);
1131 int * a
= (int *)p_nb
;
1136 /*****************************************************************************
1137 * CallBackConfigEnum: callback to add available devices to the preferences list
1138 *****************************************************************************/
1139 static int CALLBACK
CallBackConfigEnum( LPGUID p_guid
, LPCWSTR psz_desc
,
1140 LPCWSTR psz_mod
, LPVOID _p_item
)
1142 VLC_UNUSED( psz_mod
); VLC_UNUSED( p_guid
);
1144 module_config_t
*p_item
= (module_config_t
*) _p_item
;
1146 p_item
->ppsz_list
[p_item
->i_list
] = FromWide( psz_desc
);
1147 p_item
->ppsz_list_text
[p_item
->i_list
] = FromWide( psz_desc
);
1152 /*****************************************************************************
1153 * ReloadDirectXDevices: store the list of devices in preferences
1154 *****************************************************************************/
1155 static int ReloadDirectXDevices( vlc_object_t
*p_this
, char const *psz_name
,
1156 vlc_value_t newval
, vlc_value_t oldval
, void *data
)
1158 VLC_UNUSED( newval
); VLC_UNUSED( oldval
); VLC_UNUSED( data
);
1160 module_config_t
*p_item
= config_FindConfig( p_this
, psz_name
);
1161 if( !p_item
) return VLC_SUCCESS
;
1163 /* Clear-up the current list */
1164 if( p_item
->i_list
)
1166 for( int i
= 0; i
< p_item
->i_list
; i
++ )
1168 free((char *)(p_item
->ppsz_list
[i
]) );
1169 free((char *)(p_item
->ppsz_list_text
[i
]) );
1173 HRESULT (WINAPI
*OurDirectSoundEnumerate
)(LPDSENUMCALLBACKW
, LPVOID
);
1175 HANDLE hdsound_dll
= LoadLibrary("DSOUND.DLL");
1176 if( hdsound_dll
== NULL
)
1178 msg_Warn( p_this
, "cannot open DSOUND.DLL" );
1182 /* Get DirectSoundEnumerate */
1183 OurDirectSoundEnumerate
= (void *)
1184 GetProcAddress( hdsound_dll
, "DirectSoundEnumerateW" );
1186 if( OurDirectSoundEnumerate
== NULL
)
1190 OurDirectSoundEnumerate(CallBackConfigNBEnum
, &nb_devices
);
1191 msg_Dbg(p_this
,"found %d devices", nb_devices
);
1193 p_item
->ppsz_list
= xrealloc( p_item
->ppsz_list
,
1194 nb_devices
* sizeof(char *) );
1195 p_item
->ppsz_list_text
= xrealloc( p_item
->ppsz_list_text
,
1196 nb_devices
* sizeof(char *) );
1199 OurDirectSoundEnumerate(CallBackConfigEnum
, p_item
);
1201 /* Signal change to the interface */
1202 p_item
->b_dirty
= true;
1205 FreeLibrary(hdsound_dll
);