]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/directx.c
Untested attempt to DirectAudio support for 7.1 system
[vlc] / modules / audio_output / directx.c
index 0ec3af41c21c450baffbee073099ccbd3ee64605..2dd86f97319790bb2953ac46445d4fb60fe02e25 100644 (file)
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 /*****************************************************************************
  * Preamble
  *****************************************************************************/
-#include <errno.h>                                                 /* ENOMEM */
-#include <fcntl.h>                                       /* open(), O_WRONLY */
-#include <string.h>                                            /* strerror() */
 
-#include <stdlib.h>                            /* calloc(), malloc(), free() */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
 
 #include <vlc/vlc.h>
-#include <vlc/aout.h>
-#include "aout_internal.h"
+#include <vlc_plugin.h>
+#include <vlc_aout.h>
 
 #include <windows.h>
 #include <mmsystem.h>
 #include <dsound.h>
 
-#define FRAME_SIZE 2048              /* The size is in samples, not in bytes */
-#define FRAMES_NUM 8
-
-/* frame buffer status */
-#define FRAME_QUEUED 0
-#define FRAME_EMPTY 1
+#define FRAME_SIZE ((int)p_aout->output.output.i_rate/20) /* Size in samples */
+#define FRAMES_NUM 8                                      /* Needs to be > 3 */
 
 /*****************************************************************************
  * DirectSound GUIDs.
@@ -51,7 +47,6 @@
  * the linking stage.
  *****************************************************************************/
 #include <initguid.h>
-DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
 
 /*****************************************************************************
  * Useful macros
@@ -90,6 +85,9 @@ DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0,
 #   define SPEAKER_RESERVED               0x80000000
 #endif
 
+#ifndef DSSPEAKER_DSSPEAKER_DIRECTOUT
+#   define DSSPEAKER_DSSPEAKER_DIRECTOUT         0x00000000
+#endif
 #ifndef DSSPEAKER_HEADPHONE
 #   define DSSPEAKER_HEADPHONE         0x00000001
 #endif
@@ -108,6 +106,15 @@ DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0,
 #ifndef DSSPEAKER_5POINT1
 #   define DSSPEAKER_5POINT1           0x00000006
 #endif
+#ifndef DSSPEAKER_7POINT1
+#   define DSSPEAKER_7POINT1           0x00000007
+#endif
+#ifndef DSSPEAKER_7POINT1_SURROUND
+#   define DSSPEAKER_7POINT1_SURROUND           0x00000008
+#endif
+#ifndef DSSPEAKER_7POINT1_WIDE
+#   define DSSPEAKER_7POINT1_WIDE           DSSPEAKER_7POINT1
+#endif
 
 #ifndef _WAVEFORMATEXTENSIBLE_
 typedef struct {
@@ -134,12 +141,12 @@ typedef struct notification_thread_t
 {
     VLC_COMMON_MEMBERS
 
-    aout_instance_t * p_aout;
-    int i_frame_status[FRAMES_NUM];           /* status of each frame buffer */
-    DSBPOSITIONNOTIFY p_events[FRAMES_NUM];      /* play notification events */
-    int i_frame_size;                         /* Size in bytes of one frame */
+    aout_instance_t *p_aout;
+    int i_frame_size;                          /* size in bytes of one frame */
+    int i_write_slot;       /* current write position in our circular buffer */
 
     mtime_t start_date;
+    HANDLE event;
 
 } notification_thread_t;
 
@@ -161,14 +168,13 @@ struct aout_sys_t
                                        * takes care of mixing all the
                                        * secondary buffers into the primary) */
 
-    LPDIRECTSOUNDNOTIFY p_dsnotify;         /* the position notify interface */
     notification_thread_t *p_notif;                  /* DirectSoundThread id */
 
     int b_playing;                                         /* playing status */
 
     int i_frame_size;                         /* Size in bytes of one frame */
 
-    vlc_bool_t b_chan_reorder;              /* do we need channel reordering */
+    bool b_chan_reorder;              /* do we need channel reordering */
     int pi_chan_table[AOUT_CHAN_MAX];
     uint32_t i_channel_mask;
     uint32_t i_bits_per_sample;
