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 "audio.h"
     30 
     31 #define AUDIO_CAP "dsound"
     32 #include "audio_int.h"
     33 
     34 #define WIN32_LEAN_AND_MEAN
     35 #include <windows.h>
     36 #include <mmsystem.h>
     37 #include <objbase.h>
     38 #include <dsound.h>
     39 
     40 /* #define DEBUG_DSOUND */
     41 
     42 static struct {
     43     int lock_retries;
     44     int restore_retries;
     45     int getstatus_retries;
     46     int set_primary;
     47     int bufsize_in;
     48     int bufsize_out;
     49     audsettings_t settings;
     50     int latency_millis;
     51 } conf = {
     52     1,
     53     1,
     54     1,
     55     0,
     56     16384,
     57     16384,
     58     {
     59         44100,
     60         2,
     61         AUD_FMT_S16
     62     },
     63     10
     64 };
     65 
     66 typedef struct {
     67     LPDIRECTSOUND dsound;
     68     LPDIRECTSOUNDCAPTURE dsound_capture;
     69     LPDIRECTSOUNDBUFFER dsound_primary_buffer;
     70     audsettings_t 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 static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
    310 {
    311     memset (wfx, 0, sizeof (*wfx));
    312 
    313     wfx->wFormatTag = WAVE_FORMAT_PCM;
    314     wfx->nChannels = as->nchannels;
    315     wfx->nSamplesPerSec = as->freq;
    316     wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
    317     wfx->nBlockAlign = 1 << (as->nchannels == 2);
    318     wfx->cbSize = 0;
    319 
    320     switch (as->fmt) {
    321     case AUD_FMT_S8:
    322     case AUD_FMT_U8:
    323         wfx->wBitsPerSample = 8;
    324         break;
    325 
    326     case AUD_FMT_S16:
    327     case AUD_FMT_U16:
    328         wfx->wBitsPerSample = 16;
    329         wfx->nAvgBytesPerSec <<= 1;
    330         wfx->nBlockAlign <<= 1;
    331         break;
    332 
    333     case AUD_FMT_S32:
    334     case AUD_FMT_U32:
    335         wfx->wBitsPerSample = 32;
    336         wfx->nAvgBytesPerSec <<= 2;
    337         wfx->nBlockAlign <<= 2;
    338         break;
    339 
    340     default:
    341         dolog ("Internal logic error: Bad audio format %d\n", as->freq);
    342         return -1;
    343     }
    344 
    345     return 0;
    346 }
    347 
    348 static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
    349 {
    350     if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
    351         dolog ("Invalid wave format, tag is not PCM, but %d\n",
    352                wfx->wFormatTag);
    353         return -1;
    354     }
    355 
    356     if (!wfx->nSamplesPerSec) {
    357         dolog ("Invalid wave format, frequency is zero\n");
    358         return -1;
    359     }
    360     as->freq = wfx->nSamplesPerSec;
    361 
    362     switch (wfx->nChannels) {
    363     case 1:
    364         as->nchannels = 1;
    365         break;
    366 
    367     case 2:
    368         as->nchannels = 2;
    369         break;
    370 
    371     default:
    372         dolog (
    373             "Invalid wave format, number of channels is not 1 or 2, but %d\n",
    374             wfx->nChannels
    375             );
    376         return -1;
    377     }
    378 
    379     switch (wfx->wBitsPerSample) {
    380     case 8:
    381         as->fmt = AUD_FMT_U8;
    382         break;
    383 
    384     case 16:
    385         as->fmt = AUD_FMT_S16;
    386         break;
    387 
    388     case 32:
    389         as->fmt = AUD_FMT_S32;
    390         break;
    391 
    392     default:
    393         dolog ("Invalid wave format, bits per sample is not "
    394                "8, 16 or 32, but %d\n",
    395                wfx->wBitsPerSample);
    396         return -1;
    397     }
    398 
    399     return 0;
    400 }
    401 
    402 #include "dsound_template.h"
    403 #define DSBTYPE_IN
    404 #include "dsound_template.h"
    405 #undef DSBTYPE_IN
    406 
    407 static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
    408 {
    409     HRESULT hr;
    410     int i;
    411 
    412     for (i = 0; i < conf.getstatus_retries; ++i) {
    413         hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
    414         if (FAILED (hr)) {
    415             dsound_logerr (hr, "Could not get playback buffer status\n");
    416             return -1;
    417         }
    418 
    419         if (*statusp & DSERR_BUFFERLOST) {
    420             if (dsound_restore_out (dsb)) {
    421                 return -1;
    422             }
    423             continue;
    424         }
    425         break;
    426     }
    427 
    428     return 0;
    429 }
    430 
    431 static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
    432                                  DWORD *statusp)
    433 {
    434     HRESULT hr;
    435 
    436     hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
    437     if (FAILED (hr)) {
    438         dsound_logerr (hr, "Could not get capture buffer status\n");
    439         return -1;
    440     }
    441 
    442     return 0;
    443 }
    444 
    445 static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
    446 {
    447     int src_len1 = dst_len;
    448     int src_len2 = 0;
    449     int pos = hw->rpos + dst_len;
    450     st_sample_t *src1 = hw->mix_buf + hw->rpos;
    451     st_sample_t *src2 = NULL;
    452 
    453     if (pos > hw->samples) {
    454         src_len1 = hw->samples - hw->rpos;
    455         src2 = hw->mix_buf;
    456         src_len2 = dst_len - src_len1;
    457         pos = src_len2;
    458     }
    459 
    460     if (src_len1) {
    461         hw->clip (dst, src1, src_len1);
    462     }
    463 
    464     if (src_len2) {
    465         dst = advance (dst, src_len1 << hw->info.shift);
    466         hw->clip (dst, src2, src_len2);
    467     }
    468 
    469     hw->rpos = pos % hw->samples;
    470 }
    471 
    472 static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
    473 {
    474     int err;
    475     LPVOID p1, p2;
    476     DWORD blen1, blen2, len1, len2;
    477 
    478     err = dsound_lock_out (
    479         dsb,
    480         &hw->info,
    481         0,
    482         hw->samples << hw->info.shift,
    483         &p1, &p2,
    484         &blen1, &blen2,
    485         1
    486         );
    487     if (err) {
    488         return;
    489     }
    490 
    491     len1 = blen1 >> hw->info.shift;
    492     len2 = blen2 >> hw->info.shift;
    493 
    494 #ifdef DEBUG_DSOUND
    495     dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
    496            p1, blen1, len1,
    497            p2, blen2, len2);
    498 #endif
    499 
    500     if (p1 && len1) {
    501         audio_pcm_info_clear_buf (&hw->info, p1, len1);
    502     }
    503 
    504     if (p2 && len2) {
    505         audio_pcm_info_clear_buf (&hw->info, p2, len2);
    506     }
    507 
    508     dsound_unlock_out (dsb, p1, p2, blen1, blen2);
    509 }
    510 
    511 static void dsound_close (dsound *s)
    512 {
    513     HRESULT hr;
    514 
    515     if (s->dsound_primary_buffer) {
    516         hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
    517         if (FAILED (hr)) {
    518             dsound_logerr (hr, "Could not release primary buffer\n");
    519         }
    520         s->dsound_primary_buffer = NULL;
    521     }
    522 }
    523 
    524 static int dsound_open (dsound *s)
    525 {
    526     int err;
    527     HRESULT hr;
    528     WAVEFORMATEX wfx;
    529     DSBUFFERDESC dsbd;
    530     HWND hwnd;
    531 
    532     hwnd = GetForegroundWindow ();
    533     hr = IDirectSound_SetCooperativeLevel (
    534         s->dsound,
    535         hwnd,
    536         DSSCL_PRIORITY
    537         );
    538 
    539     if (FAILED (hr)) {
    540         dsound_logerr (hr, "Could not set cooperative level for window %p\n",
    541                        hwnd);
    542         return -1;
    543     }
    544 
    545     if (!conf.set_primary) {
    546         return 0;
    547     }
    548 
    549     err = waveformat_from_audio_settings (&wfx, &conf.settings);
    550     if (err) {
    551         return -1;
    552     }
    553 
    554     memset (&dsbd, 0, sizeof (dsbd));
    555     dsbd.dwSize = sizeof (dsbd);
    556     dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
    557     dsbd.dwBufferBytes = 0;
    558     dsbd.lpwfxFormat = NULL;
    559 
    560     hr = IDirectSound_CreateSoundBuffer (
    561         s->dsound,
    562         &dsbd,
    563         &s->dsound_primary_buffer,
    564         NULL
    565         );
    566     if (FAILED (hr)) {
    567         dsound_logerr (hr, "Could not create primary playback buffer\n");
    568         return -1;
    569     }
    570 
    571     hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
    572     if (FAILED (hr)) {
    573         dsound_logerr (hr, "Could not set primary playback buffer format\n");
    574     }
    575 
    576     hr = IDirectSoundBuffer_GetFormat (
    577         s->dsound_primary_buffer,
    578         &wfx,
    579         sizeof (wfx),
    580         NULL
    581         );
    582     if (FAILED (hr)) {
    583         dsound_logerr (hr, "Could not get primary playback buffer format\n");
    584         goto fail0;
    585     }
    586 
    587 #ifdef DEBUG_DSOUND
    588     dolog ("Primary\n");
    589     print_wave_format (&wfx);
    590 #endif
    591 
    592     err = waveformat_to_audio_settings (&wfx, &s->settings);
    593     if (err) {
    594         goto fail0;
    595     }
    596 
    597     return 0;
    598 
    599  fail0:
    600     dsound_close (s);
    601     return -1;
    602 }
    603 
    604 static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
    605 {
    606     HRESULT hr;
    607     DWORD status;
    608     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
    609     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
    610 
    611     if (!dsb) {
    612         dolog ("Attempt to control voice without a buffer\n");
    613         return 0;
    614     }
    615 
    616     switch (cmd) {
    617     case VOICE_ENABLE:
    618         if (dsound_get_status_out (dsb, &status)) {
    619             return -1;
    620         }
    621 
    622         if (status & DSBSTATUS_PLAYING) {
    623             dolog ("warning: Voice is already playing\n");
    624             return 0;
    625         }
    626 
    627         dsound_clear_sample (hw, dsb);
    628 
    629         hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
    630         if (FAILED (hr)) {
    631             dsound_logerr (hr, "Could not start playing buffer\n");
    632             return -1;
    633         }
    634         break;
    635 
    636     case VOICE_DISABLE:
    637         if (dsound_get_status_out (dsb, &status)) {
    638             return -1;
    639         }
    640 
    641         if (status & DSBSTATUS_PLAYING) {
    642             hr = IDirectSoundBuffer_Stop (dsb);
    643             if (FAILED (hr)) {
    644                 dsound_logerr (hr, "Could not stop playing buffer\n");
    645                 return -1;
    646             }
    647         }
    648         else {
    649             dolog ("warning: Voice is not playing\n");
    650         }
    651         break;
    652     }
    653     return 0;
    654 }
    655 
    656 static int dsound_write (SWVoiceOut *sw, void *buf, int len)
    657 {
    658     return audio_pcm_sw_write (sw, buf, len);
    659 }
    660 
    661 static int dsound_run_out (HWVoiceOut *hw)
    662 {
    663     int err;
    664     HRESULT hr;
    665     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
    666     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
    667     int live, len, hwshift;
    668     DWORD blen1, blen2;
    669     DWORD len1, len2;
    670     DWORD decr;
    671     DWORD wpos, ppos, old_pos;
    672     LPVOID p1, p2;
    673     int bufsize;
    674 
    675     if (!dsb) {
    676         dolog ("Attempt to run empty with playback buffer\n");
    677         return 0;
    678     }
    679 
    680     hwshift = hw->info.shift;
    681     bufsize = hw->samples << hwshift;
    682 
    683     live = audio_pcm_hw_get_live_out (hw);
    684 
    685     hr = IDirectSoundBuffer_GetCurrentPosition (
    686         dsb,
    687         &ppos,
    688         ds->first_time ? &wpos : NULL
    689         );
    690     if (FAILED (hr)) {
    691         dsound_logerr (hr, "Could not get playback buffer position\n");
    692         return 0;
    693     }
    694 
    695     len = live << hwshift;
    696 
    697     if (ds->first_time) {
    698         if (conf.latency_millis) {
    699             DWORD cur_blat;
    700 
    701             cur_blat = audio_ring_dist (wpos, ppos, bufsize);
    702             ds->first_time = 0;
    703             old_pos = wpos;
    704             old_pos +=
    705                 millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
    706             old_pos %= bufsize;
    707             old_pos &= ~hw->info.align;
    708         }
    709         else {
    710             old_pos = wpos;
    711         }
    712 #ifdef DEBUG_DSOUND
    713         ds->played = 0;
    714         ds->mixed = 0;
    715 #endif
    716     }
    717     else {
    718         if (ds->old_pos == ppos) {
    719 #ifdef DEBUG_DSOUND
    720             dolog ("old_pos == ppos\n");
    721 #endif
    722             return 0;
    723         }
    724 
    725 #ifdef DEBUG_DSOUND
    726         ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
    727 #endif
    728         old_pos = ds->old_pos;
    729     }
    730 
    731     if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
    732         len = ppos - old_pos;
    733     }
    734     else {
    735         if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
    736             len = bufsize - old_pos + ppos;
    737         }
    738     }
    739 
    740     if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
    741         dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
    742                len, bufsize, old_pos, ppos);
    743         return 0;
    744     }
    745 
    746     len &= ~hw->info.align;
    747     if (!len) {
    748         return 0;
    749     }
    750 
    751 #ifdef DEBUG_DSOUND
    752     ds->old_ppos = ppos;
    753 #endif
    754     err = dsound_lock_out (
    755         dsb,
    756         &hw->info,
    757         old_pos,
    758         len,
    759         &p1, &p2,
    760         &blen1, &blen2,
    761         0
    762         );
    763     if (err) {
    764         return 0;
    765     }
    766 
    767     len1 = blen1 >> hwshift;
    768     len2 = blen2 >> hwshift;
    769     decr = len1 + len2;
    770 
    771     if (p1 && len1) {
    772         dsound_write_sample (hw, p1, len1);
    773     }
    774 
    775     if (p2 && len2) {
    776         dsound_write_sample (hw, p2, len2);
    777     }
    778 
    779     dsound_unlock_out (dsb, p1, p2, blen1, blen2);
    780     ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
    781 
    782 #ifdef DEBUG_DSOUND
    783     ds->mixed += decr << hwshift;
    784 
    785     dolog ("played %lu mixed %lu diff %ld sec %f\n",
    786            ds->played,
    787            ds->mixed,
    788            ds->mixed - ds->played,
    789            abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
    790 #endif
    791     return decr;
    792 }
    793 
    794 static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
    795 {
    796     HRESULT hr;
    797     DWORD status;
    798     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
    799     LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
    800 
    801     if (!dscb) {
    802         dolog ("Attempt to control capture voice without a buffer\n");
    803         return -1;
    804     }
    805 
    806     switch (cmd) {
    807     case VOICE_ENABLE:
    808         if (dsound_get_status_in (dscb, &status)) {
    809             return -1;
    810         }
    811 
    812         if (status & DSCBSTATUS_CAPTURING) {
    813             dolog ("warning: Voice is already capturing\n");
    814             return 0;
    815         }
    816 
    817         /* clear ?? */
    818 
    819         hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
    820         if (FAILED (hr)) {
    821             dsound_logerr (hr, "Could not start capturing\n");
    822             return -1;
    823         }
    824         break;
    825 
    826     case VOICE_DISABLE:
    827         if (dsound_get_status_in (dscb, &status)) {
    828             return -1;
    829         }
    830 
    831         if (status & DSCBSTATUS_CAPTURING) {
    832             hr = IDirectSoundCaptureBuffer_Stop (dscb);
    833             if (FAILED (hr)) {
    834                 dsound_logerr (hr, "Could not stop capturing\n");
    835                 return -1;
    836             }
    837         }
    838         else {
    839             dolog ("warning: Voice is not capturing\n");
    840         }
    841         break;
    842     }
    843     return 0;
    844 }
    845 
    846 static int dsound_read (SWVoiceIn *sw, void *buf, int len)
    847 {
    848     return audio_pcm_sw_read (sw, buf, len);
    849 }
    850 
    851 static int dsound_run_in (HWVoiceIn *hw)
    852 {
    853     int err;
    854     HRESULT hr;
    855     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
    856     LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
    857     int live, len, dead;
    858     DWORD blen1, blen2;
    859     DWORD len1, len2;
    860     DWORD decr;
    861     DWORD cpos, rpos;
    862     LPVOID p1, p2;
    863     int hwshift;
    864 
    865     if (!dscb) {
    866         dolog ("Attempt to run without capture buffer\n");
    867         return 0;
    868     }
    869 
    870     hwshift = hw->info.shift;
    871 
    872     live = audio_pcm_hw_get_live_in (hw);
    873     dead = hw->samples - live;
    874     if (!dead) {
    875         return 0;
    876     }
    877 
    878     hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
    879         dscb,
    880         &cpos,
    881         ds->first_time ? &rpos : NULL
    882         );
    883     if (FAILED (hr)) {
    884         dsound_logerr (hr, "Could not get capture buffer position\n");
    885         return 0;
    886     }
    887 
    888     if (ds->first_time) {
    889         ds->first_time = 0;
    890         if (rpos & hw->info.align) {
    891             ldebug ("warning: Misaligned capture read position %ld(%d)\n",
    892                     rpos, hw->info.align);
    893         }
    894         hw->wpos = rpos >> hwshift;
    895     }
    896 
    897     if (cpos & hw->info.align) {
    898         ldebug ("warning: Misaligned capture position %ld(%d)\n",
    899                 cpos, hw->info.align);
    900     }
    901     cpos >>= hwshift;
    902 
    903     len = audio_ring_dist (cpos, hw->wpos, hw->samples);
    904     if (!len) {
    905         return 0;
    906     }
    907     len = audio_MIN (len, dead);
    908 
    909     err = dsound_lock_in (
    910         dscb,
    911         &hw->info,
    912         hw->wpos << hwshift,
    913         len << hwshift,
    914         &p1,
    915         &p2,
    916         &blen1,
    917         &blen2,
    918         0
    919         );
    920     if (err) {
    921         return 0;
    922     }
    923 
    924     len1 = blen1 >> hwshift;
    925     len2 = blen2 >> hwshift;
    926     decr = len1 + len2;
    927 
    928     if (p1 && len1) {
    929         hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
    930     }
    931 
    932     if (p2 && len2) {
    933         hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
    934     }
    935 
    936     dsound_unlock_in (dscb, p1, p2, blen1, blen2);
    937     hw->wpos = (hw->wpos + decr) % hw->samples;
    938     return decr;
    939 }
    940 
    941 static void dsound_audio_fini (void *opaque)
    942 {
    943     HRESULT hr;
    944     dsound *s = opaque;
    945 
    946     if (!s->dsound) {
    947         return;
    948     }
    949 
    950     hr = IDirectSound_Release (s->dsound);
    951     if (FAILED (hr)) {
    952         dsound_logerr (hr, "Could not release DirectSound\n");
    953     }
    954     s->dsound = NULL;
    955 
    956     if (!s->dsound_capture) {
    957         return;
    958     }
    959 
    960     hr = IDirectSoundCapture_Release (s->dsound_capture);
    961     if (FAILED (hr)) {
    962         dsound_logerr (hr, "Could not release DirectSoundCapture\n");
    963     }
    964     s->dsound_capture = NULL;
    965 }
    966 
    967 static void *dsound_audio_init (void)
    968 {
    969     int err;
    970     HRESULT hr;
    971     dsound *s = &glob_dsound;
    972 
    973     hr = CoInitialize (NULL);
    974     if (FAILED (hr)) {
    975         dsound_logerr (hr, "Could not initialize COM\n");
    976         return NULL;
    977     }
    978 
    979     hr = CoCreateInstance (
    980         &CLSID_DirectSound,
    981         NULL,
    982         CLSCTX_ALL,
    983         &IID_IDirectSound,
    984         (void **) &s->dsound
    985         );
    986     if (FAILED (hr)) {
    987         dsound_logerr (hr, "Could not create DirectSound instance\n");
    988         return NULL;
    989     }
    990 
    991     hr = IDirectSound_Initialize (s->dsound, NULL);
    992     if (FAILED (hr)) {
    993         dsound_logerr (hr, "Could not initialize DirectSound\n");
    994 
    995         hr = IDirectSound_Release (s->dsound);
    996         if (FAILED (hr)) {
    997             dsound_logerr (hr, "Could not release DirectSound\n");
    998         }
    999         s->dsound = NULL;
   1000         return NULL;
   1001     }
   1002 
   1003     hr = CoCreateInstance (
   1004         &CLSID_DirectSoundCapture,
   1005         NULL,
   1006         CLSCTX_ALL,
   1007         &IID_IDirectSoundCapture,
   1008         (void **) &s->dsound_capture
   1009         );
   1010     if (FAILED (hr)) {
   1011         dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
   1012     }
   1013     else {
   1014         hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
   1015         if (FAILED (hr)) {
   1016             dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
   1017 
   1018             hr = IDirectSoundCapture_Release (s->dsound_capture);
   1019             if (FAILED (hr)) {
   1020                 dsound_logerr (hr, "Could not release DirectSoundCapture\n");
   1021             }
   1022             s->dsound_capture = NULL;
   1023         }
   1024     }
   1025 
   1026     err = dsound_open (s);
   1027     if (err) {
   1028         dsound_audio_fini (s);
   1029         return NULL;
   1030     }
   1031 
   1032     return s;
   1033 }
   1034 
   1035 static struct audio_option dsound_options[] = {
   1036     {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries,
   1037      "Number of times to attempt locking the buffer", NULL, 0},
   1038     {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries,
   1039      "Number of times to attempt restoring the buffer", NULL, 0},
   1040     {"GETSTATUS_RETRIES", AUD_OPT_INT, &conf.getstatus_retries,
   1041      "Number of times to attempt getting status of the buffer", NULL, 0},
   1042     {"SET_PRIMARY", AUD_OPT_BOOL, &conf.set_primary,
   1043      "Set the parameters of primary buffer", NULL, 0},
   1044     {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis,
   1045      "(undocumented)", NULL, 0},
   1046     {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq,
   1047      "Primary buffer frequency", NULL, 0},
   1048     {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
   1049      "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0},
   1050     {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt,
   1051      "Primary buffer format", NULL, 0},
   1052     {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out,
   1053      "(undocumented)", NULL, 0},
   1054     {"BUFSIZE_IN", AUD_OPT_INT, &conf.bufsize_in,
   1055      "(undocumented)", NULL, 0},
   1056     {NULL, 0, NULL, NULL, NULL, 0}
   1057 };
   1058 
   1059 static struct audio_pcm_ops dsound_pcm_ops = {
   1060     dsound_init_out,
   1061     dsound_fini_out,
   1062     dsound_run_out,
   1063     dsound_write,
   1064     dsound_ctl_out,
   1065 
   1066     dsound_init_in,
   1067     dsound_fini_in,
   1068     dsound_run_in,
   1069     dsound_read,
   1070     dsound_ctl_in
   1071 };
   1072 
   1073 struct audio_driver dsound_audio_driver = {
   1074     INIT_FIELD (name           = ) "dsound",
   1075     INIT_FIELD (descr          = )
   1076     "DirectSound audio (www.wikipedia.org/wiki/DirectSound)",
   1077     INIT_FIELD (options        = ) dsound_options,
   1078     INIT_FIELD (init           = ) dsound_audio_init,
   1079     INIT_FIELD (fini           = ) dsound_audio_fini,
   1080     INIT_FIELD (pcm_ops        = ) &dsound_pcm_ops,
   1081     INIT_FIELD (can_be_default = ) 1,
   1082     INIT_FIELD (max_voices_out = ) INT_MAX,
   1083     INIT_FIELD (max_voices_in  = ) 1,
   1084     INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut),
   1085     INIT_FIELD (voice_size_in  = ) sizeof (DSoundVoiceIn)
   1086 };
   1087