text: /*              DirectSoundFullDuplex
   *
   * Copyright 1998 Marcus Meissner
   * Copyright 1998 Rob Riggs
   * Copyright 2000-2001 TransGaming Technologies, Inc.
   * Copyright 2005 Robert Reif
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
   */
  
  #include <stdarg.h>
  
  #define COBJMACROS
  #include "windef.h"
  #include "winbase.h"
  #include "winuser.h"
  #include "mmsystem.h"
  #include "mmddk.h"
  #include "wine/debug.h"
  #include "dsound.h"
  #include "dsound_private.h"
  
  WINE_DEFAULT_DEBUG_CHANNEL(dsound);
  
  /*****************************************************************************
   * IDirectSoundFullDuplex implementation structure
   */
  typedef struct IDirectSoundFullDuplexImpl
  {
      IUnknown               IUnknown_iface;
      IDirectSoundFullDuplex IDirectSoundFullDuplex_iface;
      LONG                   ref, refdsfd, numIfaces;
      IUnknown              *ds8_unk;     /* Aggregated IDirectSound8 */
      IUnknown              *dsc8_unk;    /* Aggregated IDirectSoundCapture8 */
  } IDirectSoundFullDuplexImpl;
  
  static void fullduplex_destroy(IDirectSoundFullDuplexImpl *This)
  {
      IDirectSound8 *ds8;
      IDirectSoundCapture8 *dsc8;
  
      if (This->ds8_unk) {
          IUnknown_QueryInterface(This->ds8_unk, &IID_IDirectSound8, (void**)&ds8);
          while(IDirectSound8_Release(ds8) > 0);
          IUnknown_Release(This->ds8_unk);
      }
      if (This->dsc8_unk) {
          IUnknown_QueryInterface(This->dsc8_unk, &IID_IDirectSoundCapture8, (void**)&dsc8);
          while(IDirectSoundCapture_Release(dsc8) > 0);
          IUnknown_Release(This->dsc8_unk);
      }
      TRACE("(%p) released\n", This);
      HeapFree(GetProcessHeap(), 0, This);
  }
  
  /*******************************************************************************
   * IUnknown implementation for DirectSoundFullDuplex
   */
  static inline IDirectSoundFullDuplexImpl *impl_from_IUnknown(IUnknown *iface)
  {
      return CONTAINING_RECORD(iface, IDirectSoundFullDuplexImpl, IUnknown_iface);
  }
  
  static HRESULT WINAPI IUnknownImpl_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
  {
      IDirectSoundFullDuplexImpl *This = impl_from_IUnknown(iface);
  
      TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
  
      if (!ppv) {
          WARN("invalid parameter\n");
          return E_INVALIDARG;
      }
  
      if (IsEqualIID(riid, &IID_IUnknown)) {
          IUnknown_AddRef(&This->IUnknown_iface);
          *ppv = &This->IUnknown_iface;
          return S_OK;
      } else if (IsEqualIID(riid, &IID_IDirectSoundFullDuplex)) {
          IDirectSoundFullDuplex_AddRef(&This->IDirectSoundFullDuplex_iface);
          *ppv = &This->IDirectSoundFullDuplex_iface;
          return S_OK;
      } else if (This->ds8_unk && (IsEqualIID(riid, &IID_IDirectSound) ||
                                   IsEqualIID(riid, &IID_IDirectSound8)))
          return IUnknown_QueryInterface(This->ds8_unk, riid, ppv);
      else if (This->dsc8_unk && IsEqualIID(riid, &IID_IDirectSoundCapture))
          return IUnknown_QueryInterface(This->dsc8_unk, riid, ppv);
  
      *ppv = NULL;
      return E_NOINTERFACE;
  }
  
  static ULONG WINAPI IUnknownImpl_AddRef(IUnknown *iface)
  {
      IDirectSoundFullDuplexImpl *This = impl_from_IUnknown(iface);
      ULONG ref = InterlockedIncrement(&This->ref);
  
      TRACE("(%p) ref=%ld\n", This, ref);
  
      if(ref == 1)
          InterlockedIncrement(&This->numIfaces);
      return ref;
  }
  
  static ULONG WINAPI IUnknownImpl_Release(IUnknown *iface)
  {
      IDirectSoundFullDuplexImpl *This = impl_from_IUnknown(iface);
      ULONG ref = InterlockedDecrement(&This->ref);
  
      TRACE("(%p) ref=%ld\n", This, ref);
  
      if (!ref && !InterlockedDecrement(&This->numIfaces))
          fullduplex_destroy(This);
      return ref;
  }
  
  static const IUnknownVtbl unk_vtbl =
  {
      IUnknownImpl_QueryInterface,
      IUnknownImpl_AddRef,
      IUnknownImpl_Release
  };
  
  /***************************************************************************
   * IDirectSoundFullDuplex implementation
   */
  static inline IDirectSoundFullDuplexImpl *impl_from_IDirectSoundFullDuplex(IDirectSoundFullDuplex *iface)
  {
      return CONTAINING_RECORD(iface, IDirectSoundFullDuplexImpl, IDirectSoundFullDuplex_iface);
  }
  
  static HRESULT WINAPI IDirectSoundFullDuplexImpl_QueryInterface(IDirectSoundFullDuplex *iface,
          REFIID riid, void **ppv)
  {
      IDirectSoundFullDuplexImpl *This = impl_from_IDirectSoundFullDuplex(iface);
      TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
      return IUnknown_QueryInterface(&This->IUnknown_iface, riid, ppv);
  }
  
  static ULONG WINAPI IDirectSoundFullDuplexImpl_AddRef(IDirectSoundFullDuplex *iface)
  {
      IDirectSoundFullDuplexImpl *This = impl_from_IDirectSoundFullDuplex(iface);
      ULONG ref = InterlockedIncrement(&This->refdsfd);
  
      TRACE("(%p) ref=%ld\n", This, ref);
  
      if(ref == 1)
          InterlockedIncrement(&This->numIfaces);
      return ref;
  }
  
  static ULONG WINAPI IDirectSoundFullDuplexImpl_Release(IDirectSoundFullDuplex *iface)
  {
      IDirectSoundFullDuplexImpl *This = impl_from_IDirectSoundFullDuplex(iface);
      ULONG ref = InterlockedDecrement(&This->refdsfd);
  
      TRACE("(%p) ref=%ld\n", This, ref);
  
      if (!ref && !InterlockedDecrement(&This->numIfaces))
          fullduplex_destroy(This);
      return ref;
  }
  
  static HRESULT WINAPI IDirectSoundFullDuplexImpl_Initialize(IDirectSoundFullDuplex *iface,
          const GUID *capture_dev, const GUID *render_dev, const DSCBUFFERDESC *cbufdesc,
          const DSBUFFERDESC *bufdesc, HWND hwnd, DWORD level, IDirectSoundCaptureBuffer8 **dscb8,
          IDirectSoundBuffer8 **dsb8)
  {
      IDirectSoundFullDuplexImpl *This = impl_from_IDirectSoundFullDuplex(iface);
      IDirectSound8 *ds8 = NULL;
      IDirectSoundCapture8 *dsc8 = NULL;
      HRESULT hr;
  
      TRACE("(%p,%s,%s,%p,%p,%p,%lx,%p,%p)\n", This, debugstr_guid(capture_dev),
              debugstr_guid(render_dev), cbufdesc, bufdesc, hwnd, level, dscb8, dsb8);
  
      if (!dscb8 || !dsb8)
          return E_INVALIDARG;
  
      *dscb8 = NULL;
      *dsb8 = NULL;
  
      if (This->ds8_unk || This->dsc8_unk) {
          WARN("already initialized\n");
          return DSERR_ALREADYINITIALIZED;
      }
  
      hr = IDirectSoundImpl_Create(&This->IUnknown_iface, &IID_IUnknown, (void**)&This->ds8_unk,
              TRUE);
      if (SUCCEEDED(hr)) {
          IUnknown_QueryInterface(This->ds8_unk, &IID_IDirectSound8, (void**)&ds8);
          hr = IDirectSound8_Initialize(ds8, render_dev);
      }
      if (hr != DS_OK) {
          WARN("Creating/initializing IDirectSound8 failed\n");
          goto error;
      }
  
      IDirectSound8_SetCooperativeLevel(ds8, hwnd, level);
  
      hr = IDirectSound8_CreateSoundBuffer(ds8, bufdesc, (IDirectSoundBuffer**)dsb8, NULL);
      if (hr != DS_OK) {
          WARN("IDirectSoundBuffer_Create() failed\n");
          goto error;
      }
  
      hr = IDirectSoundCaptureImpl_Create(&This->IUnknown_iface, &IID_IUnknown,
              (void**)&This->dsc8_unk, TRUE);
      if (SUCCEEDED(hr)) {
          IUnknown_QueryInterface(This->dsc8_unk, &IID_IDirectSoundCapture8, (void**)&dsc8);
          hr = IDirectSoundCapture_Initialize(dsc8, capture_dev);
      }
      if (hr != DS_OK) {
          WARN("Creating/initializing IDirectSoundCapture8 failed\n");
          goto error;
      }
  
      hr = IDirectSoundCapture_CreateCaptureBuffer(dsc8, cbufdesc,
              (IDirectSoundCaptureBuffer**)dscb8, NULL);
      if (hr != DS_OK) {
          WARN("IDirectSoundCapture_CreateCaptureBuffer() failed\n");
          goto error;
      }
  
      IDirectSound8_Release(ds8);
      IDirectSoundCapture_Release(dsc8);
      return DS_OK;
  
  error:
      if (*dsb8) {
          IDirectSoundBuffer8_Release(*dsb8);
          *dsb8 = NULL;
      }
      if (ds8)
          IDirectSound8_Release(ds8);
      if (This->ds8_unk) {
          IUnknown_Release(This->ds8_unk);
          This->ds8_unk = NULL;
      }
      if (*dscb8) {
          IDirectSoundCaptureBuffer8_Release(*dscb8);
          *dscb8 = NULL;
      }
      if (dsc8)
          IDirectSoundCapture_Release(dsc8);
      if (This->dsc8_unk) {
          IUnknown_Release(This->dsc8_unk);
          This->dsc8_unk = NULL;
      }
      return hr;
  }
  
  static const IDirectSoundFullDuplexVtbl dsfd_vtbl =
  {
      /* IUnknown methods */
      IDirectSoundFullDuplexImpl_QueryInterface,
      IDirectSoundFullDuplexImpl_AddRef,
      IDirectSoundFullDuplexImpl_Release,
  
      /* IDirectSoundFullDuplex methods */
      IDirectSoundFullDuplexImpl_Initialize
  };
  
  HRESULT DSOUND_FullDuplexCreate(REFIID riid, void **ppv)
  {
      IDirectSoundFullDuplexImpl *obj;
      HRESULT hr;
  
      TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
  
      *ppv = NULL;
      obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj));
      if (!obj) {
          WARN("out of memory\n");
          return DSERR_OUTOFMEMORY;
      }
  
      setup_dsound_options();
  
      obj->IDirectSoundFullDuplex_iface.lpVtbl = &dsfd_vtbl;
      obj->IUnknown_iface.lpVtbl = &unk_vtbl;
      obj->ref = 1;
      obj->refdsfd = 0;
      obj->numIfaces = 1;
  
      hr = IUnknown_QueryInterface(&obj->IUnknown_iface, riid, ppv);
      IUnknown_Release(&obj->IUnknown_iface);
  
      return hr;
  }
  
  /***************************************************************************
   * DirectSoundFullDuplexCreate [DSOUND.10]
   *
   * Create and initialize a DirectSoundFullDuplex interface.
   *
   * PARAMS
   *    capture_dev         [I] Address of sound capture device GUID.
   *    render_dev          [I] Address of sound render device GUID.
   *    cbufdesc            [I] Address of capture buffer description.
   *    bufdesc             [I] Address of  render buffer description.
   *    hwnd                [I] Handle to application window.
   *    level               [I] Cooperative level.
   *    dsfd                [O] Address where full duplex interface returned.
   *    dscb8               [0] Address where capture buffer interface returned.
   *    dsb8                [0] Address where render buffer interface returned.
   *    outer_unk           [I] Must be NULL.
   *
   * RETURNS
   *    Success: DS_OK
   *    Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
   *             DSERR_OUTOFMEMORY DSERR_INVALIDCALL DSERR_NODRIVER
   */
  HRESULT WINAPI DirectSoundFullDuplexCreate(const GUID *capture_dev, const GUID *render_dev,
          const DSCBUFFERDESC *cbufdesc, const DSBUFFERDESC *bufdesc, HWND hwnd, DWORD level,
          IDirectSoundFullDuplex **dsfd, IDirectSoundCaptureBuffer8 **dscb8,
          IDirectSoundBuffer8 **dsb8, IUnknown *outer_unk)
  {
      HRESULT hr;
  
      TRACE("(%s,%s,%p,%p,%p,%lx,%p,%p,%p,%p)\n", debugstr_guid(capture_dev),
              debugstr_guid(render_dev), cbufdesc, bufdesc, hwnd, level, dsfd, dscb8, dsb8,
              outer_unk);
  
      if (!dsfd)
          return DSERR_INVALIDPARAM;
      if (outer_unk) {
          *dsfd = NULL;
          return DSERR_NOAGGREGATION;
      }
  
      hr = DSOUND_FullDuplexCreate(&IID_IDirectSoundFullDuplex, (void**)dsfd);
      if (hr == DS_OK) {
          hr = IDirectSoundFullDuplex_Initialize(*dsfd, capture_dev, render_dev, cbufdesc, bufdesc,
                  hwnd, level, dscb8, dsb8);
          if (hr != DS_OK) {
              IDirectSoundFullDuplex_Release(*dsfd);
              *dsfd = NULL;
              WARN("IDirectSoundFullDuplexImpl_Initialize failed\n");
          }
      }
  
      return hr;
  }
  