waveout.c 39.5 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>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
36
#include <vlc_charset.h>                        /* FromLocaleDup, LocaleFree */
37
#include <vlc_atomic.h>
38

39
#include "windows_audio_common.h"
40

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

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

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

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

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
65
static int VolumeSet( aout_instance_t *, audio_volume_t, bool );
66

67 68 69 70 71 72 73 74
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)";

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


79 80 81
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
82 83 84 85 86
#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")
87

88 89 90 91 92 93
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 )
94
    add_bool( "waveout-float32", true, FLOAT_TEXT, FLOAT_LONGTEXT, true )
95

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

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

/*****************************************************************************
 * 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
{
113 114
    uint32_t i_wave_device_id;               /* ID of selected output device */

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

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

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

121 122
    vlc_thread_t thread;
    vlc_atomic_t abort;
gbazin's avatar
 
gbazin committed
123
    HANDLE event;
124 125 126 127 128 129 130
    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
131

gbazin's avatar
 
gbazin committed
132
    int i_buffer_size;
133

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

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

/*****************************************************************************
 * 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
146
{
gbazin's avatar
 
gbazin committed
147
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
gbazin's avatar
 
gbazin committed
148
    vlc_value_t val;
149 150

    /* Allocate structure */
gbazin's avatar
 
gbazin committed
151
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
152

gbazin's avatar
 
gbazin committed
153
    if( p_aout->output.p_sys == NULL )
ivoire's avatar
ivoire committed
154
        return VLC_ENOMEM;
155

gbazin's avatar
 
gbazin committed
156
    p_aout->output.pf_play = Play;
157
    p_aout->output.pf_pause = NULL;
158 159 160 161

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


    /*
      check for configured audio device!
    */
168
    char *psz_waveout_dev = var_CreateGetString( p_aout, "waveout-audio-device");
169 170 171 172 173 174 175 176 177 178 179 180 181

    p_aout->output.p_sys->i_wave_device_id =
         findDeviceID( psz_waveout_dev );

    if(p_aout->output.p_sys->i_wave_device_id == WAVE_MAPPER)
    {
       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
182
    free( psz_waveout_dev );
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201


    WAVEOUTCAPS waveoutcaps;
    if(waveOutGetDevCaps( p_aout->output.p_sys->i_wave_device_id,
                          &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
202
    if( var_Type( p_aout, "audio-device" ) == 0 )
gbazin's avatar
 
gbazin committed
203
    {
gbazin's avatar
 
gbazin committed
204
        Probe( p_aout );
gbazin's avatar
 
gbazin committed
205
    }
gbazin's avatar
 
gbazin committed
206 207

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

215

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

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

        /* 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_buffer_size =
            p_aout->output.output.i_bytes_per_frame;

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

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

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

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

        aout_VolumeSoftInit( p_aout );
288

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

gbazin's avatar
 
gbazin committed
303

gbazin's avatar
 
gbazin committed
304 305
    waveOutReset( p_aout->output.p_sys->h_waveout );

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

gbazin's avatar
 
gbazin committed
316 317

    /* Zero the buffer. WinCE doesn't have calloc(). */
318 319
    memset( p_aout->output.p_sys->p_silence_buffer, 0,
            p_aout->output.p_sys->i_buffer_size );
320

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

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

gbazin's avatar
 
gbazin committed
330 331

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

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

347
    return VLC_SUCCESS;
348 349
}

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

gbazin's avatar
 
gbazin committed
359
    var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
360
    text.psz_string = _("Audio Device");
gbazin's avatar
 
gbazin committed
361
    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
gbazin's avatar
 
gbazin committed
362 363 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;
    if( p_aout->output.output.i_physical_channels == i_physical_channels )
    {
369 370 371
        if( OpenWaveOutPCM( p_aout,
                            p_aout->output.p_sys->i_wave_device_id,
                            &i_format,
gbazin's avatar
 
gbazin committed
372
                            i_physical_channels, 6,
373
                            p_aout->output.output.i_rate, true )
gbazin's avatar
 
gbazin committed
374 375
            == VLC_SUCCESS )
        {
gbazin's avatar
 
gbazin committed
376
            val.i_int = AOUT_VAR_5_1;
377
            text.psz_string = (char *)_("5.1");
gbazin's avatar
 
gbazin committed
378 379
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
 
gbazin committed
380
            msg_Dbg( p_aout, "device supports 5.1 channels" );
gbazin's avatar
 
gbazin committed
381
        }
gbazin's avatar
 
gbazin committed
382 383 384 385 386
    }

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

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

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

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

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

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

        /* get the playing date of the first aout buffer */
        _p_aout->output.p_sys->start_date =
482
            aout_FifoFirstDate( &_p_aout->output.fifo );
483 484 485 486 487 488 489 490

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

        /* wake up the audio output thread */
        SetEvent( _p_aout->output.p_sys->event );
    } else {
        SetEvent( _p_aout->output.p_sys->new_buffer_event );
    }
491 492 493 494 495 496
}

