directx.c 43.7 KB
Newer Older
1
/*****************************************************************************
2
 * directx.c: Windows DirectX audio output method
3
 *****************************************************************************
4
 * Copyright (C) 2001 the VideoLAN team
5
 * $Id$
6
 *
7
 * Authors: Gildas Bazin <gbazin@videolan.org>
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
23
24
25
26
27
28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/


29
30
31
32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
35
#include <vlc_aout.h>
36

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

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

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

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

gbazin's avatar
   
gbazin committed
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#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

88
89
90
#ifndef DSSPEAKER_DSSPEAKER_DIRECTOUT
#   define DSSPEAKER_DSSPEAKER_DIRECTOUT         0x00000000
#endif
gbazin's avatar
   
gbazin committed
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#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
109
110
111
112
113
114
115
116
117
#ifndef DSSPEAKER_7POINT1
#   define DSSPEAKER_7POINT1           0x00000007
#endif
#ifndef DSSPEAKER_7POINT1_SURROUND
#   define DSSPEAKER_7POINT1_SURROUND           0x00000008
#endif
#ifndef DSSPEAKER_7POINT1_WIDE
#   define DSSPEAKER_7POINT1_WIDE           DSSPEAKER_7POINT1
#endif
gbazin's avatar
   
gbazin committed
118

gbazin's avatar
   
gbazin committed
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#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
133
134
135
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
136

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

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

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

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

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

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

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

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

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

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

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

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

gbazin's avatar
   
gbazin committed
217
218
219
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
220
221
222
223
224
225
226
227
228
#define DEVICE_TEXT N_("Output device")
#define DEVICE_LONGTEXT N_( \
    "DirectX device number: 0 default device, 1..N device by number" \
    "(Note that the default device appears as 0 AND another number)." )
#define FLOAT_TEXT N_("Use float32 output")
#define FLOAT_LONGTEXT N_( \
    "The option allows you to enable or disable the high-quality float32 " \
    "audio output mode (which is not well supported by some soundcards)." )

gbazin's avatar
   
gbazin committed
229
vlc_module_begin();
230
    set_description( N_("DirectX audio output") );
231
    set_shortname( "DirectX" );
gbazin's avatar
   
gbazin committed
232
    set_capability( "audio output", 100 );
zorglub's avatar
zorglub committed
233
234
    set_category( CAT_AUDIO );
    set_subcategory( SUBCAT_AUDIO_AOUT );
gbazin's avatar
   
gbazin committed
235
    add_shortcut( "directx" );
236
    add_integer( "directx-audio-device", 0, NULL, DEVICE_TEXT,
237
                 DEVICE_LONGTEXT, true );
dionoea's avatar
dionoea committed
238
    add_bool( "directx-audio-float32", 0, 0, FLOAT_TEXT,
239
              FLOAT_LONGTEXT, true );
gbazin's avatar
   
gbazin committed
240
241
242
    set_callbacks( OpenAudio, CloseAudio );
vlc_module_end();

243
244
245
246
247
/*****************************************************************************
 * OpenAudio: open the audio device
 *****************************************************************************
 * This function opens and setups Direct Sound.
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
248
static int OpenAudio( vlc_object_t *p_this )
249
{
gbazin's avatar
   
gbazin committed
250
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
gbazin's avatar
   
gbazin committed
251
    vlc_value_t val;
252

gbazin's avatar
   
gbazin committed
253
    msg_Dbg( p_aout, "OpenAudio" );
254
255

   /* Allocate structure */
gbazin's avatar
   
gbazin committed
256
257
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
    if( p_aout->output.p_sys == NULL )
258
        return VLC_ENOMEM;
259
260

    /* Initialize some variables */
gbazin's avatar
   
gbazin committed
261
262
263
    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
264
    p_aout->output.p_sys->b_playing = 0;
gbazin's avatar
   
gbazin committed
265
266

    p_aout->output.pf_play = Play;
267
    aout_VolumeSoftInit( p_aout );
268

