cleanup: Silence compilation warnings on MinGW-w64
[mplayer.git] / libao2 / ao_dsound.c
blob63cc02e92c04fe8970459381fe462fdeff1e8609
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 #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 #if 0
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
91 #endif
93 #ifndef _WAVEFORMATEXTENSIBLE_
94 typedef struct {
95 WAVEFORMATEX Format;
96 union {
97 WORD wValidBitsPerSample; /* bits of precision */
98 WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
99 WORD wReserved; /* If neither applies, set to zero. */
100 } Samples;
101 DWORD dwChannelMask; /* which channels are */
102 /* present in stream */
103 GUID SubFormat;
104 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
105 #endif
107 static const int channel_mask[] = {
108 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY,
109 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
110 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY,
111 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY
114 static HINSTANCE hdsound_dll = NULL; ///handle to the dll
115 static LPDIRECTSOUND hds = NULL; ///direct sound object
116 static LPDIRECTSOUNDBUFFER hdspribuf = NULL; ///primary direct sound buffer
117 static LPDIRECTSOUNDBUFFER hdsbuf = NULL; ///secondary direct sound buffer (stream buffer)
118 static int buffer_size = 0; ///size in bytes of the direct sound buffer
119 static int write_offset = 0; ///offset of the write cursor in the direct sound buffer
120 static int min_free_space = 0; ///if the free space is below this value get_space() will return 0
121 ///there will always be at least this amout of free space to prevent
122 ///get_space() from returning wrong values when buffer is 100% full.
123 ///will be replaced with nBlockAlign in init()
124 static int device_num = 0; ///wanted device number
125 static GUID device; ///guid of the device
127 /***************************************************************************************/
130 \brief output error message
131 \param err error code
132 \return string with the error message
134 static char * dserr2str(int err)
136 switch (err) {
137 case DS_OK: return "DS_OK";
138 case DS_NO_VIRTUALIZATION: return "DS_NO_VIRTUALIZATION";
139 case DSERR_ALLOCATED: return "DS_NO_VIRTUALIZATION";
140 case DSERR_CONTROLUNAVAIL: return "DSERR_CONTROLUNAVAIL";
141 case DSERR_INVALIDPARAM: return "DSERR_INVALIDPARAM";
142 case DSERR_INVALIDCALL: return "DSERR_INVALIDCALL";
143 case DSERR_GENERIC: return "DSERR_GENERIC";
144 case DSERR_PRIOLEVELNEEDED: return "DSERR_PRIOLEVELNEEDED";
145 case DSERR_OUTOFMEMORY: return "DSERR_OUTOFMEMORY";
146 case DSERR_BADFORMAT: return "DSERR_BADFORMAT";
147 case DSERR_UNSUPPORTED: return "DSERR_UNSUPPORTED";
148 case DSERR_NODRIVER: return "DSERR_NODRIVER";
149 case DSERR_ALREADYINITIALIZED: return "DSERR_ALREADYINITIALIZED";
150 case DSERR_NOAGGREGATION: return "DSERR_NOAGGREGATION";
151 case DSERR_BUFFERLOST: return "DSERR_BUFFERLOST";
152 case DSERR_OTHERAPPHASPRIO: return "DSERR_OTHERAPPHASPRIO";
153 case DSERR_UNINITIALIZED: return "DSERR_UNINITIALIZED";
154 case DSERR_NOINTERFACE: return "DSERR_NOINTERFACE";
155 case DSERR_ACCESSDENIED: return "DSERR_ACCESSDENIED";
156 default: return "unknown";
161 \brief uninitialize direct sound
163 static void UninitDirectSound(void)
165 // finally release the DirectSound object
166 if (hds) {
167 IDirectSound_Release(hds);
168 hds = NULL;
170 // free DSOUND.DLL
171 if (hdsound_dll) {
172 FreeLibrary(hdsound_dll);
173 hdsound_dll = NULL;
175 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound uninitialized\n");
179 \brief print the commandline help
181 static void print_help(void)
183 mp_msg(MSGT_AO, MSGL_FATAL,
184 "\n-ao dsound commandline help:\n"
185 "Example: mplayer -ao dsound:device=1\n"
186 " sets 1st device\n"
187 "\nOptions:\n"
188 " device=<device-number>\n"
189 " Sets device number, use -v to get a list\n");
194 \brief enumerate direct sound devices
195 \return TRUE to continue with the enumeration
197 static BOOL CALLBACK DirectSoundEnum(LPGUID guid,LPCSTR desc,LPCSTR module,LPVOID context)
199 int* device_index=context;
200 mp_msg(MSGT_AO, MSGL_V,"%i %s ",*device_index,desc);
201 if(device_num==*device_index){
202 mp_msg(MSGT_AO, MSGL_V,"<--");
203 if(guid){
204 memcpy(&device,guid,sizeof(GUID));
207 mp_msg(MSGT_AO, MSGL_V,"\n");
208 (*device_index)++;
209 return TRUE;
214 \brief initilize direct sound
215 \return 0 if error, 1 if ok
217 static int InitDirectSound(void)
219 DSCAPS dscaps;
221 // initialize directsound
222 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
223 HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKA, LPVOID);
224 int device_index=0;
225 const opt_t subopts[] = {
226 {"device", OPT_ARG_INT, &device_num,NULL},
227 {NULL}
229 if (subopt_parse(ao_subdevice, subopts) != 0) {
230 print_help();
231 return 0;
234 hdsound_dll = LoadLibrary("DSOUND.DLL");
235 if (hdsound_dll == NULL) {
236 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot load DSOUND.DLL\n");
237 return 0;
239 OurDirectSoundCreate = (void*)GetProcAddress(hdsound_dll, "DirectSoundCreate");
240 OurDirectSoundEnumerate = (void*)GetProcAddress(hdsound_dll, "DirectSoundEnumerateA");
242 if (OurDirectSoundCreate == NULL || OurDirectSoundEnumerate == NULL) {
243 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: GetProcAddress FAILED\n");
244 FreeLibrary(hdsound_dll);
245 return 0;
248 // Enumerate all directsound devices
249 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Output Devices:\n");
250 OurDirectSoundEnumerate(DirectSoundEnum,&device_index);
252 // Create the direct sound object
253 if FAILED(OurDirectSoundCreate((device_num)?&device:NULL, &hds, NULL )) {
254 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create a DirectSound device\n");
255 FreeLibrary(hdsound_dll);
256 return 0;
259 /* Set DirectSound Cooperative level, ie what control we want over Windows
260 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
261 * settings of the primary buffer, but also that only the sound of our
262 * application will be hearable when it will have the focus.
263 * !!! (this is not really working as intended yet because to set the
264 * cooperative level you need the window handle of your application, and
265 * I don't know of any easy way to get it. Especially since we might play
266 * sound without any video, and so what window handle should we use ???
267 * The hack for now is to use the Desktop window handle - it seems to be
268 * working */
269 if (IDirectSound_SetCooperativeLevel(hds, GetDesktopWindow(), DSSCL_EXCLUSIVE)) {
270 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot set direct sound cooperative level\n");
271 IDirectSound_Release(hds);
272 FreeLibrary(hdsound_dll);
273 return 0;
275 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound initialized\n");
277 memset(&dscaps, 0, sizeof(DSCAPS));
278 dscaps.dwSize = sizeof(DSCAPS);
279 if (DS_OK == IDirectSound_GetCaps(hds, &dscaps)) {
280 if (dscaps.dwFlags & DSCAPS_EMULDRIVER) mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound is emulated, waveOut may give better performance\n");
281 } else {
282 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: cannot get device capabilities\n");
285 return 1;
289 \brief destroy the direct sound buffer
291 static void DestroyBuffer(void)
293 if (hdsbuf) {
294 IDirectSoundBuffer_Release(hdsbuf);
295 hdsbuf = NULL;
297 if (hdspribuf) {
298 IDirectSoundBuffer_Release(hdspribuf);
299 hdspribuf = NULL;
304 \brief fill sound buffer
305 \param data pointer to the sound data to copy
306 \param len length of the data to copy in bytes
307 \return number of copyed bytes
309 static int write_buffer(unsigned char *data, int len)
311 HRESULT res;
312 LPVOID lpvPtr1;
313 DWORD dwBytes1;
314 LPVOID lpvPtr2;
315 DWORD dwBytes2;
317 // Lock the buffer
318 res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
319 // If the buffer was lost, restore and retry lock.
320 if (DSERR_BUFFERLOST == res)
322 IDirectSoundBuffer_Restore(hdsbuf);
323 res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
327 if (SUCCEEDED(res))
329 if( (ao_data.channels == 6) && !AF_FORMAT_IS_AC3(ao_data.format) ) {
330 // reorder channels while writing to pointers.
331 // it's this easy because buffer size and len are always
332 // aligned to multiples of channels*bytespersample
333 // there's probably some room for speed improvements here
334 const int chantable[6] = {0, 1, 4, 5, 2, 3}; // reorder "matrix"
335 int i, j;
336 int numsamp,sampsize;
338 sampsize = af_fmt2bits(ao_data.format)>>3; // bytes per sample
339 numsamp = dwBytes1 / (ao_data.channels * sampsize); // number of samples for each channel in this buffer
341 for( i = 0; i < numsamp; i++ ) for( j = 0; j < ao_data.channels; j++ ) {
342 memcpy((char*)lpvPtr1+(i*ao_data.channels*sampsize)+(chantable[j]*sampsize),data+(i*ao_data.channels*sampsize)+(j*sampsize),sampsize);
345 if (NULL != lpvPtr2 )
347 numsamp = dwBytes2 / (ao_data.channels * sampsize);
348 for( i = 0; i < numsamp; i++ ) for( j = 0; j < ao_data.channels; j++ ) {
349 memcpy((char*)lpvPtr2+(i*ao_data.channels*sampsize)+(chantable[j]*sampsize),data+dwBytes1+(i*ao_data.channels*sampsize)+(j*sampsize),sampsize);
353 write_offset+=dwBytes1+dwBytes2;
354 if(write_offset>=buffer_size)write_offset=dwBytes2;
355 } else {
356 // Write to pointers without reordering.
357 fast_memcpy(lpvPtr1,data,dwBytes1);
358 if (NULL != lpvPtr2 )fast_memcpy(lpvPtr2,data+dwBytes1,dwBytes2);
359 write_offset+=dwBytes1+dwBytes2;
360 if(write_offset>=buffer_size)write_offset=dwBytes2;
363 // Release the data back to DirectSound.
364 res = IDirectSoundBuffer_Unlock(hdsbuf,lpvPtr1,dwBytes1,lpvPtr2,dwBytes2);
365 if (SUCCEEDED(res))
367 // Success.
368 DWORD status;
369 IDirectSoundBuffer_GetStatus(hdsbuf, &status);
370 if (!(status & DSBSTATUS_PLAYING)){
371 res = IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
373 return dwBytes1+dwBytes2;
376 // Lock, Unlock, or Restore failed.
377 return 0;
380 /***************************************************************************************/
383 \brief handle control commands
384 \param cmd command
385 \param arg argument
386 \return CONTROL_OK or -1 in case the command can't be handled
388 static int control(int cmd, void *arg)
390 DWORD volume;
391 switch (cmd) {
392 case AOCONTROL_GET_VOLUME: {
393 ao_control_vol_t* vol = (ao_control_vol_t*)arg;
394 IDirectSoundBuffer_GetVolume(hdsbuf, &volume);
395 vol->left = vol->right = pow(10.0, (float)(volume+10000) / 5000.0);
396 //printf("ao_dsound: volume: %f\n",vol->left);
397 return CONTROL_OK;
399 case AOCONTROL_SET_VOLUME: {
400 ao_control_vol_t* vol = (ao_control_vol_t*)arg;
401 volume = (DWORD)(log10(vol->right) * 5000.0) - 10000;
402 IDirectSoundBuffer_SetVolume(hdsbuf, volume);
403 //printf("ao_dsound: volume: %f\n",vol->left);
404 return CONTROL_OK;
407 return -1;
411 \brief setup sound device
412 \param rate samplerate
413 \param channels number of channels
414 \param format format
415 \param flags unused
416 \return 1=success 0=fail
418 static int init(int rate, int channels, int format, int flags)
420 int res;
421 if (!InitDirectSound()) return 0;
423 // ok, now create the buffers
424 WAVEFORMATEXTENSIBLE wformat;
425 DSBUFFERDESC dsbpridesc;
426 DSBUFFERDESC dsbdesc;
428 //check if the channel count and format is supported in general
429 if (channels > 6) {
430 UninitDirectSound();
431 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: 8 channel audio not yet supported\n");
432 return 0;
435 if (AF_FORMAT_IS_AC3(format))
436 format = AF_FORMAT_AC3_NE;
437 switch(format){
438 case AF_FORMAT_AC3_NE:
439 case AF_FORMAT_S24_LE:
440 case AF_FORMAT_S16_LE:
441 case AF_FORMAT_U8:
442 break;
443 default:
444 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: format %s not supported defaulting to Signed 16-bit Little-Endian\n",af_fmt2str_short(format));
445 format=AF_FORMAT_S16_LE;
447 //fill global ao_data
448 ao_data.channels = channels;
449 ao_data.samplerate = rate;
450 ao_data.format = format;
451 ao_data.bps = channels * rate * (af_fmt2bits(format)>>3);
452 if(ao_data.buffersize==-1) ao_data.buffersize = ao_data.bps; // space for 1 sec
453 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, channels, af_fmt2str_short(format));
454 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Buffersize:%d bytes (%d msec)\n", ao_data.buffersize, ao_data.buffersize / ao_data.bps * 1000);
456 //fill waveformatex
457 ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
458 wformat.Format.cbSize = (channels > 2) ? sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) : 0;
459 wformat.Format.nChannels = channels;
460 wformat.Format.nSamplesPerSec = rate;
461 if (AF_FORMAT_IS_AC3(format)) {
462 wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
463 wformat.Format.wBitsPerSample = 16;
464 wformat.Format.nBlockAlign = 4;
465 } else {
466 wformat.Format.wFormatTag = (channels > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
467 wformat.Format.wBitsPerSample = af_fmt2bits(format);
468 wformat.Format.nBlockAlign = wformat.Format.nChannels * (wformat.Format.wBitsPerSample >> 3);
471 // fill in primary sound buffer descriptor
472 memset(&dsbpridesc, 0, sizeof(DSBUFFERDESC));
473 dsbpridesc.dwSize = sizeof(DSBUFFERDESC);
474 dsbpridesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
475 dsbpridesc.dwBufferBytes = 0;
476 dsbpridesc.lpwfxFormat = NULL;
479 // fill in the secondary sound buffer (=stream buffer) descriptor
480 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
481 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
482 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
483 | DSBCAPS_GLOBALFOCUS /** Allows background playing */
484 | DSBCAPS_CTRLVOLUME; /** volume control enabled */
486 if (channels > 2) {
487 wformat.dwChannelMask = channel_mask[channels - 3];
488 wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
489 wformat.Samples.wValidBitsPerSample = wformat.Format.wBitsPerSample;
490 // Needed for 5.1 on emu101k - shit soundblaster
491 dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
493 wformat.Format.nAvgBytesPerSec = wformat.Format.nSamplesPerSec * wformat.Format.nBlockAlign;
495 dsbdesc.dwBufferBytes = ao_data.buffersize;
496 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat;
497 buffer_size = dsbdesc.dwBufferBytes;
498 write_offset = 0;
499 min_free_space = wformat.Format.nBlockAlign;
500 ao_data.outburst = wformat.Format.nBlockAlign * 512;
502 // create primary buffer and set its format
504 res = IDirectSound_CreateSoundBuffer( hds, &dsbpridesc, &hdspribuf, NULL );
505 if ( res != DS_OK ) {
506 UninitDirectSound();
507 mp_msg(MSGT_AO, MSGL_ERR,"ao_dsound: cannot create primary buffer (%s)\n", dserr2str(res));
508 return 0;
510 res = IDirectSoundBuffer_SetFormat( hdspribuf, (WAVEFORMATEX *)&wformat );
511 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));
513 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: primary buffer created\n");
515 // now create the stream buffer
517 res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
518 if (res != DS_OK) {
519 if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) {
520 // Try without DSBCAPS_LOCHARDWARE
521 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
522 res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
524 if (res != DS_OK) {
525 UninitDirectSound();
526 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create secondary (stream)buffer (%s)\n", dserr2str(res));
527 return 0;
530 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: secondary (stream)buffer created\n");
531 return 1;
537 \brief stop playing and empty buffers (for seeking/pause)
539 static void reset(void)
541 IDirectSoundBuffer_Stop(hdsbuf);
542 // reset directsound buffer
543 IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0);
544 write_offset=0;
548 \brief stop playing, keep buffers (for pause)
550 static void audio_pause(void)
552 IDirectSoundBuffer_Stop(hdsbuf);
556 \brief resume playing, after audio_pause()
558 static void audio_resume(void)
560 IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
564 \brief close audio device
565 \param immed stop playback immediately
567 static void uninit(int immed)
569 if(immed)reset();
570 else{
571 DWORD status;
572 IDirectSoundBuffer_Play(hdsbuf, 0, 0, 0);
573 while(!IDirectSoundBuffer_GetStatus(hdsbuf,&status) && (status&DSBSTATUS_PLAYING))
574 usec_sleep(20000);
576 DestroyBuffer();
577 UninitDirectSound();
581 \brief find out how many bytes can be written into the audio buffer without
582 \return free space in bytes, has to return 0 if the buffer is almost full
584 static int get_space(void)
586 int space;
587 DWORD play_offset;
588 IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
589 space=buffer_size-(write_offset-play_offset);
590 // | | <-- const --> | | |
591 // buffer start play_cursor write_cursor write_offset buffer end
592 // play_cursor is the actual postion of the play cursor
593 // write_cursor is the position after which it is assumed to be save to write data
594 // write_offset is the postion where we actually write the data to
595 if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
596 if(space < min_free_space)return 0;
597 return space-min_free_space;
601 \brief play 'len' bytes of 'data'
602 \param data pointer to the data to play
603 \param len size in bytes of the data buffer, gets rounded down to outburst*n
604 \param flags currently unused
605 \return number of played bytes
607 static int play(void* data, int len, int flags)
609 DWORD play_offset;
610 int space;
612 // make sure we have enough space to write data
613 IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
614 space=buffer_size-(write_offset-play_offset);
615 if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
616 if(space < len) len = space;
618 if (!(flags & AOPLAY_FINAL_CHUNK))
619 len = (len / ao_data.outburst) * ao_data.outburst;
620 return write_buffer(data, len);
624 \brief get the delay between the first and last sample in the buffer
625 \return delay in seconds
627 static float get_delay(void)
629 DWORD play_offset;
630 int space;
631 IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
632 space=play_offset-write_offset;
633 if(space <= 0)space += buffer_size;
634 return (float)(buffer_size - space) / (float)ao_data.bps;