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    bool first_time;
  57    dsound *s;
  58} DSoundVoiceOut;
  59
  60typedef struct {
  61    HWVoiceIn hw;
  62    LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
  63    bool first_time;
  64    dsound *s;
  65} DSoundVoiceIn;
  66
  67static void dsound_log_hresult (HRESULT hr)
  68{
  69    const char *str = "BUG";
  70
  71    switch (hr) {
  72    case DS_OK:
  73        str = "The method succeeded";
  74        break;
  75#ifdef DS_NO_VIRTUALIZATION
  76    case DS_NO_VIRTUALIZATION:
  77        str = "The buffer was created, but another 3D algorithm was substituted";
  78        break;
  79#endif
  80#ifdef DS_INCOMPLETE
  81    case DS_INCOMPLETE:
  82        str = "The method succeeded, but not all the optional effects were obtained";
  83        break;
  84#endif
  85#ifdef DSERR_ACCESSDENIED
  86    case DSERR_ACCESSDENIED:
  87        str = "The request failed because access was denied";
  88        break;
  89#endif
  90#ifdef DSERR_ALLOCATED
  91    case DSERR_ALLOCATED:
  92        str = "The request failed because resources, "
  93              "such as a priority level, were already in use "
  94              "by another caller";
  95        break;
  96#endif
  97#ifdef DSERR_ALREADYINITIALIZED
  98    case DSERR_ALREADYINITIALIZED:
  99        str = "The object is already initialized";
 100        break;
 101#endif
 102#ifdef DSERR_BADFORMAT
 103    case DSERR_BADFORMAT:
 104        str = "The specified wave format is not supported";
 105        break;
 106#endif
 107#ifdef DSERR_BADSENDBUFFERGUID
 108    case DSERR_BADSENDBUFFERGUID:
 109        str = "The GUID specified in an audiopath file "
 110              "does not match a valid mix-in buffer";
 111        break;
 112#endif
 113#ifdef DSERR_BUFFERLOST
 114    case DSERR_BUFFERLOST:
 115        str = "The buffer memory has been lost and must be restored";
 116        break;
 117#endif
 118#ifdef DSERR_BUFFERTOOSMALL
 119    case DSERR_BUFFERTOOSMALL:
 120        str = "The buffer size is not great enough to "
 121              "enable effects processing";
 122        break;
 123#endif
 124#ifdef DSERR_CONTROLUNAVAIL
 125    case DSERR_CONTROLUNAVAIL:
 126        str = "The buffer control (volume, pan, and so on) "
 127              "requested by the caller is not available. "
 128              "Controls must be specified when the buffer is created, "
 129              "using the dwFlags member of DSBUFFERDESC";
 130        break;
 131#endif
 132#ifdef DSERR_DS8_REQUIRED
 133    case DSERR_DS8_REQUIRED:
 134        str = "A DirectSound object of class CLSID_DirectSound8 or later "
 135              "is required for the requested functionality. "
 136              "For more information, see IDirectSound8 Interface";
 137        break;
 138#endif
 139#ifdef DSERR_FXUNAVAILABLE
 140    case DSERR_FXUNAVAILABLE:
 141        str = "The effects requested could not be found on the system, "
 142              "or they are in the wrong order or in the wrong location; "
 143              "for example, an effect expected in hardware "
 144              "was found in software";
 145        break;
 146#endif
 147#ifdef DSERR_GENERIC
 148    case DSERR_GENERIC:
 149        str = "An undetermined error occurred inside the DirectSound subsystem";
 150        break;
 151#endif
 152#ifdef DSERR_INVALIDCALL
 153    case DSERR_INVALIDCALL:
 154        str = "This function is not valid for the current state of this object";
 155        break;
 156#endif
 157#ifdef DSERR_INVALIDPARAM
 158    case DSERR_INVALIDPARAM:
 159        str = "An invalid parameter was passed to the returning function";
 160        break;
 161#endif
 162#ifdef DSERR_NOAGGREGATION
 163    case DSERR_NOAGGREGATION:
 164        str = "The object does not support aggregation";
 165        break;
 166#endif
 167#ifdef DSERR_NODRIVER
 168    case DSERR_NODRIVER:
 169        str = "No sound driver is available for use, "
 170              "or the given GUID is not a valid DirectSound device ID";
 171        break;
 172#endif
 173#ifdef DSERR_NOINTERFACE
 174    case DSERR_NOINTERFACE:
 175        str = "The requested COM interface is not available";
 176        break;
 177#endif
 178#ifdef DSERR_OBJECTNOTFOUND
 179    case DSERR_OBJECTNOTFOUND:
 180        str = "The requested object was not found";
 181        break;
 182#endif
 183#ifdef DSERR_OTHERAPPHASPRIO
 184    case DSERR_OTHERAPPHASPRIO:
 185        str = "Another application has a higher priority level, "
 186              "preventing this call from succeeding";
 187        break;
 188#endif
 189#ifdef DSERR_OUTOFMEMORY
 190    case DSERR_OUTOFMEMORY:
 191        str = "The DirectSound subsystem could not allocate "
 192               "sufficient memory to complete the caller's request";
 193        break;
 194#endif
 195#ifdef DSERR_PRIOLEVELNEEDED
 196    case DSERR_PRIOLEVELNEEDED:
 197        str = "A cooperative level of DSSCL_PRIORITY or higher is required";
 198        break;
 199#endif
 200#ifdef DSERR_SENDLOOP
 201    case DSERR_SENDLOOP:
 202        str = "A circular loop of send effects was detected";
 203        break;
 204#endif
 205#ifdef DSERR_UNINITIALIZED
 206    case DSERR_UNINITIALIZED:
 207        str = "The Initialize method has not been called "
 208              "or has not been called successfully "
 209              "before other methods were called";
 210        break;
 211#endif
 212#ifdef DSERR_UNSUPPORTED
 213    case DSERR_UNSUPPORTED:
 214        str = "The function called is not supported at this time";
 215        break;
 216#endif
 217    default:
 218        AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT 0x%lx)\n", hr);
 219        return;
 220    }
 221
 222    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
 223}
 224
 225static void G_GNUC_PRINTF (2, 3) dsound_logerr (
 226    HRESULT hr,
 227    const char *fmt,
 228    ...
 229    )
 230{
 231    va_list ap;
 232
 233    va_start (ap, fmt);
 234    AUD_vlog (AUDIO_CAP, fmt, ap);
 235    va_end (ap);
 236
 237    dsound_log_hresult (hr);
 238}
 239
 240static void G_GNUC_PRINTF (3, 4) dsound_logerr2 (
 241    HRESULT hr,
 242    const char *typ,
 243    const char *fmt,
 244    ...
 245    )
 246{
 247    va_list ap;
 248
 249    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
 250    va_start (ap, fmt);
 251    AUD_vlog (AUDIO_CAP, fmt, ap);
 252    va_end (ap);
 253
 254    dsound_log_hresult (hr);
 255}
 256
 257#ifdef DEBUG_DSOUND
 258static void print_wave_format (WAVEFORMATEX *wfx)
 259{
 260    dolog ("tag             = %d\n", wfx->wFormatTag);
 261    dolog ("nChannels       = %d\n", wfx->nChannels);
 262    dolog ("nSamplesPerSec  = %ld\n", wfx->nSamplesPerSec);
 263    dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec);
 264    dolog ("nBlockAlign     = %d\n", wfx->nBlockAlign);
 265    dolog ("wBitsPerSample  = %d\n", wfx->wBitsPerSample);
 266    dolog ("cbSize          = %d\n", wfx->cbSize);
 267}
 268#endif
 269
 270static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s)
 271{
 272    HRESULT hr;
 273
 274    hr = IDirectSoundBuffer_Restore (dsb);
 275
 276    if (hr != DS_OK) {
 277        dsound_logerr (hr, "Could not restore playback buffer\n");
 278        return -1;
 279    }
 280    return 0;
 281}
 282
 283#include "dsound_template.h"
 284#define DSBTYPE_IN
 285#include "dsound_template.h"
 286#undef DSBTYPE_IN
 287
 288static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
 289                                  dsound *s)
 290{
 291    HRESULT hr;
 292
 293    hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
 294    if (FAILED (hr)) {
 295        dsound_logerr (hr, "Could not get playback buffer status\n");
 296        return -1;
 297    }
 298
 299    if (*statusp & DSBSTATUS_BUFFERLOST) {
 300        dsound_restore_out(dsb, s);
 301        return -1;
 302    }
 303
 304    return 0;
 305}
 306
 307static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
 308                                 DWORD *statusp)
 309{
 310    HRESULT hr;
 311
 312    hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
 313    if (FAILED (hr)) {
 314        dsound_logerr (hr, "Could not get capture buffer status\n");
 315        return -1;
 316    }
 317
 318    return 0;
 319}
 320
 321static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
 322                                 dsound *s)
 323{
 324    int err;
 325    LPVOID p1, p2;
 326    DWORD blen1, blen2, len1, len2;
 327
 328    err = dsound_lock_out (
 329        dsb,
 330        &hw->info,
 331        0,
 332        hw->size_emul,
 333        &p1, &p2,
 334        &blen1, &blen2,
 335        1,
 336        s
 337        );
 338    if (err) {
 339        return;
 340    }
 341
 342    len1 = blen1 / hw->info.bytes_per_frame;
 343    len2 = blen2 / hw->info.bytes_per_frame;
 344
 345#ifdef DEBUG_DSOUND
 346    dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
 347           p1, blen1, len1,
 348           p2, blen2, len2);
 349#endif
 350
 351    if (p1 && len1) {
 352        audio_pcm_info_clear_buf (&hw->info, p1, len1);
 353    }
 354
 355    if (p2 && len2) {
 356        audio_pcm_info_clear_buf (&hw->info, p2, len2);
 357    }
 358
 359    dsound_unlock_out (dsb, p1, p2, blen1, blen2);
 360}
 361
 362static int dsound_set_cooperative_level(dsound *s)
 363{
 364    HRESULT hr;
 365    HWND hwnd;
 366
 367    hwnd = GetDesktopWindow();
 368    hr = IDirectSound_SetCooperativeLevel (
 369        s->dsound,
 370        hwnd,
 371        DSSCL_PRIORITY
 372        );
 373
 374    if (FAILED (hr)) {
 375        dsound_logerr (hr, "Could not set cooperative level for window %p\n",
 376                       hwnd);
 377        return -1;
 378    }
 379
 380    return 0;
 381}
 382
 383static void dsound_enable_out(HWVoiceOut *hw, bool enable)
 384{
 385    HRESULT hr;
 386    DWORD status;
 387    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
 388    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
 389    dsound *s = ds->s;
 390
 391    if (!dsb) {
 392        dolog ("Attempt to control voice without a buffer\n");
 393        return;
 394    }
 395
 396    if (enable) {
 397        if (dsound_get_status_out (dsb, &status, s)) {
 398            return;
 399        }
 400
 401        if (status & DSBSTATUS_PLAYING) {
 402            dolog ("warning: Voice is already playing\n");
 403            return;
 404        }
 405
 406        dsound_clear_sample (hw, dsb, s);
 407
 408        hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
 409        if (FAILED (hr)) {
 410            dsound_logerr (hr, "Could not start playing buffer\n");
 411            return;
 412        }
 413    } else {
 414        if (dsound_get_status_out (dsb, &status, s)) {
 415            return;
 416        }
 417
 418        if (status & DSBSTATUS_PLAYING) {
 419            hr = IDirectSoundBuffer_Stop (dsb);
 420            if (FAILED (hr)) {
 421                dsound_logerr (hr, "Could not stop playing buffer\n");
 422                return;
 423            }
 424        } else {
 425            dolog ("warning: Voice is not playing\n");
 426        }
 427    }
 428}
 429
 430static size_t dsound_buffer_get_free(HWVoiceOut *hw)
 431{
 432    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
 433    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
 434    HRESULT hr;
 435    DWORD ppos, wpos;
 436
 437    hr = IDirectSoundBuffer_GetCurrentPosition(
 438        dsb, &ppos, ds->first_time ? &wpos : NULL);
 439    if (FAILED(hr)) {
 440        dsound_logerr(hr, "Could not get playback buffer position\n");
 441        return 0;
 442    }
 443
 444    if (ds->first_time) {
 445        hw->pos_emul = wpos;
 446        ds->first_time = false;
 447    }
 448
 449    return audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
 450}
 451
 452static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
 453{
 454    DSoundVoiceOut *ds = (DSoundVoiceOut *)hw;
 455    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
 456    DWORD act_size;
 457    size_t req_size;
 458    int err;
 459    void *ret;
 460
 461    req_size = MIN(*size, hw->size_emul - hw->pos_emul);
 462    assert(req_size > 0);
 463
 464    err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
 465                          &act_size, NULL, false, ds->s);
 466    if (err) {
 467        dolog("Failed to lock buffer\n");
 468        *size = 0;
 469        return NULL;
 470    }
 471
 472    *size = act_size;
 473    return ret;
 474}
 475
 476static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
 477{
 478    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
 479    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
 480    int err = dsound_unlock_out(dsb, buf, NULL, len, 0);
 481
 482    if (err) {
 483        dolog("Failed to unlock buffer!!\n");
 484        return 0;
 485    }
 486    hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
 487
 488    return len;
 489}
 490
 491static void dsound_enable_in(HWVoiceIn *hw, bool enable)
 492{
 493    HRESULT hr;
 494    DWORD status;
 495    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
 496    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
 497
 498    if (!dscb) {
 499        dolog ("Attempt to control capture voice without a buffer\n");
 500        return;
 501    }
 502
 503    if (enable) {
 504        if (dsound_get_status_in (dscb, &status)) {
 505            return;
 506        }
 507
 508        if (status & DSCBSTATUS_CAPTURING) {
 509            dolog ("warning: Voice is already capturing\n");
 510            return;
 511        }
 512
 513        /* clear ?? */
 514
 515        hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
 516        if (FAILED (hr)) {
 517            dsound_logerr (hr, "Could not start capturing\n");
 518            return;
 519        }
 520    } else {
 521        if (dsound_get_status_in (dscb, &status)) {
 522            return;
 523        }
 524
 525        if (status & DSCBSTATUS_CAPTURING) {
 526            hr = IDirectSoundCaptureBuffer_Stop (dscb);
 527            if (FAILED (hr)) {
 528                dsound_logerr (hr, "Could not stop capturing\n");
 529                return;
 530            }
 531        } else {
 532            dolog ("warning: Voice is not capturing\n");
 533        }
 534    }
 535}
 536
 537static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
 538{
 539    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
 540    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
 541    HRESULT hr;
 542    DWORD rpos, act_size;
 543    size_t req_size;
 544    int err;
 545    void *ret;
 546
 547    hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, NULL, &rpos);
 548    if (FAILED(hr)) {
 549        dsound_logerr(hr, "Could not get capture buffer position\n");
 550        *size = 0;
 551        return NULL;
 552    }
 553
 554    if (ds->first_time) {
 555        hw->pos_emul = rpos;
 556        ds->first_time = false;
 557    }
 558
 559    req_size = audio_ring_dist(rpos, hw->pos_emul, hw->size_emul);
 560    req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul));
 561
 562    if (req_size == 0) {
 563        *size = 0;
 564        return NULL;
 565    }
 566
 567    err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
 568                         &act_size, NULL, false, ds->s);
 569    if (err) {
 570        dolog("Failed to lock buffer\n");
 571        *size = 0;
 572        return NULL;
 573    }
 574
 575    *size = act_size;
 576    return ret;
 577}
 578
 579static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len)
 580{
 581    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
 582    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
 583    int err = dsound_unlock_in(dscb, buf, NULL, len, 0);
 584
 585    if (err) {
 586        dolog("Failed to unlock buffer!!\n");
 587        return;
 588    }
 589    hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
 590}
 591
 592static void dsound_audio_fini (void *opaque)
 593{
 594    HRESULT hr;
 595    dsound *s = opaque;
 596
 597    if (!s->dsound) {
 598        g_free(s);
 599        return;
 600    }
 601
 602    hr = IDirectSound_Release (s->dsound);
 603    if (FAILED (hr)) {
 604        dsound_logerr (hr, "Could not release DirectSound\n");
 605    }
 606    s->dsound = NULL;
 607
 608    if (!s->dsound_capture) {
 609        g_free(s);
 610        return;
 611    }
 612
 613    hr = IDirectSoundCapture_Release (s->dsound_capture);
 614    if (FAILED (hr)) {
 615        dsound_logerr (hr, "Could not release DirectSoundCapture\n");
 616    }
 617    s->dsound_capture = NULL;
 618
 619    g_free(s);
 620}
 621
 622static void *dsound_audio_init(Audiodev *dev)
 623{
 624    int err;
 625    HRESULT hr;
 626    dsound *s = g_new0(dsound, 1);
 627    AudiodevDsoundOptions *dso;
 628
 629    assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
 630    s->dev = dev;
 631    dso = &dev->u.dsound;
 632
 633    if (!dso->has_latency) {
 634        dso->has_latency = true;
 635        dso->latency = 10000; /* 10 ms */
 636    }
 637
 638    hr = CoInitialize (NULL);
 639    if (FAILED (hr)) {
 640        dsound_logerr (hr, "Could not initialize COM\n");
 641        g_free(s);
 642        return NULL;
 643    }
 644
 645    hr = CoCreateInstance (
 646        &CLSID_DirectSound,
 647        NULL,
 648        CLSCTX_ALL,
 649        &IID_IDirectSound,
 650        (void **) &s->dsound
 651        );
 652    if (FAILED (hr)) {
 653        dsound_logerr (hr, "Could not create DirectSound instance\n");
 654        g_free(s);
 655        return NULL;
 656    }
 657
 658    hr = IDirectSound_Initialize (s->dsound, NULL);
 659    if (FAILED (hr)) {
 660        dsound_logerr (hr, "Could not initialize DirectSound\n");
 661
 662        hr = IDirectSound_Release (s->dsound);
 663        if (FAILED (hr)) {
 664            dsound_logerr (hr, "Could not release DirectSound\n");
 665        }
 666        g_free(s);
 667        return NULL;
 668    }
 669
 670    hr = CoCreateInstance (
 671        &CLSID_DirectSoundCapture,
 672        NULL,
 673        CLSCTX_ALL,
 674        &IID_IDirectSoundCapture,
 675        (void **) &s->dsound_capture
 676        );
 677    if (FAILED (hr)) {
 678        dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
 679    } else {
 680        hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
 681        if (FAILED (hr)) {
 682            dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
 683
 684            hr = IDirectSoundCapture_Release (s->dsound_capture);
 685            if (FAILED (hr)) {
 686                dsound_logerr (hr, "Could not release DirectSoundCapture\n");
 687            }
 688            s->dsound_capture = NULL;
 689        }
 690    }
 691
 692    err = dsound_set_cooperative_level(s);
 693    if (err) {
 694        dsound_audio_fini (s);
 695        return NULL;
 696    }
 697
 698    return s;
 699}
 700
 701static struct audio_pcm_ops dsound_pcm_ops = {
 702    .init_out = dsound_init_out,
 703    .fini_out = dsound_fini_out,
 704    .write    = audio_generic_write,
 705    .buffer_get_free = dsound_buffer_get_free,
 706    .get_buffer_out = dsound_get_buffer_out,
 707    .put_buffer_out = dsound_put_buffer_out,
 708    .enable_out = dsound_enable_out,
 709
 710    .init_in  = dsound_init_in,
 711    .fini_in  = dsound_fini_in,
 712    .read     = audio_generic_read,
 713    .get_buffer_in = dsound_get_buffer_in,
 714    .put_buffer_in = dsound_put_buffer_in,
 715    .enable_in = dsound_enable_in,
 716};
 717
 718static struct audio_driver dsound_audio_driver = {
 719    .name           = "dsound",
 720    .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound",
 721    .init           = dsound_audio_init,
 722    .fini           = dsound_audio_fini,
 723    .pcm_ops        = &dsound_pcm_ops,
 724    .can_be_default = 1,
 725    .max_voices_out = INT_MAX,
 726    .max_voices_in  = 1,
 727    .voice_size_out = sizeof (DSoundVoiceOut),
 728    .voice_size_in  = sizeof (DSoundVoiceIn)
 729};
 730
 731static void register_audio_dsound(void)
 732{
 733    audio_driver_register(&dsound_audio_driver);
 734}
 735type_init(register_audio_dsound);
 736