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 133
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" )
134 135
    add_shortcut( "directsound" )

136 137
    add_string( "directx-audio-device-name", "default", NULL,
             DEVICE_TEXT, DEVICE_LONGTEXT, false )
138 139 140 141
        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 ()
ivoire's avatar
ivoire committed
142
    add_bool( "directx-audio-float32", false, NULL, FLOAT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
143
              FLOAT_LONGTEXT, true )
144 145 146 147
    add_string( "directx-audio-speaker", "Windows default", NULL,
                 SPEAKER_TEXT, SPEAKER_LONGTEXT, true )
        change_string_list( speaker_list, 0, 0 )
        change_need_restart ()
148

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

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

    const char * const * ppsz_compare = speaker_list;
165

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

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

    /* Initialize some variables */
gbazin's avatar
 
gbazin committed
174 175 176
    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
177
    p_aout->output.p_sys->b_playing = 0;
gbazin's avatar
 
gbazin committed
178 179

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

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

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

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

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

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

gbazin's avatar
 
gbazin committed
214 215 216 217 218 219 220 221
    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
222
        goto error;
gbazin's avatar
 
gbazin committed
223 224 225
    }

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

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

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

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

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

        /* 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
305
    }
306

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

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

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

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

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

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

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

374 375 376 377 378
    /* 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
379 380 381
    if( p_aout->output.output.i_physical_channels == i_physical_channels )
    {
        if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 8,
382
                                  p_aout->output.output.i_rate, true )
ivoire's avatar
ivoire committed
383 384 385 386 387 388 389 390 391 392 393
            == 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" );
        }
    }
394

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

gbazin's avatar
 
gbazin committed
418 419 420 421 422 423 424
    /* 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,
425
                               p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
426 427
            == VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
428
            val.i_int = AOUT_VAR_2F2R;
429
            text.psz_string = _("2 Front 2 Rear");
gbazin's avatar
 
gbazin committed
430 431
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
432 433 434 435 436
            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
437 438 439 440 441 442 443
            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,
444
                           p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
445 446
        == VLC_SUCCESS )
    {
gbazin's avatar
 
gbazin committed
447
        val.i_int = AOUT_VAR_STEREO;
448
        text.psz_string = _("Stereo");
gbazin's avatar
 
gbazin committed
449
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
450 451 452 453 454 455
        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!)" );
        }
456
        else msg_Dbg( p_aout, "device supports 2 channels" );
gbazin's avatar
 
gbazin committed
457 458 459 460 461
    }

    /* Test for mono support */
    i_physical_channels = AOUT_CHAN_CENTER;
    if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 1,
462
                           p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
463 464
        == VLC_SUCCESS )
    {
gbazin's avatar
 
gbazin committed
465
        val.i_int = AOUT_VAR_MONO;
466
        text.psz_string = _("Mono");
gbazin's avatar
 
gbazin committed
467
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
468 469 470 471 472 473 474 475 476
        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;
477
        msg_Dbg( p_aout, "GetSpeakerConfig failed" );
gbazin's avatar
 
gbazin committed
478 479 480
    }
    switch( DSSPEAKER_CONFIG(ui_speaker_config) )
    {
481
    case DSSPEAKER_7POINT1:
482
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is 7.1" );
483 484
        val.i_int = AOUT_VAR_7_1;
        break;
gbazin's avatar
 
gbazin committed
485
    case DSSPEAKER_5POINT1:
486
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is 5.1" );
gbazin's avatar
 
gbazin committed
487
        val.i_int = AOUT_VAR_5_1;
gbazin's avatar
 
gbazin committed
488 489
        break;
    case DSSPEAKER_QUAD:
490
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is Quad" );
gbazin's avatar
 
gbazin committed
491
        val.i_int = AOUT_VAR_2F2R;
gbazin's avatar
 
gbazin committed
492
        break;
gbazin's avatar
 
gbazin committed
493 494
#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
495
    case DSSPEAKER_MONO:
gbazin's avatar
 
gbazin committed
496
        val.i_int = AOUT_VAR_MONO;
gbazin's avatar
 
gbazin committed
497
        break;
gbazin's avatar
 
gbazin committed
498
#endif
gbazin's avatar
 
gbazin committed
499
    case DSSPEAKER_SURROUND:
500
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is surround" );
gbazin's avatar
 
gbazin committed
501
    case DSSPEAKER_STEREO:
502
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is stereo" );
gbazin's avatar
 
gbazin committed
503
    default:
504
        /* If nothing else is found, choose stereo output */
gbazin's avatar
 
gbazin committed
505
        val.i_int = AOUT_VAR_STEREO;
gbazin's avatar
 
gbazin committed
506 507
        break;
    }
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 538

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

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

gbazin's avatar
 
gbazin committed
561 562 563 564 565 566 567 568
    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
569
    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
570
    var_SetBool( p_aout, "intf-change", true );
gbazin's avatar
 
gbazin committed
571 572
}

gbazin's avatar
 
gbazin committed
573
/*****************************************************************************
gbazin's avatar
 
gbazin committed
574 575 576
 * 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
577
 *****************************************************************************/
578
static void Play( aout_instance_t *p_aout )
gbazin's avatar
 
gbazin committed
579
{
gbazin's avatar
 
gbazin committed
580 581 582 583 584 585 586 587 588 589 590
    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 */
591
        for( int i = 0; i < FRAMES_NUM; i++ )
592 593 594 595 596
        {
            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
597 598

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

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

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

    /* kill the position notification thread, if any */
614
    if( p_sys->p_notif )
615
    {
616 617 618
        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
619

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /* 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
732 733 734
    if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
                                          GetDesktopWindow(),
                                          DSSCL_EXCLUSIVE) )
735 736 737 738
    {
        msg_Warn( p_aout, "cannot set direct sound cooperative level" );
    }

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

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

750 751 752
}

/*****************************************************************************
gbazin's avatar
 
gbazin committed
753
 * CreateDSBuffer: Creates a direct sound buffer of the required format.
754 755 756 757 758 759 760 761
 *****************************************************************************
 * 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
762
 * you have to release the current one and create another.
763
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
764 765
static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
                           int i_channels, int i_nb_channels, int i_rate,
766
                           int i_bytes_per_frame, bool b_probe )
767
{
gbazin's avatar
 
gbazin committed
768
    WAVEFORMATEXTENSIBLE waveformat;
769
    DSBUFFERDESC         dsbdesc;
gbazin's avatar
 
gbazin committed
770
    unsigned int         i;
771

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

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

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

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

gbazin's avatar
 
gbazin committed
810 811 812 813 814 815 816
    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;

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

820 821 822 823 824 825
    /* 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
826 827 828 829 830 831 832 833 834 835
    /* 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
836

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

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