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