waveout.c 38.8 KB
Newer Older
1 2 3
/*****************************************************************************
 * waveout.c : Windows waveOut plugin for vlc
 *****************************************************************************
4
 * Copyright (C) 2001-2009 the VideoLAN team
5
 * $Id$
6
 *
7
 * Authors: Gildas Bazin <gbazin@videolan.org>
8
 *          André Weber
9
 *
10 11 12 13
 * 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.
14
 *
15 16 17 18 19 20
 * 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
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
21 22
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
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
#include <vlc_aout_intf.h>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
37
#include <vlc_charset.h>                        /* FromLocaleDup, LocaleFree */
38
#include <vlc_atomic.h>
39

40
#include "windows_audio_common.h"
41

42
#define FRAME_SIZE 4096              /* The size is in samples, not in bytes */
gbazin's avatar
 
gbazin committed
43

44 45 46
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
gbazin's avatar
 
gbazin committed
47 48
static int  Open         ( vlc_object_t * );
static void Close        ( vlc_object_t * );
49
static void Play         ( audio_output_t * );
50

gbazin's avatar
 
gbazin committed
51 52 53
/*****************************************************************************
 * notification_thread_t: waveOut event thread
 *****************************************************************************/
54
/* local functions */
55 56
static void Probe        ( audio_output_t * );
static int OpenWaveOut   ( audio_output_t *, uint32_t,
57
                           int, int, int, int, bool );
58
static int OpenWaveOutPCM( audio_output_t *, uint32_t,
59
                           vlc_fourcc_t*, int, int, int, bool );
60
static int PlayWaveOut   ( audio_output_t *, HWAVEOUT, WAVEHDR *,
61
                           aout_buffer_t *, bool );
gbazin's avatar
 
gbazin committed
62 63

static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD, DWORD, DWORD );
64
static void* WaveOutThread( void * );
gbazin's avatar
 
gbazin committed
65

66
static int VolumeSet( audio_output_t *, float, bool );
67

68 69 70 71 72 73 74 75
static int WaveOutClearDoneBuffers(aout_sys_t *p_sys);

static int ReloadWaveoutDevices( vlc_object_t *, char const *,
                                vlc_value_t, vlc_value_t, void * );
static uint32_t findDeviceID(char *);

static const char psz_device_name_fmt[] = "%s ($%x,$%x)";

76 77
static const char *const ppsz_adev[] = { "wavemapper", };
static const char *const ppsz_adev_text[] = { N_("Microsoft Soundmapper") };
78 79


80 81 82
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
83 84 85 86 87
#define DEVICE_TEXT N_("Select Audio Device")
#define DEVICE_LONG N_("Select special Audio device, or let windows "\
                       "decide (default), change needs VLC restart "\
                       "to apply.")
#define DEFAULT_AUDIO_DEVICE N_("Default Audio Device")
88

89 90 91 92 93 94
vlc_module_begin ()
    set_shortname( "WaveOut" )
    set_description( N_("Win32 waveOut extension output") )
    set_capability( "audio output", 50 )
    set_category( CAT_AUDIO )
    set_subcategory( SUBCAT_AUDIO_AOUT )
95
    add_bool( "waveout-float32", true, FLOAT_TEXT, FLOAT_LONGTEXT, true )
96

97
    add_string( "waveout-audio-device", "wavemapper",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
98
                 DEVICE_TEXT, DEVICE_LONG, false )
99
       add_deprecated_alias( "waveout-dev" )   /* deprecated since 0.9.3 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
100
       change_string_list( ppsz_adev, ppsz_adev_text, ReloadWaveoutDevices )
101
       change_action_add( ReloadWaveoutDevices, N_("Refresh list") )
102

103 104
    set_callbacks( Open, Close )
vlc_module_end ()
105 106 107 108 109 110 111 112 113

/*****************************************************************************
 * aout_sys_t: waveOut audio output method descriptor
 *****************************************************************************
 * This structure is part of the audio output thread descriptor.
 * It describes the waveOut specific properties of an audio device.
 *****************************************************************************/
