* waveout.c : Windows waveOut plugin for vlc
*****************************************************************************
* Copyright (C) 2001 VideoLAN
- * $Id: waveout.c,v 1.1 2002/08/07 21:36:55 massiot Exp $
+ * $Id: waveout.c,v 1.6 2002/08/30 23:27:06 massiot Exp $
*
* Authors: Gildas Bazin <gbazin@netcourrier.com>
*
#include <vlc/vlc.h>
#include <vlc/aout.h>
+#include "aout_internal.h"
#include <mmsystem.h>
+#define FRAME_SIZE 2048 /* The size is in samples, not in bytes */
+
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
-
-static int SetFormat ( aout_thread_t * );
-static int GetBufInfo ( aout_thread_t *, int );
-static void Play ( aout_thread_t *, byte_t *, int );
+static void Play ( aout_instance_t * );
/* local functions */
-static int OpenWaveOutDevice( aout_thread_t *p_aout );
+static int OpenWaveOut ( aout_instance_t *p_aout, int i_format,
+ int i_channels, int i_rate );
+static int PlayWaveOut ( aout_instance_t *, HWAVEOUT, WAVEHDR *,
+ aout_buffer_t * );
+static void CALLBACK WaveOutCallback ( HWAVEOUT h_waveout, UINT uMsg,
+ DWORD _p_aout,
+ DWORD dwParam1, DWORD dwParam2 );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("Win32 waveOut extension module") );
- set_capability( "audio output", 250 );
+ set_capability( "audio output", 50 );
set_callbacks( Open, Close );
vlc_module_end();
* This structure is part of the audio output thread descriptor.
* It describes the waveOut specific properties of an audio device.
*****************************************************************************/
-
-#define NUMBUF 3 /* We use triple buffering to be on the safe side */
-
struct aout_sys_t
{
HWAVEOUT h_waveout; /* handle to waveout instance */
- WAVEFORMATEX waveformat; /* Audio format */
+ WAVEFORMATEX waveformat; /* audio format */
- WAVEHDR waveheader[NUMBUF];
+ WAVEHDR waveheader[2];
- int i_current_buffer;
+ int i_buffer_size;
- DWORD dw_counter; /* Number of bytes played since beginning */
+ byte_t *p_silence_buffer; /* buffer we use to play silence */
};
/*****************************************************************************
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
- aout_thread_t *p_aout = (aout_thread_t *)p_this;
- int i;
+ aout_instance_t *p_aout = (aout_instance_t *)p_this;
/* Allocate structure */
- p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
+ p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
- if( p_aout->p_sys == NULL )
+ if( p_aout->output.p_sys == NULL )
{
msg_Err( p_aout, "out of memory" );
- return( 1 );
+ return 1;
+ }
+
+ p_aout->output.pf_play = Play;
+
+ /* calculate the frame size in bytes */
+ p_aout->output.p_sys->i_buffer_size = FRAME_SIZE * sizeof(s16)
+ * p_aout->output.p_sys->waveformat.nChannels;
+ /* Allocate silence buffer */
+ p_aout->output.p_sys->p_silence_buffer =
+ calloc( p_aout->output.p_sys->i_buffer_size, 1 );
+ if( p_aout->output.p_sys->p_silence_buffer == NULL )
+ {
+ msg_Err( p_aout, "out of memory" );
+ return 1;
}
- p_aout->pf_setformat = SetFormat;
- p_aout->pf_getbufinfo = GetBufInfo;
- p_aout->pf_play = Play;
+ /* We need to open the device with default values to be sure it is
+ * available */
+ if ( OpenWaveOut( p_aout, WAVE_FORMAT_PCM, 2, 44100 ) )
+ {
+ msg_Err( p_aout, "cannot open waveout" );
+ return 1;
+ }
+
+ waveOutReset( p_aout->output.p_sys->h_waveout );
- /* Initialize some variables */
- p_aout->p_sys->i_current_buffer = 0;
- for( i=0; i<NUMBUF; i++)
- p_aout->p_sys->waveheader[i].lpData = malloc( 1 );
+ p_aout->output.output.i_format = AOUT_FMT_S16_NE;
+ p_aout->output.i_nb_samples = FRAME_SIZE;
- return OpenWaveOutDevice( p_aout );
+ /* We need to kick off the playback in order to have the callback properly
+ * working */
+ PlayWaveOut( p_aout, p_aout->output.p_sys->h_waveout,
+ &p_aout->output.p_sys->waveheader[0], NULL );
+ PlayWaveOut( p_aout, p_aout->output.p_sys->h_waveout,
+ &p_aout->output.p_sys->waveheader[1], NULL );
+ return 0;
}
/*****************************************************************************
- * SetFormat: reset the audio device and sets its format
+ * Play: play a sound buffer
*****************************************************************************
- * This functions set a new audio format.
- * For this we need to close the current device and create another
- * one with the desired format.
+ * This doesn't actually play the buffer. This just stores the buffer so it
+ * can be played by the callback thread.
*****************************************************************************/
-static int SetFormat( aout_thread_t *p_aout )
+static void Play( aout_instance_t *p_aout )
{
- msg_Dbg( p_aout, "SetFormat" );
+}
+
+/*****************************************************************************
+ * Close: close the audio device
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+ aout_instance_t *p_aout = (aout_instance_t *)p_this;
- /* Check if the format has changed */
+ /* Before calling waveOutClose we must reset the device */
+ waveOutReset( p_aout->output.p_sys->h_waveout );
- if( (p_aout->p_sys->waveformat.nChannels != p_aout->i_channels) ||
- (p_aout->p_sys->waveformat.nSamplesPerSec != p_aout->i_rate) )
+ /* Close the device */
+ if( waveOutClose( p_aout->output.p_sys->h_waveout ) != MMSYSERR_NOERROR )
{
- /* Before calling waveOutClose we must reset the device */
- waveOutReset( p_aout->p_sys->h_waveout );
+ msg_Err( p_aout, "waveOutClose failed" );
+ }
- if( waveOutClose( p_aout->p_sys->h_waveout ) != MMSYSERR_NOERROR )
- {
- msg_Err( p_aout, "waveOutClose failed" );
- }
+ /* Free silence buffer */
+ free( p_aout->output.p_sys->p_silence_buffer );
- return OpenWaveOutDevice( p_aout );
+ if( p_aout->output.p_sys != NULL )
+ {
+ free( p_aout->output.p_sys );
+ p_aout->output.p_sys = NULL;
}
-
- return 0;
}
/*****************************************************************************
- * GetBufInfo: buffer status query
- *****************************************************************************
- * returns the number of bytes in the audio buffer that have not yet been
- * sent to the sound device.
- *****************************************************************************/
-static int GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
+ * OpenWaveOut: open the waveout sound device
+ ****************************************************************************/
+static int OpenWaveOut( aout_instance_t *p_aout, int i_format,
+ int i_channels, int i_rate )
{
- MMTIME mmtime;
+ MMRESULT result;
+
+ /* Set sound format */
+ p_aout->output.p_sys->waveformat.wFormatTag = i_format;
+ p_aout->output.p_sys->waveformat.nChannels = i_channels;
+ p_aout->output.p_sys->waveformat.nSamplesPerSec = i_rate;
+ p_aout->output.p_sys->waveformat.wBitsPerSample = 16;
+ p_aout->output.p_sys->waveformat.nBlockAlign =
+ p_aout->output.p_sys->waveformat.wBitsPerSample / 8 * i_channels;
+ p_aout->output.p_sys->waveformat.nAvgBytesPerSec =
+ p_aout->output.p_sys->waveformat.nSamplesPerSec *
+ p_aout->output.p_sys->waveformat.nBlockAlign;
- mmtime.wType = TIME_BYTES;
- if( (waveOutGetPosition(p_aout->p_sys->h_waveout, &mmtime, sizeof(MMTIME)))
- != MMSYSERR_NOERROR || (mmtime.wType != TIME_BYTES) )
+ /* Open the device */
+ result = waveOutOpen( &p_aout->output.p_sys->h_waveout, WAVE_MAPPER,
+ &p_aout->output.p_sys->waveformat,
+ (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
+ CALLBACK_FUNCTION );
+ if( result == WAVERR_BADFORMAT )
{
- msg_Warn( p_aout, "waveOutGetPosition failed" );
- return i_buffer_limit;
+ msg_Err( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" );
+ return( 1 );
+ }
+ if( result != MMSYSERR_NOERROR )
+ {
+ msg_Err( p_aout, "waveOutOpen failed" );
+ return 1;
}
-
-#if 0
- msg_Dbg( p_aout, "GetBufInfo: %i",
- p_aout->p_sys->dw_counter - mmtime.u.cb );
-#endif
-
- return (p_aout->p_sys->dw_counter - mmtime.u.cb);
+ return 0;
}
/*****************************************************************************
- * Play: play a sound buffer
- *****************************************************************************
- * This function writes a buffer of i_length bytes
+ * PlayWaveOut: play a buffer through the WaveOut device
*****************************************************************************/
-static void Play( aout_thread_t *p_aout, byte_t *p_buffer, int i_size )
+static int PlayWaveOut( aout_instance_t *p_aout, HWAVEOUT h_waveout,
+ WAVEHDR *p_waveheader, aout_buffer_t *p_buffer )
{
MMRESULT result;
- int current_buffer = p_aout->p_sys->i_current_buffer;
-
- p_aout->p_sys->i_current_buffer = (current_buffer + 1) % NUMBUF;
-
- /* Unprepare the old buffer */
- waveOutUnprepareHeader( p_aout->p_sys->h_waveout,
- &p_aout->p_sys->waveheader[current_buffer],
- sizeof(WAVEHDR) );
/* Prepare the buffer */
- p_aout->p_sys->waveheader[current_buffer].lpData =
- realloc( p_aout->p_sys->waveheader[current_buffer].lpData, i_size );
- if( !p_aout->p_sys->waveheader[current_buffer].lpData )
- {
- msg_Err( p_aout, "could not allocate buffer" );
- return;
- }
- p_aout->p_sys->waveheader[current_buffer].dwBufferLength = i_size;
- p_aout->p_sys->waveheader[current_buffer].dwFlags = 0;
+ if( p_buffer != NULL )
+ p_waveheader->lpData = p_buffer->p_buffer;
+ else
+ /* Use silence buffer instead */
+ p_waveheader->lpData = p_aout->output.p_sys->p_silence_buffer;
- result = waveOutPrepareHeader( p_aout->p_sys->h_waveout,
- &p_aout->p_sys->waveheader[current_buffer],
- sizeof(WAVEHDR) );
+ p_waveheader->dwUser = (DWORD_PTR)p_buffer;
+ p_waveheader->dwBufferLength = p_aout->output.p_sys->i_buffer_size;
+ p_waveheader->dwFlags = 0;
+
+ result = waveOutPrepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
if( result != MMSYSERR_NOERROR )
{
msg_Err( p_aout, "waveOutPrepareHeader failed" );
- return;
+ return 1;
}
- /* Send the buffer the waveOut queue */
- p_aout->p_vlc->pf_memcpy( p_aout->p_sys->waveheader[current_buffer].lpData,
- p_buffer, i_size );
- result = waveOutWrite( p_aout->p_sys->h_waveout,
- &p_aout->p_sys->waveheader[current_buffer],
- sizeof(WAVEHDR) );
+ /* Send the buffer to the waveOut queue */
+ result = waveOutWrite( h_waveout, p_waveheader, sizeof(WAVEHDR) );
if( result != MMSYSERR_NOERROR )
{
msg_Err( p_aout, "waveOutWrite failed" );
- return;
+ return 1;
}
- /* keep track of number of bytes played */
- p_aout->p_sys->dw_counter += i_size;
-
+ return 0;
}
/*****************************************************************************
- * Close: close the audio device
+ * WaveOutCallback: what to do once WaveOut has played its sound samples
*****************************************************************************/
-static void Close( vlc_object_t *p_this )
-{
- aout_thread_t *p_aout = (aout_thread_t *)p_this;
- int i;
-
- /* Before calling waveOutClose we must reset the device */
- waveOutReset( p_aout->p_sys->h_waveout );
-
- /* Close the device */
- if( waveOutClose( p_aout->p_sys->h_waveout ) != MMSYSERR_NOERROR )
- {
- msg_Err( p_aout, "waveOutClose failed" );
- }
-
- /* Deallocate memory */
- for( i=0; i<NUMBUF; i++ )
- free( p_aout->p_sys->waveheader[i].lpData );
-
- if( p_aout->p_sys != NULL )
- {
- free( p_aout->p_sys );
- p_aout->p_sys = NULL;
- }
-}
-
-/*****************************************************************************
- * OpenWaveOutDevice: open the sound device
- ****************************************************************************/
-static int OpenWaveOutDevice( aout_thread_t *p_aout )
+static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
+ DWORD _p_aout,
+ DWORD dwParam1, DWORD dwParam2 )
{
- MMRESULT result;
+ aout_instance_t * p_aout = (aout_instance_t *)_p_aout;
+ WAVEHDR *p_waveheader = (WAVEHDR *)dwParam1;
+ aout_buffer_t * p_buffer;
- /* initialize played bytes counter */
- p_aout->p_sys->dw_counter = 0;
+ if( uMsg != WOM_DONE ) return;
- /* Set sound format */
- p_aout->p_sys->waveformat.wFormatTag = WAVE_FORMAT_PCM;
- p_aout->p_sys->waveformat.nChannels = p_aout->i_channels;
- p_aout->p_sys->waveformat.nSamplesPerSec = p_aout->i_rate;
- p_aout->p_sys->waveformat.wBitsPerSample = 16;
- p_aout->p_sys->waveformat.nBlockAlign =
- p_aout->p_sys->waveformat.wBitsPerSample / 8 * p_aout->i_channels;
- p_aout->p_sys->waveformat.nAvgBytesPerSec =
- p_aout->p_sys->waveformat.nSamplesPerSec *
- p_aout->p_sys->waveformat.nBlockAlign;
+ /* 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 );
+ /* FIXME : take into account WaveOut latency instead of mdate() */
+ p_buffer = aout_OutputNextBuffer( p_aout, mdate(), VLC_FALSE );
- /* Open the device */
- result = waveOutOpen( &p_aout->p_sys->h_waveout, WAVE_MAPPER,
- &p_aout->p_sys->waveformat,
- 0 /*callback*/, 0 /*callback data*/, CALLBACK_NULL );
- if( result != MMSYSERR_NOERROR )
- {
- msg_Err( p_aout, "waveOutOpen failed" );
- return( 1 );
- }
-
- return( 0 );
+ PlayWaveOut( p_aout, h_waveout, p_waveheader, p_buffer );
}