directx.c 41 KB
Newer Older
1
/*****************************************************************************
2
 * directx.c: Windows DirectX audio output method
3 4
 *****************************************************************************
 * Copyright (C) 2001 VideoLAN
5
 * $Id$
6
 *
7
 * Authors: Gildas Bazin <gbazin@videolan.org>
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 *
 * 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
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <errno.h>                                                 /* ENOMEM */
#include <fcntl.h>                                       /* open(), O_WRONLY */
#include <string.h>                                            /* strerror() */

#include <stdlib.h>                            /* calloc(), malloc(), free() */

#include <vlc/vlc.h>
#include <vlc/aout.h>
gbazin's avatar
 
gbazin committed
35
#include "aout_internal.h"
36

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

gbazin's avatar
 
gbazin committed
41
#define FRAME_SIZE 2048              /* The size is in samples, not in bytes */
gbazin's avatar
 
gbazin committed
42
#define FRAMES_NUM 8
gbazin's avatar
 
gbazin committed
43 44 45 46

/* frame buffer status */
#define FRAME_QUEUED 0
#define FRAME_EMPTY 1
gbazin's avatar
 
gbazin committed
47

48 49 50 51 52 53 54 55
/*****************************************************************************
 * DirectSound GUIDs.
 * Defining them here allows us to get rid of the dxguid library during
 * the linking stage.
 *****************************************************************************/
#include <initguid.h>
DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);

gbazin's avatar
 
gbazin committed
56 57 58 59 60 61 62
/*****************************************************************************
 * Useful macros
 *****************************************************************************/
#ifndef WAVE_FORMAT_IEEE_FLOAT
#   define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif

gbazin's avatar
 
gbazin committed
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
#ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
#   define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
#endif

#ifndef WAVE_FORMAT_EXTENSIBLE
#define  WAVE_FORMAT_EXTENSIBLE   0xFFFE
#endif

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

gbazin's avatar
 
gbazin committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
#ifndef DSSPEAKER_HEADPHONE
#   define DSSPEAKER_HEADPHONE         0x00000001
#endif
#ifndef DSSPEAKER_MONO
#   define DSSPEAKER_MONO              0x00000002
#endif
#ifndef DSSPEAKER_QUAD
#   define DSSPEAKER_QUAD              0x00000003
#endif
#ifndef DSSPEAKER_STEREO
#   define DSSPEAKER_STEREO            0x00000004
#endif
#ifndef DSSPEAKER_SURROUND
#   define DSSPEAKER_SURROUND          0x00000005
#endif
#ifndef DSSPEAKER_5POINT1
#   define DSSPEAKER_5POINT1           0x00000006
#endif

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

gbazin's avatar
 
gbazin committed
126 127 128
DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_PCM, WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
DEFINE_GUID( _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF, WAVE_FORMAT_DOLBY_AC3_SPDIF, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
gbazin's avatar
 
gbazin committed
129

130 131 132 133 134 135 136
/*****************************************************************************
 * notification_thread_t: DirectX event thread
 *****************************************************************************/
typedef struct notification_thread_t
{
    VLC_COMMON_MEMBERS

gbazin's avatar
 
gbazin committed
137
    aout_instance_t * p_aout;
gbazin's avatar
 
gbazin committed
138 139 140 141 142
    int i_frame_status[FRAMES_NUM];           /* status of each frame buffer */
    DSBPOSITIONNOTIFY p_events[FRAMES_NUM];      /* play notification events */
    int i_frame_size;                         /* Size in bytes of one frame */

    mtime_t start_date;
143 144 145 146 147 148 149 150 151 152 153

} 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
154
    HINSTANCE           hdsound_dll;      /* handle of the opened dsound dll */
155 156 157 158 159 160
    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) */

    LPDIRECTSOUNDNOTIFY p_dsnotify;         /* the position notify interface */
gbazin's avatar
 
gbazin committed
161
    notification_thread_t *p_notif;                  /* DirectSoundThread id */
162

gbazin's avatar
 
gbazin committed
163
    int b_playing;                                         /* playing status */
164

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

gbazin's avatar
 
gbazin committed
167
    vlc_bool_t b_chan_reorder;              /* do we need channel reordering */
168
    int pi_chan_table[AOUT_CHAN_MAX];
gbazin's avatar
 
gbazin committed
169
    uint32_t i_channel_mask;
170 171
    uint32_t i_bits_per_sample;
    uint32_t i_channels;
172 173
};

174
static const uint32_t pi_channels_src[] =
gbazin's avatar
 