struct aout_sys_t
{
114 115
    uint32_t i_wave_device_id;               /* ID of selected output device */

116 117
    HWAVEOUT h_waveout;                        /* handle to waveout instance */

gbazin's avatar
 
gbazin committed
118
    WAVEFORMATEXTENSIBLE waveformat;                         /* audio format */
119

gbazin's avatar
 
gbazin committed
120
    WAVEHDR waveheader[FRAMES_NUM];
121

122 123
    vlc_thread_t thread;
    vlc_atomic_t abort;
gbazin's avatar
 
gbazin committed
124
    HANDLE event;
125 126 127 128 129 130 131
    HANDLE new_buffer_event;

    // rental from alsa.c to synchronize startup of audiothread
    int b_playing;                                         /* playing status */
    mtime_t start_date;

    int i_repeat_counter;
gbazin's avatar
 
gbazin committed
132

gbazin's avatar
 
gbazin committed
133
    int i_buffer_size;
134

135
    uint8_t *p_silence_buffer;              /* buffer we use to play silence */
gbazin's avatar
 
gbazin committed
136

137
    bool b_chan_reorder;              /* do we need channel reordering */
gbazin's avatar
gbazin committed
138
    int pi_chan_table[AOUT_CHAN_MAX];
139 140 141 142 143 144 145 146
};

/*****************************************************************************
 * Open: open the audio device
 *****************************************************************************
 * This function opens and setups Win32 waveOut
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
gbazin's avatar
 
gbazin committed
147
{
148
    audio_output_t *p_aout = (audio_output_t *)p_this;
gbazin's avatar
 
gbazin committed
149
    vlc_value_t val;
150 151

    /* Allocate structure */
152
    p_aout->sys = malloc( sizeof( aout_sys_t ) );
153

154
    if( p_aout->sys == NULL )
ivoire's avatar
ivoire committed
155
        return VLC_ENOMEM;
156

157 158
    p_aout->pf_play = Play;
    p_aout->pf_pause = NULL;
159
    p_aout->pf_flush = NULL;
160 161 162 163

    /*
     initialize/update Device selection List
    */
164
    ReloadWaveoutDevices( p_this, "waveout-audio-device", val, val, NULL);
165 166 167 168 169


    /*
      check for configured audio device!
    */
170
    char *psz_waveout_dev = var_CreateGetString( p_aout, "waveout-audio-device");
171

172
    p_aout->sys->i_wave_device_id =
173 174
         findDeviceID( psz_waveout_dev );

175
    if(p_aout->sys->i_wave_device_id == WAVE_MAPPER)
176 177 178 179 180 181 182 183
    {
       if(psz_waveout_dev &&
          stricmp(psz_waveout_dev,"wavemapper"))
       {
           msg_Warn( p_aout, "configured audio device '%s' not available, "\
                         "use default instead", psz_waveout_dev );
       }
    }
ivoire's avatar
ivoire committed
184
    free( psz_waveout_dev );
185 186 187


    WAVEOUTCAPS waveoutcaps;
