1 /*****************************************************************************
2 * aout_directx.c: Windows DirectX audio output method
3 *****************************************************************************
4 * Copyright (C) 1999, 2000 VideoLAN
5 * $Id: aout_directx.c,v 1.10 2001/08/05 18:57:59 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 #define MODULE_NAME directx
25 #include "modules_inner.h"
28 /*****************************************************************************
30 *****************************************************************************/
33 #include <errno.h> /* ENOMEM */
34 #include <fcntl.h> /* open(), O_WRONLY */
35 #include <string.h> /* strerror() */
37 #include <stdio.h> /* "intf_msg.h" */
38 #include <stdlib.h> /* calloc(), malloc(), free() */
41 #include "common.h" /* boolean_t, byte_t */
46 #if defined( _MSC_VER )
52 #include "audio_output.h" /* aout_thread_t */
54 #include "intf_msg.h" /* intf_DbgMsg(), intf_ErrMsg() */
58 #include "modules_export.h"
60 /*****************************************************************************
61 * aout_sys_t: directx audio output method descriptor
62 *****************************************************************************
63 * This structure is part of the audio output thread descriptor.
64 * It describes the direct sound specific properties of an audio device.
65 *****************************************************************************/
67 typedef struct aout_sys_s
69 LPDIRECTSOUND p_dsobject; /* main Direct Sound object */
71 LPDIRECTSOUNDBUFFER p_dsbuffer_primary; /* the actual sound card buffer
72 (not used directly) */
74 LPDIRECTSOUNDBUFFER p_dsbuffer; /* the sound buffer we use (direct sound
75 * takes care of mixing all the
76 * secondary buffers into the primary) */
78 LPDIRECTSOUNDNOTIFY p_dsnotify; /* the position notify interface */
80 HINSTANCE hdsound_dll; /* handle of the opened dsound dll */
82 long l_buffer_size; /* secondary sound buffer size */
83 long l_write_position; /* next write position for the buffer */
85 volatile boolean_t b_buffer_underflown; /* buffer underflow detection */
86 volatile long l_data_played_from_beginning; /* for underflow detection */
87 volatile long l_data_written_from_beginning; /* for underflow detection */
89 vlc_mutex_t buffer_lock; /* audio buffer lock */
91 vlc_thread_t notification_thread_id; /* DirectSoundThread id */
93 DSBPOSITIONNOTIFY notification_events[2]; /* play notification events */
95 boolean_t b_notification_thread_die; /* flag to kill the thread */
99 /*****************************************************************************
101 *****************************************************************************/
102 static int aout_Probe ( probedata_t *p_data );
103 static int aout_Open ( aout_thread_t *p_aout );
104 static int aout_SetFormat ( aout_thread_t *p_aout );
105 static long aout_GetBufInfo ( aout_thread_t *p_aout, long l_buffer_info );
106 static void aout_Play ( aout_thread_t *p_aout,
107 byte_t *buffer, int i_size );
108 static void aout_Close ( aout_thread_t *p_aout );
110 /* local functions */
111 static int DirectxCreateSecondaryBuffer ( aout_thread_t *p_aout );
112 static void DirectxDestroySecondaryBuffer( aout_thread_t *p_aout );
113 static int DirectxInitDSound ( aout_thread_t *p_aout );
114 static void DirectSoundThread ( aout_thread_t *p_aout );
116 /*****************************************************************************
117 * Functions exported as capabilities. They are declared as static so that
118 * we don't pollute the namespace too much.
119 *****************************************************************************/
120 void _M( aout_getfunctions )( function_list_t * p_function_list )
122 p_function_list->pf_probe = aout_Probe;
123 p_function_list->functions.aout.pf_open = aout_Open;
124 p_function_list->functions.aout.pf_setformat = aout_SetFormat;
125 p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
126 p_function_list->functions.aout.pf_play = aout_Play;
127 p_function_list->functions.aout.pf_close = aout_Close;
130 /*****************************************************************************
131 * aout_Probe: probe the audio device and return a score
132 *****************************************************************************
133 * This function tries to probe for a Direct Sound device and returns a
134 * score to the plugin manager so that it can select the best plugin.
135 *****************************************************************************/
136 static int aout_Probe( probedata_t *p_data )
138 /* For now just assume the computer has a sound device */
139 if( TestMethod( AOUT_METHOD_VAR, "directx" ) )
146 /*****************************************************************************
147 * aout_Open: open the audio device
148 *****************************************************************************
149 * This function opens and setups Direct Sound.
150 *****************************************************************************/
151 static int aout_Open( aout_thread_t *p_aout )
154 DSBUFFERDESC dsbuffer_desc;
156 intf_WarnMsg( 3, "aout: DirectX aout_Open ");
158 /* Allocate structure */
159 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
161 if( p_aout->p_sys == NULL )
163 intf_ErrMsg( "aout error: %s", strerror(ENOMEM) );
167 /* Initialize some variables */
168 p_aout->p_sys->p_dsobject = NULL;
169 p_aout->p_sys->p_dsbuffer_primary = NULL;
170 p_aout->p_sys->p_dsbuffer = NULL;
171 p_aout->p_sys->p_dsnotify = NULL;
172 p_aout->p_sys->b_notification_thread_die = 0;
173 p_aout->p_sys->l_data_written_from_beginning = 0;
174 p_aout->p_sys->l_data_played_from_beginning = 0;
175 vlc_mutex_init( &p_aout->p_sys->buffer_lock );
178 p_aout->psz_device = 0;
179 p_aout->i_format = AOUT_FORMAT_DEFAULT;
180 p_aout->i_channels = 1 + main_GetIntVariable( AOUT_STEREO_VAR,
181 AOUT_STEREO_DEFAULT );
182 p_aout->l_rate = main_GetIntVariable( AOUT_RATE_VAR,
185 /* Initialise DirectSound */
186 if( DirectxInitDSound( p_aout ) )
188 intf_WarnMsg( 3, "aout: can't initialise DirectSound ");
192 /* Obtain (not create) Direct Sound primary buffer */
193 memset( &dsbuffer_desc, 0, sizeof(DSBUFFERDESC) );
194 dsbuffer_desc.dwSize = sizeof(DSBUFFERDESC);
195 dsbuffer_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
196 intf_WarnMsg( 3, "aout: Create direct sound primary buffer ");
197 dsresult = IDirectSound_CreateSoundBuffer(p_aout->p_sys->p_dsobject,
199 &p_aout->p_sys->p_dsbuffer_primary,
201 if( dsresult != DS_OK )
203 intf_WarnMsg( 3, "aout: can't create direct sound primary buffer ");
204 IDirectSound_Release( p_aout->p_sys->p_dsobject );
205 p_aout->p_sys->p_dsobject = NULL;
206 p_aout->p_sys->p_dsbuffer_primary = NULL;
211 /* Now we need to setup DirectSound play notification */
213 /* first we need to create the notification events */
214 p_aout->p_sys->notification_events[0].hEventNotify =
215 CreateEvent( NULL, FALSE, FALSE, NULL );
216 p_aout->p_sys->notification_events[1].hEventNotify =
217 CreateEvent( NULL, FALSE, FALSE, NULL );
219 /* then launch the notification thread */
220 intf_WarnMsg( 3, "aout: aout_Open creating DirectSoundThread" );
221 if( vlc_thread_create( &p_aout->p_sys->notification_thread_id,
222 "DirectSound Notification Thread",
223 (void *) DirectSoundThread, (void *) p_aout) )
225 intf_ErrMsg( "aout error: can't create DirectSoundThread" );
226 intf_ErrMsg("aout error: %s", strerror(ENOMEM));
227 /* Let's go on anyway */
233 /*****************************************************************************
234 * aout_SetFormat: reset the audio device and sets its format
235 *****************************************************************************
236 * This functions set a new audio format.
237 * For this we need to close the current secondary buffer and create another
238 * one with the desired format.
239 *****************************************************************************/
240 static int aout_SetFormat( aout_thread_t *p_aout )
243 WAVEFORMATEX *p_waveformat;
244 unsigned long i_size_struct;
246 intf_WarnMsg( 3, "aout: DirectX aout_SetFormat ");
248 /* Set the format of Direct Sound primary buffer */
250 /* first we need to know the current format */
251 dsresult = IDirectSoundBuffer_GetFormat( p_aout->p_sys->p_dsbuffer_primary,
252 NULL, 0, &i_size_struct );
253 if( dsresult == DS_OK )
255 p_waveformat = malloc( i_size_struct );
256 dsresult = IDirectSoundBuffer_GetFormat(
257 p_aout->p_sys->p_dsbuffer_primary,
258 p_waveformat, i_size_struct,
262 if( dsresult == DS_OK )
264 /* Here we'll change the format */
265 p_waveformat->nChannels = 2;
266 p_waveformat->nSamplesPerSec = (p_aout->l_rate < 44100) ? 44100
268 p_waveformat->wBitsPerSample = 16;
269 p_waveformat->nBlockAlign = p_waveformat->wBitsPerSample / 8 *
270 p_waveformat->nChannels;
271 p_waveformat->nAvgBytesPerSec = p_waveformat->nSamplesPerSec *
272 p_waveformat->nBlockAlign;
274 dsresult = IDirectSoundBuffer_SetFormat(
275 p_aout->p_sys->p_dsbuffer_primary,
278 else intf_WarnMsg( 3, "aout: can't get primary buffer format" );
280 if( dsresult != DS_OK )
281 intf_WarnMsg( 3, "aout: can't set primary buffer format" );
284 /* Now we need to take care of Direct Sound secondary buffer */
286 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
288 /* first release the current secondary buffer */
289 DirectxDestroySecondaryBuffer( p_aout );
291 /* then create a new secondary buffer */
292 if( DirectxCreateSecondaryBuffer( p_aout ) )
294 intf_WarnMsg( 3, "aout: DirectX aout_SetFormat cannot create buffer");
295 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
299 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
301 p_aout->i_latency = 0;
306 /*****************************************************************************
307 * aout_GetBufInfo: buffer status query
308 *****************************************************************************
309 * returns the number of bytes in the audio buffer that have not yet been
310 * sent to the sound device.
311 *****************************************************************************/
312 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
314 long l_play_position, l_notused, l_result;
317 if( p_aout->p_sys->b_buffer_underflown )
319 intf_WarnMsg( 3, "aout: DirectX aout_GetBufInfo underflow");
320 return( l_buffer_limit );
323 dsresult = IDirectSoundBuffer_GetCurrentPosition(p_aout->p_sys->p_dsbuffer,
324 &l_play_position, &l_notused);
325 if( dsresult != DS_OK )
327 intf_WarnMsg(3,"aout: DirectX aout_GetBufInfo cannot get current pos");
328 return( l_buffer_limit );
331 l_result = (p_aout->p_sys->l_write_position >= l_play_position) ?
332 (p_aout->p_sys->l_write_position - l_play_position)
333 : (p_aout->p_sys->l_buffer_size - l_play_position
334 + p_aout->p_sys->l_write_position);
337 intf_WarnMsg( 3, "aout: DirectX aout_GetBufInfo: %li", l_result);
342 /*****************************************************************************
343 * aout_Play: play a sound buffer
344 *****************************************************************************
345 * This function writes a buffer of i_length bytes
346 * Don't forget that DirectSound buffers are circular buffers.
347 *****************************************************************************/
348 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
350 VOID *p_write_position, *p_start_buffer;
351 long l_bytes1, l_bytes2, l_play_position;
354 /* protect buffer access (because of DirectSoundThread) */
355 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
357 if( p_aout->p_sys->b_buffer_underflown )
359 /* there has been an underflow so we need to play the new sample
360 * as soon as possible. This is why we query the play position */
361 dsresult = IDirectSoundBuffer_GetCurrentPosition(
362 p_aout->p_sys->p_dsbuffer,
364 &p_aout->p_sys->l_write_position );
365 if( dsresult != DS_OK )
367 intf_WarnMsg( 3, "aout: aout_Play can'get buffer position");
368 p_aout->p_sys->l_write_position = 0;
371 intf_WarnMsg( 3, "aout: aout_Play underflow");
372 /* reinitialise the underflow detection counters */
373 p_aout->p_sys->b_buffer_underflown = 0;
374 p_aout->p_sys->l_data_written_from_beginning = 0;
376 #define WRITE_P p_aout->p_sys->l_write_position
377 #define PLAY_P l_play_position
378 #define BUF_SIZE p_aout->p_sys->l_buffer_size
379 p_aout->p_sys->l_data_played_from_beginning = -(WRITE_P %(BUF_SIZE/2));
380 if( PLAY_P < BUF_SIZE/2 && WRITE_P > BUF_SIZE/2 )
382 p_aout->p_sys->l_data_played_from_beginning -= (BUF_SIZE/2);
384 if( PLAY_P > BUF_SIZE/2 && WRITE_P < BUF_SIZE/2 )
386 p_aout->p_sys->l_data_played_from_beginning -= (BUF_SIZE/2);
393 /* Before copying anything, we have to lock the buffer */
394 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
395 p_aout->p_sys->l_write_position, /* Offset of lock start */
396 i_size, /* Number of bytes to lock */
397 &p_write_position, /* Address of lock start */
398 &l_bytes1, /* Count of bytes locked before wrap around */
399 &p_start_buffer, /* Buffer adress (if wrap around) */
400 &l_bytes2, /* Count of bytes after wrap around */
402 if( dsresult == DSERR_BUFFERLOST )
404 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
405 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
406 p_aout->p_sys->l_write_position,
415 if( dsresult != DS_OK )
417 intf_WarnMsg( 3, "aout: DirectX aout_Play can't lock buffer");
418 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
422 /* Now do the actual memcpy (two memcpy because the buffer is circular) */
423 memcpy( p_write_position, buffer, l_bytes1 );
424 if( p_start_buffer != NULL )
426 memcpy( p_start_buffer, buffer + l_bytes1, l_bytes2 );
429 /* Now the data has been copied, unlock the buffer */
430 IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer,
431 p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
433 /* Update the write position index of the buffer*/
434 p_aout->p_sys->l_write_position += i_size;
435 p_aout->p_sys->l_write_position %= p_aout->p_sys->l_buffer_size;
436 p_aout->p_sys->l_data_written_from_beginning += i_size;
438 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
440 /* The play function has no effect if the buffer is already playing */
441 dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
444 DSBPLAY_LOOPING ); /* Flags */
445 if( dsresult == DSERR_BUFFERLOST )
447 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
448 dsresult = IDirectSoundBuffer_Play( p_aout->p_sys->p_dsbuffer,
451 DSBPLAY_LOOPING ); /* Flags */
453 if( dsresult != DS_OK )
455 intf_WarnMsg( 3, "aout: DirectX aout_Play can't play buffer");
461 /*****************************************************************************
462 * aout_Close: close the audio device
463 *****************************************************************************/
464 static void aout_Close( aout_thread_t *p_aout )
467 intf_WarnMsg( 3, "aout: DirectX aout_Close ");
469 /* kill the position notification thread */
470 p_aout->p_sys->b_notification_thread_die = 1;
471 SetEvent( p_aout->p_sys->notification_events[0].hEventNotify );
472 vlc_thread_join( p_aout->p_sys->notification_thread_id );
473 vlc_mutex_destroy( &p_aout->p_sys->buffer_lock );
475 /* release the secondary buffer */
476 DirectxDestroySecondaryBuffer( p_aout );
478 /* then release the primary buffer */
479 if( p_aout->p_sys->p_dsbuffer_primary != NULL )
481 IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer_primary );
482 p_aout->p_sys->p_dsbuffer_primary = NULL;
485 /* finally release the DirectSound object */
486 if( p_aout->p_sys->p_dsobject != NULL )
488 IDirectSound_Release( p_aout->p_sys->p_dsobject );
489 p_aout->p_sys->p_dsobject = NULL;
492 /* free DSOUND.DLL */
493 if( p_aout->p_sys->hdsound_dll != NULL )
495 FreeLibrary( p_aout->p_sys->hdsound_dll );
496 p_aout->p_sys->hdsound_dll = NULL;
499 /* Close the Output. */
500 if ( p_aout->p_sys != NULL )
502 free( p_aout->p_sys );
503 p_aout->p_sys = NULL;
507 /*****************************************************************************
509 *****************************************************************************
510 *****************************************************************************/
511 static int DirectxInitDSound( aout_thread_t *p_aout )
513 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
515 p_aout->p_sys->hdsound_dll = LoadLibrary("DSOUND.DLL");
516 if( p_aout->p_sys->hdsound_dll == NULL )
518 intf_WarnMsg( 3, "aout: can't open DSOUND.DLL ");
522 OurDirectSoundCreate = (void *)GetProcAddress( p_aout->p_sys->hdsound_dll,
523 "DirectSoundCreate" );
525 if( OurDirectSoundCreate == NULL )
527 intf_WarnMsg( 3, "aout: GetProcAddress FAILED ");
528 FreeLibrary( p_aout->p_sys->hdsound_dll );
529 p_aout->p_sys->hdsound_dll = NULL;
533 /* Create the direct sound object */
534 if( OurDirectSoundCreate(NULL, &p_aout->p_sys->p_dsobject, NULL) != DS_OK )
536 intf_WarnMsg( 3, "aout: can't create a direct sound device ");
537 p_aout->p_sys->p_dsobject = NULL;
538 FreeLibrary( p_aout->p_sys->hdsound_dll );
539 p_aout->p_sys->hdsound_dll = NULL;
543 /* Set DirectSound Cooperative level, ie what control we want over Windows
544 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
545 * settings of the primary buffer, but also that only the sound of our
546 * application will be hearable when it will have the focus.
547 * !!! (this is not really working as intended yet because to set the
548 * cooperative level you need the window handle of your application, and
549 * I don't know of any easy way to get it. Especially since we might play
550 * sound without any video, and so what window handle should we use ???
551 * The hack for now is to use the Desktop window handle - it seems to be
553 if( IDirectSound_SetCooperativeLevel(p_aout->p_sys->p_dsobject,
557 intf_WarnMsg( 3, "aout: can't set direct sound cooperative level ");
563 /*****************************************************************************
564 * DirectxCreateSecondaryBuffer
565 *****************************************************************************
566 * This function creates the buffer we'll use to play audio.
567 * In DirectSound there are two kinds of buffers:
568 * - the primary buffer: which is the actual buffer that the soundcard plays
569 * - the secondary buffer(s): these buffers are the one actually used by
570 * applications and DirectSound takes care of mixing them into the primary.
572 * Once you create a secondary buffer, you cannot change its format anymore so
573 * you have to release the current and create another one.
574 *****************************************************************************/
575 static int DirectxCreateSecondaryBuffer( aout_thread_t *p_aout )
577 WAVEFORMATEX waveformat;
578 DSBUFFERDESC dsbdesc;
581 /* First set the buffer format */
582 memset(&waveformat, 0, sizeof(WAVEFORMATEX));
583 waveformat.wFormatTag = WAVE_FORMAT_PCM;
584 waveformat.nChannels = p_aout->i_channels;
585 waveformat.nSamplesPerSec = p_aout->l_rate;
586 waveformat.wBitsPerSample = 16;
587 waveformat.nBlockAlign = waveformat.wBitsPerSample / 8 *
588 waveformat.nChannels;
589 waveformat.nAvgBytesPerSec = waveformat.nSamplesPerSec *
590 waveformat.nBlockAlign;
592 /* Then fill in the descriptor */
593 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
594 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
595 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */
596 | DSBCAPS_CTRLPOSITIONNOTIFY /* We need notification */
597 | DSBCAPS_GLOBALFOCUS; /* Allows background playing */
598 dsbdesc.dwBufferBytes = waveformat.nAvgBytesPerSec * 2; /* 2 sec buffer */
599 dsbdesc.lpwfxFormat = &waveformat;
601 if( IDirectSound_CreateSoundBuffer( p_aout->p_sys->p_dsobject,
603 &p_aout->p_sys->p_dsbuffer,
606 intf_WarnMsg( 3, "aout: can't create direct sound secondary buffer ");
607 p_aout->p_sys->p_dsbuffer = NULL;
611 /* backup the size of the secondary sound buffer */
612 memset(&dsbcaps, 0, sizeof(DSBCAPS));
613 dsbcaps.dwSize = sizeof(DSBCAPS);
614 IDirectSoundBuffer_GetCaps( p_aout->p_sys->p_dsbuffer, &dsbcaps );
615 p_aout->p_sys->l_buffer_size = dsbcaps.dwBufferBytes;
616 p_aout->p_sys->l_write_position = 0;
618 intf_WarnMsg( 3, "aout: DirectX DirectxCreateSecondaryBuffer: %li",
619 p_aout->p_sys->l_buffer_size);
621 /* Now the secondary buffer is created, we need to setup its position
623 p_aout->p_sys->notification_events[0].dwOffset = 0; /* notif position */
624 p_aout->p_sys->notification_events[1].dwOffset = dsbcaps.dwBufferBytes / 2;
626 /* Get the IDirectSoundNotify interface */
627 if FAILED( IDirectSoundBuffer_QueryInterface( p_aout->p_sys->p_dsbuffer,
628 &IID_IDirectSoundNotify,
629 (LPVOID *)&p_aout->p_sys->p_dsnotify ) )
631 intf_WarnMsg( 3, "aout: DirectX can't get Notify interface" );
633 p_aout->p_sys->p_dsnotify = NULL;
637 if FAILED( IDirectSoundNotify_SetNotificationPositions(
638 p_aout->p_sys->p_dsnotify,
640 p_aout->p_sys->notification_events ) )
642 intf_WarnMsg( 3, "aout: DirectX can't set position Notification" );
649 /*****************************************************************************
650 * DirectxCreateSecondaryBuffer
651 *****************************************************************************
652 * This function destroy the secondary buffer.
653 *****************************************************************************/
654 static void DirectxDestroySecondaryBuffer( aout_thread_t *p_aout )
656 /* make sure the buffer isn't playing */
657 if( p_aout->p_sys->p_dsbuffer != NULL )
659 IDirectSoundBuffer_Stop( p_aout->p_sys->p_dsbuffer );
662 if( p_aout->p_sys->p_dsnotify != NULL )
664 IDirectSoundNotify_Release( p_aout->p_sys->p_dsnotify );
665 p_aout->p_sys->p_dsnotify = NULL;
668 if( p_aout->p_sys->p_dsbuffer != NULL )
670 IDirectSoundBuffer_Release( p_aout->p_sys->p_dsbuffer );
671 p_aout->p_sys->p_dsbuffer = NULL;
675 /*****************************************************************************
676 * DirectSoundThread: this thread will capture play notification events.
677 *****************************************************************************
678 * As Direct Sound uses circular buffers, we need to use event notification to
680 * Using event notification implies blocking the thread until the event is
681 * signaled so we really need to run this in a separate thread.
682 *****************************************************************************/
683 static void DirectSoundThread( aout_thread_t *p_aout )
685 HANDLE notification_events[2];
686 VOID *p_write_position, *p_start_buffer;
687 long l_bytes1, l_bytes2;
689 long l_buffer_size, l_play_position, l_data_in_buffer;
691 notification_events[0]=p_aout->p_sys->notification_events[0].hEventNotify;
692 notification_events[1]=p_aout->p_sys->notification_events[1].hEventNotify;
694 /* this thread must be high-priority */
695 if( !SetThreadPriority( GetCurrentThread(),
696 THREAD_PRIORITY_ABOVE_NORMAL ) )
698 intf_WarnMsg( 3, "aout: DirectSoundThread could not renice itself" );
701 intf_WarnMsg( 3, "aout: DirectSoundThread ready" );
703 while( !p_aout->p_sys->b_notification_thread_die )
705 /* wait for the position notification */
706 l_play_position = WaitForMultipleObjects( 2, notification_events,
708 vlc_mutex_lock( &p_aout->p_sys->buffer_lock );
710 if( p_aout->p_sys->b_notification_thread_die )
715 /* check for buffer underflow (bodge for wrap around) */
716 l_buffer_size = p_aout->p_sys->l_buffer_size;
717 l_play_position = (l_play_position - WAIT_OBJECT_0) * l_buffer_size/2;
718 p_aout->p_sys->l_data_played_from_beginning += (l_buffer_size/2);
719 l_data_in_buffer = p_aout->p_sys->l_data_written_from_beginning -
720 p_aout->p_sys->l_data_played_from_beginning;
722 /* detect wrap-around */
723 if( l_data_in_buffer < (-l_buffer_size/2) )
725 intf_WarnMsg(3,"aout: DirectSoundThread wrap around: %li", l_data_in_buffer);
726 l_data_in_buffer += l_buffer_size;
729 /* detect underflow */
730 if( l_data_in_buffer <= 0 )
732 intf_WarnMsg(3,"aout: DirectSoundThread underflow: %li", l_data_in_buffer);
733 p_aout->p_sys->b_buffer_underflown = 1;
734 p_aout->p_sys->l_write_position =
735 (l_play_position + l_buffer_size/2) % l_buffer_size;
736 l_data_in_buffer = l_buffer_size / 2;
737 p_aout->p_sys->l_data_played_from_beginning -= (l_buffer_size/2);
741 /* Clear the data which has already been played */
743 /* Before copying anything, we have to lock the buffer */
744 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
745 p_aout->p_sys->l_write_position, /* Offset of lock start */
746 l_buffer_size - l_data_in_buffer, /* Number of bytes */
747 &p_write_position, /* Address of lock start */
748 &l_bytes1, /* Count of bytes locked before wrap around */
749 &p_start_buffer, /* Buffer adress (if wrap around) */
750 &l_bytes2, /* Count of bytes after wrap around */
752 if( dsresult == DSERR_BUFFERLOST )
754 IDirectSoundBuffer_Restore( p_aout->p_sys->p_dsbuffer );
755 dsresult = IDirectSoundBuffer_Lock( p_aout->p_sys->p_dsbuffer,
756 p_aout->p_sys->l_write_position,
757 l_buffer_size - l_data_in_buffer,
764 if( dsresult != DS_OK )
766 intf_WarnMsg( 3, "aout: DirectX aout_Play can't lock buffer");
767 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
771 /* Now do the actual memcpy (two because the buffer is circular) */
772 memset( p_write_position, 0, l_bytes1 );
773 if( p_start_buffer != NULL )
775 memset( p_start_buffer, 0, l_bytes2 );
778 /* Now the data has been copied, unlock the buffer */
779 IDirectSoundBuffer_Unlock( p_aout->p_sys->p_dsbuffer,
780 p_write_position, l_bytes1, p_start_buffer, l_bytes2 );
782 vlc_mutex_unlock( &p_aout->p_sys->buffer_lock );
786 /* free the events */
787 CloseHandle( notification_events[0] );
788 CloseHandle( notification_events[1] );
790 intf_WarnMsg( 3, "aout: DirectSoundThread exiting" );