]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/directx.c
* intf.* : try to restore an a bit more intelligent behavior for the playlist toggle...
[vlc] / modules / audio_output / directx.c
index d94d2c799232c5fd4f9c8b7a79b30ad7340adc3e..17913d23f5f0332cd36933b499211f930261615d 100644 (file)
@@ -2,9 +2,9 @@
  * directx.c: Windows DirectX audio output method
  *****************************************************************************
  * Copyright (C) 2001 VideoLAN
- * $Id: directx.c,v 1.15 2003/03/03 14:21:08 gbazin Exp $
+ * $Id$
  *
- * Authors: Gildas Bazin <gbazin@netcourrier.com>
+ * 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
@@ -39,7 +39,7 @@
 #include <dsound.h>
 
 #define FRAME_SIZE 2048              /* The size is in samples, not in bytes */
-#define FRAMES_NUM 4
+#define FRAMES_NUM 8
 
 /* frame buffer status */
 #define FRAME_QUEUED 0
@@ -123,15 +123,9 @@ typedef struct {
 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
 #endif
 
-#ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
-DEFINE_GUID( KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
-#endif
-#ifndef KSDATAFORMAT_SUBTYPE_PCM
-DEFINE_GUID( KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
-#endif
-#ifndef KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF
-DEFINE_GUID( KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
-#endif
+DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
+DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
+DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
 
 /*****************************************************************************
  * notification_thread_t: DirectX event thread
@@ -171,22 +165,27 @@ struct aout_sys_t
     int i_frame_size;                         /* Size in bytes of one frame */
 
     vlc_bool_t b_chan_reorder;              /* do we need channel reordering */
-    int *pi_chan_table;
+    int pi_chan_table[AOUT_CHAN_MAX];
     uint32_t i_channel_mask;
+    uint32_t i_bits_per_sample;
+    uint32_t i_channels;
 };
 
-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 };
 
 /*****************************************************************************
  * Local prototypes.
@@ -204,16 +203,15 @@ static void DestroyDSBuffer   ( aout_instance_t * );
 static void DirectSoundThread ( notification_thread_t * );
 static int  FillBuffer        ( aout_instance_t *, int, aout_buffer_t * );
 
-static void CheckReordering   ( aout_instance_t *, int );
-static void InterleaveFloat32 ( float *, float *, int *, int );
-static void InterleaveS16     ( int16_t *, int16_t *, int *, int );
-
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
 vlc_module_begin();
-    set_description( _("DirectX audio module") );
+    set_description( _("DirectX audio output") );
+    set_shortname( "DirectX" );
     set_capability( "audio output", 100 );
+    set_category( CAT_AUDIO );
+    set_subcategory( SUBCAT_AUDIO_AOUT );
     add_shortcut( "directx" );
     set_callbacks( OpenAudio, CloseAudio );
 vlc_module_end();
@@ -245,7 +243,6 @@ static int OpenAudio( vlc_object_t *p_this )
     p_aout->output.p_sys->p_dsnotify = NULL;
     p_aout->output.p_sys->p_notif = NULL;
     p_aout->output.p_sys->b_playing = 0;
-    p_aout->output.p_sys->pi_chan_table = NULL;
 
     p_aout->output.pf_play = Play;
     aout_VolumeSoftInit( p_aout );
@@ -257,11 +254,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;
-
     if( var_Type( p_aout, "audio-device" ) == 0 )
     {
         Probe( p_aout );
@@ -270,19 +262,22 @@ static int OpenAudio( vlc_object_t *p_this )
     if( var_Get( p_aout, "audio-device", &val ) < 0 )
     {
         /* Probe() has failed. */
-        free( p_aout->output.p_sys );
-        return VLC_EGENERIC;
+        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( !strcmp( val.psz_string, N_("A/52 over S/PDIF") ) )
+    if( val.i_int == AOUT_VAR_SPDIF )
     {
-        free( val.psz_string );
         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
 
         /* Calculate the frame size in bytes */
@@ -299,7 +294,7 @@ static int OpenAudio( vlc_object_t *p_this )
                             p_aout->output.p_sys->i_frame_size, VLC_FALSE )
             != VLC_SUCCESS )
         {
-            msg_Err( p_aout, "cannot open waveout audio device" );
+            msg_Err( p_aout, "cannot open directx audio device" );
             free( p_aout->output.p_sys );
             return VLC_EGENERIC;
         }
@@ -308,20 +303,26 @@ static int OpenAudio( vlc_object_t *p_this )
     }
     else
     {
-        if( !strcmp( val.psz_string, N_("5.1") ) )
+        if( val.i_int == AOUT_VAR_5_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_LFE;
         }
-        else if( !strcmp( val.psz_string, N_("2 Front 2 Rear") ) )
+        else if( val.i_int == AOUT_VAR_3F2R )
+        {
+            p_aout->output.output.i_physical_channels
+                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
+                   | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
+        }
+        else if( val.i_int == AOUT_VAR_2F2R )
         {
             p_aout->output.output.i_physical_channels
                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
                    | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
         }
-        else if( !strcmp( val.psz_string, "Mono" ) )
+        else if( val.i_int == AOUT_VAR_MONO )
         {
             p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
         }
@@ -330,7 +331,6 @@ static int OpenAudio( vlc_object_t *p_this )
             p_aout->output.output.i_physical_channels
                 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
         }
