1331 lines
44 KiB
C
1331 lines
44 KiB
C
/*
|
|
* Direct Sound Capture driver
|
|
*
|
|
* Copyright 2004 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 "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#ifdef HAVE_SYS_IOCTL_H
|
|
# include <sys/ioctl.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_MMAN_H
|
|
# include <sys/mman.h>
|
|
#endif
|
|
#ifdef HAVE_POLL_H
|
|
#include <poll.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_POLL_H
|
|
# include <sys/poll.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_ERRNO_H
|
|
#include <sys/errno.h>
|
|
#endif
|
|
#include <sys/soundcard.h>
|
|
|
|
#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 "wine/debug.h"
|
|
|
|
#include "audio.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(dscapture);
|
|
|
|
/*======================================================================*
|
|
* Low level DSOUND capture definitions *
|
|
*======================================================================*/
|
|
|
|
typedef struct IDsCaptureDriverPropertySetImpl IDsCaptureDriverPropertySetImpl;
|
|
typedef struct IDsCaptureDriverNotifyImpl IDsCaptureDriverNotifyImpl;
|
|
typedef struct IDsCaptureDriverImpl IDsCaptureDriverImpl;
|
|
typedef struct IDsCaptureDriverBufferImpl IDsCaptureDriverBufferImpl;
|
|
|
|
struct IDsCaptureDriverPropertySetImpl
|
|
{
|
|
/* IUnknown fields */
|
|
IDsDriverPropertySet IDsDriverPropertySet_iface;
|
|
LONG ref;
|
|
|
|
IDsCaptureDriverBufferImpl* capture_buffer;
|
|
};
|
|
|
|
struct IDsCaptureDriverNotifyImpl
|
|
{
|
|
/* IUnknown fields */
|
|
IDsDriverNotify IDsDriverNotify_iface;
|
|
LONG ref;
|
|
|
|
IDsCaptureDriverBufferImpl* capture_buffer;
|
|
};
|
|
|
|
struct IDsCaptureDriverImpl
|
|
{
|
|
/* IUnknown fields */
|
|
IDsCaptureDriver IDsCaptureDriver_iface;
|
|
LONG ref;
|
|
|
|
/* IDsCaptureDriverImpl fields */
|
|
UINT wDevID;
|
|
IDsCaptureDriverBufferImpl* capture_buffer;
|
|
};
|
|
|
|
struct IDsCaptureDriverBufferImpl
|
|
{
|
|
/* IUnknown fields */
|
|
IDsCaptureDriverBuffer IDsCaptureDriverBuffer_iface;
|
|
LONG ref;
|
|
|
|
/* IDsCaptureDriverBufferImpl fields */
|
|
IDsCaptureDriverImpl* drv;
|
|
LPBYTE buffer; /* user buffer */
|
|
DWORD buflen; /* user buffer length */
|
|
LPBYTE mapping; /* DMA buffer */
|
|
DWORD maplen; /* DMA buffer length */
|
|
BOOL is_direct_map; /* DMA == user ? */
|
|
DWORD fragsize;
|
|
DWORD map_writepos; /* DMA write offset */
|
|
DWORD map_readpos; /* DMA read offset */
|
|
DWORD writeptr; /* user write offset */
|
|
DWORD readptr; /* user read offset */
|
|
|
|
/* IDsDriverNotifyImpl fields */
|
|
IDsCaptureDriverNotifyImpl* notify;
|
|
int notify_index;
|
|
LPDSBPOSITIONNOTIFY notifies;
|
|
int nrofnotifies;
|
|
|
|
/* IDsDriverPropertySetImpl fields */
|
|
IDsCaptureDriverPropertySetImpl* property_set;
|
|
|
|
BOOL is_capturing;
|
|
BOOL is_looping;
|
|
WAVEFORMATEX wfx;
|
|
HANDLE hThread;
|
|
DWORD dwThreadID;
|
|
HANDLE hStartUpEvent;
|
|
HANDLE hExitEvent;
|
|
int pipe_fd[2];
|
|
int fd;
|
|
};
|
|
|
|
static inline IDsCaptureDriverPropertySetImpl *impl_from_IDsDriverPropertySet(IDsDriverPropertySet *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, IDsCaptureDriverPropertySetImpl, IDsDriverPropertySet_iface);
|
|
}
|
|
|
|
static inline IDsCaptureDriverNotifyImpl *impl_from_IDsDriverNotify(IDsDriverNotify *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, IDsCaptureDriverNotifyImpl, IDsDriverNotify_iface);
|
|
}
|
|
|
|
static inline IDsCaptureDriverImpl *impl_from_IDsCaptureDriver(IDsCaptureDriver *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, IDsCaptureDriverImpl, IDsCaptureDriver_iface);
|
|
}
|
|
|
|
static inline IDsCaptureDriverBufferImpl *impl_from_IDsCaptureDriverBuffer(IDsCaptureDriverBuffer *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, IDsCaptureDriverBufferImpl, IDsCaptureDriverBuffer_iface);
|
|
}
|
|
|
|
static HRESULT IDsCaptureDriverPropertySetImpl_Create(
|
|
IDsCaptureDriverBufferImpl * dscdb,
|
|
IDsCaptureDriverPropertySetImpl **pdscdps);
|
|
|
|
static HRESULT IDsCaptureDriverNotifyImpl_Create(
|
|
IDsCaptureDriverBufferImpl * dsdcb,
|
|
IDsCaptureDriverNotifyImpl **pdscdn);
|
|
|
|
/*======================================================================*
|
|
* Low level DSOUND capture property set implementation *
|
|
*======================================================================*/
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_QueryInterface(
|
|
PIDSDRIVERPROPERTYSET iface,
|
|
REFIID riid,
|
|
LPVOID *ppobj)
|
|
{
|
|
IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
|
|
TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
|
|
|
|
if ( IsEqualGUID(riid, &IID_IUnknown) ||
|
|
IsEqualGUID(riid, &IID_IDsDriverPropertySet) ) {
|
|
IDsDriverPropertySet_AddRef(iface);
|
|
*ppobj = This;
|
|
return DS_OK;
|
|
}
|
|
|
|
FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
|
|
|
|
*ppobj = 0;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI IDsCaptureDriverPropertySetImpl_AddRef(
|
|
PIDSDRIVERPROPERTYSET iface)
|
|
{
|
|
IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
|
|
ULONG refCount = InterlockedIncrement(&This->ref);
|
|
|
|
TRACE("(%p) ref was %d\n", This, refCount - 1);
|
|
|
|
return refCount;
|
|
}
|
|
|
|
static ULONG WINAPI IDsCaptureDriverPropertySetImpl_Release(
|
|
PIDSDRIVERPROPERTYSET iface)
|
|
{
|
|
IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
|
|
ULONG refCount = InterlockedDecrement(&This->ref);
|
|
|
|
TRACE("(%p) ref was %d\n", This, refCount + 1);
|
|
|
|
if (!refCount) {
|
|
IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER)This->capture_buffer);
|
|
This->capture_buffer->property_set = NULL;
|
|
HeapFree(GetProcessHeap(),0,This);
|
|
TRACE("(%p) released\n",This);
|
|
}
|
|
return refCount;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Get(
|
|
PIDSDRIVERPROPERTYSET iface,
|
|
PDSPROPERTY pDsProperty,
|
|
LPVOID pPropertyParams,
|
|
ULONG cbPropertyParams,
|
|
LPVOID pPropertyData,
|
|
ULONG cbPropertyData,
|
|
PULONG pcbReturnedData )
|
|
{
|
|
IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
|
|
FIXME("(%p,%p,%p,%x,%p,%x,%p)\n",This,pDsProperty,pPropertyParams,
|
|
cbPropertyParams,pPropertyData,cbPropertyData,pcbReturnedData);
|
|
return DSERR_UNSUPPORTED;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Set(
|
|
PIDSDRIVERPROPERTYSET iface,
|
|
PDSPROPERTY pDsProperty,
|
|
LPVOID pPropertyParams,
|
|
ULONG cbPropertyParams,
|
|
LPVOID pPropertyData,
|
|
ULONG cbPropertyData )
|
|
{
|
|
IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
|
|
FIXME("(%p,%p,%p,%x,%p,%x)\n",This,pDsProperty,pPropertyParams,
|
|
cbPropertyParams,pPropertyData,cbPropertyData);
|
|
return DSERR_UNSUPPORTED;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_QuerySupport(
|
|
PIDSDRIVERPROPERTYSET iface,
|
|
REFGUID PropertySetId,
|
|
ULONG PropertyId,
|
|
PULONG pSupport )
|
|
{
|
|
IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
|
|
FIXME("(%p,%s,%x,%p)\n",This,debugstr_guid(PropertySetId),PropertyId,
|
|
pSupport);
|
|
return DSERR_UNSUPPORTED;
|
|
}
|
|
|
|
static const IDsDriverPropertySetVtbl dscdpsvt =
|
|
{
|
|
IDsCaptureDriverPropertySetImpl_QueryInterface,
|
|
IDsCaptureDriverPropertySetImpl_AddRef,
|
|
IDsCaptureDriverPropertySetImpl_Release,
|
|
IDsCaptureDriverPropertySetImpl_Get,
|
|
IDsCaptureDriverPropertySetImpl_Set,
|
|
IDsCaptureDriverPropertySetImpl_QuerySupport,
|
|
};
|
|
|
|
/*======================================================================*
|
|
* Low level DSOUND capture notify implementation *
|
|
*======================================================================*/
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverNotifyImpl_QueryInterface(
|
|
PIDSDRIVERNOTIFY iface,
|
|
REFIID riid,
|
|
LPVOID *ppobj)
|
|
{
|
|
IDsCaptureDriverNotifyImpl *This = impl_from_IDsDriverNotify(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 IDsCaptureDriverNotifyImpl_AddRef(
|
|
PIDSDRIVERNOTIFY iface)
|
|
{
|
|
IDsCaptureDriverNotifyImpl *This = impl_from_IDsDriverNotify(iface);
|
|
ULONG refCount = InterlockedIncrement(&This->ref);
|
|
|
|
TRACE("(%p) ref was %d\n", This, refCount - 1);
|
|
|
|
return refCount;
|
|
}
|
|
|
|
static ULONG WINAPI IDsCaptureDriverNotifyImpl_Release(
|
|
PIDSDRIVERNOTIFY iface)
|
|
{
|
|
IDsCaptureDriverNotifyImpl *This = impl_from_IDsDriverNotify(iface);
|
|
ULONG refCount = InterlockedDecrement(&This->ref);
|
|
|
|
TRACE("(%p) ref was %d\n", This, refCount + 1);
|
|
|
|
if (!refCount) {
|
|
IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER)This->capture_buffer);
|
|
This->capture_buffer->notify = NULL;
|
|
HeapFree(GetProcessHeap(),0,This);
|
|
TRACE("(%p) released\n",This);
|
|
}
|
|
return refCount;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverNotifyImpl_SetNotificationPositions(
|
|
PIDSDRIVERNOTIFY iface,
|
|
DWORD howmuch,
|
|
LPCDSBPOSITIONNOTIFY notify)
|
|
{
|
|
IDsCaptureDriverNotifyImpl *This = impl_from_IDsDriverNotify(iface);
|
|
TRACE("(%p,0x%08x,%p)\n",This,howmuch,notify);
|
|
|
|
if (!notify) {
|
|
WARN("invalid parameter\n");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
if (TRACE_ON(dscapture)) {
|
|
DWORD i;
|
|
for (i=0;i<howmuch;i++)
|
|
TRACE("notify at %d to 0x%08lx\n",
|
|
notify[i].dwOffset,(DWORD_PTR)notify[i].hEventNotify);
|
|
}
|
|
|
|
/* Make an internal copy of the caller-supplied array.
|
|
* Replace the existing copy if one is already present. */
|
|
if (This->capture_buffer->notifies)
|
|
This->capture_buffer->notifies = HeapReAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY, This->capture_buffer->notifies,
|
|
howmuch * sizeof(DSBPOSITIONNOTIFY));
|
|
else
|
|
This->capture_buffer->notifies = HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY, howmuch * sizeof(DSBPOSITIONNOTIFY));
|
|
|
|
memcpy(This->capture_buffer->notifies, notify,
|
|
howmuch * sizeof(DSBPOSITIONNOTIFY));
|
|
This->capture_buffer->nrofnotifies = howmuch;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static const IDsDriverNotifyVtbl dscdnvt =
|
|
{
|
|
IDsCaptureDriverNotifyImpl_QueryInterface,
|
|
IDsCaptureDriverNotifyImpl_AddRef,
|
|
IDsCaptureDriverNotifyImpl_Release,
|
|
IDsCaptureDriverNotifyImpl_SetNotificationPositions,
|
|
};
|
|
|
|
/*======================================================================*
|
|
* Low level DSOUND capture implementation *
|
|
*======================================================================*/
|
|
|
|
static HRESULT DSCDB_MapBuffer(IDsCaptureDriverBufferImpl *dscdb)
|
|
{
|
|
if (!dscdb->mapping) {
|
|
dscdb->mapping = mmap(NULL, dscdb->maplen, PROT_READ, MAP_SHARED,
|
|
WInDev[dscdb->drv->wDevID].ossdev.fd, 0);
|
|
if (dscdb->mapping == (LPBYTE)-1) {
|
|
TRACE("(%p): Could not map sound device for direct access (%s)\n",
|
|
dscdb, strerror(errno));
|
|
return DSERR_GENERIC;
|
|
}
|
|
TRACE("(%p): sound device has been mapped for direct access at %p, "
|
|
"size=%d\n", dscdb, dscdb->mapping, dscdb->maplen);
|
|
}
|
|
return DS_OK;
|
|
}
|
|
|
|
static HRESULT DSCDB_UnmapBuffer(IDsCaptureDriverBufferImpl *dscdb)
|
|
{
|
|
if (dscdb->mapping) {
|
|
if (munmap(dscdb->mapping, dscdb->maplen) < 0) {
|
|
ERR("(%p): Could not unmap sound device (%s)\n",
|
|
dscdb, strerror(errno));
|
|
return DSERR_GENERIC;
|
|
}
|
|
dscdb->mapping = NULL;
|
|
TRACE("(%p): sound device unmapped\n", dscdb);
|
|
}
|
|
return DS_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverBufferImpl_QueryInterface(
|
|
PIDSCDRIVERBUFFER iface,
|
|
REFIID riid,
|
|
LPVOID *ppobj)
|
|
{
|
|
IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
|
|
TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
|
|
|
|
*ppobj = 0;
|
|
|
|
if ( IsEqualGUID(riid, &IID_IUnknown) ||
|
|
IsEqualGUID(riid, &IID_IDsCaptureDriverBuffer) ) {
|
|
IDsCaptureDriverBuffer_AddRef(iface);
|
|
*ppobj = This;
|
|
return DS_OK;
|
|
}
|
|
|
|
if ( IsEqualGUID( &IID_IDsDriverNotify, riid ) ) {
|
|
if (!This->notify)
|
|
IDsCaptureDriverNotifyImpl_Create(This, &(This->notify));
|
|
if (This->notify) {
|
|
IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY)This->notify);
|
|
*ppobj = This->notify;
|
|
return DS_OK;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
if ( IsEqualGUID( &IID_IDsDriverPropertySet, riid ) ) {
|
|
if (!This->property_set)
|
|
IDsCaptureDriverPropertySetImpl_Create(This, &(This->property_set));
|
|
if (This->property_set) {
|
|
IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET)This->property_set);
|
|
*ppobj = This->property_set;
|
|
return DS_OK;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
FIXME("(%p,%s,%p) unsupported GUID\n", This, debugstr_guid(riid), ppobj);
|
|
return DSERR_UNSUPPORTED;
|
|
}
|
|
|
|
static ULONG WINAPI IDsCaptureDriverBufferImpl_AddRef(PIDSCDRIVERBUFFER iface)
|
|
{
|
|
IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
|
|
ULONG refCount = InterlockedIncrement(&This->ref);
|
|
|
|
TRACE("(%p) ref was %d\n", This, refCount - 1);
|
|
|
|
return refCount;
|
|
}
|
|
|
|
static ULONG WINAPI IDsCaptureDriverBufferImpl_Release(PIDSCDRIVERBUFFER iface)
|
|
{
|
|
IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
|
|
ULONG refCount = InterlockedDecrement(&This->ref);
|
|
TRACE("(%p) ref was %d\n", This, refCount + 1);
|
|
|
|
if (!refCount) {
|
|
WINE_WAVEIN* wwi;
|
|
|
|
wwi = &WInDev[This->drv->wDevID];
|
|
|
|
if (This->hThread) {
|
|
int x = 0;
|
|
|
|
/* request thread termination */
|
|
write(This->pipe_fd[1], &x, sizeof(x));
|
|
|
|
/* wait for reply */
|
|
WaitForSingleObject(This->hExitEvent, INFINITE);
|
|
CloseHandle(This->hExitEvent);
|
|
}
|
|
|
|
close(This->pipe_fd[0]);
|
|
close(This->pipe_fd[1]);
|
|
|
|
DSCDB_UnmapBuffer(This);
|
|
|
|
OSS_CloseDevice(&wwi->ossdev);
|
|
wwi->state = WINE_WS_CLOSED;
|
|
wwi->dwFragmentSize = 0;
|
|
This->drv->capture_buffer = NULL;
|
|
|
|
HeapFree(GetProcessHeap(), 0, This->notifies);
|
|
HeapFree(GetProcessHeap(),0,This);
|
|
TRACE("(%p) released\n",This);
|
|
}
|
|
return refCount;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverBufferImpl_Lock(
|
|
PIDSCDRIVERBUFFER iface,
|
|
LPVOID* ppvAudio1,
|
|
LPDWORD pdwLen1,
|
|
LPVOID* ppvAudio2,
|
|
LPDWORD pdwLen2,
|
|
DWORD dwWritePosition,
|
|
DWORD dwWriteLen,
|
|
DWORD dwFlags)
|
|
{
|
|
IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
|
|
TRACE("(%p,%p,%p,%p,%p,%d,%d,0x%08x)\n",This,ppvAudio1,pdwLen1,
|
|
ppvAudio2,pdwLen2,dwWritePosition,dwWriteLen,dwFlags);
|
|
|
|
if (This->is_direct_map) {
|
|
if (ppvAudio1)
|
|
*ppvAudio1 = This->mapping + dwWritePosition;
|
|
|
|
if (dwWritePosition + dwWriteLen < This->maplen) {
|
|
if (pdwLen1)
|
|
*pdwLen1 = dwWriteLen;
|
|
if (ppvAudio2)
|
|
*ppvAudio2 = 0;
|
|
if (pdwLen2)
|
|
*pdwLen2 = 0;
|
|
} else {
|
|
if (pdwLen1)
|
|
*pdwLen1 = This->maplen - dwWritePosition;
|
|
if (ppvAudio2)
|
|
*ppvAudio2 = 0;
|
|
if (pdwLen2)
|
|
*pdwLen2 = dwWriteLen - (This->maplen - dwWritePosition);
|
|
}
|
|
} else {
|
|
if (ppvAudio1)
|
|
*ppvAudio1 = This->buffer + dwWritePosition;
|
|
|
|
if (dwWritePosition + dwWriteLen < This->buflen) {
|
|
if (pdwLen1)
|
|
*pdwLen1 = dwWriteLen;
|
|
if (ppvAudio2)
|
|
*ppvAudio2 = 0;
|
|
if (pdwLen2)
|
|
*pdwLen2 = 0;
|
|
} else {
|
|
if (pdwLen1)
|
|
*pdwLen1 = This->buflen - dwWritePosition;
|
|
if (ppvAudio2)
|
|
*ppvAudio2 = 0;
|
|
if (pdwLen2)
|
|
*pdwLen2 = dwWriteLen - (This->buflen - dwWritePosition);
|
|
}
|
|
}
|
|
|
|
return DS_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverBufferImpl_Unlock(
|
|
PIDSCDRIVERBUFFER iface,
|
|
LPVOID pvAudio1,
|
|
DWORD dwLen1,
|
|
LPVOID pvAudio2,
|
|
DWORD dwLen2)
|
|
{
|
|
IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
|
|
TRACE("(%p,%p,%d,%p,%d)\n",This,pvAudio1,dwLen1,pvAudio2,dwLen2);
|
|
|
|
if (This->is_direct_map)
|
|
This->map_readpos = (This->map_readpos + dwLen1 + dwLen2) % This->maplen;
|
|
else
|
|
This->readptr = (This->readptr + dwLen1 + dwLen2) % This->buflen;
|
|
|
|
return DS_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetPosition(
|
|
PIDSCDRIVERBUFFER iface,
|
|
LPDWORD lpdwCapture,
|
|
LPDWORD lpdwRead)
|
|
{
|
|
IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
|
|
TRACE("(%p,%p,%p)\n",This,lpdwCapture,lpdwRead);
|
|
|
|
if (WInDev[This->drv->wDevID].state == WINE_WS_CLOSED) {
|
|
ERR("device not open, but accessing?\n");
|
|
return DSERR_UNINITIALIZED;
|
|
}
|
|
|
|
if (!This->is_capturing) {
|
|
if (lpdwCapture)
|
|
*lpdwCapture = 0;
|
|
if (lpdwRead)
|
|
*lpdwRead = 0;
|
|
}
|
|
|
|
if (This->is_direct_map) {
|
|
if (lpdwCapture)
|
|
*lpdwCapture = This->map_writepos;
|
|
if (lpdwRead) {
|
|
*lpdwRead = This->map_readpos;
|
|
}
|
|
} else {
|
|
if (lpdwCapture)
|
|
*lpdwCapture = This->writeptr;
|
|
if (lpdwRead)
|
|
*lpdwRead = This->readptr;
|
|
}
|
|
|
|
TRACE("capturepos=%d, readpos=%d\n", lpdwCapture?*lpdwCapture:0,
|
|
lpdwRead?*lpdwRead:0);
|
|
return DS_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetStatus(
|
|
PIDSCDRIVERBUFFER iface,
|
|
LPDWORD lpdwStatus)
|
|
{
|
|
IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
|
|
TRACE("(%p,%p)\n",This,lpdwStatus);
|
|
|
|
if (This->is_capturing) {
|
|
if (This->is_looping)
|
|
*lpdwStatus = DSCBSTATUS_CAPTURING | DSCBSTATUS_LOOPING;
|
|
else
|
|
*lpdwStatus = DSCBSTATUS_CAPTURING;
|
|
} else
|
|
*lpdwStatus = 0;
|
|
|
|
return DS_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverBufferImpl_Start(
|
|
PIDSCDRIVERBUFFER iface,
|
|
DWORD dwFlags)
|
|
{
|
|
IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
|
|
int enable;
|
|
TRACE("(%p,%x)\n",This,dwFlags);
|
|
|
|
if (This->is_capturing)
|
|
return DS_OK;
|
|
|
|
if (dwFlags & DSCBSTART_LOOPING)
|
|
This->is_looping = TRUE;
|
|
|
|
WInDev[This->drv->wDevID].ossdev.bInputEnabled = TRUE;
|
|
enable = getEnables(&WInDev[This->drv->wDevID].ossdev);
|
|
if (ioctl(WInDev[This->drv->wDevID].ossdev.fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
|
|
if (errno == EINVAL) {
|
|
/* Don't give up yet. OSS trigger support is inconsistent. */
|
|
if (WInDev[This->drv->wDevID].ossdev.open_count == 1) {
|
|
/* try the opposite output enable */
|
|
if (WInDev[This->drv->wDevID].ossdev.bOutputEnabled == FALSE)
|
|
WInDev[This->drv->wDevID].ossdev.bOutputEnabled = TRUE;
|
|
else
|
|
WInDev[This->drv->wDevID].ossdev.bOutputEnabled = FALSE;
|
|
/* try it again */
|
|
enable = getEnables(&WInDev[This->drv->wDevID].ossdev);
|
|
if (ioctl(WInDev[This->drv->wDevID].ossdev.fd, SNDCTL_DSP_SETTRIGGER, &enable) >= 0) {
|
|
This->is_capturing = TRUE;
|
|
return DS_OK;
|
|
}
|
|
}
|
|
}
|
|
ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
|
|
WInDev[This->drv->wDevID].ossdev.dev_name, strerror(errno));
|
|
WInDev[This->drv->wDevID].ossdev.bInputEnabled = FALSE;
|
|
return DSERR_GENERIC;
|
|
}
|
|
|
|
This->is_capturing = TRUE;
|
|
return DS_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverBufferImpl_Stop(PIDSCDRIVERBUFFER iface)
|
|
{
|
|
IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
|
|
int enable;
|
|
TRACE("(%p)\n",This);
|
|
|
|
if (!This->is_capturing)
|
|
return DS_OK;
|
|
|
|
/* no more capturing */
|
|
WInDev[This->drv->wDevID].ossdev.bInputEnabled = FALSE;
|
|
enable = getEnables(&WInDev[This->drv->wDevID].ossdev);
|
|
if (ioctl(WInDev[This->drv->wDevID].ossdev.fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
|
|
ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
|
|
WInDev[This->drv->wDevID].ossdev.dev_name, strerror(errno));
|
|
return DSERR_GENERIC;
|
|
}
|
|
|
|
/* send a final event if necessary */
|
|
if (This->nrofnotifies > 0) {
|
|
if (This->notifies[This->nrofnotifies - 1].dwOffset == DSBPN_OFFSETSTOP)
|
|
SetEvent(This->notifies[This->nrofnotifies - 1].hEventNotify);
|
|
}
|
|
|
|
This->is_capturing = FALSE;
|
|
This->is_looping = FALSE;
|
|
|
|
if (This->hThread) {
|
|
int x = 0;
|
|
write(This->pipe_fd[1], &x, sizeof(x));
|
|
WaitForSingleObject(This->hExitEvent, INFINITE);
|
|
CloseHandle(This->hExitEvent);
|
|
This->hExitEvent = INVALID_HANDLE_VALUE;
|
|
This->hThread = 0;
|
|
}
|
|
|
|
return DS_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverBufferImpl_SetFormat(
|
|
PIDSCDRIVERBUFFER iface,
|
|
LPWAVEFORMATEX pwfx)
|
|
{
|
|
IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
|
|
FIXME("(%p): stub!\n",This);
|
|
return DSERR_UNSUPPORTED;
|
|
}
|
|
|
|
static const IDsCaptureDriverBufferVtbl dscdbvt =
|
|
{
|
|
IDsCaptureDriverBufferImpl_QueryInterface,
|
|
IDsCaptureDriverBufferImpl_AddRef,
|
|
IDsCaptureDriverBufferImpl_Release,
|
|
IDsCaptureDriverBufferImpl_Lock,
|
|
IDsCaptureDriverBufferImpl_Unlock,
|
|
IDsCaptureDriverBufferImpl_SetFormat,
|
|
IDsCaptureDriverBufferImpl_GetPosition,
|
|
IDsCaptureDriverBufferImpl_GetStatus,
|
|
IDsCaptureDriverBufferImpl_Start,
|
|
IDsCaptureDriverBufferImpl_Stop
|
|
};
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverImpl_QueryInterface(
|
|
PIDSCDRIVER iface,
|
|
REFIID riid,
|
|
LPVOID *ppobj)
|
|
{
|
|
IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
|
|
TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
|
|
|
|
if ( IsEqualGUID(riid, &IID_IUnknown) ||
|
|
IsEqualGUID(riid, &IID_IDsCaptureDriver) ) {
|
|
IDsCaptureDriver_AddRef(iface);
|
|
*ppobj = This;
|
|
return DS_OK;
|
|
}
|
|
|
|
FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
|
|
|
|
*ppobj = 0;
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI IDsCaptureDriverImpl_AddRef(PIDSCDRIVER iface)
|
|
{
|
|
IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
|
|
ULONG refCount = InterlockedIncrement(&This->ref);
|
|
|
|
TRACE("(%p) ref was %d\n", This, refCount - 1);
|
|
|
|
return refCount;
|
|
}
|
|
|
|
static ULONG WINAPI IDsCaptureDriverImpl_Release(PIDSCDRIVER iface)
|
|
{
|
|
IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(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 IDsCaptureDriverImpl_GetDriverDesc(
|
|
PIDSCDRIVER iface,
|
|
PDSDRIVERDESC pDesc)
|
|
{
|
|
IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
|
|
TRACE("(%p,%p)\n",This,pDesc);
|
|
|
|
if (!pDesc) {
|
|
TRACE("invalid parameter\n");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
/* copy version from driver */
|
|
*pDesc = WInDev[This->wDevID].ossdev.ds_desc;
|
|
|
|
pDesc->dnDevNode = WInDev[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 IDsCaptureDriverImpl_Open(PIDSCDRIVER iface)
|
|
{
|
|
IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
|
|
TRACE("(%p)\n",This);
|
|
return DS_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverImpl_Close(PIDSCDRIVER iface)
|
|
{
|
|
IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
|
|
TRACE("(%p)\n",This);
|
|
if (This->capture_buffer) {
|
|
ERR("problem with DirectSound: capture buffer not released\n");
|
|
return DSERR_GENERIC;
|
|
}
|
|
return DS_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverImpl_GetCaps(
|
|
PIDSCDRIVER iface,
|
|
PDSCDRIVERCAPS pCaps)
|
|
{
|
|
IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
|
|
TRACE("(%p,%p)\n",This,pCaps);
|
|
*pCaps = WInDev[This->wDevID].ossdev.dsc_caps;
|
|
return DS_OK;
|
|
}
|
|
|
|
static void DSCDB_CheckEvent(
|
|
IDsCaptureDriverBufferImpl *dscb,
|
|
DWORD writepos,
|
|
DWORD len,
|
|
DWORD buflen)
|
|
{
|
|
LPDSBPOSITIONNOTIFY event = dscb->notifies + dscb->notify_index;
|
|
DWORD offset = event->dwOffset;
|
|
TRACE("(%p,%d,%d,%d)\n", dscb, writepos, len, buflen);
|
|
|
|
TRACE("(%p) buflen = %d, writeptr = %d\n",
|
|
dscb, dscb->buflen, dscb->writeptr);
|
|
TRACE("checking %d, position %d, event = %p\n",
|
|
dscb->notify_index, offset, event->hEventNotify);
|
|
|
|
if ((writepos + len) > offset) {
|
|
TRACE("signalled event %p (%d) %d\n",
|
|
event->hEventNotify, dscb->notify_index, offset);
|
|
SetEvent(event->hEventNotify);
|
|
dscb->notify_index = (dscb->notify_index + 1) % dscb->nrofnotifies;
|
|
return;
|
|
} else if ((writepos + len) > buflen) {
|
|
writepos = writepos + len - buflen;
|
|
if ((writepos + len) > offset) {
|
|
TRACE("signalled event %p (%d) %d\n",
|
|
event->hEventNotify, dscb->notify_index, offset);
|
|
SetEvent(event->hEventNotify);
|
|
dscb->notify_index = (dscb->notify_index + 1) % dscb->nrofnotifies;
|
|
return;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* FIXME: using memcpy can cause strange crashes so use this fake one */
|
|
static void * my_memcpy(void * dst, const void * src, int length)
|
|
{
|
|
int i;
|
|
for (i = 0; i < length; i++)
|
|
((char *)dst)[i] = ((const char *)src)[i];
|
|
return dst;
|
|
}
|
|
|
|
static DWORD CALLBACK DSCDB_Thread(LPVOID lpParameter)
|
|
{
|
|
IDsCaptureDriverBufferImpl *This = lpParameter;
|
|
struct pollfd poll_list[2];
|
|
int retval;
|
|
DWORD offset = 0;
|
|
DWORD map_offset = 0;
|
|
TRACE("(%p)\n", lpParameter);
|
|
|
|
poll_list[0].fd = This->fd; /* data available */
|
|
poll_list[1].fd = This->pipe_fd[0]; /* message from parent process */
|
|
poll_list[0].events = POLLIN;
|
|
poll_list[1].events = POLLIN;
|
|
|
|
/* let other process know we are running */
|
|
SetEvent(This->hStartUpEvent);
|
|
|
|
while (1) {
|
|
/* wait for something to happen */
|
|
retval = poll(poll_list,(unsigned long)2,-1);
|
|
/* Retval will always be greater than 0 or -1 in this case.
|
|
* Since we're doing it while blocking
|
|
*/
|
|
if (retval < 0) {
|
|
ERR("Error while polling: %s\n",strerror(errno));
|
|
continue;
|
|
}
|
|
|
|
/* check for exit command */
|
|
if ((poll_list[1].revents & POLLIN) == POLLIN) {
|
|
TRACE("(%p) done\n", lpParameter);
|
|
/* acknowledge command and exit */
|
|
SetEvent(This->hExitEvent);
|
|
ExitThread(0);
|
|
return 0;
|
|
}
|
|
|
|
/* check for data */
|
|
if ((poll_list[0].revents & POLLIN) == POLLIN) {
|
|
count_info info;
|
|
int fragsize, first, second;
|
|
|
|
/* get the current DMA position */
|
|
if (ioctl(This->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
|
|
ERR("ioctl(%s, SNDCTL_DSP_GETIPTR) failed (%s)\n",
|
|
WInDev[This->drv->wDevID].ossdev.dev_name, strerror(errno));
|
|
return DSERR_GENERIC;
|
|
}
|
|
|
|
if (This->is_direct_map) {
|
|
offset = This->map_writepos;
|
|
This->map_writepos = info.ptr;
|
|
|
|
if (info.ptr < offset)
|
|
fragsize = info.ptr + This->maplen - offset;
|
|
else
|
|
fragsize = info.ptr - offset;
|
|
|
|
DSCDB_CheckEvent(This, offset, fragsize, This->maplen);
|
|
} else {
|
|
map_offset = This->map_writepos;
|
|
offset = This->writeptr;
|
|
|
|
/* test for mmap buffer wrap */
|
|
if (info.ptr < map_offset) {
|
|
/* mmap buffer wrapped */
|
|
fragsize = info.ptr + This->maplen - map_offset;
|
|
|
|
/* check for user buffer wrap */
|
|
if ((offset + fragsize) > This->buflen) {
|
|
/* both buffers wrapped
|
|
* figure out which wrapped first
|
|
*/
|
|
if ((This->maplen - map_offset) > (This->buflen - offset)) {
|
|
/* user buffer wrapped first */
|
|
first = This->buflen - offset;
|
|
second = (This->maplen - map_offset) - first;
|
|
my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
|
|
my_memcpy(This->buffer, This->mapping + map_offset + first, second);
|
|
my_memcpy(This->buffer + second, This->mapping, fragsize - (first + second));
|
|
} else {
|
|
/* mmap buffer wrapped first */
|
|
first = This->maplen - map_offset;
|
|
second = (This->buflen - offset) - first;
|
|
my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
|
|
my_memcpy(This->buffer + offset + first, This->mapping, second);
|
|
my_memcpy(This->buffer, This->mapping + second, fragsize - (first + second));
|
|
}
|
|
} else {
|
|
/* only mmap buffer wrapped */
|
|
first = This->maplen - map_offset;
|
|
my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
|
|
my_memcpy(This->buffer + offset + first, This->mapping, fragsize - first);
|
|
}
|
|
} else {
|
|
/* mmap buffer didn't wrap */
|
|
fragsize = info.ptr - map_offset;
|
|
|
|
/* check for user buffer wrap */
|
|
if ((offset + fragsize) > This->buflen) {
|
|
first = This->buflen - offset;
|
|
my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
|
|
my_memcpy(This->buffer, This->mapping + map_offset + first, fragsize - first);
|
|
} else
|
|
my_memcpy(This->buffer + offset, This->mapping + map_offset, fragsize);
|
|
}
|
|
|
|
This->map_writepos = info.ptr;
|
|
This->writeptr = (This->writeptr + fragsize) % This->buflen;
|
|
DSCDB_CheckEvent(This, offset, fragsize, This->buflen);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static HRESULT WINAPI IDsCaptureDriverImpl_CreateCaptureBuffer(
|
|
PIDSCDRIVER iface,
|
|
LPWAVEFORMATEX pwfx,
|
|
DWORD dwFlags,
|
|
DWORD dwCardAddress,
|
|
LPDWORD pdwcbBufferSize,
|
|
LPBYTE *ppbBuffer,
|
|
LPVOID *ppvObj)
|
|
{
|
|
IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
|
|
IDsCaptureDriverBufferImpl** ippdscdb = (IDsCaptureDriverBufferImpl**)ppvObj;
|
|
HRESULT err;
|
|
audio_buf_info info;
|
|
int audio_fragment, fsize, shift, ret;
|
|
BOOL bNewBuffer = FALSE;
|
|
WINE_WAVEIN* wwi;
|
|
TRACE("(%p,%p,%x,%x,%p,%p,%p)\n",This,pwfx,dwFlags,dwCardAddress,
|
|
pdwcbBufferSize,ppbBuffer,ppvObj);
|
|
|
|
if (This->capture_buffer) {
|
|
TRACE("already allocated\n");
|
|
return DSERR_ALLOCATED;
|
|
}
|
|
|
|
/* must be given a buffer size */
|
|
if (pdwcbBufferSize == NULL || *pdwcbBufferSize == 0) {
|
|
TRACE("invalid parameter: pdwcbBufferSize\n");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
/* must be given a buffer pointer */
|
|
if (ppbBuffer == NULL) {
|
|
TRACE("invalid parameter: ppbBuffer\n");
|
|
return DSERR_INVALIDPARAM;
|
|
}
|
|
|
|
/* may or may not be given a buffer */
|
|
if (*ppbBuffer == NULL) {
|
|
TRACE("creating buffer\n");
|
|
bNewBuffer = TRUE; /* not given a buffer so create one */
|
|
} else
|
|
TRACE("using supplied buffer\n");
|
|
|
|
*ippdscdb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsCaptureDriverBufferImpl));
|
|
if (*ippdscdb == NULL) {
|
|
TRACE("out of memory\n");
|
|
return DSERR_OUTOFMEMORY;
|
|
}
|
|
|
|
(*ippdscdb)->IDsCaptureDriverBuffer_iface.lpVtbl = &dscdbvt;
|
|
(*ippdscdb)->ref = 1;
|
|
(*ippdscdb)->drv = This;
|
|
(*ippdscdb)->notify = NULL;
|
|
(*ippdscdb)->notify_index = 0;
|
|
(*ippdscdb)->notifies = NULL;
|
|
(*ippdscdb)->nrofnotifies = 0;
|
|
(*ippdscdb)->property_set = NULL;
|
|
(*ippdscdb)->is_capturing = FALSE;
|
|
(*ippdscdb)->is_looping = FALSE;
|
|
(*ippdscdb)->wfx = *pwfx;
|
|
(*ippdscdb)->buflen = *pdwcbBufferSize;
|
|
|
|
if (bNewBuffer)
|
|
(*ippdscdb)->buffer = NULL;
|
|
else
|
|
(*ippdscdb)->buffer = *ppbBuffer;
|
|
|
|
wwi = &WInDev[This->wDevID];
|
|
|
|
if (wwi->state == WINE_WS_CLOSED) {
|
|
unsigned int frag_size;
|
|
|
|
if (wwi->ossdev.open_count > 0) {
|
|
/* opened already so use existing fragment size */
|
|
audio_fragment = wwi->ossdev.audio_fragment;
|
|
} else {
|
|
/* calculate a fragment size */
|
|
unsigned int mask = 0xffffffff;
|
|
|
|
/* calculate largest fragment size less than 10 ms. */
|
|
fsize = pwfx->nAvgBytesPerSec / 100; /* 10 ms chunk */
|
|
shift = 0;
|
|
while ((1 << shift) <= fsize)
|
|
shift++;
|
|
shift--;
|
|
fsize = 1 << shift;
|
|
TRACE("shift = %d, fragment size = %d\n", shift, fsize);
|
|
TRACE("BufferSize=%d(%08x)\n", *pdwcbBufferSize, *pdwcbBufferSize);
|
|
|
|
/* See if we can directly map the buffer first.
|
|
* (buffer length is multiple of a power of 2)
|
|
*/
|
|
mask = (mask >> (32 - shift));
|
|
TRACE("mask=%08x\n", mask);
|
|
if (*pdwcbBufferSize & mask) {
|
|
/* no so try a smaller fragment size greater than 1 ms */
|
|
int new_shift = shift - 1;
|
|
int min_shift = 0;
|
|
int min_fsize = pwfx->nAvgBytesPerSec / 1000;
|
|
BOOL found_one = FALSE;
|
|
while ((1 << min_shift) <= min_fsize)
|
|
min_shift++;
|
|
min_shift--;
|
|
while (new_shift > min_shift) {
|
|
if (*pdwcbBufferSize & (-1 >> (32 - new_shift))) {
|
|
new_shift--;
|
|
continue;
|
|
} else {
|
|
found_one = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (found_one) {
|
|
/* found a smaller one that will work */
|
|
audio_fragment = ((*pdwcbBufferSize >> new_shift) << 16) | new_shift;
|
|
(*ippdscdb)->is_direct_map = TRUE;
|
|
TRACE("new shift = %d, fragment size = %d\n",
|
|
new_shift, 1 << (audio_fragment & 0xffff));
|
|
} else {
|
|
/* buffer can't be direct mapped */
|
|
audio_fragment = 0x00100000 + shift; /* 16 fragments of 2^shift */
|
|
(*ippdscdb)->is_direct_map = FALSE;
|
|
}
|
|
} else {
|
|
/* good fragment size */
|
|
audio_fragment = ((*pdwcbBufferSize >> shift) << 16) | shift;
|
|
(*ippdscdb)->is_direct_map = TRUE;
|
|
}
|
|
}
|
|
frag_size = 1 << (audio_fragment & 0xffff);
|
|
TRACE("is_direct_map = %s\n", (*ippdscdb)->is_direct_map ? "TRUE" : "FALSE");
|
|
TRACE("requesting %d %d byte fragments (%d bytes) (%d ms/fragment)\n",
|
|
audio_fragment >> 16, frag_size, frag_size * (audio_fragment >> 16),
|
|
(frag_size * 1000) / pwfx->nAvgBytesPerSec);
|
|
|
|
ret = OSS_OpenDevice(&wwi->ossdev, O_RDWR, &audio_fragment, 1,
|
|
pwfx->nSamplesPerSec,
|
|
(pwfx->nChannels > 1) ? 1 : 0,
|
|
(pwfx->wBitsPerSample == 16)
|
|
? AFMT_S16_LE : AFMT_U8);
|
|
|
|
if (ret != 0) {
|
|
WARN("OSS_OpenDevice failed\n");
|
|
HeapFree(GetProcessHeap(),0,*ippdscdb);
|
|
*ippdscdb = NULL;
|
|
return DSERR_GENERIC;
|
|
}
|
|
|
|
wwi->state = WINE_WS_STOPPED;
|
|
|
|
/* find out what fragment and buffer sizes OSS gave us */
|
|
if (ioctl(wwi->ossdev.fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
|
|
ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n",
|
|
wwi->ossdev.dev_name, strerror(errno));
|
|
OSS_CloseDevice(&wwi->ossdev);
|
|
wwi->state = WINE_WS_CLOSED;
|
|
HeapFree(GetProcessHeap(),0,*ippdscdb);
|
|
*ippdscdb = NULL;
|
|
return DSERR_GENERIC;
|
|
}
|
|
|
|
TRACE("got %d %d byte fragments (%d bytes) (%d ms/fragment)\n",
|
|
info.fragstotal, info.fragsize, info.fragstotal * info.fragsize,
|
|
info.fragsize * 1000 / pwfx->nAvgBytesPerSec);
|
|
|
|
wwi->dwTotalRecorded = 0;
|
|
memcpy(&wwi->waveFormat, pwfx, sizeof(PCMWAVEFORMAT));
|
|
wwi->dwFragmentSize = info.fragsize;
|
|
|
|
/* make sure we got what we asked for */
|
|
if ((*ippdscdb)->buflen != info.fragstotal * info.fragsize) {
|
|
TRACE("Couldn't create requested buffer\n");
|
|
if ((*ippdscdb)->is_direct_map) {
|
|
(*ippdscdb)->is_direct_map = FALSE;
|
|
TRACE("is_direct_map = FALSE\n");
|
|
}
|
|
} else if (info.fragsize != frag_size) {
|
|
TRACE("same buffer length but different fragment size\n");
|
|
}
|
|
}
|
|
(*ippdscdb)->fd = WInDev[This->wDevID].ossdev.fd;
|
|
|
|
if (pipe((*ippdscdb)->pipe_fd) < 0) {
|
|
TRACE("pipe() failed (%s)\n", strerror(errno));
|
|
OSS_CloseDevice(&wwi->ossdev);
|
|
wwi->state = WINE_WS_CLOSED;
|
|
HeapFree(GetProcessHeap(),0,*ippdscdb);
|
|
*ippdscdb = NULL;
|
|
return DSERR_GENERIC;
|
|
}
|
|
|
|
/* check how big the DMA buffer is now */
|
|
if (ioctl(wwi->ossdev.fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
|
|
ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n",
|
|
wwi->ossdev.dev_name, strerror(errno));
|
|
OSS_CloseDevice(&wwi->ossdev);
|
|
wwi->state = WINE_WS_CLOSED;
|
|
close((*ippdscdb)->pipe_fd[0]);
|
|
close((*ippdscdb)->pipe_fd[1]);
|
|
HeapFree(GetProcessHeap(),0,*ippdscdb);
|
|
*ippdscdb = NULL;
|
|
return DSERR_GENERIC;
|
|
}
|
|
|
|
(*ippdscdb)->maplen = info.fragstotal * info.fragsize;
|
|
(*ippdscdb)->fragsize = info.fragsize;
|
|
(*ippdscdb)->map_writepos = 0;
|
|
(*ippdscdb)->map_readpos = 0;
|
|
|
|
/* map the DMA buffer */
|
|
err = DSCDB_MapBuffer(*ippdscdb);
|
|
if (err != DS_OK) {
|
|
OSS_CloseDevice(&wwi->ossdev);
|
|
wwi->state = WINE_WS_CLOSED;
|
|
close((*ippdscdb)->pipe_fd[0]);
|
|
close((*ippdscdb)->pipe_fd[1]);
|
|
HeapFree(GetProcessHeap(),0,*ippdscdb);
|
|
*ippdscdb = NULL;
|
|
return err;
|
|
}
|
|
|
|
/* create the buffer if necessary */
|
|
if (!(*ippdscdb)->buffer)
|
|
(*ippdscdb)->buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,(*ippdscdb)->buflen);
|
|
|
|
if ((*ippdscdb)->buffer == NULL) {
|
|
OSS_CloseDevice(&wwi->ossdev);
|
|
wwi->state = WINE_WS_CLOSED;
|
|
close((*ippdscdb)->pipe_fd[0]);
|
|
close((*ippdscdb)->pipe_fd[1]);
|
|
HeapFree(GetProcessHeap(),0,*ippdscdb);
|
|
*ippdscdb = NULL;
|
|
return DSERR_OUTOFMEMORY;
|
|
}
|
|
|
|
This->capture_buffer = *ippdscdb;
|
|
|
|
(*ippdscdb)->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
(*ippdscdb)->hExitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
|
|
(*ippdscdb)->hThread = CreateThread(NULL, 0, DSCDB_Thread, *ippdscdb, 0, &((*ippdscdb)->dwThreadID));
|
|
WaitForSingleObject((*ippdscdb)->hStartUpEvent, INFINITE);
|
|
CloseHandle((*ippdscdb)->hStartUpEvent);
|
|
(*ippdscdb)->hStartUpEvent = INVALID_HANDLE_VALUE;
|
|
|
|
return DS_OK;
|
|
}
|
|
|
|
static const IDsCaptureDriverVtbl dscdvt =
|
|
{
|
|
IDsCaptureDriverImpl_QueryInterface,
|
|
IDsCaptureDriverImpl_AddRef,
|
|
IDsCaptureDriverImpl_Release,
|
|
IDsCaptureDriverImpl_GetDriverDesc,
|
|
IDsCaptureDriverImpl_Open,
|
|
IDsCaptureDriverImpl_Close,
|
|
IDsCaptureDriverImpl_GetCaps,
|
|
IDsCaptureDriverImpl_CreateCaptureBuffer
|
|
};
|
|
|
|
static HRESULT IDsCaptureDriverPropertySetImpl_Create(
|
|
IDsCaptureDriverBufferImpl * dscdb,
|
|
IDsCaptureDriverPropertySetImpl **pdscdps)
|
|
{
|
|
IDsCaptureDriverPropertySetImpl * dscdps;
|
|
TRACE("(%p,%p)\n",dscdb,pdscdps);
|
|
|
|
dscdps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*dscdps));
|
|
if (dscdps == NULL) {
|
|
WARN("out of memory\n");
|
|
return DSERR_OUTOFMEMORY;
|
|
}
|
|
|
|
dscdps->ref = 0;
|
|
dscdps->IDsDriverPropertySet_iface.lpVtbl = &dscdpsvt;
|
|
dscdps->capture_buffer = dscdb;
|
|
dscdb->property_set = dscdps;
|
|
IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER)dscdb);
|
|
|
|
*pdscdps = dscdps;
|
|
return DS_OK;
|
|
}
|
|
|
|
static HRESULT IDsCaptureDriverNotifyImpl_Create(
|
|
IDsCaptureDriverBufferImpl * dscdb,
|
|
IDsCaptureDriverNotifyImpl **pdscdn)
|
|
{
|
|
IDsCaptureDriverNotifyImpl * dscdn;
|
|
TRACE("(%p,%p)\n",dscdb,pdscdn);
|
|
|
|
dscdn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*dscdn));
|
|
if (dscdn == NULL) {
|
|
WARN("out of memory\n");
|
|
return DSERR_OUTOFMEMORY;
|
|
}
|
|
|
|
dscdn->ref = 0;
|
|
dscdn->IDsDriverNotify_iface.lpVtbl = &dscdnvt;
|
|
dscdn->capture_buffer = dscdb;
|
|
dscdb->notify = dscdn;
|
|
IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER)dscdb);
|
|
|
|
*pdscdn = dscdn;
|
|
return DS_OK;
|
|
}
|
|
|
|
DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv)
|
|
{
|
|
IDsCaptureDriverImpl** idrv = (IDsCaptureDriverImpl**)drv;
|
|
TRACE("(%d,%p)\n",wDevID,drv);
|
|
|
|
/* the HAL isn't much better than the HEL if we can't do mmap() */
|
|
if (!(WInDev[wDevID].ossdev.in_caps_support & WAVECAPS_DIRECTSOUND)) {
|
|
ERR("DirectSoundCapture 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(IDsCaptureDriverImpl));
|
|
if (!*idrv)
|
|
return MMSYSERR_NOMEM;
|
|
(*idrv)->IDsCaptureDriver_iface.lpVtbl = &dscdvt;
|
|
(*idrv)->ref = 1;
|
|
|
|
(*idrv)->wDevID = wDevID;
|
|
(*idrv)->capture_buffer = NULL;
|
|
return MMSYSERR_NOERROR;
|
|
}
|
|
|
|
DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc)
|
|
{
|
|
memcpy(desc, &(WInDev[wDevID].ossdev.ds_desc), sizeof(DSDRIVERDESC));
|
|
return MMSYSERR_NOERROR;
|
|
}
|