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