@@ -201,8 +207,8 @@ static void Play       ( aout_instance_t * );
 /* local functions */
 static void Probe             ( aout_instance_t * );
 static int  InitDirectSound   ( aout_instance_t * );
-static int  CreateDSBuffer    ( aout_instance_t *, int, int, int, int, int, vlc_bool_t );
-static int  CreateDSBufferPCM ( aout_instance_t *, int*, int, int, int, vlc_bool_t );
+static int  CreateDSBuffer    ( aout_instance_t *, int, int, int, int, int, bool );
+static int  CreateDSBufferPCM ( aout_instance_t *, int*, int, int, int, bool );
 static void DestroyDSBuffer   ( aout_instance_t * );
 static void DirectSoundThread ( notification_thread_t * );
 static int  FillBuffer        ( aout_instance_t *, int, aout_buffer_t * );
@@ -227,9 +233,9 @@ vlc_module_begin();
     set_subcategory( SUBCAT_AUDIO_AOUT );
     add_shortcut( "directx" );
     add_integer( "directx-audio-device", 0, NULL, DEVICE_TEXT,
-                 DEVICE_LONGTEXT, VLC_TRUE );
+                 DEVICE_LONGTEXT, true );
     add_bool( "directx-audio-float32", 0, 0, FLOAT_TEXT,
-              FLOAT_LONGTEXT, VLC_TRUE );
+              FLOAT_LONGTEXT, true );
     set_callbacks( OpenAudio, CloseAudio );
 vlc_module_end();
 
@@ -242,7 +248,6 @@ static int OpenAudio( vlc_object_t *p_this )
 {
     aout_instance_t * p_aout = (aout_instance_t *)p_this;
     vlc_value_t val;
-    int i;
 
     msg_Dbg( p_aout, "OpenAudio" );
 
@@ -251,13 +256,12 @@ static int OpenAudio( vlc_object_t *p_this )
     if( p_aout->output.p_sys == NULL )
     {
         msg_Err( p_aout, "out of memory" );
-        return VLC_EGENERIC;
+        return VLC_ENOMEM;
     }
 
     /* Initialize some variables */
     p_aout->output.p_sys->p_dsobject = NULL;
     p_aout->output.p_sys->p_dsbuffer = NULL;
-    p_aout->output.p_sys->p_dsnotify = NULL;
     p_aout->output.p_sys->p_notif = NULL;
     p_aout->output.p_sys->b_playing = 0;
 
@@ -291,16 +295,6 @@ static int OpenAudio( vlc_object_t *p_this )
         goto error;
     }
 
-    /* Now we need to setup our DirectSound play notification structure */
-    p_aout->output.p_sys->p_notif =
-        vlc_object_create( p_aout, sizeof(notification_thread_t) );
-    p_aout->output.p_sys->p_notif->p_aout = p_aout;
-
-    /* Then create the notification events */
-    for( i = 0; i < FRAMES_NUM; i++ )
-        p_aout->output.p_sys->p_notif->p_events[i].hEventNotify =
-            CreateEvent( NULL, FALSE, FALSE, NULL );
-
     /* Open the device */
     if( val.i_int == AOUT_VAR_SPDIF )
     {
@@ -317,7 +311,7 @@ static int OpenAudio( vlc_object_t *p_this )
                             p_aout->output.output.i_physical_channels,
                             aout_FormatNbChannels( &p_aout->output.output ),
                             p_aout->output.output.i_rate,
-                            p_aout->output.p_sys->i_frame_size, VLC_FALSE )
+                            p_aout->output.p_sys->i_frame_size, false )
             != VLC_SUCCESS )
         {
             msg_Err( p_aout, "cannot open directx audio device" );
@@ -336,6 +330,14 @@ static int OpenAudio( vlc_object_t *p_this )
                    | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
                    | AOUT_CHAN_LFE;
         }
