qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

dsoundaudio.c (19322B)


      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 
     46 typedef struct {
     47     LPDIRECTSOUND dsound;
     48     LPDIRECTSOUNDCAPTURE dsound_capture;
     49     struct audsettings settings;
     50     Audiodev *dev;
     51 } dsound;
     52 
     53 typedef struct {
     54     HWVoiceOut hw;
     55     LPDIRECTSOUNDBUFFER dsound_buffer;
     56     bool first_time;
     57     dsound *s;
     58 } DSoundVoiceOut;
     59 
     60 typedef struct {
     61     HWVoiceIn hw;
     62     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
     63     bool first_time;
     64     dsound *s;
     65 } DSoundVoiceIn;
     66 
     67 static 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 
    225 static 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 
    240 static 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
    258 static 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 
    270 static 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 
    288 static 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 
    307 static 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 
    321 static 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 
    362 static 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 
    383 static 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 
    430 static 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 
    452 static 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 
    476 static 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 
    491 static 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 
    537 static 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 
    579 static 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 
    592 static 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 
    622 static 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 
    701 static 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 
    718 static 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 
    731 static void register_audio_dsound(void)
    732 {
    733     audio_driver_register(&dsound_audio_driver);
    734 }
    735 type_init(register_audio_dsound);