1 /*****************************************************************************
2 * aout_directx.c: Windows DirectX audio output method
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: aout_directx.c,v 1.19 2002/02/24 20:51:09 gbazin Exp $
7 * Authors: Gildas Bazin <gbazin@netcourrier.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 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 General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <errno.h> /* ENOMEM */
28 #include <fcntl.h> /* open(), O_WRONLY */
29 #include <string.h> /* strerror() */
31 #include <stdio.h> /* "intf_msg.h" */
32 #include <stdlib.h> /* calloc(), malloc(), free() */
34 #include <videolan/vlc.h>
39 #include "audio_output.h" /* aout_thread_t */
41 /*****************************************************************************
43 * Defining them here allows us to get rid of the dxguid library during
45 *****************************************************************************/
47 DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
49 /*****************************************************************************
50 * aout_sys_t: directx audio output method descriptor
51 *****************************************************************************
52 * This structure is part of the audio output thread descriptor.
53 * It describes the direct sound specific properties of an audio device.
54 *****************************************************************************/
56 typedef struct aout_sys_s
58 LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
60 LPDIRECTSOUNDBUFFER p_dsbuffer_primary; /* the actual sound card buffer
61 (not used directly) */
63 LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
64 * takes care of mixing all the
65 * secondary buffers into the primary) */
67 LPDIRECTSOUNDNOTIFY p_dsnotify; /* the position notify interface */
69 HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
71 long l_buffer_size; /* secondary sound buffer size */
72 long l_write_position; /* next write position for the buffer */
74 volatile boolean_t b_buffer_underflown; /* buffer underflow detection */
75 volatile long l_data_played_from_beginning; /* for underflow detection */
76 volatile long l_data_written_from_beginning; /* for underflow detection */
78 vlc_mutex_t buffer_lock; /* audio buffer lock */
80 vlc_thread_t notification_thread_id; /* DirectSoundThread id */
82 DSBPOSITIONNOTIFY notification_events[2]; /* play notification events */
84 boolean_t b_notification_thread_die; /* flag to kill the thread */
88 /*****************************************************************************
90 *****************************************************************************/
91 static int aout_Open ( aout_thread_t *p_aout );
92 static int aout_SetFormat ( aout_thread_t *p_aout );
93 static long aout_GetBufInfo ( aout_thread_t *p_aout, long l_buffer_info );
94 static void aout_Play ( aout_thread_t *p_aout,
95 byte_t *buffer, int i_size );
96 static void aout_Close ( aout_thread_t *p_aout );
99 static int DirectxCreateSecondaryBuffer ( aout_thread_t *p_aout );
100 static void DirectxDestroySecondaryBuffer( aout_thread_t *p_aout );
101 static int DirectxInitDSound ( aout_thread_t *p_aout );
102 static void DirectSoundThread ( aout_thread_t *p_aout );
104 /*****************************************************************************
105 * Functions exported as capabilities. They are declared as static so that
106 * we don't pollute the namespace too much.
107 *****************************************************************************/
108 void _M( aout_getfunctions )( function_list_t * p_function_list )
110 p_function_list->functions.aout.pf_open = aout_Open;
111 p_function_list->functions.aout.pf_setformat = aout_SetFormat;
112 p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
113 p_function_list->functions.aout.pf_play = aout_Play;
114 p_function_list->functions.aout.pf_close = aout_Close;
117 /*****************************************************************************
118 * aout_Open: open the audio device
119 *****************************************************************************
120 * This function opens and setups Direct Sound.
121 *****************************************************************************/
122 static int aout_Open( aout_thread_t *p_aout )
125 DSBUFFERDESC dsbuffer_desc;
127 intf_WarnMsg( 3, "aout: DirectX aout_Open ");
129 /* Allocate structure */
130 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
132 if( p_aout->p_sys == NULL )
134 intf_ErrMsg( "aout error: %s", strerror(ENOMEM) );
138 /* Initialize some variables */
139 p_aout->p_sys->p_dsobject = NULL;
140 p_aout->p_sys->p_dsbuffer_primary = NULL;
141 p_aout->p_sys->p_dsbuffer = NULL;
142 p_aout->p_sys->p_dsnotify = NULL;
143 p_aout->p_sys->b_notification_thread_die = 0;
144 p_aout->p_sys->l_data_written_from_beginning = 0;
145 p_aout->p_sys->l_data_played_from_beginning = 0;
146 vlc_mutex_init( &p_aout->p_sys->buffer_lock );
149 /* Initialise DirectSound */
150 if( DirectxInitDSound( p_aout ) )
152 intf_WarnMsg( 3, "aout: can't initialise DirectSound ");
156 /* Obtain (not create) Direct Sound primary buffer */
157 memset( &dsbuffer_desc, 0, sizeof(DSBUFFERDESC) );
158 dsbuffer_desc.dwSize = sizeof(DSBUFFERDESC);
159 dsbuffer_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
160 intf_WarnMsg( 3, "aout: Create direct sound primary buffer ");
161 dsresult = IDirectSound_CreateSoundBuffer(p_aout->p_sys->p_dsobject,
163 &p_aout->p_sys->p_dsbuffer_primary,
165 if( dsresult != DS_OK )
167 intf_WarnMsg( 3, "aout: can't create direct sound primary buffer ");
168 IDirectSound_Release( p_aout->p_sys->p_dsobject );
169 p_aout->p_sys->p_dsobject = NULL;
170 p_aout->p_sys->p_dsbuffer_primary = NULL;
175 /* Now we need to setup DirectSound play notification */
177 /* first we need to create the notification events */
178 p_aout->p_sys->notification_events[0].hEventNotify =
179 CreateEvent( NULL, FALSE, FALSE, NULL );
180 p_aout->p_sys->notification_events[1].hEventNotify =
181 CreateEvent( NULL, FALSE, FALSE, NULL );
183 /* then launch the notification thread */
184 intf_WarnMsg( 3, "aout: aout_Open creating DirectSoundThread" );
185 if( vlc_thread_create( &p_aout->p_sys->notification_thread_id,
186 "DirectSound Notification Thread",
187 (void *) DirectSoundThread, (void *) p_aout) )
189 intf_ErrMsg( "aout error: can't create DirectSoundThread" );
190 intf_ErrMsg("aout error: %s", strerror(ENOMEM));
191 /* Let's go on anyway */
197 /*****************************************************************************
198 * aout_SetFormat: reset the audio device and sets its format
199 *****************************************************************************
200 * This functions set a new audio format.
201 * For this we need to close the current secondary buffer and create another
202 * one with the desired format.
203 *****************************************************************************/
204 static int aout_SetFormat( aout_thread_t *p_aout )
207 WAVEFORMATEX *p_waveformat;
208 unsigned long i_size_struct;
210 intf_WarnMsg( 3, "aout: DirectX aout_SetFormat ");
212 /* Set the format of Direct Sound primary buffer */
214 /* first we need to know the current format */
215 dsresult = IDirectSoundBuffer_GetFormat( p_aout->p_sys->p_dsbuffer_primary,
216 NULL, 0, &i_size_struct );
217 if( dsresult == DS_OK )
219 p_waveformat = malloc( i_size_struct );
220 dsresult = IDirectSoundBuffer_GetFormat(
221 p_aout->p_sys->p_dsbuffer_primary,
222 p_waveformat, i_size_struct,
226 if( dsresult == DS_OK )
228 /* Here we'll change the format */
229 p_waveformat->nChannels = 2;
230 p_waveformat->nSamplesPerSec = (p_aout->l_rate < 44100) ? 44100
232 p_waveformat->wBitsPerSample = 16;
233 p_waveformat->nBlockAlign = p_waveformat->wBitsPerSample / 8 *
234 p_waveformat->nChannels;
235 p_waveformat->nAvgBytesPerSec = p_waveformat->nSamplesPerSec *
236 p_waveformat->nBlockAlign;
238 dsresult = IDirectSoundBuffer_SetFormat(
239 p_aout->p_sys->p_dsbuffer_primary,
242 else intf_WarnMsg( 3, "aout: can't get primary buffer format" );
244 if( dsresult != DS_OK )
245 intf_WarnMsg( 3, "aout: can't set primary buffer format" );
248 /* Now we need to take care of Direct Sound secondary buffer */
250 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
252 /* first release the current secondary buffer */
253 DirectxDestroySecondaryBuffer( p_aout );
255 /* then create a new secondary buffer */
256 if( DirectxCreateSecondaryBuffer( p_aout ) )
258 intf_WarnMsg( 3, "aout: DirectX aout_SetFormat cannot create buffer");
259 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
263 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
268 /*****************************************************************************
269 * aout_GetBufInfo: buffer status query
270 *****************************************************************************
271 * returns the number of bytes in the audio buffer that have not yet been
272 * sent to the sound device.
273 *****************************************************************************/
274 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
276 long l_play_position, l_notused, l_result;
279 if( p_aout->p_sys->b_buffer_underflown )
281 intf_WarnMsg( 3, "aout: DirectX aout_GetBufInfo underflow");
282 return( l_buffer_limit );
285 dsresult = IDirectSoundBuffer_GetCurrentPosition(p_aout->p_sys->p_dsbuffer,
286 &l_play_position, &l_notused);
287 if( dsresult != DS_OK )
289 intf_WarnMsg(3,"aout: DirectX aout_GetBufInfo cannot get current pos");
290 return( l_buffer_limit );
293 l_result = (p_aout->p_sys->l_write_position >= l_play_position) ?
294 (p_aout->p_sys->l_write_position - l_play_position)
295 : (p_aout->p_sys->l_buffer_size - l_play_position
296 + p_aout->p_sys->l_write_position);
299 intf_WarnMsg( 3, "aout: DirectX aout_GetBufInfo: %li", l_result);
304 /*****************************************************************************
305 * aout_Play: play a sound buffer
306 *****************************************************************************
307 * This function writes a buffer of i_length bytes
308 * Don't forget that DirectSound buffers are circular buffers.
309 *****************************************************************************/
310 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
312 VOID *p_write_position, *p_start_buffer;
313 long l_bytes1, l_bytes2, l_play_position;
316 /* protect buffer access (because of DirectSoundThread) */
317 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
319 if( p_aout->p_sys->b_buffer_underflown )
321 /* there has been an underflow so we need to play the new sample
322 * as soon as possible. This is why we query the play position */
323 dsresult = IDirectSoundBuffer_GetCurrentPosition(
324 p_aout->p_sys->p_dsbuffer,
326 &p_aout->p_sys->l_write_position );
327 if( dsresult != DS_OK )
329 intf_WarnMsg( 3, "aout: aout_Play can'get buffer position");
330 p_aout->p_sys->l_write_position = 0;
333 intf_WarnMsg( 3, "aout: aout_Play underflow");
334 /* reinitialise the underflow detection counters */
335 p_aout->p_sys->b_buffer_underflown = 0;
336 p_aout->p_sys->l_data_written_from_beginning = 0;
338 #define WRITE_P p_aout->p_sys->l_write_position
339 #define PLAY_P l_play_position
340 #define BUF_SIZE p_aout->p_sys->l_buffer_size
341 p_aout->p_sys->l_data_played_from_beginning = -(WRITE_P %(BUF_SIZE/2));
342 if( PLAY_P < BUF_SIZE/2 && WRITE_P > BUF_SIZE/2 )
344 p_aout->p_sys->l_data_played_from_beginning -= (BUF_SIZE/2);
346 if( PLAY_P > BUF_SIZE/2 && WRITE_P < BUF_SIZE/2 )
348 p_aout->p_sys->l_data_played_from_beginning -= (BUF_SIZE/2);
355 /* Before copying anything, we have to lock the buffer */
356 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
357 p_aout->p_sys->l_write_position, /* Offset of lock start */
358 i_size, /* Number of bytes to lock */
359 &p_write_position, /* Address of lock start */
360 &l_bytes1, /* Count of bytes locked before wrap around */
361 &p_start_buffer, /* Buffer adress (if wrap around) */
362 &l_bytes2, /* Count of bytes after wrap around */
364 if( dsresult == DSERR_BUFFERLOST )
366 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
367 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
368 p_aout->p_sys->l_write_position,
377 if( dsresult != DS_OK )
379 intf_WarnMsg( 3, "aout: DirectX aout_Play can't lock buffer");
380 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
384 /* Now do the actual memcpy (two memcpy because the buffer is circular) */
385 memcpy( p_write_position, buffer, l_bytes1 );
386 if( p_start_buffer != NULL )
388 memcpy( p_start_buffer, buffer + l_bytes1, l_bytes2 );
391 /* Now the data has been copied, unlock the buffer */
392 IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer,
393 p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
395 /* Update the write position index of the buffer*/
396 p_aout->p_sys->l_write_position += i_size;
397 p_aout->p_sys->l_write_position %= p_aout->p_sys->l_buffer_size;
398 p_aout->p_sys->l_data_written_from_beginning += i_size;
400 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
402 /* The play function has no effect if the buffer is already playing */
403 dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
406 DSBPLAY_LOOPING ); /* Flags */
407 if( dsresult == DSERR_BUFFERLOST )
409 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
410 dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
413 DSBPLAY_LOOPING ); /* Flags */
415 if( dsresult != DS_OK )
417 intf_WarnMsg( 3, "aout: DirectX aout_Play can't play buffer");
423 /*****************************************************************************
424 * aout_Close: close the audio device
425 *****************************************************************************/
426 static void aout_Close( aout_thread_t *p_aout )
429 intf_WarnMsg( 3, "aout: DirectX aout_Close ");
431 /* kill the position notification thread */
432 p_aout->p_sys->b_notification_thread_die = 1;
433 SetEvent( p_aout->p_sys->notification_events[0].hEventNotify );
434 vlc_thread_join( p_aout->p_sys->notification_thread_id );
435 vlc_mutex_destroy( &p_aout->p_sys->buffer_lock );
437 /* release the secondary buffer */
438 DirectxDestroySecondaryBuffer( p_aout );
440 /* then release the primary buffer */
441 if( p_aout->p_sys->p_dsbuffer_primary != NULL )
443 IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer_primary );
444 p_aout->p_sys->p_dsbuffer_primary = NULL;
447 /* finally release the DirectSound object */
448 if( p_aout->p_sys->p_dsobject != NULL )
450 IDirectSound_Release( p_aout->p_sys->p_dsobject );
451 p_aout->p_sys->p_dsobject = NULL;
454 /* free DSOUND.DLL */
455 if( p_aout->p_sys->hdsound_dll != NULL )
457 FreeLibrary( p_aout->p_sys->hdsound_dll );
458 p_aout->p_sys->hdsound_dll = NULL;
461 /* Close the Output. */
462 if ( p_aout->p_sys != NULL )
464 free( p_aout->p_sys );
465 p_aout->p_sys = NULL;
469 /*****************************************************************************
471 *****************************************************************************
472 *****************************************************************************/
473 static int DirectxInitDSound( aout_thread_t *p_aout )
475 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
477 p_aout->p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
478 if( p_aout->p_sys->hdsound_dll == NULL )
480 intf_WarnMsg( 3, "aout: can't open DSOUND.DLL ");
484 OurDirectSoundCreate = (void *)GetProcAddress( p_aout->p_sys->hdsound_dll,
485 "DirectSoundCreate" );
487 if( OurDirectSoundCreate == NULL )
489 intf_WarnMsg( 3, "aout: GetProcAddress FAILED ");
490 FreeLibrary( p_aout->p_sys->hdsound_dll );
491 p_aout->p_sys->hdsound_dll = NULL;
495 /* Create the direct sound object */
496 if( OurDirectSoundCreate(NULL, &p_aout->p_sys->p_dsobject, NULL) != DS_OK )
498 intf_WarnMsg( 3, "aout: can't create a direct sound device ");
499 p_aout->p_sys->p_dsobject = NULL;
500 FreeLibrary( p_aout->p_sys->hdsound_dll );
501 p_aout->p_sys->hdsound_dll = NULL;
505 /* Set DirectSound Cooperative level, ie what control we want over Windows
506 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
507 * settings of the primary buffer, but also that only the sound of our
508 * application will be hearable when it will have the focus.
509 * !!! (this is not really working as intended yet because to set the
510 * cooperative level you need the window handle of your application, and
511 * I don't know of any easy way to get it. Especially since we might play
512 * sound without any video, and so what window handle should we use ???
513 * The hack for now is to use the Desktop window handle - it seems to be
515 if( IDirectSound_SetCooperativeLevel(p_aout->p_sys->p_dsobject,
519 intf_WarnMsg( 3, "aout: can't set direct sound cooperative level ");
525 /*****************************************************************************
526 * DirectxCreateSecondaryBuffer
527 *****************************************************************************
528 * This function creates the buffer we'll use to play audio.
529 * In DirectSound there are two kinds of buffers:
530 * - the primary buffer: which is the actual buffer that the soundcard plays
531 * - the secondary buffer(s): these buffers are the one actually used by
532 * applications and DirectSound takes care of mixing them into the primary.
534 * Once you create a secondary buffer, you cannot change its format anymore so
535 * you have to release the current and create another one.
536 *****************************************************************************/
537 static int DirectxCreateSecondaryBuffer( aout_thread_t *p_aout )
539 WAVEFORMATEX waveformat;
540 DSBUFFERDESC dsbdesc;
543 /* First set the buffer format */
544 memset(&waveformat, 0, sizeof(WAVEFORMATEX));
545 waveformat.wFormatTag = WAVE_FORMAT_PCM;
546 waveformat.nChannels = p_aout->i_channels;
547 waveformat.nSamplesPerSec = p_aout->l_rate;
548 waveformat.wBitsPerSample = 16;
549 waveformat.nBlockAlign = waveformat.wBitsPerSample / 8 *
550 waveformat.nChannels;
551 waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec *
552 waveformat.nBlockAlign;
554 /* Then fill in the descriptor */
555 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
556 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
557 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
558 | DSBCAPS_CTRLPOSITIONNOTIFY /* We need notification */
559 | DSBCAPS_GLOBALFOCUS; /* Allows background playing */
560 dsbdesc.dwBufferBytes = waveformat.nAvgBytesPerSec * 2; /* 2 sec buffer */
561 dsbdesc.lpwfxFormat = &waveformat;
563 if( IDirectSound_CreateSoundBuffer( p_aout->p_sys->p_dsobject,
565 &p_aout->p_sys->p_dsbuffer,
568 intf_WarnMsg( 3, "aout: can't create direct sound secondary buffer ");
569 p_aout->p_sys->p_dsbuffer = NULL;
573 /* backup the size of the secondary sound buffer */
574 memset(&dsbcaps, 0, sizeof(DSBCAPS));
575 dsbcaps.dwSize = sizeof(DSBCAPS);
576 IDirectSoundBuffer_GetCaps( p_aout->p_sys->p_dsbuffer, &dsbcaps );
577 p_aout->p_sys->l_buffer_size = dsbcaps.dwBufferBytes;
578 p_aout->p_sys->l_write_position = 0;
580 intf_WarnMsg( 3, "aout: DirectX DirectxCreateSecondaryBuffer: %li",
581 p_aout->p_sys->l_buffer_size);
583 /* Now the secondary buffer is created, we need to setup its position
585 p_aout->p_sys->notification_events[0].dwOffset = 0; /* notif position */
586 p_aout->p_sys->notification_events[1].dwOffset = dsbcaps.dwBufferBytes / 2;
588 /* Get the IDirectSoundNotify interface */
589 if FAILED( IDirectSoundBuffer_QueryInterface( p_aout->p_sys->p_dsbuffer,
590 &IID_IDirectSoundNotify,
591 (LPVOID *)&p_aout->p_sys->p_dsnotify ) )
593 intf_WarnMsg( 3, "aout: DirectX can't get Notify interface" );
595 p_aout->p_sys->p_dsnotify = NULL;
599 if FAILED( IDirectSoundNotify_SetNotificationPositions(
600 p_aout->p_sys->p_dsnotify,
602 p_aout->p_sys->notification_events ) )
604 intf_WarnMsg( 3, "aout: DirectX can't set position Notification" );
611 /*****************************************************************************
612 * DirectxCreateSecondaryBuffer
613 *****************************************************************************
614 * This function destroy the secondary buffer.
615 *****************************************************************************/
616 static void DirectxDestroySecondaryBuffer( aout_thread_t *p_aout )
618 /* make sure the buffer isn't playing */
619 if( p_aout->p_sys->p_dsbuffer != NULL )
621 IDirectSoundBuffer_Stop( p_aout->p_sys->p_dsbuffer );
624 if( p_aout->p_sys->p_dsnotify != NULL )
626 IDirectSoundNotify_Release( p_aout->p_sys->p_dsnotify );
627 p_aout->p_sys->p_dsnotify = NULL;
630 if( p_aout->p_sys->p_dsbuffer != NULL )
632 IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer );
633 p_aout->p_sys->p_dsbuffer = NULL;
637 /*****************************************************************************
638 * DirectSoundThread: this thread will capture play notification events.
639 *****************************************************************************
640 * As Direct Sound uses circular buffers, we need to use event notification to
642 * Using event notification implies blocking the thread until the event is
643 * signaled so we really need to run this in a separate thread.
644 *****************************************************************************/
645 static void DirectSoundThread( aout_thread_t *p_aout )
647 HANDLE notification_events[2];
648 VOID *p_write_position, *p_start_buffer;
649 long l_bytes1, l_bytes2;
651 long l_buffer_size, l_play_position, l_data_in_buffer;
653 notification_events[0]=p_aout->p_sys->notification_events[0].hEventNotify;
654 notification_events[1]=p_aout->p_sys->notification_events[1].hEventNotify;
656 /* this thread must be high-priority */
657 if( !SetThreadPriority( GetCurrentThread(),
658 THREAD_PRIORITY_ABOVE_NORMAL ) )
660 intf_WarnMsg( 3, "aout: DirectSoundThread could not renice itself" );
663 intf_WarnMsg( 3, "aout: DirectSoundThread ready" );
665 while( !p_aout->p_sys->b_notification_thread_die )
667 /* wait for the position notification */
668 l_play_position = WaitForMultipleObjects( 2, notification_events,
670 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
672 if( p_aout->p_sys->b_notification_thread_die )
677 /* check for buffer underflow (bodge for wrap around) */
678 l_buffer_size = p_aout->p_sys->l_buffer_size;
679 l_play_position = (l_play_position - WAIT_OBJECT_0) * l_buffer_size/2;
680 p_aout->p_sys->l_data_played_from_beginning += (l_buffer_size/2);
681 l_data_in_buffer = p_aout->p_sys->l_data_written_from_beginning -
682 p_aout->p_sys->l_data_played_from_beginning;
684 /* detect wrap-around */
685 if( l_data_in_buffer < (-l_buffer_size/2) )
687 intf_WarnMsg(3,"aout: DirectSoundThread wrap around: %li", l_data_in_buffer);
688 l_data_in_buffer += l_buffer_size;
691 /* detect underflow */
692 if( l_data_in_buffer <= 0 )
694 intf_WarnMsg(3,"aout: DirectSoundThread underflow: %li", l_data_in_buffer);
695 p_aout->p_sys->b_buffer_underflown = 1;
696 p_aout->p_sys->l_write_position =
697 (l_play_position + l_buffer_size/2) % l_buffer_size;
698 l_data_in_buffer = l_buffer_size / 2;
699 p_aout->p_sys->l_data_played_from_beginning -= (l_buffer_size/2);
703 /* Clear the data which has already been played */
705 /* Before copying anything, we have to lock the buffer */
706 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
707 p_aout->p_sys->l_write_position, /* Offset of lock start */
708 l_buffer_size - l_data_in_buffer, /* Number of bytes */
709 &p_write_position, /* Address of lock start */
710 &l_bytes1, /* Count of bytes locked before wrap around */
711 &p_start_buffer, /* Buffer adress (if wrap around) */
712 &l_bytes2, /* Count of bytes after wrap around */
714 if( dsresult == DSERR_BUFFERLOST )
716 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
717 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
718 p_aout->p_sys->l_write_position,
719 l_buffer_size - l_data_in_buffer,
726 if( dsresult != DS_OK )
728 intf_WarnMsg( 3, "aout: DirectX aout_Play can't lock buffer");
729 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
733 /* Now do the actual memcpy (two because the buffer is circular) */
734 memset( p_write_position, 0, l_bytes1 );
735 if( p_start_buffer != NULL )
737 memset( p_start_buffer, 0, l_bytes2 );
740 /* Now the data has been copied, unlock the buffer */
741 IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer,
742 p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
744 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
748 /* free the events */
749 CloseHandle( notification_events[0] );
750 CloseHandle( notification_events[1] );
752 intf_WarnMsg( 3, "aout: DirectSoundThread exiting" );