+        else if( val.i_int == AOUT_VAR_7_1 )
+        {
+                    p_aout->output.output.i_physical_channels
+                        = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
+                           | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
+                           | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT
+                           | AOUT_CHAN_LFE;
+        }
         else if( val.i_int == AOUT_VAR_3F2R )
         {
             p_aout->output.output.i_physical_channels
@@ -361,7 +363,7 @@ static int OpenAudio( vlc_object_t *p_this )
         if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format,
                                p_aout->output.output.i_physical_channels,
                                aout_FormatNbChannels( &p_aout->output.output ),
-                               p_aout->output.output.i_rate, VLC_FALSE )
+                               p_aout->output.output.i_rate, false )
             != VLC_SUCCESS )
         {
             msg_Err( p_aout, "cannot open directx audio device" );
@@ -372,20 +374,29 @@ static int OpenAudio( vlc_object_t *p_this )
         /* Calculate the frame size in bytes */
         p_aout->output.i_nb_samples = FRAME_SIZE;
         aout_FormatPrepare( &p_aout->output.output );
-        p_aout->output.p_sys->i_frame_size =
-            FRAME_SIZE * p_aout->output.output.i_bytes_per_frame;
-
         aout_VolumeSoftInit( p_aout );
     }
 
+    /* Now we need to setup our DirectSound play notification structure */
+    p_aout->output.p_sys->p_notif =
+        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->event = CreateEvent( 0, FALSE, FALSE, 0 );
+    p_aout->output.p_sys->p_notif->i_frame_size =
+        p_aout->output.p_sys->i_frame_size;
+
     /* then launch the notification thread */
     msg_Dbg( p_aout, "creating DirectSoundThread" );
     if( vlc_thread_create( p_aout->output.p_sys->p_notif,
                            "DirectSound Notification Thread",
                            DirectSoundThread,
-                           VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
+                           VLC_THREAD_PRIORITY_HIGHEST, false ) )
     {
         msg_Err( p_aout, "cannot create DirectSoundThread" );
+        CloseHandle( p_aout->output.p_sys->p_notif->event );
+        vlc_object_release( p_aout->output.p_sys->p_notif );
+        p_aout->output.p_sys->p_notif = NULL;
         goto error;
     }
 
@@ -419,17 +430,36 @@ static void Probe( aout_instance_t * p_aout )
     if( p_aout->output.output.i_physical_channels == i_physical_channels )
     {
         if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 6,
-                               p_aout->output.output.i_rate, VLC_TRUE )
+                               p_aout->output.output.i_rate, true )
             == VLC_SUCCESS )
         {
             val.i_int = AOUT_VAR_5_1;
-            text.psz_string = N_("5.1");
+            text.psz_string = "5.1";
             var_Change( p_aout, "audio-device",
                         VLC_VAR_ADDCHOICE, &val, &text );
             msg_Dbg( p_aout, "device supports 5.1 channels" );
         }
     }
 
+    /* Test for 7.1 support */
+    i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
+                             AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
+                             AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT |
+                             AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
+       if( p_aout->output.output.i_physical_channels == i_physical_channels )
+       {
+           if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 8,
+                                  p_aout->output.output.i_rate, true )
+               == VLC_SUCCESS )
+           {
+               val.i_int = AOUT_VAR_7_1;
+               text.psz_string = "7.1";
+               var_Change( p_aout, "audio-device",
+                           VLC_VAR_ADDCHOICE, &val, &text );
+               msg_Dbg( p_aout, "device supports 7.1 channels" );
+           }
+       }
+
     /* Test for 3 Front 2 Rear support */
     i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
                           AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
