directx.c 45.3 KB
Newer Older
1
/*****************************************************************************
2
 * directx.c: Windows DirectX audio output method
3
 *****************************************************************************
4
 * Copyright (C) 2001-2009 the VideoLAN team
5
 * $Id$
6
 *
7
 * Authors: Gildas Bazin <gbazin@videolan.org>
8 9 10 11 12 13 14 15 16 17 18 19
 *
 * 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
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
20 21
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26 27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <vlc_common.h>
33
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
34
#include <vlc_aout.h>
35
#include <vlc_charset.h>
36

37 38
#include "windows_audio_common.h"

39 40
#include <dsound.h>

41
#define FRAME_SIZE ((int)p_aout->output.output.i_rate/20) /* Size in samples */
gbazin's avatar
 
gbazin committed
42

43 44 45 46 47 48 49
/*****************************************************************************
 * notification_thread_t: DirectX event thread
 *****************************************************************************/
typedef struct notification_thread_t
{
    VLC_COMMON_MEMBERS

50 51 52
    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 */
gbazin's avatar
 
gbazin committed
53 54

    mtime_t start_date;
55
    HANDLE event;
56 57 58 59 60 61 62 63 64 65 66

} notification_thread_t;

/*****************************************************************************
 * aout_sys_t: directx audio output method descriptor
 *****************************************************************************
 * This structure is part of the audio output thread descriptor.
 * It describes the direct sound specific properties of an audio device.
 *****************************************************************************/
struct aout_sys_t
{
gbazin's avatar
 
gbazin committed
67
    HINSTANCE           hdsound_dll;      /* handle of the opened dsound dll */
68

69
    char *              psz_device;              /* user defined device name */
70 71
    LPGUID              p_device_guid;

72 73 74 75 76
    LPDIRECTSOUND       p_dsobject;              /* main Direct Sound object */
    LPDIRECTSOUNDBUFFER p_dsbuffer;   /* the sound buffer we use (direct sound
                                       * takes care of mixing all the
                                       * secondary buffers into the primary) */

gbazin's avatar
 
gbazin committed
77
    notification_thread_t *p_notif;                  /* DirectSoundThread id */
78

79
    int      b_playing;                                    /* playing status */
80

81
    int      i_frame_size;                     /* Size in bytes of one frame */
gbazin's avatar
 
gbazin committed
82

83
    int      i_speaker_setup;                      /* Speaker setup override */
84

85 86
    bool     b_chan_reorder;                /* do we need channel reordering */
    int      pi_chan_table[AOUT_CHAN_MAX];
gbazin's avatar
 
gbazin committed
87
    uint32_t i_channel_mask;
88 89
    uint32_t i_bits_per_sample;
    uint32_t i_channels;
90 91 92 93 94
};

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
95 96 97
static int  OpenAudio  ( vlc_object_t * );
static void CloseAudio ( vlc_object_t * );
static void Play       ( aout_instance_t * );
98 99

/* local functions */
gbazin's avatar
 
gbazin committed
100 101
static void Probe             ( aout_instance_t * );
static int  InitDirectSound   ( aout_instance_t * );
102
static int  CreateDSBuffer    ( aout_instance_t *, int, int, int, int, int, bool );
103
static int  CreateDSBufferPCM ( aout_instance_t *, vlc_fourcc_t*, int, int, int, bool );
gbazin's avatar
 
gbazin committed
104
static void DestroyDSBuffer   ( aout_instance_t * );
105
static void* DirectSoundThread( vlc_object_t * );
gbazin's avatar
 
gbazin committed
106 107
static int  FillBuffer        ( aout_instance_t *, int, aout_buffer_t * );

108 109 110
static int ReloadDirectXDevices( vlc_object_t *, char const *,
                                vlc_value_t, vlc_value_t, void * );

111 112 113
/* Speaker setup override options list */
static const char *const speaker_list[] = { "Windows default", "Mono", "Stereo",
                                            "Quad", "5.1", "7.1" };
114 115
static const char *const ppsz_adev[] = {"default",  };
static const char *const ppsz_adev_text[] = {"default", };
116

gbazin's avatar
 
gbazin committed
117 118 119
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
120
#define DEVICE_TEXT N_("Output device")
121
#define DEVICE_LONGTEXT N_("Select your audio output device")
122

