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