Cosmetics: Fix indentation
[mplayer/greg.git] / libao2 / ao_dsound.c
blobf66042a793ec3266da17e91d5a045a3efd271ba2
1 /*
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.
23 /**
24 \todo verify/extend multichannel support
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <windows.h>
31 #define DIRECTSOUND_VERSION 0x0600
32 #include <dsound.h>
33 #include <math.h>
35 #include "config.h"
36 #include "libaf/af_format.h"
37 #include "audio_out.h"
38 #include "audio_out_internal.h"
39 #include "mp_msg.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",
48 "dsound",
49 "Gabor Szecsi <deje@miki.hu>",
53 LIBAO_EXTERN(dsound)
55 /**
56 \todo use the definitions from the win32 api headers when they define these
58 #if 1
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_
93 typedef struct {
94 WAVEFORMATEX Format;
95 union {
96 WORD wValidBitsPerSample; /* bits of precision */
97 WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
98 WORD wReserved; /* If neither applies, set to zero. */
99 } Samples;
100 DWORD dwChannelMask; /* which channels are */
101 /* present in stream */
102 GUID SubFormat;
103 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
104 #endif
106 #endif
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)
137 switch (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
167 if (hds) {
168 IDirectSound_Release(hds);
169 hds = NULL;
171 // free DSOUND.DLL
172 if (hdsound_dll) {
173 FreeLibrary(hdsound_dll);
174 hdsound_dll = NULL;
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"
187 " sets 1st device\n"
188 "\nOptions:\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,"<--");
204 if(guid){
205 memcpy(&device,guid,sizeof(GUID));
208 mp_msg(MSGT_AO, MSGL_V,"\n");
209 (*device_index)++;
210 return TRUE;
215 \brief initilize direct sound
216 \return 0 if error, 1 if ok
218 static int InitDirectSound(void)
220 DSCAPS dscaps;
222 // initialize directsound
223 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
224 HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKA, LPVOID);
225 int device_index=0;
226 const opt_t subopts[] = {
227 {"device", OPT_ARG_INT, &device_num,NULL},
228 {NULL}
230 if (subopt_parse(ao_subdevice, subopts) != 0) {
231 print_help();
232 return 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");
238 return 0;
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);
246 return 0;
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);
257 return 0;
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
269 * working */
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);
274 return 0;
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");
282 } else {
283 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: cannot get device capabilities\n");
286 return 1;
290 \brief destroy the direct sound buffer
292 static void DestroyBuffer(void)
294 if (hdsbuf) {
295 IDirectSoundBuffer_Release(hdsbuf);
296 hdsbuf = NULL;
298 if (hdspribuf) {
299 IDirectSoundBuffer_Release(hdspribuf);
300 hdspribuf = NULL;
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)
312 HRESULT res;
313 LPVOID lpvPtr1;
314 DWORD dwBytes1;
315 LPVOID lpvPtr2;
316 DWORD dwBytes2;
318 // Lock the buffer
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);
328 if (SUCCEEDED(res))
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"
336 int i, j;
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;
356 } else {
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);
366 if (SUCCEEDED(res))
368 // Success.
369 DWORD status;
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.
378 return 0;
381 /***************************************************************************************/
384 \brief handle control commands
385 \param cmd command
386 \param arg argument
387 \return CONTROL_OK or -1 in case the command can't be handled
389 static int control(int cmd, void *arg)
391 DWORD volume;
392 switch (cmd) {
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);
398 return CONTROL_OK;
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);
405 return CONTROL_OK;
408 return -1;
412 \brief setup sound device
413 \param rate samplerate
414 \param channels number of channels
415 \param format format
416 \param flags unused
417 \return 1=success 0=fail
419 static int init(int rate, int channels, int format, int flags)
421 int res;
422 if (!InitDirectSound()) return 0;
424 // ok, now create the buffers
425 WAVEFORMATEXTENSIBLE wformat;
426 DSBUFFERDESC dsbpridesc;
427 DSBUFFERDESC dsbdesc;
429 //check if the channel count and format is supported in general
430 if (channels > 6) {
431 UninitDirectSound();
432 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: 8 channel audio not yet supported\n");
433 return 0;
435 switch(format){
436 case AF_FORMAT_AC3:
437 case AF_FORMAT_S24_LE:
438 case AF_FORMAT_S16_LE:
439 case AF_FORMAT_U8:
440 break;
441 default:
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);
454 //fill waveformatex
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 (format == AF_FORMAT_AC3) {
460 wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
461 wformat.Format.wBitsPerSample = 16;
462 wformat.Format.nBlockAlign = 4;
463 } else {
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 */
484 if (channels > 2) {
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;
496 write_offset = 0;
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 ) {
504 UninitDirectSound();
505 mp_msg(MSGT_AO, MSGL_ERR,"ao_dsound: cannot create primary buffer (%s)\n", dserr2str(res));
506 return 0;
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);
516 if (res != DS_OK) {
517 if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) {
518 // Try without DSBCAPS_LOCHARDWARE
519 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
520 res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
522 if (res != DS_OK) {
523 UninitDirectSound();
524 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create secondary (stream)buffer (%s)\n", dserr2str(res));
525 return 0;
528 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: secondary (stream)buffer created\n");
529 return 1;
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);
542 write_offset=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)
567 if(immed)reset();
568 else{
569 DWORD status;
570 IDirectSoundBuffer_Play(hdsbuf, 0, 0, 0);
571 while(!IDirectSoundBuffer_GetStatus(hdsbuf,&status) && (status&DSBSTATUS_PLAYING))
572 usec_sleep(20000);
574 DestroyBuffer();
575 UninitDirectSound();
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)
584 int space;
585 DWORD play_offset;
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)
607 DWORD play_offset;
608 int space;
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)
627 DWORD play_offset;
628 int space;
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;