/*****************************************************************************
* waveout.c : Windows waveOut plugin for vlc
*****************************************************************************
- * Copyright (C) 2001 VideoLAN
+ * Copyright (C) 2001 the VideoLAN team
* $Id$
*
* Authors: Gildas Bazin <gbazin@videolan.org>
- *
+ *
* 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
* (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
#include <windows.h>
#include <mmsystem.h>
-#define FRAME_SIZE 1024 /* The size is in samples, not in bytes */
+#define FRAME_SIZE 4096 /* The size is in samples, not in bytes */
#define FRAMES_NUM 8
/*****************************************************************************
*****************************************************************************/
#ifdef UNDER_CE
# define DWORD_PTR DWORD
+# ifdef waveOutGetDevCaps
+# undef waveOutGetDevCaps
+ MMRESULT WINAPI waveOutGetDevCaps(UINT, LPWAVEOUTCAPS, UINT);
+# endif
#endif
#ifndef WAVE_FORMAT_IEEE_FLOAT
static void Probe ( aout_instance_t * );
static int OpenWaveOut ( aout_instance_t *, int, int, int, int, vlc_bool_t );
static int OpenWaveOutPCM( aout_instance_t *, int*, int, int, int, vlc_bool_t );
-static void CheckReordering( aout_instance_t *, int );
static int PlayWaveOut ( aout_instance_t *, HWAVEOUT, WAVEHDR *,
aout_buffer_t * );
static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD, DWORD, DWORD );
static void WaveOutThread( notification_thread_t * );
-static void InterleaveFloat32( float *, int *, int );
-static void InterleaveS16( int16_t *, int *, int );
+static int VolumeInfos( aout_instance_t *, audio_volume_t * );
+static int VolumeGet( aout_instance_t *, audio_volume_t * );
+static int VolumeSet( aout_instance_t *, audio_volume_t );
/*****************************************************************************
* Module descriptor
"audio output mode (which is not well supported by some soundcards)." )
vlc_module_begin();
+ set_shortname( "WaveOut" );
set_description( _("Win32 waveOut extension output") );
set_capability( "audio output", 50 );
- add_bool( "waveout-float32", 1, NULL, FLOAT_TEXT, FLOAT_LONGTEXT, VLC_TRUE );
+ set_category( CAT_AUDIO );
+ set_subcategory( SUBCAT_AUDIO_AOUT );
+ add_bool( "waveout-float32", 1, 0, FLOAT_TEXT, FLOAT_LONGTEXT, VLC_TRUE );
set_callbacks( Open, Close );
vlc_module_end();
byte_t *p_silence_buffer; /* buffer we use to play silence */
vlc_bool_t b_chan_reorder; /* do we need channel reordering */
- int *pi_chan_table;
+ int pi_chan_table[AOUT_CHAN_MAX];
};
-static const uint32_t pi_channels_in[] =
+static const uint32_t pi_channels_src[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
+ AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
- AOUT_CHAN_CENTER, AOUT_CHAN_LFE };
+ AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 };
+static const uint32_t pi_channels_in[] =
+ { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
+ SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT,
+ SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
+ SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, 0 };
static const uint32_t pi_channels_out[] =
{ SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
+ SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY,
SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
- SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY };
-static const uint32_t pi_channels_ordered[] =
- { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT, SPEAKER_FRONT_CENTER,
- SPEAKER_LOW_FREQUENCY,
- SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT };
+ SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, 0 };
/*****************************************************************************
* Open: open the audio device
}
p_aout->output.pf_play = Play;
- p_aout->output.p_sys->pi_chan_table = NULL;
p_aout->b_die = VLC_FALSE;
if( var_Type( p_aout, "audio-device" ) == 0 )
}
else
{
+ WAVEOUTCAPS wocaps;
+
if( val.i_int == AOUT_VAR_5_1 )
{
p_aout->output.output.i_physical_channels
p_aout->output.output.i_bytes_per_frame;
aout_VolumeSoftInit( p_aout );
+
+ /* Check for hardware volume support */
+ if( waveOutGetDevCaps( (UINT_PTR)p_aout->output.p_sys->h_waveout,
+ &wocaps, sizeof(wocaps) ) == MMSYSERR_NOERROR &&
+ wocaps.dwSupport & WAVECAPS_VOLUME )
+ {
+ DWORD i_dummy;
+ if( waveOutGetVolume( p_aout->output.p_sys->h_waveout, &i_dummy )
+ == MMSYSERR_NOERROR )
+ {
+ p_aout->output.pf_volume_infos = VolumeInfos;
+ p_aout->output.pf_volume_get = VolumeGet;
+ p_aout->output.pf_volume_set = VolumeSet;
+ }
+ }
}
for( i = 0; i < FRAMES_NUM; i++ )
{
p_aout->output.p_sys->waveheader[i].dwFlags = WHDR_DONE;
+ 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 );
static void Close( vlc_object_t *p_this )
{
aout_instance_t *p_aout = (aout_instance_t *)p_this;
+ aout_sys_t *p_sys = p_aout->output.p_sys;
/* Before calling waveOutClose we must reset the device */
p_aout->b_die = VLC_TRUE;
- /* wake up the audio thread */
- SetEvent( p_aout->output.p_sys->event );
- vlc_thread_join( p_aout->output.p_sys->p_notif );
- CloseHandle( p_aout->output.p_sys->event );
-
- /* Wait for the waveout buffers to be freed */
- while( VLC_TRUE )
- {
- int i;
- vlc_bool_t b_not_done = VLC_FALSE;
+ waveOutReset( p_sys->h_waveout );
- for( i = 0; i < FRAMES_NUM; i++ )
- {
- if( !(p_aout->output.p_sys->waveheader[i].dwFlags & WHDR_DONE) )
- b_not_done = VLC_TRUE;
- }
-
- if( !b_not_done )
- break;
-
- msleep( 1000 );
- }
-
- waveOutReset( p_aout->output.p_sys->h_waveout );
+ /* wake up the audio thread */
+ SetEvent( p_sys->event );
+ vlc_thread_join( p_sys->p_notif );
+ vlc_object_destroy( p_sys->p_notif );
+ CloseHandle( p_sys->event );
/* Close the device */
- if( waveOutClose( p_aout->output.p_sys->h_waveout ) != MMSYSERR_NOERROR )
+ if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
{
msg_Err( p_aout, "waveOutClose failed" );
}
- free( p_aout->output.p_sys->p_silence_buffer );
- if( p_aout->output.p_sys->pi_chan_table )
- free( p_aout->output.p_sys->pi_chan_table );
- free( p_aout->output.p_sys );
+ free( p_sys->p_silence_buffer );
+ free( p_sys );
}
/*****************************************************************************
#define waveformat p_aout->output.p_sys->waveformat
waveformat.dwChannelMask = 0;
- for( i = 0; i < sizeof(pi_channels_in)/sizeof(uint32_t); i++ )
+ for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ )
{
- if( i_channels & pi_channels_in[i] )
- waveformat.dwChannelMask |= pi_channels_out[i];
+ if( i_channels & pi_channels_src[i] )
+ waveformat.dwChannelMask |= pi_channels_in[i];
}
switch( i_format )
return VLC_EGENERIC;
}
- CheckReordering( p_aout, i_nb_channels );
+ p_aout->output.p_sys->b_chan_reorder =
+ aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
+ waveformat.dwChannelMask, i_nb_channels,
+ p_aout->output.p_sys->pi_chan_table );
+
+ if( p_aout->output.p_sys->b_chan_reorder )
+ {
+ msg_Dbg( p_aout, "channel reordering needed" );
+ }
return VLC_SUCCESS;
}
}
-/*****************************************************************************
- * CheckReordering: Check if we need to do some channel re-ordering (the ac3
- * channel order is different from the one chosen by
- * Microsoft).
- *****************************************************************************/
-static void CheckReordering( aout_instance_t *p_aout, int i_nb_channels )
-{
- int i, j, k, l;
-
-#define waveformat p_aout->output.p_sys->waveformat
-#define pi_chan_table p_aout->output.p_sys->pi_chan_table
-
- p_aout->output.p_sys->b_chan_reorder = VLC_FALSE;
-
- pi_chan_table = malloc( i_nb_channels * sizeof(int) );
- if( !pi_chan_table )
- {
- return;
- }
-
- for( i = 0, j = 0;
- i < (int)(sizeof(pi_channels_out)/sizeof(uint32_t)); i++ )
- {
- if( waveformat.dwChannelMask & pi_channels_out[i] )
- {
- for( k = 0, l = 0;
- pi_channels_out[i] != pi_channels_ordered[k]; k++ )
- {
- if( waveformat.dwChannelMask & pi_channels_ordered[k] )
- {
- l++;
- }
- }
-
- pi_chan_table[j] = l;
-
- j++;
- }
- }
-
- for( i = 0; i < i_nb_channels; i++ )
- {
- if( pi_chan_table[i] != i )
- {
- p_aout->output.p_sys->b_chan_reorder = VLC_TRUE;
- }
- }
-
- if( p_aout->output.p_sys->b_chan_reorder )
- {
- msg_Dbg( p_aout, "channel reordering needed" );
- }
-
-#undef pi_chan_table
-#undef waveformat
-}
-
/*****************************************************************************
* PlayWaveOut: play a buffer through the WaveOut device
*****************************************************************************/
/* Use silence buffer instead */
p_waveheader->lpData = p_aout->output.p_sys->p_silence_buffer;
- p_waveheader->dwUser = (DWORD_PTR)p_buffer;
+ 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->dwFlags = 0;
DWORD dwParam1, DWORD dwParam2 )
{
aout_instance_t *p_aout = (aout_instance_t *)_p_aout;
- WAVEHDR *p_waveheader = (WAVEHDR *)dwParam1;
int i, i_queued_frames = 0;
if( uMsg != WOM_DONE ) return;
- /* Unprepare and free the buffer which has just been played */
- waveOutUnprepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
- if( p_waveheader->dwUser )
- aout_BufferFree( (aout_buffer_t *)p_waveheader->dwUser );
-
if( p_aout->b_die ) return;
/* Find out the current latency */
}
/*****************************************************************************
- * InterleaveFloat32/S16: change the channel order to the Microsoft one.
- *****************************************************************************/
-static void InterleaveFloat32( float *p_buf, int *pi_chan_table,
- int i_nb_channels )
-{
- int i, j;
- float p_tmp[10];
-
- for( i = 0; i < FRAME_SIZE; i++ )
- {
- for( j = 0; j < i_nb_channels; j++ )
- {
- p_tmp[pi_chan_table[j]] = p_buf[i*i_nb_channels + j];
- }
-
- memcpy( &p_buf[i*i_nb_channels], p_tmp,
- i_nb_channels * sizeof(float) );
- }
-}
-
-static void InterleaveS16( int16_t *p_buf, int *pi_chan_table,
- int i_nb_channels )
-{
- int i, j;
- int16_t p_tmp[10];
-
- for( i = 0; i < FRAME_SIZE; i++ )
- {
- for( j = 0; j < i_nb_channels; j++ )
- {
- p_tmp[pi_chan_table[j]] = p_buf[i*i_nb_channels + j];
- }
-
- memcpy( &p_buf[i*i_nb_channels], p_tmp,
- i_nb_channels * sizeof(int16_t) );
- }
-}
-
-/*****************************************************************************
- * WaveOutThread: this thread will capture play notification events.
+ * WaveOutThread: this thread will capture play notification events.
*****************************************************************************
* We use this thread to feed new audio samples to the sound card because
* we are not authorized to use waveOutWrite() directly in the waveout
static void WaveOutThread( notification_thread_t *p_notif )
{
aout_instance_t *p_aout = p_notif->p_aout;
+ aout_sys_t *p_sys = p_aout->output.p_sys;
aout_buffer_t *p_buffer = NULL;
- vlc_bool_t b_sleek;
+ WAVEHDR *p_waveheader = p_sys->waveheader;
int i, i_queued_frames;
+ vlc_bool_t b_sleek;
/* We don't want any resampling when using S/PDIF */
b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i');
while( 1 )
{
- WaitForSingleObject( p_aout->output.p_sys->event, INFINITE );
- if( p_aout->b_die ) return;
+ WaitForSingleObject( p_sys->event, INFINITE );
- /* Find out the current latency */
+ /* Cleanup and find out the current latency */
i_queued_frames = 0;
for( i = 0; i < FRAMES_NUM; i++ )
{
+ if( (p_waveheader[i].dwFlags & WHDR_DONE) &&
+ 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( (aout_buffer_t *)p_waveheader[i].dwUser );
+
+ p_waveheader[i].dwUser = 0;
+ }
+
/* Check if frame buf is available */
- if( !(p_aout->output.p_sys->waveheader[i].dwFlags & WHDR_DONE) )
+ if( !(p_waveheader[i].dwFlags & WHDR_DONE) )
{
i_queued_frames++;
}
}
+ if( p_aout->b_die ) return;
+
/* Try to fill in as many frame buffers as possible */
for( i = 0; i < FRAMES_NUM; i++ )
{
/* Check if frame buf is available */
- if( p_aout->output.p_sys->waveheader[i].dwFlags & WHDR_DONE )
+ if( p_waveheader[i].dwFlags & WHDR_DONE )
{
/* Take into account the latency */
p_buffer = aout_OutputNextBuffer( p_aout,
break;
}
- /* Do the channel reordering here */
- if( p_buffer && p_aout->output.p_sys->b_chan_reorder )
+ /* Do the channel reordering */
+ if( p_buffer && p_sys->b_chan_reorder )
{
- if( p_aout->output.output.i_format ==
- VLC_FOURCC('s','1','6','l') )
- InterleaveS16( (int16_t *)p_buffer->p_buffer,
- p_aout->output.p_sys->pi_chan_table,
- aout_FormatNbChannels( &p_aout->output.output ) );
- else
- InterleaveFloat32( (float *)p_buffer->p_buffer,
- p_aout->output.p_sys->pi_chan_table,
- aout_FormatNbChannels( &p_aout->output.output ) );
+ aout_ChannelReorder( p_buffer->p_buffer,
+ p_buffer->i_nb_bytes,
+ p_sys->waveformat.Format.nChannels,
+ p_sys->pi_chan_table,
+ p_sys->waveformat.Format.wBitsPerSample );
}
- PlayWaveOut( p_aout, p_aout->output.p_sys->h_waveout,
- &p_aout->output.p_sys->waveheader[i] , p_buffer );
+ PlayWaveOut( p_aout, p_sys->h_waveout,
+ &p_waveheader[i], p_buffer );
i_queued_frames++;
}
}
}
}
+
+static int VolumeInfos( aout_instance_t * p_aout, audio_volume_t * pi_soft )
+{
+ *pi_soft = AOUT_VOLUME_MAX / 2;
+ return 0;
+}
+
+static int VolumeGet( aout_instance_t * p_aout, audio_volume_t * pi_volume )
+{
+ DWORD i_waveout_vol;
+
+#ifdef UNDER_CE
+ waveOutGetVolume( 0, &i_waveout_vol );
+#else
+ waveOutGetVolume( p_aout->output.p_sys->h_waveout, &i_waveout_vol );
+#endif
+
+ i_waveout_vol &= 0xFFFF;
+ *pi_volume = p_aout->output.i_volume =
+ (i_waveout_vol * AOUT_VOLUME_MAX + 0xFFFF /*rounding*/) / 2 / 0xFFFF;
+ return 0;
+}
+
+static int VolumeSet( aout_instance_t * p_aout, audio_volume_t i_volume )
+{
+ unsigned long i_waveout_vol = i_volume * 0xFFFF * 2 / AOUT_VOLUME_MAX;
+ i_waveout_vol |= (i_waveout_vol << 16);
+
+#ifdef UNDER_CE
+ waveOutSetVolume( 0, i_waveout_vol );
+#else
+ waveOutSetVolume( p_aout->output.p_sys->h_waveout, i_waveout_vol );
+#endif
+
+ p_aout->output.i_volume = i_volume;
+ return 0;
+}