mixer: fix lowering hw volume while muted
[mplayer.git] / libao2 / ao_dsound.c
blobb33f2949fd3669f968d280c7ff22e26641ed65a2
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 global_ao->no_persistent_volume = true;
425 // ok, now create the buffers
426 WAVEFORMATEXTENSIBLE wformat;
427 DSBUFFERDESC dsbpridesc;
428 DSBUFFERDESC dsbdesc;
430 //check if the channel count and format is supported in general
431 if (channels > 6) {
432 UninitDirectSound();
433 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: 8 channel audio not yet supported\n");
434 return 0;
437 if (AF_FORMAT_IS_AC3(format))
438 format = AF_FORMAT_AC3_NE;
439 switch(format){
440 case AF_FORMAT_AC3_NE:
441 case AF_FORMAT_S24_LE:
442 case AF_FORMAT_S16_LE:
443 case AF_FORMAT_U8:
444 break;
445 default:
446 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: format %s not supported defaulting to Signed 16-bit Little-Endian\n",af_fmt2str_short(format));
447 format=AF_FORMAT_S16_LE;
449 //fill global ao_data
450 ao_data.channels = channels;
451 ao_data.samplerate = rate;
452 ao_data.format = format;
453 ao_data.bps = channels * rate * (af_fmt2bits(format)>>3);
454 if(ao_data.buffersize==-1) ao_data.buffersize = ao_data.bps; // space for 1 sec
455 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, channels, af_fmt2str_short(format));
456 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Buffersize:%d bytes (%d msec)\n", ao_data.buffersize, ao_data.buffersize / ao_data.bps * 1000);
458 //fill waveformatex
459 ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
460 wformat.Format.cbSize = (channels > 2) ? sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) : 0;
461 wformat.Format.nChannels = channels;
462 wformat.Format.nSamplesPerSec = rate;
463 if (AF_FORMAT_IS_AC3(format)) {
464 wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
465 wformat.Format.wBitsPerSample = 16;
466 wformat.Format.nBlockAlign = 4;
467 } else {
468 wformat.Format.wFormatTag = (channels > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
469 wformat.Format.wBitsPerSample = af_fmt2bits(format);
470 wformat.Format.nBlockAlign = wformat.Format.nChannels * (wformat.Format.wBitsPerSample >> 3);
473 // fill in primary sound buffer descriptor
474 memset(&dsbpridesc, 0, sizeof(DSBUFFERDESC));
475 dsbpridesc.dwSize = sizeof(DSBUFFERDESC);
476 dsbpridesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
477 dsbpridesc.dwBufferBytes = 0;
478 dsbpridesc.lpwfxFormat = NULL;
481 // fill in the secondary sound buffer (=stream buffer) descriptor
482 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
483 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
484 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
485 | DSBCAPS_GLOBALFOCUS /** Allows background playing */
486 | DSBCAPS_CTRLVOLUME; /** volume control enabled */
488 if (channels > 2) {
489 wformat.dwChannelMask = channel_mask[channels - 3];
490 wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
491 wformat.Samples.wValidBitsPerSample = wformat.Format.wBitsPerSample;
492 // Needed for 5.1 on emu101k - shit soundblaster
493 dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
495 wformat.Format.nAvgBytesPerSec = wformat.Format.nSamplesPerSec * wformat.Format.nBlockAlign;
497 dsbdesc.dwBufferBytes = ao_data.buffersize;
498 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat;
499 buffer_size = dsbdesc.dwBufferBytes;
500 write_offset = 0;
501 min_free_space = wformat.Format.nBlockAlign;
502 ao_data.outburst = wformat.Format.nBlockAlign * 512;
504 // create primary buffer and set its format
506 res = IDirectSound_CreateSoundBuffer( hds, &dsbpridesc, &hdspribuf, NULL );
507 if ( res != DS_OK ) {
508 UninitDirectSound();
509 mp_msg(MSGT_AO, MSGL_ERR,"ao_dsound: cannot create primary buffer (%s)\n", dserr2str(res));
510 return 0;
512 res = IDirectSoundBuffer_SetFormat( hdspribuf, (WAVEFORMATEX *)&wformat );
513 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));
515 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: primary buffer created\n");
517 // now create the stream buffer
519 res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
520 if (res != DS_OK) {
521 if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) {
522 // Try without DSBCAPS_LOCHARDWARE
523 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
524 res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
526 if (res != DS_OK) {
527 UninitDirectSound();
528 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create secondary (stream)buffer (%s)\n", dserr2str(res));
529 return 0;
532 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: secondary (stream)buffer created\n");
533 return 1;
539 \brief stop playing and empty buffers (for seeking/pause)
541 static void reset(void)
543 IDirectSoundBuffer_Stop(hdsbuf);
544 // reset directsound buffer
545 IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0);
546 write_offset=0;
550 \brief stop playing, keep buffers (for pause)
552 static void audio_pause(void)
554 IDirectSoundBuffer_Stop(hdsbuf);
558 \brief resume playing, after audio_pause()
560 static void audio_resume(void)
562 IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
566 \brief close audio device
567 \param immed stop playback immediately
569 static void uninit(int immed)
571 if(immed)reset();
572 else{
573 DWORD status;
574 IDirectSoundBuffer_Play(hdsbuf, 0, 0, 0);
575 while(!IDirectSoundBuffer_GetStatus(hdsbuf,&status) && (status&DSBSTATUS_PLAYING))
576 usec_sleep(20000);
578 DestroyBuffer();
579 UninitDirectSound();
583 \brief find out how many bytes can be written into the audio buffer without
584 \return free space in bytes, has to return 0 if the buffer is almost full
586 static int get_space(void)
588 int space;
589 DWORD play_offset;
590 IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
591 space=buffer_size-(write_offset-play_offset);
592 // | | <-- const --> | | |
593 // buffer start play_cursor write_cursor write_offset buffer end
594 // play_cursor is the actual postion of the play cursor
595 // write_cursor is the position after which it is assumed to be save to write data
596 // write_offset is the postion where we actually write the data to
597 if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
598 if(space < min_free_space)return 0;
599 return space-min_free_space;
603 \brief play 'len' bytes of 'data'
604 \param data pointer to the data to play
605 \param len size in bytes of the data buffer, gets rounded down to outburst*n
606 \param flags currently unused
607 \return number of played bytes
609 static int play(void* data, int len, int flags)
611 DWORD play_offset;
612 int space;
614 // make sure we have enough space to write data
615 IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
616 space=buffer_size-(write_offset-play_offset);
617 if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
618 if(space < len) len = space;
620 if (!(flags & AOPLAY_FINAL_CHUNK))
621 len = (len / ao_data.outburst) * ao_data.outburst;
622 return write_buffer(data, len);
626 \brief get the delay between the first and last sample in the buffer
627 \return delay in seconds
629 static float get_delay(void)
631 DWORD play_offset;
632 int space;
633 IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
634 space=play_offset-write_offset;
635 if(space <= 0)space += buffer_size;
636 return (float)(buffer_size - space) / (float)ao_data.bps;