269
270
271
272
273
274
275
276
277
    /* Retrieve config values */
    var_Create( p_aout, "directx-audio-float32",
                VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Create( p_aout, "directx-audio-device",
                VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
    var_Get( p_aout, "directx-audio-device", &val );
    p_aout->output.p_sys->i_device_id = val.i_int;
    p_aout->output.p_sys->p_device_guid = 0;

278
    /* Initialise DirectSound */
gbazin's avatar
   
gbazin committed
279
    if( InitDirectSound( p_aout ) )
280
    {
gbazin's avatar
   
gbazin committed
281
282
        msg_Err( p_aout, "cannot initialize DirectSound" );
        goto error;
283
284
    }

gbazin's avatar
   
gbazin committed
285
286
287
288
289
290
291
292
    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
293
        goto error;
gbazin's avatar
   
gbazin committed
294
295
296
    }

    /* Open the device */
gbazin's avatar
   
gbazin committed
297
    if( val.i_int == AOUT_VAR_SPDIF )
298
    {
gbazin's avatar
   
gbazin committed
299
300
301
302
303
304
305
306
307
308
309
310
311
        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,
312
                            p_aout->output.p_sys->i_frame_size, false )
gbazin's avatar
   
gbazin committed
313
314
            != VLC_SUCCESS )
        {
gbazin's avatar
   
gbazin committed
315
            msg_Err( p_aout, "cannot open directx audio device" );
gbazin's avatar
   
gbazin committed
316
317
318
319
320
321
322
323
            free( p_aout->output.p_sys );
            return VLC_EGENERIC;
        }

        aout_VolumeNoneInit( p_aout );
    }
    else
    {
gbazin's avatar
   
gbazin committed
324
        if( val.i_int == AOUT_VAR_5_1 )
gbazin's avatar
   
gbazin committed
325
326
327
328
329
330
        {
            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;
        }
331
332
333
334
335
336
337
338
        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;
        }
339
340
341
342
343
344
        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
345
        else if( val.i_int == AOUT_VAR_2F2R )
gbazin's avatar
   
gbazin committed
346
347
348
349
350
        {
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
                   | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
        }
gbazin's avatar
   
gbazin committed
351
        else if( val.i_int == AOUT_VAR_MONO )
gbazin's avatar
   
gbazin committed
352
353
354
355
356
357
358
359
        {
            p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
        }
        else
        {
            p_aout->output.output.i_physical_channels
                = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
        }
360

gbazin's avatar
   
gbazin committed
361
362
363
        if( CreateDSBufferPCM( p_aout, &p_aout->output.output.i_format,
                               p_aout->output.output.i_physical_channels,
                               aout_FormatNbChannels( &p_aout->output.output ),
364
                               p_aout->output.output.i_rate, false )
gbazin's avatar
   
gbazin committed
365
            != VLC_SUCCESS )
gbazin's avatar
   
gbazin committed
366
        {
gbazin's avatar
   
gbazin committed
367
            msg_Err( p_aout, "cannot open directx audio device" );
gbazin's avatar
   
gbazin committed
368
369
            free( p_aout->output.p_sys );
            return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
370
        }
gbazin's avatar
   
gbazin committed
371
372
373
374
375

        /* 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
376
    }
377

378
379
380
381
382
383
384
385
386
    /* 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
387
388
389
390
    /* 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
391
                           DirectSoundThread,
392
                           VLC_THREAD_PRIORITY_HIGHEST, false ) )
Christophe Massiot's avatar
Christophe Massiot committed
393
394
    {
        msg_Err( p_aout, "cannot create DirectSoundThread" );
395
        CloseHandle( p_aout->output.p_sys->p_notif->event );
396
        vlc_object_release( p_aout->output.p_sys->p_notif );
397
        p_aout->output.p_sys->p_notif = NULL;
Christophe Massiot's avatar
Christophe Massiot committed
398
399
400
401
402
        goto error;
    }

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

gbazin's avatar
   
gbazin committed
403
    return VLC_SUCCESS;
Christophe Massiot's avatar
Christophe Massiot committed
404
405

 error:
gbazin's avatar
   
gbazin committed
406
    CloseAudio( VLC_OBJECT(p_aout) );
407
    return VLC_EGENERIC;
gbazin's avatar
   
gbazin committed
408
409
}

gbazin's avatar
   
gbazin committed
410
411
412
413
414
/*****************************************************************************
 * Probe: probe the audio device for available formats and channels
 *****************************************************************************/
