1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29: #include "qemu-common.h"
30: #include "audio.h"
31:
32: #define AUDIO_CAP "dsound"
33: #include "audio_int.h"
34:
35: #define WIN32_LEAN_AND_MEAN
36: #include <windows.h>
37: #include <mmsystem.h>
38: #include <objbase.h>
39: #include <dsound.h>
40:
41:
42:
43: static struct {
44: int lock_retries;
45: int restore_retries;
46: int getstatus_retries;
47: int set_primary;
48: int bufsize_in;
49: int bufsize_out;
50: audsettings_t settings;
51: int latency_millis;
52: } conf = {
53: 1,
54: 1,
55: 1,
56: 0,
57: 16384,
58: 16384,
59: {
60: 44100,
61: 2,
62: AUD_FMT_S16
63: },
64: 10
65: };
66:
67: typedef struct {
68: LPDIRECTSOUND dsound;
69: LPDIRECTSOUNDCAPTURE dsound_capture;
70: LPDIRECTSOUNDBUFFER dsound_primary_buffer;
71: audsettings_t settings;
72: } dsound;
73:
74: static dsound glob_dsound;
75:
76: typedef struct {
77: HWVoiceOut hw;
78: LPDIRECTSOUNDBUFFER dsound_buffer;
79: DWORD old_pos;
80: int first_time;
81: #ifdef DEBUG_DSOUND
82: DWORD old_ppos;
83: DWORD played;
84: DWORD mixed;
85: #endif
86: } DSoundVoiceOut;
87:
88: typedef struct {
89: HWVoiceIn hw;
90: int first_time;
91: LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
92: } DSoundVoiceIn;
93:
94: static void dsound_log_hresult (HRESULT hr)
95: {
96: const char *str = "BUG";
97:
98: switch (hr) {
99: case DS_OK:
100: str = "The method succeeded";
101: break;
102: #ifdef DS_NO_VIRTUALIZATION
103: case DS_NO_VIRTUALIZATION:
104: str = "The buffer was created, but another 3D algorithm was substituted";
105: break;
106: #endif
107: #ifdef DS_INCOMPLETE
108: case DS_INCOMPLETE:
109: str = "The method succeeded, but not all the optional effects were obtained";
110: break;
111: #endif
112: #ifdef DSERR_ACCESSDENIED
113: case DSERR_ACCESSDENIED:
114: str = "The request failed because access was denied";
115: break;
116: #endif
117: #ifdef DSERR_ALLOCATED
118: case DSERR_ALLOCATED:
119: str = "The request failed because resources, such as a priority level, were already in use by another caller";
120: break;
121: #endif
122: #ifdef DSERR_ALREADYINITIALIZED
123: case DSERR_ALREADYINITIALIZED:
124: str = "The object is already initialized";
125: break;
126: #endif
127: #ifdef DSERR_BADFORMAT
128: case DSERR_BADFORMAT:
129: str = "The specified wave format is not supported";
130: break;
131: #endif
132: #ifdef DSERR_BADSENDBUFFERGUID
133: case DSERR_BADSENDBUFFERGUID:
134: str = "The GUID specified in an audiopath file does not match a valid mix-in buffer";
135: break;
136: #endif
137: #ifdef DSERR_BUFFERLOST
138: case DSERR_BUFFERLOST:
139: str = "The buffer memory has been lost and must be restored";
140: break;
141: #endif
142: #ifdef DSERR_BUFFERTOOSMALL
143: case DSERR_BUFFERTOOSMALL:
144: str = "The buffer size is not great enough to enable effects processing";
145: break;
146: #endif
147: #ifdef DSERR_CONTROLUNAVAIL
148: case DSERR_CONTROLUNAVAIL:
149: 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";
150: break;
151: #endif
152: #ifdef DSERR_DS8_REQUIRED
153: case DSERR_DS8_REQUIRED:
154: str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface";
155: break;
156: #endif
157: #ifdef DSERR_FXUNAVAILABLE
158: case DSERR_FXUNAVAILABLE:
159: 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";
160: break;
161: #endif
162: #ifdef DSERR_GENERIC
163: case DSERR_GENERIC :
164: str = "An undetermined error occurred inside the DirectSound subsystem";
165: break;
166: #endif
167: #ifdef DSERR_INVALIDCALL
168: case DSERR_INVALIDCALL:
169: str = "This function is not valid for the current state of this object";
170: break;
171: #endif
172: #ifdef DSERR_INVALIDPARAM
173: case DSERR_INVALIDPARAM:
174: str = "An invalid parameter was passed to the returning function";
175: break;
176: #endif
177: #ifdef DSERR_NOAGGREGATION
178: case DSERR_NOAGGREGATION:
179: str = "The object does not support aggregation";
180: break;
181: #endif
182: #ifdef DSERR_NODRIVER
183: case DSERR_NODRIVER:
184: str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID";
185: break;
186: #endif
187: #ifdef DSERR_NOINTERFACE
188: case DSERR_NOINTERFACE:
189: str = "The requested COM interface is not available";
190: break;
191: #endif
192: #ifdef DSERR_OBJECTNOTFOUND
193: case DSERR_OBJECTNOTFOUND:
194: str = "The requested object was not found";
195: break;
196: #endif
197: #ifdef DSERR_OTHERAPPHASPRIO
198: case DSERR_OTHERAPPHASPRIO:
199: str = "Another application has a higher priority level, preventing this call from succeeding";
200: break;
201: #endif
202: #ifdef DSERR_OUTOFMEMORY
203: case DSERR_OUTOFMEMORY:
204: str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request";
205: break;
206: #endif
207: #ifdef DSERR_PRIOLEVELNEEDED
208: case DSERR_PRIOLEVELNEEDED:
209: str = "A cooperative level of DSSCL_PRIORITY or higher is required";
210: break;
211: #endif
212: #ifdef DSERR_SENDLOOP
213: case DSERR_SENDLOOP:
214: str = "A circular loop of send effects was detected";
215: break;
216: #endif
217: #ifdef DSERR_UNINITIALIZED
218: case DSERR_UNINITIALIZED:
219: str = "The Initialize method has not been called or has not been called successfully before other methods were called";
220: break;
221: #endif
222: #ifdef DSERR_UNSUPPORTED
223: case DSERR_UNSUPPORTED:
224: str = "The function called is not supported at this time";
225: break;
226: #endif
227: default:
228: AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr);
229: return;
230: }
231:
232: AUD_log (AUDIO_CAP, "Reason: %s\n", str);
233: }
234:
235: static void GCC_FMT_ATTR (2, 3) dsound_logerr (
236: HRESULT hr,
237: const char *fmt,
238: ...
239: )
240: {
241: va_list ap;
242:
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 void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
251: HRESULT hr,
252: const char *typ,
253: const char *fmt,
254: ...
255: )
256: {
257: va_list ap;
258:
259: AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
260: va_start (ap, fmt);
261: AUD_vlog (AUDIO_CAP, fmt, ap);
262: va_end (ap);
263:
264: dsound_log_hresult (hr);
265: }
266:
267: static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
268: {
269: return (millis * info->bytes_per_second) / 1000;
270: }
271:
272: #ifdef DEBUG_DSOUND
273: static void print_wave_format (WAVEFORMATEX *wfx)
274: {
275: dolog ("tag = %d\n", wfx->wFormatTag);
276: dolog ("nChannels = %d\n", wfx->nChannels);
277: dolog ("nSamplesPerSec = %ld\n", wfx->nSamplesPerSec);
278: dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec);
279: dolog ("nBlockAlign = %d\n", wfx->nBlockAlign);
280: dolog ("wBitsPerSample = %d\n", wfx->wBitsPerSample);
281: dolog ("cbSize = %d\n", wfx->cbSize);
282: }
283: #endif
284:
285: static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
286: {
287: HRESULT hr;
288: int i;
289:
290: for (i = 0; i < conf.restore_retries; ++i) {
291: hr = IDirectSoundBuffer_Restore (dsb);
292:
293: switch (hr) {
294: case DS_OK:
295: return 0;
296:
297: case DSERR_BUFFERLOST:
298: continue;
299:
300: default:
301: dsound_logerr (hr, "Could not restore playback buffer\n");
302: return -1;
303: }
304: }
305:
306: dolog ("%d attempts to restore playback buffer failed\n", i);
307: return -1;
308: }
309:
310: static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
311: {
312: memset (wfx, 0, sizeof (*wfx));
313:
314: wfx->wFormatTag = WAVE_FORMAT_PCM;
315: wfx->nChannels = as->nchannels;
316: wfx->nSamplesPerSec = as->freq;
317: wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
318: wfx->nBlockAlign = 1 << (as->nchannels == 2);
319: wfx->cbSize = 0;
320:
321: switch (as->fmt) {
322: case AUD_FMT_S8:
323: wfx->wBitsPerSample = 8;
324: break;
325:
326: case AUD_FMT_U8:
327: wfx->wBitsPerSample = 8;
328: break;
329:
330: case AUD_FMT_S16:
331: wfx->wBitsPerSample = 16;
332: wfx->nAvgBytesPerSec <<= 1;
333: wfx->nBlockAlign <<= 1;
334: break;
335:
336: case AUD_FMT_U16:
337: wfx->wBitsPerSample = 16;
338: wfx->nAvgBytesPerSec <<= 1;
339: wfx->nBlockAlign <<= 1;
340: break;
341:
342: default:
343: dolog ("Internal logic error: Bad audio format %d\n", as->freq);
344: return -1;
345: }
346:
347: return 0;
348: }
349:
350: static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
351: {
352: if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
353: dolog ("Invalid wave format, tag is not PCM, but %d\n",
354: wfx->wFormatTag);
355: return -1;
356: }
357:
358: if (!wfx->nSamplesPerSec) {
359: dolog ("Invalid wave format, frequency is zero\n");
360: return -1;
361: }
362: as->freq = wfx->nSamplesPerSec;
363:
364: switch (wfx->nChannels) {
365: case 1:
366: as->nchannels = 1;
367: break;
368:
369: case 2:
370: as->nchannels = 2;
371: break;
372:
373: default:
374: dolog (
375: "Invalid wave format, number of channels is not 1 or 2, but %d\n",
376: wfx->nChannels
377: );
378: return -1;
379: }
380:
381: switch (wfx->wBitsPerSample) {
382: case 8:
383: as->fmt = AUD_FMT_U8;
384: break;
385:
386: case 16:
387: as->fmt = AUD_FMT_S16;
388: break;
389:
390: default:
391: dolog ("Invalid wave format, bits per sample is not 8 or 16, but %d\n",
392: wfx->wBitsPerSample);
393: return -1;
394: }
395:
396: return 0;
397: }
398:
399: #include "dsound_template.h"
400: #define DSBTYPE_IN
401: #include "dsound_template.h"
402: #undef DSBTYPE_IN
403:
404: static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
405: {
406: HRESULT hr;
407: int i;
408:
409: for (i = 0; i < conf.getstatus_retries; ++i) {
410: hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
411: if (FAILED (hr)) {
412: dsound_logerr (hr, "Could not get playback buffer status\n");
413: return -1;
414: }
415:
416: if (*statusp & DSERR_BUFFERLOST) {
417: if (dsound_restore_out (dsb)) {
418: return -1;
419: }
420: continue;
421: }
422: break;
423: }
424:
425: return 0;
426: }
427:
428: static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
429: DWORD *statusp)
430: {
431: HRESULT hr;
432:
433: hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
434: if (FAILED (hr)) {
435: dsound_logerr (hr, "Could not get capture buffer status\n");
436: return -1;
437: }
438:
439: return 0;
440: }
441:
442: static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
443: {
444: int src_len1 = dst_len;
445: int src_len2 = 0;
446: int pos = hw->rpos + dst_len;
447: st_sample_t *src1 = hw->mix_buf + hw->rpos;
448: st_sample_t *src2 = NULL;
449:
450: if (pos > hw->samples) {
451: src_len1 = hw->samples - hw->rpos;
452: src2 = hw->mix_buf;
453: src_len2 = dst_len - src_len1;
454: pos = src_len2;
455: }
456:
457: if (src_len1) {
458: hw->clip (dst, src1, src_len1);
459: }
460:
461: if (src_len2) {
462: dst = advance (dst, src_len1 << hw->info.shift);
463: hw->clip (dst, src2, src_len2);
464: }
465:
466: hw->rpos = pos % hw->samples;
467: }
468:
469: static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
470: {
471: int err;
472: LPVOID p1, p2;
473: DWORD blen1, blen2, len1, len2;
474:
475: err = dsound_lock_out (
476: dsb,
477: &hw->info,
478: 0,
479: hw->samples << hw->info.shift,
480: &p1, &p2,
481: &blen1, &blen2,
482: 1
483: );
484: if (err) {
485: return;
486: }
487:
488: len1 = blen1 >> hw->info.shift;
489: len2 = blen2 >> hw->info.shift;
490:
491: #ifdef DEBUG_DSOUND
492: dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
493: p1, blen1, len1,
494: p2, blen2, len2);
495: #endif
496:
497: if (p1 && len1) {
498: audio_pcm_info_clear_buf (&hw->info, p1, len1);
499: }
500:
501: if (p2 && len2) {
502: audio_pcm_info_clear_buf (&hw->info, p2, len2);
503: }
504:
505: dsound_unlock_out (dsb, p1, p2, blen1, blen2);
506: }
507:
508: static void dsound_close (dsound *s)
509: {
510: HRESULT hr;
511:
512: if (s->dsound_primary_buffer) {
513: hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
514: if (FAILED (hr)) {
515: dsound_logerr (hr, "Could not release primary buffer\n");
516: }
517: s->dsound_primary_buffer = NULL;
518: }
519: }
520:
521: static int dsound_open (dsound *s)
522: {
523: int err;
524: HRESULT hr;
525: WAVEFORMATEX wfx;
526: DSBUFFERDESC dsbd;
527: HWND hwnd;
528:
529: hwnd = GetForegroundWindow ();
530: hr = IDirectSound_SetCooperativeLevel (
531: s->dsound,
532: hwnd,
533: DSSCL_PRIORITY
534: );
535:
536: if (FAILED (hr)) {
537: dsound_logerr (hr, "Could not set cooperative level for window %p\n",
538: hwnd);
539: return -1;
540: }
541:
542: if (!conf.set_primary) {
543: return 0;
544: }
545:
546: err = waveformat_from_audio_settings (&wfx, &conf.settings);
547: if (err) {
548: return -1;
549: }
550:
551: memset (&dsbd, 0, sizeof (dsbd));
552: dsbd.dwSize = sizeof (dsbd);
553: dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
554: dsbd.dwBufferBytes = 0;
555: dsbd.lpwfxFormat = NULL;
556:
557: hr = IDirectSound_CreateSoundBuffer (
558: s->dsound,
559: &dsbd,
560: &s->dsound_primary_buffer,
561: NULL
562: );
563: if (FAILED (hr)) {
564: dsound_logerr (hr, "Could not create primary playback buffer\n");
565: return -1;
566: }
567:
568: hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
569: if (FAILED (hr)) {
570: dsound_logerr (hr, "Could not set primary playback buffer format\n");
571: }
572:
573: hr = IDirectSoundBuffer_GetFormat (
574: s->dsound_primary_buffer,
575: &wfx,
576: sizeof (wfx),
577: NULL
578: );
579: if (FAILED (hr)) {
580: dsound_logerr (hr, "Could not get primary playback buffer format\n");
581: goto fail0;
582: }
583:
584: #ifdef DEBUG_DSOUND
585: dolog ("Primary\n");
586: print_wave_format (&wfx);
587: #endif
588:
589: err = waveformat_to_audio_settings (&wfx, &s->settings);
590: if (err) {
591: goto fail0;
592: }
593:
594: return 0;
595:
596: fail0:
597: dsound_close (s);
598: return -1;
599: }