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 "\
140 #define DEFAULT_AUDIO_DEVICE N_("Default Audio Device")
142 #define AUDIO_CHAN_TEXT N_("Audio output channels")
143 #define AUDIO_CHAN_LONGTEXT N_("Channels available for audio output. " \
144 "If the input has more channels than the output, it will be down-mixed. " \
145 "This parameter is ignored when digital pass-through is active.")
147 #define VOLUME_TEXT N_("Audio volume")
150 set_shortname( "WaveOut" )
151 set_description( N_("Win32 waveOut extension output") )
152 set_capability( "audio output", 50 )
153 set_category( CAT_AUDIO )
154 set_subcategory( SUBCAT_AUDIO_AOUT )
155 add_string( "waveout-audio-device", "wavemapper",
156 DEVICE_TEXT, DEVICE_LONG, false )
157 change_string_cb( ReloadWaveoutDevices )
158 add_float( "waveout-volume", 1.0f, VOLUME_TEXT, NULL, true )
159 change_float_range(0.0f, 2.0f)
160 add_bool( "waveout-float32", true, FLOAT_TEXT, FLOAT_LONGTEXT, true )
161 add_integer ("waveout-audio-channels", 6, AUDIO_CHAN_TEXT,
162 AUDIO_CHAN_LONGTEXT, false)
163 change_integer_range(1,6)
164 set_callbacks( Open, Close )
167 /*****************************************************************************
168 * Opens the audio device
169 *****************************************************************************
170 * This function opens and setups Win32 waveOut
171 *****************************************************************************/
172 static int Start( audio_output_t *p_aout, audio_sample_format_t *restrict fmt )
174 p_aout->time_get = WaveOutTimeGet;
176 p_aout->pause = WaveOutPause;
177 p_aout->flush = WaveOutFlush;
179 /* Default behaviour is to use software gain */
180 p_aout->sys->b_soft = true;
183 check for configured audio device!
185 fmt->i_format = var_InheritBool( p_aout, "waveout-float32" )?
186 VLC_CODEC_FL32: VLC_CODEC_S16N;
188 char *dev = var_GetNonEmptyString( p_aout, "waveout-audio-device");
189 uint32_t devid = findDeviceID( dev );
191 if(devid == WAVE_MAPPER && dev != NULL && stricmp(dev,"wavemapper"))
192 msg_Warn( p_aout, "configured audio device '%s' not available, "
193 "using default instead", dev );
196 WAVEOUTCAPS waveoutcaps;
197 if(waveOutGetDevCaps( devid, &waveoutcaps,
198 sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR)
200 /* log debug some infos about driver, to know who to blame
201 if it doesn't work */
202 msg_Dbg( p_aout, "Drivername: %ls", waveoutcaps.szPname);
203 msg_Dbg( p_aout, "Driver Version: %d.%d",
204 (waveoutcaps.vDriverVersion>>8)&255,
205 waveoutcaps.vDriverVersion & 255);
206 msg_Dbg( p_aout, "Manufacturer identifier: 0x%x", waveoutcaps.wMid );
207 msg_Dbg( p_aout, "Product identifier: 0x%x", waveoutcaps.wPid );
212 /* Open the device */
213 if( AOUT_FMT_SPDIF(fmt) && var_InheritBool (p_aout, "spdif") )
216 if( OpenWaveOut( p_aout, devid, VLC_CODEC_SPDIFL,
217 fmt->i_physical_channels,
218 aout_FormatNbChannels( fmt ), fmt->i_rate, false )
221 fmt->i_format = VLC_CODEC_SPDIFL;
223 /* Calculate the frame size in bytes */
224 fmt->i_bytes_per_frame = AOUT_SPDIF_SIZE;
225 fmt->i_frame_length = A52_FRAME_NB;
226 p_aout->sys->i_buffer_size = fmt->i_bytes_per_frame;
227 p_aout->sys->b_spdif = true;
232 "cannot open waveout audio device for spdif fallback to PCM" );
235 if( fmt->i_format != VLC_CODEC_SPDIFL )
237 int max_chan = var_InheritInteger( p_aout, "waveout-audio-channels");
238 int i_channels = aout_FormatNbChannels(fmt);
239 i_channels = ( i_channels < max_chan )? i_channels: max_chan;
245 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
246 | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT
247 | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
250 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
251 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
255 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
256 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
259 fmt->i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
263 fmt->i_physical_channels = AOUT_CHANS_STEREO;
267 fmt->i_physical_channels = AOUT_CHAN_CENTER;
269 msg_Dbg( p_aout, "Trying %d channels", i_channels );
271 while( ( OpenWaveOutPCM( p_aout, devid, &fmt->i_format,
272 fmt->i_physical_channels, i_channels,
273 fmt->i_rate, false ) != VLC_SUCCESS ) &&
278 msg_Err(p_aout, "Waveout couldn't find appropriate channel mapping");
282 /* Calculate the frame size in bytes */
283 aout_FormatPrepare( fmt );
284 p_aout->sys->i_buffer_size = FRAME_SIZE * fmt->i_bytes_per_frame;
286 if( waveoutcaps.dwSupport & WAVECAPS_VOLUME )
288 aout_GainRequest( p_aout, 1.0f );
289 p_aout->sys->b_soft = false;
292 WaveoutMuteSet( p_aout, p_aout->sys->b_mute );
294 p_aout->sys->b_spdif = false;
297 p_aout->sys->i_rate = fmt->i_rate;
299 waveOutReset( p_aout->sys->h_waveout );
301 /* Allocate silence buffer */
302 p_aout->sys->p_silence_buffer =
303 malloc( p_aout->sys->i_buffer_size );
304 if( p_aout->sys->p_silence_buffer == NULL )
306 msg_Err( p_aout, "Couldn't alloc silence buffer... aborting");
309 p_aout->sys->i_repeat_counter = 0;
312 /* Zero the buffer. WinCE doesn't have calloc(). */
313 memset( p_aout->sys->p_silence_buffer, 0,
314 p_aout->sys->i_buffer_size );
316 /* Now we need to setup our waveOut play notification structure */
317 p_aout->sys->i_frames = 0;
318 p_aout->sys->i_played_length = 0;
319 p_aout->sys->p_free_list = NULL;
324 /*****************************************************************************
325 * Play: play a sound buffer
326 *****************************************************************************
327 * This doesn't actually play the buffer. This just stores the buffer so it
328 * can be played by the callback thread.
329 *****************************************************************************/
330 static void Play( audio_output_t *p_aout, block_t *block )
332 struct lkwavehdr * p_waveheader =
333 (struct lkwavehdr *) malloc(sizeof(struct lkwavehdr));
336 msg_Err(p_aout, "Couldn't alloc WAVEHDR");
338 block_Release( block );
342 p_waveheader->p_next = NULL;
344 if( block && p_aout->sys->chans_to_reorder )
346 aout_ChannelReorder( block->p_buffer, block->i_buffer,
347 p_aout->sys->waveformat.Format.nChannels,
348 p_aout->sys->chan_table, p_aout->sys->format );
350 while( PlayWaveOut( p_aout, p_aout->sys->h_waveout, p_waveheader, block,
351 p_aout->sys->b_spdif ) != VLC_SUCCESS )
354 msg_Warn( p_aout, "Couln't write frame... sleeping");
355 msleep( block->i_length );
358 WaveOutClean( p_aout->sys );
359 WaveoutPollVolume( p_aout );
361 vlc_mutex_lock( &p_aout->sys->lock );
362 p_aout->sys->i_frames++;
363 p_aout->sys->i_played_length += block->i_length;
364 vlc_mutex_unlock( &p_aout->sys->lock );
367 /*****************************************************************************
368 * Close: close the audio device
369 *****************************************************************************/
370 static void Stop( audio_output_t *p_aout )
372 aout_sys_t *p_sys = p_aout->sys;
374 /* Before calling waveOutClose we must reset the device */
375 MMRESULT result = waveOutReset( p_sys->h_waveout );
376 if(result != MMSYSERR_NOERROR)
378 msg_Err( p_aout, "waveOutReset failed 0x%x", result );
380 now we must wait, that all buffers are played
381 because cancel doesn't work in this case...
383 if(result == MMSYSERR_NOTSUPPORTED)
386 clear currently played (done) buffers,
387 if returnvalue > 0 (means some buffer still playing)
388 wait for the driver event callback that one buffer
389 is finished with playing, and check again
390 the timeout of 5000ms is just, an emergency exit
391 of this loop, to avoid deadlock in case of other
392 (currently not known bugs, problems, errors cases?)
394 WaveOutFlush( p_aout, true );
398 /* wait for the frames to be queued in cleaning list */
399 WaveOutFlush( p_aout, true );
400 WaveOutClean( p_aout->sys );
402 /* now we can Close the device */
403 if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
405 msg_Err( p_aout, "waveOutClose failed" );
408 free( p_sys->p_silence_buffer );
409 p_aout->sys->i_played_length = 0;
410 p_sys->b_soft = true;
413 /*****************************************************************************
414 * OpenWaveOut: open the waveout sound device
415 ****************************************************************************/
416 static int OpenWaveOut( audio_output_t *p_aout, uint32_t i_device_id, int i_format,
417 int i_channels, int i_nb_channels, int i_rate,
422 /* Set sound format */
424 #define waveformat p_aout->sys->waveformat
426 waveformat.dwChannelMask = 0;
427 for( unsigned i = 0; pi_vlc_chan_order_wg4[i]; i++ )
428 if( i_channels & pi_vlc_chan_order_wg4[i] )
429 waveformat.dwChannelMask |= pi_channels_in[i];
433 case VLC_CODEC_SPDIFL:
435 /* To prevent channel re-ordering */
436 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
437 waveformat.Format.wBitsPerSample = 16;
438 waveformat.Samples.wValidBitsPerSample =
439 waveformat.Format.wBitsPerSample;
440 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
441 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
445 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
446 waveformat.Samples.wValidBitsPerSample =
447 waveformat.Format.wBitsPerSample;
448 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
449 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
453 waveformat.Format.wBitsPerSample = 16;
454 waveformat.Samples.wValidBitsPerSample =
455 waveformat.Format.wBitsPerSample;
456 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
457 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_PCM;
461 waveformat.Format.nChannels = i_nb_channels;
462 waveformat.Format.nSamplesPerSec = i_rate;
463 waveformat.Format.nBlockAlign =
464 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
465 waveformat.Format.nAvgBytesPerSec =
466 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
468 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
469 if( i_nb_channels <= 2 )
471 waveformat.Format.cbSize = 0;
475 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
476 waveformat.Format.cbSize =
477 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
481 msg_Dbg( p_aout, "OpenWaveDevice-ID: %u", i_device_id);
482 msg_Dbg( p_aout,"waveformat.Format.cbSize = %d",
483 waveformat.Format.cbSize);
484 msg_Dbg( p_aout,"waveformat.Format.wFormatTag = %u",
485 waveformat.Format.wFormatTag);
486 msg_Dbg( p_aout,"waveformat.Format.nChannels = %u",
487 waveformat.Format.nChannels);
488 msg_Dbg( p_aout,"waveformat.Format.nSamplesPerSec = %d",
489 (int)waveformat.Format.nSamplesPerSec);
490 msg_Dbg( p_aout,"waveformat.Format.nAvgBytesPerSec = %u",
491 (int)waveformat.Format.nAvgBytesPerSec);
492 msg_Dbg( p_aout,"waveformat.Format.nBlockAlign = %d",
493 waveformat.Format.nBlockAlign);
494 msg_Dbg( p_aout,"waveformat.Format.wBitsPerSample = %d",
495 waveformat.Format.wBitsPerSample);
496 msg_Dbg( p_aout,"waveformat.Samples.wValidBitsPerSample = %d",
497 waveformat.Samples.wValidBitsPerSample);
498 msg_Dbg( p_aout,"waveformat.Samples.wSamplesPerBlock = %d",
499 waveformat.Samples.wSamplesPerBlock);
500 msg_Dbg( p_aout,"waveformat.dwChannelMask = %lx",
501 waveformat.dwChannelMask);
504 /* Open the device */
505 result = waveOutOpen( &p_aout->sys->h_waveout, i_device_id,
506 (WAVEFORMATEX *)&waveformat,
507 (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
508 CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) );
509 if( result == WAVERR_BADFORMAT )
511 msg_Warn( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" );
514 if( result == MMSYSERR_ALLOCATED )
516 msg_Warn( p_aout, "waveOutOpen failed WAVERR_ALLOCATED" );
519 if( result != MMSYSERR_NOERROR )
521 msg_Warn( p_aout, "waveOutOpen failed" );
525 p_aout->sys->chans_to_reorder =
526 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
527 waveformat.dwChannelMask,
528 p_aout->sys->chan_table );
529 if( p_aout->sys->chans_to_reorder )
530 msg_Dbg( p_aout, "channel reordering needed" );
531 p_aout->sys->format = i_format;
539 /*****************************************************************************
540 * OpenWaveOutPCM: open a PCM waveout sound device
541 ****************************************************************************/
542 static int OpenWaveOutPCM( audio_output_t *p_aout, uint32_t i_device_id,
543 vlc_fourcc_t *i_format,
544 int i_channels, int i_nb_channels, int i_rate,
547 bool b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32");
549 if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_CODEC_FL32,
550 i_channels, i_nb_channels, i_rate, b_probe )
553 if ( OpenWaveOut( p_aout, i_device_id, VLC_CODEC_S16N,
554 i_channels, i_nb_channels, i_rate, b_probe )
561 *i_format = VLC_CODEC_S16N;
567 *i_format = VLC_CODEC_FL32;
572 /*****************************************************************************
573 * PlayWaveOut: play a buffer through the WaveOut device
574 *****************************************************************************/
575 static int PlayWaveOut( audio_output_t *p_aout, HWAVEOUT h_waveout,
576 struct lkwavehdr *p_waveheader, block_t *p_buffer, bool b_spdif)
580 /* Prepare the buffer */
581 if( p_buffer != NULL )
583 p_waveheader->hdr.lpData = (LPSTR)p_buffer->p_buffer;
584 p_waveheader->hdr.dwBufferLength = p_buffer->i_buffer;
586 copy the buffer to the silence buffer :) so in case we don't
587 get the next buffer fast enough (I will repeat this one a time
588 for AC3 / DTS and SPDIF this will sound better instead of
593 memcpy( p_aout->sys->p_silence_buffer,
595 p_aout->sys->i_buffer_size );
596 p_aout->sys->i_repeat_counter = 2;
599 /* Use silence buffer instead */
600 if(p_aout->sys->i_repeat_counter)
602 p_aout->sys->i_repeat_counter--;
603 if(!p_aout->sys->i_repeat_counter)
605 memset( p_aout->sys->p_silence_buffer,
606 0x00, p_aout->sys->i_buffer_size );
609 p_waveheader->hdr.lpData = (LPSTR)p_aout->sys->p_silence_buffer;
610 p_waveheader->hdr.dwBufferLength = p_aout->sys->i_buffer_size;
613 p_waveheader->hdr.dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
614 p_waveheader->hdr.dwFlags = 0;
616 result = waveOutPrepareHeader( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
617 if( result != MMSYSERR_NOERROR )
619 msg_Err( p_aout, "waveOutPrepareHeader failed" );
623 /* Send the buffer to the waveOut queue */
624 result = waveOutWrite( h_waveout, &p_waveheader->hdr, sizeof(WAVEHDR) );
625 if( result != MMSYSERR_NOERROR )
627 msg_Err( p_aout, "waveOutWrite failed" );
634 /*****************************************************************************
635 * WaveOutCallback: what to do once WaveOut has played its sound samples
636 *****************************************************************************/
637 static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
639 DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
643 audio_output_t *p_aout = (audio_output_t *)_p_aout;
644 struct lkwavehdr * p_waveheader = (struct lkwavehdr *) dwParam1;
646 if( uMsg != WOM_DONE ) return;
648 vlc_mutex_lock( &p_aout->sys->lock );
649 p_waveheader->p_next = p_aout->sys->p_free_list;
650 p_aout->sys->p_free_list = p_waveheader;
651 p_aout->sys->i_frames--;
652 vlc_cond_broadcast( &p_aout->sys->cond );
653 vlc_mutex_unlock( &p_aout->sys->lock );
656 static void WaveOutClean( aout_sys_t * p_sys )
658 struct lkwavehdr *p_whdr, *p_list;
660 vlc_mutex_lock(&p_sys->lock);
661 p_list = p_sys->p_free_list;
662 p_sys->p_free_list = NULL;
663 vlc_mutex_unlock(&p_sys->lock);
668 p_list = p_list->p_next;
669 WaveOutClearBuffer( p_sys->h_waveout, &p_whdr->hdr );
674 static void WaveOutClearBuffer( HWAVEOUT h_waveout, WAVEHDR *p_waveheader )
676 block_t *p_buffer = (block_t *)(p_waveheader->dwUser);
677 /* Unprepare and free the buffers which has just been played */
678 waveOutUnprepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
680 if( p_waveheader->dwUser != 1 )
681 block_Release( p_buffer );
685 reload the configuration drop down list, of the Audio Devices
687 static int ReloadWaveoutDevices( vlc_object_t *p_this, char const *psz_name,
688 char ***values, char ***descs )
690 int n = 0, nb_devices = waveOutGetNumDevs();
692 VLC_UNUSED( p_this); VLC_UNUSED( psz_name );
694 *values = xmalloc( (nb_devices + 1) * sizeof(char *) );
695 *descs = xmalloc( (nb_devices + 1) * sizeof(char *) );
697 (*values)[n] = strdup( "wavemapper" );
698 (*descs)[n] = strdup( _("Microsoft Soundmapper") );
701 for(int i = 0; i < nb_devices; i++)
704 wchar_t dev_name[MAXPNAMELEN+32];
706 if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
710 _snwprintf(dev_name, MAXPNAMELEN + 32, device_name_fmt,
711 caps.szPname, caps.wMid, caps.wPid);
712 (*values)[n] = FromWide( dev_name );
713 (*descs)[n] = strdup( (*values)[n] );
721 convert devicename to device ID for output
722 if device not found return WAVE_MAPPER, so let
723 windows decide which preferred audio device
726 static uint32_t findDeviceID(char *psz_device_name)
728 if( !psz_device_name )
731 uint32_t wave_devices = waveOutGetNumDevs();
733 for( uint32_t i = 0; i < wave_devices; i++ )
736 wchar_t dev_name[MAXPNAMELEN+32];
738 if( waveOutGetDevCaps( i, &caps, sizeof(WAVEOUTCAPS) )
739 != MMSYSERR_NOERROR )
742 _snwprintf( dev_name, MAXPNAMELEN + 32, device_name_fmt,
743 caps.szPname, caps.wMid, caps.wPid );
744 char *u8 = FromWide(dev_name);
745 if( !stricmp(u8, psz_device_name) )
756 static int DeviceSelect (audio_output_t *aout, const char *id)
758 var_SetString(aout, "waveout-audio-device", (id != NULL) ? id : "");
759 aout_DeviceReport (aout, id);
760 aout_RestartRequest (aout, AOUT_RESTART_OUTPUT);
764 static int Open(vlc_object_t *obj)
766 audio_output_t *aout = (audio_output_t *)obj;
767 aout_sys_t *sys = malloc(sizeof (*sys));
769 if (unlikely(sys == NULL))
774 aout->volume_set = WaveoutVolumeSet;
775 aout->mute_set = WaveoutMuteSet;
776 aout->device_select = DeviceSelect;
778 sys->f_volume = var_InheritFloat(aout, "waveout-volume");
779 sys->b_mute = var_InheritBool(aout, "mute");
781 aout_MuteReport(aout, sys->b_mute);
782 aout_VolumeReport(aout, sys->f_volume );
784 if( vlc_timer_create( &sys->volume_poll_timer,
785 WaveoutPollVolume, aout ) )
787 msg_Err( aout, "Couldn't create volume polling timer" );
792 vlc_mutex_init( &sys->lock );
793 vlc_cond_init( &sys->cond );
795 /* WaveOut does not support hot-plug events so list devices at startup */
797 int count = ReloadWaveoutDevices(VLC_OBJECT(aout), NULL, &ids, &names);
800 for (int i = 0; i < count; i++)
802 aout_HotplugReport(aout, ids[i], names[i]);
809 var_Create(aout, "waveout-audio-device", VLC_VAR_STRING|VLC_VAR_DOINHERIT);
813 static void Close(vlc_object_t *obj)
815 audio_output_t *aout = (audio_output_t *)obj;
816 aout_sys_t *sys = aout->sys;
818 var_Destroy(aout, "waveout-audio-device");
820 vlc_timer_destroy( sys->volume_poll_timer );
821 vlc_cond_destroy( &sys->cond );
822 vlc_mutex_destroy( &sys->lock );
827 static int WaveOutTimeGet(audio_output_t * p_aout, mtime_t *delay)
830 mmtime.wType = TIME_SAMPLES;
832 if( !p_aout->sys->i_frames )
835 if( waveOutGetPosition( p_aout->sys->h_waveout, &mmtime, sizeof(MMTIME) )
836 != MMSYSERR_NOERROR )
838 msg_Err( p_aout, "waveOutGetPosition failed");
842 mtime_t i_pos = (mtime_t) mmtime.u.sample * CLOCK_FREQ / p_aout->sys->i_rate;
843 *delay = p_aout->sys->i_played_length - i_pos;
847 static void WaveOutFlush( audio_output_t *p_aout, bool wait)
852 res = waveOutReset( p_aout->sys->h_waveout );
853 p_aout->sys->i_played_length = 0;
854 if( res != MMSYSERR_NOERROR )
855 msg_Err( p_aout, "waveOutReset failed");
859 vlc_mutex_lock( &p_aout->sys->lock );
860 while( p_aout->sys->i_frames )
862 vlc_cond_wait( &p_aout->sys->cond, &p_aout->sys-> lock );
864 vlc_mutex_unlock( &p_aout->sys->lock );
868 static void WaveOutPause( audio_output_t * p_aout, bool pause, mtime_t date)
874 vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 1, 200000 );
875 res = waveOutPause( p_aout->sys->h_waveout );
876 if( res != MMSYSERR_NOERROR )
878 msg_Err( p_aout, "waveOutPause failed (0x%x)", res);
884 vlc_timer_schedule( p_aout->sys->volume_poll_timer, false, 0, 0 );
885 res = waveOutRestart( p_aout->sys->h_waveout );
886 if( res != MMSYSERR_NOERROR )
888 msg_Err( p_aout, "waveOutRestart failed (0x%x)", res);
894 static int WaveoutVolumeSet( audio_output_t *p_aout, float volume )
896 aout_sys_t *sys = p_aout->sys;
900 float gain = volume * volume * volume;
901 if ( !sys->b_mute && aout_GainRequest( p_aout, gain ) )
906 const HWAVEOUT hwo = sys->h_waveout;
908 uint32_t vol = lroundf( volume * 0x7fff.fp0 );
918 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
919 if( r != MMSYSERR_NOERROR )
921 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
927 vlc_mutex_lock(&p_aout->sys->lock);
928 sys->f_volume = volume;
930 if( var_InheritBool( p_aout, "volume-save" ) )
931 config_PutFloat( p_aout, "waveout-volume", volume );
933 aout_VolumeReport( p_aout, volume );
934 vlc_mutex_unlock(&p_aout->sys->lock);
939 static int WaveoutMuteSet( audio_output_t * p_aout, bool mute )
941 aout_sys_t *sys = p_aout->sys;
945 float gain = sys->f_volume * sys->f_volume * sys->f_volume;
946 if ( aout_GainRequest( p_aout, mute ? 0.f : gain ) )
952 const HWAVEOUT hwo = sys->h_waveout;
953 uint32_t vol = mute ? 0 : lroundf( sys->f_volume * 0x7fff.fp0 );
958 MMRESULT r = waveOutSetVolume( hwo, vol | ( vol << 16 ) );
959 if( r != MMSYSERR_NOERROR )
961 msg_Err( p_aout, "waveOutSetVolume failed (%u)", r );
966 vlc_mutex_lock(&p_aout->sys->lock);
968 aout_MuteReport( p_aout, mute );
969 vlc_mutex_unlock(&p_aout->sys->lock);
974 static void WaveoutPollVolume( void * aout )
976 audio_output_t * p_aout = (audio_output_t *) aout;
979 MMRESULT r = waveOutGetVolume( p_aout->sys->h_waveout, (LPDWORD) &vol );
981 if( r != MMSYSERR_NOERROR )
983 msg_Err( p_aout, "waveOutGetVolume failed (%u)", r );
987 float volume = (float) ( vol & UINT32_C( 0xffff ) );
988 volume /= 0x7fff.fp0;
990 vlc_mutex_lock(&p_aout->sys->lock);
991 if( !p_aout->sys->b_mute && volume != p_aout->sys->f_volume )
993 p_aout->sys->f_volume = volume;
995 if( var_InheritBool( p_aout, "volume-save" ) )
996 config_PutFloat( p_aout, "waveout-volume", volume );
998 aout_VolumeReport( p_aout, volume );
1000 vlc_mutex_unlock(&p_aout->sys->lock);