waveout.c 40.1 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>
Clément Stenac's avatar
Clément Stenac committed
35
#include <vlc_aout.h>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
36
#include <vlc_charset.h>                        /* FromLocaleDup, LocaleFree */
37

38
#include "windows_audio_common.h"
39

40
#define FRAME_SIZE 4096              /* The size is in samples, not in bytes */
Gildas Bazin's avatar
 
Gildas Bazin committed
41

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

Gildas Bazin's avatar
 
Gildas Bazin committed
49 50 51 52 53 54 55 56 57 58
/*****************************************************************************
 * notification_thread_t: waveOut event thread
 *****************************************************************************/
typedef struct notification_thread_t
{
    VLC_COMMON_MEMBERS
    aout_instance_t *p_aout;

} notification_thread_t;

59
/* local functions */
Gildas Bazin's avatar
 
Gildas Bazin committed
60
static void Probe        ( aout_instance_t * );
61
static int OpenWaveOut   ( aout_instance_t *, uint32_t,
62
                           int, int, int, int, bool );
63
static int OpenWaveOutPCM( aout_instance_t *, uint32_t,
64
                           vlc_fourcc_t*, int, int, int, bool );
Gildas Bazin's avatar
 
Gildas Bazin committed
65
static int PlayWaveOut   ( aout_instance_t *, HWAVEOUT, WAVEHDR *,
66
                           aout_buffer_t *, bool );
Gildas Bazin's avatar
 
Gildas Bazin committed
67 68

static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD, DWORD, DWORD );
69
static void* WaveOutThread( vlc_object_t * );
Gildas Bazin's avatar
 
Gildas Bazin committed
70

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

73 74 75 76 77 78 79 80
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)";

81 82
static const char *const ppsz_adev[] = { "wavemapper", };
static const char *const ppsz_adev_text[] = { N_("Microsoft Soundmapper") };
83 84


85 86 87
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
88 89 90 91 92
#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")
93

94 95 96 97 98 99
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 )
100
    add_bool( "waveout-float32", true, FLOAT_TEXT, FLOAT_LONGTEXT, true )
101

102
    add_string( "waveout-audio-device", "wavemapper",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
103
                 DEVICE_TEXT, DEVICE_LONG, false )
104
       add_deprecated_alias( "waveout-dev" )   /* deprecated since 0.9.3 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
105
       change_string_list( ppsz_adev, ppsz_adev_text, ReloadWaveoutDevices )
106
       change_action_add( ReloadWaveoutDevices, N_("Refresh list") )
107

108 109
    set_callbacks( Open, Close )
vlc_module_end ()
110 111 112 113 114 115 116 117 118

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

121 122
    HWAVEOUT h_waveout;                        /* handle to waveout instance */

Gildas Bazin's avatar
 
Gildas Bazin committed
123
    WAVEFORMATEXTENSIBLE waveformat;                         /* audio format */
124

Gildas Bazin's avatar
 
Gildas Bazin committed
125
    WAVEHDR waveheader[FRAMES_NUM];
126

Gildas Bazin's avatar
 
Gildas Bazin committed
127 128
    notification_thread_t *p_notif;                      /* WaveOutThread id */
    HANDLE event;
129 130 131 132 133 134 135
    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;
Gildas Bazin's avatar
 
Gildas Bazin committed
136

Gildas Bazin's avatar
 
Gildas Bazin committed
137
    int i_buffer_size;
138

139
    uint8_t *p_silence_buffer;              /* buffer we use to play silence */
Gildas Bazin's avatar
 
Gildas Bazin committed
140

141
    bool b_chan_reorder;              /* do we need channel reordering */
142
    int pi_chan_table[AOUT_CHAN_MAX];
143 144 145 146 147 148 149 150
};

/*****************************************************************************
 * Open: open the audio device
 *****************************************************************************
 * This function opens and setups Win32 waveOut
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
Gildas Bazin's avatar
 
Gildas Bazin committed
151
{
Gildas Bazin's avatar
 
Gildas Bazin committed
152
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
Gildas Bazin's avatar
 
Gildas Bazin committed
153
    vlc_value_t val;
154 155

    /* Allocate structure */
Gildas Bazin's avatar
 
Gildas Bazin committed
156
    p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
157

Gildas Bazin's avatar
 
Gildas Bazin committed
158
    if( p_aout->output.p_sys == NULL )
Rémi Duraffort's avatar
Rémi Duraffort committed
159
        return VLC_ENOMEM;
160

Gildas Bazin's avatar
 
Gildas Bazin committed
161
    p_aout->output.pf_play = Play;
162
    p_aout->b_die = false;
163

164 165 166 167

    /*
     initialize/update Device selection List
    */
168
    ReloadWaveoutDevices( p_this, "waveout-audio-device", val, val, NULL);
169 170 171 172 173


    /*
      check for configured audio device!
    */
174
    char *psz_waveout_dev = var_CreateGetString( p_aout, "waveout-audio-device");
175 176 177 178 179 180 181 182 183 184 185 186 187

    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 );
       }
    }
188
    free( psz_waveout_dev );
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207


    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 );
    }



Gildas Bazin's avatar
 
Gildas Bazin committed
208
    if( var_Type( p_aout, "audio-device" ) == 0 )
Gildas Bazin's avatar
 
Gildas Bazin committed
209
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
210
        Probe( p_aout );
Gildas Bazin's avatar
 
Gildas Bazin committed
211
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
212 213

    if( var_Get( p_aout, "audio-device", &val ) < 0 )
Gildas Bazin's avatar
 
Gildas Bazin committed
214
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
215
        /* Probe() has failed. */
216
        var_Destroy( p_aout, "waveout-audio-device");
Gildas Bazin's avatar
 
Gildas Bazin committed
217 218
        free( p_aout->output.p_sys );
        return VLC_EGENERIC;
Gildas Bazin's avatar
 
Gildas Bazin committed
219 220
    }

221

Gildas Bazin's avatar
 
Gildas Bazin committed
222
    /* Open the device */
Gildas Bazin's avatar
 
Gildas Bazin committed
223
    if( val.i_int == AOUT_VAR_SPDIF )
Gildas Bazin's avatar
 
Gildas Bazin committed
224
    {
225
        p_aout->output.output.i_format = VLC_CODEC_SPDIFL;
Gildas Bazin's avatar
 
Gildas Bazin committed
226

227 228
        if( OpenWaveOut( p_aout,
                         p_aout->output.p_sys->i_wave_device_id,
229
                         VLC_CODEC_SPDIFL,
Gildas Bazin's avatar
 
Gildas Bazin committed
230 231
                         p_aout->output.output.i_physical_channels,
                         aout_FormatNbChannels( &p_aout->output.output ),
232
                         p_aout->output.output.i_rate, false )
Gildas Bazin's avatar
 
Gildas Bazin committed
233 234 235 236 237 238
            != VLC_SUCCESS )
        {
            msg_Err( p_aout, "cannot open waveout audio device" );
            free( p_aout->output.p_sys );
            return VLC_EGENERIC;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
239 240 241 242 243 244 245 246 247

        /* 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 );
Gildas Bazin's avatar
 
Gildas Bazin committed
248 249 250
    }
    else
    {
251 252
        WAVEOUTCAPS wocaps;

253
        switch( val.i_int )
Gildas Bazin's avatar
 
Gildas Bazin committed
254
        {
255
        case AOUT_VAR_5_1:
Gildas Bazin's avatar
 
Gildas Bazin committed
256
            p_aout->output.output.i_physical_channels
257 258 259 260 261
                    = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
                      | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
                      | AOUT_CHAN_LFE;
            break;
        case AOUT_VAR_2F2R:
Gildas Bazin's avatar
 
Gildas Bazin committed
262
            p_aout->output.output.i_physical_channels
263 264 265 266
                    = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
                      | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
            break;
        case AOUT_VAR_MONO:
Gildas Bazin's avatar
 
Gildas Bazin committed
267
            p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
268 269
            break;
        default:
Gildas Bazin's avatar
 
Gildas Bazin committed
270
            p_aout->output.output.i_physical_channels
271
                    = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
Gildas Bazin's avatar
 
Gildas Bazin committed
272
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
273

274 275 276
        if( OpenWaveOutPCM( p_aout,
                            p_aout->output.p_sys->i_wave_device_id,
                            &p_aout->output.output.i_format,
Gildas Bazin's avatar
 
Gildas Bazin committed
277 278
                            p_aout->output.output.i_physical_channels,
                            aout_FormatNbChannels( &p_aout->output.output ),
279
                            p_aout->output.output.i_rate, false )
Gildas Bazin's avatar
 
Gildas Bazin committed
280
            != VLC_SUCCESS )
Gildas Bazin's avatar
 
Gildas Bazin committed
281
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
282
            msg_Err( p_aout, "cannot open waveout audio device" );
Gildas Bazin's avatar
 
Gildas Bazin committed
283
            free( p_aout->output.p_sys );
284
            return VLC_EGENERIC;
Gildas Bazin's avatar
 
Gildas Bazin committed
285
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
286 287 288 289 290 291 292 293

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

295 296
        /* Check for hardware volume support */
        if( waveOutGetDevCaps( (UINT_PTR)p_aout->output.p_sys->h_waveout,
297 298
                               &wocaps, sizeof(wocaps) ) == MMSYSERR_NOERROR &&
            wocaps.dwSupport & WAVECAPS_VOLUME )
299 300 301 302 303 304 305 306
        {
            DWORD i_dummy;
            if( waveOutGetVolume( p_aout->output.p_sys->h_waveout, &i_dummy )
                == MMSYSERR_NOERROR )
            {
                p_aout->output.pf_volume_set = VolumeSet;
            }
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
307 308
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
309

Gildas Bazin's avatar
 
Gildas Bazin committed
310 311
    waveOutReset( p_aout->output.p_sys->h_waveout );

Gildas Bazin's avatar
 
Gildas Bazin committed
312
    /* Allocate silence buffer */
313 314
    p_aout->output.p_sys->p_silence_buffer =
        malloc( p_aout->output.p_sys->i_buffer_size );
Gildas Bazin's avatar
 
Gildas Bazin committed
315 316
    if( p_aout->output.p_sys->p_silence_buffer == NULL )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
317
        free( p_aout->output.p_sys );
318
        return VLC_ENOMEM;
Gildas Bazin's avatar
 
Gildas Bazin committed
319
    }
320 321
    p_aout->output.p_sys->i_repeat_counter = 0;

Gildas Bazin's avatar
 
Gildas Bazin committed
322 323

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

Gildas Bazin's avatar
 
Gildas Bazin committed
327 328 329 330 331
    /* Now we need to setup our waveOut 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->event = CreateEvent( NULL, FALSE, FALSE, NULL );
332 333 334 335 336 337 338
    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;

Gildas Bazin's avatar
 
Gildas Bazin committed
339 340 341

    /* Then launch the notification thread */
    if( vlc_thread_create( p_aout->output.p_sys->p_notif,
342
                           WaveOutThread, VLC_THREAD_PRIORITY_OUTPUT ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
343 344 345 346
    {
        msg_Err( p_aout, "cannot create WaveOutThread" );
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
347 348
    /* We need to kick off the playback in order to have the callback properly
     * working */
349
    for( int i = 0; i < FRAMES_NUM; i++ )
Gildas Bazin's avatar
 
Gildas Bazin committed
350
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
351
        p_aout->output.p_sys->waveheader[i].dwFlags = WHDR_DONE;
352
        p_aout->output.p_sys->waveheader[i].dwUser = 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
353
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
354

355
    return VLC_SUCCESS;
356 357
}

Gildas Bazin's avatar
 
Gildas Bazin committed
358 359 360 361 362
/*****************************************************************************
 * Probe: probe the audio device for available formats and channels
 *****************************************************************************/
static void Probe( aout_instance_t * p_aout )
{
Gildas Bazin's avatar
 
Gildas Bazin committed
363
    vlc_value_t val, text;
364
    vlc_fourcc_t i_format;
Gildas Bazin's avatar
 
Gildas Bazin committed
365 366
    unsigned int i_physical_channels;

Gildas Bazin's avatar
 
Gildas Bazin committed
367
    var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
368
    text.psz_string = _("Audio Device");
Gildas Bazin's avatar
 
Gildas Bazin committed
369
    var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
Gildas Bazin's avatar
 
Gildas Bazin committed
370 371 372 373 374 375 376

    /* 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 )
    {
377 378 379
        if( OpenWaveOutPCM( p_aout,
                            p_aout->output.p_sys->i_wave_device_id,
                            &i_format,
Gildas Bazin's avatar
 
Gildas Bazin committed
380
                            i_physical_channels, 6,
381
                            p_aout->output.output.i_rate, true )
Gildas Bazin's avatar
 
Gildas Bazin committed
382 383
            == VLC_SUCCESS )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
384
            val.i_int = AOUT_VAR_5_1;
385
            text.psz_string = (char *)_("5.1");
Gildas Bazin's avatar
 
Gildas Bazin committed
386 387
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
Gildas Bazin's avatar
 
Gildas Bazin committed
388
            msg_Dbg( p_aout, "device supports 5.1 channels" );
Gildas Bazin's avatar
 
Gildas Bazin committed
389
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
390 391 392 393 394
    }

    /* Test for 2 Front 2 Rear support */
    i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
                          AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
Gildas Bazin's avatar
 
Gildas Bazin committed
395 396
    if( ( p_aout->output.output.i_physical_channels & i_physical_channels )
        == i_physical_channels )
Gildas Bazin's avatar
 
Gildas Bazin committed
397
    {
398 399 400
        if( OpenWaveOutPCM( p_aout,
                            p_aout->output.p_sys->i_wave_device_id,
                            &i_format,
Gildas Bazin's avatar
 
Gildas Bazin committed
401
                            i_physical_channels, 4,
402
                            p_aout->output.output.i_rate, true )
Gildas Bazin's avatar
 
Gildas Bazin committed
403 404
            == VLC_SUCCESS )
        {
Gildas Bazin's avatar
 
Gildas Bazin committed
405
            val.i_int = AOUT_VAR_2F2R;
406
            text.psz_string = (char *)_("2 Front 2 Rear");
Gildas Bazin's avatar
 
Gildas Bazin committed
407 408
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
Gildas Bazin's avatar
 
Gildas Bazin committed
409
            msg_Dbg( p_aout, "device supports 4 channels" );
Gildas Bazin's avatar
 
Gildas Bazin committed
410
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
411 412 413 414
    }

    /* Test for stereo support */
    i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
415 416 417
    if( OpenWaveOutPCM( p_aout,
                        p_aout->output.p_sys->i_wave_device_id,
                        &i_format,
Gildas Bazin's avatar
 
Gildas Bazin committed
418
                        i_physical_channels, 2,
419
                        p_aout->output.output.i_rate, true )
Gildas Bazin's avatar
 
Gildas Bazin committed
420 421
        == VLC_SUCCESS )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
422
        val.i_int = AOUT_VAR_STEREO;
423
        text.psz_string = (char *)_("Stereo");
Gildas Bazin's avatar
 
Gildas Bazin committed
424
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
Gildas Bazin's avatar
 
Gildas Bazin committed
425
        msg_Dbg( p_aout, "device supports 2 channels" );
Gildas Bazin's avatar
 
Gildas Bazin committed
426
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
427 428 429

    /* Test for mono support */
    i_physical_channels = AOUT_CHAN_CENTER;
430 431 432
    if( OpenWaveOutPCM( p_aout,
                        p_aout->output.p_sys->i_wave_device_id,
                        &i_format,
Gildas Bazin's avatar
 
Gildas Bazin committed
433
                        i_physical_channels, 1,
434
                        p_aout->output.output.i_rate, true )
Gildas Bazin's avatar
 
Gildas Bazin committed
435 436
        == VLC_SUCCESS )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
437
        val.i_int = AOUT_VAR_MONO;
438
        text.psz_string = (char *)_("Mono");
Gildas Bazin's avatar
 
Gildas Bazin committed
439
        var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
Gildas Bazin's avatar
 
Gildas Bazin committed
440
        msg_Dbg( p_aout, "device supports 1 channel" );
Gildas Bazin's avatar
 
Gildas Bazin committed
441
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
442

Gildas Bazin's avatar
 
Gildas Bazin committed
443 444 445
    /* Test for SPDIF support */
    if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
    {
446 447
        if( OpenWaveOut( p_aout,
                         p_aout->output.p_sys->i_wave_device_id,
448
                         VLC_CODEC_SPDIFL,
Gildas Bazin's avatar
 
Gildas Bazin committed
449 450
                         p_aout->output.output.i_physical_channels,
                         aout_FormatNbChannels( &p_aout->output.output ),
451
                         p_aout->output.output.i_rate, true )
Gildas Bazin's avatar
 
Gildas Bazin committed
452 453 454
            == VLC_SUCCESS )
        {
            msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
Gildas Bazin's avatar
 
Gildas Bazin committed
455
            val.i_int = AOUT_VAR_SPDIF;
456
            text.psz_string = (char *)_("A/52 over S/PDIF");
Gildas Bazin's avatar
 
Gildas Bazin committed
457 458
            var_Change( p_aout, "audio-device",
                        VLC_VAR_ADDCHOICE, &val, &text );
459
            if( var_InheritBool( p_aout, "spdif" ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
460 461 462 463
                var_Set( p_aout, "audio-device", val );
        }
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
464 465 466 467 468 469 470 471
    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;
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
472
    var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
473
    var_TriggerCallback( p_aout, "intf-change" );
Gildas Bazin's avatar
 
Gildas Bazin committed
474 475
}

476 477 478
/*****************************************************************************
 * Play: play a sound buffer
 *****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
479 480
 * This doesn't actually play the buffer. This just stores the buffer so it
 * can be played by the callback thread.
481
 *****************************************************************************/
Gildas Bazin's avatar
 
Gildas Bazin committed
482
static void Play( aout_instance_t *_p_aout )
483
{
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
    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 =
            aout_FifoFirstDate( _p_aout, &_p_aout->output.fifo );

        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 );
    }
499 500 501 502 503 504
}

/*****************************************************************************
 * Close: close the audio device
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
Gildas Bazin's avatar
 
Gildas Bazin committed
505
{
Gildas Bazin's avatar
 
Gildas Bazin committed
506
    aout_instance_t *p_aout = (aout_instance_t *)p_this;
507
    aout_sys_t *p_sys = p_aout->output.p_sys;
508 509

    /* Before calling waveOutClose we must reset the device */
510
    vlc_object_kill( p_aout );
Gildas Bazin's avatar
 
Gildas Bazin committed
511

512
    /* wake up the audio thread, to recognize that p_aout died */
513
    SetEvent( p_sys->event );
514 515
    SetEvent( p_sys->new_buffer_event );

516
    vlc_thread_join( p_sys->p_notif );
517
    vlc_object_release( p_sys->p_notif );
Gildas Bazin's avatar
 
Gildas Bazin committed
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 555 556 557 558 559 560 561
    /*
      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 */
562
    if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
563 564 565 566
    {
        msg_Err( p_aout, "waveOutClose failed" );
    }

567 568 569 570 571 572 573
    /*
      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);

574 575
    free( p_sys->p_silence_buffer );
    free( p_sys );
576 577 578
}

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
579
 * OpenWaveOut: open the waveout sound device
580
 ****************************************************************************/
581
static int OpenWaveOut( aout_instance_t *p_aout, uint32_t i_device_id, int i_format,
Gildas Bazin's avatar
 
Gildas Bazin committed
582
                        int i_channels, int i_nb_channels, int i_rate,
583
                        bool b_probe )
584 585 586 587
{
    MMRESULT result;

    /* Set sound format */
Gildas Bazin's avatar
 
Gildas Bazin committed
588 589 590 591

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

    waveformat.dwChannelMask = 0;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
592
    for( unsigned i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ )
Gildas Bazin's avatar
 
Gildas Bazin committed
593
    {
594 595
        if( i_channels & pi_channels_src[i] )
            waveformat.dwChannelMask |= pi_channels_in[i];
Gildas Bazin's avatar
 
Gildas Bazin committed
596
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
597 598 599

    switch( i_format )
    {
600
    case VLC_CODEC_SPDIFL:
Gildas Bazin's avatar
 
Gildas Bazin committed
601
        i_nb_channels = 2;
Gildas Bazin's avatar
 
Gildas Bazin committed
602 603
        /* To prevent channel re-ordering */
        waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
Gildas Bazin's avatar
 
Gildas Bazin committed
604 605 606 607
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
Gildas Bazin's avatar
 
Gildas Bazin committed
608
        waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
Gildas Bazin's avatar
 
Gildas Bazin committed
609 610
        break;

611
    case VLC_CODEC_FL32:
Gildas Bazin's avatar
 
Gildas Bazin committed
612 613 614 615
        waveformat.Format.wBitsPerSample = sizeof(float) * 8;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
Gildas Bazin's avatar
 
Gildas Bazin committed
616
        waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
Gildas Bazin's avatar
 
Gildas Bazin committed
617
        break;
Gildas Bazin's avatar
 
Gildas Bazin committed
618

619
    case VLC_CODEC_S16L:
Gildas Bazin's avatar
 
Gildas Bazin committed
620 621 622 623
        waveformat.Format.wBitsPerSample = 16;
        waveformat.Samples.wValidBitsPerSample =
            waveformat.Format.wBitsPerSample;
        waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
Gildas Bazin's avatar
 
Gildas Bazin committed
624
        waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_PCM;
Gildas Bazin's avatar
 
Gildas Bazin committed
625 626 627
        break;
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
    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);
    }
646

647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
    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);
    }

671
    /* Open the device */
672
    result = waveOutOpen( &p_aout->output.p_sys->h_waveout, i_device_id,
Gildas Bazin's avatar
 
Gildas Bazin committed
673
                          (WAVEFORMATEX *)&waveformat,
Gildas Bazin's avatar
 
Gildas Bazin committed
674
                          (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
Gildas Bazin's avatar
 
Gildas Bazin committed
675
                          CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) );
Gildas Bazin's avatar
 
Gildas Bazin committed
676 677
    if( result == WAVERR_BADFORMAT )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
678
        msg_Warn( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" );
Gildas Bazin's avatar
 
Gildas Bazin committed
679 680 681 682 683 684
        return VLC_EGENERIC;
    }
    if( result == MMSYSERR_ALLOCATED )
    {
        msg_Warn( p_aout, "waveOutOpen failed WAVERR_ALLOCATED" );
        return VLC_EGENERIC;
Gildas Bazin's avatar
 
Gildas Bazin committed
685
    }
686 687
    if( result != MMSYSERR_NOERROR )
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
688
        msg_Warn( p_aout, "waveOutOpen failed" );
Gildas Bazin's avatar
 
Gildas Bazin committed
689
        return VLC_EGENERIC;
Gildas Bazin's avatar
 
Gildas Bazin committed
690 691
    }

692 693 694 695 696 697 698 699 700
    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" );
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
701 702 703 704 705 706 707 708 709 710

    return VLC_SUCCESS;

#undef waveformat

}

/*****************************************************************************
 * OpenWaveOutPCM: open a PCM waveout sound device
 ****************************************************************************/
711 712
static int OpenWaveOutPCM( aout_instance_t *p_aout, uint32_t i_device_id,
                           vlc_fourcc_t *i_format,
Gildas Bazin's avatar
 
Gildas Bazin committed
713
                           int i_channels, int i_nb_channels, int i_rate,
714
                           bool b_probe )
Gildas Bazin's avatar
 
Gildas Bazin committed
715
{
716
    bool b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32");
717

718
    if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_CODEC_FL32,
719
                                   i_channels, i_nb_channels, i_rate, b_probe )
Gildas Bazin's avatar
 
Gildas Bazin committed
720 721
        != VLC_SUCCESS )
    {
722
        if ( OpenWaveOut( p_aout, i_device_id, VLC_CODEC_S16L,
Gildas Bazin's avatar
 
Gildas Bazin committed
723 724 725 726 727 728 729
                          i_channels, i_nb_channels, i_rate, b_probe )
             != VLC_SUCCESS )
        {
            return VLC_EGENERIC;
        }
        else
        {
730
            *i_format = VLC_CODEC_S16L;
Gildas Bazin's avatar
 
Gildas Bazin committed
731 732 733 734 735
            return VLC_SUCCESS;
        }
    }
    else
    {
736
        *i_format = VLC_CODEC_FL32;
Gildas Bazin's avatar
 
Gildas Bazin committed
737 738 739 740
        return VLC_SUCCESS;
    }
}

Gildas Bazin's avatar
 
Gildas Bazin committed
741 742 743 744
/*****************************************************************************
 * PlayWaveOut: play a buffer through the WaveOut device
 *****************************************************************************/
static int PlayWaveOut( aout_instance_t *p_aout, HWAVEOUT h_waveout,
745
                        WAVEHDR *p_waveheader, aout_buffer_t *p_buffer,
746
                        bool b_spdif)
Gildas Bazin's avatar
 
Gildas Bazin committed
747 748 749 750
{
    MMRESULT result;

    /* Prepare the buffer */
751
    if( p_buffer != NULL )
752
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
753
        p_waveheader->lpData = (LPSTR)p_buffer->p_buffer;
754 755 756 757 758 759 760 761
        /*
          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)
        {
762 763 764
           vlc_memcpy( p_aout->output.p_sys->p_silence_buffer,
                       p_buffer->p_buffer,
                       p_aout->output.p_sys->i_buffer_size );
765 766 767
           p_aout->output.p_sys->i_repeat_counter = 2;
        }
    } else {
Gildas Bazin's avatar
 
Gildas Bazin committed
768
        /* Use silence buffer instead */
769 770 771 772 773
        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)
           {
774 775
               vlc_memset( p_aout->output.p_sys->p_silence_buffer,
                           0x00, p_aout->output.p_sys->i_buffer_size );
776 777
           }
        }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