/*****************************************************************************
 * Close: close the audio device
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
gbazin's avatar
 
gbazin committed
497
{
gbazin's avatar
 
gbazin committed
498
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
gbazin's avatar
gbazin committed
499
    aout_sys_t *p_sys = p_aout->output.p_sys;
500 501

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

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

508
    vlc_join( p_sys->thread, NULL );
gbazin's avatar
 
gbazin committed
509

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
    /*
      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
553
    if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
554 555 556 557
    {
        msg_Err( p_aout, "waveOutClose failed" );
    }

558 559 560 561 562 563 564
    /*
      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
565 566
    free( p_sys->p_silence_buffer );
    free( p_sys );
567 568 569
}

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

    /* Set sound format */
gbazin's avatar
 
gbazin committed
579 580 581 582

#define waveformat p_aout->output.p_sys->waveformat

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

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

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

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

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

638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
    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);
    }

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

gbazin's avatar
gbazin committed
683 684 685 686 687 688 689 690 691
    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
692 693 694 695 696 697 698 699 700 701

    return VLC_SUCCESS;

#undef waveformat

}

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

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

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

    /* Prepare the buffer */
742
    if( p_buffer != NULL )
743
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
744
        p_waveheader->lpData = (LPSTR)p_buffer->p_buffer;
745 746 747 748 749 750 751 752
        /*
          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)
        {
753 754 755
           vlc_memcpy( p_aout->output.p_sys->p_silence_buffer,
                       p_buffer->p_buffer,
                       p_aout->output.p_sys->i_buffer_size );
756 757 758
           p_aout->output.p_sys->i_repeat_counter = 2;
        }
    } else {
gbazin's avatar
 
gbazin committed
759
        /* Use silence buffer instead */
760 761 762 763 764
        if(p_aout->output.p_sys->i_repeat_counter)
        {
           p_aout->output.p_sys->i_repeat_counter--;
           if(!p_aout->output.p_sys->i_repeat_counter)
           {
765 766
               vlc_memset( p_aout->output.p_sys->p_silence_buffer,
                           0x00, p_aout->output.p_sys->i_buffer_size );
767 768
           }
        }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
769
        p_waveheader->lpData = (LPSTR)p_aout->output.p_sys->p_silence_buffer;
770
    }
gbazin's avatar
 
gbazin committed
771

gbazin's avatar
gbazin committed
772
    p_waveheader->dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
gbazin's avatar
 
gbazin committed
773 774 775 776 777 778 779
    p_waveheader->dwBufferLength = p_aout->output.p_sys->i_buffer_size;
    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
780
        return VLC_EGENERIC;
gbazin's avatar
 