gbazin committed
175
    { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
176
      AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
gbazin's avatar
 
gbazin committed
177
      AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
178 179 180 181 182 183
      AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 };
static const uint32_t pi_channels_in[] =
    { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
      SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT,
      SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
      SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY, 0 };
gbazin's avatar
 
gbazin committed
184 185
static const uint32_t pi_channels_out[] =
    { SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
186
      SPEAKER_FRONT_CENTER, SPEAKER_LOW_FREQUENCY,
gbazin's avatar
 
gbazin committed
187
      SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
188
      SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT, 0 };
gbazin's avatar
 
gbazin committed
189

190 191 192
/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
193 194 195
static int  OpenAudio  ( vlc_object_t * );
static void CloseAudio ( vlc_object_t * );
static void Play       ( aout_instance_t * );
196 197

/* local functions */
gbazin's avatar
 
gbazin committed
198 199 200 201 202 203 204 205
static void Probe             ( aout_instance_t * );
static int  InitDirectSound   ( aout_instance_t * );
static int  CreateDSBuffer    ( aout_instance_t *, int, int, int, int, int, vlc_bool_t );
static int  CreateDSBufferPCM ( aout_instance_t *, int*, int, int, int, vlc_bool_t );
static void DestroyDSBuffer   ( aout_instance_t * );
static void DirectSoundThread ( notification_thread_t * );
static int  FillBuffer        ( aout_instance_t *, int, aout_buffer_t * );

gbazin's avatar
 
gbazin committed
206 207 208 209
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
vlc_module_begin();
210
    set_description( _("DirectX audio output") );
211
    set_shortname( "DirectX" );
gbazin's avatar
 
gbazin committed
212
    set_capability( "audio output", 100 );
zorglub's avatar
zorglub committed
213 214
    set_category( CAT_AUDIO );
    set_subcategory( SUBCAT_AUDIO_AOUT );
gbazin's avatar
 
gbazin committed
215 216 217 218
    add_shortcut( "directx" );
    set_callbacks( OpenAudio, CloseAudio );
vlc_module_end();

219 220 221 222 223
/*****************************************************************************
 * OpenAudio: open the audio device
 *****************************************************************************
 * This function opens and setups Direct Sound.
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
224
static int OpenAudio( vlc_object_t *p_this )
225
{
gbazin's avatar
 
gbazin committed
226
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
gbazin's avatar
 
gbazin committed
227
    vlc_value_t val;
gbazin's avatar
 
gbazin committed
228
    int i;
229

gbazin's avatar
 
gbazin committed
230
    msg_Dbg( p_aout, "OpenAudio" );
231 232

   /* Allocate structure */
gbazin's avatar
 
gbazin committed
233 234
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->output.p_sys == NULL )
235 236
    {
        msg_Err( p_aout, "out of memory" );
237
        return VLC_EGENERIC;
238 239 240
    }

    /* Initialize some variables */
gbazin's avatar
 
gbazin committed
241 242 243 244
    p_aout->output.p_sys->p_dsobject = NULL;
    p_aout->output.p_sys->p_dsbuffer = NULL;
    p_aout->output.p_sys->p_dsnotify = NULL;
    p_aout->output.p_sys->p_notif = NULL;
gbazin's avatar
 
gbazin committed
245
    p_aout->output.p_sys->b_playing = 0;
gbazin's avatar
 
gbazin committed
246 247

    p_aout->output.pf_play = Play;
248
    aout_VolumeSoftInit( p_aout );
249 250

    /* Initialise DirectSound */
gbazin's avatar
 
gbazin committed
251
    if( InitDirectSound( p_aout ) )
252
    {
gbazin's avatar
 
gbazin committed
253 254
        msg_Err( p_aout, "cannot initialize DirectSound" );
        goto error;
255 256
    }

gbazin's avatar
 
gbazin committed
257 258 259 260 261 262 263 264
    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
265
        goto error;
gbazin's avatar
 
gbazin committed
266 267
    }

gbazin's avatar
 
gbazin committed
268 269 270 271 272
    /* 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;

gbazin's avatar
 
gbazin committed
273
    /* Then create the notification events */
gbazin's avatar
 
gbazin committed
274 275 276
    for( i = 0; i < FRAMES_NUM; i++ )
        p_aout->output.p_sys->p_notif->p_events[i].hEventNotify =
            CreateEvent( NULL, FALSE, FALSE, NULL );
277

gbazin's avatar
 
gbazin committed
278
    /* Open the device */
gbazin's avatar
 