123
#define SPEAKER_TEXT N_("Speaker configuration")
124 125
#define SPEAKER_LONGTEXT N_("Select speaker configuration you want to use. " \
    "This option doesn't upmix! So NO e.g. Stereo -> 5.1 conversion." )
126

127 128 129 130 131 132
vlc_module_begin ()
    set_description( N_("DirectX audio output") )
    set_shortname( "DirectX" )
    set_capability( "audio output", 100 )
    set_category( CAT_AUDIO )
    set_subcategory( SUBCAT_AUDIO_AOUT )
133
    add_shortcut( "directx", "directsound" )
134

135
    add_string( "directx-audio-device-name", "default",
136
             DEVICE_TEXT, DEVICE_LONGTEXT, false )
137 138 139 140
        add_deprecated_alias( "directx-audio-device" ) /* Since 1.1.0 */
        change_string_list( ppsz_adev, ppsz_adev_text, ReloadDirectXDevices )
        change_action_add( ReloadDirectXDevices, N_("Refresh list") )
        change_need_restart ()
141
    add_bool( "directx-audio-float32", false, FLOAT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
142
              FLOAT_LONGTEXT, true )
143
    add_string( "directx-audio-speaker", "Windows default",
144 145 146
                 SPEAKER_TEXT, SPEAKER_LONGTEXT, true )
        change_string_list( speaker_list, 0, 0 )
        change_need_restart ()
147

148 149
    set_callbacks( OpenAudio, CloseAudio )
vlc_module_end ()
gbazin's avatar
 
gbazin committed
150

151 152 153 154 155
/*****************************************************************************
 * OpenAudio: open the audio device
 *****************************************************************************
 * This function opens and setups Direct Sound.
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
156
static int OpenAudio( vlc_object_t *p_this )
157
{
gbazin's avatar
 
gbazin committed
158
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
gbazin's avatar
 
gbazin committed
159
    vlc_value_t val;
160 161 162 163
    char * psz_speaker;
    int i = 0;

    const char * const * ppsz_compare = speaker_list;
164

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
165
    msg_Dbg( p_aout, "Opening DirectSound Audio Output" );
166 167

   /* Allocate structure */
gbazin's avatar
 
gbazin committed
168 169
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->output.p_sys == NULL )
170
        return VLC_ENOMEM;
171 172

    /* Initialize some variables */
gbazin's avatar
 
gbazin committed
173 174 175
    p_aout->output.p_sys->p_dsobject = NULL;
    p_aout->output.p_sys->p_dsbuffer = NULL;
    p_aout->output.p_sys->p_notif = NULL;
gbazin's avatar
 
gbazin committed
176
    p_aout->output.p_sys->b_playing = 0;
gbazin's avatar
 
gbazin committed
177 178

    p_aout->output.pf_play = Play;
179
    aout_VolumeSoftInit( p_aout );
180

181 182 183
    /* Retrieve config values */
    var_Create( p_aout, "directx-audio-float32",
                VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
184
    psz_speaker = var_CreateGetString( p_aout, "directx-audio-speaker" );
185 186 187 188 189 190 191 192 193 194 195 196

    while ( *ppsz_compare != NULL )
    {
        if ( !strncmp( *ppsz_compare, psz_speaker, strlen(*ppsz_compare) ) )
        {
            break;
        }
        ppsz_compare++; i++;
    }

    if ( *ppsz_compare == NULL )
    {
197
        msg_Err( p_aout, "(%s) isn't valid speaker setup option", psz_speaker );
198 199 200
        msg_Err( p_aout, "Defaulting to Windows default speaker config");
        i = 0;
    }
201
    free( psz_speaker );
202 203
    p_aout->output.p_sys->i_speaker_setup = i;

204 205
    p_aout->output.p_sys->p_device_guid = 0;

206
    /* Initialise DirectSound */
gbazin's avatar
 
gbazin committed
207
    if( InitDirectSound( p_aout ) )
208
    {
gbazin's avatar
 
gbazin committed
209 210
        msg_Err( p_aout, "cannot initialize DirectSound" );
        goto error;
211 212
    }

gbazin's avatar
 
gbazin committed
213 214 215 216 217 218 219 220
    if( var_Type( p_aout, "audio-device" ) == 0 )
    {
        Probe( p_aout );
    }

    if( var_Get( p_aout, "audio-device", &val ) < 0 )
    {
        /* Probe() has failed. */
gbazin's avatar
 
gbazin committed
221
        goto error;
gbazin's avatar
 
gbazin committed
222 223 224
    }

    /* Open the device */
gbazin's avatar
 
gbazin committed
225
    if( val.i_int == AOUT_VAR_SPDIF )
226
    {
227
        p_aout->output.output.i_format = VLC_CODEC_SPDIFL;
gbazin's avatar
 
gbazin committed
228 229 230 231 232 233 234 235

        /* Calculate the frame size in bytes */
        p_aout->output.i_nb_samples = A52_FRAME_NB;
        p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
        p_aout->output.output.i_frame_length = A52_FRAME_NB;
        p_aout->output.p_sys->i_frame_size =
            p_aout->output.output.i_bytes_per_frame;

236
        if( CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL,
gbazin's avatar
 
gbazin committed
237 238 239
                            p_aout->output.output.i_physical_channels,
                            aout_FormatNbChannels( &p_aout->output.output ),
                            p_aout->output.output.i_rate,
240
                            p_aout->output.p_sys->i_frame_size, false )
gbazin's avatar
 
gbazin committed
241 242
            != VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
243
            msg_Err( p_aout, "cannot open directx audio device" );
gbazin's avatar
 
gbazin committed
244 245 246 247 248 249 250 251
            free( p_aout->output.p_sys );
            return VLC_EGENERIC;
        }

        aout_VolumeNoneInit( p_aout );
    }
    else
    {
gbazin's avatar
 
gbazin committed
252
        if( val.i_int == AOUT_VAR_5_1 )
gbazin's avatar
 
gbazin committed
253 254 255 256 257 258
        {
            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;
        }
259 260 261 262 263 264 265 266
        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;
        }
