directx.c 46 KB
Newer Older
1
/*****************************************************************************
2
 * directx.c: Windows DirectX audio output method
3
 *****************************************************************************
4
 * Copyright (C) 2001 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 20
 *
 * 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
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
21
 * 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

gbazin's avatar
 
gbazin committed
36
#include <windows.h>
37 38 39
#include <mmsystem.h>
#include <dsound.h>

40 41
#define FRAME_SIZE ((int)p_aout->output.output.i_rate/20) /* Size in samples */
#define FRAMES_NUM 8                                      /* Needs to be > 3 */
gbazin's avatar
 
gbazin committed
42

43 44 45 46 47 48 49
/*****************************************************************************
 * DirectSound GUIDs.
 * Defining them here allows us to get rid of the dxguid library during
 * the linking stage.
 *****************************************************************************/
#include <initguid.h>

gbazin's avatar
 
gbazin committed
50 51 52 53 54 55 56
/*****************************************************************************
 * Useful macros
 *****************************************************************************/
#ifndef WAVE_FORMAT_IEEE_FLOAT
#   define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif

gbazin's avatar
 
gbazin committed
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
#ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
#   define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
#endif

#ifndef WAVE_FORMAT_EXTENSIBLE
#define  WAVE_FORMAT_EXTENSIBLE   0xFFFE
#endif

#ifndef SPEAKER_FRONT_LEFT
#   define SPEAKER_FRONT_LEFT             0x1
#   define SPEAKER_FRONT_RIGHT            0x2
#   define SPEAKER_FRONT_CENTER           0x4
#   define SPEAKER_LOW_FREQUENCY          0x8
#   define SPEAKER_BACK_LEFT              0x10
#   define SPEAKER_BACK_RIGHT             0x20
#   define SPEAKER_FRONT_LEFT_OF_CENTER   0x40
#   define SPEAKER_FRONT_RIGHT_OF_CENTER  0x80
#   define SPEAKER_BACK_CENTER            0x100
#   define SPEAKER_SIDE_LEFT              0x200
#   define SPEAKER_SIDE_RIGHT             0x400
#   define SPEAKER_TOP_CENTER             0x800
#   define SPEAKER_TOP_FRONT_LEFT         0x1000
#   define SPEAKER_TOP_FRONT_CENTER       0x2000
#   define SPEAKER_TOP_FRONT_RIGHT        0x4000
#   define SPEAKER_TOP_BACK_LEFT          0x8000
#   define SPEAKER_TOP_BACK_CENTER        0x10000
#   define SPEAKER_TOP_BACK_RIGHT         0x20000
#   define SPEAKER_RESERVED               0x80000000
#endif

87 88 89
#ifndef DSSPEAKER_DSSPEAKER_DIRECTOUT
#   define DSSPEAKER_DSSPEAKER_DIRECTOUT         0x00000000
#endif
gbazin's avatar
 
gbazin committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
#ifndef DSSPEAKER_HEADPHONE
#   define DSSPEAKER_HEADPHONE         0x00000001
#endif
#ifndef DSSPEAKER_MONO
#   define DSSPEAKER_MONO              0x00000002
#endif
#ifndef DSSPEAKER_QUAD
#   define DSSPEAKER_QUAD              0x00000003
#endif
#ifndef DSSPEAKER_STEREO
#   define DSSPEAKER_STEREO            0x00000004
#endif
#ifndef DSSPEAKER_SURROUND
#   define DSSPEAKER_SURROUND          0x00000005
#endif
#ifndef DSSPEAKER_5POINT1
#   define DSSPEAKER_5POINT1           0x00000006
#endif
108 109 110 111 112 113 114 115 116
#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
gbazin's avatar
 
gbazin committed
117

gbazin's avatar
 
gbazin committed
118 119 120 121 122 123 124 125 126 127 128 129 130 131
#ifndef _WAVEFORMATEXTENSIBLE_
typedef struct {
    WAVEFORMATEX    Format;
    union {
        WORD wValidBitsPerSample;       /* bits of precision  */
        WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */
        WORD wReserved;                 /* If neither applies, set to zero. */
    } Samples;
    DWORD           dwChannelMask;      /* which channels are */
                                        /* present in stream  */
    GUID            SubFormat;
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
#endif

gbazin's avatar
 
gbazin committed
132 133 134
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 );
gbazin's avatar
 
