1 /*****************************************************************************
2 * directx.c: Windows DirectX audio output method
3 *****************************************************************************
4 * Copyright (C) 2001 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
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
36 #define FRAME_SIZE ((int)p_aout->output.output.i_rate/20) /* Size in samples */
37 #define FRAMES_NUM 8 /* Needs to be > 3 */
39 /*****************************************************************************
41 * Defining them here allows us to get rid of the dxguid library during
43 *****************************************************************************/
46 /*****************************************************************************
48 *****************************************************************************/
49 #ifndef WAVE_FORMAT_IEEE_FLOAT
50 # define WAVE_FORMAT_IEEE_FLOAT 0x0003
53 #ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
54 # define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
57 #ifndef WAVE_FORMAT_EXTENSIBLE
58 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
61 #ifndef SPEAKER_FRONT_LEFT
62 # define SPEAKER_FRONT_LEFT 0x1
63 # define SPEAKER_FRONT_RIGHT 0x2
64 # define SPEAKER_FRONT_CENTER 0x4
65 # define SPEAKER_LOW_FREQUENCY 0x8
66 # define SPEAKER_BACK_LEFT 0x10
67 # define SPEAKER_BACK_RIGHT 0x20
68 # define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
69 # define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
70 # define SPEAKER_BACK_CENTER 0x100
71 # define SPEAKER_SIDE_LEFT 0x200
72 # define SPEAKER_SIDE_RIGHT 0x400
73 # define SPEAKER_TOP_CENTER 0x800
74 # define SPEAKER_TOP_FRONT_LEFT 0x1000
75 # define SPEAKER_TOP_FRONT_CENTER 0x2000
76 # define SPEAKER_TOP_FRONT_RIGHT 0x4000
77 # define SPEAKER_TOP_BACK_LEFT 0x8000
78 # define SPEAKER_TOP_BACK_CENTER 0x10000
79 # define SPEAKER_TOP_BACK_RIGHT 0x20000
80 # define SPEAKER_RESERVED 0x80000000
83 #ifndef DSSPEAKER_HEADPHONE
84 # define DSSPEAKER_HEADPHONE 0x00000001
86 #ifndef DSSPEAKER_MONO
87 # define DSSPEAKER_MONO 0x00000002
89 #ifndef DSSPEAKER_QUAD
90 # define DSSPEAKER_QUAD 0x00000003
92 #ifndef DSSPEAKER_STEREO
93 # define DSSPEAKER_STEREO 0x00000004
95 #ifndef DSSPEAKER_SURROUND
96 # define DSSPEAKER_SURROUND 0x00000005
98 #ifndef DSSPEAKER_5POINT1
99 # define DSSPEAKER_5POINT1 0x00000006
102 #ifndef _WAVEFORMATEXTENSIBLE_
106 WORD wValidBitsPerSample; /* bits of precision */
107 WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
108 WORD wReserved; /* If neither applies, set to zero. */
110 DWORD dwChannelMask; /* which channels are */
111 /* present in stream */
113 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
116 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
117 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
118 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
120 /*****************************************************************************
121 * notification_thread_t: DirectX event thread
122 *****************************************************************************/
123 typedef struct notification_thread_t
127 aout_instance_t *p_aout;
128 int i_frame_size; /* size in bytes of one frame */
129 int i_write_slot; /* current write position in our circular buffer */
134 } notification_thread_t;
136 /*****************************************************************************
137 * aout_sys_t: directx audio output method descriptor
138 *****************************************************************************
139 * This structure is part of the audio output thread descriptor.
140 * It describes the direct sound specific properties of an audio device.
141 *****************************************************************************/
144 HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
146 int i_device_id; /* user defined device */
147 LPGUID p_device_guid;
149 LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
150 LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
151 * takes care of mixing all the
152 * secondary buffers into the primary) */
154 notification_thread_t *p_notif; /* DirectSoundThread id */
156 int b_playing; /* playing status */
158 int i_frame_size; /* Size in bytes of one frame */
160 vlc_bool_t b_chan_reorder; /* do we need channel reordering */
161 int pi_chan_table[AOUT_CHAN_MAX];
162 uint32_t i_channel_mask;
163 uint32_t i_bits_per_sample;
167 static const uint32_t pi_channels_src[] =
168 { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
169 AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
170 AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
171 AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 };
172 static const uint32_t pi_channels_in[] =
173 { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
174 SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT,
175 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
176 SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, 0 };
177 static const uint32_t pi_channels_out[] =
178 { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
179 SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY,
180 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
181 SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, 0 };
183 /*****************************************************************************
185 *****************************************************************************/
186 static int OpenAudio ( vlc_object_t * );
187 static void CloseAudio ( vlc_object_t * );
188 static void Play ( aout_instance_t * );
190 /* local functions */
191 static void Probe ( aout_instance_t * );
192 static int InitDirectSound ( aout_instance_t * );
193 static int CreateDSBuffer ( aout_instance_t *, int, int, int, int, int, vlc_bool_t );
194 static int CreateDSBufferPCM ( aout_instance_t *, int*, int, int, int, vlc_bool_t );
195 static void DestroyDSBuffer ( aout_instance_t * );
196 static void DirectSoundThread ( notification_thread_t * );
197 static int FillBuffer ( aout_instance_t *, int, aout_buffer_t * );
199 /*****************************************************************************
201 *****************************************************************************/
202 #define DEVICE_TEXT N_("Output device")
203 #define DEVICE_LONGTEXT N_( \
204 "DirectX device number: 0 default device, 1..N device by number" \
205 "(Note that the default device appears as 0 AND another number)." )
206 #define FLOAT_TEXT N_("Use float32 output")
207 #define FLOAT_LONGTEXT N_( \
208 "The option allows you to enable or disable the high-quality float32 " \
209 "audio output mode (which is not well supported by some soundcards)." )
212 set_description( _("DirectX audio output") );
213 set_shortname( "DirectX" );
214 set_capability( "audio output", 100 );
215 set_category( CAT_AUDIO );
216 set_subcategory( SUBCAT_AUDIO_AOUT );
217 add_shortcut( "directx" );
218 add_integer( "directx-audio-device", 0, NULL, DEVICE_TEXT,
219 DEVICE_LONGTEXT, VLC_TRUE );
220 add_bool( "directx-audio-float32", 0, 0, FLOAT_TEXT,
221 FLOAT_LONGTEXT, VLC_TRUE );
222 set_callbacks( OpenAudio, CloseAudio );
225 /*****************************************************************************
226 * OpenAudio: open the audio device
227 *****************************************************************************
228 * This function opens and setups Direct Sound.
229 *****************************************************************************/
230 static int OpenAudio( vlc_object_t *p_this )
232 aout_instance_t * p_aout = (aout_instance_t *)p_this;
235 msg_Dbg( p_aout, "OpenAudio" );
237 /* Allocate structure */
238 p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
239 if( p_aout->output.p_sys == NULL )
241 msg_Err( p_aout, "out of memory" );
245 /* Initialize some variables */
246 p_aout->output.p_sys->p_dsobject = NULL;
247 p_aout->output.p_sys->p_dsbuffer = NULL;
248 p_aout->output.p_sys->p_notif = NULL;
249 p_aout->output.p_sys->b_playing = 0;
251 p_aout->output.pf_play = Play;
252 aout_VolumeSoftInit( p_aout );
254 /* Retrieve config values */
255 var_Create( p_aout, "directx-audio-float32",
256 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
257 var_Create( p_aout, "directx-audio-device",
258 VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
259 var_Get( p_aout, "directx-audio-device", &val );
260 p_aout->output.p_sys->i_device_id = val.i_int;
261 p_aout->output.p_sys->p_device_guid = 0;
263 /* Initialise DirectSound */
264 if( InitDirectSound( p_aout ) )
266 msg_Err( p_aout, "cannot initialize DirectSound" );
270 if( var_Type( p_aout, "audio-device" ) == 0 )
275 if( var_Get( p_aout, "audio-device", &val ) < 0 )
277 /* Probe() has failed. */
281 /* Open the device */
282 if( val.i_int == AOUT_VAR_SPDIF )
284 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
286 /* Calculate the frame size in bytes */
287 p_aout->output.i_nb_samples = A52_FRAME_NB;
288 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
289 p_aout->output.output.i_frame_length = A52_FRAME_NB;
290 p_aout->output.p_sys->i_frame_size =
291 p_aout->output.output.i_bytes_per_frame;
293 if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
294 p_aout->output.output.i_physical_channels,
295 aout_FormatNbChannels( &p_aout->output.output ),
296 p_aout->output.output.i_rate,
297 p_aout->output.p_sys->i_frame_size, VLC_FALSE )
300 msg_Err( p_aout, "cannot open directx audio device" );
301 free( p_aout->output.p_sys );
305 aout_VolumeNoneInit( p_aout );
309 if( val.i_int == AOUT_VAR_5_1 )
311 p_aout->output.output.i_physical_channels
312 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
313 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
316 else if( val.i_int == AOUT_VAR_3F2R )
318 p_aout->output.output.i_physical_channels
319 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
320 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
322 else if( val.i_int == AOUT_VAR_2F2R )
324 p_aout->output.output.i_physical_channels
325 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
326 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
328 else if( val.i_int == AOUT_VAR_MONO )
330 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
334 p_aout->output.output.i_physical_channels
335 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
338 if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format,
339 p_aout->output.output.i_physical_channels,
340 aout_FormatNbChannels( &p_aout->output.output ),
341 p_aout->output.output.i_rate, VLC_FALSE )
344 msg_Err( p_aout, "cannot open directx audio device" );
345 free( p_aout->output.p_sys );
349 /* Calculate the frame size in bytes */
350 p_aout->output.i_nb_samples = FRAME_SIZE;
351 aout_FormatPrepare( &p_aout->output.output );
352 aout_VolumeSoftInit( p_aout );
355 /* Now we need to setup our DirectSound play notification structure */
356 p_aout->output.p_sys->p_notif =
357 vlc_object_create( p_aout, sizeof(notification_thread_t) );
358 p_aout->output.p_sys->p_notif->p_aout = p_aout;
360 p_aout->output.p_sys->p_notif->event = CreateEvent( 0, FALSE, FALSE, 0 );
361 p_aout->output.p_sys->p_notif->i_frame_size =
362 p_aout->output.p_sys->i_frame_size;
364 /* then launch the notification thread */
365 msg_Dbg( p_aout, "creating DirectSoundThread" );
366 if( vlc_thread_create( p_aout->output.p_sys->p_notif,
367 "DirectSound Notification Thread",
369 VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
371 msg_Err( p_aout, "cannot create DirectSoundThread" );
372 CloseHandle( p_aout->output.p_sys->p_notif->event );
373 vlc_object_destroy( p_aout->output.p_sys->p_notif );
374 p_aout->output.p_sys->p_notif = NULL;
378 vlc_object_attach( p_aout->output.p_sys->p_notif, p_aout );
383 CloseAudio( VLC_OBJECT(p_aout) );
387 /*****************************************************************************
388 * Probe: probe the audio device for available formats and channels
389 *****************************************************************************/
390 static void Probe( aout_instance_t * p_aout )
392 vlc_value_t val, text;
394 unsigned int i_physical_channels;
395 DWORD ui_speaker_config;
397 var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
398 text.psz_string = _("Audio Device");
399 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
401 /* Test for 5.1 support */
402 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
403 AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
404 AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
405 if( p_aout->output.output.i_physical_channels == i_physical_channels )
407 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 6,
408 p_aout->output.output.i_rate, VLC_TRUE )
411 val.i_int = AOUT_VAR_5_1;
412 text.psz_string = "5.1";
413 var_Change( p_aout, "audio-device",
414 VLC_VAR_ADDCHOICE, &val, &text );
415 msg_Dbg( p_aout, "device supports 5.1 channels" );
419 /* Test for 3 Front 2 Rear support */
420 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
421 AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
423 if( p_aout->output.output.i_physical_channels == i_physical_channels )
425 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 5,
426 p_aout->output.output.i_rate, VLC_TRUE )
429 val.i_int = AOUT_VAR_3F2R;
430 text.psz_string = N_("3 Front 2 Rear");
431 var_Change( p_aout, "audio-device",
432 VLC_VAR_ADDCHOICE, &val, &text );
433 msg_Dbg( p_aout, "device supports 5 channels" );
437 /* Test for 2 Front 2 Rear support */
438 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
439 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
440 if( ( p_aout->output.output.i_physical_channels & i_physical_channels )
441 == i_physical_channels )
443 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 4,
444 p_aout->output.output.i_rate, VLC_TRUE )
447 val.i_int = AOUT_VAR_2F2R;
448 text.psz_string = N_("2 Front 2 Rear");
449 var_Change( p_aout, "audio-device",
450 VLC_VAR_ADDCHOICE, &val, &text );
451 msg_Dbg( p_aout, "device supports 4 channels" );
455 /* Test for stereo support */
456 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
457 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 2,
458 p_aout->output.output.i_rate, VLC_TRUE )
461 val.i_int = AOUT_VAR_STEREO;
462 text.psz_string = N_("Stereo");
463 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
464 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
465 msg_Dbg( p_aout, "device supports 2 channels" );
468 /* Test for mono support */
469 i_physical_channels = AOUT_CHAN_CENTER;
470 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 1,
471 p_aout->output.output.i_rate, VLC_TRUE )
474 val.i_int = AOUT_VAR_MONO;
475 text.psz_string = N_("Mono");
476 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
477 msg_Dbg( p_aout, "device supports 1 channel" );
480 /* Check the speaker configuration to determine which channel config should
482 if FAILED( IDirectSound_GetSpeakerConfig( p_aout->output.p_sys->p_dsobject,
483 &ui_speaker_config ) )
485 ui_speaker_config = DSSPEAKER_STEREO;
487 switch( DSSPEAKER_CONFIG(ui_speaker_config) )
489 case DSSPEAKER_5POINT1:
490 val.i_int = AOUT_VAR_5_1;
493 val.i_int = AOUT_VAR_2F2R;
495 #if 0 /* Lots of people just get their settings wrong and complain that
496 * this is a problem with VLC so just don't ever set mono by default. */
498 val.i_int = AOUT_VAR_MONO;
501 case DSSPEAKER_SURROUND:
502 case DSSPEAKER_STEREO:
504 val.i_int = AOUT_VAR_STEREO;
507 var_Set( p_aout, "audio-device", val );
509 /* Test for SPDIF support */
510 if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
512 if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
513 p_aout->output.output.i_physical_channels,
514 aout_FormatNbChannels( &p_aout->output.output ),
515 p_aout->output.output.i_rate,
516 AOUT_SPDIF_SIZE, VLC_TRUE )
519 msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
520 val.i_int = AOUT_VAR_SPDIF;
521 text.psz_string = N_("A/52 over S/PDIF");
522 var_Change( p_aout, "audio-device",
523 VLC_VAR_ADDCHOICE, &val, &text );
524 if( config_GetInt( p_aout, "spdif" ) )
525 var_Set( p_aout, "audio-device", val );
529 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
532 /* Probe() has failed. */
533 var_Destroy( p_aout, "audio-device" );
537 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
539 val.b_bool = VLC_TRUE;
540 var_Set( p_aout, "intf-change", val );
543 /*****************************************************************************
544 * Play: we'll start playing the directsound buffer here because at least here
545 * we know the first buffer has been put in the aout fifo and we also
547 *****************************************************************************/
548 static void Play( aout_instance_t *p_aout )
550 if( !p_aout->output.p_sys->b_playing )
552 aout_buffer_t *p_buffer;
555 p_aout->output.p_sys->b_playing = 1;
557 /* get the playing date of the first aout buffer */
558 p_aout->output.p_sys->p_notif->start_date =
559 aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
561 /* fill in the first samples */
562 for( i = 0; i < FRAMES_NUM; i++ )
564 p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
565 if( !p_buffer ) break;
566 FillBuffer( p_aout, i, p_buffer );
569 /* wake up the audio output thread */
570 SetEvent( p_aout->output.p_sys->p_notif->event );
574 /*****************************************************************************
575 * CloseAudio: close the audio device
576 *****************************************************************************/
577 static void CloseAudio( vlc_object_t *p_this )
579 aout_instance_t * p_aout = (aout_instance_t *)p_this;
580 aout_sys_t *p_sys = p_aout->output.p_sys;
582 msg_Dbg( p_aout, "closing audio device" );
584 /* kill the position notification thread, if any */
587 vlc_object_detach( p_sys->p_notif );
588 vlc_object_kill( p_sys->p_notif );
589 /* wake up the audio thread if needed */
590 if( !p_sys->b_playing ) SetEvent( p_sys->p_notif->event );
592 vlc_thread_join( p_sys->p_notif );
593 vlc_object_destroy( p_sys->p_notif );
596 /* release the secondary buffer */
597 DestroyDSBuffer( p_aout );
599 /* finally release the DirectSound object */
600 if( p_sys->p_dsobject ) IDirectSound_Release( p_sys->p_dsobject );
602 /* free DSOUND.DLL */
603 if( p_sys->hdsound_dll ) FreeLibrary( p_sys->hdsound_dll );
605 free( p_aout->output.p_sys->p_device_guid );
609 /*****************************************************************************
610 * CallBackDirectSoundEnum: callback to enumerate available devices
611 *****************************************************************************/
612 static int CALLBACK CallBackDirectSoundEnum( LPGUID p_guid, LPCSTR psz_desc,
613 LPCSTR psz_mod, LPVOID _p_aout )
615 aout_instance_t *p_aout = (aout_instance_t *)_p_aout;
617 msg_Dbg( p_aout, "found device: %s", psz_desc );
619 if( p_aout->output.p_sys->i_device_id == 0 && p_guid )
621 p_aout->output.p_sys->p_device_guid = malloc( sizeof( GUID ) );
622 *p_aout->output.p_sys->p_device_guid = *p_guid;
623 msg_Dbg( p_aout, "using device: %s", psz_desc );
626 p_aout->output.p_sys->i_device_id--;
630 /*****************************************************************************
631 * InitDirectSound: handle all the gory details of DirectSound initialisation
632 *****************************************************************************/
633 static int InitDirectSound( aout_instance_t *p_aout )
635 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
636 HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACK, LPVOID);
638 p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
639 if( p_aout->output.p_sys->hdsound_dll == NULL )
641 msg_Warn( p_aout, "cannot open DSOUND.DLL" );
645 OurDirectSoundCreate = (void *)
646 GetProcAddress( p_aout->output.p_sys->hdsound_dll,
647 "DirectSoundCreate" );
648 if( OurDirectSoundCreate == NULL )
650 msg_Warn( p_aout, "GetProcAddress FAILED" );
654 /* Get DirectSoundEnumerate */
655 OurDirectSoundEnumerate = (void *)
656 GetProcAddress( p_aout->output.p_sys->hdsound_dll,
657 "DirectSoundEnumerateA" );
658 if( OurDirectSoundEnumerate )
660 /* Attempt enumeration */
661 if( FAILED( OurDirectSoundEnumerate( CallBackDirectSoundEnum,
664 msg_Dbg( p_aout, "enumeration of DirectSound devices failed" );
668 /* Create the direct sound object */
669 if FAILED( OurDirectSoundCreate( p_aout->output.p_sys->p_device_guid,
670 &p_aout->output.p_sys->p_dsobject,
673 msg_Warn( p_aout, "cannot create a direct sound device" );
677 /* Set DirectSound Cooperative level, ie what control we want over Windows
678 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
679 * settings of the primary buffer, but also that only the sound of our
680 * application will be hearable when it will have the focus.
681 * !!! (this is not really working as intended yet because to set the
682 * cooperative level you need the window handle of your application, and
683 * I don't know of any easy way to get it. Especially since we might play
684 * sound without any video, and so what window handle should we use ???
685 * The hack for now is to use the Desktop window handle - it seems to be
687 if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
691 msg_Warn( p_aout, "cannot set direct sound cooperative level" );
697 p_aout->output.p_sys->p_dsobject = NULL;
698 if( p_aout->output.p_sys->hdsound_dll )
700 FreeLibrary( p_aout->output.p_sys->hdsound_dll );
701 p_aout->output.p_sys->hdsound_dll = NULL;
707 /*****************************************************************************
708 * CreateDSBuffer: Creates a direct sound buffer of the required format.
709 *****************************************************************************
710 * This function creates the buffer we'll use to play audio.
711 * In DirectSound there are two kinds of buffers:
712 * - the primary buffer: which is the actual buffer that the soundcard plays
713 * - the secondary buffer(s): these buffers are the one actually used by
714 * applications and DirectSound takes care of mixing them into the primary.
716 * Once you create a secondary buffer, you cannot change its format anymore so
717 * you have to release the current one and create another.
718 *****************************************************************************/
719 static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
720 int i_channels, int i_nb_channels, int i_rate,
721 int i_bytes_per_frame, vlc_bool_t b_probe )
723 WAVEFORMATEXTENSIBLE waveformat;
724 DSBUFFERDESC dsbdesc;
727 /* First set the sound buffer format */
728 waveformat.dwChannelMask = 0;
729 for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ )
731 if( i_channels & pi_channels_src[i] )
732 waveformat.dwChannelMask |= pi_channels_in[i];
737 case VLC_FOURCC('s','p','d','i'):
739 /* To prevent channel re-ordering */
740 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
741 waveformat.Format.wBitsPerSample = 16;
742 waveformat.Samples.wValidBitsPerSample =
743 waveformat.Format.wBitsPerSample;
744 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
745 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
748 case VLC_FOURCC('f','l','3','2'):
749 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
750 waveformat.Samples.wValidBitsPerSample =
751 waveformat.Format.wBitsPerSample;
752 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
753 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
756 case VLC_FOURCC('s','1','6','l'):
757 waveformat.Format.wBitsPerSample = 16;
758 waveformat.Samples.wValidBitsPerSample =
759 waveformat.Format.wBitsPerSample;
760 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
761 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
765 waveformat.Format.nChannels = i_nb_channels;
766 waveformat.Format.nSamplesPerSec = i_rate;
767 waveformat.Format.nBlockAlign =
768 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
769 waveformat.Format.nAvgBytesPerSec =
770 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
772 p_aout->output.p_sys->i_bits_per_sample = waveformat.Format.wBitsPerSample;
773 p_aout->output.p_sys->i_channels = i_nb_channels;
775 /* Then fill in the direct sound descriptor */
776 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
777 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
778 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
779 | DSBCAPS_GLOBALFOCUS; /* Allows background playing */
781 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
782 if( i_nb_channels <= 2 )
784 waveformat.Format.cbSize = 0;
788 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
789 waveformat.Format.cbSize =
790 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
792 /* Needed for 5.1 on emu101k */
793 dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
796 dsbdesc.dwBufferBytes = FRAMES_NUM * i_bytes_per_frame; /* buffer size */
797 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;
799 if FAILED( IDirectSound_CreateSoundBuffer(
800 p_aout->output.p_sys->p_dsobject, &dsbdesc,
801 &p_aout->output.p_sys->p_dsbuffer, NULL) )
803 if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE )
805 /* Try without DSBCAPS_LOCHARDWARE */
806 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
807 if FAILED( IDirectSound_CreateSoundBuffer(
808 p_aout->output.p_sys->p_dsobject, &dsbdesc,
809 &p_aout->output.p_sys->p_dsbuffer, NULL) )
814 msg_Dbg( p_aout, "couldn't use hardware sound buffer" );
822 /* Stop here if we were just probing */
825 IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
826 p_aout->output.p_sys->p_dsbuffer = NULL;
830 p_aout->output.p_sys->i_frame_size = i_bytes_per_frame;
831 p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask;
832 p_aout->output.p_sys->b_chan_reorder =
833 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
834 waveformat.dwChannelMask, i_nb_channels,
835 p_aout->output.p_sys->pi_chan_table );
837 if( p_aout->output.p_sys->b_chan_reorder )
839 msg_Dbg( p_aout, "channel reordering needed" );
845 /*****************************************************************************
846 * CreateDSBufferPCM: creates a PCM direct sound buffer.
847 *****************************************************************************
848 * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
849 * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
850 ****************************************************************************/
851 static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format,
852 int i_channels, int i_nb_channels, int i_rate,
857 var_Get( p_aout, "directx-audio-float32", &val );
859 /* Float32 audio samples are not supported for 5.1 output on the emu101k */
861 if( !val.b_bool || i_nb_channels > 2 ||
862 CreateDSBuffer( p_aout, VLC_FOURCC('f','l','3','2'),
863 i_channels, i_nb_channels, i_rate,
864 FRAME_SIZE * 4 * i_nb_channels, b_probe )
867 if ( CreateDSBuffer( p_aout, VLC_FOURCC('s','1','6','l'),
868 i_channels, i_nb_channels, i_rate,
869 FRAME_SIZE * 2 * i_nb_channels, b_probe )
876 *i_format = VLC_FOURCC('s','1','6','l');
882 *i_format = VLC_FOURCC('f','l','3','2');
887 /*****************************************************************************
889 *****************************************************************************
890 * This function destroys the secondary buffer.
891 *****************************************************************************/
892 static void DestroyDSBuffer( aout_instance_t *p_aout )
894 if( p_aout->output.p_sys->p_dsbuffer )
896 IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
897 p_aout->output.p_sys->p_dsbuffer = NULL;
901 /*****************************************************************************
902 * FillBuffer: Fill in one of the direct sound frame buffers.
903 *****************************************************************************
904 * Returns VLC_SUCCESS on success.
905 *****************************************************************************/
906 static int FillBuffer( aout_instance_t *p_aout, int i_frame,
907 aout_buffer_t *p_buffer )
909 notification_thread_t *p_notif = p_aout->output.p_sys->p_notif;
910 aout_sys_t *p_sys = p_aout->output.p_sys;
911 void *p_write_position, *p_wrap_around;
912 long l_bytes1, l_bytes2;
915 /* Before copying anything, we have to lock the buffer */
916 dsresult = IDirectSoundBuffer_Lock(
917 p_sys->p_dsbuffer, /* DS buffer */
918 i_frame * p_notif->i_frame_size, /* Start offset */
919 p_notif->i_frame_size, /* Number of bytes */
920 &p_write_position, /* Address of lock start */
921 &l_bytes1, /* Count of bytes locked before wrap around */
922 &p_wrap_around, /* Buffer adress (if wrap around) */
923 &l_bytes2, /* Count of bytes after wrap around */
925 if( dsresult == DSERR_BUFFERLOST )
927 IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
928 dsresult = IDirectSoundBuffer_Lock(
930 i_frame * p_notif->i_frame_size,
931 p_notif->i_frame_size,
938 if( dsresult != DS_OK )
940 msg_Warn( p_notif, "cannot lock buffer" );
941 if( p_buffer ) aout_BufferFree( p_buffer );
945 if( p_buffer == NULL )
947 memset( p_write_position, 0, l_bytes1 );
951 if( p_sys->b_chan_reorder )
953 /* Do the channel reordering here */
954 aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_nb_bytes,
955 p_sys->i_channels, p_sys->pi_chan_table,
956 p_sys->i_bits_per_sample );
959 p_aout->p_libvlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
961 aout_BufferFree( p_buffer );
964 /* Now the data has been copied, unlock the buffer */
965 IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
966 p_wrap_around, l_bytes2 );
968 p_notif->i_write_slot = (i_frame + 1) % FRAMES_NUM;
972 /*****************************************************************************
973 * DirectSoundThread: this thread will capture play notification events.
974 *****************************************************************************
975 * We use this thread to emulate a callback mechanism. The thread probes for
976 * event notification and fills up the DS secondary buffer when needed.
977 *****************************************************************************/
978 static void DirectSoundThread( notification_thread_t *p_notif )
980 aout_instance_t *p_aout = p_notif->p_aout;
986 /* We don't want any resampling when using S/PDIF output */
987 b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i');
989 /* Tell the main thread that we are ready */
990 vlc_thread_ready( p_notif );
992 msg_Dbg( p_notif, "DirectSoundThread ready" );
994 /* Wait here until Play() is called */
995 WaitForSingleObject( p_notif->event, INFINITE );
997 if( !p_notif->b_die )
999 mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 );
1001 /* start playing the buffer */
1002 dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer,
1005 DSBPLAY_LOOPING ); /* Flags */
1006 if( dsresult == DSERR_BUFFERLOST )
1008 IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
1009 dsresult = IDirectSoundBuffer_Play(
1010 p_aout->output.p_sys->p_dsbuffer,
1013 DSBPLAY_LOOPING ); /* Flags */
1015 if( dsresult != DS_OK )
1017 msg_Err( p_aout, "cannot start playing buffer" );
1020 last_time = mdate();
1022 while( !p_notif->b_die )
1024 long l_read, l_free_slots;
1025 mtime_t mtime = mdate();
1029 * Fill in as much audio data as we can in our circular buffer
1032 /* Find out current play position */
1033 if FAILED( IDirectSoundBuffer_GetCurrentPosition(
1034 p_aout->output.p_sys->p_dsbuffer, &l_read, NULL ) )
1036 msg_Err( p_aout, "GetCurrentPosition() failed!" );
1040 /* Detect underruns */
1041 if( l_queued && mtime - last_time >
1042 I64C(1000000) * l_queued / p_aout->output.output.i_rate )
1044 msg_Dbg( p_aout, "detected underrun!" );
1048 /* Try to fill in as many frame buffers as possible */
1049 l_read /= p_aout->output.output.i_bytes_per_frame;
1050 l_queued = p_notif->i_write_slot * FRAME_SIZE - l_read;
1051 if( l_queued < 0 ) l_queued += (FRAME_SIZE * FRAMES_NUM);
1052 l_free_slots = (FRAMES_NUM * FRAME_SIZE - l_queued) / FRAME_SIZE;
1054 for( i = 0; i < l_free_slots; i++ )
1056 aout_buffer_t *p_buffer = aout_OutputNextBuffer( p_aout,
1057 mtime + I64C(1000000) * (i * FRAME_SIZE + l_queued) /
1058 p_aout->output.output.i_rate, b_sleek );
1060 /* If there is no audio data available and we have some buffered
1061 * already, then just wait for the next time */
1062 if( !p_buffer && (i || l_queued / FRAME_SIZE) ) break;
1064 if( FillBuffer( p_aout, p_notif->i_write_slot % FRAMES_NUM,
1065 p_buffer ) != VLC_SUCCESS ) break;
1068 /* Sleep a reasonable amount of time */
1069 l_queued += (i * FRAME_SIZE);
1070 msleep( I64C(1000000) * l_queued / p_aout->output.output.i_rate / 2 );
1073 /* make sure the buffer isn't playing */
1074 IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer );
1076 /* free the event */
1077 CloseHandle( p_notif->event );
1079 msg_Dbg( p_notif, "DirectSoundThread exiting" );