directx.c 45.1 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
#define FRAME_SIZE ((int)p_aout->output.output.i_rate/20) /* Size in samples */
gbazin's avatar
 
gbazin committed
40

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

48 49 50
    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
51 52

    mtime_t start_date;
53
    HANDLE event;
54 55 56 57 58 59 60 61 62 63 64

} 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
65
    HINSTANCE           hdsound_dll;      /* handle of the opened dsound dll */
66

67
    char *              psz_device;              /* user defined device name */
68 69
    LPGUID              p_device_guid;

70 71 72 73 74
    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
75
    notification_thread_t *p_notif;                  /* DirectSoundThread id */
76

77
    int      b_playing;                                    /* playing status */
78

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

81
    int      i_speaker_setup;                      /* Speaker setup override */
82

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

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

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

106 107 108
static int ReloadDirectXDevices( vlc_object_t *, char const *,
                                vlc_value_t, vlc_value_t, void * );

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

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

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

125 126 127 128 129 130
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 )
131
    add_shortcut( "directx", "directsound" )
132

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

144 145
    set_callbacks( OpenAudio, CloseAudio )
vlc_module_end ()
gbazin's avatar
 
gbazin committed
146

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

    const char * const * ppsz_compare = speaker_list;
160

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
161
    msg_Dbg( p_aout, "Opening DirectSound Audio Output" );
162 163

   /* Allocate structure */
gbazin's avatar
 
gbazin committed
164 165
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->output.p_sys == NULL )
166
        return VLC_ENOMEM;
167 168

    /* Initialize some variables */
gbazin's avatar
 
gbazin committed
169 170 171
    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
172
    p_aout->output.p_sys->b_playing = 0;
gbazin's avatar
 
gbazin committed
173 174

    p_aout->output.pf_play = Play;
175
    aout_VolumeSoftInit( p_aout );
176

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

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

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

200 201
    p_aout->output.p_sys->p_device_guid = 0;

202
    /* Initialise DirectSound */
gbazin's avatar
 
gbazin committed
203
    if( InitDirectSound( p_aout ) )
204
    {
gbazin's avatar
 
gbazin committed
205 206
        msg_Err( p_aout, "cannot initialize DirectSound" );
        goto error;
207 208
    }

gbazin's avatar
 
gbazin committed
209 210 211 212 213 214 215 216
    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
217
        goto error;
gbazin's avatar
 
gbazin committed
218 219 220
    }

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

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

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

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

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

        /* 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
300
    }
301

302 303 304 305 306 307 308 309 310
    /* 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
311 312 313
    /* then launch the notification thread */
    msg_Dbg( p_aout, "creating DirectSoundThread" );
    if( vlc_thread_create( p_aout->output.p_sys->p_notif,
gbazin's avatar
 
gbazin committed
314
                           DirectSoundThread,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
315
                           VLC_THREAD_PRIORITY_HIGHEST ) )
Christophe Massiot's avatar
Christophe Massiot committed
316 317
    {
        msg_Err( p_aout, "cannot create DirectSoundThread" );
318
        CloseHandle( p_aout->output.p_sys->p_notif->event );
319
        vlc_object_release( p_aout->output.p_sys->p_notif );
320
        p_aout->output.p_sys->p_notif = NULL;
Christophe Massiot's avatar
Christophe Massiot committed
321 322 323 324 325
        goto error;
    }

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

gbazin's avatar
 
gbazin committed
326
    return VLC_SUCCESS;
Christophe Massiot's avatar
Christophe Massiot committed
327 328

 error:
gbazin's avatar
 
gbazin committed
329
    CloseAudio( VLC_OBJECT(p_aout) );
330
    return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
331 332
}

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

gbazin's avatar
 
gbazin committed
344
    var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
345
    text.psz_string = _("Audio Device");
gbazin's avatar
 
gbazin committed
346
    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
gbazin's avatar
 
gbazin committed
347 348 349 350 351 352 353 354

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

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

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

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

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

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

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

gbazin's avatar
 
gbazin committed
555 556 557 558 559 560 561 562
    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
563
    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
564
    var_TriggerCallback( p_aout, "intf-change" );
gbazin's avatar
 
gbazin committed
565 566
}

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

        /* wake up the audio output thread */
593
        SetEvent( p_aout->output.p_sys->p_notif->event );
gbazin's avatar
 
gbazin committed
594
    }
595 596 597 598 599
}