188
    if(waveOutGetDevCaps( p_aout->sys->i_wave_device_id,
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
                          &waveoutcaps,
                          sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
    {
      /* log debug some infos about driver, to know who to blame
         if it doesn't work */
        msg_Dbg( p_aout, "Drivername: %s", waveoutcaps.szPname);
        msg_Dbg( p_aout, "Driver Version: %d.%d",
                          (waveoutcaps.vDriverVersion>>8)&255,
                          waveoutcaps.vDriverVersion & 255);
        msg_Dbg( p_aout, "Manufacturer identifier: 0x%x", waveoutcaps.wMid );
        msg_Dbg( p_aout, "Product identifier: 0x%x", waveoutcaps.wPid );
    }



gbazin's avatar
 
gbazin committed
204
    if( var_Type( p_aout, "audio-device" ) == 0 )
gbazin's avatar
 
gbazin committed
205
    {
gbazin's avatar
 
gbazin committed
206
        Probe( p_aout );
gbazin's avatar
 
gbazin committed
207
    }
gbazin's avatar
 
gbazin committed
208 209

    if( var_Get( p_aout, "audio-device", &val ) < 0 )
gbazin's avatar
 
gbazin committed
210
    {
gbazin's avatar
 
gbazin committed
211
        /* Probe() has failed. */
212
        var_Destroy( p_aout, "waveout-audio-device");
213
        free( p_aout->sys );
gbazin's avatar
 
gbazin committed
214
        return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
215 216
    }

217

gbazin's avatar
 
gbazin committed
218
    /* Open the device */
gbazin's avatar
 
gbazin committed
219
    if( val.i_int == AOUT_VAR_SPDIF )
gbazin's avatar
 
gbazin committed
220
    {
221
        p_aout->format.i_format = VLC_CODEC_SPDIFL;
gbazin's avatar
 
gbazin committed
222

223
        if( OpenWaveOut( p_aout,
224
                         p_aout->sys->i_wave_device_id,
225
                         VLC_CODEC_SPDIFL,
226 227 228
                         p_aout->format.i_physical_channels,
                         aout_FormatNbChannels( &p_aout->format ),
                         p_aout->format.i_rate, false )
gbazin's avatar
 
gbazin committed
229 230 231
            != VLC_SUCCESS )
        {
            msg_Err( p_aout, "cannot open waveout audio device" );
232
            free( p_aout->sys );
gbazin's avatar
 
gbazin committed
233 234
            return VLC_EGENERIC;
        }
gbazin's avatar
 
gbazin committed
235 236

        /* Calculate the frame size in bytes */
237 238 239 240 241
        p_aout->i_nb_samples = A52_FRAME_NB;
        p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE;
        p_aout->format.i_frame_length = A52_FRAME_NB;
        p_aout->sys->i_buffer_size =
            p_aout->format.i_bytes_per_frame;
gbazin's avatar
 
gbazin committed
242 243

        aout_VolumeNoneInit( p_aout );
gbazin's avatar
 
gbazin committed
244 245 246
    }
    else
    {
247 248
        WAVEOUTCAPS wocaps;

249
        switch( val.i_int )
gbazin's avatar
 
gbazin committed
250
        {
251
        case AOUT_VAR_5_1:
252
            p_aout->format.i_physical_channels
253 254 255 256 257
                    = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
                      | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
                      | AOUT_CHAN_LFE;
            break;
        case AOUT_VAR_2F2R:
258
            p_aout->format.i_physical_channels
259 260 261 262
                    = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
                      | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
            break;
        case AOUT_VAR_MONO:
263
            p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
264 265
            break;
        default:
266
            p_aout->format.i_physical_channels
267
                    = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
gbazin's avatar
 
gbazin committed
268
        }
gbazin's avatar
 
gbazin committed
269

270
        if( OpenWaveOutPCM( p_aout,
271 272 273 274 275
                            p_aout->sys->i_wave_device_id,
                            &p_aout->format.i_format,
                            p_aout->format.i_physical_channels,
                            aout_FormatNbChannels( &p_aout->format ),
                            p_aout->format.i_rate, false )
gbazin's avatar
 
gbazin committed
276
            != VLC_SUCCESS )
gbazin's avatar
 
gbazin committed
277
        {
gbazin's avatar
 
gbazin committed
278
            msg_Err( p_aout, "cannot open waveout audio device" );
279
            free( p_aout->sys );
280
            return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
281
        }
gbazin's avatar
 
gbazin committed
282 283

        /* Calculate the frame size in bytes */
284 285 286 287
        p_aout->i_nb_samples = FRAME_SIZE;
        aout_FormatPrepare( &p_aout->format );
        p_aout->sys->i_buffer_size = FRAME_SIZE *
            p_aout->format.i_bytes_per_frame;
gbazin's avatar
 
gbazin committed
288 289

        aout_VolumeSoftInit( p_aout );
290

291
        /* Check for hardware volume support */
292
        if( waveOutGetDevCaps( (UINT_PTR)p_aout->sys->h_waveout,
293 294
                               &wocaps, sizeof(wocaps) ) == MMSYSERR_NOERROR &&
            wocaps.dwSupport & WAVECAPS_VOLUME )
295 296
        {
            DWORD i_dummy;
297
            if( waveOutGetVolume( p_aout->sys->h_waveout, &i_dummy )
298 299
                == MMSYSERR_NOERROR )
            {
300
                p_aout->pf_volume_set = VolumeSet;
301 302
            }
        }
gbazin's avatar
 
gbazin committed
303 304
    }

gbazin's avatar
 
gbazin committed
305

306
    waveOutReset( p_aout->sys->h_waveout );
gbazin's avatar
 
gbazin committed
307

gbazin's avatar
 
gbazin committed
308
    /* Allocate silence buffer */
309 310 311
    p_aout->sys->p_silence_buffer =
        malloc( p_aout->sys->i_buffer_size );
    if( p_aout->sys->p_silence_buffer == NULL )
gbazin's avatar
 
gbazin committed
312
    {
313
        free( p_aout->sys );
314
        return VLC_ENOMEM;
gbazin's avatar
 
gbazin committed
315
    }
316
    p_aout->sys->i_repeat_counter = 0;
317

gbazin's avatar
 
gbazin committed
318 319

    /* Zero the buffer. WinCE doesn't have calloc(). */
320 321
    memset( p_aout->sys->p_silence_buffer, 0,
            p_aout->sys->i_buffer_size );
322

gbazin's avatar
 
gbazin committed
323
    /* Now we need to setup our waveOut play notification structure */
324 325
    p_aout->sys->event = CreateEvent( NULL, FALSE, FALSE, NULL );
    p_aout->sys->new_buffer_event = CreateEvent( NULL, FALSE, FALSE, NULL );
326 327 328

    /* define startpoint of playback on first call to play()
      like alsa does (instead of playing a blank sample) */
329 330
    p_aout->sys->b_playing = 0;
    p_aout->sys->start_date = 0;
331

gbazin's avatar
 
gbazin committed
332 333

    /* Then launch the notification thread */
334 335
    vlc_atomic_set( &p_aout->sys->abort, 0);
    if( vlc_clone( &p_aout->sys->thread,
336
                   WaveOutThread, p_aout, VLC_THREAD_PRIORITY_OUTPUT ) )
gbazin's avatar
 
gbazin committed
337 338 339 340
    {
        msg_Err( p_aout, "cannot create WaveOutThread" );
    }

gbazin's avatar
 
gbazin committed
341 342
    /* We need to kick off the playback in order to have the callback properly
     * working */
343
    for( int i = 0; i < FRAMES_NUM; i++ )
gbazin's avatar
 
gbazin committed
344
    {
345 346
        p_aout->sys->waveheader[i].dwFlags = WHDR_DONE;
        p_aout->sys->waveheader[i].dwUser = 0;
gbazin's avatar
 
gbazin committed
347
    }
gbazin's avatar
 
gbazin committed
348

349
    return VLC_SUCCESS;
350 351
}

gbazin's avatar
 
gbazin committed
352 353 354
/*****************************************************************************
 * Probe: probe the audio device for available formats and channels
 *****************************************************************************/
355
static void Probe( audio_output_t * p_aout )
gbazin's avatar
 
gbazin committed
356
{
gbazin's avatar
 
gbazin committed
357
    vlc_value_t val, text;
358
    vlc_fourcc_t i_format;
gbazin's avatar
 
gbazin committed
359 360
    unsigned int i_physical_channels;

gbazin's avatar
 
gbazin committed
361
    var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
362
    text.psz_string = _("Audio Device");
gbazin's avatar
 
gbazin committed
363
    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
gbazin's avatar
 
gbazin committed
364 365 366 367 368

    /* 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;
369
    if( p_aout->format.i_physical_channels == i_physical_channels )
gbazin's avatar
 
gbazin committed
370
    {
371
        if( OpenWaveOutPCM( p_aout,
372
                            p_aout->sys->i_wave_device_id,
373
                            &i_format,
gbazin's avatar
 
gbazin committed
374
                            i_physical_channels, 6,
375
                            p_aout->format.i_rate, true )
gbazin's avatar
 
gbazin committed
376 377
            == VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
378
            val.i_int = AOUT_VAR_5_1;
379
            text.psz_string = (char *)_("5.1");
gbazin's avatar
 
gbazin committed
380 381
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
382
            msg_Dbg( p_aout, "device supports 5.1 channels" );
gbazin's avatar
 
gbazin committed
383
        }
gbazin's avatar
 
gbazin committed
384 385 386 387 388
    }

    /* Test for 2 Front 2 Rear support */
    i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
                          AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
389
    if( ( p_aout->format.i_physical_channels & i_physical_channels )
gbazin's avatar
 
gbazin committed
390
        == i_physical_channels )
gbazin's avatar
 
gbazin committed
391
    {
392
        if( OpenWaveOutPCM( p_aout,
393
                            p_aout->sys->i_wave_device_id,
394
                            &i_format,
gbazin's avatar
 
gbazin committed
395
                            i_physical_channels, 4,
396
                            p_aout->format.i_rate, true )
gbazin's avatar
 
gbazin committed
397 398
            == VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
399
            val.i_int = AOUT_VAR_2F2R;
400
            text.psz_string = (char *)_("2 Front 2 Rear");
gbazin's avatar
 
gbazin committed
401 402
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
403
            msg_Dbg( p_aout, "device supports 4 channels" );
gbazin's avatar
 
gbazin committed
404
        }
gbazin's avatar
 
gbazin committed
405 406 407 408
    }

    /* Test for stereo support */
    i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
409
    if( OpenWaveOutPCM( p_aout,
410
                        p_aout->sys->i_wave_device_id,
411
                        &i_format,
gbazin's avatar
 
gbazin committed
412
                        i_physical_channels, 2,
413
                        p_aout->format.i_rate, true )
gbazin's avatar
 
gbazin committed
414 415
        == VLC_SUCCESS )
    {
gbazin's avatar
 
gbazin committed
416
        val.i_int = AOUT_VAR_STEREO;
417
        text.psz_string = (char *)_("Stereo");
gbazin's avatar
 
gbazin committed
418
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
419
        msg_Dbg( p_aout, "device supports 2 channels" );
gbazin's avatar
 
gbazin committed
420
    }
gbazin's avatar
 
gbazin committed
421 422 423

    /* Test for mono support */
    i_physical_channels = AOUT_CHAN_CENTER;
424
    if( OpenWaveOutPCM( p_aout,
425
                        p_aout->sys->i_wave_device_id,
426
                        &i_format,
gbazin's avatar
 
gbazin committed
427
                        i_physical_channels, 1,
428
                        p_aout->format.i_rate, true )
gbazin's avatar
 
gbazin committed
429 430
        == VLC_SUCCESS )
    {
gbazin's avatar
 
gbazin committed
431
        val.i_int = AOUT_VAR_MONO;
432
        text.psz_string = (char *)_("Mono");
gbazin's avatar
 
gbazin committed
433
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
434
        msg_Dbg( p_aout, "device supports 1 channel" );
gbazin's avatar
 
gbazin committed
435
    }
gbazin's avatar
 
gbazin committed
436

gbazin's avatar
 
gbazin committed
437
    /* Test for SPDIF support */
438
    if ( AOUT_FMT_NON_LINEAR( &p_aout->format ) )
gbazin's avatar
 
gbazin committed
439
    {
440
        if( OpenWaveOut( p_aout,
441
                         p_aout->sys->i_wave_device_id,
442
                         VLC_CODEC_SPDIFL,
443 444 445
                         p_aout->format.i_physical_channels,
                         aout_FormatNbChannels( &p_aout->format ),
                         p_aout->format.i_rate, true )
gbazin's avatar
 
gbazin committed
446 447 448
            == VLC_SUCCESS )
        {
            msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
gbazin's avatar
 
gbazin committed
449
            val.i_int = AOUT_VAR_SPDIF;
450
            text.psz_string = (char *)_("A/52 over S/PDIF");
gbazin's avatar
 
gbazin committed
451 452
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
453
            if( var_InheritBool( p_aout, "spdif" ) )
gbazin's avatar
 
gbazin committed
454 455 456 457
                var_Set( p_aout, "audio-device", val );
        }
    }

gbazin's avatar
 
gbazin committed
458 459 460 461 462 463 464 465
    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
466
    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
467
    var_TriggerCallback( p_aout, "intf-change" );
gbazin's avatar
 
gbazin committed
468 469
}

470 471 472
/*****************************************************************************
 * Play: play a sound buffer
 *****************************************************************************
gbazin's avatar
 
gbazin committed
473 474
 * This doesn't actually play the buffer. This just stores the buffer so it
 * can be played by the callback thread.
475
 *****************************************************************************/
476
static void Play( audio_output_t *_p_aout )
477
{
478
    if( !_p_aout->sys->b_playing )
479
    {
480
        _p_aout->sys->b_playing = 1;
481 482

        /* get the playing date of the first aout buffer */
483 484
        _p_aout->sys->start_date =
            aout_FifoFirstDate( &_p_aout->fifo );
485 486 487 488

        msg_Dbg( _p_aout, "Wakeup sleeping output thread.");

        /* wake up the audio output thread */
489
        SetEvent( _p_aout->sys->event );
490
    } else {
491
        SetEvent( _p_aout->sys->new_buffer_event );
492
    }
493 494 495 496 497 498
}

/*****************************************************************************
 * Close: close the audio device
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
gbazin's avatar
 
gbazin committed
499
{
500
    audio_output_t *p_aout = (audio_output_t *)p_this;
501
    aout_sys_t *p_sys = p_aout->sys;
502 503

    /* Before calling waveOutClose we must reset the device */
504
    vlc_atomic_set( &p_sys->abort, 1);
gbazin's avatar
 
gbazin committed
505

506
    /* wake up the audio thread, to recognize that p_aout died */
507
    SetEvent( p_sys->event );
508 509
    SetEvent( p_sys->new_buffer_event );

510
    vlc_join( p_sys->thread, NULL );
gbazin's avatar
 
gbazin committed
511

512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
    /*
      kill the real output then - when the feed thread
      is surely terminated!
      old code could be too early in case that "feeding"
      was running on termination

      at this point now its sure, that there will be no new
      data send to the driver, and we can cancel the last
      running playbuffers
    */
    MMRESULT result = waveOutReset( p_sys->h_waveout );
    if(result != MMSYSERR_NOERROR)
    {
       msg_Err( p_aout, "waveOutReset failed 0x%x", result );
       /*
        now we must wait, that all buffers are played
        because cancel doesn't work in this case...
       */
       if(result == MMSYSERR_NOTSUPPORTED)
       {
           /*
             clear currently played (done) buffers,
             if returnvalue > 0 (means some buffer still playing)
             wait for the driver event callback that one buffer
             is finished with playing, and check again
             the timeout of 5000ms is just, an emergency exit
             of this loop, to avoid deadlock in case of other
             (currently not known bugs, problems, errors cases?)
           */
           while(
                 (WaveOutClearDoneBuffers( p_sys ) > 0)
                 &&
                 (WaitForSingleObject( p_sys->event, 5000) == WAIT_OBJECT_0)
                )
           {
                 msg_Dbg( p_aout, "Wait for waveout device...");
           }
       }
    } else {
        WaveOutClearDoneBuffers( p_sys );
    }

    /* now we can Close the device */
gbazin's avatar
gbazin committed
555
    if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
556 557 558 559
    {
        msg_Err( p_aout, "waveOutClose failed" );
    }

560 561 562 563 564 565 566
    /*
      because so long, the waveout device is playing, the callback
      could occur and need the events
    */
    CloseHandle( p_sys->event );
    CloseHandle( p_sys->new_buffer_event);

gbazin's avatar
gbazin committed
567 568
    free( p_sys->p_silence_buffer );
    free( p_sys );
569 570 571
}

