1 /*
2  * Windows DirectSound interface
3  *
4  * Copyright (c) 2004 Gabor Szecsi <deje@miki.hu>
5  *
6  * This file is part of MPlayer.
7  *
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.
12  *
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.
17  *
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.
21  */
22 
23 /**
24 \todo verify/extend multichannel support
25 */
26 
27 
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>
34 
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"
43 
44 
45 static const ao_info_t info =
46 {
47 	"Windows DirectSound audio output",
48 	"dsound",
49 	"Gabor Szecsi <deje@miki.hu>",
50 	""
51 };
52 
53 LIBAO_EXTERN(dsound)
54 
55 /**
56 \todo use the definitions from the win32 api headers when they define these
57 */
58 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
59 #define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
60 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
61 
62 static const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x1,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}};
63 
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
83 
84 #ifndef _WAVEFORMATEXTENSIBLE_
85 typedef struct {
86     WAVEFORMATEX    Format;
87     union {
88         WORD wValidBitsPerSample;       /* bits of precision  */
89         WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */
90         WORD wReserved;                 /* If neither applies, set to zero. */
91     } Samples;
92     DWORD           dwChannelMask;      /* which channels are */
93                                         /* present in stream  */
94     GUID            SubFormat;
95 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
96 #endif
97 
98 static const int channel_mask[] = {
99   SPEAKER_FRONT_LEFT   | SPEAKER_FRONT_RIGHT  | SPEAKER_LOW_FREQUENCY,
100   SPEAKER_FRONT_LEFT   | SPEAKER_FRONT_RIGHT  | SPEAKER_BACK_LEFT    | SPEAKER_BACK_RIGHT,
101   SPEAKER_FRONT_LEFT   | SPEAKER_FRONT_RIGHT  | SPEAKER_BACK_LEFT    | SPEAKER_BACK_RIGHT   | SPEAKER_LOW_FREQUENCY,
102   SPEAKER_FRONT_LEFT   | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT  | SPEAKER_BACK_LEFT    | SPEAKER_BACK_RIGHT     | SPEAKER_LOW_FREQUENCY
103 };
104 
105 static HINSTANCE hdsound_dll = NULL;      ///handle to the dll
106 static LPDIRECTSOUND hds = NULL;          ///direct sound object
107 static LPDIRECTSOUNDBUFFER hdspribuf = NULL; ///primary direct sound buffer
108 static LPDIRECTSOUNDBUFFER hdsbuf = NULL; ///secondary direct sound buffer (stream buffer)
109 static int buffer_size = 0;               ///size in bytes of the direct sound buffer
110 static int write_offset = 0;              ///offset of the write cursor in the direct sound buffer
111 static int min_free_space = 0;            ///if the free space is below this value get_space() will return 0
112                                           ///there will always be at least this amout of free space to prevent
113                                           ///get_space() from returning wrong values when buffer is 100% full.
114                                           ///will be replaced with nBlockAlign in init()
115 static int device_num = 0;                ///wanted device number
116 static GUID device;                       ///guid of the device
117 
118 /***************************************************************************************/
119 
120 /**
121 \brief output error message
122 \param err error code
123 \return string with the error message
124 */
dserr2str(int err)125 static char * dserr2str(int err)
126 {
127 	switch (err) {
128 		case DS_OK: return "DS_OK";
129 		case DS_NO_VIRTUALIZATION: return "DS_NO_VIRTUALIZATION";
130 		case DSERR_ALLOCATED: return "DS_NO_VIRTUALIZATION";
131 		case DSERR_CONTROLUNAVAIL: return "DSERR_CONTROLUNAVAIL";
132 		case DSERR_INVALIDPARAM: return "DSERR_INVALIDPARAM";
133 		case DSERR_INVALIDCALL: return "DSERR_INVALIDCALL";
134 		case DSERR_GENERIC: return "DSERR_GENERIC";
135 		case DSERR_PRIOLEVELNEEDED: return "DSERR_PRIOLEVELNEEDED";
136 		case DSERR_OUTOFMEMORY: return "DSERR_OUTOFMEMORY";
137 		case DSERR_BADFORMAT: return "DSERR_BADFORMAT";
138 		case DSERR_UNSUPPORTED: return "DSERR_UNSUPPORTED";
139 		case DSERR_NODRIVER: return "DSERR_NODRIVER";
140 		case DSERR_ALREADYINITIALIZED: return "DSERR_ALREADYINITIALIZED";
141 		case DSERR_NOAGGREGATION: return "DSERR_NOAGGREGATION";
142 		case DSERR_BUFFERLOST: return "DSERR_BUFFERLOST";
143 		case DSERR_OTHERAPPHASPRIO: return "DSERR_OTHERAPPHASPRIO";
144 		case DSERR_UNINITIALIZED: return "DSERR_UNINITIALIZED";
145 		case DSERR_NOINTERFACE: return "DSERR_NOINTERFACE";
146 		case DSERR_ACCESSDENIED: return "DSERR_ACCESSDENIED";
147 		default: return "unknown";
148 	}
149 }
150 
151 /**
152 \brief uninitialize direct sound
153 */
UninitDirectSound(void)154 static void UninitDirectSound(void)
155 {
156     // finally release the DirectSound object
157     if (hds) {
158     	IDirectSound_Release(hds);
159     	hds = NULL;
160     }
161     // free DSOUND.DLL
162     if (hdsound_dll) {
163     	FreeLibrary(hdsound_dll);
164     	hdsound_dll = NULL;
165     }
166 	mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound uninitialized\n");
167 }
168 
169 /**
170 \brief print the commandline help
171 */
print_help(void)172 static void print_help(void)
173 {
174   mp_msg(MSGT_AO, MSGL_FATAL,
175            "\n-ao dsound commandline help:\n"
176            "Example: mplayer -ao dsound:device=1\n"
177            "  sets 1st device\n"
178            "\nOptions:\n"
179            "  device=<device-number>\n"
180            "    Sets device number, use -v to get a list\n");
181 }
182 
183 
184 /**
185 \brief enumerate direct sound devices
186 \return TRUE to continue with the enumeration
187 */
DirectSoundEnum(LPGUID guid,LPCSTR desc,LPCSTR module,LPVOID context)188 static BOOL CALLBACK DirectSoundEnum(LPGUID guid,LPCSTR desc,LPCSTR module,LPVOID context)
189 {
190     int* device_index=context;
191     mp_msg(MSGT_AO, MSGL_V,"%i %s ",*device_index,desc);
192     if(device_num==*device_index){
193         mp_msg(MSGT_AO, MSGL_V,"<--");
194         if(guid){
195             memcpy(&device,guid,sizeof(GUID));
196         }
197     }
198     mp_msg(MSGT_AO, MSGL_V,"\n");
199     (*device_index)++;
200     return TRUE;
201 }
202 
203 
204 /**
205 \brief initilize direct sound
206 \return 0 if error, 1 if ok
207 */
InitDirectSound(void)208 static int InitDirectSound(void)
209 {
210 	DSCAPS dscaps;
211 
212 	// initialize directsound
213     HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
214 	HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKA, LPVOID);
215 	int device_index=0;
216 	const opt_t subopts[] = {
217 	  {"device", OPT_ARG_INT, &device_num,NULL},
218 	  {NULL}
219 	};
220 	if (subopt_parse(ao_subdevice, subopts) != 0) {
221 		print_help();
222 		return 0;
223 	}
224 
225 	hdsound_dll = LoadLibrary("DSOUND.DLL");
226 	if (hdsound_dll == NULL) {
227 		mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot load DSOUND.DLL\n");
228 		return 0;
229 	}
230 	OurDirectSoundCreate = (void*)GetProcAddress(hdsound_dll, "DirectSoundCreate");
231 	OurDirectSoundEnumerate = (void*)GetProcAddress(hdsound_dll, "DirectSoundEnumerateA");
232 
233 	if (OurDirectSoundCreate == NULL || OurDirectSoundEnumerate == NULL) {
234 		mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: GetProcAddress FAILED\n");
235 		FreeLibrary(hdsound_dll);
236 		return 0;
237 	}
238 
239 	// Enumerate all directsound devices
240 	mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Output Devices:\n");
241 	OurDirectSoundEnumerate(DirectSoundEnum,&device_index);
242 
243 	// Create the direct sound object
244 	if FAILED(OurDirectSoundCreate((device_num)?&device:NULL, &hds, NULL )) {
245 		mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create a DirectSound device\n");
246 		FreeLibrary(hdsound_dll);
247 		return 0;
248 	}
249 
250 	/* Set DirectSound Cooperative level, ie what control we want over Windows
251 	 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
252 	 * settings of the primary buffer, but also that only the sound of our
253 	 * application will be hearable when it will have the focus.
254 	 * !!! (this is not really working as intended yet because to set the
255 	 * cooperative level you need the window handle of your application, and
256 	 * I don't know of any easy way to get it. Especially since we might play
257 	 * sound without any video, and so what window handle should we use ???
258 	 * The hack for now is to use the Desktop window handle - it seems to be
259 	 * working */
260 	if (IDirectSound_SetCooperativeLevel(hds, GetDesktopWindow(), DSSCL_EXCLUSIVE)) {
261 		mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot set direct sound cooperative level\n");
262 		IDirectSound_Release(hds);
263 		FreeLibrary(hdsound_dll);
264 		return 0;
265 	}
266 	mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound initialized\n");
267 
268 	memset(&dscaps, 0, sizeof(DSCAPS));
269 	dscaps.dwSize = sizeof(DSCAPS);
270 	if (DS_OK == IDirectSound_GetCaps(hds, &dscaps)) {
271 		if (dscaps.dwFlags & DSCAPS_EMULDRIVER) mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound is emulated, waveOut may give better performance\n");
272 	} else {
273 		mp_msg(MSGT_AO, MSGL_V, "ao_dsound: cannot get device capabilities\n");
274 	}
275 
276 	return 1;
277 }
278 
279 /**
280 \brief destroy the direct sound buffer
281 */
DestroyBuffer(void)282 static void DestroyBuffer(void)
283 {
284 	if (hdsbuf) {
285 		IDirectSoundBuffer_Release(hdsbuf);
286 		hdsbuf = NULL;
287 	}
288 	if (hdspribuf) {
289 		IDirectSoundBuffer_Release(hdspribuf);
290 		hdspribuf = NULL;
291 	}
292 }
293 
294 /**
295 \brief fill sound buffer
296 \param data pointer to the sound data to copy
297 \param len length of the data to copy in bytes
298 \return number of copyed bytes
299 */
write_buffer(unsigned char * data,int len)300 static int write_buffer(unsigned char *data, int len)
301 {
302   HRESULT res;
303   LPVOID lpvPtr1;
304   DWORD dwBytes1;
305   LPVOID lpvPtr2;
306   DWORD dwBytes2;
307 
308   // Lock the buffer
309   res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
310   // If the buffer was lost, restore and retry lock.
311   if (DSERR_BUFFERLOST == res)
312   {
313     IDirectSoundBuffer_Restore(hdsbuf);
314 	res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
315   }
316 
317 
318   if (SUCCEEDED(res))
319   {
320   	if( (ao_data.channels == 6) && !AF_FORMAT_IS_AC3(ao_data.format) ) {
321   	    // reorder channels while writing to pointers.
322   	    // it's this easy because buffer size and len are always
323   	    // aligned to multiples of channels*bytespersample
324   	    // there's probably some room for speed improvements here
325   	    static const int chantable[6] = {0, 1, 4, 5, 2, 3}; // reorder "matrix"
326   	    int i, j;
327   	    int numsamp,sampsize;
328 
329   	    sampsize = af_fmt2bits(ao_data.format)>>3; // bytes per sample
330   	    numsamp = dwBytes1 / (ao_data.channels * sampsize);  // number of samples for each channel in this buffer
331 
332   	    for( i = 0; i < numsamp; i++ ) for( j = 0; j < ao_data.channels; j++ ) {
333   	        memcpy((char *)lpvPtr1+(i*ao_data.channels*sampsize)+(chantable[j]*sampsize),data+(i*ao_data.channels*sampsize)+(j*sampsize),sampsize);
334   	    }
335 
336   	    if (NULL != lpvPtr2 )
337   	    {
338   	        numsamp = dwBytes2 / (ao_data.channels * sampsize);
339   	        for( i = 0; i < numsamp; i++ ) for( j = 0; j < ao_data.channels; j++ ) {
340   	            memcpy((char *)lpvPtr2+(i*ao_data.channels*sampsize)+(chantable[j]*sampsize),data+dwBytes1+(i*ao_data.channels*sampsize)+(j*sampsize),sampsize);
341   	        }
342   	    }
343 
344   	    write_offset+=dwBytes1+dwBytes2;
345   	    if(write_offset>=buffer_size)write_offset=dwBytes2;
346   	} else {
347   	    // Write to pointers without reordering.
348 	fast_memcpy(lpvPtr1,data,dwBytes1);
349     if (NULL != lpvPtr2 )fast_memcpy(lpvPtr2,data+dwBytes1,dwBytes2);
350 	write_offset+=dwBytes1+dwBytes2;
351     if(write_offset>=buffer_size)write_offset=dwBytes2;
352   	}
353 
354    // Release the data back to DirectSound.
355     res = IDirectSoundBuffer_Unlock(hdsbuf,lpvPtr1,dwBytes1,lpvPtr2,dwBytes2);
356     if (SUCCEEDED(res))
357     {
358 	  // Success.
359 	  DWORD status;
360 	  IDirectSoundBuffer_GetStatus(hdsbuf, &status);
361       if (!(status & DSBSTATUS_PLAYING)){
362 	    res = IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
363 	  }
364 	  return dwBytes1+dwBytes2;
365     }
366   }
367   // Lock, Unlock, or Restore failed.
368   return 0;
369 }
370 
371 /***************************************************************************************/
372 
373 /**
374 \brief handle control commands
375 \param cmd command
376 \param arg argument
377 \return CONTROL_OK or -1 in case the command can't be handled
378 */
control(int cmd,void * arg)379 static int control(int cmd, void *arg)
380 {
381 	DWORD volume;
382 	switch (cmd) {
383 		case AOCONTROL_GET_VOLUME: {
384 			ao_control_vol_t* vol = (ao_control_vol_t*)arg;
385 			IDirectSoundBuffer_GetVolume(hdsbuf, &volume);
386 			vol->left = vol->right = pow(10.0, (float)(volume+10000) / 5000.0);
387 			//printf("ao_dsound: volume: %f\n",vol->left);
388 			return CONTROL_OK;
389 		}
390 		case AOCONTROL_SET_VOLUME: {
391 			ao_control_vol_t* vol = (ao_control_vol_t*)arg;
392 			volume = (DWORD)(log10(vol->right) * 5000.0) - 10000;
393 			IDirectSoundBuffer_SetVolume(hdsbuf, volume);
394 			//printf("ao_dsound: volume: %f\n",vol->left);
395 			return CONTROL_OK;
396 		}
397 	}
398 	return -1;
399 }
400 
401 /**
402 \brief setup sound device
403 \param rate samplerate
404 \param channels number of channels
405 \param format format
406 \param flags unused
407 \return 1=success 0=fail
408 */
init(int rate,int channels,int format,int flags)409 static int init(int rate, int channels, int format, int flags)
410 {
411     int res;
412 
413 	WAVEFORMATEXTENSIBLE wformat;
414 	DSBUFFERDESC dsbpridesc;
415 	DSBUFFERDESC dsbdesc;
416 
417 	if (!InitDirectSound()) return 0;
418 
419 	//check if the channel count and format is supported in general
420 	if (channels > 6) {
421 		UninitDirectSound();
422 		mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: 8 channel audio not yet supported\n");
423 		return 0;
424 	}
425 
426 	if (AF_FORMAT_IS_AC3(format))
427 		format = AF_FORMAT_AC3_NE;
428 	switch(format){
429 		case AF_FORMAT_AC3_NE:
430 		case AF_FORMAT_S24_LE:
431 		case AF_FORMAT_S16_LE:
432 		case AF_FORMAT_U8:
433 			break;
434 		default:
435 			mp_msg(MSGT_AO, MSGL_V,"ao_dsound: format %s not supported defaulting to Signed 16-bit Little-Endian\n",af_fmt2str_short(format));
436 			format=AF_FORMAT_S16_LE;
437 	}
438 	//fill global ao_data
439 	ao_data.channels = channels;
440 	ao_data.samplerate = rate;
441 	ao_data.format = format;
442 	ao_data.bps = channels * rate * (af_fmt2bits(format)>>3);
443 	if(ao_data.buffersize==-1) ao_data.buffersize = ao_data.bps; // space for 1 sec
444 	mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, channels, af_fmt2str_short(format));
445 	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 
447 	//fill waveformatex
448 	ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
449 	wformat.Format.cbSize          = (channels > 2) ? sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) : 0;
450 	wformat.Format.nChannels       = channels;
451 	wformat.Format.nSamplesPerSec  = rate;
452 	if (AF_FORMAT_IS_AC3(format)) {
453 		wformat.Format.wFormatTag      = WAVE_FORMAT_DOLBY_AC3_SPDIF;
454 		wformat.Format.wBitsPerSample  = 16;
455 		wformat.Format.nBlockAlign     = 4;
456 	} else {
457 		wformat.Format.wFormatTag      = (channels > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
458 		wformat.Format.wBitsPerSample  = af_fmt2bits(format);
459 		wformat.Format.nBlockAlign     = wformat.Format.nChannels * (wformat.Format.wBitsPerSample >> 3);
460 	}
461 
462 	// fill in primary sound buffer descriptor
463 	memset(&dsbpridesc, 0, sizeof(DSBUFFERDESC));
464 	dsbpridesc.dwSize = sizeof(DSBUFFERDESC);
465 	dsbpridesc.dwFlags       = DSBCAPS_PRIMARYBUFFER;
466 	dsbpridesc.dwBufferBytes = 0;
467 	dsbpridesc.lpwfxFormat   = NULL;
468 
469 
470 	// fill in the secondary sound buffer (=stream buffer) descriptor
471 	memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
472 	dsbdesc.dwSize = sizeof(DSBUFFERDESC);
473 	dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
474 	                | DSBCAPS_GLOBALFOCUS         /** Allows background playing */
475 	                | DSBCAPS_CTRLVOLUME;         /** volume control enabled */
476 
477 	if (channels > 2) {
478 		wformat.dwChannelMask = channel_mask[channels - 3];
479 		wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
480 		wformat.Samples.wValidBitsPerSample = wformat.Format.wBitsPerSample;
481 		// Needed for 5.1 on emu101k - shit soundblaster
482 		dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
483 	}
484 	wformat.Format.nAvgBytesPerSec = wformat.Format.nSamplesPerSec * wformat.Format.nBlockAlign;
485 
486 	dsbdesc.dwBufferBytes = ao_data.buffersize;
487 	dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat;
488 	buffer_size = dsbdesc.dwBufferBytes;
489 	write_offset = 0;
490 	min_free_space = wformat.Format.nBlockAlign;
491 	ao_data.outburst = wformat.Format.nBlockAlign * 512;
492 
493 	// create primary buffer and set its format
494 
495 	res = IDirectSound_CreateSoundBuffer( hds, &dsbpridesc, &hdspribuf, NULL );
496 	if ( res != DS_OK ) {
497 		UninitDirectSound();
498 		mp_msg(MSGT_AO, MSGL_ERR,"ao_dsound: cannot create primary buffer (%s)\n", dserr2str(res));
499 		return 0;
500 	}
501 	res = IDirectSoundBuffer_SetFormat( hdspribuf, (WAVEFORMATEX *)&wformat );
502 	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 
504 	mp_msg(MSGT_AO, MSGL_V, "ao_dsound: primary buffer created\n");
505 
506 	// now create the stream buffer
507 
508 	res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
509 	if (res != DS_OK) {
510 		if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) {
511 			// Try without DSBCAPS_LOCHARDWARE
512 			dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
513 			res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
514 		}
515 		if (res != DS_OK) {
516 			UninitDirectSound();
517 			mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create secondary (stream)buffer (%s)\n", dserr2str(res));
518 			return 0;
519 		}
520 	}
521 	mp_msg(MSGT_AO, MSGL_V, "ao_dsound: secondary (stream)buffer created\n");
522 	return 1;
523 }
524 
525 
526 
527 /**
528 \brief stop playing and empty buffers (for seeking/pause)
529 */
reset(void)530 static void reset(void)
531 {
532 	IDirectSoundBuffer_Stop(hdsbuf);
533 	// reset directsound buffer
534 	IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0);
535 	write_offset=0;
536 }
537 
538 /**
539 \brief stop playing, keep buffers (for pause)
540 */
audio_pause(void)541 static void audio_pause(void)
542 {
543 	IDirectSoundBuffer_Stop(hdsbuf);
544 }
545 
546 /**
547 \brief resume playing, after audio_pause()
548 */
audio_resume(void)549 static void audio_resume(void)
550 {
551 	IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
552 }
553 
554 /**
555 \brief close audio device
556 \param immed stop playback immediately
557 */
uninit(int immed)558 static void uninit(int immed)
559 {
560 	if(immed)reset();
561 	else{
562 		DWORD status;
563 		IDirectSoundBuffer_Play(hdsbuf, 0, 0, 0);
564 		// This should not be necessary, but a lot of drivers
565 		// do not correctly report the status here, causing
566 		// audio to be discarded. So we sleep approximately
567 		// the right amount of time first.
568 		usec_sleep(get_delay() * 1000 * 1000);
569 		while(!IDirectSoundBuffer_GetStatus(hdsbuf,&status) && (status&DSBSTATUS_PLAYING))
570 			usec_sleep(5000);
571 	}
572 	DestroyBuffer();
573 	UninitDirectSound();
574 }
575 
576 /**
577 \brief find out how many bytes can be written into the audio buffer without
578 \return free space in bytes, has to return 0 if the buffer is almost full
579 */
get_space(void)580 static int get_space(void)
581 {
582 	int space;
583 	DWORD play_offset;
584 	IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
585 	space=buffer_size-(write_offset-play_offset);
586 	//                |                                                      | <-- const --> |                |                 |
587 	//                buffer start                                           play_cursor     write_cursor     write_offset      buffer end
588 	// play_cursor is the actual postion of the play cursor
589 	// write_cursor is the position after which it is assumed to be save to write data
590 	// write_offset is the postion where we actually write the data to
591 	if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
592 	if(space < min_free_space)return 0;
593 	return space-min_free_space;
594 }
595 
596 /**
597 \brief play 'len' bytes of 'data'
598 \param data pointer to the data to play
599 \param len size in bytes of the data buffer, gets rounded down to outburst*n
600 \param flags currently unused
601 \return number of played bytes
602 */
play(void * data,int len,int flags)603 static int play(void* data, int len, int flags)
604 {
605 	DWORD play_offset;
606 	int space;
607 
608 	// make sure we have enough space to write data
609 	IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
610 	space=buffer_size-(write_offset-play_offset);
611 	if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
612 	if(space < len) len = space;
613 
614 	if (!(flags & AOPLAY_FINAL_CHUNK))
615 	len = (len / ao_data.outburst) * ao_data.outburst;
616 	return write_buffer(data, len);
617 }
618 
619 /**
620 \brief get the delay between the first and last sample in the buffer
621 \return delay in seconds
622 */
get_delay(void)623 static float get_delay(void)
624 {
625 	DWORD play_offset;
626 	int space;
627 	IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
628 	space=play_offset-write_offset;
629 	if(space <= 0)space += buffer_size;
630 	return (float)(buffer_size - space) / (float)ao_data.bps;
631 }
632