/* * 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