-        free( val.psz_string );
 
         if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format,
                                p_aout->output.output.i_physical_channels,
@@ -338,7 +338,7 @@ static int OpenAudio( vlc_object_t *p_this )
                                p_aout->output.output.i_rate, VLC_FALSE )
             != VLC_SUCCESS )
         {
-            msg_Err( p_aout, "cannot open waveout audio device" );
+            msg_Err( p_aout, "cannot open directx audio device" );
             free( p_aout->output.p_sys );
             return VLC_EGENERIC;
         }
@@ -377,12 +377,14 @@ static int OpenAudio( vlc_object_t *p_this )
  *****************************************************************************/
 static void Probe( aout_instance_t * p_aout )
 {
-    vlc_value_t val;
+    vlc_value_t val, text;
     int i_format;
     unsigned int i_physical_channels;
     DWORD ui_speaker_config;
 
-    var_Create( p_aout, "audio-device", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
+    var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
+    text.psz_string = _("Audio Device");
+    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
 
     /* Test for 5.1 support */
     i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
@@ -394,12 +396,32 @@ static void Probe( aout_instance_t * p_aout )
                                p_aout->output.output.i_rate, VLC_TRUE )
             == VLC_SUCCESS )
         {
-            val.psz_string = N_("5.1");
-            var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
+            val.i_int = AOUT_VAR_5_1;
+            text.psz_string = N_("5.1");
+            var_Change( p_aout, "audio-device",
+                        VLC_VAR_ADDCHOICE, &val, &text );
             msg_Dbg( p_aout, "device supports 5.1 channels" );
         }
     }
 
+    /* Test for 3 Front 2 Rear support */
+    i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
+                          AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
+                          AOUT_CHAN_REARRIGHT;
+    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 )
+            == VLC_SUCCESS )
+        {
+            val.i_int = AOUT_VAR_3F2R;
+            text.psz_string = N_("3 Front 2 Rear");
+            var_Change( p_aout, "audio-device",
+                        VLC_VAR_ADDCHOICE, &val, &text );
+            msg_Dbg( p_aout, "device supports 5 channels" );
+        }
+    }
+
     /* Test for 2 Front 2 Rear support */
     i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
                           AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
@@ -410,8 +432,10 @@ static void Probe( aout_instance_t * p_aout )
                                p_aout->output.output.i_rate, VLC_TRUE )
             == VLC_SUCCESS )
         {
-            val.psz_string = N_("2 Front 2 Rear");
-            var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
+            val.i_int = AOUT_VAR_2F2R;
+            text.psz_string = N_("2 Front 2 Rear");
+            var_Change( p_aout, "audio-device",
+                        VLC_VAR_ADDCHOICE, &val, &text );
             msg_Dbg( p_aout, "device supports 4 channels" );
         }
     }
@@ -422,8 +446,10 @@ static void Probe( aout_instance_t * p_aout )
                            p_aout->output.output.i_rate, VLC_TRUE )
         == VLC_SUCCESS )
     {
-        val.psz_string = N_("Stereo");
-        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
+        val.i_int = AOUT_VAR_STEREO;
+        text.psz_string = N_("Stereo");
+        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
+        var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
         msg_Dbg( p_aout, "device supports 2 channels" );
     }
 