@@ -437,7 +467,7 @@ static void Probe( aout_instance_t * p_aout )
     if( p_aout->output.output.i_physical_channels == i_physical_channels )
     {
         if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 5,
-                               p_aout->output.output.i_rate, VLC_TRUE )
+                               p_aout->output.output.i_rate, true )
             == VLC_SUCCESS )
         {
             val.i_int = AOUT_VAR_3F2R;
@@ -455,7 +485,7 @@ static void Probe( aout_instance_t * p_aout )
         == i_physical_channels )
     {
         if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 4,
-                               p_aout->output.output.i_rate, VLC_TRUE )
+                               p_aout->output.output.i_rate, true )
             == VLC_SUCCESS )
         {
             val.i_int = AOUT_VAR_2F2R;
@@ -469,7 +499,7 @@ static void Probe( aout_instance_t * p_aout )
     /* Test for stereo support */
     i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
     if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 2,
-                           p_aout->output.output.i_rate, VLC_TRUE )
+                           p_aout->output.output.i_rate, true )
         == VLC_SUCCESS )
     {
         val.i_int = AOUT_VAR_STEREO;
@@ -482,7 +512,7 @@ static void Probe( aout_instance_t * p_aout )
     /* Test for mono support */
     i_physical_channels = AOUT_CHAN_CENTER;
     if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 1,
-                           p_aout->output.output.i_rate, VLC_TRUE )
+                           p_aout->output.output.i_rate, true )
         == VLC_SUCCESS )
     {
         val.i_int = AOUT_VAR_MONO;
@@ -500,6 +530,9 @@ static void Probe( aout_instance_t * p_aout )
     }
     switch( DSSPEAKER_CONFIG(ui_speaker_config) )
     {
+    case DSSPEAKER_7POINT1:
+        val.i_int = AOUT_VAR_7_1;
+        break;
     case DSSPEAKER_5POINT1:
         val.i_int = AOUT_VAR_5_1;
         break;
@@ -527,7 +560,7 @@ static void Probe( aout_instance_t * p_aout )
                             p_aout->output.output.i_physical_channels,
                             aout_FormatNbChannels( &p_aout->output.output ),
                             p_aout->output.output.i_rate,
-                            AOUT_SPDIF_SIZE, VLC_TRUE )
+                            AOUT_SPDIF_SIZE, true )
             == VLC_SUCCESS )
         {
             msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
@@ -550,7 +583,7 @@ static void Probe( aout_instance_t * p_aout )
 
     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
 
-    val.b_bool = VLC_TRUE;
+    val.b_bool = true;
     var_Set( p_aout, "intf-change", val );
 }
 
@@ -564,6 +597,7 @@ static void Play( aout_instance_t *p_aout )
     if( !p_aout->output.p_sys->b_playing )
     {
         aout_buffer_t *p_buffer;
+        int i;
 
         p_aout->output.p_sys->b_playing = 1;
 
@@ -572,11 +606,15 @@ static void Play( aout_instance_t *p_aout )
             aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
 
         /* fill in the first samples */
-        p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
-        FillBuffer( p_aout, 0, p_buffer );
+        for( i = 0; i < FRAMES_NUM; i++ )
+        {
+            p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
+            if( !p_buffer ) break;
+            FillBuffer( p_aout, i, p_buffer );
+        }
 
         /* wake up the audio output thread */
-        SetEvent( p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );
+        SetEvent( p_aout->output.p_sys->p_notif->event );
     }
 }
 
@@ -588,23 +626,18 @@ static void CloseAudio( 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;
 
-    msg_Dbg( p_aout, "CloseAudio" );
+    msg_Dbg( p_aout, "closing audio device" );
 
     /* kill the position notification thread, if any */
     if( p_sys->p_notif )
     {
         vlc_object_detach( p_sys->p_notif );
-        if( p_sys->p_notif->b_thread )
-        {
-            p_sys->p_notif->b_die = 1;
+        vlc_object_kill( p_sys->p_notif );
+        /* wake up the audio thread if needed */
+        if( !p_sys->b_playing ) SetEvent( p_sys->p_notif->event );
 
-            if( !p_sys->b_playing )
-                /* wake up the audio thread */
-                SetEvent( p_sys->p_notif->p_events[0].hEventNotify );
-
-            vlc_thread_join( p_sys->p_notif );
-        }
-        vlc_object_destroy( p_sys->p_notif );
+        vlc_thread_join( p_sys->p_notif );
+        vlc_object_release( p_sys->p_notif );
     }
 
     /* release the secondary buffer */
@@ -612,13 +645,11 @@ static void CloseAudio( vlc_object_t *p_this )
 
     /* finally release the DirectSound object */
     if( p_sys->p_dsobject ) IDirectSound_Release( p_sys->p_dsobject );
-    
     /* free DSOUND.DLL */
     if( p_sys->hdsound_dll ) FreeLibrary( p_sys->hdsound_dll );
 
-    if( p_aout->output.p_sys->p_device_guid )
-        free( p_aout->output.p_sys->p_device_guid );
-
+    free( p_aout->output.p_sys->p_device_guid );
     free( p_sys );
 }
 
