1 /*****************************************************************************
2 * directsound.c: DirectSound audio output plugin for VLC
3 *****************************************************************************
4 * Copyright (C) 2001-2009 VLC authors and VideoLAN
7 * Authors: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation, Inc.,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
37 #include <vlc_charset.h>
39 #include "windows_audio_common.h"
41 #define DS_BUF_SIZE (6*1024*1024)
43 /*****************************************************************************
44 * aout_sys_t: directx audio output method descriptor
45 *****************************************************************************
46 * This structure is part of the audio output thread descriptor.
47 * It describes the direct sound specific properties of an audio device.
48 *****************************************************************************/
51 HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
53 LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
54 LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
55 * takes care of mixing all the
56 * secondary buffers into the primary) */
58 LPDIRECTSOUNDNOTIFY p_notify;
67 int i_bytes_per_sample; /* Size in bytes of one frame */
68 int i_rate; /* Sample rate */
70 uint8_t chans_to_reorder; /* do we need channel reordering */
71 uint8_t chan_table[AOUT_CHAN_MAX];
72 uint32_t i_channel_mask;
78 /*****************************************************************************
80 *****************************************************************************/
81 static int Open( vlc_object_t * );
82 static void Close( vlc_object_t * );
83 static void Stop( audio_output_t * );
84 static void Play( audio_output_t *, block_t * );
85 static int VolumeSet( audio_output_t *, float );
86 static int MuteSet( audio_output_t *, bool );
87 static void Flush( audio_output_t *, bool );
88 static void Pause( audio_output_t *, bool, mtime_t );
89 static int TimeGet( audio_output_t *, mtime_t *);
92 static int InitDirectSound ( audio_output_t * );
93 static int CreateDSBuffer ( audio_output_t *, int, int, int, int, bool );
94 static int CreateDSBufferPCM ( audio_output_t *, vlc_fourcc_t*, int, int, bool );
95 static void DestroyDSBuffer ( audio_output_t * );
96 static int FillBuffer ( audio_output_t *, block_t * );
98 static int ReloadDirectXDevices( vlc_object_t *, const char *,
101 /* Speaker setup override options list */
102 static const char *const speaker_list[] = { "Windows default", "Mono", "Stereo",
103 "Quad", "5.1", "7.1" };
105 /*****************************************************************************
107 *****************************************************************************/
108 #define DEVICE_TEXT N_("Output device")
109 #define DEVICE_LONGTEXT N_("Select your audio output device")
111 #define SPEAKER_TEXT N_("Speaker configuration")
112 #define SPEAKER_LONGTEXT N_("Select speaker configuration you want to use. " \
113 "This option doesn't upmix! So NO e.g. Stereo -> 5.1 conversion." )
115 #define VOLUME_TEXT N_("Audio volume")
116 #define VOLUME_LONGTEXT N_("Audio volume in hundredths of decibels (dB).")
119 set_description( N_("DirectX audio output") )
120 set_shortname( "DirectX" )
121 set_capability( "audio output", 100 )
122 set_category( CAT_AUDIO )
123 set_subcategory( SUBCAT_AUDIO_AOUT )
124 add_shortcut( "directx", "aout_directx" )
126 add_string( "directx-audio-device", NULL,
127 DEVICE_TEXT, DEVICE_LONGTEXT, false )
128 change_string_cb( ReloadDirectXDevices )
129 add_obsolete_string( "directx-audio-device-name")
130 add_bool( "directx-audio-float32", true, FLOAT_TEXT,
131 FLOAT_LONGTEXT, true )
132 add_string( "directx-audio-speaker", "Windows default",
133 SPEAKER_TEXT, SPEAKER_LONGTEXT, true )
134 change_string_list( speaker_list, speaker_list )
135 add_float( "directx-volume", 1.0f,
136 VOLUME_TEXT, VOLUME_LONGTEXT, true )
137 change_integer_range( DSBVOLUME_MIN, DSBVOLUME_MAX )
139 set_callbacks( Open, Close )
142 /*****************************************************************************
143 * OpenAudio: open the audio device
144 *****************************************************************************
145 * This function opens and setups Direct Sound.
146 *****************************************************************************/
147 static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
152 const char * const * ppsz_compare = speaker_list;
154 msg_Dbg( p_aout, "Opening DirectSound Audio Output" );
156 /* Retrieve config values */
157 var_Create( p_aout, "directx-audio-float32",
158 VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
159 psz_speaker = var_CreateGetString( p_aout, "directx-audio-speaker" );
161 while ( *ppsz_compare != NULL )
163 if ( !strncmp( *ppsz_compare, psz_speaker, strlen(*ppsz_compare) ) )
170 if ( *ppsz_compare == NULL )
172 msg_Err( p_aout, "(%s) isn't valid speaker setup option", psz_speaker );
173 msg_Err( p_aout, "Defaulting to Windows default speaker config");
178 /* Initialise DirectSound */
179 if( InitDirectSound( p_aout ) )
181 msg_Err( p_aout, "cannot initialize DirectSound" );
187 DWORD ui_speaker_config;
188 int i_channels = 2; /* Default to stereo */
189 int i_orig_channels = aout_FormatNbChannels( fmt );
191 /* Check the speaker configuration to determine which channel config
192 * should be the default */
193 if( FAILED( IDirectSound_GetSpeakerConfig( p_aout->sys->p_dsobject,
194 &ui_speaker_config ) ) )
196 ui_speaker_config = DSSPEAKER_STEREO;
197 msg_Dbg( p_aout, "GetSpeakerConfig failed" );
200 const char *name = "Unknown";
201 switch( DSSPEAKER_CONFIG(ui_speaker_config) )
203 case DSSPEAKER_7POINT1:
204 case DSSPEAKER_7POINT1_SURROUND:
208 case DSSPEAKER_5POINT1:
209 case DSSPEAKER_5POINT1_SURROUND:
217 #if 0 /* Lots of people just get their settings wrong and complain that
218 * this is a problem with VLC so just don't ever set mono by default. */
221 fmt->i_physical_channels = AOUT_CHAN_CENTER;
224 case DSSPEAKER_SURROUND:
228 case DSSPEAKER_STEREO:
234 i_channels = ( i_channels < i_orig_channels )? i_channels: i_orig_channels;
236 msg_Dbg( p_aout, "%s speaker config: %s and stream has %d channels, using %d channels",
237 "Windows", name, i_orig_channels, i_channels );
242 fmt->i_physical_channels = AOUT_CHANS_7_1;
246 fmt->i_physical_channels = AOUT_CHANS_5_1;
250 fmt->i_physical_channels = AOUT_CHANS_4_0;
253 fmt->i_physical_channels = AOUT_CHANS_2_0;
258 { /* Overriden speaker configuration */
259 const char *name = "Non-existant";
264 fmt->i_physical_channels = AOUT_CHAN_CENTER;
268 fmt->i_physical_channels = AOUT_CHANS_2_0;
272 fmt->i_physical_channels = AOUT_CHANS_4_0;
276 fmt->i_physical_channels = AOUT_CHANS_5_1;
280 fmt->i_physical_channels = AOUT_CHANS_7_1;
283 msg_Dbg( p_aout, "%s speaker config: %s", "VLC", name );
286 /* Open the device */
287 if ( AOUT_FMT_SPDIF( fmt )
288 && var_InheritBool( p_aout, "spdif" )
289 && CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL, fmt->i_physical_channels,
290 aout_FormatNbChannels( fmt ), fmt->i_rate, true )
293 msg_Dbg( p_aout, "using A/52 pass-through over S/PDIF" );
294 fmt->i_format = VLC_CODEC_SPDIFL;
296 /* Calculate the frame size in bytes */
297 fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
298 fmt->i_frame_length = A52_FRAME_NB;
302 aout_FormatPrepare( fmt );
304 if( CreateDSBufferPCM( p_aout, &fmt->i_format,
305 fmt->i_physical_channels, fmt->i_rate, false )
308 msg_Err( p_aout, "cannot open directx audio device" );
312 p_aout->sys->i_write = 0;
314 /* Force volume update */
315 VolumeSet( p_aout, p_aout->sys->volume.volume );
316 MuteSet( p_aout, p_aout->sys->volume.mute );
318 /* then launch the notification thread */
319 p_aout->time_get = TimeGet;
321 p_aout->pause = Pause;
322 p_aout->flush = Flush;
331 /*****************************************************************************
332 * Play: we'll start playing the directsound buffer here because at least here
333 * we know the first buffer has been put in the aout fifo and we also
335 *****************************************************************************/
336 static void Play( audio_output_t *p_aout, block_t *p_buffer )
338 if( FillBuffer( p_aout, p_buffer ) == VLC_SUCCESS )
340 /* start playing the buffer */
341 HRESULT dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer,
342 0, 0, DSBPLAY_LOOPING );
343 if( dsresult == DSERR_BUFFERLOST )
345 IDirectSoundBuffer_Restore( p_aout->sys->p_dsbuffer );
346 dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer,
347 0, 0, DSBPLAY_LOOPING );
349 if( dsresult != DS_OK )
350 msg_Err( p_aout, "cannot start playing buffer" );
354 static int VolumeSet( audio_output_t *p_aout, float volume )
356 aout_sys_t *sys = p_aout->sys;
359 /* millibels from linear amplification map 200% on DSBVOLUME_MAX */
360 LONG mb = lroundf( 6000.f * log10f( volume / 2.f ));
362 /* Clamp to allowed DirectSound range */
363 static_assert( DSBVOLUME_MIN < DSBVOLUME_MAX, "DSBVOLUME_* confused" );
364 if( mb > DSBVOLUME_MAX )
369 if( mb <= DSBVOLUME_MIN )
373 sys->volume.volume = volume;
374 if( !sys->volume.mute && sys->p_dsbuffer &&
375 IDirectSoundBuffer_SetVolume( sys->p_dsbuffer, mb ) != DS_OK )
377 /* Convert back to UI volume */
378 aout_VolumeReport( p_aout, volume );
380 if( var_InheritBool( p_aout, "volume-save" ) )
381 config_PutFloat( p_aout, "directx-volume", volume );
385 static int MuteSet( audio_output_t *p_aout, bool mute )
388 aout_sys_t *sys = p_aout->sys;
390 sys->volume.mute = mute;
392 if( sys->p_dsbuffer )
393 res = IDirectSoundBuffer_SetVolume( sys->p_dsbuffer,
394 mute? DSBVOLUME_MIN : sys->volume.mb );
396 aout_MuteReport( p_aout, mute );
397 return (res != DS_OK);
400 /*****************************************************************************
401 * CloseAudio: close the audio device
402 *****************************************************************************/
403 static void Stop( audio_output_t *p_aout )
405 aout_sys_t *p_sys = p_aout->sys;
406 msg_Dbg( p_aout, "closing audio device" );
408 if( p_sys->p_notify )
409 IDirectSoundNotify_Release(p_sys->p_notify );
410 p_sys->p_notify = NULL;
412 if( p_sys->p_dsbuffer )
413 IDirectSoundBuffer_Stop( p_sys->p_dsbuffer );
414 /* release the secondary buffer */
415 DestroyDSBuffer( p_aout );
417 /* finally release the DirectSound object */
418 if( p_sys->p_dsobject )
419 IDirectSound_Release( p_sys->p_dsobject );
422 /*****************************************************************************
423 * InitDirectSound: handle all the gory details of DirectSound initialisation
424 *****************************************************************************/
425 static int InitDirectSound( audio_output_t *p_aout )
427 aout_sys_t *sys = p_aout->sys;
428 GUID guid, *p_guid = NULL;
429 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
431 OurDirectSoundCreate = (void *)
432 GetProcAddress( p_aout->sys->hdsound_dll,
433 "DirectSoundCreate" );
434 if( OurDirectSoundCreate == NULL )
436 msg_Warn( p_aout, "GetProcAddress FAILED" );
440 char *dev = var_GetNonEmptyString( p_aout, "directx-audio-device" );
443 LPOLESTR lpsz = ToWide( dev );
446 if( SUCCEEDED( IIDFromString( lpsz, &guid ) ) )
449 msg_Err( p_aout, "bad device GUID: %ls", lpsz );
453 /* Create the direct sound object */
454 if FAILED( OurDirectSoundCreate( p_guid, &sys->p_dsobject, NULL ) )
456 msg_Warn( p_aout, "cannot create a direct sound device" );
460 /* Set DirectSound Cooperative level, ie what control we want over Windows
461 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
462 * settings of the primary buffer, but also that only the sound of our
463 * application will be hearable when it will have the focus.
464 * !!! (this is not really working as intended yet because to set the
465 * cooperative level you need the window handle of your application, and
466 * I don't know of any easy way to get it. Especially since we might play
467 * sound without any video, and so what window handle should we use ???
468 * The hack for now is to use the Desktop window handle - it seems to be
470 #if !VLC_WINSTORE_APP
471 if( IDirectSound_SetCooperativeLevel( p_aout->sys->p_dsobject,
475 msg_Warn( p_aout, "cannot set direct sound cooperative level" );
481 sys->p_dsobject = NULL;
486 /*****************************************************************************
487 * CreateDSBuffer: Creates a direct sound buffer of the required format.
488 *****************************************************************************
489 * This function creates the buffer we'll use to play audio.
490 * In DirectSound there are two kinds of buffers:
491 * - the primary buffer: which is the actual buffer that the soundcard plays
492 * - the secondary buffer(s): these buffers are the one actually used by
493 * applications and DirectSound takes care of mixing them into the primary.
495 * Once you create a secondary buffer, you cannot change its format anymore so
496 * you have to release the current one and create another.
497 *****************************************************************************/
498 static int CreateDSBuffer( audio_output_t *p_aout, int i_format,
499 int i_channels, int i_nb_channels, int i_rate,
502 WAVEFORMATEXTENSIBLE waveformat;
503 DSBUFFERDESC dsbdesc;
505 /* First set the sound buffer format */
506 waveformat.dwChannelMask = 0;
507 for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ )
508 if( i_channels & pi_vlc_chan_order_wg4[i] )
509 waveformat.dwChannelMask |= pi_channels_in[i];
513 case VLC_CODEC_SPDIFL:
515 /* To prevent channel re-ordering */
516 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
517 waveformat.Format.wBitsPerSample = 16;
518 waveformat.Samples.wValidBitsPerSample =
519 waveformat.Format.wBitsPerSample;
520 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
521 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
525 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
526 waveformat.Samples.wValidBitsPerSample =
527 waveformat.Format.wBitsPerSample;
528 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
529 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
530 aout_GainRequest(p_aout, 8.f);
534 waveformat.Format.wBitsPerSample = 16;
535 waveformat.Samples.wValidBitsPerSample =
536 waveformat.Format.wBitsPerSample;
537 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
538 waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
539 aout_GainRequest(p_aout, 8.f);
543 waveformat.Format.nChannels = i_nb_channels;
544 waveformat.Format.nSamplesPerSec = i_rate;
545 waveformat.Format.nBlockAlign =
546 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
547 waveformat.Format.nAvgBytesPerSec =
548 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
550 p_aout->sys->i_bytes_per_sample = waveformat.Format.nBlockAlign;
551 p_aout->sys->format = i_format;
553 /* Then fill in the direct sound descriptor */
554 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
555 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
556 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /* Better position accuracy */
557 | DSBCAPS_GLOBALFOCUS /* Allows background playing */
558 | DSBCAPS_CTRLVOLUME /* Allows volume control */
559 | DSBCAPS_CTRLPOSITIONNOTIFY; /* Allow position notifications */
561 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
562 if( i_nb_channels <= 2 )
564 waveformat.Format.cbSize = 0;
568 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
569 waveformat.Format.cbSize =
570 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
572 /* Needed for 5.1 on emu101k */
573 dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
576 dsbdesc.dwBufferBytes = DS_BUF_SIZE; /* buffer size */
577 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;
579 if FAILED( IDirectSound_CreateSoundBuffer(
580 p_aout->sys->p_dsobject, &dsbdesc,
581 &p_aout->sys->p_dsbuffer, NULL) )
583 if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE )
585 /* Try without DSBCAPS_LOCHARDWARE */
586 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
587 if FAILED( IDirectSound_CreateSoundBuffer(
588 p_aout->sys->p_dsobject, &dsbdesc,
589 &p_aout->sys->p_dsbuffer, NULL) )
594 msg_Dbg( p_aout, "couldn't use hardware sound buffer" );
602 /* Stop here if we were just probing */
605 IDirectSoundBuffer_Release( p_aout->sys->p_dsbuffer );
606 p_aout->sys->p_dsbuffer = NULL;
610 p_aout->sys->i_rate = i_rate;
611 p_aout->sys->i_channel_mask = waveformat.dwChannelMask;
612 p_aout->sys->chans_to_reorder =
613 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
614 waveformat.dwChannelMask,
615 p_aout->sys->chan_table );
616 if( p_aout->sys->chans_to_reorder )
618 msg_Dbg( p_aout, "channel reordering needed" );
621 if( IDirectSoundBuffer_QueryInterface( p_aout->sys->p_dsbuffer,
622 &IID_IDirectSoundNotify,
623 (void **) &p_aout->sys->p_notify )
627 msg_Err(p_aout, "Couldn't query IDirectSoundNotify");
628 p_aout->sys->p_notify = NULL;
631 FillBuffer(p_aout,NULL);
636 /*****************************************************************************
637 * CreateDSBufferPCM: creates a PCM direct sound buffer.
638 *****************************************************************************
639 * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
640 * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
641 ****************************************************************************/
642 static int CreateDSBufferPCM( audio_output_t *p_aout, vlc_fourcc_t *i_format,
643 int i_channels, int i_rate, bool b_probe )
645 unsigned i_nb_channels = popcount( i_channels );
647 if( !var_GetBool( p_aout, "directx-audio-float32" ) ||
648 CreateDSBuffer( p_aout, VLC_CODEC_FL32,
649 i_channels, i_nb_channels, i_rate, b_probe )
652 if ( CreateDSBuffer( p_aout, VLC_CODEC_S16N,
653 i_channels, i_nb_channels, i_rate, b_probe )
660 *i_format = VLC_CODEC_S16N;
666 *i_format = VLC_CODEC_FL32;
671 /*****************************************************************************
673 *****************************************************************************
674 * This function destroys the secondary buffer.
675 *****************************************************************************/
676 static void DestroyDSBuffer( audio_output_t *p_aout )
678 if( p_aout->sys->p_dsbuffer )
680 IDirectSoundBuffer_Release( p_aout->sys->p_dsbuffer );
681 p_aout->sys->p_dsbuffer = NULL;
685 /*****************************************************************************
686 * FillBuffer: Fill in one of the direct sound frame buffers.
687 *****************************************************************************
688 * Returns VLC_SUCCESS on success.
689 *****************************************************************************/
690 static int FillBuffer( audio_output_t *p_aout, block_t *p_buffer )
692 aout_sys_t *p_sys = p_aout->sys;
694 size_t towrite = (p_buffer)? p_buffer->i_buffer : DS_BUF_SIZE;
695 void *p_write_position, *p_wrap_around;
696 unsigned long l_bytes1, l_bytes2;
702 size_t toerase = p_sys->i_bytes_per_sample * p_sys->i_rate / 4;
703 mtime_t max = towrite;
705 if( IDirectSoundBuffer_GetCurrentPosition( p_aout->sys->p_dsbuffer, (LPDWORD) &i_read, NULL) == DS_OK )
707 max = (mtime_t)i_read - (mtime_t)p_aout->sys->i_write;
712 if( towrite + toerase <= max )
713 i_buf = towrite + toerase;
717 /* Before copying anything, we have to lock the buffer */
718 dsresult = IDirectSoundBuffer_Lock(
719 p_sys->p_dsbuffer, /* DS buffer */
720 p_aout->sys->i_write, /* Start offset */
721 i_buf, /* Number of bytes */
722 &p_write_position, /* Address of lock start */
723 &l_bytes1, /* Count of bytes locked before wrap around */
724 &p_wrap_around, /* Buffer address (if wrap around) */
725 &l_bytes2, /* Count of bytes after wrap around */
726 0 ); /* Flags: DSBLOCK_FROMWRITECURSOR is buggy */
727 if( dsresult == DSERR_BUFFERLOST )
729 IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
730 dsresult = IDirectSoundBuffer_Lock(
732 p_aout->sys->i_write,
740 if( dsresult != DS_OK )
742 msg_Warn( p_aout, "cannot lock buffer" );
743 if( p_buffer ) block_Release( p_buffer );
747 if( p_buffer == NULL )
749 memset( p_write_position, 0, l_bytes1 );
750 memset( p_wrap_around, 0, l_bytes2 );
754 if( p_sys->chans_to_reorder )
755 /* Do the channel reordering here */
756 aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer,
757 p_sys->chans_to_reorder, p_sys->chan_table,
761 i_size = ( p_buffer->i_buffer < l_bytes1 ) ? p_buffer->i_buffer : l_bytes1;
762 memcpy( p_write_position, p_buffer->p_buffer, i_size );
763 memset( (uint8_t*) p_write_position + i_size, 0, l_bytes1 - i_size );
765 if( l_bytes1 < p_buffer->i_buffer)
767 /* Compute the remaining buffer space to be written */
768 i_buf = i_buf - i_size - (l_bytes1 - i_size);
769 i_size = ( p_buffer->i_buffer - l_bytes1 < l_bytes2 ) ? p_buffer->i_buffer - l_bytes1 : l_bytes2;
770 memcpy( p_wrap_around, p_buffer->p_buffer + l_bytes1, i_size );
771 memset( (uint8_t*) p_wrap_around + i_size, 0, i_buf - i_size );
773 block_Release( p_buffer );
776 /* Now the data has been copied, unlock the buffer */
777 IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
778 p_wrap_around, l_bytes2 );
780 p_sys->i_write += towrite;
781 p_sys->i_write %= DS_BUF_SIZE;
793 static int CALLBACK DeviceEnumCallback( LPGUID guid, LPCWSTR desc,
794 LPCWSTR mod, LPVOID data )
796 ds_list_t *list = data;
799 if( StringFromGUID2( guid, buf, 48 ) <= 0 )
803 list->ids = xrealloc( list->ids, list->count * sizeof(char *) );
804 list->names = xrealloc( list->names, list->count * sizeof(char *) );
805 list->ids[list->count - 1] = FromWide( buf );
806 list->names[list->count - 1] = FromWide( desc );
807 if( list->ids == NULL || list->names == NULL )
814 /*****************************************************************************
815 * ReloadDirectXDevices: store the list of devices in preferences
816 *****************************************************************************/
817 static int ReloadDirectXDevices( vlc_object_t *p_this, char const *psz_name,
818 char ***values, char ***descs )
822 .ids = xmalloc(sizeof (char *)),
823 .names = xmalloc(sizeof (char *)),
825 list.ids[0] = xstrdup("");
826 list.names[0] = xstrdup(_("Default"));
830 HANDLE hdsound_dll = LoadLibrary(_T("DSOUND.DLL"));
831 if( hdsound_dll == NULL )
833 msg_Warn( p_this, "cannot open DSOUND.DLL" );
837 /* Get DirectSoundEnumerate */
838 HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKW, LPVOID) =
839 (void *)GetProcAddress( hdsound_dll, "DirectSoundEnumerateW" );
840 if( OurDirectSoundEnumerate != NULL )
842 OurDirectSoundEnumerate( DeviceEnumCallback, &list );
843 msg_Dbg( p_this, "found %u devices", list.count );
845 FreeLibrary(hdsound_dll);
853 static int DeviceSelect (audio_output_t *aout, const char *id)
855 var_SetString(aout, "directx-audio-device", (id != NULL) ? id : "");
856 aout_DeviceReport (aout, id);
857 aout_RestartRequest (aout, AOUT_RESTART_OUTPUT);
861 static int Open(vlc_object_t *obj)
863 audio_output_t *aout = (audio_output_t *)obj;
865 HINSTANCE hdsound_dll = LoadLibrary(_T("DSOUND.DLL"));
866 if (hdsound_dll == NULL)
868 msg_Warn(aout, "cannot open DSOUND.DLL");
872 aout_sys_t *sys = calloc(1, sizeof (*sys));
873 if (unlikely(sys == NULL))
876 sys->hdsound_dll = hdsound_dll;
881 aout->volume_set = VolumeSet;
882 aout->mute_set = MuteSet;
883 aout->device_select = DeviceSelect;
886 sys->volume.volume = var_InheritFloat(aout, "directx-volume");
887 aout_VolumeReport(aout, sys->volume.volume );
888 MuteSet(aout, var_InheritBool(aout, "mute"));
890 sys->hnotify_evt = CreateEvent(NULL, FALSE, TRUE, NULL);
891 if( !sys->hnotify_evt )
893 msg_Err(aout, "cannot create Event");
894 FreeLibrary(sys->hdsound_dll);
899 /* DirectSound does not support hot-plug events (unless with WASAPI) */
901 int count = ReloadDirectXDevices(obj, NULL, &ids, &names);
904 for (int i = 0; i < count; i++)
906 aout_HotplugReport(aout, ids[i], names[i]);
914 char *dev = var_CreateGetNonEmptyString(aout, "directx-audio-device");
915 aout_DeviceReport(aout, dev);
921 static void Close(vlc_object_t *obj)
923 audio_output_t *aout = (audio_output_t *)obj;
924 aout_sys_t *sys = aout->sys;
926 var_Destroy(aout, "directx-audio-device");
927 CloseHandle(sys->hnotify_evt);
928 FreeLibrary(sys->hdsound_dll); /* free DSOUND.DLL */
932 static void Flush ( audio_output_t * aout, bool drain )
934 IDirectSoundBuffer_Stop( aout->sys->p_dsbuffer );
936 IDirectSoundBuffer_SetCurrentPosition( aout->sys->p_dsbuffer,
937 aout->sys->i_write );
940 static void Pause( audio_output_t * aout, bool pause, mtime_t date )
944 IDirectSoundBuffer_Stop( aout->sys->p_dsbuffer );
946 IDirectSoundBuffer_Play( aout->sys->p_dsbuffer, 0, 0, DSBPLAY_LOOPING );
950 static int TimeGet( audio_output_t * aout, mtime_t * delay )
955 if( IDirectSoundBuffer_GetCurrentPosition( aout->sys->p_dsbuffer, (LPDWORD) &read, NULL) != DS_OK )
960 size = (mtime_t)aout->sys->i_write - (mtime_t) read;
964 *delay = ( size / aout->sys->i_bytes_per_sample ) * CLOCK_FREQ / aout->sys->i_rate;