@@ -433,8 +459,9 @@ static void Probe( aout_instance_t * p_aout )
                            p_aout->output.output.i_rate, VLC_TRUE )
         == VLC_SUCCESS )
     {
-        val.psz_string = N_("Mono");
-        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );        
+        val.i_int = AOUT_VAR_MONO;
+        text.psz_string = N_("Mono");
+        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
         msg_Dbg( p_aout, "device supports 1 channel" );
     }
 
@@ -448,18 +475,21 @@ static void Probe( aout_instance_t * p_aout )
     switch( DSSPEAKER_CONFIG(ui_speaker_config) )
     {
     case DSSPEAKER_5POINT1:
-        val.psz_string = N_("5.1");
+        val.i_int = AOUT_VAR_5_1;
         break;
     case DSSPEAKER_QUAD:
-        val.psz_string = N_("2 Front 2 Rear");
+        val.i_int = AOUT_VAR_2F2R;
         break;
+#if 0 /* Lots of people just get their settings wrong and complain that
+       * this is a problem with VLC so just don't ever set mono by default. */
     case DSSPEAKER_MONO:
-        val.psz_string = N_("Mono");
+        val.i_int = AOUT_VAR_MONO;
         break;
+#endif
     case DSSPEAKER_SURROUND:
     case DSSPEAKER_STEREO:
     default:
-        val.psz_string = N_("Stereo");
+        val.i_int = AOUT_VAR_STEREO;
         break;
     }
     var_Set( p_aout, "audio-device", val );
@@ -475,13 +505,23 @@ static void Probe( aout_instance_t * p_aout )
             == VLC_SUCCESS )
         {
             msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
-            val.psz_string = N_("A/52 over S/PDIF");
-            var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
+            val.i_int = AOUT_VAR_SPDIF;
+            text.psz_string = N_("A/52 over S/PDIF");
+            var_Change( p_aout, "audio-device",
+                        VLC_VAR_ADDCHOICE, &val, &text );
             if( config_GetInt( p_aout, "spdif" ) )
                 var_Set( p_aout, "audio-device", val );
         }
     }
 
+    var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
+    if( val.i_int <= 0 )
+    {
+        /* Probe() has failed. */
+        var_Destroy( p_aout, "audio-device" );
+        return;
+    }
+
     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
 
     val.b_bool = VLC_TRUE;
@@ -520,42 +560,37 @@ static void Play( aout_instance_t *p_aout )
 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" );
 
     /* kill the position notification thread, if any */
-    if( p_aout->output.p_sys->p_notif )
+    if( p_sys->p_notif )
     {
-        vlc_object_detach( p_aout->output.p_sys->p_notif );
-        if( p_aout->output.p_sys->p_notif->b_thread )
+        vlc_object_detach( p_sys->p_notif );
+        if( p_sys->p_notif->b_thread )
         {
-            p_aout->output.p_sys->p_notif->b_die = 1;
+            p_sys->p_notif->b_die = 1;
 
-            if( !p_aout->output.p_sys->b_playing )
+            if( !p_sys->b_playing )
                 /* wake up the audio thread */
-                SetEvent(
-                    p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );
+                SetEvent( p_sys->p_notif->p_events[0].hEventNotify );
 
-            vlc_thread_join( p_aout->output.p_sys->p_notif );
+            vlc_thread_join( p_sys->p_notif );
         }
-        vlc_object_destroy( p_aout->output.p_sys->p_notif );
+        vlc_object_destroy( p_sys->p_notif );
     }
 
     /* release the secondary buffer */
     DestroyDSBuffer( p_aout );
 
     /* finally release the DirectSound object */
-    if( p_aout->output.p_sys->p_dsobject )
-        IDirectSound_Release( p_aout->output.p_sys->p_dsobject );
+    if( p_sys->p_dsobject ) IDirectSound_Release( p_sys->p_dsobject );
     
     /* free DSOUND.DLL */
-    if( p_aout->output.p_sys->hdsound_dll )
-       FreeLibrary( p_aout->output.p_sys->hdsound_dll );
-
-    if( p_aout->output.p_sys->pi_chan_table )
-        free( p_aout->output.p_sys->pi_chan_table );
+    if( p_sys->hdsound_dll ) FreeLibrary( p_sys->hdsound_dll );
 
