1 /*****************************************************************************
2 * waveout.c : Windows waveOut plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2001-2009 VLC authors and VideoLAN
6 * Authors: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
40 #include <vlc_charset.h> /* FromWide() */
42 #include "audio_output/windows_audio_common.h"
44 #define FRAME_SIZE 4096 /* The size is in samples, not in bytes */
46 /*****************************************************************************
48 *****************************************************************************/
49 static int Open ( vlc_object_t
* );
50 static void Close ( vlc_object_t
* );
51 static void Play ( audio_output_t
*, block_t
*, vlc_tick_t
);
53 /*****************************************************************************
54 * notification_thread_t: waveOut event thread
55 *****************************************************************************/
57 typedef struct aout_sys_t aout_sys_t
;
62 struct lkwavehdr
* p_next
;
66 static int OpenWaveOut ( audio_output_t
*, uint32_t,
67 int, int, int, int, bool );
68 static int OpenWaveOutPCM( audio_output_t
*, uint32_t,
69 vlc_fourcc_t
*, int, int, int, bool );
70 static int PlayWaveOut ( audio_output_t
*, HWAVEOUT
, struct lkwavehdr
*,
73 static void CALLBACK
WaveOutCallback ( HWAVEOUT
, UINT
, DWORD_PTR
, DWORD_PTR
, DWORD_PTR
);
75 static void WaveOutClean( aout_sys_t
* p_sys
);
77 static void WaveOutClearBuffer( HWAVEOUT
, WAVEHDR
*);
79 static uint32_t findDeviceID(char *);
80 static int WaveOutTimeGet(audio_output_t
* , vlc_tick_t
*);
81 static void WaveOutFlush( audio_output_t
*);
82 static void WaveOutDrain( audio_output_t
*);
83 static void WaveOutPause( audio_output_t
*, bool, vlc_tick_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 vlc_tick_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 add_float( "waveout-volume", 1.0f
, VOLUME_TEXT
, NULL
, true )
157 change_float_range(0.0f
, 2.0f
)
158 add_bool( "waveout-float32", true, FLOAT_TEXT
, FLOAT_LONGTEXT
, true )
159 add_integer ("waveout-audio-channels", 9, AUDIO_CHAN_TEXT
,
160 AUDIO_CHAN_LONGTEXT
, false)
161 change_integer_range(1,9)
162 set_callbacks( Open
, Close
)
165 /*****************************************************************************
166 * Opens the audio device
167 *****************************************************************************
168 * This function opens and setups Win32 waveOut
169 *****************************************************************************/
170 static int Start( audio_output_t
*p_aout
, audio_sample_format_t
*restrict fmt
)
172 if( aout_FormatNbChannels( fmt
) == 0 )
175 p_aout
->time_get
= WaveOutTimeGet
;
177 p_aout
->pause
= WaveOutPause
;
178 p_aout
->flush
= WaveOutFlush
;
179 p_aout
->drain
= WaveOutDrain
;
181 aout_sys_t
*sys
= p_aout
->sys
;
183 /* Default behaviour is to use software gain */
186 char *dev
= var_GetNonEmptyString( p_aout
, "waveout-audio-device");
187 uint32_t devid
= findDeviceID( dev
);
189 if(devid
== WAVE_MAPPER
&& dev
!= NULL
&& stricmp(dev
,"wavemapper"))
190 msg_Warn( p_aout
, "configured audio device '%s' not available, "
191 "using default instead", dev
);
194 WAVEOUTCAPS waveoutcaps
;
195 if(waveOutGetDevCaps( devid
, &waveoutcaps
,
196 sizeof(WAVEOUTCAPS
)) == MMSYSERR_NOERROR
)
198 /* log debug some infos about driver, to know who to blame
199 if it doesn't work */
200 msg_Dbg( p_aout
, "Drivername: %ls", waveoutcaps
.szPname
);
201 msg_Dbg( p_aout
, "Driver Version: %d.%d",
202 (waveoutcaps
.vDriverVersion
>>8)&255,
203 waveoutcaps
.vDriverVersion
& 255);
204 msg_Dbg( p_aout
, "Manufacturer identifier: 0x%x", waveoutcaps
.wMid
);
205 msg_Dbg( p_aout
, "Product identifier: 0x%x", waveoutcaps
.wPid
);
210 /* Open the device */
211 if( AOUT_FMT_SPDIF(fmt
) && var_InheritBool (p_aout
, "spdif") )
214 if( OpenWaveOut( p_aout
, devid
, VLC_CODEC_SPDIFL
,
215 fmt
->i_physical_channels
,
216 aout_FormatNbChannels( fmt
), fmt
->i_rate
, false )
219 fmt
->i_format
= VLC_CODEC_SPDIFL
;
221 /* Calculate the frame size in bytes */
222 fmt
->i_bytes_per_frame
= AOUT_SPDIF_SIZE
;
223 fmt
->i_frame_length
= A52_FRAME_NB
;
224 sys
->i_buffer_size
= fmt
->i_bytes_per_frame
;
230 "cannot open waveout audio device for spdif fallback to PCM" );
233 if( fmt
->i_format
!= VLC_CODEC_SPDIFL
)
236 check for configured audio device!
238 fmt
->i_format
= var_InheritBool( p_aout
, "waveout-float32" )?
239 VLC_CODEC_FL32
: VLC_CODEC_S16N
;
241 int max_chan
= var_InheritInteger( p_aout
, "waveout-audio-channels");
242 int i_channels
= aout_FormatNbChannels(fmt
);
243 i_channels
= ( i_channels
< max_chan
)? i_channels
: max_chan
;
249 fmt
->i_physical_channels
= AOUT_CHANS_8_1
;
252 fmt
->i_physical_channels
= AOUT_CHANS_7_1
;
255 fmt
->i_physical_channels
= AOUT_CHANS_7_0
;
258 fmt
->i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
259 | AOUT_CHAN_CENTER
| AOUT_CHAN_REARLEFT
260 | AOUT_CHAN_REARRIGHT
| AOUT_CHAN_LFE
;
263 fmt
->i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
264 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
268 fmt
->i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
269 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
;
272 fmt
->i_physical_channels
= AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
276 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
;
280 fmt
->i_physical_channels
= AOUT_CHAN_CENTER
;
282 msg_Dbg( p_aout
, "Trying %d channels", i_channels
);
284 while( ( OpenWaveOutPCM( p_aout
, devid
, &fmt
->i_format
,
285 fmt
->i_physical_channels
, i_channels
,
286 fmt
->i_rate
, false ) != VLC_SUCCESS
) &&
291 msg_Err(p_aout
, "Waveout couldn't find appropriate channel mapping");
295 /* Calculate the frame size in bytes */
296 aout_FormatPrepare( fmt
);
297 sys
->i_buffer_size
= FRAME_SIZE
* fmt
->i_bytes_per_frame
;
299 if( waveoutcaps
.dwSupport
& WAVECAPS_VOLUME
)
301 aout_GainRequest( p_aout
, 1.0f
);
305 WaveoutMuteSet( p_aout
, sys
->b_mute
);
307 sys
->b_spdif
= false;
310 sys
->i_rate
= fmt
->i_rate
;
312 waveOutReset( sys
->h_waveout
);
314 /* Allocate silence buffer */
315 sys
->p_silence_buffer
=
316 malloc( sys
->i_buffer_size
);
317 if( sys
->p_silence_buffer
== NULL
)
319 msg_Err( p_aout
, "Couldn't alloc silence buffer... aborting");
322 sys
->i_repeat_counter
= 0;
325 /* Zero the buffer. WinCE doesn't have calloc(). */
326 memset( sys
->p_silence_buffer
, 0,
327 sys
->i_buffer_size
);
329 /* Now we need to setup our waveOut play notification structure */
331 sys
->i_played_length
= 0;
332 sys
->p_free_list
= NULL
;
334 fmt
->channel_type
= AUDIO_CHANNEL_TYPE_BITMAP
;
339 /*****************************************************************************
340 * Play: play a sound buffer
341 *****************************************************************************
342 * This doesn't actually play the buffer. This just stores the buffer so it
343 * can be played by the callback thread.
344 *****************************************************************************/
345 static void Play( audio_output_t
*p_aout
, block_t
*block
, vlc_tick_t date
)
347 aout_sys_t
*sys
= p_aout
->sys
;
349 struct lkwavehdr
* p_waveheader
=
350 (struct lkwavehdr
*) malloc(sizeof(struct lkwavehdr
));
353 msg_Err(p_aout
, "Couldn't alloc WAVEHDR");
355 block_Release( block
);
359 p_waveheader
->p_next
= NULL
;
361 if( block
&& sys
->chans_to_reorder
)
363 aout_ChannelReorder( block
->p_buffer
, block
->i_buffer
,
364 sys
->waveformat
.Format
.nChannels
,
365 sys
->chan_table
, sys
->format
);
367 while( PlayWaveOut( p_aout
, sys
->h_waveout
, p_waveheader
, block
,
368 sys
->b_spdif
) != VLC_SUCCESS
)
371 msg_Warn( p_aout
, "Couln't write frame... sleeping");
372 vlc_tick_sleep( block
->i_length
);
376 WaveoutPollVolume( p_aout
);
378 vlc_mutex_lock( &sys
->lock
);
380 sys
->i_played_length
+= block
->i_length
;
381 vlc_mutex_unlock( &sys
->lock
);
385 /*****************************************************************************
386 * Close: close the audio device
387 *****************************************************************************/
388 static void Stop( audio_output_t
*p_aout
)
390 aout_sys_t
*p_sys
= p_aout
->sys
;
392 /* Before calling waveOutClose we must reset the device */
393 waveOutReset( p_sys
->h_waveout
);
395 /* wait for the frames to be queued in cleaning list */
396 WaveOutDrain( p_aout
);
397 WaveOutClean( p_sys
);
399 /* now we can Close the device */
400 if( waveOutClose( p_sys
->h_waveout
) != MMSYSERR_NOERROR
)
402 msg_Err( p_aout
, "waveOutClose failed" );
405 free( p_sys
->p_silence_buffer
);
406 p_sys
->i_played_length
= 0;
407 p_sys
->b_soft
= true;
410 /*****************************************************************************
411 * OpenWaveOut: open the waveout sound device
412 ****************************************************************************/
413 static int OpenWaveOut( audio_output_t
*p_aout
, uint32_t i_device_id
, int i_format
,
414 int i_channels
, int i_nb_channels
, int i_rate
,
418 aout_sys_t
*sys
= p_aout
->sys
;
420 /* Set sound format */
422 #define waveformat sys->waveformat
424 waveformat
.dwChannelMask
= 0;
425 for( unsigned i
= 0; pi_vlc_chan_order_wg4
[i
]; i
++ )
426 if( i_channels
& pi_vlc_chan_order_wg4
[i
] )
427 waveformat
.dwChannelMask
|= pi_channels_in
[i
];
431 case VLC_CODEC_SPDIFL
:
433 /* To prevent channel re-ordering */
434 waveformat
.dwChannelMask
= SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
;
435 waveformat
.Format
.wBitsPerSample
= 16;
436 waveformat
.Samples
.wValidBitsPerSample
=
437 waveformat
.Format
.wBitsPerSample
;
438 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_DOLBY_AC3_SPDIF
;
439 waveformat
.SubFormat
= __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF
;
443 waveformat
.Format
.wBitsPerSample
= sizeof(float) * 8;
444 waveformat
.Samples
.wValidBitsPerSample
=
445 waveformat
.Format
.wBitsPerSample
;
446 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_IEEE_FLOAT
;
447 waveformat
.SubFormat
= __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
451 waveformat
.Format
.wBitsPerSample
= 16;
452 waveformat
.Samples
.wValidBitsPerSample
=
453 waveformat
.Format
.wBitsPerSample
;
454 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
455 waveformat
.SubFormat
= __KSDATAFORMAT_SUBTYPE_PCM
;
459 waveformat
.Format
.nChannels
= i_nb_channels
;
460 waveformat
.Format
.nSamplesPerSec
= i_rate
;
461 waveformat
.Format
.nBlockAlign
=
462 waveformat
.Format
.wBitsPerSample
/ 8 * i_nb_channels
;
463 waveformat
.Format
.nAvgBytesPerSec
=
464 waveformat
.Format
.nSamplesPerSec
* waveformat
.Format
.nBlockAlign
;
466 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
467 if( i_nb_channels
<= 2 )
469 waveformat
.Format
.cbSize
= 0;
473 waveformat
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
474 waveformat
.Format
.cbSize
=
475 sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
479 msg_Dbg( p_aout
, "OpenWaveDevice-ID: %u", i_device_id
);
480 msg_Dbg( p_aout
,"waveformat.Format.cbSize = %d",
481 waveformat
.Format
.cbSize
);
482 msg_Dbg( p_aout
,"waveformat.Format.wFormatTag = %u",
483 waveformat
.Format
.wFormatTag
);
484 msg_Dbg( p_aout
,"waveformat.Format.nChannels = %u",
485 waveformat
.Format
.nChannels
);
486 msg_Dbg( p_aout
,"waveformat.Format.nSamplesPerSec = %d",
487 (int)waveformat
.Format
.nSamplesPerSec
);
488 msg_Dbg( p_aout
,"waveformat.Format.nAvgBytesPerSec = %u",
489 (int)waveformat
.Format
.nAvgBytesPerSec
);
490 msg_Dbg( p_aout
,"waveformat.Format.nBlockAlign = %d",
491 waveformat
.Format
.nBlockAlign
);
492 msg_Dbg( p_aout
,"waveformat.Format.wBitsPerSample = %d",
493 waveformat
.Format
.wBitsPerSample
);
494 msg_Dbg( p_aout
,"waveformat.Samples.wValidBitsPerSample = %d",
495 waveformat
.Samples
.wValidBitsPerSample
);
496 msg_Dbg( p_aout
,"waveformat.Samples.wSamplesPerBlock = %d",
497 waveformat
.Samples
.wSamplesPerBlock
);
498 msg_Dbg( p_aout
,"waveformat.dwChannelMask = %u",
499 waveformat
.dwChannelMask
);
502 /* Open the device */
503 result
= waveOutOpen( &sys
->h_waveout
, i_device_id
,
504 (WAVEFORMATEX
*)&waveformat
,
505 (DWORD_PTR
)WaveOutCallback
, (DWORD_PTR
)p_aout
,
506 CALLBACK_FUNCTION
| (b_probe
?WAVE_FORMAT_QUERY
:0) );
507 if( result
== WAVERR_BADFORMAT
)
509 msg_Warn( p_aout
, "waveOutOpen failed WAVERR_BADFORMAT" );
512 if( result
== MMSYSERR_ALLOCATED
)
514 msg_Warn( p_aout
, "waveOutOpen failed WAVERR_ALLOCATED" );
517 if( result
!= MMSYSERR_NOERROR
)
519 msg_Warn( p_aout
, "waveOutOpen failed" );
523 sys
->chans_to_reorder
=
524 aout_CheckChannelReorder( pi_channels_in
, pi_channels_out
,
525 waveformat
.dwChannelMask
,
527 if( sys
->chans_to_reorder
)
528 msg_Dbg( p_aout
, "channel reordering needed" );
529 sys
->format
= i_format
;
537 /*****************************************************************************
538 * OpenWaveOutPCM: open a PCM waveout sound device
539 ****************************************************************************/
540 static int OpenWaveOutPCM( audio_output_t
*p_aout
, uint32_t i_device_id
,
541 vlc_fourcc_t
*i_format
,
542 int i_channels
, int i_nb_channels
, int i_rate
,
545 bool b_use_float32
= var_CreateGetBool( p_aout
, "waveout-float32");
547 if( !b_use_float32
|| OpenWaveOut( p_aout
, i_device_id
, VLC_CODEC_FL32
,
548 i_channels
, i_nb_channels
, i_rate
, b_probe
)
551 if ( OpenWaveOut( p_aout
, i_device_id
, VLC_CODEC_S16N
,
552 i_channels
, i_nb_channels
, i_rate
, b_probe
)
559 *i_format
= VLC_CODEC_S16N
;
565 *i_format
= VLC_CODEC_FL32
;
570 /*****************************************************************************
571 * PlayWaveOut: play a buffer through the WaveOut device
572 *****************************************************************************/
573 static int PlayWaveOut( audio_output_t
*p_aout
, HWAVEOUT h_waveout
,
574 struct lkwavehdr
*p_waveheader
, block_t
*p_buffer
, bool b_spdif
)
577 aout_sys_t
*sys
= p_aout
->sys
;
579 /* Prepare the buffer */
580 if( p_buffer
!= NULL
)
582 p_waveheader
->hdr
.lpData
= (LPSTR
)p_buffer
->p_buffer
;
583 p_waveheader
->hdr
.dwBufferLength
= p_buffer
->i_buffer
;
585 copy the buffer to the silence buffer :) so in case we don't
586 get the next buffer fast enough (I will repeat this one a time
587 for AC3 / DTS and SPDIF this will sound better instead of
592 memcpy( sys
->p_silence_buffer
,
594 sys
->i_buffer_size
);
595 sys
->i_repeat_counter
= 2;
598 /* Use silence buffer instead */
599 if(sys
->i_repeat_counter
)
601 sys
->i_repeat_counter
--;
602 if(!sys
->i_repeat_counter
)
604 memset( sys
->p_silence_buffer
,
605 0x00, sys
->i_buffer_size
);
608 p_waveheader
->hdr
.lpData
= (LPSTR
)sys
->p_silence_buffer
;
609 p_waveheader
->hdr
.dwBufferLength
= sys
->i_buffer_size
;
612 p_waveheader
->hdr
.dwUser
= p_buffer
? (DWORD_PTR
)p_buffer
: (DWORD_PTR
)1;
613 p_waveheader
->hdr
.dwFlags
= 0;
615 result
= waveOutPrepareHeader( h_waveout
, &p_waveheader
->hdr
, sizeof(WAVEHDR
) );
616 if( result
!= MMSYSERR_NOERROR
)
618 msg_Err( p_aout
, "waveOutPrepareHeader failed" );
622 /* Send the buffer to the waveOut queue */
623 result
= waveOutWrite( h_waveout
, &p_waveheader
->hdr
, sizeof(WAVEHDR
) );
624 if( result
!= MMSYSERR_NOERROR
)
626 msg_Err( p_aout
, "waveOutWrite failed" );
633 /*****************************************************************************
634 * WaveOutCallback: what to do once WaveOut has played its sound samples
635 *****************************************************************************/
636 static void CALLBACK
WaveOutCallback( HWAVEOUT h_waveout
, UINT uMsg
,
638 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
642 aout_sys_t
*sys
= ((audio_output_t
*)_p_aout
)->sys
;
643 struct lkwavehdr
* p_waveheader
= (struct lkwavehdr
*) dwParam1
;
645 if( uMsg
!= WOM_DONE
) return;
647 vlc_mutex_lock( &sys
->lock
);
648 p_waveheader
->p_next
= sys
->p_free_list
;
649 sys
->p_free_list
= p_waveheader
;
651 vlc_cond_broadcast( &sys
->cond
);
652 vlc_mutex_unlock( &sys
->lock
);
655 static void WaveOutClean( aout_sys_t
* p_sys
)
657 struct lkwavehdr
*p_whdr
, *p_list
;
659 vlc_mutex_lock(&p_sys
->lock
);
660 p_list
= p_sys
->p_free_list
;
661 p_sys
->p_free_list
= NULL
;
662 vlc_mutex_unlock(&p_sys
->lock
);
667 p_list
= p_list
->p_next
;
668 WaveOutClearBuffer( p_sys
->h_waveout
, &p_whdr
->hdr
);
673 static void WaveOutClearBuffer( HWAVEOUT h_waveout
, WAVEHDR
*p_waveheader
)
675 block_t
*p_buffer
= (block_t
*)(p_waveheader
->dwUser
);
676 /* Unprepare and free the buffers which has just been played */
677 waveOutUnprepareHeader( h_waveout
, p_waveheader
, sizeof(WAVEHDR
) );
679 if( p_waveheader
->dwUser
!= 1 )
680 block_Release( p_buffer
);
684 reload the configuration drop down list, of the Audio Devices
686 static int ReloadWaveoutDevices( char const *psz_name
,
687 char ***values
, char ***descs
)
689 int n
= 0, nb_devices
= waveOutGetNumDevs();
691 VLC_UNUSED( psz_name
);
693 *values
= xmalloc( (nb_devices
+ 1) * sizeof(char *) );
694 *descs
= xmalloc( (nb_devices
+ 1) * sizeof(char *) );
696 (*values
)[n
] = strdup( "wavemapper" );
697 (*descs
)[n
] = strdup( _("Microsoft Soundmapper") );
700 for(int i
= 0; i
< nb_devices
; i
++)
703 wchar_t dev_name
[MAXPNAMELEN
+32];
705 if(waveOutGetDevCaps(i
, &caps
, sizeof(WAVEOUTCAPS
))
709 _snwprintf(dev_name
, MAXPNAMELEN
+ 32, device_name_fmt
,
710 caps
.szPname
, caps
.wMid
, caps
.wPid
);
711 (*values
)[n
] = FromWide( dev_name
);
712 (*descs
)[n
] = strdup( (*values
)[n
] );
719 VLC_CONFIG_STRING_ENUM(ReloadWaveoutDevices
)
722 convert devicename to device ID for output
723 if device not found return WAVE_MAPPER, so let
724 windows decide which preferred audio device
727 static uint32_t findDeviceID(char *psz_device_name
)
729 if( !psz_device_name
)
732 uint32_t wave_devices
= waveOutGetNumDevs();
734 for( uint32_t i
= 0; i
< wave_devices
; i
++ )
737 wchar_t dev_name
[MAXPNAMELEN
+32];
739 if( waveOutGetDevCaps( i
, &caps
, sizeof(WAVEOUTCAPS
) )
740 != MMSYSERR_NOERROR
)
743 _snwprintf( dev_name
, MAXPNAMELEN
+ 32, device_name_fmt
,
744 caps
.szPname
, caps
.wMid
, caps
.wPid
);
745 char *u8
= FromWide(dev_name
);
746 if( !stricmp(u8
, psz_device_name
) )
757 static int DeviceSelect (audio_output_t
*aout
, const char *id
)
759 var_SetString(aout
, "waveout-audio-device", (id
!= NULL
) ? id
: "");
760 aout_DeviceReport (aout
, id
);
761 aout_RestartRequest (aout
, AOUT_RESTART_OUTPUT
);
765 static int Open(vlc_object_t
*obj
)
767 audio_output_t
*aout
= (audio_output_t
*)obj
;
768 aout_sys_t
*sys
= malloc(sizeof (*sys
));
770 if (unlikely(sys
== NULL
))
775 aout
->volume_set
= WaveoutVolumeSet
;
776 aout
->mute_set
= WaveoutMuteSet
;
777 aout
->device_select
= DeviceSelect
;
779 sys
->f_volume
= var_InheritFloat(aout
, "waveout-volume");
780 sys
->b_mute
= var_InheritBool(aout
, "mute");
782 aout_MuteReport(aout
, sys
->b_mute
);
783 aout_VolumeReport(aout
, sys
->f_volume
);
785 if( vlc_timer_create( &sys
->volume_poll_timer
,
786 WaveoutPollVolume
, aout
) )
788 msg_Err( aout
, "Couldn't create volume polling timer" );
793 vlc_mutex_init( &sys
->lock
);
794 vlc_cond_init( &sys
->cond
);
796 /* WaveOut does not support hot-plug events so list devices at startup */
798 int count
= ReloadWaveoutDevices(NULL
, &ids
, &names
);
801 for (int i
= 0; i
< count
; i
++)
803 aout_HotplugReport(aout
, ids
[i
], names
[i
]);
811 char *dev
= var_CreateGetNonEmptyString(aout
, "waveout-audio-device");
812 aout_DeviceReport(aout
, dev
);
818 static void Close(vlc_object_t
*obj
)
820 audio_output_t
*aout
= (audio_output_t
*)obj
;
821 aout_sys_t
*sys
= aout
->sys
;
823 var_Destroy(aout
, "waveout-audio-device");
825 vlc_timer_destroy( sys
->volume_poll_timer
);
829 static int WaveOutTimeGet(audio_output_t
* p_aout
, vlc_tick_t
*delay
)
832 mmtime
.wType
= TIME_SAMPLES
;
833 aout_sys_t
*sys
= p_aout
->sys
;
838 if( waveOutGetPosition( sys
->h_waveout
, &mmtime
, sizeof(MMTIME
) )
839 != MMSYSERR_NOERROR
)
841 msg_Err( p_aout
, "waveOutGetPosition failed");
845 vlc_tick_t i_pos
= vlc_tick_from_samples(mmtime
.u
.sample
, sys
->i_rate
);
846 *delay
= sys
->i_played_length
- i_pos
;
850 static void WaveOutFlush( audio_output_t
*p_aout
)
853 aout_sys_t
*sys
= p_aout
->sys
;
855 res
= waveOutReset( sys
->h_waveout
);
856 sys
->i_played_length
= 0;
857 if( res
!= MMSYSERR_NOERROR
)
858 msg_Err( p_aout
, "waveOutReset failed");
861 static void WaveOutDrain( audio_output_t
*p_aout
)
863 aout_sys_t
*sys
= p_aout
->sys
;
865 vlc_mutex_lock( &sys
->lock
);
866 while( sys
->i_frames
)
868 vlc_cond_wait( &sys
->cond
, &sys
->lock
);
870 vlc_mutex_unlock( &sys
->lock
);
873 static void WaveOutPause( audio_output_t
* p_aout
, bool pause
, vlc_tick_t date
)
877 aout_sys_t
*sys
= p_aout
->sys
;
881 vlc_timer_schedule_asap( sys
->volume_poll_timer
, VLC_TICK_FROM_MS(200) );
882 res
= waveOutPause( sys
->h_waveout
);
883 if( res
!= MMSYSERR_NOERROR
)
885 msg_Err( p_aout
, "waveOutPause failed (0x%x)", res
);
891 vlc_timer_disarm( sys
->volume_poll_timer
);
892 res
= waveOutRestart( sys
->h_waveout
);
893 if( res
!= MMSYSERR_NOERROR
)
895 msg_Err( p_aout
, "waveOutRestart failed (0x%x)", res
);
901 static int WaveoutVolumeSet( audio_output_t
*p_aout
, float volume
)
903 aout_sys_t
*sys
= p_aout
->sys
;
907 float gain
= volume
* volume
* volume
;
908 if ( !sys
->b_mute
&& aout_GainRequest( p_aout
, gain
) )
913 const HWAVEOUT hwo
= sys
->h_waveout
;
915 uint32_t vol
= lroundf( volume
* 0x7fff.fp0
);
925 MMRESULT r
= waveOutSetVolume( hwo
, vol
| ( vol
<< 16 ) );
926 if( r
!= MMSYSERR_NOERROR
)
928 msg_Err( p_aout
, "waveOutSetVolume failed (%u)", r
);
934 vlc_mutex_lock(&sys
->lock
);
935 sys
->f_volume
= volume
;
937 if( var_InheritBool( p_aout
, "volume-save" ) )
938 config_PutFloat( "waveout-volume", volume
);
940 aout_VolumeReport( p_aout
, volume
);
941 vlc_mutex_unlock(&sys
->lock
);
946 static int WaveoutMuteSet( audio_output_t
* p_aout
, bool mute
)
948 aout_sys_t
*sys
= p_aout
->sys
;
952 float gain
= sys
->f_volume
* sys
->f_volume
* sys
->f_volume
;
953 if ( aout_GainRequest( p_aout
, mute
? 0.f
: gain
) )
959 const HWAVEOUT hwo
= sys
->h_waveout
;
960 uint32_t vol
= mute
? 0 : lroundf( sys
->f_volume
* 0x7fff.fp0
);
965 MMRESULT r
= waveOutSetVolume( hwo
, vol
| ( vol
<< 16 ) );
966 if( r
!= MMSYSERR_NOERROR
)
968 msg_Err( p_aout
, "waveOutSetVolume failed (%u)", r
);
973 vlc_mutex_lock(&sys
->lock
);
975 aout_MuteReport( p_aout
, mute
);
976 vlc_mutex_unlock(&sys
->lock
);
981 static void WaveoutPollVolume( void * _aout
)
983 audio_output_t
* aout
= (audio_output_t
*) _aout
;
984 aout_sys_t
*sys
= aout
->sys
;
987 MMRESULT r
= waveOutGetVolume( sys
->h_waveout
, (LPDWORD
) &vol
);
989 if( r
!= MMSYSERR_NOERROR
)
991 msg_Err( aout
, "waveOutGetVolume failed (%u)", r
);
995 float volume
= (float) ( vol
& UINT32_C( 0xffff ) );
996 volume
/= 0x7fff.fp0
;
998 vlc_mutex_lock(&sys
->lock
);
999 if( !sys
->b_mute
&& volume
!= sys
->f_volume
)
1001 sys
->f_volume
= volume
;
1003 if( var_InheritBool( aout
, "volume-save" ) )
1004 config_PutFloat( "waveout-volume", volume
);
1006 aout_VolumeReport( aout
, volume
);
1008 vlc_mutex_unlock(&sys
->lock
);