gbazin committed
135

136 137 138 139 140 141 142
/*****************************************************************************
 * notification_thread_t: DirectX event thread
 *****************************************************************************/
typedef struct notification_thread_t
{
    VLC_COMMON_MEMBERS

143 144 145
    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
146 147

    mtime_t start_date;
148
    HANDLE event;
149 150 151 152 153 154 155 156 157 158 159

} 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
160
    HINSTANCE           hdsound_dll;      /* handle of the opened dsound dll */
161 162 163 164

    int                 i_device_id;                 /*  user defined device */
    LPGUID              p_device_guid;

165 166 167 168 169
    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
170
    notification_thread_t *p_notif;                  /* DirectSoundThread id */
171

gbazin's avatar
 
gbazin committed
172
    int b_playing;                                         /* playing status */
173

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

176 177
    int i_speaker_setup;                 /* Speaker setup override */

178
    bool b_chan_reorder;              /* do we need channel reordering */
179
    int pi_chan_table[AOUT_CHAN_MAX];
gbazin's avatar
 
gbazin committed
180
    uint32_t i_channel_mask;
181 182
    uint32_t i_bits_per_sample;
    uint32_t i_channels;
183 184
};

185
static const uint32_t pi_channels_src[] =
gbazin's avatar
 
gbazin committed
186
    { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
187
      AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
188
      AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_REARCENTER,
189 190 191 192
      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,
193
      SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT, SPEAKER_BACK_CENTER,
194
      SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, 0 };
gbazin's avatar
 
gbazin committed
195 196
static const uint32_t pi_channels_out[] =
    { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
197
      SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY,
gbazin's avatar
 
gbazin committed
198
      SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
199
      SPEAKER_BACK_CENTER,
200
      SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, 0 };
gbazin's avatar
 
gbazin committed
201

202 203 204
/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
205 206 207
static int  OpenAudio  ( vlc_object_t * );
static void CloseAudio ( vlc_object_t * );
static void Play       ( aout_instance_t * );
208 209

/* local functions */
gbazin's avatar
 
gbazin committed
210 211
static void Probe             ( aout_instance_t * );
static int  InitDirectSound   ( aout_instance_t * );
212 213
static int  CreateDSBuffer    ( aout_instance_t *, int, int, int, int, int, bool );
static int  CreateDSBufferPCM ( aout_instance_t *, int*, int, int, int, bool );
gbazin's avatar
 
gbazin committed
214
static void DestroyDSBuffer   ( aout_instance_t * );
215
static void* DirectSoundThread( vlc_object_t * );
gbazin's avatar
 
gbazin committed
216 217
static int  FillBuffer        ( aout_instance_t *, int, aout_buffer_t * );

218 219 220 221
/* Speaker setup override options list */
static const char *const speaker_list[] = { "Windows default", "Mono", "Stereo",
                                            "Quad", "5.1", "7.1" };

gbazin's avatar
 
gbazin committed
222 223 224
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
225 226 227 228 229 230 231 232
#define DEVICE_TEXT N_("Output device")
#define DEVICE_LONGTEXT N_( \
    "DirectX device number: 0 default device, 1..N device by number" \
    "(Note that the default device appears as 0 AND another number)." )
#define FLOAT_TEXT N_("Use float32 output")
#define FLOAT_LONGTEXT N_( \
    "The option allows you to enable or disable the high-quality float32 " \
    "audio output mode (which is not well supported by some soundcards)." )
233 234 235
#define SPEAKER_TEXT N_("Select speaker configuration")
#define SPEAKER_LONGTEXT N_("Select speaker configuration you want to use. " \
    "This option doesn't upmix! So NO e.g. Stereo -> 5.1 conversion." )
236

237 238 239 240 241 242 243
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 )
    add_shortcut( "directx" )
244
    add_integer( "directx-audio-device", 0, NULL, DEVICE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
245
                 DEVICE_LONGTEXT, true )
dionoea's avatar
dionoea committed
246
    add_bool( "directx-audio-float32", 0, 0, FLOAT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
247
              FLOAT_LONGTEXT, true )
