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 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
41 #define FRAME_SIZE ((int)p_aout->output.output.i_rate/20) /* Size in samples */
42 #define FRAMES_NUM 8 /* Needs to be > 3 */
44 /*****************************************************************************
46 * Defining them here allows us to get rid of the dxguid library during
48 *****************************************************************************/
51 /*****************************************************************************
53 *****************************************************************************/
54 #ifndef WAVE_FORMAT_IEEE_FLOAT
55 # define WAVE_FORMAT_IEEE_FLOAT 0x0003
58 #ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
59 # define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
62 #ifndef WAVE_FORMAT_EXTENSIBLE
63 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
66 #ifndef SPEAKER_FRONT_LEFT
67 # define SPEAKER_FRONT_LEFT 0x1
68 # define SPEAKER_FRONT_RIGHT 0x2
69 # define SPEAKER_FRONT_CENTER 0x4
70 # define SPEAKER_LOW_FREQUENCY 0x8
71 # define SPEAKER_BACK_LEFT 0x10
72 # define SPEAKER_BACK_RIGHT 0x20
73 # define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
74 # define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
75 # define SPEAKER_BACK_CENTER 0x100
76 # define SPEAKER_SIDE_LEFT 0x200
77 # define SPEAKER_SIDE_RIGHT 0x400
78 # define SPEAKER_TOP_CENTER 0x800
79 # define SPEAKER_TOP_FRONT_LEFT 0x1000
80 # define SPEAKER_TOP_FRONT_CENTER 0x2000
81 # define SPEAKER_TOP_FRONT_RIGHT 0x4000
82 # define SPEAKER_TOP_BACK_LEFT 0x8000
83 # define SPEAKER_TOP_BACK_CENTER 0x10000
84 # define SPEAKER_TOP_BACK_RIGHT 0x20000
85 # define SPEAKER_RESERVED 0x80000000
88 #ifndef DSSPEAKER_DSSPEAKER_DIRECTOUT
89 # define DSSPEAKER_DSSPEAKER_DIRECTOUT 0x00000000
91 #ifndef DSSPEAKER_HEADPHONE
92 # define DSSPEAKER_HEADPHONE 0x00000001
94 #ifndef DSSPEAKER_MONO
95 # define DSSPEAKER_MONO 0x00000002
97 #ifndef DSSPEAKER_QUAD
98 # define DSSPEAKER_QUAD 0x00000003
100 #ifndef DSSPEAKER_STEREO
101 # define DSSPEAKER_STEREO 0x00000004
103 #ifndef DSSPEAKER_SURROUND
104 # define DSSPEAKER_SURROUND 0x00000005
106 #ifndef DSSPEAKER_5POINT1
107 # define DSSPEAKER_5POINT1 0x00000006
109 #ifndef DSSPEAKER_7POINT1
110 # define DSSPEAKER_7POINT1 0x00000007
112 #ifndef DSSPEAKER_7POINT1_SURROUND
113 # define DSSPEAKER_7POINT1_SURROUND 0x00000008
115 #ifndef DSSPEAKER_7POINT1_WIDE
116 # define DSSPEAKER_7POINT1_WIDE DSSPEAKER_7POINT1
119 #ifndef _WAVEFORMATEXTENSIBLE_
123 WORD wValidBitsPerSample; /* bits of precision */
124 WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
125 WORD wReserved; /* If neither applies, set to zero. */
127 DWORD dwChannelMask; /* which channels are */
128 /* present in stream */
130 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
133 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
134 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
135 DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
137 /*****************************************************************************
138 * notification_thread_t: DirectX event thread
139 *****************************************************************************/
140 typedef struct notification_thread_t
144 aout_instance_t *p_aout;
145 int i_frame_size; /* size in bytes of one frame */
146 int i_write_slot; /* current write position in our circular buffer */
151 } notification_thread_t;
153 /*****************************************************************************
154 * aout_sys_t: directx audio output method descriptor
155 *****************************************************************************
156 * This structure is part of the audio output thread descriptor.
157 * It describes the direct sound specific properties of an audio device.
158 *****************************************************************************/
161 HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
163 int i_device_id; /* user defined device */
164 LPGUID p_device_guid;
166 LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
167 LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
168 * takes care of mixing all the
169 * secondary buffers into the primary) */
171 notification_thread_t *p_notif; /* DirectSoundThread id */
173 int b_playing; /* playing status */
175 int i_frame_size; /* Size in bytes of one frame */
177 bool b_chan_reorder; /* do we need channel reordering */
178 int pi_chan_table[AOUT_CHAN_MAX];
179 uint32_t i_channel_mask;
180 uint32_t i_bits_per_sample;
184 static const uint32_t pi_channels_src[] =
185 { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
186 AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
187 AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
188 AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 };
189 static const uint32_t pi_channels_in[] =
190 { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
191 SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT,
192 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
193 SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, 0 };
194 static const uint32_t pi_channels_out[] =
195 { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
196 SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY,
197 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
198 SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, 0 };
200 /*****************************************************************************
202 *****************************************************************************/
203 static int OpenAudio ( vlc_object_t * );
204 static void CloseAudio ( vlc_object_t * );
205 static void Play ( aout_instance_t * );
207 /* local functions */
208 static void Probe ( aout_instance_t * );
209 static int InitDirectSound ( aout_instance_t * );
210 static int CreateDSBuffer ( aout_instance_t *, int, int, int, int, int, bool );
211 static int CreateDSBufferPCM ( aout_instance_t *, int*, int, int, int, bool );
212 static void DestroyDSBuffer ( aout_instance_t * );
213 static void DirectSoundThread ( notification_thread_t * );
214 static int FillBuffer ( aout_instance_t *, int, aout_buffer_t * );
216 /*****************************************************************************
218 *****************************************************************************/
219 #define DEVICE_TEXT N_("Output device")
220 #define DEVICE_LONGTEXT N_( \
221 "DirectX device number: 0 default device, 1..N device by number" \
222 "(Note that the default device appears as 0 AND another number)." )
223 #define FLOAT_TEXT N_("Use float32 output")
224 #define FLOAT_LONGTEXT N_( \
225 "The option allows you to enable or disable the high-quality float32 " \
226 "audio output mode (which is not well supported by some soundcards)." )
229 set_description( N_("DirectX audio output") );
230 set_shortname( "DirectX" );
231 set_capability( "audio output", 100 );
232 set_category( CAT_AUDIO );
233 set_subcategory( SUBCAT_AUDIO_AOUT );
234 add_shortcut( "directx" );
235 add_integer( "directx-audio-device", 0, NULL, DEVICE_TEXT,
236 DEVICE_LONGTEXT, true );
237 add_bool( "directx-audio-float32", 0, 0, FLOAT_TEXT,
238 FLOAT_LONGTEXT, true );
239 set_callbacks( OpenAudio, CloseAudio );
242 /*****************************************************************************
243 * OpenAudio: open the audio device
244 *****************************************************************************
245 * This function opens and setups Direct Sound.
246 *****************************************************************************/
247 static int OpenAudio( vlc_object_t *p_this )
249 aout_instance_t * p_aout = (aout_instance_t *)p_this;
252 msg_Dbg( p_aout, "OpenAudio" );
254 /* Allocate structure */
255 p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
256 if( p_aout->output.p_sys == NULL )
259 /* Initialize some variables */
260 p_aout->output.p_sys->p_dsobject = NULL;
261 p_aout->output.p_sys->p_dsbuffer = NULL;
262 p_aout->output.p_sys->p_notif = NULL;
263 p_aout->output.p_sys->b_playing = 0;
265 p_aout->output.pf_play = Play;
266 aout_VolumeSoftInit( p_aout );
268 /* Retrieve config values */
269 var_Create( p_aout, "directx-audio-float32",
270 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
271 var_Create( p_aout, "directx-audio-device",
272 VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
273 var_Get( p_aout, "directx-audio-device", &val );
274 p_aout->output.p_sys->i_device_id = val.i_int;
275 p_aout->output.p_sys->p_device_guid = 0;
277 /* Initialise DirectSound */
278 if( InitDirectSound( p_aout ) )
280 msg_Err( p_aout, "cannot initialize DirectSound" );
284 if( var_Type( p_aout, "audio-device" ) == 0 )
289 if( var_Get( p_aout, "audio-device", &val ) < 0 )
291 /* Probe() has failed. */
295 /* Open the device */
296 if( val.i_int == AOUT_VAR_SPDIF )
298 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
300 /* Calculate the frame size in bytes */
301 p_aout->output.i_nb_samples = A52_FRAME_NB;
302 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
303 p_aout->output.output.i_frame_length = A52_FRAME_NB;
304 p_aout->output.p_sys->i_frame_size =
305 p_aout->output.output.i_bytes_per_frame;
307 if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
308 p_aout->output.output.i_physical_channels,
309 aout_FormatNbChannels( &p_aout->output.output ),
310 p_aout->output.output.i_rate,
311 p_aout->output.p_sys->i_frame_size, false )
314 msg_Err( p_aout, "cannot open directx audio device" );
315 free( p_aout->output.p_sys );
319 aout_VolumeNoneInit( p_aout );
323 if( val.i_int == AOUT_VAR_5_1 )
325 p_aout->output.output.i_physical_channels
326 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
327 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
330 else if( val.i_int == AOUT_VAR_7_1 )
332 p_aout->output.output.i_physical_channels
333 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
334 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
335 | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT
338 else if( val.i_int == AOUT_VAR_3F2R )
340 p_aout->output.output.i_physical_channels
341 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
342 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
344 else if( val.i_int == AOUT_VAR_2F2R )
346 p_aout->output.output.i_physical_channels
347 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
348 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
350 else if( val.i_int == AOUT_VAR_MONO )
352 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
356 p_aout->output.output.i_physical_channels
357 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
360 if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format,
361 p_aout->output.output.i_physical_channels,
362 aout_FormatNbChannels( &p_aout->output.output ),
363 p_aout->output.output.i_rate, false )
366 msg_Err( p_aout, "cannot open directx audio device" );
367 free( p_aout->output.p_sys );
371 /* Calculate the frame size in bytes */
372 p_aout->output.i_nb_samples = FRAME_SIZE;
373 aout_FormatPrepare( &p_aout->output.output );
374 aout_VolumeSoftInit( p_aout );
377 /* Now we need to setup our DirectSound play notification structure */
378 p_aout->output.p_sys->p_notif =
379 vlc_object_create( p_aout, sizeof(notification_thread_t) );
380 p_aout->output.p_sys->p_notif->p_aout = p_aout;
382 p_aout->output.p_sys->p_notif->event = CreateEvent( 0, FALSE, FALSE, 0 );
383 p_aout->output.p_sys->p_notif->i_frame_size =
384 p_aout->output.p_sys->i_frame_size;
386 /* then launch the notification thread */
387 msg_Dbg( p_aout, "creating DirectSoundThread" );
388 if( vlc_thread_create( p_aout->output.p_sys->p_notif,
389 "DirectSound Notification Thread",
391 VLC_THREAD_PRIORITY_HIGHEST, false ) )
393 msg_Err( p_aout, "cannot create DirectSoundThread" );
394 CloseHandle( p_aout->output.p_sys->p_notif->event );
395 vlc_object_release( p_aout->output.p_sys->p_notif );
396 p_aout->output.p_sys->p_notif = NULL;
400 vlc_object_attach( p_aout->output.p_sys->p_notif, p_aout );
405 CloseAudio( VLC_OBJECT(p_aout) );
409 /*****************************************************************************
410 * Probe: probe the audio device for available formats and channels
411 *****************************************************************************/
412 static void Probe( aout_instance_t * p_aout )
414 vlc_value_t val, text;
416 unsigned int i_physical_channels;
417 DWORD ui_speaker_config;
418 bool is_default_output_set = false;
420 var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
421 text.psz_string = _("Audio Device");
422 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
424 /* Test for 5.1 support */
425 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
426 AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
427 AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
428 if( p_aout->output.output.i_physical_channels == i_physical_channels )
430 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 6,
431 p_aout->output.output.i_rate, true )
434 val.i_int = AOUT_VAR_5_1;
435 text.psz_string = "5.1";
436 var_Change( p_aout, "audio-device",
437 VLC_VAR_ADDCHOICE, &val, &text );
438 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
439 is_default_output_set = true;
440 msg_Dbg( p_aout, "device supports 5.1 channels" );
444 /* Test for 7.1 support */
445 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
446 AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
447 AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT |
448 AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
449 if( p_aout->output.output.i_physical_channels == i_physical_channels )
451 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 8,
452 p_aout->output.output.i_rate, true )
455 val.i_int = AOUT_VAR_7_1;
456 text.psz_string = "7.1";
457 var_Change( p_aout, "audio-device",
458 VLC_VAR_ADDCHOICE, &val, &text );
459 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
460 is_default_output_set = true;
461 msg_Dbg( p_aout, "device supports 7.1 channels" );
465 /* Test for 3 Front 2 Rear support */
466 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
467 AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
469 if( p_aout->output.output.i_physical_channels == i_physical_channels )
471 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 5,
472 p_aout->output.output.i_rate, true )
475 val.i_int = AOUT_VAR_3F2R;
476 text.psz_string = N_("3 Front 2 Rear");
477 var_Change( p_aout, "audio-device",
478 VLC_VAR_ADDCHOICE, &val, &text );
479 if(!is_default_output_set)
481 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
482 is_default_output_set = true;
484 msg_Dbg( p_aout, "device supports 5 channels" );
488 /* Test for 2 Front 2 Rear support */
489 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
490 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
491 if( ( p_aout->output.output.i_physical_channels & i_physical_channels )
492 == i_physical_channels )
494 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 4,
495 p_aout->output.output.i_rate, true )
498 val.i_int = AOUT_VAR_2F2R;
499 text.psz_string = N_("2 Front 2 Rear");
500 var_Change( p_aout, "audio-device",
501 VLC_VAR_ADDCHOICE, &val, &text );
502 if(!is_default_output_set)
504 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
505 is_default_output_set = true;
507 msg_Dbg( p_aout, "device supports 4 channels" );
511 /* Test for stereo support */
512 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
513 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 2,
514 p_aout->output.output.i_rate, true )
517 val.i_int = AOUT_VAR_STEREO;
518 text.psz_string = N_("Stereo");
519 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
520 if(!is_default_output_set)
522 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
523 is_default_output_set = true;
524 msg_Dbg( p_aout, "device supports 2 channels (DEFAULT!)" );
526 msg_Dbg( p_aout, "device supports 2 channels" );
529 /* Test for mono support */
530 i_physical_channels = AOUT_CHAN_CENTER;
531 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 1,
532 p_aout->output.output.i_rate, true )
535 val.i_int = AOUT_VAR_MONO;
536 text.psz_string = N_("Mono");
537 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
538 msg_Dbg( p_aout, "device supports 1 channel" );
541 /* Check the speaker configuration to determine which channel config should
543 if FAILED( IDirectSound_GetSpeakerConfig( p_aout->output.p_sys->p_dsobject,
544 &ui_speaker_config ) )
546 ui_speaker_config = DSSPEAKER_STEREO;
547 msg_Dbg( p_aout, "GetSpeakerConfig failed" );
549 switch( DSSPEAKER_CONFIG(ui_speaker_config) )
551 case DSSPEAKER_7POINT1:
552 msg_Dbg( p_aout, "Windows says your SpeakerConfig is 7.1" );
553 val.i_int = AOUT_VAR_7_1;
555 case DSSPEAKER_5POINT1:
556 msg_Dbg( p_aout, "Windows says your SpeakerConfig is 5.1" );
557 val.i_int = AOUT_VAR_5_1;
560 msg_Dbg( p_aout, "Windows says your SpeakerConfig is Quad" );
561 val.i_int = AOUT_VAR_2F2R;
563 #if 0 /* Lots of people just get their settings wrong and complain that
564 * this is a problem with VLC so just don't ever set mono by default. */
566 val.i_int = AOUT_VAR_MONO;
569 case DSSPEAKER_SURROUND:
570 msg_Dbg( p_aout, "Windows says your SpeakerConfig is surround" );
571 case DSSPEAKER_STEREO:
572 msg_Dbg( p_aout, "Windows says your SpeakerConfig is stereo" );
574 /* If nothing else is found, choose stereo output */
575 val.i_int = AOUT_VAR_STEREO;
578 var_Set( p_aout, "audio-device", val );
580 /* Test for SPDIF support */
581 if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
583 if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
584 p_aout->output.output.i_physical_channels,
585 aout_FormatNbChannels( &p_aout->output.output ),
586 p_aout->output.output.i_rate,
587 AOUT_SPDIF_SIZE, true )
590 msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
591 val.i_int = AOUT_VAR_SPDIF;
592 text.psz_string = N_("A/52 over S/PDIF");
593 var_Change( p_aout, "audio-device",
594 VLC_VAR_ADDCHOICE, &val, &text );
595 if( config_GetInt( p_aout, "spdif" ) )
596 var_Set( p_aout, "audio-device", val );
600 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
603 /* Probe() has failed. */
604 var_Destroy( p_aout, "audio-device" );
608 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
611 var_Set( p_aout, "intf-change", val );
614 /*****************************************************************************
615 * Play: we'll start playing the directsound buffer here because at least here
616 * we know the first buffer has been put in the aout fifo and we also
618 *****************************************************************************/
619 static void Play( aout_instance_t *p_aout )
621 if( !p_aout->output.p_sys->b_playing )
623 aout_buffer_t *p_buffer;
626 p_aout->output.p_sys->b_playing = 1;
628 /* get the playing date of the first aout buffer */
629 p_aout->output.p_sys->p_notif->start_date =
630 aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
632 /* fill in the first samples */
633 for( i = 0; i < FRAMES_NUM; i++ )
635 p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
636 if( !p_buffer ) break;
637 FillBuffer( p_aout, i, p_buffer );
640 /* wake up the audio output thread */
641 SetEvent( p_aout->output.p_sys->p_notif->event );
645 /*****************************************************************************
646 * CloseAudio: close the audio device
647 *****************************************************************************/
648 static void CloseAudio( vlc_object_t *p_this )
650 aout_instance_t * p_aout = (aout_instance_t *)p_this;
651 aout_sys_t *p_sys = p_aout->output.p_sys;
653 msg_Dbg( p_aout, "closing audio device" );
655 /* kill the position notification thread, if any */
658 vlc_object_detach( p_sys->p_notif );
659 vlc_object_kill( p_sys->p_notif );
660 /* wake up the audio thread if needed */
661 if( !p_sys->b_playing ) SetEvent( p_sys->p_notif->event );
663 vlc_thread_join( p_sys->p_notif );
664 vlc_object_release( p_sys->p_notif );
667 /* release the secondary buffer */
668 DestroyDSBuffer( p_aout );
670 /* finally release the DirectSound object */
671 if( p_sys->p_dsobject ) IDirectSound_Release( p_sys->p_dsobject );
673 /* free DSOUND.DLL */
674 if( p_sys->hdsound_dll ) FreeLibrary( p_sys->hdsound_dll );
676 free( p_aout->output.p_sys->p_device_guid );
680 /*****************************************************************************
681 * CallBackDirectSoundEnum: callback to enumerate available devices
682 *****************************************************************************/
683 static int CALLBACK CallBackDirectSoundEnum( LPGUID p_guid, LPCSTR psz_desc,
684 LPCSTR psz_mod, LPVOID _p_aout )
686 aout_instance_t *p_aout = (aout_instance_t *)_p_aout;
688 msg_Dbg( p_aout, "found device: %s", psz_desc );
690 if( p_aout->output.p_sys->i_device_id == 0 && p_guid )
692 p_aout->output.p_sys->p_device_guid = malloc( sizeof( GUID ) );
693 *p_aout->output.p_sys->p_device_guid = *p_guid;
694 msg_Dbg( p_aout, "using device: %s", psz_desc );
697 p_aout->output.p_sys->i_device_id--;
701 /*****************************************************************************
702 * InitDirectSound: handle all the gory details of DirectSound initialisation
703 *****************************************************************************/
704 static int InitDirectSound( aout_instance_t *p_aout )
706 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
707 HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACK, LPVOID);
709 p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
710 if( p_aout->output.p_sys->hdsound_dll == NULL )
712 msg_Warn( p_aout, "cannot open DSOUND.DLL" );
716 OurDirectSoundCreate = (void *)
717 GetProcAddress( p_aout->output.p_sys->hdsound_dll,
718 "DirectSoundCreate" );
719 if( OurDirectSoundCreate == NULL )
721 msg_Warn( p_aout, "GetProcAddress FAILED" );
725 /* Get DirectSoundEnumerate */
726 OurDirectSoundEnumerate = (void *)
727 GetProcAddress( p_aout->output.p_sys->hdsound_dll,
728 "DirectSoundEnumerateA" );
729 if( OurDirectSoundEnumerate )
731 /* Attempt enumeration */
732 if( FAILED( OurDirectSoundEnumerate( CallBackDirectSoundEnum,
735 msg_Dbg( p_aout, "enumeration of DirectSound devices failed" );
739 /* Create the direct sound object */
740 if FAILED( OurDirectSoundCreate( p_aout->output.p_sys->p_device_guid,
741 &p_aout->output.p_sys->p_dsobject,
744 msg_Warn( p_aout, "cannot create a direct sound device" );
748 /* Set DirectSound Cooperative level, ie what control we want over Windows
749 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
750 * settings of the primary buffer, but also that only the sound of our
751 * application will be hearable when it will have the focus.
752 * !!! (this is not really working as intended yet because to set the
753 * cooperative level you need the window handle of your application, and
754 * I don't know of any easy way to get it. Especially since we might play
755 * sound without any video, and so what window handle should we use ???
756 * The hack for now is to use the Desktop window handle - it seems to be
758 if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
762 msg_Warn( p_aout, "cannot set direct sound cooperative level" );
768 p_aout->output.p_sys->p_dsobject = NULL;
769 if( p_aout->output.p_sys->hdsound_dll )
771 FreeLibrary( p_aout->output.p_sys->hdsound_dll );
772 p_aout->output.p_sys->hdsound_dll = NULL;
778 /*****************************************************************************
779 * CreateDSBuffer: Creates a direct sound buffer of the required format.
780 *****************************************************************************
781 * This function creates the buffer we'll use to play audio.
782 * In DirectSound there are two kinds of buffers:
783 * - the primary buffer: which is the actual buffer that the soundcard plays
784 * - the secondary buffer(s): these buffers are the one actually used by
785 * applications and DirectSound takes care of mixing them into the primary.
787 * Once you create a secondary buffer, you cannot change its format anymore so
788 * you have to release the current one and create another.
789 *****************************************************************************/
790 static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
791 int i_channels, int i_nb_channels, int i_rate,
792 int i_bytes_per_frame, bool b_probe )
794 WAVEFORMATEXTENSIBLE waveformat;
795 DSBUFFERDESC dsbdesc;
798 /* First set the sound buffer format */
799 waveformat.dwChannelMask = 0;
800 for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ )
802 if( i_channels & pi_channels_src[i] )
803 waveformat.dwChannelMask |= pi_channels_in[i];
808 case VLC_FOURCC('s','p','d','i'):
810 /* To prevent channel re-ordering */
811 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
812 waveformat.Format.wBitsPerSample = 16;
813 waveformat.Samples.wValidBitsPerSample =
814 waveformat.Format.wBitsPerSample;
815 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
816 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
819 case VLC_FOURCC('f','l','3','2'):
820 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
821 waveformat.Samples.wValidBitsPerSample =
822 waveformat.Format.wBitsPerSample;
823 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
824 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
827 case VLC_FOURCC('s','1','6','l'):
828 waveformat.Format.wBitsPerSample = 16;
829 waveformat.Samples.wValidBitsPerSample =
830 waveformat.Format.wBitsPerSample;
831 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
832 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
836 waveformat.Format.nChannels = i_nb_channels;
837 waveformat.Format.nSamplesPerSec = i_rate;
838 waveformat.Format.nBlockAlign =
839 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
840 waveformat.Format.nAvgBytesPerSec =
841 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
843 p_aout->output.p_sys->i_bits_per_sample = waveformat.Format.wBitsPerSample;
844 p_aout->output.p_sys->i_channels = i_nb_channels;
846 /* Then fill in the direct sound descriptor */
847 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
848 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
849 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
850 | DSBCAPS_GLOBALFOCUS; /* Allows background playing */
852 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
853 if( i_nb_channels <= 2 )
855 waveformat.Format.cbSize = 0;
859 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
860 waveformat.Format.cbSize =
861 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
863 /* Needed for 5.1 on emu101k */
864 dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
867 dsbdesc.dwBufferBytes = FRAMES_NUM * i_bytes_per_frame; /* buffer size */
868 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;
870 if FAILED( IDirectSound_CreateSoundBuffer(
871 p_aout->output.p_sys->p_dsobject, &dsbdesc,
872 &p_aout->output.p_sys->p_dsbuffer, NULL) )
874 if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE )
876 /* Try without DSBCAPS_LOCHARDWARE */
877 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
878 if FAILED( IDirectSound_CreateSoundBuffer(
879 p_aout->output.p_sys->p_dsobject, &dsbdesc,
880 &p_aout->output.p_sys->p_dsbuffer, NULL) )
885 msg_Dbg( p_aout, "couldn't use hardware sound buffer" );
893 /* Stop here if we were just probing */
896 IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
897 p_aout->output.p_sys->p_dsbuffer = NULL;
901 p_aout->output.p_sys->i_frame_size = i_bytes_per_frame;
902 p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask;
903 p_aout->output.p_sys->b_chan_reorder =
904 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
905 waveformat.dwChannelMask, i_nb_channels,
906 p_aout->output.p_sys->pi_chan_table );
908 if( p_aout->output.p_sys->b_chan_reorder )
910 msg_Dbg( p_aout, "channel reordering needed" );
916 /*****************************************************************************
917 * CreateDSBufferPCM: creates a PCM direct sound buffer.
918 *****************************************************************************
919 * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
920 * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
921 ****************************************************************************/
922 static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format,
923 int i_channels, int i_nb_channels, int i_rate,
928 var_Get( p_aout, "directx-audio-float32", &val );
930 /* Float32 audio samples are not supported for 5.1 output on the emu101k */
932 if( !val.b_bool || i_nb_channels > 2 ||
933 CreateDSBuffer( p_aout, VLC_FOURCC('f','l','3','2'),
934 i_channels, i_nb_channels, i_rate,
935 FRAME_SIZE * 4 * i_nb_channels, b_probe )
938 if ( CreateDSBuffer( p_aout, VLC_FOURCC('s','1','6','l'),
939 i_channels, i_nb_channels, i_rate,
940 FRAME_SIZE * 2 * i_nb_channels, b_probe )
947 *i_format = VLC_FOURCC('s','1','6','l');
953 *i_format = VLC_FOURCC('f','l','3','2');
958 /*****************************************************************************
960 *****************************************************************************
961 * This function destroys the secondary buffer.
962 *****************************************************************************/
963 static void DestroyDSBuffer( aout_instance_t *p_aout )
965 if( p_aout->output.p_sys->p_dsbuffer )
967 IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
968 p_aout->output.p_sys->p_dsbuffer = NULL;
972 /*****************************************************************************
973 * FillBuffer: Fill in one of the direct sound frame buffers.
974 *****************************************************************************
975 * Returns VLC_SUCCESS on success.
976 *****************************************************************************/
977 static int FillBuffer( aout_instance_t *p_aout, int i_frame,
978 aout_buffer_t *p_buffer )
980 notification_thread_t *p_notif = p_aout->output.p_sys->p_notif;
981 aout_sys_t *p_sys = p_aout->output.p_sys;
982 void *p_write_position, *p_wrap_around;
983 long l_bytes1, l_bytes2;
986 /* Before copying anything, we have to lock the buffer */
987 dsresult = IDirectSoundBuffer_Lock(
988 p_sys->p_dsbuffer, /* DS buffer */
989 i_frame * p_notif->i_frame_size, /* Start offset */
990 p_notif->i_frame_size, /* Number of bytes */
991 &p_write_position, /* Address of lock start */
992 &l_bytes1, /* Count of bytes locked before wrap around */
993 &p_wrap_around, /* Buffer adress (if wrap around) */
994 &l_bytes2, /* Count of bytes after wrap around */
996 if( dsresult == DSERR_BUFFERLOST )
998 IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
999 dsresult = IDirectSoundBuffer_Lock(
1001 i_frame * p_notif->i_frame_size,
1002 p_notif->i_frame_size,
1009 if( dsresult != DS_OK )
1011 msg_Warn( p_notif, "cannot lock buffer" );
1012 if( p_buffer ) aout_BufferFree( p_buffer );
1013 return VLC_EGENERIC;
1016 if( p_buffer == NULL )
1018 memset( p_write_position, 0, l_bytes1 );
1022 if( p_sys->b_chan_reorder )
1024 /* Do the channel reordering here */
1025 aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_nb_bytes,
1026 p_sys->i_channels, p_sys->pi_chan_table,
1027 p_sys->i_bits_per_sample );
1030 vlc_memcpy( p_write_position, p_buffer->p_buffer, l_bytes1 );
1031 aout_BufferFree( p_buffer );
1034 /* Now the data has been copied, unlock the buffer */
1035 IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
1036 p_wrap_around, l_bytes2 );
1038 p_notif->i_write_slot = (i_frame + 1) % FRAMES_NUM;
1042 /*****************************************************************************
1043 * DirectSoundThread: this thread will capture play notification events.
1044 *****************************************************************************
1045 * We use this thread to emulate a callback mechanism. The thread probes for
1046 * event notification and fills up the DS secondary buffer when needed.
1047 *****************************************************************************/
1048 static void DirectSoundThread( notification_thread_t *p_notif )
1050 aout_instance_t *p_aout = p_notif->p_aout;
1056 /* We don't want any resampling when using S/PDIF output */
1057 b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i');
1059 /* Tell the main thread that we are ready */
1060 vlc_thread_ready( p_notif );
1062 msg_Dbg( p_notif, "DirectSoundThread ready" );
1064 /* Wait here until Play() is called */
1065 WaitForSingleObject( p_notif->event, INFINITE );
1067 if( vlc_object_alive (p_notif) )
1069 mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 );
1071 /* start playing the buffer */
1072 dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer,
1075 DSBPLAY_LOOPING ); /* Flags */
1076 if( dsresult == DSERR_BUFFERLOST )
1078 IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
1079 dsresult = IDirectSoundBuffer_Play(
1080 p_aout->output.p_sys->p_dsbuffer,
1083 DSBPLAY_LOOPING ); /* Flags */
1085 if( dsresult != DS_OK )
1087 msg_Err( p_aout, "cannot start playing buffer" );
1090 last_time = mdate();
1092 while( vlc_object_alive (p_notif) )
1094 long l_read, l_free_slots;
1095 mtime_t mtime = mdate();
1099 * Fill in as much audio data as we can in our circular buffer
1102 /* Find out current play position */
1103 if FAILED( IDirectSoundBuffer_GetCurrentPosition(
1104 p_aout->output.p_sys->p_dsbuffer, &l_read, NULL ) )
1106 msg_Err( p_aout, "GetCurrentPosition() failed!" );
1110 /* Detect underruns */
1111 if( l_queued && mtime - last_time >
1112 INT64_C(1000000) * l_queued / p_aout->output.output.i_rate )
1114 msg_Dbg( p_aout, "detected underrun!" );
1118 /* Try to fill in as many frame buffers as possible */
1119 l_read /= p_aout->output.output.i_bytes_per_frame;
1120 l_queued = p_notif->i_write_slot * FRAME_SIZE - l_read;
1121 if( l_queued < 0 ) l_queued += (FRAME_SIZE * FRAMES_NUM);
1122 l_free_slots = (FRAMES_NUM * FRAME_SIZE - l_queued) / FRAME_SIZE;
1124 for( i = 0; i < l_free_slots; i++ )
1126 aout_buffer_t *p_buffer = aout_OutputNextBuffer( p_aout,
1127 mtime + INT64_C(1000000) * (i * FRAME_SIZE + l_queued) /
1128 p_aout->output.output.i_rate, b_sleek );
1130 /* If there is no audio data available and we have some buffered
1131 * already, then just wait for the next time */
1132 if( !p_buffer && (i || l_queued / FRAME_SIZE) ) break;
1134 if( FillBuffer( p_aout, p_notif->i_write_slot % FRAMES_NUM,
1135 p_buffer ) != VLC_SUCCESS ) break;
1138 /* Sleep a reasonable amount of time */
1139 l_queued += (i * FRAME_SIZE);
1140 msleep( INT64_C(1000000) * l_queued / p_aout->output.output.i_rate / 2 );
1143 /* make sure the buffer isn't playing */
1144 IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer );
1146 /* free the event */
1147 CloseHandle( p_notif->event );
1149 msg_Dbg( p_notif, "DirectSoundThread exiting" );