qemu/audio/dsoundaudio.c
<<
>>
Prefs
   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
  44static 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
  66typedef struct {
  67    LPDIRECTSOUND dsound;
  68    LPDIRECTSOUNDCAPTURE dsound_capture;
  69    LPDIRECTSOUNDBUFFER dsound_primary_buffer;
  70    struct audsettings settings;
  71} dsound;
  72
  73static dsound glob_dsound;
  74
  75typedef 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
  87typedef struct {
  88    HWVoiceIn hw;
  89    int first_time;
  90    LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
  91} DSoundVoiceIn;
  92
  93static 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
 234static 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
 249static 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
 266static 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
 272static 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
 284static 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
 314static 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
 338static 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
 352static 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
 379static 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
 418static 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
 431static 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
 511static 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
 563static int dsound_write (SWVoiceOut *sw, void *buf, int len)
 564{
 565    return audio_pcm_sw_write (sw, buf, len);
 566}
 567
 568static 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
 699static 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
 751static int dsound_read (SWVoiceIn *sw, void *buf, int len)
 752{
 753    return audio_pcm_sw_read (sw, buf, len);
 754}
 755
 756static 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);
 835    }
 836
 837    if (p2 && len2) {
 838        hw->conv (hw->conv_buf, p2, len2);
 839    }
 840
 841    dsound_unlock_in (dscb, p1, p2, blen1, blen2);
 842    hw->wpos = (hw->wpos + decr) % hw->samples;
 843    return decr;
 844}
 845
 846static 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
 872static 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
 940static 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
1004static 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
1018struct 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