static void Probe( aout_instance_t * p_aout )
{
gbazin's avatar
   
gbazin committed
415
    vlc_value_t val, text;
gbazin's avatar
   
gbazin committed
416
417
418
    int i_format;
    unsigned int i_physical_channels;
    DWORD ui_speaker_config;
419
    bool is_default_output_set = false;
gbazin's avatar
   
gbazin committed
420

gbazin's avatar
   
gbazin committed
421
    var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
422
    text.psz_string = _("Audio Device");
gbazin's avatar
   
gbazin committed
423
    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
gbazin's avatar
   
gbazin committed
424
425
426
427
428
429
430
431

    /* 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,
432
                               p_aout->output.output.i_rate, true )
gbazin's avatar
   
gbazin committed
433
434
            == VLC_SUCCESS )
        {
gbazin's avatar
   
gbazin committed
435
            val.i_int = AOUT_VAR_5_1;
436
            text.psz_string = "5.1";
gbazin's avatar
   
gbazin committed
437
438
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
439
440
            var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
            is_default_output_set = true;
gbazin's avatar
   
gbazin committed
441
442
443
444
            msg_Dbg( p_aout, "device supports 5.1 channels" );
        }
    }

445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
    /* Test for 7.1 support */
    i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
                             AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
                             AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT |
                             AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
       if( p_aout->output.output.i_physical_channels == i_physical_channels )
       {
           if( CreateDSBufferPCM( p_aout, &i_format, i_physical_channels, 8,
                                  p_aout->output.output.i_rate, true )
               == VLC_SUCCESS )
           {
               val.i_int = AOUT_VAR_7_1;
               text.psz_string = "7.1";
               var_Change( p_aout, "audio-device",
                           VLC_VAR_ADDCHOICE, &val, &text );
460
461
               var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
               is_default_output_set = true;
462
463
464
465
               msg_Dbg( p_aout, "device supports 7.1 channels" );
           }
       }

466
467
468
469
470
471
472
    /* 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,
473
                               p_aout->output.output.i_rate, true )
474
475
476
477
478
479
            == 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 );
480
481
482
483
484
            if(!is_default_output_set)
            {
                var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
                is_default_output_set = true;
            }
485
486
487
488
            msg_Dbg( p_aout, "device supports 5 channels" );
        }
    }

gbazin's avatar
   
gbazin committed
489
490
491
492
493
494
495
    /* 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,
496
                               p_aout->output.output.i_rate, true )
gbazin's avatar
   
gbazin committed
497
498
            == VLC_SUCCESS )
        {
gbazin's avatar
   
gbazin committed
499
500
501
502
            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 );
503
504
505
506
507
            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
508
509
510
511
512
513
514
            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,
515
                           p_aout->output.output.i_rate, true )
gbazin's avatar
   
gbazin committed
516
517
        == VLC_SUCCESS )
    {
gbazin's avatar
   
gbazin committed
518
519
520
        val.i_int = AOUT_VAR_STEREO;
        text.psz_string = N_("Stereo");
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
521
522
523
524
525
526
        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!)" );
        }
gbazin's avatar
   
gbazin committed
527
528
529
530
531
532
        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,
533
                           p_aout->output.output.i_rate, true )
gbazin's avatar
   
gbazin committed
534
535
        == VLC_SUCCESS )
    {
gbazin's avatar
   
gbazin committed
536
537
538
        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
539
540
541
542
543
544
545
546
547
        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;
548
        msg_Dbg( p_aout, "GetSpeakerConfig failed" );
gbazin's avatar
   
gbazin committed
549
550
551
    }
    switch( DSSPEAKER_CONFIG(ui_speaker_config) )
    {
552
    case DSSPEAKER_7POINT1:
553
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is 7.1" );
554
555
        val.i_int = AOUT_VAR_7_1;
        break;
gbazin's avatar
   
gbazin committed
556
    case DSSPEAKER_5POINT1:
557
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is 5.1" );
gbazin's avatar
   
gbazin committed
558
        val.i_int = AOUT_VAR_5_1;
gbazin's avatar
   
gbazin committed
559
560
        break;
    case DSSPEAKER_QUAD:
561
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is Quad" );
gbazin's avatar
   
gbazin committed
562
        val.i_int = AOUT_VAR_2F2R;
gbazin's avatar
   
gbazin committed
563
        break;
gbazin's avatar
   
gbazin committed
564
565
#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
566
    case DSSPEAKER_MONO:
gbazin's avatar
   
gbazin committed
567
        val.i_int = AOUT_VAR_MONO;
gbazin's avatar
   
gbazin committed
568
        break;
gbazin's avatar
   
gbazin committed
569
#endif
gbazin's avatar
   
gbazin committed
570
    case DSSPEAKER_SURROUND:
571
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is surround" );
gbazin's avatar
   
gbazin committed
572
    case DSSPEAKER_STEREO:
573
        msg_Dbg( p_aout, "Windows says your SpeakerConfig is stereo" );
gbazin's avatar
   
gbazin committed
574
    default:
575
        /* If nothing else is found, choose stereo output */
gbazin's avatar
   
gbazin committed
576
        val.i_int = AOUT_VAR_STEREO;
gbazin's avatar
   
gbazin committed
577
578
579
580
        break;
    }
    var_Set( p_aout, "audio-device", val );

