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