]> git.sesse.net Git - vlc/blob - modules/audio_output/waveout.c
waveout: implement device_select (fixes #8491)
[vlc] / modules / audio_output / waveout.c
1 /*****************************************************************************
2  * waveout.c : Windows waveOut plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2001-2009 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *          AndrĂ© Weber
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <stdio.h>
34 #include <math.h>
35 #include <wchar.h>
36
37 #define UNICODE
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_aout.h>
41 #include <vlc_charset.h> /* FromWide() */
42 #include <vlc_atomic.h>
43
44 #include "windows_audio_common.h"
45
46 #define FRAME_SIZE 4096              /* The size is in samples, not in bytes */
47
48 /*****************************************************************************
49  * Local prototypes
50  *****************************************************************************/
51 static int  Open         ( vlc_object_t * );
52 static void Close        ( vlc_object_t * );
53 static void Play         ( audio_output_t *, block_t * );
54
55 /*****************************************************************************
56  * notification_thread_t: waveOut event thread
57  *****************************************************************************/
58 struct lkwavehdr
59 {
60     WAVEHDR hdr;
61     struct lkwavehdr * p_next;
62 };
63
64 /* local functions */
65 static int OpenWaveOut   ( audio_output_t *, uint32_t,
66                            int, int, int, int, bool );
67 static int OpenWaveOutPCM( audio_output_t *, uint32_t,
68                            vlc_fourcc_t*, int, int, int, bool );
69 static int PlayWaveOut   ( audio_output_t *, HWAVEOUT, struct lkwavehdr *,
70                            block_t *, bool );
71
72 static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR );
73
74 static void WaveOutClean( aout_sys_t * p_sys );
75
76 static void WaveOutClearBuffer( HWAVEOUT, WAVEHDR *);
77
78 static int ReloadWaveoutDevices( vlc_object_t *, const char *,
79                                  char ***, char *** );
80 static uint32_t findDeviceID(char *);
81 static int WaveOutTimeGet(audio_output_t * , mtime_t *);
82 static void WaveOutFlush( audio_output_t *, bool);
83 static void WaveOutPause( audio_output_t *, bool, mtime_t);
84 static int WaveoutVolumeSet(audio_output_t * p_aout, float volume);
85 static int WaveoutMuteSet(audio_output_t * p_aout, bool mute);
86
87 static void WaveoutPollVolume( void * );
88
89 static const wchar_t device_name_fmt[] = L"%ls ($%x,$%x)";
90
91 /*****************************************************************************
92  * aout_sys_t: waveOut audio output method descriptor
93  *****************************************************************************
94  * This structure is part of the audio output thread descriptor.
95  * It describes the waveOut specific properties of an audio device.
96  *****************************************************************************/
97
98 struct aout_sys_t
99 {
100     HWAVEOUT h_waveout;                        /* handle to waveout instance */
101
102     WAVEFORMATEXTENSIBLE waveformat;                         /* audio format */
103
104     size_t i_frames;
105
106     int i_repeat_counter;
107
108     int i_buffer_size;
109
110     int i_rate;
111
112     uint8_t *p_silence_buffer;              /* buffer we use to play silence */
113
114     float f_volume;
115
116     bool b_spdif;
117     bool b_mute;
118     bool b_soft;                            /* Use software gain */
119     uint8_t chans_to_reorder;              /* do we need channel reordering */
120
121     uint8_t chan_table[AOUT_CHAN_MAX];
122     vlc_fourcc_t format;
123
124     mtime_t i_played_length;
125
126     struct lkwavehdr * p_free_list;
127
128     vlc_mutex_t lock;
129     vlc_cond_t cond;
130     vlc_timer_t volume_poll_timer;
131 };
132
133 /*****************************************************************************
134  * Module descriptor
135  *****************************************************************************/
136 #define DEVICE_TEXT N_("Select Audio Device")
137 #define DEVICE_LONG N_("Select special Audio device, or let windows "\
138                        "decide (default), change needs VLC restart "\
139                        "to apply.")
140 #define DEFAULT_AUDIO_DEVICE N_("Default Audio Device")
141
142 #define AUDIO_CHAN_TEXT N_("Audio output channels")
143 #define AUDIO_CHAN_LONGTEXT N_("Channels available for audio output. " \
144     "If the input has more channels than the output, it will be down-mixed. " \
145     "This parameter is ignored when digital pass-through is active.")
146
147 #define VOLUME_TEXT N_("Audio volume")
148
149 vlc_module_begin ()
150     set_shortname( "WaveOut" )
151     set_description( N_("Win32 waveOut extension output") )
152     set_capability( "audio output", 50 )
153     set_category( CAT_AUDIO )
154     set_subcategory( SUBCAT_AUDIO_AOUT )
155     add_string( "waveout-audio-device", "wavemapper",
156                  DEVICE_TEXT, DEVICE_LONG, false )
157        change_string_cb( ReloadWaveoutDevices )
158     add_float( "waveout-volume", 1.0f, VOLUME_TEXT, NULL, true )
159          change_float_range(0.0f, 2.0f)
160     add_bool( "waveout-float32", true, FLOAT_TEXT, FLOAT_LONGTEXT, true )
161     add_integer ("waveout-audio-channels", 6, AUDIO_CHAN_TEXT,
162                  AUDIO_CHAN_LONGTEXT, false)
163         change_integer_range(1,6)
164     set_callbacks( Open, Close )
165 vlc_module_end ()
166
167 /*****************************************************************************
168  * Opens the audio device
169  *****************************************************************************
170  * This function opens and setups Win32 waveOut
171  *****************************************************************************/
172 static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
173 {
174     p_aout->time_get = WaveOutTimeGet;
175     p_aout->play = Play;
176     p_aout->pause = WaveOutPause;
177     p_aout->flush = WaveOutFlush;
178
179     /* Default behaviour is to use software gain */
180     p_aout->sys->b_soft = true;
181
182     /*
183       check for configured audio device!
184     */
185     fmt->i_format = var_InheritBool( p_aout, "waveout-float32" )?
186         VLC_CODEC_FL32: VLC_CODEC_S16N;
187
188     char *dev = var_GetNonEmptyString( p_aout, "waveout-audio-device");
189     uint32_t devid = findDeviceID( dev );
190
191     if(devid == WAVE_MAPPER && dev != NULL && stricmp(dev,"wavemapper"))
192         msg_Warn( p_aout, "configured audio device '%s' not available, "
193                           "using default instead", dev );
194     free( dev );
195
196     WAVEOUTCAPS waveoutcaps;
197     if(waveOutGetDevCaps( devid, &waveoutcaps,
198                           sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
199     {
200       /* log debug some infos about driver, to know who to blame
201          if it doesn't work */
202         msg_Dbg( p_aout, "Drivername: %ls", waveoutcaps.szPname);
203         msg_Dbg( p_aout, "Driver Version: %d.%d",
204                           (waveoutcaps.vDriverVersion>>8)&255,
205                           waveoutcaps.vDriverVersion & 255);
206         msg_Dbg( p_aout, "Manufacturer identifier: 0x%x", waveoutcaps.wMid );
207         msg_Dbg( p_aout, "Product identifier: 0x%x", waveoutcaps.wPid );
208     }
209
210
211
212     /* Open the device */
213     if( AOUT_FMT_SPDIF(fmt) && var_InheritBool (p_aout, "spdif") )
214     {
215
216         if( OpenWaveOut( p_aout, devid, VLC_CODEC_SPDIFL,
217                          fmt->i_physical_channels,
218                          aout_FormatNbChannels( fmt ), fmt->i_rate, false )
219             == VLC_SUCCESS )
220         {
221             fmt->i_format = VLC_CODEC_SPDIFL;
222
223             /* Calculate the frame size in bytes */
224             fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
225             fmt->i_frame_length = A52_FRAME_NB;
226             p_aout->sys->i_buffer_size = fmt->i_bytes_per_frame;
227             p_aout->sys->b_spdif = true;
228
229         }
230         else
231             msg_Err( p_aout,
232                      "cannot open waveout audio device for spdif fallback to PCM" );
233     }
234
235     if( fmt->i_format != VLC_CODEC_SPDIFL )
236     {
237         int max_chan = var_InheritInteger( p_aout, "waveout-audio-channels");
238         int i_channels = aout_FormatNbChannels(fmt);
239         i_channels = ( i_channels < max_chan )? i_channels: max_chan;
240         do
241         {
242             switch(i_channels)
243             {
244                 case 6:
245                     fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
246                         | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT
247                         | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
248                     break;
249                 case 5:
250                     fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
251                         | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
252                         | AOUT_CHAN_LFE;
253                     break;
254                 case 4:
255                     fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
256                         | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
257                     break;
258                 case 3:
259                     fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
260                         | AOUT_CHAN_LFE;
261                     break;
262                 case 2:
263                     fmt->i_physical_channels = AOUT_CHANS_STEREO;
264                     break;
265                 case 1:
266                 default:
267                     fmt->i_physical_channels = AOUT_CHAN_CENTER;
268             }
269             msg_Dbg( p_aout, "Trying %d channels", i_channels );
270         }
271         while( ( OpenWaveOutPCM( p_aout, devid, &fmt->i_format,
272                                  fmt->i_physical_channels, i_channels,
273                                  fmt->i_rate, false ) != VLC_SUCCESS ) &&
274                --i_channels );
275
276         if( !i_channels )
277         {
278             msg_Err(p_aout, "Waveout couldn't find appropriate channel mapping");
279             return VLC_EGENERIC;
280         }
281
282         /* Calculate the frame size in bytes */
283         aout_FormatPrepare( fmt );
284         p_aout->sys->i_buffer_size = FRAME_SIZE * fmt->i_bytes_per_frame;
285
286         if( waveoutcaps.dwSupport & WAVECAPS_VOLUME )
287         {
288             aout_GainRequest( p_aout, 1.0f );
289             p_aout->sys->b_soft = false;
290         }
291
292         WaveoutMuteSet( p_aout, p_aout->sys->b_mute );
293
294         p_aout->sys->b_spdif = false;
295     }
296
297     p_aout->sys->i_rate = fmt->i_rate;
298
299     waveOutReset( p_aout->sys->h_waveout );
300
301     /* Allocate silence buffer */
302     p_aout->sys->p_silence_buffer =
303         malloc( p_aout->sys->i_buffer_size );
304     if( p_aout->sys->p_silence_buffer == NULL )
305     {
306         msg_Err( p_aout, "Couldn't alloc silence buffer... aborting");
307         return VLC_ENOMEM;
308     }
309     p_aout->sys->i_repeat_counter = 0;
310
311
312     /* Zero the buffer. WinCE doesn't have calloc(). */
313     memset( p_aout->sys->p_silence_buffer, 0,
314             p_aout->sys->i_buffer_size );
315
316     /* Now we need to setup our waveOut play notification structure */
317     p_aout->sys->i_frames = 0;
318     p_aout->sys->i_played_length = 0;
319     p_aout->sys->p_free_list = NULL;
320
321     return VLC_SUCCESS;
322 }
323
324 /*****************************************************************************
325  * Play: play a sound buffer
326  *****************************************************************************
327  * This doesn't actually play the buffer. This just stores the buffer so it
328  * can be played by the callback thread.
329  *****************************************************************************/
330 static void Play( audio_output_t *p_aout, block_t *block )
331 {
332     struct lkwavehdr * p_waveheader =
333         (struct lkwavehdr *) malloc(sizeof(struct lkwavehdr));
334     if(!p_waveheader)
335     {
336         msg_Err(p_aout, "Couldn't alloc WAVEHDR");
337         if( block )
338             block_Release( block );
339         return;
340     }
341
342     p_waveheader->p_next = NULL;
343
344     if( block && p_aout->sys->chans_to_reorder )
345     {
346         aout_ChannelReorder( block->p_buffer, block->i_buffer,
347                              p_aout->sys->waveformat.Format.nChannels,
348                              p_aout->sys->chan_table, p_aout->sys->format );
349     }
350     while( PlayWaveOut( p_aout, p_aout->sys->h_waveout, p_waveheader, block,
351                         p_aout->sys->b_spdif ) != VLC_SUCCESS )
352
353     {
354         msg_Warn( p_aout, "Couln't write frame... sleeping");
355         msleep( block->i_length );
356     }
357
358     WaveOutClean( p_aout->sys );
359     WaveoutPollVolume( p_aout );
360
361     vlc_mutex_lock( &p_aout->sys->lock );
362     p_aout->sys->i_frames++;
363     p_aout->sys->i_played_length += block->i_length;
364     vlc_mutex_unlock( &p_aout->sys->lock );
365 }
366
367 /*****************************************************************************
368  * Close: close the audio device
369  *****************************************************************************/
370 static void Stop( audio_output_t *p_aout )
371 {
372     aout_sys_t *p_sys = p_aout->sys;
373
374     /* Before calling waveOutClose we must reset the device */
375     MMRESULT result = waveOutReset( p_sys->h_waveout );
376     if(result != MMSYSERR_NOERROR)
377     {
378        msg_Err( p_aout, "waveOutReset failed 0x%x", result );
379        /*
380         now we must wait, that all buffers are played
381         because cancel doesn't work in this case...
382        */
383        if(result == MMSYSERR_NOTSUPPORTED)
384        {
385            /*
386              clear currently played (done) buffers,
387              if returnvalue > 0 (means some buffer still playing)
388              wait for the driver event callback that one buffer
389              is finished with playing, and check again
390              the timeout of 5000ms is just, an emergency exit
391              of this loop, to avoid deadlock in case of other
392              (currently not known bugs, problems, errors cases?)
393            */
394             WaveOutFlush( p_aout, true );
395        }
396     }
397
398     /* wait for the frames to be queued in cleaning list */
399     WaveOutFlush( p_aout, true );
400     WaveOutClean( p_aout->sys );
401
402     /* now we can Close the device */
403     if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
404     {
405         msg_Err( p_aout, "waveOutClose failed" );
406     }
407
408     free( p_sys->p_silence_buffer );
409     p_aout->sys->i_played_length = 0;
410     p_sys->b_soft = true;
411 }
412
413 /*****************************************************************************
414  * OpenWaveOut: open the waveout sound device
415  ****************************************************************************/
416 static int OpenWaveOut( audio_output_t *p_aout, uint32_t i_device_id, int i_format,
417                         int i_channels, int i_nb_channels, int i_rate,
418                         bool b_probe )
419 {
420     MMRESULT result;
421
422     /* Set sound format */
423
424 #define waveformat p_aout->sys->waveformat
425
426     waveformat.dwChannelMask = 0;
427     for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ )
428         if( i_channels & pi_vlc_chan_order_wg4[i] )
429             waveformat.dwChannelMask |= pi_channels_in[i];
430
431     switch( i_format )
432     {
433     case VLC_CODEC_SPDIFL:
434         i_nb_channels = 2;
435         /* To prevent channel re-ordering */
436         waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
437         waveformat.Format.wBitsPerSample = 16;
438         waveformat.Samples.wValidBitsPerSample =
439             waveformat.Format.wBitsPerSample;
440         waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
441         waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
442         break;
443
444     case VLC_CODEC_FL32:
445         waveformat.Format.wBitsPerSample = sizeof(float) * 8;
446         waveformat.Samples.wValidBitsPerSample =
447             waveformat.Format.wBitsPerSample;
448         waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
449         waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
450         break;
451
452     case VLC_CODEC_S16N:
453         waveformat.Format.wBitsPerSample = 16;
454         waveformat.Samples.wValidBitsPerSample =
455             waveformat.Format.wBitsPerSample;
456         waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
457         waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_PCM;
458         break;
459     }
460
461     waveformat.Format.nChannels = i_nb_channels;
462     waveformat.Format.nSamplesPerSec = i_rate;
463     waveformat.Format.nBlockAlign =
464         waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
465     waveformat.Format.nAvgBytesPerSec =
466         waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
467
468     /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
469     if( i_nb_channels <= 2 )
470     {
471         waveformat.Format.cbSize = 0;
472     }
473     else
474     {
475         waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
476         waveformat.Format.cbSize =
477             sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
478     }
479
480     if(!b_probe) {
481         msg_Dbg( p_aout, "OpenWaveDevice-ID: %u", i_device_id);
482         msg_Dbg( p_aout,"waveformat.Format.cbSize          = %d",
483                  waveformat.Format.cbSize);
484         msg_Dbg( p_aout,"waveformat.Format.wFormatTag      = %u",
485                  waveformat.Format.wFormatTag);
486         msg_Dbg( p_aout,"waveformat.Format.nChannels       = %u",
487                  waveformat.Format.nChannels);
488         msg_Dbg( p_aout,"waveformat.Format.nSamplesPerSec  = %d",
489                  (int)waveformat.Format.nSamplesPerSec);
490         msg_Dbg( p_aout,"waveformat.Format.nAvgBytesPerSec = %u",
491                  (int)waveformat.Format.nAvgBytesPerSec);
492         msg_Dbg( p_aout,"waveformat.Format.nBlockAlign     = %d",
493                  waveformat.Format.nBlockAlign);
494         msg_Dbg( p_aout,"waveformat.Format.wBitsPerSample  = %d",
495                  waveformat.Format.wBitsPerSample);
496         msg_Dbg( p_aout,"waveformat.Samples.wValidBitsPerSample = %d",
497                  waveformat.Samples.wValidBitsPerSample);
498         msg_Dbg( p_aout,"waveformat.Samples.wSamplesPerBlock = %d",
499                  waveformat.Samples.wSamplesPerBlock);
500         msg_Dbg( p_aout,"waveformat.dwChannelMask          = %lx",
501                  waveformat.dwChannelMask);
502     }
503
504     /* Open the device */
505     result = waveOutOpen( &p_aout->sys->h_waveout, i_device_id,
506                           (WAVEFORMATEX *)&waveformat,
507                           (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
508                           CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) );
509     if( result == WAVERR_BADFORMAT )
510     {
511         msg_Warn( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" );
512         return VLC_EGENERIC;
513     }
514     if( result == MMSYSERR_ALLOCATED )
515     {
516         msg_Warn( p_aout, "waveOutOpen failed WAVERR_ALLOCATED" );
517         return VLC_EGENERIC;
518     }
519     if( result != MMSYSERR_NOERROR )
520     {
521         msg_Warn( p_aout, "waveOutOpen failed" );
522         return VLC_EGENERIC;
523     }
524
525     p_aout->sys->chans_to_reorder =
526         aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
527                                   waveformat.dwChannelMask,
528                                   p_aout->sys->chan_table );
529     if( p_aout->sys->chans_to_reorder )
530         msg_Dbg( p_aout, "channel reordering needed" );
531     p_aout->sys->format = i_format;
532
533     return VLC_SUCCESS;
534
535 #undef waveformat
536
537 }
538
539 /*****************************************************************************
540  * OpenWaveOutPCM: open a PCM waveout sound device
541  ****************************************************************************/
542 static int OpenWaveOutPCM( audio_output_t *p_aout, uint32_t i_device_id,
543                            vlc_fourcc_t *i_format,
544                            int i_channels, int i_nb_channels, int i_rate,
545                            bool b_probe )
546 {
547     bool b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32");
548
549     if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_CODEC_FL32,
550                                    i_channels, i_nb_channels, i_rate, b_probe )
551         != VLC_SUCCESS )
552     {
553         if ( OpenWaveOut( p_aout, i_device_id, VLC_CODEC_S16N,
554                           i_channels, i_nb_channels, i_rate, b_probe )
555              != VLC_SUCCESS )
556         {
557             return VLC_EGENERIC;
558         }
559         else
560         {
561             *i_format = VLC_CODEC_S16N;
562             return VLC_SUCCESS;
563         }
564     }
565     else
566     {
567         *i_format = VLC_CODEC_FL32;
568         return VLC_SUCCESS;
569     }
570 }
571
572 /*****************************************************************************
573  * PlayWaveOut: play a buffer through the WaveOut device
574  *****************************************************************************/
575 static int PlayWaveOut( audio_output_t *p_aout, HWAVEOUT h_waveout,
576                         struct lkwavehdr *p_waveheader, block_t *p_buffer, bool b_spdif)
577 {
578     MMRESULT result;
579
580     /* Prepare the buffer */
581     if( p_buffer != NULL )
582     {
583         p_waveheader->hdr.lpData = (LPSTR)p_buffer->p_buffer;
584         p_waveheader->hdr.dwBufferLength = p_buffer->i_buffer;
585         /*
586           copy the buffer to the silence buffer :) so in case we don't
587           get the next buffer fast enough (I will repeat this one a time
588           for AC3 / DTS and SPDIF this will sound better instead of
589           a hickup)
590         */
591         if(b_spdif)
592         {
593            memcpy( p_aout->sys->p_silence_buffer,
594                        p_buffer->p_buffer,
595                        p_aout->sys->i_buffer_size );
596            p_aout->sys->i_repeat_counter = 2;
597         }
598     } else {
599         /* Use silence buffer instead */
600         if(p_aout->sys->i_repeat_counter)
601         {
602            p_aout->sys->i_repeat_counter--;
603            if(!p_aout->sys->i_repeat_counter)
604            {
605                memset( p_aout->sys->p_silence_buffer,
606                            0x00, p_aout->sys->i_buffer_size );
607            }
608         }
609         p_waveheader->hdr.lpData = (LPSTR)p_aout->sys->p_silence_buffer;
610         p_waveheader->hdr.dwBufferLength = p_aout->sys->i_buffer_size;
611     }
612
613     p_waveheader->hdr.dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
614     p_waveheader->hdr.dwFlags = 0;
615
616     result = waveOutPrepareHeader( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
617     if( result != MMSYSERR_NOERROR )
618     {
619         msg_Err( p_aout, "waveOutPrepareHeader failed" );
620         return VLC_EGENERIC;
621     }
622
623     /* Send the buffer to the waveOut queue */
624     result = waveOutWrite( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
625     if( result != MMSYSERR_NOERROR )
626     {
627         msg_Err( p_aout, "waveOutWrite failed" );
628         return VLC_EGENERIC;
629     }
630
631     return VLC_SUCCESS;
632 }
633
634 /*****************************************************************************
635  * WaveOutCallback: what to do once WaveOut has played its sound samples
636  *****************************************************************************/
637 static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
638                                       DWORD_PTR _p_aout,
639                                       DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
640 {
641     (void) h_waveout;
642     (void) dwParam2;
643     audio_output_t *p_aout = (audio_output_t *)_p_aout;
644     struct lkwavehdr * p_waveheader =  (struct lkwavehdr *) dwParam1;
645
646     if( uMsg != WOM_DONE ) return;
647
648     vlc_mutex_lock( &p_aout->sys->lock );
649     p_waveheader->p_next = p_aout->sys->p_free_list;
650     p_aout->sys->p_free_list = p_waveheader;
651     p_aout->sys->i_frames--;
652     vlc_cond_broadcast( &p_aout->sys->cond );
653     vlc_mutex_unlock( &p_aout->sys->lock );
654 }
655
656 static void WaveOutClean( aout_sys_t * p_sys )
657 {
658     struct lkwavehdr *p_whdr, *p_list;
659
660     vlc_mutex_lock(&p_sys->lock);
661     p_list =  p_sys->p_free_list;
662     p_sys->p_free_list = NULL;
663     vlc_mutex_unlock(&p_sys->lock);
664
665     while( p_list )
666     {
667         p_whdr = p_list;
668         p_list = p_list->p_next;
669         WaveOutClearBuffer( p_sys->h_waveout, &p_whdr->hdr );
670         free(p_whdr);
671     }
672 }
673
674 static void WaveOutClearBuffer( HWAVEOUT h_waveout, WAVEHDR *p_waveheader )
675 {
676     block_t *p_buffer = (block_t *)(p_waveheader->dwUser);
677     /* Unprepare and free the buffers which has just been played */
678     waveOutUnprepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
679
680     if( p_waveheader->dwUser != 1 )
681         block_Release( p_buffer );
682 }
683
684 /*
685   reload the configuration drop down list, of the Audio Devices
686 */
687 static int ReloadWaveoutDevices( vlc_object_t *p_this, char const *psz_name,
688                                  char ***values, char ***descs )
689 {
690     int n = 0, nb_devices = waveOutGetNumDevs();
691
692     VLC_UNUSED( p_this); VLC_UNUSED( psz_name );
693
694     *values = xmalloc( (nb_devices + 1) * sizeof(char *) );
695     *descs = xmalloc( (nb_devices + 1) * sizeof(char *) );
696
697     (*values)[n] = strdup( "wavemapper" );
698     (*descs)[n] = strdup( _("Microsoft Soundmapper") );
699     n++;
700
701     for(int i = 0; i < nb_devices; i++)
702     {
703         WAVEOUTCAPS caps;
704         wchar_t dev_name[MAXPNAMELEN+32];
705
706         if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
707                                                            != MMSYSERR_NOERROR)
708             continue;
709
710         _snwprintf(dev_name, MAXPNAMELEN + 32, device_name_fmt,
711                    caps.szPname, caps.wMid, caps.wPid);
712         (*values)[n] = FromWide( dev_name );
713         (*descs)[n] = strdup( (*values)[n] );
714         n++;
715     }
716
717     return n;
718 }
719
720 /*
721   convert devicename to device ID for output
722   if device not found return WAVE_MAPPER, so let
723   windows decide which preferred audio device
724   should be used.
725 */
726 static uint32_t findDeviceID(char *psz_device_name)
727 {
728     if( !psz_device_name )
729        return WAVE_MAPPER;
730
731     uint32_t wave_devices = waveOutGetNumDevs();
732
733     for( uint32_t i = 0; i < wave_devices; i++ )
734     {
735         WAVEOUTCAPS caps;
736         wchar_t dev_name[MAXPNAMELEN+32];
737
738         if( waveOutGetDevCaps( i, &caps, sizeof(WAVEOUTCAPS) )
739                                                           != MMSYSERR_NOERROR )
740             continue;
741
742         _snwprintf( dev_name, MAXPNAMELEN + 32, device_name_fmt,
743                   caps.szPname, caps.wMid, caps.wPid );
744         char *u8 = FromWide(dev_name);
745         if( !stricmp(u8, psz_device_name) )
746         {
747             free( u8 );
748             return i;
749         }
750         free( u8 );
751     }
752
753     return WAVE_MAPPER;
754 }
755
756 static int DeviceSelect (audio_output_t *aout, const char *id)
757 {
758     var_SetString(aout, "waveout-audio-device", (id != NULL) ? id : "");
759     aout_DeviceReport (aout, id);
760     aout_RestartRequest (aout, AOUT_RESTART_OUTPUT);
761     return 0;
762 }
763
764 static int Open(vlc_object_t *obj)
765 {
766     audio_output_t *aout = (audio_output_t *)obj;
767     aout_sys_t *sys = malloc(sizeof (*sys));
768
769     if (unlikely(sys == NULL))
770         return VLC_ENOMEM;
771     aout->sys = sys;
772     aout->start = Start;
773     aout->stop = Stop;
774     aout->volume_set = WaveoutVolumeSet;
775     aout->mute_set = WaveoutMuteSet;
776     aout->device_select = DeviceSelect;
777
778     sys->f_volume = var_InheritFloat(aout, "waveout-volume");
779     sys->b_mute = var_InheritBool(aout, "mute");
780
781     aout_MuteReport(aout, sys->b_mute);
782     aout_VolumeReport(aout, sys->f_volume );
783
784     if( vlc_timer_create( &sys->volume_poll_timer,
785                           WaveoutPollVolume, aout ) )
786     {
787         msg_Err( aout, "Couldn't create volume polling timer" );
788         free( sys );
789         return VLC_ENOMEM;
790     }
791
792     vlc_mutex_init( &sys->lock );
793     vlc_cond_init( &sys->cond );
794
795     /* WaveOut does not support hot-plug events so list devices at startup */
796     char **ids, **names;
797     int count = ReloadWaveoutDevices(VLC_OBJECT(aout), NULL, &ids, &names);
798     if (count >= 0)
799     {
800         for (int i = 0; i < count; i++)
801         {
802             aout_HotplugReport(aout, ids[i], names[i]);
803             free(names[i]);
804             free(ids[i]);
805         }
806         free(names);
807         free(ids);
808     }
809     var_Create(aout, "waveout-audio-device", VLC_VAR_STRING|VLC_VAR_DOINHERIT);
810     return VLC_SUCCESS;
811 }
812
813 static void Close(vlc_object_t *obj)
814 {
815     audio_output_t *aout = (audio_output_t *)obj;
816     aout_sys_t *sys = aout->sys;
817
818     var_Destroy(aout, "waveout-audio-device");
819
820     vlc_timer_destroy( sys->volume_poll_timer );
821     vlc_cond_destroy( &sys->cond );
822     vlc_mutex_destroy( &sys->lock );
823
824     free(sys);
825 }
826
827 static int WaveOutTimeGet(audio_output_t * p_aout, mtime_t *delay)
828 {
829     MMTIME mmtime;
830     mmtime.wType = TIME_SAMPLES;
831
832     if( !p_aout->sys->i_frames )
833         return -1;
834
835     if( waveOutGetPosition( p_aout->sys->h_waveout, &mmtime, sizeof(MMTIME) )
836             != MMSYSERR_NOERROR )
837     {
838         msg_Err( p_aout, "waveOutGetPosition failed");
839         return -1;
840     }
841
842     mtime_t i_pos = (mtime_t) mmtime.u.sample * CLOCK_FREQ / p_aout->sys->i_rate;
843     *delay = p_aout->sys->i_played_length - i_pos;
844     return 0;
845 }
846
847 static void WaveOutFlush( audio_output_t *p_aout, bool wait)
848 {
849     MMRESULT res;
850     if( !wait )
851     {
852         res  = waveOutReset( p_aout->sys->h_waveout );
853         p_aout->sys->i_played_length = 0;
854         if( res != MMSYSERR_NOERROR )
855             msg_Err( p_aout, "waveOutReset failed");
856     }
857     else
858     {
859         vlc_mutex_lock( &p_aout->sys->lock );
860         while( p_aout->sys->i_frames )
861         {
862             vlc_cond_wait( &p_aout->sys->cond, &p_aout->sys-> lock );
863         }
864         vlc_mutex_unlock( &p_aout->sys->lock );
865     }
866 }
867
868 static void WaveOutPause( audio_output_t * p_aout, bool pause, mtime_t date)
869 {
870     MMRESULT res;
871     (void) date;
872     if(pause)
873     {
874         vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 1, 200000 );
875         res = waveOutPause( p_aout->sys->h_waveout );
876         if( res != MMSYSERR_NOERROR )
877         {
878             msg_Err( p_aout, "waveOutPause failed (0x%x)", res);
879             return;
880         }
881     }
882     else
883     {
884         vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 0, 0 );
885         res = waveOutRestart( p_aout->sys->h_waveout );
886         if( res != MMSYSERR_NOERROR )
887         {
888             msg_Err( p_aout, "waveOutRestart failed (0x%x)", res);
889             return;
890         }
891     }
892 }
893
894 static int WaveoutVolumeSet( audio_output_t *p_aout, float volume )
895 {
896     aout_sys_t *sys = p_aout->sys;
897
898     if( sys->b_soft )
899     {
900         float gain = volume * volume * volume;
901         if ( !sys->b_mute && aout_GainRequest( p_aout, gain ) )
902             return -1;
903     }
904     else
905     {
906         const HWAVEOUT hwo = sys->h_waveout;
907
908         uint32_t vol = lroundf( volume * 0x7fff.fp0 );
909
910         if( !sys->b_mute )
911         {
912             if( vol > 0xffff )
913             {
914                 vol = 0xffff;
915                 volume = 2.0f;
916             }
917
918             MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
919             if( r != MMSYSERR_NOERROR )
920             {
921                 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
922                 return -1;
923             }
924         }
925     }
926
927     vlc_mutex_lock(&p_aout->sys->lock);
928     sys->f_volume = volume;
929
930     if( var_InheritBool( p_aout, "volume-save" ) )
931         config_PutFloat( p_aout, "waveout-volume", volume );
932
933     aout_VolumeReport( p_aout, volume );
934     vlc_mutex_unlock(&p_aout->sys->lock);
935
936     return 0;
937 }
938
939 static int WaveoutMuteSet( audio_output_t * p_aout, bool mute )
940 {
941     aout_sys_t *sys = p_aout->sys;
942
943     if( sys->b_soft )
944     {
945         float gain = sys->f_volume * sys->f_volume * sys->f_volume;
946         if ( aout_GainRequest( p_aout, mute ? 0.f : gain ) )
947             return -1;
948     }
949     else
950     {
951
952         const HWAVEOUT hwo = sys->h_waveout;
953         uint32_t vol = mute ? 0 : lroundf( sys->f_volume * 0x7fff.fp0 );
954
955         if( vol > 0xffff )
956             vol = 0xffff;
957
958         MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
959         if( r != MMSYSERR_NOERROR )
960         {
961             msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
962             return -1;
963         }
964     }
965
966     vlc_mutex_lock(&p_aout->sys->lock);
967     sys->b_mute = mute;
968     aout_MuteReport( p_aout, mute );
969     vlc_mutex_unlock(&p_aout->sys->lock);
970
971     return 0;
972 }
973
974 static void WaveoutPollVolume( void * aout )
975 {
976     audio_output_t * p_aout = (audio_output_t *) aout;
977     uint32_t vol;
978
979     MMRESULT r = waveOutGetVolume( p_aout->sys->h_waveout, (LPDWORD) &vol );
980
981     if( r != MMSYSERR_NOERROR )
982     {
983         msg_Err( p_aout, "waveOutGetVolume failed (%u)", r );
984         return;
985     }
986
987     float volume = (float) ( vol & UINT32_C( 0xffff ) );
988     volume /= 0x7fff.fp0;
989
990     vlc_mutex_lock(&p_aout->sys->lock);
991     if( !p_aout->sys->b_mute && volume != p_aout->sys->f_volume )
992     {
993         p_aout->sys->f_volume = volume;
994
995         if( var_InheritBool( p_aout, "volume-save" ) )
996             config_PutFloat( p_aout, "waveout-volume", volume );
997
998         aout_VolumeReport( p_aout, volume );
999     }
1000     vlc_mutex_unlock(&p_aout->sys->lock);
1001
1002     return;
1003 }