248 249 250 251
    add_string( "directx-audio-speaker", "Windows default", NULL,
                 SPEAKER_TEXT, SPEAKER_LONGTEXT, true )
        change_string_list( speaker_list, 0, 0 )
        change_need_restart ()
252 253
    set_callbacks( OpenAudio, CloseAudio )
vlc_module_end ()
gbazin's avatar
 
gbazin committed
254

255 256 257 258 259
/*****************************************************************************
 * OpenAudio: open the audio device
 *****************************************************************************
 * This function opens and setups Direct Sound.
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
260
static int OpenAudio( vlc_object_t *p_this )
261
{
gbazin's avatar
 
gbazin committed
262
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
gbazin's avatar
 
gbazin committed
263
    vlc_value_t val;
264 265 266 267
    char * psz_speaker;
    int i = 0;

    const char * const * ppsz_compare = speaker_list;
268

gbazin's avatar
 
gbazin committed
269
    msg_Dbg( p_aout, "OpenAudio" );
270 271

   /* Allocate structure */
gbazin's avatar
 
gbazin committed
272 273
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->output.p_sys == NULL )
274
        return VLC_ENOMEM;
275 276

    /* Initialize some variables */
gbazin's avatar
 
gbazin committed
277 278 279
    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
280
    p_aout->output.p_sys->b_playing = 0;
gbazin's avatar
 
gbazin committed
281 282

    p_aout->output.pf_play = Play;
283
    aout_VolumeSoftInit( p_aout );
284

