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