267 268 269 270 271 272
        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;
        }
gbazin's avatar
 
gbazin committed
273
        else if( val.i_int == AOUT_VAR_2F2R )
gbazin's avatar
 
gbazin committed
274 275 276 277 278
        {
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
                   | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
        }
gbazin's avatar
 
gbazin committed
279
        else if( val.i_int == AOUT_VAR_MONO )
gbazin's avatar
 
gbazin committed
280 281 282 283 284 285 286 287
        {
            p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
        }
        else
        {
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
        }
288

gbazin's avatar
 
gbazin committed
289 290 291
        if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format,
                               p_aout->output.output.i_physical_channels,
                               aout_FormatNbChannels( &p_aout->output.output ),
292
                               p_aout->output.output.i_rate, false )
gbazin's avatar
 
gbazin committed
293
            != VLC_SUCCESS )
gbazin's avatar
 
gbazin committed
294
        {
gbazin's avatar
 
gbazin committed
295
            msg_Err( p_aout, "cannot open directx audio device" );
gbazin's avatar
 
gbazin committed
296 297
            free( p_aout->output.p_sys );
            return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
298
        }
gbazin's avatar
 
gbazin committed
299 300 301 302 303

        /* Calculate the frame size in bytes */
        p_aout->output.i_nb_samples = FRAME_SIZE;
        aout_FormatPrepare( &p_aout->output.output );
        aout_VolumeSoftInit( p_aout );
gbazin's avatar
 
gbazin committed
304
    }
305

306 307 308 309 310 311 312 313 314
    /* 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;

Christophe Massiot's avatar
Christophe Massiot committed
315 316 317 318
    /* 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",
gbazin's avatar
 
gbazin committed
319
                           DirectSoundThread,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
320
                           VLC_THREAD_PRIORITY_HIGHEST ) )
Christophe Massiot's avatar
Christophe Massiot committed
321 322
    {
        msg_Err( p_aout, "cannot create DirectSoundThread" );
323
        CloseHandle( p_aout->output.p_sys->p_notif->event );
324
        vlc_object_release( p_aout->output.p_sys->p_notif );
325
        p_aout->output.p_sys->p_notif = NULL;
Christophe Massiot's avatar
Christophe Massiot committed
326 327 328 329 330
        goto error;
    }

    vlc_object_attach( p_aout->output.p_sys->p_notif, p_aout );

gbazin's avatar
 
gbazin committed
331
    return VLC_SUCCESS;
Christophe Massiot's avatar
Christophe Massiot committed
332 333

 error:
gbazin's avatar
 
gbazin committed
334
    CloseAudio( VLC_OBJECT(p_aout) );
335
    return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
336 337
}

gbazin's avatar
 
gbazin committed
338 339 340 341 342
/*****************************************************************************
 * Probe: probe the audio device for available formats and channels
 *****************************************************************************/