/*****************************************************************************
gbazin's avatar
 
gbazin committed
572
 * OpenWaveOut: open the waveout sound device
573
 ****************************************************************************/
574
static int OpenWaveOut( audio_output_t *p_aout, uint32_t i_device_id, int i_format,
gbazin's avatar
 
gbazin committed
575
                        int i_channels, int i_nb_channels, int i_rate,
576
                        bool b_probe )
577 578 579 580
{
    MMRESULT result;

    /* Set sound format */
gbazin's avatar
 
gbazin committed
581

582
#define waveformat p_aout->sys->waveformat
gbazin's avatar
 
gbazin committed
583 584

    waveformat.dwChannelMask = 0;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
585
    for( unsigned i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ )
gbazin's avatar
 
gbazin committed
586
    {
587 588
        if( i_channels & pi_channels_src[i] )
            waveformat.dwChannelMask |= pi_channels_in[i];
gbazin's avatar
 
gbazin committed
589
    }
gbazin's avatar
 
gbazin committed
590 591 592

    switch( i_format )
    {
593
    case VLC_CODEC_SPDIFL:
gbazin's avatar
 
gbazin committed
594
        i_nb_channels = 2;
gbazin's avatar
 
gbazin committed
595 596
        /* To prevent channel re-ordering */
        waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
gbazin's avatar
 
gbazin committed
597 598 599 600
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
gbazin's avatar
 
gbazin committed
601
        waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
gbazin's avatar
 
gbazin committed
602 603
        break;

604
    case VLC_CODEC_FL32:
gbazin's avatar
 
gbazin committed
605 606 607 608
        waveformat.Format.wBitsPerSample = sizeof(float) * 8;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
gbazin's avatar
 
gbazin committed
609
        waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
gbazin's avatar
 
gbazin committed
610
        break;
gbazin's avatar
 
gbazin committed
611

612
    case VLC_CODEC_S16L:
gbazin's avatar
 
gbazin committed
613 614 615 616
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
gbazin's avatar
 
gbazin committed
617
        waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_PCM;
gbazin's avatar
 
gbazin committed
618 619 620
        break;
    }

gbazin's avatar
 
gbazin committed
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
    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;

    /* 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);
    }
639

640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
    if(!b_probe) {
        msg_Dbg( p_aout, "OpenWaveDevice-ID: %u", i_device_id);
        msg_Dbg( p_aout,"waveformat.Format.cbSize          = %d",
                 waveformat.Format.cbSize);
        msg_Dbg( p_aout,"waveformat.Format.wFormatTag      = %u",
                 waveformat.Format.wFormatTag);
        msg_Dbg( p_aout,"waveformat.Format.nChannels       = %u",
                 waveformat.Format.nChannels);
        msg_Dbg( p_aout,"waveformat.Format.nSamplesPerSec  = %d",
                 (int)waveformat.Format.nSamplesPerSec);
        msg_Dbg( p_aout,"waveformat.Format.nAvgBytesPerSec = %u",
                 (int)waveformat.Format.nAvgBytesPerSec);
        msg_Dbg( p_aout,"waveformat.Format.nBlockAlign     = %d",
                 waveformat.Format.nBlockAlign);
        msg_Dbg( p_aout,"waveformat.Format.wBitsPerSample  = %d",
                 waveformat.Format.wBitsPerSample);
        msg_Dbg( p_aout,"waveformat.Samples.wValidBitsPerSample = %d",
                 waveformat.Samples.wValidBitsPerSample);
        msg_Dbg( p_aout,"waveformat.Samples.wSamplesPerBlock = %d",
                 waveformat.Samples.wSamplesPerBlock);
        msg_Dbg( p_aout,"waveformat.dwChannelMask          = %lu",
                 waveformat.dwChannelMask);
    }

664
    /* Open the device */
665
    result = waveOutOpen( &p_aout->sys->h_waveout, i_device_id,
gbazin's avatar
 
gbazin committed
666
                          (WAVEFORMATEX *)&waveformat,
gbazin's avatar
 
gbazin committed
667
                          (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
gbazin's avatar
 
gbazin committed
668
                          CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) );
gbazin's avatar
 
gbazin committed
669 670
    if( result == WAVERR_BADFORMAT )
    {
gbazin's avatar
 
gbazin committed
671
        msg_Warn( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" );
gbazin's avatar
 
gbazin committed
672 673 674 675 676 677
        return VLC_EGENERIC;
    }
    if( result == MMSYSERR_ALLOCATED )
    {
        msg_Warn( p_aout, "waveOutOpen failed WAVERR_ALLOCATED" );
        return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
678
    }
679 680
    if( result != MMSYSERR_NOERROR )
    {
gbazin's avatar
 
gbazin committed
681
        msg_Warn( p_aout, "waveOutOpen failed" );
gbazin's avatar
 
gbazin committed
682
        return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
683 684
    }

685
    p_aout->sys->b_chan_reorder =
gbazin's avatar
gbazin committed
686 687
        aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
                                  waveformat.dwChannelMask, i_nb_channels,
688
                                  p_aout->sys->pi_chan_table );
gbazin's avatar
gbazin committed
689

690
    if( p_aout->sys->b_chan_reorder )
gbazin's avatar
gbazin committed
691 692 693
    {
        msg_Dbg( p_aout, "channel reordering needed" );
    }
gbazin's avatar
 
gbazin committed
694 695 696 697 698 699 700 701 702 703

    return VLC_SUCCESS;

#undef waveformat

}

