1 /*****************************************************************************
2 * aout_directx.c: Windows DirectX audio output method
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: aout_directx.c,v 1.14 2001/12/30 07:09: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 <stdio.h> /* "intf_msg.h" */
32 #include <stdlib.h> /* calloc(), malloc(), free() */
34 #include <videolan/vlc.h>
36 #if defined( _MSC_VER )
42 #include "audio_output.h" /* aout_thread_t */
44 /*****************************************************************************
45 * aout_sys_t: directx audio output method descriptor
46 *****************************************************************************
47 * This structure is part of the audio output thread descriptor.
48 * It describes the direct sound specific properties of an audio device.
49 *****************************************************************************/
51 typedef struct aout_sys_s
53 LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
55 LPDIRECTSOUNDBUFFER p_dsbuffer_primary; /* the actual sound card buffer
56 (not used directly) */
58 LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
59 * takes care of mixing all the
60 * secondary buffers into the primary) */
62 LPDIRECTSOUNDNOTIFY p_dsnotify; /* the position notify interface */
64 HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
66 long l_buffer_size; /* secondary sound buffer size */
67 long l_write_position; /* next write position for the buffer */
69 volatile boolean_t b_buffer_underflown; /* buffer underflow detection */
70 volatile long l_data_played_from_beginning; /* for underflow detection */
71 volatile long l_data_written_from_beginning; /* for underflow detection */
73 vlc_mutex_t buffer_lock; /* audio buffer lock */
75 vlc_thread_t notification_thread_id; /* DirectSoundThread id */
77 DSBPOSITIONNOTIFY notification_events[2]; /* play notification events */
79 boolean_t b_notification_thread_die; /* flag to kill the thread */
83 /*****************************************************************************
85 *****************************************************************************/
86 static int aout_Probe ( probedata_t *p_data );
87 static int aout_Open ( aout_thread_t *p_aout );
88 static int aout_SetFormat ( aout_thread_t *p_aout );
89 static long aout_GetBufInfo ( aout_thread_t *p_aout, long l_buffer_info );
90 static void aout_Play ( aout_thread_t *p_aout,
91 byte_t *buffer, int i_size );
92 static void aout_Close ( aout_thread_t *p_aout );
95 static int DirectxCreateSecondaryBuffer ( aout_thread_t *p_aout );
96 static void DirectxDestroySecondaryBuffer( aout_thread_t *p_aout );
97 static int DirectxInitDSound ( aout_thread_t *p_aout );
98 static void DirectSoundThread ( aout_thread_t *p_aout );
100 /*****************************************************************************
101 * Functions exported as capabilities. They are declared as static so that
102 * we don't pollute the namespace too much.
103 *****************************************************************************/
104 void _M( aout_getfunctions )( function_list_t * p_function_list )
106 p_function_list->pf_probe = aout_Probe;
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_Probe: probe the audio device and return a score
116 *****************************************************************************
117 * This function tries to probe for a Direct Sound device and returns a
118 * score to the plugin manager so that it can select the best plugin.
119 *****************************************************************************/
120 static int aout_Probe( probedata_t *p_data )
122 /* For now just assume the computer has a sound device */
123 if( TestMethod( AOUT_METHOD_VAR, "directx" ) )
130 /*****************************************************************************
131 * aout_Open: open the audio device
132 *****************************************************************************
133 * This function opens and setups Direct Sound.
134 *****************************************************************************/
135 static int aout_Open( aout_thread_t *p_aout )
138 DSBUFFERDESC dsbuffer_desc;
140 intf_WarnMsg( 3, "aout: DirectX aout_Open ");
142 /* Allocate structure */
143 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
145 if( p_aout->p_sys == NULL )
147 intf_ErrMsg( "aout error: %s", strerror(ENOMEM) );
151 /* Initialize some variables */
152 p_aout->p_sys->p_dsobject = NULL;
153 p_aout->p_sys->p_dsbuffer_primary = NULL;
154 p_aout->p_sys->p_dsbuffer = NULL;
155 p_aout->p_sys->p_dsnotify = NULL;
156 p_aout->p_sys->b_notification_thread_die = 0;
157 p_aout->p_sys->l_data_written_from_beginning = 0;
158 p_aout->p_sys->l_data_played_from_beginning = 0;
159 vlc_mutex_init( &p_aout->p_sys->buffer_lock );
162 p_aout->psz_device = 0;
163 p_aout->i_format = AOUT_FORMAT_DEFAULT;
164 p_aout->i_channels = 1 + main_GetIntVariable( AOUT_STEREO_VAR,
165 AOUT_STEREO_DEFAULT );
166 p_aout->l_rate = main_GetIntVariable( AOUT_RATE_VAR,
169 /* Initialise DirectSound */
170 if( DirectxInitDSound( p_aout ) )
172 intf_WarnMsg( 3, "aout: can't initialise DirectSound ");
176 /* Obtain (not create) Direct Sound primary buffer */
177 memset( &dsbuffer_desc, 0, sizeof(DSBUFFERDESC) );
178 dsbuffer_desc.dwSize = sizeof(DSBUFFERDESC);
179 dsbuffer_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
180 intf_WarnMsg( 3, "aout: Create direct sound primary buffer ");
181 dsresult = IDirectSound_CreateSoundBuffer(p_aout->p_sys->p_dsobject,
183 &p_aout->p_sys->p_dsbuffer_primary,
185 if( dsresult != DS_OK )
187 intf_WarnMsg( 3, "aout: can't create direct sound primary buffer ");
188 IDirectSound_Release( p_aout->p_sys->p_dsobject );
189 p_aout->p_sys->p_dsobject = NULL;
190 p_aout->p_sys->p_dsbuffer_primary = NULL;
195 /* Now we need to setup DirectSound play notification */
197 /* first we need to create the notification events */
198 p_aout->p_sys->notification_events[0].hEventNotify =
199 CreateEvent( NULL, FALSE, FALSE, NULL );
200 p_aout->p_sys->notification_events[1].hEventNotify =
201 CreateEvent( NULL, FALSE, FALSE, NULL );
203 /* then launch the notification thread */
204 intf_WarnMsg( 3, "aout: aout_Open creating DirectSoundThread" );
205 if( vlc_thread_create( &p_aout->p_sys->notification_thread_id,
206 "DirectSound Notification Thread",
207 (void *) DirectSoundThread, (void *) p_aout) )
209 intf_ErrMsg( "aout error: can't create DirectSoundThread" );
210 intf_ErrMsg("aout error: %s", strerror(ENOMEM));
211 /* Let's go on anyway */
217 /*****************************************************************************
218 * aout_SetFormat: reset the audio device and sets its format
219 *****************************************************************************
220 * This functions set a new audio format.
221 * For this we need to close the current secondary buffer and create another
222 * one with the desired format.
223 *****************************************************************************/
224 static int aout_SetFormat( aout_thread_t *p_aout )
227 WAVEFORMATEX *p_waveformat;
228 unsigned long i_size_struct;
230 intf_WarnMsg( 3, "aout: DirectX aout_SetFormat ");
232 /* Set the format of Direct Sound primary buffer */
234 /* first we need to know the current format */
235 dsresult = IDirectSoundBuffer_GetFormat( p_aout->p_sys->p_dsbuffer_primary,
236 NULL, 0, &i_size_struct );
237 if( dsresult == DS_OK )
239 p_waveformat = malloc( i_size_struct );
240 dsresult = IDirectSoundBuffer_GetFormat(
241 p_aout->p_sys->p_dsbuffer_primary,
242 p_waveformat, i_size_struct,
246 if( dsresult == DS_OK )
248 /* Here we'll change the format */
249 p_waveformat->nChannels = 2;
250 p_waveformat->nSamplesPerSec = (p_aout->l_rate < 44100) ? 44100
252 p_waveformat->wBitsPerSample = 16;
253 p_waveformat->nBlockAlign = p_waveformat->wBitsPerSample / 8 *
254 p_waveformat->nChannels;
255 p_waveformat->nAvgBytesPerSec = p_waveformat->nSamplesPerSec *
256 p_waveformat->nBlockAlign;
258 dsresult = IDirectSoundBuffer_SetFormat(
259 p_aout->p_sys->p_dsbuffer_primary,
262 else intf_WarnMsg( 3, "aout: can't get primary buffer format" );
264 if( dsresult != DS_OK )
265 intf_WarnMsg( 3, "aout: can't set primary buffer format" );
268 /* Now we need to take care of Direct Sound secondary buffer */
270 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
272 /* first release the current secondary buffer */
273 DirectxDestroySecondaryBuffer( p_aout );
275 /* then create a new secondary buffer */
276 if( DirectxCreateSecondaryBuffer( p_aout ) )
278 intf_WarnMsg( 3, "aout: DirectX aout_SetFormat cannot create buffer");
279 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
283 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
285 p_aout->i_latency = 0;
290 /*****************************************************************************
291 * aout_GetBufInfo: buffer status query
292 *****************************************************************************
293 * returns the number of bytes in the audio buffer that have not yet been
294 * sent to the sound device.
295 *****************************************************************************/
296 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
298 long l_play_position, l_notused, l_result;
301 if( p_aout->p_sys->b_buffer_underflown )
303 intf_WarnMsg( 3, "aout: DirectX aout_GetBufInfo underflow");
304 return( l_buffer_limit );
307 dsresult = IDirectSoundBuffer_GetCurrentPosition(p_aout->p_sys->p_dsbuffer,
308 &l_play_position, &l_notused);
309 if( dsresult != DS_OK )
311 intf_WarnMsg(3,"aout: DirectX aout_GetBufInfo cannot get current pos");
312 return( l_buffer_limit );
315 l_result = (p_aout->p_sys->l_write_position >= l_play_position) ?
316 (p_aout->p_sys->l_write_position - l_play_position)
317 : (p_aout->p_sys->l_buffer_size - l_play_position
318 + p_aout->p_sys->l_write_position);
321 intf_WarnMsg( 3, "aout: DirectX aout_GetBufInfo: %li", l_result);
326 /*****************************************************************************
327 * aout_Play: play a sound buffer
328 *****************************************************************************
329 * This function writes a buffer of i_length bytes
330 * Don't forget that DirectSound buffers are circular buffers.
331 *****************************************************************************/
332 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
334 VOID *p_write_position, *p_start_buffer;
335 long l_bytes1, l_bytes2, l_play_position;
338 /* protect buffer access (because of DirectSoundThread) */
339 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
341 if( p_aout->p_sys->b_buffer_underflown )
343 /* there has been an underflow so we need to play the new sample
344 * as soon as possible. This is why we query the play position */
345 dsresult = IDirectSoundBuffer_GetCurrentPosition(
346 p_aout->p_sys->p_dsbuffer,
348 &p_aout->p_sys->l_write_position );
349 if( dsresult != DS_OK )
351 intf_WarnMsg( 3, "aout: aout_Play can'get buffer position");
352 p_aout->p_sys->l_write_position = 0;
355 intf_WarnMsg( 3, "aout: aout_Play underflow");
356 /* reinitialise the underflow detection counters */
357 p_aout->p_sys->b_buffer_underflown = 0;
358 p_aout->p_sys->l_data_written_from_beginning = 0;
360 #define WRITE_P p_aout->p_sys->l_write_position
361 #define PLAY_P l_play_position
362 #define BUF_SIZE p_aout->p_sys->l_buffer_size
363 p_aout->p_sys->l_data_played_from_beginning = -(WRITE_P %(BUF_SIZE/2));
364 if( PLAY_P < BUF_SIZE/2 && WRITE_P > BUF_SIZE/2 )
366 p_aout->p_sys->l_data_played_from_beginning -= (BUF_SIZE/2);
368 if( PLAY_P > BUF_SIZE/2 && WRITE_P < BUF_SIZE/2 )
370 p_aout->p_sys->l_data_played_from_beginning -= (BUF_SIZE/2);
377 /* Before copying anything, we have to lock the buffer */
378 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
379 p_aout->p_sys->l_write_position, /* Offset of lock start */
380 i_size, /* Number of bytes to lock */
381 &p_write_position, /* Address of lock start */
382 &l_bytes1, /* Count of bytes locked before wrap around */
383 &p_start_buffer, /* Buffer adress (if wrap around) */
384 &l_bytes2, /* Count of bytes after wrap around */
386 if( dsresult == DSERR_BUFFERLOST )
388 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
389 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
390 p_aout->p_sys->l_write_position,
399 if( dsresult != DS_OK )
401 intf_WarnMsg( 3, "aout: DirectX aout_Play can't lock buffer");
402 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
406 /* Now do the actual memcpy (two memcpy because the buffer is circular) */
407 memcpy( p_write_position, buffer, l_bytes1 );
408 if( p_start_buffer != NULL )
410 memcpy( p_start_buffer, buffer + l_bytes1, l_bytes2 );
413 /* Now the data has been copied, unlock the buffer */
414 IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer,
415 p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
417 /* Update the write position index of the buffer*/
418 p_aout->p_sys->l_write_position += i_size;
419 p_aout->p_sys->l_write_position %= p_aout->p_sys->l_buffer_size;
420 p_aout->p_sys->l_data_written_from_beginning += i_size;
422 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
424 /* The play function has no effect if the buffer is already playing */
425 dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
428 DSBPLAY_LOOPING ); /* Flags */
429 if( dsresult == DSERR_BUFFERLOST )
431 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
432 dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
435 DSBPLAY_LOOPING ); /* Flags */
437 if( dsresult != DS_OK )
439 intf_WarnMsg( 3, "aout: DirectX aout_Play can't play buffer");
445 /*****************************************************************************
446 * aout_Close: close the audio device
447 *****************************************************************************/
448 static void aout_Close( aout_thread_t *p_aout )
451 intf_WarnMsg( 3, "aout: DirectX aout_Close ");
453 /* kill the position notification thread */
454 p_aout->p_sys->b_notification_thread_die = 1;
455 SetEvent( p_aout->p_sys->notification_events[0].hEventNotify );
456 vlc_thread_join( p_aout->p_sys->notification_thread_id );
457 vlc_mutex_destroy( &p_aout->p_sys->buffer_lock );
459 /* release the secondary buffer */
460 DirectxDestroySecondaryBuffer( p_aout );
462 /* then release the primary buffer */
463 if( p_aout->p_sys->p_dsbuffer_primary != NULL )
465 IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer_primary );
466 p_aout->p_sys->p_dsbuffer_primary = NULL;
469 /* finally release the DirectSound object */
470 if( p_aout->p_sys->p_dsobject != NULL )
472 IDirectSound_Release( p_aout->p_sys->p_dsobject );
473 p_aout->p_sys->p_dsobject = NULL;
476 /* free DSOUND.DLL */
477 if( p_aout->p_sys->hdsound_dll != NULL )
479 FreeLibrary( p_aout->p_sys->hdsound_dll );
480 p_aout->p_sys->hdsound_dll = NULL;
483 /* Close the Output. */
484 if ( p_aout->p_sys != NULL )
486 free( p_aout->p_sys );
487 p_aout->p_sys = NULL;
491 /*****************************************************************************
493 *****************************************************************************
494 *****************************************************************************/
495 static int DirectxInitDSound( aout_thread_t *p_aout )
497 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
499 p_aout->p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
500 if( p_aout->p_sys->hdsound_dll == NULL )
502 intf_WarnMsg( 3, "aout: can't open DSOUND.DLL ");
506 OurDirectSoundCreate = (void *)GetProcAddress( p_aout->p_sys->hdsound_dll,
507 "DirectSoundCreate" );
509 if( OurDirectSoundCreate == NULL )
511 intf_WarnMsg( 3, "aout: GetProcAddress FAILED ");
512 FreeLibrary( p_aout->p_sys->hdsound_dll );
513 p_aout->p_sys->hdsound_dll = NULL;
517 /* Create the direct sound object */
518 if( OurDirectSoundCreate(NULL, &p_aout->p_sys->p_dsobject, NULL) != DS_OK )
520 intf_WarnMsg( 3, "aout: can't create a direct sound device ");
521 p_aout->p_sys->p_dsobject = NULL;
522 FreeLibrary( p_aout->p_sys->hdsound_dll );
523 p_aout->p_sys->hdsound_dll = NULL;
527 /* Set DirectSound Cooperative level, ie what control we want over Windows
528 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
529 * settings of the primary buffer, but also that only the sound of our
530 * application will be hearable when it will have the focus.
531 * !!! (this is not really working as intended yet because to set the
532 * cooperative level you need the window handle of your application, and
533 * I don't know of any easy way to get it. Especially since we might play
534 * sound without any video, and so what window handle should we use ???
535 * The hack for now is to use the Desktop window handle - it seems to be
537 if( IDirectSound_SetCooperativeLevel(p_aout->p_sys->p_dsobject,
541 intf_WarnMsg( 3, "aout: can't set direct sound cooperative level ");
547 /*****************************************************************************
548 * DirectxCreateSecondaryBuffer
549 *****************************************************************************
550 * This function creates the buffer we'll use to play audio.
551 * In DirectSound there are two kinds of buffers:
552 * - the primary buffer: which is the actual buffer that the soundcard plays
553 * - the secondary buffer(s): these buffers are the one actually used by
554 * applications and DirectSound takes care of mixing them into the primary.
556 * Once you create a secondary buffer, you cannot change its format anymore so
557 * you have to release the current and create another one.
558 *****************************************************************************/
559 static int DirectxCreateSecondaryBuffer( aout_thread_t *p_aout )
561 WAVEFORMATEX waveformat;
562 DSBUFFERDESC dsbdesc;
565 /* First set the buffer format */
566 memset(&waveformat, 0, sizeof(WAVEFORMATEX));
567 waveformat.wFormatTag = WAVE_FORMAT_PCM;
568 waveformat.nChannels = p_aout->i_channels;
569 waveformat.nSamplesPerSec = p_aout->l_rate;
570 waveformat.wBitsPerSample = 16;
571 waveformat.nBlockAlign = waveformat.wBitsPerSample / 8 *
572 waveformat.nChannels;
573 waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec *
574 waveformat.nBlockAlign;
576 /* Then fill in the descriptor */
577 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
578 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
579 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
580 | DSBCAPS_CTRLPOSITIONNOTIFY /* We need notification */
581 | DSBCAPS_GLOBALFOCUS; /* Allows background playing */
582 dsbdesc.dwBufferBytes = waveformat.nAvgBytesPerSec * 2; /* 2 sec buffer */
583 dsbdesc.lpwfxFormat = &waveformat;
585 if( IDirectSound_CreateSoundBuffer( p_aout->p_sys->p_dsobject,
587 &p_aout->p_sys->p_dsbuffer,
590 intf_WarnMsg( 3, "aout: can't create direct sound secondary buffer ");
591 p_aout->p_sys->p_dsbuffer = NULL;
595 /* backup the size of the secondary sound buffer */
596 memset(&dsbcaps, 0, sizeof(DSBCAPS));
597 dsbcaps.dwSize = sizeof(DSBCAPS);
598 IDirectSoundBuffer_GetCaps( p_aout->p_sys->p_dsbuffer, &dsbcaps );
599 p_aout->p_sys->l_buffer_size = dsbcaps.dwBufferBytes;
600 p_aout->p_sys->l_write_position = 0;
602 intf_WarnMsg( 3, "aout: DirectX DirectxCreateSecondaryBuffer: %li",
603 p_aout->p_sys->l_buffer_size);
605 /* Now the secondary buffer is created, we need to setup its position
607 p_aout->p_sys->notification_events[0].dwOffset = 0; /* notif position */
608 p_aout->p_sys->notification_events[1].dwOffset = dsbcaps.dwBufferBytes / 2;
610 /* Get the IDirectSoundNotify interface */
611 if FAILED( IDirectSoundBuffer_QueryInterface( p_aout->p_sys->p_dsbuffer,
612 &IID_IDirectSoundNotify,
613 (LPVOID *)&p_aout->p_sys->p_dsnotify ) )
615 intf_WarnMsg( 3, "aout: DirectX can't get Notify interface" );
617 p_aout->p_sys->p_dsnotify = NULL;
621 if FAILED( IDirectSoundNotify_SetNotificationPositions(
622 p_aout->p_sys->p_dsnotify,
624 p_aout->p_sys->notification_events ) )
626 intf_WarnMsg( 3, "aout: DirectX can't set position Notification" );
633 /*****************************************************************************
634 * DirectxCreateSecondaryBuffer
635 *****************************************************************************
636 * This function destroy the secondary buffer.
637 *****************************************************************************/
638 static void DirectxDestroySecondaryBuffer( aout_thread_t *p_aout )
640 /* make sure the buffer isn't playing */
641 if( p_aout->p_sys->p_dsbuffer != NULL )
643 IDirectSoundBuffer_Stop( p_aout->p_sys->p_dsbuffer );
646 if( p_aout->p_sys->p_dsnotify != NULL )
648 IDirectSoundNotify_Release( p_aout->p_sys->p_dsnotify );
649 p_aout->p_sys->p_dsnotify = NULL;
652 if( p_aout->p_sys->p_dsbuffer != NULL )
654 IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer );
655 p_aout->p_sys->p_dsbuffer = NULL;
659 /*****************************************************************************
660 * DirectSoundThread: this thread will capture play notification events.
661 *****************************************************************************
662 * As Direct Sound uses circular buffers, we need to use event notification to
664 * Using event notification implies blocking the thread until the event is
665 * signaled so we really need to run this in a separate thread.
666 *****************************************************************************/
667 static void DirectSoundThread( aout_thread_t *p_aout )
669 HANDLE notification_events[2];
670 VOID *p_write_position, *p_start_buffer;
671 long l_bytes1, l_bytes2;
673 long l_buffer_size, l_play_position, l_data_in_buffer;
675 notification_events[0]=p_aout->p_sys->notification_events[0].hEventNotify;
676 notification_events[1]=p_aout->p_sys->notification_events[1].hEventNotify;
678 /* this thread must be high-priority */
679 if( !SetThreadPriority( GetCurrentThread(),
680 THREAD_PRIORITY_ABOVE_NORMAL ) )
682 intf_WarnMsg( 3, "aout: DirectSoundThread could not renice itself" );
685 intf_WarnMsg( 3, "aout: DirectSoundThread ready" );
687 while( !p_aout->p_sys->b_notification_thread_die )
689 /* wait for the position notification */
690 l_play_position = WaitForMultipleObjects( 2, notification_events,
692 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
694 if( p_aout->p_sys->b_notification_thread_die )
699 /* check for buffer underflow (bodge for wrap around) */
700 l_buffer_size = p_aout->p_sys->l_buffer_size;
701 l_play_position = (l_play_position - WAIT_OBJECT_0) * l_buffer_size/2;
702 p_aout->p_sys->l_data_played_from_beginning += (l_buffer_size/2);
703 l_data_in_buffer = p_aout->p_sys->l_data_written_from_beginning -
704 p_aout->p_sys->l_data_played_from_beginning;
706 /* detect wrap-around */
707 if( l_data_in_buffer < (-l_buffer_size/2) )
709 intf_WarnMsg(3,"aout: DirectSoundThread wrap around: %li", l_data_in_buffer);
710 l_data_in_buffer += l_buffer_size;
713 /* detect underflow */
714 if( l_data_in_buffer <= 0 )
716 intf_WarnMsg(3,"aout: DirectSoundThread underflow: %li", l_data_in_buffer);
717 p_aout->p_sys->b_buffer_underflown = 1;
718 p_aout->p_sys->l_write_position =
719 (l_play_position + l_buffer_size/2) % l_buffer_size;
720 l_data_in_buffer = l_buffer_size / 2;
721 p_aout->p_sys->l_data_played_from_beginning -= (l_buffer_size/2);
725 /* Clear the data which has already been played */
727 /* Before copying anything, we have to lock the buffer */
728 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
729 p_aout->p_sys->l_write_position, /* Offset of lock start */
730 l_buffer_size - l_data_in_buffer, /* Number of bytes */
731 &p_write_position, /* Address of lock start */
732 &l_bytes1, /* Count of bytes locked before wrap around */
733 &p_start_buffer, /* Buffer adress (if wrap around) */
734 &l_bytes2, /* Count of bytes after wrap around */
736 if( dsresult == DSERR_BUFFERLOST )
738 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
739 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
740 p_aout->p_sys->l_write_position,
741 l_buffer_size - l_data_in_buffer,
748 if( dsresult != DS_OK )
750 intf_WarnMsg( 3, "aout: DirectX aout_Play can't lock buffer");
751 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
755 /* Now do the actual memcpy (two because the buffer is circular) */
756 memset( p_write_position, 0, l_bytes1 );
757 if( p_start_buffer != NULL )
759 memset( p_start_buffer, 0, l_bytes2 );
762 /* Now the data has been copied, unlock the buffer */
763 IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer,
764 p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
766 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
770 /* free the events */
771 CloseHandle( notification_events[0] );
772 CloseHandle( notification_events[1] );
774 intf_WarnMsg( 3, "aout: DirectSoundThread exiting" );