1 /*****************************************************************************
2 * waveout.c : Windows waveOut plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2001-2009 VLC authors and VideoLAN
7 * Authors: Gildas Bazin <gbazin@videolan.org>
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 /*****************************************************************************
27 *****************************************************************************/
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
41 #include <vlc_charset.h> /* FromWide() */
42 #include <vlc_atomic.h>
44 #include "windows_audio_common.h"
46 #define FRAME_SIZE 4096 /* The size is in samples, not in bytes */
48 /*****************************************************************************
50 *****************************************************************************/
51 static int Open ( vlc_object_t * );
52 static void Close ( vlc_object_t * );
53 static void Play ( audio_output_t *, block_t * );
55 /*****************************************************************************
56 * notification_thread_t: waveOut event thread
57 *****************************************************************************/
61 struct lkwavehdr * p_next;
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 *,
72 static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR );
74 static void WaveOutClean( aout_sys_t * p_sys );
76 static void WaveOutClearBuffer( HWAVEOUT, WAVEHDR *);
78 static int ReloadWaveoutDevices( vlc_object_t *, const 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);
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 *****************************************************************************/
100 uint32_t i_wave_device_id; /* ID of selected output device */
102 HWAVEOUT h_waveout; /* handle to waveout instance */
104 WAVEFORMATEXTENSIBLE waveformat; /* audio format */
108 int i_repeat_counter;
114 uint8_t *p_silence_buffer; /* buffer we use to play silence */
120 bool b_soft; /* Use software gain */
121 uint8_t chans_to_reorder; /* do we need channel reordering */
123 uint8_t chan_table[AOUT_CHAN_MAX];
126 mtime_t i_played_length;
128 struct lkwavehdr * p_free_list;
132 vlc_timer_t volume_poll_timer;
135 /*****************************************************************************
137 *****************************************************************************/
138 #define DEVICE_TEXT N_("Select Audio Device")
139 #define DEVICE_LONG N_("Select special Audio device, or let windows "\
140 "decide (default), change needs VLC restart "\
142 #define DEFAULT_AUDIO_DEVICE N_("Default Audio Device")
144 #define AUDIO_CHAN_TEXT N_("Audio output channels")
145 #define AUDIO_CHAN_LONGTEXT N_("Channels available for audio output. " \
146 "If the input has more channels than the output, it will be down-mixed. " \
147 "This parameter is ignored when digital pass-through is active.")
149 #define VOLUME_TEXT N_("Audio volume")
152 set_shortname( "WaveOut" )
153 set_description( N_("Win32 waveOut extension output") )
154 set_capability( "audio output", 50 )
155 set_category( CAT_AUDIO )
156 set_subcategory( SUBCAT_AUDIO_AOUT )
157 add_string( "waveout-audio-device", "wavemapper",
158 DEVICE_TEXT, DEVICE_LONG, false )
159 change_string_cb( ReloadWaveoutDevices )
160 add_float( "waveout-volume", 1.0f, VOLUME_TEXT, NULL, true )
161 change_float_range(0.0f, 2.0f)
162 add_bool( "waveout-float32", true, FLOAT_TEXT, FLOAT_LONGTEXT, true )
163 add_integer ("waveout-audio-channels", 6, AUDIO_CHAN_TEXT,
164 AUDIO_CHAN_LONGTEXT, false)
165 change_integer_range(1,6)
166 set_callbacks( Open, Close )
169 /*****************************************************************************
170 * Opens the audio device
171 *****************************************************************************
172 * This function opens and setups Win32 waveOut
173 *****************************************************************************/
174 static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
178 p_aout->time_get = WaveOutTimeGet;
180 p_aout->pause = WaveOutPause;
181 p_aout->flush = WaveOutFlush;
183 /* Default behaviour is to use software gain */
184 p_aout->sys->b_soft = true;
187 check for configured audio device!
189 fmt->i_format = var_InheritBool( p_aout, "waveout-float32" )?
190 VLC_CODEC_FL32: VLC_CODEC_S16N;
192 char *psz_waveout_dev = var_CreateGetString( p_aout, "waveout-audio-device");
194 p_aout->sys->i_wave_device_id =
195 findDeviceID( psz_waveout_dev );
197 if(p_aout->sys->i_wave_device_id == WAVE_MAPPER)
199 if(psz_waveout_dev &&
200 stricmp(psz_waveout_dev,"wavemapper"))
202 msg_Warn( p_aout, "configured audio device '%s' not available, "\
203 "use default instead", psz_waveout_dev );
206 free( psz_waveout_dev );
209 WAVEOUTCAPS waveoutcaps;
210 if(waveOutGetDevCaps( p_aout->sys->i_wave_device_id,
212 sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
214 /* log debug some infos about driver, to know who to blame
215 if it doesn't work */
216 msg_Dbg( p_aout, "Drivername: %ls", waveoutcaps.szPname);
217 msg_Dbg( p_aout, "Driver Version: %d.%d",
218 (waveoutcaps.vDriverVersion>>8)&255,
219 waveoutcaps.vDriverVersion & 255);
220 msg_Dbg( p_aout, "Manufacturer identifier: 0x%x", waveoutcaps.wMid );
221 msg_Dbg( p_aout, "Product identifier: 0x%x", waveoutcaps.wPid );
226 /* Open the device */
227 if( AOUT_FMT_SPDIF(fmt) && var_InheritBool (p_aout, "spdif") )
230 if( OpenWaveOut( p_aout, p_aout->sys->i_wave_device_id,
231 VLC_CODEC_SPDIFL, fmt->i_physical_channels,
232 aout_FormatNbChannels( fmt ), fmt->i_rate, false )
235 fmt->i_format = VLC_CODEC_SPDIFL;
237 /* Calculate the frame size in bytes */
238 fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
239 fmt->i_frame_length = A52_FRAME_NB;
240 p_aout->sys->i_buffer_size = fmt->i_bytes_per_frame;
241 p_aout->sys->b_spdif = true;
246 "cannot open waveout audio device for spdif fallback to PCM" );
249 if( fmt->i_format != VLC_CODEC_SPDIFL )
251 int max_chan = var_InheritInteger( p_aout, "waveout-audio-channels");
252 int i_channels = aout_FormatNbChannels(fmt);
253 i_channels = ( i_channels < max_chan )? i_channels: max_chan;
259 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
260 | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT
261 | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
264 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
265 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
269 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
270 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
273 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
277 fmt->i_physical_channels = AOUT_CHANS_STEREO;
281 fmt->i_physical_channels = AOUT_CHAN_CENTER;
283 msg_Dbg( p_aout, "Trying %d channels", i_channels );
285 while( ( OpenWaveOutPCM( p_aout, p_aout->sys->i_wave_device_id,
286 &fmt->i_format, fmt->i_physical_channels,
287 i_channels, fmt->i_rate, false ) != VLC_SUCCESS ) &&
292 msg_Err(p_aout, "Waveout couldn't find appropriate channel mapping");
296 /* Calculate the frame size in bytes */
297 aout_FormatPrepare( fmt );
298 p_aout->sys->i_buffer_size = FRAME_SIZE * fmt->i_bytes_per_frame;
300 if( waveoutcaps.dwSupport & WAVECAPS_VOLUME )
302 aout_GainRequest( p_aout, 1.0f );
303 p_aout->sys->b_soft = false;
306 WaveoutMuteSet( p_aout, p_aout->sys->b_mute );
308 p_aout->sys->b_spdif = false;
311 p_aout->sys->i_rate = fmt->i_rate;
313 waveOutReset( p_aout->sys->h_waveout );
315 /* Allocate silence buffer */
316 p_aout->sys->p_silence_buffer =
317 malloc( p_aout->sys->i_buffer_size );
318 if( p_aout->sys->p_silence_buffer == NULL )
320 msg_Err( p_aout, "Couldn't alloc silence buffer... aborting");
323 p_aout->sys->i_repeat_counter = 0;
326 /* Zero the buffer. WinCE doesn't have calloc(). */
327 memset( p_aout->sys->p_silence_buffer, 0,
328 p_aout->sys->i_buffer_size );
330 /* Now we need to setup our waveOut play notification structure */
331 p_aout->sys->i_frames = 0;
332 p_aout->sys->i_played_length = 0;
333 p_aout->sys->p_free_list = NULL;
338 /*****************************************************************************
339 * Play: play a sound buffer
340 *****************************************************************************
341 * This doesn't actually play the buffer. This just stores the buffer so it
342 * can be played by the callback thread.
343 *****************************************************************************/
344 static void Play( audio_output_t *p_aout, block_t *block )
346 struct lkwavehdr * p_waveheader =
347 (struct lkwavehdr *) malloc(sizeof(struct lkwavehdr));
350 msg_Err(p_aout, "Couldn't alloc WAVEHDR");
352 block_Release( block );
356 p_waveheader->p_next = NULL;
358 if( block && p_aout->sys->chans_to_reorder )
360 aout_ChannelReorder( block->p_buffer, block->i_buffer,
361 p_aout->sys->waveformat.Format.nChannels,
362 p_aout->sys->chan_table, p_aout->sys->format );
364 while( PlayWaveOut( p_aout, p_aout->sys->h_waveout, p_waveheader, block,
365 p_aout->sys->b_spdif ) != VLC_SUCCESS )
368 msg_Warn( p_aout, "Couln't write frame... sleeping");
369 msleep( block->i_length );
372 WaveOutClean( p_aout->sys );
373 WaveoutPollVolume( p_aout );
375 vlc_mutex_lock( &p_aout->sys->lock );
376 p_aout->sys->i_frames++;
377 p_aout->sys->i_played_length += block->i_length;
378 vlc_mutex_unlock( &p_aout->sys->lock );
381 /*****************************************************************************
382 * Close: close the audio device
383 *****************************************************************************/
384 static void Stop( audio_output_t *p_aout )
386 aout_sys_t *p_sys = p_aout->sys;
388 /* Before calling waveOutClose we must reset the device */
389 MMRESULT result = waveOutReset( p_sys->h_waveout );
390 if(result != MMSYSERR_NOERROR)
392 msg_Err( p_aout, "waveOutReset failed 0x%x", result );
394 now we must wait, that all buffers are played
395 because cancel doesn't work in this case...
397 if(result == MMSYSERR_NOTSUPPORTED)
400 clear currently played (done) buffers,
401 if returnvalue > 0 (means some buffer still playing)
402 wait for the driver event callback that one buffer
403 is finished with playing, and check again
404 the timeout of 5000ms is just, an emergency exit
405 of this loop, to avoid deadlock in case of other
406 (currently not known bugs, problems, errors cases?)
408 WaveOutFlush( p_aout, true );
412 /* wait for the frames to be queued in cleaning list */
413 WaveOutFlush( p_aout, true );
414 WaveOutClean( p_aout->sys );
416 /* now we can Close the device */
417 if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
419 msg_Err( p_aout, "waveOutClose failed" );
422 free( p_sys->p_silence_buffer );
423 p_aout->sys->i_played_length = 0;
424 p_sys->b_soft = true;
427 /*****************************************************************************
428 * OpenWaveOut: open the waveout sound device
429 ****************************************************************************/
430 static int OpenWaveOut( audio_output_t *p_aout, uint32_t i_device_id, int i_format,
431 int i_channels, int i_nb_channels, int i_rate,
436 /* Set sound format */
438 #define waveformat p_aout->sys->waveformat
440 waveformat.dwChannelMask = 0;
441 for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ )
442 if( i_channels & pi_vlc_chan_order_wg4[i] )
443 waveformat.dwChannelMask |= pi_channels_in[i];
447 case VLC_CODEC_SPDIFL:
449 /* To prevent channel re-ordering */
450 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
451 waveformat.Format.wBitsPerSample = 16;
452 waveformat.Samples.wValidBitsPerSample =
453 waveformat.Format.wBitsPerSample;
454 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
455 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
459 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
460 waveformat.Samples.wValidBitsPerSample =
461 waveformat.Format.wBitsPerSample;
462 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
463 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
467 waveformat.Format.wBitsPerSample = 16;
468 waveformat.Samples.wValidBitsPerSample =
469 waveformat.Format.wBitsPerSample;
470 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
471 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_PCM;
475 waveformat.Format.nChannels = i_nb_channels;
476 waveformat.Format.nSamplesPerSec = i_rate;
477 waveformat.Format.nBlockAlign =
478 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
479 waveformat.Format.nAvgBytesPerSec =
480 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
482 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
483 if( i_nb_channels <= 2 )
485 waveformat.Format.cbSize = 0;
489 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
490 waveformat.Format.cbSize =
491 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
495 msg_Dbg( p_aout, "OpenWaveDevice-ID: %u", i_device_id);
496 msg_Dbg( p_aout,"waveformat.Format.cbSize = %d",
497 waveformat.Format.cbSize);
498 msg_Dbg( p_aout,"waveformat.Format.wFormatTag = %u",
499 waveformat.Format.wFormatTag);
500 msg_Dbg( p_aout,"waveformat.Format.nChannels = %u",
501 waveformat.Format.nChannels);
502 msg_Dbg( p_aout,"waveformat.Format.nSamplesPerSec = %d",
503 (int)waveformat.Format.nSamplesPerSec);
504 msg_Dbg( p_aout,"waveformat.Format.nAvgBytesPerSec = %u",
505 (int)waveformat.Format.nAvgBytesPerSec);
506 msg_Dbg( p_aout,"waveformat.Format.nBlockAlign = %d",
507 waveformat.Format.nBlockAlign);
508 msg_Dbg( p_aout,"waveformat.Format.wBitsPerSample = %d",
509 waveformat.Format.wBitsPerSample);
510 msg_Dbg( p_aout,"waveformat.Samples.wValidBitsPerSample = %d",
511 waveformat.Samples.wValidBitsPerSample);
512 msg_Dbg( p_aout,"waveformat.Samples.wSamplesPerBlock = %d",
513 waveformat.Samples.wSamplesPerBlock);
514 msg_Dbg( p_aout,"waveformat.dwChannelMask = %lx",
515 waveformat.dwChannelMask);
518 /* Open the device */
519 result = waveOutOpen( &p_aout->sys->h_waveout, i_device_id,
520 (WAVEFORMATEX *)&waveformat,
521 (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
522 CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) );
523 if( result == WAVERR_BADFORMAT )
525 msg_Warn( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" );
528 if( result == MMSYSERR_ALLOCATED )
530 msg_Warn( p_aout, "waveOutOpen failed WAVERR_ALLOCATED" );
533 if( result != MMSYSERR_NOERROR )
535 msg_Warn( p_aout, "waveOutOpen failed" );
539 p_aout->sys->chans_to_reorder =
540 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
541 waveformat.dwChannelMask,
542 p_aout->sys->chan_table );
543 if( p_aout->sys->chans_to_reorder )
544 msg_Dbg( p_aout, "channel reordering needed" );
545 p_aout->sys->format = i_format;
553 /*****************************************************************************
554 * OpenWaveOutPCM: open a PCM waveout sound device
555 ****************************************************************************/
556 static int OpenWaveOutPCM( audio_output_t *p_aout, uint32_t i_device_id,
557 vlc_fourcc_t *i_format,
558 int i_channels, int i_nb_channels, int i_rate,
561 bool b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32");
563 if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_CODEC_FL32,
564 i_channels, i_nb_channels, i_rate, b_probe )
567 if ( OpenWaveOut( p_aout, i_device_id, VLC_CODEC_S16N,
568 i_channels, i_nb_channels, i_rate, b_probe )
575 *i_format = VLC_CODEC_S16N;
581 *i_format = VLC_CODEC_FL32;
586 /*****************************************************************************
587 * PlayWaveOut: play a buffer through the WaveOut device
588 *****************************************************************************/
589 static int PlayWaveOut( audio_output_t *p_aout, HWAVEOUT h_waveout,
590 struct lkwavehdr *p_waveheader, block_t *p_buffer, bool b_spdif)
594 /* Prepare the buffer */
595 if( p_buffer != NULL )
597 p_waveheader->hdr.lpData = (LPSTR)p_buffer->p_buffer;
598 p_waveheader->hdr.dwBufferLength = p_buffer->i_buffer;
600 copy the buffer to the silence buffer :) so in case we don't
601 get the next buffer fast enough (I will repeat this one a time
602 for AC3 / DTS and SPDIF this will sound better instead of
607 memcpy( p_aout->sys->p_silence_buffer,
609 p_aout->sys->i_buffer_size );
610 p_aout->sys->i_repeat_counter = 2;
613 /* Use silence buffer instead */
614 if(p_aout->sys->i_repeat_counter)
616 p_aout->sys->i_repeat_counter--;
617 if(!p_aout->sys->i_repeat_counter)
619 memset( p_aout->sys->p_silence_buffer,
620 0x00, p_aout->sys->i_buffer_size );
623 p_waveheader->hdr.lpData = (LPSTR)p_aout->sys->p_silence_buffer;
624 p_waveheader->hdr.dwBufferLength = p_aout->sys->i_buffer_size;
627 p_waveheader->hdr.dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
628 p_waveheader->hdr.dwFlags = 0;
630 result = waveOutPrepareHeader( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
631 if( result != MMSYSERR_NOERROR )
633 msg_Err( p_aout, "waveOutPrepareHeader failed" );
637 /* Send the buffer to the waveOut queue */
638 result = waveOutWrite( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
639 if( result != MMSYSERR_NOERROR )
641 msg_Err( p_aout, "waveOutWrite failed" );
648 /*****************************************************************************
649 * WaveOutCallback: what to do once WaveOut has played its sound samples
650 *****************************************************************************/
651 static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
653 DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
657 audio_output_t *p_aout = (audio_output_t *)_p_aout;
658 struct lkwavehdr * p_waveheader = (struct lkwavehdr *) dwParam1;
660 if( uMsg != WOM_DONE ) return;
662 vlc_mutex_lock( &p_aout->sys->lock );
663 p_waveheader->p_next = p_aout->sys->p_free_list;
664 p_aout->sys->p_free_list = p_waveheader;
665 p_aout->sys->i_frames--;
666 vlc_cond_broadcast( &p_aout->sys->cond );
667 vlc_mutex_unlock( &p_aout->sys->lock );
670 static void WaveOutClean( aout_sys_t * p_sys )
672 struct lkwavehdr *p_whdr, *p_list;
674 vlc_mutex_lock(&p_sys->lock);
675 p_list = p_sys->p_free_list;
676 p_sys->p_free_list = NULL;
677 vlc_mutex_unlock(&p_sys->lock);
682 p_list = p_list->p_next;
683 WaveOutClearBuffer( p_sys->h_waveout, &p_whdr->hdr );
688 static void WaveOutClearBuffer( HWAVEOUT h_waveout, WAVEHDR *p_waveheader )
690 block_t *p_buffer = (block_t *)(p_waveheader->dwUser);
691 /* Unprepare and free the buffers which has just been played */
692 waveOutUnprepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
694 if( p_waveheader->dwUser != 1 )
695 block_Release( p_buffer );
699 reload the configuration drop down list, of the Audio Devices
701 static int ReloadWaveoutDevices( vlc_object_t *p_this, char const *psz_name,
702 char ***values, char ***descs )
704 int n = 0, nb_devices = waveOutGetNumDevs();
706 VLC_UNUSED( p_this); VLC_UNUSED( psz_name );
708 *values = xmalloc( (nb_devices + 1) * sizeof(char *) );
709 *descs = xmalloc( (nb_devices + 1) * sizeof(char *) );
711 (*values)[n] = strdup( "wavemapper" );
712 (*descs)[n] = strdup( _("Microsoft Soundmapper") );
715 for(int i = 0; i < nb_devices; i++)
718 wchar_t dev_name[MAXPNAMELEN+32];
720 if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
724 _snwprintf(dev_name, MAXPNAMELEN + 32, device_name_fmt,
725 caps.szPname, caps.wMid, caps.wPid);
726 (*values)[n] = FromWide( dev_name );
727 (*descs)[n] = strdup( (*values)[n] );
735 convert devicename to device ID for output
736 if device not found return WAVE_MAPPER, so let
737 windows decide which preferred audio device
740 static uint32_t findDeviceID(char *psz_device_name)
742 if( !psz_device_name )
745 uint32_t wave_devices = waveOutGetNumDevs();
747 for( uint32_t i = 0; i < wave_devices; i++ )
750 wchar_t dev_name[MAXPNAMELEN+32];
752 if( waveOutGetDevCaps( i, &caps, sizeof(WAVEOUTCAPS) )
753 != MMSYSERR_NOERROR )
756 _snwprintf( dev_name, MAXPNAMELEN + 32, device_name_fmt,
757 caps.szPname, caps.wMid, caps.wPid );
758 char *u8 = FromWide(dev_name);
759 if( !stricmp(u8, psz_device_name) )
770 static int Open(vlc_object_t *obj)
772 audio_output_t *aout = (audio_output_t *)obj;
773 aout_sys_t *sys = malloc(sizeof (*sys));
775 if (unlikely(sys == NULL))
780 aout->volume_set = WaveoutVolumeSet;
781 aout->mute_set = WaveoutMuteSet;
783 sys->f_volume = var_InheritFloat(aout, "waveout-volume");
784 sys->b_mute = var_InheritBool(aout, "mute");
786 aout_MuteReport(aout, sys->b_mute);
787 aout_VolumeReport(aout, sys->f_volume );
789 if( vlc_timer_create( &sys->volume_poll_timer,
790 WaveoutPollVolume, aout ) )
792 msg_Err( aout, "Couldn't create volume polling timer" );
797 vlc_mutex_init( &sys->lock );
798 vlc_cond_init( &sys->cond );
803 static void Close(vlc_object_t *obj)
805 audio_output_t *aout = (audio_output_t *)obj;
806 aout_sys_t *sys = aout->sys;
808 vlc_timer_destroy( sys->volume_poll_timer );
810 vlc_cond_destroy( &sys->cond );
811 vlc_mutex_destroy( &sys->lock );
816 static int WaveOutTimeGet(audio_output_t * p_aout, mtime_t *delay)
819 mmtime.wType = TIME_SAMPLES;
821 if( !p_aout->sys->i_frames )
824 if( waveOutGetPosition( p_aout->sys->h_waveout, &mmtime, sizeof(MMTIME) )
825 != MMSYSERR_NOERROR )
827 msg_Err( p_aout, "waveOutGetPosition failed");
831 mtime_t i_pos = (mtime_t) mmtime.u.sample * CLOCK_FREQ / p_aout->sys->i_rate;
832 *delay = p_aout->sys->i_played_length - i_pos;
836 static void WaveOutFlush( audio_output_t *p_aout, bool wait)
841 res = waveOutReset( p_aout->sys->h_waveout );
842 p_aout->sys->i_played_length = 0;
843 if( res != MMSYSERR_NOERROR )
844 msg_Err( p_aout, "waveOutReset failed");
848 vlc_mutex_lock( &p_aout->sys->lock );
849 while( p_aout->sys->i_frames )
851 vlc_cond_wait( &p_aout->sys->cond, &p_aout->sys-> lock );
853 vlc_mutex_unlock( &p_aout->sys->lock );
857 static void WaveOutPause( audio_output_t * p_aout, bool pause, mtime_t date)
863 vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 1, 200000 );
864 res = waveOutPause( p_aout->sys->h_waveout );
865 if( res != MMSYSERR_NOERROR )
867 msg_Err( p_aout, "waveOutPause failed (0x%x)", res);
873 vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 0, 0 );
874 res = waveOutRestart( p_aout->sys->h_waveout );
875 if( res != MMSYSERR_NOERROR )
877 msg_Err( p_aout, "waveOutRestart failed (0x%x)", res);
883 static int WaveoutVolumeSet( audio_output_t *p_aout, float volume )
885 aout_sys_t *sys = p_aout->sys;
889 float gain = volume * volume * volume;
890 if ( !sys->b_mute && aout_GainRequest( p_aout, gain ) )
895 const HWAVEOUT hwo = sys->h_waveout;
897 uint32_t vol = lroundf( volume * 0x7fff.fp0 );
907 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
908 if( r != MMSYSERR_NOERROR )
910 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
916 vlc_mutex_lock(&p_aout->sys->lock);
917 sys->f_volume = volume;
919 if( var_InheritBool( p_aout, "volume-save" ) )
920 config_PutFloat( p_aout, "waveout-volume", volume );
922 aout_VolumeReport( p_aout, volume );
923 vlc_mutex_unlock(&p_aout->sys->lock);
928 static int WaveoutMuteSet( audio_output_t * p_aout, bool mute )
930 aout_sys_t *sys = p_aout->sys;
934 float gain = sys->f_volume * sys->f_volume * sys->f_volume;
935 if ( aout_GainRequest( p_aout, mute ? 0.f : gain ) )
941 const HWAVEOUT hwo = sys->h_waveout;
942 uint32_t vol = mute ? 0 : lroundf( sys->f_volume * 0x7fff.fp0 );
947 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
948 if( r != MMSYSERR_NOERROR )
950 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
955 vlc_mutex_lock(&p_aout->sys->lock);
957 aout_MuteReport( p_aout, mute );
958 vlc_mutex_unlock(&p_aout->sys->lock);
963 static void WaveoutPollVolume( void * aout )
965 audio_output_t * p_aout = (audio_output_t *) aout;
968 MMRESULT r = waveOutGetVolume( p_aout->sys->h_waveout, (LPDWORD) &vol );
970 if( r != MMSYSERR_NOERROR )
972 msg_Err( p_aout, "waveOutGetVolume failed (%u)", r );
976 float volume = (float) ( vol & UINT32_C( 0xffff ) );
977 volume /= 0x7fff.fp0;
979 vlc_mutex_lock(&p_aout->sys->lock);
980 if( !p_aout->sys->b_mute && volume != p_aout->sys->f_volume )
982 p_aout->sys->f_volume = volume;
984 if( var_InheritBool( p_aout, "volume-save" ) )
985 config_PutFloat( p_aout, "waveout-volume", volume );
987 aout_VolumeReport( p_aout, volume );
989 vlc_mutex_unlock(&p_aout->sys->lock);