2 * Windows DirectSound interface
4 * Copyright (c) 2004 Gabor Szecsi <deje@miki.hu>
6 * This file is part of MPlayer.
8 * MPlayer is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * MPlayer is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 \todo verify/extend multichannel support
31 #define DIRECTSOUND_VERSION 0x0600
36 #include "libaf/af_format.h"
37 #include "audio_out.h"
38 #include "audio_out_internal.h"
40 #include "libvo/fastmemcpy.h"
41 #include "osdep/timer.h"
42 #include "subopt-helper.h"
45 static const ao_info_t info
=
47 "Windows DirectSound audio output",
49 "Gabor Szecsi <deje@miki.hu>",
56 \todo use the definitions from the win32 api headers when they define these
58 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
59 #define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
60 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
62 static const GUID KSDATAFORMAT_SUBTYPE_PCM
= {0x1,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}};
64 #define SPEAKER_FRONT_LEFT 0x1
65 #define SPEAKER_FRONT_RIGHT 0x2
66 #define SPEAKER_FRONT_CENTER 0x4
67 #define SPEAKER_LOW_FREQUENCY 0x8
68 #define SPEAKER_BACK_LEFT 0x10
69 #define SPEAKER_BACK_RIGHT 0x20
70 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
71 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
72 #define SPEAKER_BACK_CENTER 0x100
73 #define SPEAKER_SIDE_LEFT 0x200
74 #define SPEAKER_SIDE_RIGHT 0x400
75 #define SPEAKER_TOP_CENTER 0x800
76 #define SPEAKER_TOP_FRONT_LEFT 0x1000
77 #define SPEAKER_TOP_FRONT_CENTER 0x2000
78 #define SPEAKER_TOP_FRONT_RIGHT 0x4000
79 #define SPEAKER_TOP_BACK_LEFT 0x8000
80 #define SPEAKER_TOP_BACK_CENTER 0x10000
81 #define SPEAKER_TOP_BACK_RIGHT 0x20000
82 #define SPEAKER_RESERVED 0x80000000
84 #define DSSPEAKER_HEADPHONE 0x00000001
85 #define DSSPEAKER_MONO 0x00000002
86 #define DSSPEAKER_QUAD 0x00000003
87 #define DSSPEAKER_STEREO 0x00000004
88 #define DSSPEAKER_SURROUND 0x00000005
89 #define DSSPEAKER_5POINT1 0x00000006
91 #ifndef _WAVEFORMATEXTENSIBLE_
95 WORD wValidBitsPerSample
; /* bits of precision */
96 WORD wSamplesPerBlock
; /* valid if wBitsPerSample==0 */
97 WORD wReserved
; /* If neither applies, set to zero. */
99 DWORD dwChannelMask
; /* which channels are */
100 /* present in stream */
102 } WAVEFORMATEXTENSIBLE
, *PWAVEFORMATEXTENSIBLE
;
105 static const int channel_mask
[] = {
106 SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_LOW_FREQUENCY
,
107 SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_BACK_LEFT
| SPEAKER_BACK_RIGHT
,
108 SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_BACK_LEFT
| SPEAKER_BACK_RIGHT
| SPEAKER_LOW_FREQUENCY
,
109 SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_CENTER
| SPEAKER_FRONT_RIGHT
| SPEAKER_BACK_LEFT
| SPEAKER_BACK_RIGHT
| SPEAKER_LOW_FREQUENCY
112 static HINSTANCE hdsound_dll
= NULL
; ///handle to the dll
113 static LPDIRECTSOUND hds
= NULL
; ///direct sound object
114 static LPDIRECTSOUNDBUFFER hdspribuf
= NULL
; ///primary direct sound buffer
115 static LPDIRECTSOUNDBUFFER hdsbuf
= NULL
; ///secondary direct sound buffer (stream buffer)
116 static int buffer_size
= 0; ///size in bytes of the direct sound buffer
117 static int write_offset
= 0; ///offset of the write cursor in the direct sound buffer
118 static int min_free_space
= 0; ///if the free space is below this value get_space() will return 0
119 ///there will always be at least this amout of free space to prevent
120 ///get_space() from returning wrong values when buffer is 100% full.
121 ///will be replaced with nBlockAlign in init()
122 static int device_num
= 0; ///wanted device number
123 static GUID device
; ///guid of the device
125 /***************************************************************************************/
128 \brief output error message
129 \param err error code
130 \return string with the error message
132 static char * dserr2str(int err
)
135 case DS_OK
: return "DS_OK";
136 case DS_NO_VIRTUALIZATION
: return "DS_NO_VIRTUALIZATION";
137 case DSERR_ALLOCATED
: return "DS_NO_VIRTUALIZATION";
138 case DSERR_CONTROLUNAVAIL
: return "DSERR_CONTROLUNAVAIL";
139 case DSERR_INVALIDPARAM
: return "DSERR_INVALIDPARAM";
140 case DSERR_INVALIDCALL
: return "DSERR_INVALIDCALL";
141 case DSERR_GENERIC
: return "DSERR_GENERIC";
142 case DSERR_PRIOLEVELNEEDED
: return "DSERR_PRIOLEVELNEEDED";
143 case DSERR_OUTOFMEMORY
: return "DSERR_OUTOFMEMORY";
144 case DSERR_BADFORMAT
: return "DSERR_BADFORMAT";
145 case DSERR_UNSUPPORTED
: return "DSERR_UNSUPPORTED";
146 case DSERR_NODRIVER
: return "DSERR_NODRIVER";
147 case DSERR_ALREADYINITIALIZED
: return "DSERR_ALREADYINITIALIZED";
148 case DSERR_NOAGGREGATION
: return "DSERR_NOAGGREGATION";
149 case DSERR_BUFFERLOST
: return "DSERR_BUFFERLOST";
150 case DSERR_OTHERAPPHASPRIO
: return "DSERR_OTHERAPPHASPRIO";
151 case DSERR_UNINITIALIZED
: return "DSERR_UNINITIALIZED";
152 case DSERR_NOINTERFACE
: return "DSERR_NOINTERFACE";
153 case DSERR_ACCESSDENIED
: return "DSERR_ACCESSDENIED";
154 default: return "unknown";
159 \brief uninitialize direct sound
161 static void UninitDirectSound(void)
163 // finally release the DirectSound object
165 IDirectSound_Release(hds
);
170 FreeLibrary(hdsound_dll
);
173 mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: DirectSound uninitialized\n");
177 \brief print the commandline help
179 static void print_help(void)
181 mp_msg(MSGT_AO
, MSGL_FATAL
,
182 "\n-ao dsound commandline help:\n"
183 "Example: mplayer -ao dsound:device=1\n"
186 " device=<device-number>\n"
187 " Sets device number, use -v to get a list\n");
192 \brief enumerate direct sound devices
193 \return TRUE to continue with the enumeration
195 static BOOL CALLBACK
DirectSoundEnum(LPGUID guid
,LPCSTR desc
,LPCSTR module
,LPVOID context
)
197 int* device_index
=context
;
198 mp_msg(MSGT_AO
, MSGL_V
,"%i %s ",*device_index
,desc
);
199 if(device_num
==*device_index
){
200 mp_msg(MSGT_AO
, MSGL_V
,"<--");
202 memcpy(&device
,guid
,sizeof(GUID
));
205 mp_msg(MSGT_AO
, MSGL_V
,"\n");
212 \brief initilize direct sound
213 \return 0 if error, 1 if ok
215 static int InitDirectSound(void)
219 // initialize directsound
220 HRESULT (WINAPI
*OurDirectSoundCreate
)(LPGUID
, LPDIRECTSOUND
*, LPUNKNOWN
);
221 HRESULT (WINAPI
*OurDirectSoundEnumerate
)(LPDSENUMCALLBACKA
, LPVOID
);
223 const opt_t subopts
[] = {
224 {"device", OPT_ARG_INT
, &device_num
,NULL
},
227 if (subopt_parse(ao_subdevice
, subopts
) != 0) {
232 hdsound_dll
= LoadLibrary("DSOUND.DLL");
233 if (hdsound_dll
== NULL
) {
234 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_dsound: cannot load DSOUND.DLL\n");
237 OurDirectSoundCreate
= (void*)GetProcAddress(hdsound_dll
, "DirectSoundCreate");
238 OurDirectSoundEnumerate
= (void*)GetProcAddress(hdsound_dll
, "DirectSoundEnumerateA");
240 if (OurDirectSoundCreate
== NULL
|| OurDirectSoundEnumerate
== NULL
) {
241 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_dsound: GetProcAddress FAILED\n");
242 FreeLibrary(hdsound_dll
);
246 // Enumerate all directsound devices
247 mp_msg(MSGT_AO
, MSGL_V
,"ao_dsound: Output Devices:\n");
248 OurDirectSoundEnumerate(DirectSoundEnum
,&device_index
);
250 // Create the direct sound object
251 if FAILED(OurDirectSoundCreate((device_num
)?&device
:NULL
, &hds
, NULL
)) {
252 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_dsound: cannot create a DirectSound device\n");
253 FreeLibrary(hdsound_dll
);
257 /* Set DirectSound Cooperative level, ie what control we want over Windows
258 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
259 * settings of the primary buffer, but also that only the sound of our
260 * application will be hearable when it will have the focus.
261 * !!! (this is not really working as intended yet because to set the
262 * cooperative level you need the window handle of your application, and
263 * I don't know of any easy way to get it. Especially since we might play
264 * sound without any video, and so what window handle should we use ???
265 * The hack for now is to use the Desktop window handle - it seems to be
267 if (IDirectSound_SetCooperativeLevel(hds
, GetDesktopWindow(), DSSCL_EXCLUSIVE
)) {
268 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_dsound: cannot set direct sound cooperative level\n");
269 IDirectSound_Release(hds
);
270 FreeLibrary(hdsound_dll
);
273 mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: DirectSound initialized\n");
275 memset(&dscaps
, 0, sizeof(DSCAPS
));
276 dscaps
.dwSize
= sizeof(DSCAPS
);
277 if (DS_OK
== IDirectSound_GetCaps(hds
, &dscaps
)) {
278 if (dscaps
.dwFlags
& DSCAPS_EMULDRIVER
) mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: DirectSound is emulated, waveOut may give better performance\n");
280 mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: cannot get device capabilities\n");
287 \brief destroy the direct sound buffer
289 static void DestroyBuffer(void)
292 IDirectSoundBuffer_Release(hdsbuf
);
296 IDirectSoundBuffer_Release(hdspribuf
);
302 \brief fill sound buffer
303 \param data pointer to the sound data to copy
304 \param len length of the data to copy in bytes
305 \return number of copyed bytes
307 static int write_buffer(unsigned char *data
, int len
)
316 res
= IDirectSoundBuffer_Lock(hdsbuf
,write_offset
, len
, &lpvPtr1
, &dwBytes1
, &lpvPtr2
, &dwBytes2
, 0);
317 // If the buffer was lost, restore and retry lock.
318 if (DSERR_BUFFERLOST
== res
)
320 IDirectSoundBuffer_Restore(hdsbuf
);
321 res
= IDirectSoundBuffer_Lock(hdsbuf
,write_offset
, len
, &lpvPtr1
, &dwBytes1
, &lpvPtr2
, &dwBytes2
, 0);
327 if( (ao_data
.channels
== 6) && !AF_FORMAT_IS_AC3(ao_data
.format
) ) {
328 // reorder channels while writing to pointers.
329 // it's this easy because buffer size and len are always
330 // aligned to multiples of channels*bytespersample
331 // there's probably some room for speed improvements here
332 const int chantable
[6] = {0, 1, 4, 5, 2, 3}; // reorder "matrix"
334 int numsamp
,sampsize
;
336 sampsize
= af_fmt2bits(ao_data
.format
)>>3; // bytes per sample
337 numsamp
= dwBytes1
/ (ao_data
.channels
* sampsize
); // number of samples for each channel in this buffer
339 for( i
= 0; i
< numsamp
; i
++ ) for( j
= 0; j
< ao_data
.channels
; j
++ ) {
340 memcpy(lpvPtr1
+(i
*ao_data
.channels
*sampsize
)+(chantable
[j
]*sampsize
),data
+(i
*ao_data
.channels
*sampsize
)+(j
*sampsize
),sampsize
);
343 if (NULL
!= lpvPtr2
)
345 numsamp
= dwBytes2
/ (ao_data
.channels
* sampsize
);
346 for( i
= 0; i
< numsamp
; i
++ ) for( j
= 0; j
< ao_data
.channels
; j
++ ) {
347 memcpy(lpvPtr2
+(i
*ao_data
.channels
*sampsize
)+(chantable
[j
]*sampsize
),data
+dwBytes1
+(i
*ao_data
.channels
*sampsize
)+(j
*sampsize
),sampsize
);
351 write_offset
+=dwBytes1
+dwBytes2
;
352 if(write_offset
>=buffer_size
)write_offset
=dwBytes2
;
354 // Write to pointers without reordering.
355 fast_memcpy(lpvPtr1
,data
,dwBytes1
);
356 if (NULL
!= lpvPtr2
)fast_memcpy(lpvPtr2
,data
+dwBytes1
,dwBytes2
);
357 write_offset
+=dwBytes1
+dwBytes2
;
358 if(write_offset
>=buffer_size
)write_offset
=dwBytes2
;
361 // Release the data back to DirectSound.
362 res
= IDirectSoundBuffer_Unlock(hdsbuf
,lpvPtr1
,dwBytes1
,lpvPtr2
,dwBytes2
);
367 IDirectSoundBuffer_GetStatus(hdsbuf
, &status
);
368 if (!(status
& DSBSTATUS_PLAYING
)){
369 res
= IDirectSoundBuffer_Play(hdsbuf
, 0, 0, DSBPLAY_LOOPING
);
371 return dwBytes1
+dwBytes2
;
374 // Lock, Unlock, or Restore failed.
378 /***************************************************************************************/
381 \brief handle control commands
384 \return CONTROL_OK or -1 in case the command can't be handled
386 static int control(int cmd
, void *arg
)
390 case AOCONTROL_GET_VOLUME
: {
391 ao_control_vol_t
* vol
= (ao_control_vol_t
*)arg
;
392 IDirectSoundBuffer_GetVolume(hdsbuf
, &volume
);
393 vol
->left
= vol
->right
= pow(10.0, (float)(volume
+10000) / 5000.0);
394 //printf("ao_dsound: volume: %f\n",vol->left);
397 case AOCONTROL_SET_VOLUME
: {
398 ao_control_vol_t
* vol
= (ao_control_vol_t
*)arg
;
399 volume
= (DWORD
)(log10(vol
->right
) * 5000.0) - 10000;
400 IDirectSoundBuffer_SetVolume(hdsbuf
, volume
);
401 //printf("ao_dsound: volume: %f\n",vol->left);
409 \brief setup sound device
410 \param rate samplerate
411 \param channels number of channels
414 \return 1=success 0=fail
416 static int init(int rate
, int channels
, int format
, int flags
)
419 if (!InitDirectSound()) return 0;
421 // ok, now create the buffers
422 WAVEFORMATEXTENSIBLE wformat
;
423 DSBUFFERDESC dsbpridesc
;
424 DSBUFFERDESC dsbdesc
;
426 //check if the channel count and format is supported in general
429 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_dsound: 8 channel audio not yet supported\n");
433 if (AF_FORMAT_IS_AC3(format
))
434 format
= AF_FORMAT_AC3_NE
;
436 case AF_FORMAT_AC3_NE
:
437 case AF_FORMAT_S24_LE
:
438 case AF_FORMAT_S16_LE
:
442 mp_msg(MSGT_AO
, MSGL_V
,"ao_dsound: format %s not supported defaulting to Signed 16-bit Little-Endian\n",af_fmt2str_short(format
));
443 format
=AF_FORMAT_S16_LE
;
445 //fill global ao_data
446 ao_data
.channels
= channels
;
447 ao_data
.samplerate
= rate
;
448 ao_data
.format
= format
;
449 ao_data
.bps
= channels
* rate
* (af_fmt2bits(format
)>>3);
450 if(ao_data
.buffersize
==-1) ao_data
.buffersize
= ao_data
.bps
; // space for 1 sec
451 mp_msg(MSGT_AO
, MSGL_V
,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate
, channels
, af_fmt2str_short(format
));
452 mp_msg(MSGT_AO
, MSGL_V
,"ao_dsound: Buffersize:%d bytes (%d msec)\n", ao_data
.buffersize
, ao_data
.buffersize
/ ao_data
.bps
* 1000);
455 ZeroMemory(&wformat
, sizeof(WAVEFORMATEXTENSIBLE
));
456 wformat
.Format
.cbSize
= (channels
> 2) ? sizeof(WAVEFORMATEXTENSIBLE
)-sizeof(WAVEFORMATEX
) : 0;
457 wformat
.Format
.nChannels
= channels
;
458 wformat
.Format
.nSamplesPerSec
= rate
;
459 if (AF_FORMAT_IS_AC3(format
)) {
460 wformat
.Format
.wFormatTag
= WAVE_FORMAT_DOLBY_AC3_SPDIF
;
461 wformat
.Format
.wBitsPerSample
= 16;
462 wformat
.Format
.nBlockAlign
= 4;
464 wformat
.Format
.wFormatTag
= (channels
> 2) ? WAVE_FORMAT_EXTENSIBLE
: WAVE_FORMAT_PCM
;
465 wformat
.Format
.wBitsPerSample
= af_fmt2bits(format
);
466 wformat
.Format
.nBlockAlign
= wformat
.Format
.nChannels
* (wformat
.Format
.wBitsPerSample
>> 3);
469 // fill in primary sound buffer descriptor
470 memset(&dsbpridesc
, 0, sizeof(DSBUFFERDESC
));
471 dsbpridesc
.dwSize
= sizeof(DSBUFFERDESC
);
472 dsbpridesc
.dwFlags
= DSBCAPS_PRIMARYBUFFER
;
473 dsbpridesc
.dwBufferBytes
= 0;
474 dsbpridesc
.lpwfxFormat
= NULL
;
477 // fill in the secondary sound buffer (=stream buffer) descriptor
478 memset(&dsbdesc
, 0, sizeof(DSBUFFERDESC
));
479 dsbdesc
.dwSize
= sizeof(DSBUFFERDESC
);
480 dsbdesc
.dwFlags
= DSBCAPS_GETCURRENTPOSITION2
/** Better position accuracy */
481 | DSBCAPS_GLOBALFOCUS
/** Allows background playing */
482 | DSBCAPS_CTRLVOLUME
; /** volume control enabled */
485 wformat
.dwChannelMask
= channel_mask
[channels
- 3];
486 wformat
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
487 wformat
.Samples
.wValidBitsPerSample
= wformat
.Format
.wBitsPerSample
;
488 // Needed for 5.1 on emu101k - shit soundblaster
489 dsbdesc
.dwFlags
|= DSBCAPS_LOCHARDWARE
;
491 wformat
.Format
.nAvgBytesPerSec
= wformat
.Format
.nSamplesPerSec
* wformat
.Format
.nBlockAlign
;
493 dsbdesc
.dwBufferBytes
= ao_data
.buffersize
;
494 dsbdesc
.lpwfxFormat
= (WAVEFORMATEX
*)&wformat
;
495 buffer_size
= dsbdesc
.dwBufferBytes
;
497 min_free_space
= wformat
.Format
.nBlockAlign
;
498 ao_data
.outburst
= wformat
.Format
.nBlockAlign
* 512;
500 // create primary buffer and set its format
502 res
= IDirectSound_CreateSoundBuffer( hds
, &dsbpridesc
, &hdspribuf
, NULL
);
503 if ( res
!= DS_OK
) {
505 mp_msg(MSGT_AO
, MSGL_ERR
,"ao_dsound: cannot create primary buffer (%s)\n", dserr2str(res
));
508 res
= IDirectSoundBuffer_SetFormat( hdspribuf
, (WAVEFORMATEX
*)&wformat
);
509 if ( res
!= DS_OK
) mp_msg(MSGT_AO
, MSGL_WARN
,"ao_dsound: cannot set primary buffer format (%s), using standard setting (bad quality)", dserr2str(res
));
511 mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: primary buffer created\n");
513 // now create the stream buffer
515 res
= IDirectSound_CreateSoundBuffer(hds
, &dsbdesc
, &hdsbuf
, NULL
);
517 if (dsbdesc
.dwFlags
& DSBCAPS_LOCHARDWARE
) {
518 // Try without DSBCAPS_LOCHARDWARE
519 dsbdesc
.dwFlags
&= ~DSBCAPS_LOCHARDWARE
;
520 res
= IDirectSound_CreateSoundBuffer(hds
, &dsbdesc
, &hdsbuf
, NULL
);
524 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_dsound: cannot create secondary (stream)buffer (%s)\n", dserr2str(res
));
528 mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: secondary (stream)buffer created\n");
535 \brief stop playing and empty buffers (for seeking/pause)
537 static void reset(void)
539 IDirectSoundBuffer_Stop(hdsbuf
);
540 // reset directsound buffer
541 IDirectSoundBuffer_SetCurrentPosition(hdsbuf
, 0);
546 \brief stop playing, keep buffers (for pause)
548 static void audio_pause(void)
550 IDirectSoundBuffer_Stop(hdsbuf
);
554 \brief resume playing, after audio_pause()
556 static void audio_resume(void)
558 IDirectSoundBuffer_Play(hdsbuf
, 0, 0, DSBPLAY_LOOPING
);
562 \brief close audio device
563 \param immed stop playback immediately
565 static void uninit(int immed
)
570 IDirectSoundBuffer_Play(hdsbuf
, 0, 0, 0);
571 while(!IDirectSoundBuffer_GetStatus(hdsbuf
,&status
) && (status
&DSBSTATUS_PLAYING
))
579 \brief find out how many bytes can be written into the audio buffer without
580 \return free space in bytes, has to return 0 if the buffer is almost full
582 static int get_space(void)
586 IDirectSoundBuffer_GetCurrentPosition(hdsbuf
,&play_offset
,NULL
);
587 space
=buffer_size
-(write_offset
-play_offset
);
588 // | | <-- const --> | | |
589 // buffer start play_cursor write_cursor write_offset buffer end
590 // play_cursor is the actual postion of the play cursor
591 // write_cursor is the position after which it is assumed to be save to write data
592 // write_offset is the postion where we actually write the data to
593 if(space
> buffer_size
)space
-= buffer_size
; // write_offset < play_offset
594 if(space
< min_free_space
)return 0;
595 return space
-min_free_space
;
599 \brief play 'len' bytes of 'data'
600 \param data pointer to the data to play
601 \param len size in bytes of the data buffer, gets rounded down to outburst*n
602 \param flags currently unused
603 \return number of played bytes
605 static int play(void* data
, int len
, int flags
)
610 // make sure we have enough space to write data
611 IDirectSoundBuffer_GetCurrentPosition(hdsbuf
,&play_offset
,NULL
);
612 space
=buffer_size
-(write_offset
-play_offset
);
613 if(space
> buffer_size
)space
-= buffer_size
; // write_offset < play_offset
614 if(space
< len
) len
= space
;
616 if (!(flags
& AOPLAY_FINAL_CHUNK
))
617 len
= (len
/ ao_data
.outburst
) * ao_data
.outburst
;
618 return write_buffer(data
, len
);
622 \brief get the delay between the first and last sample in the buffer
623 \return delay in seconds
625 static float get_delay(void)
629 IDirectSoundBuffer_GetCurrentPosition(hdsbuf
,&play_offset
,NULL
);
630 space
=play_offset
-write_offset
;
631 if(space
<= 0)space
+= buffer_size
;
632 return (float)(buffer_size
- space
) / (float)ao_data
.bps
;