|
|
1.1 root 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:
1.1.1.3 ! root 29: #include "qemu-common.h"
! 30: #include "audio.h"
1.1 root 31:
32: #define AUDIO_CAP "dsound"
33: #include "audio_int.h"
34:
1.1.1.3 ! root 35: #define WIN32_LEAN_AND_MEAN
1.1 root 36: #include <windows.h>
1.1.1.3 ! root 37: #include <mmsystem.h>
1.1 root 38: #include <objbase.h>
39: #include <dsound.h>
40:
41: /* #define DEBUG_DSOUND */
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: }
600:
601: static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
602: {
603: HRESULT hr;
604: DWORD status;
605: DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
606: LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
607:
608: if (!dsb) {
609: dolog ("Attempt to control voice without a buffer\n");
610: return 0;
611: }
612:
613: switch (cmd) {
614: case VOICE_ENABLE:
615: if (dsound_get_status_out (dsb, &status)) {
616: return -1;
617: }
618:
619: if (status & DSBSTATUS_PLAYING) {
620: dolog ("warning: Voice is already playing\n");
621: return 0;
622: }
623:
624: dsound_clear_sample (hw, dsb);
625:
626: hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
627: if (FAILED (hr)) {
628: dsound_logerr (hr, "Could not start playing buffer\n");
629: return -1;
630: }
631: break;
632:
633: case VOICE_DISABLE:
634: if (dsound_get_status_out (dsb, &status)) {
635: return -1;
636: }
637:
638: if (status & DSBSTATUS_PLAYING) {
639: hr = IDirectSoundBuffer_Stop (dsb);
640: if (FAILED (hr)) {
641: dsound_logerr (hr, "Could not stop playing buffer\n");
642: return -1;
643: }
644: }
645: else {
646: dolog ("warning: Voice is not playing\n");
647: }
648: break;
649: }
650: return 0;
651: }
652:
653: static int dsound_write (SWVoiceOut *sw, void *buf, int len)
654: {
655: return audio_pcm_sw_write (sw, buf, len);
656: }
657:
658: static int dsound_run_out (HWVoiceOut *hw)
659: {
660: int err;
661: HRESULT hr;
662: DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
663: LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
664: int live, len, hwshift;
665: DWORD blen1, blen2;
666: DWORD len1, len2;
667: DWORD decr;
668: DWORD wpos, ppos, old_pos;
669: LPVOID p1, p2;
670: int bufsize;
671:
672: if (!dsb) {
673: dolog ("Attempt to run empty with playback buffer\n");
674: return 0;
675: }
676:
677: hwshift = hw->info.shift;
678: bufsize = hw->samples << hwshift;
679:
680: live = audio_pcm_hw_get_live_out (hw);
681:
682: hr = IDirectSoundBuffer_GetCurrentPosition (
683: dsb,
684: &ppos,
685: ds->first_time ? &wpos : NULL
686: );
687: if (FAILED (hr)) {
688: dsound_logerr (hr, "Could not get playback buffer position\n");
689: return 0;
690: }
691:
692: len = live << hwshift;
693:
694: if (ds->first_time) {
695: if (conf.latency_millis) {
696: DWORD cur_blat;
697:
698: cur_blat = audio_ring_dist (wpos, ppos, bufsize);
699: ds->first_time = 0;
700: old_pos = wpos;
701: old_pos +=
702: millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
703: old_pos %= bufsize;
704: old_pos &= ~hw->info.align;
705: }
706: else {
707: old_pos = wpos;
708: }
709: #ifdef DEBUG_DSOUND
710: ds->played = 0;
711: ds->mixed = 0;
712: #endif
713: }
714: else {
715: if (ds->old_pos == ppos) {
716: #ifdef DEBUG_DSOUND
717: dolog ("old_pos == ppos\n");
718: #endif
719: return 0;
720: }
721:
722: #ifdef DEBUG_DSOUND
723: ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
724: #endif
725: old_pos = ds->old_pos;
726: }
727:
728: if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
729: len = ppos - old_pos;
730: }
731: else {
732: if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
733: len = bufsize - old_pos + ppos;
734: }
735: }
736:
737: if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
738: dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
739: len, bufsize, old_pos, ppos);
740: return 0;
741: }
742:
743: len &= ~hw->info.align;
744: if (!len) {
745: return 0;
746: }
747:
748: #ifdef DEBUG_DSOUND
749: ds->old_ppos = ppos;
750: #endif
751: err = dsound_lock_out (
752: dsb,
753: &hw->info,
754: old_pos,
755: len,
756: &p1, &p2,
757: &blen1, &blen2,
758: 0
759: );
760: if (err) {
761: return 0;
762: }
763:
764: len1 = blen1 >> hwshift;
765: len2 = blen2 >> hwshift;
766: decr = len1 + len2;
767:
768: if (p1 && len1) {
769: dsound_write_sample (hw, p1, len1);
770: }
771:
772: if (p2 && len2) {
773: dsound_write_sample (hw, p2, len2);
774: }
775:
776: dsound_unlock_out (dsb, p1, p2, blen1, blen2);
777: ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
778:
779: #ifdef DEBUG_DSOUND
780: ds->mixed += decr << hwshift;
781:
782: dolog ("played %lu mixed %lu diff %ld sec %f\n",
783: ds->played,
784: ds->mixed,
785: ds->mixed - ds->played,
786: abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
787: #endif
788: return decr;
789: }
790:
791: static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
792: {
793: HRESULT hr;
794: DWORD status;
795: DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
796: LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
797:
798: if (!dscb) {
799: dolog ("Attempt to control capture voice without a buffer\n");
800: return -1;
801: }
802:
803: switch (cmd) {
804: case VOICE_ENABLE:
805: if (dsound_get_status_in (dscb, &status)) {
806: return -1;
807: }
808:
809: if (status & DSCBSTATUS_CAPTURING) {
810: dolog ("warning: Voice is already capturing\n");
811: return 0;
812: }
813:
814: /* clear ?? */
815:
816: hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
817: if (FAILED (hr)) {
818: dsound_logerr (hr, "Could not start capturing\n");
819: return -1;
820: }
821: break;
822:
823: case VOICE_DISABLE:
824: if (dsound_get_status_in (dscb, &status)) {
825: return -1;
826: }
827:
828: if (status & DSCBSTATUS_CAPTURING) {
829: hr = IDirectSoundCaptureBuffer_Stop (dscb);
830: if (FAILED (hr)) {
831: dsound_logerr (hr, "Could not stop capturing\n");
832: return -1;
833: }
834: }
835: else {
836: dolog ("warning: Voice is not capturing\n");
837: }
838: break;
839: }
840: return 0;
841: }
842:
843: static int dsound_read (SWVoiceIn *sw, void *buf, int len)
844: {
845: return audio_pcm_sw_read (sw, buf, len);
846: }
847:
848: static int dsound_run_in (HWVoiceIn *hw)
849: {
850: int err;
851: HRESULT hr;
852: DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
853: LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
854: int live, len, dead;
855: DWORD blen1, blen2;
856: DWORD len1, len2;
857: DWORD decr;
858: DWORD cpos, rpos;
859: LPVOID p1, p2;
860: int hwshift;
861:
862: if (!dscb) {
863: dolog ("Attempt to run without capture buffer\n");
864: return 0;
865: }
866:
867: hwshift = hw->info.shift;
868:
869: live = audio_pcm_hw_get_live_in (hw);
870: dead = hw->samples - live;
871: if (!dead) {
872: return 0;
873: }
874:
875: hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
876: dscb,
877: &cpos,
878: ds->first_time ? &rpos : NULL
879: );
880: if (FAILED (hr)) {
881: dsound_logerr (hr, "Could not get capture buffer position\n");
882: return 0;
883: }
884:
885: if (ds->first_time) {
886: ds->first_time = 0;
887: if (rpos & hw->info.align) {
888: ldebug ("warning: Misaligned capture read position %ld(%d)\n",
889: rpos, hw->info.align);
890: }
891: hw->wpos = rpos >> hwshift;
892: }
893:
894: if (cpos & hw->info.align) {
895: ldebug ("warning: Misaligned capture position %ld(%d)\n",
896: cpos, hw->info.align);
897: }
898: cpos >>= hwshift;
899:
900: len = audio_ring_dist (cpos, hw->wpos, hw->samples);
901: if (!len) {
902: return 0;
903: }
904: len = audio_MIN (len, dead);
905:
906: err = dsound_lock_in (
907: dscb,
908: &hw->info,
909: hw->wpos << hwshift,
910: len << hwshift,
911: &p1,
912: &p2,
913: &blen1,
914: &blen2,
915: 0
916: );
917: if (err) {
918: return 0;
919: }
920:
921: len1 = blen1 >> hwshift;
922: len2 = blen2 >> hwshift;
923: decr = len1 + len2;
924:
925: if (p1 && len1) {
926: hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
927: }
928:
929: if (p2 && len2) {
930: hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
931: }
932:
933: dsound_unlock_in (dscb, p1, p2, blen1, blen2);
934: hw->wpos = (hw->wpos + decr) % hw->samples;
935: return decr;
936: }
937:
938: static void dsound_audio_fini (void *opaque)
939: {
940: HRESULT hr;
941: dsound *s = opaque;
942:
943: if (!s->dsound) {
944: return;
945: }
946:
947: hr = IDirectSound_Release (s->dsound);
948: if (FAILED (hr)) {
949: dsound_logerr (hr, "Could not release DirectSound\n");
950: }
951: s->dsound = NULL;
952:
953: if (!s->dsound_capture) {
954: return;
955: }
956:
957: hr = IDirectSoundCapture_Release (s->dsound_capture);
958: if (FAILED (hr)) {
959: dsound_logerr (hr, "Could not release DirectSoundCapture\n");
960: }
961: s->dsound_capture = NULL;
962: }
963:
964: static void *dsound_audio_init (void)
965: {
966: int err;
967: HRESULT hr;
968: dsound *s = &glob_dsound;
969:
970: hr = CoInitialize (NULL);
971: if (FAILED (hr)) {
972: dsound_logerr (hr, "Could not initialize COM\n");
973: return NULL;
974: }
975:
976: hr = CoCreateInstance (
977: &CLSID_DirectSound,
978: NULL,
979: CLSCTX_ALL,
980: &IID_IDirectSound,
981: (void **) &s->dsound
982: );
983: if (FAILED (hr)) {
984: dsound_logerr (hr, "Could not create DirectSound instance\n");
985: return NULL;
986: }
987:
988: hr = IDirectSound_Initialize (s->dsound, NULL);
989: if (FAILED (hr)) {
990: dsound_logerr (hr, "Could not initialize DirectSound\n");
1.1.1.2 root 991:
992: hr = IDirectSound_Release (s->dsound);
993: if (FAILED (hr)) {
994: dsound_logerr (hr, "Could not release DirectSound\n");
995: }
996: s->dsound = NULL;
1.1 root 997: return NULL;
998: }
999:
1000: hr = CoCreateInstance (
1001: &CLSID_DirectSoundCapture,
1002: NULL,
1003: CLSCTX_ALL,
1004: &IID_IDirectSoundCapture,
1005: (void **) &s->dsound_capture
1006: );
1007: if (FAILED (hr)) {
1008: dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
1009: }
1010: else {
1011: hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
1012: if (FAILED (hr)) {
1013: dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
1014:
1015: hr = IDirectSoundCapture_Release (s->dsound_capture);
1016: if (FAILED (hr)) {
1017: dsound_logerr (hr, "Could not release DirectSoundCapture\n");
1018: }
1019: s->dsound_capture = NULL;
1020: }
1021: }
1022:
1023: err = dsound_open (s);
1024: if (err) {
1025: dsound_audio_fini (s);
1026: return NULL;
1027: }
1028:
1029: return s;
1030: }
1031:
1032: static struct audio_option dsound_options[] = {
1033: {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries,
1034: "Number of times to attempt locking the buffer", NULL, 0},
1035: {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries,
1036: "Number of times to attempt restoring the buffer", NULL, 0},
1037: {"GETSTATUS_RETRIES", AUD_OPT_INT, &conf.getstatus_retries,
1038: "Number of times to attempt getting status of the buffer", NULL, 0},
1039: {"SET_PRIMARY", AUD_OPT_BOOL, &conf.set_primary,
1040: "Set the parameters of primary buffer", NULL, 0},
1041: {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis,
1042: "(undocumented)", NULL, 0},
1043: {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq,
1044: "Primary buffer frequency", NULL, 0},
1045: {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
1046: "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0},
1047: {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt,
1048: "Primary buffer format", NULL, 0},
1049: {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out,
1050: "(undocumented)", NULL, 0},
1051: {"BUFSIZE_IN", AUD_OPT_INT, &conf.bufsize_in,
1052: "(undocumented)", NULL, 0},
1053: {NULL, 0, NULL, NULL, NULL, 0}
1054: };
1055:
1056: static struct audio_pcm_ops dsound_pcm_ops = {
1057: dsound_init_out,
1058: dsound_fini_out,
1059: dsound_run_out,
1060: dsound_write,
1061: dsound_ctl_out,
1062:
1063: dsound_init_in,
1064: dsound_fini_in,
1065: dsound_run_in,
1066: dsound_read,
1067: dsound_ctl_in
1068: };
1069:
1070: struct audio_driver dsound_audio_driver = {
1071: INIT_FIELD (name = ) "dsound",
1072: INIT_FIELD (descr = )
1073: "DirectSound http://wikipedia.org/wiki/DirectSound",
1074: INIT_FIELD (options = ) dsound_options,
1075: INIT_FIELD (init = ) dsound_audio_init,
1076: INIT_FIELD (fini = ) dsound_audio_fini,
1077: INIT_FIELD (pcm_ops = ) &dsound_pcm_ops,
1078: INIT_FIELD (can_be_default = ) 1,
1079: INIT_FIELD (max_voices_out = ) INT_MAX,
1080: INIT_FIELD (max_voices_in = ) 1,
1081: INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut),
1082: INIT_FIELD (voice_size_in = ) sizeof (DSoundVoiceIn)
1083: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.