/* * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD) * * Copyright 1994 Martin Ayotte * 1999 Eric Pouech (async playing in waveOut/waveIn) * 2000 Eric Pouech (loops in waveOut) * 2002 Eric Pouech (full duplex) * * 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 "config.h" #include "wine/port.h" #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_SYS_MMAN_H # include #endif #ifdef HAVE_POLL_H #include #endif #ifdef HAVE_SYS_POLL_H # include #endif #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "winerror.h" #include "mmddk.h" #include "mmreg.h" #include "dsound.h" #include "dsdriver.h" #include "oss.h" #include "wine/debug.h" #include "audio.h" WINE_DEFAULT_DEBUG_CHANNEL(wave); #ifdef HAVE_OSS /*======================================================================* * Low level DSOUND definitions * *======================================================================*/ typedef struct IDsDriverPropertySetImpl IDsDriverPropertySetImpl; typedef struct IDsDriverNotifyImpl IDsDriverNotifyImpl; typedef struct IDsDriverImpl IDsDriverImpl; typedef struct IDsDriverBufferImpl IDsDriverBufferImpl; struct IDsDriverPropertySetImpl { /* IUnknown fields */ const IDsDriverPropertySetVtbl *lpVtbl; LONG ref; IDsDriverBufferImpl* buffer; }; struct IDsDriverNotifyImpl { /* IUnknown fields */ const IDsDriverNotifyVtbl *lpVtbl; LONG ref; /* IDsDriverNotifyImpl fields */ LPDSBPOSITIONNOTIFY notifies; int nrofnotifies; IDsDriverBufferImpl* buffer; }; struct IDsDriverImpl { /* IUnknown fields */ const IDsDriverVtbl *lpVtbl; LONG ref; /* IDsDriverImpl fields */ UINT wDevID; IDsDriverBufferImpl* primary; int nrofsecondaries; IDsDriverBufferImpl** secondaries; }; struct IDsDriverBufferImpl { /* IUnknown fields */ const IDsDriverBufferVtbl *lpVtbl; LONG ref; /* IDsDriverBufferImpl fields */ IDsDriverImpl* drv; DWORD buflen; WAVEFORMATPCMEX wfex; LPBYTE mapping; DWORD maplen; int fd; DWORD dwFlags; /* IDsDriverNotifyImpl fields */ IDsDriverNotifyImpl* notify; int notify_index; /* IDsDriverPropertySetImpl fields */ IDsDriverPropertySetImpl* property_set; }; static HRESULT WINAPI IDsDriverPropertySetImpl_Create( IDsDriverBufferImpl * dsdb, IDsDriverPropertySetImpl **pdsdps); static HRESULT WINAPI IDsDriverNotifyImpl_Create( IDsDriverBufferImpl * dsdb, IDsDriverNotifyImpl **pdsdn); /*======================================================================* * Low level DSOUND property set implementation * *======================================================================*/ static HRESULT WINAPI IDsDriverPropertySetImpl_QueryInterface( PIDSDRIVERPROPERTYSET iface, REFIID riid, LPVOID *ppobj) { IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface; TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj); if ( IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDsDriverPropertySet) ) { IDsDriverPropertySet_AddRef(iface); *ppobj = (LPVOID)This; return DS_OK; } FIXME( "Unknown IID %s\n", debugstr_guid( riid ) ); *ppobj = 0; return E_NOINTERFACE; } static ULONG WINAPI IDsDriverPropertySetImpl_AddRef(PIDSDRIVERPROPERTYSET iface) { IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface; ULONG refCount = InterlockedIncrement(&This->ref); TRACE("(%p) ref was %d\n", This, refCount - 1); return refCount; } static ULONG WINAPI IDsDriverPropertySetImpl_Release(PIDSDRIVERPROPERTYSET iface) { IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface; ULONG refCount = InterlockedDecrement(&This->ref); TRACE("(%p) ref was %d\n", This, refCount + 1); if (!refCount) { IDsDriverBuffer_Release((PIDSDRIVERBUFFER)This->buffer); HeapFree(GetProcessHeap(),0,This); TRACE("(%p) released\n",This); } return refCount; } static HRESULT WINAPI IDsDriverPropertySetImpl_Get( PIDSDRIVERPROPERTYSET iface, PDSPROPERTY pDsProperty, LPVOID pPropertyParams, ULONG cbPropertyParams, LPVOID pPropertyData, ULONG cbPropertyData, PULONG pcbReturnedData ) { IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface; FIXME("(%p,%p,%p,%x,%p,%x,%p)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData,pcbReturnedData); return DSERR_UNSUPPORTED; } static HRESULT WINAPI IDsDriverPropertySetImpl_Set( PIDSDRIVERPROPERTYSET iface, PDSPROPERTY pDsProperty, LPVOID pPropertyParams, ULONG cbPropertyParams, LPVOID pPropertyData, ULONG cbPropertyData ) { IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface; FIXME("(%p,%p,%p,%x,%p,%x)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData); return DSERR_UNSUPPORTED; } static HRESULT WINAPI IDsDriverPropertySetImpl_QuerySupport( PIDSDRIVERPROPERTYSET iface, REFGUID PropertySetId, ULONG PropertyId, PULONG pSupport ) { IDsDriverPropertySetImpl *This = (IDsDriverPropertySetImpl *)iface; FIXME("(%p,%s,%x,%p)\n",This,debugstr_guid(PropertySetId),PropertyId,pSupport); return DSERR_UNSUPPORTED; } static const IDsDriverPropertySetVtbl dsdpsvt = { IDsDriverPropertySetImpl_QueryInterface, IDsDriverPropertySetImpl_AddRef, IDsDriverPropertySetImpl_Release, IDsDriverPropertySetImpl_Get, IDsDriverPropertySetImpl_Set, IDsDriverPropertySetImpl_QuerySupport, }; /*======================================================================* * Low level DSOUND notify implementation * *======================================================================*/ static HRESULT WINAPI IDsDriverNotifyImpl_QueryInterface( PIDSDRIVERNOTIFY iface, REFIID riid, LPVOID *ppobj) { IDsDriverNotifyImpl *This = (IDsDriverNotifyImpl *)iface; TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj); if ( IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDsDriverNotify) ) { IDsDriverNotify_AddRef(iface); *ppobj = This; return DS_OK; } FIXME( "Unknown IID %s\n", debugstr_guid( riid ) ); *ppobj = 0; return E_NOINTERFACE; } static ULONG WINAPI IDsDriverNotifyImpl_AddRef(PIDSDRIVERNOTIFY iface) { IDsDriverNotifyImpl *This = (IDsDriverNotifyImpl *)iface; ULONG refCount = InterlockedIncrement(&This->ref); TRACE("(%p) ref was %d\n", This, refCount - 1); return refCount; } static ULONG WINAPI IDsDriverNotifyImpl_Release(PIDSDRIVERNOTIFY iface) { IDsDriverNotifyImpl *This = (IDsDriverNotifyImpl *)iface; ULONG refCount = InterlockedDecrement(&This->ref); TRACE("(%p) ref was %d\n", This, refCount + 1); if (!refCount) { IDsDriverBuffer_Release((PIDSDRIVERBUFFER)This->buffer); HeapFree(GetProcessHeap(), 0, This->notifies); HeapFree(GetProcessHeap(),0,This); TRACE("(%p) released\n",This); } return refCount; } static HRESULT WINAPI IDsDriverNotifyImpl_SetNotificationPositions( PIDSDRIVERNOTIFY iface, DWORD howmuch, LPCDSBPOSITIONNOTIFY notify) { IDsDriverNotifyImpl *This = (IDsDriverNotifyImpl *)iface; TRACE("(%p,0x%08x,%p)\n",This,howmuch,notify); if (!notify) { WARN("invalid parameter\n"); return DSERR_INVALIDPARAM; } if (TRACE_ON(wave)) { int i; for (i=0;inotifies) This->notifies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->notifies, howmuch * sizeof(DSBPOSITIONNOTIFY)); else This->notifies = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, howmuch * sizeof(DSBPOSITIONNOTIFY)); memcpy(This->notifies, notify, howmuch * sizeof(DSBPOSITIONNOTIFY)); This->nrofnotifies = howmuch; return S_OK; } static const IDsDriverNotifyVtbl dsdnvt = { IDsDriverNotifyImpl_QueryInterface, IDsDriverNotifyImpl_AddRef, IDsDriverNotifyImpl_Release, IDsDriverNotifyImpl_SetNotificationPositions, }; /*======================================================================* * Low level DSOUND implementation * *======================================================================*/ static HRESULT DSDB_MapBuffer(IDsDriverBufferImpl *dsdb) { TRACE("(%p), format=%dx%dx%d\n", dsdb, dsdb->wfex.Format.nSamplesPerSec, dsdb->wfex.Format.wBitsPerSample, dsdb->wfex.Format.nChannels); if (!dsdb->mapping) { dsdb->mapping = mmap(NULL, dsdb->maplen, PROT_WRITE, MAP_SHARED, dsdb->fd, 0); if (dsdb->mapping == (LPBYTE)-1) { ERR("Could not map sound device for direct access (%s)\n", strerror(errno)); ERR("Please run winecfg, open \"Audio\" page and set\n" "\"Hardware Acceleration\" to \"Emulation\".\n"); return DSERR_GENERIC; } TRACE("The sound device has been mapped for direct access at %p, size=%d\n", dsdb->mapping, dsdb->maplen); /* for some reason, es1371 and sblive! sometimes have junk in here. * clear it, or we get junk noise */ /* some libc implementations are buggy: their memset reads from the buffer... * to work around it, we have to zero the block by hand. We don't do the expected: * memset(dsdb->mapping,0, dsdb->maplen); */ { unsigned char* p1 = dsdb->mapping; unsigned len = dsdb->maplen; unsigned char silence = (dsdb->wfex.Format.wBitsPerSample == 8) ? 128 : 0; unsigned long ulsilence = (dsdb->wfex.Format.wBitsPerSample == 8) ? 0x80808080 : 0; if (len >= 16) /* so we can have at least a 4 long area to store... */ { /* the mmap:ed value is (at least) dword aligned * so, start filling the complete unsigned long:s */ int b = len >> 2; unsigned long* p4 = (unsigned long*)p1; while (b--) *p4++ = ulsilence; /* prepare for filling the rest */ len &= 3; p1 = (unsigned char*)p4; } /* in all cases, fill the remaining bytes */ while (len-- != 0) *p1++ = silence; } } return DS_OK; } static HRESULT DSDB_UnmapBuffer(IDsDriverBufferImpl *dsdb) { TRACE("(%p)\n",dsdb); if (dsdb->mapping) { if (munmap(dsdb->mapping, dsdb->maplen) < 0) { ERR("(%p): Could not unmap sound device (%s)\n", dsdb, strerror(errno)); return DSERR_GENERIC; } dsdb->mapping = NULL; TRACE("(%p): sound device unmapped\n", dsdb); } return DS_OK; } static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj) { IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; TRACE("(%p,%s,%p)\n",iface,debugstr_guid(riid),*ppobj); if ( IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDsDriverBuffer) ) { IDsDriverBuffer_AddRef(iface); *ppobj = (LPVOID)This; return DS_OK; } if ( IsEqualGUID( &IID_IDsDriverNotify, riid ) ) { if (!This->notify) IDsDriverNotifyImpl_Create(This, &(This->notify)); if (This->notify) { IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY)This->notify); *ppobj = (LPVOID)This->notify; return DS_OK; } *ppobj = 0; return E_FAIL; } if ( IsEqualGUID( &IID_IDsDriverPropertySet, riid ) ) { if (!This->property_set) IDsDriverPropertySetImpl_Create(This, &(This->property_set)); if (This->property_set) { IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET)This->property_set); *ppobj = (LPVOID)This->property_set; return DS_OK; } *ppobj = 0; return E_FAIL; } FIXME( "Unknown IID %s\n", debugstr_guid( riid ) ); *ppobj = 0; return E_NOINTERFACE; } static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface) { IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; ULONG refCount = InterlockedIncrement(&This->ref); TRACE("(%p) ref was %d\n", This, refCount - 1); return refCount; } static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface) { IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; ULONG refCount = InterlockedDecrement(&This->ref); TRACE("(%p) ref was %d\n", This, refCount + 1); if (refCount) return refCount; if (This == This->drv->primary) This->drv->primary = NULL; else { int i; for (i = 0; i < This->drv->nrofsecondaries; i++) if (This->drv->secondaries[i] == This) break; if (i < This->drv->nrofsecondaries) { /* Put the last buffer of the list in the (now empty) position */ This->drv->secondaries[i] = This->drv->secondaries[This->drv->nrofsecondaries - 1]; This->drv->nrofsecondaries--; This->drv->secondaries = HeapReAlloc(GetProcessHeap(),0, This->drv->secondaries, sizeof(PIDSDRIVERBUFFER)*This->drv->nrofsecondaries); TRACE("(%p) buffer count is now %d\n", This, This->drv->nrofsecondaries); } WOutDev[This->drv->wDevID].ossdev->ds_caps.dwFreeHwMixingAllBuffers++; WOutDev[This->drv->wDevID].ossdev->ds_caps.dwFreeHwMixingStreamingBuffers++; } DSDB_UnmapBuffer(This); HeapFree(GetProcessHeap(),0,This); TRACE("(%p) released\n",This); return 0; } static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface, LPVOID*ppvAudio1,LPDWORD pdwLen1, LPVOID*ppvAudio2,LPDWORD pdwLen2, DWORD dwWritePosition,DWORD dwWriteLen, DWORD dwFlags) { /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */ /* since we (GetDriverDesc flags) have specified DSDDESC_DONTNEEDPRIMARYLOCK, * and that we don't support secondary buffers, this method will never be called */ TRACE("(%p): stub\n",iface); return DSERR_UNSUPPORTED; } static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface, LPVOID pvAudio1,DWORD dwLen1, LPVOID pvAudio2,DWORD dwLen2) { /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */ TRACE("(%p): stub\n",iface); return DSERR_UNSUPPORTED; } static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface, LPWAVEFORMATEX pwfx) { /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */ TRACE("(%p,%p)\n",iface,pwfx); /* On our request (GetDriverDesc flags), DirectSound has by now used * waveOutClose/waveOutOpen to set the format... * unfortunately, this means our mmap() is now gone... * so we need to somehow signal to our DirectSound implementation * that it should completely recreate this HW buffer... * this unexpected error code should do the trick... */ return DSERR_BUFFERLOST; } static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq) { /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */ TRACE("(%p,%d): stub\n",iface,dwFreq); return DSERR_UNSUPPORTED; } static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan) { DWORD vol; IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; TRACE("(%p,%p)\n",This,pVolPan); vol = pVolPan->dwTotalLeftAmpFactor | (pVolPan->dwTotalRightAmpFactor << 16); if (wodSetVolume(This->drv->wDevID, vol) != MMSYSERR_NOERROR) { WARN("wodSetVolume failed\n"); return DSERR_INVALIDPARAM; } return DS_OK; } static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos) { /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */ TRACE("(%p,%d): stub\n",iface,dwNewPos); return DSERR_UNSUPPORTED; } static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface, LPDWORD lpdwPlay, LPDWORD lpdwWrite) { IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; count_info info; DWORD ptr; TRACE("(%p)\n",iface); if (WOutDev[This->drv->wDevID].state == WINE_WS_CLOSED) { ERR("device not open, but accessing?\n"); return DSERR_UNINITIALIZED; } if (ioctl(This->fd, SNDCTL_DSP_GETOPTR, &info) < 0) { ERR("ioctl(%s, SNDCTL_DSP_GETOPTR) failed (%s)\n", WOutDev[This->drv->wDevID].ossdev->dev_name, strerror(errno)); return DSERR_GENERIC; } ptr = info.ptr & ~3; /* align the pointer, just in case */ if (lpdwPlay) *lpdwPlay = ptr; if (lpdwWrite) { /* add some safety margin (not strictly necessary, but...) */ if (WOutDev[This->drv->wDevID].ossdev->duplex_out_caps.dwSupport & WAVECAPS_SAMPLEACCURATE) *lpdwWrite = ptr + 32; else *lpdwWrite = ptr + WOutDev[This->drv->wDevID].dwFragmentSize; while (*lpdwWrite > This->buflen) *lpdwWrite -= This->buflen; } TRACE("playpos=%d, writepos=%d\n", lpdwPlay?*lpdwPlay:0, lpdwWrite?*lpdwWrite:0); return DS_OK; } static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags) { IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; int enable; TRACE("(%p,%x,%x,%x)\n",iface,dwRes1,dwRes2,dwFlags); WOutDev[This->drv->wDevID].ossdev->bOutputEnabled = TRUE; enable = getEnables(WOutDev[This->drv->wDevID].ossdev); if (ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) { if (errno == EINVAL) { /* Don't give up yet. OSS trigger support is inconsistent. */ if (WOutDev[This->drv->wDevID].ossdev->open_count == 1) { /* try the opposite input enable */ if (WOutDev[This->drv->wDevID].ossdev->bInputEnabled == FALSE) WOutDev[This->drv->wDevID].ossdev->bInputEnabled = TRUE; else WOutDev[This->drv->wDevID].ossdev->bInputEnabled = FALSE; /* try it again */ enable = getEnables(WOutDev[This->drv->wDevID].ossdev); if (ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &enable) >= 0) return DS_OK; } } ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WOutDev[This->drv->wDevID].ossdev->dev_name, strerror(errno)); WOutDev[This->drv->wDevID].ossdev->bOutputEnabled = FALSE; return DSERR_GENERIC; } return DS_OK; } static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface) { IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; int enable; TRACE("(%p)\n",iface); /* no more playing */ WOutDev[This->drv->wDevID].ossdev->bOutputEnabled = FALSE; enable = getEnables(WOutDev[This->drv->wDevID].ossdev); if (ioctl(This->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) { ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WOutDev[This->drv->wDevID].ossdev->dev_name, strerror(errno)); return DSERR_GENERIC; } #if 0 /* the play position must be reset to the beginning of the buffer */ if (ioctl(This->fd, SNDCTL_DSP_RESET, 0) < 0) { ERR("ioctl(%s, SNDCTL_DSP_RESET) failed (%s)\n", WOutDev[This->drv->wDevID].ossdev->dev_name, strerror(errno)); return DSERR_GENERIC; } #endif /* Most OSS drivers just can't stop the playback without closing the device... * so we need to somehow signal to our DirectSound implementation * that it should completely recreate this HW buffer... * this unexpected error code should do the trick... */ /* FIXME: ...unless we are doing full duplex, then it's not nice to close the device */ if (WOutDev[This->drv->wDevID].ossdev->open_count == 1) return DSERR_BUFFERLOST; return DS_OK; } static const IDsDriverBufferVtbl dsdbvt = { IDsDriverBufferImpl_QueryInterface, IDsDriverBufferImpl_AddRef, IDsDriverBufferImpl_Release, IDsDriverBufferImpl_Lock, IDsDriverBufferImpl_Unlock, IDsDriverBufferImpl_SetFormat, IDsDriverBufferImpl_SetFrequency, IDsDriverBufferImpl_SetVolumePan, IDsDriverBufferImpl_SetPosition, IDsDriverBufferImpl_GetPosition, IDsDriverBufferImpl_Play, IDsDriverBufferImpl_Stop }; static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj) { IDsDriverImpl *This = (IDsDriverImpl *)iface; TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj); if ( IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDsDriver) ) { IDsDriver_AddRef(iface); *ppobj = (LPVOID)This; return DS_OK; } FIXME( "Unknown IID %s\n", debugstr_guid( riid ) ); *ppobj = 0; return E_NOINTERFACE; } static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface) { IDsDriverImpl *This = (IDsDriverImpl *)iface; ULONG refCount = InterlockedIncrement(&This->ref); TRACE("(%p) ref was %d\n", This, refCount - 1); return refCount; } static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface) { IDsDriverImpl *This = (IDsDriverImpl *)iface; ULONG refCount = InterlockedDecrement(&This->ref); TRACE("(%p) ref was %d\n", This, refCount + 1); if (!refCount) { HeapFree(GetProcessHeap(),0,This); TRACE("(%p) released\n",This); } return refCount; } static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc) { IDsDriverImpl *This = (IDsDriverImpl *)iface; TRACE("(%p,%p)\n",iface,pDesc); /* copy version from driver */ memcpy(pDesc, &(WOutDev[This->wDevID].ossdev->ds_desc), sizeof(DSDRIVERDESC)); pDesc->dwFlags |= DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT | DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK | DSDDESC_DONTNEEDSECONDARYLOCK; pDesc->dnDevNode = WOutDev[This->wDevID].waveDesc.dnDevNode; pDesc->wVxdId = 0; pDesc->wReserved = 0; pDesc->ulDeviceNum = This->wDevID; pDesc->dwHeapType = DSDHEAP_NOHEAP; pDesc->pvDirectDrawHeap = NULL; pDesc->dwMemStartAddress = 0; pDesc->dwMemEndAddress = 0; pDesc->dwMemAllocExtra = 0; pDesc->pvReserved1 = NULL; pDesc->pvReserved2 = NULL; return DS_OK; } static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface) { IDsDriverImpl *This = (IDsDriverImpl *)iface; int enable; TRACE("(%p)\n",iface); /* make sure the card doesn't start playing before we want it to */ WOutDev[This->wDevID].ossdev->bOutputEnabled = FALSE; WOutDev[This->wDevID].ossdev->bInputEnabled = FALSE; enable = getEnables(WOutDev[This->wDevID].ossdev); if (ioctl(WOutDev[This->wDevID].ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) { ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",WOutDev[This->wDevID].ossdev->dev_name, strerror(errno)); return DSERR_GENERIC; } return DS_OK; } static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface) { IDsDriverImpl *This = (IDsDriverImpl *)iface; TRACE("(%p)\n",iface); if (This->primary) { ERR("problem with DirectSound: primary not released\n"); return DSERR_GENERIC; } return DS_OK; } static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps) { IDsDriverImpl *This = (IDsDriverImpl *)iface; TRACE("(%p,%p)\n",iface,pCaps); memcpy(pCaps, &(WOutDev[This->wDevID].ossdev->ds_caps), sizeof(DSDRIVERCAPS)); return DS_OK; } static HRESULT WINAPI DSD_CreatePrimaryBuffer(PIDSDRIVER iface, LPWAVEFORMATEX pwfx, DWORD dwFlags, DWORD dwCardAddress, LPDWORD pdwcbBufferSize, LPBYTE *ppbBuffer, LPVOID *ppvObj) { IDsDriverImpl *This = (IDsDriverImpl *)iface; IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj; HRESULT err; audio_buf_info info; int enable = 0; TRACE("(%p,%p,%x,%x,%p,%p,%p)\n",iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj); if (This->primary) return DSERR_ALLOCATED; if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN)) return DSERR_CONTROLUNAVAIL; *ippdsdb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsDriverBufferImpl)); if (*ippdsdb == NULL) return DSERR_OUTOFMEMORY; (*ippdsdb)->lpVtbl = &dsdbvt; (*ippdsdb)->ref = 1; (*ippdsdb)->drv = This; copy_format(pwfx, &(*ippdsdb)->wfex); (*ippdsdb)->fd = WOutDev[This->wDevID].ossdev->fd; (*ippdsdb)->dwFlags = dwFlags; /* check how big the DMA buffer is now */ if (ioctl((*ippdsdb)->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { ERR("ioctl(%s, SNDCTL_DSP_GETOSPACE) failed (%s)\n", WOutDev[This->wDevID].ossdev->dev_name, strerror(errno)); HeapFree(GetProcessHeap(),0,*ippdsdb); *ippdsdb = NULL; return DSERR_GENERIC; } (*ippdsdb)->maplen = (*ippdsdb)->buflen = info.fragstotal * info.fragsize; /* map the DMA buffer */ err = DSDB_MapBuffer(*ippdsdb); if (err != DS_OK) { HeapFree(GetProcessHeap(),0,*ippdsdb); *ippdsdb = NULL; return err; } /* primary buffer is ready to go */ *pdwcbBufferSize = (*ippdsdb)->maplen; *ppbBuffer = (*ippdsdb)->mapping; /* some drivers need some extra nudging after mapping */ WOutDev[This->wDevID].ossdev->bInputEnabled = FALSE; WOutDev[This->wDevID].ossdev->bOutputEnabled = FALSE; enable = getEnables(WOutDev[This->wDevID].ossdev); if (ioctl((*ippdsdb)->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) { ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WOutDev[This->wDevID].ossdev->dev_name, strerror(errno)); return DSERR_GENERIC; } This->primary = *ippdsdb; return DS_OK; } static HRESULT WINAPI DSD_CreateSecondaryBuffer(PIDSDRIVER iface, LPWAVEFORMATEX pwfx, DWORD dwFlags, DWORD dwCardAddress, LPDWORD pdwcbBufferSize, LPBYTE *ppbBuffer, LPVOID *ppvObj) { IDsDriverImpl *This = (IDsDriverImpl *)iface; IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj; FIXME("(%p,%p,%x,%x,%p,%p,%p): stub\n",This,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj); *ippdsdb = 0; return DSERR_UNSUPPORTED; } static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface, LPWAVEFORMATEX pwfx, DWORD dwFlags, DWORD dwCardAddress, LPDWORD pdwcbBufferSize, LPBYTE *ppbBuffer, LPVOID *ppvObj) { TRACE("(%p,%p,%x,%x,%p,%p,%p)\n",iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj); if (dwFlags & DSBCAPS_PRIMARYBUFFER) return DSD_CreatePrimaryBuffer(iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj); return DSD_CreateSecondaryBuffer(iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj); } static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface, PIDSDRIVERBUFFER pBuffer, LPVOID *ppvObj) { /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */ TRACE("(%p,%p): stub\n",iface,pBuffer); return DSERR_INVALIDCALL; } static const IDsDriverVtbl dsdvt = { IDsDriverImpl_QueryInterface, IDsDriverImpl_AddRef, IDsDriverImpl_Release, IDsDriverImpl_GetDriverDesc, IDsDriverImpl_Open, IDsDriverImpl_Close, IDsDriverImpl_GetCaps, IDsDriverImpl_CreateSoundBuffer, IDsDriverImpl_DuplicateSoundBuffer }; static HRESULT WINAPI IDsDriverPropertySetImpl_Create( IDsDriverBufferImpl * dsdb, IDsDriverPropertySetImpl **pdsdps) { IDsDriverPropertySetImpl * dsdps; TRACE("(%p,%p)\n",dsdb,pdsdps); dsdps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dsdps)); if (dsdps == NULL) { WARN("out of memory\n"); return DSERR_OUTOFMEMORY; } dsdps->ref = 0; dsdps->lpVtbl = &dsdpsvt; dsdps->buffer = dsdb; dsdb->property_set = dsdps; IDsDriverBuffer_AddRef((PIDSDRIVER)dsdb); *pdsdps = dsdps; return DS_OK; } static HRESULT WINAPI IDsDriverNotifyImpl_Create( IDsDriverBufferImpl * dsdb, IDsDriverNotifyImpl **pdsdn) { IDsDriverNotifyImpl * dsdn; TRACE("(%p,%p)\n",dsdb,pdsdn); dsdn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dsdn)); if (dsdn == NULL) { WARN("out of memory\n"); return DSERR_OUTOFMEMORY; } dsdn->ref = 0; dsdn->lpVtbl = &dsdnvt; dsdn->buffer = dsdb; dsdb->notify = dsdn; IDsDriverBuffer_AddRef((PIDSDRIVER)dsdb); *pdsdn = dsdn; return DS_OK; } DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv) { IDsDriverImpl** idrv = (IDsDriverImpl**)drv; TRACE("(%d,%p)\n",wDevID,drv); /* the HAL isn't much better than the HEL if we can't do mmap() */ if (!(WOutDev[wDevID].ossdev->duplex_out_caps.dwSupport & WAVECAPS_DIRECTSOUND)) { ERR("DirectSound flag not set\n"); MESSAGE("This sound card's driver does not support direct access\n"); MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n"); return MMSYSERR_NOTSUPPORTED; } *idrv = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsDriverImpl)); if (!*idrv) return MMSYSERR_NOMEM; (*idrv)->lpVtbl = &dsdvt; (*idrv)->ref = 1; (*idrv)->wDevID = wDevID; (*idrv)->primary = NULL; (*idrv)->nrofsecondaries = 0; (*idrv)->secondaries = NULL; return MMSYSERR_NOERROR; } DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc) { TRACE("(%d,%p)\n",wDevID,desc); memcpy(desc, &(WOutDev[wDevID].ossdev->ds_desc), sizeof(DSDRIVERDESC)); return MMSYSERR_NOERROR; } #endif /* HAVE_OSS */