static void Probe( aout_instance_t * p_aout )
{
gbazin's avatar
 
gbazin committed
343
    vlc_value_t val, text;
344
    vlc_fourcc_t i_format;
gbazin's avatar
 
gbazin committed
345 346
    unsigned int i_physical_channels;
    DWORD ui_speaker_config;
347
    bool is_default_output_set = false;
gbazin's avatar
 
gbazin committed
348

gbazin's avatar
 
gbazin committed
349
    var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
350
    text.psz_string = _("Audio Device");
gbazin's avatar
 
gbazin committed
351
    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
gbazin's avatar
 
gbazin committed
352 353 354 355 356 357 358 359

    /* Test for 5.1 support */
    i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
                          AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
                          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, 6,
360
                               p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
361 362
            == VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
363
            val.i_int = AOUT_VAR_5_1;
364
            text.psz_string = (char*) "5.1";
gbazin's avatar
 
gbazin committed
365 366
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
367 368
            var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
            is_default_output_set = true;
gbazin's avatar
 
gbazin committed
369 370 371 372
            msg_Dbg( p_aout, "device supports 5.1 channels" );
        }
    }

373 374 375 376 377
    /* 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;
ivoire's avatar
ivoire committed
378 379 380
    if( p_aout->output.output.i_physical_channels == i_physical_channels )
    {
        if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 8,
381
                                  p_aout->output.output.i_rate, true )
ivoire's avatar
ivoire committed
382 383 384 385 386 387 388 389 390 391 392
            == VLC_SUCCESS )
        {
            val.i_int = AOUT_VAR_7_1;
            text.psz_string = (char*) "7.1";
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
            var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
            is_default_output_set = true;
            msg_Dbg( p_aout, "device supports 7.1 channels" );
        }
    }
393

394 395 396 397 398 399 400
    /* 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,
401
                               p_aout->output.output.i_rate, true )
402 403 404
            == VLC_SUCCESS )
        {
            val.i_int = AOUT_VAR_3F2R;
405
            text.psz_string = _("3 Front 2 Rear");
406 407
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
408 409 410 411 412
            if(!is_default_output_set)
            {
                var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
                is_default_output_set = true;
            }
413 414 415 416
            msg_Dbg( p_aout, "device supports 5 channels" );
        }
    }

gbazin's avatar
 
gbazin committed
417 418 419 420 421 422 423
    /* Test for 2 Front 2 Rear support */
    i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
                          AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
    if( ( p_aout->output.output.i_physical_channels & i_physical_channels )
        == i_physical_channels )
    {
        if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 4,
424
                               p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
425 426
            == VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
427
            val.i_int = AOUT_VAR_2F2R;
428
            text.psz_string = _("2 Front 2 Rear");
gbazin's avatar
 
gbazin committed
429 430
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
431 432 433 434 435
            if(!is_default_output_set)
            {
                var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
                is_default_output_set = true;
            }
gbazin's avatar
 
gbazin committed
436 437 438 439 440 441 442
            msg_Dbg( p_aout, "device supports 4 channels" );
        }
    }

    /* Test for stereo support */
    i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
    if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 2,
443
                           p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
444 445
        == VLC_SUCCESS )
    {
gbazin's avatar
 
gbazin committed
446
        val.i_int = AOUT_VAR_STEREO;
447
        text.psz_string = _("Stereo");
gbazin's avatar
 
gbazin committed
448
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
449 450 451 452 453 454
        if(!is_default_output_set)
        {
            var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
            is_default_output_set = true;
            msg_Dbg( p_aout, "device supports 2 channels (DEFAULT!)" );
        }
455
        else msg_Dbg( p_aout, "device supports 2 channels" );
gbazin's avatar
 
gbazin committed
456 457 458 459 460
    }

    /* Test for mono support */
    i_physical_channels = AOUT_CHAN_CENTER;
    if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 1,
461
                           p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