-    free( p_aout->output.p_sys );
+    free( p_sys );
 }
 
 /*****************************************************************************
@@ -641,10 +676,10 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
 
     /* First set the sound buffer format */
     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 )
@@ -657,7 +692,7 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
         waveformat.Samples.wValidBitsPerSample =
             waveformat.Format.wBitsPerSample;
         waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
-        waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
+        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
         break;
 
     case VLC_FOURCC('f','l','3','2'):
@@ -665,7 +700,7 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
         waveformat.Samples.wValidBitsPerSample =
             waveformat.Format.wBitsPerSample;
         waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
-        waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
         break;
 
     case VLC_FOURCC('s','1','6','l'):
@@ -673,7 +708,7 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
         waveformat.Samples.wValidBitsPerSample =
             waveformat.Format.wBitsPerSample;
         waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
-        waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
         break;
     }
 
@@ -684,6 +719,16 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
     waveformat.Format.nAvgBytesPerSec =
         waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
 
+    p_aout->output.p_sys->i_bits_per_sample = waveformat.Format.wBitsPerSample;
+    p_aout->output.p_sys->i_channels = i_nb_channels;
+
+    /* Then fill in the direct sound descriptor */
+    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 */
     if( i_nb_channels <= 2 )
     {
@@ -694,15 +739,11 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
         waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
         waveformat.Format.cbSize =
             sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
-    }
 
+        /* Needed for 5.1 on emu101k */
+        dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
+    }
 
-    /* Then fill in the direct sound descriptor */
-    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 */
     dsbdesc.dwBufferBytes = FRAMES_NUM * i_bytes_per_frame;   /* buffer size */
     dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;
 
@@ -710,7 +751,23 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
                    p_aout->output.p_sys->p_dsobject, &dsbdesc,
                    &p_aout->output.p_sys->p_dsbuffer, NULL) )
     {
-        goto error;
+        if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE )
+        {
+            /* Try without DSBCAPS_LOCHARDWARE */
+            dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
+            if FAILED( IDirectSound_CreateSoundBuffer(
+                   p_aout->output.p_sys->p_dsobject, &dsbdesc,
+                   &p_aout->output.p_sys->p_dsbuffer, NULL) )
+            {
+                return VLC_EGENERIC;
+            }
+            if( !b_probe )
+                msg_Dbg( p_aout, "couldn't use hardware sound buffer" );
+        }
+        else
+        {
+            return VLC_EGENERIC;
+        }
     }
 
     /* Stop here if we were just probing */
@@ -754,21 +811,21 @@ static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
     }
 
     p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask;
-    CheckReordering( p_aout, i_nb_channels );
 
-    return VLC_SUCCESS;
+    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 );
 
- error:
-    if( p_aout->output.p_sys->p_dsbuffer )
-    {
-        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
-        p_aout->output.p_sys->p_dsbuffer = NULL;
-    }
-    if( p_aout->output.p_sys->p_dsnotify )
+    if( p_aout->output.p_sys->b_chan_reorder )
     {
-        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
-        p_aout->output.p_sys->p_dsnotify = NULL;
+        msg_Dbg( p_aout, "channel reordering needed" );
     }
+
+    return VLC_SUCCESS;
+
+ error:
+    DestroyDSBuffer( p_aout );
     return VLC_EGENERIC;
 }
 