285 286 287
    /* Retrieve config values */
    var_Create( p_aout, "directx-audio-float32",
                VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311

    var_Create( p_aout, "directx-audio-speaker",
                VLC_VAR_STRING | VLC_VAR_DOINHERIT );
    var_Get( p_this, "directx-audio-speaker", &val );
    psz_speaker = val.psz_string;

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

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

312 313 314 315 316 317
    var_Create( p_aout, "directx-audio-device",
                VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
    var_Get( p_aout, "directx-audio-device", &val );
    p_aout->output.p_sys->i_device_id = val.i_int;
    p_aout->output.p_sys->p_device_guid = 0;

318
    /* Initialise DirectSound */
gbazin's avatar
 
gbazin committed
319
    if( InitDirectSound( p_aout ) )
320
    {
gbazin's avatar
 
gbazin committed
321 322
        msg_Err( p_aout, "cannot initialize DirectSound" );
        goto error;
323 324
    }

gbazin's avatar
 
gbazin committed
325 326 327 328 329 330 331 332
    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
333
        goto error;
gbazin's avatar
 
gbazin committed
334 335 336
    }

    /* Open the device */
gbazin's avatar
 
gbazin committed
337
    if( val.i_int == AOUT_VAR_SPDIF )
338
    {
gbazin's avatar
 
gbazin committed
339 340 341 342 343 344 345 346 347 348 349 350 351
        p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');

        /* 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;

        if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
                            p_aout->output.output.i_physical_channels,
                            aout_FormatNbChannels( &p_aout->output.output ),
                            p_aout->output.output.i_rate,
352
                            p_aout->output.p_sys->i_frame_size, false )
gbazin's avatar
 
gbazin committed
353 354
            != VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
355
            msg_Err( p_aout, "cannot open directx audio device" );
gbazin's avatar
 
gbazin committed
356 357 358 359 360 361 362 363
            free( p_aout->output.p_sys );
            return VLC_EGENERIC;
        }

        aout_VolumeNoneInit( p_aout );
    }
    else
    {
gbazin's avatar
 
gbazin committed
364
        if( val.i_int == AOUT_VAR_5_1 )
gbazin's avatar
 
gbazin committed
365 366 367 368 369 370
        {
            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;
        }
371 372 373 374 375 376 377 378
        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;
        }
379 380 381 382 383 384
        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
385
        else if( val.i_int == AOUT_VAR_2F2R )
gbazin's avatar
 
gbazin committed
386 387 388 389 390
        {
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
                   | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
        }
gbazin's avatar
 
gbazin committed
391
        else if( val.i_int == AOUT_VAR_MONO )
gbazin's avatar
 
gbazin committed
392 393 394 395 396 397 398 399
        {
            p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
        }
        else
        {
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
        }
400

gbazin's avatar
 
gbazin committed
401 402 403
        if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format,
                               p_aout->output.output.i_physical_channels,
                               aout_FormatNbChannels( &p_aout->output.output ),
404
                               p_aout->output.output.i_rate, false )
gbazin's avatar
 
gbazin committed
405
            != VLC_SUCCESS )
gbazin's avatar
 
gbazin committed
406
        {
gbazin's avatar
 
gbazin committed
407
            msg_Err( p_aout, "cannot open directx audio device" );
gbazin's avatar
 
gbazin committed
408 409
            free( p_aout->output.p_sys );
            return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
410
        }
gbazin's avatar
 
gbazin committed
411 412 413 414 415

        /* 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
416
    }
417

418 419 420 421 422 423 424 425 426
    /* 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
427 428 429 430
    /* 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
431
                           DirectSoundThread,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
432
                           VLC_THREAD_PRIORITY_HIGHEST ) )
Christophe Massiot's avatar
Christophe Massiot committed
433 434
    {
        msg_Err( p_aout, "cannot create DirectSoundThread" );
435
        CloseHandle( p_aout->output.p_sys->p_notif->event );
436
        vlc_object_release( p_aout->output.p_sys->p_notif );
437
        p_aout->output.p_sys->p_notif = NULL;
Christophe Massiot's avatar
Christophe Massiot committed
438 439 440 441 442
        goto error;
    }

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

gbazin's avatar
 
gbazin committed
443
    return VLC_SUCCESS;
Christophe Massiot's avatar
Christophe Massiot committed
444 445

 error:
gbazin's avatar
 
gbazin committed
446
    CloseAudio( VLC_OBJECT(p_aout) );
447
    return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
448 449
}

gbazin's avatar
 
gbazin committed
450 451 452 453 454
/*****************************************************************************
 * Probe: probe the audio device for available formats and channels
 *****************************************************************************/
static void Probe( aout_instance_t * p_aout )
{
gbazin's avatar
 
gbazin committed
455
    vlc_value_t val, text;
gbazin's avatar
 
gbazin committed
456 457 458
    int i_format;
    unsigned int i_physical_channels;
    DWORD ui_speaker_config;
459
    bool is_default_output_set = false;
gbazin's avatar
 
gbazin committed
460

gbazin's avatar
 
gbazin committed
461
    var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
462
    text.psz_string = _("Audio Device");
gbazin's avatar
 
gbazin committed
463
    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
gbazin's avatar
 
gbazin committed
464 465 466 467 468 469 470 471

    /* 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,
472
                               p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
473 474
            == VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
475
            val.i_int = AOUT_VAR_5_1;
476
            text.psz_string = (char*) "5.1";
gbazin's avatar
 
gbazin committed
477 478
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
479 480
            var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
            is_default_output_set = true;
gbazin's avatar
 
gbazin committed
481 482 483 484
            msg_Dbg( p_aout, "device supports 5.1 channels" );
        }
    }

485 486 487 488 489 490 491 492 493 494 495 496
    /* 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;
497
               text.psz_string = (char*) "7.1";
498 499
               var_Change( p_aout, "audio-device",
                           VLC_VAR_ADDCHOICE, &val, &text );
500 501
               var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
               is_default_output_set = true;
502 503 504 505
               msg_Dbg( p_aout, "device supports 7.1 channels" );
           }
       }

506 507 508 509 510 511 512
    /* 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,
513
                               p_aout->output.output.i_rate, true )
514 515 516
            == VLC_SUCCESS )
        {
            val.i_int = AOUT_VAR_3F2R;
517
            text.psz_string = _("3 Front 2 Rear");
518 519
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
520 521 522 523 524
            if(!is_default_output_set)
            {
                var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
                is_default_output_set = true;
            }
525 526 527 528
            msg_Dbg( p_aout, "device supports 5 channels" );
        }
    }

gbazin's avatar
 
gbazin committed
529 530 531 532 533 534 535
    /* 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,
536
                               p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
537 538
            == VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
539
            val.i_int = AOUT_VAR_2F2R;
540
            text.psz_string = _("2 Front 2 Rear");
gbazin's avatar
 
gbazin committed
541 542
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
543 544 545 546 547
            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
548 549 550 551 552 553 554
            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,
555
                           p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
556 557
        == VLC_SUCCESS )
    {
gbazin's avatar
 
gbazin committed
558
        val.i_int = AOUT_VAR_STEREO;
559
        text.psz_string = _("Stereo");
gbazin's avatar
 
gbazin committed
560
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
561 562 563 564 565 566
        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!)" );
        }
567
        else msg_Dbg( p_aout, "device supports 2 channels" );
gbazin's avatar
 
gbazin committed
568 569 570 571 572
    }

    /* Test for mono support */
    i_physical_channels = AOUT_CHAN_CENTER;
    if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 1,
573
                           p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
574 575
        == VLC_SUCCESS )
    {
gbazin's avatar
 
gbazin committed
576
        val.i_int = AOUT_VAR_MONO;
577
        text.psz_string = _("Mono");
gbazin's avatar
 
gbazin committed
578
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
579 580 581 582 583 584 585 586 587
        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;
588
        msg_Dbg( p_aout, "GetSpeakerConfig failed" );
gbazin's avatar
 
gbazin committed
589 590 591
    }
    switch( DSSPEAKER_CONFIG(ui_speaker_config) )
    {
592
    case DSSPEAKER_7POINT1:
593
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is 7.1" );
594 595
        val.i_int = AOUT_VAR_7_1;
        break;
gbazin's avatar
 
gbazin committed
596
    case DSSPEAKER_5POINT1:
597
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is 5.1" );
gbazin's avatar
 
gbazin committed
598
        val.i_int = AOUT_VAR_5_1;
gbazin's avatar
 
gbazin committed
599 600
        break;
    case DSSPEAKER_QUAD:
601
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is Quad" );
gbazin's avatar
 
gbazin committed
602
        val.i_int = AOUT_VAR_2F2R;
gbazin's avatar
 
gbazin committed
603
        break;
gbazin's avatar
 
gbazin committed
604 605
#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
606
    case DSSPEAKER_MONO:
gbazin's avatar
 
gbazin committed
607
        val.i_int = AOUT_VAR_MONO;
gbazin's avatar
 
gbazin committed
608
        break;
gbazin's avatar
 
gbazin committed
609
#endif
gbazin's avatar
 
gbazin committed
610
    case DSSPEAKER_SURROUND:
611
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is surround" );
gbazin's avatar
 
gbazin committed
612
    case DSSPEAKER_STEREO:
613
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is stereo" );
gbazin's avatar
 
gbazin committed
614
    default:
615
        /* If nothing else is found, choose stereo output */
gbazin's avatar
 
gbazin committed
616
        val.i_int = AOUT_VAR_STEREO;
gbazin's avatar
 
gbazin committed
617 618
        break;
    }
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649

    /* 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
650 651
    var_Set( p_aout, "audio-device", val );

gbazin's avatar
 
gbazin committed
652 653 654 655 656 657 658
    /* Test for SPDIF support */
    if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
    {
        if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
                            p_aout->output.output.i_physical_channels,
                            aout_FormatNbChannels( &p_aout->output.output ),
                            p_aout->output.output.i_rate,
659
                            AOUT_SPDIF_SIZE, true )
gbazin's avatar
 
gbazin committed
660 661 662
            == VLC_SUCCESS )
        {
            msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
gbazin's avatar
 
gbazin committed
663
            val.i_int = AOUT_VAR_SPDIF;
664
            text.psz_string = _("A/52 over S/PDIF");
gbazin's avatar
 
gbazin committed
665 666
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
667 668 669 670 671
            if( config_GetInt( p_aout, "spdif" ) )
                var_Set( p_aout, "audio-device", val );
        }
    }