462 463
        == VLC_SUCCESS )
    {
gbazin's avatar
 
gbazin committed
464
        val.i_int = AOUT_VAR_MONO;
465
        text.psz_string = _("Mono");
gbazin's avatar
 
gbazin committed
466
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
467 468 469 470 471 472 473 474 475
        msg_Dbg( p_aout, "device supports 1 channel" );
    }

    /* Check the speaker configuration to determine which channel config should
     * be the default */
    if FAILED( IDirectSound_GetSpeakerConfig( p_aout->output.p_sys->p_dsobject,
                                              &ui_speaker_config ) )
    {
        ui_speaker_config = DSSPEAKER_STEREO;
476
        msg_Dbg( p_aout, "GetSpeakerConfig failed" );
gbazin's avatar
 
gbazin committed
477 478 479
    }
    switch( DSSPEAKER_CONFIG(ui_speaker_config) )
    {
480
    case DSSPEAKER_7POINT1:
481
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is 7.1" );
482 483
        val.i_int = AOUT_VAR_7_1;
        break;
gbazin's avatar
 
gbazin committed
484
    case DSSPEAKER_5POINT1:
485
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is 5.1" );
gbazin's avatar
 
gbazin committed
486
        val.i_int = AOUT_VAR_5_1;
gbazin's avatar
 
gbazin committed
487 488
        break;
    case DSSPEAKER_QUAD:
489
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is Quad" );
gbazin's avatar
 
gbazin committed
490
        val.i_int = AOUT_VAR_2F2R;
gbazin's avatar
 
gbazin committed
491
        break;
gbazin's avatar
 
gbazin committed
492 493
#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. */
gbazin's avatar
 
gbazin committed
494
    case DSSPEAKER_MONO:
gbazin's avatar
 
gbazin committed
495
        val.i_int = AOUT_VAR_MONO;
gbazin's avatar
 
gbazin committed
496
        break;
gbazin's avatar
 
gbazin committed
497
#endif
gbazin's avatar
 
gbazin committed
498
    case DSSPEAKER_SURROUND:
499
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is surround" );
gbazin's avatar
 
gbazin committed
500
    case DSSPEAKER_STEREO:
501
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is stereo" );
gbazin's avatar
 
gbazin committed
502
    default:
503
        /* If nothing else is found, choose stereo output */
gbazin's avatar
 
gbazin committed
504
        val.i_int = AOUT_VAR_STEREO;
gbazin's avatar
 
gbazin committed
505 506
        break;
    }
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537

    /* Check if we want to override speaker config */
    switch( p_aout->output.p_sys->i_speaker_setup )
    {
    case 0: /* Default value aka Windows default speaker setup */
        break;
    case 1: /* Mono */
        msg_Dbg( p_aout, "SpeakerConfig is forced to Mono" );
        val.i_int = AOUT_VAR_MONO;
        break;
    case 2: /* Stereo */
        msg_Dbg( p_aout, "SpeakerConfig is forced to Stereo" );
        val.i_int = AOUT_VAR_STEREO;
        break;
    case 3: /* Quad */
        msg_Dbg( p_aout, "SpeakerConfig is forced to Quad" );
        val.i_int = AOUT_VAR_2F2R;
        break;
    case 4: /* 5.1 */
        msg_Dbg( p_aout, "SpeakerConfig is forced to 5.1" );
        val.i_int = AOUT_VAR_5_1;
        break;
    case 5: /* 7.1 */
        msg_Dbg( p_aout, "SpeakerConfig is forced to 7.1" );
        val.i_int = AOUT_VAR_7_1;
        break;
    default:
        msg_Dbg( p_aout, "SpeakerConfig is forced to non-existing value" );
        break;
    }

gbazin's avatar
 
gbazin committed
538 539
    var_Set( p_aout, "audio-device", val );

gbazin's avatar
 
gbazin committed
540 541 542
    /* Test for SPDIF support */
    if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
    {
543
        if( CreateDSBuffer( p_aout, VLC_CODEC_SPDIFL,
gbazin's avatar
 
gbazin committed
544 545 546
                            p_aout->output.output.i_physical_channels,
                            aout_FormatNbChannels( &p_aout->output.output ),
                            p_aout->output.output.i_rate,
547
                            AOUT_SPDIF_SIZE, true )
gbazin's avatar
 
gbazin committed
548 549 550
            == VLC_SUCCESS )
        {
            msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
gbazin's avatar
 
gbazin committed
551
            val.i_int = AOUT_VAR_SPDIF;
552
            text.psz_string = _("A/52 over S/PDIF");
gbazin's avatar
 
gbazin committed
553 554
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
555
            if( var_InheritBool( p_aout, "spdif" ) )
gbazin's avatar
 
gbazin committed
556 557 558 559
                var_Set( p_aout, "audio-device", val );
        }
    }

gbazin's avatar
 
gbazin committed
560 561 562 563 564 565 566 567
    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;
    }