/*****************************************************************************
 * CloseAudio: close the audio device
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
600
static void CloseAudio( vlc_object_t *p_this )
601
{
gbazin's avatar
 
gbazin committed
602
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
603
    aout_sys_t *p_sys = p_aout->output.p_sys;
604

605
    msg_Dbg( p_aout, "closing audio device" );
606 607

    /* kill the position notification thread, if any */
608
    if( p_sys->p_notif )
609
    {
610 611 612
        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
613

614
        vlc_thread_join( p_sys->p_notif );
615
        vlc_object_release( p_sys->p_notif );
616 617 618
    }

    /* release the secondary buffer */
gbazin's avatar
 
gbazin committed
619
    DestroyDSBuffer( p_aout );
620 621

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

624
    /* free DSOUND.DLL */
625
    if( p_sys->hdsound_dll ) FreeLibrary( p_sys->hdsound_dll );
gbazin's avatar
 
gbazin committed
626

627
    free( p_aout->output.p_sys->p_device_guid );
628
    free( p_sys );
629 630
}

631 632 633
/*****************************************************************************
 * CallBackDirectSoundEnum: callback to enumerate available devices
 *****************************************************************************/
634 635
static int CALLBACK CallBackDirectSoundEnum( LPGUID p_guid, LPCWSTR psz_desc,
                                             LPCWSTR psz_mod, LPVOID _p_aout )
636
{
637 638
    VLC_UNUSED( psz_mod );

639 640
    aout_instance_t *p_aout = (aout_instance_t *)_p_aout;

641 642
    char *psz_device = FromWide( psz_desc );
    msg_Dbg( p_aout, "found device: %s", psz_device );
643

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

    free( psz_device );
665
    return true;
666 667
}

668
/*****************************************************************************
gbazin's avatar
 
gbazin committed
669
 * InitDirectSound: handle all the gory details of DirectSound initialisation
670
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
671
static int InitDirectSound( aout_instance_t *p_aout )
672 673
{
    HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
674
    HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKW, LPVOID);
675

gbazin's avatar
 
gbazin committed
676 677
    p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
    if( p_aout->output.p_sys->hdsound_dll == NULL )
678
    {
gbazin's avatar
 
gbazin committed
679 680
        msg_Warn( p_aout, "cannot open DSOUND.DLL" );
        goto error;
681 682
    }

683 684 685
    OurDirectSoundCreate = (void *)
        GetProcAddress( p_aout->output.p_sys->hdsound_dll,
                        "DirectSoundCreate" );
686 687
    if( OurDirectSoundCreate == NULL )
    {
gbazin's avatar
 
gbazin committed
688 689
        msg_Warn( p_aout, "GetProcAddress FAILED" );
        goto error;
690 691
    }

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

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

    /* 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
726 727 728
    if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
                                          GetDesktopWindow(),
                                          DSSCL_EXCLUSIVE) )
729 730 731 732
    {
        msg_Warn( p_aout, "cannot set direct sound cooperative level" );
    }

gbazin's avatar
 
gbazin committed
733
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
734 735 736 737 738 739 740 741

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

744 745 746
}

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

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

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

787
    case VLC_CODEC_FL32:
gbazin's avatar
 
gbazin committed
788 789 790 791
        waveformat.Format.wBitsPerSample = sizeof(float) * 8;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
gbazin's avatar
 
gbazin committed
792
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
gbazin's avatar
 
gbazin committed
793 794
        break;

795
    case VLC_CODEC_S16L:
gbazin's avatar
 
gbazin committed
796 797 798 799
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
gbazin's avatar
 
gbazin committed
800
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
gbazin's avatar
 
gbazin committed
801 802
        break;
    }
803

gbazin's avatar
 
gbazin committed
804 805 806 807 808 809 810
    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;

811 812 813
    p_aout->output.p_sys->i_bits_per_sample = waveformat.Format.wBitsPerSample;
    p_aout->output.p_sys->i_channels = i_nb_channels;

814 815 816 817 818 819
    /* 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
820 821 822 823 824 825 826 827 828 829
    /* 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
830

831 832 833
        /* Needed for 5.1 on emu101k */
        dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
    }
gbazin's avatar
 
gbazin committed
834 835 836 837 838 839 840 841

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

gbazin's avatar
 
gbazin committed
861 862 863 864 865 866 867
    /* Stop here if we were just probing */
    if( b_probe )
    {
        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
        p_aout->output.p_sys->p_dsbuffer = NULL;
        return VLC_SUCCESS;
    }
gbazin's avatar
 
gbazin committed
868

869
    p_aout->output.p_sys->i_frame_size = i_bytes_per_frame;
gbazin's avatar
 
gbazin committed
870
    p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask;