| /* | 
 |  * QEMU DirectSound audio driver header | 
 |  * | 
 |  * Copyright (c) 2005 Vassili Karpov (malc) | 
 |  * | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
 |  * of this software and associated documentation files (the "Software"), to deal | 
 |  * in the Software without restriction, including without limitation the rights | 
 |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
 |  * copies of the Software, and to permit persons to whom the Software is | 
 |  * furnished to do so, subject to the following conditions: | 
 |  * | 
 |  * The above copyright notice and this permission notice shall be included in | 
 |  * all copies or substantial portions of the Software. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
 |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
 |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
 |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
 |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
 |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
 |  * THE SOFTWARE. | 
 |  */ | 
 | #ifdef DSBTYPE_IN | 
 | #define NAME "capture buffer" | 
 | #define NAME2 "DirectSoundCapture" | 
 | #define TYPE in | 
 | #define IFACE IDirectSoundCaptureBuffer | 
 | #define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER | 
 | #define FIELD dsound_capture_buffer | 
 | #define FIELD2 dsound_capture | 
 | #else | 
 | #define NAME "playback buffer" | 
 | #define NAME2 "DirectSound" | 
 | #define TYPE out | 
 | #define IFACE IDirectSoundBuffer | 
 | #define BUFPTR LPDIRECTSOUNDBUFFER | 
 | #define FIELD dsound_buffer | 
 | #define FIELD2 dsound | 
 | #endif | 
 |  | 
 | static int glue (dsound_unlock_, TYPE) ( | 
 |     BUFPTR buf, | 
 |     LPVOID p1, | 
 |     LPVOID p2, | 
 |     DWORD blen1, | 
 |     DWORD blen2 | 
 |     ) | 
 | { | 
 |     HRESULT hr; | 
 |  | 
 |     hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2); | 
 |     if (FAILED (hr)) { | 
 |         dsound_logerr (hr, "Could not unlock " NAME "\n"); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | static int glue (dsound_lock_, TYPE) ( | 
 |     BUFPTR buf, | 
 |     struct audio_pcm_info *info, | 
 |     DWORD pos, | 
 |     DWORD len, | 
 |     LPVOID *p1p, | 
 |     LPVOID *p2p, | 
 |     DWORD *blen1p, | 
 |     DWORD *blen2p, | 
 |     int entire | 
 |     ) | 
 | { | 
 |     HRESULT hr; | 
 |     int i; | 
 |     LPVOID p1 = NULL, p2 = NULL; | 
 |     DWORD blen1 = 0, blen2 = 0; | 
 |     DWORD flag; | 
 |  | 
 | #ifdef DSBTYPE_IN | 
 |     flag = entire ? DSCBLOCK_ENTIREBUFFER : 0; | 
 | #else | 
 |     flag = entire ? DSBLOCK_ENTIREBUFFER : 0; | 
 | #endif | 
 |     for (i = 0; i < conf.lock_retries; ++i) { | 
 |         hr = glue (IFACE, _Lock) ( | 
 |             buf, | 
 |             pos, | 
 |             len, | 
 |             &p1, | 
 |             &blen1, | 
 |             &p2, | 
 |             &blen2, | 
 |             flag | 
 |             ); | 
 |  | 
 |         if (FAILED (hr)) { | 
 | #ifndef DSBTYPE_IN | 
 |             if (hr == DSERR_BUFFERLOST) { | 
 |                 if (glue (dsound_restore_, TYPE) (buf)) { | 
 |                     dsound_logerr (hr, "Could not lock " NAME "\n"); | 
 |                     goto fail; | 
 |                 } | 
 |                 continue; | 
 |             } | 
 | #endif | 
 |             dsound_logerr (hr, "Could not lock " NAME "\n"); | 
 |             goto fail; | 
 |         } | 
 |  | 
 |         break; | 
 |     } | 
 |  | 
 |     if (i == conf.lock_retries) { | 
 |         dolog ("%d attempts to lock " NAME " failed\n", i); | 
 |         goto fail; | 
 |     } | 
 |  | 
 |     if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) { | 
 |         dolog ("DirectSound returned misaligned buffer %ld %ld\n", | 
 |                blen1, blen2); | 
 |         glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2); | 
 |         goto fail; | 
 |     } | 
 |  | 
 |     if (!p1 && blen1) { | 
 |         dolog ("warning: !p1 && blen1=%ld\n", blen1); | 
 |         blen1 = 0; | 
 |     } | 
 |  | 
 |     if (!p2 && blen2) { | 
 |         dolog ("warning: !p2 && blen2=%ld\n", blen2); | 
 |         blen2 = 0; | 
 |     } | 
 |  | 
 |     *p1p = p1; | 
 |     *p2p = p2; | 
 |     *blen1p = blen1; | 
 |     *blen2p = blen2; | 
 |     return 0; | 
 |  | 
 |  fail: | 
 |     *p1p = NULL - 1; | 
 |     *p2p = NULL - 1; | 
 |     *blen1p = -1; | 
 |     *blen2p = -1; | 
 |     return -1; | 
 | } | 
 |  | 
 | #ifdef DSBTYPE_IN | 
 | static void dsound_fini_in (HWVoiceIn *hw) | 
 | #else | 
 | static void dsound_fini_out (HWVoiceOut *hw) | 
 | #endif | 
 | { | 
 |     HRESULT hr; | 
 | #ifdef DSBTYPE_IN | 
 |     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | 
 | #else | 
 |     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | 
 | #endif | 
 |  | 
 |     if (ds->FIELD) { | 
 |         hr = glue (IFACE, _Stop) (ds->FIELD); | 
 |         if (FAILED (hr)) { | 
 |             dsound_logerr (hr, "Could not stop " NAME "\n"); | 
 |         } | 
 |  | 
 |         hr = glue (IFACE, _Release) (ds->FIELD); | 
 |         if (FAILED (hr)) { | 
 |             dsound_logerr (hr, "Could not release " NAME "\n"); | 
 |         } | 
 |         ds->FIELD = NULL; | 
 |     } | 
 | } | 
 |  | 
 | #ifdef DSBTYPE_IN | 
 | static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as) | 
 | #else | 
 | static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as) | 
 | #endif | 
 | { | 
 |     int err; | 
 |     HRESULT hr; | 
 |     dsound *s = &glob_dsound; | 
 |     WAVEFORMATEX wfx; | 
 |     struct audsettings obt_as; | 
 | #ifdef DSBTYPE_IN | 
 |     const char *typ = "ADC"; | 
 |     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; | 
 |     DSCBUFFERDESC bd; | 
 |     DSCBCAPS bc; | 
 | #else | 
 |     const char *typ = "DAC"; | 
 |     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; | 
 |     DSBUFFERDESC bd; | 
 |     DSBCAPS bc; | 
 | #endif | 
 |  | 
 |     if (!s->FIELD2) { | 
 |         dolog ("Attempt to initialize voice without " NAME2 " object\n"); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     err = waveformat_from_audio_settings (&wfx, as); | 
 |     if (err) { | 
 |         return -1; | 
 |     } | 
 |  | 
 |     memset (&bd, 0, sizeof (bd)); | 
 |     bd.dwSize = sizeof (bd); | 
 |     bd.lpwfxFormat = &wfx; | 
 | #ifdef DSBTYPE_IN | 
 |     bd.dwBufferBytes = conf.bufsize_in; | 
 |     hr = IDirectSoundCapture_CreateCaptureBuffer ( | 
 |         s->dsound_capture, | 
 |         &bd, | 
 |         &ds->dsound_capture_buffer, | 
 |         NULL | 
 |         ); | 
 | #else | 
 |     bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; | 
 |     bd.dwBufferBytes = conf.bufsize_out; | 
 |     hr = IDirectSound_CreateSoundBuffer ( | 
 |         s->dsound, | 
 |         &bd, | 
 |         &ds->dsound_buffer, | 
 |         NULL | 
 |         ); | 
 | #endif | 
 |  | 
 |     if (FAILED (hr)) { | 
 |         dsound_logerr2 (hr, typ, "Could not create " NAME "\n"); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     hr = glue (IFACE, _GetFormat) (ds->FIELD, &wfx, sizeof (wfx), NULL); | 
 |     if (FAILED (hr)) { | 
 |         dsound_logerr2 (hr, typ, "Could not get " NAME " format\n"); | 
 |         goto fail0; | 
 |     } | 
 |  | 
 | #ifdef DEBUG_DSOUND | 
 |     dolog (NAME "\n"); | 
 |     print_wave_format (&wfx); | 
 | #endif | 
 |  | 
 |     memset (&bc, 0, sizeof (bc)); | 
 |     bc.dwSize = sizeof (bc); | 
 |  | 
 |     hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc); | 
 |     if (FAILED (hr)) { | 
 |         dsound_logerr2 (hr, typ, "Could not get " NAME " format\n"); | 
 |         goto fail0; | 
 |     } | 
 |  | 
 |     err = waveformat_to_audio_settings (&wfx, &obt_as); | 
 |     if (err) { | 
 |         goto fail0; | 
 |     } | 
 |  | 
 |     ds->first_time = 1; | 
 |     obt_as.endianness = 0; | 
 |     audio_pcm_init_info (&hw->info, &obt_as); | 
 |  | 
 |     if (bc.dwBufferBytes & hw->info.align) { | 
 |         dolog ( | 
 |             "GetCaps returned misaligned buffer size %ld, alignment %d\n", | 
 |             bc.dwBufferBytes, hw->info.align + 1 | 
 |             ); | 
 |     } | 
 |     hw->samples = bc.dwBufferBytes >> hw->info.shift; | 
 |  | 
 | #ifdef DEBUG_DSOUND | 
 |     dolog ("caps %ld, desc %ld\n", | 
 |            bc.dwBufferBytes, bd.dwBufferBytes); | 
 |  | 
 |     dolog ("bufsize %d, freq %d, chan %d, fmt %d\n", | 
 |            hw->bufsize, settings.freq, settings.nchannels, settings.fmt); | 
 | #endif | 
 |     return 0; | 
 |  | 
 |  fail0: | 
 |     glue (dsound_fini_, TYPE) (hw); | 
 |     return -1; | 
 | } | 
 |  | 
 | #undef NAME | 
 | #undef NAME2 | 
 | #undef TYPE | 
 | #undef IFACE | 
 | #undef BUFPTR | 
 | #undef FIELD | 
 | #undef FIELD2 |