Commit 79223aae authored by André Weber's avatar André Weber

Bugfix for #492: Audio output on Windows useing SPDIF for AC3 / DTS

But its still very time critical, because paout gets sometimes empty
so that aout_OutputNextBuffer(..) doesn't deliver new audiobuffers,
sometimes it happens - that these buffers arrive very late for output.
Reasons: -changed CPU load, (extra running application, also fast forward
seeking and and jumping may lead to this situation.)

Enhancement #897: added an option to let the user  choose is prefered audio device. (needs restart of VLC to get applied)
parent ced125da
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <vlc/vlc.h> #include <vlc/vlc.h>
#include <vlc_aout.h> #include <vlc_aout.h>
#include <vlc_charset.h>
#include <windows.h> #include <windows.h>
#include <mmsystem.h> #include <mmsystem.h>
...@@ -120,10 +121,12 @@ typedef struct notification_thread_t ...@@ -120,10 +121,12 @@ typedef struct notification_thread_t
/* local functions */ /* local functions */
static void Probe ( aout_instance_t * ); static void Probe ( aout_instance_t * );
static int OpenWaveOut ( aout_instance_t *, int, int, int, int, vlc_bool_t ); static int OpenWaveOut ( aout_instance_t *, uint32_t,
static int OpenWaveOutPCM( aout_instance_t *, int*, int, int, int, vlc_bool_t ); int, int, int, int, vlc_bool_t );
static int OpenWaveOutPCM( aout_instance_t *, uint32_t,
int*, int, int, int, vlc_bool_t );
static int PlayWaveOut ( aout_instance_t *, HWAVEOUT, WAVEHDR *, static int PlayWaveOut ( aout_instance_t *, HWAVEOUT, WAVEHDR *,
aout_buffer_t * ); aout_buffer_t *, vlc_bool_t );
static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD, DWORD, DWORD ); static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD, DWORD, DWORD );
static void WaveOutThread( notification_thread_t * ); static void WaveOutThread( notification_thread_t * );
...@@ -132,6 +135,19 @@ static int VolumeInfos( aout_instance_t *, audio_volume_t * ); ...@@ -132,6 +135,19 @@ static int VolumeInfos( aout_instance_t *, audio_volume_t * );
static int VolumeGet( aout_instance_t *, audio_volume_t * ); static int VolumeGet( aout_instance_t *, audio_volume_t * );
static int VolumeSet( aout_instance_t *, audio_volume_t ); static int VolumeSet( aout_instance_t *, audio_volume_t );
static int WaveOutClearDoneBuffers(aout_sys_t *p_sys);
static int ReloadWaveoutDevices( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
static uint32_t findDeviceID(char *);
static const char psz_device_name_fmt[] = "%s ($%x,$%x)";
static const char *ppsz_adev[] = { "wavemapper", };
static const char *ppsz_adev_text[] = { N_("Microsoft Soundmapper") };
/***************************************************************************** /*****************************************************************************
* Module descriptor * Module descriptor
*****************************************************************************/ *****************************************************************************/
...@@ -139,6 +155,11 @@ static int VolumeSet( aout_instance_t *, audio_volume_t ); ...@@ -139,6 +155,11 @@ static int VolumeSet( aout_instance_t *, audio_volume_t );
#define FLOAT_LONGTEXT N_( \ #define FLOAT_LONGTEXT N_( \
"The option allows you to enable or disable the high-quality float32 " \ "The option allows you to enable or disable the high-quality float32 " \
"audio output mode (which is not well supported by some soundcards)." ) "audio output mode (which is not well supported by some soundcards)." )
#define DEVICE_TEXT N_("Select Audio Device")
#define DEVICE_LONG N_("Select special Audio device, or let windows "\
"decide (default), change needs VLC restart "\
"to apply.")
#define DEFAULT_AUDIO_DEVICE N_("Default Audio Device")
vlc_module_begin(); vlc_module_begin();
set_shortname( "WaveOut" ); set_shortname( "WaveOut" );
...@@ -147,6 +168,14 @@ vlc_module_begin(); ...@@ -147,6 +168,14 @@ vlc_module_begin();
set_category( CAT_AUDIO ); set_category( CAT_AUDIO );
set_subcategory( SUBCAT_AUDIO_AOUT ); set_subcategory( SUBCAT_AUDIO_AOUT );
add_bool( "waveout-float32", 1, 0, FLOAT_TEXT, FLOAT_LONGTEXT, VLC_TRUE ); add_bool( "waveout-float32", 1, 0, FLOAT_TEXT, FLOAT_LONGTEXT, VLC_TRUE );
add_string( "waveout-dev", "wavemapper", NULL,
DEVICE_TEXT, DEVICE_LONG, VLC_FALSE );
change_string_list( ppsz_adev, ppsz_adev_text, ReloadWaveoutDevices );
change_need_restart();
change_action_add( ReloadWaveoutDevices, N_("Refresh list") );
set_callbacks( Open, Close ); set_callbacks( Open, Close );
vlc_module_end(); vlc_module_end();
...@@ -158,6 +187,8 @@ vlc_module_end(); ...@@ -158,6 +187,8 @@ vlc_module_end();
*****************************************************************************/ *****************************************************************************/
struct aout_sys_t struct aout_sys_t
{ {
uint32_t i_wave_device_id; /* ID of selected output device */
HWAVEOUT h_waveout; /* handle to waveout instance */ HWAVEOUT h_waveout; /* handle to waveout instance */
WAVEFORMATEXTENSIBLE waveformat; /* audio format */ WAVEFORMATEXTENSIBLE waveformat; /* audio format */
...@@ -166,6 +197,13 @@ struct aout_sys_t ...@@ -166,6 +197,13 @@ struct aout_sys_t
notification_thread_t *p_notif; /* WaveOutThread id */ notification_thread_t *p_notif; /* WaveOutThread id */
HANDLE event; HANDLE event;
HANDLE new_buffer_event;
// rental from alsa.c to synchronize startup of audiothread
int b_playing; /* playing status */
mtime_t start_date;
int i_repeat_counter;
int i_buffer_size; int i_buffer_size;
...@@ -214,6 +252,50 @@ static int Open( vlc_object_t *p_this ) ...@@ -214,6 +252,50 @@ static int Open( vlc_object_t *p_this )
p_aout->output.pf_play = Play; p_aout->output.pf_play = Play;
p_aout->b_die = VLC_FALSE; p_aout->b_die = VLC_FALSE;
/*
initialize/update Device selection List
*/
ReloadWaveoutDevices( p_this, "waveout-dev", val, val, NULL);
/*
check for configured audio device!
*/
char *psz_waveout_dev = var_CreateGetString( p_aout, "waveout-dev");
p_aout->output.p_sys->i_wave_device_id =
findDeviceID( psz_waveout_dev );
if(p_aout->output.p_sys->i_wave_device_id == WAVE_MAPPER)
{
if(psz_waveout_dev &&
stricmp(psz_waveout_dev,"wavemapper"))
{
msg_Warn( p_aout, "configured audio device '%s' not available, "\
"use default instead", psz_waveout_dev );
}
}
if(psz_waveout_dev) free( psz_waveout_dev );
WAVEOUTCAPS waveoutcaps;
if(waveOutGetDevCaps( p_aout->output.p_sys->i_wave_device_id,
&waveoutcaps,
sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
{
/* log debug some infos about driver, to know who to blame
if it doesn't work */
msg_Dbg( p_aout, "Drivername: %s", waveoutcaps.szPname);
msg_Dbg( p_aout, "Driver Version: %d.%d",
(waveoutcaps.vDriverVersion>>8)&255,
waveoutcaps.vDriverVersion & 255);
msg_Dbg( p_aout, "Manufacturer identifier: 0x%x", waveoutcaps.wMid );
msg_Dbg( p_aout, "Product identifier: 0x%x", waveoutcaps.wPid );
}
if( var_Type( p_aout, "audio-device" ) == 0 ) if( var_Type( p_aout, "audio-device" ) == 0 )
{ {
Probe( p_aout ); Probe( p_aout );
...@@ -222,18 +304,20 @@ static int Open( vlc_object_t *p_this ) ...@@ -222,18 +304,20 @@ static int Open( vlc_object_t *p_this )
if( var_Get( p_aout, "audio-device", &val ) < 0 ) if( var_Get( p_aout, "audio-device", &val ) < 0 )
{ {
/* Probe() has failed. */ /* Probe() has failed. */
var_Destroy( p_aout, "waveout-device");
free( p_aout->output.p_sys ); free( p_aout->output.p_sys );
return VLC_EGENERIC; return VLC_EGENERIC;
} }
var_Create( p_aout, "waveout-float32", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
/* Open the device */ /* Open the device */
if( val.i_int == AOUT_VAR_SPDIF ) if( val.i_int == AOUT_VAR_SPDIF )
{ {
p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i'); p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
if( OpenWaveOut( p_aout, VLC_FOURCC('s','p','d','i'), if( OpenWaveOut( p_aout,
p_aout->output.p_sys->i_wave_device_id,
VLC_FOURCC('s','p','d','i'),
p_aout->output.output.i_physical_channels, p_aout->output.output.i_physical_channels,
aout_FormatNbChannels( &p_aout->output.output ), aout_FormatNbChannels( &p_aout->output.output ),
p_aout->output.output.i_rate, VLC_FALSE ) p_aout->output.output.i_rate, VLC_FALSE )
...@@ -280,7 +364,9 @@ static int Open( vlc_object_t *p_this ) ...@@ -280,7 +364,9 @@ static int Open( vlc_object_t *p_this )
= AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
} }
if( OpenWaveOutPCM( p_aout, &p_aout->output.output.i_format, if( OpenWaveOutPCM( p_aout,
p_aout->output.p_sys->i_wave_device_id,
&p_aout->output.output.i_format,
p_aout->output.output.i_physical_channels, p_aout->output.output.i_physical_channels,
aout_FormatNbChannels( &p_aout->output.output ), aout_FormatNbChannels( &p_aout->output.output ),
p_aout->output.output.i_rate, VLC_FALSE ) p_aout->output.output.i_rate, VLC_FALSE )
...@@ -327,6 +413,8 @@ static int Open( vlc_object_t *p_this ) ...@@ -327,6 +413,8 @@ static int Open( vlc_object_t *p_this )
msg_Err( p_aout, "out of memory" ); msg_Err( p_aout, "out of memory" );
return 1; return 1;
} }
p_aout->output.p_sys->i_repeat_counter = 0;
/* Zero the buffer. WinCE doesn't have calloc(). */ /* Zero the buffer. WinCE doesn't have calloc(). */
memset( p_aout->output.p_sys->p_silence_buffer, 0, memset( p_aout->output.p_sys->p_silence_buffer, 0,
...@@ -337,11 +425,18 @@ static int Open( vlc_object_t *p_this ) ...@@ -337,11 +425,18 @@ static int Open( vlc_object_t *p_this )
vlc_object_create( p_aout, sizeof(notification_thread_t) ); 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->p_aout = p_aout;
p_aout->output.p_sys->event = CreateEvent( NULL, FALSE, FALSE, NULL ); p_aout->output.p_sys->event = CreateEvent( NULL, FALSE, FALSE, NULL );
p_aout->output.p_sys->new_buffer_event = CreateEvent( NULL, FALSE, FALSE, NULL );
/* define startpoint of playback on first call to play()
like alsa does (instead of playing a blank sample) */
p_aout->output.p_sys->b_playing = 0;
p_aout->output.p_sys->start_date = 0;
/* Then launch the notification thread */ /* Then launch the notification thread */
if( vlc_thread_create( p_aout->output.p_sys->p_notif, if( vlc_thread_create( p_aout->output.p_sys->p_notif,
"waveOut Notification Thread", WaveOutThread, "waveOut Notification Thread", WaveOutThread,
VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) ) VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
{ {
msg_Err( p_aout, "cannot create WaveOutThread" ); msg_Err( p_aout, "cannot create WaveOutThread" );
} }
...@@ -353,8 +448,6 @@ static int Open( vlc_object_t *p_this ) ...@@ -353,8 +448,6 @@ static int Open( vlc_object_t *p_this )
p_aout->output.p_sys->waveheader[i].dwFlags = WHDR_DONE; p_aout->output.p_sys->waveheader[i].dwFlags = WHDR_DONE;
p_aout->output.p_sys->waveheader[i].dwUser = 0; p_aout->output.p_sys->waveheader[i].dwUser = 0;
} }
PlayWaveOut( p_aout, p_aout->output.p_sys->h_waveout,
&p_aout->output.p_sys->waveheader[0], NULL );
return 0; return 0;
} }
...@@ -378,13 +471,15 @@ static void Probe( aout_instance_t * p_aout ) ...@@ -378,13 +471,15 @@ static void Probe( aout_instance_t * p_aout )
AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
if( p_aout->output.output.i_physical_channels == i_physical_channels ) if( p_aout->output.output.i_physical_channels == i_physical_channels )
{ {
if( OpenWaveOutPCM( p_aout, &i_format, if( OpenWaveOutPCM( p_aout,
p_aout->output.p_sys->i_wave_device_id,
&i_format,
i_physical_channels, 6, i_physical_channels, 6,
p_aout->output.output.i_rate, VLC_TRUE ) p_aout->output.output.i_rate, VLC_TRUE )
== VLC_SUCCESS ) == VLC_SUCCESS )
{ {
val.i_int = AOUT_VAR_5_1; val.i_int = AOUT_VAR_5_1;
text.psz_string = N_("5.1"); text.psz_string = (char *)N_("5.1");
var_Change( p_aout, "audio-device", var_Change( p_aout, "audio-device",
VLC_VAR_ADDCHOICE, &val, &text ); VLC_VAR_ADDCHOICE, &val, &text );
msg_Dbg( p_aout, "device supports 5.1 channels" ); msg_Dbg( p_aout, "device supports 5.1 channels" );
...@@ -397,13 +492,15 @@ static void Probe( aout_instance_t * p_aout ) ...@@ -397,13 +492,15 @@ static void Probe( aout_instance_t * p_aout )
if( ( p_aout->output.output.i_physical_channels & i_physical_channels ) if( ( p_aout->output.output.i_physical_channels & i_physical_channels )
== i_physical_channels ) == i_physical_channels )
{ {
if( OpenWaveOutPCM( p_aout, &i_format, if( OpenWaveOutPCM( p_aout,
p_aout->output.p_sys->i_wave_device_id,
&i_format,
i_physical_channels, 4, i_physical_channels, 4,
p_aout->output.output.i_rate, VLC_TRUE ) p_aout->output.output.i_rate, VLC_TRUE )
== VLC_SUCCESS ) == VLC_SUCCESS )
{ {
val.i_int = AOUT_VAR_2F2R; val.i_int = AOUT_VAR_2F2R;
text.psz_string = N_("2 Front 2 Rear"); text.psz_string = (char *)N_("2 Front 2 Rear");
var_Change( p_aout, "audio-device", var_Change( p_aout, "audio-device",
VLC_VAR_ADDCHOICE, &val, &text ); VLC_VAR_ADDCHOICE, &val, &text );
msg_Dbg( p_aout, "device supports 4 channels" ); msg_Dbg( p_aout, "device supports 4 channels" );
...@@ -412,26 +509,30 @@ static void Probe( aout_instance_t * p_aout ) ...@@ -412,26 +509,30 @@ static void Probe( aout_instance_t * p_aout )
/* Test for stereo support */ /* Test for stereo support */
i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
if( OpenWaveOutPCM( p_aout, &i_format, if( OpenWaveOutPCM( p_aout,
p_aout->output.p_sys->i_wave_device_id,
&i_format,
i_physical_channels, 2, i_physical_channels, 2,
p_aout->output.output.i_rate, VLC_TRUE ) p_aout->output.output.i_rate, VLC_TRUE )
== VLC_SUCCESS ) == VLC_SUCCESS )
{ {
val.i_int = AOUT_VAR_STEREO; val.i_int = AOUT_VAR_STEREO;
text.psz_string = N_("Stereo"); text.psz_string = (char *)N_("Stereo");
var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
msg_Dbg( p_aout, "device supports 2 channels" ); msg_Dbg( p_aout, "device supports 2 channels" );
} }
/* Test for mono support */ /* Test for mono support */
i_physical_channels = AOUT_CHAN_CENTER; i_physical_channels = AOUT_CHAN_CENTER;
if( OpenWaveOutPCM( p_aout, &i_format, if( OpenWaveOutPCM( p_aout,
p_aout->output.p_sys->i_wave_device_id,
&i_format,
i_physical_channels, 1, i_physical_channels, 1,
p_aout->output.output.i_rate, VLC_TRUE ) p_aout->output.output.i_rate, VLC_TRUE )
== VLC_SUCCESS ) == VLC_SUCCESS )
{ {
val.i_int = AOUT_VAR_MONO; val.i_int = AOUT_VAR_MONO;
text.psz_string = N_("Mono"); text.psz_string = (char *)N_("Mono");
var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
msg_Dbg( p_aout, "device supports 1 channel" ); msg_Dbg( p_aout, "device supports 1 channel" );
} }
...@@ -439,7 +540,9 @@ static void Probe( aout_instance_t * p_aout ) ...@@ -439,7 +540,9 @@ static void Probe( aout_instance_t * p_aout )
/* Test for SPDIF support */ /* Test for SPDIF support */
if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
{ {
if( OpenWaveOut( p_aout, VLC_FOURCC('s','p','d','i'), if( OpenWaveOut( p_aout,
p_aout->output.p_sys->i_wave_device_id,
VLC_FOURCC('s','p','d','i'),
p_aout->output.output.i_physical_channels, p_aout->output.output.i_physical_channels,
aout_FormatNbChannels( &p_aout->output.output ), aout_FormatNbChannels( &p_aout->output.output ),
p_aout->output.output.i_rate, VLC_TRUE ) p_aout->output.output.i_rate, VLC_TRUE )
...@@ -447,7 +550,7 @@ static void Probe( aout_instance_t * p_aout ) ...@@ -447,7 +550,7 @@ static void Probe( aout_instance_t * p_aout )
{ {
msg_Dbg( p_aout, "device supports A/52 over S/PDIF" ); msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
val.i_int = AOUT_VAR_SPDIF; val.i_int = AOUT_VAR_SPDIF;
text.psz_string = N_("A/52 over S/PDIF"); text.psz_string = (char *)N_("A/52 over S/PDIF");
var_Change( p_aout, "audio-device", var_Change( p_aout, "audio-device",
VLC_VAR_ADDCHOICE, &val, &text ); VLC_VAR_ADDCHOICE, &val, &text );
if( config_GetInt( p_aout, "spdif" ) ) if( config_GetInt( p_aout, "spdif" ) )
...@@ -477,6 +580,21 @@ static void Probe( aout_instance_t * p_aout ) ...@@ -477,6 +580,21 @@ static void Probe( aout_instance_t * p_aout )
*****************************************************************************/ *****************************************************************************/
static void Play( aout_instance_t *_p_aout ) static void Play( aout_instance_t *_p_aout )
{ {
if( !_p_aout->output.p_sys->b_playing )
{
_p_aout->output.p_sys->b_playing = 1;
/* get the playing date of the first aout buffer */
_p_aout->output.p_sys->start_date =
aout_FifoFirstDate( _p_aout, &_p_aout->output.fifo );
msg_Dbg( _p_aout, "Wakeup sleeping output thread.");
/* wake up the audio output thread */
SetEvent( _p_aout->output.p_sys->event );
} else {
SetEvent( _p_aout->output.p_sys->new_buffer_event );
}
} }
/***************************************************************************** /*****************************************************************************
...@@ -490,20 +608,68 @@ static void Close( vlc_object_t *p_this ) ...@@ -490,20 +608,68 @@ static void Close( vlc_object_t *p_this )
/* Before calling waveOutClose we must reset the device */ /* Before calling waveOutClose we must reset the device */
vlc_object_kill( p_aout ); vlc_object_kill( p_aout );
waveOutReset( p_sys->h_waveout ); /* wake up the audio thread, to recognize that p_aout died */
/* wake up the audio thread */
SetEvent( p_sys->event ); SetEvent( p_sys->event );
SetEvent( p_sys->new_buffer_event );
vlc_thread_join( p_sys->p_notif ); vlc_thread_join( p_sys->p_notif );
vlc_object_release( p_sys->p_notif ); vlc_object_release( p_sys->p_notif );
CloseHandle( p_sys->event );
/* Close the device */ /*
kill the real output then - when the feed thread
is surely terminated!
old code could be too early in case that "feeding"
was running on termination
at this point now its sure, that there will be no new
data send to the driver, and we can cancel the last
running playbuffers
*/
MMRESULT result = waveOutReset( p_sys->h_waveout );
if(result != MMSYSERR_NOERROR)
{
msg_Err( p_aout, "waveOutReset failed 0x%x", result );
/*
now we must wait, that all buffers are played
because cancel doesn't work in this case...
*/
if(result == MMSYSERR_NOTSUPPORTED)
{
/*
clear currently played (done) buffers,
if returnvalue > 0 (means some buffer still playing)
wait for the driver event callback that one buffer
is finished with playing, and check again
the timeout of 5000ms is just, an emergency exit
of this loop, to avoid deadlock in case of other
(currently not known bugs, problems, errors cases?)
*/
while(
(WaveOutClearDoneBuffers( p_sys ) > 0)
&&
(WaitForSingleObject( p_sys->event, 5000) == WAIT_OBJECT_0)
)
{
msg_Dbg( p_aout, "Wait for waveout device...");
}
}
} else {
WaveOutClearDoneBuffers( p_sys );
}
/* now we can Close the device */
if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR ) if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
{ {
msg_Err( p_aout, "waveOutClose failed" ); msg_Err( p_aout, "waveOutClose failed" );
} }
/*
because so long, the waveout device is playing, the callback
could occur and need the events
*/
CloseHandle( p_sys->event );
CloseHandle( p_sys->new_buffer_event);
free( p_sys->p_silence_buffer ); free( p_sys->p_silence_buffer );
free( p_sys ); free( p_sys );
} }
...@@ -511,7 +677,7 @@ static void Close( vlc_object_t *p_this ) ...@@ -511,7 +677,7 @@ static void Close( vlc_object_t *p_this )
/***************************************************************************** /*****************************************************************************
* OpenWaveOut: open the waveout sound device * OpenWaveOut: open the waveout sound device
****************************************************************************/ ****************************************************************************/
static int OpenWaveOut( aout_instance_t *p_aout, int i_format, static int OpenWaveOut( aout_instance_t *p_aout, uint32_t i_device_id, int i_format,
int i_channels, int i_nb_channels, int i_rate, int i_channels, int i_nb_channels, int i_rate,
vlc_bool_t b_probe ) vlc_bool_t b_probe )
{ {
...@@ -578,8 +744,32 @@ static int OpenWaveOut( aout_instance_t *p_aout, int i_format, ...@@ -578,8 +744,32 @@ static int OpenWaveOut( aout_instance_t *p_aout, int i_format,
sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
} }
if(!b_probe) {
msg_Dbg( p_aout, "OpenWaveDevice-ID: %u", i_device_id);
msg_Dbg( p_aout,"waveformat.Format.cbSize = %d",
waveformat.Format.cbSize);
msg_Dbg( p_aout,"waveformat.Format.wFormatTag = %u",
waveformat.Format.wFormatTag);
msg_Dbg( p_aout,"waveformat.Format.nChannels = %u",
waveformat.Format.nChannels);
msg_Dbg( p_aout,"waveformat.Format.nSamplesPerSec = %d",
(int)waveformat.Format.nSamplesPerSec);
msg_Dbg( p_aout,"waveformat.Format.nAvgBytesPerSec = %u",
(int)waveformat.Format.nAvgBytesPerSec);
msg_Dbg( p_aout,"waveformat.Format.nBlockAlign = %d",
waveformat.Format.nBlockAlign);
msg_Dbg( p_aout,"waveformat.Format.wBitsPerSample = %d",
waveformat.Format.wBitsPerSample);
msg_Dbg( p_aout,"waveformat.Samples.wValidBitsPerSample = %d",
waveformat.Samples.wValidBitsPerSample);
msg_Dbg( p_aout,"waveformat.Samples.wSamplesPerBlock = %d",
waveformat.Samples.wSamplesPerBlock);
msg_Dbg( p_aout,"waveformat.dwChannelMask = %lu",
waveformat.dwChannelMask);
}
/* Open the device */ /* Open the device */
result = waveOutOpen( &p_aout->output.p_sys->h_waveout, WAVE_MAPPER, result = waveOutOpen( &p_aout->output.p_sys->h_waveout, i_device_id,
(WAVEFORMATEX *)&waveformat, (WAVEFORMATEX *)&waveformat,
(DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout, (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) ); CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) );
...@@ -618,19 +808,17 @@ static int OpenWaveOut( aout_instance_t *p_aout, int i_format, ...@@ -618,19 +808,17 @@ static int OpenWaveOut( aout_instance_t *p_aout, int i_format,
/***************************************************************************** /*****************************************************************************
* OpenWaveOutPCM: open a PCM waveout sound device * OpenWaveOutPCM: open a PCM waveout sound device
****************************************************************************/ ****************************************************************************/
static int OpenWaveOutPCM( aout_instance_t *p_aout, int *i_format, static int OpenWaveOutPCM( aout_instance_t *p_aout, uint32_t i_device_id, int *i_format,
int i_channels, int i_nb_channels, int i_rate, int i_channels, int i_nb_channels, int i_rate,
vlc_bool_t b_probe ) vlc_bool_t b_probe )
{ {
vlc_value_t val; vlc_bool_t b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32");
var_Get( p_aout, "waveout-float32", &val );
if( !val.b_bool || OpenWaveOut( p_aout, VLC_FOURCC('f','l','3','2'), if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_FOURCC('f','l','3','2'),
i_channels, i_nb_channels, i_rate, b_probe ) i_channels, i_nb_channels, i_rate, b_probe )
!= VLC_SUCCESS ) != VLC_SUCCESS )
{ {
if ( OpenWaveOut( p_aout, VLC_FOURCC('s','1','6','l'), if ( OpenWaveOut( p_aout, i_device_id, VLC_FOURCC('s','1','6','l'),
i_channels, i_nb_channels, i_rate, b_probe ) i_channels, i_nb_channels, i_rate, b_probe )
!= VLC_SUCCESS ) != VLC_SUCCESS )
{ {
...@@ -653,16 +841,43 @@ static int OpenWaveOutPCM( aout_instance_t *p_aout, int *i_format, ...@@ -653,16 +841,43 @@ static int OpenWaveOutPCM( aout_instance_t *p_aout, int *i_format,
* PlayWaveOut: play a buffer through the WaveOut device * PlayWaveOut: play a buffer through the WaveOut device
*****************************************************************************/ *****************************************************************************/
static int PlayWaveOut( aout_instance_t *p_aout, HWAVEOUT h_waveout, static int PlayWaveOut( aout_instance_t *p_aout, HWAVEOUT h_waveout,
WAVEHDR *p_waveheader, aout_buffer_t *p_buffer ) WAVEHDR *p_waveheader, aout_buffer_t *p_buffer,
vlc_bool_t b_spdif)
{ {
MMRESULT result; MMRESULT result;
/* Prepare the buffer */ /* Prepare the buffer */
if( p_buffer != NULL ) if( p_buffer != NULL )
{
p_waveheader->lpData = p_buffer->p_buffer; p_waveheader->lpData = p_buffer->p_buffer;
else /*
copy the buffer to the silence buffer :) so in case we don't
get the next buffer fast enough (I will repeat this one a time
for AC3 / DTS and SPDIF this will sound better instead of
a hickup)
*/
if(b_spdif)
{
p_aout->p_libvlc->pf_memcpy( p_aout->output.p_sys->p_silence_buffer,
p_buffer->p_buffer,
p_aout->output.p_sys->i_buffer_size );
p_aout->output.p_sys->i_repeat_counter = 2;
}
} else {
/* Use silence buffer instead */ /* Use silence buffer instead */
if(p_aout->output.p_sys->i_repeat_counter)
{
p_aout->output.p_sys->i_repeat_counter--;
if(!p_aout->output.p_sys->i_repeat_counter)
{
p_aout->p_libvlc->pf_memset( p_aout->output.p_sys->p_silence_buffer,
0x00,
p_aout->output.p_sys->i_buffer_size
);
}
}
p_waveheader->lpData = p_aout->output.p_sys->p_silence_buffer; p_waveheader->lpData = p_aout->output.p_sys->p_silence_buffer;
}
p_waveheader->dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1; p_waveheader->dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
p_waveheader->dwBufferLength = p_aout->output.p_sys->i_buffer_size; p_waveheader->dwBufferLength = p_aout->output.p_sys->i_buffer_size;
...@@ -711,10 +926,47 @@ static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg, ...@@ -711,10 +926,47 @@ static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
} }
/* Don't wake up the thread too much */ /* Don't wake up the thread too much */
if( i_queued_frames < FRAMES_NUM / 2 ) if( i_queued_frames <= FRAMES_NUM/2 )
SetEvent( p_aout->output.p_sys->event ); SetEvent( p_aout->output.p_sys->event );
} }
/****************************************************************************
* WaveOutClearDoneBuffers: Clear all done marked buffers, and free aout_bufer
****************************************************************************
* return value is the number of still playing buffers in the queue
****************************************************************************/
static int WaveOutClearDoneBuffers(aout_sys_t *p_sys)
{
WAVEHDR *p_waveheader = p_sys->waveheader;
int i_queued_frames = 0;
for( int i = 0; i < FRAMES_NUM; i++ )
{
if( (p_waveheader[i].dwFlags & WHDR_DONE) &&
p_waveheader[i].dwUser )
{
aout_buffer_t *p_buffer =
(aout_buffer_t *)(p_waveheader[i].dwUser);
/* Unprepare and free the buffers which has just been played */
waveOutUnprepareHeader( p_sys->h_waveout, &p_waveheader[i],
sizeof(WAVEHDR) );
if( p_waveheader[i].dwUser != 1 )
aout_BufferFree( p_buffer );
p_waveheader[i].dwUser = 0;
}
/* Check if frame buf is available */
if( !(p_waveheader[i].dwFlags & WHDR_DONE) )
{
i_queued_frames++;
}
}
return i_queued_frames;
}
/***************************************************************************** /*****************************************************************************
* WaveOutThread: this thread will capture play notification events. * WaveOutThread: this thread will capture play notification events.
***************************************************************************** *****************************************************************************
...@@ -730,39 +982,36 @@ static void WaveOutThread( notification_thread_t *p_notif ) ...@@ -730,39 +982,36 @@ static void WaveOutThread( notification_thread_t *p_notif )
WAVEHDR *p_waveheader = p_sys->waveheader; WAVEHDR *p_waveheader = p_sys->waveheader;
int i, i_queued_frames; int i, i_queued_frames;
vlc_bool_t b_sleek; vlc_bool_t b_sleek;
mtime_t next_date;
uint32_t i_buffer_length = 64;
/* We don't want any resampling when using S/PDIF */ /* We don't want any resampling when using S/PDIF */
b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'); b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i');
while( 1 ) // wait for first call to "play()"
{ while( !p_sys->start_date && !p_aout->b_die )
WaitForSingleObject( p_sys->event, INFINITE ); WaitForSingleObject( p_sys->event, INFINITE );
if( p_aout->b_die )
return;
/* Cleanup and find out the current latency */ msg_Dbg( p_aout, "will start to play in "I64Fd" us",
i_queued_frames = 0; (p_sys->start_date - AOUT_PTS_TOLERANCE/4)-mdate());
for( i = 0; i < FRAMES_NUM; i++ )
{
if( (p_waveheader[i].dwFlags & WHDR_DONE) &&
p_waveheader[i].dwUser )
{
aout_buffer_t *p_buffer =
(aout_buffer_t *)(p_waveheader[i].dwUser);
/* Unprepare and free the buffers which has just been played */
waveOutUnprepareHeader( p_sys->h_waveout, &p_waveheader[i],
sizeof(WAVEHDR) );
if( p_waveheader[i].dwUser != 1 ) // than wait a short time... before grabbing first frames
aout_BufferFree( p_buffer ); mwait( p_sys->start_date - AOUT_PTS_TOLERANCE/4 );
p_waveheader[i].dwUser = 0; #define waveout_warn(msg) msg_Warn( p_aout, "aout_OutputNextBuffer no buffer "\
} "got next_date=%d ms, "\
"%d frames to play, "\
"starving? %d, %s",(int)(next_date/(mtime_t)1000), \
i_queued_frames, \
p_aout->output.b_starving, msg);
next_date = mdate();
/* Check if frame buf is available */ while( !p_aout->b_die )
if( !(p_waveheader[i].dwFlags & WHDR_DONE) ) {
{ /* Cleanup and find out the current latency */
i_queued_frames++; i_queued_frames = WaveOutClearDoneBuffers( p_sys );
}
}
if( p_aout->b_die ) return; if( p_aout->b_die ) return;
...@@ -772,18 +1021,57 @@ static void WaveOutThread( notification_thread_t *p_notif ) ...@@ -772,18 +1021,57 @@ static void WaveOutThread( notification_thread_t *p_notif )
/* Check if frame buf is available */ /* Check if frame buf is available */
if( p_waveheader[i].dwFlags & WHDR_DONE ) if( p_waveheader[i].dwFlags & WHDR_DONE )
{ {
// next_date = mdate() + 1000000 * i_queued_frames /
// p_aout->output.output.i_rate * p_aout->output.i_nb_samples;
// the realtime has got our back-site:) to come in sync
if(next_date < mdate())
next_date = mdate();
/* Take into account the latency */ /* Take into account the latency */
p_buffer = aout_OutputNextBuffer( p_aout, p_buffer = aout_OutputNextBuffer( p_aout,
mdate() + 1000000 * i_queued_frames / next_date,
p_aout->output.output.i_rate * p_aout->output.i_nb_samples,
b_sleek ); b_sleek );
if(!p_buffer)
{
#if 0
msg_Dbg( p_aout, "aout_OutputNextBuffer no buffer "\
"got next_date=%d ms, "\
"%d frames to play, "\
"starving? %d",(int)(next_date/(mtime_t)1000),
i_queued_frames,
p_aout->output.b_starving);
#endif
if(p_aout->output.b_starving)
{
// means we are too early to request a new buffer?
waveout_warn("waiting...")
next_date = aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
mwait( next_date - AOUT_PTS_TOLERANCE/4 );
next_date = mdate();
p_buffer = aout_OutputNextBuffer( p_aout,
next_date,
b_sleek
);
}
}
if( !p_buffer && i_queued_frames ) if( !p_buffer && i_queued_frames )
{ {
/* We aren't late so no need to play a blank sample */ /* We aren't late so no need to play a blank sample */
break; break;
} }
if( p_buffer )
{
mtime_t buffer_length = (p_buffer->end_date
- p_buffer->start_date);
next_date = next_date + buffer_length;
i_buffer_length = buffer_length/1000;
}
/* Do the channel reordering */ /* Do the channel reordering */
if( p_buffer && p_sys->b_chan_reorder ) if( p_buffer && p_sys->b_chan_reorder )
{ {
...@@ -795,12 +1083,33 @@ static void WaveOutThread( notification_thread_t *p_notif ) ...@@ -795,12 +1083,33 @@ static void WaveOutThread( notification_thread_t *p_notif )
} }
PlayWaveOut( p_aout, p_sys->h_waveout, PlayWaveOut( p_aout, p_sys->h_waveout,
&p_waveheader[i], p_buffer ); &p_waveheader[i], p_buffer, b_sleek );
i_queued_frames++; i_queued_frames++;
} }
} }
if( p_aout->b_die ) return;
/*
deal with the case that the loop didn't fillup the buffer to the
max - instead of waiting that half the buffer is played before
fillup the waveout buffers, wait only for the next sample buffer
to arrive at the play method...
this will also avoid, that the last buffer is play until the
end, and then trying to get more data, so it will also
work - if the next buffer will arrive some ms before the
last buffer is finished.
*/
if(i_queued_frames < FRAMES_NUM)
WaitForSingleObject( p_sys->new_buffer_event, INFINITE );
else
WaitForSingleObject( p_sys->event, INFINITE );
} }
#undef waveout_warn
} }
static int VolumeInfos( aout_instance_t * p_aout, audio_volume_t * pi_soft ) static int VolumeInfos( aout_instance_t * p_aout, audio_volume_t * pi_soft )
...@@ -839,3 +1148,98 @@ static int VolumeSet( aout_instance_t * p_aout, audio_volume_t i_volume ) ...@@ -839,3 +1148,98 @@ static int VolumeSet( aout_instance_t * p_aout, audio_volume_t i_volume )
p_aout->output.i_volume = i_volume; p_aout->output.i_volume = i_volume;
return 0; return 0;
} }
/*
reload the configuration drop down list, of the Audio Devices
*/
static int ReloadWaveoutDevices( vlc_object_t *p_this, char const *psz_name,
vlc_value_t newval, vlc_value_t oldval, void *data )
{
int i;
module_config_t *p_item = config_FindConfig( p_this, psz_name );
if( !p_item ) return VLC_SUCCESS;
/* Clear-up the current list */
if( p_item->i_list )
{
/* Keep the first entry */
for( i = 1; i < p_item->i_list; i++ )
{
free((char *)(p_item->ppsz_list[i]) );
free((char *)(p_item->ppsz_list_text[i]) );
}
/* TODO: Remove when no more needed */
p_item->ppsz_list[i] = NULL;
p_item->ppsz_list_text[i] = NULL;
}
p_item->i_list = 1;
int wave_devices = waveOutGetNumDevs();
p_item->ppsz_list =
(char **)realloc( p_item->ppsz_list,
(wave_devices+2) * sizeof(char *) );
p_item->ppsz_list_text =
(char **)realloc( p_item->ppsz_list_text,
(wave_devices+2) * sizeof(char *) );
WAVEOUTCAPS caps;
char sz_dev_name[MAXPNAMELEN+32];
int j=1;
for(int i=0; i<wave_devices; i++)
{
if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
== MMSYSERR_NOERROR)
{
sprintf(sz_dev_name,psz_device_name_fmt,caps.szPname,
caps.wMid,
caps.wPid
);
p_item->ppsz_list[j] = strdup( sz_dev_name );
p_item->ppsz_list_text[j] = FromLocaleDup( sz_dev_name );
p_item->i_list++;
j++;
}
}
p_item->ppsz_list[j] = NULL;
p_item->ppsz_list_text[j] = NULL;
/* Signal change to the interface */
p_item->b_dirty = VLC_TRUE;
return VLC_SUCCESS;
}
/*
convert devicename to device ID for output
if device not found return WAVE_MAPPER, so let
windows decide which prefered audio device
should be used.
*/
static uint32_t findDeviceID(char *psz_device_name)
{
if(!psz_device_name)
return WAVE_MAPPER;
uint32_t wave_devices = waveOutGetNumDevs();
WAVEOUTCAPS caps;
char sz_dev_name[MAXPNAMELEN+32];
for(uint32_t i=0; i<wave_devices; i++)
{
if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
== MMSYSERR_NOERROR)
{
sprintf(sz_dev_name,psz_device_name_fmt,caps.szPname,
caps.wMid,
caps.wPid
);
if(!stricmp(sz_dev_name,psz_device_name))
return i;
}
}
return WAVE_MAPPER;
}
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