gbazin's avatar
   
gbazin committed
581
582
583
584
585
586
587
    /* 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,
588
                            AOUT_SPDIF_SIZE, true )
gbazin's avatar
   
gbazin committed
589
590
591
            == VLC_SUCCESS )
        {
            msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
gbazin's avatar
   
gbazin committed
592
593
594
595
            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
596
597
598
599
600
            if( config_GetInt( p_aout, "spdif" ) )
                var_Set( p_aout, "audio-device", val );
        }
    }

gbazin's avatar
   
gbazin committed
601
602
603
604
605
606
607
608
    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
609
610
    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );

611
    val.b_bool = true;
gbazin's avatar
   
gbazin committed
612
613
614
    var_Set( p_aout, "intf-change", val );
}

gbazin's avatar
   
gbazin committed
615
/*****************************************************************************
gbazin's avatar
   
gbazin committed
616
617
618
 * 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
619
 *****************************************************************************/
620
static void Play( aout_instance_t *p_aout )
gbazin's avatar
   
gbazin committed
621
{
gbazin's avatar
   
gbazin committed
622
623
624
    if( !p_aout->output.p_sys->b_playing )
    {
        aout_buffer_t *p_buffer;
625
        int i;
gbazin's avatar
   
gbazin committed
626
627
628
629
630
631
632
633

        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 */
634
635
636
637
638
639
        for( i = 0; i < FRAMES_NUM; i++ )
        {
            p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo );
            if( !p_buffer ) break;
            FillBuffer( p_aout, i, p_buffer );
        }
gbazin's avatar
   
gbazin committed
640
641

        /* wake up the audio output thread */
642
        SetEvent( p_aout->output.p_sys->p_notif->event );
gbazin's avatar
   
gbazin committed
643
    }
644
645
646
647
648
}

/*****************************************************************************
 * CloseAudio: close the audio device
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
649
static void CloseAudio( vlc_object_t *p_this )
650
{
gbazin's avatar
   
gbazin committed
651
    aout_instance_t * p_aout = (aout_instance_t *)p_this;
652
    aout_sys_t *p_sys = p_aout->output.p_sys;
653

654
    msg_Dbg( p_aout, "closing audio device" );
655
656

    /* kill the position notification thread, if any */
657
    if( p_sys->p_notif )
658
    {
659
        vlc_object_detach( p_sys->p_notif );
660
661
662
        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
663

664
        vlc_thread_join( p_sys->p_notif );
665
        vlc_object_release( p_sys->p_notif );
666
667
668
    }

    /* release the secondary buffer */
gbazin's avatar
   
gbazin committed
669
    DestroyDSBuffer( p_aout );
670
671

    /* finally release the DirectSound object */
672
    if( p_sys->p_dsobject ) IDirectSound_Release( p_sys->p_dsobject );
673
 
674
    /* free DSOUND.DLL */
675
    if( p_sys->hdsound_dll ) FreeLibrary( p_sys->hdsound_dll );
gbazin's avatar
   
gbazin committed
676

677
    free( p_aout->output.p_sys->p_device_guid );
678
    free( p_sys );
679
680
}

681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
/*****************************************************************************
 * CallBackDirectSoundEnum: callback to enumerate available devices
 *****************************************************************************/