gbazin committed
279
    if( val.i_int == AOUT_VAR_SPDIF )
280
    {
gbazin's avatar
 
gbazin committed
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
        p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');

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

        if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
                            p_aout->output.output.i_physical_channels,
                            aout_FormatNbChannels( &p_aout->output.output ),
                            p_aout->output.output.i_rate,
                            p_aout->output.p_sys->i_frame_size, VLC_FALSE )
            != VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
297
            msg_Err( p_aout, "cannot open directx audio device" );
gbazin's avatar
 
gbazin committed
298 299 300 301 302 303 304 305
            free( p_aout->output.p_sys );
            return VLC_EGENERIC;
        }

        aout_VolumeNoneInit( p_aout );
    }
    else
    {
gbazin's avatar
 
gbazin committed
306
        if( val.i_int == AOUT_VAR_5_1 )
gbazin's avatar
 
gbazin committed
307 308 309 310 311 312
        {
            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;
        }
313 314 315 316 317 318
        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
319
        else if( val.i_int == AOUT_VAR_2F2R )
gbazin's avatar
 
gbazin committed
320 321 322 323 324
        {
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
                   | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
        }
gbazin's avatar
 
gbazin committed
325
        else if( val.i_int == AOUT_VAR_MONO )
gbazin's avatar
 
gbazin committed
326 327 328 329 330 331 332 333
        {
            p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
        }
        else
        {
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
        }
334

gbazin's avatar
 
gbazin committed
335 336 337 338 339
        if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format,
                               p_aout->output.output.i_physical_channels,
                               aout_FormatNbChannels( &p_aout->output.output ),
                               p_aout->output.output.i_rate, VLC_FALSE )
            != VLC_SUCCESS )
gbazin's avatar
 
gbazin committed
340
        {
gbazin's avatar
 
gbazin committed
341
            msg_Err( p_aout, "cannot open directx audio device" );
gbazin's avatar
 
gbazin committed
342 343
            free( p_aout->output.p_sys );
            return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
344
        }
gbazin's avatar
 
gbazin committed
345 346 347 348 349 350 351 352

        /* Calculate the frame size in bytes */
        p_aout->output.i_nb_samples = FRAME_SIZE;
        aout_FormatPrepare( &p_aout->output.output );
        p_aout->output.p_sys->i_frame_size =
            FRAME_SIZE * p_aout->output.output.i_bytes_per_frame;

        aout_VolumeSoftInit( p_aout );
gbazin's avatar
 
gbazin committed
353
    }
354

Christophe Massiot's avatar
Christophe Massiot committed
355 356 357 358
    /* 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
359
                           DirectSoundThread,
gbazin's avatar
 
gbazin committed
360
                           VLC_THREAD_PRIORITY_HIGHEST, VLC_FALSE ) )
Christophe Massiot's avatar
Christophe Massiot committed
361 362 363 364 365 366 367
    {
        msg_Err( p_aout, "cannot create DirectSoundThread" );
        goto error;
    }

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

gbazin's avatar
 
gbazin committed
368
    return VLC_SUCCESS;
Christophe Massiot's avatar
Christophe Massiot committed
369 370

 error:
gbazin's avatar
 
gbazin committed
371
    CloseAudio( VLC_OBJECT(p_aout) );
372
    return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
373 374
}

gbazin's avatar
 
gbazin committed
375 376 377 378 379
/*****************************************************************************
 * Probe: probe the audio device for available formats and channels
 *****************************************************************************/
