]>
Commit | Line | Data |
---|---|---|
1d14ffa9 FB |
1 | /* |
2 | * QEMU DirectSound audio driver | |
3 | * | |
4 | * Copyright (c) 2005 Vassili Karpov (malc) | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | ||
25 | /* | |
26 | * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation | |
27 | */ | |
28 | ||
749bc4bf PB |
29 | #include "qemu-common.h" |
30 | #include "audio.h" | |
1d14ffa9 FB |
31 | |
32 | #define AUDIO_CAP "dsound" | |
33 | #include "audio_int.h" | |
34 | ||
35 | #include <windows.h> | |
4fddf62a | 36 | #include <mmsystem.h> |
1d14ffa9 FB |
37 | #include <objbase.h> |
38 | #include <dsound.h> | |
39 | ||
d5631638 | 40 | #include "audio_win_int.h" |
41 | ||
1d14ffa9 FB |
42 | /* #define DEBUG_DSOUND */ |
43 | ||
191e1f0a | 44 | typedef struct { |
1d14ffa9 FB |
45 | int bufsize_in; |
46 | int bufsize_out; | |
1d14ffa9 | 47 | int latency_millis; |
191e1f0a | 48 | } DSoundConf; |
1d14ffa9 FB |
49 | |
50 | typedef struct { | |
51 | LPDIRECTSOUND dsound; | |
52 | LPDIRECTSOUNDCAPTURE dsound_capture; | |
1ea879e5 | 53 | struct audsettings settings; |
191e1f0a | 54 | DSoundConf conf; |
1d14ffa9 FB |
55 | } dsound; |
56 | ||
1d14ffa9 FB |
57 | typedef struct { |
58 | HWVoiceOut hw; | |
59 | LPDIRECTSOUNDBUFFER dsound_buffer; | |
60 | DWORD old_pos; | |
61 | int first_time; | |
191e1f0a | 62 | dsound *s; |
1d14ffa9 FB |
63 | #ifdef DEBUG_DSOUND |
64 | DWORD old_ppos; | |
65 | DWORD played; | |
66 | DWORD mixed; | |
67 | #endif | |
68 | } DSoundVoiceOut; | |
69 | ||
70 | typedef struct { | |
71 | HWVoiceIn hw; | |
72 | int first_time; | |
73 | LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; | |
191e1f0a | 74 | dsound *s; |
1d14ffa9 FB |
75 | } DSoundVoiceIn; |
76 | ||
77 | static void dsound_log_hresult (HRESULT hr) | |
78 | { | |
79 | const char *str = "BUG"; | |
80 | ||
81 | switch (hr) { | |
82 | case DS_OK: | |
83 | str = "The method succeeded"; | |
84 | break; | |
85 | #ifdef DS_NO_VIRTUALIZATION | |
86 | case DS_NO_VIRTUALIZATION: | |
87 | str = "The buffer was created, but another 3D algorithm was substituted"; | |
88 | break; | |
89 | #endif | |
90 | #ifdef DS_INCOMPLETE | |
91 | case DS_INCOMPLETE: | |
92 | str = "The method succeeded, but not all the optional effects were obtained"; | |
93 | break; | |
94 | #endif | |
95 | #ifdef DSERR_ACCESSDENIED | |
96 | case DSERR_ACCESSDENIED: | |
97 | str = "The request failed because access was denied"; | |
98 | break; | |
99 | #endif | |
100 | #ifdef DSERR_ALLOCATED | |
101 | case DSERR_ALLOCATED: | |
102 | str = "The request failed because resources, such as a priority level, were already in use by another caller"; | |
103 | break; | |
104 | #endif | |
105 | #ifdef DSERR_ALREADYINITIALIZED | |
106 | case DSERR_ALREADYINITIALIZED: | |
107 | str = "The object is already initialized"; | |
108 | break; | |
109 | #endif | |
110 | #ifdef DSERR_BADFORMAT | |
111 | case DSERR_BADFORMAT: | |
112 | str = "The specified wave format is not supported"; | |
113 | break; | |
114 | #endif | |
115 | #ifdef DSERR_BADSENDBUFFERGUID | |
116 | case DSERR_BADSENDBUFFERGUID: | |
117 | str = "The GUID specified in an audiopath file does not match a valid mix-in buffer"; | |
118 | break; | |
119 | #endif | |
120 | #ifdef DSERR_BUFFERLOST | |
121 | case DSERR_BUFFERLOST: | |
122 | str = "The buffer memory has been lost and must be restored"; | |
123 | break; | |
124 | #endif | |
125 | #ifdef DSERR_BUFFERTOOSMALL | |
126 | case DSERR_BUFFERTOOSMALL: | |
127 | str = "The buffer size is not great enough to enable effects processing"; | |
128 | break; | |
129 | #endif | |
130 | #ifdef DSERR_CONTROLUNAVAIL | |
131 | case DSERR_CONTROLUNAVAIL: | |
132 | str = "The buffer control (volume, pan, and so on) requested by the caller is not available. Controls must be specified when the buffer is created, using the dwFlags member of DSBUFFERDESC"; | |
133 | break; | |
134 | #endif | |
135 | #ifdef DSERR_DS8_REQUIRED | |
136 | case DSERR_DS8_REQUIRED: | |
137 | str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface"; | |
138 | break; | |
139 | #endif | |
140 | #ifdef DSERR_FXUNAVAILABLE | |
141 | case DSERR_FXUNAVAILABLE: | |
142 | str = "The effects requested could not be found on the system, or they are in the wrong order or in the wrong location; for example, an effect expected in hardware was found in software"; | |
143 | break; | |
144 | #endif | |
145 | #ifdef DSERR_GENERIC | |
146 | case DSERR_GENERIC : | |
147 | str = "An undetermined error occurred inside the DirectSound subsystem"; | |
148 | break; | |
149 | #endif | |
150 | #ifdef DSERR_INVALIDCALL | |
151 | case DSERR_INVALIDCALL: | |
152 | str = "This function is not valid for the current state of this object"; | |
153 | break; | |
154 | #endif | |
155 | #ifdef DSERR_INVALIDPARAM | |
156 | case DSERR_INVALIDPARAM: | |
157 | str = "An invalid parameter was passed to the returning function"; | |
158 | break; | |
159 | #endif | |
160 | #ifdef DSERR_NOAGGREGATION | |
161 | case DSERR_NOAGGREGATION: | |
162 | str = "The object does not support aggregation"; | |
163 | break; | |
164 | #endif | |
165 | #ifdef DSERR_NODRIVER | |
166 | case DSERR_NODRIVER: | |
167 | str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID"; | |
168 | break; | |
169 | #endif | |
170 | #ifdef DSERR_NOINTERFACE | |
171 | case DSERR_NOINTERFACE: | |
172 | str = "The requested COM interface is not available"; | |
173 | break; | |
174 | #endif | |
175 | #ifdef DSERR_OBJECTNOTFOUND | |
176 | case DSERR_OBJECTNOTFOUND: | |
177 | str = "The requested object was not found"; | |
178 | break; | |
179 | #endif | |
180 | #ifdef DSERR_OTHERAPPHASPRIO | |
181 | case DSERR_OTHERAPPHASPRIO: | |
182 | str = "Another application has a higher priority level, preventing this call from succeeding"; | |
183 | break; | |
184 | #endif | |
185 | #ifdef DSERR_OUTOFMEMORY | |
186 | case DSERR_OUTOFMEMORY: | |
187 | str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request"; | |
188 | break; | |
189 | #endif | |
190 | #ifdef DSERR_PRIOLEVELNEEDED | |
191 | case DSERR_PRIOLEVELNEEDED: | |
192 | str = "A cooperative level of DSSCL_PRIORITY or higher is required"; | |
193 | break; | |
194 | #endif | |
195 | #ifdef DSERR_SENDLOOP | |
196 | case DSERR_SENDLOOP: | |
197 | str = "A circular loop of send effects was detected"; | |
198 | break; | |
199 | #endif | |
200 | #ifdef DSERR_UNINITIALIZED | |
201 | case DSERR_UNINITIALIZED: | |
202 | str = "The Initialize method has not been called or has not been called successfully before other methods were called"; | |
203 | break; | |
204 | #endif | |
205 | #ifdef DSERR_UNSUPPORTED | |
206 | case DSERR_UNSUPPORTED: | |
207 | str = "The function called is not supported at this time"; | |
208 | break; | |
209 | #endif | |
210 | default: | |
211 | AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr); | |
212 | return; | |
213 | } | |
214 | ||
215 | AUD_log (AUDIO_CAP, "Reason: %s\n", str); | |
216 | } | |
217 | ||
218 | static void GCC_FMT_ATTR (2, 3) dsound_logerr ( | |
219 | HRESULT hr, | |
220 | const char *fmt, | |
221 | ... | |
222 | ) | |
223 | { | |
224 | va_list ap; | |
225 | ||
226 | va_start (ap, fmt); | |
227 | AUD_vlog (AUDIO_CAP, fmt, ap); | |
228 | va_end (ap); | |
229 | ||
230 | dsound_log_hresult (hr); | |
231 | } | |
232 | ||
233 | static void GCC_FMT_ATTR (3, 4) dsound_logerr2 ( | |
234 | HRESULT hr, | |
235 | const char *typ, | |
236 | const char *fmt, | |
237 | ... | |
238 | ) | |
239 | { | |
240 | va_list ap; | |
241 | ||
c0fe3827 | 242 | AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); |
1d14ffa9 FB |
243 | va_start (ap, fmt); |
244 | AUD_vlog (AUDIO_CAP, fmt, ap); | |
245 | va_end (ap); | |
246 | ||
247 | dsound_log_hresult (hr); | |
248 | } | |
249 | ||
250 | static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis) | |
251 | { | |
252 | return (millis * info->bytes_per_second) / 1000; | |
253 | } | |
254 | ||
255 | #ifdef DEBUG_DSOUND | |
256 | static void print_wave_format (WAVEFORMATEX *wfx) | |
257 | { | |
258 | dolog ("tag = %d\n", wfx->wFormatTag); | |
259 | dolog ("nChannels = %d\n", wfx->nChannels); | |
260 | dolog ("nSamplesPerSec = %ld\n", wfx->nSamplesPerSec); | |
261 | dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec); | |
262 | dolog ("nBlockAlign = %d\n", wfx->nBlockAlign); | |
263 | dolog ("wBitsPerSample = %d\n", wfx->wBitsPerSample); | |
264 | dolog ("cbSize = %d\n", wfx->cbSize); | |
265 | } | |
266 | #endif | |
267 | ||
191e1f0a | 268 | static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s) |
1d14ffa9 FB |
269 | { |
270 | HRESULT hr; | |
1d14ffa9 | 271 | |
2762955f | 272 | hr = IDirectSoundBuffer_Restore (dsb); |
1d14ffa9 | 273 | |
2762955f KZ |
274 | if (hr != DS_OK) { |
275 | dsound_logerr (hr, "Could not restore playback buffer\n"); | |
276 | return -1; | |
1d14ffa9 | 277 | } |
2762955f | 278 | return 0; |
1d14ffa9 FB |
279 | } |
280 | ||
1d14ffa9 FB |
281 | #include "dsound_template.h" |
282 | #define DSBTYPE_IN | |
283 | #include "dsound_template.h" | |
284 | #undef DSBTYPE_IN | |
285 | ||
191e1f0a KZ |
286 | static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp, |
287 | dsound *s) | |
1d14ffa9 FB |
288 | { |
289 | HRESULT hr; | |
1d14ffa9 | 290 | |
2762955f KZ |
291 | hr = IDirectSoundBuffer_GetStatus (dsb, statusp); |
292 | if (FAILED (hr)) { | |
293 | dsound_logerr (hr, "Could not get playback buffer status\n"); | |
294 | return -1; | |
295 | } | |
1d14ffa9 | 296 | |
2762955f KZ |
297 | if (*statusp & DSERR_BUFFERLOST) { |
298 | dsound_restore_out(dsb, s); | |
299 | return -1; | |
1d14ffa9 FB |
300 | } |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
305 | static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb, | |
306 | DWORD *statusp) | |
307 | { | |
308 | HRESULT hr; | |
309 | ||
310 | hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp); | |
311 | if (FAILED (hr)) { | |
c0fe3827 | 312 | dsound_logerr (hr, "Could not get capture buffer status\n"); |
1d14ffa9 FB |
313 | return -1; |
314 | } | |
315 | ||
316 | return 0; | |
317 | } | |
318 | ||
319 | static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) | |
320 | { | |
321 | int src_len1 = dst_len; | |
322 | int src_len2 = 0; | |
323 | int pos = hw->rpos + dst_len; | |
1ea879e5 | 324 | struct st_sample *src1 = hw->mix_buf + hw->rpos; |
325 | struct st_sample *src2 = NULL; | |
1d14ffa9 FB |
326 | |
327 | if (pos > hw->samples) { | |
328 | src_len1 = hw->samples - hw->rpos; | |
329 | src2 = hw->mix_buf; | |
330 | src_len2 = dst_len - src_len1; | |
331 | pos = src_len2; | |
332 | } | |
333 | ||
334 | if (src_len1) { | |
335 | hw->clip (dst, src1, src_len1); | |
1d14ffa9 FB |
336 | } |
337 | ||
338 | if (src_len2) { | |
339 | dst = advance (dst, src_len1 << hw->info.shift); | |
340 | hw->clip (dst, src2, src_len2); | |
1d14ffa9 FB |
341 | } |
342 | ||
343 | hw->rpos = pos % hw->samples; | |
344 | } | |
345 | ||
191e1f0a KZ |
346 | static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, |
347 | dsound *s) | |
1d14ffa9 FB |
348 | { |
349 | int err; | |
350 | LPVOID p1, p2; | |
351 | DWORD blen1, blen2, len1, len2; | |
352 | ||
353 | err = dsound_lock_out ( | |
354 | dsb, | |
355 | &hw->info, | |
356 | 0, | |
357 | hw->samples << hw->info.shift, | |
358 | &p1, &p2, | |
359 | &blen1, &blen2, | |
191e1f0a KZ |
360 | 1, |
361 | s | |
1d14ffa9 FB |
362 | ); |
363 | if (err) { | |
364 | return; | |
365 | } | |
366 | ||
367 | len1 = blen1 >> hw->info.shift; | |
368 | len2 = blen2 >> hw->info.shift; | |
369 | ||
370 | #ifdef DEBUG_DSOUND | |
371 | dolog ("clear %p,%ld,%ld %p,%ld,%ld\n", | |
372 | p1, blen1, len1, | |
373 | p2, blen2, len2); | |
374 | #endif | |
375 | ||
376 | if (p1 && len1) { | |
377 | audio_pcm_info_clear_buf (&hw->info, p1, len1); | |
378 | } | |
379 | ||
380 | if (p2 && len2) { | |
381 | audio_pcm_info_clear_buf (&hw->info, p2, len2); | |
382 | } | |
383 | ||
384 | dsound_unlock_out (dsb, p1, p2, blen1, blen2); | |
385 | } | |
386 | ||
1d14ffa9 FB |
387 | static int dsound_open (dsound *s) |
388 | { | |
1d14ffa9 | 389 | HRESULT hr; |
1d14ffa9 FB |
390 | HWND hwnd; |
391 | ||
392 | hwnd = GetForegroundWindow (); | |
393 | hr = IDirectSound_SetCooperativeLevel ( | |
394 | s->dsound, | |
395 | hwnd, | |
396 | DSSCL_PRIORITY | |
397 | ); | |
398 | ||
399 | if (FAILED (hr)) { | |
c0fe3827 | 400 | dsound_logerr (hr, "Could not set cooperative level for window %p\n", |
1d14ffa9 FB |
401 | hwnd); |
402 | return -1; | |
403 | } | |
404 | ||
1d14ffa9 | 405 | return 0; |
1d14ffa9 FB |
406 | } |
407 | ||
408 | static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) | |
409 | { | |
410 | HRESULT hr; | |
411 | DWORD status; | |
412 | DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | |
413 | LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; | |
191e1f0a | 414 | dsound *s = ds->s; |
1d14ffa9 FB |
415 | |
416 | if (!dsb) { | |
417 | dolog ("Attempt to control voice without a buffer\n"); | |
418 | return 0; | |
419 | } | |
420 | ||
421 | switch (cmd) { | |
422 | case VOICE_ENABLE: | |
191e1f0a | 423 | if (dsound_get_status_out (dsb, &status, s)) { |
1d14ffa9 FB |
424 | return -1; |
425 | } | |
426 | ||
427 | if (status & DSBSTATUS_PLAYING) { | |
c0fe3827 | 428 | dolog ("warning: Voice is already playing\n"); |
1d14ffa9 FB |
429 | return 0; |
430 | } | |
431 | ||
191e1f0a | 432 | dsound_clear_sample (hw, dsb, s); |
1d14ffa9 FB |
433 | |
434 | hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); | |
435 | if (FAILED (hr)) { | |
c0fe3827 | 436 | dsound_logerr (hr, "Could not start playing buffer\n"); |
1d14ffa9 FB |
437 | return -1; |
438 | } | |
439 | break; | |
440 | ||
441 | case VOICE_DISABLE: | |
191e1f0a | 442 | if (dsound_get_status_out (dsb, &status, s)) { |
1d14ffa9 FB |
443 | return -1; |
444 | } | |
445 | ||
446 | if (status & DSBSTATUS_PLAYING) { | |
447 | hr = IDirectSoundBuffer_Stop (dsb); | |
448 | if (FAILED (hr)) { | |
c0fe3827 | 449 | dsound_logerr (hr, "Could not stop playing buffer\n"); |
1d14ffa9 FB |
450 | return -1; |
451 | } | |
452 | } | |
453 | else { | |
c0fe3827 | 454 | dolog ("warning: Voice is not playing\n"); |
1d14ffa9 FB |
455 | } |
456 | break; | |
457 | } | |
458 | return 0; | |
459 | } | |
460 | ||
461 | static int dsound_write (SWVoiceOut *sw, void *buf, int len) | |
462 | { | |
463 | return audio_pcm_sw_write (sw, buf, len); | |
464 | } | |
465 | ||
bdff253c | 466 | static int dsound_run_out (HWVoiceOut *hw, int live) |
1d14ffa9 FB |
467 | { |
468 | int err; | |
469 | HRESULT hr; | |
470 | DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | |
471 | LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; | |
bdff253c | 472 | int len, hwshift; |
1d14ffa9 FB |
473 | DWORD blen1, blen2; |
474 | DWORD len1, len2; | |
475 | DWORD decr; | |
476 | DWORD wpos, ppos, old_pos; | |
477 | LPVOID p1, p2; | |
c0fe3827 | 478 | int bufsize; |
191e1f0a KZ |
479 | dsound *s = ds->s; |
480 | DSoundConf *conf = &s->conf; | |
1d14ffa9 FB |
481 | |
482 | if (!dsb) { | |
483 | dolog ("Attempt to run empty with playback buffer\n"); | |
484 | return 0; | |
485 | } | |
486 | ||
487 | hwshift = hw->info.shift; | |
c0fe3827 | 488 | bufsize = hw->samples << hwshift; |
1d14ffa9 | 489 | |
1d14ffa9 FB |
490 | hr = IDirectSoundBuffer_GetCurrentPosition ( |
491 | dsb, | |
492 | &ppos, | |
493 | ds->first_time ? &wpos : NULL | |
494 | ); | |
495 | if (FAILED (hr)) { | |
c0fe3827 | 496 | dsound_logerr (hr, "Could not get playback buffer position\n"); |
1d14ffa9 FB |
497 | return 0; |
498 | } | |
499 | ||
500 | len = live << hwshift; | |
501 | ||
502 | if (ds->first_time) { | |
191e1f0a | 503 | if (conf->latency_millis) { |
c0fe3827 | 504 | DWORD cur_blat; |
1d14ffa9 | 505 | |
c0fe3827 | 506 | cur_blat = audio_ring_dist (wpos, ppos, bufsize); |
1d14ffa9 FB |
507 | ds->first_time = 0; |
508 | old_pos = wpos; | |
509 | old_pos += | |
191e1f0a | 510 | millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat; |
c0fe3827 | 511 | old_pos %= bufsize; |
1d14ffa9 FB |
512 | old_pos &= ~hw->info.align; |
513 | } | |
514 | else { | |
515 | old_pos = wpos; | |
516 | } | |
517 | #ifdef DEBUG_DSOUND | |
518 | ds->played = 0; | |
519 | ds->mixed = 0; | |
520 | #endif | |
521 | } | |
522 | else { | |
523 | if (ds->old_pos == ppos) { | |
524 | #ifdef DEBUG_DSOUND | |
525 | dolog ("old_pos == ppos\n"); | |
526 | #endif | |
527 | return 0; | |
528 | } | |
529 | ||
530 | #ifdef DEBUG_DSOUND | |
531 | ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize); | |
532 | #endif | |
533 | old_pos = ds->old_pos; | |
534 | } | |
535 | ||
536 | if ((old_pos < ppos) && ((old_pos + len) > ppos)) { | |
537 | len = ppos - old_pos; | |
538 | } | |
539 | else { | |
c0fe3827 FB |
540 | if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) { |
541 | len = bufsize - old_pos + ppos; | |
1d14ffa9 FB |
542 | } |
543 | } | |
544 | ||
c0fe3827 FB |
545 | if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) { |
546 | dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n", | |
547 | len, bufsize, old_pos, ppos); | |
1d14ffa9 FB |
548 | return 0; |
549 | } | |
550 | ||
551 | len &= ~hw->info.align; | |
552 | if (!len) { | |
553 | return 0; | |
554 | } | |
555 | ||
556 | #ifdef DEBUG_DSOUND | |
557 | ds->old_ppos = ppos; | |
558 | #endif | |
559 | err = dsound_lock_out ( | |
560 | dsb, | |
561 | &hw->info, | |
562 | old_pos, | |
563 | len, | |
564 | &p1, &p2, | |
565 | &blen1, &blen2, | |
191e1f0a KZ |
566 | 0, |
567 | s | |
1d14ffa9 FB |
568 | ); |
569 | if (err) { | |
570 | return 0; | |
571 | } | |
572 | ||
573 | len1 = blen1 >> hwshift; | |
574 | len2 = blen2 >> hwshift; | |
575 | decr = len1 + len2; | |
576 | ||
577 | if (p1 && len1) { | |
578 | dsound_write_sample (hw, p1, len1); | |
579 | } | |
580 | ||
581 | if (p2 && len2) { | |
582 | dsound_write_sample (hw, p2, len2); | |
583 | } | |
584 | ||
585 | dsound_unlock_out (dsb, p1, p2, blen1, blen2); | |
c0fe3827 | 586 | ds->old_pos = (old_pos + (decr << hwshift)) % bufsize; |
1d14ffa9 FB |
587 | |
588 | #ifdef DEBUG_DSOUND | |
589 | ds->mixed += decr << hwshift; | |
590 | ||
591 | dolog ("played %lu mixed %lu diff %ld sec %f\n", | |
592 | ds->played, | |
593 | ds->mixed, | |
594 | ds->mixed - ds->played, | |
595 | abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second); | |
596 | #endif | |
597 | return decr; | |
598 | } | |
599 | ||
600 | static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...) | |
601 | { | |
602 | HRESULT hr; | |
603 | DWORD status; | |
604 | DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | |
605 | LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; | |
606 | ||
607 | if (!dscb) { | |
608 | dolog ("Attempt to control capture voice without a buffer\n"); | |
609 | return -1; | |
610 | } | |
611 | ||
612 | switch (cmd) { | |
613 | case VOICE_ENABLE: | |
614 | if (dsound_get_status_in (dscb, &status)) { | |
615 | return -1; | |
616 | } | |
617 | ||
618 | if (status & DSCBSTATUS_CAPTURING) { | |
c0fe3827 | 619 | dolog ("warning: Voice is already capturing\n"); |
1d14ffa9 FB |
620 | return 0; |
621 | } | |
622 | ||
623 | /* clear ?? */ | |
624 | ||
625 | hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING); | |
626 | if (FAILED (hr)) { | |
c0fe3827 | 627 | dsound_logerr (hr, "Could not start capturing\n"); |
1d14ffa9 FB |
628 | return -1; |
629 | } | |
630 | break; | |
631 | ||
632 | case VOICE_DISABLE: | |
633 | if (dsound_get_status_in (dscb, &status)) { | |
634 | return -1; | |
635 | } | |
636 | ||
637 | if (status & DSCBSTATUS_CAPTURING) { | |
638 | hr = IDirectSoundCaptureBuffer_Stop (dscb); | |
639 | if (FAILED (hr)) { | |
c0fe3827 | 640 | dsound_logerr (hr, "Could not stop capturing\n"); |
1d14ffa9 FB |
641 | return -1; |
642 | } | |
643 | } | |
644 | else { | |
c0fe3827 | 645 | dolog ("warning: Voice is not capturing\n"); |
1d14ffa9 FB |
646 | } |
647 | break; | |
648 | } | |
649 | return 0; | |
650 | } | |
651 | ||
652 | static int dsound_read (SWVoiceIn *sw, void *buf, int len) | |
653 | { | |
654 | return audio_pcm_sw_read (sw, buf, len); | |
655 | } | |
656 | ||
657 | static int dsound_run_in (HWVoiceIn *hw) | |
658 | { | |
659 | int err; | |
660 | HRESULT hr; | |
661 | DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | |
662 | LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; | |
663 | int live, len, dead; | |
664 | DWORD blen1, blen2; | |
665 | DWORD len1, len2; | |
666 | DWORD decr; | |
667 | DWORD cpos, rpos; | |
668 | LPVOID p1, p2; | |
669 | int hwshift; | |
191e1f0a | 670 | dsound *s = ds->s; |
1d14ffa9 FB |
671 | |
672 | if (!dscb) { | |
673 | dolog ("Attempt to run without capture buffer\n"); | |
674 | return 0; | |
675 | } | |
676 | ||
677 | hwshift = hw->info.shift; | |
678 | ||
679 | live = audio_pcm_hw_get_live_in (hw); | |
680 | dead = hw->samples - live; | |
681 | if (!dead) { | |
682 | return 0; | |
683 | } | |
684 | ||
685 | hr = IDirectSoundCaptureBuffer_GetCurrentPosition ( | |
686 | dscb, | |
687 | &cpos, | |
688 | ds->first_time ? &rpos : NULL | |
689 | ); | |
690 | if (FAILED (hr)) { | |
c0fe3827 | 691 | dsound_logerr (hr, "Could not get capture buffer position\n"); |
1d14ffa9 FB |
692 | return 0; |
693 | } | |
694 | ||
695 | if (ds->first_time) { | |
696 | ds->first_time = 0; | |
697 | if (rpos & hw->info.align) { | |
c0fe3827 | 698 | ldebug ("warning: Misaligned capture read position %ld(%d)\n", |
1d14ffa9 FB |
699 | rpos, hw->info.align); |
700 | } | |
701 | hw->wpos = rpos >> hwshift; | |
702 | } | |
703 | ||
704 | if (cpos & hw->info.align) { | |
c0fe3827 | 705 | ldebug ("warning: Misaligned capture position %ld(%d)\n", |
1d14ffa9 FB |
706 | cpos, hw->info.align); |
707 | } | |
708 | cpos >>= hwshift; | |
709 | ||
710 | len = audio_ring_dist (cpos, hw->wpos, hw->samples); | |
711 | if (!len) { | |
712 | return 0; | |
713 | } | |
714 | len = audio_MIN (len, dead); | |
715 | ||
716 | err = dsound_lock_in ( | |
717 | dscb, | |
718 | &hw->info, | |
719 | hw->wpos << hwshift, | |
720 | len << hwshift, | |
721 | &p1, | |
722 | &p2, | |
723 | &blen1, | |
724 | &blen2, | |
191e1f0a KZ |
725 | 0, |
726 | s | |
1d14ffa9 FB |
727 | ); |
728 | if (err) { | |
729 | return 0; | |
730 | } | |
731 | ||
732 | len1 = blen1 >> hwshift; | |
733 | len2 = blen2 >> hwshift; | |
734 | decr = len1 + len2; | |
735 | ||
736 | if (p1 && len1) { | |
00e07679 | 737 | hw->conv (hw->conv_buf + hw->wpos, p1, len1); |
1d14ffa9 FB |
738 | } |
739 | ||
740 | if (p2 && len2) { | |
00e07679 | 741 | hw->conv (hw->conv_buf, p2, len2); |
1d14ffa9 FB |
742 | } |
743 | ||
744 | dsound_unlock_in (dscb, p1, p2, blen1, blen2); | |
745 | hw->wpos = (hw->wpos + decr) % hw->samples; | |
746 | return decr; | |
747 | } | |
748 | ||
191e1f0a | 749 | static DSoundConf glob_conf = { |
191e1f0a KZ |
750 | .bufsize_in = 16384, |
751 | .bufsize_out = 16384, | |
191e1f0a KZ |
752 | .latency_millis = 10 |
753 | }; | |
754 | ||
1d14ffa9 FB |
755 | static void dsound_audio_fini (void *opaque) |
756 | { | |
757 | HRESULT hr; | |
758 | dsound *s = opaque; | |
759 | ||
760 | if (!s->dsound) { | |
191e1f0a | 761 | g_free(s); |
1d14ffa9 FB |
762 | return; |
763 | } | |
764 | ||
765 | hr = IDirectSound_Release (s->dsound); | |
766 | if (FAILED (hr)) { | |
c0fe3827 | 767 | dsound_logerr (hr, "Could not release DirectSound\n"); |
1d14ffa9 FB |
768 | } |
769 | s->dsound = NULL; | |
770 | ||
771 | if (!s->dsound_capture) { | |
191e1f0a | 772 | g_free(s); |
1d14ffa9 FB |
773 | return; |
774 | } | |
775 | ||
776 | hr = IDirectSoundCapture_Release (s->dsound_capture); | |
777 | if (FAILED (hr)) { | |
c0fe3827 | 778 | dsound_logerr (hr, "Could not release DirectSoundCapture\n"); |
1d14ffa9 FB |
779 | } |
780 | s->dsound_capture = NULL; | |
191e1f0a KZ |
781 | |
782 | g_free(s); | |
1d14ffa9 FB |
783 | } |
784 | ||
785 | static void *dsound_audio_init (void) | |
786 | { | |
787 | int err; | |
788 | HRESULT hr; | |
191e1f0a | 789 | dsound *s = g_malloc0(sizeof(dsound)); |
1d14ffa9 | 790 | |
191e1f0a | 791 | s->conf = glob_conf; |
1d14ffa9 FB |
792 | hr = CoInitialize (NULL); |
793 | if (FAILED (hr)) { | |
c0fe3827 | 794 | dsound_logerr (hr, "Could not initialize COM\n"); |
191e1f0a | 795 | g_free(s); |
1d14ffa9 FB |
796 | return NULL; |
797 | } | |
798 | ||
799 | hr = CoCreateInstance ( | |
800 | &CLSID_DirectSound, | |
801 | NULL, | |
802 | CLSCTX_ALL, | |
803 | &IID_IDirectSound, | |
804 | (void **) &s->dsound | |
805 | ); | |
806 | if (FAILED (hr)) { | |
c0fe3827 | 807 | dsound_logerr (hr, "Could not create DirectSound instance\n"); |
191e1f0a | 808 | g_free(s); |
1d14ffa9 FB |
809 | return NULL; |
810 | } | |
811 | ||
812 | hr = IDirectSound_Initialize (s->dsound, NULL); | |
813 | if (FAILED (hr)) { | |
c0fe3827 | 814 | dsound_logerr (hr, "Could not initialize DirectSound\n"); |
8ead62cf FB |
815 | |
816 | hr = IDirectSound_Release (s->dsound); | |
817 | if (FAILED (hr)) { | |
818 | dsound_logerr (hr, "Could not release DirectSound\n"); | |
819 | } | |
191e1f0a | 820 | g_free(s); |
1d14ffa9 FB |
821 | return NULL; |
822 | } | |
823 | ||
824 | hr = CoCreateInstance ( | |
825 | &CLSID_DirectSoundCapture, | |
826 | NULL, | |
827 | CLSCTX_ALL, | |
828 | &IID_IDirectSoundCapture, | |
829 | (void **) &s->dsound_capture | |
830 | ); | |
831 | if (FAILED (hr)) { | |
c0fe3827 | 832 | dsound_logerr (hr, "Could not create DirectSoundCapture instance\n"); |
1d14ffa9 FB |
833 | } |
834 | else { | |
835 | hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL); | |
836 | if (FAILED (hr)) { | |
c0fe3827 | 837 | dsound_logerr (hr, "Could not initialize DirectSoundCapture\n"); |
1d14ffa9 FB |
838 | |
839 | hr = IDirectSoundCapture_Release (s->dsound_capture); | |
840 | if (FAILED (hr)) { | |
c0fe3827 | 841 | dsound_logerr (hr, "Could not release DirectSoundCapture\n"); |
1d14ffa9 FB |
842 | } |
843 | s->dsound_capture = NULL; | |
844 | } | |
845 | } | |
846 | ||
847 | err = dsound_open (s); | |
848 | if (err) { | |
849 | dsound_audio_fini (s); | |
850 | return NULL; | |
851 | } | |
852 | ||
853 | return s; | |
854 | } | |
855 | ||
856 | static struct audio_option dsound_options[] = { | |
98f9f48c | 857 | { |
858 | .name = "LATENCY_MILLIS", | |
859 | .tag = AUD_OPT_INT, | |
191e1f0a | 860 | .valp = &glob_conf.latency_millis, |
98f9f48c | 861 | .descr = "(undocumented)" |
862 | }, | |
98f9f48c | 863 | { |
864 | .name = "BUFSIZE_OUT", | |
865 | .tag = AUD_OPT_INT, | |
191e1f0a | 866 | .valp = &glob_conf.bufsize_out, |
98f9f48c | 867 | .descr = "(undocumented)" |
868 | }, | |
869 | { | |
870 | .name = "BUFSIZE_IN", | |
871 | .tag = AUD_OPT_INT, | |
191e1f0a | 872 | .valp = &glob_conf.bufsize_in, |
98f9f48c | 873 | .descr = "(undocumented)" |
874 | }, | |
2700efa3 | 875 | { /* End of list */ } |
1d14ffa9 FB |
876 | }; |
877 | ||
35f4b58c | 878 | static struct audio_pcm_ops dsound_pcm_ops = { |
1dd3e4d1 JQ |
879 | .init_out = dsound_init_out, |
880 | .fini_out = dsound_fini_out, | |
881 | .run_out = dsound_run_out, | |
882 | .write = dsound_write, | |
883 | .ctl_out = dsound_ctl_out, | |
884 | ||
885 | .init_in = dsound_init_in, | |
886 | .fini_in = dsound_fini_in, | |
887 | .run_in = dsound_run_in, | |
888 | .read = dsound_read, | |
889 | .ctl_in = dsound_ctl_in | |
1d14ffa9 FB |
890 | }; |
891 | ||
892 | struct audio_driver dsound_audio_driver = { | |
bee37f32 JQ |
893 | .name = "dsound", |
894 | .descr = "DirectSound http://wikipedia.org/wiki/DirectSound", | |
895 | .options = dsound_options, | |
896 | .init = dsound_audio_init, | |
897 | .fini = dsound_audio_fini, | |
898 | .pcm_ops = &dsound_pcm_ops, | |
899 | .can_be_default = 1, | |
900 | .max_voices_out = INT_MAX, | |
901 | .max_voices_in = 1, | |
902 | .voice_size_out = sizeof (DSoundVoiceOut), | |
15c875a3 | 903 | .voice_size_in = sizeof (DSoundVoiceIn) |
1d14ffa9 | 904 | }; |