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
59 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
60 #define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
61 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
63 static const GUID KSDATAFORMAT_SUBTYPE_PCM
= {0x1,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}};
65 #define SPEAKER_FRONT_LEFT 0x1
66 #define SPEAKER_FRONT_RIGHT 0x2
67 #define SPEAKER_FRONT_CENTER 0x4
68 #define SPEAKER_LOW_FREQUENCY 0x8
69 #define SPEAKER_BACK_LEFT 0x10
70 #define SPEAKER_BACK_RIGHT 0x20
71 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
72 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
73 #define SPEAKER_BACK_CENTER 0x100
74 #define SPEAKER_SIDE_LEFT 0x200
75 #define SPEAKER_SIDE_RIGHT 0x400
76 #define SPEAKER_TOP_CENTER 0x800
77 #define SPEAKER_TOP_FRONT_LEFT 0x1000
78 #define SPEAKER_TOP_FRONT_CENTER 0x2000
79 #define SPEAKER_TOP_FRONT_RIGHT 0x4000
80 #define SPEAKER_TOP_BACK_LEFT 0x8000
81 #define SPEAKER_TOP_BACK_CENTER 0x10000
82 #define SPEAKER_TOP_BACK_RIGHT 0x20000
83 #define SPEAKER_RESERVED 0x80000000
85 #define DSSPEAKER_HEADPHONE 0x00000001
86 #define DSSPEAKER_MONO 0x00000002
87 #define DSSPEAKER_QUAD 0x00000003
88 #define DSSPEAKER_STEREO 0x00000004
89 #define DSSPEAKER_SURROUND 0x00000005
90 #define DSSPEAKER_5POINT1 0x00000006
92 #ifndef _WAVEFORMATEXTENSIBLE_
96 WORD wValidBitsPerSample
; /* bits of precision */
97 WORD wSamplesPerBlock
; /* valid if wBitsPerSample==0 */
98 WORD wReserved
; /* If neither applies, set to zero. */
100 DWORD dwChannelMask
; /* which channels are */
101 /* present in stream */
103 } WAVEFORMATEXTENSIBLE
, *PWAVEFORMATEXTENSIBLE
;
108 static const int channel_mask
[] = {
109 SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_LOW_FREQUENCY
,
110 SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_BACK_LEFT
| SPEAKER_BACK_RIGHT
,
111 SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_BACK_LEFT
| SPEAKER_BACK_RIGHT
| SPEAKER_LOW_FREQUENCY
,
112 SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_CENTER
| SPEAKER_FRONT_RIGHT
| SPEAKER_BACK_LEFT
| SPEAKER_BACK_RIGHT
| SPEAKER_LOW_FREQUENCY
115 static HINSTANCE hdsound_dll
= NULL
; ///handle to the dll
116 static LPDIRECTSOUND hds
= NULL
; ///direct sound object
117 static LPDIRECTSOUNDBUFFER hdspribuf
= NULL
; ///primary direct sound buffer
118 static LPDIRECTSOUNDBUFFER hdsbuf
= NULL
; ///secondary direct sound buffer (stream buffer)
119 static int buffer_size
= 0; ///size in bytes of the direct sound buffer
120 static int write_offset
= 0; ///offset of the write cursor in the direct sound buffer
121 static int min_free_space
= 0; ///if the free space is below this value get_space() will return 0
122 ///there will always be at least this amout of free space to prevent
123 ///get_space() from returning wrong values when buffer is 100% full.
124 ///will be replaced with nBlockAlign in init()
125 static int device_num
= 0; ///wanted device number
126 static GUID device
; ///guid of the device
128 /***************************************************************************************/
131 \brief output error message
132 \param err error code
133 \return string with the error message
135 static char * dserr2str(int err
)
138 case DS_OK
: return "DS_OK";
139 case DS_NO_VIRTUALIZATION
: return "DS_NO_VIRTUALIZATION";
140 case DSERR_ALLOCATED
: return "DS_NO_VIRTUALIZATION";
141 case DSERR_CONTROLUNAVAIL
: return "DSERR_CONTROLUNAVAIL";
142 case DSERR_INVALIDPARAM
: return "DSERR_INVALIDPARAM";
143 case DSERR_INVALIDCALL
: return "DSERR_INVALIDCALL";
144 case DSERR_GENERIC
: return "DSERR_GENERIC";
145 case DSERR_PRIOLEVELNEEDED
: return "DSERR_PRIOLEVELNEEDED";
146 case DSERR_OUTOFMEMORY
: return "DSERR_OUTOFMEMORY";
147 case DSERR_BADFORMAT
: return "DSERR_BADFORMAT";
148 case DSERR_UNSUPPORTED
: return "DSERR_UNSUPPORTED";
149 case DSERR_NODRIVER
: return "DSERR_NODRIVER";
150 case DSERR_ALREADYINITIALIZED
: return "DSERR_ALREADYINITIALIZED";
151 case DSERR_NOAGGREGATION
: return "DSERR_NOAGGREGATION";
152 case DSERR_BUFFERLOST
: return "DSERR_BUFFERLOST";
153 case DSERR_OTHERAPPHASPRIO
: return "DSERR_OTHERAPPHASPRIO";
154 case DSERR_UNINITIALIZED
: return "DSERR_UNINITIALIZED";
155 case DSERR_NOINTERFACE
: return "DSERR_NOINTERFACE";
156 case DSERR_ACCESSDENIED
: return "DSERR_ACCESSDENIED";
157 default: return "unknown";
162 \brief uninitialize direct sound
164 static void UninitDirectSound(void)
166 // finally release the DirectSound object
168 IDirectSound_Release(hds
);
173 FreeLibrary(hdsound_dll
);
176 mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: DirectSound uninitialized\n");
180 \brief print the commandline help
182 static void print_help(void)
184 mp_msg(MSGT_AO
, MSGL_FATAL
,
185 "\n-ao dsound commandline help:\n"
186 "Example: mplayer -ao dsound:device=1\n"
189 " device=<device-number>\n"
190 " Sets device number, use -v to get a list\n");
195 \brief enumerate direct sound devices
196 \return TRUE to continue with the enumeration
198 static BOOL CALLBACK
DirectSoundEnum(LPGUID guid
,LPCSTR desc
,LPCSTR module
,LPVOID context
)
200 int* device_index
=context
;
201 mp_msg(MSGT_AO
, MSGL_V
,"%i %s ",*device_index
,desc
);
202 if(device_num
==*device_index
){
203 mp_msg(MSGT_AO
, MSGL_V
,"<--");
205 memcpy(&device
,guid
,sizeof(GUID
));
208 mp_msg(MSGT_AO
, MSGL_V
,"\n");
215 \brief initilize direct sound
216 \return 0 if error, 1 if ok
218 static int InitDirectSound(void)
222 // initialize directsound
223 HRESULT (WINAPI
*OurDirectSoundCreate
)(LPGUID
, LPDIRECTSOUND
*, LPUNKNOWN
);
224 HRESULT (WINAPI
*OurDirectSoundEnumerate
)(LPDSENUMCALLBACKA
, LPVOID
);
226 const opt_t subopts
[] = {
227 {"device", OPT_ARG_INT
, &device_num
,NULL
},
230 if (subopt_parse(ao_subdevice
, subopts
) != 0) {
235 hdsound_dll
= LoadLibrary("DSOUND.DLL");
236 if (hdsound_dll
== NULL
) {
237 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_dsound: cannot load DSOUND.DLL\n");
240 OurDirectSoundCreate
= (void*)GetProcAddress(hdsound_dll
, "DirectSoundCreate");
241 OurDirectSoundEnumerate
= (void*)GetProcAddress(hdsound_dll
, "DirectSoundEnumerateA");
243 if (OurDirectSoundCreate
== NULL
|| OurDirectSoundEnumerate
== NULL
) {
244 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_dsound: GetProcAddress FAILED\n");
245 FreeLibrary(hdsound_dll
);
249 // Enumerate all directsound devices
250 mp_msg(MSGT_AO
, MSGL_V
,"ao_dsound: Output Devices:\n");
251 OurDirectSoundEnumerate(DirectSoundEnum
,&device_index
);
253 // Create the direct sound object
254 if FAILED(OurDirectSoundCreate((device_num
)?&device
:NULL
, &hds
, NULL
)) {
255 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_dsound: cannot create a DirectSound device\n");
256 FreeLibrary(hdsound_dll
);
260 /* Set DirectSound Cooperative level, ie what control we want over Windows
261 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
262 * settings of the primary buffer, but also that only the sound of our
263 * application will be hearable when it will have the focus.
264 * !!! (this is not really working as intended yet because to set the
265 * cooperative level you need the window handle of your application, and
266 * I don't know of any easy way to get it. Especially since we might play
267 * sound without any video, and so what window handle should we use ???
268 * The hack for now is to use the Desktop window handle - it seems to be
270 if (IDirectSound_SetCooperativeLevel(hds
, GetDesktopWindow(), DSSCL_EXCLUSIVE
)) {
271 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_dsound: cannot set direct sound cooperative level\n");
272 IDirectSound_Release(hds
);
273 FreeLibrary(hdsound_dll
);
276 mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: DirectSound initialized\n");
278 memset(&dscaps
, 0, sizeof(DSCAPS
));
279 dscaps
.dwSize
= sizeof(DSCAPS
);
280 if (DS_OK
== IDirectSound_GetCaps(hds
, &dscaps
)) {
281 if (dscaps
.dwFlags
& DSCAPS_EMULDRIVER
) mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: DirectSound is emulated, waveOut may give better performance\n");
283 mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: cannot get device capabilities\n");
290 \brief destroy the direct sound buffer
292 static void DestroyBuffer(void)
295 IDirectSoundBuffer_Release(hdsbuf
);
299 IDirectSoundBuffer_Release(hdspribuf
);
305 \brief fill sound buffer
306 \param data pointer to the sound data to copy
307 \param len length of the data to copy in bytes
308 \return number of copyed bytes
310 static int write_buffer(unsigned char *data
, int len
)
319 res
= IDirectSoundBuffer_Lock(hdsbuf
,write_offset
, len
, &lpvPtr1
, &dwBytes1
, &lpvPtr2
, &dwBytes2
, 0);
320 // If the buffer was lost, restore and retry lock.
321 if (DSERR_BUFFERLOST
== res
)
323 IDirectSoundBuffer_Restore(hdsbuf
);
324 res
= IDirectSoundBuffer_Lock(hdsbuf
,write_offset
, len
, &lpvPtr1
, &dwBytes1
, &lpvPtr2
, &dwBytes2
, 0);
330 if( (ao_data
.channels
== 6) && (ao_data
.format
!=AF_FORMAT_AC3
) ) {
331 // reorder channels while writing to pointers.
332 // it's this easy because buffer size and len are always
333 // aligned to multiples of channels*bytespersample
334 // there's probably some room for speed improvements here
335 const int chantable
[6] = {0, 1, 4, 5, 2, 3}; // reorder "matrix"
337 int numsamp
,sampsize
;
339 sampsize
= af_fmt2bits(ao_data
.format
)>>3; // bytes per sample
340 numsamp
= dwBytes1
/ (ao_data
.channels
* sampsize
); // number of samples for each channel in this buffer
342 for( i
= 0; i
< numsamp
; i
++ ) for( j
= 0; j
< ao_data
.channels
; j
++ ) {
343 memcpy(lpvPtr1
+(i
*ao_data
.channels
*sampsize
)+(chantable
[j
]*sampsize
),data
+(i
*ao_data
.channels
*sampsize
)+(j
*sampsize
),sampsize
);
346 if (NULL
!= lpvPtr2
)
348 numsamp
= dwBytes2
/ (ao_data
.channels
* sampsize
);
349 for( i
= 0; i
< numsamp
; i
++ ) for( j
= 0; j
< ao_data
.channels
; j
++ ) {
350 memcpy(lpvPtr2
+(i
*ao_data
.channels
*sampsize
)+(chantable
[j
]*sampsize
),data
+dwBytes1
+(i
*ao_data
.channels
*sampsize
)+(j
*sampsize
),sampsize
);
354 write_offset
+=dwBytes1
+dwBytes2
;
355 if(write_offset
>=buffer_size
)write_offset
=dwBytes2
;
357 // Write to pointers without reordering.
358 fast_memcpy(lpvPtr1
,data
,dwBytes1
);
359 if (NULL
!= lpvPtr2
)fast_memcpy(lpvPtr2
,data
+dwBytes1
,dwBytes2
);
360 write_offset
+=dwBytes1
+dwBytes2
;
361 if(write_offset
>=buffer_size
)write_offset
=dwBytes2
;
364 // Release the data back to DirectSound.
365 res
= IDirectSoundBuffer_Unlock(hdsbuf
,lpvPtr1
,dwBytes1
,lpvPtr2
,dwBytes2
);
370 IDirectSoundBuffer_GetStatus(hdsbuf
, &status
);
371 if (!(status
& DSBSTATUS_PLAYING
)){
372 res
= IDirectSoundBuffer_Play(hdsbuf
, 0, 0, DSBPLAY_LOOPING
);
374 return dwBytes1
+dwBytes2
;
377 // Lock, Unlock, or Restore failed.
381 /***************************************************************************************/
384 \brief handle control commands
387 \return CONTROL_OK or -1 in case the command can't be handled
389 static int control(int cmd
, void *arg
)
393 case AOCONTROL_GET_VOLUME
: {
394 ao_control_vol_t
* vol
= (ao_control_vol_t
*)arg
;
395 IDirectSoundBuffer_GetVolume(hdsbuf
, &volume
);
396 vol
->left
= vol
->right
= pow(10.0, (float)(volume
+10000) / 5000.0);
397 //printf("ao_dsound: volume: %f\n",vol->left);
400 case AOCONTROL_SET_VOLUME
: {
401 ao_control_vol_t
* vol
= (ao_control_vol_t
*)arg
;
402 volume
= (DWORD
)(log10(vol
->right
) * 5000.0) - 10000;
403 IDirectSoundBuffer_SetVolume(hdsbuf
, volume
);
404 //printf("ao_dsound: volume: %f\n",vol->left);
412 \brief setup sound device
413 \param rate samplerate
414 \param channels number of channels
417 \return 1=success 0=fail
419 static int init(int rate
, int channels
, int format
, int flags
)
422 if (!InitDirectSound()) return 0;
424 // ok, now create the buffers
425 WAVEFORMATEXTENSIBLE wformat
;
426 DSBUFFERDESC dsbpridesc
;
427 DSBUFFERDESC dsbdesc
;
429 //check if the format is supported in general
432 case AF_FORMAT_S24_LE
:
433 case AF_FORMAT_S16_LE
:
437 mp_msg(MSGT_AO
, MSGL_V
,"ao_dsound: format %s not supported defaulting to Signed 16-bit Little-Endian\n",af_fmt2str_short(format
));
438 format
=AF_FORMAT_S16_LE
;
440 //fill global ao_data
441 ao_data
.channels
= channels
;
442 ao_data
.samplerate
= rate
;
443 ao_data
.format
= format
;
444 ao_data
.bps
= channels
* rate
* (af_fmt2bits(format
)>>3);
445 if(ao_data
.buffersize
==-1) ao_data
.buffersize
= ao_data
.bps
; // space for 1 sec
446 mp_msg(MSGT_AO
, MSGL_V
,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate
, channels
, af_fmt2str_short(format
));
447 mp_msg(MSGT_AO
, MSGL_V
,"ao_dsound: Buffersize:%d bytes (%d msec)\n", ao_data
.buffersize
, ao_data
.buffersize
/ ao_data
.bps
* 1000);
450 ZeroMemory(&wformat
, sizeof(WAVEFORMATEXTENSIBLE
));
451 wformat
.Format
.cbSize
= (channels
> 2) ? sizeof(WAVEFORMATEXTENSIBLE
)-sizeof(WAVEFORMATEX
) : 0;
452 wformat
.Format
.nChannels
= channels
;
453 wformat
.Format
.nSamplesPerSec
= rate
;
454 if (format
== AF_FORMAT_AC3
) {
455 wformat
.Format
.wFormatTag
= WAVE_FORMAT_DOLBY_AC3_SPDIF
;
456 wformat
.Format
.wBitsPerSample
= 16;
457 wformat
.Format
.nBlockAlign
= 4;
459 wformat
.Format
.wFormatTag
= (channels
> 2) ? WAVE_FORMAT_EXTENSIBLE
: WAVE_FORMAT_PCM
;
460 wformat
.Format
.wBitsPerSample
= af_fmt2bits(format
);
461 wformat
.Format
.nBlockAlign
= wformat
.Format
.nChannels
* (wformat
.Format
.wBitsPerSample
>> 3);
464 // fill in primary sound buffer descriptor
465 memset(&dsbpridesc
, 0, sizeof(DSBUFFERDESC
));
466 dsbpridesc
.dwSize
= sizeof(DSBUFFERDESC
);
467 dsbpridesc
.dwFlags
= DSBCAPS_PRIMARYBUFFER
;
468 dsbpridesc
.dwBufferBytes
= 0;
469 dsbpridesc
.lpwfxFormat
= NULL
;
472 // fill in the secondary sound buffer (=stream buffer) descriptor
473 memset(&dsbdesc
, 0, sizeof(DSBUFFERDESC
));
474 dsbdesc
.dwSize
= sizeof(DSBUFFERDESC
);
475 dsbdesc
.dwFlags
= DSBCAPS_GETCURRENTPOSITION2
/** Better position accuracy */
476 | DSBCAPS_GLOBALFOCUS
/** Allows background playing */
477 | DSBCAPS_CTRLVOLUME
; /** volume control enabled */
480 wformat
.dwChannelMask
= channel_mask
[channels
- 3];
481 wformat
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
482 wformat
.Samples
.wValidBitsPerSample
= wformat
.Format
.wBitsPerSample
;
483 // Needed for 5.1 on emu101k - shit soundblaster
484 dsbdesc
.dwFlags
|= DSBCAPS_LOCHARDWARE
;
486 wformat
.Format
.nAvgBytesPerSec
= wformat
.Format
.nSamplesPerSec
* wformat
.Format
.nBlockAlign
;
488 dsbdesc
.dwBufferBytes
= ao_data
.buffersize
;
489 dsbdesc
.lpwfxFormat
= (WAVEFORMATEX
*)&wformat
;
490 buffer_size
= dsbdesc
.dwBufferBytes
;
492 min_free_space
= wformat
.Format
.nBlockAlign
;
493 ao_data
.outburst
= wformat
.Format
.nBlockAlign
* 512;
495 // create primary buffer and set its format
497 res
= IDirectSound_CreateSoundBuffer( hds
, &dsbpridesc
, &hdspribuf
, NULL
);
498 if ( res
!= DS_OK
) {
500 mp_msg(MSGT_AO
, MSGL_ERR
,"ao_dsound: cannot create primary buffer (%s)\n", dserr2str(res
));
503 res
= IDirectSoundBuffer_SetFormat( hdspribuf
, (WAVEFORMATEX
*)&wformat
);
504 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
));
506 mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: primary buffer created\n");
508 // now create the stream buffer
510 res
= IDirectSound_CreateSoundBuffer(hds
, &dsbdesc
, &hdsbuf
, NULL
);
512 if (dsbdesc
.dwFlags
& DSBCAPS_LOCHARDWARE
) {
513 // Try without DSBCAPS_LOCHARDWARE
514 dsbdesc
.dwFlags
&= ~DSBCAPS_LOCHARDWARE
;
515 res
= IDirectSound_CreateSoundBuffer(hds
, &dsbdesc
, &hdsbuf
, NULL
);
519 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_dsound: cannot create secondary (stream)buffer (%s)\n", dserr2str(res
));
523 mp_msg(MSGT_AO
, MSGL_V
, "ao_dsound: secondary (stream)buffer created\n");
530 \brief stop playing and empty buffers (for seeking/pause)
532 static void reset(void)
534 IDirectSoundBuffer_Stop(hdsbuf
);
535 // reset directsound buffer
536 IDirectSoundBuffer_SetCurrentPosition(hdsbuf
, 0);
541 \brief stop playing, keep buffers (for pause)
543 static void audio_pause(void)
545 IDirectSoundBuffer_Stop(hdsbuf
);
549 \brief resume playing, after audio_pause()
551 static void audio_resume(void)
553 IDirectSoundBuffer_Play(hdsbuf
, 0, 0, DSBPLAY_LOOPING
);
557 \brief close audio device
558 \param immed stop playback immediately
560 static void uninit(int immed
)
565 IDirectSoundBuffer_Play(hdsbuf
, 0, 0, 0);
566 while(!IDirectSoundBuffer_GetStatus(hdsbuf
,&status
) && (status
&DSBSTATUS_PLAYING
))
574 \brief find out how many bytes can be written into the audio buffer without
575 \return free space in bytes, has to return 0 if the buffer is almost full
577 static int get_space(void)
581 IDirectSoundBuffer_GetCurrentPosition(hdsbuf
,&play_offset
,NULL
);
582 space
=buffer_size
-(write_offset
-play_offset
);
583 // | | <-- const --> | | |
584 // buffer start play_cursor write_cursor write_offset buffer end
585 // play_cursor is the actual postion of the play cursor
586 // write_cursor is the position after which it is assumed to be save to write data
587 // write_offset is the postion where we actually write the data to
588 if(space
> buffer_size
)space
-= buffer_size
; // write_offset < play_offset
589 if(space
< min_free_space
)return 0;
590 return space
-min_free_space
;
594 \brief play 'len' bytes of 'data'
595 \param data pointer to the data to play
596 \param len size in bytes of the data buffer, gets rounded down to outburst*n
597 \param flags currently unused
598 \return number of played bytes
600 static int play(void* data
, int len
, int flags
)
605 // make sure we have enough space to write data
606 IDirectSoundBuffer_GetCurrentPosition(hdsbuf
,&play_offset
,NULL
);
607 space
=buffer_size
-(write_offset
-play_offset
);
608 if(space
> buffer_size
)space
-= buffer_size
; // write_offset < play_offset
609 if(space
< len
) len
= space
;
611 if (!(flags
& AOPLAY_FINAL_CHUNK
))
612 len
= (len
/ ao_data
.outburst
) * ao_data
.outburst
;
613 return write_buffer(data
, len
);
617 \brief get the delay between the first and last sample in the buffer
618 \return delay in seconds
620 static float get_delay(void)
624 IDirectSoundBuffer_GetCurrentPosition(hdsbuf
,&play_offset
,NULL
);
625 space
=play_offset
-write_offset
;
626 if(space
<= 0)space
+= buffer_size
;
627 return (float)(buffer_size
- space
) / (float)ao_data
.bps
;