static void Probe( aout_instance_t * p_aout )
{
gbazin's avatar
 
gbazin committed
380
    vlc_value_t val, text;
gbazin's avatar
 
gbazin committed
381 382 383 384
    int i_format;
    unsigned int i_physical_channels;
    DWORD ui_speaker_config;

gbazin's avatar
 
gbazin committed
385
    var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
386
    text.psz_string = _("Audio Device");
gbazin's avatar
 
gbazin committed
387
    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
gbazin's avatar
 
gbazin committed
388 389 390 391 392 393 394 395 396 397 398

    /* 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,
                               p_aout->output.output.i_rate, VLC_TRUE )
            == VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
399 400 401 402
            val.i_int = AOUT_VAR_5_1;
            text.psz_string = N_("5.1");
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
403 404 405 406
            msg_Dbg( p_aout, "device supports 5.1 channels" );
        }
    }

407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
    /* 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,
                               p_aout->output.output.i_rate, VLC_TRUE )
            == VLC_SUCCESS )
        {
            val.i_int = AOUT_VAR_3F2R;
            text.psz_string = N_("3 Front 2 Rear");
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
            msg_Dbg( p_aout, "device supports 5 channels" );
        }
    }

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

    /* Test for mono support */
    i_physical_channels = AOUT_CHAN_CENTER;
    if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 1,
                           p_aout->output.output.i_rate, VLC_TRUE )
        == VLC_SUCCESS )
    {
gbazin's avatar
 
gbazin committed
462 463 464
        val.i_int = AOUT_VAR_MONO;
        text.psz_string = N_("Mono");
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
465 466 467 468 469 470 471 472 473 474 475 476 477
        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;
    }
    switch( DSSPEAKER_CONFIG(ui_speaker_config) )
    {
    case DSSPEAKER_5POINT1:
gbazin's avatar
 
gbazin committed
478
        val.i_int = AOUT_VAR_5_1;
gbazin's avatar
 
gbazin committed
479 480
        break;
    case DSSPEAKER_QUAD:
gbazin's avatar
 
gbazin committed
481
        val.i_int = AOUT_VAR_2F2R;
gbazin's avatar
 
gbazin committed
482
        break;
gbazin's avatar
 
gbazin committed
483 484
#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
485
    case DSSPEAKER_MONO:
gbazin's avatar
 
gbazin committed
486
        val.i_int = AOUT_VAR_MONO;
gbazin's avatar
 
gbazin committed
487
        break;
gbazin's avatar
 
gbazin committed
488
#endif
gbazin's avatar
 
gbazin committed
489 490 491
    case DSSPEAKER_SURROUND:
    case DSSPEAKER_STEREO:
    default:
gbazin's avatar
 
gbazin committed
492
        val.i_int = AOUT_VAR_STEREO;
gbazin's avatar
 
gbazin committed
493 494 495 496
        break;
    }
    var_Set( p_aout, "audio-device", val );

gbazin's avatar
 
gbazin committed
497 498 499 500 501 502 503 504 505 506 507
    /* Test for SPDIF support */
    if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
    {
        if( CreateDSBuffer( p_aout, VLC_FOURCC('s','p','d','i'),
                            p_aout->output.output.i_physical_channels,
                            aout_FormatNbChannels( &p_aout->output.output ),
                            p_aout->output.output.i_rate,
                            AOUT_SPDIF_SIZE, VLC_TRUE )
            == VLC_SUCCESS )
        {
            msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
gbazin's avatar
 
gbazin committed
508 509 510 511
            val.i_int = AOUT_VAR_SPDIF;
            text.psz_string = N_("A/52 over S/PDIF");
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
512 513 514 515 516
            if( config_GetInt( p_aout, "spdif" ) )
                var_Set( p_aout, "audio-device", val );
        }
    }

gbazin's avatar
 
gbazin committed
517 518 519 520 521 522 523 524
    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
525 526 527 528 529 530
    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );

    val.b_bool = VLC_TRUE;
    var_Set( p_aout, "intf-change", val );
}

gbazin's avatar
 
gbazin committed
531
/*****************************************************************************
gbazin's avatar
 
gbazin committed
532 533 534
 * 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
535
 *****************************************************************************/
536
static void Play( aout_instance_t *p_aout )
gbazin's avatar
 
gbazin committed
537
{
gbazin's avatar
 
gbazin committed
538 539 540 541 542 543 544 545 546 547 548 549
    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 */
        p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
gbazin's avatar
 
gbazin committed
550
        FillBuffer( p_aout, 0, p_buffer );
gbazin's avatar
 
gbazin committed
551 552 553 554

        /* wake up the audio output thread */
        SetEvent( p_aout->output.p_sys->p_notif->p_events[0].hEventNotify );
    }
555 556 557 558 559
}

/*****************************************************************************
 * CloseAudio: close the audio device
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
560
static void CloseAudio( vlc_object_t *p_this )
561
{
gbazin's avatar
 
gbazin committed
562
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
563
    aout_sys_t *p_sys = p_aout->output.p_sys;
564

gbazin's avatar
 
gbazin committed
565
    msg_Dbg( p_aout, "CloseAudio" );
566 567

    /* kill the position notification thread, if any */
568
    if( p_sys->p_notif )
