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 HWAVEOUT h_waveout; /* handle to waveout instance */
102 WAVEFORMATEXTENSIBLE waveformat; /* audio format */
106 int i_repeat_counter;
112 uint8_t *p_silence_buffer; /* buffer we use to play silence */
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];
124 mtime_t i_played_length;
126 struct lkwavehdr * p_free_list;
130 vlc_timer_t volume_poll_timer;
133 /*****************************************************************************
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 "\
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")
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 change_string_cb( ReloadWaveoutDevices )
157 add_float( "waveout-volume", 1.0f, VOLUME_TEXT, NULL, true )
158 change_float_range(0.0f, 2.0f)
159 add_bool( "waveout-float32", true, FLOAT_TEXT, FLOAT_LONGTEXT, true )
160 add_integer ("waveout-audio-channels", 9, AUDIO_CHAN_TEXT,
161 AUDIO_CHAN_LONGTEXT, false)
162 change_integer_range(1,9)
163 set_callbacks( Open, Close )
166 /*****************************************************************************
167 * Opens the audio device
168 *****************************************************************************
169 * This function opens and setups Win32 waveOut
170 *****************************************************************************/
171 static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
173 p_aout->time_get = WaveOutTimeGet;
175 p_aout->pause = WaveOutPause;
176 p_aout->flush = WaveOutFlush;
178 /* Default behaviour is to use software gain */
179 p_aout->sys->b_soft = true;
182 check for configured audio device!
184 fmt->i_format = var_InheritBool( p_aout, "waveout-float32" )?
185 VLC_CODEC_FL32: VLC_CODEC_S16N;
187 char *dev = var_GetNonEmptyString( p_aout, "waveout-audio-device");
188 uint32_t devid = findDeviceID( dev );
190 if(devid == WAVE_MAPPER && dev != NULL && stricmp(dev,"wavemapper"))
191 msg_Warn( p_aout, "configured audio device '%s' not available, "
192 "using default instead", dev );
195 WAVEOUTCAPS waveoutcaps;
196 if(waveOutGetDevCaps( devid, &waveoutcaps,
197 sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
199 /* log debug some infos about driver, to know who to blame
200 if it doesn't work */
201 msg_Dbg( p_aout, "Drivername: %ls", waveoutcaps.szPname);
202 msg_Dbg( p_aout, "Driver Version: %d.%d",
203 (waveoutcaps.vDriverVersion>>8)&255,
204 waveoutcaps.vDriverVersion & 255);
205 msg_Dbg( p_aout, "Manufacturer identifier: 0x%x", waveoutcaps.wMid );
206 msg_Dbg( p_aout, "Product identifier: 0x%x", waveoutcaps.wPid );
211 /* Open the device */
212 if( AOUT_FMT_SPDIF(fmt) && var_InheritBool (p_aout, "spdif") )
215 if( OpenWaveOut( p_aout, devid, VLC_CODEC_SPDIFL,
216 fmt->i_physical_channels,
217 aout_FormatNbChannels( fmt ), fmt->i_rate, false )
220 fmt->i_format = VLC_CODEC_SPDIFL;
222 /* Calculate the frame size in bytes */
223 fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
224 fmt->i_frame_length = A52_FRAME_NB;
225 p_aout->sys->i_buffer_size = fmt->i_bytes_per_frame;
226 p_aout->sys->b_spdif = true;
231 "cannot open waveout audio device for spdif fallback to PCM" );
234 if( fmt->i_format != VLC_CODEC_SPDIFL )
236 int max_chan = var_InheritInteger( p_aout, "waveout-audio-channels");
237 int i_channels = aout_FormatNbChannels(fmt);
238 i_channels = ( i_channels < max_chan )? i_channels: max_chan;
244 fmt->i_physical_channels = AOUT_CHANS_8_1;
247 fmt->i_physical_channels = AOUT_CHANS_7_1;
250 fmt->i_physical_channels = AOUT_CHANS_7_0;
253 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
254 | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT
255 | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
258 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
259 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
263 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
264 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
267 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
271 fmt->i_physical_channels = AOUT_CHANS_STEREO;
275 fmt->i_physical_channels = AOUT_CHAN_CENTER;
277 msg_Dbg( p_aout, "Trying %d channels", i_channels );
279 while( ( OpenWaveOutPCM( p_aout, devid, &fmt->i_format,
280 fmt->i_physical_channels, i_channels,
281 fmt->i_rate, false ) != VLC_SUCCESS ) &&
286 msg_Err(p_aout, "Waveout couldn't find appropriate channel mapping");
290 /* Calculate the frame size in bytes */
291 aout_FormatPrepare( fmt );
292 p_aout->sys->i_buffer_size = FRAME_SIZE * fmt->i_bytes_per_frame;
294 if( waveoutcaps.dwSupport & WAVECAPS_VOLUME )
296 aout_GainRequest( p_aout, 1.0f );
297 p_aout->sys->b_soft = false;
300 WaveoutMuteSet( p_aout, p_aout->sys->b_mute );
302 p_aout->sys->b_spdif = false;
305 p_aout->sys->i_rate = fmt->i_rate;
307 waveOutReset( p_aout->sys->h_waveout );
309 /* Allocate silence buffer */
310 p_aout->sys->p_silence_buffer =
311 malloc( p_aout->sys->i_buffer_size );
312 if( p_aout->sys->p_silence_buffer == NULL )
314 msg_Err( p_aout, "Couldn't alloc silence buffer... aborting");
317 p_aout->sys->i_repeat_counter = 0;
320 /* Zero the buffer. WinCE doesn't have calloc(). */
321 memset( p_aout->sys->p_silence_buffer, 0,
322 p_aout->sys->i_buffer_size );
324 /* Now we need to setup our waveOut play notification structure */
325 p_aout->sys->i_frames = 0;
326 p_aout->sys->i_played_length = 0;
327 p_aout->sys->p_free_list = NULL;
332 /*****************************************************************************
333 * Play: play a sound buffer
334 *****************************************************************************
335 * This doesn't actually play the buffer. This just stores the buffer so it
336 * can be played by the callback thread.
337 *****************************************************************************/
338 static void Play( audio_output_t *p_aout, block_t *block )
340 struct lkwavehdr * p_waveheader =
341 (struct lkwavehdr *) malloc(sizeof(struct lkwavehdr));
344 msg_Err(p_aout, "Couldn't alloc WAVEHDR");
346 block_Release( block );
350 p_waveheader->p_next = NULL;
352 if( block && p_aout->sys->chans_to_reorder )
354 aout_ChannelReorder( block->p_buffer, block->i_buffer,
355 p_aout->sys->waveformat.Format.nChannels,
356 p_aout->sys->chan_table, p_aout->sys->format );
358 while( PlayWaveOut( p_aout, p_aout->sys->h_waveout, p_waveheader, block,
359 p_aout->sys->b_spdif ) != VLC_SUCCESS )
362 msg_Warn( p_aout, "Couln't write frame... sleeping");
363 msleep( block->i_length );
366 WaveOutClean( p_aout->sys );
367 WaveoutPollVolume( p_aout );
369 vlc_mutex_lock( &p_aout->sys->lock );
370 p_aout->sys->i_frames++;
371 p_aout->sys->i_played_length += block->i_length;
372 vlc_mutex_unlock( &p_aout->sys->lock );
375 /*****************************************************************************
376 * Close: close the audio device
377 *****************************************************************************/
378 static void Stop( audio_output_t *p_aout )
380 aout_sys_t *p_sys = p_aout->sys;
382 /* Before calling waveOutClose we must reset the device */
383 MMRESULT result = waveOutReset( p_sys->h_waveout );
384 if(result != MMSYSERR_NOERROR)
386 msg_Err( p_aout, "waveOutReset failed 0x%x", result );
388 now we must wait, that all buffers are played
389 because cancel doesn't work in this case...
391 if(result == MMSYSERR_NOTSUPPORTED)
394 clear currently played (done) buffers,
395 if returnvalue > 0 (means some buffer still playing)
396 wait for the driver event callback that one buffer
397 is finished with playing, and check again
398 the timeout of 5000ms is just, an emergency exit
399 of this loop, to avoid deadlock in case of other
400 (currently not known bugs, problems, errors cases?)
402 WaveOutFlush( p_aout, true );
406 /* wait for the frames to be queued in cleaning list */
407 WaveOutFlush( p_aout, true );
408 WaveOutClean( p_aout->sys );
410 /* now we can Close the device */
411 if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
413 msg_Err( p_aout, "waveOutClose failed" );
416 free( p_sys->p_silence_buffer );
417 p_aout->sys->i_played_length = 0;
418 p_sys->b_soft = true;
421 /*****************************************************************************
422 * OpenWaveOut: open the waveout sound device
423 ****************************************************************************/
424 static int OpenWaveOut( audio_output_t *p_aout, uint32_t i_device_id, int i_format,
425 int i_channels, int i_nb_channels, int i_rate,
430 /* Set sound format */
432 #define waveformat p_aout->sys->waveformat
434 waveformat.dwChannelMask = 0;
435 for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ )
436 if( i_channels & pi_vlc_chan_order_wg4[i] )
437 waveformat.dwChannelMask |= pi_channels_in[i];
441 case VLC_CODEC_SPDIFL:
443 /* To prevent channel re-ordering */
444 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
445 waveformat.Format.wBitsPerSample = 16;
446 waveformat.Samples.wValidBitsPerSample =
447 waveformat.Format.wBitsPerSample;
448 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
449 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
453 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
454 waveformat.Samples.wValidBitsPerSample =
455 waveformat.Format.wBitsPerSample;
456 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
457 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
461 waveformat.Format.wBitsPerSample = 16;
462 waveformat.Samples.wValidBitsPerSample =
463 waveformat.Format.wBitsPerSample;
464 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
465 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_PCM;
469 waveformat.Format.nChannels = i_nb_channels;
470 waveformat.Format.nSamplesPerSec = i_rate;
471 waveformat.Format.nBlockAlign =
472 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
473 waveformat.Format.nAvgBytesPerSec =
474 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
476 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
477 if( i_nb_channels <= 2 )
479 waveformat.Format.cbSize = 0;
483 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
484 waveformat.Format.cbSize =
485 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
489 msg_Dbg( p_aout, "OpenWaveDevice-ID: %u", i_device_id);
490 msg_Dbg( p_aout,"waveformat.Format.cbSize = %d",
491 waveformat.Format.cbSize);
492 msg_Dbg( p_aout,"waveformat.Format.wFormatTag = %u",
493 waveformat.Format.wFormatTag);
494 msg_Dbg( p_aout,"waveformat.Format.nChannels = %u",
495 waveformat.Format.nChannels);
496 msg_Dbg( p_aout,"waveformat.Format.nSamplesPerSec = %d",
497 (int)waveformat.Format.nSamplesPerSec);
498 msg_Dbg( p_aout,"waveformat.Format.nAvgBytesPerSec = %u",
499 (int)waveformat.Format.nAvgBytesPerSec);
500 msg_Dbg( p_aout,"waveformat.Format.nBlockAlign = %d",
501 waveformat.Format.nBlockAlign);
502 msg_Dbg( p_aout,"waveformat.Format.wBitsPerSample = %d",
503 waveformat.Format.wBitsPerSample);
504 msg_Dbg( p_aout,"waveformat.Samples.wValidBitsPerSample = %d",
505 waveformat.Samples.wValidBitsPerSample);
506 msg_Dbg( p_aout,"waveformat.Samples.wSamplesPerBlock = %d",
507 waveformat.Samples.wSamplesPerBlock);
508 msg_Dbg( p_aout,"waveformat.dwChannelMask = %lx",
509 waveformat.dwChannelMask);
512 /* Open the device */
513 result = waveOutOpen( &p_aout->sys->h_waveout, i_device_id,
514 (WAVEFORMATEX *)&waveformat,
515 (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
516 CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) );
517 if( result == WAVERR_BADFORMAT )
519 msg_Warn( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" );
522 if( result == MMSYSERR_ALLOCATED )
524 msg_Warn( p_aout, "waveOutOpen failed WAVERR_ALLOCATED" );
527 if( result != MMSYSERR_NOERROR )
529 msg_Warn( p_aout, "waveOutOpen failed" );
533 p_aout->sys->chans_to_reorder =
534 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
535 waveformat.dwChannelMask,
536 p_aout->sys->chan_table );
537 if( p_aout->sys->chans_to_reorder )
538 msg_Dbg( p_aout, "channel reordering needed" );
539 p_aout->sys->format = i_format;
547 /*****************************************************************************
548 * OpenWaveOutPCM: open a PCM waveout sound device
549 ****************************************************************************/
550 static int OpenWaveOutPCM( audio_output_t *p_aout, uint32_t i_device_id,
551 vlc_fourcc_t *i_format,
552 int i_channels, int i_nb_channels, int i_rate,
555 bool b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32");
557 if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_CODEC_FL32,
558 i_channels, i_nb_channels, i_rate, b_probe )
561 if ( OpenWaveOut( p_aout, i_device_id, VLC_CODEC_S16N,
562 i_channels, i_nb_channels, i_rate, b_probe )
569 *i_format = VLC_CODEC_S16N;
575 *i_format = VLC_CODEC_FL32;
580 /*****************************************************************************
581 * PlayWaveOut: play a buffer through the WaveOut device
582 *****************************************************************************/
583 static int PlayWaveOut( audio_output_t *p_aout, HWAVEOUT h_waveout,
584 struct lkwavehdr *p_waveheader, block_t *p_buffer, bool b_spdif)
588 /* Prepare the buffer */
589 if( p_buffer != NULL )
591 p_waveheader->hdr.lpData = (LPSTR)p_buffer->p_buffer;
592 p_waveheader->hdr.dwBufferLength = p_buffer->i_buffer;
594 copy the buffer to the silence buffer :) so in case we don't
595 get the next buffer fast enough (I will repeat this one a time
596 for AC3 / DTS and SPDIF this will sound better instead of
601 memcpy( p_aout->sys->p_silence_buffer,
603 p_aout->sys->i_buffer_size );
604 p_aout->sys->i_repeat_counter = 2;
607 /* Use silence buffer instead */
608 if(p_aout->sys->i_repeat_counter)
610 p_aout->sys->i_repeat_counter--;
611 if(!p_aout->sys->i_repeat_counter)
613 memset( p_aout->sys->p_silence_buffer,
614 0x00, p_aout->sys->i_buffer_size );
617 p_waveheader->hdr.lpData = (LPSTR)p_aout->sys->p_silence_buffer;
618 p_waveheader->hdr.dwBufferLength = p_aout->sys->i_buffer_size;
621 p_waveheader->hdr.dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
622 p_waveheader->hdr.dwFlags = 0;
624 result = waveOutPrepareHeader( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
625 if( result != MMSYSERR_NOERROR )
627 msg_Err( p_aout, "waveOutPrepareHeader failed" );
631 /* Send the buffer to the waveOut queue */
632 result = waveOutWrite( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
633 if( result != MMSYSERR_NOERROR )
635 msg_Err( p_aout, "waveOutWrite failed" );
642 /*****************************************************************************
643 * WaveOutCallback: what to do once WaveOut has played its sound samples
644 *****************************************************************************/
645 static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
647 DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
651 audio_output_t *p_aout = (audio_output_t *)_p_aout;
652 struct lkwavehdr * p_waveheader = (struct lkwavehdr *) dwParam1;
654 if( uMsg != WOM_DONE ) return;
656 vlc_mutex_lock( &p_aout->sys->lock );
657 p_waveheader->p_next = p_aout->sys->p_free_list;
658 p_aout->sys->p_free_list = p_waveheader;
659 p_aout->sys->i_frames--;
660 vlc_cond_broadcast( &p_aout->sys->cond );
661 vlc_mutex_unlock( &p_aout->sys->lock );
664 static void WaveOutClean( aout_sys_t * p_sys )
666 struct lkwavehdr *p_whdr, *p_list;
668 vlc_mutex_lock(&p_sys->lock);
669 p_list = p_sys->p_free_list;
670 p_sys->p_free_list = NULL;
671 vlc_mutex_unlock(&p_sys->lock);
676 p_list = p_list->p_next;
677 WaveOutClearBuffer( p_sys->h_waveout, &p_whdr->hdr );
682 static void WaveOutClearBuffer( HWAVEOUT h_waveout, WAVEHDR *p_waveheader )
684 block_t *p_buffer = (block_t *)(p_waveheader->dwUser);
685 /* Unprepare and free the buffers which has just been played */
686 waveOutUnprepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
688 if( p_waveheader->dwUser != 1 )
689 block_Release( p_buffer );
693 reload the configuration drop down list, of the Audio Devices
695 static int ReloadWaveoutDevices( vlc_object_t *p_this, char const *psz_name,
696 char ***values, char ***descs )
698 int n = 0, nb_devices = waveOutGetNumDevs();
700 VLC_UNUSED( p_this); VLC_UNUSED( psz_name );
702 *values = xmalloc( (nb_devices + 1) * sizeof(char *) );
703 *descs = xmalloc( (nb_devices + 1) * sizeof(char *) );
705 (*values)[n] = strdup( "wavemapper" );
706 (*descs)[n] = strdup( _("Microsoft Soundmapper") );
709 for(int i = 0; i < nb_devices; i++)
712 wchar_t dev_name[MAXPNAMELEN+32];
714 if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
718 _snwprintf(dev_name, MAXPNAMELEN + 32, device_name_fmt,
719 caps.szPname, caps.wMid, caps.wPid);
720 (*values)[n] = FromWide( dev_name );
721 (*descs)[n] = strdup( (*values)[n] );
729 convert devicename to device ID for output
730 if device not found return WAVE_MAPPER, so let
731 windows decide which preferred audio device
734 static uint32_t findDeviceID(char *psz_device_name)
736 if( !psz_device_name )
739 uint32_t wave_devices = waveOutGetNumDevs();
741 for( uint32_t i = 0; i < wave_devices; i++ )
744 wchar_t dev_name[MAXPNAMELEN+32];
746 if( waveOutGetDevCaps( i, &caps, sizeof(WAVEOUTCAPS) )
747 != MMSYSERR_NOERROR )
750 _snwprintf( dev_name, MAXPNAMELEN + 32, device_name_fmt,
751 caps.szPname, caps.wMid, caps.wPid );
752 char *u8 = FromWide(dev_name);
753 if( !stricmp(u8, psz_device_name) )
764 static int DeviceSelect (audio_output_t *aout, const char *id)
766 var_SetString(aout, "waveout-audio-device", (id != NULL) ? id : "");
767 aout_DeviceReport (aout, id);
768 aout_RestartRequest (aout, AOUT_RESTART_OUTPUT);
772 static int Open(vlc_object_t *obj)
774 audio_output_t *aout = (audio_output_t *)obj;
775 aout_sys_t *sys = malloc(sizeof (*sys));
777 if (unlikely(sys == NULL))
782 aout->volume_set = WaveoutVolumeSet;
783 aout->mute_set = WaveoutMuteSet;
784 aout->device_select = DeviceSelect;
786 sys->f_volume = var_InheritFloat(aout, "waveout-volume");
787 sys->b_mute = var_InheritBool(aout, "mute");
789 aout_MuteReport(aout, sys->b_mute);
790 aout_VolumeReport(aout, sys->f_volume );
792 if( vlc_timer_create( &sys->volume_poll_timer,
793 WaveoutPollVolume, aout ) )
795 msg_Err( aout, "Couldn't create volume polling timer" );
800 vlc_mutex_init( &sys->lock );
801 vlc_cond_init( &sys->cond );
803 /* WaveOut does not support hot-plug events so list devices at startup */
805 int count = ReloadWaveoutDevices(VLC_OBJECT(aout), NULL, &ids, &names);
808 for (int i = 0; i < count; i++)
810 aout_HotplugReport(aout, ids[i], names[i]);
818 char *dev = var_CreateGetNonEmptyString(aout, "waveout-audio-device");
819 aout_DeviceReport(aout, dev);
825 static void Close(vlc_object_t *obj)
827 audio_output_t *aout = (audio_output_t *)obj;
828 aout_sys_t *sys = aout->sys;
830 var_Destroy(aout, "waveout-audio-device");
832 vlc_timer_destroy( sys->volume_poll_timer );
833 vlc_cond_destroy( &sys->cond );
834 vlc_mutex_destroy( &sys->lock );
839 static int WaveOutTimeGet(audio_output_t * p_aout, mtime_t *delay)
842 mmtime.wType = TIME_SAMPLES;
844 if( !p_aout->sys->i_frames )
847 if( waveOutGetPosition( p_aout->sys->h_waveout, &mmtime, sizeof(MMTIME) )
848 != MMSYSERR_NOERROR )
850 msg_Err( p_aout, "waveOutGetPosition failed");
854 mtime_t i_pos = (mtime_t) mmtime.u.sample * CLOCK_FREQ / p_aout->sys->i_rate;
855 *delay = p_aout->sys->i_played_length - i_pos;
859 static void WaveOutFlush( audio_output_t *p_aout, bool wait)
864 res = waveOutReset( p_aout->sys->h_waveout );
865 p_aout->sys->i_played_length = 0;
866 if( res != MMSYSERR_NOERROR )
867 msg_Err( p_aout, "waveOutReset failed");
871 vlc_mutex_lock( &p_aout->sys->lock );
872 while( p_aout->sys->i_frames )
874 vlc_cond_wait( &p_aout->sys->cond, &p_aout->sys-> lock );
876 vlc_mutex_unlock( &p_aout->sys->lock );
880 static void WaveOutPause( audio_output_t * p_aout, bool pause, mtime_t date)
886 vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 1, 200000 );
887 res = waveOutPause( p_aout->sys->h_waveout );
888 if( res != MMSYSERR_NOERROR )
890 msg_Err( p_aout, "waveOutPause failed (0x%x)", res);
896 vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 0, 0 );
897 res = waveOutRestart( p_aout->sys->h_waveout );
898 if( res != MMSYSERR_NOERROR )
900 msg_Err( p_aout, "waveOutRestart failed (0x%x)", res);
906 static int WaveoutVolumeSet( audio_output_t *p_aout, float volume )
908 aout_sys_t *sys = p_aout->sys;
912 float gain = volume * volume * volume;
913 if ( !sys->b_mute && aout_GainRequest( p_aout, gain ) )
918 const HWAVEOUT hwo = sys->h_waveout;
920 uint32_t vol = lroundf( volume * 0x7fff.fp0 );
930 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
931 if( r != MMSYSERR_NOERROR )
933 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
939 vlc_mutex_lock(&p_aout->sys->lock);
940 sys->f_volume = volume;
942 if( var_InheritBool( p_aout, "volume-save" ) )
943 config_PutFloat( p_aout, "waveout-volume", volume );
945 aout_VolumeReport( p_aout, volume );
946 vlc_mutex_unlock(&p_aout->sys->lock);
951 static int WaveoutMuteSet( audio_output_t * p_aout, bool mute )
953 aout_sys_t *sys = p_aout->sys;
957 float gain = sys->f_volume * sys->f_volume * sys->f_volume;
958 if ( aout_GainRequest( p_aout, mute ? 0.f : gain ) )
964 const HWAVEOUT hwo = sys->h_waveout;
965 uint32_t vol = mute ? 0 : lroundf( sys->f_volume * 0x7fff.fp0 );
970 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
971 if( r != MMSYSERR_NOERROR )
973 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
978 vlc_mutex_lock(&p_aout->sys->lock);
980 aout_MuteReport( p_aout, mute );
981 vlc_mutex_unlock(&p_aout->sys->lock);
986 static void WaveoutPollVolume( void * aout )
988 audio_output_t * p_aout = (audio_output_t *) aout;
991 MMRESULT r = waveOutGetVolume( p_aout->sys->h_waveout, (LPDWORD) &vol );
993 if( r != MMSYSERR_NOERROR )
995 msg_Err( p_aout, "waveOutGetVolume failed (%u)", r );
999 float volume = (float) ( vol & UINT32_C( 0xffff ) );
1000 volume /= 0x7fff.fp0;
1002 vlc_mutex_lock(&p_aout->sys->lock);
1003 if( !p_aout->sys->b_mute && volume != p_aout->sys->f_volume )
1005 p_aout->sys->f_volume = volume;
1007 if( var_InheritBool( p_aout, "volume-save" ) )
1008 config_PutFloat( p_aout, "waveout-volume", volume );
1010 aout_VolumeReport( p_aout, volume );
1012 vlc_mutex_unlock(&p_aout->sys->lock);