/*****************************************************************************
 * OpenWaveOutPCM: open a PCM waveout sound device
 ****************************************************************************/
704
static int OpenWaveOutPCM( audio_output_t *p_aout, uint32_t i_device_id,
705
                           vlc_fourcc_t *i_format,
gbazin's avatar
 
gbazin committed
706
                           int i_channels, int i_nb_channels, int i_rate,
707
                           bool b_probe )
gbazin's avatar
 
gbazin committed
708
{
709
    bool b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32");
710

711
    if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_CODEC_FL32,
712
                                   i_channels, i_nb_channels, i_rate, b_probe )
gbazin's avatar
 
gbazin committed
713 714
        != VLC_SUCCESS )
    {
715
        if ( OpenWaveOut( p_aout, i_device_id, VLC_CODEC_S16L,
gbazin's avatar
 
gbazin committed
716 717 718 719 720 721 722
                          i_channels, i_nb_channels, i_rate, b_probe )
             != VLC_SUCCESS )
        {
            return VLC_EGENERIC;
        }
        else
        {
723
            *i_format = VLC_CODEC_S16L;
gbazin's avatar
 
gbazin committed
724 725 726 727 728
            return VLC_SUCCESS;
        }
    }
    else
    {
729
        *i_format = VLC_CODEC_FL32;
gbazin's avatar
 
gbazin committed
730 731 732 733
        return VLC_SUCCESS;
    }
}