778
        p_waveheader->lpData = (LPSTR)p_aout->output.p_sys->p_silence_buffer;
779
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
780

781
    p_waveheader->dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
Gildas Bazin's avatar
 
Gildas Bazin committed
782 783 784 785 786 787 788
    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" );
Gildas Bazin's avatar
 
Gildas Bazin committed
789
        return VLC_EGENERIC;
Gildas Bazin's avatar
 
Gildas Bazin committed
790 791 792 793 794 795 796
    }

    /* 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" );
Gildas Bazin's avatar
 
Gildas Bazin committed
797
        return VLC_EGENERIC;
798 799
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
800
    return VLC_SUCCESS;
Gildas Bazin's avatar
 
Gildas Bazin committed
801 802 803 804 805 806 807 808 809
}

/*****************************************************************************
 * 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 )
{
Rémi Duraffort's avatar
Rémi Duraffort committed
810
    (void)h_waveout;    (void)dwParam1;    (void)dwParam2;
Gildas Bazin's avatar
 
Gildas Bazin committed
811
    aout_instance_t *p_aout = (aout_instance_t *)_p_aout;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
812
    int i_queued_frames = 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
813 814 815

    if( uMsg != WOM_DONE ) return;

816
    if( !vlc_object_alive (p_aout) ) return;
Gildas Bazin's avatar
 
Gildas Bazin committed
817

Gildas Bazin's avatar
 
Gildas Bazin committed
818
    /* Find out the current latency */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