569
    {
570 571
        vlc_object_detach( p_sys->p_notif );
        if( p_sys->p_notif->b_thread )
gbazin's avatar
 
gbazin committed
572
        {
573
            p_sys->p_notif->b_die = 1;
gbazin's avatar
 
gbazin committed
574

575
            if( !p_sys->b_playing )
gbazin's avatar
 
gbazin committed
576
                /* wake up the audio thread */
577
                SetEvent( p_sys->p_notif->p_events[0].hEventNotify );
gbazin's avatar
 
gbazin committed
578

579
            vlc_thread_join( p_sys->p_notif );
gbazin's avatar
 
gbazin committed
580
        }
581
        vlc_object_destroy( p_sys->p_notif );
582 583 584
    }

    /* release the secondary buffer */
gbazin's avatar
 
gbazin committed
585
    DestroyDSBuffer( p_aout );
586 587

    /* finally release the DirectSound object */
588
    if( p_sys->p_dsobject ) IDirectSound_Release( p_sys->p_dsobject );
589 590
    
    /* free DSOUND.DLL */
591
    if( p_sys->hdsound_dll ) FreeLibrary( p_sys->hdsound_dll );
gbazin's avatar
 
gbazin committed
592

593
    free( p_sys );
594 595 596
}

/*****************************************************************************
gbazin's avatar
 
gbazin committed
597
 * InitDirectSound: handle all the gory details of DirectSound initialisation
598
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
599
static int InitDirectSound( aout_instance_t *p_aout )
600 601 602
{
    HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);

gbazin's avatar
 
gbazin committed
603 604
    p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
    if( p_aout->output.p_sys->hdsound_dll == NULL )
605
    {
gbazin's avatar
 
gbazin committed
606 607
        msg_Warn( p_aout, "cannot open DSOUND.DLL" );
        goto error;
608 609
    }

gbazin's avatar
 
gbazin committed
610 611 612
    OurDirectSoundCreate = (void *)GetProcAddress(
                                           p_aout->output.p_sys->hdsound_dll,
                                           "DirectSoundCreate" );
613 614
    if( OurDirectSoundCreate == NULL )
    {
gbazin's avatar
 
gbazin committed
615 616
        msg_Warn( p_aout, "GetProcAddress FAILED" );
        goto error;
617 618 619
    }

    /* Create the direct sound object */
gbazin's avatar
 
gbazin committed
620 621
    if FAILED( OurDirectSoundCreate( NULL, &p_aout->output.p_sys->p_dsobject,
                                     NULL ) )
622 623
    {
        msg_Warn( p_aout, "cannot create a direct sound device" );
gbazin's avatar
 
gbazin committed
624
        goto error;
625 626 627 628 629 630 631 632 633 634 635 636
    }

    /* 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
637 638 639
    if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
                                          GetDesktopWindow(),
                                          DSSCL_EXCLUSIVE) )
640 641 642 643
    {
        msg_Warn( p_aout, "cannot set direct sound cooperative level" );
    }

gbazin's avatar
 
gbazin committed
644
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
645 646 647 648 649 650 651 652

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

655 656 657
}

/*****************************************************************************
gbazin's avatar
 
gbazin committed
658
 * CreateDSBuffer: Creates a direct sound buffer of the required format.
659 660 661 662 663 664 665 666
 *****************************************************************************
 * 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
667
 * you have to release the current one and create another.
668
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
669 670 671
static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
                           int i_channels, int i_nb_channels, int i_rate,
                           int i_bytes_per_frame, vlc_bool_t b_probe )
672
{
gbazin's avatar
 
gbazin committed
673
    WAVEFORMATEXTENSIBLE waveformat;
674
    DSBUFFERDESC         dsbdesc;
gbazin's avatar
 
gbazin committed
675
    unsigned int         i;
676

gbazin's avatar
 
gbazin committed
677 678
    /* First set the sound buffer format */
    waveformat.dwChannelMask = 0;
679
    for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ )
gbazin's avatar
 
gbazin committed
680
    {
681 682
        if( i_channels & pi_channels_src[i] )
            waveformat.dwChannelMask |= pi_channels_in[i];
683
    }
gbazin's avatar
 
gbazin committed
684

gbazin's avatar
 
gbazin committed
685
    switch( i_format )
gbazin's avatar
 