@@ -674,7 +705,7 @@ static int InitDirectSound( aout_instance_t *p_aout )
     if( OurDirectSoundEnumerate )
     {
         /* Attempt enumeration */
-        if( FAILED( OurDirectSoundEnumerate( CallBackDirectSoundEnum, 
+        if( FAILED( OurDirectSoundEnumerate( CallBackDirectSoundEnum,
                                              p_aout ) ) )
         {
             msg_Dbg( p_aout, "enumeration of DirectSound devices failed" );
@@ -682,7 +713,7 @@ static int InitDirectSound( aout_instance_t *p_aout )
     }
 
     /* Create the direct sound object */
-    if FAILED( OurDirectSoundCreate( p_aout->output.p_sys->p_device_guid, 
+    if FAILED( OurDirectSoundCreate( p_aout->output.p_sys->p_device_guid,
                                      &p_aout->output.p_sys->p_dsobject,
                                      NULL ) )
     {
@@ -734,7 +765,7 @@ static int InitDirectSound( aout_instance_t *p_aout )
  *****************************************************************************/
 static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
                            int i_channels, int i_nb_channels, int i_rate,
-                           int i_bytes_per_frame, vlc_bool_t b_probe )
+                           int i_bytes_per_frame, bool b_probe )
 {
     WAVEFORMATEXTENSIBLE waveformat;
     DSBUFFERDESC         dsbdesc;
@@ -792,7 +823,6 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
     memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
     dsbdesc.dwSize = sizeof(DSBUFFERDESC);
     dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
-                    | DSBCAPS_CTRLPOSITIONNOTIFY     /* We need notification */
                     | DSBCAPS_GLOBALFOCUS;      /* Allows background playing */
 
     /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
@@ -844,40 +874,8 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
         return VLC_SUCCESS;
     }
 
-    /* Backup the size of a frame */
-    p_aout->output.p_sys->p_notif->i_frame_size = i_bytes_per_frame;
-
-    /* Now the secondary buffer is created, we need to setup its position
-     * notification */
-    for( i = 0; i < FRAMES_NUM; i++ )
-    {
-        p_aout->output.p_sys->p_notif->p_events[i].dwOffset = i *
-            p_aout->output.p_sys->p_notif->i_frame_size;
-
-        p_aout->output.p_sys->p_notif->i_frame_status[i] = FRAME_EMPTY;
-    }
-
-    /* Get the IDirectSoundNotify interface */
-    if FAILED( IDirectSoundBuffer_QueryInterface(
-                                p_aout->output.p_sys->p_dsbuffer,
-                                &IID_IDirectSoundNotify,
-                                (LPVOID *)&p_aout->output.p_sys->p_dsnotify ) )
-    {
-        msg_Err( p_aout, "cannot get IDirectSoundNotify interface" );
-        goto error;
-    }
-
-    if FAILED( IDirectSoundNotify_SetNotificationPositions(
-                                    p_aout->output.p_sys->p_dsnotify,
-                                    FRAMES_NUM,
-                                    p_aout->output.p_sys->p_notif->p_events ) )
-    {
-        msg_Err( p_aout, "cannot set position notification" );
-        goto error;
-    }
-
+    p_aout->output.p_sys->i_frame_size = i_bytes_per_frame;
     p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask;
-
     p_aout->output.p_sys->b_chan_reorder =
         aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
                                   waveformat.dwChannelMask, i_nb_channels,
@@ -889,10 +887,6 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
     }
 
     return VLC_SUCCESS;
-
- error:
-    DestroyDSBuffer( p_aout );
-    return VLC_EGENERIC;
 }
 
 /*****************************************************************************
@@ -903,7 +897,7 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
  ****************************************************************************/
 static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format,
                               int i_channels, int i_nb_channels, int i_rate,
-                              vlc_bool_t b_probe )
+                              bool b_probe )
 {
     vlc_value_t val;
 
@@ -944,12 +938,6 @@ static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format,
  *****************************************************************************/
 static void DestroyDSBuffer( aout_instance_t *p_aout )
 {
-    if( p_aout->output.p_sys->p_dsnotify )
-    {
-        IDirectSoundNotify_Release( p_aout->output.p_sys->p_dsnotify );
-        p_aout->output.p_sys->p_dsnotify = NULL;
-    }
-
     if( p_aout->output.p_sys->p_dsbuffer )
     {
         IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
@@ -1015,8 +1003,7 @@ static int FillBuffer( aout_instance_t *p_aout, int i_frame,
                                  p_sys->i_bits_per_sample );
         }
 
-        p_aout->p_vlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
-                                  l_bytes1 );
+        vlc_memcpy( p_write_position, p_buffer->p_buffer, l_bytes1 );
         aout_BufferFree( p_buffer );
     }
 
@@ -1024,26 +1011,23 @@ static int FillBuffer( aout_instance_t *p_aout, int i_frame,
     IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
                                p_wrap_around, l_bytes2 );
 
+    p_notif->i_write_slot = (i_frame + 1) % FRAMES_NUM;
     return VLC_SUCCESS;
 }
 
 /*****************************************************************************
- * DirectSoundThread: this thread will capture play notification events. 
+ * DirectSoundThread: this thread will capture play notification events.
  *****************************************************************************
  * We use this thread to emulate a callback mechanism. The thread probes for
  * event notification and fills up the DS secondary buffer when needed.
  *****************************************************************************/
 static void DirectSoundThread( notification_thread_t *p_notif )
 {
-    HANDLE  notification_events[FRAMES_NUM];
-    HRESULT dsresult;
     aout_instance_t *p_aout = p_notif->p_aout;
-    int i, i_which_frame, i_last_frame, i_next_frame;
-    mtime_t mtime;
-    vlc_bool_t b_sleek;
-
-    for( i = 0; i < FRAMES_NUM; i++ )
-        notification_events[i] = p_notif->p_events[i].hEventNotify;
+    bool b_sleek;
+    mtime_t last_time;
+    HRESULT dsresult;
+    long l_queued = 0;
 
     /* We don't want any resampling when using S/PDIF output */
     b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i');
@@ -1054,7 +1038,7 @@ static void DirectSoundThread( notification_thread_t *p_notif )
     msg_Dbg( p_notif, "DirectSoundThread ready" );
 
     /* Wait here until Play() is called */
-    WaitForSingleObject( notification_events[0], INFINITE );
+    WaitForSingleObject( p_notif->event, INFINITE );
 
     if( !p_notif->b_die )
     {
@@ -1079,88 +1063,64 @@ static void DirectSoundThread( notification_thread_t *p_notif )
             msg_Err( p_aout, "cannot start playing buffer" );
         }
     }