gbazin's avatar
 
gbazin committed
672 673 674 675 676 677 678 679
    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
680 681
    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );

682
    val.b_bool = true;
gbazin's avatar
 
gbazin committed
683 684 685
    var_Set( p_aout, "intf-change", val );
}

gbazin's avatar
 
gbazin committed
686
/*****************************************************************************
gbazin's avatar
 
gbazin committed
687 688 689
 * 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
690
 *****************************************************************************/
691
static void Play( aout_instance_t *p_aout )
gbazin's avatar
 
gbazin committed
692
{
gbazin's avatar
 
gbazin committed
693 694 695
    if( !p_aout->output.p_sys->b_playing )
    {
        aout_buffer_t *p_buffer;
696
        int i;
gbazin's avatar
 
gbazin committed
697 698 699 700 701 702 703 704

        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 */
705 706 707 708 709 710
        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 );
        }
gbazin's avatar
 
gbazin committed
711 712

        /* wake up the audio output thread */
713
        SetEvent( p_aout->output.p_sys->p_notif->event );
gbazin's avatar
 
gbazin committed
714
    }
715 716 717 718 719
}

/*****************************************************************************
 * CloseAudio: close the audio device
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
720
static void CloseAudio( vlc_object_t *p_this )
721
{
gbazin's avatar
 
gbazin committed
722
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
723
    aout_sys_t *p_sys = p_aout->output.p_sys;
724

725
    msg_Dbg( p_aout, "closing audio device" );
726 727

    /* kill the position notification thread, if any */
