1 /*****************************************************************************
2 * aout_directx.c: Windows DirectX audio output method
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: aout_directx.c,v 1.22 2002/06/01 12:31:58 sam 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 <stdlib.h> /* calloc(), malloc(), free() */
39 /*****************************************************************************
41 * Defining them here allows us to get rid of the dxguid library during
43 *****************************************************************************/
45 DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
47 /*****************************************************************************
48 * aout_sys_t: directx audio output method descriptor
49 *****************************************************************************
50 * This structure is part of the audio output thread descriptor.
51 * It describes the direct sound specific properties of an audio device.
52 *****************************************************************************/
56 LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
58 LPDIRECTSOUNDBUFFER p_dsbuffer_primary; /* the actual sound card buffer
59 (not used directly) */
61 LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
62 * takes care of mixing all the
63 * secondary buffers into the primary) */
65 LPDIRECTSOUNDNOTIFY p_dsnotify; /* the position notify interface */
67 HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
69 long l_buffer_size; /* secondary sound buffer size */
70 long l_write_position; /* next write position for the buffer */
72 volatile vlc_bool_t b_buffer_underflown; /* buffer underflow detection */
73 volatile long l_data_played_from_beginning; /* for underflow detection */
74 volatile long l_data_written_from_beginning; /* for underflow detection */
76 vlc_mutex_t buffer_lock; /* audio buffer lock */
78 vlc_thread_t notification_thread_id; /* DirectSoundThread id */
80 DSBPOSITIONNOTIFY notification_events[2]; /* play notification events */
82 vlc_bool_t b_notification_thread_die; /* flag to kill the thread */
85 /*****************************************************************************
87 *****************************************************************************/
88 static int aout_Open ( aout_thread_t *p_aout );
89 static int aout_SetFormat ( aout_thread_t *p_aout );
90 static int aout_GetBufInfo ( aout_thread_t *p_aout, int i_buffer_info );
91 static void aout_Play ( aout_thread_t *p_aout,
92 byte_t *buffer, int i_size );
93 static void aout_Close ( aout_thread_t *p_aout );
96 static int DirectxCreateSecondaryBuffer ( aout_thread_t *p_aout );
97 static void DirectxDestroySecondaryBuffer( aout_thread_t *p_aout );
98 static int DirectxInitDSound ( aout_thread_t *p_aout );
99 static void DirectSoundThread ( aout_thread_t *p_aout );
101 /*****************************************************************************
102 * Functions exported as capabilities. They are declared as static so that
103 * we don't pollute the namespace too much.
104 *****************************************************************************/
105 void _M( aout_getfunctions )( function_list_t * p_function_list )
107 p_function_list->functions.aout.pf_open = aout_Open;
108 p_function_list->functions.aout.pf_setformat = aout_SetFormat;
109 p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
110 p_function_list->functions.aout.pf_play = aout_Play;
111 p_function_list->functions.aout.pf_close = aout_Close;
114 /*****************************************************************************
115 * aout_Open: open the audio device
116 *****************************************************************************
117 * This function opens and setups Direct Sound.
118 *****************************************************************************/
119 static int aout_Open( aout_thread_t *p_aout )
122 DSBUFFERDESC dsbuffer_desc;
124 msg_Dbg( p_aout, "aout_Open" );
126 /* Allocate structure */
127 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
129 if( p_aout->p_sys == NULL )
131 msg_Err( p_aout, "out of memory" );
135 /* Initialize some variables */
136 p_aout->p_sys->p_dsobject = NULL;
137 p_aout->p_sys->p_dsbuffer_primary = NULL;
138 p_aout->p_sys->p_dsbuffer = NULL;
139 p_aout->p_sys->p_dsnotify = NULL;
140 p_aout->p_sys->b_notification_thread_die = 0;
141 p_aout->p_sys->l_data_written_from_beginning = 0;
142 p_aout->p_sys->l_data_played_from_beginning = 0;
143 vlc_mutex_init( &p_aout->p_sys->buffer_lock );
146 /* Initialise DirectSound */
147 if( DirectxInitDSound( p_aout ) )
149 msg_Warn( p_aout, "cannot initialize DirectSound" );
153 /* Obtain (not create) Direct Sound primary buffer */
154 memset( &dsbuffer_desc, 0, sizeof(DSBUFFERDESC) );
155 dsbuffer_desc.dwSize = sizeof(DSBUFFERDESC);
156 dsbuffer_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
157 msg_Warn( p_aout, "create direct sound primary buffer" );
158 dsresult = IDirectSound_CreateSoundBuffer(p_aout->p_sys->p_dsobject,
160 &p_aout->p_sys->p_dsbuffer_primary,
162 if( dsresult != DS_OK )
164 msg_Warn( p_aout, "cannot create direct sound primary buffer" );
165 IDirectSound_Release( p_aout->p_sys->p_dsobject );
166 p_aout->p_sys->p_dsobject = NULL;
167 p_aout->p_sys->p_dsbuffer_primary = NULL;
172 /* Now we need to setup DirectSound play notification */
174 /* first we need to create the notification events */
175 p_aout->p_sys->notification_events[0].hEventNotify =
176 CreateEvent( NULL, FALSE, FALSE, NULL );
177 p_aout->p_sys->notification_events[1].hEventNotify =
178 CreateEvent( NULL, FALSE, FALSE, NULL );
180 /* then launch the notification thread */
181 msg_Dbg( p_aout, "creating DirectSoundThread" );
182 if( vlc_thread_create( p_aout, &p_aout->p_sys->notification_thread_id,
183 "DirectSound Notification Thread",
184 (void *) DirectSoundThread, (void *) p_aout) )
186 msg_Err( p_aout, "cannot create DirectSoundThread" );
187 /* Let's go on anyway */
193 /*****************************************************************************
194 * aout_SetFormat: reset the audio device and sets its format
195 *****************************************************************************
196 * This functions set a new audio format.
197 * For this we need to close the current secondary buffer and create another
198 * one with the desired format.
199 *****************************************************************************/
200 static int aout_SetFormat( aout_thread_t *p_aout )
203 WAVEFORMATEX *p_waveformat;
204 unsigned long i_size_struct;
206 msg_Dbg( p_aout, "aout_SetFormat" );
208 /* Set the format of Direct Sound primary buffer */
210 /* first we need to know the current format */
211 dsresult = IDirectSoundBuffer_GetFormat( p_aout->p_sys->p_dsbuffer_primary,
212 NULL, 0, &i_size_struct );
213 if( dsresult == DS_OK )
215 p_waveformat = malloc( i_size_struct );
216 dsresult = IDirectSoundBuffer_GetFormat(
217 p_aout->p_sys->p_dsbuffer_primary,
218 p_waveformat, i_size_struct,
222 if( dsresult == DS_OK )
224 /* Here we'll change the format */
225 p_waveformat->nChannels = 2;
226 p_waveformat->nSamplesPerSec = (p_aout->i_rate < 44100) ? 44100
228 p_waveformat->wBitsPerSample = 16;
229 p_waveformat->nBlockAlign = p_waveformat->wBitsPerSample / 8 *
230 p_waveformat->nChannels;
231 p_waveformat->nAvgBytesPerSec = p_waveformat->nSamplesPerSec *
232 p_waveformat->nBlockAlign;
234 dsresult = IDirectSoundBuffer_SetFormat(
235 p_aout->p_sys->p_dsbuffer_primary,
238 else msg_Warn( p_aout, "cannot get primary buffer format" );
240 if( dsresult != DS_OK )
241 msg_Warn( p_aout, "cannot set primary buffer format" );
244 /* Now we need to take care of Direct Sound secondary buffer */
246 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
248 /* first release the current secondary buffer */
249 DirectxDestroySecondaryBuffer( p_aout );
251 /* then create a new secondary buffer */
252 if( DirectxCreateSecondaryBuffer( p_aout ) )
254 msg_Warn( p_aout, "cannot create buffer" );
255 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
259 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
264 /*****************************************************************************
265 * aout_GetBufInfo: buffer status query
266 *****************************************************************************
267 * returns the number of bytes in the audio buffer that have not yet been
268 * sent to the sound device.
269 *****************************************************************************/
270 static int aout_GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
272 long l_play_position, l_notused, l_result;
275 if( p_aout->p_sys->b_buffer_underflown )
277 msg_Warn( p_aout, "aout_GetBufInfo underflow" );
278 return( i_buffer_limit );
281 dsresult = IDirectSoundBuffer_GetCurrentPosition(p_aout->p_sys->p_dsbuffer,
282 &l_play_position, &l_notused);
283 if( dsresult != DS_OK )
285 msg_Warn( p_aout, "aout_GetBufInfo cannot get current pos" );
286 return( i_buffer_limit );
289 l_result = (p_aout->p_sys->l_write_position >= l_play_position) ?
290 (p_aout->p_sys->l_write_position - l_play_position)
291 : (p_aout->p_sys->l_buffer_size - l_play_position
292 + p_aout->p_sys->l_write_position);
295 msg_Dbg( p_aout, "aout_GetBufInfo: %i", i_result);
300 /*****************************************************************************
301 * aout_Play: play a sound buffer
302 *****************************************************************************
303 * This function writes a buffer of i_length bytes
304 * Don't forget that DirectSound buffers are circular buffers.
305 *****************************************************************************/
306 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
308 VOID *p_write_position, *p_start_buffer;
309 long l_bytes1, l_bytes2, l_play_position;
312 /* protect buffer access (because of DirectSoundThread) */
313 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
315 if( p_aout->p_sys->b_buffer_underflown )
317 /* there has been an underflow so we need to play the new sample
318 * as soon as possible. This is why we query the play position */
319 dsresult = IDirectSoundBuffer_GetCurrentPosition(
320 p_aout->p_sys->p_dsbuffer,
322 &p_aout->p_sys->l_write_position );
323 if( dsresult != DS_OK )
325 msg_Warn( p_aout, "cannot get buffer position" );
326 p_aout->p_sys->l_write_position = 0;
329 msg_Warn( p_aout, "aout_Play underflow" );
330 /* reinitialise the underflow detection counters */
331 p_aout->p_sys->b_buffer_underflown = 0;
332 p_aout->p_sys->l_data_written_from_beginning = 0;
334 #define WRITE_P p_aout->p_sys->l_write_position
335 #define PLAY_P l_play_position
336 #define BUF_SIZE p_aout->p_sys->l_buffer_size
337 p_aout->p_sys->l_data_played_from_beginning = -(WRITE_P %(BUF_SIZE/2));
338 if( PLAY_P < BUF_SIZE/2 && WRITE_P > BUF_SIZE/2 )
340 p_aout->p_sys->l_data_played_from_beginning -= (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);
351 /* Before copying anything, we have to lock the buffer */
352 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
353 p_aout->p_sys->l_write_position, /* Offset of lock start */
354 i_size, /* Number of bytes to lock */
355 &p_write_position, /* Address of lock start */
356 &l_bytes1, /* Count of bytes locked before wrap around */
357 &p_start_buffer, /* Buffer adress (if wrap around) */
358 &l_bytes2, /* Count of bytes after wrap around */
360 if( dsresult == DSERR_BUFFERLOST )
362 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
363 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
364 p_aout->p_sys->l_write_position,
373 if( dsresult != DS_OK )
375 msg_Warn( p_aout, "aout_Play cannot lock buffer" );
376 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
380 /* Now do the actual memcpy (two memcpy because the buffer is circular) */
381 memcpy( p_write_position, buffer, l_bytes1 );
382 if( p_start_buffer != NULL )
384 memcpy( p_start_buffer, buffer + l_bytes1, l_bytes2 );
387 /* Now the data has been copied, unlock the buffer */
388 IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer,
389 p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
391 /* Update the write position index of the buffer*/
392 p_aout->p_sys->l_write_position += i_size;
393 p_aout->p_sys->l_write_position %= p_aout->p_sys->l_buffer_size;
394 p_aout->p_sys->l_data_written_from_beginning += i_size;
396 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
398 /* The play function has no effect if the buffer is already playing */
399 dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
402 DSBPLAY_LOOPING ); /* Flags */
403 if( dsresult == DSERR_BUFFERLOST )
405 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
406 dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
409 DSBPLAY_LOOPING ); /* Flags */
411 if( dsresult != DS_OK )
413 msg_Warn( p_aout, "aout_Play cannot play buffer" );
419 /*****************************************************************************
420 * aout_Close: close the audio device
421 *****************************************************************************/
422 static void aout_Close( aout_thread_t *p_aout )
425 msg_Dbg( p_aout, "aout_Close" );
427 /* kill the position notification thread */
428 p_aout->p_sys->b_notification_thread_die = 1;
429 SetEvent( p_aout->p_sys->notification_events[0].hEventNotify );
430 vlc_thread_join( p_aout, p_aout->p_sys->notification_thread_id );
431 vlc_mutex_destroy( &p_aout->p_sys->buffer_lock );
433 /* release the secondary buffer */
434 DirectxDestroySecondaryBuffer( p_aout );
436 /* then release the primary buffer */
437 if( p_aout->p_sys->p_dsbuffer_primary != NULL )
439 IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer_primary );
440 p_aout->p_sys->p_dsbuffer_primary = NULL;
443 /* finally release the DirectSound object */
444 if( p_aout->p_sys->p_dsobject != NULL )
446 IDirectSound_Release( p_aout->p_sys->p_dsobject );
447 p_aout->p_sys->p_dsobject = NULL;
450 /* free DSOUND.DLL */
451 if( p_aout->p_sys->hdsound_dll != NULL )
453 FreeLibrary( p_aout->p_sys->hdsound_dll );
454 p_aout->p_sys->hdsound_dll = NULL;
457 /* Close the Output. */
458 if ( p_aout->p_sys != NULL )
460 free( p_aout->p_sys );
461 p_aout->p_sys = NULL;
465 /*****************************************************************************
467 *****************************************************************************
468 *****************************************************************************/
469 static int DirectxInitDSound( aout_thread_t *p_aout )
471 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
473 p_aout->p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
474 if( p_aout->p_sys->hdsound_dll == NULL )
476 msg_Warn( p_aout, "cannot open DSOUND.DLL" );
480 OurDirectSoundCreate = (void *)GetProcAddress( p_aout->p_sys->hdsound_dll,
481 "DirectSoundCreate" );
483 if( OurDirectSoundCreate == NULL )
485 msg_Warn( p_aout, "GetProcAddress FAILED" );
486 FreeLibrary( p_aout->p_sys->hdsound_dll );
487 p_aout->p_sys->hdsound_dll = NULL;
491 /* Create the direct sound object */
492 if( OurDirectSoundCreate(NULL, &p_aout->p_sys->p_dsobject, NULL) != DS_OK )
494 msg_Warn( p_aout, "cannot create a direct sound device" );
495 p_aout->p_sys->p_dsobject = NULL;
496 FreeLibrary( p_aout->p_sys->hdsound_dll );
497 p_aout->p_sys->hdsound_dll = NULL;
501 /* Set DirectSound Cooperative level, ie what control we want over Windows
502 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
503 * settings of the primary buffer, but also that only the sound of our
504 * application will be hearable when it will have the focus.
505 * !!! (this is not really working as intended yet because to set the
506 * cooperative level you need the window handle of your application, and
507 * I don't know of any easy way to get it. Especially since we might play
508 * sound without any video, and so what window handle should we use ???
509 * The hack for now is to use the Desktop window handle - it seems to be
511 if( IDirectSound_SetCooperativeLevel(p_aout->p_sys->p_dsobject,
515 msg_Warn( p_aout, "cannot set direct sound cooperative level" );
521 /*****************************************************************************
522 * DirectxCreateSecondaryBuffer
523 *****************************************************************************
524 * This function creates the buffer we'll use to play audio.
525 * In DirectSound there are two kinds of buffers:
526 * - the primary buffer: which is the actual buffer that the soundcard plays
527 * - the secondary buffer(s): these buffers are the one actually used by
528 * applications and DirectSound takes care of mixing them into the primary.
530 * Once you create a secondary buffer, you cannot change its format anymore so
531 * you have to release the current and create another one.
532 *****************************************************************************/
533 static int DirectxCreateSecondaryBuffer( aout_thread_t *p_aout )
535 WAVEFORMATEX waveformat;
536 DSBUFFERDESC dsbdesc;
539 /* First set the buffer format */
540 memset(&waveformat, 0, sizeof(WAVEFORMATEX));
541 waveformat.wFormatTag = WAVE_FORMAT_PCM;
542 waveformat.nChannels = p_aout->i_channels;
543 waveformat.nSamplesPerSec = p_aout->i_rate;
544 waveformat.wBitsPerSample = 16;
545 waveformat.nBlockAlign = waveformat.wBitsPerSample / 8 *
546 waveformat.nChannels;
547 waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec *
548 waveformat.nBlockAlign;
550 /* Then fill in the descriptor */
551 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
552 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
553 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
554 | DSBCAPS_CTRLPOSITIONNOTIFY /* We need notification */
555 | DSBCAPS_GLOBALFOCUS; /* Allows background playing */
556 dsbdesc.dwBufferBytes = waveformat.nAvgBytesPerSec * 2; /* 2 sec buffer */
557 dsbdesc.lpwfxFormat = &waveformat;
559 if( IDirectSound_CreateSoundBuffer( p_aout->p_sys->p_dsobject,
561 &p_aout->p_sys->p_dsbuffer,
564 msg_Warn( p_aout, "cannot create direct sound secondary buffer" );
565 p_aout->p_sys->p_dsbuffer = NULL;
569 /* backup the size of the secondary sound buffer */
570 memset(&dsbcaps, 0, sizeof(DSBCAPS));
571 dsbcaps.dwSize = sizeof(DSBCAPS);
572 IDirectSoundBuffer_GetCaps( p_aout->p_sys->p_dsbuffer, &dsbcaps );
573 p_aout->p_sys->l_buffer_size = dsbcaps.dwBufferBytes;
574 p_aout->p_sys->l_write_position = 0;
576 msg_Dbg( p_aout, "DirectxCreateSecondaryBuffer: %li",
577 p_aout->p_sys->l_buffer_size );
579 /* Now the secondary buffer is created, we need to setup its position
581 p_aout->p_sys->notification_events[0].dwOffset = 0; /* notif position */
582 p_aout->p_sys->notification_events[1].dwOffset = dsbcaps.dwBufferBytes / 2;
584 /* Get the IDirectSoundNotify interface */
585 if FAILED( IDirectSoundBuffer_QueryInterface( p_aout->p_sys->p_dsbuffer,
586 &IID_IDirectSoundNotify,
587 (LPVOID *)&p_aout->p_sys->p_dsnotify ) )
589 msg_Warn( p_aout, "cannot get Notify interface" );
591 p_aout->p_sys->p_dsnotify = NULL;
595 if FAILED( IDirectSoundNotify_SetNotificationPositions(
596 p_aout->p_sys->p_dsnotify,
598 p_aout->p_sys->notification_events ) )
600 msg_Warn( p_aout, "cannot set position Notification" );
607 /*****************************************************************************
608 * DirectxCreateSecondaryBuffer
609 *****************************************************************************
610 * This function destroy the secondary buffer.
611 *****************************************************************************/
612 static void DirectxDestroySecondaryBuffer( aout_thread_t *p_aout )
614 /* make sure the buffer isn't playing */
615 if( p_aout->p_sys->p_dsbuffer != NULL )
617 IDirectSoundBuffer_Stop( p_aout->p_sys->p_dsbuffer );
620 if( p_aout->p_sys->p_dsnotify != NULL )
622 IDirectSoundNotify_Release( p_aout->p_sys->p_dsnotify );
623 p_aout->p_sys->p_dsnotify = NULL;
626 if( p_aout->p_sys->p_dsbuffer != NULL )
628 IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer );
629 p_aout->p_sys->p_dsbuffer = NULL;
633 /*****************************************************************************
634 * DirectSoundThread: this thread will capture play notification events.
635 *****************************************************************************
636 * As Direct Sound uses circular buffers, we need to use event notification to
638 * Using event notification implies blocking the thread until the event is
639 * signaled so we really need to run this in a separate thread.
640 *****************************************************************************/
641 static void DirectSoundThread( aout_thread_t *p_aout )
643 HANDLE notification_events[2];
644 VOID *p_write_position, *p_start_buffer;
645 long l_bytes1, l_bytes2;
647 long l_buffer_size, l_play_position, l_data_in_buffer;
649 notification_events[0]=p_aout->p_sys->notification_events[0].hEventNotify;
650 notification_events[1]=p_aout->p_sys->notification_events[1].hEventNotify;
652 /* this thread must be high-priority */
653 if( !SetThreadPriority( GetCurrentThread(),
654 THREAD_PRIORITY_ABOVE_NORMAL ) )
656 msg_Warn( p_aout, "DirectSoundThread could not renice itself" );
659 msg_Dbg( p_aout, "DirectSoundThread ready" );
661 while( !p_aout->p_sys->b_notification_thread_die )
663 /* wait for the position notification */
664 l_play_position = WaitForMultipleObjects( 2, notification_events,
666 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
668 if( p_aout->p_sys->b_notification_thread_die )
673 /* check for buffer underflow (bodge for wrap around) */
674 l_buffer_size = p_aout->p_sys->l_buffer_size;
675 l_play_position = (l_play_position - WAIT_OBJECT_0) * l_buffer_size/2;
676 p_aout->p_sys->l_data_played_from_beginning += (l_buffer_size/2);
677 l_data_in_buffer = p_aout->p_sys->l_data_written_from_beginning -
678 p_aout->p_sys->l_data_played_from_beginning;
680 /* detect wrap-around */
681 if( l_data_in_buffer < (-l_buffer_size/2) )
683 msg_Dbg( p_aout, "DirectSoundThread wrap around: %li",
685 l_data_in_buffer += l_buffer_size;
688 /* detect underflow */
689 if( l_data_in_buffer <= 0 )
692 "DirectSoundThread underflow: %li", l_data_in_buffer );
693 p_aout->p_sys->b_buffer_underflown = 1;
694 p_aout->p_sys->l_write_position =
695 (l_play_position + l_buffer_size/2) % l_buffer_size;
696 l_data_in_buffer = l_buffer_size / 2;
697 p_aout->p_sys->l_data_played_from_beginning -= (l_buffer_size/2);
701 /* Clear the data which has already been played */
703 /* Before copying anything, we have to lock the buffer */
704 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
705 p_aout->p_sys->l_write_position, /* Offset of lock start */
706 l_buffer_size - l_data_in_buffer, /* Number of bytes */
707 &p_write_position, /* Address of lock start */
708 &l_bytes1, /* Count of bytes locked before wrap around */
709 &p_start_buffer, /* Buffer adress (if wrap around) */
710 &l_bytes2, /* Count of bytes after wrap around */
712 if( dsresult == DSERR_BUFFERLOST )
714 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
715 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
716 p_aout->p_sys->l_write_position,
717 l_buffer_size - l_data_in_buffer,
724 if( dsresult != DS_OK )
726 msg_Warn( p_aout, "aout_Play cannot lock buffer" );
727 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
731 /* Now do the actual memcpy (two because the buffer is circular) */
732 memset( p_write_position, 0, l_bytes1 );
733 if( p_start_buffer != NULL )
735 memset( p_start_buffer, 0, l_bytes2 );
738 /* Now the data has been copied, unlock the buffer */
739 IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer,
740 p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
742 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
746 /* free the events */
747 CloseHandle( notification_events[0] );
748 CloseHandle( notification_events[1] );
750 msg_Dbg( p_aout, "DirectSoundThread exiting" );