+    last_time = mdate();
 
     while( !p_notif->b_die )
     {
-        aout_buffer_t *p_buffer;
-        long l_latency;
-
-        /* wait for the position notification */
-        i_which_frame = WaitForMultipleObjects( FRAMES_NUM,
-                                                notification_events, 0,
-                                                INFINITE ) - WAIT_OBJECT_0;
-
-        if( p_notif->b_die )
-            break;
+        long l_read, l_free_slots;
+        mtime_t mtime = mdate();
+        int i;
 
-        mtime = mdate();
+        /*
+         * Fill in as much audio data as we can in our circular buffer
+         */
 
-        /* We take into account the current latency */
-        if SUCCEEDED( IDirectSoundBuffer_GetCurrentPosition(
-                        p_aout->output.p_sys->p_dsbuffer,
-                        &l_latency, NULL ) )
+        /* Find out current play position */
+        if FAILED( IDirectSoundBuffer_GetCurrentPosition(
+                   p_aout->output.p_sys->p_dsbuffer, &l_read, NULL ) )
         {
-            if( l_latency > (i_which_frame * FRAME_SIZE)
-                  && l_latency < ((i_which_frame+1) * FRAME_SIZE) )
-            {
-                l_latency = - ( l_latency /
-                                p_aout->output.output.i_bytes_per_frame %
-                                FRAME_SIZE );
-            }
-            else
-            {
-                l_latency = FRAME_SIZE - ( l_latency /
-                                      p_aout->output.output.i_bytes_per_frame %
-                                      FRAME_SIZE );
-            }
+            msg_Err( p_aout, "GetCurrentPosition() failed!" );
+            l_read = 0;
         }
-        else
+
+        /* Detect underruns */
+        if( l_queued && mtime - last_time >
+            INT64_C(1000000) * l_queued / p_aout->output.output.i_rate )
         {
-            l_latency = 0;
+            msg_Dbg( p_aout, "detected underrun!" );
         }
-
-        /* Mark last frame as empty */
-        i_last_frame = (i_which_frame + FRAMES_NUM -1) % FRAMES_NUM;
-        i_next_frame = (i_which_frame + 1) % FRAMES_NUM;
-        p_notif->i_frame_status[i_last_frame] = FRAME_EMPTY;
+        last_time = mtime;
 
         /* Try to fill in as many frame buffers as possible */
-        for( i = i_next_frame; (i % FRAMES_NUM) != i_which_frame; i++ )
-        {
-
-            /* Check if frame buf is already filled */
-            if( p_notif->i_frame_status[i % FRAMES_NUM] == FRAME_QUEUED )
-                continue;
+        l_read /= p_aout->output.output.i_bytes_per_frame;
+        l_queued = p_notif->i_write_slot * FRAME_SIZE - l_read;
+        if( l_queued < 0 ) l_queued += (FRAME_SIZE * FRAMES_NUM);
+        l_free_slots = (FRAMES_NUM * FRAME_SIZE - l_queued) / FRAME_SIZE;
 
-            p_buffer = aout_OutputNextBuffer( p_aout,
-                mtime + 1000000 / p_aout->output.output.i_rate *
-                ((i - i_next_frame + 1) * FRAME_SIZE + l_latency), b_sleek );
+        for( i = 0; i < l_free_slots; i++ )
+        {
+            aout_buffer_t *p_buffer = aout_OutputNextBuffer( p_aout,
+                mtime + INT64_C(1000000) * (i * FRAME_SIZE + l_queued) /
+                p_aout->output.output.i_rate, b_sleek );
 
             /* If there is no audio data available and we have some buffered
              * already, then just wait for the next time */
-            if( !p_buffer && (i != i_next_frame) )
-            {
-                //msg_Err( p_aout, "only %i frame buffers filled!",
-                //         i - i_next_frame );
-                break;
-            }
-
-            if( FillBuffer( p_aout, (i%FRAMES_NUM), p_buffer )
-                != VLC_SUCCESS )
-                break;
+            if( !p_buffer && (i || l_queued / FRAME_SIZE) ) break;
 
-            /* Mark the frame buffer as QUEUED */
-            p_notif->i_frame_status[i%FRAMES_NUM] = FRAME_QUEUED;
+            if( FillBuffer( p_aout, p_notif->i_write_slot % FRAMES_NUM,
+                            p_buffer ) != VLC_SUCCESS ) break;
         }
 
+        /* Sleep a reasonable amount of time */
+        l_queued += (i * FRAME_SIZE);
+        msleep( INT64_C(1000000) * l_queued / p_aout->output.output.i_rate / 2 );
     }
 
     /* make sure the buffer isn't playing */
     IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer );
 
-    /* free the events */
-    for( i = 0; i < FRAMES_NUM; i++ )
-        CloseHandle( notification_events[i] );
+    /* free the event */
+    CloseHandle( p_notif->event );
 
     msg_Dbg( p_notif, "DirectSoundThread exiting" );
 }