728
    if( p_sys->p_notif )
729
    {
730
        vlc_object_detach( p_sys->p_notif );
731 732 733
        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
734

735
        vlc_thread_join( p_sys->p_notif );
736
        vlc_object_release( p_sys->p_notif );
737 738 739
    }

    /* release the secondary buffer */
gbazin's avatar
 
gbazin committed
740
    DestroyDSBuffer( p_aout );
741 742

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

745
    /* free DSOUND.DLL */
746
    if( p_sys->hdsound_dll ) FreeLibrary( p_sys->hdsound_dll );
gbazin's avatar
 
gbazin committed
747

748
    free( p_aout->output.p_sys->p_device_guid );
749
    free( p_sys );
750 751
}

752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
/*****************************************************************************
 * CallBackDirectSoundEnum: callback to enumerate available devices
 *****************************************************************************/
static int CALLBACK CallBackDirectSoundEnum( LPGUID p_guid, LPCSTR psz_desc,
                                             LPCSTR psz_mod, LPVOID _p_aout )
{
    aout_instance_t *p_aout = (aout_instance_t *)_p_aout;

    msg_Dbg( p_aout, "found device: %s", psz_desc );

    if( p_aout->output.p_sys->i_device_id == 0 && p_guid )
    {
        p_aout->output.p_sys->p_device_guid = malloc( sizeof( GUID ) );
        *p_aout->output.p_sys->p_device_guid = *p_guid;
        msg_Dbg( p_aout, "using device: %s", psz_desc );
    }

    p_aout->output.p_sys->i_device_id--;
    return 1;
}

773
/*****************************************************************************
gbazin's avatar
 
gbazin committed
774
 * InitDirectSound: handle all the gory details of DirectSound initialisation
775
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
776
static int InitDirectSound( aout_instance_t *p_aout )
777 778
{
    HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
779
    HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACK, LPVOID);
780

gbazin's avatar
 
gbazin committed
781 782
    p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
    if( p_aout->output.p_sys->hdsound_dll == NULL )
783
    {
gbazin's avatar
 
gbazin committed
784 785
        msg_Warn( p_aout, "cannot open DSOUND.DLL" );
        goto error;
786 787
    }

788 789 790
    OurDirectSoundCreate = (void *)
        GetProcAddress( p_aout->output.p_sys->hdsound_dll,
                        "DirectSoundCreate" );
791 792
    if( OurDirectSoundCreate == NULL )
    {
gbazin's avatar
 
gbazin committed
793 794
        msg_Warn( p_aout, "GetProcAddress FAILED" );
        goto error;
795 796
    }

797 798 799 800 801 802 803
    /* Get DirectSoundEnumerate */
    OurDirectSoundEnumerate = (void *)
       GetProcAddress( p_aout->output.p_sys->hdsound_dll,
                       "DirectSoundEnumerateA" );
    if( OurDirectSoundEnumerate )
    {
        /* Attempt enumeration */
804
        if( FAILED( OurDirectSoundEnumerate( CallBackDirectSoundEnum,
805 806 807 808 809 810
                                             p_aout ) ) )
        {
            msg_Dbg( p_aout, "enumeration of DirectSound devices failed" );
        }
    }

811
    /* Create the direct sound object */
