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 GCC_FMT_ATTR (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 GCC_FMT_ATTR (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 void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
 431{
 432    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
 433    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
 434    HRESULT hr;
 435    DWORD ppos, wpos, act_size;
 436    size_t req_size;
 437    int err;
 438    void *ret;
 439
 440    hr = IDirectSoundBuffer_GetCurrentPosition(
 441        dsb, &ppos, ds->first_time ? &wpos : NULL);
 442    if (FAILED(hr)) {
 443        dsound_logerr(hr, "Could not get playback buffer position\n");
 444        *size = 0;
 445        return NULL;
 446    }
 447
 448    if (ds->first_time) {
 449        hw->pos_emul = wpos;
 450        ds->first_time = false;
 451    }
 452
 453    req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
 454    req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
 455
 456    if (req_size == 0) {
 457        *size = 0;
 458        return NULL;
 459    }
 460
 461    err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
 462                          &act_size, NULL, false, ds->s);
 463    if (err) {
 464        dolog("Failed to lock buffer\n");
 465        *size = 0;
 466        return NULL;
 467    }
 468
 469    *size = act_size;
 470    return ret;
 471}
 472
 473static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
 474{
 475    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
 476    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
 477    int err = dsound_unlock_out(dsb, buf, NULL, len, 0);
 478
 479    if (err) {
 480        dolog("Failed to unlock buffer!!\n");
 481        return 0;
 482    }
 483    hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
 484
 485    return len;
 486}
 487
 488static void dsound_enable_in(HWVoiceIn *hw, bool enable)
 489{
 490    HRESULT hr;
 491    DWORD status;
 492    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
 493    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
 494
 495    if (!dscb) {
 496        dolog ("Attempt to control capture voice without a buffer\n");
 497        return;
 498    }
 499
 500    if (enable) {
 501        if (dsound_get_status_in (dscb, &status)) {
 502            return;
 503        }
 504
 505        if (status & DSCBSTATUS_CAPTURING) {
 506            dolog ("warning: Voice is already capturing\n");
 507            return;
 508        }
 509
 510        /* clear ?? */
 511
 512        hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
 513        if (FAILED (hr)) {
 514            dsound_logerr (hr, "Could not start capturing\n");
 515            return;
 516        }
 517    } else {
 518        if (dsound_get_status_in (dscb, &status)) {
 519            return;
 520        }
 521
 522        if (status & DSCBSTATUS_CAPTURING) {
 523            hr = IDirectSoundCaptureBuffer_Stop (dscb);
 524            if (FAILED (hr)) {
 525                dsound_logerr (hr, "Could not stop capturing\n");
 526                return;
 527            }
 528        } else {
 529            dolog ("warning: Voice is not capturing\n");
 530        }
 531    }
 532}
 533
 534static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
 535{
 536    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
 537    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
 538    HRESULT hr;
 539    DWORD cpos, rpos, act_size;
 540    size_t req_size;
 541    int err;
 542    void *ret;
 543
 544    hr = IDirectSoundCaptureBuffer_GetCurrentPosition(
 545        dscb, &cpos, ds->first_time ? &rpos : NULL);
 546    if (FAILED(hr)) {
 547        dsound_logerr(hr, "Could not get capture buffer position\n");
 548        *size = 0;
 549        return NULL;
 550    }
 551
 552    if (ds->first_time) {
 553        hw->pos_emul = rpos;
 554        ds->first_time = false;
 555    }
 556
 557    req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul);
 558    req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul));
 559
 560    if (req_size == 0) {
 561        *size = 0;
 562        return NULL;
 563    }
 564
 565    err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
 566                         &act_size, NULL, false, ds->s);
 567    if (err) {
 568        dolog("Failed to lock buffer\n");
 569        *size = 0;
 570        return NULL;
 571    }
 572
 573    *size = act_size;
 574    return ret;
 575}
 576
 577static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len)
 578{
 579    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
 580    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
 581    int err = dsound_unlock_in(dscb, buf, NULL, len, 0);
 582
 583    if (err) {
 584        dolog("Failed to unlock buffer!!\n");
 585        return;
 586    }
 587    hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
 588}
 589
 590static void dsound_audio_fini (void *opaque)
 591{
 592    HRESULT hr;
 593    dsound *s = opaque;
 594
 595    if (!s->dsound) {
 596        g_free(s);
 597        return;
 598    }
 599
 600    hr = IDirectSound_Release (s->dsound);
 601    if (FAILED (hr)) {
 602        dsound_logerr (hr, "Could not release DirectSound\n");
 603    }
 604    s->dsound = NULL;
 605
 606    if (!s->dsound_capture) {
 607        g_free(s);
 608        return;
 609    }
 610
 611    hr = IDirectSoundCapture_Release (s->dsound_capture);
 612    if (FAILED (hr)) {
 613        dsound_logerr (hr, "Could not release DirectSoundCapture\n");
 614    }
 615    s->dsound_capture = NULL;
 616
 617    g_free(s);
 618}
 619
 620static void *dsound_audio_init(Audiodev *dev)
 621{
 622    int err;
 623    HRESULT hr;
 624    dsound *s = g_malloc0(sizeof(dsound));
 625    AudiodevDsoundOptions *dso;
 626
 627    assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
 628    s->dev = dev;
 629    dso = &dev->u.dsound;
 630
 631    if (!dso->has_latency) {
 632        dso->has_latency = true;
 633        dso->latency = 10000; /* 10 ms */
 634    }
 635
 636    hr = CoInitialize (NULL);
 637    if (FAILED (hr)) {
 638        dsound_logerr (hr, "Could not initialize COM\n");
 639        g_free(s);
 640        return NULL;
 641    }
 642
 643    hr = CoCreateInstance (
 644        &CLSID_DirectSound,
 645        NULL,
 646        CLSCTX_ALL,
 647        &IID_IDirectSound,
 648        (void **) &s->dsound
 649        );
 650    if (FAILED (hr)) {
 651        dsound_logerr (hr, "Could not create DirectSound instance\n");
 652        g_free(s);
 653        return NULL;
 654    }
 655
 656    hr = IDirectSound_Initialize (s->dsound, NULL);
 657    if (FAILED (hr)) {
 658        dsound_logerr (hr, "Could not initialize DirectSound\n");
 659
 660        hr = IDirectSound_Release (s->dsound);
 661        if (FAILED (hr)) {
 662            dsound_logerr (hr, "Could not release DirectSound\n");
 663        }
 664        g_free(s);
 665        return NULL;
 666    }
 667
 668    hr = CoCreateInstance (
 669        &CLSID_DirectSoundCapture,
 670        NULL,
 671        CLSCTX_ALL,
 672        &IID_IDirectSoundCapture,
 673        (void **) &s->dsound_capture
 674        );
 675    if (FAILED (hr)) {
 676        dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
 677    } else {
 678        hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
 679        if (FAILED (hr)) {
 680            dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
 681
 682            hr = IDirectSoundCapture_Release (s->dsound_capture);
 683            if (FAILED (hr)) {
 684                dsound_logerr (hr, "Could not release DirectSoundCapture\n");
 685            }
 686            s->dsound_capture = NULL;
 687        }
 688    }
 689
 690    err = dsound_set_cooperative_level(s);
 691    if (err) {
 692        dsound_audio_fini (s);
 693        return NULL;
 694    }
 695
 696    return s;
 697}
 698
 699static struct audio_pcm_ops dsound_pcm_ops = {
 700    .init_out = dsound_init_out,
 701    .fini_out = dsound_fini_out,
 702    .write    = audio_generic_write,
 703    .get_buffer_out = dsound_get_buffer_out,
 704    .put_buffer_out = dsound_put_buffer_out,
 705    .enable_out = dsound_enable_out,
 706
 707    .init_in  = dsound_init_in,
 708    .fini_in  = dsound_fini_in,
 709    .read     = audio_generic_read,
 710    .get_buffer_in = dsound_get_buffer_in,
 711    .put_buffer_in = dsound_put_buffer_in,
 712    .enable_in = dsound_enable_in,
 713};
 714
 715static struct audio_driver dsound_audio_driver = {
 716    .name           = "dsound",
 717    .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound",
 718    .init           = dsound_audio_init,
 719    .fini           = dsound_audio_fini,
 720    .pcm_ops        = &dsound_pcm_ops,
 721    .can_be_default = 1,
 722    .max_voices_out = INT_MAX,
 723    .max_voices_in  = 1,
 724    .voice_size_out = sizeof (DSoundVoiceOut),
 725    .voice_size_in  = sizeof (DSoundVoiceIn)
 726};
 727
 728static void register_audio_dsound(void)
 729{
 730    audio_driver_register(&dsound_audio_driver);
 731}
 732type_init(register_audio_dsound);
 733