X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Fdirectx.c;h=32cdf840e0b1934dbb68c984ef258cc0ec4c272b;hb=f6c636aeaff535dcab1e8e99e7c40eca874ec6bf;hp=98ce3147fd48389ba0678edc5d1e3cfc7ce96ac2;hpb=4b44912ce0888ed40d35aa5d782cd17cbf7e9c6b;p=vlc diff --git a/modules/audio_output/directx.c b/modules/audio_output/directx.c index 98ce3147fd..32cdf840e0 100644 --- a/modules/audio_output/directx.c +++ b/modules/audio_output/directx.c @@ -1,24 +1,24 @@ /***************************************************************************** * directx.c: Windows DirectX audio output method ***************************************************************************** - * Copyright (C) 2001 the VideoLAN team + * Copyright (C) 2001-2009 VLC authors and VideoLAN * $Id$ * * Authors: Gildas Bazin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -29,125 +29,16 @@ # include "config.h" #endif +#include + #include #include #include +#include -#include -#include -#include - -#define FRAME_SIZE ((int)p_aout->output.output.i_rate/20) /* Size in samples */ -#define FRAMES_NUM 8 /* Needs to be > 3 */ - -/***************************************************************************** - * DirectSound GUIDs. - * Defining them here allows us to get rid of the dxguid library during - * the linking stage. - *****************************************************************************/ -#include - -/***************************************************************************** - * Useful macros - *****************************************************************************/ -#ifndef WAVE_FORMAT_IEEE_FLOAT -# define WAVE_FORMAT_IEEE_FLOAT 0x0003 -#endif - -#ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF -# define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092 -#endif - -#ifndef WAVE_FORMAT_EXTENSIBLE -#define WAVE_FORMAT_EXTENSIBLE 0xFFFE -#endif - -#ifndef SPEAKER_FRONT_LEFT -# define SPEAKER_FRONT_LEFT 0x1 -# define SPEAKER_FRONT_RIGHT 0x2 -# define SPEAKER_FRONT_CENTER 0x4 -# define SPEAKER_LOW_FREQUENCY 0x8 -# define SPEAKER_BACK_LEFT 0x10 -# define SPEAKER_BACK_RIGHT 0x20 -# define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 -# define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 -# define SPEAKER_BACK_CENTER 0x100 -# define SPEAKER_SIDE_LEFT 0x200 -# define SPEAKER_SIDE_RIGHT 0x400 -# define SPEAKER_TOP_CENTER 0x800 -# define SPEAKER_TOP_FRONT_LEFT 0x1000 -# define SPEAKER_TOP_FRONT_CENTER 0x2000 -# define SPEAKER_TOP_FRONT_RIGHT 0x4000 -# define SPEAKER_TOP_BACK_LEFT 0x8000 -# define SPEAKER_TOP_BACK_CENTER 0x10000 -# define SPEAKER_TOP_BACK_RIGHT 0x20000 -# define SPEAKER_RESERVED 0x80000000 -#endif - -#ifndef DSSPEAKER_DSSPEAKER_DIRECTOUT -# define DSSPEAKER_DSSPEAKER_DIRECTOUT 0x00000000 -#endif -#ifndef DSSPEAKER_HEADPHONE -# define DSSPEAKER_HEADPHONE 0x00000001 -#endif -#ifndef DSSPEAKER_MONO -# define DSSPEAKER_MONO 0x00000002 -#endif -#ifndef DSSPEAKER_QUAD -# define DSSPEAKER_QUAD 0x00000003 -#endif -#ifndef DSSPEAKER_STEREO -# define DSSPEAKER_STEREO 0x00000004 -#endif -#ifndef DSSPEAKER_SURROUND -# define DSSPEAKER_SURROUND 0x00000005 -#endif -#ifndef DSSPEAKER_5POINT1 -# define DSSPEAKER_5POINT1 0x00000006 -#endif -#ifndef DSSPEAKER_7POINT1 -# define DSSPEAKER_7POINT1 0x00000007 -#endif -#ifndef DSSPEAKER_7POINT1_SURROUND -# define DSSPEAKER_7POINT1_SURROUND 0x00000008 -#endif -#ifndef DSSPEAKER_7POINT1_WIDE -# define DSSPEAKER_7POINT1_WIDE DSSPEAKER_7POINT1 -#endif - -#ifndef _WAVEFORMATEXTENSIBLE_ -typedef struct { - WAVEFORMATEX Format; - union { - WORD wValidBitsPerSample; /* bits of precision */ - WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */ - WORD wReserved; /* If neither applies, set to zero. */ - } Samples; - DWORD dwChannelMask; /* which channels are */ - /* present in stream */ - GUID SubFormat; -} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE; -#endif - -DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); -DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); -DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 ); - -/***************************************************************************** - * notification_thread_t: DirectX event thread - *****************************************************************************/ -typedef struct notification_thread_t -{ - VLC_COMMON_MEMBERS - - aout_instance_t *p_aout; - int i_frame_size; /* size in bytes of one frame */ - int i_write_slot; /* current write position in our circular buffer */ - - mtime_t start_date; - HANDLE event; +#include "windows_audio_common.h" -} notification_thread_t; +#define DS_BUF_SIZE (4*1024*1024) /***************************************************************************** * aout_sys_t: directx audio output method descriptor @@ -157,63 +48,55 @@ typedef struct notification_thread_t *****************************************************************************/ struct aout_sys_t { - HINSTANCE hdsound_dll; /* handle of the opened dsound dll */ + HINSTANCE hdsound_dll; /* handle of the opened dsound dll */ - int i_device_id; /* user defined device */ - LPGUID p_device_guid; - - LPDIRECTSOUND p_dsobject; /* main Direct Sound object */ + LPDIRECTSOUND p_dsobject; /* main Direct Sound object */ LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound * takes care of mixing all the * secondary buffers into the primary) */ - notification_thread_t *p_notif; /* DirectSoundThread id */ - - int b_playing; /* playing status */ - - int i_frame_size; /* Size in bytes of one frame */ + LPDIRECTSOUNDNOTIFY p_notify; + HANDLE hnotify_evt; + struct + { + float volume; + LONG mb; + bool mute; + } volume; - int i_speaker_setup; /* Speaker setup override */ + int i_bytes_per_sample; /* Size in bytes of one frame */ + int i_rate; /* Sample rate */ - bool b_chan_reorder; /* do we need channel reordering */ - int pi_chan_table[AOUT_CHAN_MAX]; + uint8_t chans_to_reorder; /* do we need channel reordering */ + uint8_t chan_table[AOUT_CHAN_MAX]; uint32_t i_channel_mask; - uint32_t i_bits_per_sample; - uint32_t i_channels; -}; + vlc_fourcc_t format; -static const uint32_t pi_channels_src[] = - { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, - AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, - AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_REARCENTER, - AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 }; -static const uint32_t pi_channels_in[] = - { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT, - SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, - SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, SPEAKER_BACK_CENTER, - SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, 0 }; -static const uint32_t pi_channels_out[] = - { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT, - SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, - SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, - SPEAKER_BACK_CENTER, - SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, 0 }; + size_t i_write; +}; /***************************************************************************** * Local prototypes. *****************************************************************************/ -static int OpenAudio ( vlc_object_t * ); -static void CloseAudio ( vlc_object_t * ); -static void Play ( aout_instance_t * ); +static int Open( vlc_object_t * ); +static void Close( vlc_object_t * ); +static void Stop( audio_output_t * ); +static void Play( audio_output_t *, block_t * ); +static int VolumeSet( audio_output_t *, float ); +static int MuteSet( audio_output_t *, bool ); +static void Flush( audio_output_t *, bool ); +static void Pause( audio_output_t *, bool, mtime_t ); +static int TimeGet( audio_output_t *, mtime_t *); /* local functions */ -static void Probe ( aout_instance_t * ); -static int InitDirectSound ( aout_instance_t * ); -static int CreateDSBuffer ( aout_instance_t *, int, int, int, int, int, bool ); -static int CreateDSBufferPCM ( aout_instance_t *, int*, int, int, int, bool ); -static void DestroyDSBuffer ( aout_instance_t * ); -static void* DirectSoundThread( vlc_object_t * ); -static int FillBuffer ( aout_instance_t *, int, aout_buffer_t * ); +static int InitDirectSound ( audio_output_t * ); +static int CreateDSBuffer ( audio_output_t *, int, int, int, int, bool ); +static int CreateDSBufferPCM ( audio_output_t *, vlc_fourcc_t*, int, int, bool ); +static void DestroyDSBuffer ( audio_output_t * ); +static int FillBuffer ( audio_output_t *, block_t * ); + +static int ReloadDirectXDevices( vlc_object_t *, const char *, + char ***, char *** ); /* Speaker setup override options list */ static const char *const speaker_list[] = { "Windows default", "Mono", "Stereo", @@ -223,33 +106,37 @@ static const char *const speaker_list[] = { "Windows default", "Mono", "Stereo", * Module descriptor *****************************************************************************/ #define DEVICE_TEXT N_("Output device") -#define DEVICE_LONGTEXT N_( \ - "DirectX device number: 0 default device, 1..N device by number" \ - "(Note that the default device appears as 0 AND another number)." ) -#define FLOAT_TEXT N_("Use float32 output") -#define FLOAT_LONGTEXT N_( \ - "The option allows you to enable or disable the high-quality float32 " \ - "audio output mode (which is not well supported by some soundcards)." ) -#define SPEAKER_TEXT N_("Select speaker configuration") +#define DEVICE_LONGTEXT N_("Select your audio output device") + +#define SPEAKER_TEXT N_("Speaker configuration") #define SPEAKER_LONGTEXT N_("Select speaker configuration you want to use. " \ "This option doesn't upmix! So NO e.g. Stereo -> 5.1 conversion." ) +#define VOLUME_TEXT N_("Audio volume") +#define VOLUME_LONGTEXT N_("Audio volume in hundredths of decibels (dB).") + vlc_module_begin () set_description( N_("DirectX audio output") ) set_shortname( "DirectX" ) set_capability( "audio output", 100 ) set_category( CAT_AUDIO ) set_subcategory( SUBCAT_AUDIO_AOUT ) - add_shortcut( "directx" ) - add_integer( "directx-audio-device", 0, NULL, DEVICE_TEXT, - DEVICE_LONGTEXT, true ) - add_bool( "directx-audio-float32", 0, 0, FLOAT_TEXT, + add_shortcut( "directx", "aout_directx" ) + + add_string( "directx-audio-device", NULL, + DEVICE_TEXT, DEVICE_LONGTEXT, false ) + change_string_cb( ReloadDirectXDevices ) + add_obsolete_string( "directx-audio-device-name") + add_bool( "directx-audio-float32", true, FLOAT_TEXT, FLOAT_LONGTEXT, true ) - add_string( "directx-audio-speaker", "Windows default", NULL, + add_string( "directx-audio-speaker", "Windows default", SPEAKER_TEXT, SPEAKER_LONGTEXT, true ) - change_string_list( speaker_list, 0, 0 ) - change_need_restart () - set_callbacks( OpenAudio, CloseAudio ) + change_string_list( speaker_list, speaker_list ) + add_float( "directx-volume", 1.0f, + VOLUME_TEXT, VOLUME_LONGTEXT, true ) + change_integer_range( DSBVOLUME_MIN, DSBVOLUME_MAX ) + + set_callbacks( Open, Close ) vlc_module_end () /***************************************************************************** @@ -257,30 +144,14 @@ vlc_module_end () ***************************************************************************** * This function opens and setups Direct Sound. *****************************************************************************/ -static int OpenAudio( vlc_object_t *p_this ) +static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt ) { - aout_instance_t * p_aout = (aout_instance_t *)p_this; - vlc_value_t val; char * psz_speaker; int i = 0; const char * const * ppsz_compare = speaker_list; - msg_Dbg( p_aout, "OpenAudio" ); - - /* Allocate structure */ - p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) ); - if( p_aout->output.p_sys == NULL ) - return VLC_ENOMEM; - - /* Initialize some variables */ - p_aout->output.p_sys->p_dsobject = NULL; - p_aout->output.p_sys->p_dsbuffer = NULL; - p_aout->output.p_sys->p_notif = NULL; - p_aout->output.p_sys->b_playing = 0; - - p_aout->output.pf_play = Play; - aout_VolumeSoftInit( p_aout ); + msg_Dbg( p_aout, "Opening DirectSound Audio Output" ); /* Retrieve config values */ var_Create( p_aout, "directx-audio-float32", @@ -298,17 +169,11 @@ static int OpenAudio( vlc_object_t *p_this ) if ( *ppsz_compare == NULL ) { - msg_Err( p_aout, "(%s) isn't valid speaker setup option", - psz_speaker ); + msg_Err( p_aout, "(%s) isn't valid speaker setup option", psz_speaker ); msg_Err( p_aout, "Defaulting to Windows default speaker config"); i = 0; } free( psz_speaker ); - p_aout->output.p_sys->i_speaker_setup = i; - - p_aout->output.p_sys->i_device_id = var_CreateGetInteger( p_aout, - "directx-audio-device" ); - p_aout->output.p_sys->p_device_guid = 0; /* Initialise DirectSound */ if( InitDirectSound( p_aout ) ) @@ -317,469 +182,231 @@ static int OpenAudio( vlc_object_t *p_this ) goto error; } - if( var_Type( p_aout, "audio-device" ) == 0 ) + if( i == 0 ) { - Probe( p_aout ); - } + DWORD ui_speaker_config; - if( var_Get( p_aout, "audio-device", &val ) < 0 ) - { - /* Probe() has failed. */ - goto error; + /* Check the speaker configuration to determine which channel config + * should be the default */ + if( FAILED( IDirectSound_GetSpeakerConfig( p_aout->sys->p_dsobject, + &ui_speaker_config ) ) ) + { + ui_speaker_config = DSSPEAKER_STEREO; + msg_Dbg( p_aout, "GetSpeakerConfig failed" ); + } + fmt->i_physical_channels = AOUT_CHANS_2_0; + + const char *name = "Unknown"; + switch( DSSPEAKER_CONFIG(ui_speaker_config) ) + { + case DSSPEAKER_7POINT1: + case DSSPEAKER_7POINT1_SURROUND: + name = "7.1"; + fmt->i_physical_channels = AOUT_CHANS_7_1; + break; + case DSSPEAKER_5POINT1: + case DSSPEAKER_5POINT1_SURROUND: + name = "5.1"; + fmt->i_physical_channels = AOUT_CHANS_5_1; + break; + case DSSPEAKER_QUAD: + name = "Quad"; + fmt->i_physical_channels = AOUT_CHANS_4_0; + break; +#if 0 /* Lots of people just get their settings wrong and complain that + * this is a problem with VLC so just don't ever set mono by default. */ + case DSSPEAKER_MONO: + name = "Mono"; + fmt->i_physical_channels = AOUT_CHAN_CENTER; + break; +#endif + case DSSPEAKER_SURROUND: /* XXX: stereo, really? -- Courmisch */ + name = "Surround"; + break; + case DSSPEAKER_STEREO: + name = "Stereo"; + break; + } + msg_Dbg( p_aout, "%s speaker config: %s", "Windows", name ); + } + else + { /* Overriden speaker configuration */ + const char *name = "Non-existant"; + switch( i ) + { + case 1: /* Mono */ + name = "Mono"; + fmt->i_physical_channels = AOUT_CHAN_CENTER; + break; + case 2: /* Stereo */ + name = "Stereo"; + fmt->i_physical_channels = AOUT_CHANS_2_0; + break; + case 3: /* Quad */ + name = "Quad"; + fmt->i_physical_channels = AOUT_CHANS_4_0; + break; + case 4: /* 5.1 */ + name = "5.1"; + fmt->i_physical_channels = AOUT_CHANS_5_1; + break; + case 5: /* 7.1 */ + name = "7.1"; + fmt->i_physical_channels = AOUT_CHANS_7_1; + break; + } + msg_Dbg( p_aout, "%s speaker config: %s", "VLC", name ); } /* Open the device */ - if( val.i_int == AOUT_VAR_SPDIF ) + if ( AOUT_FMT_SPDIF( fmt ) + && var_InheritBool( p_aout, "spdif" ) + && CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL, fmt->i_physical_channels, + aout_FormatNbChannels( fmt ), fmt->i_rate, true ) + == VLC_SUCCESS ) { - p_aout->output.output.i_format = VLC_CODEC_SPDIFL; + msg_Dbg( p_aout, "using A/52 pass-through over S/PDIF" ); + fmt->i_format = VLC_CODEC_SPDIFL; /* Calculate the frame size in bytes */ - p_aout->output.i_nb_samples = A52_FRAME_NB; - p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE; - p_aout->output.output.i_frame_length = A52_FRAME_NB; - p_aout->output.p_sys->i_frame_size = - p_aout->output.output.i_bytes_per_frame; - - if( CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL, - p_aout->output.output.i_physical_channels, - aout_FormatNbChannels( &p_aout->output.output ), - p_aout->output.output.i_rate, - p_aout->output.p_sys->i_frame_size, false ) - != VLC_SUCCESS ) - { - msg_Err( p_aout, "cannot open directx audio device" ); - free( p_aout->output.p_sys ); - return VLC_EGENERIC; - } - - aout_VolumeNoneInit( p_aout ); + fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE; + fmt->i_frame_length = A52_FRAME_NB; } else { - if( val.i_int == AOUT_VAR_5_1 ) - { - p_aout->output.output.i_physical_channels - = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER - | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT - | AOUT_CHAN_LFE; - } - else if( val.i_int == AOUT_VAR_7_1 ) - { - p_aout->output.output.i_physical_channels - = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER - | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT - | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT - | AOUT_CHAN_LFE; - } - else if( val.i_int == AOUT_VAR_3F2R ) - { - p_aout->output.output.i_physical_channels - = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER - | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; - } - else if( val.i_int == AOUT_VAR_2F2R ) - { - p_aout->output.output.i_physical_channels - = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT - | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; - } - else if( val.i_int == AOUT_VAR_MONO ) - { - p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER; - } - else - { - p_aout->output.output.i_physical_channels - = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; - } + aout_FormatPrepare( fmt ); - if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format, - p_aout->output.output.i_physical_channels, - aout_FormatNbChannels( &p_aout->output.output ), - p_aout->output.output.i_rate, false ) + if( CreateDSBufferPCM( p_aout, &fmt->i_format, + fmt->i_physical_channels, fmt->i_rate, false ) != VLC_SUCCESS ) { msg_Err( p_aout, "cannot open directx audio device" ); - free( p_aout->output.p_sys ); - return VLC_EGENERIC; + goto error; } - - /* Calculate the frame size in bytes */ - p_aout->output.i_nb_samples = FRAME_SIZE; - aout_FormatPrepare( &p_aout->output.output ); - aout_VolumeSoftInit( p_aout ); } + p_aout->sys->i_write = 0; - /* Now we need to setup our DirectSound play notification structure */ - p_aout->output.p_sys->p_notif = - vlc_object_create( p_aout, sizeof(notification_thread_t) ); - p_aout->output.p_sys->p_notif->p_aout = p_aout; - - p_aout->output.p_sys->p_notif->event = CreateEvent( 0, FALSE, FALSE, 0 ); - p_aout->output.p_sys->p_notif->i_frame_size = - p_aout->output.p_sys->i_frame_size; + /* Force volume update */ + VolumeSet( p_aout, p_aout->sys->volume.volume ); + MuteSet( p_aout, p_aout->sys->volume.mute ); /* then launch the notification thread */ - msg_Dbg( p_aout, "creating DirectSoundThread" ); - if( vlc_thread_create( p_aout->output.p_sys->p_notif, - "DirectSound Notification Thread", - DirectSoundThread, - VLC_THREAD_PRIORITY_HIGHEST ) ) - { - msg_Err( p_aout, "cannot create DirectSoundThread" ); - CloseHandle( p_aout->output.p_sys->p_notif->event ); - vlc_object_release( p_aout->output.p_sys->p_notif ); - p_aout->output.p_sys->p_notif = NULL; - goto error; - } - - vlc_object_attach( p_aout->output.p_sys->p_notif, p_aout ); + p_aout->time_get = TimeGet; + p_aout->play = Play; + p_aout->pause = Pause; + p_aout->flush = Flush; return VLC_SUCCESS; error: - CloseAudio( VLC_OBJECT(p_aout) ); + Stop( p_aout ); return VLC_EGENERIC; } /***************************************************************************** - * Probe: probe the audio device for available formats and channels + * Play: we'll start playing the directsound buffer here because at least here + * we know the first buffer has been put in the aout fifo and we also + * know its date. *****************************************************************************/ -static void Probe( aout_instance_t * p_aout ) +static void Play( audio_output_t *p_aout, block_t *p_buffer ) { - vlc_value_t val, text; - int i_format; - unsigned int i_physical_channels; - DWORD ui_speaker_config; - bool is_default_output_set = false; - - var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); - text.psz_string = _("Audio Device"); - var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL ); - - /* Test for 5.1 support */ - i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | - AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | - AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; - if( p_aout->output.output.i_physical_channels == i_physical_channels ) - { - if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 6, - p_aout->output.output.i_rate, true ) - == VLC_SUCCESS ) - { - val.i_int = AOUT_VAR_5_1; - text.psz_string = (char*) "5.1"; - var_Change( p_aout, "audio-device", - VLC_VAR_ADDCHOICE, &val, &text ); - var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL ); - is_default_output_set = true; - msg_Dbg( p_aout, "device supports 5.1 channels" ); - } - } - - /* Test for 7.1 support */ - i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | - AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | - AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | - AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; - if( p_aout->output.output.i_physical_channels == i_physical_channels ) - { - if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 8, - p_aout->output.output.i_rate, true ) - == VLC_SUCCESS ) - { - val.i_int = AOUT_VAR_7_1; - text.psz_string = (char*) "7.1"; - var_Change( p_aout, "audio-device", - VLC_VAR_ADDCHOICE, &val, &text ); - var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL ); - is_default_output_set = true; - msg_Dbg( p_aout, "device supports 7.1 channels" ); - } - } - - /* Test for 3 Front 2 Rear support */ - i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | - AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | - AOUT_CHAN_REARRIGHT; - if( p_aout->output.output.i_physical_channels == i_physical_channels ) - { - if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 5, - p_aout->output.output.i_rate, true ) - == VLC_SUCCESS ) - { - val.i_int = AOUT_VAR_3F2R; - text.psz_string = _("3 Front 2 Rear"); - var_Change( p_aout, "audio-device", - VLC_VAR_ADDCHOICE, &val, &text ); - if(!is_default_output_set) - { - var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL ); - is_default_output_set = true; - } - msg_Dbg( p_aout, "device supports 5 channels" ); - } - } - - /* Test for 2 Front 2 Rear support */ - i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | - AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; - if( ( p_aout->output.output.i_physical_channels & i_physical_channels ) - == i_physical_channels ) - { - if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 4, - p_aout->output.output.i_rate, true ) - == VLC_SUCCESS ) - { - val.i_int = AOUT_VAR_2F2R; - text.psz_string = _("2 Front 2 Rear"); - var_Change( p_aout, "audio-device", - VLC_VAR_ADDCHOICE, &val, &text ); - if(!is_default_output_set) - { - var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL ); - is_default_output_set = true; - } - msg_Dbg( p_aout, "device supports 4 channels" ); - } - } - - /* Test for stereo support */ - i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; - if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 2, - p_aout->output.output.i_rate, true ) - == VLC_SUCCESS ) + if( FillBuffer( p_aout, p_buffer ) == VLC_SUCCESS ) { - val.i_int = AOUT_VAR_STEREO; - text.psz_string = _("Stereo"); - var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); - if(!is_default_output_set) + /* start playing the buffer */ + HRESULT dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer, + 0, 0, DSBPLAY_LOOPING ); + if( dsresult == DSERR_BUFFERLOST ) { - var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL ); - is_default_output_set = true; - msg_Dbg( p_aout, "device supports 2 channels (DEFAULT!)" ); + IDirectSoundBuffer_Restore( p_aout->sys->p_dsbuffer ); + dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer, + 0, 0, DSBPLAY_LOOPING ); } - else msg_Dbg( p_aout, "device supports 2 channels" ); - } - - /* Test for mono support */ - i_physical_channels = AOUT_CHAN_CENTER; - if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 1, - p_aout->output.output.i_rate, true ) - == VLC_SUCCESS ) - { - val.i_int = AOUT_VAR_MONO; - text.psz_string = _("Mono"); - var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); - msg_Dbg( p_aout, "device supports 1 channel" ); + if( dsresult != DS_OK ) + msg_Err( p_aout, "cannot start playing buffer" ); } +} - /* Check the speaker configuration to determine which channel config should - * be the default */ - if FAILED( IDirectSound_GetSpeakerConfig( p_aout->output.p_sys->p_dsobject, - &ui_speaker_config ) ) - { - ui_speaker_config = DSSPEAKER_STEREO; - msg_Dbg( p_aout, "GetSpeakerConfig failed" ); - } - switch( DSSPEAKER_CONFIG(ui_speaker_config) ) - { - case DSSPEAKER_7POINT1: - msg_Dbg( p_aout, "Windows says your SpeakerConfig is 7.1" ); - val.i_int = AOUT_VAR_7_1; - break; - case DSSPEAKER_5POINT1: - msg_Dbg( p_aout, "Windows says your SpeakerConfig is 5.1" ); - val.i_int = AOUT_VAR_5_1; - break; - case DSSPEAKER_QUAD: - msg_Dbg( p_aout, "Windows says your SpeakerConfig is Quad" ); - val.i_int = AOUT_VAR_2F2R; - break; -#if 0 /* Lots of people just get their settings wrong and complain that - * this is a problem with VLC so just don't ever set mono by default. */ - case DSSPEAKER_MONO: - val.i_int = AOUT_VAR_MONO; - break; -#endif - case DSSPEAKER_SURROUND: - msg_Dbg( p_aout, "Windows says your SpeakerConfig is surround" ); - case DSSPEAKER_STEREO: - msg_Dbg( p_aout, "Windows says your SpeakerConfig is stereo" ); - default: - /* If nothing else is found, choose stereo output */ - val.i_int = AOUT_VAR_STEREO; - break; - } +static int VolumeSet( audio_output_t *p_aout, float volume ) +{ + aout_sys_t *sys = p_aout->sys; + int ret = 0; - /* Check if we want to override speaker config */ - switch( p_aout->output.p_sys->i_speaker_setup ) - { - case 0: /* Default value aka Windows default speaker setup */ - break; - case 1: /* Mono */ - msg_Dbg( p_aout, "SpeakerConfig is forced to Mono" ); - val.i_int = AOUT_VAR_MONO; - break; - case 2: /* Stereo */ - msg_Dbg( p_aout, "SpeakerConfig is forced to Stereo" ); - val.i_int = AOUT_VAR_STEREO; - break; - case 3: /* Quad */ - msg_Dbg( p_aout, "SpeakerConfig is forced to Quad" ); - val.i_int = AOUT_VAR_2F2R; - break; - case 4: /* 5.1 */ - msg_Dbg( p_aout, "SpeakerConfig is forced to 5.1" ); - val.i_int = AOUT_VAR_5_1; - break; - case 5: /* 7.1 */ - msg_Dbg( p_aout, "SpeakerConfig is forced to 7.1" ); - val.i_int = AOUT_VAR_7_1; - break; - default: - msg_Dbg( p_aout, "SpeakerConfig is forced to non-existing value" ); - break; - } + /* Convert UI volume to linear factor (cube) */ + float vol = volume * volume * volume; - var_Set( p_aout, "audio-device", val ); + /* millibels from linear amplification */ + LONG mb = lroundf(2000.f * log10f(vol)); - /* Test for SPDIF support */ - if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) + /* Clamp to allowed DirectSound range */ + static_assert( DSBVOLUME_MIN < DSBVOLUME_MAX, "DSBVOLUME_* confused" ); + if( mb > DSBVOLUME_MAX ) { - if( CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL, - p_aout->output.output.i_physical_channels, - aout_FormatNbChannels( &p_aout->output.output ), - p_aout->output.output.i_rate, - AOUT_SPDIF_SIZE, true ) - == VLC_SUCCESS ) - { - msg_Dbg( p_aout, "device supports A/52 over S/PDIF" ); - val.i_int = AOUT_VAR_SPDIF; - text.psz_string = _("A/52 over S/PDIF"); - var_Change( p_aout, "audio-device", - VLC_VAR_ADDCHOICE, &val, &text ); - if( config_GetInt( p_aout, "spdif" ) ) - var_Set( p_aout, "audio-device", val ); - } + mb = DSBVOLUME_MAX; + ret = -1; } + if( mb <= DSBVOLUME_MIN ) + mb = DSBVOLUME_MIN; - var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL ); - if( val.i_int <= 0 ) - { - /* Probe() has failed. */ - var_Destroy( p_aout, "audio-device" ); - return; - } + sys->volume.mb = mb; + sys->volume.volume = volume; + if( !sys->volume.mute && sys->p_dsbuffer && + IDirectSoundBuffer_SetVolume( sys->p_dsbuffer, mb ) != DS_OK ) + return -1; + /* Convert back to UI volume */ + aout_VolumeReport( p_aout, volume ); - var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL ); - var_SetBool( p_aout, "intf-change", true ); + if( var_InheritBool( p_aout, "volume-save" ) ) + config_PutFloat( p_aout, "directx-volume", volume ); + return ret; } -/***************************************************************************** - * Play: we'll start playing the directsound buffer here because at least here - * we know the first buffer has been put in the aout fifo and we also - * know its date. - *****************************************************************************/ -static void Play( aout_instance_t *p_aout ) +static int MuteSet( audio_output_t *p_aout, bool mute ) { - if( !p_aout->output.p_sys->b_playing ) - { - aout_buffer_t *p_buffer; - int i; + HRESULT res = DS_OK; + aout_sys_t *sys = p_aout->sys; - p_aout->output.p_sys->b_playing = 1; + sys->volume.mute = mute; - /* get the playing date of the first aout buffer */ - p_aout->output.p_sys->p_notif->start_date = - aout_FifoFirstDate( p_aout, &p_aout->output.fifo ); + if( sys->p_dsbuffer ) + res = IDirectSoundBuffer_SetVolume( sys->p_dsbuffer, + mute? DSBVOLUME_MIN : sys->volume.mb ); - /* fill in the first samples */ - for( i = 0; i < FRAMES_NUM; i++ ) - { - p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo ); - if( !p_buffer ) break; - FillBuffer( p_aout, i, p_buffer ); - } - - /* wake up the audio output thread */ - SetEvent( p_aout->output.p_sys->p_notif->event ); - } + aout_MuteReport( p_aout, mute ); + return (res != DS_OK); } /***************************************************************************** * CloseAudio: close the audio device *****************************************************************************/ -static void CloseAudio( vlc_object_t *p_this ) +static void Stop( audio_output_t *p_aout ) { - aout_instance_t * p_aout = (aout_instance_t *)p_this; - aout_sys_t *p_sys = p_aout->output.p_sys; - + aout_sys_t *p_sys = p_aout->sys; msg_Dbg( p_aout, "closing audio device" ); - /* kill the position notification thread, if any */ - if( p_sys->p_notif ) - { - vlc_object_detach( p_sys->p_notif ); - vlc_object_kill( p_sys->p_notif ); - /* wake up the audio thread if needed */ - if( !p_sys->b_playing ) SetEvent( p_sys->p_notif->event ); - - vlc_thread_join( p_sys->p_notif ); - vlc_object_release( p_sys->p_notif ); - } + if( p_sys->p_notify ) + IDirectSoundNotify_Release(p_sys->p_notify ); + p_sys->p_notify = NULL; + IDirectSoundBuffer_Stop( p_sys->p_dsbuffer ); /* release the secondary buffer */ DestroyDSBuffer( p_aout ); /* finally release the DirectSound object */ - if( p_sys->p_dsobject ) IDirectSound_Release( p_sys->p_dsobject ); - - /* free DSOUND.DLL */ - if( p_sys->hdsound_dll ) FreeLibrary( p_sys->hdsound_dll ); - - free( p_aout->output.p_sys->p_device_guid ); - free( p_sys ); -} - -/***************************************************************************** - * CallBackDirectSoundEnum: callback to enumerate available devices - *****************************************************************************/ -static int CALLBACK CallBackDirectSoundEnum( LPGUID p_guid, LPCSTR psz_desc, - LPCSTR psz_mod, LPVOID _p_aout ) -{ - aout_instance_t *p_aout = (aout_instance_t *)_p_aout; - - msg_Dbg( p_aout, "found device: %s", psz_desc ); - - if( p_aout->output.p_sys->i_device_id == 0 && p_guid ) - { - p_aout->output.p_sys->p_device_guid = malloc( sizeof( GUID ) ); - *p_aout->output.p_sys->p_device_guid = *p_guid; - msg_Dbg( p_aout, "using device: %s", psz_desc ); - } - - p_aout->output.p_sys->i_device_id--; - return 1; + if( p_sys->p_dsobject ) + IDirectSound_Release( p_sys->p_dsobject ); } /***************************************************************************** * InitDirectSound: handle all the gory details of DirectSound initialisation *****************************************************************************/ -static int InitDirectSound( aout_instance_t *p_aout ) +static int InitDirectSound( audio_output_t *p_aout ) { + aout_sys_t *sys = p_aout->sys; + GUID guid, *p_guid = NULL; HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); - HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACK, LPVOID); - - p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL"); - if( p_aout->output.p_sys->hdsound_dll == NULL ) - { - msg_Warn( p_aout, "cannot open DSOUND.DLL" ); - goto error; - } OurDirectSoundCreate = (void *) - GetProcAddress( p_aout->output.p_sys->hdsound_dll, + GetProcAddress( p_aout->sys->hdsound_dll, "DirectSoundCreate" ); if( OurDirectSoundCreate == NULL ) { @@ -787,24 +414,21 @@ static int InitDirectSound( aout_instance_t *p_aout ) goto error; } - /* Get DirectSoundEnumerate */ - OurDirectSoundEnumerate = (void *) - GetProcAddress( p_aout->output.p_sys->hdsound_dll, - "DirectSoundEnumerateA" ); - if( OurDirectSoundEnumerate ) + char *dev = var_GetNonEmptyString( p_aout, "directx-audio-device" ); + if( dev != NULL ) { - /* Attempt enumeration */ - if( FAILED( OurDirectSoundEnumerate( CallBackDirectSoundEnum, - p_aout ) ) ) - { - msg_Dbg( p_aout, "enumeration of DirectSound devices failed" ); - } + LPOLESTR lpsz = ToWide( dev ); + free( dev ); + + if( SUCCEEDED( IIDFromString( lpsz, &guid ) ) ) + p_guid = &guid; + else + msg_Err( p_aout, "bad device GUID: %ls", lpsz ); + free( lpsz ); } /* Create the direct sound object */ - if FAILED( OurDirectSoundCreate( p_aout->output.p_sys->p_device_guid, - &p_aout->output.p_sys->p_dsobject, - NULL ) ) + if FAILED( OurDirectSoundCreate( p_guid, &sys->p_dsobject, NULL ) ) { msg_Warn( p_aout, "cannot create a direct sound device" ); goto error; @@ -820,22 +444,18 @@ static int InitDirectSound( aout_instance_t *p_aout ) * sound without any video, and so what window handle should we use ??? * The hack for now is to use the Desktop window handle - it seems to be * working */ - if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject, +#if !VLC_WINSTORE_APP + if( IDirectSound_SetCooperativeLevel( p_aout->sys->p_dsobject, GetDesktopWindow(), DSSCL_EXCLUSIVE) ) { msg_Warn( p_aout, "cannot set direct sound cooperative level" ); } - +#endif return VLC_SUCCESS; error: - p_aout->output.p_sys->p_dsobject = NULL; - if( p_aout->output.p_sys->hdsound_dll ) - { - FreeLibrary( p_aout->output.p_sys->hdsound_dll ); - p_aout->output.p_sys->hdsound_dll = NULL; - } + sys->p_dsobject = NULL; return VLC_EGENERIC; } @@ -852,21 +472,18 @@ static int InitDirectSound( aout_instance_t *p_aout ) * Once you create a secondary buffer, you cannot change its format anymore so * you have to release the current one and create another. *****************************************************************************/ -static int CreateDSBuffer( aout_instance_t *p_aout, int i_format, +static int CreateDSBuffer( audio_output_t *p_aout, int i_format, int i_channels, int i_nb_channels, int i_rate, - int i_bytes_per_frame, bool b_probe ) + bool b_probe ) { WAVEFORMATEXTENSIBLE waveformat; DSBUFFERDESC dsbdesc; - unsigned int i; /* First set the sound buffer format */ waveformat.dwChannelMask = 0; - for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ ) - { - if( i_channels & pi_channels_src[i] ) + for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ ) + if( i_channels & pi_vlc_chan_order_wg4[i] ) waveformat.dwChannelMask |= pi_channels_in[i]; - } switch( i_format ) { @@ -889,7 +506,7 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format, waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; break; - case VLC_CODEC_S16L: + case VLC_CODEC_S16N: waveformat.Format.wBitsPerSample = 16; waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample; @@ -905,14 +522,16 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format, waveformat.Format.nAvgBytesPerSec = waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign; - p_aout->output.p_sys->i_bits_per_sample = waveformat.Format.wBitsPerSample; - p_aout->output.p_sys->i_channels = i_nb_channels; + p_aout->sys->i_bytes_per_sample = waveformat.Format.nBlockAlign; + p_aout->sys->format = i_format; /* Then fill in the direct sound descriptor */ memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); - dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */ - | DSBCAPS_GLOBALFOCUS; /* Allows background playing */ + dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /* Better position accuracy */ + | DSBCAPS_GLOBALFOCUS /* Allows background playing */ + | DSBCAPS_CTRLVOLUME /* Allows volume control */ + | DSBCAPS_CTRLPOSITIONNOTIFY; /* Allow position notifications */ /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */ if( i_nb_channels <= 2 ) @@ -929,20 +548,20 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format, dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE; } - dsbdesc.dwBufferBytes = FRAMES_NUM * i_bytes_per_frame; /* buffer size */ + dsbdesc.dwBufferBytes = DS_BUF_SIZE; /* buffer size */ dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat; if FAILED( IDirectSound_CreateSoundBuffer( - p_aout->output.p_sys->p_dsobject, &dsbdesc, - &p_aout->output.p_sys->p_dsbuffer, NULL) ) + p_aout->sys->p_dsobject, &dsbdesc, + &p_aout->sys->p_dsbuffer, NULL) ) { if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE ) { /* Try without DSBCAPS_LOCHARDWARE */ dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE; if FAILED( IDirectSound_CreateSoundBuffer( - p_aout->output.p_sys->p_dsobject, &dsbdesc, - &p_aout->output.p_sys->p_dsbuffer, NULL) ) + p_aout->sys->p_dsobject, &dsbdesc, + &p_aout->sys->p_dsbuffer, NULL) ) { return VLC_EGENERIC; } @@ -958,23 +577,34 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format, /* Stop here if we were just probing */ if( b_probe ) { - IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer ); - p_aout->output.p_sys->p_dsbuffer = NULL; + IDirectSoundBuffer_Release( p_aout->sys->p_dsbuffer ); + p_aout->sys->p_dsbuffer = NULL; return VLC_SUCCESS; } - p_aout->output.p_sys->i_frame_size = i_bytes_per_frame; - p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask; - p_aout->output.p_sys->b_chan_reorder = + p_aout->sys->i_rate = i_rate; + p_aout->sys->i_channel_mask = waveformat.dwChannelMask; + p_aout->sys->chans_to_reorder = aout_CheckChannelReorder( pi_channels_in, pi_channels_out, - waveformat.dwChannelMask, i_nb_channels, - p_aout->output.p_sys->pi_chan_table ); - - if( p_aout->output.p_sys->b_chan_reorder ) + waveformat.dwChannelMask, + p_aout->sys->chan_table ); + if( p_aout->sys->chans_to_reorder ) { msg_Dbg( p_aout, "channel reordering needed" ); } + if( IDirectSoundBuffer_QueryInterface( p_aout->sys->p_dsbuffer, + &IID_IDirectSoundNotify, + (void **) &p_aout->sys->p_notify ) + != DS_OK ) + { + + msg_Err(p_aout, "Couldn't query IDirectSoundNotify"); + p_aout->sys->p_notify = NULL; + } + + FillBuffer(p_aout,NULL); + return VLC_SUCCESS; } @@ -984,28 +614,25 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format, * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer. ****************************************************************************/ -static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format, - int i_channels, int i_nb_channels, int i_rate, - bool b_probe ) +static int CreateDSBufferPCM( audio_output_t *p_aout, vlc_fourcc_t *i_format, + int i_channels, int i_rate, bool b_probe ) { - /* Float32 audio samples are not supported for 5.1 output on the emu101k */ + unsigned i_nb_channels = popcount( i_channels ); + if( !var_GetBool( p_aout, "directx-audio-float32" ) || - i_nb_channels > 2 || CreateDSBuffer( p_aout, VLC_CODEC_FL32, - i_channels, i_nb_channels, i_rate, - FRAME_SIZE * 4 * i_nb_channels, b_probe ) + i_channels, i_nb_channels, i_rate, b_probe ) != VLC_SUCCESS ) { - if ( CreateDSBuffer( p_aout, VLC_CODEC_S16L, - i_channels, i_nb_channels, i_rate, - FRAME_SIZE * 2 * i_nb_channels, b_probe ) + if ( CreateDSBuffer( p_aout, VLC_CODEC_S16N, + i_channels, i_nb_channels, i_rate, b_probe ) != VLC_SUCCESS ) { return VLC_EGENERIC; } else { - *i_format = VLC_CODEC_S16L; + *i_format = VLC_CODEC_S16N; return VLC_SUCCESS; } } @@ -1021,12 +648,12 @@ static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format, ***************************************************************************** * This function destroys the secondary buffer. *****************************************************************************/ -static void DestroyDSBuffer( aout_instance_t *p_aout ) +static void DestroyDSBuffer( audio_output_t *p_aout ) { - if( p_aout->output.p_sys->p_dsbuffer ) + if( p_aout->sys->p_dsbuffer ) { - IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer ); - p_aout->output.p_sys->p_dsbuffer = NULL; + IDirectSoundBuffer_Release( p_aout->sys->p_dsbuffer ); + p_aout->sys->p_dsbuffer = NULL; } } @@ -1035,32 +662,32 @@ static void DestroyDSBuffer( aout_instance_t *p_aout ) ***************************************************************************** * Returns VLC_SUCCESS on success. *****************************************************************************/ -static int FillBuffer( aout_instance_t *p_aout, int i_frame, - aout_buffer_t *p_buffer ) +static int FillBuffer( audio_output_t *p_aout, block_t *p_buffer ) { - notification_thread_t *p_notif = p_aout->output.p_sys->p_notif; - aout_sys_t *p_sys = p_aout->output.p_sys; + aout_sys_t *p_sys = p_aout->sys; + + size_t towrite = (p_buffer)?p_buffer->i_buffer:DS_BUF_SIZE; void *p_write_position, *p_wrap_around; - long l_bytes1, l_bytes2; + unsigned long l_bytes1, l_bytes2; HRESULT dsresult; /* Before copying anything, we have to lock the buffer */ dsresult = IDirectSoundBuffer_Lock( - p_sys->p_dsbuffer, /* DS buffer */ - i_frame * p_notif->i_frame_size, /* Start offset */ - p_notif->i_frame_size, /* Number of bytes */ - &p_write_position, /* Address of lock start */ - &l_bytes1, /* Count of bytes locked before wrap around */ - &p_wrap_around, /* Buffer adress (if wrap around) */ - &l_bytes2, /* Count of bytes after wrap around */ - 0 ); /* Flags */ + p_sys->p_dsbuffer, /* DS buffer */ + p_aout->sys->i_write, /* Start offset */ + towrite, /* Number of bytes */ + &p_write_position, /* Address of lock start */ + &l_bytes1, /* Count of bytes locked before wrap around */ + &p_wrap_around, /* Buffer address (if wrap around) */ + &l_bytes2, /* Count of bytes after wrap around */ + 0 ); /* Flags: DSBLOCK_FROMWRITECURSOR is buggy */ if( dsresult == DSERR_BUFFERLOST ) { IDirectSoundBuffer_Restore( p_sys->p_dsbuffer ); dsresult = IDirectSoundBuffer_Lock( p_sys->p_dsbuffer, - i_frame * p_notif->i_frame_size, - p_notif->i_frame_size, + p_aout->sys->i_write, + towrite, &p_write_position, &l_bytes1, &p_wrap_around, @@ -1069,8 +696,8 @@ static int FillBuffer( aout_instance_t *p_aout, int i_frame, } if( dsresult != DS_OK ) { - msg_Warn( p_notif, "cannot lock buffer" ); - if( p_buffer ) aout_BufferFree( p_buffer ); + msg_Warn( p_aout, "cannot lock buffer" ); + if( p_buffer ) block_Release( p_buffer ); return VLC_EGENERIC; } @@ -1080,133 +707,227 @@ static int FillBuffer( aout_instance_t *p_aout, int i_frame, } else { - if( p_sys->b_chan_reorder ) - { + if( p_sys->chans_to_reorder ) /* Do the channel reordering here */ - aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_nb_bytes, - p_sys->i_channels, p_sys->pi_chan_table, - p_sys->i_bits_per_sample ); - } + aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer, + p_sys->chans_to_reorder, p_sys->chan_table, + p_sys->format ); - vlc_memcpy( p_write_position, p_buffer->p_buffer, l_bytes1 ); - aout_BufferFree( p_buffer ); + memcpy( p_write_position, p_buffer->p_buffer, l_bytes1 ); + if( l_bytes1 < p_buffer->i_buffer) + memcpy(p_wrap_around, p_buffer->p_buffer + l_bytes1, l_bytes2); + block_Release( p_buffer ); } /* Now the data has been copied, unlock the buffer */ IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1, p_wrap_around, l_bytes2 ); - p_notif->i_write_slot = (i_frame + 1) % FRAMES_NUM; + p_sys->i_write += towrite; + p_sys->i_write %= DS_BUF_SIZE; + return VLC_SUCCESS; } +typedef struct +{ + unsigned count; + char **ids; + char **names; +} ds_list_t; + +static int CALLBACK DeviceEnumCallback( LPGUID guid, LPCWSTR desc, + LPCWSTR mod, LPVOID data ) +{ + ds_list_t *list = data; + OLECHAR buf[48]; + + StringFromGUID2( guid, buf, 48 ); + + list->count++; + list->ids = xrealloc( list->ids, list->count * sizeof(char *) ); + list->names = xrealloc( list->names, list->count * sizeof(char *) ); + list->ids[list->count - 1] = FromWide( buf ); + list->names[list->count - 1] = FromWide( desc ); + if( list->ids == NULL || list->names == NULL ) + abort(); + + (void) mod; + return true; +} + /***************************************************************************** - * DirectSoundThread: this thread will capture play notification events. - ***************************************************************************** - * We use this thread to emulate a callback mechanism. The thread probes for - * event notification and fills up the DS secondary buffer when needed. + * ReloadDirectXDevices: store the list of devices in preferences *****************************************************************************/ -static void* DirectSoundThread( vlc_object_t *p_this ) +static int ReloadDirectXDevices( vlc_object_t *p_this, char const *psz_name, + char ***values, char ***descs ) { - notification_thread_t *p_notif = (notification_thread_t*)p_this; - aout_instance_t *p_aout = p_notif->p_aout; - bool b_sleek; - mtime_t last_time; - HRESULT dsresult; - long l_queued = 0; - int canc = vlc_savecancel (); + ds_list_t list = { + .count = 1, + .ids = xmalloc(sizeof (char *)), + .names = xmalloc(sizeof (char *)), + }; + list.ids[0] = xstrdup(""); + list.names[0] = xstrdup(_("Default")); - /* We don't want any resampling when using S/PDIF output */ - b_sleek = p_aout->output.output.i_format == VLC_CODEC_SPDIFL; + (void) psz_name; - msg_Dbg( p_notif, "DirectSoundThread ready" ); + HANDLE hdsound_dll = LoadLibrary(_T("DSOUND.DLL")); + if( hdsound_dll == NULL ) + { + msg_Warn( p_this, "cannot open DSOUND.DLL" ); + goto out; + } - /* Wait here until Play() is called */ - WaitForSingleObject( p_notif->event, INFINITE ); + /* Get DirectSoundEnumerate */ + HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKW, LPVOID) = + (void *)GetProcAddress( hdsound_dll, "DirectSoundEnumerateW" ); + if( OurDirectSoundEnumerate != NULL ) + { + OurDirectSoundEnumerate( DeviceEnumCallback, &list ); + msg_Dbg( p_this, "found %u devices", list.count ); + } + FreeLibrary(hdsound_dll); - if( vlc_object_alive (p_notif) ) +out: + *values = list.ids; + *descs = list.names; + return list.count; +} + +static int DeviceSelect (audio_output_t *aout, const char *id) +{ + var_SetString(aout, "directx-audio-device", (id != NULL) ? id : ""); + aout_DeviceReport (aout, id); + aout_RestartRequest (aout, AOUT_RESTART_OUTPUT); + return 0; +} + +static int Open(vlc_object_t *obj) +{ + audio_output_t *aout = (audio_output_t *)obj; + aout_sys_t *sys = calloc(1, sizeof (*sys)); + if (unlikely(sys == NULL)) + return VLC_ENOMEM; + + sys->hdsound_dll = LoadLibrary(_T("DSOUND.DLL")); + if (sys->hdsound_dll == NULL) { - mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 ); + msg_Warn(aout, "cannot open DSOUND.DLL"); + free(sys); + return VLC_EGENERIC; + } - /* start playing the buffer */ - dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer, - 0, /* Unused */ - 0, /* Unused */ - DSBPLAY_LOOPING ); /* Flags */ - if( dsresult == DSERR_BUFFERLOST ) - { - IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer ); - dsresult = IDirectSoundBuffer_Play( - p_aout->output.p_sys->p_dsbuffer, - 0, /* Unused */ - 0, /* Unused */ - DSBPLAY_LOOPING ); /* Flags */ - } - if( dsresult != DS_OK ) + aout->sys = sys; + aout->start = Start; + aout->stop = Stop; + aout->volume_set = VolumeSet; + aout->mute_set = MuteSet; + aout->device_select = DeviceSelect; + + /* Volume */ + sys->volume.volume = var_InheritFloat(aout, "directx-volume"); + aout_VolumeReport(aout, sys->volume.volume ); + MuteSet(aout, var_InheritBool(aout, "mute")); + + sys->hnotify_evt = CreateEvent(NULL, FALSE, TRUE, NULL); + if( !sys->hnotify_evt ) + { + msg_Err(aout, "cannot create Event"); + FreeLibrary(sys->hdsound_dll); + free(sys); + return VLC_EGENERIC; + } + + /* DirectSound does not support hot-plug events (unless with WASAPI) */ + char **ids, **names; + int count = ReloadDirectXDevices(obj, NULL, &ids, &names); + if (count >= 0) + { + for (int i = 0; i < count; i++) { - msg_Err( p_aout, "cannot start playing buffer" ); + aout_HotplugReport(aout, ids[i], names[i]); + free(names[i]); + free(ids[i]); } + free(names); + free(ids); } - last_time = mdate(); - while( vlc_object_alive (p_notif) ) - { - long l_read, l_free_slots; - mtime_t mtime = mdate(); - int i; + char *dev = var_CreateGetNonEmptyString(aout, "directx-audio-device"); + aout_DeviceReport(aout, dev); + free(dev); - /* - * Fill in as much audio data as we can in our circular buffer - */ + return VLC_SUCCESS; +} - /* Find out current play position */ - if FAILED( IDirectSoundBuffer_GetCurrentPosition( - p_aout->output.p_sys->p_dsbuffer, &l_read, NULL ) ) - { - msg_Err( p_aout, "GetCurrentPosition() failed!" ); - l_read = 0; - } +static void Close(vlc_object_t *obj) +{ + audio_output_t *aout = (audio_output_t *)obj; + aout_sys_t *sys = aout->sys; + + var_Destroy(aout, "directx-audio-device"); + CloseHandle(sys->hnotify_evt); + FreeLibrary(sys->hdsound_dll); /* free DSOUND.DLL */ + free(sys); +} - /* Detect underruns */ - if( l_queued && mtime - last_time > - INT64_C(1000000) * l_queued / p_aout->output.output.i_rate ) +static void Flush ( audio_output_t * aout, bool drain ) +{ + aout_sys_t *sys = aout->sys; + size_t read; + if( drain ) + { + if( sys->p_notify ) { - msg_Dbg( p_aout, "detected underrun!" ); + DSBPOSITIONNOTIFY notif = {.dwOffset = aout->sys->i_write, .hEventNotify = sys->hnotify_evt } ; + if( IDirectSoundNotify_SetNotificationPositions( sys->p_notify, 1, ¬if ) == DS_OK ) + { + WaitForSingleObject( sys->hnotify_evt, INFINITE ); + IDirectSoundBuffer_Stop( aout->sys->p_dsbuffer ); + } } - last_time = mtime; - - /* Try to fill in as many frame buffers as possible */ - l_read /= p_aout->output.output.i_bytes_per_frame; - l_queued = p_notif->i_write_slot * FRAME_SIZE - l_read; - if( l_queued < 0 ) l_queued += (FRAME_SIZE * FRAMES_NUM); - l_free_slots = (FRAMES_NUM * FRAME_SIZE - l_queued) / FRAME_SIZE; + else + while( IDirectSoundBuffer_GetCurrentPosition( aout->sys->p_dsbuffer,(LPDWORD) &read, NULL) == DS_OK ) + { + read %= DS_BUF_SIZE; + if( read == aout->sys->i_write ) + break; + msleep(10000); + } + } + else + { + IDirectSoundBuffer_Stop( aout->sys->p_dsbuffer ); + IDirectSoundBuffer_SetCurrentPosition( aout->sys->p_dsbuffer, + aout->sys->i_write ); + } +} - for( i = 0; i < l_free_slots; i++ ) - { - aout_buffer_t *p_buffer = aout_OutputNextBuffer( p_aout, - mtime + INT64_C(1000000) * (i * FRAME_SIZE + l_queued) / - p_aout->output.output.i_rate, b_sleek ); +static void Pause( audio_output_t * aout, bool pause, mtime_t date ) +{ + (void) date; + if( pause ) + IDirectSoundBuffer_Stop( aout->sys->p_dsbuffer ); + else + IDirectSoundBuffer_Play( aout->sys->p_dsbuffer, 0, 0, DSBPLAY_LOOPING ); +} - /* If there is no audio data available and we have some buffered - * already, then just wait for the next time */ - if( !p_buffer && (i || l_queued / FRAME_SIZE) ) break; - if( FillBuffer( p_aout, p_notif->i_write_slot % FRAMES_NUM, - p_buffer ) != VLC_SUCCESS ) break; - } +static int TimeGet( audio_output_t * aout, mtime_t * delay ) +{ + uint32_t read; + mtime_t size; - /* Sleep a reasonable amount of time */ - l_queued += (i * FRAME_SIZE); - msleep( INT64_C(1000000) * l_queued / p_aout->output.output.i_rate / 2 ); - } + if( IDirectSoundBuffer_GetCurrentPosition( aout->sys->p_dsbuffer, (LPDWORD) &read, NULL) != DS_OK ) + return 1; - /* make sure the buffer isn't playing */ - IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer ); + read %= DS_BUF_SIZE; - /* free the event */ - CloseHandle( p_notif->event ); + size = (mtime_t)aout->sys->i_write - (mtime_t) read; + if( size < 0 ) + size += DS_BUF_SIZE; - vlc_restorecancel (canc); - msg_Dbg( p_notif, "DirectSoundThread exiting" ); - return NULL; + *delay = ( size / aout->sys->i_bytes_per_sample ) * CLOCK_FREQ / aout->sys->i_rate; + return 0; }