gbazin committed
686
    {
gbazin's avatar
 
gbazin committed
687 688
    case VLC_FOURCC('s','p','d','i'):
        i_nb_channels = 2;
gbazin's avatar
 
gbazin committed
689 690
        /* To prevent channel re-ordering */
        waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
gbazin's avatar
 
gbazin committed
691 692 693 694
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
gbazin's avatar
 
gbazin committed
695
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
gbazin's avatar
 
gbazin committed
696
        break;
gbazin's avatar
 
gbazin committed
697

gbazin's avatar
 
gbazin committed
698
    case VLC_FOURCC('f','l','3','2'):
gbazin's avatar
 
gbazin committed
699 700 701 702
        waveformat.Format.wBitsPerSample = sizeof(float) * 8;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
gbazin's avatar
 
gbazin committed
703
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
gbazin's avatar
 
gbazin committed
704 705 706 707 708 709 710
        break;

    case VLC_FOURCC('s','1','6','l'):
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
gbazin's avatar
 
gbazin committed
711
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
gbazin's avatar
 
gbazin committed
712 713
        break;
    }
714

gbazin's avatar
 
gbazin committed
715 716 717 718 719 720 721
    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;

722 723 724
    p_aout->output.p_sys->i_bits_per_sample = waveformat.Format.wBitsPerSample;
    p_aout->output.p_sys->i_channels = i_nb_channels;

725 726 727 728 729 730 731
    /* Then fill in the direct sound descriptor */
    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
    dsbdesc.dwSize = sizeof(DSBUFFERDESC);
    dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
                    | DSBCAPS_CTRLPOSITIONNOTIFY     /* We need notification */
                    | DSBCAPS_GLOBALFOCUS;      /* Allows background playing */

gbazin's avatar
 
gbazin committed
732 733 734 735 736 737 738 739 740 741
    /* 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
742

743 744 745
        /* Needed for 5.1 on emu101k */
        dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
    }
gbazin's avatar
 
gbazin committed
746 747 748 749 750 751 752 753

    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) )
    {
754 755 756 757 758
        if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE )
        {
            /* Try without DSBCAPS_LOCHARDWARE */
            dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
            if FAILED( IDirectSound_CreateSoundBuffer(
gbazin's avatar
 
gbazin committed
759 760
                   p_aout->output.p_sys->p_dsobject, &dsbdesc,
                   &p_aout->output.p_sys->p_dsbuffer, NULL) )
761 762 763 764 765 766 767
            {
                return VLC_EGENERIC;
            }
            if( !b_probe )
                msg_Dbg( p_aout, "couldn't use hardware sound buffer" );
        }
        else
gbazin's avatar
 
gbazin committed
768 769 770
        {
            return VLC_EGENERIC;
        }
771 772
    }

gbazin's avatar
 
gbazin committed
773 774 775 776 777 778 779
    /* 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
780

gbazin's avatar
 
gbazin committed
781 782
    /* Backup the size of a frame */
    p_aout->output.p_sys->p_notif->i_frame_size = i_bytes_per_frame;
783 784 785

    /* Now the secondary buffer is created, we need to setup its position
     * notification */
gbazin's avatar
 
gbazin committed
786 787 788 789 790 791 792
    for( i = 0; i < FRAMES_NUM; i++ )
    {
        p_aout->output.p_sys->p_notif->p_events[i].dwOffset = i *
            p_aout->output.p_sys->p_notif->i_frame_size;

        p_aout->output.p_sys->p_notif->i_frame_status[i] = FRAME_EMPTY;
    }
793 794

    /* Get the IDirectSoundNotify interface */
gbazin's avatar
 
gbazin committed
795 796 797 798
    if FAILED( IDirectSoundBuffer_QueryInterface(
                                p_aout->output.p_sys->p_dsbuffer,
                                &IID_IDirectSoundNotify,
                                (LPVOID *)&p_aout->output.p_sys->p_dsnotify ) )
799
    {
gbazin's avatar
 
gbazin committed
800
        msg_Err( p_aout, "cannot get IDirectSoundNotify interface" );
gbazin's avatar
 
gbazin committed
801
        goto error;
802
    }
gbazin's avatar
 
gbazin committed
803

804
    if FAILED( IDirectSoundNotify_SetNotificationPositions(
gbazin's avatar
 
gbazin committed
805 806
                                    p_aout->output.p_sys->p_dsnotify,
                                    FRAMES_NUM,
gbazin's avatar
 
gbazin committed
807
                                    p_aout->output.p_sys->p_notif->p_events ) )
808
    {
gbazin's avatar
 
gbazin committed
809
        msg_Err( p_aout, "cannot set position notification" );
gbazin's avatar
 
gbazin committed
810
        goto error;
811
    }
gbazin's avatar
 
gbazin committed
812

gbazin's avatar
 
gbazin committed
813
    p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask;
814 815 816 817 818 819 820 821 822 823

    p_aout->output.p_sys->b_chan_reorder =
        aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
                                  waveformat.dwChannelMask, i_nb_channels,
                                  p_aout->output.p_sys->pi_chan_table );

    if( p_aout->output.p_sys->b_chan_reorder )
    {
        msg_Dbg( p_aout, "channel reordering needed" );
    }