gbazin's avatar
 
gbazin committed
734 735 736
/*****************************************************************************
 * PlayWaveOut: play a buffer through the WaveOut device
 *****************************************************************************/
737
static int PlayWaveOut( audio_output_t *p_aout, HWAVEOUT h_waveout,
738
                        WAVEHDR *p_waveheader, aout_buffer_t *p_buffer,
739
                        bool b_spdif)
gbazin's avatar
 
gbazin committed
740 741 742 743
{
    MMRESULT result;

    /* Prepare the buffer */
744
    if( p_buffer != NULL )
745
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
746
        p_waveheader->lpData = (LPSTR)p_buffer->p_buffer;
747 748 749 750 751 752 753 754
        /*
          copy the buffer to the silence buffer :) so in case we don't
          get the next buffer fast enough (I will repeat this one a time
          for AC3 / DTS and SPDIF this will sound better instead of
          a hickup)
        */
        if(b_spdif)
        {
755
           vlc_memcpy( p_aout->sys->p_silence_buffer,
756
                       p_buffer->p_buffer,
757 758
                       p_aout->sys->i_buffer_size );
           p_aout->sys->i_repeat_counter = 2;
759 760
        }
    } else {
gbazin's avatar
 
gbazin committed
761
        /* Use silence buffer instead */
762
        if(p_aout->sys->i_repeat_counter)
763
        {
764 765
           p_aout->sys->i_repeat_counter--;
           if(!p_aout->sys->i_repeat_counter)
766
           {
767 768
               vlc_memset( p_aout->sys->p_silence_buffer,
                           0x00, p_aout->sys->i_buffer_size );
769 770
           }
        }
771
        p_waveheader->lpData = (LPSTR)p_aout->sys->p_silence_buffer;
772
    }