static int CALLBACK CallBackDirectSoundEnum( LPGUID p_guid, LPCSTR psz_desc,
                                             LPCSTR psz_mod, LPVOID _p_aout )
{
    aout_instance_t *p_aout = (aout_instance_t *)_p_aout;

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

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

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

702
/*****************************************************************************
gbazin's avatar
   
gbazin committed
703
 * InitDirectSound: handle all the gory details of DirectSound initialisation
704
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
705
static int InitDirectSound( aout_instance_t *p_aout )
706
707
{
    HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
708
    HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACK, LPVOID);
709

gbazin's avatar
   
gbazin committed
710
711
    p_aout->output.p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
    if( p_aout->output.p_sys->hdsound_dll == NULL )
712
    {
gbazin's avatar
   
gbazin committed
713
714
        msg_Warn( p_aout, "cannot open DSOUND.DLL" );
        goto error;
715
716
    }

717
718
719
    OurDirectSoundCreate = (void *)
        GetProcAddress( p_aout->output.p_sys->hdsound_dll,
                        "DirectSoundCreate" );
720
721
    if( OurDirectSoundCreate == NULL )
    {
gbazin's avatar
   
gbazin committed
722
723
        msg_Warn( p_aout, "GetProcAddress FAILED" );
        goto error;
724
725
    }

726
727
728
729
730
731
732
    /* Get DirectSoundEnumerate */
    OurDirectSoundEnumerate = (void *)
       GetProcAddress( p_aout->output.p_sys->hdsound_dll,
                       "DirectSoundEnumerateA" );
    if( OurDirectSoundEnumerate )
    {
        /* Attempt enumeration */
733
        if( FAILED( OurDirectSoundEnumerate( CallBackDirectSoundEnum,
734
735
736
737
738
739
                                             p_aout ) ) )
        {
            msg_Dbg( p_aout, "enumeration of DirectSound devices failed" );
        }
    }

740
    /* Create the direct sound object */
741
    if FAILED( OurDirectSoundCreate( p_aout->output.p_sys->p_device_guid,
742
                                     &p_aout->output.p_sys->p_dsobject,
gbazin's avatar
   
gbazin committed
743
                                     NULL ) )
744
745
    {
        msg_Warn( p_aout, "cannot create a direct sound device" );
gbazin's avatar
   
gbazin committed
746
        goto error;
747
748
749
750
751
752
753
754
755
756
757
758
    }

    /* 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
759
760
761
    if( IDirectSound_SetCooperativeLevel( p_aout->output.p_sys->p_dsobject,
                                          GetDesktopWindow(),
                                          DSSCL_EXCLUSIVE) )
762
763
764
765
    {
        msg_Warn( p_aout, "cannot set direct sound cooperative level" );
    }

gbazin's avatar
   
gbazin committed
766
    return VLC_SUCCESS;
gbazin's avatar
   
gbazin committed
767
768
769
770
771
772
773
774

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

777
778
779
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
780
 * CreateDSBuffer: Creates a direct sound buffer of the required format.
781
782
783
784
785
786
787
788
 *****************************************************************************
 * 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
789
 * you have to release the current one and create another.
790
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
791
792
static int CreateDSBuffer( aout_instance_t *p_aout, int i_format,
                           int i_channels, int i_nb_channels, int i_rate,
793
                           int i_bytes_per_frame, bool b_probe )
794
{
gbazin's avatar
   
gbazin committed
795
    WAVEFORMATEXTENSIBLE waveformat;
796
    DSBUFFERDESC         dsbdesc;
gbazin's avatar
   
gbazin committed
797
    unsigned int         i;
798

gbazin's avatar
   
gbazin committed
799
800
    /* First set the sound buffer format */
    waveformat.dwChannelMask = 0;
801
    for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ )
gbazin's avatar
   
gbazin committed
802
    {
803
804
        if( i_channels & pi_channels_src[i] )
            waveformat.dwChannelMask |= pi_channels_in[i];
805
    }
gbazin's avatar
   
gbazin committed
806

gbazin's avatar
   
gbazin committed
807
    switch( i_format )
gbazin's avatar
   
gbazin committed
808
    {
gbazin's avatar
   
gbazin committed
809
810
    case VLC_FOURCC('s','p','d','i'):
        i_nb_channels = 2;
gbazin's avatar
   
gbazin committed
811
812
        /* To prevent channel re-ordering */
        waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
gbazin's avatar
   
gbazin committed
813
814
815
816
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
gbazin's avatar
   
gbazin committed
817
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
gbazin's avatar
   
gbazin committed
818
        break;
gbazin's avatar
   
gbazin committed
819

gbazin's avatar
   
gbazin committed
820
    case VLC_FOURCC('f','l','3','2'):
gbazin's avatar
   
gbazin committed
821
822
823
824
        waveformat.Format.wBitsPerSample = sizeof(float) * 8;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
gbazin's avatar
   
gbazin committed
825
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
gbazin's avatar
   
gbazin committed
826
827
828
829
830
831
832
        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
833
        waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM;
gbazin's avatar
   
gbazin committed
834
835
        break;
    }
