qt: playlist: use item title if available
[vlc.git] / modules / audio_output / waveout.c
blob936085fa368019100b108ddc5e3c6ffefe0299f0
1 /*****************************************************************************
2 * waveout.c : Windows waveOut plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2001-2009 VLC authors and VideoLAN
6 * Authors: Gildas Bazin <gbazin@videolan.org>
7 * André Weber
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <math.h>
34 #ifndef UNICODE
35 # define UNICODE
36 #endif
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
39 #include <vlc_aout.h>
40 #include <vlc_charset.h> /* FromWide() */
42 #include "audio_output/windows_audio_common.h"
44 #define FRAME_SIZE 4096 /* The size is in samples, not in bytes */
46 /*****************************************************************************
47 * Local prototypes
48 *****************************************************************************/
49 static int Open ( vlc_object_t * );
50 static void Close ( vlc_object_t * );
51 static void Play ( audio_output_t *, block_t *, vlc_tick_t );
53 /*****************************************************************************
54 * notification_thread_t: waveOut event thread
55 *****************************************************************************/
57 typedef struct aout_sys_t aout_sys_t;
59 struct lkwavehdr
61 WAVEHDR hdr;
62 struct lkwavehdr * p_next;
65 /* local functions */
66 static int OpenWaveOut ( audio_output_t *, uint32_t,
67 int, int, int, int, bool );
68 static int OpenWaveOutPCM( audio_output_t *, uint32_t,
69 vlc_fourcc_t*, int, int, int, bool );
70 static int PlayWaveOut ( audio_output_t *, HWAVEOUT, struct lkwavehdr *,
71 block_t *, bool );
73 static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR );
75 static void WaveOutClean( aout_sys_t * p_sys );
77 static void WaveOutClearBuffer( HWAVEOUT, WAVEHDR *);
79 static uint32_t findDeviceID(char *);
80 static int WaveOutTimeGet(audio_output_t * , vlc_tick_t *);
81 static void WaveOutFlush( audio_output_t *);
82 static void WaveOutDrain( audio_output_t *);
83 static void WaveOutPause( audio_output_t *, bool, vlc_tick_t);
84 static int WaveoutVolumeSet(audio_output_t * p_aout, float volume);
85 static int WaveoutMuteSet(audio_output_t * p_aout, bool mute);
87 static void WaveoutPollVolume( void * );
89 static const wchar_t device_name_fmt[] = L"%ls ($%x,$%x)";
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 *****************************************************************************/
98 struct aout_sys_t
100 HWAVEOUT h_waveout; /* handle to waveout instance */
102 WAVEFORMATEXTENSIBLE waveformat; /* audio format */
104 size_t i_frames;
106 int i_repeat_counter;
108 int i_buffer_size;
110 int i_rate;
112 uint8_t *p_silence_buffer; /* buffer we use to play silence */
114 float f_volume;
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 */
121 uint8_t chan_table[AOUT_CHAN_MAX];
122 vlc_fourcc_t format;
124 vlc_tick_t i_played_length;
126 struct lkwavehdr * p_free_list;
128 vlc_mutex_t lock;
129 vlc_cond_t cond;
130 vlc_timer_t volume_poll_timer;
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.")
141 #define AUDIO_CHAN_TEXT N_("Audio output channels")
142 #define AUDIO_CHAN_LONGTEXT N_("Channels available for audio output. " \
143 "If the input has more channels than the output, it will be down-mixed. " \
144 "This parameter is ignored when digital pass-through is active.")
146 #define VOLUME_TEXT N_("Audio volume")
148 vlc_module_begin ()
149 set_shortname( "WaveOut" )
150 set_description( N_("WaveOut audio output") )
151 set_capability( "audio output", 50 )
152 set_category( CAT_AUDIO )
153 set_subcategory( SUBCAT_AUDIO_AOUT )
154 add_string( "waveout-audio-device", "wavemapper",
155 DEVICE_TEXT, DEVICE_LONG, false )
156 add_float( "waveout-volume", 1.0f, VOLUME_TEXT, NULL, true )
157 change_float_range(0.0f, 2.0f)
158 add_bool( "waveout-float32", true, FLOAT_TEXT, FLOAT_LONGTEXT, true )
159 add_integer ("waveout-audio-channels", 9, AUDIO_CHAN_TEXT,
160 AUDIO_CHAN_LONGTEXT, false)
161 change_integer_range(1,9)
162 set_callbacks( Open, Close )
163 vlc_module_end ()
165 /*****************************************************************************
166 * Opens the audio device
167 *****************************************************************************
168 * This function opens and setups Win32 waveOut
169 *****************************************************************************/
170 static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
172 if( aout_FormatNbChannels( fmt ) == 0 )
173 return VLC_EGENERIC;
175 p_aout->time_get = WaveOutTimeGet;
176 p_aout->play = Play;
177 p_aout->pause = WaveOutPause;
178 p_aout->flush = WaveOutFlush;
179 p_aout->drain = WaveOutDrain;
181 aout_sys_t *sys = p_aout->sys;
183 /* Default behaviour is to use software gain */
184 sys->b_soft = true;
186 char *dev = var_GetNonEmptyString( p_aout, "waveout-audio-device");
187 uint32_t devid = findDeviceID( dev );
189 if(devid == WAVE_MAPPER && dev != NULL && stricmp(dev,"wavemapper"))
190 msg_Warn( p_aout, "configured audio device '%s' not available, "
191 "using default instead", dev );
192 free( dev );
194 WAVEOUTCAPS waveoutcaps;
195 if(waveOutGetDevCaps( devid, &waveoutcaps,
196 sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
198 /* log debug some infos about driver, to know who to blame
199 if it doesn't work */
200 msg_Dbg( p_aout, "Drivername: %ls", waveoutcaps.szPname);
201 msg_Dbg( p_aout, "Driver Version: %d.%d",
202 (waveoutcaps.vDriverVersion>>8)&255,
203 waveoutcaps.vDriverVersion & 255);
204 msg_Dbg( p_aout, "Manufacturer identifier: 0x%x", waveoutcaps.wMid );
205 msg_Dbg( p_aout, "Product identifier: 0x%x", waveoutcaps.wPid );
210 /* Open the device */
211 if( AOUT_FMT_SPDIF(fmt) && var_InheritBool (p_aout, "spdif") )
214 if( OpenWaveOut( p_aout, devid, VLC_CODEC_SPDIFL,
215 fmt->i_physical_channels,
216 aout_FormatNbChannels( fmt ), fmt->i_rate, false )
217 == VLC_SUCCESS )
219 fmt->i_format = VLC_CODEC_SPDIFL;
221 /* Calculate the frame size in bytes */
222 fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
223 fmt->i_frame_length = A52_FRAME_NB;
224 sys->i_buffer_size = fmt->i_bytes_per_frame;
225 sys->b_spdif = true;
228 else
229 msg_Err( p_aout,
230 "cannot open waveout audio device for spdif fallback to PCM" );
233 if( fmt->i_format != VLC_CODEC_SPDIFL )
236 check for configured audio device!
238 fmt->i_format = var_InheritBool( p_aout, "waveout-float32" )?
239 VLC_CODEC_FL32: VLC_CODEC_S16N;
241 int max_chan = var_InheritInteger( p_aout, "waveout-audio-channels");
242 int i_channels = aout_FormatNbChannels(fmt);
243 i_channels = ( i_channels < max_chan )? i_channels: max_chan;
246 switch(i_channels)
248 case 9:
249 fmt->i_physical_channels = AOUT_CHANS_8_1;
250 break;
251 case 8:
252 fmt->i_physical_channels = AOUT_CHANS_7_1;
253 break;
254 case 7:
255 fmt->i_physical_channels = AOUT_CHANS_7_0;
256 break;
257 case 6:
258 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
259 | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT
260 | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
261 break;
262 case 5:
263 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
264 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
265 | AOUT_CHAN_LFE;
266 break;
267 case 4:
268 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
269 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
270 break;
271 case 3:
272 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
273 | AOUT_CHAN_LFE;
274 break;
275 case 2:
276 fmt->i_physical_channels = AOUT_CHANS_STEREO;
277 break;
278 case 1:
279 default:
280 fmt->i_physical_channels = AOUT_CHAN_CENTER;
282 msg_Dbg( p_aout, "Trying %d channels", i_channels );
284 while( ( OpenWaveOutPCM( p_aout, devid, &fmt->i_format,
285 fmt->i_physical_channels, i_channels,
286 fmt->i_rate, false ) != VLC_SUCCESS ) &&
287 --i_channels );
289 if( !i_channels )
291 msg_Err(p_aout, "Waveout couldn't find appropriate channel mapping");
292 return VLC_EGENERIC;
295 /* Calculate the frame size in bytes */
296 aout_FormatPrepare( fmt );
297 sys->i_buffer_size = FRAME_SIZE * fmt->i_bytes_per_frame;
299 if( waveoutcaps.dwSupport & WAVECAPS_VOLUME )
301 aout_GainRequest( p_aout, 1.0f );
302 sys->b_soft = false;
305 WaveoutMuteSet( p_aout, sys->b_mute );
307 sys->b_spdif = false;
310 sys->i_rate = fmt->i_rate;
312 waveOutReset( sys->h_waveout );
314 /* Allocate silence buffer */
315 sys->p_silence_buffer =
316 malloc( sys->i_buffer_size );
317 if( sys->p_silence_buffer == NULL )
319 msg_Err( p_aout, "Couldn't alloc silence buffer... aborting");
320 return VLC_ENOMEM;
322 sys->i_repeat_counter = 0;
325 /* Zero the buffer. WinCE doesn't have calloc(). */
326 memset( sys->p_silence_buffer, 0,
327 sys->i_buffer_size );
329 /* Now we need to setup our waveOut play notification structure */
330 sys->i_frames = 0;
331 sys->i_played_length = 0;
332 sys->p_free_list = NULL;
334 fmt->channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
336 return VLC_SUCCESS;
339 /*****************************************************************************
340 * Play: play a sound buffer
341 *****************************************************************************
342 * This doesn't actually play the buffer. This just stores the buffer so it
343 * can be played by the callback thread.
344 *****************************************************************************/
345 static void Play( audio_output_t *p_aout, block_t *block, vlc_tick_t date )
347 aout_sys_t *sys = p_aout->sys;
349 struct lkwavehdr * p_waveheader =
350 (struct lkwavehdr *) malloc(sizeof(struct lkwavehdr));
351 if(!p_waveheader)
353 msg_Err(p_aout, "Couldn't alloc WAVEHDR");
354 if( block )
355 block_Release( block );
356 return;
359 p_waveheader->p_next = NULL;
361 if( block && sys->chans_to_reorder )
363 aout_ChannelReorder( block->p_buffer, block->i_buffer,
364 sys->waveformat.Format.nChannels,
365 sys->chan_table, sys->format );
367 while( PlayWaveOut( p_aout, sys->h_waveout, p_waveheader, block,
368 sys->b_spdif ) != VLC_SUCCESS )
371 msg_Warn( p_aout, "Couln't write frame... sleeping");
372 vlc_tick_sleep( block->i_length );
375 WaveOutClean( sys );
376 WaveoutPollVolume( p_aout );
378 vlc_mutex_lock( &sys->lock );
379 sys->i_frames++;
380 sys->i_played_length += block->i_length;
381 vlc_mutex_unlock( &sys->lock );
382 (void) date;
385 /*****************************************************************************
386 * Close: close the audio device
387 *****************************************************************************/
388 static void Stop( audio_output_t *p_aout )
390 aout_sys_t *p_sys = p_aout->sys;
392 /* Before calling waveOutClose we must reset the device */
393 waveOutReset( p_sys->h_waveout );
395 /* wait for the frames to be queued in cleaning list */
396 WaveOutDrain( p_aout );
397 WaveOutClean( p_sys );
399 /* now we can Close the device */
400 if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
402 msg_Err( p_aout, "waveOutClose failed" );
405 free( p_sys->p_silence_buffer );
406 p_sys->i_played_length = 0;
407 p_sys->b_soft = true;
410 /*****************************************************************************
411 * OpenWaveOut: open the waveout sound device
412 ****************************************************************************/
413 static int OpenWaveOut( audio_output_t *p_aout, uint32_t i_device_id, int i_format,
414 int i_channels, int i_nb_channels, int i_rate,
415 bool b_probe )
417 MMRESULT result;
418 aout_sys_t *sys = p_aout->sys;
420 /* Set sound format */
422 #define waveformat sys->waveformat
424 waveformat.dwChannelMask = 0;
425 for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ )
426 if( i_channels & pi_vlc_chan_order_wg4[i] )
427 waveformat.dwChannelMask |= pi_channels_in[i];
429 switch( i_format )
431 case VLC_CODEC_SPDIFL:
432 i_nb_channels = 2;
433 /* To prevent channel re-ordering */
434 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
435 waveformat.Format.wBitsPerSample = 16;
436 waveformat.Samples.wValidBitsPerSample =
437 waveformat.Format.wBitsPerSample;
438 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
439 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
440 break;
442 case VLC_CODEC_FL32:
443 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
444 waveformat.Samples.wValidBitsPerSample =
445 waveformat.Format.wBitsPerSample;
446 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
447 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
448 break;
450 case VLC_CODEC_S16N:
451 waveformat.Format.wBitsPerSample = 16;
452 waveformat.Samples.wValidBitsPerSample =
453 waveformat.Format.wBitsPerSample;
454 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
455 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_PCM;
456 break;
459 waveformat.Format.nChannels = i_nb_channels;
460 waveformat.Format.nSamplesPerSec = i_rate;
461 waveformat.Format.nBlockAlign =
462 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
463 waveformat.Format.nAvgBytesPerSec =
464 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
466 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
467 if( i_nb_channels <= 2 )
469 waveformat.Format.cbSize = 0;
471 else
473 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
474 waveformat.Format.cbSize =
475 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
478 if(!b_probe) {
479 msg_Dbg( p_aout, "OpenWaveDevice-ID: %u", i_device_id);
480 msg_Dbg( p_aout,"waveformat.Format.cbSize = %d",
481 waveformat.Format.cbSize);
482 msg_Dbg( p_aout,"waveformat.Format.wFormatTag = %u",
483 waveformat.Format.wFormatTag);
484 msg_Dbg( p_aout,"waveformat.Format.nChannels = %u",
485 waveformat.Format.nChannels);
486 msg_Dbg( p_aout,"waveformat.Format.nSamplesPerSec = %d",
487 (int)waveformat.Format.nSamplesPerSec);
488 msg_Dbg( p_aout,"waveformat.Format.nAvgBytesPerSec = %u",
489 (int)waveformat.Format.nAvgBytesPerSec);
490 msg_Dbg( p_aout,"waveformat.Format.nBlockAlign = %d",
491 waveformat.Format.nBlockAlign);
492 msg_Dbg( p_aout,"waveformat.Format.wBitsPerSample = %d",
493 waveformat.Format.wBitsPerSample);
494 msg_Dbg( p_aout,"waveformat.Samples.wValidBitsPerSample = %d",
495 waveformat.Samples.wValidBitsPerSample);
496 msg_Dbg( p_aout,"waveformat.Samples.wSamplesPerBlock = %d",
497 waveformat.Samples.wSamplesPerBlock);
498 msg_Dbg( p_aout,"waveformat.dwChannelMask = %u",
499 waveformat.dwChannelMask);
502 /* Open the device */
503 result = waveOutOpen( &sys->h_waveout, i_device_id,
504 (WAVEFORMATEX *)&waveformat,
505 (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
506 CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) );
507 if( result == WAVERR_BADFORMAT )
509 msg_Warn( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" );
510 return VLC_EGENERIC;
512 if( result == MMSYSERR_ALLOCATED )
514 msg_Warn( p_aout, "waveOutOpen failed WAVERR_ALLOCATED" );
515 return VLC_EGENERIC;
517 if( result != MMSYSERR_NOERROR )
519 msg_Warn( p_aout, "waveOutOpen failed" );
520 return VLC_EGENERIC;
523 sys->chans_to_reorder =
524 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
525 waveformat.dwChannelMask,
526 sys->chan_table );
527 if( sys->chans_to_reorder )
528 msg_Dbg( p_aout, "channel reordering needed" );
529 sys->format = i_format;
531 return VLC_SUCCESS;
533 #undef waveformat
537 /*****************************************************************************
538 * OpenWaveOutPCM: open a PCM waveout sound device
539 ****************************************************************************/
540 static int OpenWaveOutPCM( audio_output_t *p_aout, uint32_t i_device_id,
541 vlc_fourcc_t *i_format,
542 int i_channels, int i_nb_channels, int i_rate,
543 bool b_probe )
545 bool b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32");
547 if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_CODEC_FL32,
548 i_channels, i_nb_channels, i_rate, b_probe )
549 != VLC_SUCCESS )
551 if ( OpenWaveOut( p_aout, i_device_id, VLC_CODEC_S16N,
552 i_channels, i_nb_channels, i_rate, b_probe )
553 != VLC_SUCCESS )
555 return VLC_EGENERIC;
557 else
559 *i_format = VLC_CODEC_S16N;
560 return VLC_SUCCESS;
563 else
565 *i_format = VLC_CODEC_FL32;
566 return VLC_SUCCESS;
570 /*****************************************************************************
571 * PlayWaveOut: play a buffer through the WaveOut device
572 *****************************************************************************/
573 static int PlayWaveOut( audio_output_t *p_aout, HWAVEOUT h_waveout,
574 struct lkwavehdr *p_waveheader, block_t *p_buffer, bool b_spdif)
576 MMRESULT result;
577 aout_sys_t *sys = p_aout->sys;
579 /* Prepare the buffer */
580 if( p_buffer != NULL )
582 p_waveheader->hdr.lpData = (LPSTR)p_buffer->p_buffer;
583 p_waveheader->hdr.dwBufferLength = p_buffer->i_buffer;
585 copy the buffer to the silence buffer :) so in case we don't
586 get the next buffer fast enough (I will repeat this one a time
587 for AC3 / DTS and SPDIF this will sound better instead of
588 a hickup)
590 if(b_spdif)
592 memcpy( sys->p_silence_buffer,
593 p_buffer->p_buffer,
594 sys->i_buffer_size );
595 sys->i_repeat_counter = 2;
597 } else {
598 /* Use silence buffer instead */
599 if(sys->i_repeat_counter)
601 sys->i_repeat_counter--;
602 if(!sys->i_repeat_counter)
604 memset( sys->p_silence_buffer,
605 0x00, sys->i_buffer_size );
608 p_waveheader->hdr.lpData = (LPSTR)sys->p_silence_buffer;
609 p_waveheader->hdr.dwBufferLength = sys->i_buffer_size;
612 p_waveheader->hdr.dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
613 p_waveheader->hdr.dwFlags = 0;
615 result = waveOutPrepareHeader( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
616 if( result != MMSYSERR_NOERROR )
618 msg_Err( p_aout, "waveOutPrepareHeader failed" );
619 return VLC_EGENERIC;
622 /* Send the buffer to the waveOut queue */
623 result = waveOutWrite( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
624 if( result != MMSYSERR_NOERROR )
626 msg_Err( p_aout, "waveOutWrite failed" );
627 return VLC_EGENERIC;
630 return VLC_SUCCESS;
633 /*****************************************************************************
634 * WaveOutCallback: what to do once WaveOut has played its sound samples
635 *****************************************************************************/
636 static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
637 DWORD_PTR _p_aout,
638 DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
640 (void) h_waveout;
641 (void) dwParam2;
642 aout_sys_t *sys = ((audio_output_t *)_p_aout)->sys;
643 struct lkwavehdr * p_waveheader = (struct lkwavehdr *) dwParam1;
645 if( uMsg != WOM_DONE ) return;
647 vlc_mutex_lock( &sys->lock );
648 p_waveheader->p_next = sys->p_free_list;
649 sys->p_free_list = p_waveheader;
650 sys->i_frames--;
651 vlc_cond_broadcast( &sys->cond );
652 vlc_mutex_unlock( &sys->lock );
655 static void WaveOutClean( aout_sys_t * p_sys )
657 struct lkwavehdr *p_whdr, *p_list;
659 vlc_mutex_lock(&p_sys->lock);
660 p_list = p_sys->p_free_list;
661 p_sys->p_free_list = NULL;
662 vlc_mutex_unlock(&p_sys->lock);
664 while( p_list )
666 p_whdr = p_list;
667 p_list = p_list->p_next;
668 WaveOutClearBuffer( p_sys->h_waveout, &p_whdr->hdr );
669 free(p_whdr);
673 static void WaveOutClearBuffer( HWAVEOUT h_waveout, WAVEHDR *p_waveheader )
675 block_t *p_buffer = (block_t *)(p_waveheader->dwUser);
676 /* Unprepare and free the buffers which has just been played */
677 waveOutUnprepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
679 if( p_waveheader->dwUser != 1 )
680 block_Release( p_buffer );
684 reload the configuration drop down list, of the Audio Devices
686 static int ReloadWaveoutDevices( char const *psz_name,
687 char ***values, char ***descs )
689 int n = 0, nb_devices = waveOutGetNumDevs();
691 VLC_UNUSED( psz_name );
693 *values = xmalloc( (nb_devices + 1) * sizeof(char *) );
694 *descs = xmalloc( (nb_devices + 1) * sizeof(char *) );
696 (*values)[n] = strdup( "wavemapper" );
697 (*descs)[n] = strdup( _("Microsoft Soundmapper") );
698 n++;
700 for(int i = 0; i < nb_devices; i++)
702 WAVEOUTCAPS caps;
703 wchar_t dev_name[MAXPNAMELEN+32];
705 if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
706 != MMSYSERR_NOERROR)
707 continue;
709 _snwprintf(dev_name, MAXPNAMELEN + 32, device_name_fmt,
710 caps.szPname, caps.wMid, caps.wPid);
711 (*values)[n] = FromWide( dev_name );
712 (*descs)[n] = strdup( (*values)[n] );
713 n++;
716 return n;
719 VLC_CONFIG_STRING_ENUM(ReloadWaveoutDevices)
722 convert devicename to device ID for output
723 if device not found return WAVE_MAPPER, so let
724 windows decide which preferred audio device
725 should be used.
727 static uint32_t findDeviceID(char *psz_device_name)
729 if( !psz_device_name )
730 return WAVE_MAPPER;
732 uint32_t wave_devices = waveOutGetNumDevs();
734 for( uint32_t i = 0; i < wave_devices; i++ )
736 WAVEOUTCAPS caps;
737 wchar_t dev_name[MAXPNAMELEN+32];
739 if( waveOutGetDevCaps( i, &caps, sizeof(WAVEOUTCAPS) )
740 != MMSYSERR_NOERROR )
741 continue;
743 _snwprintf( dev_name, MAXPNAMELEN + 32, device_name_fmt,
744 caps.szPname, caps.wMid, caps.wPid );
745 char *u8 = FromWide(dev_name);
746 if( !stricmp(u8, psz_device_name) )
748 free( u8 );
749 return i;
751 free( u8 );
754 return WAVE_MAPPER;
757 static int DeviceSelect (audio_output_t *aout, const char *id)
759 var_SetString(aout, "waveout-audio-device", (id != NULL) ? id : "");
760 aout_DeviceReport (aout, id);
761 aout_RestartRequest (aout, AOUT_RESTART_OUTPUT);
762 return 0;
765 static int Open(vlc_object_t *obj)
767 audio_output_t *aout = (audio_output_t *)obj;
768 aout_sys_t *sys = malloc(sizeof (*sys));
770 if (unlikely(sys == NULL))
771 return VLC_ENOMEM;
772 aout->sys = sys;
773 aout->start = Start;
774 aout->stop = Stop;
775 aout->volume_set = WaveoutVolumeSet;
776 aout->mute_set = WaveoutMuteSet;
777 aout->device_select = DeviceSelect;
779 sys->f_volume = var_InheritFloat(aout, "waveout-volume");
780 sys->b_mute = var_InheritBool(aout, "mute");
782 aout_MuteReport(aout, sys->b_mute);
783 aout_VolumeReport(aout, sys->f_volume );
785 if( vlc_timer_create( &sys->volume_poll_timer,
786 WaveoutPollVolume, aout ) )
788 msg_Err( aout, "Couldn't create volume polling timer" );
789 free( sys );
790 return VLC_ENOMEM;
793 vlc_mutex_init( &sys->lock );
794 vlc_cond_init( &sys->cond );
796 /* WaveOut does not support hot-plug events so list devices at startup */
797 char **ids, **names;
798 int count = ReloadWaveoutDevices(NULL, &ids, &names);
799 if (count >= 0)
801 for (int i = 0; i < count; i++)
803 aout_HotplugReport(aout, ids[i], names[i]);
804 free(names[i]);
805 free(ids[i]);
807 free(names);
808 free(ids);
811 char *dev = var_CreateGetNonEmptyString(aout, "waveout-audio-device");
812 aout_DeviceReport(aout, dev);
813 free(dev);
815 return VLC_SUCCESS;
818 static void Close(vlc_object_t *obj)
820 audio_output_t *aout = (audio_output_t *)obj;
821 aout_sys_t *sys = aout->sys;
823 var_Destroy(aout, "waveout-audio-device");
825 vlc_timer_destroy( sys->volume_poll_timer );
826 free(sys);
829 static int WaveOutTimeGet(audio_output_t * p_aout, vlc_tick_t *delay)
831 MMTIME mmtime;
832 mmtime.wType = TIME_SAMPLES;
833 aout_sys_t *sys = p_aout->sys;
835 if( !sys->i_frames )
836 return -1;
838 if( waveOutGetPosition( sys->h_waveout, &mmtime, sizeof(MMTIME) )
839 != MMSYSERR_NOERROR )
841 msg_Err( p_aout, "waveOutGetPosition failed");
842 return -1;
845 vlc_tick_t i_pos = vlc_tick_from_samples(mmtime.u.sample, sys->i_rate);
846 *delay = sys->i_played_length - i_pos;
847 return 0;
850 static void WaveOutFlush( audio_output_t *p_aout)
852 MMRESULT res;
853 aout_sys_t *sys = p_aout->sys;
855 res = waveOutReset( sys->h_waveout );
856 sys->i_played_length = 0;
857 if( res != MMSYSERR_NOERROR )
858 msg_Err( p_aout, "waveOutReset failed");
861 static void WaveOutDrain( audio_output_t *p_aout)
863 aout_sys_t *sys = p_aout->sys;
865 vlc_mutex_lock( &sys->lock );
866 while( sys->i_frames )
868 vlc_cond_wait( &sys->cond, &sys->lock );
870 vlc_mutex_unlock( &sys->lock );
873 static void WaveOutPause( audio_output_t * p_aout, bool pause, vlc_tick_t date)
875 MMRESULT res;
876 (void) date;
877 aout_sys_t *sys = p_aout->sys;
879 if(pause)
881 vlc_timer_schedule_asap( sys->volume_poll_timer, VLC_TICK_FROM_MS(200) );
882 res = waveOutPause( sys->h_waveout );
883 if( res != MMSYSERR_NOERROR )
885 msg_Err( p_aout, "waveOutPause failed (0x%x)", res);
886 return;
889 else
891 vlc_timer_disarm( sys->volume_poll_timer );
892 res = waveOutRestart( sys->h_waveout );
893 if( res != MMSYSERR_NOERROR )
895 msg_Err( p_aout, "waveOutRestart failed (0x%x)", res);
896 return;
901 static int WaveoutVolumeSet( audio_output_t *p_aout, float volume )
903 aout_sys_t *sys = p_aout->sys;
905 if( sys->b_soft )
907 float gain = volume * volume * volume;
908 if ( !sys->b_mute && aout_GainRequest( p_aout, gain ) )
909 return -1;
911 else
913 const HWAVEOUT hwo = sys->h_waveout;
915 uint32_t vol = lroundf( volume * 0x7fff.fp0 );
917 if( !sys->b_mute )
919 if( vol > 0xffff )
921 vol = 0xffff;
922 volume = 2.0f;
925 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
926 if( r != MMSYSERR_NOERROR )
928 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
929 return -1;
934 vlc_mutex_lock(&sys->lock);
935 sys->f_volume = volume;
937 if( var_InheritBool( p_aout, "volume-save" ) )
938 config_PutFloat( "waveout-volume", volume );
940 aout_VolumeReport( p_aout, volume );
941 vlc_mutex_unlock(&sys->lock);
943 return 0;
946 static int WaveoutMuteSet( audio_output_t * p_aout, bool mute )
948 aout_sys_t *sys = p_aout->sys;
950 if( sys->b_soft )
952 float gain = sys->f_volume * sys->f_volume * sys->f_volume;
953 if ( aout_GainRequest( p_aout, mute ? 0.f : gain ) )
954 return -1;
956 else
959 const HWAVEOUT hwo = sys->h_waveout;
960 uint32_t vol = mute ? 0 : lroundf( sys->f_volume * 0x7fff.fp0 );
962 if( vol > 0xffff )
963 vol = 0xffff;
965 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
966 if( r != MMSYSERR_NOERROR )
968 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
969 return -1;
973 vlc_mutex_lock(&sys->lock);
974 sys->b_mute = mute;
975 aout_MuteReport( p_aout, mute );
976 vlc_mutex_unlock(&sys->lock);
978 return 0;
981 static void WaveoutPollVolume( void * _aout )
983 audio_output_t * aout = (audio_output_t *) _aout;
984 aout_sys_t *sys = aout->sys;
985 uint32_t vol;
987 MMRESULT r = waveOutGetVolume( sys->h_waveout, (LPDWORD) &vol );
989 if( r != MMSYSERR_NOERROR )
991 msg_Err( aout, "waveOutGetVolume failed (%u)", r );
992 return;
995 float volume = (float) ( vol & UINT32_C( 0xffff ) );
996 volume /= 0x7fff.fp0;
998 vlc_mutex_lock(&sys->lock);
999 if( !sys->b_mute && volume != sys->f_volume )
1001 sys->f_volume = volume;
1003 if( var_InheritBool( aout, "volume-save" ) )
1004 config_PutFloat( "waveout-volume", volume );
1006 aout_VolumeReport( aout, volume );
1008 vlc_mutex_unlock(&sys->lock);
1010 return;