dsoundaudio.c revision 55f4e4a5ec657a017e3bf75299ad71fd1c968dd3
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
29#include "vl.h"
30
31#define AUDIO_CAP "dsound"
32#include "audio_int.h"
33
34#include <windows.h>
35#include <objbase.h>
36#include <dsound.h>
37
38/* #define DEBUG_DSOUND */
39
40static struct {
41    int lock_retries;
42    int restore_retries;
43    int getstatus_retries;
44    int set_primary;
45    int bufsize_in;
46    int bufsize_out;
47    audsettings_t settings;
48    int latency_millis;
49} conf = {
50    1,
51    1,
52    1,
53    0,
54    16384,
55    16384,
56    {
57        44100,
58        2,
59        AUD_FMT_S16
60    },
61    10
62};
63
64typedef struct {
65    LPDIRECTSOUND dsound;
66    LPDIRECTSOUNDCAPTURE dsound_capture;
67    LPDIRECTSOUNDBUFFER dsound_primary_buffer;
68    audsettings_t settings;
69} dsound;
70
71static dsound glob_dsound;
72
73typedef struct {
74    HWVoiceOut hw;
75    LPDIRECTSOUNDBUFFER dsound_buffer;
76    DWORD old_pos;
77    int first_time;
78#ifdef DEBUG_DSOUND
79    DWORD old_ppos;
80    DWORD played;
81    DWORD mixed;
82#endif
83} DSoundVoiceOut;
84
85typedef struct {
86    HWVoiceIn hw;
87    int first_time;
88    LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
89} DSoundVoiceIn;
90
91static void dsound_log_hresult (HRESULT hr)
92{
93    const char *str = "BUG";
94
95    switch (hr) {
96    case DS_OK:
97        str = "The method succeeded";
98        break;
99#ifdef DS_NO_VIRTUALIZATION
100    case DS_NO_VIRTUALIZATION:
101        str = "The buffer was created, but another 3D algorithm was substituted";
102        break;
103#endif
104#ifdef DS_INCOMPLETE
105    case DS_INCOMPLETE:
106        str = "The method succeeded, but not all the optional effects were obtained";
107        break;
108#endif
109#ifdef DSERR_ACCESSDENIED
110    case DSERR_ACCESSDENIED:
111        str = "The request failed because access was denied";
112        break;
113#endif
114#ifdef DSERR_ALLOCATED
115    case DSERR_ALLOCATED:
116        str = "The request failed because resources, such as a priority level, were already in use by another caller";
117        break;
118#endif
119#ifdef DSERR_ALREADYINITIALIZED
120    case DSERR_ALREADYINITIALIZED:
121        str = "The object is already initialized";
122        break;
123#endif
124#ifdef DSERR_BADFORMAT
125    case DSERR_BADFORMAT:
126        str = "The specified wave format is not supported";
127        break;
128#endif
129#ifdef DSERR_BADSENDBUFFERGUID
130    case DSERR_BADSENDBUFFERGUID:
131        str = "The GUID specified in an audiopath file does not match a valid mix-in buffer";
132        break;
133#endif
134#ifdef DSERR_BUFFERLOST
135    case DSERR_BUFFERLOST:
136        str = "The buffer memory has been lost and must be restored";
137        break;
138#endif
139#ifdef DSERR_BUFFERTOOSMALL
140    case DSERR_BUFFERTOOSMALL:
141        str = "The buffer size is not great enough to enable effects processing";
142        break;
143#endif
144#ifdef DSERR_CONTROLUNAVAIL
145    case DSERR_CONTROLUNAVAIL:
146        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";
147        break;
148#endif
149#ifdef DSERR_DS8_REQUIRED
150    case DSERR_DS8_REQUIRED:
151        str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface";
152        break;
153#endif
154#ifdef DSERR_FXUNAVAILABLE
155    case DSERR_FXUNAVAILABLE:
156        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";
157        break;
158#endif
159#ifdef DSERR_GENERIC
160    case DSERR_GENERIC :
161        str = "An undetermined error occurred inside the DirectSound subsystem";
162        break;
163#endif
164#ifdef DSERR_INVALIDCALL
165    case DSERR_INVALIDCALL:
166        str = "This function is not valid for the current state of this object";
167        break;
168#endif
169#ifdef DSERR_INVALIDPARAM
170    case DSERR_INVALIDPARAM:
171        str = "An invalid parameter was passed to the returning function";
172        break;
173#endif
174#ifdef DSERR_NOAGGREGATION
175    case DSERR_NOAGGREGATION:
176        str = "The object does not support aggregation";
177        break;
178#endif
179#ifdef DSERR_NODRIVER
180    case DSERR_NODRIVER:
181        str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID";
182        break;
183#endif
184#ifdef DSERR_NOINTERFACE
185    case DSERR_NOINTERFACE:
186        str = "The requested COM interface is not available";
187        break;
188#endif
189#ifdef DSERR_OBJECTNOTFOUND
190    case DSERR_OBJECTNOTFOUND:
191        str = "The requested object was not found";
192        break;
193#endif
194#ifdef DSERR_OTHERAPPHASPRIO
195    case DSERR_OTHERAPPHASPRIO:
196        str = "Another application has a higher priority level, preventing this call from succeeding";
197        break;
198#endif
199#ifdef DSERR_OUTOFMEMORY
200    case DSERR_OUTOFMEMORY:
201        str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request";
202        break;
203#endif
204#ifdef DSERR_PRIOLEVELNEEDED
205    case DSERR_PRIOLEVELNEEDED:
206        str = "A cooperative level of DSSCL_PRIORITY or higher is required";
207        break;
208#endif
209#ifdef DSERR_SENDLOOP
210    case DSERR_SENDLOOP:
211        str = "A circular loop of send effects was detected";
212        break;
213#endif
214#ifdef DSERR_UNINITIALIZED
215    case DSERR_UNINITIALIZED:
216        str = "The Initialize method has not been called or has not been called successfully before other methods were called";
217        break;
218#endif
219#ifdef DSERR_UNSUPPORTED
220    case DSERR_UNSUPPORTED:
221        str = "The function called is not supported at this time";
222        break;
223#endif
224    default:
225        AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr);
226        return;
227    }
228
229    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
230}
231
232static void GCC_FMT_ATTR (2, 3) dsound_logerr (
233    HRESULT hr,
234    const char *fmt,
235    ...
236    )
237{
238    va_list ap;
239
240    va_start (ap, fmt);
241    AUD_vlog (AUDIO_CAP, fmt, ap);
242    va_end (ap);
243
244    dsound_log_hresult (hr);
245}
246
247static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
248    HRESULT hr,
249    const char *typ,
250    const char *fmt,
251    ...
252    )
253{
254    va_list ap;
255
256    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
257    va_start (ap, fmt);
258    AUD_vlog (AUDIO_CAP, fmt, ap);
259    va_end (ap);
260
261    dsound_log_hresult (hr);
262}
263
264static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
265{
266    return (millis * info->bytes_per_second) / 1000;
267}
268
269#ifdef DEBUG_DSOUND
270static void print_wave_format (WAVEFORMATEX *wfx)
271{
272    dolog ("tag             = %d\n", wfx->wFormatTag);
273    dolog ("nChannels       = %d\n", wfx->nChannels);
274    dolog ("nSamplesPerSec  = %ld\n", wfx->nSamplesPerSec);
275    dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec);
276    dolog ("nBlockAlign     = %d\n", wfx->nBlockAlign);
277    dolog ("wBitsPerSample  = %d\n", wfx->wBitsPerSample);
278    dolog ("cbSize          = %d\n", wfx->cbSize);
279}
280#endif
281
282static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
283{
284    HRESULT hr;
285    int i;
286
287    for (i = 0; i < conf.restore_retries; ++i) {
288        hr = IDirectSoundBuffer_Restore (dsb);
289
290        switch (hr) {
291        case DS_OK:
292            return 0;
293
294        case DSERR_BUFFERLOST:
295            continue;
296
297        default:
298            dsound_logerr (hr, "Could not restore playback buffer\n");
299            return -1;
300        }
301    }
302
303    dolog ("%d attempts to restore playback buffer failed\n", i);
304    return -1;
305}
306
307static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
308{
309    memset (wfx, 0, sizeof (*wfx));
310
311    wfx->wFormatTag = WAVE_FORMAT_PCM;
312    wfx->nChannels = as->nchannels;
313    wfx->nSamplesPerSec = as->freq;
314    wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
315    wfx->nBlockAlign = 1 << (as->nchannels == 2);
316    wfx->cbSize = 0;
317
318    switch (as->fmt) {
319    case AUD_FMT_S8:
320        wfx->wBitsPerSample = 8;
321        break;
322
323    case AUD_FMT_U8:
324        wfx->wBitsPerSample = 8;
325        break;
326
327    case AUD_FMT_S16:
328        wfx->wBitsPerSample = 16;
329        wfx->nAvgBytesPerSec <<= 1;
330        wfx->nBlockAlign <<= 1;
331        break;
332
333    case AUD_FMT_U16:
334        wfx->wBitsPerSample = 16;
335        wfx->nAvgBytesPerSec <<= 1;
336        wfx->nBlockAlign <<= 1;
337        break;
338
339    default:
340        dolog ("Internal logic error: Bad audio format %d\n", as->freq);
341        return -1;
342    }
343
344    return 0;
345}
346
347static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
348{
349    if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
350        dolog ("Invalid wave format, tag is not PCM, but %d\n",
351               wfx->wFormatTag);
352        return -1;
353    }
354
355    if (!wfx->nSamplesPerSec) {
356        dolog ("Invalid wave format, frequency is zero\n");
357        return -1;
358    }
359    as->freq = wfx->nSamplesPerSec;
360
361    switch (wfx->nChannels) {
362    case 1:
363        as->nchannels = 1;
364        break;
365
366    case 2:
367        as->nchannels = 2;
368        break;
369
370    default:
371        dolog (
372            "Invalid wave format, number of channels is not 1 or 2, but %d\n",
373            wfx->nChannels
374            );
375        return -1;
376    }
377
378    switch (wfx->wBitsPerSample) {
379    case 8:
380        as->fmt = AUD_FMT_U8;
381        break;
382
383    case 16:
384        as->fmt = AUD_FMT_S16;
385        break;
386
387    default:
388        dolog ("Invalid wave format, bits per sample is not 8 or 16, but %d\n",
389               wfx->wBitsPerSample);
390        return -1;
391    }
392
393    return 0;
394}
395
396#include "dsound_template.h"
397#define DSBTYPE_IN
398#include "dsound_template.h"
399#undef DSBTYPE_IN
400
401static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
402{
403    HRESULT hr;
404    int i;
405
406    for (i = 0; i < conf.getstatus_retries; ++i) {
407        hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
408        if (FAILED (hr)) {
409            dsound_logerr (hr, "Could not get playback buffer status\n");
410            return -1;
411        }
412
413        if (*statusp & DSERR_BUFFERLOST) {
414            if (dsound_restore_out (dsb)) {
415                return -1;
416            }
417            continue;
418        }
419        break;
420    }
421
422    return 0;
423}
424
425static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
426                                 DWORD *statusp)
427{
428    HRESULT hr;
429
430    hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
431    if (FAILED (hr)) {
432        dsound_logerr (hr, "Could not get capture buffer status\n");
433        return -1;
434    }
435
436    return 0;
437}
438
439static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
440{
441    int src_len1 = dst_len;
442    int src_len2 = 0;
443    int pos = hw->rpos + dst_len;
444    st_sample_t *src1 = hw->mix_buf + hw->rpos;
445    st_sample_t *src2 = NULL;
446
447    if (pos > hw->samples) {
448        src_len1 = hw->samples - hw->rpos;
449        src2 = hw->mix_buf;
450        src_len2 = dst_len - src_len1;
451        pos = src_len2;
452    }
453
454    if (src_len1) {
455        hw->clip (dst, src1, src_len1);
456    }
457
458    if (src_len2) {
459        dst = advance (dst, src_len1 << hw->info.shift);
460        hw->clip (dst, src2, src_len2);
461    }
462
463    hw->rpos = pos % hw->samples;
464}
465
466static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
467{
468    int err;
469    LPVOID p1, p2;
470    DWORD blen1, blen2, len1, len2;
471
472    err = dsound_lock_out (
473        dsb,
474        &hw->info,
475        0,
476        hw->samples << hw->info.shift,
477        &p1, &p2,
478        &blen1, &blen2,
479        1
480        );
481    if (err) {
482        return;
483    }
484
485    len1 = blen1 >> hw->info.shift;
486    len2 = blen2 >> hw->info.shift;
487
488#ifdef DEBUG_DSOUND
489    dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
490           p1, blen1, len1,
491           p2, blen2, len2);
492#endif
493
494    if (p1 && len1) {
495        audio_pcm_info_clear_buf (&hw->info, p1, len1);
496    }
497
498    if (p2 && len2) {
499        audio_pcm_info_clear_buf (&hw->info, p2, len2);
500    }
501
502    dsound_unlock_out (dsb, p1, p2, blen1, blen2);
503}
504
505static void dsound_close (dsound *s)
506{
507    HRESULT hr;
508
509    if (s->dsound_primary_buffer) {
510        hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
511        if (FAILED (hr)) {
512            dsound_logerr (hr, "Could not release primary buffer\n");
513        }
514        s->dsound_primary_buffer = NULL;
515    }
516}
517
518static int dsound_open (dsound *s)
519{
520    int err;
521    HRESULT hr;
522    WAVEFORMATEX wfx;
523    DSBUFFERDESC dsbd;
524    HWND hwnd;
525
526    hwnd = GetForegroundWindow ();
527    hr = IDirectSound_SetCooperativeLevel (
528        s->dsound,
529        hwnd,
530        DSSCL_PRIORITY
531        );
532
533    if (FAILED (hr)) {
534        dsound_logerr (hr, "Could not set cooperative level for window %p\n",
535                       hwnd);
536        return -1;
537    }
538
539    if (!conf.set_primary) {
540        return 0;
541    }
542
543    err = waveformat_from_audio_settings (&wfx, &conf.settings);
544    if (err) {
545        return -1;
546    }
547
548    memset (&dsbd, 0, sizeof (dsbd));
549    dsbd.dwSize = sizeof (dsbd);
550    dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
551    dsbd.dwBufferBytes = 0;
552    dsbd.lpwfxFormat = NULL;
553
554    hr = IDirectSound_CreateSoundBuffer (
555        s->dsound,
556        &dsbd,
557        &s->dsound_primary_buffer,
558        NULL
559        );
560    if (FAILED (hr)) {
561        dsound_logerr (hr, "Could not create primary playback buffer\n");
562        return -1;
563    }
564
565    hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
566    if (FAILED (hr)) {
567        dsound_logerr (hr, "Could not set primary playback buffer format\n");
568    }
569
570    hr = IDirectSoundBuffer_GetFormat (
571        s->dsound_primary_buffer,
572        &wfx,
573        sizeof (wfx),
574        NULL
575        );
576    if (FAILED (hr)) {
577        dsound_logerr (hr, "Could not get primary playback buffer format\n");
578        goto fail0;
579    }
580
581#ifdef DEBUG_DSOUND
582    dolog ("Primary\n");
583    print_wave_format (&wfx);
584#endif
585
586    err = waveformat_to_audio_settings (&wfx, &s->settings);
587    if (err) {
588        goto fail0;
589    }
590
591    return 0;
592
593 fail0:
594    dsound_close (s);
595    return -1;
596}
597
598static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
599{
600    HRESULT hr;
601    DWORD status;
602    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
603    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
604
605    if (!dsb) {
606        dolog ("Attempt to control voice without a buffer\n");
607        return 0;
608    }
609
610    switch (cmd) {
611    case VOICE_ENABLE:
612        if (dsound_get_status_out (dsb, &status)) {
613            return -1;
614        }
615
616        if (status & DSBSTATUS_PLAYING) {
617            dolog ("warning: Voice is already playing\n");
618            return 0;
619        }
620
621        dsound_clear_sample (hw, dsb);
622
623        hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
624        if (FAILED (hr)) {
625            dsound_logerr (hr, "Could not start playing buffer\n");
626            return -1;
627        }
628        break;
629
630    case VOICE_DISABLE:
631        if (dsound_get_status_out (dsb, &status)) {
632            return -1;
633        }
634
635        if (status & DSBSTATUS_PLAYING) {
636            hr = IDirectSoundBuffer_Stop (dsb);
637            if (FAILED (hr)) {
638                dsound_logerr (hr, "Could not stop playing buffer\n");
639                return -1;
640            }
641        }
642        else {
643            dolog ("warning: Voice is not playing\n");
644        }
645        break;
646    }
647    return 0;
648}
649
650static int dsound_write (SWVoiceOut *sw, void *buf, int len)
651{
652    return audio_pcm_sw_write (sw, buf, len);
653}
654
655static int dsound_run_out (HWVoiceOut *hw)
656{
657    int err;
658    HRESULT hr;
659    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
660    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
661    int live, len, hwshift;
662    DWORD blen1, blen2;
663    DWORD len1, len2;
664    DWORD decr;
665    DWORD wpos, ppos, old_pos;
666    LPVOID p1, p2;
667    int bufsize;
668
669    if (!dsb) {
670        dolog ("Attempt to run empty with playback buffer\n");
671        return 0;
672    }
673
674    hwshift = hw->info.shift;
675    bufsize = hw->samples << hwshift;
676
677    live = audio_pcm_hw_get_live_out (hw);
678
679    hr = IDirectSoundBuffer_GetCurrentPosition (
680        dsb,
681        &ppos,
682        ds->first_time ? &wpos : NULL
683        );
684    if (FAILED (hr)) {
685        dsound_logerr (hr, "Could not get playback buffer position\n");
686        return 0;
687    }
688
689    len = live << hwshift;
690
691    if (ds->first_time) {
692        if (conf.latency_millis) {
693            DWORD cur_blat;
694
695            cur_blat = audio_ring_dist (wpos, ppos, bufsize);
696            ds->first_time = 0;
697            old_pos = wpos;
698            old_pos +=
699                millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
700            old_pos %= bufsize;
701            old_pos &= ~hw->info.align;
702        }
703        else {
704            old_pos = wpos;
705        }
706#ifdef DEBUG_DSOUND
707        ds->played = 0;
708        ds->mixed = 0;
709#endif
710    }
711    else {
712        if (ds->old_pos == ppos) {
713#ifdef DEBUG_DSOUND
714            dolog ("old_pos == ppos\n");
715#endif
716            return 0;
717        }
718
719#ifdef DEBUG_DSOUND
720        ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
721#endif
722        old_pos = ds->old_pos;
723    }
724
725    if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
726        len = ppos - old_pos;
727    }
728    else {
729        if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
730            len = bufsize - old_pos + ppos;
731        }
732    }
733
734    if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
735        dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
736               len, bufsize, old_pos, ppos);
737        return 0;
738    }
739
740    len &= ~hw->info.align;
741    if (!len) {
742        return 0;
743    }
744
745#ifdef DEBUG_DSOUND
746    ds->old_ppos = ppos;
747#endif
748    err = dsound_lock_out (
749        dsb,
750        &hw->info,
751        old_pos,
752        len,
753        &p1, &p2,
754        &blen1, &blen2,
755        0
756        );
757    if (err) {
758        return 0;
759    }
760
761    len1 = blen1 >> hwshift;
762    len2 = blen2 >> hwshift;
763    decr = len1 + len2;
764
765    if (p1 && len1) {
766        dsound_write_sample (hw, p1, len1);
767    }
768
769    if (p2 && len2) {
770        dsound_write_sample (hw, p2, len2);
771    }
772
773    dsound_unlock_out (dsb, p1, p2, blen1, blen2);
774    ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
775
776#ifdef DEBUG_DSOUND
777    ds->mixed += decr << hwshift;
778
779    dolog ("played %lu mixed %lu diff %ld sec %f\n",
780           ds->played,
781           ds->mixed,
782           ds->mixed - ds->played,
783           abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
784#endif
785    return decr;
786}
787
788static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
789{
790    HRESULT hr;
791    DWORD status;
792    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
793    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
794
795    if (!dscb) {
796        dolog ("Attempt to control capture voice without a buffer\n");
797        return -1;
798    }
799
800    switch (cmd) {
801    case VOICE_ENABLE:
802        if (dsound_get_status_in (dscb, &status)) {
803            return -1;
804        }
805
806        if (status & DSCBSTATUS_CAPTURING) {
807            dolog ("warning: Voice is already capturing\n");
808            return 0;
809        }
810
811        /* clear ?? */
812
813        hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
814        if (FAILED (hr)) {
815            dsound_logerr (hr, "Could not start capturing\n");
816            return -1;
817        }
818        break;
819
820    case VOICE_DISABLE:
821        if (dsound_get_status_in (dscb, &status)) {
822            return -1;
823        }
824
825        if (status & DSCBSTATUS_CAPTURING) {
826            hr = IDirectSoundCaptureBuffer_Stop (dscb);
827            if (FAILED (hr)) {
828                dsound_logerr (hr, "Could not stop capturing\n");
829                return -1;
830            }
831        }
832        else {
833            dolog ("warning: Voice is not capturing\n");
834        }
835        break;
836    }
837    return 0;
838}
839
840static int dsound_read (SWVoiceIn *sw, void *buf, int len)
841{
842    return audio_pcm_sw_read (sw, buf, len);
843}
844
845static int dsound_run_in (HWVoiceIn *hw)
846{
847    int err;
848    HRESULT hr;
849    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
850    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
851    int live, len, dead;
852    DWORD blen1, blen2;
853    DWORD len1, len2;
854    DWORD decr;
855    DWORD cpos, rpos;
856    LPVOID p1, p2;
857    int hwshift;
858
859    if (!dscb) {
860        dolog ("Attempt to run without capture buffer\n");
861        return 0;
862    }
863
864    hwshift = hw->info.shift;
865
866    live = audio_pcm_hw_get_live_in (hw);
867    dead = hw->samples - live;
868    if (!dead) {
869        return 0;
870    }
871
872    hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
873        dscb,
874        &cpos,
875        ds->first_time ? &rpos : NULL
876        );
877    if (FAILED (hr)) {
878        dsound_logerr (hr, "Could not get capture buffer position\n");
879        return 0;
880    }
881
882    if (ds->first_time) {
883        ds->first_time = 0;
884        if (rpos & hw->info.align) {
885            ldebug ("warning: Misaligned capture read position %ld(%d)\n",
886                    rpos, hw->info.align);
887        }
888        hw->wpos = rpos >> hwshift;
889    }
890
891    if (cpos & hw->info.align) {
892        ldebug ("warning: Misaligned capture position %ld(%d)\n",
893                cpos, hw->info.align);
894    }
895    cpos >>= hwshift;
896
897    len = audio_ring_dist (cpos, hw->wpos, hw->samples);
898    if (!len) {
899        return 0;
900    }
901    len = audio_MIN (len, dead);
902
903    err = dsound_lock_in (
904        dscb,
905        &hw->info,
906        hw->wpos << hwshift,
907        len << hwshift,
908        &p1,
909        &p2,
910        &blen1,
911        &blen2,
912        0
913        );
914    if (err) {
915        return 0;
916    }
917
918    len1 = blen1 >> hwshift;
919    len2 = blen2 >> hwshift;
920    decr = len1 + len2;
921
922    if (p1 && len1) {
923        hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
924    }
925
926    if (p2 && len2) {
927        hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
928    }
929
930    dsound_unlock_in (dscb, p1, p2, blen1, blen2);
931    hw->wpos = (hw->wpos + decr) % hw->samples;
932    return decr;
933}
934
935static void dsound_audio_fini (void *opaque)
936{
937    HRESULT hr;
938    dsound *s = opaque;
939
940    if (!s->dsound) {
941        return;
942    }
943
944    hr = IDirectSound_Release (s->dsound);
945    if (FAILED (hr)) {
946        dsound_logerr (hr, "Could not release DirectSound\n");
947    }
948    s->dsound = NULL;
949
950    if (!s->dsound_capture) {
951        return;
952    }
953
954    hr = IDirectSoundCapture_Release (s->dsound_capture);
955    if (FAILED (hr)) {
956        dsound_logerr (hr, "Could not release DirectSoundCapture\n");
957    }
958    s->dsound_capture = NULL;
959}
960
961static void *dsound_audio_init (void)
962{
963    int err;
964    HRESULT hr;
965    dsound *s = &glob_dsound;
966
967    hr = CoInitialize (NULL);
968    if (FAILED (hr)) {
969        dsound_logerr (hr, "Could not initialize COM\n");
970        return NULL;
971    }
972
973    hr = CoCreateInstance (
974        &CLSID_DirectSound,
975        NULL,
976        CLSCTX_ALL,
977        &IID_IDirectSound,
978        (void **) &s->dsound
979        );
980    if (FAILED (hr)) {
981        dsound_logerr (hr, "Could not create DirectSound instance\n");
982        return NULL;
983    }
984
985    hr = IDirectSound_Initialize (s->dsound, NULL);
986    if (FAILED (hr)) {
987        dsound_logerr (hr, "Could not initialize DirectSound\n");
988
989        hr = IDirectSound_Release (s->dsound);
990        if (FAILED (hr)) {
991            dsound_logerr (hr, "Could not release DirectSound\n");
992        }
993        s->dsound = NULL;
994        return NULL;
995    }
996
997    hr = CoCreateInstance (
998        &CLSID_DirectSoundCapture,
999        NULL,
1000        CLSCTX_ALL,
1001        &IID_IDirectSoundCapture,
1002        (void **) &s->dsound_capture
1003        );
1004    if (FAILED (hr)) {
1005        dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
1006    }
1007    else {
1008        hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
1009        if (FAILED (hr)) {
1010            dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
1011
1012            hr = IDirectSoundCapture_Release (s->dsound_capture);
1013            if (FAILED (hr)) {
1014                dsound_logerr (hr, "Could not release DirectSoundCapture\n");
1015            }
1016            s->dsound_capture = NULL;
1017        }
1018    }
1019
1020    err = dsound_open (s);
1021    if (err) {
1022        dsound_audio_fini (s);
1023        return NULL;
1024    }
1025
1026    return s;
1027}
1028
1029static struct audio_option dsound_options[] = {
1030    {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries,
1031     "Number of times to attempt locking the buffer", NULL, 0},
1032    {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries,
1033     "Number of times to attempt restoring the buffer", NULL, 0},
1034    {"GETSTATUS_RETRIES", AUD_OPT_INT, &conf.getstatus_retries,
1035     "Number of times to attempt getting status of the buffer", NULL, 0},
1036    {"SET_PRIMARY", AUD_OPT_BOOL, &conf.set_primary,
1037     "Set the parameters of primary buffer", NULL, 0},
1038    {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis,
1039     "(undocumented)", NULL, 0},
1040    {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq,
1041     "Primary buffer frequency", NULL, 0},
1042    {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
1043     "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0},
1044    {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt,
1045     "Primary buffer format", NULL, 0},
1046    {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out,
1047     "(undocumented)", NULL, 0},
1048    {"BUFSIZE_IN", AUD_OPT_INT, &conf.bufsize_in,
1049     "(undocumented)", NULL, 0},
1050    {NULL, 0, NULL, NULL, NULL, 0}
1051};
1052
1053static struct audio_pcm_ops dsound_pcm_ops = {
1054    dsound_init_out,
1055    dsound_fini_out,
1056    dsound_run_out,
1057    dsound_write,
1058    dsound_ctl_out,
1059
1060    dsound_init_in,
1061    dsound_fini_in,
1062    dsound_run_in,
1063    dsound_read,
1064    dsound_ctl_in
1065};
1066
1067struct audio_driver dsound_audio_driver = {
1068    INIT_FIELD (name           = ) "dsound",
1069    INIT_FIELD (descr          = )
1070    "DirectSound audio (www.wikipedia.org/wiki/DirectSound)",
1071    INIT_FIELD (options        = ) dsound_options,
1072    INIT_FIELD (init           = ) dsound_audio_init,
1073    INIT_FIELD (fini           = ) dsound_audio_fini,
1074    INIT_FIELD (pcm_ops        = ) &dsound_pcm_ops,
1075    INIT_FIELD (can_be_default = ) 1,
1076    INIT_FIELD (max_voices_out = ) INT_MAX,
1077    INIT_FIELD (max_voices_in  = ) 1,
1078    INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut),
1079    INIT_FIELD (voice_size_in  = ) sizeof (DSoundVoiceIn)
1080};
1081