Add a comment that explains why this header has no multiple inclusion guards.
[mplayer/greg.git] / libao2 / ao_dsound.c
blobd66de37ec505dd56a0e1ada6d0c6d2bf682d5567
1 /******************************************************************************
2 * ao_dsound.c: Windows DirectSound interface for MPlayer
3 * Copyright (c) 2004 Gabor Szecsi <deje@miki.hu>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *****************************************************************************/
20 /**
21 \todo verify/extend multichannel support
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <windows.h>
28 #define DIRECTSOUND_VERSION 0x0600
29 #include <dsound.h>
30 #include <math.h>
32 #include "config.h"
33 #include "libaf/af_format.h"
34 #include "audio_out.h"
35 #include "audio_out_internal.h"
36 #include "mp_msg.h"
37 #include "libvo/fastmemcpy.h"
38 #include "osdep/timer.h"
39 #include "subopt-helper.h"
42 static ao_info_t info =
44 "Windows DirectSound audio output",
45 "dsound",
46 "Gabor Szecsi <deje@miki.hu>",
50 LIBAO_EXTERN(dsound)
52 /**
53 \todo use the definitions from the win32 api headers when they define these
55 #if 1
56 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
57 #define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
58 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
60 static const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x1,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}};
62 #define SPEAKER_FRONT_LEFT 0x1
63 #define SPEAKER_FRONT_RIGHT 0x2
64 #define SPEAKER_FRONT_CENTER 0x4
65 #define SPEAKER_LOW_FREQUENCY 0x8
66 #define SPEAKER_BACK_LEFT 0x10
67 #define SPEAKER_BACK_RIGHT 0x20
68 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
69 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
70 #define SPEAKER_BACK_CENTER 0x100
71 #define SPEAKER_SIDE_LEFT 0x200
72 #define SPEAKER_SIDE_RIGHT 0x400
73 #define SPEAKER_TOP_CENTER 0x800
74 #define SPEAKER_TOP_FRONT_LEFT 0x1000
75 #define SPEAKER_TOP_FRONT_CENTER 0x2000
76 #define SPEAKER_TOP_FRONT_RIGHT 0x4000
77 #define SPEAKER_TOP_BACK_LEFT 0x8000
78 #define SPEAKER_TOP_BACK_CENTER 0x10000
79 #define SPEAKER_TOP_BACK_RIGHT 0x20000
80 #define SPEAKER_RESERVED 0x80000000
82 #define DSSPEAKER_HEADPHONE 0x00000001
83 #define DSSPEAKER_MONO 0x00000002
84 #define DSSPEAKER_QUAD 0x00000003
85 #define DSSPEAKER_STEREO 0x00000004
86 #define DSSPEAKER_SURROUND 0x00000005
87 #define DSSPEAKER_5POINT1 0x00000006
89 #ifndef _WAVEFORMATEXTENSIBLE_
90 typedef struct {
91 WAVEFORMATEX Format;
92 union {
93 WORD wValidBitsPerSample; /* bits of precision */
94 WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
95 WORD wReserved; /* If neither applies, set to zero. */
96 } Samples;
97 DWORD dwChannelMask; /* which channels are */
98 /* present in stream */
99 GUID SubFormat;
100 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
101 #endif
103 #endif
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)
134 switch (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
164 if (hds) {
165 IDirectSound_Release(hds);
166 hds = NULL;
168 // free DSOUND.DLL
169 if (hdsound_dll) {
170 FreeLibrary(hdsound_dll);
171 hdsound_dll = NULL;
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"
184 " sets 1st device\n"
185 "\nOptions:\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,"<--");
201 if(guid){
202 memcpy(&device,guid,sizeof(GUID));
205 mp_msg(MSGT_AO, MSGL_V,"\n");
206 (*device_index)++;
207 return TRUE;
212 \brief initilize direct sound
213 \return 0 if error, 1 if ok
215 static int InitDirectSound(void)
217 DSCAPS dscaps;
219 // initialize directsound
220 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
221 HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKA, LPVOID);
222 int device_index=0;
223 opt_t subopts[] = {
224 {"device", OPT_ARG_INT, &device_num,NULL},
225 {NULL}
227 if (subopt_parse(ao_subdevice, subopts) != 0) {
228 print_help();
229 return 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");
235 return 0;
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);
243 return 0;
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);
254 return 0;
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
266 * working */
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);
271 return 0;
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");
279 } else {
280 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: cannot get device capabilities\n");
283 return 1;
287 \brief destroy the direct sound buffer
289 static void DestroyBuffer(void)
291 if (hdsbuf) {
292 IDirectSoundBuffer_Release(hdsbuf);
293 hdsbuf = NULL;
295 if (hdspribuf) {
296 IDirectSoundBuffer_Release(hdspribuf);
297 hdspribuf = NULL;
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)
309 HRESULT res;
310 LPVOID lpvPtr1;
311 DWORD dwBytes1;
312 LPVOID lpvPtr2;
313 DWORD dwBytes2;
315 // Lock the buffer
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);
325 if (SUCCEEDED(res))
327 if( (ao_data.channels == 6) && (ao_data.format!=AF_FORMAT_AC3) ) {
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"
333 int i, j;
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;
353 } else {
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);
363 if (SUCCEEDED(res))
365 // Success.
366 DWORD status;
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.
375 return 0;
378 /***************************************************************************************/
381 \brief handle control commands
382 \param cmd command
383 \param arg argument
384 \return CONTROL_OK or -1 in case the command can't be handled
386 static int control(int cmd, void *arg)
388 DWORD volume;
389 switch (cmd) {
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);
395 return CONTROL_OK;
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);
402 return CONTROL_OK;
405 return -1;
408 /**
409 \brief setup sound device
410 \param rate samplerate
411 \param channels number of channels
412 \param format format
413 \param flags unused
414 \return 1=success 0=fail
416 static int init(int rate, int channels, int format, int flags)
418 int res;
419 if (!InitDirectSound()) return 0;
421 // ok, now create the buffers
422 WAVEFORMATEXTENSIBLE wformat;
423 DSBUFFERDESC dsbpridesc;
424 DSBUFFERDESC dsbdesc;
426 //check if the format is supported in general
427 switch(format){
428 case AF_FORMAT_AC3:
429 case AF_FORMAT_S24_LE:
430 case AF_FORMAT_S16_LE:
431 case AF_FORMAT_S8:
432 break;
433 default:
434 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: format %s not supported defaulting to Signed 16-bit Little-Endian\n",af_fmt2str_short(format));
435 format=AF_FORMAT_S16_LE;
437 //fill global ao_data
438 ao_data.channels = channels;
439 ao_data.samplerate = rate;
440 ao_data.format = format;
441 ao_data.bps = channels * rate * (af_fmt2bits(format)>>3);
442 if(ao_data.buffersize==-1) ao_data.buffersize = ao_data.bps; // space for 1 sec
443 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, channels, af_fmt2str_short(format));
444 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Buffersize:%d bytes (%d msec)\n", ao_data.buffersize, ao_data.buffersize / ao_data.bps * 1000);
446 //fill waveformatex
447 ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
448 wformat.Format.cbSize = (channels > 2) ? sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) : 0;
449 wformat.Format.nChannels = channels;
450 wformat.Format.nSamplesPerSec = rate;
451 if (format == AF_FORMAT_AC3) {
452 wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
453 wformat.Format.wBitsPerSample = 16;
454 wformat.Format.nBlockAlign = 4;
455 } else {
456 wformat.Format.wFormatTag = (channels > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
457 wformat.Format.wBitsPerSample = af_fmt2bits(format);
458 wformat.Format.nBlockAlign = wformat.Format.nChannels * (wformat.Format.wBitsPerSample >> 3);
461 // fill in primary sound buffer descriptor
462 memset(&dsbpridesc, 0, sizeof(DSBUFFERDESC));
463 dsbpridesc.dwSize = sizeof(DSBUFFERDESC);
464 dsbpridesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
465 dsbpridesc.dwBufferBytes = 0;
466 dsbpridesc.lpwfxFormat = NULL;
469 // fill in the secondary sound buffer (=stream buffer) descriptor
470 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
471 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
472 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
473 | DSBCAPS_GLOBALFOCUS /** Allows background playing */
474 | DSBCAPS_CTRLVOLUME; /** volume control enabled */
476 if (channels > 2) {
477 wformat.dwChannelMask = channel_mask[channels - 3];
478 wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
479 wformat.Samples.wValidBitsPerSample = wformat.Format.wBitsPerSample;
480 // Needed for 5.1 on emu101k - shit soundblaster
481 dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
483 wformat.Format.nAvgBytesPerSec = wformat.Format.nSamplesPerSec * wformat.Format.nBlockAlign;
485 dsbdesc.dwBufferBytes = ao_data.buffersize;
486 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat;
487 buffer_size = dsbdesc.dwBufferBytes;
488 write_offset = 0;
489 min_free_space = wformat.Format.nBlockAlign;
490 ao_data.outburst = wformat.Format.nBlockAlign * 512;
492 // create primary buffer and set its format
494 res = IDirectSound_CreateSoundBuffer( hds, &dsbpridesc, &hdspribuf, NULL );
495 if ( res != DS_OK ) {
496 UninitDirectSound();
497 mp_msg(MSGT_AO, MSGL_ERR,"ao_dsound: cannot create primary buffer (%s)\n", dserr2str(res));
498 return 0;
500 res = IDirectSoundBuffer_SetFormat( hdspribuf, (WAVEFORMATEX *)&wformat );
501 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));
503 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: primary buffer created\n");
505 // now create the stream buffer
507 res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
508 if (res != DS_OK) {
509 if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) {
510 // Try without DSBCAPS_LOCHARDWARE
511 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
512 res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
514 if (res != DS_OK) {
515 UninitDirectSound();
516 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create secondary (stream)buffer (%s)\n", dserr2str(res));
517 return 0;
520 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: secondary (stream)buffer created\n");
521 return 1;
527 \brief stop playing and empty buffers (for seeking/pause)
529 static void reset(void)
531 IDirectSoundBuffer_Stop(hdsbuf);
532 // reset directsound buffer
533 IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0);
534 write_offset=0;
538 \brief stop playing, keep buffers (for pause)
540 static void audio_pause(void)
542 IDirectSoundBuffer_Stop(hdsbuf);
546 \brief resume playing, after audio_pause()
548 static void audio_resume(void)
550 IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
553 /**
554 \brief close audio device
555 \param immed stop playback immediately
557 static void uninit(int immed)
559 if(immed)reset();
560 else{
561 DWORD status;
562 IDirectSoundBuffer_Play(hdsbuf, 0, 0, 0);
563 while(!IDirectSoundBuffer_GetStatus(hdsbuf,&status) && (status&DSBSTATUS_PLAYING))
564 usec_sleep(20000);
566 DestroyBuffer();
567 UninitDirectSound();
571 \brief find out how many bytes can be written into the audio buffer without
572 \return free space in bytes, has to return 0 if the buffer is almost full
574 static int get_space(void)
576 int space;
577 DWORD play_offset;
578 IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
579 space=buffer_size-(write_offset-play_offset);
580 // | | <-- const --> | | |
581 // buffer start play_cursor write_cursor write_offset buffer end
582 // play_cursor is the actual postion of the play cursor
583 // write_cursor is the position after which it is assumed to be save to write data
584 // write_offset is the postion where we actually write the data to
585 if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
586 if(space < min_free_space)return 0;
587 return space-min_free_space;
591 \brief play 'len' bytes of 'data'
592 \param data pointer to the data to play
593 \param len size in bytes of the data buffer, gets rounded down to outburst*n
594 \param flags currently unused
595 \return number of played bytes
597 static int play(void* data, int len, int flags)
599 DWORD play_offset;
600 int space;
602 // make sure we have enough space to write data
603 IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
604 space=buffer_size-(write_offset-play_offset);
605 if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
606 if(space < len) len = space;
608 if (!(flags & AOPLAY_FINAL_CHUNK))
609 len = (len / ao_data.outburst) * ao_data.outburst;
610 return write_buffer(data, len);
614 \brief get the delay between the first and last sample in the buffer
615 \return delay in seconds
617 static float get_delay(void)
619 DWORD play_offset;
620 int space;
621 IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
622 space=play_offset-write_offset;
623 if(space <= 0)space += buffer_size;
624 return (float)(buffer_size - space) / (float)ao_data.bps;