gbazin committed
781 782 783 784 785 786 787
    }

    /* 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
788
        return VLC_EGENERIC;
789 790
    }

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

/*****************************************************************************
 * 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
801
    (void)h_waveout;    (void)dwParam1;    (void)dwParam2;
gbazin's avatar
 
gbazin committed
802
    aout_instance_t *p_aout = (aout_instance_t *)_p_aout;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
803
    int i_queued_frames = 0;
gbazin's avatar
 
gbazin committed
804 805 806

    if( uMsg != WOM_DONE ) return;

807
    if( vlc_atomic_get(&p_aout->output.p_sys->abort) ) return;
gbazin's avatar
 
gbazin committed
808

gbazin's avatar
 
gbazin committed
809
    /* Find out the current latency */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
810
    for( int i = 0; i < FRAMES_NUM; i++ )
gbazin's avatar
 
gbazin committed
811
    {
gbazin's avatar
 
gbazin committed
812 813 814 815 816
        /* Check if frame buf is available */
        if( !(p_aout->output.p_sys->waveheader[i].dwFlags & WHDR_DONE) )
        {
            i_queued_frames++;
        }
gbazin's avatar
 
gbazin committed
817 818
    }

gbazin's avatar
 
gbazin committed
819
    /* Don't wake up the thread too much */
820
    if( i_queued_frames <= FRAMES_NUM/2 )
gbazin's avatar
 
gbazin committed
821
        SetEvent( p_aout->output.p_sys->event );
822
}
gbazin's avatar
 
gbazin committed
823

824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860

/****************************************************************************
 * WaveOutClearDoneBuffers: Clear all done marked buffers, and free aout_bufer
 ****************************************************************************
 * return value is the number of still playing buffers in the queue
 ****************************************************************************/
static int WaveOutClearDoneBuffers(aout_sys_t *p_sys)
{
    WAVEHDR *p_waveheader = p_sys->waveheader;
    int i_queued_frames = 0;

    for( int i = 0; i < FRAMES_NUM; i++ )
    {
        if( (p_waveheader[i].dwFlags & WHDR_DONE) &&
            p_waveheader[i].dwUser )
        {
            aout_buffer_t *p_buffer =
                    (aout_buffer_t *)(p_waveheader[i].dwUser);
            /* Unprepare and free the buffers which has just been played */
            waveOutUnprepareHeader( p_sys->h_waveout, &p_waveheader[i],
                                    sizeof(WAVEHDR) );

            if( p_waveheader[i].dwUser != 1 )
                aout_BufferFree( p_buffer );

            p_waveheader[i].dwUser = 0;
        }

        /* Check if frame buf is available */
        if( !(p_waveheader[i].dwFlags & WHDR_DONE) )
        {
            i_queued_frames++;
        }
    }
    return i_queued_frames;
}

gbazin's avatar
 
gbazin committed
861
/*****************************************************************************
862
 * WaveOutThread: this thread will capture play notification events.
gbazin's avatar
 
gbazin committed
863 864 865 866 867
 *****************************************************************************
 * We use this thread to feed new audio samples to the sound card because
 * we are not authorized to use waveOutWrite() directly in the waveout
 * callback.
 *****************************************************************************/
868
static void* WaveOutThread( void *data )
gbazin's avatar
 
gbazin committed
869
{
870
    aout_instance_t *p_aout = data;
gbazin's avatar
gbazin committed
871
    aout_sys_t *p_sys = p_aout->output.p_sys;
gbazin's avatar
 
gbazin committed
872
    aout_buffer_t *p_buffer = NULL;
gbazin's avatar
gbazin committed
873
    WAVEHDR *p_waveheader = p_sys->waveheader;
gbazin's avatar
 
gbazin committed
874
    int i, i_queued_frames;
875
    bool b_sleek;
876 877
    mtime_t next_date;
    uint32_t i_buffer_length = 64;
878
    int canc = vlc_savecancel ();
gbazin's avatar
 
gbazin committed
879 880

    /* We don't want any resampling when using S/PDIF */
881
    b_sleek = p_aout->output.output.i_format == VLC_CODEC_SPDIFL;
gbazin's avatar
&