gbazin's avatar
 
gbazin committed
568
    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
569
    var_SetBool( p_aout, "intf-change", true );
gbazin's avatar
 
gbazin committed
570 571
}

gbazin's avatar
 
gbazin committed
572
/*****************************************************************************
gbazin's avatar
 
gbazin committed
573 574 575
 * Play: we'll start playing the directsound buffer here because at least here
 *       we know the first buffer has been put in the aout fifo and we also
 *       know its date.
gbazin's avatar
 
gbazin committed
576
 *****************************************************************************/
577
static void Play( aout_instance_t *p_aout )
gbazin's avatar
 
gbazin committed
578
{
gbazin's avatar
 
gbazin committed
579 580 581 582 583 584 585 586 587 588 589
    if( !p_aout->output.p_sys->b_playing )
    {
        aout_buffer_t *p_buffer;

        p_aout->output.p_sys->b_playing = 1;

        /* get the playing date of the first aout buffer */
        p_aout->output.p_sys->p_notif->start_date =
            aout_FifoFirstDate( p_aout, &p_aout->output.fifo );

        /* fill in the first samples */
590
        for( int i = 0; i < FRAMES_NUM; i++ )
591 592 593 594 595
        {
            p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
            if( !p_buffer ) break;
            FillBuffer( p_aout, i, p_buffer );
        }
gbazin's avatar
 
gbazin committed
596 597

        /* wake up the audio output thread */
598
        SetEvent( p_aout->output.p_sys->p_notif->event );
gbazin's avatar
 
gbazin committed
599
    }
600 601 602 603 604
}

/*****************************************************************************
 * CloseAudio: close the audio device
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
605
static void CloseAudio( vlc_object_t *p_this )
606
{
gbazin's avatar
 
gbazin committed
607
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
608
    aout_sys_t *p_sys = p_aout->output.p_sys;
609

610
    msg_Dbg( p_aout, "closing audio device" );
611 612

    /* kill the position notification thread, if any */
613
    if( p_sys->p_notif )
614
    {
615 616 617
        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 );
gbazin's avatar
 
gbazin committed
618

619
        vlc_thread_join( p_sys->p_notif );
620
        vlc_object_release( p_sys->p_notif );
621 622 623
    }

    /* release the secondary buffer */
gbazin's avatar
 
gbazin committed
624
    DestroyDSBuffer( p_aout );
625 626

    /* finally release the DirectSound object */
627
    if( p_sys->p_dsobject ) IDirectSound_Release( p_sys->p_dsobject );
628

629
    /* free DSOUND.DLL */
630
    if( p_sys->hdsound_dll ) FreeLibrary( p_sys->hdsound_dll );
gbazin's avatar
 
gbazin committed
631

632
    free( p_aout->output.p_sys->p_device_guid );
633
    free( p_sys );
634 635
}

636 637 638
/*****************************************************************************
 * CallBackDirectSoundEnum: callback to enumerate available devices
 *****************************************************************************/
639 640
static int CALLBACK CallBackDirectSoundEnum( LPGUID p_guid, LPCWSTR psz_desc,
                                             LPCWSTR psz_mod, LPVOID _p_aout )
641
{
642 643
    VLC_UNUSED( psz_mod );

644 645
    aout_instance_t *p_aout = (aout_instance_t *)_p_aout;

646 647
    char *psz_device = FromWide( psz_desc );
    msg_Dbg( p_aout, "found device: %s", psz_device );
648

649 650
    if( p_aout->output.p_sys->psz_device &&
        !strcmp(p_aout->output.p_sys->psz_device, psz_device) && p_guid )
651
    {
652
        /* Use the device corresponding to psz_device */
653 654
        p_aout->output.p_sys->p_device_guid = malloc( sizeof( GUID ) );
        *p_aout->output.p_sys->p_device_guid = *p_guid;
655
        msg_Dbg( p_aout, "using device: %s", psz_device );
656
    }
657 658 659 660 661
    else
    {
        /* If no default device has been selected, chose the first one */
        if( !p_aout->output.p_sys->psz_device && p_guid )
        {
662
            p_aout->output.p_sys->psz_device = strdup( psz_device );
663 664
            p_aout->output.p_sys->p_device_guid = malloc( sizeof( GUID ) );
            *p_aout->output.p_sys->p_device_guid = *p_guid;
665
            msg_Dbg( p_aout, "using device: %s", psz_device );
666 667
        }
    }
668 669

    free( psz_device );
670
    return true;
671 672
}