gbazin's avatar
 
gbazin committed
773

gbazin's avatar
gbazin committed
774
    p_waveheader->dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
775
    p_waveheader->dwBufferLength = p_aout->sys->i_buffer_size;
gbazin's avatar
 
gbazin committed
776 777 778 779 780 781
    p_waveheader->dwFlags = 0;

    result = waveOutPrepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
    if( result != MMSYSERR_NOERROR )
    {
        msg_Err( p_aout, "waveOutPrepareHeader failed" );
gbazin's avatar
 
gbazin committed
782
        return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
783 784 785 786 787 788 789
    }

    /* Send the buffer to the waveOut queue */
    result = waveOutWrite( h_waveout, p_waveheader, sizeof(WAVEHDR) );
    if( result != MMSYSERR_NOERROR )
    {
        msg_Err( p_aout, "waveOutWrite failed" );
gbazin's avatar
 
gbazin committed
790
        return VLC_EGENERIC;
791 792
    }

gbazin's avatar
 
gbazin committed
793
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
794 795 796 797 798 799 800 801 802
}

/*****************************************************************************
 * WaveOutCallback: what to do once WaveOut has played its sound samples
 *****************************************************************************/
static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
                                      DWORD _p_aout,
                                      DWORD dwParam1, DWORD dwParam2 )
{
ivoire's avatar
ivoire committed
803
    (void)h_waveout;    (void)dwParam1;    (void)dwParam2;
804
    audio_output_t *p_aout = (audio_output_t *)_p_aout;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
805
    int i_queued_frames = 0;
gbazin's avatar
 
gbazin committed
806 807 808

    if( uMsg != WOM_DONE ) return;

809
    if( vlc_atomic_get(&p_aout->sys->abort) ) return;
gbazin's avatar
 
gbazin committed