836

gbazin's avatar
   
gbazin committed
837
838
839
840
841
842
843
    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;

844
845
846
    p_aout->output.p_sys->i_bits_per_sample = waveformat.Format.wBitsPerSample;
    p_aout->output.p_sys->i_channels = i_nb_channels;

847
848
849
850
851
852
    /* 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
853
854
855
856
857
858
859
860
861
862
    /* 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
863

864
865
866
        /* Needed for 5.1 on emu101k */
        dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
    }
gbazin's avatar
   
gbazin committed
867
868
869
870
871
872
873
874

    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) )
    {
875
876
877
878
879
        if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE )
        {
            /* Try without DSBCAPS_LOCHARDWARE */
            dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
            if FAILED( IDirectSound_CreateSoundBuffer(
gbazin's avatar
   
gbazin committed
880
881
                   p_aout->output.p_sys->p_dsobject, &dsbdesc,
                   &p_aout->output.p_sys->p_dsbuffer, NULL) )
882
883
884
885
886
887
888
            {
                return VLC_EGENERIC;
            }
            if( !b_probe )
                msg_Dbg( p_aout, "couldn't use hardware sound buffer" );
        }
        else
gbazin's avatar
   
gbazin committed
889
890
891
        {
            return VLC_EGENERIC;
        }
892
893
    }

gbazin's avatar
   
gbazin committed
894
895
896
897
898
899
900
    /* 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
901

902
    p_aout->output.p_sys->i_frame_size = i_bytes_per_frame;
gbazin's avatar
   
gbazin committed
903
    p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask;
904
905
906
907
908
909
910
911
912
    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
913

gbazin's avatar
   
gbazin committed
914
    return VLC_SUCCESS;
915
916
917
}

/*****************************************************************************
gbazin's avatar
   
gbazin committed
918
919
920
921
922
923
924
 * 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,
925
                              bool b_probe )
gbazin's avatar
   
gbazin committed
926
{
927
928
929
930
    vlc_value_t val;

    var_Get( p_aout, "directx-audio-float32", &val );

gbazin's avatar
   
gbazin committed
931
932
    /* Float32 audio samples are not supported for 5.1 output on the emu101k */

933
    if( !val.b_bool || i_nb_channels > 2 ||
gbazin's avatar
   
gbazin committed
934
        CreateDSBuffer( p_aout, VLC_FOURCC('f','l','3','2'),
gbazin's avatar
   
gbazin committed
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
                        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
961
 *****************************************************************************
gbazin's avatar
   
gbazin committed
962
 * This function destroys the secondary buffer.
963
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
964
static void DestroyDSBuffer( aout_instance_t *p_aout )
965
{
gbazin's avatar
   
gbazin committed
966
    if( p_aout->output.p_sys->p_dsbuffer )
967
    {
gbazin's avatar
   
gbazin committed
968
969
        IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer );
        p_aout->output.p_sys->p_dsbuffer = NULL;
970
971
972
    }
}

gbazin's avatar
   
gbazin committed
973
/*****************************************************************************
gbazin's avatar
   
gbazin committed
974
 * FillBuffer: Fill in one of the direct sound frame buffers.
gbazin's avatar
   
gbazin committed
975
976
977
 *****************************************************************************
 * Returns VLC_SUCCESS on success.
 *****************************************************************************/
gbazin's avatar
   
gbazin committed
978
979
static int FillBuffer( aout_instance_t *p_aout, int i_frame,
                       aout_buffer_t *p_buffer )
gbazin's avatar
   
gbazin committed
980
981
{
    notification_thread_t *p_notif = p_aout->output.p_sys->p_notif;
982
    aout_sys_t *p_sys = p_aout->output.p_sys;
gbazin's avatar
   
gbazin committed
983
984
985
986
987
988
    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(
989
                p_sys->p_dsbuffer,                              /* DS buffer */
gbazin's avatar
   
gbazin committed
990
991
992
993
994
995
996
997
998
                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 )
    {
999
        IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
gbazin's avatar
   
gbazin committed
1000
        dsresult = IDirectSoundBuffer_Lock(
1001
                               p_sys->p_dsbuffer,
gbazin's avatar
   
gbazin committed
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
                               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
1013
        if( p_buffer ) aout_BufferFree( p_buffer );
gbazin's avatar
   
gbazin committed
1014
1015
1016
        return VLC_EGENERIC;
    }

gbazin's avatar
   
gbazin committed
1017
1018
1019
1020
1021
    if( p_buffer == NULL )
    {
        memset( p_write_position, 0, l_bytes1 );
    }
    else
gbazin's avatar
   
gbazin committed
1022
    {
1023
1024
1025
1026
1027
1028
1029
1030
        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 );
        }

1031
        vlc_memcpy( p_write_position, p_buffer->p_buffer, l_bytes1 );
gbazin's avatar
   
gbazin committed
1032
1033
        aout_BufferFree( p_buffer );
    }
gbazin's avatar
   
gbazin committed
1034
1035

    /* Now the data has been copied, unlock the buffer */
1036
    IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1,
gbazin's avatar
   
gbazin committed
1037
1038
                               p_wrap_around, l_bytes2 );