819
    for( int i = 0; i < FRAMES_NUM; i++ )
Gildas Bazin's avatar
 
Gildas Bazin committed
820
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
821 822 823 824 825
        /* Check if frame buf is available */
        if( !(p_aout->output.p_sys->waveheader[i].dwFlags & WHDR_DONE) )
        {
            i_queued_frames++;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
826 827
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
828
    /* Don't wake up the thread too much */
829
    if( i_queued_frames <= FRAMES_NUM/2 )
Gildas Bazin's avatar
 
Gildas Bazin committed
830
        SetEvent( p_aout->output.p_sys->event );
831
}
Gildas Bazin's avatar
 
Gildas Bazin committed
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 861 862 863 864 865 866 867 868 869

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

Gildas Bazin's avatar
 
Gildas Bazin committed
870
/*****************************************************************************
871
 * WaveOutThread: this thread will capture play notification events.
Gildas Bazin's avatar
 
Gildas Bazin committed
872 873 874 875 876
 *****************************************************************************
 * 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.
 *****************************************************************************/
877
static void* WaveOutThread( vlc_object_t *p_this )
Gildas Bazin's avatar
 
Gildas Bazin committed
878
{
879
    notification_thread_t *p_notif = (notification_thread_t*)p_this;
Gildas Bazin's avatar
 
Gildas Bazin committed
880
    aout_instance_t *p_aout = p_notif->p_aout;
881
    aout_sys_t *p_sys = p_aout->output.p_sys;
Gildas Bazin's avatar
 
Gildas Bazin committed
882
    aout_buffer_t *p_buffer = NULL;
883
    WAVEHDR *p_waveheader = p_sys->waveheader;
Gildas Bazin's avatar
 
Gildas Bazin committed
884
    int i, i_queued_frames;
885
    bool b_sleek;
886 887
    mtime_t next_date;
    uint32_t i_buffer_length = 64;
888
    int canc = vlc_savecancel ();
Gildas Bazin's avatar
 
Gildas Bazin committed
889 890

    /* We don't want any resampling when using S/PDIF */
891
    b_sleek = p_aout->output.output.i_format == VLC_CODEC_SPDIFL;
Gildas Bazin's avatar
 
Gildas Bazin committed
892

893
    // wait for first call to "play()"
894
    while( !p_sys->start_date && vlc_object_alive (p_aout) )
895
           WaitForSingleObject( p_sys->event, INFINITE );
896
    if( !vlc_object_alive (p_aout) )
897
        return NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
898

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
899
    msg_Dbg( p_aout, "will start to play in %"PRId64" us",
900
             (p_sys->start_date - AOUT_PTS_TOLERANCE/4)-mdate());
901

902 903
    // than wait a short time... before grabbing first frames
    mwait( p_sys->start_date - AOUT_PTS_TOLERANCE/4 );
904

905 906 907 908 909 910 911
#define waveout_warn(msg) msg_Warn( p_aout, "aout_OutputNextBuffer no buffer "\
                           "got next_date=%d ms, "\
                           "%d frames to play, "\
                           "starving? %d, %s",(int)(next_date/(mtime_t)1000), \
                           i_queued_frames, \
                           p_aout->output.b_starving, msg);
    next_date = mdate();
