Commit db527697 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

DirectSound: reorder to reduce forward declarations

parent 43291e2e
...@@ -40,61 +40,8 @@ ...@@ -40,61 +40,8 @@
#define DS_BUF_SIZE (6*1024*1024) #define DS_BUF_SIZE (6*1024*1024)
/*****************************************************************************
* aout_sys_t: directx audio output method descriptor
*****************************************************************************
* This structure is part of the audio output thread descriptor.
* It describes the direct sound specific properties of an audio device.
*****************************************************************************/
struct aout_sys_t
{
HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
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
{
float volume;
LONG mb;
bool mute;
} volume;
int i_bytes_per_sample; /* Size in bytes of one frame */
int i_rate; /* Sample rate */
uint8_t chans_to_reorder; /* do we need channel reordering */
uint8_t chan_table[AOUT_CHAN_MAX];
uint32_t i_channel_mask;
vlc_fourcc_t format;
size_t i_write;
};
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
static int Open( vlc_object_t * ); static int Open( vlc_object_t * );
static void Close( 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 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 *, static int ReloadDirectXDevices( vlc_object_t *, const char *,
char ***, char *** ); char ***, char *** );
...@@ -139,359 +86,200 @@ vlc_module_begin () ...@@ -139,359 +86,200 @@ vlc_module_begin ()
set_callbacks( Open, Close ) set_callbacks( Open, Close )
vlc_module_end () vlc_module_end ()
/***************************************************************************** /**
* OpenAudio: open the audio device * DirectSound audio output method descriptor
***************************************************************************** *
* This function opens and setups Direct Sound. * This structure is part of the audio output thread descriptor.
*****************************************************************************/ * It describes the direct sound specific properties of an audio device.
static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt ) */
struct aout_sys_t
{ {
char * psz_speaker; HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
int i = 0;
const char * const * ppsz_compare = speaker_list;
msg_Dbg( p_aout, "Opening DirectSound Audio Output" );
/* Retrieve config values */ LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
var_Create( p_aout, "directx-audio-float32", LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); * takes care of mixing all the
psz_speaker = var_CreateGetString( p_aout, "directx-audio-speaker" ); * secondary buffers into the primary) */
while ( *ppsz_compare != NULL ) LPDIRECTSOUNDNOTIFY p_notify;
{ HANDLE hnotify_evt;
if ( !strncmp( *ppsz_compare, psz_speaker, strlen(*ppsz_compare) ) ) struct
{ {
break; float volume;
} LONG mb;
ppsz_compare++; i++; bool mute;
} } volume;
if ( *ppsz_compare == NULL ) int i_bytes_per_sample; /* Size in bytes of one frame */
{ int i_rate; /* Sample rate */
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 );
/* Initialise DirectSound */ uint8_t chans_to_reorder; /* do we need channel reordering */
if( InitDirectSound( p_aout ) ) uint8_t chan_table[AOUT_CHAN_MAX];
{ uint32_t i_channel_mask;
msg_Err( p_aout, "cannot initialize DirectSound" ); vlc_fourcc_t format;
goto error;
}
if ( AOUT_FMT_SPDIF( fmt ) && var_InheritBool( p_aout, "spdif" ) size_t i_write;
&& CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL, fmt->i_physical_channels, };
aout_FormatNbChannels( fmt ), fmt->i_rate, false )
== 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 */ static int TimeGet( audio_output_t * aout, mtime_t * delay )
fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE; {
fmt->i_frame_length = A52_FRAME_NB; DWORD read;
} mtime_t size;
else
{
if( i == 0 )
{
DWORD ui_speaker_config;
int i_channels = 2; /* Default to stereo */
int i_orig_channels = aout_FormatNbChannels( fmt );
/* Check the speaker configuration to determine which channel if( IDirectSoundBuffer_GetCurrentPosition( aout->sys->p_dsbuffer, &read,
* config should be the default */ NULL) != DS_OK )
if( FAILED( IDirectSound_GetSpeakerConfig( p_aout->sys->p_dsobject, return -1;
&ui_speaker_config ) ) )
{
ui_speaker_config = DSSPEAKER_STEREO;
msg_Dbg( p_aout, "GetSpeakerConfig failed" );
}
const char *name = "Unknown"; read %= DS_BUF_SIZE;
switch( DSSPEAKER_CONFIG(ui_speaker_config) )
{
case DSSPEAKER_7POINT1:
case DSSPEAKER_7POINT1_SURROUND:
name = "7.1";
i_channels = 8;
break;
case DSSPEAKER_5POINT1:
case DSSPEAKER_5POINT1_SURROUND:
name = "5.1";
i_channels = 6;
break;
case DSSPEAKER_QUAD:
name = "Quad";
i_channels = 4;
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";
i_channels = 1;
break;
#endif
case DSSPEAKER_SURROUND:
name = "Surround";
i_channels = 4;
break;
case DSSPEAKER_STEREO:
name = "Stereo";
i_channels = 2;
break;
}
if( i_channels >= i_orig_channels ) size = (mtime_t)aout->sys->i_write - (mtime_t) read;
i_channels = i_orig_channels; if( size < 0 )
size += DS_BUF_SIZE;
msg_Dbg( p_aout, "%s speaker config: %s and stream has " *delay = ( size / aout->sys->i_bytes_per_sample ) * CLOCK_FREQ
"%d channels, using %d channels", "Windows", name, / aout->sys->i_rate;
i_orig_channels, i_channels ); return 0;
}
switch( i_channels ) /**
{ * Fills in one of the DirectSound frame buffers.
case 8: *
fmt->i_physical_channels = AOUT_CHANS_7_1; * @return VLC_SUCCESS on success.
break; */
case 7: static int FillBuffer( audio_output_t *p_aout, block_t *p_buffer )
case 6: {
fmt->i_physical_channels = AOUT_CHANS_5_1; aout_sys_t *p_sys = p_aout->sys;
break;
case 5:
case 4:
fmt->i_physical_channels = AOUT_CHANS_4_0;
break;
default:
fmt->i_physical_channels = AOUT_CHANS_2_0;
break;
}
}
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 */ size_t towrite = (p_buffer)? p_buffer->i_buffer : DS_BUF_SIZE;
aout_FormatPrepare( fmt ); void *p_write_position, *p_wrap_around;
unsigned long l_bytes1, l_bytes2;
uint32_t i_read;
size_t i_size;
mtime_t i_buf;
HRESULT dsresult;
if( CreateDSBufferPCM( p_aout, &fmt->i_format, size_t toerase = p_sys->i_bytes_per_sample * p_sys->i_rate / 4;
fmt->i_physical_channels, fmt->i_rate, false ) mtime_t max = towrite;
!= VLC_SUCCESS )
if( IDirectSoundBuffer_GetCurrentPosition( p_aout->sys->p_dsbuffer, (LPDWORD) &i_read, NULL) == DS_OK )
{ {
msg_Err( p_aout, "cannot open directx audio device" ); max = (mtime_t)i_read - (mtime_t)p_aout->sys->i_write;
goto error; if( max <= 0 )
} max += DS_BUF_SIZE;
} }
p_aout->sys->i_write = 0;
/* Force volume update */
VolumeSet( p_aout, p_aout->sys->volume.volume );
MuteSet( p_aout, p_aout->sys->volume.mute );
/* then launch the notification thread */
p_aout->time_get = TimeGet;
p_aout->play = Play;
p_aout->pause = Pause;
p_aout->flush = Flush;
return VLC_SUCCESS; if( towrite + toerase <= max )
i_buf = towrite + toerase;
error: else
Stop( p_aout ); i_buf = towrite;
return VLC_EGENERIC;
}
/***************************************************************************** /* Before copying anything, we have to lock the buffer */
* Play: we'll start playing the directsound buffer here because at least here dsresult = IDirectSoundBuffer_Lock(
* we know the first buffer has been put in the aout fifo and we also p_sys->p_dsbuffer, /* DS buffer */
* know its date. p_aout->sys->i_write, /* Start offset */
*****************************************************************************/ i_buf, /* Number of bytes */
static void Play( audio_output_t *p_aout, block_t *p_buffer ) &p_write_position, /* Address of lock start */
{ &l_bytes1, /* Count of bytes locked before wrap around */
if( FillBuffer( p_aout, p_buffer ) == VLC_SUCCESS ) &p_wrap_around, /* Buffer address (if wrap around) */
{ &l_bytes2, /* Count of bytes after wrap around */
/* start playing the buffer */ 0 ); /* Flags: DSBLOCK_FROMWRITECURSOR is buggy */
HRESULT dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer,
0, 0, DSBPLAY_LOOPING );
if( dsresult == DSERR_BUFFERLOST ) if( dsresult == DSERR_BUFFERLOST )
{ {
IDirectSoundBuffer_Restore( p_aout->sys->p_dsbuffer ); IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer, dsresult = IDirectSoundBuffer_Lock(
0, 0, DSBPLAY_LOOPING ); p_sys->p_dsbuffer,
p_aout->sys->i_write,
i_buf,
&p_write_position,
&l_bytes1,
&p_wrap_around,
&l_bytes2,
0 );
} }
if( dsresult != DS_OK ) if( dsresult != DS_OK )
msg_Err( p_aout, "cannot start playing buffer" ); {
msg_Warn( p_aout, "cannot lock buffer" );
if( p_buffer != NULL )
block_Release( p_buffer );
return VLC_EGENERIC;
} }
}
static int VolumeSet( audio_output_t *p_aout, float volume )
{
aout_sys_t *sys = p_aout->sys;
int ret = 0;
/* Directsound doesn't support amplification, so we use software
gain if we need it and only for this */
float gain = volume > 1.f ? volume * volume * volume : 1.f;
aout_GainRequest( p_aout, gain );
/* millibels from linear amplification */
LONG mb = lroundf( 6000.f * log10f( __MIN( volume, 1.f ) ));
/* Clamp to allowed DirectSound range */ if( p_buffer == NULL )
static_assert( DSBVOLUME_MIN < DSBVOLUME_MAX, "DSBVOLUME_* confused" );
if( mb > DSBVOLUME_MAX )
{ {
mb = DSBVOLUME_MAX; memset( p_write_position, 0, l_bytes1 );
ret = -1; memset( p_wrap_around, 0, l_bytes2 );
} }
if( mb <= DSBVOLUME_MIN ) else
mb = DSBVOLUME_MIN; {
if( p_sys->chans_to_reorder ) /* Do the channel reordering here */
sys->volume.mb = mb; aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer,
sys->volume.volume = volume; p_sys->chans_to_reorder, p_sys->chan_table,
if( !sys->volume.mute && sys->p_dsbuffer && p_sys->format );
IDirectSoundBuffer_SetVolume( sys->p_dsbuffer, mb ) != DS_OK )
return -1;
/* Convert back to UI volume */
aout_VolumeReport( p_aout, volume );
if( var_InheritBool( p_aout, "volume-save" ) )
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;
if( sys->p_dsbuffer )
res = IDirectSoundBuffer_SetVolume( sys->p_dsbuffer,
mute? DSBVOLUME_MIN : sys->volume.mb );
aout_MuteReport( p_aout, mute ); i_size = ( p_buffer->i_buffer < l_bytes1 ) ? p_buffer->i_buffer : l_bytes1;
return (res != DS_OK); memcpy( p_write_position, p_buffer->p_buffer, i_size );
} memset( (uint8_t*) p_write_position + i_size, 0, l_bytes1 - i_size );
/***************************************************************************** if( l_bytes1 < p_buffer->i_buffer)
* CloseAudio: close the audio device { /* Compute the remaining buffer space to be written */
*****************************************************************************/ i_buf = i_buf - i_size - (l_bytes1 - i_size);
static void Stop( audio_output_t *p_aout ) i_size = ( p_buffer->i_buffer - l_bytes1 < l_bytes2 ) ? p_buffer->i_buffer - l_bytes1 : l_bytes2;
{ memcpy( p_wrap_around, p_buffer->p_buffer + l_bytes1, i_size );
aout_sys_t *p_sys = p_aout->sys; memset( (uint8_t*) p_wrap_around + i_size, 0, i_buf - i_size );
msg_Dbg( p_aout, "closing audio device" ); }
block_Release( p_buffer );
}
if( p_sys->p_notify ) /* Now the data has been copied, unlock the buffer */
IDirectSoundNotify_Release(p_sys->p_notify ); IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
p_sys->p_notify = NULL; p_wrap_around, l_bytes2 );
if( p_sys->p_dsbuffer ) p_sys->i_write += towrite;
IDirectSoundBuffer_Stop( p_sys->p_dsbuffer ); p_sys->i_write %= DS_BUF_SIZE;
/* release the secondary buffer */
DestroyDSBuffer( p_aout );
/* finally release the DirectSound object */ return VLC_SUCCESS;
if( p_sys->p_dsobject )
IDirectSound_Release( p_sys->p_dsobject );
} }
/***************************************************************************** static void Play( audio_output_t *p_aout, block_t *p_buffer )
* InitDirectSound: handle all the gory details of DirectSound initialisation
*****************************************************************************/
static int InitDirectSound( audio_output_t *p_aout )
{ {
aout_sys_t *sys = p_aout->sys; if( FillBuffer( p_aout, p_buffer ) != VLC_SUCCESS )
GUID guid, *p_guid = NULL; return;
HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
OurDirectSoundCreate = (void *) /* start playing the buffer */
GetProcAddress( p_aout->sys->hdsound_dll, HRESULT dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer,
"DirectSoundCreate" ); 0, 0, DSBPLAY_LOOPING );
if( OurDirectSoundCreate == NULL ) if( dsresult == DSERR_BUFFERLOST )
{ {
msg_Warn( p_aout, "GetProcAddress FAILED" ); IDirectSoundBuffer_Restore( p_aout->sys->p_dsbuffer );
goto error; dsresult = IDirectSoundBuffer_Play( p_aout->sys->p_dsbuffer,
0, 0, DSBPLAY_LOOPING );
} }
if( dsresult != DS_OK )
msg_Err( p_aout, "cannot start playing buffer" );
}
char *dev = var_GetNonEmptyString( p_aout, "directx-audio-device" ); static void Pause( audio_output_t * aout, bool pause, mtime_t date )
if( dev != NULL ) {
{ if( pause )
LPOLESTR lpsz = ToWide( dev ); IDirectSoundBuffer_Stop( aout->sys->p_dsbuffer );
free( dev );
if( SUCCEEDED( IIDFromString( lpsz, &guid ) ) )
p_guid = &guid;
else else
msg_Err( p_aout, "bad device GUID: %ls", lpsz ); IDirectSoundBuffer_Play( aout->sys->p_dsbuffer, 0, 0,
free( lpsz ); DSBPLAY_LOOPING );
} (void) date;
}
/* Create the direct sound object */
if FAILED( OurDirectSoundCreate( p_guid, &sys->p_dsobject, NULL ) )
{
msg_Warn( p_aout, "cannot create a direct sound device" );
goto error;
}
/* Set DirectSound Cooperative level, ie what control we want over Windows
* sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
* settings of the primary buffer, but also that only the sound of our
* application will be hearable when it will have the focus.
* !!! (this is not really working as intended yet because to set the
* cooperative level you need the window handle of your application, and
* I don't know of any easy way to get it. Especially since we might play
* 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:
sys->p_dsobject = NULL;
return VLC_EGENERIC;
static void Flush( audio_output_t * aout, bool drain )
{
IDirectSoundBuffer_Stop( aout->sys->p_dsbuffer );
if( !drain )
IDirectSoundBuffer_SetCurrentPosition( aout->sys->p_dsbuffer,
aout->sys->i_write );
} }
/***************************************************************************** /**
* CreateDSBuffer: Creates a direct sound buffer of the required format. * Creates a DirectSound buffer of the required format.
***************************************************************************** *
* This function creates the buffer we'll use to play audio. * This function creates the buffer we'll use to play audio.
* In DirectSound there are two kinds of buffers: * In DirectSound there are two kinds of buffers:
* - the primary buffer: which is the actual buffer that the soundcard plays * - the primary buffer: which is the actual buffer that the soundcard plays
...@@ -500,7 +288,7 @@ static int InitDirectSound( audio_output_t *p_aout ) ...@@ -500,7 +288,7 @@ static int InitDirectSound( audio_output_t *p_aout )
* *
* Once you create a secondary buffer, you cannot change its format anymore so * Once you create a secondary buffer, you cannot change its format anymore so
* you have to release the current one and create another. * you have to release the current one and create another.
*****************************************************************************/ */
static int CreateDSBuffer( audio_output_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_channels, int i_nb_channels, int i_rate,
bool b_probe ) bool b_probe )
...@@ -641,154 +429,359 @@ static int CreateDSBuffer( audio_output_t *p_aout, int i_format, ...@@ -641,154 +429,359 @@ static int CreateDSBuffer( audio_output_t *p_aout, int i_format,
return VLC_SUCCESS; return VLC_SUCCESS;
} }
/***************************************************************************** /**
* CreateDSBufferPCM: creates a PCM direct sound buffer. * Creates a PCM DirectSound buffer.
***************************************************************************** *
* We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
* the hardware, otherwise we create a WAVE_FORMAT_PCM buffer. * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
****************************************************************************/ */
static int CreateDSBufferPCM( audio_output_t *p_aout, vlc_fourcc_t *i_format, static int CreateDSBufferPCM( audio_output_t *p_aout, vlc_fourcc_t *i_format,
int i_channels, int i_rate, bool b_probe ) int i_channels, int i_rate, bool b_probe )
{ {
unsigned i_nb_channels = popcount( i_channels ); unsigned i_nb_channels = popcount( i_channels );
if( !var_GetBool( p_aout, "directx-audio-float32" ) || if( var_GetBool( p_aout, "directx-audio-float32" )
CreateDSBuffer( p_aout, VLC_CODEC_FL32, && CreateDSBuffer( p_aout, VLC_CODEC_FL32, i_channels, i_nb_channels,
i_channels, i_nb_channels, i_rate, b_probe ) i_rate, b_probe ) == VLC_SUCCESS )
!= VLC_SUCCESS )
{ {
if ( CreateDSBuffer( p_aout, VLC_CODEC_S16N, *i_format = VLC_CODEC_FL32;
i_channels, i_nb_channels, i_rate, b_probe ) return VLC_SUCCESS;
!= VLC_SUCCESS ) }
if( CreateDSBuffer( p_aout, VLC_CODEC_S16N, i_channels, i_nb_channels,
i_rate, b_probe ) == VLC_SUCCESS )
{
*i_format = VLC_CODEC_S16N;
return VLC_SUCCESS;
}
return VLC_EGENERIC;
}
/**
* Closes the audio device.
*/
static void Stop( audio_output_t *p_aout )
{
aout_sys_t *p_sys = p_aout->sys;
msg_Dbg( p_aout, "closing audio device" );
if( p_sys->p_notify != NULL )
{
IDirectSoundNotify_Release(p_sys->p_notify );
p_sys->p_notify = NULL;
}
if( p_sys->p_dsbuffer != NULL )
{
IDirectSoundBuffer_Stop( p_sys->p_dsbuffer );
IDirectSoundBuffer_Release( p_aout->sys->p_dsbuffer );
p_aout->sys->p_dsbuffer = NULL;
}
if( p_sys->p_dsobject != NULL )
{
IDirectSound_Release( p_sys->p_dsobject );
p_sys->p_dsobject = NULL;
}
}
/**
* Handles all the gory details of DirectSound initialization.
*/
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);
OurDirectSoundCreate = (void *)
GetProcAddress( p_aout->sys->hdsound_dll,
"DirectSoundCreate" );
if( OurDirectSoundCreate == NULL )
{
msg_Warn( p_aout, "GetProcAddress FAILED" );
goto error;
}
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 );
}
/* Create the direct sound object */
if FAILED( OurDirectSoundCreate( p_guid, &sys->p_dsobject, NULL ) )
{
msg_Warn( p_aout, "cannot create a direct sound device" );
goto error;
}
/* Set DirectSound Cooperative level, ie what control we want over Windows
* sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
* settings of the primary buffer, but also that only the sound of our
* application will be hearable when it will have the focus.
* !!! (this is not really working as intended yet because to set the
* cooperative level you need the window handle of your application, and
* I don't know of any easy way to get it. Especially since we might play
* 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:
sys->p_dsobject = NULL;
return VLC_EGENERIC;
}
static int VolumeSet( audio_output_t *p_aout, float volume )
{
aout_sys_t *sys = p_aout->sys;
int ret = 0;
/* Directsound doesn't support amplification, so we use software
gain if we need it and only for this */
float gain = volume > 1.f ? volume * volume * volume : 1.f;
aout_GainRequest( p_aout, gain );
/* millibels from linear amplification */
LONG mb = lroundf( 6000.f * log10f( __MIN( volume, 1.f ) ));
/* Clamp to allowed DirectSound range */
static_assert( DSBVOLUME_MIN < DSBVOLUME_MAX, "DSBVOLUME_* confused" );
if( mb > DSBVOLUME_MAX )
{
mb = DSBVOLUME_MAX;
ret = -1;
}
if( mb <= DSBVOLUME_MIN )
mb = DSBVOLUME_MIN;
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 );
if( var_InheritBool( p_aout, "volume-save" ) )
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;
if( sys->p_dsbuffer )
res = IDirectSoundBuffer_SetVolume( sys->p_dsbuffer,
mute? DSBVOLUME_MIN : sys->volume.mb );
aout_MuteReport( p_aout, mute );
return (res != DS_OK);
}
static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
{
char *psz_speaker;
int i = 0;
const char *const *ppsz_compare = speaker_list;
msg_Dbg( p_aout, "Opening DirectSound Audio Output" );
/* Retrieve config values */
var_Create( p_aout, "directx-audio-float32",
VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
psz_speaker = var_CreateGetString( p_aout, "directx-audio-speaker" );
while ( *ppsz_compare != NULL )
{ {
return VLC_EGENERIC; if ( !strncmp( *ppsz_compare, psz_speaker, strlen(*ppsz_compare) ) )
}
else
{ {
*i_format = VLC_CODEC_S16N; break;
return VLC_SUCCESS;
} }
ppsz_compare++; i++;
} }
else
if ( *ppsz_compare == NULL )
{ {
*i_format = VLC_CODEC_FL32; msg_Err( p_aout, "(%s) isn't valid speaker setup option", psz_speaker );
return VLC_SUCCESS; msg_Err( p_aout, "Defaulting to Windows default speaker config");
i = 0;
} }
} free( psz_speaker );
/***************************************************************************** /* Initialise DirectSound */
* DestroyDSBuffer if( InitDirectSound( p_aout ) )
*****************************************************************************
* This function destroys the secondary buffer.
*****************************************************************************/
static void DestroyDSBuffer( audio_output_t *p_aout )
{
if( p_aout->sys->p_dsbuffer )
{ {
IDirectSoundBuffer_Release( p_aout->sys->p_dsbuffer ); msg_Err( p_aout, "cannot initialize DirectSound" );
p_aout->sys->p_dsbuffer = NULL; goto error;
} }
}
/*****************************************************************************
* FillBuffer: Fill in one of the direct sound frame buffers.
*****************************************************************************
* Returns VLC_SUCCESS on success.
*****************************************************************************/
static int FillBuffer( audio_output_t *p_aout, block_t *p_buffer )
{
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;
unsigned long l_bytes1, l_bytes2;
uint32_t i_read;
size_t i_size;
mtime_t i_buf;
HRESULT dsresult;
size_t toerase = p_sys->i_bytes_per_sample * p_sys->i_rate / 4;
mtime_t max = towrite;
if( IDirectSoundBuffer_GetCurrentPosition( p_aout->sys->p_dsbuffer, (LPDWORD) &i_read, NULL) == DS_OK ) 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, false )
== VLC_SUCCESS )
{ {
max = (mtime_t)i_read - (mtime_t)p_aout->sys->i_write; msg_Dbg( p_aout, "using A/52 pass-through over S/PDIF" );
if( max <= 0 ) fmt->i_format = VLC_CODEC_SPDIFL;
max += DS_BUF_SIZE;
}
if( towrite + toerase <= max ) /* Calculate the frame size in bytes */
i_buf = towrite + toerase; fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
fmt->i_frame_length = A52_FRAME_NB;
}
else else
i_buf = towrite; {
if( i == 0 )
{
DWORD ui_speaker_config;
int i_channels = 2; /* Default to stereo */
int i_orig_channels = aout_FormatNbChannels( fmt );
/* Before copying anything, we have to lock the buffer */ /* Check the speaker configuration to determine which channel
dsresult = IDirectSoundBuffer_Lock( * config should be the default */
p_sys->p_dsbuffer, /* DS buffer */ if( FAILED( IDirectSound_GetSpeakerConfig( p_aout->sys->p_dsobject,
p_aout->sys->i_write, /* Start offset */ &ui_speaker_config ) ) )
i_buf, /* 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 ); ui_speaker_config = DSSPEAKER_STEREO;
dsresult = IDirectSoundBuffer_Lock( msg_Dbg( p_aout, "GetSpeakerConfig failed" );
p_sys->p_dsbuffer,
p_aout->sys->i_write,
i_buf,
&p_write_position,
&l_bytes1,
&p_wrap_around,
&l_bytes2,
0 );
} }
if( dsresult != DS_OK )
const char *name = "Unknown";
switch( DSSPEAKER_CONFIG(ui_speaker_config) )
{ {
msg_Warn( p_aout, "cannot lock buffer" ); case DSSPEAKER_7POINT1:
if( p_buffer ) block_Release( p_buffer ); case DSSPEAKER_7POINT1_SURROUND:
return VLC_EGENERIC; name = "7.1";
i_channels = 8;
break;
case DSSPEAKER_5POINT1:
case DSSPEAKER_5POINT1_SURROUND:
name = "5.1";
i_channels = 6;
break;
case DSSPEAKER_QUAD:
name = "Quad";
i_channels = 4;
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";
i_channels = 1;
break;
#endif
case DSSPEAKER_SURROUND:
name = "Surround";
i_channels = 4;
break;
case DSSPEAKER_STEREO:
name = "Stereo";
i_channels = 2;
break;
} }
if( p_buffer == NULL ) if( i_channels >= i_orig_channels )
i_channels = i_orig_channels;
msg_Dbg( p_aout, "%s speaker config: %s and stream has "
"%d channels, using %d channels", "Windows", name,
i_orig_channels, i_channels );
switch( i_channels )
{ {
memset( p_write_position, 0, l_bytes1 ); case 8:
memset( p_wrap_around, 0, l_bytes2 ); fmt->i_physical_channels = AOUT_CHANS_7_1;
break;
case 7:
case 6:
fmt->i_physical_channels = AOUT_CHANS_5_1;
break;
case 5:
case 4:
fmt->i_physical_channels = AOUT_CHANS_4_0;
break;
default:
fmt->i_physical_channels = AOUT_CHANS_2_0;
break;
}
} }
else else
{ /* Overriden speaker configuration */
const char *name = "Non-existant";
switch( i )
{ {
if( p_sys->chans_to_reorder ) case 1: /* Mono */
/* Do the channel reordering here */ name = "Mono";
aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer, fmt->i_physical_channels = AOUT_CHAN_CENTER;
p_sys->chans_to_reorder, p_sys->chan_table, break;
p_sys->format ); 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 );
}
i_size = ( p_buffer->i_buffer < l_bytes1 ) ? p_buffer->i_buffer : l_bytes1; /* Open the device */
memcpy( p_write_position, p_buffer->p_buffer, i_size ); aout_FormatPrepare( fmt );
memset( (uint8_t*) p_write_position + i_size, 0, l_bytes1 - i_size );
if( l_bytes1 < p_buffer->i_buffer) if( CreateDSBufferPCM( p_aout, &fmt->i_format,
fmt->i_physical_channels, fmt->i_rate, false )
!= VLC_SUCCESS )
{ {
/* Compute the remaining buffer space to be written */ msg_Err( p_aout, "cannot open directx audio device" );
i_buf = i_buf - i_size - (l_bytes1 - i_size); goto error;
i_size = ( p_buffer->i_buffer - l_bytes1 < l_bytes2 ) ? p_buffer->i_buffer - l_bytes1 : l_bytes2;
memcpy( p_wrap_around, p_buffer->p_buffer + l_bytes1, i_size );
memset( (uint8_t*) p_wrap_around + i_size, 0, i_buf - i_size );
} }
block_Release( p_buffer );
} }
p_aout->sys->i_write = 0;
/* Now the data has been copied, unlock the buffer */ /* Force volume update */
IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1, VolumeSet( p_aout, p_aout->sys->volume.volume );
p_wrap_around, l_bytes2 ); MuteSet( p_aout, p_aout->sys->volume.mute );
p_sys->i_write += towrite; /* then launch the notification thread */
p_sys->i_write %= DS_BUF_SIZE; p_aout->time_get = TimeGet;
p_aout->play = Play;
p_aout->pause = Pause;
p_aout->flush = Flush;
return VLC_SUCCESS; return VLC_SUCCESS;
error:
Stop( p_aout );
return VLC_EGENERIC;
} }
typedef struct typedef struct
...@@ -819,9 +812,9 @@ static int CALLBACK DeviceEnumCallback( LPGUID guid, LPCWSTR desc, ...@@ -819,9 +812,9 @@ static int CALLBACK DeviceEnumCallback( LPGUID guid, LPCWSTR desc,
return true; return true;
} }
/***************************************************************************** /**
* ReloadDirectXDevices: store the list of devices in preferences * Stores the list of devices in preferences
*****************************************************************************/ */
static int ReloadDirectXDevices( vlc_object_t *p_this, char const *psz_name, static int ReloadDirectXDevices( vlc_object_t *p_this, char const *psz_name,
char ***values, char ***descs ) char ***values, char ***descs )
{ {
...@@ -936,39 +929,3 @@ static void Close(vlc_object_t *obj) ...@@ -936,39 +929,3 @@ static void Close(vlc_object_t *obj)
FreeLibrary(sys->hdsound_dll); /* free DSOUND.DLL */ FreeLibrary(sys->hdsound_dll); /* free DSOUND.DLL */
free(sys); free(sys);
} }
static void Flush ( audio_output_t * aout, bool drain )
{
IDirectSoundBuffer_Stop( aout->sys->p_dsbuffer );
if( !drain )
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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment