+/****************************************************************************
+ * WaveOutClearDoneBuffers: Clear all done marked buffers, and free aout_bufer
+ ****************************************************************************
+ * return value is the number of still playing buffers in the queue
+ ****************************************************************************/
+static int WaveOutClearDoneBuffers(aout_sys_t *p_sys)
+{
+ WAVEHDR *p_waveheader = p_sys->waveheader;
+ int i_queued_frames = 0;
+
+ for( int i = 0; i < FRAMES_NUM; i++ )
+ {
+ if( (p_waveheader[i].dwFlags & WHDR_DONE) &&
+ p_waveheader[i].dwUser )
+ {
+ aout_buffer_t *p_buffer =
+ (aout_buffer_t *)(p_waveheader[i].dwUser);
+ /* Unprepare and free the buffers which has just been played */
+ waveOutUnprepareHeader( p_sys->h_waveout, &p_waveheader[i],
+ sizeof(WAVEHDR) );
+
+ if( p_waveheader[i].dwUser != 1 )
+ aout_BufferFree( p_buffer );
+
+ p_waveheader[i].dwUser = 0;
+ }
+
+ /* Check if frame buf is available */
+ if( !(p_waveheader[i].dwFlags & WHDR_DONE) )
+ {
+ i_queued_frames++;
+ }
+ }
+ return i_queued_frames;
+}
+
+/*****************************************************************************
+ * WaveOutThread: this thread will capture play notification events.
+ *****************************************************************************
+ * We use this thread to feed new audio samples to the sound card because
+ * we are not authorized to use waveOutWrite() directly in the waveout
+ * callback.
+ *****************************************************************************/
+static void WaveOutThread( notification_thread_t *p_notif )
+{
+ aout_instance_t *p_aout = p_notif->p_aout;
+ aout_sys_t *p_sys = p_aout->output.p_sys;
+ aout_buffer_t *p_buffer = NULL;
+ WAVEHDR *p_waveheader = p_sys->waveheader;
+ int i, i_queued_frames;
+ bool b_sleek;
+ mtime_t next_date;
+ uint32_t i_buffer_length = 64;
+
+ /* We don't want any resampling when using S/PDIF */
+ b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i');
+
+ // wait for first call to "play()"
+ while( !p_sys->start_date && vlc_object_alive (p_aout) )
+ WaitForSingleObject( p_sys->event, INFINITE );
+ if( !vlc_object_alive (p_aout) )
+ return;
+
+ msg_Dbg( p_aout, "will start to play in %"PRId64" us",
+ (p_sys->start_date - AOUT_PTS_TOLERANCE/4)-mdate());
+
+ // than wait a short time... before grabbing first frames
+ mwait( p_sys->start_date - AOUT_PTS_TOLERANCE/4 );
+
+#define waveout_warn(msg) msg_Warn( p_aout, "aout_OutputNextBuffer no buffer "\
+ "got next_date=%d ms, "\
+ "%d frames to play, "\
+ "starving? %d, %s",(int)(next_date/(mtime_t)1000), \
+ i_queued_frames, \
+ p_aout->output.b_starving, msg);
+ next_date = mdate();
+
+ while( vlc_object_alive (p_aout) )
+ {
+ /* Cleanup and find out the current latency */
+ i_queued_frames = WaveOutClearDoneBuffers( p_sys );
+
+ if( !vlc_object_alive (p_aout) ) return;
+
+ /* Try to fill in as many frame buffers as possible */
+ for( i = 0; i < FRAMES_NUM; i++ )
+ {
+ /* Check if frame buf is available */
+ if( p_waveheader[i].dwFlags & WHDR_DONE )
+ {
+ // next_date = mdate() + 1000000 * i_queued_frames /
+ // p_aout->output.output.i_rate * p_aout->output.i_nb_samples;
+
+ // the realtime has got our back-site:) to come in sync
+ if(next_date < mdate())
+ next_date = mdate();
+
+
+ /* Take into account the latency */
+ p_buffer = aout_OutputNextBuffer( p_aout,
+ next_date,
+ b_sleek );
+
+ if(!p_buffer)
+ {
+#if 0
+ msg_Dbg( p_aout, "aout_OutputNextBuffer no buffer "\
+ "got next_date=%d ms, "\
+ "%d frames to play, "\
+ "starving? %d",(int)(next_date/(mtime_t)1000),
+ i_queued_frames,
+ p_aout->output.b_starving);
+#endif
+ if(p_aout->output.b_starving)
+ {
+ // means we are too early to request a new buffer?
+ waveout_warn("waiting...")
+ next_date = aout_FifoFirstDate( p_aout, &p_aout->output.fifo );
+ mwait( next_date - AOUT_PTS_TOLERANCE/4 );
+ next_date = mdate();
+ p_buffer = aout_OutputNextBuffer( p_aout,
+ next_date,
+ b_sleek
+ );
+ }
+ }
+
+ if( !p_buffer && i_queued_frames )
+ {
+ /* We aren't late so no need to play a blank sample */
+ break;
+ }
+
+ if( p_buffer )
+ {
+ mtime_t buffer_length = (p_buffer->end_date
+ - p_buffer->start_date);
+ next_date = next_date + buffer_length;
+ i_buffer_length = buffer_length/1000;
+ }
+
+ /* Do the channel reordering */
+ if( p_buffer && p_sys->b_chan_reorder )
+ {
+ aout_ChannelReorder( p_buffer->p_buffer,
+ p_buffer->i_nb_bytes,
+ p_sys->waveformat.Format.nChannels,
+ p_sys->pi_chan_table,
+ p_sys->waveformat.Format.wBitsPerSample );
+ }
+
+ PlayWaveOut( p_aout, p_sys->h_waveout,
+ &p_waveheader[i], p_buffer, b_sleek );
+
+ i_queued_frames++;
+ }
+ }
+
+ if( !vlc_object_alive (p_aout) ) return;
+
+ /*
+ deal with the case that the loop didn't fillup the buffer to the
+ max - instead of waiting that half the buffer is played before
+ fillup the waveout buffers, wait only for the next sample buffer
+ to arrive at the play method...
+
+ this will also avoid, that the last buffer is play until the
+ end, and then trying to get more data, so it will also
+ work - if the next buffer will arrive some ms before the
+ last buffer is finished.
+ */
+ if(i_queued_frames < FRAMES_NUM)
+ WaitForSingleObject( p_sys->new_buffer_event, INFINITE );
+ else
+ WaitForSingleObject( p_sys->event, INFINITE );
+
+ }
+
+#undef waveout_warn
+}
+
+static int VolumeInfos( aout_instance_t * p_aout, audio_volume_t * pi_soft )
+{
+ *pi_soft = AOUT_VOLUME_MAX / 2;
+ return 0;
+}
+
+static int VolumeGet( aout_instance_t * p_aout, audio_volume_t * pi_volume )
+{
+ DWORD i_waveout_vol;
+
+#ifdef UNDER_CE
+ waveOutGetVolume( 0, &i_waveout_vol );
+#else
+ waveOutGetVolume( p_aout->output.p_sys->h_waveout, &i_waveout_vol );
+#endif
+
+ i_waveout_vol &= 0xFFFF;
+ *pi_volume = p_aout->output.i_volume =
+ (i_waveout_vol * AOUT_VOLUME_MAX + 0xFFFF /*rounding*/) / 2 / 0xFFFF;
+ return 0;
+}
+
+static int VolumeSet( aout_instance_t * p_aout, audio_volume_t i_volume )
+{
+ unsigned long i_waveout_vol = i_volume * 0xFFFF * 2 / AOUT_VOLUME_MAX;
+ i_waveout_vol |= (i_waveout_vol << 16);
+
+#ifdef UNDER_CE
+ waveOutSetVolume( 0, i_waveout_vol );
+#else
+ waveOutSetVolume( p_aout->output.p_sys->h_waveout, i_waveout_vol );
+#endif
+
+ p_aout->output.i_volume = i_volume;
+ return 0;
+}
+
+
+/*
+ reload the configuration drop down list, of the Audio Devices
+*/
+static int ReloadWaveoutDevices( vlc_object_t *p_this, char const *psz_name,
+ vlc_value_t newval, vlc_value_t oldval, void *data )
+{
+ int i;
+
+ module_config_t *p_item = config_FindConfig( p_this, psz_name );
+ if( !p_item ) return VLC_SUCCESS;
+
+ /* Clear-up the current list */
+ if( p_item->i_list )
+ {
+ /* Keep the first entry */
+ for( i = 1; i < p_item->i_list; i++ )
+ {
+ free((char *)(p_item->ppsz_list[i]) );
+ free((char *)(p_item->ppsz_list_text[i]) );
+ }
+ /* TODO: Remove when no more needed */
+ p_item->ppsz_list[i] = NULL;
+ p_item->ppsz_list_text[i] = NULL;
+ }
+ p_item->i_list = 1;
+
+ int wave_devices = waveOutGetNumDevs();
+
+ p_item->ppsz_list =
+ (char **)realloc( p_item->ppsz_list,
+ (wave_devices+2) * sizeof(char *) );
+ p_item->ppsz_list_text =
+ (char **)realloc( p_item->ppsz_list_text,
+ (wave_devices+2) * sizeof(char *) );
+
+ WAVEOUTCAPS caps;
+ char sz_dev_name[MAXPNAMELEN+32];
+ int j=1;
+ for(int i=0; i<wave_devices; i++)
+ {
+ if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
+ == MMSYSERR_NOERROR)
+ {
+ sprintf( sz_dev_name, psz_device_name_fmt, caps.szPname,
+ caps.wMid,
+ caps.wPid
+ );
+ p_item->ppsz_list[j] = FromLocaleDup( sz_dev_name );
+ p_item->ppsz_list_text[j] = FromLocaleDup( sz_dev_name );
+ p_item->i_list++;
+ j++;
+ }
+
+ }
+ p_item->ppsz_list[j] = NULL;
+ p_item->ppsz_list_text[j] = NULL;
+
+ /* Signal change to the interface */
+ p_item->b_dirty = true;
+
+ return VLC_SUCCESS;
+}
+
+/*
+ convert devicename to device ID for output
+ if device not found return WAVE_MAPPER, so let
+ windows decide which preferred audio device
+ should be used.
+*/
+static uint32_t findDeviceID(char *psz_device_name)
+{
+ if(!psz_device_name)
+ return WAVE_MAPPER;
+
+ uint32_t wave_devices = waveOutGetNumDevs();
+ WAVEOUTCAPS caps;
+ char sz_dev_name[MAXPNAMELEN+32];
+ for(uint32_t i=0; i<wave_devices; i++)
+ {
+ if(waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS))
+ == MMSYSERR_NOERROR)
+ {
+ sprintf(sz_dev_name, psz_device_name_fmt, caps.szPname,
+ caps.wMid,
+ caps.wPid
+ );
+ char *psz_temp = FromLocaleDup(sz_dev_name);
+
+ if( !stricmp(psz_temp, psz_device_name) )
+ {
+ LocaleFree( psz_temp );
+ return i;
+ }
+ LocaleFree( psz_temp );
+ }
+ }