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 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
#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 );
gbazin's avatar
 
gbazin committed
556 557 558 559 560
            if( config_GetInt( p_aout, "spdif" ) )
                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
    if( !p_aout->output.p_sys->b_playing )
    {
        aout_buffer_t *p_buffer;
583
        int i;
gbazin's avatar
 
gbazin committed
584 585 586 587 588 589 590 591

        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 */
592 593 594 595 596 597
        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
598 599

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

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

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

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

622
        vlc_thread_join( p_sys->p_notif );
623
        vlc_object_release( p_sys->p_notif );
624 625 626
    }

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

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

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

635
    free( p_aout->output.p_sys->p_device_guid );
636
    free( p_sys );
637 638
}

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

647 648
    aout_instance_t *p_aout = (aout_instance_t *)_p_aout;

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

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

    free( psz_device );
672 673 674
    return 1;
}

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

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

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

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

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

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

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

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

751 752 753
}

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

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

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

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

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

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

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

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

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

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