2 * Direct Sound Capture driver
4 * Copyright 2004 Robert Reif
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/port.h"
33 #ifdef HAVE_SYS_IOCTL_H
34 # include <sys/ioctl.h>
36 #ifdef HAVE_SYS_MMAN_H
37 # include <sys/mman.h>
42 #ifdef HAVE_SYS_POLL_H
43 # include <sys/poll.h>
56 #include "wine/debug.h"
60 WINE_DEFAULT_DEBUG_CHANNEL(dscapture
);
64 /*======================================================================*
65 * Low level DSOUND capture definitions *
66 *======================================================================*/
68 typedef struct IDsCaptureDriverPropertySetImpl IDsCaptureDriverPropertySetImpl
;
69 typedef struct IDsCaptureDriverNotifyImpl IDsCaptureDriverNotifyImpl
;
70 typedef struct IDsCaptureDriverImpl IDsCaptureDriverImpl
;
71 typedef struct IDsCaptureDriverBufferImpl IDsCaptureDriverBufferImpl
;
73 struct IDsCaptureDriverPropertySetImpl
76 const IDsDriverPropertySetVtbl
*lpVtbl
;
79 IDsCaptureDriverBufferImpl
* capture_buffer
;
82 struct IDsCaptureDriverNotifyImpl
85 const IDsDriverNotifyVtbl
*lpVtbl
;
88 IDsCaptureDriverBufferImpl
* capture_buffer
;
91 struct IDsCaptureDriverImpl
94 const IDsCaptureDriverVtbl
*lpVtbl
;
97 /* IDsCaptureDriverImpl fields */
99 IDsCaptureDriverBufferImpl
* capture_buffer
;
102 struct IDsCaptureDriverBufferImpl
104 /* IUnknown fields */
105 const IDsCaptureDriverBufferVtbl
*lpVtbl
;
108 /* IDsCaptureDriverBufferImpl fields */
109 IDsCaptureDriverImpl
* drv
;
110 LPBYTE buffer
; /* user buffer */
111 DWORD buflen
; /* user buffer length */
112 LPBYTE mapping
; /* DMA buffer */
113 DWORD maplen
; /* DMA buffer length */
114 BOOL is_direct_map
; /* DMA == user ? */
116 DWORD map_writepos
; /* DMA write offset */
117 DWORD map_readpos
; /* DMA read offset */
118 DWORD writeptr
; /* user write offset */
119 DWORD readptr
; /* user read offset */
121 /* IDsDriverNotifyImpl fields */
122 IDsCaptureDriverNotifyImpl
* notify
;
124 LPDSBPOSITIONNOTIFY notifies
;
127 /* IDsDriverPropertySetImpl fields */
128 IDsCaptureDriverPropertySetImpl
* property_set
;
135 HANDLE hStartUpEvent
;
141 static HRESULT
IDsCaptureDriverPropertySetImpl_Create(
142 IDsCaptureDriverBufferImpl
* dscdb
,
143 IDsCaptureDriverPropertySetImpl
**pdscdps
);
145 static HRESULT
IDsCaptureDriverNotifyImpl_Create(
146 IDsCaptureDriverBufferImpl
* dsdcb
,
147 IDsCaptureDriverNotifyImpl
**pdscdn
);
149 /*======================================================================*
150 * Low level DSOUND capture property set implementation *
151 *======================================================================*/
153 static HRESULT WINAPI
IDsCaptureDriverPropertySetImpl_QueryInterface(
154 PIDSDRIVERPROPERTYSET iface
,
158 IDsCaptureDriverPropertySetImpl
*This
= (IDsCaptureDriverPropertySetImpl
*)iface
;
159 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
161 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
162 IsEqualGUID(riid
, &IID_IDsDriverPropertySet
) ) {
163 IDsDriverPropertySet_AddRef(iface
);
168 FIXME( "Unknown IID %s\n", debugstr_guid( riid
) );
171 return E_NOINTERFACE
;
174 static ULONG WINAPI
IDsCaptureDriverPropertySetImpl_AddRef(
175 PIDSDRIVERPROPERTYSET iface
)
177 IDsCaptureDriverPropertySetImpl
*This
= (IDsCaptureDriverPropertySetImpl
*)iface
;
178 ULONG refCount
= InterlockedIncrement(&This
->ref
);
180 TRACE("(%p) ref was %d\n", This
, refCount
- 1);
185 static ULONG WINAPI
IDsCaptureDriverPropertySetImpl_Release(
186 PIDSDRIVERPROPERTYSET iface
)
188 IDsCaptureDriverPropertySetImpl
*This
= (IDsCaptureDriverPropertySetImpl
*)iface
;
189 ULONG refCount
= InterlockedDecrement(&This
->ref
);
191 TRACE("(%p) ref was %d\n", This
, refCount
+ 1);
194 IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER
)This
->capture_buffer
);
195 This
->capture_buffer
->property_set
= NULL
;
196 HeapFree(GetProcessHeap(),0,This
);
197 TRACE("(%p) released\n",This
);
202 static HRESULT WINAPI
IDsCaptureDriverPropertySetImpl_Get(
203 PIDSDRIVERPROPERTYSET iface
,
204 PDSPROPERTY pDsProperty
,
205 LPVOID pPropertyParams
,
206 ULONG cbPropertyParams
,
207 LPVOID pPropertyData
,
208 ULONG cbPropertyData
,
209 PULONG pcbReturnedData
)
211 IDsCaptureDriverPropertySetImpl
*This
= (IDsCaptureDriverPropertySetImpl
*)iface
;
212 FIXME("(%p,%p,%p,%x,%p,%x,%p)\n",This
,pDsProperty
,pPropertyParams
,
213 cbPropertyParams
,pPropertyData
,cbPropertyData
,pcbReturnedData
);
214 return DSERR_UNSUPPORTED
;
217 static HRESULT WINAPI
IDsCaptureDriverPropertySetImpl_Set(
218 PIDSDRIVERPROPERTYSET iface
,
219 PDSPROPERTY pDsProperty
,
220 LPVOID pPropertyParams
,
221 ULONG cbPropertyParams
,
222 LPVOID pPropertyData
,
223 ULONG cbPropertyData
)
225 IDsCaptureDriverPropertySetImpl
*This
= (IDsCaptureDriverPropertySetImpl
*)iface
;
226 FIXME("(%p,%p,%p,%x,%p,%x)\n",This
,pDsProperty
,pPropertyParams
,
227 cbPropertyParams
,pPropertyData
,cbPropertyData
);
228 return DSERR_UNSUPPORTED
;
231 static HRESULT WINAPI
IDsCaptureDriverPropertySetImpl_QuerySupport(
232 PIDSDRIVERPROPERTYSET iface
,
233 REFGUID PropertySetId
,
237 IDsCaptureDriverPropertySetImpl
*This
= (IDsCaptureDriverPropertySetImpl
*)iface
;
238 FIXME("(%p,%s,%x,%p)\n",This
,debugstr_guid(PropertySetId
),PropertyId
,
240 return DSERR_UNSUPPORTED
;
243 static const IDsDriverPropertySetVtbl dscdpsvt
=
245 IDsCaptureDriverPropertySetImpl_QueryInterface
,
246 IDsCaptureDriverPropertySetImpl_AddRef
,
247 IDsCaptureDriverPropertySetImpl_Release
,
248 IDsCaptureDriverPropertySetImpl_Get
,
249 IDsCaptureDriverPropertySetImpl_Set
,
250 IDsCaptureDriverPropertySetImpl_QuerySupport
,
253 /*======================================================================*
254 * Low level DSOUND capture notify implementation *
255 *======================================================================*/
257 static HRESULT WINAPI
IDsCaptureDriverNotifyImpl_QueryInterface(
258 PIDSDRIVERNOTIFY iface
,
262 IDsCaptureDriverNotifyImpl
*This
= (IDsCaptureDriverNotifyImpl
*)iface
;
263 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
265 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
266 IsEqualGUID(riid
, &IID_IDsDriverNotify
) ) {
267 IDsDriverNotify_AddRef(iface
);
272 FIXME( "Unknown IID %s\n", debugstr_guid( riid
) );
275 return E_NOINTERFACE
;
278 static ULONG WINAPI
IDsCaptureDriverNotifyImpl_AddRef(
279 PIDSDRIVERNOTIFY iface
)
281 IDsCaptureDriverNotifyImpl
*This
= (IDsCaptureDriverNotifyImpl
*)iface
;
282 ULONG refCount
= InterlockedIncrement(&This
->ref
);
284 TRACE("(%p) ref was %d\n", This
, refCount
- 1);
289 static ULONG WINAPI
IDsCaptureDriverNotifyImpl_Release(
290 PIDSDRIVERNOTIFY iface
)
292 IDsCaptureDriverNotifyImpl
*This
= (IDsCaptureDriverNotifyImpl
*)iface
;
293 ULONG refCount
= InterlockedDecrement(&This
->ref
);
295 TRACE("(%p) ref was %d\n", This
, refCount
+ 1);
298 IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER
)This
->capture_buffer
);
299 This
->capture_buffer
->notify
= NULL
;
300 HeapFree(GetProcessHeap(),0,This
);
301 TRACE("(%p) released\n",This
);
306 static HRESULT WINAPI
IDsCaptureDriverNotifyImpl_SetNotificationPositions(
307 PIDSDRIVERNOTIFY iface
,
309 LPCDSBPOSITIONNOTIFY notify
)
311 IDsCaptureDriverNotifyImpl
*This
= (IDsCaptureDriverNotifyImpl
*)iface
;
312 TRACE("(%p,0x%08x,%p)\n",This
,howmuch
,notify
);
315 WARN("invalid parameter\n");
316 return DSERR_INVALIDPARAM
;
319 if (TRACE_ON(dscapture
)) {
321 for (i
=0;i
<howmuch
;i
++)
322 TRACE("notify at %d to 0x%08lx\n",
323 notify
[i
].dwOffset
,(DWORD_PTR
)notify
[i
].hEventNotify
);
326 /* Make an internal copy of the caller-supplied array.
327 * Replace the existing copy if one is already present. */
328 if (This
->capture_buffer
->notifies
)
329 This
->capture_buffer
->notifies
= HeapReAlloc(GetProcessHeap(),
330 HEAP_ZERO_MEMORY
, This
->capture_buffer
->notifies
,
331 howmuch
* sizeof(DSBPOSITIONNOTIFY
));
333 This
->capture_buffer
->notifies
= HeapAlloc(GetProcessHeap(),
334 HEAP_ZERO_MEMORY
, howmuch
* sizeof(DSBPOSITIONNOTIFY
));
336 memcpy(This
->capture_buffer
->notifies
, notify
,
337 howmuch
* sizeof(DSBPOSITIONNOTIFY
));
338 This
->capture_buffer
->nrofnotifies
= howmuch
;
343 static const IDsDriverNotifyVtbl dscdnvt
=
345 IDsCaptureDriverNotifyImpl_QueryInterface
,
346 IDsCaptureDriverNotifyImpl_AddRef
,
347 IDsCaptureDriverNotifyImpl_Release
,
348 IDsCaptureDriverNotifyImpl_SetNotificationPositions
,
351 /*======================================================================*
352 * Low level DSOUND capture implementation *
353 *======================================================================*/
355 static HRESULT
DSCDB_MapBuffer(IDsCaptureDriverBufferImpl
*dscdb
)
357 if (!dscdb
->mapping
) {
358 dscdb
->mapping
= mmap(NULL
, dscdb
->maplen
, PROT_READ
, MAP_SHARED
,
359 WInDev
[dscdb
->drv
->wDevID
].ossdev
.fd
, 0);
360 if (dscdb
->mapping
== (LPBYTE
)-1) {
361 TRACE("(%p): Could not map sound device for direct access (%s)\n",
362 dscdb
, strerror(errno
));
363 return DSERR_GENERIC
;
365 TRACE("(%p): sound device has been mapped for direct access at %p, "
366 "size=%d\n", dscdb
, dscdb
->mapping
, dscdb
->maplen
);
371 static HRESULT
DSCDB_UnmapBuffer(IDsCaptureDriverBufferImpl
*dscdb
)
373 if (dscdb
->mapping
) {
374 if (munmap(dscdb
->mapping
, dscdb
->maplen
) < 0) {
375 ERR("(%p): Could not unmap sound device (%s)\n",
376 dscdb
, strerror(errno
));
377 return DSERR_GENERIC
;
379 dscdb
->mapping
= NULL
;
380 TRACE("(%p): sound device unmapped\n", dscdb
);
385 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_QueryInterface(
386 PIDSCDRIVERBUFFER iface
,
390 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
391 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
395 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
396 IsEqualGUID(riid
, &IID_IDsCaptureDriverBuffer
) ) {
397 IDsCaptureDriverBuffer_AddRef(iface
);
402 if ( IsEqualGUID( &IID_IDsDriverNotify
, riid
) ) {
404 IDsCaptureDriverNotifyImpl_Create(This
, &(This
->notify
));
406 IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY
)This
->notify
);
407 *ppobj
= This
->notify
;
413 if ( IsEqualGUID( &IID_IDsDriverPropertySet
, riid
) ) {
414 if (!This
->property_set
)
415 IDsCaptureDriverPropertySetImpl_Create(This
, &(This
->property_set
));
416 if (This
->property_set
) {
417 IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET
)This
->property_set
);
418 *ppobj
= This
->property_set
;
424 FIXME("(%p,%s,%p) unsupported GUID\n", This
, debugstr_guid(riid
), ppobj
);
425 return DSERR_UNSUPPORTED
;
428 static ULONG WINAPI
IDsCaptureDriverBufferImpl_AddRef(PIDSCDRIVERBUFFER iface
)
430 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
431 ULONG refCount
= InterlockedIncrement(&This
->ref
);
433 TRACE("(%p) ref was %d\n", This
, refCount
- 1);
438 static ULONG WINAPI
IDsCaptureDriverBufferImpl_Release(PIDSCDRIVERBUFFER iface
)
440 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
441 ULONG refCount
= InterlockedDecrement(&This
->ref
);
442 TRACE("(%p) ref was %d\n", This
, refCount
+ 1);
447 wwi
= &WInDev
[This
->drv
->wDevID
];
452 /* request thread termination */
453 write(This
->pipe_fd
[1], &x
, sizeof(x
));
456 WaitForSingleObject(This
->hExitEvent
, INFINITE
);
457 CloseHandle(This
->hExitEvent
);
460 close(This
->pipe_fd
[0]);
461 close(This
->pipe_fd
[1]);
463 DSCDB_UnmapBuffer(This
);
465 OSS_CloseDevice(&wwi
->ossdev
);
466 wwi
->state
= WINE_WS_CLOSED
;
467 wwi
->dwFragmentSize
= 0;
468 This
->drv
->capture_buffer
= NULL
;
470 HeapFree(GetProcessHeap(), 0, This
->notifies
);
471 HeapFree(GetProcessHeap(),0,This
);
472 TRACE("(%p) released\n",This
);
477 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Lock(
478 PIDSCDRIVERBUFFER iface
,
483 DWORD dwWritePosition
,
487 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
488 TRACE("(%p,%p,%p,%p,%p,%d,%d,0x%08x)\n",This
,ppvAudio1
,pdwLen1
,
489 ppvAudio2
,pdwLen2
,dwWritePosition
,dwWriteLen
,dwFlags
);
491 if (This
->is_direct_map
) {
493 *ppvAudio1
= This
->mapping
+ dwWritePosition
;
495 if (dwWritePosition
+ dwWriteLen
< This
->maplen
) {
497 *pdwLen1
= dwWriteLen
;
504 *pdwLen1
= This
->maplen
- dwWritePosition
;
508 *pdwLen2
= dwWriteLen
- (This
->maplen
- dwWritePosition
);
512 *ppvAudio1
= This
->buffer
+ dwWritePosition
;
514 if (dwWritePosition
+ dwWriteLen
< This
->buflen
) {
516 *pdwLen1
= dwWriteLen
;
523 *pdwLen1
= This
->buflen
- dwWritePosition
;
527 *pdwLen2
= dwWriteLen
- (This
->buflen
- dwWritePosition
);
534 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Unlock(
535 PIDSCDRIVERBUFFER iface
,
541 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
542 TRACE("(%p,%p,%d,%p,%d)\n",This
,pvAudio1
,dwLen1
,pvAudio2
,dwLen2
);
544 if (This
->is_direct_map
)
545 This
->map_readpos
= (This
->map_readpos
+ dwLen1
+ dwLen2
) % This
->maplen
;
547 This
->readptr
= (This
->readptr
+ dwLen1
+ dwLen2
) % This
->buflen
;
552 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_GetPosition(
553 PIDSCDRIVERBUFFER iface
,
557 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
558 TRACE("(%p,%p,%p)\n",This
,lpdwCapture
,lpdwRead
);
560 if (WInDev
[This
->drv
->wDevID
].state
== WINE_WS_CLOSED
) {
561 ERR("device not open, but accessing?\n");
562 return DSERR_UNINITIALIZED
;
565 if (!This
->is_capturing
) {
572 if (This
->is_direct_map
) {
574 *lpdwCapture
= This
->map_writepos
;
576 *lpdwRead
= This
->map_readpos
;
580 *lpdwCapture
= This
->writeptr
;
582 *lpdwRead
= This
->readptr
;
585 TRACE("capturepos=%d, readpos=%d\n", lpdwCapture
?*lpdwCapture
:0,
586 lpdwRead
?*lpdwRead
:0);
590 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_GetStatus(
591 PIDSCDRIVERBUFFER iface
,
594 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
595 TRACE("(%p,%p)\n",This
,lpdwStatus
);
597 if (This
->is_capturing
) {
598 if (This
->is_looping
)
599 *lpdwStatus
= DSCBSTATUS_CAPTURING
| DSCBSTATUS_LOOPING
;
601 *lpdwStatus
= DSCBSTATUS_CAPTURING
;
608 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Start(
609 PIDSCDRIVERBUFFER iface
,
612 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
614 TRACE("(%p,%x)\n",This
,dwFlags
);
616 if (This
->is_capturing
)
619 if (dwFlags
& DSCBSTART_LOOPING
)
620 This
->is_looping
= TRUE
;
622 WInDev
[This
->drv
->wDevID
].ossdev
.bInputEnabled
= TRUE
;
623 enable
= getEnables(&WInDev
[This
->drv
->wDevID
].ossdev
);
624 if (ioctl(WInDev
[This
->drv
->wDevID
].ossdev
.fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
625 if (errno
== EINVAL
) {
626 /* Don't give up yet. OSS trigger support is inconsistent. */
627 if (WInDev
[This
->drv
->wDevID
].ossdev
.open_count
== 1) {
628 /* try the opposite output enable */
629 if (WInDev
[This
->drv
->wDevID
].ossdev
.bOutputEnabled
== FALSE
)
630 WInDev
[This
->drv
->wDevID
].ossdev
.bOutputEnabled
= TRUE
;
632 WInDev
[This
->drv
->wDevID
].ossdev
.bOutputEnabled
= FALSE
;
634 enable
= getEnables(&WInDev
[This
->drv
->wDevID
].ossdev
);
635 if (ioctl(WInDev
[This
->drv
->wDevID
].ossdev
.fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) >= 0) {
636 This
->is_capturing
= TRUE
;
641 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
642 WInDev
[This
->drv
->wDevID
].ossdev
.dev_name
, strerror(errno
));
643 WInDev
[This
->drv
->wDevID
].ossdev
.bInputEnabled
= FALSE
;
644 return DSERR_GENERIC
;
647 This
->is_capturing
= TRUE
;
651 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Stop(PIDSCDRIVERBUFFER iface
)
653 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
655 TRACE("(%p)\n",This
);
657 if (!This
->is_capturing
)
660 /* no more capturing */
661 WInDev
[This
->drv
->wDevID
].ossdev
.bInputEnabled
= FALSE
;
662 enable
= getEnables(&WInDev
[This
->drv
->wDevID
].ossdev
);
663 if (ioctl(WInDev
[This
->drv
->wDevID
].ossdev
.fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
664 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
665 WInDev
[This
->drv
->wDevID
].ossdev
.dev_name
, strerror(errno
));
666 return DSERR_GENERIC
;
669 /* send a final event if necessary */
670 if (This
->nrofnotifies
> 0) {
671 if (This
->notifies
[This
->nrofnotifies
- 1].dwOffset
== DSBPN_OFFSETSTOP
)
672 SetEvent(This
->notifies
[This
->nrofnotifies
- 1].hEventNotify
);
675 This
->is_capturing
= FALSE
;
676 This
->is_looping
= FALSE
;
680 write(This
->pipe_fd
[1], &x
, sizeof(x
));
681 WaitForSingleObject(This
->hExitEvent
, INFINITE
);
682 CloseHandle(This
->hExitEvent
);
683 This
->hExitEvent
= INVALID_HANDLE_VALUE
;
690 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_SetFormat(
691 PIDSCDRIVERBUFFER iface
,
694 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
695 FIXME("(%p): stub!\n",This
);
696 return DSERR_UNSUPPORTED
;
699 static const IDsCaptureDriverBufferVtbl dscdbvt
=
701 IDsCaptureDriverBufferImpl_QueryInterface
,
702 IDsCaptureDriverBufferImpl_AddRef
,
703 IDsCaptureDriverBufferImpl_Release
,
704 IDsCaptureDriverBufferImpl_Lock
,
705 IDsCaptureDriverBufferImpl_Unlock
,
706 IDsCaptureDriverBufferImpl_SetFormat
,
707 IDsCaptureDriverBufferImpl_GetPosition
,
708 IDsCaptureDriverBufferImpl_GetStatus
,
709 IDsCaptureDriverBufferImpl_Start
,
710 IDsCaptureDriverBufferImpl_Stop
713 static HRESULT WINAPI
IDsCaptureDriverImpl_QueryInterface(
718 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
719 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
721 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
722 IsEqualGUID(riid
, &IID_IDsCaptureDriver
) ) {
723 IDsCaptureDriver_AddRef(iface
);
728 FIXME( "Unknown IID %s\n", debugstr_guid( riid
) );
732 return E_NOINTERFACE
;
735 static ULONG WINAPI
IDsCaptureDriverImpl_AddRef(PIDSCDRIVER iface
)
737 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
738 ULONG refCount
= InterlockedIncrement(&This
->ref
);
740 TRACE("(%p) ref was %d\n", This
, refCount
- 1);
745 static ULONG WINAPI
IDsCaptureDriverImpl_Release(PIDSCDRIVER iface
)
747 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
748 ULONG refCount
= InterlockedDecrement(&This
->ref
);
750 TRACE("(%p) ref was %d\n", This
, refCount
+ 1);
753 HeapFree(GetProcessHeap(),0,This
);
754 TRACE("(%p) released\n",This
);
759 static HRESULT WINAPI
IDsCaptureDriverImpl_GetDriverDesc(
763 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
764 TRACE("(%p,%p)\n",This
,pDesc
);
767 TRACE("invalid parameter\n");
768 return DSERR_INVALIDPARAM
;
771 /* copy version from driver */
772 *pDesc
= WInDev
[This
->wDevID
].ossdev
.ds_desc
;
774 pDesc
->dnDevNode
= WInDev
[This
->wDevID
].waveDesc
.dnDevNode
;
776 pDesc
->wReserved
= 0;
777 pDesc
->ulDeviceNum
= This
->wDevID
;
778 pDesc
->dwHeapType
= DSDHEAP_NOHEAP
;
779 pDesc
->pvDirectDrawHeap
= NULL
;
780 pDesc
->dwMemStartAddress
= 0;
781 pDesc
->dwMemEndAddress
= 0;
782 pDesc
->dwMemAllocExtra
= 0;
783 pDesc
->pvReserved1
= NULL
;
784 pDesc
->pvReserved2
= NULL
;
788 static HRESULT WINAPI
IDsCaptureDriverImpl_Open(PIDSCDRIVER iface
)
790 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
791 TRACE("(%p)\n",This
);
795 static HRESULT WINAPI
IDsCaptureDriverImpl_Close(PIDSCDRIVER iface
)
797 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
798 TRACE("(%p)\n",This
);
799 if (This
->capture_buffer
) {
800 ERR("problem with DirectSound: capture buffer not released\n");
801 return DSERR_GENERIC
;
806 static HRESULT WINAPI
IDsCaptureDriverImpl_GetCaps(
808 PDSCDRIVERCAPS pCaps
)
810 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
811 TRACE("(%p,%p)\n",This
,pCaps
);
812 *pCaps
= WInDev
[This
->wDevID
].ossdev
.dsc_caps
;
816 static void DSCDB_CheckEvent(
817 IDsCaptureDriverBufferImpl
*dscb
,
822 LPDSBPOSITIONNOTIFY event
= dscb
->notifies
+ dscb
->notify_index
;
823 DWORD offset
= event
->dwOffset
;
824 TRACE("(%p,%d,%d,%d)\n", dscb
, writepos
, len
, buflen
);
826 TRACE("(%p) buflen = %d, writeptr = %d\n",
827 dscb
, dscb
->buflen
, dscb
->writeptr
);
828 TRACE("checking %d, position %d, event = %p\n",
829 dscb
->notify_index
, offset
, event
->hEventNotify
);
831 if ((writepos
+ len
) > offset
) {
832 TRACE("signalled event %p (%d) %d\n",
833 event
->hEventNotify
, dscb
->notify_index
, offset
);
834 SetEvent(event
->hEventNotify
);
835 dscb
->notify_index
= (dscb
->notify_index
+ 1) % dscb
->nrofnotifies
;
837 } else if ((writepos
+ len
) > buflen
) {
838 writepos
= writepos
+ len
- buflen
;
839 if ((writepos
+ len
) > offset
) {
840 TRACE("signalled event %p (%d) %d\n",
841 event
->hEventNotify
, dscb
->notify_index
, offset
);
842 SetEvent(event
->hEventNotify
);
843 dscb
->notify_index
= (dscb
->notify_index
+ 1) % dscb
->nrofnotifies
;
851 /* FIXME: using memcpy can cause strange crashes so use this fake one */
852 static void * my_memcpy(void * dst
, const void * src
, int length
)
855 for (i
= 0; i
< length
; i
++)
856 ((char *)dst
)[i
] = ((const char *)src
)[i
];
860 static DWORD CALLBACK
DSCDB_Thread(LPVOID lpParameter
)
862 IDsCaptureDriverBufferImpl
*This
= lpParameter
;
863 struct pollfd poll_list
[2];
866 DWORD map_offset
= 0;
867 TRACE("(%p)\n", lpParameter
);
869 poll_list
[0].fd
= This
->fd
; /* data available */
870 poll_list
[1].fd
= This
->pipe_fd
[0]; /* message from parent process */
871 poll_list
[0].events
= POLLIN
;
872 poll_list
[1].events
= POLLIN
;
874 /* let other process know we are running */
875 SetEvent(This
->hStartUpEvent
);
878 /* wait for something to happen */
879 retval
= poll(poll_list
,(unsigned long)2,-1);
880 /* Retval will always be greater than 0 or -1 in this case.
881 * Since we're doing it while blocking
884 ERR("Error while polling: %s\n",strerror(errno
));
888 /* check for exit command */
889 if ((poll_list
[1].revents
& POLLIN
) == POLLIN
) {
890 TRACE("(%p) done\n", lpParameter
);
891 /* acknowledge command and exit */
892 SetEvent(This
->hExitEvent
);
898 if ((poll_list
[0].revents
& POLLIN
) == POLLIN
) {
900 int fragsize
, first
, second
;
902 /* get the current DMA position */
903 if (ioctl(This
->fd
, SNDCTL_DSP_GETIPTR
, &info
) < 0) {
904 ERR("ioctl(%s, SNDCTL_DSP_GETIPTR) failed (%s)\n",
905 WInDev
[This
->drv
->wDevID
].ossdev
.dev_name
, strerror(errno
));
906 return DSERR_GENERIC
;
909 if (This
->is_direct_map
) {
910 offset
= This
->map_writepos
;
911 This
->map_writepos
= info
.ptr
;
913 if (info
.ptr
< offset
)
914 fragsize
= info
.ptr
+ This
->maplen
- offset
;
916 fragsize
= info
.ptr
- offset
;
918 DSCDB_CheckEvent(This
, offset
, fragsize
, This
->maplen
);
920 map_offset
= This
->map_writepos
;
921 offset
= This
->writeptr
;
923 /* test for mmap buffer wrap */
924 if (info
.ptr
< map_offset
) {
925 /* mmap buffer wrapped */
926 fragsize
= info
.ptr
+ This
->maplen
- map_offset
;
928 /* check for user buffer wrap */
929 if ((offset
+ fragsize
) > This
->buflen
) {
930 /* both buffers wrapped
931 * figure out which wrapped first
933 if ((This
->maplen
- map_offset
) > (This
->buflen
- offset
)) {
934 /* user buffer wrapped first */
935 first
= This
->buflen
- offset
;
936 second
= (This
->maplen
- map_offset
) - first
;
937 my_memcpy(This
->buffer
+ offset
, This
->mapping
+ map_offset
, first
);
938 my_memcpy(This
->buffer
, This
->mapping
+ map_offset
+ first
, second
);
939 my_memcpy(This
->buffer
+ second
, This
->mapping
, fragsize
- (first
+ second
));
941 /* mmap buffer wrapped first */
942 first
= This
->maplen
- map_offset
;
943 second
= (This
->buflen
- offset
) - first
;
944 my_memcpy(This
->buffer
+ offset
, This
->mapping
+ map_offset
, first
);
945 my_memcpy(This
->buffer
+ offset
+ first
, This
->mapping
, second
);
946 my_memcpy(This
->buffer
, This
->mapping
+ second
, fragsize
- (first
+ second
));
949 /* only mmap buffer wrapped */
950 first
= This
->maplen
- map_offset
;
951 my_memcpy(This
->buffer
+ offset
, This
->mapping
+ map_offset
, first
);
952 my_memcpy(This
->buffer
+ offset
+ first
, This
->mapping
, fragsize
- first
);
955 /* mmap buffer didn't wrap */
956 fragsize
= info
.ptr
- map_offset
;
958 /* check for user buffer wrap */
959 if ((offset
+ fragsize
) > This
->buflen
) {
960 first
= This
->buflen
- offset
;
961 my_memcpy(This
->buffer
+ offset
, This
->mapping
+ map_offset
, first
);
962 my_memcpy(This
->buffer
, This
->mapping
+ map_offset
+ first
, fragsize
- first
);
964 my_memcpy(This
->buffer
+ offset
, This
->mapping
+ map_offset
, fragsize
);
967 This
->map_writepos
= info
.ptr
;
968 This
->writeptr
= (This
->writeptr
+ fragsize
) % This
->buflen
;
969 DSCDB_CheckEvent(This
, offset
, fragsize
, This
->buflen
);
975 static HRESULT WINAPI
IDsCaptureDriverImpl_CreateCaptureBuffer(
980 LPDWORD pdwcbBufferSize
,
984 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
985 IDsCaptureDriverBufferImpl
** ippdscdb
= (IDsCaptureDriverBufferImpl
**)ppvObj
;
988 int audio_fragment
, fsize
, shift
, ret
;
989 BOOL bNewBuffer
= FALSE
;
991 TRACE("(%p,%p,%x,%x,%p,%p,%p)\n",This
,pwfx
,dwFlags
,dwCardAddress
,
992 pdwcbBufferSize
,ppbBuffer
,ppvObj
);
994 if (This
->capture_buffer
) {
995 TRACE("already allocated\n");
996 return DSERR_ALLOCATED
;
999 /* must be given a buffer size */
1000 if (pdwcbBufferSize
== NULL
|| *pdwcbBufferSize
== 0) {
1001 TRACE("invalid parameter: pdwcbBufferSize\n");
1002 return DSERR_INVALIDPARAM
;
1005 /* must be given a buffer pointer */
1006 if (ppbBuffer
== NULL
) {
1007 TRACE("invalid parameter: ppbBuffer\n");
1008 return DSERR_INVALIDPARAM
;
1011 /* may or may not be given a buffer */
1012 if (*ppbBuffer
== NULL
) {
1013 TRACE("creating buffer\n");
1014 bNewBuffer
= TRUE
; /* not given a buffer so create one */
1016 TRACE("using supplied buffer\n");
1018 *ippdscdb
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDsCaptureDriverBufferImpl
));
1019 if (*ippdscdb
== NULL
) {
1020 TRACE("out of memory\n");
1021 return DSERR_OUTOFMEMORY
;
1024 (*ippdscdb
)->lpVtbl
= &dscdbvt
;
1025 (*ippdscdb
)->ref
= 1;
1026 (*ippdscdb
)->drv
= This
;
1027 (*ippdscdb
)->notify
= NULL
;
1028 (*ippdscdb
)->notify_index
= 0;
1029 (*ippdscdb
)->notifies
= NULL
;
1030 (*ippdscdb
)->nrofnotifies
= 0;
1031 (*ippdscdb
)->property_set
= NULL
;
1032 (*ippdscdb
)->is_capturing
= FALSE
;
1033 (*ippdscdb
)->is_looping
= FALSE
;
1034 (*ippdscdb
)->wfx
= *pwfx
;
1035 (*ippdscdb
)->buflen
= *pdwcbBufferSize
;
1038 (*ippdscdb
)->buffer
= NULL
;
1040 (*ippdscdb
)->buffer
= *ppbBuffer
;
1042 wwi
= &WInDev
[This
->wDevID
];
1044 if (wwi
->state
== WINE_WS_CLOSED
) {
1045 unsigned int frag_size
;
1047 if (wwi
->ossdev
.open_count
> 0) {
1048 /* opened already so use existing fragment size */
1049 audio_fragment
= wwi
->ossdev
.audio_fragment
;
1051 /* calculate a fragment size */
1052 unsigned int mask
= 0xffffffff;
1054 /* calculate largest fragment size less than 10 ms. */
1055 fsize
= pwfx
->nAvgBytesPerSec
/ 100; /* 10 ms chunk */
1057 while ((1 << shift
) <= fsize
)
1061 TRACE("shift = %d, fragment size = %d\n", shift
, fsize
);
1062 TRACE("BufferSize=%d(%08x)\n", *pdwcbBufferSize
, *pdwcbBufferSize
);
1064 /* See if we can directly map the buffer first.
1065 * (buffer length is multiple of a power of 2)
1067 mask
= (mask
>> (32 - shift
));
1068 TRACE("mask=%08x\n", mask
);
1069 if (*pdwcbBufferSize
& mask
) {
1070 /* no so try a smaller fragment size greater than 1 ms */
1071 int new_shift
= shift
- 1;
1073 int min_fsize
= pwfx
->nAvgBytesPerSec
/ 1000;
1074 BOOL found_one
= FALSE
;
1075 while ((1 << min_shift
) <= min_fsize
)
1078 while (new_shift
> min_shift
) {
1079 if (*pdwcbBufferSize
& (-1 >> (32 - new_shift
))) {
1088 /* found a smaller one that will work */
1089 audio_fragment
= ((*pdwcbBufferSize
>> new_shift
) << 16) | new_shift
;
1090 (*ippdscdb
)->is_direct_map
= TRUE
;
1091 TRACE("new shift = %d, fragment size = %d\n",
1092 new_shift
, 1 << (audio_fragment
& 0xffff));
1094 /* buffer can't be direct mapped */
1095 audio_fragment
= 0x00100000 + shift
; /* 16 fragments of 2^shift */
1096 (*ippdscdb
)->is_direct_map
= FALSE
;
1099 /* good fragment size */
1100 audio_fragment
= ((*pdwcbBufferSize
>> shift
) << 16) | shift
;
1101 (*ippdscdb
)->is_direct_map
= TRUE
;
1104 frag_size
= 1 << (audio_fragment
& 0xffff);
1105 TRACE("is_direct_map = %s\n", (*ippdscdb
)->is_direct_map
? "TRUE" : "FALSE");
1106 TRACE("requesting %d %d byte fragments (%d bytes) (%d ms/fragment)\n",
1107 audio_fragment
>> 16, frag_size
, frag_size
* (audio_fragment
>> 16),
1108 (frag_size
* 1000) / pwfx
->nAvgBytesPerSec
);
1110 ret
= OSS_OpenDevice(&wwi
->ossdev
, O_RDWR
, &audio_fragment
, 1,
1111 pwfx
->nSamplesPerSec
,
1112 (pwfx
->nChannels
> 1) ? 1 : 0,
1113 (pwfx
->wBitsPerSample
== 16)
1114 ? AFMT_S16_LE
: AFMT_U8
);
1117 WARN("OSS_OpenDevice failed\n");
1118 HeapFree(GetProcessHeap(),0,*ippdscdb
);
1120 return DSERR_GENERIC
;
1123 wwi
->state
= WINE_WS_STOPPED
;
1125 /* find out what fragment and buffer sizes OSS gave us */
1126 if (ioctl(wwi
->ossdev
.fd
, SNDCTL_DSP_GETISPACE
, &info
) < 0) {
1127 ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n",
1128 wwi
->ossdev
.dev_name
, strerror(errno
));
1129 OSS_CloseDevice(&wwi
->ossdev
);
1130 wwi
->state
= WINE_WS_CLOSED
;
1131 HeapFree(GetProcessHeap(),0,*ippdscdb
);
1133 return DSERR_GENERIC
;
1136 TRACE("got %d %d byte fragments (%d bytes) (%d ms/fragment)\n",
1137 info
.fragstotal
, info
.fragsize
, info
.fragstotal
* info
.fragsize
,
1138 info
.fragsize
* 1000 / pwfx
->nAvgBytesPerSec
);
1140 wwi
->dwTotalRecorded
= 0;
1141 memcpy(&wwi
->waveFormat
, pwfx
, sizeof(PCMWAVEFORMAT
));
1142 wwi
->dwFragmentSize
= info
.fragsize
;
1144 /* make sure we got what we asked for */
1145 if ((*ippdscdb
)->buflen
!= info
.fragstotal
* info
.fragsize
) {
1146 TRACE("Couldn't create requested buffer\n");
1147 if ((*ippdscdb
)->is_direct_map
) {
1148 (*ippdscdb
)->is_direct_map
= FALSE
;
1149 TRACE("is_direct_map = FALSE\n");
1151 } else if (info
.fragsize
!= frag_size
) {
1152 TRACE("same buffer length but different fragment size\n");
1155 (*ippdscdb
)->fd
= WInDev
[This
->wDevID
].ossdev
.fd
;
1157 if (pipe((*ippdscdb
)->pipe_fd
) < 0) {
1158 TRACE("pipe() failed (%s)\n", strerror(errno
));
1159 OSS_CloseDevice(&wwi
->ossdev
);
1160 wwi
->state
= WINE_WS_CLOSED
;
1161 HeapFree(GetProcessHeap(),0,*ippdscdb
);
1163 return DSERR_GENERIC
;
1166 /* check how big the DMA buffer is now */
1167 if (ioctl(wwi
->ossdev
.fd
, SNDCTL_DSP_GETISPACE
, &info
) < 0) {
1168 ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n",
1169 wwi
->ossdev
.dev_name
, strerror(errno
));
1170 OSS_CloseDevice(&wwi
->ossdev
);
1171 wwi
->state
= WINE_WS_CLOSED
;
1172 close((*ippdscdb
)->pipe_fd
[0]);
1173 close((*ippdscdb
)->pipe_fd
[1]);
1174 HeapFree(GetProcessHeap(),0,*ippdscdb
);
1176 return DSERR_GENERIC
;
1179 (*ippdscdb
)->maplen
= info
.fragstotal
* info
.fragsize
;
1180 (*ippdscdb
)->fragsize
= info
.fragsize
;
1181 (*ippdscdb
)->map_writepos
= 0;
1182 (*ippdscdb
)->map_readpos
= 0;
1184 /* map the DMA buffer */
1185 err
= DSCDB_MapBuffer(*ippdscdb
);
1187 OSS_CloseDevice(&wwi
->ossdev
);
1188 wwi
->state
= WINE_WS_CLOSED
;
1189 close((*ippdscdb
)->pipe_fd
[0]);
1190 close((*ippdscdb
)->pipe_fd
[1]);
1191 HeapFree(GetProcessHeap(),0,*ippdscdb
);
1196 /* create the buffer if necessary */
1197 if (!(*ippdscdb
)->buffer
)
1198 (*ippdscdb
)->buffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,(*ippdscdb
)->buflen
);
1200 if ((*ippdscdb
)->buffer
== NULL
) {
1201 OSS_CloseDevice(&wwi
->ossdev
);
1202 wwi
->state
= WINE_WS_CLOSED
;
1203 close((*ippdscdb
)->pipe_fd
[0]);
1204 close((*ippdscdb
)->pipe_fd
[1]);
1205 HeapFree(GetProcessHeap(),0,*ippdscdb
);
1207 return DSERR_OUTOFMEMORY
;
1210 This
->capture_buffer
= *ippdscdb
;
1212 (*ippdscdb
)->hStartUpEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1213 (*ippdscdb
)->hExitEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1215 (*ippdscdb
)->hThread
= CreateThread(NULL
, 0, DSCDB_Thread
, *ippdscdb
, 0, &((*ippdscdb
)->dwThreadID
));
1216 WaitForSingleObject((*ippdscdb
)->hStartUpEvent
, INFINITE
);
1217 CloseHandle((*ippdscdb
)->hStartUpEvent
);
1218 (*ippdscdb
)->hStartUpEvent
= INVALID_HANDLE_VALUE
;
1223 static const IDsCaptureDriverVtbl dscdvt
=
1225 IDsCaptureDriverImpl_QueryInterface
,
1226 IDsCaptureDriverImpl_AddRef
,
1227 IDsCaptureDriverImpl_Release
,
1228 IDsCaptureDriverImpl_GetDriverDesc
,
1229 IDsCaptureDriverImpl_Open
,
1230 IDsCaptureDriverImpl_Close
,
1231 IDsCaptureDriverImpl_GetCaps
,
1232 IDsCaptureDriverImpl_CreateCaptureBuffer
1235 static HRESULT
IDsCaptureDriverPropertySetImpl_Create(
1236 IDsCaptureDriverBufferImpl
* dscdb
,
1237 IDsCaptureDriverPropertySetImpl
**pdscdps
)
1239 IDsCaptureDriverPropertySetImpl
* dscdps
;
1240 TRACE("(%p,%p)\n",dscdb
,pdscdps
);
1242 dscdps
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*dscdps
));
1243 if (dscdps
== NULL
) {
1244 WARN("out of memory\n");
1245 return DSERR_OUTOFMEMORY
;
1249 dscdps
->lpVtbl
= &dscdpsvt
;
1250 dscdps
->capture_buffer
= dscdb
;
1251 dscdb
->property_set
= dscdps
;
1252 IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER
)dscdb
);
1258 static HRESULT
IDsCaptureDriverNotifyImpl_Create(
1259 IDsCaptureDriverBufferImpl
* dscdb
,
1260 IDsCaptureDriverNotifyImpl
**pdscdn
)
1262 IDsCaptureDriverNotifyImpl
* dscdn
;
1263 TRACE("(%p,%p)\n",dscdb
,pdscdn
);
1265 dscdn
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*dscdn
));
1266 if (dscdn
== NULL
) {
1267 WARN("out of memory\n");
1268 return DSERR_OUTOFMEMORY
;
1272 dscdn
->lpVtbl
= &dscdnvt
;
1273 dscdn
->capture_buffer
= dscdb
;
1274 dscdb
->notify
= dscdn
;
1275 IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER
)dscdb
);
1281 DWORD
widDsCreate(UINT wDevID
, PIDSCDRIVER
* drv
)
1283 IDsCaptureDriverImpl
** idrv
= (IDsCaptureDriverImpl
**)drv
;
1284 TRACE("(%d,%p)\n",wDevID
,drv
);
1286 /* the HAL isn't much better than the HEL if we can't do mmap() */
1287 if (!(WInDev
[wDevID
].ossdev
.in_caps_support
& WAVECAPS_DIRECTSOUND
)) {
1288 ERR("DirectSoundCapture flag not set\n");
1289 MESSAGE("This sound card's driver does not support direct access\n");
1290 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
1291 return MMSYSERR_NOTSUPPORTED
;
1294 *idrv
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDsCaptureDriverImpl
));
1296 return MMSYSERR_NOMEM
;
1297 (*idrv
)->lpVtbl
= &dscdvt
;
1300 (*idrv
)->wDevID
= wDevID
;
1301 (*idrv
)->capture_buffer
= NULL
;
1302 return MMSYSERR_NOERROR
;
1305 DWORD
widDsDesc(UINT wDevID
, PDSDRIVERDESC desc
)
1307 memcpy(desc
, &(WInDev
[wDevID
].ossdev
.ds_desc
), sizeof(DSDRIVERDESC
));
1308 return MMSYSERR_NOERROR
;
1311 #endif /* HAVE_OSS */