gbazin's avatar
 
gbazin committed
824

gbazin's avatar
 
gbazin committed
825
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
826 827

 error:
gbazin's avatar
 
gbazin committed
828
    DestroyDSBuffer( p_aout );
829
    return VLC_EGENERIC;
830 831 832
}

/*****************************************************************************
gbazin's avatar
 
gbazin committed
833 834 835 836 837 838 839 840 841
 * CreateDSBufferPCM: creates a PCM direct sound buffer.
 *****************************************************************************
 * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by
 * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer.
 ****************************************************************************/
static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format,
                              int i_channels, int i_nb_channels, int i_rate,
                              vlc_bool_t b_probe )
{
gbazin's avatar
 
gbazin committed
842 843 844 845
    /* Float32 audio samples are not supported for 5.1 output on the emu101k */

    if( i_nb_channels > 2 ||
        CreateDSBuffer( p_aout, VLC_FOURCC('f','l','3','2'),
gbazin's avatar
 
gbazin committed
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
                        i_channels, i_nb_channels, i_rate,
                        FRAME_SIZE * 4 * i_nb_channels, b_probe )
        != VLC_SUCCESS )
    {
        if ( CreateDSBuffer( p_aout, VLC_FOURCC('s','1','6','l'),
                             i_channels, i_nb_channels, i_rate,
                             FRAME_SIZE * 2 * i_nb_channels, b_probe )
             != VLC_SUCCESS )
        {
            return VLC_EGENERIC;
        }
        else
        {
            *i_format = VLC_FOURCC('s','1','6','l');
            return VLC_SUCCESS;
        }
    }
    else
    {
        *i_format = VLC_FOURCC('f','l','3','2');
        return VLC_SUCCESS;
    }
}

/*****************************************************************************
 * DestroyDSBuffer
872
 *****************************************************************************
gbazin's avatar
 
gbazin committed
873
 * This function destroys the secondary buffer.
874
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
875
static void DestroyDSBuffer( aout_instance_t *p_aout )
876
{
gbazin's avatar
 
gbazin committed
877
    if( p_aout->output.p_sys->p_dsnotify )
878
    {
gbazin's avatar
 
gbazin committed
879 880
        IDirectSoundNotify_Release( p_aout->output.p_sys->p_dsnotify );
        p_aout->output.p_sys->p_dsnotify = NULL;
881 882
    }

gbazin's avatar
 
gbazin committed
883
    if( p_aout->output.p_sys->p_dsbuffer )
884
    {
gbazin's avatar
 
gbazin committed
885 886
        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
        p_aout->output.p_sys->p_dsbuffer = NULL;
887 888 889
    }
}

gbazin's avatar
 
gbazin committed
890
/*****************************************************************************
gbazin's avatar
 
gbazin committed
891
 * FillBuffer: Fill in one of the direct sound frame buffers.
gbazin's avatar
 
gbazin committed
892 893 894
 *****************************************************************************
 * Returns VLC_SUCCESS on success.
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
895 896
static int FillBuffer( aout_instance_t *p_aout, int i_frame,
                       aout_buffer_t *p_buffer )
gbazin's avatar
 
gbazin committed
897 898
{
    notification_thread_t *p_notif = p_aout->output.p_sys->p_notif;
899
    aout_sys_t *p_sys = p_aout->output.p_sys;
gbazin's avatar
 
gbazin committed
900 901 902 903 904 905
    void *p_write_position, *p_wrap_around;
    long l_bytes1, l_bytes2;
    HRESULT dsresult;

    /* Before copying anything, we have to lock the buffer */
    dsresult = IDirectSoundBuffer_Lock(
906
                p_sys->p_dsbuffer,                              /* DS buffer */
gbazin's avatar
 
gbazin committed
907 908 909 910 911 912 913 914 915
                i_frame * p_notif->i_frame_size,             /* Start offset */
                p_notif->i_frame_size,                    /* Number of bytes */
                &p_write_position,                  /* Address of lock start */
                &l_bytes1,       /* Count of bytes locked before wrap around */
                &p_wrap_around,            /* Buffer adress (if wrap around) */
                &l_bytes2,               /* Count of bytes after wrap around */
                0 );                                                /* Flags */
    if( dsresult == DSERR_BUFFERLOST )
    {
916
        IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
gbazin's avatar
 
gbazin committed
917
        dsresult = IDirectSoundBuffer_Lock(
918
                               p_sys->p_dsbuffer,
gbazin's avatar
 
gbazin committed
919 920 921 922 923 924 925 926 927 928 929
                               i_frame * p_notif->i_frame_size,
                               p_notif->i_frame_size,
                               &p_write_position,
                               &l_bytes1,
                               &p_wrap_around,
                               &l_bytes2,
                               0 );
    }
    if( dsresult != DS_OK )
    {
        msg_Warn( p_notif, "cannot lock buffer" );
gbazin's avatar
 
gbazin committed
930
        if( p_buffer ) aout_BufferFree( p_buffer );
gbazin's avatar
 
gbazin committed
931 932 933
        return VLC_EGENERIC;
    }