673
/*****************************************************************************
gbazin's avatar
 
gbazin committed
674
 * InitDirectSound: handle all the gory details of DirectSound initialisation
675
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
676
static int InitDirectSound( aout_instance_t *p_aout )
677 678
{
    HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
679
    HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKW, LPVOID);
680

gbazin's avatar
 
gbazin committed
681 682
    p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
    if( p_aout->output.p_sys->hdsound_dll == NULL )
683
    {
gbazin's avatar
 
gbazin committed
684 685
        msg_Warn( p_aout, "cannot open DSOUND.DLL" );
        goto error;
686 687
    }

688 689 690
    OurDirectSoundCreate = (void *)
        GetProcAddress( p_aout->output.p_sys->hdsound_dll,
                        "DirectSoundCreate" );
691 692
    if( OurDirectSoundCreate == NULL )
    {
gbazin's avatar
 
gbazin committed
693 694
        msg_Warn( p_aout, "GetProcAddress FAILED" );
        goto error;
695 696
    }

697 698 699
    /* Get DirectSoundEnumerate */
    OurDirectSoundEnumerate = (void *)
       GetProcAddress( p_aout->output.p_sys->hdsound_dll,
700
                       "DirectSoundEnumerateW" );
701 702
    if( OurDirectSoundEnumerate )
    {
703
        p_aout->output.p_sys->psz_device = var_InheritString(p_aout, "directx-audio-device-name");
704
        /* Attempt enumeration */
705
        if( FAILED( OurDirectSoundEnumerate( CallBackDirectSoundEnum,
706 707 708 709 710 711
                                             p_aout ) ) )
        {
            msg_Dbg( p_aout, "enumeration of DirectSound devices failed" );
        }
    }

712
    /* Create the direct sound object */
713
    if FAILED( OurDirectSoundCreate( p_aout->output.p_sys->p_device_guid,
714
                                     &p_aout->output.p_sys->p_dsobject,
gbazin's avatar
 
gbazin committed
715
                                     NULL ) )
716 717
    {
        msg_Warn( p_aout, "cannot create a direct sound device" );
gbazin's avatar
 
gbazin committed
718
        goto error;
719 720 721 722 723 724 725 726 727 728 729 730
    }

    /* Set DirectSound Cooperative level, ie what control we want over Windows
     * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
     * settings of the primary buffer, but also that only the sound of our
     * application will be hearable when it will have the focus.
     * !!! (this is not really working as intended yet because to set the
     * cooperative level you need the window handle of your application, and
     * I don't know of any easy way to get it. Especially since we might play
     * sound without any video, and so what window handle should we use ???
     * The hack for now is to use the Desktop window handle - it seems to be
     * working */
gbazin's avatar
 
gbazin committed
731 732 733
    if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
                                          GetDesktopWindow(),
                                          DSSCL_EXCLUSIVE) )
734 735 736 737
    {
        msg_Warn( p_aout, "cannot set direct sound cooperative level" );
    }

gbazin's avatar
 
gbazin committed
738
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
739 740 741 742 743 744 745 746

 error:
    p_aout->output.p_sys->p_dsobject = NULL;
    if( p_aout->output.p_sys->hdsound_dll )
    {
        FreeLibrary( p_aout->output.p_sys->hdsound_dll );
        p_aout->output.p_sys->hdsound_dll = NULL;
    }
gbazin's avatar
 
gbazin committed
747
    return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
748

749 750 751
}

/*****************************************************************************
gbazin's avatar
 
gbazin committed
752
 * CreateDSBuffer: Creates a direct sound buffer of the required format.
753 754 755 756 757 758 759 760
 *****************************************************************************
 * This function creates the buffer we'll use to play audio.
 * In DirectSound there are two kinds of buffers:
 * - the primary buffer: which is the actual buffer that the soundcard plays
 * - the secondary buffer(s): these buffers are the one actually used by
 *    applications and DirectSound takes care of mixing them into the primary.
 *
 * Once you create a secondary buffer, you cannot change its format anymore so
gbazin's avatar
 
gbazin committed
761
 * you have to release the current one and create another.
762
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
763 764
static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
                           int i_channels, int i_nb_channels, int i_rate,
765
                           int i_bytes_per_frame, bool b_probe )
766
{
gbazin's avatar
 
gbazin committed
767
    WAVEFORMATEXTENSIBLE waveformat;
768
    DSBUFFERDESC         dsbdesc;
gbazin's avatar
 
gbazin committed
769
    unsigned int         i;
770

gbazin's avatar
 
gbazin committed
771 772
    /* First set the sound buffer format */
    waveformat.dwChannelMask = 0;
