X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Fdirectx.c;h=32cdf840e0b1934dbb68c984ef258cc0ec4c272b;hb=f6c636aeaff535dcab1e8e99e7c40eca874ec6bf;hp=ea2b99ca1edc9ecae0f8b82decc6082006c58f5d;hpb=c19df7839cb08fb07d349d7ac9bfeaebc6d9d18c;p=vlc diff --git a/modules/audio_output/directx.c b/modules/audio_output/directx.c index ea2b99ca1e..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-2009 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 + * 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., - * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -35,25 +35,10 @@ #include #include #include -#include #include "windows_audio_common.h" -/***************************************************************************** - * notification_thread_t: DirectX event thread - *****************************************************************************/ -typedef struct notification_thread_t -{ - 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; - - vlc_thread_t thread; - vlc_atomic_t abort; - -} notification_thread_t; +#define DS_BUF_SIZE (4*1024*1024) /***************************************************************************** * aout_sys_t: directx audio output method descriptor @@ -63,31 +48,31 @@ typedef struct notification_thread_t *****************************************************************************/ struct aout_sys_t { - aout_packet_t packet; - HINSTANCE hdsound_dll; /* handle of the opened dsound dll */ + HINSTANCE hdsound_dll; /* handle of the opened dsound dll */ - 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) */ + + LPDIRECTSOUNDNOTIFY p_notify; + HANDLE hnotify_evt; struct { - LONG volume; + float volume; LONG mb; bool mute; } volume; - notification_thread_t notif; /* DirectSoundThread id */ - - int i_frame_size; /* Size in bytes of one frame */ + int i_bytes_per_sample; /* Size in bytes of one frame */ + int i_rate; /* Sample rate */ - int i_speaker_setup; /* Speaker setup override */ - - uint8_t chans_to_reorder; /* do we need channel reordering */ + 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; + + size_t i_write; }; /***************************************************************************** @@ -96,18 +81,19 @@ struct aout_sys_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 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( audio_output_t *, const audio_sample_format_t * ); static int InitDirectSound ( audio_output_t * ); -static int CreateDSBuffer ( audio_output_t *, int, int, int, int, int, bool ); +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 void* DirectSoundThread( void * ); -static int FillBuffer ( audio_output_t *, int, block_t * ); +static int FillBuffer ( audio_output_t *, block_t * ); static int ReloadDirectXDevices( vlc_object_t *, const char *, char ***, char *** ); @@ -141,12 +127,12 @@ vlc_module_begin () DEVICE_TEXT, DEVICE_LONGTEXT, false ) change_string_cb( ReloadDirectXDevices ) add_obsolete_string( "directx-audio-device-name") - add_bool( "directx-audio-float32", false, FLOAT_TEXT, + add_bool( "directx-audio-float32", true, FLOAT_TEXT, FLOAT_LONGTEXT, true ) add_string( "directx-audio-speaker", "Windows default", SPEAKER_TEXT, SPEAKER_LONGTEXT, true ) change_string_list( speaker_list, speaker_list ) - add_integer( "directx-volume", DSBVOLUME_MAX, + add_float( "directx-volume", 1.0f, VOLUME_TEXT, VOLUME_LONGTEXT, true ) change_integer_range( DSBVOLUME_MIN, DSBVOLUME_MAX ) @@ -160,7 +146,6 @@ vlc_module_end () *****************************************************************************/ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt ) { - vlc_value_t val; char * psz_speaker; int i = 0; @@ -189,7 +174,6 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt ) i = 0; } free( psz_speaker ); - p_aout->sys->i_speaker_setup = i; /* Initialise DirectSound */ if( InitDirectSound( p_aout ) ) @@ -198,54 +182,98 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt ) goto error; } - if( var_Type( p_aout, "audio-device" ) == 0 ) + if( i == 0 ) { - Probe( p_aout, fmt ); - } + DWORD ui_speaker_config; - if( var_Get( p_aout, "audio-device", &val ) < 0 ) - { - msg_Err( p_aout, "DirectSound Probe 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 ) { + msg_Dbg( p_aout, "using A/52 pass-through over S/PDIF" ); fmt->i_format = VLC_CODEC_SPDIFL; /* Calculate the frame size in bytes */ fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE; fmt->i_frame_length = A52_FRAME_NB; - p_aout->sys->i_frame_size = fmt->i_bytes_per_frame; - - if( CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL, - fmt->i_physical_channels, - aout_FormatNbChannels( fmt ), fmt->i_rate, - p_aout->sys->i_frame_size, false ) - != VLC_SUCCESS ) - { - msg_Err( p_aout, "cannot open directx audio device" ); - goto error; - } - - aout_PacketInit( p_aout, &p_aout->sys->packet, A52_FRAME_NB, fmt ); } else { - if( val.i_int == AOUT_VAR_5_1 ) - fmt->i_physical_channels = AOUT_CHANS_5_0; - else if( val.i_int == AOUT_VAR_7_1 ) - fmt->i_physical_channels = AOUT_CHANS_7_1; - else if( val.i_int == AOUT_VAR_3F2R ) - fmt->i_physical_channels = AOUT_CHANS_5_0; - else if( val.i_int == AOUT_VAR_2F2R ) - fmt->i_physical_channels = AOUT_CHANS_4_0; - else if( val.i_int == AOUT_VAR_MONO ) - fmt->i_physical_channels = AOUT_CHAN_CENTER; - else - fmt->i_physical_channels = AOUT_CHANS_2_0; - aout_FormatPrepare( fmt ); if( CreateDSBufferPCM( p_aout, &fmt->i_format, @@ -255,37 +283,19 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt ) msg_Err( p_aout, "cannot open directx audio device" ); goto error; } - - /* Calculate the frame size in bytes */ - aout_PacketInit( p_aout, &p_aout->sys->packet, fmt->i_rate / 20, fmt ); } + p_aout->sys->i_write = 0; - /* Force volume update in thread. TODO: use session volume on Vista+ */ - p_aout->sys->volume.volume = p_aout->sys->volume.mb; - - /* Now we need to setup our DirectSound play notification structure */ - vlc_atomic_set(&p_aout->sys->notif.abort, 0); - p_aout->sys->notif.event = CreateEvent( 0, FALSE, FALSE, 0 ); - if( unlikely(p_aout->sys->notif.event == NULL) ) - abort(); - p_aout->sys->notif.i_frame_size = p_aout->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_clone( &p_aout->sys->notif.thread, DirectSoundThread, p_aout, - VLC_THREAD_PRIORITY_HIGHEST ) ) - { - msg_Err( p_aout, "cannot create DirectSoundThread" ); - CloseHandle( p_aout->sys->notif.event ); - p_aout->sys->notif.event = NULL; - aout_PacketDestroy( p_aout ); - goto error; - } - - p_aout->time_get = aout_PacketTimeGet; + p_aout->time_get = TimeGet; p_aout->play = Play; - p_aout->pause = aout_PacketPause; - p_aout->flush = aout_PacketFlush; + p_aout->pause = Pause; + p_aout->flush = Flush; + return VLC_SUCCESS; error: @@ -293,218 +303,6 @@ static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt ) return VLC_EGENERIC; } -/***************************************************************************** - * Probe: probe the audio device for available formats and channels - *****************************************************************************/ -static void Probe( audio_output_t * p_aout, const audio_sample_format_t *fmt ) -{ - vlc_value_t val, text; - vlc_fourcc_t i_format; - 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 */ - if( fmt->i_physical_channels == AOUT_CHANS_5_1 ) - { - if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_5_1, - fmt->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 */ - if( fmt->i_physical_channels == AOUT_CHANS_7_1 ) - { - if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_7_1, - fmt->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 */ - if( fmt->i_physical_channels == AOUT_CHANS_5_0 ) - { - if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_5_0, - fmt->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 */ - if( ( fmt->i_physical_channels & AOUT_CHANS_4_0 ) == AOUT_CHANS_4_0 ) - { - if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_4_0, - fmt->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 */ - if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHANS_2_0, - fmt->i_rate, true ) == 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) - { - 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!)" ); - } - else msg_Dbg( p_aout, "device supports 2 channels" ); - } - - /* Test for mono support */ - if( CreateDSBufferPCM( p_aout, &i_format, AOUT_CHAN_CENTER, - fmt->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" ); - } - - /* 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" ); - } - switch( DSSPEAKER_CONFIG(ui_speaker_config) ) - { - case DSSPEAKER_7POINT1: - case DSSPEAKER_7POINT1_SURROUND: - msg_Dbg( p_aout, "Windows says your SpeakerConfig is 7.1" ); - val.i_int = AOUT_VAR_7_1; - break; - case DSSPEAKER_5POINT1: - case DSSPEAKER_5POINT1_SURROUND: - 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; - } - - /* Check if we want to override speaker config */ - switch( p_aout->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; - } - - var_Set( p_aout, "audio-device", val ); - - /* Test for SPDIF support */ - if ( AOUT_FMT_SPDIF( fmt ) ) - { - if( CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL, - fmt->i_physical_channels, - aout_FormatNbChannels( fmt ), fmt->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( var_InheritBool( p_aout, "spdif" ) ) - var_Set( p_aout, "audio-device", val ); - } - } - - 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; - } - - var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL ); -} - /***************************************************************************** * 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 @@ -512,33 +310,36 @@ static void Probe( audio_output_t * p_aout, const audio_sample_format_t *fmt ) *****************************************************************************/ static void Play( audio_output_t *p_aout, block_t *p_buffer ) { - /* get the playing date of the first aout buffer */ - p_aout->sys->notif.start_date = p_buffer->i_pts; - - /* fill in the first samples (zeroes) */ - FillBuffer( p_aout, 0, NULL ); - - /* wake up the audio output thread */ - SetEvent( p_aout->sys->notif.event ); - - aout_PacketPlay( p_aout, p_buffer ); - p_aout->play = aout_PacketPlay; + if( FillBuffer( p_aout, p_buffer ) == VLC_SUCCESS ) + { + /* start playing the buffer */ + HRESULT dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer, + 0, 0, DSBPLAY_LOOPING ); + if( dsresult == DSERR_BUFFERLOST ) + { + IDirectSoundBuffer_Restore( p_aout->sys->p_dsbuffer ); + dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer, + 0, 0, DSBPLAY_LOOPING ); + } + if( dsresult != DS_OK ) + msg_Err( p_aout, "cannot start playing buffer" ); + } } -static int VolumeSet( audio_output_t *p_aout, float vol ) +static int VolumeSet( audio_output_t *p_aout, float volume ) { aout_sys_t *sys = p_aout->sys; int ret = 0; /* Convert UI volume to linear factor (cube) */ - vol = vol * vol * vol; + float vol = volume * volume * volume; /* millibels from linear amplification */ LONG mb = lroundf(2000.f * log10f(vol)); /* Clamp to allowed DirectSound range */ static_assert( DSBVOLUME_MIN < DSBVOLUME_MAX, "DSBVOLUME_* confused" ); - if( mb >= DSBVOLUME_MAX ) + if( mb > DSBVOLUME_MAX ) { mb = DSBVOLUME_MAX; ret = -1; @@ -547,28 +348,31 @@ static int VolumeSet( audio_output_t *p_aout, float vol ) mb = DSBVOLUME_MIN; sys->volume.mb = mb; - if (!sys->volume.mute) - InterlockedExchange(&sys->volume.volume, 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 */ - vol = cbrtf(powf(10.f, ((float)mb) / 2000.f)); - aout_VolumeReport( p_aout, vol ); + aout_VolumeReport( p_aout, volume ); if( var_InheritBool( p_aout, "volume-save" ) ) - config_PutInt( p_aout, "directx-volume", mb ); + config_PutFloat( p_aout, "directx-volume", volume ); return ret; } static int MuteSet( audio_output_t *p_aout, bool mute ) { + HRESULT res = DS_OK; aout_sys_t *sys = p_aout->sys; sys->volume.mute = mute; - InterlockedExchange(&sys->volume.volume, - mute ? DSBVOLUME_MIN : sys->volume.mb); + + if( sys->p_dsbuffer ) + res = IDirectSoundBuffer_SetVolume( sys->p_dsbuffer, + mute? DSBVOLUME_MIN : sys->volume.mb ); aout_MuteReport( p_aout, mute ); - return 0; + return (res != DS_OK); } /***************************************************************************** @@ -577,27 +381,19 @@ static int MuteSet( audio_output_t *p_aout, bool mute ) static void Stop( audio_output_t *p_aout ) { 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->notif.event != NULL ) - { - vlc_atomic_set(&p_aout->sys->notif.abort, 1); - /* wake up the audio thread if needed */ - if( p_aout->play == Play ) - SetEvent( p_sys->notif.event ); - - vlc_join( p_sys->notif.thread, NULL ); - CloseHandle( p_sys->notif.event ); - aout_PacketDestroy( p_aout ); - } + 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 ); + if( p_sys->p_dsobject ) + IDirectSound_Release( p_sys->p_dsobject ); } /***************************************************************************** @@ -618,17 +414,17 @@ static int InitDirectSound( audio_output_t *p_aout ) goto error; } - char *dev = var_InheritString( p_aout, "directx-audio-device" ); + char *dev = var_GetNonEmptyString( p_aout, "directx-audio-device" ); if( dev != NULL ) { 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 ); - free( dev ); } /* Create the direct sound object */ @@ -648,12 +444,14 @@ static int InitDirectSound( audio_output_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 !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: @@ -676,7 +474,7 @@ static int InitDirectSound( audio_output_t *p_aout ) *****************************************************************************/ 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; @@ -708,7 +506,7 @@ static int CreateDSBuffer( audio_output_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; @@ -724,15 +522,16 @@ static int CreateDSBuffer( audio_output_t *p_aout, int i_format, waveformat.Format.nAvgBytesPerSec = waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign; - p_aout->sys->i_bits_per_sample = waveformat.Format.wBitsPerSample; - p_aout->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 */ - | DSBCAPS_CTRLVOLUME; /* Allows volume control */ + 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 ) @@ -749,7 +548,7 @@ static int CreateDSBuffer( audio_output_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( @@ -783,7 +582,7 @@ static int CreateDSBuffer( audio_output_t *p_aout, int i_format, return VLC_SUCCESS; } - p_aout->sys->i_frame_size = i_bytes_per_frame; + 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, @@ -794,6 +593,18 @@ static int CreateDSBuffer( audio_output_t *p_aout, int i_format, 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; } @@ -808,24 +619,20 @@ static int CreateDSBufferPCM( audio_output_t *p_aout, vlc_fourcc_t *i_format, { unsigned i_nb_channels = popcount( i_channels ); - /* Float32 audio samples are not supported for 5.1 output on the emu101k */ 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, - (i_rate / 20) * 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, - (i_rate / 20) * 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; } } @@ -855,31 +662,32 @@ static void DestroyDSBuffer( audio_output_t *p_aout ) ***************************************************************************** * Returns VLC_SUCCESS on success. *****************************************************************************/ -static int FillBuffer( audio_output_t *p_aout, int i_frame, block_t *p_buffer ) +static int FillBuffer( audio_output_t *p_aout, block_t *p_buffer ) { aout_sys_t *p_sys = p_aout->sys; - notification_thread_t *p_notif = &p_sys->notif; + + size_t towrite = (p_buffer)?p_buffer->i_buffer:DS_BUF_SIZE; void *p_write_position, *p_wrap_around; 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 address (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, @@ -903,9 +711,11 @@ static int FillBuffer( audio_output_t *p_aout, int i_frame, block_t *p_buffer ) /* Do the channel reordering here */ aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer, p_sys->chans_to_reorder, p_sys->chan_table, - p_sys->i_bits_per_sample ); + p_sys->format ); 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 ); } @@ -913,117 +723,10 @@ static int FillBuffer( audio_output_t *p_aout, int i_frame, block_t *p_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; - return VLC_SUCCESS; -} - -/***************************************************************************** - * 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. - *****************************************************************************/ -static void* DirectSoundThread( void *data ) -{ - audio_output_t *p_aout = (audio_output_t *)data; - notification_thread_t *p_notif = &p_aout->sys->notif; - mtime_t last_time; - - msg_Dbg( p_aout, "DirectSoundThread ready" ); + p_sys->i_write += towrite; + p_sys->i_write %= DS_BUF_SIZE; - /* Wait here until Play() is called */ - WaitForSingleObject( p_notif->event, INFINITE ); - - if( !vlc_atomic_get( &p_notif->abort) ) - { - HRESULT dsresult; - mwait( p_notif->start_date - AOUT_MAX_PTS_ADVANCE / 2 ); - - /* start playing the buffer */ - dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer, - 0, /* Unused */ - 0, /* Unused */ - DSBPLAY_LOOPING ); /* Flags */ - if( dsresult == DSERR_BUFFERLOST ) - { - IDirectSoundBuffer_Restore( p_aout->sys->p_dsbuffer ); - dsresult = IDirectSoundBuffer_Play( - p_aout->sys->p_dsbuffer, - 0, /* Unused */ - 0, /* Unused */ - DSBPLAY_LOOPING ); /* Flags */ - } - if( dsresult != DS_OK ) - { - msg_Err( p_aout, "cannot start playing buffer" ); - } - } - last_time = mdate(); - - while( !vlc_atomic_get( &p_notif->abort ) ) - { - DWORD l_read; - int l_queued = 0, l_free_slots; - unsigned i_frame_siz = p_aout->sys->packet.samples; - mtime_t mtime = mdate(); - int i; - - /* Update volume if required */ - LONG volume = InterlockedExchange( &p_aout->sys->volume.volume, -1 ); - if( unlikely(volume != -1) ) - IDirectSoundBuffer_SetVolume( p_aout->sys->p_dsbuffer, volume ); - - /* - * Fill in as much audio data as we can in our circular buffer - */ - - /* Find out current play position */ - if FAILED( IDirectSoundBuffer_GetCurrentPosition( - p_aout->sys->p_dsbuffer, &l_read, NULL ) ) - { - msg_Err( p_aout, "GetCurrentPosition() failed!" ); - l_read = 0; - } - - /* Detect underruns */ - if( l_queued && mtime - last_time > - INT64_C(1000000) * l_queued / p_aout->sys->packet.format.i_rate ) - { - msg_Dbg( p_aout, "detected underrun!" ); - } - last_time = mtime; - - /* Try to fill in as many frame buffers as possible */ - l_read /= (p_aout->sys->packet.format.i_bytes_per_frame / - p_aout->sys->packet.format.i_frame_length); - l_queued = p_notif->i_write_slot * i_frame_siz - l_read; - if( l_queued < 0 ) l_queued += (i_frame_siz * FRAMES_NUM); - l_free_slots = (FRAMES_NUM * i_frame_siz - l_queued) / i_frame_siz; - - for( i = 0; i < l_free_slots; i++ ) - { - block_t *p_buffer = aout_PacketNext( p_aout, - mtime + INT64_C(1000000) * (i * i_frame_siz + l_queued) / - p_aout->sys->packet.format.i_rate ); - - /* 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 / i_frame_siz) ) break; - - if( FillBuffer( p_aout, p_notif->i_write_slot % FRAMES_NUM, - p_buffer ) != VLC_SUCCESS ) break; - } - - /* Sleep a reasonable amount of time */ - l_queued += (i * i_frame_siz); - msleep( INT64_C(1000000) * l_queued / p_aout->sys->packet.format.i_rate / 2 ); - } - - /* make sure the buffer isn't playing */ - IDirectSoundBuffer_Stop( p_aout->sys->p_dsbuffer ); - - msg_Dbg( p_aout, "DirectSoundThread exiting" ); - return NULL; + return VLC_SUCCESS; } typedef struct @@ -1059,7 +762,13 @@ static int CALLBACK DeviceEnumCallback( LPGUID guid, LPCWSTR desc, static int ReloadDirectXDevices( vlc_object_t *p_this, char const *psz_name, char ***values, char ***descs ) { - ds_list_t list = { 0, NULL, NULL }; + ds_list_t list = { + .count = 1, + .ids = xmalloc(sizeof (char *)), + .names = xmalloc(sizeof (char *)), + }; + list.ids[0] = xstrdup(""); + list.names[0] = xstrdup(_("Default")); (void) psz_name; @@ -1072,7 +781,7 @@ static int ReloadDirectXDevices( vlc_object_t *p_this, char const *psz_name, /* Get DirectSoundEnumerate */ HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKW, LPVOID) = - (void *)GetProcAddress( hdsound_dll, _T("DirectSoundEnumerateW") ); + (void *)GetProcAddress( hdsound_dll, "DirectSoundEnumerateW" ); if( OurDirectSoundEnumerate != NULL ) { OurDirectSoundEnumerate( DeviceEnumCallback, &list ); @@ -1086,6 +795,14 @@ out: 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; @@ -1106,13 +823,41 @@ static int Open(vlc_object_t *obj) aout->stop = Stop; aout->volume_set = VolumeSet; aout->mute_set = MuteSet; + aout->device_select = DeviceSelect; /* Volume */ - LONG mb = var_InheritInteger(aout, "directx-volume"); - sys->volume.mb = mb; - aout_VolumeReport(aout, cbrtf(powf(10.f, ((float)mb) / 2000.f))); + 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++) + { + aout_HotplugReport(aout, ids[i], names[i]); + free(names[i]); + free(ids[i]); + } + free(names); + free(ids); + } + + char *dev = var_CreateGetNonEmptyString(aout, "directx-audio-device"); + aout_DeviceReport(aout, dev); + free(dev); + return VLC_SUCCESS; } @@ -1121,6 +866,68 @@ 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); } + +static void Flush ( audio_output_t * aout, bool drain ) +{ + aout_sys_t *sys = aout->sys; + size_t read; + if( drain ) + { + if( sys->p_notify ) + { + 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 ); + } + } + 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 ); + } +} + +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 ); +} + + +static int TimeGet( audio_output_t * aout, mtime_t * delay ) +{ + uint32_t read; + mtime_t size; + + if( IDirectSoundBuffer_GetCurrentPosition( aout->sys->p_dsbuffer, (LPDWORD) &read, NULL) != DS_OK ) + return 1; + + read %= DS_BUF_SIZE; + + size = (mtime_t)aout->sys->i_write - (mtime_t) read; + if( size < 0 ) + size += DS_BUF_SIZE; + + *delay = ( size / aout->sys->i_bytes_per_sample ) * CLOCK_FREQ / aout->sys->i_rate; + return 0; +}