1 /*****************************************************************************
2 * waveout.c : Windows waveOut plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2001-2009 the VideoLAN team
7 * Authors: Gildas Bazin <gbazin@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 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 General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
36 #include <vlc_charset.h>
38 #include "windows_audio_common.h"
40 #define FRAME_SIZE 4096 /* The size is in samples, not in bytes */
42 /*****************************************************************************
44 *****************************************************************************/
45 static int Open ( vlc_object_t * );
46 static void Close ( vlc_object_t * );
47 static void Play ( aout_instance_t * );
49 /*****************************************************************************
50 * notification_thread_t: waveOut event thread
51 *****************************************************************************/
52 typedef struct notification_thread_t
55 aout_instance_t *p_aout;
57 } notification_thread_t;
60 static void Probe ( aout_instance_t * );
61 static int OpenWaveOut ( aout_instance_t *, uint32_t,
62 int, int, int, int, bool );
63 static int OpenWaveOutPCM( aout_instance_t *, uint32_t,
64 vlc_fourcc_t*, int, int, int, bool );
65 static int PlayWaveOut ( aout_instance_t *, HWAVEOUT, WAVEHDR *,
66 aout_buffer_t *, bool );
68 static void CALLBACK WaveOutCallback ( HWAVEOUT, UINT, DWORD, DWORD, DWORD );
69 static void* WaveOutThread( vlc_object_t * );
71 static int VolumeGet( aout_instance_t *, audio_volume_t * );
72 static int VolumeSet( aout_instance_t *, audio_volume_t );
74 static int WaveOutClearDoneBuffers(aout_sys_t *p_sys);
76 static int ReloadWaveoutDevices( vlc_object_t *, char const *,
77 vlc_value_t, vlc_value_t, void * );
78 static uint32_t findDeviceID(char *);
80 static const char psz_device_name_fmt[] = "%s ($%x,$%x)";
82 static const char *const ppsz_adev[] = { "wavemapper", };
83 static const char *const ppsz_adev_text[] = { N_("Microsoft Soundmapper") };
87 /*****************************************************************************
89 *****************************************************************************/
90 #define DEVICE_TEXT N_("Select Audio Device")
91 #define DEVICE_LONG N_("Select special Audio device, or let windows "\
92 "decide (default), change needs VLC restart "\
94 #define DEFAULT_AUDIO_DEVICE N_("Default Audio Device")
97 set_shortname( "WaveOut" )
98 set_description( N_("Win32 waveOut extension output") )
99 set_capability( "audio output", 50 )
100 set_category( CAT_AUDIO )
101 set_subcategory( SUBCAT_AUDIO_AOUT )
102 add_bool( "waveout-float32", true, NULL, FLOAT_TEXT, FLOAT_LONGTEXT, true )
104 add_string( "waveout-audio-device", "wavemapper", NULL,
105 DEVICE_TEXT, DEVICE_LONG, false )
106 add_deprecated_alias( "waveout-dev" ) /* deprecated since 0.9.3 */
107 change_string_list( ppsz_adev, ppsz_adev_text, ReloadWaveoutDevices )
108 change_need_restart ()
109 change_action_add( ReloadWaveoutDevices, N_("Refresh list") )
112 set_callbacks( Open, Close )
115 /*****************************************************************************
116 * aout_sys_t: waveOut audio output method descriptor
117 *****************************************************************************
118 * This structure is part of the audio output thread descriptor.
119 * It describes the waveOut specific properties of an audio device.
120 *****************************************************************************/
123 uint32_t i_wave_device_id; /* ID of selected output device */
125 HWAVEOUT h_waveout; /* handle to waveout instance */
127 WAVEFORMATEXTENSIBLE waveformat; /* audio format */
129 WAVEHDR waveheader[FRAMES_NUM];
131 notification_thread_t *p_notif; /* WaveOutThread id */
133 HANDLE new_buffer_event;
135 // rental from alsa.c to synchronize startup of audiothread
136 int b_playing; /* playing status */
139 int i_repeat_counter;
143 uint8_t *p_silence_buffer; /* buffer we use to play silence */
145 bool b_chan_reorder; /* do we need channel reordering */
146 int pi_chan_table[AOUT_CHAN_MAX];
149 /*****************************************************************************
150 * Open: open the audio device
151 *****************************************************************************
152 * This function opens and setups Win32 waveOut
153 *****************************************************************************/
154 static int Open( vlc_object_t *p_this )
156 aout_instance_t *p_aout = (aout_instance_t *)p_this;
159 /* Allocate structure */
160 p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
162 if( p_aout->output.p_sys == NULL )
165 p_aout->output.pf_play = Play;
166 p_aout->b_die = false;
170 initialize/update Device selection List
172 ReloadWaveoutDevices( p_this, "waveout-audio-device", val, val, NULL);
176 check for configured audio device!
178 char *psz_waveout_dev = var_CreateGetString( p_aout, "waveout-audio-device");
180 p_aout->output.p_sys->i_wave_device_id =
181 findDeviceID( psz_waveout_dev );
183 if(p_aout->output.p_sys->i_wave_device_id == WAVE_MAPPER)
185 if(psz_waveout_dev &&
186 stricmp(psz_waveout_dev,"wavemapper"))
188 msg_Warn( p_aout, "configured audio device '%s' not available, "\
189 "use default instead", psz_waveout_dev );
192 free( psz_waveout_dev );
195 WAVEOUTCAPS waveoutcaps;
196 if(waveOutGetDevCaps( p_aout->output.p_sys->i_wave_device_id,
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: %s", 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 if( var_Type( p_aout, "audio-device" ) == 0 )
217 if( var_Get( p_aout, "audio-device", &val ) < 0 )
219 /* Probe() has failed. */
220 var_Destroy( p_aout, "waveout-audio-device");
221 free( p_aout->output.p_sys );
226 /* Open the device */
227 if( val.i_int == AOUT_VAR_SPDIF )
229 p_aout->output.output.i_format = VLC_CODEC_SPDIFL;
231 if( OpenWaveOut( p_aout,
232 p_aout->output.p_sys->i_wave_device_id,
234 p_aout->output.output.i_physical_channels,
235 aout_FormatNbChannels( &p_aout->output.output ),
236 p_aout->output.output.i_rate, false )
239 msg_Err( p_aout, "cannot open waveout audio device" );
240 free( p_aout->output.p_sys );
244 /* Calculate the frame size in bytes */
245 p_aout->output.i_nb_samples = A52_FRAME_NB;
246 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
247 p_aout->output.output.i_frame_length = A52_FRAME_NB;
248 p_aout->output.p_sys->i_buffer_size =
249 p_aout->output.output.i_bytes_per_frame;
251 aout_VolumeNoneInit( p_aout );
260 p_aout->output.output.i_physical_channels
261 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
262 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
266 p_aout->output.output.i_physical_channels
267 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
268 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
271 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
274 p_aout->output.output.i_physical_channels
275 = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
278 if( OpenWaveOutPCM( p_aout,
279 p_aout->output.p_sys->i_wave_device_id,
280 &p_aout->output.output.i_format,
281 p_aout->output.output.i_physical_channels,
282 aout_FormatNbChannels( &p_aout->output.output ),
283 p_aout->output.output.i_rate, false )
286 msg_Err( p_aout, "cannot open waveout audio device" );
287 free( p_aout->output.p_sys );
291 /* Calculate the frame size in bytes */
292 p_aout->output.i_nb_samples = FRAME_SIZE;
293 aout_FormatPrepare( &p_aout->output.output );
294 p_aout->output.p_sys->i_buffer_size = FRAME_SIZE *
295 p_aout->output.output.i_bytes_per_frame;
297 aout_VolumeSoftInit( p_aout );
299 /* Check for hardware volume support */
300 if( waveOutGetDevCaps( (UINT_PTR)p_aout->output.p_sys->h_waveout,
301 &wocaps, sizeof(wocaps) ) == MMSYSERR_NOERROR &&
302 wocaps.dwSupport & WAVECAPS_VOLUME )
305 if( waveOutGetVolume( p_aout->output.p_sys->h_waveout, &i_dummy )
306 == MMSYSERR_NOERROR )
308 p_aout->output.pf_volume_get = VolumeGet;
309 p_aout->output.pf_volume_set = VolumeSet;
315 waveOutReset( p_aout->output.p_sys->h_waveout );
317 /* Allocate silence buffer */
318 p_aout->output.p_sys->p_silence_buffer =
319 malloc( p_aout->output.p_sys->i_buffer_size );
320 if( p_aout->output.p_sys->p_silence_buffer == NULL )
322 free( p_aout->output.p_sys );
325 p_aout->output.p_sys->i_repeat_counter = 0;
328 /* Zero the buffer. WinCE doesn't have calloc(). */
329 memset( p_aout->output.p_sys->p_silence_buffer, 0,
330 p_aout->output.p_sys->i_buffer_size );
332 /* Now we need to setup our waveOut play notification structure */
333 p_aout->output.p_sys->p_notif =
334 vlc_object_create( p_aout, sizeof(notification_thread_t) );
335 p_aout->output.p_sys->p_notif->p_aout = p_aout;
336 p_aout->output.p_sys->event = CreateEvent( NULL, FALSE, FALSE, NULL );
337 p_aout->output.p_sys->new_buffer_event = CreateEvent( NULL, FALSE, FALSE, NULL );
339 /* define startpoint of playback on first call to play()
340 like alsa does (instead of playing a blank sample) */
341 p_aout->output.p_sys->b_playing = 0;
342 p_aout->output.p_sys->start_date = 0;
345 /* Then launch the notification thread */
346 if( vlc_thread_create( p_aout->output.p_sys->p_notif,
347 "waveOut Notification Thread", WaveOutThread,
348 VLC_THREAD_PRIORITY_OUTPUT ) )
350 msg_Err( p_aout, "cannot create WaveOutThread" );
353 /* We need to kick off the playback in order to have the callback properly
355 for( int i = 0; i < FRAMES_NUM; i++ )
357 p_aout->output.p_sys->waveheader[i].dwFlags = WHDR_DONE;
358 p_aout->output.p_sys->waveheader[i].dwUser = 0;
364 /*****************************************************************************
365 * Probe: probe the audio device for available formats and channels
366 *****************************************************************************/
367 static void Probe( aout_instance_t * p_aout )
369 vlc_value_t val, text;
370 vlc_fourcc_t i_format;
371 unsigned int i_physical_channels;
373 var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
374 text.psz_string = _("Audio Device");
375 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
377 /* Test for 5.1 support */
378 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
379 AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT |
380 AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE;
381 if( p_aout->output.output.i_physical_channels == i_physical_channels )
383 if( OpenWaveOutPCM( p_aout,
384 p_aout->output.p_sys->i_wave_device_id,
386 i_physical_channels, 6,
387 p_aout->output.output.i_rate, true )
390 val.i_int = AOUT_VAR_5_1;
391 text.psz_string = (char *)_("5.1");
392 var_Change( p_aout, "audio-device",
393 VLC_VAR_ADDCHOICE, &val, &text );
394 msg_Dbg( p_aout, "device supports 5.1 channels" );
398 /* Test for 2 Front 2 Rear support */
399 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
400 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
401 if( ( p_aout->output.output.i_physical_channels & i_physical_channels )
402 == i_physical_channels )
404 if( OpenWaveOutPCM( p_aout,
405 p_aout->output.p_sys->i_wave_device_id,
407 i_physical_channels, 4,
408 p_aout->output.output.i_rate, true )
411 val.i_int = AOUT_VAR_2F2R;
412 text.psz_string = (char *)_("2 Front 2 Rear");
413 var_Change( p_aout, "audio-device",
414 VLC_VAR_ADDCHOICE, &val, &text );
415 msg_Dbg( p_aout, "device supports 4 channels" );
419 /* Test for stereo support */
420 i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
421 if( OpenWaveOutPCM( p_aout,
422 p_aout->output.p_sys->i_wave_device_id,
424 i_physical_channels, 2,
425 p_aout->output.output.i_rate, true )
428 val.i_int = AOUT_VAR_STEREO;
429 text.psz_string = (char *)_("Stereo");
430 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
431 msg_Dbg( p_aout, "device supports 2 channels" );
434 /* Test for mono support */
435 i_physical_channels = AOUT_CHAN_CENTER;
436 if( OpenWaveOutPCM( p_aout,
437 p_aout->output.p_sys->i_wave_device_id,
439 i_physical_channels, 1,
440 p_aout->output.output.i_rate, true )
443 val.i_int = AOUT_VAR_MONO;
444 text.psz_string = (char *)_("Mono");
445 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
446 msg_Dbg( p_aout, "device supports 1 channel" );
449 /* Test for SPDIF support */
450 if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
452 if( OpenWaveOut( p_aout,
453 p_aout->output.p_sys->i_wave_device_id,
455 p_aout->output.output.i_physical_channels,
456 aout_FormatNbChannels( &p_aout->output.output ),
457 p_aout->output.output.i_rate, true )
460 msg_Dbg( p_aout, "device supports A/52 over S/PDIF" );
461 val.i_int = AOUT_VAR_SPDIF;
462 text.psz_string = (char *)_("A/52 over S/PDIF");
463 var_Change( p_aout, "audio-device",
464 VLC_VAR_ADDCHOICE, &val, &text );
465 if( var_InheritBool( p_aout, "spdif" ) )
466 var_Set( p_aout, "audio-device", val );
470 var_Change( p_aout, "audio-device", VLC_VAR_CHOICESCOUNT, &val, NULL );
473 /* Probe() has failed. */
474 var_Destroy( p_aout, "audio-device" );
478 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
479 var_SetBool( p_aout, "intf-change", true );
482 /*****************************************************************************
483 * Play: play a sound buffer
484 *****************************************************************************
485 * This doesn't actually play the buffer. This just stores the buffer so it
486 * can be played by the callback thread.
487 *****************************************************************************/
488 static void Play( aout_instance_t *_p_aout )
490 if( !_p_aout->output.p_sys->b_playing )
492 _p_aout->output.p_sys->b_playing = 1;
494 /* get the playing date of the first aout buffer */
495 _p_aout->output.p_sys->start_date =
496 aout_FifoFirstDate( _p_aout, &_p_aout->output.fifo );
498 msg_Dbg( _p_aout, "Wakeup sleeping output thread.");
500 /* wake up the audio output thread */
501 SetEvent( _p_aout->output.p_sys->event );
503 SetEvent( _p_aout->output.p_sys->new_buffer_event );
507 /*****************************************************************************
508 * Close: close the audio device
509 *****************************************************************************/
510 static void Close( vlc_object_t *p_this )
512 aout_instance_t *p_aout = (aout_instance_t *)p_this;
513 aout_sys_t *p_sys = p_aout->output.p_sys;
515 /* Before calling waveOutClose we must reset the device */
516 vlc_object_kill( p_aout );
518 /* wake up the audio thread, to recognize that p_aout died */
519 SetEvent( p_sys->event );
520 SetEvent( p_sys->new_buffer_event );
522 vlc_thread_join( p_sys->p_notif );
523 vlc_object_release( p_sys->p_notif );
526 kill the real output then - when the feed thread
527 is surely terminated!
528 old code could be too early in case that "feeding"
529 was running on termination
531 at this point now its sure, that there will be no new
532 data send to the driver, and we can cancel the last
535 MMRESULT result = waveOutReset( p_sys->h_waveout );
536 if(result != MMSYSERR_NOERROR)
538 msg_Err( p_aout, "waveOutReset failed 0x%x", result );
540 now we must wait, that all buffers are played
541 because cancel doesn't work in this case...
543 if(result == MMSYSERR_NOTSUPPORTED)
546 clear currently played (done) buffers,
547 if returnvalue > 0 (means some buffer still playing)
548 wait for the driver event callback that one buffer
549 is finished with playing, and check again
550 the timeout of 5000ms is just, an emergency exit
551 of this loop, to avoid deadlock in case of other
552 (currently not known bugs, problems, errors cases?)
555 (WaveOutClearDoneBuffers( p_sys ) > 0)
557 (WaitForSingleObject( p_sys->event, 5000) == WAIT_OBJECT_0)
560 msg_Dbg( p_aout, "Wait for waveout device...");
564 WaveOutClearDoneBuffers( p_sys );
567 /* now we can Close the device */
568 if( waveOutClose( p_sys->h_waveout ) != MMSYSERR_NOERROR )
570 msg_Err( p_aout, "waveOutClose failed" );
574 because so long, the waveout device is playing, the callback
575 could occur and need the events
577 CloseHandle( p_sys->event );
578 CloseHandle( p_sys->new_buffer_event);
580 free( p_sys->p_silence_buffer );
584 /*****************************************************************************
585 * OpenWaveOut: open the waveout sound device
586 ****************************************************************************/
587 static int OpenWaveOut( aout_instance_t *p_aout, uint32_t i_device_id, int i_format,
588 int i_channels, int i_nb_channels, int i_rate,
594 /* Set sound format */
596 #define waveformat p_aout->output.p_sys->waveformat
598 waveformat.dwChannelMask = 0;
599 for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ )
601 if( i_channels & pi_channels_src[i] )
602 waveformat.dwChannelMask |= pi_channels_in[i];
607 case VLC_CODEC_SPDIFL:
609 /* To prevent channel re-ordering */
610 waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
611 waveformat.Format.wBitsPerSample = 16;
612 waveformat.Samples.wValidBitsPerSample =
613 waveformat.Format.wBitsPerSample;
614 waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
615 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF;
619 waveformat.Format.wBitsPerSample = sizeof(float) * 8;
620 waveformat.Samples.wValidBitsPerSample =
621 waveformat.Format.wBitsPerSample;
622 waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
623 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
627 waveformat.Format.wBitsPerSample = 16;
628 waveformat.Samples.wValidBitsPerSample =
629 waveformat.Format.wBitsPerSample;
630 waveformat.Format.wFormatTag = WAVE_FORMAT_PCM;
631 waveformat.SubFormat = __KSDATAFORMAT_SUBTYPE_PCM;
635 waveformat.Format.nChannels = i_nb_channels;
636 waveformat.Format.nSamplesPerSec = i_rate;
637 waveformat.Format.nBlockAlign =
638 waveformat.Format.wBitsPerSample / 8 * i_nb_channels;
639 waveformat.Format.nAvgBytesPerSec =
640 waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
642 /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */
643 if( i_nb_channels <= 2 )
645 waveformat.Format.cbSize = 0;
649 waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
650 waveformat.Format.cbSize =
651 sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
655 msg_Dbg( p_aout, "OpenWaveDevice-ID: %u", i_device_id);
656 msg_Dbg( p_aout,"waveformat.Format.cbSize = %d",
657 waveformat.Format.cbSize);
658 msg_Dbg( p_aout,"waveformat.Format.wFormatTag = %u",
659 waveformat.Format.wFormatTag);
660 msg_Dbg( p_aout,"waveformat.Format.nChannels = %u",
661 waveformat.Format.nChannels);
662 msg_Dbg( p_aout,"waveformat.Format.nSamplesPerSec = %d",
663 (int)waveformat.Format.nSamplesPerSec);
664 msg_Dbg( p_aout,"waveformat.Format.nAvgBytesPerSec = %u",
665 (int)waveformat.Format.nAvgBytesPerSec);
666 msg_Dbg( p_aout,"waveformat.Format.nBlockAlign = %d",
667 waveformat.Format.nBlockAlign);
668 msg_Dbg( p_aout,"waveformat.Format.wBitsPerSample = %d",
669 waveformat.Format.wBitsPerSample);
670 msg_Dbg( p_aout,"waveformat.Samples.wValidBitsPerSample = %d",
671 waveformat.Samples.wValidBitsPerSample);
672 msg_Dbg( p_aout,"waveformat.Samples.wSamplesPerBlock = %d",
673 waveformat.Samples.wSamplesPerBlock);
674 msg_Dbg( p_aout,"waveformat.dwChannelMask = %lu",
675 waveformat.dwChannelMask);
678 /* Open the device */
679 result = waveOutOpen( &p_aout->output.p_sys->h_waveout, i_device_id,
680 (WAVEFORMATEX *)&waveformat,
681 (DWORD_PTR)WaveOutCallback, (DWORD_PTR)p_aout,
682 CALLBACK_FUNCTION | (b_probe?WAVE_FORMAT_QUERY:0) );
683 if( result == WAVERR_BADFORMAT )
685 msg_Warn( p_aout, "waveOutOpen failed WAVERR_BADFORMAT" );
688 if( result == MMSYSERR_ALLOCATED )
690 msg_Warn( p_aout, "waveOutOpen failed WAVERR_ALLOCATED" );
693 if( result != MMSYSERR_NOERROR )
695 msg_Warn( p_aout, "waveOutOpen failed" );
699 p_aout->output.p_sys->b_chan_reorder =
700 aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
701 waveformat.dwChannelMask, i_nb_channels,
702 p_aout->output.p_sys->pi_chan_table );
704 if( p_aout->output.p_sys->b_chan_reorder )
706 msg_Dbg( p_aout, "channel reordering needed" );
715 /*****************************************************************************
716 * OpenWaveOutPCM: open a PCM waveout sound device
717 ****************************************************************************/
718 static int OpenWaveOutPCM( aout_instance_t *p_aout, uint32_t i_device_id,
719 vlc_fourcc_t *i_format,
720 int i_channels, int i_nb_channels, int i_rate,
723 bool b_use_float32 = var_CreateGetBool( p_aout, "waveout-float32");
725 if( !b_use_float32 || OpenWaveOut( p_aout, i_device_id, VLC_CODEC_FL32,
726 i_channels, i_nb_channels, i_rate, b_probe )
729 if ( OpenWaveOut( p_aout, i_device_id, VLC_CODEC_S16L,
730 i_channels, i_nb_channels, i_rate, b_probe )
737 *i_format = VLC_CODEC_S16L;
743 *i_format = VLC_CODEC_FL32;
748 /*****************************************************************************
749 * PlayWaveOut: play a buffer through the WaveOut device
750 *****************************************************************************/
751 static int PlayWaveOut( aout_instance_t *p_aout, HWAVEOUT h_waveout,
752 WAVEHDR *p_waveheader, aout_buffer_t *p_buffer,
757 /* Prepare the buffer */
758 if( p_buffer != NULL )
760 p_waveheader->lpData = p_buffer->p_buffer;
762 copy the buffer to the silence buffer :) so in case we don't
763 get the next buffer fast enough (I will repeat this one a time
764 for AC3 / DTS and SPDIF this will sound better instead of
769 vlc_memcpy( p_aout->output.p_sys->p_silence_buffer,
771 p_aout->output.p_sys->i_buffer_size );
772 p_aout->output.p_sys->i_repeat_counter = 2;
775 /* Use silence buffer instead */
776 if(p_aout->output.p_sys->i_repeat_counter)
778 p_aout->output.p_sys->i_repeat_counter--;
779 if(!p_aout->output.p_sys->i_repeat_counter)
781 vlc_memset( p_aout->output.p_sys->p_silence_buffer,
782 0x00, p_aout->output.p_sys->i_buffer_size );
785 p_waveheader->lpData = p_aout->output.p_sys->p_silence_buffer;
788 p_waveheader->dwUser = p_buffer ? (DWORD_PTR)p_buffer : (DWORD_PTR)1;
789 p_waveheader->dwBufferLength = p_aout->output.p_sys->i_buffer_size;
790 p_waveheader->dwFlags = 0;
792 result = waveOutPrepareHeader( h_waveout, p_waveheader, sizeof(WAVEHDR) );
793 if( result != MMSYSERR_NOERROR )
795 msg_Err( p_aout, "waveOutPrepareHeader failed" );
799 /* Send the buffer to the waveOut queue */
800 result = waveOutWrite( h_waveout, p_waveheader, sizeof(WAVEHDR) );
801 if( result != MMSYSERR_NOERROR )
803 msg_Err( p_aout, "waveOutWrite failed" );
810 /*****************************************************************************
811 * WaveOutCallback: what to do once WaveOut has played its sound samples
812 *****************************************************************************/
813 static void CALLBACK WaveOutCallback( HWAVEOUT h_waveout, UINT uMsg,
815 DWORD dwParam1, DWORD dwParam2 )
817 (void)h_waveout; (void)dwParam1; (void)dwParam2;
818 aout_instance_t *p_aout = (aout_instance_t *)_p_aout;
819 int i, i_queued_frames = 0;
821 if( uMsg != WOM_DONE ) return;
823 if( !vlc_object_alive (p_aout) ) return;
825 /* Find out the current latency */
826 for( i = 0; i < FRAMES_NUM; i++ )
828 /* Check if frame buf is available */
829 if( !(p_aout->output.p_sys->waveheader[i].dwFlags & WHDR_DONE) )
835 /* Don't wake up the thread too much */
836 if( i_queued_frames <= FRAMES_NUM/2 )
837 SetEvent( p_aout->output.p_sys->event );
841 /****************************************************************************
842 * WaveOutClearDoneBuffers: Clear all done marked buffers, and free aout_bufer
843 ****************************************************************************
844 * return value is the number of still playing buffers in the queue
845 ****************************************************************************/
846 static int WaveOutClearDoneBuffers(aout_sys_t *p_sys)
848 WAVEHDR *p_waveheader = p_sys->waveheader;
849 int i_queued_frames = 0;
851 for( int i = 0; i < FRAMES_NUM; i++ )
853 if( (p_waveheader[i].dwFlags & WHDR_DONE) &&
854 p_waveheader[i].dwUser )
856 aout_buffer_t *p_buffer =
857 (aout_buffer_t *)(p_waveheader[i].dwUser);
858 /* Unprepare and free the buffers which has just been played */
859 waveOutUnprepareHeader( p_sys->h_waveout, &p_waveheader[i],
862 if( p_waveheader[i].dwUser != 1 )
863 aout_BufferFree( p_buffer );
865 p_waveheader[i].dwUser = 0;
868 /* Check if frame buf is available */
869 if( !(p_waveheader[i].dwFlags & WHDR_DONE) )
874 return i_queued_frames;
877 /*****************************************************************************
878 * WaveOutThread: this thread will capture play notification events.
879 *****************************************************************************
880 * We use this thread to feed new audio samples to the sound card because
881 * we are not authorized to use waveOutWrite() directly in the waveout
883 *****************************************************************************/
884 static void* WaveOutThread( vlc_object_t *p_this )
886 notification_thread_t *p_notif = (notification_thread_t*)p_this;
887 aout_instance_t *p_aout = p_notif->p_aout;
888 aout_sys_t *p_sys = p_aout->output.p_sys;
889 aout_buffer_t *p_buffer = NULL;
890 WAVEHDR *p_waveheader = p_sys->waveheader;
891 int i, i_queued_frames;
894 uint32_t i_buffer_length = 64;
895 int canc = vlc_savecancel ();
897 /* We don't want any resampling when using S/PDIF */
898 b_sleek = p_aout->output.output.i_format == VLC_CODEC_SPDIFL;
900 // wait for first call to "play()"
901 while( !p_sys->start_date && vlc_object_alive (p_aout) )
902 WaitForSingleObject( p_sys->event, INFINITE );
903 if( !vlc_object_alive (p_aout) )
906 msg_Dbg( p_aout, "will start to play in %"PRId64" us",
907 (p_sys->start_date - AOUT_PTS_TOLERANCE/4)-mdate());
909 // than wait a short time... before grabbing first frames
910 mwait( p_sys->start_date - AOUT_PTS_TOLERANCE/4 );
912 #define waveout_warn(msg) msg_Warn( p_aout, "aout_OutputNextBuffer no buffer "\
913 "got next_date=%d ms, "\
914 "%d frames to play, "\
915 "starving? %d, %s",(int)(next_date/(mtime_t)1000), \
917 p_aout->output.b_starving, msg);
920 while( vlc_object_alive (p_aout) )
922 /* Cleanup and find out the current latency */
923 i_queued_frames = WaveOutClearDoneBuffers( p_sys );
925 if( !vlc_object_alive (p_aout) ) return NULL;
927 /* Try to fill in as many frame buffers as possible */
928 for( i = 0; i < FRAMES_NUM; i++ )
930 /* Check if frame buf is available */
931 if( p_waveheader[i].dwFlags & WHDR_DONE )
933 // next_date = mdate() + 1000000 * i_queued_frames /
934 // p_aout->output.output.i_rate * p_aout->output.i_nb_samples;
936 // the realtime has got our back-site:) to come in sync
937 if(next_date < mdate())
941 /* Take into account the latency */
942 p_buffer = aout_OutputNextBuffer( p_aout,
949 msg_Dbg( p_aout, "aout_OutputNextBuffer no buffer "\
950 "got next_date=%d ms, "\
951 "%d frames to play, "\
952 "starving? %d",(int)(next_date/(mtime_t)1000),
954 p_aout->output.b_starving);
956 if(p_aout->output.b_starving)
958 // means we are too early to request a new buffer?
959 waveout_warn("waiting...")
960 next_date = aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
961 mwait( next_date - AOUT_PTS_TOLERANCE/4 );
963 p_buffer = aout_OutputNextBuffer( p_aout,
970 if( !p_buffer && i_queued_frames )
972 /* We aren't late so no need to play a blank sample */
978 mtime_t buffer_length = p_buffer->i_length;
979 next_date = next_date + buffer_length;
980 i_buffer_length = buffer_length/1000;
983 /* Do the channel reordering */
984 if( p_buffer && p_sys->b_chan_reorder )
986 aout_ChannelReorder( p_buffer->p_buffer,
988 p_sys->waveformat.Format.nChannels,
989 p_sys->pi_chan_table,
990 p_sys->waveformat.Format.wBitsPerSample );
993 PlayWaveOut( p_aout, p_sys->h_waveout,
994 &p_waveheader[i], p_buffer, b_sleek );
1000 if( !vlc_object_alive (p_aout) ) return NULL;
1003 deal with the case that the loop didn't fillup the buffer to the
1004 max - instead of waiting that half the buffer is played before
1005 fillup the waveout buffers, wait only for the next sample buffer
1006 to arrive at the play method...
1008 this will also avoid, that the last buffer is play until the
1009 end, and then trying to get more data, so it will also
1010 work - if the next buffer will arrive some ms before the
1011 last buffer is finished.
1013 if(i_queued_frames < FRAMES_NUM)
1014 WaitForSingleObject( p_sys->new_buffer_event, INFINITE );
1016 WaitForSingleObject( p_sys->event, INFINITE );
1021 vlc_restorecancel (canc);
1025 static int VolumeGet( aout_instance_t * p_aout, audio_volume_t * pi_volume )
1027 DWORD i_waveout_vol;
1030 waveOutGetVolume( 0, &i_waveout_vol );
1032 waveOutGetVolume( p_aout->output.p_sys->h_waveout, &i_waveout_vol );
1035 i_waveout_vol &= 0xFFFF;
1036 *pi_volume = p_aout->output.i_volume =
1037 (i_waveout_vol * AOUT_VOLUME_MAX + 0xFFFF /*rounding*/) / 2 / 0xFFFF;
1041 static int VolumeSet( aout_instance_t * p_aout, audio_volume_t i_volume )
1043 unsigned long i_waveout_vol = i_volume * 0xFFFF * 2 / AOUT_VOLUME_MAX;
1044 i_waveout_vol |= (i_waveout_vol << 16);
1047 waveOutSetVolume( 0, i_waveout_vol );
1049 waveOutSetVolume( p_aout->output.p_sys->h_waveout, i_waveout_vol );
1052 p_aout->output.i_volume = i_volume;
1058 reload the configuration drop down list, of the Audio Devices
1060 static int ReloadWaveoutDevices( vlc_object_t *p_this, char const *psz_name,
1061 vlc_value_t newval, vlc_value_t oldval, void *data )
1063 VLC_UNUSED( newval ); VLC_UNUSED( oldval ); VLC_UNUSED( data );
1065 module_config_t *p_item = config_FindConfig( p_this, psz_name );
1066 if( !p_item ) return VLC_SUCCESS;
1068 /* Clear-up the current list */
1069 if( p_item->i_list )
1073 /* Keep the first entry */
1074 for( i = 1; i < p_item->i_list; i++ )
1076 free((char *)(p_item->ppsz_list[i]) );
1077 free((char *)(p_item->ppsz_list_text[i]) );
1079 /* TODO: Remove when no more needed */
1080 p_item->ppsz_list[i] = NULL;
1081 p_item->ppsz_list_text[i] = NULL;
1085 int wave_devices = waveOutGetNumDevs();
1087 p_item->ppsz_list = xrealloc( p_item->ppsz_list,
1088 (wave_devices+2) * sizeof(char *) );
1089 p_item->ppsz_list_text = xrealloc( p_item->ppsz_list_text,
1090 (wave_devices+2) * sizeof(char *) );
1093 char sz_dev_name[MAXPNAMELEN+32];
1095 for(int i=0; i<wave_devices; i++)
1097 if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
1098 == MMSYSERR_NOERROR)
1100 sprintf( sz_dev_name, psz_device_name_fmt, caps.szPname,
1104 p_item->ppsz_list[j] = FromLocaleDup( sz_dev_name );
1105 p_item->ppsz_list_text[j] = FromLocaleDup( sz_dev_name );
1111 p_item->ppsz_list[j] = NULL;
1112 p_item->ppsz_list_text[j] = NULL;
1114 /* Signal change to the interface */
1115 p_item->b_dirty = true;
1121 convert devicename to device ID for output
1122 if device not found return WAVE_MAPPER, so let
1123 windows decide which preferred audio device
1126 static uint32_t findDeviceID(char *psz_device_name)
1128 if(!psz_device_name)
1131 uint32_t wave_devices = waveOutGetNumDevs();
1133 char sz_dev_name[MAXPNAMELEN+32];
1134 for(uint32_t i=0; i<wave_devices; i++)
1136 if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
1137 == MMSYSERR_NOERROR)
1139 sprintf(sz_dev_name, psz_device_name_fmt, caps.szPname,
1143 char *psz_temp = FromLocaleDup(sz_dev_name);
1145 if( !stricmp(psz_temp, psz_device_name) )
1147 LocaleFree( psz_temp );
1150 LocaleFree( psz_temp );