773
    for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ )
gbazin's avatar
 
gbazin committed
774
    {
775 776
        if( i_channels & pi_channels_src[i] )
            waveformat.dwChannelMask |= pi_channels_in[i];
777
    }
gbazin's avatar
 
gbazin committed
778

gbazin's avatar
 
gbazin committed
779
    switch( i_format )
gbazin's avatar
 
gbazin committed
780
    {
781
    case VLC_CODEC_SPDIFL:
gbazin's avatar
 
gbazin committed
782
        i_nb_channels = 2;
gbazin's avatar
 
gbazin committed
783 784
        /* To prevent channel re-ordering */
        waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
gbazin's avatar
 
gbazin committed
785 786 787 788
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
gbazin's avatar
 
gbazin committed
789
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
gbazin's avatar
 
gbazin committed
790
        break;
gbazin's avatar
 
gbazin committed
791

792
    case VLC_CODEC_FL32:
gbazin's avatar
 
gbazin committed
793 794 795 796
        waveformat.Format.wBitsPerSample = sizeof(float) * 8;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
gbazin's avatar
 
gbazin committed
797
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
gbazin's avatar
 
gbazin committed
798 799
        break;

800
    case VLC_CODEC_S16L:
gbazin's avatar
 
gbazin committed
801 802 803 804
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
gbazin's avatar
 
gbazin committed
805
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
gbazin's avatar
 
gbazin committed
806 807
        break;
    }
808

gbazin's avatar
 
gbazin committed
809 810 811 812 813 814 815
    waveformat.Format.nChannels = i_nb_channels;
    waveformat.Format.nSamplesPerSec = i_rate;
    waveformat.Format.nBlockAlign =
        waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
    waveformat.Format.nAvgBytesPerSec =
        waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;

816 817 818
    p_aout->output.p_sys->i_bits_per_sample = waveformat.Format.wBitsPerSample;
    p_aout->output.p_sys->i_channels = i_nb_channels;

819 820 821 822 823 824
    /* Then fill in the direct sound descriptor */
    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
    dsbdesc.dwSize = sizeof(DSBUFFERDESC);
    dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
                    | DSBCAPS_GLOBALFOCUS;      /* Allows background playing */

gbazin's avatar
 
gbazin committed
825 826 827 828 829 830 831 832 833 834
    /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
    if( i_nb_channels <= 2 )
    {
        waveformat.Format.cbSize = 0;
    }
    else
    {
        waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
        waveformat.Format.cbSize =
            sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
gbazin's avatar
 
gbazin committed
835

836 837 838
        /* Needed for 5.1 on emu101k */
        dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
    }
gbazin's avatar
 
gbazin committed
839 840 841 842 843 844 845 846

    dsbdesc.dwBufferBytes = FRAMES_NUM * i_bytes_per_frame;   /* buffer size */
    dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat;

    if FAILED( IDirectSound_CreateSoundBuffer(
                   p_aout->output.p_sys->p_dsobject, &dsbdesc,
                   &p_aout->output.p_sys->p_dsbuffer, NULL) )
    {
847 848 849 850 851
        if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE )
        {
            /* Try without DSBCAPS_LOCHARDWARE */
            dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
            if FAILED( IDirectSound_CreateSoundBuffer(
gbazin's avatar
 
gbazin committed
852 853
                   p_aout->output.p_sys->p_dsobject, &dsbdesc,
                   &p_aout->output.p_sys->p_dsbuffer, NULL) )
854 855 856 857 858 859 860
            {
                return VLC_EGENERIC;
            }
            if( !b_probe )
                msg_Dbg( p_aout, "couldn't use hardware sound buffer" );
        }
        else
gbazin's avatar
 
gbazin committed
861 862 863
        {
            return VLC_EGENERIC;
        }
864 865
    }

gbazin's avatar
 
gbazin committed
866 867 868 869 870 871 872
    /* Stop here if we were just probing */
    if( b_probe )
    {
        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
        p_aout->output.p_sys->p_dsbuffer = NULL;
        return VLC_SUCCESS;
    }
gbazin's avatar
 
gbazin committed
873

874
    p_aout->output