Mercurial > mplayer.hg
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 } |