1 /*****************************************************************************
2 * directx.c: Windows DirectX audio output method
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: directx.c,v 1.14 2003/02/20 16:07:38 gbazin Exp $
7 * Authors: Gildas Bazin <gbazin@netcourrier.com>
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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <errno.h> /* ENOMEM */
28 #include <fcntl.h> /* open(), O_WRONLY */
29 #include <string.h> /* strerror() */
31 #include <stdlib.h> /* calloc(), malloc(), free() */
35 #include "aout_internal.h"
41 #define FRAME_SIZE 2048 /* The size is in samples, not in bytes */
44 /* frame buffer status */
45 #define FRAME_QUEUED 0
48 /*****************************************************************************
50 * Defining them here allows us to get rid of the dxguid library during
52 *****************************************************************************/
54 DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
56 /*****************************************************************************
58 *****************************************************************************/
59 #ifndef WAVE_FORMAT_IEEE_FLOAT
60 # define WAVE_FORMAT_IEEE_FLOAT 0x0003
63 #ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
64 # define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
67 #ifndef WAVE_FORMAT_EXTENSIBLE
68 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
71 #ifndef SPEAKER_FRONT_LEFT
72 # define SPEAKER_FRONT_LEFT 0x1
73 # define SPEAKER_FRONT_RIGHT 0x2
74 # define SPEAKER_FRONT_CENTER 0x4
75 # define SPEAKER_LOW_FREQUENCY 0x8
76 # define SPEAKER_BACK_LEFT 0x10
77 # define SPEAKER_BACK_RIGHT 0x20
78 # define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
79 # define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
80 # define SPEAKER_BACK_CENTER 0x100
81 # define SPEAKER_SIDE_LEFT 0x200
82 # define SPEAKER_SIDE_RIGHT 0x400
83 # define SPEAKER_TOP_CENTER 0x800
84 # define SPEAKER_TOP_FRONT_LEFT 0x1000
85 # define SPEAKER_TOP_FRONT_CENTER 0x2000
86 # define SPEAKER_TOP_FRONT_RIGHT 0x4000
87 # define SPEAKER_TOP_BACK_LEFT 0x8000
88 # define SPEAKER_TOP_BACK_CENTER 0x10000
89 # define SPEAKER_TOP_BACK_RIGHT 0x20000
90 # define SPEAKER_RESERVED 0x80000000
93 #ifndef _WAVEFORMATEXTENSIBLE_
97 WORD wValidBitsPerSample; /* bits of precision */
98 WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
99 WORD wReserved; /* If neither applies, set to zero. */
101 DWORD dwChannelMask; /* which channels are */
102 /* present in stream */
104 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
107 #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
108 DEFINE_GUID( KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
110 #ifndef KSDATAFORMAT_SUBTYPE_PCM
111 DEFINE_GUID( KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
113 #ifndef KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF
114 DEFINE_GUID( KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
117 /*****************************************************************************
118 * notification_thread_t: DirectX event thread
119 *****************************************************************************/
120 typedef struct notification_thread_t
124 aout_instance_t * p_aout;
125 int i_frame_status[FRAMES_NUM]; /* status of each frame buffer */
126 DSBPOSITIONNOTIFY p_events[FRAMES_NUM]; /* play notification events */
127 int i_frame_size; /* Size in bytes of one frame */
131 } notification_thread_t;
133 /*****************************************************************************
134 * aout_sys_t: directx audio output method descriptor
135 *****************************************************************************
136 * This structure is part of the audio output thread descriptor.
137 * It describes the direct sound specific properties of an audio device.
138 *****************************************************************************/
141 HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
142 LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
143 LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
144 * takes care of mixing all the
145 * secondary buffers into the primary) */
147 LPDIRECTSOUNDNOTIFY p_dsnotify; /* the position notify interface */
148 notification_thread_t *p_notif; /* DirectSoundThread id */
150 int b_playing; /* playing status */
152 int i_frame_size; /* Size in bytes of one frame */
154 vlc_bool_t b_chan_reorder; /* do we need channel reordering */
156 uint32_t i_channel_mask;
159 static const uint32_t pi_channels_in[] =
160 { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
161 AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
162 AOUT_CHAN_CENTER, AOUT_CHAN_LFE };
163 static const uint32_t pi_channels_out[] =
164 { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
165 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
166 SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY };
167 static const uint32_t pi_channels_ordered[] =
168 { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT, SPEAKER_FRONT_CENTER,
169 SPEAKER_LOW_FREQUENCY,
170 SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT };
172 /*****************************************************************************
174 *****************************************************************************/
175 static int OpenAudio ( vlc_object_t * );
176 static void CloseAudio ( vlc_object_t * );
177 static void Play ( aout_instance_t * );
179 /* local functions */
180 static void Probe ( aout_instance_t * );
181 static int InitDirectSound ( aout_instance_t * );
182 static int CreateDSBuffer ( aout_instance_t *, int, int, int, int, int, vlc_bool_t );
183 static int CreateDSBufferPCM ( aout_instance_t *, int*, int, int, int, vlc_bool_t );
184 static void DestroyDSBuffer ( aout_instance_t * );
185 static void DirectSoundThread ( notification_thread_t * );
186 static int FillBuffer ( aout_instance_t *, int, aout_buffer_t * );
188 static void CheckReordering ( aout_instance_t *, int );
189 static void InterleaveFloat32 ( float *, float *, int *, int );
190 static void InterleaveS16 ( int16_t *, int16_t *, int *, int );
192 /*****************************************************************************
194 *****************************************************************************/
196 set_description( _("DirectX audio module") );
197 set_capability( "audio output", 100 );
198 add_shortcut( "directx" );
199 set_callbacks( OpenAudio, CloseAudio );
202 /*****************************************************************************
203 * OpenAudio: open the audio device
204 *****************************************************************************
205 * This function opens and setups Direct Sound.
206 *****************************************************************************/
207 static int OpenAudio( vlc_object_t *p_this )
209 aout_instance_t * p_aout = (aout_instance_t *)p_this;
213 msg_Dbg( p_aout, "OpenAudio" );
215 /* Allocate structure */
216 p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
217 if( p_aout->output.p_sys == NULL )
219 msg_Err( p_aout, "out of memory" );
223 /* Initialize some variables */
224 p_aout->output.p_sys->p_dsobject = NULL;
225 p_aout->output.p_sys->p_dsbuffer = NULL;
226 p_aout->output.p_sys->p_dsnotify = NULL;
227 p_aout->output.p_sys->p_notif = NULL;
228 p_aout->output.p_sys->b_playing = 0;
229 p_aout->output.p_sys->pi_chan_table = NULL;
231 p_aout->output.pf_play = Play;
232 aout_VolumeSoftInit( p_aout );
234 /* Initialise DirectSound */
235 if( InitDirectSound( p_aout ) )
237 msg_Err( p_aout, "cannot initialize DirectSound" );
241 /* Now we need to setup our DirectSound play notification structure */
242 p_aout->output.p_sys->p_notif =
243 vlc_object_create( p_aout, sizeof(notification_thread_t) );
244 p_aout->output.p_sys->p_notif->p_aout = p_aout;
246 if( var_Type( p_aout, "audio-device" ) == 0 )
251 if( var_Get( p_aout, "audio-device", &val ) < 0 )
253 /* Probe() has failed. */
254 free( p_aout->output.p_sys );
258 /* Then create the notification events */
259 for( i = 0; i < FRAMES_NUM; i++ )
260 p_aout->output.p_sys->p_notif->p_events[i].hEventNotify =
261 CreateEvent( NULL, FALSE, FALSE, NULL );
263 /* Open the device */
264 if( !strcmp( val.psz_string, N_("A/52 over S/PDIF") ) )
266 free( val.psz_string );
267 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
269 /* Calculate the frame size in bytes */
270 p_aout->output.i_nb_samples = A52_FRAME_NB;
271 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
272 p_aout->output.output.i_frame_length = A52_FRAME_NB;
273 p_aout->output.p_sys->i_frame_size =
274 p_aout->output.output.i_bytes_per_frame;
276 if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
277 p_aout->output.output.i_physical_channels,
278 aout_FormatNbChannels( &p_aout->output.output ),
279 p_aout->output.output.i_rate,
280 p_aout->output.p_sys->i_frame_size, VLC_FALSE )
283 msg_Err( p_aout, "cannot open waveout audio device" );
284 free( p_aout->output.p_sys );
288 aout_VolumeNoneInit( p_aout );
292 if( !strcmp( val.psz_string, N_("5.1") ) )
294 p_aout->output.output.i_physical_channels
295 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
296 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
299 else if( !strcmp( val.psz_string, N_("2 Front 2 Rear") ) )
301 p_aout->output.output.i_physical_channels
302 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
303 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
305 else if( !strcmp( val.psz_string, "Mono" ) )
307 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
311 p_aout->output.output.i_physical_channels
312 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
314 free( val.psz_string );
316 if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format,
317 p_aout->output.output.i_physical_channels,
318 aout_FormatNbChannels( &p_aout->output.output ),
319 p_aout->output.output.i_rate, VLC_FALSE )
322 msg_Err( p_aout, "cannot open waveout audio device" );
323 free( p_aout->output.p_sys );
327 /* Calculate the frame size in bytes */
328 p_aout->output.i_nb_samples = FRAME_SIZE;
329 aout_FormatPrepare( &p_aout->output.output );
330 p_aout->output.p_sys->i_frame_size =
331 FRAME_SIZE * p_aout->output.output.i_bytes_per_frame;
333 aout_VolumeSoftInit( p_aout );
336 /* then launch the notification thread */
337 msg_Dbg( p_aout, "creating DirectSoundThread" );
338 if( vlc_thread_create( p_aout->output.p_sys->p_notif,
339 "DirectSound Notification Thread",
341 VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
343 msg_Err( p_aout, "cannot create DirectSoundThread" );
347 vlc_object_attach( p_aout->output.p_sys->p_notif, p_aout );
352 CloseAudio( VLC_OBJECT(p_aout) );
356 /*****************************************************************************
357 * Probe: probe the audio device for available formats and channels
358 *****************************************************************************/
359 static void Probe( aout_instance_t * p_aout )
363 unsigned int i_physical_channels;
364 DWORD ui_speaker_config;
366 var_Create( p_aout, "audio-device", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
368 /* Test for 5.1 support */
369 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
370 AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
371 AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
372 if( p_aout->output.output.i_physical_channels == i_physical_channels )
374 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 6,
375 p_aout->output.output.i_rate, VLC_TRUE )
378 val.psz_string = N_("5.1");
379 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
380 msg_Dbg( p_aout, "device supports 5.1 channels" );
384 /* Test for 2 Front 2 Rear support */
385 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
386 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
387 if( ( p_aout->output.output.i_physical_channels & i_physical_channels )
388 == i_physical_channels )
390 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 4,
391 p_aout->output.output.i_rate, VLC_TRUE )
394 val.psz_string = N_("2 Front 2 Rear");
395 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
396 msg_Dbg( p_aout, "device supports 4 channels" );
400 /* Test for stereo support */
401 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
402 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 2,
403 p_aout->output.output.i_rate, VLC_TRUE )
406 val.psz_string = N_("Stereo");
407 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
408 msg_Dbg( p_aout, "device supports 2 channels" );
411 /* Test for mono support */
412 i_physical_channels = AOUT_CHAN_CENTER;
413 if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 1,
414 p_aout->output.output.i_rate, VLC_TRUE )
417 val.psz_string = N_("Mono");
418 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
419 msg_Dbg( p_aout, "device supports 1 channel" );
422 /* Check the speaker configuration to determine which channel config should
424 if FAILED( IDirectSound_GetSpeakerConfig( p_aout->output.p_sys->p_dsobject,
425 &ui_speaker_config ) )
427 ui_speaker_config = DSSPEAKER_STEREO;
429 switch( DSSPEAKER_CONFIG(ui_speaker_config) )
431 case DSSPEAKER_5POINT1:
432 val.psz_string = N_("5.1");
435 val.psz_string = N_("2 Front 2 Rear");
438 val.psz_string = N_("Mono");
440 case DSSPEAKER_SURROUND:
441 case DSSPEAKER_STEREO:
443 val.psz_string = N_("Stereo");
446 var_Set( p_aout, "audio-device", val );
448 /* Test for SPDIF support */
449 if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
451 if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
452 p_aout->output.output.i_physical_channels,
453 aout_FormatNbChannels( &p_aout->output.output ),
454 p_aout->output.output.i_rate,
455 AOUT_SPDIF_SIZE, VLC_TRUE )
458 msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
459 val.psz_string = N_("A/52 over S/PDIF");
460 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
461 if( config_GetInt( p_aout, "spdif" ) )
462 var_Set( p_aout, "audio-device", val );
466 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
468 val.b_bool = VLC_TRUE;
469 var_Set( p_aout, "intf-change", val );
472 /*****************************************************************************
473 * Play: we'll start playing the directsound buffer here because at least here
474 * we know the first buffer has been put in the aout fifo and we also
476 *****************************************************************************/
477 static void Play( aout_instance_t *p_aout )
479 if( !p_aout->output.p_sys->b_playing )
481 aout_buffer_t *p_buffer;
483 p_aout->output.p_sys->b_playing = 1;
485 /* get the playing date of the first aout buffer */
486 p_aout->output.p_sys->p_notif->start_date =
487 aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
489 /* fill in the first samples */
490 p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
491 FillBuffer( p_aout, 0, p_buffer );
493 /* wake up the audio output thread */
494 SetEvent( p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );
498 /*****************************************************************************
499 * CloseAudio: close the audio device
500 *****************************************************************************/
501 static void CloseAudio( vlc_object_t *p_this )
503 aout_instance_t * p_aout = (aout_instance_t *)p_this;
505 msg_Dbg( p_aout, "CloseAudio" );
507 /* kill the position notification thread, if any */
508 if( p_aout->output.p_sys->p_notif )
510 vlc_object_detach( p_aout->output.p_sys->p_notif );
511 if( p_aout->output.p_sys->p_notif->b_thread )
513 p_aout->output.p_sys->p_notif->b_die = 1;
515 if( !p_aout->output.p_sys->b_playing )
516 /* wake up the audio thread */
518 p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );
520 vlc_thread_join( p_aout->output.p_sys->p_notif );
522 vlc_object_destroy( p_aout->output.p_sys->p_notif );
525 /* release the secondary buffer */
526 DestroyDSBuffer( p_aout );
528 /* finally release the DirectSound object */
529 if( p_aout->output.p_sys->p_dsobject )
530 IDirectSound_Release( p_aout->output.p_sys->p_dsobject );
532 /* free DSOUND.DLL */
533 if( p_aout->output.p_sys->hdsound_dll )
534 FreeLibrary( p_aout->output.p_sys->hdsound_dll );
536 if( p_aout->output.p_sys->pi_chan_table )
537 free( p_aout->output.p_sys->pi_chan_table );
539 free( p_aout->output.p_sys );
542 /*****************************************************************************
543 * InitDirectSound: handle all the gory details of DirectSound initialisation
544 *****************************************************************************/
545 static int InitDirectSound( aout_instance_t *p_aout )
547 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
549 p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
550 if( p_aout->output.p_sys->hdsound_dll == NULL )
552 msg_Warn( p_aout, "cannot open DSOUND.DLL" );
556 OurDirectSoundCreate = (void *)GetProcAddress(
557 p_aout->output.p_sys->hdsound_dll,
558 "DirectSoundCreate" );
559 if( OurDirectSoundCreate == NULL )
561 msg_Warn( p_aout, "GetProcAddress FAILED" );
565 /* Create the direct sound object */
566 if FAILED( OurDirectSoundCreate( NULL, &p_aout->output.p_sys->p_dsobject,
569 msg_Warn( p_aout, "cannot create a direct sound device" );
573 /* Set DirectSound Cooperative level, ie what control we want over Windows
574 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
575 * settings of the primary buffer, but also that only the sound of our
576 * application will be hearable when it will have the focus.
577 * !!! (this is not really working as intended yet because to set the
578 * cooperative level you need the window handle of your application, and
579 * I don't know of any easy way to get it. Especially since we might play
580 * sound without any video, and so what window handle should we use ???
581 * The hack for now is to use the Desktop window handle - it seems to be
583 if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
587 msg_Warn( p_aout, "cannot set direct sound cooperative level" );
593 p_aout->output.p_sys->p_dsobject = NULL;
594 if( p_aout->output.p_sys->hdsound_dll )
596 FreeLibrary( p_aout->output.p_sys->hdsound_dll );
597 p_aout->output.p_sys->hdsound_dll = NULL;
603 /*****************************************************************************
604 * CreateDSBuffer: Creates a direct sound buffer of the required format.
605 *****************************************************************************
606 * This function creates the buffer we'll use to play audio.
607 * In DirectSound there are two kinds of buffers:
608 * - the primary buffer: which is the actual buffer that the soundcard plays
609 * - the secondary buffer(s): these buffers are the one actually used by
610 * applications and DirectSound takes care of mixing them into the primary.
612 * Once you create a secondary buffer, you cannot change its format anymore so
613 * you have to release the current one and create another.
614 *****************************************************************************/
615 static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
616 int i_channels, int i_nb_channels, int i_rate,
617 int i_bytes_per_frame, vlc_bool_t b_probe )
619 WAVEFORMATEXTENSIBLE waveformat;
620 DSBUFFERDESC dsbdesc;
623 /* First set the sound buffer format */
624 waveformat.dwChannelMask = 0;
625 for( i = 0; i < sizeof(pi_channels_in)/sizeof(uint32_t); i++ )
627 if( i_channels & pi_channels_in[i] )
628 waveformat.dwChannelMask |= pi_channels_out[i];
633 case VLC_FOURCC('s','p','d','i'):
635 /* To prevent channel re-ordering */
636 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
637 waveformat.Format.wBitsPerSample = 16;
638 waveformat.Samples.wValidBitsPerSample =
639 waveformat.Format.wBitsPerSample;
640 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
641 waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
644 case VLC_FOURCC('f','l','3','2'):
645 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
646 waveformat.Samples.wValidBitsPerSample =
647 waveformat.Format.wBitsPerSample;
648 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
649 waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
652 case VLC_FOURCC('s','1','6','l'):
653 waveformat.Format.wBitsPerSample = 16;
654 waveformat.Samples.wValidBitsPerSample =
655 waveformat.Format.wBitsPerSample;
656 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
657 waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
661 waveformat.Format.nChannels = i_nb_channels;
662 waveformat.Format.nSamplesPerSec = i_rate;
663 waveformat.Format.nBlockAlign =
664 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
665 waveformat.Format.nAvgBytesPerSec =
666 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
668 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
669 if( i_nb_channels <= 2 )
671 waveformat.Format.cbSize = 0;
675 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
676 waveformat.Format.cbSize =
677 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
681 /* Then fill in the direct sound descriptor */
682 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
683 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
684 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
685 | DSBCAPS_CTRLPOSITIONNOTIFY /* We need notification */
686 | DSBCAPS_GLOBALFOCUS; /* Allows background playing */
687 dsbdesc.dwBufferBytes = FRAMES_NUM * i_bytes_per_frame; /* buffer size */
688 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;
690 if FAILED( IDirectSound_CreateSoundBuffer(
691 p_aout->output.p_sys->p_dsobject, &dsbdesc,
692 &p_aout->output.p_sys->p_dsbuffer, NULL) )
697 /* Stop here if we were just probing */
700 IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
701 p_aout->output.p_sys->p_dsbuffer = NULL;
705 /* Backup the size of a frame */
706 p_aout->output.p_sys->p_notif->i_frame_size = i_bytes_per_frame;
708 /* Now the secondary buffer is created, we need to setup its position
710 for( i = 0; i < FRAMES_NUM; i++ )
712 p_aout->output.p_sys->p_notif->p_events[i].dwOffset = i *
713 p_aout->output.p_sys->p_notif->i_frame_size;
715 p_aout->output.p_sys->p_notif->i_frame_status[i] = FRAME_EMPTY;
718 /* Get the IDirectSoundNotify interface */
719 if FAILED( IDirectSoundBuffer_QueryInterface(
720 p_aout->output.p_sys->p_dsbuffer,
721 &IID_IDirectSoundNotify,
722 (LPVOID *)&p_aout->output.p_sys->p_dsnotify ) )
724 msg_Err( p_aout, "cannot get IDirectSoundNotify interface" );
728 if FAILED( IDirectSoundNotify_SetNotificationPositions(
729 p_aout->output.p_sys->p_dsnotify,
731 p_aout->output.p_sys->p_notif->p_events ) )
733 msg_Err( p_aout, "cannot set position notification" );
737 p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask;
738 CheckReordering( p_aout, i_nb_channels );
743 if( p_aout->output.p_sys->p_dsbuffer )
745 IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
746 p_aout->output.p_sys->p_dsbuffer = NULL;
748 if( p_aout->output.p_sys->p_dsnotify )
750 IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
751 p_aout->output.p_sys->p_dsnotify = NULL;
756 /*****************************************************************************
757 * CreateDSBufferPCM: creates a PCM direct sound buffer.
758 *****************************************************************************
759 * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
760 * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
761 ****************************************************************************/
762 static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format,
763 int i_channels, int i_nb_channels, int i_rate,
766 if( CreateDSBuffer( p_aout, VLC_FOURCC('f','l','3','2'),
767 i_channels, i_nb_channels, i_rate,
768 FRAME_SIZE * 4 * i_nb_channels, b_probe )
771 if ( CreateDSBuffer( p_aout, VLC_FOURCC('s','1','6','l'),
772 i_channels, i_nb_channels, i_rate,
773 FRAME_SIZE * 2 * i_nb_channels, b_probe )
780 *i_format = VLC_FOURCC('s','1','6','l');
786 *i_format = VLC_FOURCC('f','l','3','2');
791 /*****************************************************************************
793 *****************************************************************************
794 * This function destroys the secondary buffer.
795 *****************************************************************************/
796 static void DestroyDSBuffer( aout_instance_t *p_aout )
798 if( p_aout->output.p_sys->p_dsnotify )
800 IDirectSoundNotify_Release( p_aout->output.p_sys->p_dsnotify );
801 p_aout->output.p_sys->p_dsnotify = NULL;
804 if( p_aout->output.p_sys->p_dsbuffer )
806 IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
807 p_aout->output.p_sys->p_dsbuffer = NULL;
811 /*****************************************************************************
812 * FillBuffer: Fill in one of the direct sound frame buffers.
813 *****************************************************************************
814 * Returns VLC_SUCCESS on success.
815 *****************************************************************************/
816 static int FillBuffer( aout_instance_t *p_aout, int i_frame,
817 aout_buffer_t *p_buffer )
819 notification_thread_t *p_notif = p_aout->output.p_sys->p_notif;
820 void *p_write_position, *p_wrap_around;
821 long l_bytes1, l_bytes2;
824 /* Before copying anything, we have to lock the buffer */
825 dsresult = IDirectSoundBuffer_Lock(
826 p_aout->output.p_sys->p_dsbuffer, /* DS buffer */
827 i_frame * p_notif->i_frame_size, /* Start offset */
828 p_notif->i_frame_size, /* Number of bytes */
829 &p_write_position, /* Address of lock start */
830 &l_bytes1, /* Count of bytes locked before wrap around */
831 &p_wrap_around, /* Buffer adress (if wrap around) */
832 &l_bytes2, /* Count of bytes after wrap around */
834 if( dsresult == DSERR_BUFFERLOST )
836 IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
837 dsresult = IDirectSoundBuffer_Lock(
838 p_aout->output.p_sys->p_dsbuffer,
839 i_frame * p_notif->i_frame_size,
840 p_notif->i_frame_size,
847 if( dsresult != DS_OK )
849 msg_Warn( p_notif, "cannot lock buffer" );
850 if( p_buffer ) aout_BufferFree( p_buffer );
854 if( p_buffer == NULL )
856 memset( p_write_position, 0, l_bytes1 );
858 else if( p_aout->output.p_sys->b_chan_reorder )
860 /* Do the channel reordering here */
862 if( p_aout->output.output.i_format == VLC_FOURCC('s','1','6','l') )
863 InterleaveS16( (int16_t *)p_buffer->p_buffer,
864 (int16_t *)p_write_position,
865 p_aout->output.p_sys->pi_chan_table,
866 aout_FormatNbChannels( &p_aout->output.output ) );
868 InterleaveFloat32( (float *)p_buffer->p_buffer,
869 (float *)p_write_position,
870 p_aout->output.p_sys->pi_chan_table,
871 aout_FormatNbChannels( &p_aout->output.output ) );
875 p_aout->p_vlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
877 aout_BufferFree( p_buffer );
880 /* Now the data has been copied, unlock the buffer */
881 IDirectSoundBuffer_Unlock( p_aout->output.p_sys->p_dsbuffer,
882 p_write_position, l_bytes1,
883 p_wrap_around, l_bytes2 );
888 /*****************************************************************************
889 * DirectSoundThread: this thread will capture play notification events.
890 *****************************************************************************
891 * We use this thread to emulate a callback mechanism. The thread probes for
892 * event notification and fills up the DS secondary buffer when needed.
893 *****************************************************************************/
894 static void DirectSoundThread( notification_thread_t *p_notif )
896 HANDLE notification_events[FRAMES_NUM];
898 aout_instance_t *p_aout = p_notif->p_aout;
899 int i, i_which_frame, i_last_frame, i_next_frame;
903 for( i = 0; i < FRAMES_NUM; i++ )
904 notification_events[i] = p_notif->p_events[i].hEventNotify;
906 /* We don't want any resampling when using S/PDIF output */
907 b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i');
909 /* Tell the main thread that we are ready */
910 vlc_thread_ready( p_notif );
912 msg_Dbg( p_notif, "DirectSoundThread ready" );
914 /* Wait here until Play() is called */
915 WaitForSingleObject( notification_events[0], INFINITE );
917 if( !p_notif->b_die )
919 mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 );
921 /* start playing the buffer */
922 dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer,
925 DSBPLAY_LOOPING ); /* Flags */
926 if( dsresult == DSERR_BUFFERLOST )
928 IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
929 dsresult = IDirectSoundBuffer_Play(
930 p_aout->output.p_sys->p_dsbuffer,
933 DSBPLAY_LOOPING ); /* Flags */
935 if( dsresult != DS_OK )
937 msg_Err( p_aout, "cannot start playing buffer" );
941 while( !p_notif->b_die )
943 aout_buffer_t *p_buffer;
946 /* wait for the position notification */
947 i_which_frame = WaitForMultipleObjects( FRAMES_NUM,
948 notification_events, 0,
949 INFINITE ) - WAIT_OBJECT_0;
956 /* We take into account the current latency */
957 if SUCCEEDED( IDirectSoundBuffer_GetCurrentPosition(
958 p_aout->output.p_sys->p_dsbuffer,
961 if( l_latency > (i_which_frame * FRAME_SIZE)
962 && l_latency < ((i_which_frame+1) * FRAME_SIZE) )
964 l_latency = - ( l_latency /
965 p_aout->output.output.i_bytes_per_frame %
970 l_latency = FRAME_SIZE - ( l_latency /
971 p_aout->output.output.i_bytes_per_frame %
980 /* Mark last frame as empty */
981 i_last_frame = (i_which_frame + FRAMES_NUM -1) % FRAMES_NUM;
982 i_next_frame = (i_which_frame + 1) % FRAMES_NUM;
983 p_notif->i_frame_status[i_last_frame] = FRAME_EMPTY;
985 /* Try to fill in as many frame buffers as possible */
986 for( i = i_next_frame; (i % FRAMES_NUM) != i_which_frame; i++ )
989 /* Check if frame buf is already filled */
990 if( p_notif->i_frame_status[i % FRAMES_NUM] == FRAME_QUEUED )
993 p_buffer = aout_OutputNextBuffer( p_aout,
994 mtime + 1000000 / p_aout->output.output.i_rate *
995 ((i - i_next_frame + 1) * FRAME_SIZE + l_latency), b_sleek );
997 /* If there is no audio data available and we have some buffered
998 * already, then just wait for the next time */
999 if( !p_buffer && (i != i_next_frame) )
1001 //msg_Err( p_aout, "only %i frame buffers filled!",
1002 // i - i_next_frame );
1006 if( FillBuffer( p_aout, (i%FRAMES_NUM), p_buffer )
1010 /* Mark the frame buffer as QUEUED */
1011 p_notif->i_frame_status[i%FRAMES_NUM] = FRAME_QUEUED;
1016 /* make sure the buffer isn't playing */
1017 IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer );
1019 /* free the events */
1020 for( i = 0; i < FRAMES_NUM; i++ )
1021 CloseHandle( notification_events[i] );
1023 msg_Dbg( p_notif, "DirectSoundThread exiting" );
1026 /*****************************************************************************
1027 * CheckReordering: Check if we need to do some channel re-ordering (the ac3
1028 * channel order is different from the one chosen by
1030 *****************************************************************************/
1031 static void CheckReordering( aout_instance_t *p_aout, int i_nb_channels )
1035 #define i_channel_mask p_aout->output.p_sys->i_channel_mask
1036 #define pi_chan_table p_aout->output.p_sys->pi_chan_table
1038 p_aout->output.p_sys->b_chan_reorder = VLC_FALSE;
1040 pi_chan_table = malloc( i_nb_channels * sizeof(int) );
1041 if( !pi_chan_table )
1047 i < (int)(sizeof(pi_channels_out)/sizeof(uint32_t)); i++ )
1049 if( i_channel_mask & pi_channels_out[i] )
1052 pi_channels_out[i] != pi_channels_ordered[k]; k++ )
1054 if( i_channel_mask & pi_channels_ordered[k] )
1060 pi_chan_table[j] = l;
1066 for( i = 0; i < i_nb_channels; i++ )
1068 if( pi_chan_table[i] != i )
1070 p_aout->output.p_sys->b_chan_reorder = VLC_TRUE;
1074 if( p_aout->output.p_sys->b_chan_reorder )
1076 msg_Dbg( p_aout, "channel reordering needed" );
1079 #undef pi_chan_table
1083 /*****************************************************************************
1084 * InterleaveFloat32/S16: change the channel order to the Microsoft one.
1085 *****************************************************************************/
1086 static void InterleaveFloat32( float *p_buf, float *p_buf_out,
1087 int *pi_chan_table, int i_nb_channels )
1091 for( i = 0; i < FRAME_SIZE; i++ )
1093 for( j = 0; j < i_nb_channels; j++ )
1095 p_buf_out[i*i_nb_channels + pi_chan_table[j]] =
1096 p_buf[i*i_nb_channels + j];
1101 static void InterleaveS16( int16_t *p_buf, int16_t *p_buf_out,
1102 int *pi_chan_table, int i_nb_channels )
1106 for( i = 0; i < FRAME_SIZE; i++ )
1108 for( j = 0; j < i_nb_channels; j++ )
1110 p_buf_out[ i*i_nb_channels + pi_chan_table[j]] =
1111 p_buf[i*i_nb_channels + j];