@@ -782,7 +839,10 @@ 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 )
 {
-    if( CreateDSBuffer( p_aout, VLC_FOURCC('f','l','3','2'),
+    /* Float32 audio samples are not supported for 5.1 output on the emu101k */
+
+    if( i_nb_channels > 2 ||
+        CreateDSBuffer( p_aout, VLC_FOURCC('f','l','3','2'),
                         i_channels, i_nb_channels, i_rate,
                         FRAME_SIZE * 4 * i_nb_channels, b_probe )
         != VLC_SUCCESS )
@@ -836,13 +896,14 @@ static int FillBuffer( aout_instance_t *p_aout, int i_frame,
                        aout_buffer_t *p_buffer )
 {
     notification_thread_t *p_notif = p_aout->output.p_sys->p_notif;
+    aout_sys_t *p_sys = p_aout->output.p_sys;
     void *p_write_position, *p_wrap_around;
     long l_bytes1, l_bytes2;
     HRESULT dsresult;
 
     /* Before copying anything, we have to lock the buffer */
     dsresult = IDirectSoundBuffer_Lock(
-                p_aout->output.p_sys->p_dsbuffer,               /* DS buffer */
+                p_sys->p_dsbuffer,                              /* DS buffer */
                 i_frame * p_notif->i_frame_size,             /* Start offset */
                 p_notif->i_frame_size,                    /* Number of bytes */
                 &p_write_position,                  /* Address of lock start */
@@ -852,9 +913,9 @@ static int FillBuffer( aout_instance_t *p_aout, int i_frame,
                 0 );                                                /* Flags */
     if( dsresult == DSERR_BUFFERLOST )
     {
-        IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
+        IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
         dsresult = IDirectSoundBuffer_Lock(
-                               p_aout->output.p_sys->p_dsbuffer,
+                               p_sys->p_dsbuffer,
                                i_frame * p_notif->i_frame_size,
                                p_notif->i_frame_size,
                                &p_write_position,
@@ -874,31 +935,23 @@ static int FillBuffer( aout_instance_t *p_aout, int i_frame,
     {
         memset( p_write_position, 0, l_bytes1 );
     }
-    else if( p_aout->output.p_sys->b_chan_reorder )
-    {
-        /* Do the channel reordering here */
-
-        if( p_aout->output.output.i_format ==  VLC_FOURCC('s','1','6','l') )
-          InterleaveS16( (int16_t *)p_buffer->p_buffer,
-                         (int16_t *)p_write_position,
-                         p_aout->output.p_sys->pi_chan_table,
-                         aout_FormatNbChannels( &p_aout->output.output ) );
-        else
-          InterleaveFloat32( (float *)p_buffer->p_buffer,
-                             (float *)p_write_position,
-                             p_aout->output.p_sys->pi_chan_table,
-                             aout_FormatNbChannels( &p_aout->output.output ) );
-    }
     else
     {
+        if( p_sys->b_chan_reorder )
+        {
+            /* Do the channel reordering here */
+            aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_nb_bytes,
+                                 p_sys->i_channels, p_sys->pi_chan_table,
+                                 p_sys->i_bits_per_sample );
+        }
+
         p_aout->p_vlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
                                   l_bytes1 );
         aout_BufferFree( p_buffer );
     }
 
     /* Now the data has been copied, unlock the buffer */
-    IDirectSoundBuffer_Unlock( p_aout->output.p_sys->p_dsbuffer,
-                               p_write_position, l_bytes1,
+    IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
                                p_wrap_around, l_bytes2 );
 
     return VLC_SUCCESS;
@@ -1041,93 +1094,3 @@ static void DirectSoundThread( notification_thread_t *p_notif )
 
     msg_Dbg( p_notif, "DirectSoundThread exiting" );
 }
-
-/*****************************************************************************
- * 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 i_channel_mask p_aout->output.p_sys->i_channel_mask
-#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( i_channel_mask & pi_channels_out[i] )
-        {
-            for( k = 0, l = 0;
-                 pi_channels_out[i] != pi_channels_ordered[k]; k++ )
-            {
-                if( i_channel_mask & 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
-}
-
-/*****************************************************************************
- * InterleaveFloat32/S16: change the channel order to the Microsoft one.
- *****************************************************************************/
-static void InterleaveFloat32( float *p_buf, float *p_buf_out,
-                               int *pi_chan_table, int i_nb_channels )
-{
-    int i, j;
-
-    for( i = 0; i < FRAME_SIZE; i++ )
-    {
-        for( j = 0; j < i_nb_channels; j++ )
-        {
-            p_buf_out[i*i_nb_channels + pi_chan_table[j]] =
-                p_buf[i*i_nb_channels + j];
-        }
-    }
-}
-
-static void InterleaveS16( int16_t *p_buf, int16_t *p_buf_out,
-                           int *pi_chan_table, int i_nb_channels )
-{
-    int i, j;
-
-    for( i = 0; i < FRAME_SIZE; i++ )
-    {
-        for( j = 0; j < i_nb_channels; j++ )
-        {
-            p_buf_out[ i*i_nb_channels + pi_chan_table[j]] =
-                p_buf[i*i_nb_channels + j];
-        }
-    }
-}