1 /*****************************************************************************
2 * aout_directx.c: Windows DirectX audio output method
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: aout_directx.c,v 1.24 2002/06/02 09:03:54 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 * notification_thread_t: DirectX event thread
49 *****************************************************************************/
50 typedef struct notification_thread_s
54 aout_thread_t * p_aout;
55 DSBPOSITIONNOTIFY p_events[2]; /* play notification events */
57 } notification_thread_t;
59 /*****************************************************************************
60 * aout_sys_t: directx audio output method descriptor
61 *****************************************************************************
62 * This structure is part of the audio output thread descriptor.
63 * It describes the direct sound specific properties of an audio device.
64 *****************************************************************************/
68 LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
70 LPDIRECTSOUNDBUFFER p_dsbuffer_primary; /* the actual sound card buffer
71 (not used directly) */
73 LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
74 * takes care of mixing all the
75 * secondary buffers into the primary) */
77 LPDIRECTSOUNDNOTIFY p_dsnotify; /* the position notify interface */
79 HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
81 long l_buffer_size; /* secondary sound buffer size */
82 long l_write_position; /* next write position for the buffer */
84 volatile vlc_bool_t b_buffer_underflown; /* buffer underflow detection */
85 volatile long l_data_played_from_beginning; /* for underflow detection */
86 volatile long l_data_written_from_beginning; /* for underflow detection */
88 vlc_mutex_t buffer_lock; /* audio buffer lock */
90 notification_thread_t * p_notif; /* DirectSoundThread id */
93 /*****************************************************************************
95 *****************************************************************************/
96 static int aout_Open ( aout_thread_t * );
97 static int aout_SetFormat ( aout_thread_t * );
98 static int aout_GetBufInfo ( aout_thread_t *, int );
99 static void aout_Play ( aout_thread_t *, byte_t *, int );
100 static void aout_Close ( aout_thread_t * );
102 /* local functions */
103 static int DirectxCreateSecondaryBuffer ( aout_thread_t * );
104 static void DirectxDestroySecondaryBuffer( aout_thread_t * );
105 static int DirectxInitDSound ( aout_thread_t * );
106 static void DirectSoundThread ( notification_thread_t * );
108 /*****************************************************************************
109 * Functions exported as capabilities. They are declared as static so that
110 * we don't pollute the namespace too much.
111 *****************************************************************************/
112 void _M( aout_getfunctions )( function_list_t * p_function_list )
114 p_function_list->functions.aout.pf_open = aout_Open;
115 p_function_list->functions.aout.pf_setformat = aout_SetFormat;
116 p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
117 p_function_list->functions.aout.pf_play = aout_Play;
118 p_function_list->functions.aout.pf_close = aout_Close;
121 /*****************************************************************************
122 * aout_Open: open the audio device
123 *****************************************************************************
124 * This function opens and setups Direct Sound.
125 *****************************************************************************/
126 static int aout_Open( aout_thread_t *p_aout )
129 DSBUFFERDESC dsbuffer_desc;
131 msg_Dbg( p_aout, "aout_Open" );
133 /* Allocate structure */
134 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
136 if( p_aout->p_sys == NULL )
138 msg_Err( p_aout, "out of memory" );
142 /* Initialize some variables */
143 p_aout->p_sys->p_dsobject = NULL;
144 p_aout->p_sys->p_dsbuffer_primary = NULL;
145 p_aout->p_sys->p_dsbuffer = NULL;
146 p_aout->p_sys->p_dsnotify = NULL;
147 p_aout->p_sys->l_data_written_from_beginning = 0;
148 p_aout->p_sys->l_data_played_from_beginning = 0;
149 vlc_mutex_init( p_aout, &p_aout->p_sys->buffer_lock );
152 /* Initialise DirectSound */
153 if( DirectxInitDSound( p_aout ) )
155 msg_Warn( p_aout, "cannot initialize DirectSound" );
159 /* Obtain (not create) Direct Sound primary buffer */
160 memset( &dsbuffer_desc, 0, sizeof(DSBUFFERDESC) );
161 dsbuffer_desc.dwSize = sizeof(DSBUFFERDESC);
162 dsbuffer_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
163 msg_Warn( p_aout, "create direct sound primary buffer" );
164 dsresult = IDirectSound_CreateSoundBuffer(p_aout->p_sys->p_dsobject,
166 &p_aout->p_sys->p_dsbuffer_primary,
168 if( dsresult != DS_OK )
170 msg_Warn( p_aout, "cannot create direct sound primary buffer" );
171 IDirectSound_Release( p_aout->p_sys->p_dsobject );
172 p_aout->p_sys->p_dsobject = NULL;
173 p_aout->p_sys->p_dsbuffer_primary = NULL;
178 /* Now we need to setup DirectSound play notification */
180 /* first we need to create the notification events */
181 p_aout->p_sys->p_notif->p_events[0].hEventNotify =
182 CreateEvent( NULL, FALSE, FALSE, NULL );
183 p_aout->p_sys->p_notif->p_events[1].hEventNotify =
184 CreateEvent( NULL, FALSE, FALSE, NULL );
186 /* then launch the notification thread */
187 msg_Dbg( p_aout, "creating DirectSoundThread" );
188 p_aout->p_sys->p_notif =
189 vlc_object_create( p_aout, sizeof(notification_thread_t) );
190 p_aout->p_sys->p_notif->p_aout = p_aout;
191 if( vlc_thread_create( p_aout->p_sys->p_notif,
192 "DirectSound Notification Thread", DirectSoundThread, 1 ) )
194 msg_Err( p_aout, "cannot create DirectSoundThread" );
195 /* Let's go on anyway */
198 vlc_object_attach( p_aout->p_sys->p_notif, p_aout );
203 /*****************************************************************************
204 * aout_SetFormat: reset the audio device and sets its format
205 *****************************************************************************
206 * This functions set a new audio format.
207 * For this we need to close the current secondary buffer and create another
208 * one with the desired format.
209 *****************************************************************************/
210 static int aout_SetFormat( aout_thread_t *p_aout )
213 WAVEFORMATEX *p_waveformat;
214 unsigned long i_size_struct;
216 msg_Dbg( p_aout, "aout_SetFormat" );
218 /* Set the format of Direct Sound primary buffer */
220 /* first we need to know the current format */
221 dsresult = IDirectSoundBuffer_GetFormat( p_aout->p_sys->p_dsbuffer_primary,
222 NULL, 0, &i_size_struct );
223 if( dsresult == DS_OK )
225 p_waveformat = malloc( i_size_struct );
226 dsresult = IDirectSoundBuffer_GetFormat(
227 p_aout->p_sys->p_dsbuffer_primary,
228 p_waveformat, i_size_struct,
232 if( dsresult == DS_OK )
234 /* Here we'll change the format */
235 p_waveformat->nChannels = 2;
236 p_waveformat->nSamplesPerSec = (p_aout->i_rate < 44100) ? 44100
238 p_waveformat->wBitsPerSample = 16;
239 p_waveformat->nBlockAlign = p_waveformat->wBitsPerSample / 8 *
240 p_waveformat->nChannels;
241 p_waveformat->nAvgBytesPerSec = p_waveformat->nSamplesPerSec *
242 p_waveformat->nBlockAlign;
244 dsresult = IDirectSoundBuffer_SetFormat(
245 p_aout->p_sys->p_dsbuffer_primary,
248 else msg_Warn( p_aout, "cannot get primary buffer format" );
250 if( dsresult != DS_OK )
251 msg_Warn( p_aout, "cannot set primary buffer format" );
254 /* Now we need to take care of Direct Sound secondary buffer */
256 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
258 /* first release the current secondary buffer */
259 DirectxDestroySecondaryBuffer( p_aout );
261 /* then create a new secondary buffer */
262 if( DirectxCreateSecondaryBuffer( p_aout ) )
264 msg_Warn( p_aout, "cannot create buffer" );
265 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
269 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
274 /*****************************************************************************
275 * aout_GetBufInfo: buffer status query
276 *****************************************************************************
277 * returns the number of bytes in the audio buffer that have not yet been
278 * sent to the sound device.
279 *****************************************************************************/
280 static int aout_GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
282 long l_play_position, l_notused, l_result;
285 if( p_aout->p_sys->b_buffer_underflown )
287 msg_Warn( p_aout, "aout_GetBufInfo underflow" );
288 return( i_buffer_limit );
291 dsresult = IDirectSoundBuffer_GetCurrentPosition(p_aout->p_sys->p_dsbuffer,
292 &l_play_position, &l_notused);
293 if( dsresult != DS_OK )
295 msg_Warn( p_aout, "aout_GetBufInfo cannot get current pos" );
296 return( i_buffer_limit );
299 l_result = (p_aout->p_sys->l_write_position >= l_play_position) ?
300 (p_aout->p_sys->l_write_position - l_play_position)
301 : (p_aout->p_sys->l_buffer_size - l_play_position
302 + p_aout->p_sys->l_write_position);
305 msg_Dbg( p_aout, "aout_GetBufInfo: %i", i_result);
310 /*****************************************************************************
311 * aout_Play: play a sound buffer
312 *****************************************************************************
313 * This function writes a buffer of i_length bytes
314 * Don't forget that DirectSound buffers are circular buffers.
315 *****************************************************************************/
316 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
318 VOID *p_write_position, *p_start_buffer;
319 long l_bytes1, l_bytes2, l_play_position;
322 /* protect buffer access (because of DirectSoundThread) */
323 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
325 if( p_aout->p_sys->b_buffer_underflown )
327 /* there has been an underflow so we need to play the new sample
328 * as soon as possible. This is why we query the play position */
329 dsresult = IDirectSoundBuffer_GetCurrentPosition(
330 p_aout->p_sys->p_dsbuffer,
332 &p_aout->p_sys->l_write_position );
333 if( dsresult != DS_OK )
335 msg_Warn( p_aout, "cannot get buffer position" );
336 p_aout->p_sys->l_write_position = 0;
339 msg_Warn( p_aout, "aout_Play underflow" );
340 /* reinitialise the underflow detection counters */
341 p_aout->p_sys->b_buffer_underflown = 0;
342 p_aout->p_sys->l_data_written_from_beginning = 0;
344 #define WRITE_P p_aout->p_sys->l_write_position
345 #define PLAY_P l_play_position
346 #define BUF_SIZE p_aout->p_sys->l_buffer_size
347 p_aout->p_sys->l_data_played_from_beginning = -(WRITE_P %(BUF_SIZE/2));
348 if( PLAY_P < BUF_SIZE/2 && WRITE_P > BUF_SIZE/2 )
350 p_aout->p_sys->l_data_played_from_beginning -= (BUF_SIZE/2);
352 if( PLAY_P > BUF_SIZE/2 && WRITE_P < BUF_SIZE/2 )
354 p_aout->p_sys->l_data_played_from_beginning -= (BUF_SIZE/2);
361 /* Before copying anything, we have to lock the buffer */
362 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
363 p_aout->p_sys->l_write_position, /* Offset of lock start */
364 i_size, /* Number of bytes to lock */
365 &p_write_position, /* Address of lock start */
366 &l_bytes1, /* Count of bytes locked before wrap around */
367 &p_start_buffer, /* Buffer adress (if wrap around) */
368 &l_bytes2, /* Count of bytes after wrap around */
370 if( dsresult == DSERR_BUFFERLOST )
372 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
373 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
374 p_aout->p_sys->l_write_position,
383 if( dsresult != DS_OK )
385 msg_Warn( p_aout, "aout_Play cannot lock buffer" );
386 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
390 /* Now do the actual memcpy (two memcpy because the buffer is circular) */
391 memcpy( p_write_position, buffer, l_bytes1 );
392 if( p_start_buffer != NULL )
394 memcpy( p_start_buffer, buffer + l_bytes1, l_bytes2 );
397 /* Now the data has been copied, unlock the buffer */
398 IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer,
399 p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
401 /* Update the write position index of the buffer*/
402 p_aout->p_sys->l_write_position += i_size;
403 p_aout->p_sys->l_write_position %= p_aout->p_sys->l_buffer_size;
404 p_aout->p_sys->l_data_written_from_beginning += i_size;
406 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
408 /* The play function has no effect if the buffer is already playing */
409 dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
412 DSBPLAY_LOOPING ); /* Flags */
413 if( dsresult == DSERR_BUFFERLOST )
415 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
416 dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
419 DSBPLAY_LOOPING ); /* Flags */
421 if( dsresult != DS_OK )
423 msg_Warn( p_aout, "aout_Play cannot play buffer" );
429 /*****************************************************************************
430 * aout_Close: close the audio device
431 *****************************************************************************/
432 static void aout_Close( aout_thread_t *p_aout )
435 msg_Dbg( p_aout, "aout_Close" );
437 /* kill the position notification thread, if any */
438 vlc_object_detach_all( p_aout->p_sys->p_notif );
439 if( p_aout->p_sys->p_notif->b_thread )
441 p_aout->p_sys->p_notif->b_die = 1;
442 vlc_thread_join( p_aout->p_sys->p_notif );
444 vlc_object_destroy( p_aout->p_sys->p_notif );
446 /* release the secondary buffer */
447 DirectxDestroySecondaryBuffer( p_aout );
449 /* then release the primary buffer */
450 if( p_aout->p_sys->p_dsbuffer_primary != NULL )
452 IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer_primary );
453 p_aout->p_sys->p_dsbuffer_primary = NULL;
456 /* finally release the DirectSound object */
457 if( p_aout->p_sys->p_dsobject != NULL )
459 IDirectSound_Release( p_aout->p_sys->p_dsobject );
460 p_aout->p_sys->p_dsobject = NULL;
463 /* free DSOUND.DLL */
464 if( p_aout->p_sys->hdsound_dll != NULL )
466 FreeLibrary( p_aout->p_sys->hdsound_dll );
467 p_aout->p_sys->hdsound_dll = NULL;
470 /* Close the Output. */
471 if ( p_aout->p_sys != NULL )
473 free( p_aout->p_sys );
474 p_aout->p_sys = NULL;
478 /*****************************************************************************
480 *****************************************************************************
481 *****************************************************************************/
482 static int DirectxInitDSound( aout_thread_t *p_aout )
484 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
486 p_aout->p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
487 if( p_aout->p_sys->hdsound_dll == NULL )
489 msg_Warn( p_aout, "cannot open DSOUND.DLL" );
493 OurDirectSoundCreate = (void *)GetProcAddress( p_aout->p_sys->hdsound_dll,
494 "DirectSoundCreate" );
496 if( OurDirectSoundCreate == NULL )
498 msg_Warn( p_aout, "GetProcAddress FAILED" );
499 FreeLibrary( p_aout->p_sys->hdsound_dll );
500 p_aout->p_sys->hdsound_dll = NULL;
504 /* Create the direct sound object */
505 if( OurDirectSoundCreate(NULL, &p_aout->p_sys->p_dsobject, NULL) != DS_OK )
507 msg_Warn( p_aout, "cannot create a direct sound device" );
508 p_aout->p_sys->p_dsobject = NULL;
509 FreeLibrary( p_aout->p_sys->hdsound_dll );
510 p_aout->p_sys->hdsound_dll = NULL;
514 /* Set DirectSound Cooperative level, ie what control we want over Windows
515 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
516 * settings of the primary buffer, but also that only the sound of our
517 * application will be hearable when it will have the focus.
518 * !!! (this is not really working as intended yet because to set the
519 * cooperative level you need the window handle of your application, and
520 * I don't know of any easy way to get it. Especially since we might play
521 * sound without any video, and so what window handle should we use ???
522 * The hack for now is to use the Desktop window handle - it seems to be
524 if( IDirectSound_SetCooperativeLevel(p_aout->p_sys->p_dsobject,
528 msg_Warn( p_aout, "cannot set direct sound cooperative level" );
534 /*****************************************************************************
535 * DirectxCreateSecondaryBuffer
536 *****************************************************************************
537 * This function creates the buffer we'll use to play audio.
538 * In DirectSound there are two kinds of buffers:
539 * - the primary buffer: which is the actual buffer that the soundcard plays
540 * - the secondary buffer(s): these buffers are the one actually used by
541 * applications and DirectSound takes care of mixing them into the primary.
543 * Once you create a secondary buffer, you cannot change its format anymore so
544 * you have to release the current and create another one.
545 *****************************************************************************/
546 static int DirectxCreateSecondaryBuffer( aout_thread_t *p_aout )
548 WAVEFORMATEX waveformat;
549 DSBUFFERDESC dsbdesc;
552 /* First set the buffer format */
553 memset(&waveformat, 0, sizeof(WAVEFORMATEX));
554 waveformat.wFormatTag = WAVE_FORMAT_PCM;
555 waveformat.nChannels = p_aout->i_channels;
556 waveformat.nSamplesPerSec = p_aout->i_rate;
557 waveformat.wBitsPerSample = 16;
558 waveformat.nBlockAlign = waveformat.wBitsPerSample / 8 *
559 waveformat.nChannels;
560 waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec *
561 waveformat.nBlockAlign;
563 /* Then fill in the descriptor */
564 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
565 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
566 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
567 | DSBCAPS_CTRLPOSITIONNOTIFY /* We need notification */
568 | DSBCAPS_GLOBALFOCUS; /* Allows background playing */
569 dsbdesc.dwBufferBytes = waveformat.nAvgBytesPerSec * 2; /* 2 sec buffer */
570 dsbdesc.lpwfxFormat = &waveformat;
572 if( IDirectSound_CreateSoundBuffer( p_aout->p_sys->p_dsobject,
574 &p_aout->p_sys->p_dsbuffer,
577 msg_Warn( p_aout, "cannot create direct sound secondary buffer" );
578 p_aout->p_sys->p_dsbuffer = NULL;
582 /* backup the size of the secondary sound buffer */
583 memset(&dsbcaps, 0, sizeof(DSBCAPS));
584 dsbcaps.dwSize = sizeof(DSBCAPS);
585 IDirectSoundBuffer_GetCaps( p_aout->p_sys->p_dsbuffer, &dsbcaps );
586 p_aout->p_sys->l_buffer_size = dsbcaps.dwBufferBytes;
587 p_aout->p_sys->l_write_position = 0;
589 msg_Dbg( p_aout, "DirectxCreateSecondaryBuffer: %li",
590 p_aout->p_sys->l_buffer_size );
592 /* Now the secondary buffer is created, we need to setup its position
594 p_aout->p_sys->p_notif->p_events[0].dwOffset = 0; /* notif position */
595 p_aout->p_sys->p_notif->p_events[1].dwOffset = dsbcaps.dwBufferBytes / 2;
597 /* Get the IDirectSoundNotify interface */
598 if FAILED( IDirectSoundBuffer_QueryInterface( p_aout->p_sys->p_dsbuffer,
599 &IID_IDirectSoundNotify,
600 (LPVOID *)&p_aout->p_sys->p_dsnotify ) )
602 msg_Warn( p_aout, "cannot get Notify interface" );
604 p_aout->p_sys->p_dsnotify = NULL;
608 if FAILED( IDirectSoundNotify_SetNotificationPositions(
609 p_aout->p_sys->p_dsnotify,
611 p_aout->p_sys->p_notif->p_events ) )
613 msg_Warn( p_aout, "cannot set position Notification" );
620 /*****************************************************************************
621 * DirectxCreateSecondaryBuffer
622 *****************************************************************************
623 * This function destroy the secondary buffer.
624 *****************************************************************************/
625 static void DirectxDestroySecondaryBuffer( aout_thread_t *p_aout )
627 /* make sure the buffer isn't playing */
628 if( p_aout->p_sys->p_dsbuffer != NULL )
630 IDirectSoundBuffer_Stop( p_aout->p_sys->p_dsbuffer );
633 if( p_aout->p_sys->p_dsnotify != NULL )
635 IDirectSoundNotify_Release( p_aout->p_sys->p_dsnotify );
636 p_aout->p_sys->p_dsnotify = NULL;
639 if( p_aout->p_sys->p_dsbuffer != NULL )
641 IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer );
642 p_aout->p_sys->p_dsbuffer = NULL;
646 /*****************************************************************************
647 * DirectSoundThread: this thread will capture play notification events.
648 *****************************************************************************
649 * As Direct Sound uses circular buffers, we need to use event notification to
651 * Using event notification implies blocking the thread until the event is
652 * signaled so we really need to run this in a separate thread.
653 *****************************************************************************/
654 static void DirectSoundThread( notification_thread_t *p_notif )
656 HANDLE notification_events[2];
657 VOID *p_write_position, *p_start_buffer;
658 long l_bytes1, l_bytes2;
660 long l_buffer_size, l_play_position, l_data_in_buffer;
662 aout_thread_t *p_aout = p_notif->p_aout;
664 #define P_EVENTS p_aout->p_sys->p_notif->p_events
665 notification_events[0] = P_EVENTS[0].hEventNotify;
666 notification_events[1] = P_EVENTS[1].hEventNotify;
668 /* Tell the main thread that we are ready */
669 vlc_thread_ready( p_notif );
671 /* this thread must be high-priority */
672 if( !SetThreadPriority( GetCurrentThread(),
673 THREAD_PRIORITY_ABOVE_NORMAL ) )
675 msg_Warn( p_notif, "DirectSoundThread could not renice itself" );
678 msg_Dbg( p_notif, "DirectSoundThread ready" );
680 while( !p_notif->b_die )
682 /* wait for the position notification */
683 l_play_position = WaitForMultipleObjects( 2, notification_events,
685 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
692 /* check for buffer underflow (bodge for wrap around) */
693 l_buffer_size = p_aout->p_sys->l_buffer_size;
694 l_play_position = (l_play_position - WAIT_OBJECT_0) * l_buffer_size/2;
695 p_aout->p_sys->l_data_played_from_beginning += (l_buffer_size/2);
696 l_data_in_buffer = p_aout->p_sys->l_data_written_from_beginning -
697 p_aout->p_sys->l_data_played_from_beginning;
699 /* detect wrap-around */
700 if( l_data_in_buffer < (-l_buffer_size/2) )
702 msg_Dbg( p_notif, "DirectSoundThread wrap around: %li",
704 l_data_in_buffer += l_buffer_size;
707 /* detect underflow */
708 if( l_data_in_buffer <= 0 )
711 "DirectSoundThread underflow: %li", l_data_in_buffer );
712 p_aout->p_sys->b_buffer_underflown = 1;
713 p_aout->p_sys->l_write_position =
714 (l_play_position + l_buffer_size/2) % l_buffer_size;
715 l_data_in_buffer = l_buffer_size / 2;
716 p_aout->p_sys->l_data_played_from_beginning -= (l_buffer_size/2);
720 /* Clear the data which has already been played */
722 /* Before copying anything, we have to lock the buffer */
723 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
724 p_aout->p_sys->l_write_position, /* Offset of lock start */
725 l_buffer_size - l_data_in_buffer, /* Number of bytes */
726 &p_write_position, /* Address of lock start */
727 &l_bytes1, /* Count of bytes locked before wrap around */
728 &p_start_buffer, /* Buffer adress (if wrap around) */
729 &l_bytes2, /* Count of bytes after wrap around */
731 if( dsresult == DSERR_BUFFERLOST )
733 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
734 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
735 p_aout->p_sys->l_write_position,
736 l_buffer_size - l_data_in_buffer,
743 if( dsresult != DS_OK )
745 msg_Warn( p_notif, "aout_Play cannot lock buffer" );
746 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
750 /* Now do the actual memcpy (two because the buffer is circular) */
751 memset( p_write_position, 0, l_bytes1 );
752 if( p_start_buffer != NULL )
754 memset( p_start_buffer, 0, l_bytes2 );
757 /* Now the data has been copied, unlock the buffer */
758 IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer,
759 p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
761 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
765 /* free the events */
766 CloseHandle( notification_events[0] );
767 CloseHandle( notification_events[1] );
769 msg_Dbg( p_notif, "DirectSoundThread exiting" );