1039
    p_notif->i_write_slot = (i_frame + 1) % FRAMES_NUM;
gbazin's avatar
   
gbazin committed
1040
1041
1042
    return VLC_SUCCESS;
}

1043
/*****************************************************************************
1044
 * DirectSoundThread: this thread will capture play notification events.
1045
 *****************************************************************************
gbazin's avatar
   
gbazin committed
1046
1047
 * We use this thread to emulate a callback mechanism. The thread probes for
 * event notification and fills up the DS secondary buffer when needed.
1048
 *****************************************************************************/
1049
static void* DirectSoundThread( vlc_object_t *p_this )
1050
{
1051
    notification_thread_t *p_notif = (notification_thread_t*)p_this;
gbazin's avatar
   
gbazin committed
1052
    aout_instance_t *p_aout = p_notif->p_aout;
1053
    bool b_sleek;
1054
1055
1056
    mtime_t last_time;
    HRESULT dsresult;
    long l_queued = 0;
1057

gbazin's avatar
   
gbazin committed
1058
1059
1060
    /* 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');

1061
1062
1063
1064
1065
    /* Tell the main thread that we are ready */
    vlc_thread_ready( p_notif );

    msg_Dbg( p_notif, "DirectSoundThread ready" );

gbazin's avatar
   
gbazin committed
1066
    /* Wait here until Play() is called */
1067
    WaitForSingleObject( p_notif->event, INFINITE );
gbazin's avatar
   
gbazin committed
1068

1069
    if( vlc_object_alive (p_notif) )
gbazin's avatar
   
gbazin committed
1070
1071
    {
        mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 );
1072

gbazin's avatar
   
gbazin committed
1073
1074
1075
1076
1077
        /* 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
1078
        if( dsresult == DSERR_BUFFERLOST )
1079
        {
gbazin's avatar
   
gbazin committed
1080
            IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer );
gbazin's avatar
   
gbazin committed
1081
1082
1083
1084
1085
            dsresult = IDirectSoundBuffer_Play(
                                            p_aout->output.p_sys->p_dsbuffer,
                                            0,                     /* Unused */
                                            0,                     /* Unused */
                                            DSBPLAY_LOOPING );      /* Flags */
1086
        }
gbazin's avatar
   
gbazin committed
1087
        if( dsresult != DS_OK )
1088
        {
gbazin's avatar
   
gbazin committed
1089
            msg_Err( p_aout, "cannot start playing buffer" );
1090
        }
gbazin's avatar
   
gbazin committed
1091
    }
1092
    last_time = mdate();
gbazin's avatar
   
gbazin committed
1093

1094
    while( vlc_object_alive (p_notif) )
gbazin's avatar
   
gbazin committed
1095
    {
1096
1097
1098
        long l_read, l_free_slots;
        mtime_t mtime = mdate();
        int i;
gbazin's avatar
   
gbazin committed
1099

1100
1101
1102
        /*
         * Fill in as much audio data as we can in our circular buffer
         */
gbazin's avatar
   
gbazin committed
1103

1104
1105
1106
        /* Find out current play position */
        if FAILED( IDirectSoundBuffer_GetCurrentPosition(
                   p_aout->output.p_sys->p_dsbuffer, &l_read, NULL ) )
gbazin's avatar
   
gbazin committed
1107
        {
1108
1109
            msg_Err( p_aout, "GetCurrentPosition() failed!" );
            l_read = 0;
gbazin's avatar
   
gbazin committed
1110
1111
        }

1112
1113
        /* Detect underruns */
        if( l_queued && mtime - last_time >