gbazin's avatar
 
gbazin committed
934 935 936 937 938
    if( p_buffer == NULL )
    {
        memset( p_write_position, 0, l_bytes1 );
    }
    else
gbazin's avatar
 
gbazin committed
939
    {
940 941 942 943 944 945 946 947
        if( p_sys->b_chan_reorder )
        {
            /* Do the channel reordering here */
            aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_nb_bytes,
                                 p_sys->i_channels, p_sys->pi_chan_table,
                                 p_sys->i_bits_per_sample );
        }

gbazin's avatar
 
gbazin committed
948 949
        p_aout->p_vlc->pf_memcpy( p_write_position, p_buffer->p_buffer,
                                  l_bytes1 );
gbazin's avatar
 
gbazin committed
950 951
        aout_BufferFree( p_buffer );
    }
gbazin's avatar
 
gbazin committed
952 953

    /* Now the data has been copied, unlock the buffer */
954
    IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
gbazin's avatar
 
gbazin committed
955 956 957 958 959
                               p_wrap_around, l_bytes2 );

    return VLC_SUCCESS;
}

960 961 962
/*****************************************************************************
 * DirectSoundThread: this thread will capture play notification events. 
 *****************************************************************************
gbazin's avatar
 
gbazin committed
963 964
 * We use this thread to emulate a callback mechanism. The thread probes for
 * event notification and fills up the DS secondary buffer when needed.
965 966 967
 *****************************************************************************/
static void DirectSoundThread( notification_thread_t *p_notif )
{
gbazin's avatar
 
gbazin committed
968
    HANDLE  notification_events[FRAMES_NUM];
969
    HRESULT dsresult;
gbazin's avatar
 
gbazin committed
970
    aout_instance_t *p_aout = p_notif->p_aout;
gbazin's avatar
 
gbazin committed
971 972
    int i, i_which_frame, i_last_frame, i_next_frame;
    mtime_t mtime;
gbazin's avatar
 
gbazin committed
973
    vlc_bool_t b_sleek;
974

gbazin's avatar
 
gbazin committed
975 976
    for( i = 0; i < FRAMES_NUM; i++ )
        notification_events[i] = p_notif->p_events[i].hEventNotify;
977

gbazin's avatar
 
gbazin committed
978 979 980
    /* We don't want any resampling when using S/PDIF output */
    b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i');

981 982 983 984 985
    /* Tell the main thread that we are ready */
    vlc_thread_ready( p_notif );

    msg_Dbg( p_notif, "DirectSoundThread ready" );

gbazin's avatar
 
gbazin committed
986 987
    /* Wait here until Play() is called */
    WaitForSingleObject( notification_events[0], INFINITE );
gbazin's avatar
 
gbazin committed
988

gbazin's avatar
 
gbazin committed
989 990 991
    if( !p_notif->b_die )
    {
        mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 );
992

gbazin's avatar
 
gbazin committed
993 994 995 996 997
        /* start playing the buffer */
        dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer,
                                        0,                         /* Unused */
                                        0,                         /* Unused */
                                        DSBPLAY_LOOPING );          /* Flags */
gbazin's avatar
 
gbazin committed
998
        if( dsresult == DSERR_BUFFERLOST )
999
        {
gbazin's avatar
 
gbazin committed
1000
            IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
gbazin's avatar
 
gbazin committed
1001 1002 1003 1004 1005
            dsresult = IDirectSoundBuffer_Play(
                                            p_aout->output.p_sys->p_dsbuffer,
                                            0,                     /* Unused */
                                            0,                     /* Unused */
                                            DSBPLAY_LOOPING );      /* Flags */
1006
        }
gbazin's avatar
 
gbazin committed
1007
        if( dsresult != DS_OK )
1008
        {
gbazin's avatar