1 /*****************************************************************************
2 * aout_directx.c: Windows DirectX audio output method
3 *****************************************************************************
4 * Copyright (C) 1999, 2000 VideoLAN
5 * $Id: aout_directx.c,v 1.1 2001/06/02 01:09:03 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 #define MODULE_NAME directx
25 #include "modules_inner.h"
27 /* The most important this to do for now is to fix the audio bug we've got
28 * on startup: the audio will start later than the video (sometimes) and
29 * is trying to catching up with it.
30 * At first sight it seems to be a scheduling problem
34 /*****************************************************************************
36 *****************************************************************************/
39 #include <errno.h> /* ENOMEM */
40 #include <fcntl.h> /* open(), O_WRONLY */
41 #include <string.h> /* strerror() */
42 #include <unistd.h> /* write(), close() */
43 #include <stdio.h> /* "intf_msg.h" */
44 #include <stdlib.h> /* calloc(), malloc(), free() */
47 #include "common.h" /* boolean_t, byte_t */
54 #include "audio_output.h" /* aout_thread_t */
56 #include "intf_msg.h" /* intf_DbgMsg(), intf_ErrMsg() */
61 /*****************************************************************************
62 * aout_sys_t: directx audio output method descriptor
63 *****************************************************************************
64 * This structure is part of the audio output thread descriptor.
65 * It describes the direct sound specific properties of an audio device.
66 *****************************************************************************/
68 typedef struct aout_sys_s
70 LPDIRECTSOUND p_dsobject
; /* main Direct Sound object */
72 LPDIRECTSOUNDBUFFER p_dsbuffer_primary
; /* the actual sound card buffer
73 (not used directly) */
75 LPDIRECTSOUNDBUFFER p_dsbuffer
; /* the sound buffer we use (direct sound
76 * takes care of mixing all the
77 * secondary buffers into the primary) */
79 long l_buffer_size
; /* secondary sound buffer size */
80 long l_write_position
; /* next write position for the buffer */
86 /*****************************************************************************
88 *****************************************************************************/
89 static int aout_Probe ( probedata_t
*p_data
);
90 static int aout_Open ( aout_thread_t
*p_aout
);
91 static int aout_SetFormat ( aout_thread_t
*p_aout
);
92 static long aout_GetBufInfo ( aout_thread_t
*p_aout
, long l_buffer_info
);
93 static void aout_Play ( aout_thread_t
*p_aout
,
94 byte_t
*buffer
, int i_size
);
95 static void aout_Close ( aout_thread_t
*p_aout
);
98 static int windx_CreateSecondaryBuffer( 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
)
139 DSBUFFERDESC dsbuffer_desc
;
140 WAVEFORMATEX waveformat
;
143 /* Allocate structure */
144 p_aout
->p_sys
= malloc( sizeof( aout_sys_t
) );
146 if( p_aout
->p_sys
== NULL
)
148 intf_ErrMsg( "aout error: %s", strerror(ENOMEM
) );
152 /* Initialize some variables */
153 p_aout
->p_sys
->p_dsobject
= NULL
;
154 p_aout
->p_sys
->p_dsbuffer_primary
= NULL
;
155 p_aout
->p_sys
->p_dsbuffer
= NULL
;
157 p_aout
->psz_device
= 0;
158 p_aout
->i_format
= AOUT_FORMAT_DEFAULT
;
159 p_aout
->i_channels
= 1 + main_GetIntVariable( AOUT_STEREO_VAR
,
160 AOUT_STEREO_DEFAULT
);
161 p_aout
->l_rate
= main_GetIntVariable( AOUT_RATE_VAR
,
164 /* Create the direct sound object */
165 if( DirectSoundCreate(NULL
, &p_aout
->p_sys
->p_dsobject
, NULL
) != DS_OK
)
167 intf_WarnMsg( 3, "aout: can't create a direct sound device ");
168 p_aout
->p_sys
->p_dsobject
= NULL
;
172 /* Set DirectSound Cooperative level, ie what control we want over Windows
173 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
174 * settings of the primary buffer, but also that only the sound of our
175 * application will be hearable when it will have the focus.
176 * !!! (this is not really working as intended yet because to set the
177 * cooperative level you need the window handle of your application, and
178 * I don't know of any easy way to get it. Especially since we might play
179 * sound without any video, and so what window handle should we use ???
180 * The hack for now is to use the Desktop window handle - it seems to be
182 if( IDirectSound_SetCooperativeLevel(p_aout
->p_sys
->p_dsobject
,
186 intf_WarnMsg( 3, "aout: can't set direct sound cooperative level ");
190 /* Obtain (not create) Direct Sound primary buffer */
191 memset( &dsbuffer_desc
, 0, sizeof(DSBUFFERDESC
) );
192 dsbuffer_desc
.dwSize
= sizeof(DSBUFFERDESC
);
193 dsbuffer_desc
.dwFlags
= DSBCAPS_PRIMARYBUFFER
;
194 intf_WarnMsg( 3, "aout: Create direct sound primary buffer ");
195 dsresult
= IDirectSound_CreateSoundBuffer(p_aout
->p_sys
->p_dsobject
,
197 &p_aout
->p_sys
->p_dsbuffer_primary
,
199 if( dsresult
!= DS_OK
)
201 intf_WarnMsg( 3, "aout: can't create direct sound primary buffer ");
202 IDirectSound_Release( p_aout
->p_sys
->p_dsobject
);
203 p_aout
->p_sys
->p_dsobject
= NULL
;
204 p_aout
->p_sys
->p_dsbuffer_primary
= NULL
;
208 /* Set Direct Sound primary buffer format because the default value set by
209 * Windows is usually not the high quality value */
210 memset(&waveformat
, 0, sizeof(WAVEFORMATEX
));
211 waveformat
.wFormatTag
= WAVE_FORMAT_PCM
;
212 waveformat
.nChannels
= 2;
213 waveformat
.nSamplesPerSec
= 44100;
214 waveformat
.wBitsPerSample
= 16;
215 waveformat
.nBlockAlign
= waveformat
.wBitsPerSample
/ 8 *
216 waveformat
.nChannels
;
217 waveformat
.nAvgBytesPerSec
= waveformat
.nSamplesPerSec
*
218 waveformat
.nBlockAlign
;
220 dsresult
= IDirectSoundBuffer_SetFormat(p_aout
->p_sys
->p_dsbuffer_primary
,
222 if( dsresult
!= DS_OK
)
224 intf_WarnMsg( 3, "aout: can't set primary buffer format");
227 /* ensure the primary buffer is playing. We won't actually hear anything
228 * until the secondary buffer is playing */
229 dsresult
= IDirectSoundBuffer_Play( p_aout
->p_sys
->p_dsbuffer_primary
,
233 if( dsresult
!= DS_OK
)
235 intf_WarnMsg( 3, "aout: can't play direct sound primary buffer ");
236 IDirectSound_Release( p_aout
->p_sys
->p_dsbuffer_primary
);
237 IDirectSound_Release( p_aout
->p_sys
->p_dsobject
);
238 p_aout
->p_sys
->p_dsobject
= NULL
;
239 p_aout
->p_sys
->p_dsbuffer_primary
= NULL
;
244 /* Now create the buffer that we'll actually use: the secondary buffer */
245 if( windx_CreateSecondaryBuffer( p_aout
) )
247 intf_WarnMsg( 3, "aout: can't create direct sound secondary buffer ");
249 IDirectSound_Release( p_aout
->p_sys
->p_dsbuffer_primary
);
251 IDirectSound_Release( p_aout
->p_sys
->p_dsobject
);
252 p_aout
->p_sys
->p_dsobject
= NULL
;
253 p_aout
->p_sys
->p_dsbuffer_primary
= NULL
;
254 p_aout
->p_sys
->p_dsbuffer
= NULL
;
261 /*****************************************************************************
262 * aout_SetFormat: reset the audio device and sets its format
263 *****************************************************************************
264 * This functions set a new audio format.
265 * For this we need to close the current secondary buffer and create another
266 * one with the desired format.
267 *****************************************************************************/
268 static int aout_SetFormat( aout_thread_t
*p_aout
)
272 /* first release the current secondary buffer */
273 if( p_aout
->p_sys
->p_dsbuffer
!= NULL
)
275 IDirectSoundBuffer_Release( p_aout
->p_sys
->p_dsbuffer
);
276 p_aout
->p_sys
->p_dsbuffer
= NULL
;
279 /* then create a new secondary buffer */
280 dsresult
= windx_CreateSecondaryBuffer( p_aout
);
281 if( dsresult
!= DS_OK
)
283 intf_WarnMsg( 3, "aout: WinDX aout_SetFormat cannot create buffer");
290 /*****************************************************************************
291 * aout_GetBufInfo: buffer status query
292 *****************************************************************************
293 * returns the number of bytes in the audio buffer compared to the size of
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 dsresult
= IDirectSoundBuffer_GetCurrentPosition(p_aout
->p_sys
->p_dsbuffer
,
302 &l_play_position
, &l_notused
);
303 if( dsresult
== DSERR_BUFFERLOST
)
305 IDirectSoundBuffer_Restore( p_aout
->p_sys
->p_dsbuffer
);
306 dsresult
= IDirectSoundBuffer_GetCurrentPosition(
307 p_aout
->p_sys
->p_dsbuffer
,
308 &l_play_position
, &l_notused
311 if( dsresult
!= DS_OK
)
313 intf_WarnMsg( 3, "aout: WinDX aout_GetBufInfo cannot get current pos");
314 return( l_buffer_limit
);
317 l_result
= ((p_aout
->p_sys
->l_write_position
>= l_play_position
) ?
318 (p_aout
->p_sys
->l_write_position
- l_play_position
)/2
319 : (p_aout
->p_sys
->l_buffer_size
- l_play_position
320 + p_aout
->p_sys
->l_write_position
)/2 );
322 intf_WarnMsg( 5, "aout: WinDX 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 *****************************************************************************/
331 static void aout_Play( aout_thread_t
*p_aout
, byte_t
*buffer
, int i_size
)
333 VOID
*p_write_position
, *p_start_buffer
;
334 long l_bytes1
, l_bytes2
;
335 long l_play_position
, l_notused
, l_buffer_free_length
;
338 /* We want to copy data to the circular sound buffer, so first we need to
339 * find out were in the buffer we can write our data */
340 dsresult
= IDirectSoundBuffer_GetCurrentPosition(p_aout
->p_sys
->p_dsbuffer
,
343 if( dsresult
== DSERR_BUFFERLOST
)
345 IDirectSoundBuffer_Restore( p_aout
->p_sys
->p_dsbuffer
);
346 dsresult
= IDirectSoundBuffer_GetCurrentPosition(
347 p_aout
->p_sys
->p_dsbuffer
,
348 &l_play_position
, &l_notused
351 if( dsresult
!= DS_OK
)
353 intf_WarnMsg( 3, "aout: WinDX aout_Play can'get buffer position");
356 /* check that we are not overflowing the circular buffer (everything should
357 * be alright but just in case) */
358 l_buffer_free_length
= l_play_position
- p_aout
->p_sys
->l_write_position
;
359 if( l_buffer_free_length
<= 0 )
360 l_buffer_free_length
+= p_aout
->p_sys
->l_buffer_size
;
362 if( i_size
> l_buffer_free_length
)
364 intf_WarnMsg( 3, "aout: WinDX aout_Play buffer overflow: size %i, free %i !!!", i_size
, l_buffer_free_length
);
365 intf_WarnMsg( 3, "aout: WinDX aout_Play buffer overflow: writepos %i, readpos %i !!!", l_play_position
, p_aout
->p_sys
->l_write_position
);
366 /*i_size = l_buffer_free_length;*/
369 /* Before copying anything, we have to lock the buffer */
370 dsresult
= IDirectSoundBuffer_Lock( p_aout
->p_sys
->p_dsbuffer
,
371 p_aout
->p_sys
->l_write_position
, /* Offset of lock start */
372 i_size
, /* Number of bytes to lock */
373 &p_write_position
, /* Address of lock start */
374 &l_bytes1
, /* Count of bytes locked before wrap around */
375 &p_start_buffer
, /* Buffer adress (if wrap around) */
376 &l_bytes2
, /* Count of bytes after wrap around */
378 if( dsresult
== DSERR_BUFFERLOST
)
380 IDirectSoundBuffer_Restore( p_aout
->p_sys
->p_dsbuffer
);
381 dsresult
= IDirectSoundBuffer_Lock( p_aout
->p_sys
->p_dsbuffer
,
382 p_aout
->p_sys
->l_write_position
,
391 if( dsresult
!= DS_OK
)
393 intf_WarnMsg( 3, "aout: WinDX aout_Play can't lock buffer");
397 /* Now do the actual memcopy (two memcpy because the buffer is circular) */
398 memcpy( p_write_position
, buffer
, l_bytes1
);
399 if( p_start_buffer
!= NULL
)
400 memcpy( p_start_buffer
, buffer
+ l_bytes1
, l_bytes2
);
402 /* Now the data has been copied, unlock the buffer */
403 IDirectSoundBuffer_Unlock( p_aout
->p_sys
->p_dsbuffer
,
404 p_write_position
, l_bytes1
, p_start_buffer
, l_bytes2
);
406 /* Update the write position index of the buffer*/
407 p_aout
->p_sys
->l_write_position
+= i_size
;
408 p_aout
->p_sys
->l_write_position
%= p_aout
->p_sys
->l_buffer_size
;
410 /* The play function has no effect if the buffer is already playing */
411 dsresult
= IDirectSoundBuffer_Play( p_aout
->p_sys
->p_dsbuffer
,
414 DSBPLAY_LOOPING
); /* Flags */
415 if( dsresult
== DSERR_BUFFERLOST
)
417 IDirectSoundBuffer_Restore( p_aout
->p_sys
->p_dsbuffer
);
418 dsresult
= IDirectSoundBuffer_Play( p_aout
->p_sys
->p_dsbuffer
,
421 DSBPLAY_LOOPING
); /* Flags */
423 if( dsresult
!= DS_OK
)
425 intf_WarnMsg( 3, "aout: WinDX aout_Play can't play buffer");
431 /*****************************************************************************
432 * aout_Close: close the audio device
433 *****************************************************************************/
434 static void aout_Close( aout_thread_t
*p_aout
)
436 /* make sure the buffer isn't playing */
437 if( p_aout
->p_sys
->p_dsbuffer
!= NULL
)
439 IDirectSoundBuffer_Stop( p_aout
->p_sys
->p_dsbuffer
);
442 /* first release the secondary buffer */
443 if( p_aout
->p_sys
->p_dsbuffer
!= NULL
)
445 IDirectSoundBuffer_Release( p_aout
->p_sys
->p_dsbuffer
);
446 p_aout
->p_sys
->p_dsbuffer
= NULL
;
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 /* Close the Output. */
464 if ( p_aout
->p_sys
!= NULL
)
466 free( p_aout
->p_sys
);
467 p_aout
->p_sys
= NULL
;
471 /*****************************************************************************
472 * windx_CreateSecondaryBuffer
473 *****************************************************************************
474 * This function creates the buffer we'll use to play audio.
475 * In DirectSound there are two kinds of buffers:
476 * - the primary buffer: which is the actual buffer that the soundcard plays
477 * - the secondary buffer(s): these buffers are the one actually used by
478 * applications and DirectSound takes care of mixing them into the primary.
480 * Once you create a secondary buffer, you cannot change its format anymore so
481 * you have to release the current and create another one.
482 *****************************************************************************/
483 static int windx_CreateSecondaryBuffer( aout_thread_t
*p_aout
)
485 WAVEFORMATEX waveformat
;
486 DSBUFFERDESC dsbdesc
;
490 /* First set the buffer format */
491 memset(&waveformat
, 0, sizeof(WAVEFORMATEX
));
492 waveformat
.wFormatTag
= WAVE_FORMAT_PCM
;
493 waveformat
.nChannels
= p_aout
->i_channels
;
494 waveformat
.nSamplesPerSec
= p_aout
->l_rate
;
495 waveformat
.wBitsPerSample
= 16;
496 waveformat
.nBlockAlign
= waveformat
.wBitsPerSample
/ 8 *
497 waveformat
.nChannels
;
498 waveformat
.nAvgBytesPerSec
= waveformat
.nSamplesPerSec
*
499 waveformat
.nBlockAlign
;
501 /* Then fill in the descriptor */
502 memset(&dsbdesc
, 0, sizeof(DSBUFFERDESC
));
503 dsbdesc
.dwSize
= sizeof(DSBUFFERDESC
);
504 dsbdesc
.dwFlags
= DSBCAPS_GETCURRENTPOSITION2
/* Better position accuracy */
505 | DSBCAPS_GLOBALFOCUS
; /* Allows background playing */
508 | DSBCAPS_CTRLFREQUENCY;
510 dsbdesc
.dwBufferBytes
= waveformat
.nAvgBytesPerSec
* 4; /* 4 sec buffer */
511 dsbdesc
.lpwfxFormat
= &waveformat
;
513 if( IDirectSound_CreateSoundBuffer( p_aout
->p_sys
->p_dsobject
,
515 &p_aout
->p_sys
->p_dsbuffer
,
518 intf_WarnMsg( 3, "aout: can't create direct sound secondary buffer ");
519 p_aout
->p_sys
->p_dsbuffer
= NULL
;
523 /* backup the size of the secondary sound buffer */
524 memset(&dsbcaps
, 0, sizeof(DSBCAPS
));
525 dsbcaps
.dwSize
= sizeof(DSBCAPS
);
526 IDirectSoundBuffer_GetCaps( p_aout
->p_sys
->p_dsbuffer
, &dsbcaps
);
527 p_aout
->p_sys
->l_buffer_size
= dsbcaps
.dwBufferBytes
;
528 p_aout
->p_sys
->l_write_position
= 0;
529 intf_WarnMsg( 3, "aout: WinDX WinDX_CreateSecondaryBuffer: %li",
530 p_aout
->p_sys
->l_buffer_size
);
532 /* make sure the buffer isn't playing */
533 IDirectSoundBuffer_Stop( p_aout
->p_sys
->p_dsbuffer
);
535 /* reset play position, just to be sure (and after some tests it seems
536 * indeed necessary */
537 dsresult
= IDirectSoundBuffer_SetCurrentPosition(p_aout
->p_sys
->p_dsbuffer
,
539 if( dsresult
== DSERR_BUFFERLOST
)
541 IDirectSoundBuffer_Restore( p_aout
->p_sys
->p_dsbuffer
);
542 dsresult
= IDirectSoundBuffer_SetCurrentPosition(
543 p_aout
->p_sys
->p_dsbuffer
,
546 if( dsresult
!= DS_OK
)
548 intf_WarnMsg( 3, "aout: WinDX CreateSecondary cannot wet current pos");