configure: Improve detection of ibtool
[vlc.git] / modules / audio_output / waveout.c
blobce5d2f308e8b79f688eed2b2ee85de797688298a
1 /*****************************************************************************
2 * waveout.c : Windows waveOut plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2001-2009 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Gildas Bazin <gbazin@videolan.org>
8 * André Weber
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include <math.h>
35 #ifndef UNICODE
36 # define UNICODE
37 #endif
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_aout.h>
41 #include <vlc_charset.h> /* FromWide() */
43 #include "audio_output/windows_audio_common.h"
45 #define FRAME_SIZE 4096 /* The size is in samples, not in bytes */
47 /*****************************************************************************
48 * Local prototypes
49 *****************************************************************************/
50 static int Open ( vlc_object_t * );
51 static void Close ( vlc_object_t * );
52 static void Play ( audio_output_t *, block_t * );
54 /*****************************************************************************
55 * notification_thread_t: waveOut event thread
56 *****************************************************************************/
57 struct lkwavehdr
59 WAVEHDR hdr;
60 struct lkwavehdr * p_next;
63 /* local functions */
64 static int OpenWaveOut ( audio_output_t *, uint32_t,
65 int, int, int, int, bool );
66 static int OpenWaveOutPCM( audio_output_t *, uint32_t,
67 vlc_fourcc_t*, int, int, int, bool );
68 static int PlayWaveOut ( audio_output_t *, HWAVEOUT, struct lkwavehdr *,
69 block_t *, bool );
71 static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR );
73 static void WaveOutClean( aout_sys_t * p_sys );
75 static void WaveOutClearBuffer( HWAVEOUT, WAVEHDR *);
77 static int ReloadWaveoutDevices( vlc_object_t *, const char *,
78 char ***, char *** );
79 static uint32_t findDeviceID(char *);
80 static int WaveOutTimeGet(audio_output_t * , mtime_t *);
81 static void WaveOutFlush( audio_output_t *, bool);
82 static void WaveOutPause( audio_output_t *, bool, mtime_t);
83 static int WaveoutVolumeSet(audio_output_t * p_aout, float volume);
84 static int WaveoutMuteSet(audio_output_t * p_aout, bool mute);
86 static void WaveoutPollVolume( void * );
88 static const wchar_t device_name_fmt[] = L"%ls ($%x,$%x)";
90 /*****************************************************************************
91 * aout_sys_t: waveOut audio output method descriptor
92 *****************************************************************************
93 * This structure is part of the audio output thread descriptor.
94 * It describes the waveOut specific properties of an audio device.
95 *****************************************************************************/
97 struct aout_sys_t
99 HWAVEOUT h_waveout; /* handle to waveout instance */
101 WAVEFORMATEXTENSIBLE waveformat; /* audio format */
103 size_t i_frames;
105 int i_repeat_counter;
107 int i_buffer_size;
109 int i_rate;
111 uint8_t *p_silence_buffer; /* buffer we use to play silence */
113 float f_volume;
115 bool b_spdif;
116 bool b_mute;
117 bool b_soft; /* Use software gain */
118 uint8_t chans_to_reorder; /* do we need channel reordering */
120 uint8_t chan_table[AOUT_CHAN_MAX];
121 vlc_fourcc_t format;
123 mtime_t i_played_length;
125 struct lkwavehdr * p_free_list;
127 vlc_mutex_t lock;
128 vlc_cond_t cond;
129 vlc_timer_t volume_poll_timer;
132 /*****************************************************************************
133 * Module descriptor
134 *****************************************************************************/
135 #define DEVICE_TEXT N_("Select Audio Device")
136 #define DEVICE_LONG N_("Select special Audio device, or let windows "\
137 "decide (default), change needs VLC restart "\
138 "to apply.")
140 #define AUDIO_CHAN_TEXT N_("Audio output channels")
141 #define AUDIO_CHAN_LONGTEXT N_("Channels available for audio output. " \
142 "If the input has more channels than the output, it will be down-mixed. " \
143 "This parameter is ignored when digital pass-through is active.")
145 #define VOLUME_TEXT N_("Audio volume")
147 vlc_module_begin ()
148 set_shortname( "WaveOut" )
149 set_description( N_("WaveOut audio output") )
150 set_capability( "audio output", 50 )
151 set_category( CAT_AUDIO )
152 set_subcategory( SUBCAT_AUDIO_AOUT )
153 add_string( "waveout-audio-device", "wavemapper",
154 DEVICE_TEXT, DEVICE_LONG, false )
155 change_string_cb( ReloadWaveoutDevices )
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;
180 /* Default behaviour is to use software gain */
181 p_aout->sys->b_soft = true;
183 char *dev = var_GetNonEmptyString( p_aout, "waveout-audio-device");
184 uint32_t devid = findDeviceID( dev );
186 if(devid == WAVE_MAPPER && dev != NULL && stricmp(dev,"wavemapper"))
187 msg_Warn( p_aout, "configured audio device '%s' not available, "
188 "using default instead", dev );
189 free( dev );
191 WAVEOUTCAPS waveoutcaps;
192 if(waveOutGetDevCaps( devid, &waveoutcaps,
193 sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
195 /* log debug some infos about driver, to know who to blame
196 if it doesn't work */
197 msg_Dbg( p_aout, "Drivername: %ls", waveoutcaps.szPname);
198 msg_Dbg( p_aout, "Driver Version: %d.%d",
199 (waveoutcaps.vDriverVersion>>8)&255,
200 waveoutcaps.vDriverVersion & 255);
201 msg_Dbg( p_aout, "Manufacturer identifier: 0x%x", waveoutcaps.wMid );
202 msg_Dbg( p_aout, "Product identifier: 0x%x", waveoutcaps.wPid );
207 /* Open the device */
208 if( AOUT_FMT_SPDIF(fmt) && var_InheritBool (p_aout, "spdif") )
211 if( OpenWaveOut( p_aout, devid, VLC_CODEC_SPDIFL,
212 fmt->i_physical_channels,
213 aout_FormatNbChannels( fmt ), fmt->i_rate, false )
214 == VLC_SUCCESS )
216 fmt->i_format = VLC_CODEC_SPDIFL;
218 /* Calculate the frame size in bytes */
219 fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
220 fmt->i_frame_length = A52_FRAME_NB;
221 p_aout->sys->i_buffer_size = fmt->i_bytes_per_frame;
222 p_aout->sys->b_spdif = true;
225 else
226 msg_Err( p_aout,
227 "cannot open waveout audio device for spdif fallback to PCM" );
230 if( fmt->i_format != VLC_CODEC_SPDIFL )
233 check for configured audio device!
235 fmt->i_format = var_InheritBool( p_aout, "waveout-float32" )?
236 VLC_CODEC_FL32: VLC_CODEC_S16N;
238 int max_chan = var_InheritInteger( p_aout, "waveout-audio-channels");
239 int i_channels = aout_FormatNbChannels(fmt);
240 i_channels = ( i_channels < max_chan )? i_channels: max_chan;
243 switch(i_channels)
245 case 9:
246 fmt->i_physical_channels = AOUT_CHANS_8_1;
247 break;
248 case 8:
249 fmt->i_physical_channels = AOUT_CHANS_7_1;
250 break;
251 case 7:
252 fmt->i_physical_channels = AOUT_CHANS_7_0;
253 break;
254 case 6:
255 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
256 | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT
257 | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
258 break;
259 case 5:
260 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
261 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
262 | AOUT_CHAN_LFE;
263 break;
264 case 4:
265 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
266 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
267 break;
268 case 3:
269 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
270 | AOUT_CHAN_LFE;
271 break;
272 case 2:
273 fmt->i_physical_channels = AOUT_CHANS_STEREO;
274 break;
275 case 1:
276 default:
277 fmt->i_physical_channels = AOUT_CHAN_CENTER;
279 msg_Dbg( p_aout, "Trying %d channels", i_channels );
281 while( ( OpenWaveOutPCM( p_aout, devid, &fmt->i_format,
282 fmt->i_physical_channels, i_channels,
283 fmt->i_rate, false ) != VLC_SUCCESS ) &&
284 --i_channels );
286 if( !i_channels )
288 msg_Err(p_aout, "Waveout couldn't find appropriate channel mapping");
289 return VLC_EGENERIC;
292 /* Calculate the frame size in bytes */
293 aout_FormatPrepare( fmt );
294 p_aout->sys->i_buffer_size = FRAME_SIZE * fmt->i_bytes_per_frame;
296 if( waveoutcaps.dwSupport & WAVECAPS_VOLUME )
298 aout_GainRequest( p_aout, 1.0f );
299 p_aout->sys->b_soft = false;
302 WaveoutMuteSet( p_aout, p_aout->sys->b_mute );
304 p_aout->sys->b_spdif = false;
307 p_aout->sys->i_rate = fmt->i_rate;
309 waveOutReset( p_aout->sys->h_waveout );
311 /* Allocate silence buffer */
312 p_aout->sys->p_silence_buffer =
313 malloc( p_aout->sys->i_buffer_size );
314 if( p_aout->sys->p_silence_buffer == NULL )
316 msg_Err( p_aout, "Couldn't alloc silence buffer... aborting");
317 return VLC_ENOMEM;
319 p_aout->sys->i_repeat_counter = 0;
322 /* Zero the buffer. WinCE doesn't have calloc(). */
323 memset( p_aout->sys->p_silence_buffer, 0,
324 p_aout->sys->i_buffer_size );
326 /* Now we need to setup our waveOut play notification structure */
327 p_aout->sys->i_frames = 0;
328 p_aout->sys->i_played_length = 0;
329 p_aout->sys->p_free_list = NULL;
331 return VLC_SUCCESS;
334 /*****************************************************************************
335 * Play: play a sound buffer
336 *****************************************************************************
337 * This doesn't actually play the buffer. This just stores the buffer so it
338 * can be played by the callback thread.
339 *****************************************************************************/
340 static void Play( audio_output_t *p_aout, block_t *block )
342 struct lkwavehdr * p_waveheader =
343 (struct lkwavehdr *) malloc(sizeof(struct lkwavehdr));
344 if(!p_waveheader)
346 msg_Err(p_aout, "Couldn't alloc WAVEHDR");
347 if( block )
348 block_Release( block );
349 return;
352 p_waveheader->p_next = NULL;
354 if( block && p_aout->sys->chans_to_reorder )
356 aout_ChannelReorder( block->p_buffer, block->i_buffer,
357 p_aout->sys->waveformat.Format.nChannels,
358 p_aout->sys->chan_table, p_aout->sys->format );
360 while( PlayWaveOut( p_aout, p_aout->sys->h_waveout, p_waveheader, block,
361 p_aout->sys->b_spdif ) != VLC_SUCCESS )
364 msg_Warn( p_aout, "Couln't write frame... sleeping");
365 msleep( block->i_length );
368 WaveOutClean( p_aout->sys );
369 WaveoutPollVolume( p_aout );
371 vlc_mutex_lock( &p_aout->sys->lock );
372 p_aout->sys->i_frames++;
373 p_aout->sys->i_played_length += block->i_length;
374 vlc_mutex_unlock( &p_aout->sys->lock );
377 /*****************************************************************************
378 * Close: close the audio device
379 *****************************************************************************/
380 static void Stop( audio_output_t *p_aout )
382 aout_sys_t *p_sys = p_aout->sys;
384 /* Before calling waveOutClose we must reset the device */
385 MMRESULT result = waveOutReset( p_sys->h_waveout );
386 if(result != MMSYSERR_NOERROR)
388 msg_Err( p_aout, "waveOutReset failed 0x%x", result );
390 now we must wait, that all buffers are played
391 because cancel doesn't work in this case...
393 if(result == MMSYSERR_NOTSUPPORTED)
396 clear currently played (done) buffers,
397 if returnvalue > 0 (means some buffer still playing)
398 wait for the driver event callback that one buffer
399 is finished with playing, and check again
400 the timeout of 5000ms is just, an emergency exit
401 of this loop, to avoid deadlock in case of other
402 (currently not known bugs, problems, errors cases?)
404 WaveOutFlush( p_aout, true );
408 /* wait for the frames to be queued in cleaning list */
409 WaveOutFlush( p_aout, true );
410 WaveOutClean( p_aout->sys );
412 /* now we can Close the device */
413 if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
415 msg_Err( p_aout, "waveOutClose failed" );
418 free( p_sys->p_silence_buffer );
419 p_aout->sys->i_played_length = 0;
420 p_sys->b_soft = true;
423 /*****************************************************************************
424 * OpenWaveOut: open the waveout sound device
425 ****************************************************************************/
426 static int OpenWaveOut( audio_output_t *p_aout, uint32_t i_device_id, int i_format,
427 int i_channels, int i_nb_channels, int i_rate,
428 bool b_probe )
430 MMRESULT result;
432 /* Set sound format */
434 #define waveformat p_aout->sys->waveformat
436 waveformat.dwChannelMask = 0;
437 for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ )
438 if( i_channels & pi_vlc_chan_order_wg4[i] )
439 waveformat.dwChannelMask |= pi_channels_in[i];
441 switch( i_format )
443 case VLC_CODEC_SPDIFL:
444 i_nb_channels = 2;
445 /* To prevent channel re-ordering */
446 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
447 waveformat.Format.wBitsPerSample = 16;
448 waveformat.Samples.wValidBitsPerSample =
449 waveformat.Format.wBitsPerSample;
450 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
451 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
452 break;
454 case VLC_CODEC_FL32:
455 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
456 waveformat.Samples.wValidBitsPerSample =
457 waveformat.Format.wBitsPerSample;
458 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
459 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
460 break;
462 case VLC_CODEC_S16N:
463 waveformat.Format.wBitsPerSample = 16;
464 waveformat.Samples.wValidBitsPerSample =
465 waveformat.Format.wBitsPerSample;
466 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
467 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_PCM;
468 break;
471 waveformat.Format.nChannels = i_nb_channels;
472 waveformat.Format.nSamplesPerSec = i_rate;
473 waveformat.Format.nBlockAlign =
474 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
475 waveformat.Format.nAvgBytesPerSec =
476 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
478 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
479 if( i_nb_channels <= 2 )
481 waveformat.Format.cbSize = 0;
483 else
485 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
486 waveformat.Format.cbSize =
487 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
490 if(!b_probe) {
491 msg_Dbg( p_aout, "OpenWaveDevice-ID: %u", i_device_id);
492 msg_Dbg( p_aout,"waveformat.Format.cbSize = %d",
493 waveformat.Format.cbSize);
494 msg_Dbg( p_aout,"waveformat.Format.wFormatTag = %u",
495 waveformat.Format.wFormatTag);
496 msg_Dbg( p_aout,"waveformat.Format.nChannels = %u",
497 waveformat.Format.nChannels);
498 msg_Dbg( p_aout,"waveformat.Format.nSamplesPerSec = %d",
499 (int)waveformat.Format.nSamplesPerSec);
500 msg_Dbg( p_aout,"waveformat.Format.nAvgBytesPerSec = %u",
501 (int)waveformat.Format.nAvgBytesPerSec);
502 msg_Dbg( p_aout,"waveformat.Format.nBlockAlign = %d",
503 waveformat.Format.nBlockAlign);
504 msg_Dbg( p_aout,"waveformat.Format.wBitsPerSample = %d",
505 waveformat.Format.wBitsPerSample);
506 msg_Dbg( p_aout,"waveformat.Samples.wValidBitsPerSample = %d",
507 waveformat.Samples.wValidBitsPerSample);
508 msg_Dbg( p_aout,"waveformat.Samples.wSamplesPerBlock = %d",
509 waveformat.Samples.wSamplesPerBlock);
510 msg_Dbg( p_aout,"waveformat.dwChannelMask = %u",
511 waveformat.dwChannelMask);
514 /* Open the device */
515 result = waveOutOpen( &p_aout->sys->h_waveout, i_device_id,
516 (WAVEFORMATEX *)&waveformat,
517 (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
518 CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) );
519 if( result == WAVERR_BADFORMAT )
521 msg_Warn( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" );
522 return VLC_EGENERIC;
524 if( result == MMSYSERR_ALLOCATED )
526 msg_Warn( p_aout, "waveOutOpen failed WAVERR_ALLOCATED" );
527 return VLC_EGENERIC;
529 if( result != MMSYSERR_NOERROR )
531 msg_Warn( p_aout, "waveOutOpen failed" );
532 return VLC_EGENERIC;
535 p_aout->sys->chans_to_reorder =
536 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
537 waveformat.dwChannelMask,
538 p_aout->sys->chan_table );
539 if( p_aout->sys->chans_to_reorder )
540 msg_Dbg( p_aout, "channel reordering needed" );
541 p_aout->sys->format = i_format;
543 return VLC_SUCCESS;
545 #undef waveformat
549 /*****************************************************************************
550 * OpenWaveOutPCM: open a PCM waveout sound device
551 ****************************************************************************/
552 static int OpenWaveOutPCM( audio_output_t *p_aout, uint32_t i_device_id,
553 vlc_fourcc_t *i_format,
554 int i_channels, int i_nb_channels, int i_rate,
555 bool b_probe )
557 bool b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32");
559 if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_CODEC_FL32,
560 i_channels, i_nb_channels, i_rate, b_probe )
561 != VLC_SUCCESS )
563 if ( OpenWaveOut( p_aout, i_device_id, VLC_CODEC_S16N,
564 i_channels, i_nb_channels, i_rate, b_probe )
565 != VLC_SUCCESS )
567 return VLC_EGENERIC;
569 else
571 *i_format = VLC_CODEC_S16N;
572 return VLC_SUCCESS;
575 else
577 *i_format = VLC_CODEC_FL32;
578 return VLC_SUCCESS;
582 /*****************************************************************************
583 * PlayWaveOut: play a buffer through the WaveOut device
584 *****************************************************************************/
585 static int PlayWaveOut( audio_output_t *p_aout, HWAVEOUT h_waveout,
586 struct lkwavehdr *p_waveheader, block_t *p_buffer, bool b_spdif)
588 MMRESULT result;
590 /* Prepare the buffer */
591 if( p_buffer != NULL )
593 p_waveheader->hdr.lpData = (LPSTR)p_buffer->p_buffer;
594 p_waveheader->hdr.dwBufferLength = p_buffer->i_buffer;
596 copy the buffer to the silence buffer :) so in case we don't
597 get the next buffer fast enough (I will repeat this one a time
598 for AC3 / DTS and SPDIF this will sound better instead of
599 a hickup)
601 if(b_spdif)
603 memcpy( p_aout->sys->p_silence_buffer,
604 p_buffer->p_buffer,
605 p_aout->sys->i_buffer_size );
606 p_aout->sys->i_repeat_counter = 2;
608 } else {
609 /* Use silence buffer instead */
610 if(p_aout->sys->i_repeat_counter)
612 p_aout->sys->i_repeat_counter--;
613 if(!p_aout->sys->i_repeat_counter)
615 memset( p_aout->sys->p_silence_buffer,
616 0x00, p_aout->sys->i_buffer_size );
619 p_waveheader->hdr.lpData = (LPSTR)p_aout->sys->p_silence_buffer;
620 p_waveheader->hdr.dwBufferLength = p_aout->sys->i_buffer_size;
623 p_waveheader->hdr.dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
624 p_waveheader->hdr.dwFlags = 0;
626 result = waveOutPrepareHeader( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
627 if( result != MMSYSERR_NOERROR )
629 msg_Err( p_aout, "waveOutPrepareHeader failed" );
630 return VLC_EGENERIC;
633 /* Send the buffer to the waveOut queue */
634 result = waveOutWrite( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
635 if( result != MMSYSERR_NOERROR )
637 msg_Err( p_aout, "waveOutWrite failed" );
638 return VLC_EGENERIC;
641 return VLC_SUCCESS;
644 /*****************************************************************************
645 * WaveOutCallback: what to do once WaveOut has played its sound samples
646 *****************************************************************************/
647 static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
648 DWORD_PTR _p_aout,
649 DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
651 (void) h_waveout;
652 (void) dwParam2;
653 audio_output_t *p_aout = (audio_output_t *)_p_aout;
654 struct lkwavehdr * p_waveheader = (struct lkwavehdr *) dwParam1;
656 if( uMsg != WOM_DONE ) return;
658 vlc_mutex_lock( &p_aout->sys->lock );
659 p_waveheader->p_next = p_aout->sys->p_free_list;
660 p_aout->sys->p_free_list = p_waveheader;
661 p_aout->sys->i_frames--;
662 vlc_cond_broadcast( &p_aout->sys->cond );
663 vlc_mutex_unlock( &p_aout->sys->lock );
666 static void WaveOutClean( aout_sys_t * p_sys )
668 struct lkwavehdr *p_whdr, *p_list;
670 vlc_mutex_lock(&p_sys->lock);
671 p_list = p_sys->p_free_list;
672 p_sys->p_free_list = NULL;
673 vlc_mutex_unlock(&p_sys->lock);
675 while( p_list )
677 p_whdr = p_list;
678 p_list = p_list->p_next;
679 WaveOutClearBuffer( p_sys->h_waveout, &p_whdr->hdr );
680 free(p_whdr);
684 static void WaveOutClearBuffer( HWAVEOUT h_waveout, WAVEHDR *p_waveheader )
686 block_t *p_buffer = (block_t *)(p_waveheader->dwUser);
687 /* Unprepare and free the buffers which has just been played */
688 waveOutUnprepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
690 if( p_waveheader->dwUser != 1 )
691 block_Release( p_buffer );
695 reload the configuration drop down list, of the Audio Devices
697 static int ReloadWaveoutDevices( vlc_object_t *p_this, char const *psz_name,
698 char ***values, char ***descs )
700 int n = 0, nb_devices = waveOutGetNumDevs();
702 VLC_UNUSED( p_this); VLC_UNUSED( psz_name );
704 *values = xmalloc( (nb_devices + 1) * sizeof(char *) );
705 *descs = xmalloc( (nb_devices + 1) * sizeof(char *) );
707 (*values)[n] = strdup( "wavemapper" );
708 (*descs)[n] = strdup( _("Microsoft Soundmapper") );
709 n++;
711 for(int i = 0; i < nb_devices; i++)
713 WAVEOUTCAPS caps;
714 wchar_t dev_name[MAXPNAMELEN+32];
716 if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
717 != MMSYSERR_NOERROR)
718 continue;
720 _snwprintf(dev_name, MAXPNAMELEN + 32, device_name_fmt,
721 caps.szPname, caps.wMid, caps.wPid);
722 (*values)[n] = FromWide( dev_name );
723 (*descs)[n] = strdup( (*values)[n] );
724 n++;
727 return n;
731 convert devicename to device ID for output
732 if device not found return WAVE_MAPPER, so let
733 windows decide which preferred audio device
734 should be used.
736 static uint32_t findDeviceID(char *psz_device_name)
738 if( !psz_device_name )
739 return WAVE_MAPPER;
741 uint32_t wave_devices = waveOutGetNumDevs();
743 for( uint32_t i = 0; i < wave_devices; i++ )
745 WAVEOUTCAPS caps;
746 wchar_t dev_name[MAXPNAMELEN+32];
748 if( waveOutGetDevCaps( i, &caps, sizeof(WAVEOUTCAPS) )
749 != MMSYSERR_NOERROR )
750 continue;
752 _snwprintf( dev_name, MAXPNAMELEN + 32, device_name_fmt,
753 caps.szPname, caps.wMid, caps.wPid );
754 char *u8 = FromWide(dev_name);
755 if( !stricmp(u8, psz_device_name) )
757 free( u8 );
758 return i;
760 free( u8 );
763 return WAVE_MAPPER;
766 static int DeviceSelect (audio_output_t *aout, const char *id)
768 var_SetString(aout, "waveout-audio-device", (id != NULL) ? id : "");
769 aout_DeviceReport (aout, id);
770 aout_RestartRequest (aout, AOUT_RESTART_OUTPUT);
771 return 0;
774 static int Open(vlc_object_t *obj)
776 audio_output_t *aout = (audio_output_t *)obj;
777 aout_sys_t *sys = malloc(sizeof (*sys));
779 if (unlikely(sys == NULL))
780 return VLC_ENOMEM;
781 aout->sys = sys;
782 aout->start = Start;
783 aout->stop = Stop;
784 aout->volume_set = WaveoutVolumeSet;
785 aout->mute_set = WaveoutMuteSet;
786 aout->device_select = DeviceSelect;
788 sys->f_volume = var_InheritFloat(aout, "waveout-volume");
789 sys->b_mute = var_InheritBool(aout, "mute");
791 aout_MuteReport(aout, sys->b_mute);
792 aout_VolumeReport(aout, sys->f_volume );
794 if( vlc_timer_create( &sys->volume_poll_timer,
795 WaveoutPollVolume, aout ) )
797 msg_Err( aout, "Couldn't create volume polling timer" );
798 free( sys );
799 return VLC_ENOMEM;
802 vlc_mutex_init( &sys->lock );
803 vlc_cond_init( &sys->cond );
805 /* WaveOut does not support hot-plug events so list devices at startup */
806 char **ids, **names;
807 int count = ReloadWaveoutDevices(VLC_OBJECT(aout), NULL, &ids, &names);
808 if (count >= 0)
810 for (int i = 0; i < count; i++)
812 aout_HotplugReport(aout, ids[i], names[i]);
813 free(names[i]);
814 free(ids[i]);
816 free(names);
817 free(ids);
820 char *dev = var_CreateGetNonEmptyString(aout, "waveout-audio-device");
821 aout_DeviceReport(aout, dev);
822 free(dev);
824 return VLC_SUCCESS;
827 static void Close(vlc_object_t *obj)
829 audio_output_t *aout = (audio_output_t *)obj;
830 aout_sys_t *sys = aout->sys;
832 var_Destroy(aout, "waveout-audio-device");
834 vlc_timer_destroy( sys->volume_poll_timer );
835 vlc_cond_destroy( &sys->cond );
836 vlc_mutex_destroy( &sys->lock );
838 free(sys);
841 static int WaveOutTimeGet(audio_output_t * p_aout, mtime_t *delay)
843 MMTIME mmtime;
844 mmtime.wType = TIME_SAMPLES;
846 if( !p_aout->sys->i_frames )
847 return -1;
849 if( waveOutGetPosition( p_aout->sys->h_waveout, &mmtime, sizeof(MMTIME) )
850 != MMSYSERR_NOERROR )
852 msg_Err( p_aout, "waveOutGetPosition failed");
853 return -1;
856 mtime_t i_pos = (mtime_t) mmtime.u.sample * CLOCK_FREQ / p_aout->sys->i_rate;
857 *delay = p_aout->sys->i_played_length - i_pos;
858 return 0;
861 static void WaveOutFlush( audio_output_t *p_aout, bool wait)
863 MMRESULT res;
864 if( !wait )
866 res = waveOutReset( p_aout->sys->h_waveout );
867 p_aout->sys->i_played_length = 0;
868 if( res != MMSYSERR_NOERROR )
869 msg_Err( p_aout, "waveOutReset failed");
871 else
873 vlc_mutex_lock( &p_aout->sys->lock );
874 while( p_aout->sys->i_frames )
876 vlc_cond_wait( &p_aout->sys->cond, &p_aout->sys-> lock );
878 vlc_mutex_unlock( &p_aout->sys->lock );
882 static void WaveOutPause( audio_output_t * p_aout, bool pause, mtime_t date)
884 MMRESULT res;
885 (void) date;
886 if(pause)
888 vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 1, 200000 );
889 res = waveOutPause( p_aout->sys->h_waveout );
890 if( res != MMSYSERR_NOERROR )
892 msg_Err( p_aout, "waveOutPause failed (0x%x)", res);
893 return;
896 else
898 vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 0, 0 );
899 res = waveOutRestart( p_aout->sys->h_waveout );
900 if( res != MMSYSERR_NOERROR )
902 msg_Err( p_aout, "waveOutRestart failed (0x%x)", res);
903 return;
908 static int WaveoutVolumeSet( audio_output_t *p_aout, float volume )
910 aout_sys_t *sys = p_aout->sys;
912 if( sys->b_soft )
914 float gain = volume * volume * volume;
915 if ( !sys->b_mute && aout_GainRequest( p_aout, gain ) )
916 return -1;
918 else
920 const HWAVEOUT hwo = sys->h_waveout;
922 uint32_t vol = lroundf( volume * 0x7fff.fp0 );
924 if( !sys->b_mute )
926 if( vol > 0xffff )
928 vol = 0xffff;
929 volume = 2.0f;
932 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
933 if( r != MMSYSERR_NOERROR )
935 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
936 return -1;
941 vlc_mutex_lock(&p_aout->sys->lock);
942 sys->f_volume = volume;
944 if( var_InheritBool( p_aout, "volume-save" ) )
945 config_PutFloat( p_aout, "waveout-volume", volume );
947 aout_VolumeReport( p_aout, volume );
948 vlc_mutex_unlock(&p_aout->sys->lock);
950 return 0;
953 static int WaveoutMuteSet( audio_output_t * p_aout, bool mute )
955 aout_sys_t *sys = p_aout->sys;
957 if( sys->b_soft )
959 float gain = sys->f_volume * sys->f_volume * sys->f_volume;
960 if ( aout_GainRequest( p_aout, mute ? 0.f : gain ) )
961 return -1;
963 else
966 const HWAVEOUT hwo = sys->h_waveout;
967 uint32_t vol = mute ? 0 : lroundf( sys->f_volume * 0x7fff.fp0 );
969 if( vol > 0xffff )
970 vol = 0xffff;
972 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
973 if( r != MMSYSERR_NOERROR )
975 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
976 return -1;
980 vlc_mutex_lock(&p_aout->sys->lock);
981 sys->b_mute = mute;
982 aout_MuteReport( p_aout, mute );
983 vlc_mutex_unlock(&p_aout->sys->lock);
985 return 0;
988 static void WaveoutPollVolume( void * aout )
990 audio_output_t * p_aout = (audio_output_t *) aout;
991 uint32_t vol;
993 MMRESULT r = waveOutGetVolume( p_aout->sys->h_waveout, (LPDWORD) &vol );
995 if( r != MMSYSERR_NOERROR )
997 msg_Err( p_aout, "waveOutGetVolume failed (%u)", r );
998 return;
1001 float volume = (float) ( vol & UINT32_C( 0xffff ) );
1002 volume /= 0x7fff.fp0;
1004 vlc_mutex_lock(&p_aout->sys->lock);
1005 if( !p_aout->sys->b_mute && volume != p_aout->sys->f_volume )
1007 p_aout->sys->f_volume = volume;
1009 if( var_InheritBool( p_aout, "volume-save" ) )
1010 config_PutFloat( p_aout, "waveout-volume", volume );
1012 aout_VolumeReport( p_aout, volume );
1014 vlc_mutex_unlock(&p_aout->sys->lock);
1016 return;