912

913
    while( vlc_object_alive (p_aout) )
914 915 916
    {
        /* Cleanup and find out the current latency */
        i_queued_frames = WaveOutClearDoneBuffers( p_sys );
Gildas Bazin's avatar
 
Gildas Bazin committed
917

918
        if( !vlc_object_alive (p_aout) ) return NULL;
919

Gildas Bazin's avatar
 
Gildas Bazin committed
920 921 922 923
        /* Try to fill in as many frame buffers as possible */
        for( i = 0; i < FRAMES_NUM; i++ )
        {
            /* Check if frame buf is available */
924
            if( p_waveheader[i].dwFlags & WHDR_DONE )
Gildas Bazin's avatar
 
Gildas Bazin committed
925
            {
926 927 928 929 930 931 932 933
                // next_date = mdate() + 1000000 * i_queued_frames /
                //  p_aout->output.output.i_rate * p_aout->output.i_nb_samples;

                // the realtime has got our back-site:) to come in sync
                if(next_date < mdate())
                   next_date = mdate();


Gildas Bazin's avatar
 
Gildas Bazin committed
934 935
                /* Take into account the latency */
                p_buffer = aout_OutputNextBuffer( p_aout,
936
                    next_date,
Gildas Bazin's avatar
 
Gildas Bazin committed
937 938
                    b_sleek );

939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
                if(!p_buffer)
                {
#if 0
                    msg_Dbg( p_aout, "aout_OutputNextBuffer no buffer "\
                                      "got next_date=%d ms, "\
                                      "%d frames to play, "\
                                      "starving? %d",(int)(next_date/(mtime_t)1000),
                                                     i_queued_frames,
                                                     p_aout->output.b_starving);
#endif
                    if(p_aout->output.b_starving)
                    {
                        // means we are too early to request a new buffer?
                        waveout_warn("waiting...")
                        next_date = aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
                        mwait( next_date - AOUT_PTS_TOLERANCE/4 );
                        next_date = mdate();
                        p_buffer = aout_OutputNextBuffer( p_aout,
                                     next_date,
                                     b_sleek
                                   );
                    }
                }

Gildas Bazin's avatar
 
Gildas Bazin committed
963 964 965 966 967 968
                if( !p_buffer && i_queued_frames )
                {
                    /* We aren't late so no need to play a blank sample */
                    break;
                }

969 970
                if( p_buffer )
                {