]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/waveout.c
aout3 API change :
[vlc] / modules / audio_output / waveout.c
index 7d61fff0be3f8cee0f6b57051e64e563b338a80d..8ca19f26172d712f080669f65f1f063c5012b4d7 100644 (file)
@@ -2,7 +2,7 @@
  * 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();
 
@@ -63,20 +69,17 @@ 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 */
 };
 
 /*****************************************************************************
@@ -86,201 +89,185 @@ struct aout_sys_t
  *****************************************************************************/
 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 );
 }