comparison libao2/ao_dsound.c @ 13460:70d8f1975fc8

directsound audio output plugin, patch by Gabor Szecsi <deje at miki.hu> some minor modifications by me
author faust3
date Sat, 25 Sep 2004 15:34:42 +0000
parents
children a7d080bc610f
comparison
equal deleted inserted replaced
13459:5634013c47c8 13460:70d8f1975fc8
1 /******************************************************************************
2 * ao_dsound.c: Windows DirectSound interface for MPlayer
3 * Copyright (c) 2004 Gabor Szecsi <deje@miki.hu>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
18 *
19 *****************************************************************************/
20 /**
21 \todo verify/extend multichannel support
22 */
23
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <windows.h>
28 #include <mmsystem.h>
29 #include <dsound.h>
30
31 #include "afmt.h"
32 #include "audio_out.h"
33 #include "audio_out_internal.h"
34 #include "../mp_msg.h"
35 #include "../libvo/fastmemcpy.h"
36 #include "osdep/timer.h"
37
38
39 static ao_info_t info =
40 {
41 "Windows DirectSound audio output",
42 "dsound",
43 "Gabor Szecsi <deje@miki.hu>",
44 ""
45 };
46
47 LIBAO_EXTERN(dsound)
48
49 /**
50 \todo use the definitions from the win32 api headers when they define these
51 */
52 #if 1
53 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
54 #define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092
55 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
56
57 static const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x1,0x0000,0x0010, {0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71}};
58
59 #define SPEAKER_FRONT_LEFT 0x1
60 #define SPEAKER_FRONT_RIGHT 0x2
61 #define SPEAKER_FRONT_CENTER 0x4
62 #define SPEAKER_LOW_FREQUENCY 0x8
63 #define SPEAKER_BACK_LEFT 0x10
64 #define SPEAKER_BACK_RIGHT 0x20
65 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
66 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
67 #define SPEAKER_BACK_CENTER 0x100
68 #define SPEAKER_SIDE_LEFT 0x200
69 #define SPEAKER_SIDE_RIGHT 0x400
70 #define SPEAKER_TOP_CENTER 0x800
71 #define SPEAKER_TOP_FRONT_LEFT 0x1000
72 #define SPEAKER_TOP_FRONT_CENTER 0x2000
73 #define SPEAKER_TOP_FRONT_RIGHT 0x4000
74 #define SPEAKER_TOP_BACK_LEFT 0x8000
75 #define SPEAKER_TOP_BACK_CENTER 0x10000
76 #define SPEAKER_TOP_BACK_RIGHT 0x20000
77 #define SPEAKER_RESERVED 0x80000000
78
79 #define DSSPEAKER_HEADPHONE 0x00000001
80 #define DSSPEAKER_MONO 0x00000002
81 #define DSSPEAKER_QUAD 0x00000003
82 #define DSSPEAKER_STEREO 0x00000004
83 #define DSSPEAKER_SURROUND 0x00000005
84 #define DSSPEAKER_5POINT1 0x00000006
85
86 #ifndef _WAVEFORMATEXTENSIBLE_
87 typedef struct {
88 WAVEFORMATEX Format;
89 union {
90 WORD wValidBitsPerSample; /* bits of precision */
91 WORD wSamplesPerBlock; /* valid if wBitsPerSample==0 */
92 WORD wReserved; /* If neither applies, set to zero. */
93 } Samples;
94 DWORD dwChannelMask; /* which channels are */
95 /* present in stream */
96 GUID SubFormat;
97 } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
98 #endif
99
100 #endif
101
102 static const int channel_mask[] = {
103 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY,
104 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
105 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY,
106 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY
107 };
108
109 static HINSTANCE hdsound_dll = NULL; ///handle to the dll
110 static LPDIRECTSOUND hds = NULL; ///direct sound object
111 static LPDIRECTSOUNDBUFFER hdsbuf = NULL; ///direct sound buffer
112 static int buffer_size = 0; ///size in bytes of the direct sound buffer
113 static int write_offset = 0; ///offset of the write cursor in the direct sound buffer
114 static int min_free_space = 4096; ///if the free space is below this value get_space() will return 0
115
116 #define BUFFERSIZE 32767 /// in samples - at 48khz 0.6 sec buffer, gets multiplied with nBlockAlign
117
118 /***************************************************************************************/
119
120 /**
121 \brief output error message
122 \param err error code
123 \return string with the error message
124 */
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 */
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 initilize direct sound
171 \return 0 if error, 1 if ok
172 */
173 static int InitDirectSound(void)
174 {
175 DSCAPS dscaps;
176
177 // initialize directsound
178 HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
179 hdsound_dll = LoadLibrary("DSOUND.DLL");
180 if (hdsound_dll == NULL) {
181 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot load DSOUND.DLL\n");
182 return 0;
183 }
184 OurDirectSoundCreate = (void*)GetProcAddress(hdsound_dll, "DirectSoundCreate");
185
186 if (OurDirectSoundCreate == NULL) {
187 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: GetProcAddress FAILED\n");
188 FreeLibrary(hdsound_dll);
189 return 0;
190 }
191
192 // Create the direct sound object
193 if FAILED(OurDirectSoundCreate(NULL, &hds, NULL )) {
194 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create a DirectSound device\n");
195 FreeLibrary(hdsound_dll);
196 return 0;
197 }
198
199 /* Set DirectSound Cooperative level, ie what control we want over Windows
200 * sound device. In our case, DSSCL_EXCLUSIVE means that we can modify the
201 * settings of the primary buffer, but also that only the sound of our
202 * application will be hearable when it will have the focus.
203 * !!! (this is not really working as intended yet because to set the
204 * cooperative level you need the window handle of your application, and
205 * I don't know of any easy way to get it. Especially since we might play
206 * sound without any video, and so what window handle should we use ???
207 * The hack for now is to use the Desktop window handle - it seems to be
208 * working */
209 if (IDirectSound_SetCooperativeLevel(hds, GetDesktopWindow(), DSSCL_EXCLUSIVE)) {
210 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot set direct sound cooperative level\n");
211 IDirectSound_Release(hds);
212 FreeLibrary(hdsound_dll);
213 return 0;
214 }
215 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound initialized\n");
216
217 memset(&dscaps, 0, sizeof(DSCAPS));
218 dscaps.dwSize = sizeof(DSCAPS);
219 if (DS_OK == IDirectSound_GetCaps(hds, &dscaps)) {
220 if (dscaps.dwFlags & DSCAPS_EMULDRIVER) mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound is emulated, waveOut may give better performance\n");
221 } else {
222 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: cannot get device capabilities\n");
223 }
224
225 return 1;
226 }
227
228 /**
229 \brief destroy the direct sound buffer
230 */
231 static void DestroyBuffer(void)
232 {
233 if (hdsbuf) {
234 IDirectSoundBuffer_Release(hdsbuf);
235 hdsbuf = NULL;
236 }
237 }
238
239 /**
240 \brief fill sound buffer
241 \param data pointer to the sound data to copy
242 \param len length of the data to copy in bytes
243 \return number of copyed bytes
244 */
245 static int write_buffer(unsigned char *data, int len)
246 {
247 HRESULT res;
248 LPVOID lpvPtr1;
249 DWORD dwBytes1;
250 LPVOID lpvPtr2;
251 DWORD dwBytes2;
252
253 // Lock the buffer
254 res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
255 // If the buffer was lost, restore and retry lock.
256 if (DSERR_BUFFERLOST == res)
257 {
258 IDirectSoundBuffer_Restore(hdsbuf);
259 res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
260 }
261
262
263 if (SUCCEEDED(res))
264 {
265 // Write to pointers.
266 memcpy(lpvPtr1,data,dwBytes1);
267 if (NULL != lpvPtr2 )memcpy(lpvPtr2,data+dwBytes1,dwBytes2);
268 write_offset+=dwBytes1+dwBytes2;
269 if(write_offset>=buffer_size)write_offset-=buffer_size;
270
271 // Release the data back to DirectSound.
272 res = IDirectSoundBuffer_Unlock(hdsbuf,lpvPtr1,dwBytes1,lpvPtr2,dwBytes2);
273 if (SUCCEEDED(res))
274 {
275 // Success.
276 DWORD status;
277 IDirectSoundBuffer_GetStatus(hdsbuf, &status);
278 if (!(status & DSBSTATUS_PLAYING)){
279 res = IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
280 }
281 return dwBytes1+dwBytes2;
282 }
283 }
284 // Lock, Unlock, or Restore failed.
285 return 0;
286 }
287
288 /***************************************************************************************/
289
290 /**
291 \brief handle control commands
292 \param cmd command
293 \param arg argument
294 \return CONTROL_OK or -1 in case the command can't be handled
295 */
296 static int control(int cmd, void *arg)
297 {
298 DWORD volume;
299 switch (cmd) {
300 case AOCONTROL_GET_VOLUME: {
301 ao_control_vol_t* vol = (ao_control_vol_t*)arg;
302 IDirectSoundBuffer_GetVolume(hdsbuf, &volume);
303 vol->left = vol->right = (float)(volume+10000) / 100.0;
304 //printf("ao_dsound: volume: %f\n",vol->left);
305 return CONTROL_OK;
306 }
307 case AOCONTROL_SET_VOLUME: {
308 ao_control_vol_t* vol = (ao_control_vol_t*)arg;
309 volume = (vol->right * 100.0)-10000;
310 IDirectSoundBuffer_SetVolume(hdsbuf, volume);
311 //printf("ao_dsound: volume: %f\n",vol->left);
312 return CONTROL_OK;
313 }
314 }
315 return -1;
316 }
317
318 /**
319 \brief setup sound device
320 \param rate samplerate
321 \param channels number of channels
322 \param format format
323 \param flags unused
324 \return 1=success 0=fail
325 */
326 static int init(int rate, int channels, int format, int flags)
327 {
328 int res;
329 if (!InitDirectSound()) return 0;
330
331 // ok, now create the primary buffer
332 WAVEFORMATEXTENSIBLE wformat;
333 DSBUFFERDESC dsbdesc;
334
335 //fill global ao_data
336 ao_data.channels = channels;
337 ao_data.samplerate = rate;
338 ao_data.format = format;
339 ao_data.bps = channels * rate * (audio_out_format_bits(format)>>3);
340 if(ao_data.buffersize==-1)
341 {
342 ao_data.buffersize = audio_out_format_bits(format) >> 3;
343 ao_data.buffersize *= channels;
344 ao_data.buffersize *= BUFFERSIZE;
345 }
346 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Samplerate:%iHz Channels:%i Format:%s\n", rate, channels, audio_out_format_name(format));
347 mp_msg(MSGT_AO, MSGL_V,"ao_dsound: Buffersize:%d bytes (%d msec)\n", ao_data.buffersize, BUFFERSIZE * 1000 / rate);
348
349 //fill waveformatex
350 ZeroMemory(&wformat, sizeof(WAVEFORMATEXTENSIBLE));
351 wformat.Format.cbSize = (channels > 2) ? sizeof(WAVEFORMATEXTENSIBLE) : 0;
352 wformat.Format.nChannels = channels;
353 wformat.Format.nSamplesPerSec = rate;
354 if (format == AFMT_AC3) {
355 wformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
356 wformat.Format.wBitsPerSample = 16;
357 wformat.Format.nBlockAlign = 4;
358 } else {
359 wformat.Format.wFormatTag = (channels > 2) ? WAVE_FORMAT_EXTENSIBLE : WAVE_FORMAT_PCM;
360 wformat.Format.wBitsPerSample = audio_out_format_bits(format);
361 wformat.Format.nBlockAlign = wformat.Format.nChannels * (wformat.Format.wBitsPerSample >> 3);
362 }
363
364 // fill in the direct sound buffer descriptor
365 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
366 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
367 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 /** Better position accuracy */
368 | DSBCAPS_GLOBALFOCUS /** Allows background playing */
369 | DSBCAPS_CTRLVOLUME; /** volume control enabled */
370
371 if (channels > 2) {
372 wformat.dwChannelMask = channel_mask[channels - 3];
373 wformat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
374 wformat.Samples.wValidBitsPerSample = audio_out_format_bits(format);
375 // Needed for 5.1 on emu101k - shit soundblaster
376 dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE;
377 }
378 wformat.Format.nAvgBytesPerSec = wformat.Format.nSamplesPerSec * wformat.Format.nBlockAlign;
379
380 dsbdesc.dwBufferBytes = wformat.Format.nBlockAlign * BUFFERSIZE;
381 dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat;
382 buffer_size = dsbdesc.dwBufferBytes;
383 ao_data.outburst = wformat.Format.nBlockAlign * 512;
384
385 // now create the sound buffer
386
387 res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
388 if (res != DS_OK) {
389 if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) {
390 // Try without DSBCAPS_LOCHARDWARE
391 dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
392 res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL);
393 }
394 if (res != DS_OK) {
395 UninitDirectSound();
396 mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot create secondary buffer (%s)\n", dserr2str(res));
397 return 0;
398 }
399 }
400 mp_msg(MSGT_AO, MSGL_V, "ao_dsound: secondary buffer created\n");
401 return 1;
402 }
403
404
405
406 /**
407 \brief stop playing and empty buffers (for seeking/pause)
408 */
409 static void reset()
410 {
411 IDirectSoundBuffer_Stop(hdsbuf);
412 // reset directsound buffer
413 IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0);
414 write_offset=0;
415 }
416
417 /**
418 \brief stop playing, keep buffers (for pause)
419 */
420 static void audio_pause()
421 {
422 IDirectSoundBuffer_Stop(hdsbuf);
423 }
424
425 /**
426 \brief resume playing, after audio_pause()
427 */
428 static void audio_resume()
429 {
430 IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING);
431 }
432
433 /**
434 \brief close audio device
435 \param immed stop playback immediately, currently not supported
436 */
437 static void uninit(int immed)
438 {
439 reset();
440 DestroyBuffer();
441 UninitDirectSound();
442 }
443
444 /**
445 \brief find out how many bytes can be written into the audio buffer without
446 \return free space in bytes, has to return 0 if the buffer is almost full
447 */
448 static int get_space()
449 {
450 int space;
451 DWORD play_offset;
452 IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
453 space=buffer_size-(write_offset-play_offset);
454 // | | <-- const --> | | |
455 // buffer start play_cursor write_cursor write_offset buffer end
456 // play_cursor is the actual postion of the play cursor
457 // write_cursor is the position after which it is assumed to be save to write data
458 // write_offset is the postion where we actually write the data to
459 if(space > buffer_size)space -= buffer_size; // write_offset < play_offset
460 if(space < min_free_space)return 0;
461 return space;
462 }
463
464 /**
465 \brief play 'len' bytes of 'data'
466 \param data pointer to the data to play
467 \param len size in bytes of the data buffer, gets rounded down to outburst*n
468 \param flags currently unused
469 \return number of played bytes
470 */
471 static int play(void* data, int len, int flags)
472 {
473 len = (len / ao_data.outburst) * ao_data.outburst;
474 return write_buffer(data, len);
475 }
476
477 /**
478 \brief get the delay between the first and last sample in the buffer
479 \return delay in seconds
480 */
481 static float get_delay()
482 {
483 DWORD play_offset;
484 int space;
485 IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL);
486 space=play_offset-write_offset;
487 if(space <= 0)space += buffer_size;
488 return (float)(buffer_size - space) / (float)ao_data.bps;
489 }