812
    if FAILED( OurDirectSoundCreate( p_aout->output.p_sys->p_device_guid,
813
                                     &p_aout->output.p_sys->p_dsobject,
gbazin's avatar
 
gbazin committed
814
                                     NULL ) )
815 816
    {
        msg_Warn( p_aout, "cannot create a direct sound device" );
gbazin's avatar
 
gbazin committed
817
        goto error;
818 819 820 821 822 823 824 825 826 827 828 829
    }

    /* 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
830 831 832
    if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
                                          GetDesktopWindow(),
                                          DSSCL_EXCLUSIVE) )
833 834 835 836
    {
        msg_Warn( p_aout, "cannot set direct sound cooperative level" );
    }

gbazin's avatar
 
gbazin committed
837
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
838 839 840 841 842 843 844 845

 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
846
    return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
847

848 849 850
}

/*****************************************************************************
gbazin's avatar
 
gbazin committed
851
 * CreateDSBuffer: Creates a direct sound buffer of the required format.
852 853 854 855 856 857 858 859
 *****************************************************************************
 * 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
860
 * you have to release the current one and create another.
861
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
862 863
static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
                           int i_channels, int i_nb_channels, int i_rate,
864
                           int i_bytes_per_frame, bool b_probe )
865
{
gbazin's avatar
 
gbazin committed
866
    WAVEFORMATEXTENSIBLE waveformat;
867
    DSBUFFERDESC         dsbdesc;
gbazin's avatar
 
gbazin committed
868
    unsigned int         i;
869

gbazin's avatar
 
gbazin committed
870 871
    /* First set the sound buffer format */
    waveformat.dwChannelMask = 0;
872
    for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ )
gbazin's avatar
 
gbazin committed
873
    {
874 875
        if( i_channels & pi_channels_src[i] )
            waveformat.dwChannelMask |= pi_channels_in[i];
876
    }
gbazin's avatar
 
gbazin committed
877

gbazin's avatar
 
gbazin committed
878
    switch( i_format )
gbazin's avatar
 
gbazin committed
879
    {
gbazin's avatar
 
gbazin committed
880 881
    case VLC_FOURCC('s','p','d','i'):
        i_nb_channels = 2;
gbazin's avatar
 
gbazin committed
882 883
        /* To prevent channel re-ordering */
        waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
gbazin's avatar
 
gbazin committed
884 885 886 887
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
gbazin's avatar
 
gbazin committed
888
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
gbazin's avatar
 
gbazin committed
889
        break;
gbazin's avatar
 
gbazin committed
890

gbazin's avatar
 
gbazin committed
891
    case VLC_FOURCC('f','l','3','2'):
gbazin's avatar
 
gbazin committed
892 893 894 895
        waveformat.Format.wBitsPerSample = sizeof(float) * 8;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
gbazin's avatar
 
gbazin committed
896
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
gbazin's avatar
 
gbazin committed
897 898 899 900 901 902 903
        break;

    case VLC_FOURCC('s','1','6','l'):
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
gbazin's avatar
 
gbazin committed
904
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
gbazin's avatar
 
gbazin committed
905 906
        break;
    }
907

gbazin's avatar
 
gbazin committed
908 909 910 911 912 913 914
    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;

915 916 917
    p_aout->output.p_sys->i_bits_per_sample = waveformat.Format.wBitsPerSample;
    p_aout->output.p_sys->i_channels = i_nb_channels;

918 919 920 921 922 923
    /* 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
924 925 926 927 928 929 930 931 932 933
    /* 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
934

935 936 937
        /* Needed for 5.1 on emu101k */
        dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
    }
gbazin's avatar
 
gbazin committed
938 939 940 941 942 943 944 945

    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) )
    {
946 947 948 949 950
        if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE )
        {
            /* Try without DSBCAPS_LOCHARDWARE */
            dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
            if FAILED( IDirectSound_CreateSoundBuffer(
gbazin's avatar
 
gbazin committed
951 952
                   p_aout->output.p_sys->p_dsobject, &dsbdesc,
                   &p_aout->output.p_sys->p_dsbuffer, NULL) )
953 954 955 956 957 958 959
            {
                return VLC_EGENERIC;
            }
            if( !b_probe )
                msg_Dbg( p_aout, "couldn't use hardware sound buffer" );
        }
        else
gbazin's avatar