* DirectX plugin by Gildas Bazin <gbazin@netcourrier.com>.
[vlc.git] / plugins / directx / aout_directx.c
bloba31237e7a1b425d89c48e8abda318e57dd14a518
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 /*****************************************************************************
35 * Preamble
36 *****************************************************************************/
37 #include "defs.h"
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() */
46 #include "config.h"
47 #include "common.h" /* boolean_t, byte_t */
48 #include "threads.h"
49 #include "mtime.h"
50 #include "tests.h"
52 #include "directx.h"
54 #include "audio_output.h" /* aout_thread_t */
56 #include "intf_msg.h" /* intf_DbgMsg(), intf_ErrMsg() */
57 #include "main.h"
59 #include "modules.h"
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 */
82 boolean_t b_active;
84 } aout_sys_t;
86 /*****************************************************************************
87 * Local prototypes.
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 );
97 /* local function */
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" ) )
125 return( 999 );
127 return( 400 );
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 )
137 #if 0
138 HRESULT dsresult;
139 DSBUFFERDESC dsbuffer_desc;
140 WAVEFORMATEX waveformat;
141 #endif
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) );
149 return( 1 );
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,
162 AOUT_RATE_DEFAULT );
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;
169 return( 1 );
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
181 * working */
182 if( IDirectSound_SetCooperativeLevel(p_aout->p_sys->p_dsobject,
183 GetDesktopWindow(),
184 DSSCL_EXCLUSIVE) )
186 intf_WarnMsg( 3, "aout: can't set direct sound cooperative level ");
189 #if 0
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,
196 &dsbuffer_desc,
197 &p_aout->p_sys->p_dsbuffer_primary,
198 NULL);
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;
205 return( 1 );
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,
221 &waveformat);
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,
232 DSBPLAY_LOOPING);
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;
240 return( 1 );
242 #endif
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 ");
248 #if 0
249 IDirectSound_Release( p_aout->p_sys->p_dsbuffer_primary );
250 #endif
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;
255 return( 1 );
258 return( 0 );
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 )
270 HRESULT dsresult;
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");
284 return( 1 );
287 return( 0 );
290 /*****************************************************************************
291 * aout_GetBufInfo: buffer status query
292 *****************************************************************************
293 * returns the number of bytes in the audio buffer compared to the size of
294 * l_buffer_limit...
295 *****************************************************************************/
296 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
298 long l_play_position, l_notused, l_result;
299 HRESULT dsresult;
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);
323 return 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;
336 HRESULT dsresult;
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,
341 &l_play_position,
342 &l_notused);
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 */
377 0); /* Flags */
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,
383 i_size,
384 &p_write_position,
385 &l_bytes1,
386 &p_start_buffer,
387 &l_bytes2,
391 if( dsresult != DS_OK )
393 intf_WarnMsg( 3, "aout: WinDX aout_Play can't lock buffer");
394 return;
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,
412 0, /* Unused */
413 0, /* Unused */
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,
419 0, /* Unused */
420 0, /* Unused */
421 DSBPLAY_LOOPING ); /* Flags */
423 if( dsresult != DS_OK )
425 intf_WarnMsg( 3, "aout: WinDX aout_Play can't play buffer");
426 return;
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;
487 DSBCAPS dsbcaps;
488 HRESULT dsresult;
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 */
506 /* | DSBCAPS_CTRLPAN
507 | DSBCAPS_CTRLVOLUME
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,
514 &dsbdesc,
515 &p_aout->p_sys->p_dsbuffer,
516 NULL) != DS_OK )
518 intf_WarnMsg( 3, "aout: can't create direct sound secondary buffer ");
519 p_aout->p_sys->p_dsbuffer = NULL;
520 return( 1 );
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,
538 0 );
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,
544 0 );
546 if( dsresult != DS_OK )
548 intf_WarnMsg( 3, "aout: WinDX CreateSecondary cannot wet current pos");
551 return( 0 );