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 *****************************************************************************/
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
39 #include <vlc_charset.h> /* FromWide() */
41 #include "audio_output/windows_audio_common.h"
43 #define FRAME_SIZE 4096 /* The size is in samples, not in bytes */
45 /*****************************************************************************
47 *****************************************************************************/
48 static int Open ( vlc_object_t * );
49 static void Close ( vlc_object_t * );
50 static void Play ( audio_output_t *, block_t * );
52 /*****************************************************************************
53 * notification_thread_t: waveOut event thread
54 *****************************************************************************/
58 struct lkwavehdr * p_next;
62 static int OpenWaveOut ( audio_output_t *, uint32_t,
63 int, int, int, int, bool );
64 static int OpenWaveOutPCM( audio_output_t *, uint32_t,
65 vlc_fourcc_t*, int, int, int, bool );
66 static int PlayWaveOut ( audio_output_t *, HWAVEOUT, struct lkwavehdr *,
69 static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR );
71 static void WaveOutClean( aout_sys_t * p_sys );
73 static void WaveOutClearBuffer( HWAVEOUT, WAVEHDR *);
75 static int ReloadWaveoutDevices( vlc_object_t *, const char *,
77 static uint32_t findDeviceID(char *);
78 static int WaveOutTimeGet(audio_output_t * , mtime_t *);
79 static void WaveOutFlush( audio_output_t *, bool);
80 static void WaveOutPause( audio_output_t *, bool, mtime_t);
81 static int WaveoutVolumeSet(audio_output_t * p_aout, float volume);
82 static int WaveoutMuteSet(audio_output_t * p_aout, bool mute);
84 static void WaveoutPollVolume( void * );
86 static const wchar_t device_name_fmt[] = L"%ls ($%x,$%x)";
88 /*****************************************************************************
89 * aout_sys_t: waveOut audio output method descriptor
90 *****************************************************************************
91 * This structure is part of the audio output thread descriptor.
92 * It describes the waveOut specific properties of an audio device.
93 *****************************************************************************/
97 HWAVEOUT h_waveout; /* handle to waveout instance */
99 WAVEFORMATEXTENSIBLE waveformat; /* audio format */
103 int i_repeat_counter;
109 uint8_t *p_silence_buffer; /* buffer we use to play silence */
115 bool b_soft; /* Use software gain */
116 uint8_t chans_to_reorder; /* do we need channel reordering */
118 uint8_t chan_table[AOUT_CHAN_MAX];
121 mtime_t i_played_length;
123 struct lkwavehdr * p_free_list;
127 vlc_timer_t volume_poll_timer;
130 /*****************************************************************************
132 *****************************************************************************/
133 #define DEVICE_TEXT N_("Select Audio Device")
134 #define DEVICE_LONG N_("Select special Audio device, or let windows "\
135 "decide (default), change needs VLC restart "\
138 #define AUDIO_CHAN_TEXT N_("Audio output channels")
139 #define AUDIO_CHAN_LONGTEXT N_("Channels available for audio output. " \
140 "If the input has more channels than the output, it will be down-mixed. " \
141 "This parameter is ignored when digital pass-through is active.")
143 #define VOLUME_TEXT N_("Audio volume")
146 set_shortname( "WaveOut" )
147 set_description( N_("WaveOut audio output") )
148 set_capability( "audio output", 50 )
149 set_category( CAT_AUDIO )
150 set_subcategory( SUBCAT_AUDIO_AOUT )
151 add_string( "waveout-audio-device", "wavemapper",
152 DEVICE_TEXT, DEVICE_LONG, false )
153 change_string_cb( ReloadWaveoutDevices )
154 add_float( "waveout-volume", 1.0f, VOLUME_TEXT, NULL, true )
155 change_float_range(0.0f, 2.0f)
156 add_bool( "waveout-float32", true, FLOAT_TEXT, FLOAT_LONGTEXT, true )
157 add_integer ("waveout-audio-channels", 9, AUDIO_CHAN_TEXT,
158 AUDIO_CHAN_LONGTEXT, false)
159 change_integer_range(1,9)
160 set_callbacks( Open, Close )
163 /*****************************************************************************
164 * Opens the audio device
165 *****************************************************************************
166 * This function opens and setups Win32 waveOut
167 *****************************************************************************/
168 static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
170 p_aout->time_get = WaveOutTimeGet;
172 p_aout->pause = WaveOutPause;
173 p_aout->flush = WaveOutFlush;
175 /* Default behaviour is to use software gain */
176 p_aout->sys->b_soft = true;
178 char *dev = var_GetNonEmptyString( p_aout, "waveout-audio-device");
179 uint32_t devid = findDeviceID( dev );
181 if(devid == WAVE_MAPPER && dev != NULL && stricmp(dev,"wavemapper"))
182 msg_Warn( p_aout, "configured audio device '%s' not available, "
183 "using default instead", dev );
186 WAVEOUTCAPS waveoutcaps;
187 if(waveOutGetDevCaps( devid, &waveoutcaps,
188 sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
190 /* log debug some infos about driver, to know who to blame
191 if it doesn't work */
192 msg_Dbg( p_aout, "Drivername: %ls", waveoutcaps.szPname);
193 msg_Dbg( p_aout, "Driver Version: %d.%d",
194 (waveoutcaps.vDriverVersion>>8)&255,
195 waveoutcaps.vDriverVersion & 255);
196 msg_Dbg( p_aout, "Manufacturer identifier: 0x%x", waveoutcaps.wMid );
197 msg_Dbg( p_aout, "Product identifier: 0x%x", waveoutcaps.wPid );
202 /* Open the device */
203 if( AOUT_FMT_SPDIF(fmt) && var_InheritBool (p_aout, "spdif") )
206 if( OpenWaveOut( p_aout, devid, VLC_CODEC_SPDIFL,
207 fmt->i_physical_channels,
208 aout_FormatNbChannels( fmt ), fmt->i_rate, false )
211 fmt->i_format = VLC_CODEC_SPDIFL;
213 /* Calculate the frame size in bytes */
214 fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
215 fmt->i_frame_length = A52_FRAME_NB;
216 p_aout->sys->i_buffer_size = fmt->i_bytes_per_frame;
217 p_aout->sys->b_spdif = true;
222 "cannot open waveout audio device for spdif fallback to PCM" );
225 if( fmt->i_format != VLC_CODEC_SPDIFL )
228 check for configured audio device!
230 fmt->i_format = var_InheritBool( p_aout, "waveout-float32" )?
231 VLC_CODEC_FL32: VLC_CODEC_S16N;
233 int max_chan = var_InheritInteger( p_aout, "waveout-audio-channels");
234 int i_channels = aout_FormatNbChannels(fmt);
235 i_channels = ( i_channels < max_chan )? i_channels: max_chan;
241 fmt->i_physical_channels = AOUT_CHANS_8_1;
244 fmt->i_physical_channels = AOUT_CHANS_7_1;
247 fmt->i_physical_channels = AOUT_CHANS_7_0;
250 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
251 | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT
252 | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
255 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
256 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
260 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
261 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
264 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
268 fmt->i_physical_channels = AOUT_CHANS_STEREO;
272 fmt->i_physical_channels = AOUT_CHAN_CENTER;
274 msg_Dbg( p_aout, "Trying %d channels", i_channels );
276 while( ( OpenWaveOutPCM( p_aout, devid, &fmt->i_format,
277 fmt->i_physical_channels, i_channels,
278 fmt->i_rate, false ) != VLC_SUCCESS ) &&
283 msg_Err(p_aout, "Waveout couldn't find appropriate channel mapping");
287 /* Calculate the frame size in bytes */
288 aout_FormatPrepare( fmt );
289 p_aout->sys->i_buffer_size = FRAME_SIZE * fmt->i_bytes_per_frame;
291 if( waveoutcaps.dwSupport & WAVECAPS_VOLUME )
293 aout_GainRequest( p_aout, 1.0f );
294 p_aout->sys->b_soft = false;
297 WaveoutMuteSet( p_aout, p_aout->sys->b_mute );
299 p_aout->sys->b_spdif = false;
302 p_aout->sys->i_rate = fmt->i_rate;
304 waveOutReset( p_aout->sys->h_waveout );
306 /* Allocate silence buffer */
307 p_aout->sys->p_silence_buffer =
308 malloc( p_aout->sys->i_buffer_size );
309 if( p_aout->sys->p_silence_buffer == NULL )
311 msg_Err( p_aout, "Couldn't alloc silence buffer... aborting");
314 p_aout->sys->i_repeat_counter = 0;
317 /* Zero the buffer. WinCE doesn't have calloc(). */
318 memset( p_aout->sys->p_silence_buffer, 0,
319 p_aout->sys->i_buffer_size );
321 /* Now we need to setup our waveOut play notification structure */
322 p_aout->sys->i_frames = 0;
323 p_aout->sys->i_played_length = 0;
324 p_aout->sys->p_free_list = NULL;
329 /*****************************************************************************
330 * Play: play a sound buffer
331 *****************************************************************************
332 * This doesn't actually play the buffer. This just stores the buffer so it
333 * can be played by the callback thread.
334 *****************************************************************************/
335 static void Play( audio_output_t *p_aout, block_t *block )
337 struct lkwavehdr * p_waveheader =
338 (struct lkwavehdr *) malloc(sizeof(struct lkwavehdr));
341 msg_Err(p_aout, "Couldn't alloc WAVEHDR");
343 block_Release( block );
347 p_waveheader->p_next = NULL;
349 if( block && p_aout->sys->chans_to_reorder )
351 aout_ChannelReorder( block->p_buffer, block->i_buffer,
352 p_aout->sys->waveformat.Format.nChannels,
353 p_aout->sys->chan_table, p_aout->sys->format );
355 while( PlayWaveOut( p_aout, p_aout->sys->h_waveout, p_waveheader, block,
356 p_aout->sys->b_spdif ) != VLC_SUCCESS )
359 msg_Warn( p_aout, "Couln't write frame... sleeping");
360 msleep( block->i_length );
363 WaveOutClean( p_aout->sys );
364 WaveoutPollVolume( p_aout );
366 vlc_mutex_lock( &p_aout->sys->lock );
367 p_aout->sys->i_frames++;
368 p_aout->sys->i_played_length += block->i_length;
369 vlc_mutex_unlock( &p_aout->sys->lock );
372 /*****************************************************************************
373 * Close: close the audio device
374 *****************************************************************************/
375 static void Stop( audio_output_t *p_aout )
377 aout_sys_t *p_sys = p_aout->sys;
379 /* Before calling waveOutClose we must reset the device */
380 MMRESULT result = waveOutReset( p_sys->h_waveout );
381 if(result != MMSYSERR_NOERROR)
383 msg_Err( p_aout, "waveOutReset failed 0x%x", result );
385 now we must wait, that all buffers are played
386 because cancel doesn't work in this case...
388 if(result == MMSYSERR_NOTSUPPORTED)
391 clear currently played (done) buffers,
392 if returnvalue > 0 (means some buffer still playing)
393 wait for the driver event callback that one buffer
394 is finished with playing, and check again
395 the timeout of 5000ms is just, an emergency exit
396 of this loop, to avoid deadlock in case of other
397 (currently not known bugs, problems, errors cases?)
399 WaveOutFlush( p_aout, true );
403 /* wait for the frames to be queued in cleaning list */
404 WaveOutFlush( p_aout, true );
405 WaveOutClean( p_aout->sys );
407 /* now we can Close the device */
408 if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
410 msg_Err( p_aout, "waveOutClose failed" );
413 free( p_sys->p_silence_buffer );
414 p_aout->sys->i_played_length = 0;
415 p_sys->b_soft = true;
418 /*****************************************************************************
419 * OpenWaveOut: open the waveout sound device
420 ****************************************************************************/
421 static int OpenWaveOut( audio_output_t *p_aout, uint32_t i_device_id, int i_format,
422 int i_channels, int i_nb_channels, int i_rate,
427 /* Set sound format */
429 #define waveformat p_aout->sys->waveformat
431 waveformat.dwChannelMask = 0;
432 for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ )
433 if( i_channels & pi_vlc_chan_order_wg4[i] )
434 waveformat.dwChannelMask |= pi_channels_in[i];
438 case VLC_CODEC_SPDIFL:
440 /* To prevent channel re-ordering */
441 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
442 waveformat.Format.wBitsPerSample = 16;
443 waveformat.Samples.wValidBitsPerSample =
444 waveformat.Format.wBitsPerSample;
445 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
446 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
450 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
451 waveformat.Samples.wValidBitsPerSample =
452 waveformat.Format.wBitsPerSample;
453 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
454 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
458 waveformat.Format.wBitsPerSample = 16;
459 waveformat.Samples.wValidBitsPerSample =
460 waveformat.Format.wBitsPerSample;
461 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
462 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_PCM;
466 waveformat.Format.nChannels = i_nb_channels;
467 waveformat.Format.nSamplesPerSec = i_rate;
468 waveformat.Format.nBlockAlign =
469 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
470 waveformat.Format.nAvgBytesPerSec =
471 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
473 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
474 if( i_nb_channels <= 2 )
476 waveformat.Format.cbSize = 0;
480 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
481 waveformat.Format.cbSize =
482 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
486 msg_Dbg( p_aout, "OpenWaveDevice-ID: %u", i_device_id);
487 msg_Dbg( p_aout,"waveformat.Format.cbSize = %d",
488 waveformat.Format.cbSize);
489 msg_Dbg( p_aout,"waveformat.Format.wFormatTag = %u",
490 waveformat.Format.wFormatTag);
491 msg_Dbg( p_aout,"waveformat.Format.nChannels = %u",
492 waveformat.Format.nChannels);
493 msg_Dbg( p_aout,"waveformat.Format.nSamplesPerSec = %d",
494 (int)waveformat.Format.nSamplesPerSec);
495 msg_Dbg( p_aout,"waveformat.Format.nAvgBytesPerSec = %u",
496 (int)waveformat.Format.nAvgBytesPerSec);
497 msg_Dbg( p_aout,"waveformat.Format.nBlockAlign = %d",
498 waveformat.Format.nBlockAlign);
499 msg_Dbg( p_aout,"waveformat.Format.wBitsPerSample = %d",
500 waveformat.Format.wBitsPerSample);
501 msg_Dbg( p_aout,"waveformat.Samples.wValidBitsPerSample = %d",
502 waveformat.Samples.wValidBitsPerSample);
503 msg_Dbg( p_aout,"waveformat.Samples.wSamplesPerBlock = %d",
504 waveformat.Samples.wSamplesPerBlock);
505 msg_Dbg( p_aout,"waveformat.dwChannelMask = %u",
506 waveformat.dwChannelMask);
509 /* Open the device */
510 result = waveOutOpen( &p_aout->sys->h_waveout, i_device_id,
511 (WAVEFORMATEX *)&waveformat,
512 (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
513 CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) );
514 if( result == WAVERR_BADFORMAT )
516 msg_Warn( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" );
519 if( result == MMSYSERR_ALLOCATED )
521 msg_Warn( p_aout, "waveOutOpen failed WAVERR_ALLOCATED" );
524 if( result != MMSYSERR_NOERROR )
526 msg_Warn( p_aout, "waveOutOpen failed" );
530 p_aout->sys->chans_to_reorder =
531 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
532 waveformat.dwChannelMask,
533 p_aout->sys->chan_table );
534 if( p_aout->sys->chans_to_reorder )
535 msg_Dbg( p_aout, "channel reordering needed" );
536 p_aout->sys->format = i_format;
544 /*****************************************************************************
545 * OpenWaveOutPCM: open a PCM waveout sound device
546 ****************************************************************************/
547 static int OpenWaveOutPCM( audio_output_t *p_aout, uint32_t i_device_id,
548 vlc_fourcc_t *i_format,
549 int i_channels, int i_nb_channels, int i_rate,
552 bool b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32");
554 if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_CODEC_FL32,
555 i_channels, i_nb_channels, i_rate, b_probe )
558 if ( OpenWaveOut( p_aout, i_device_id, VLC_CODEC_S16N,
559 i_channels, i_nb_channels, i_rate, b_probe )
566 *i_format = VLC_CODEC_S16N;
572 *i_format = VLC_CODEC_FL32;
577 /*****************************************************************************
578 * PlayWaveOut: play a buffer through the WaveOut device
579 *****************************************************************************/
580 static int PlayWaveOut( audio_output_t *p_aout, HWAVEOUT h_waveout,
581 struct lkwavehdr *p_waveheader, block_t *p_buffer, bool b_spdif)
585 /* Prepare the buffer */
586 if( p_buffer != NULL )
588 p_waveheader->hdr.lpData = (LPSTR)p_buffer->p_buffer;
589 p_waveheader->hdr.dwBufferLength = p_buffer->i_buffer;
591 copy the buffer to the silence buffer :) so in case we don't
592 get the next buffer fast enough (I will repeat this one a time
593 for AC3 / DTS and SPDIF this will sound better instead of
598 memcpy( p_aout->sys->p_silence_buffer,
600 p_aout->sys->i_buffer_size );
601 p_aout->sys->i_repeat_counter = 2;
604 /* Use silence buffer instead */
605 if(p_aout->sys->i_repeat_counter)
607 p_aout->sys->i_repeat_counter--;
608 if(!p_aout->sys->i_repeat_counter)
610 memset( p_aout->sys->p_silence_buffer,
611 0x00, p_aout->sys->i_buffer_size );
614 p_waveheader->hdr.lpData = (LPSTR)p_aout->sys->p_silence_buffer;
615 p_waveheader->hdr.dwBufferLength = p_aout->sys->i_buffer_size;
618 p_waveheader->hdr.dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
619 p_waveheader->hdr.dwFlags = 0;
621 result = waveOutPrepareHeader( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
622 if( result != MMSYSERR_NOERROR )
624 msg_Err( p_aout, "waveOutPrepareHeader failed" );
628 /* Send the buffer to the waveOut queue */
629 result = waveOutWrite( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
630 if( result != MMSYSERR_NOERROR )
632 msg_Err( p_aout, "waveOutWrite failed" );
639 /*****************************************************************************
640 * WaveOutCallback: what to do once WaveOut has played its sound samples
641 *****************************************************************************/
642 static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
644 DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
648 audio_output_t *p_aout = (audio_output_t *)_p_aout;
649 struct lkwavehdr * p_waveheader = (struct lkwavehdr *) dwParam1;
651 if( uMsg != WOM_DONE ) return;
653 vlc_mutex_lock( &p_aout->sys->lock );
654 p_waveheader->p_next = p_aout->sys->p_free_list;
655 p_aout->sys->p_free_list = p_waveheader;
656 p_aout->sys->i_frames--;
657 vlc_cond_broadcast( &p_aout->sys->cond );
658 vlc_mutex_unlock( &p_aout->sys->lock );
661 static void WaveOutClean( aout_sys_t * p_sys )
663 struct lkwavehdr *p_whdr, *p_list;
665 vlc_mutex_lock(&p_sys->lock);
666 p_list = p_sys->p_free_list;
667 p_sys->p_free_list = NULL;
668 vlc_mutex_unlock(&p_sys->lock);
673 p_list = p_list->p_next;
674 WaveOutClearBuffer( p_sys->h_waveout, &p_whdr->hdr );
679 static void WaveOutClearBuffer( HWAVEOUT h_waveout, WAVEHDR *p_waveheader )
681 block_t *p_buffer = (block_t *)(p_waveheader->dwUser);
682 /* Unprepare and free the buffers which has just been played */
683 waveOutUnprepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
685 if( p_waveheader->dwUser != 1 )
686 block_Release( p_buffer );
690 reload the configuration drop down list, of the Audio Devices
692 static int ReloadWaveoutDevices( vlc_object_t *p_this, char const *psz_name,
693 char ***values, char ***descs )
695 int n = 0, nb_devices = waveOutGetNumDevs();
697 VLC_UNUSED( p_this); VLC_UNUSED( psz_name );
699 *values = xmalloc( (nb_devices + 1) * sizeof(char *) );
700 *descs = xmalloc( (nb_devices + 1) * sizeof(char *) );
702 (*values)[n] = strdup( "wavemapper" );
703 (*descs)[n] = strdup( _("Microsoft Soundmapper") );
706 for(int i = 0; i < nb_devices; i++)
709 wchar_t dev_name[MAXPNAMELEN+32];
711 if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
715 _snwprintf(dev_name, MAXPNAMELEN + 32, device_name_fmt,
716 caps.szPname, caps.wMid, caps.wPid);
717 (*values)[n] = FromWide( dev_name );
718 (*descs)[n] = strdup( (*values)[n] );
726 convert devicename to device ID for output
727 if device not found return WAVE_MAPPER, so let
728 windows decide which preferred audio device
731 static uint32_t findDeviceID(char *psz_device_name)
733 if( !psz_device_name )
736 uint32_t wave_devices = waveOutGetNumDevs();
738 for( uint32_t i = 0; i < wave_devices; i++ )
741 wchar_t dev_name[MAXPNAMELEN+32];
743 if( waveOutGetDevCaps( i, &caps, sizeof(WAVEOUTCAPS) )
744 != MMSYSERR_NOERROR )
747 _snwprintf( dev_name, MAXPNAMELEN + 32, device_name_fmt,
748 caps.szPname, caps.wMid, caps.wPid );
749 char *u8 = FromWide(dev_name);
750 if( !stricmp(u8, psz_device_name) )
761 static int DeviceSelect (audio_output_t *aout, const char *id)
763 var_SetString(aout, "waveout-audio-device", (id != NULL) ? id : "");
764 aout_DeviceReport (aout, id);
765 aout_RestartRequest (aout, AOUT_RESTART_OUTPUT);
769 static int Open(vlc_object_t *obj)
771 audio_output_t *aout = (audio_output_t *)obj;
772 aout_sys_t *sys = malloc(sizeof (*sys));
774 if (unlikely(sys == NULL))
779 aout->volume_set = WaveoutVolumeSet;
780 aout->mute_set = WaveoutMuteSet;
781 aout->device_select = DeviceSelect;
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 );
800 /* WaveOut does not support hot-plug events so list devices at startup */
802 int count = ReloadWaveoutDevices(VLC_OBJECT(aout), NULL, &ids, &names);
805 for (int i = 0; i < count; i++)
807 aout_HotplugReport(aout, ids[i], names[i]);
815 char *dev = var_CreateGetNonEmptyString(aout, "waveout-audio-device");
816 aout_DeviceReport(aout, dev);
822 static void Close(vlc_object_t *obj)
824 audio_output_t *aout = (audio_output_t *)obj;
825 aout_sys_t *sys = aout->sys;
827 var_Destroy(aout, "waveout-audio-device");
829 vlc_timer_destroy( sys->volume_poll_timer );
830 vlc_cond_destroy( &sys->cond );
831 vlc_mutex_destroy( &sys->lock );
836 static int WaveOutTimeGet(audio_output_t * p_aout, mtime_t *delay)
839 mmtime.wType = TIME_SAMPLES;
841 if( !p_aout->sys->i_frames )
844 if( waveOutGetPosition( p_aout->sys->h_waveout, &mmtime, sizeof(MMTIME) )
845 != MMSYSERR_NOERROR )
847 msg_Err( p_aout, "waveOutGetPosition failed");
851 mtime_t i_pos = (mtime_t) mmtime.u.sample * CLOCK_FREQ / p_aout->sys->i_rate;
852 *delay = p_aout->sys->i_played_length - i_pos;
856 static void WaveOutFlush( audio_output_t *p_aout, bool wait)
861 res = waveOutReset( p_aout->sys->h_waveout );
862 p_aout->sys->i_played_length = 0;
863 if( res != MMSYSERR_NOERROR )
864 msg_Err( p_aout, "waveOutReset failed");
868 vlc_mutex_lock( &p_aout->sys->lock );
869 while( p_aout->sys->i_frames )
871 vlc_cond_wait( &p_aout->sys->cond, &p_aout->sys-> lock );
873 vlc_mutex_unlock( &p_aout->sys->lock );
877 static void WaveOutPause( audio_output_t * p_aout, bool pause, mtime_t date)
883 vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 1, 200000 );
884 res = waveOutPause( p_aout->sys->h_waveout );
885 if( res != MMSYSERR_NOERROR )
887 msg_Err( p_aout, "waveOutPause failed (0x%x)", res);
893 vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 0, 0 );
894 res = waveOutRestart( p_aout->sys->h_waveout );
895 if( res != MMSYSERR_NOERROR )
897 msg_Err( p_aout, "waveOutRestart failed (0x%x)", res);
903 static int WaveoutVolumeSet( audio_output_t *p_aout, float volume )
905 aout_sys_t *sys = p_aout->sys;
909 float gain = volume * volume * volume;
910 if ( !sys->b_mute && aout_GainRequest( p_aout, gain ) )
915 const HWAVEOUT hwo = sys->h_waveout;
917 uint32_t vol = lroundf( volume * 0x7fff.fp0 );
927 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
928 if( r != MMSYSERR_NOERROR )
930 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
936 vlc_mutex_lock(&p_aout->sys->lock);
937 sys->f_volume = volume;
939 if( var_InheritBool( p_aout, "volume-save" ) )
940 config_PutFloat( p_aout, "waveout-volume", volume );
942 aout_VolumeReport( p_aout, volume );
943 vlc_mutex_unlock(&p_aout->sys->lock);
948 static int WaveoutMuteSet( audio_output_t * p_aout, bool mute )
950 aout_sys_t *sys = p_aout->sys;
954 float gain = sys->f_volume * sys->f_volume * sys->f_volume;
955 if ( aout_GainRequest( p_aout, mute ? 0.f : gain ) )
961 const HWAVEOUT hwo = sys->h_waveout;
962 uint32_t vol = mute ? 0 : lroundf( sys->f_volume * 0x7fff.fp0 );
967 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
968 if( r != MMSYSERR_NOERROR )
970 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
975 vlc_mutex_lock(&p_aout->sys->lock);
977 aout_MuteReport( p_aout, mute );
978 vlc_mutex_unlock(&p_aout->sys->lock);
983 static void WaveoutPollVolume( void * aout )
985 audio_output_t * p_aout = (audio_output_t *) aout;
988 MMRESULT r = waveOutGetVolume( p_aout->sys->h_waveout, (LPDWORD) &vol );
990 if( r != MMSYSERR_NOERROR )
992 msg_Err( p_aout, "waveOutGetVolume failed (%u)", r );
996 float volume = (float) ( vol & UINT32_C( 0xffff ) );
997 volume /= 0x7fff.fp0;
999 vlc_mutex_lock(&p_aout->sys->lock);
1000 if( !p_aout->sys->b_mute && volume != p_aout->sys->f_volume )
1002 p_aout->sys->f_volume = volume;
1004 if( var_InheritBool( p_aout, "volume-save" ) )
1005 config_PutFloat( p_aout, "waveout-volume", volume );
1007 aout_VolumeReport( p_aout, volume );
1009 vlc_mutex_unlock(&p_aout->sys->lock);