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>
45 #ifdef HAVE_SYS_ERRNO_H
46 #include <sys/errno.h>
48 #include <sys/soundcard.h>
59 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(dscapture);
65 /*======================================================================*
66 * Low level DSOUND capture definitions *
67 *======================================================================*/
69 typedef struct IDsCaptureDriverPropertySetImpl IDsCaptureDriverPropertySetImpl;
70 typedef struct IDsCaptureDriverNotifyImpl IDsCaptureDriverNotifyImpl;
71 typedef struct IDsCaptureDriverImpl IDsCaptureDriverImpl;
72 typedef struct IDsCaptureDriverBufferImpl IDsCaptureDriverBufferImpl;
74 struct IDsCaptureDriverPropertySetImpl
77 IDsDriverPropertySet IDsDriverPropertySet_iface;
80 IDsCaptureDriverBufferImpl* capture_buffer;
83 struct IDsCaptureDriverNotifyImpl
86 IDsDriverNotify IDsDriverNotify_iface;
89 IDsCaptureDriverBufferImpl* capture_buffer;
92 struct IDsCaptureDriverImpl
95 IDsCaptureDriver IDsCaptureDriver_iface;
98 /* IDsCaptureDriverImpl fields */
100 IDsCaptureDriverBufferImpl* capture_buffer;
103 struct IDsCaptureDriverBufferImpl
105 /* IUnknown fields */
106 IDsCaptureDriverBuffer IDsCaptureDriverBuffer_iface;
109 /* IDsCaptureDriverBufferImpl fields */
110 IDsCaptureDriverImpl* drv;
111 LPBYTE buffer; /* user buffer */
112 DWORD buflen; /* user buffer length */
113 LPBYTE mapping; /* DMA buffer */
114 DWORD maplen; /* DMA buffer length */
115 BOOL is_direct_map; /* DMA == user ? */
117 DWORD map_writepos; /* DMA write offset */
118 DWORD map_readpos; /* DMA read offset */
119 DWORD writeptr; /* user write offset */
120 DWORD readptr; /* user read offset */
122 /* IDsDriverNotifyImpl fields */
123 IDsCaptureDriverNotifyImpl* notify;
125 LPDSBPOSITIONNOTIFY notifies;
128 /* IDsDriverPropertySetImpl fields */
129 IDsCaptureDriverPropertySetImpl* property_set;
136 HANDLE hStartUpEvent;
142 static inline IDsCaptureDriverPropertySetImpl *impl_from_IDsDriverPropertySet(IDsDriverPropertySet *iface)
144 return CONTAINING_RECORD(iface, IDsCaptureDriverPropertySetImpl, IDsDriverPropertySet_iface);
147 static inline IDsCaptureDriverNotifyImpl *impl_from_IDsDriverNotify(IDsDriverNotify *iface)
149 return CONTAINING_RECORD(iface, IDsCaptureDriverNotifyImpl, IDsDriverNotify_iface);
152 static inline IDsCaptureDriverImpl *impl_from_IDsCaptureDriver(IDsCaptureDriver *iface)
154 return CONTAINING_RECORD(iface, IDsCaptureDriverImpl, IDsCaptureDriver_iface);
157 static inline IDsCaptureDriverBufferImpl *impl_from_IDsCaptureDriverBuffer(IDsCaptureDriverBuffer *iface)
159 return CONTAINING_RECORD(iface, IDsCaptureDriverBufferImpl, IDsCaptureDriverBuffer_iface);
162 static HRESULT IDsCaptureDriverPropertySetImpl_Create(
163 IDsCaptureDriverBufferImpl * dscdb,
164 IDsCaptureDriverPropertySetImpl **pdscdps);
166 static HRESULT IDsCaptureDriverNotifyImpl_Create(
167 IDsCaptureDriverBufferImpl * dsdcb,
168 IDsCaptureDriverNotifyImpl **pdscdn);
170 /*======================================================================*
171 * Low level DSOUND capture property set implementation *
172 *======================================================================*/
174 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_QueryInterface(
175 PIDSDRIVERPROPERTYSET iface,
179 IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
180 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
182 if ( IsEqualGUID(riid, &IID_IUnknown) ||
183 IsEqualGUID(riid, &IID_IDsDriverPropertySet) ) {
184 IDsDriverPropertySet_AddRef(iface);
189 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
192 return E_NOINTERFACE;
195 static ULONG WINAPI IDsCaptureDriverPropertySetImpl_AddRef(
196 PIDSDRIVERPROPERTYSET iface)
198 IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
199 ULONG refCount = InterlockedIncrement(&This->ref);
201 TRACE("(%p) ref was %d\n", This, refCount - 1);
206 static ULONG WINAPI IDsCaptureDriverPropertySetImpl_Release(
207 PIDSDRIVERPROPERTYSET iface)
209 IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
210 ULONG refCount = InterlockedDecrement(&This->ref);
212 TRACE("(%p) ref was %d\n", This, refCount + 1);
215 IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER)This->capture_buffer);
216 This->capture_buffer->property_set = NULL;
217 HeapFree(GetProcessHeap(),0,This);
218 TRACE("(%p) released\n",This);
223 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Get(
224 PIDSDRIVERPROPERTYSET iface,
225 PDSPROPERTY pDsProperty,
226 LPVOID pPropertyParams,
227 ULONG cbPropertyParams,
228 LPVOID pPropertyData,
229 ULONG cbPropertyData,
230 PULONG pcbReturnedData )
232 IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
233 FIXME("(%p,%p,%p,%x,%p,%x,%p)\n",This,pDsProperty,pPropertyParams,
234 cbPropertyParams,pPropertyData,cbPropertyData,pcbReturnedData);
235 return DSERR_UNSUPPORTED;
238 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Set(
239 PIDSDRIVERPROPERTYSET iface,
240 PDSPROPERTY pDsProperty,
241 LPVOID pPropertyParams,
242 ULONG cbPropertyParams,
243 LPVOID pPropertyData,
244 ULONG cbPropertyData )
246 IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
247 FIXME("(%p,%p,%p,%x,%p,%x)\n",This,pDsProperty,pPropertyParams,
248 cbPropertyParams,pPropertyData,cbPropertyData);
249 return DSERR_UNSUPPORTED;
252 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_QuerySupport(
253 PIDSDRIVERPROPERTYSET iface,
254 REFGUID PropertySetId,
258 IDsCaptureDriverPropertySetImpl *This = impl_from_IDsDriverPropertySet(iface);
259 FIXME("(%p,%s,%x,%p)\n",This,debugstr_guid(PropertySetId),PropertyId,
261 return DSERR_UNSUPPORTED;
264 static const IDsDriverPropertySetVtbl dscdpsvt =
266 IDsCaptureDriverPropertySetImpl_QueryInterface,
267 IDsCaptureDriverPropertySetImpl_AddRef,
268 IDsCaptureDriverPropertySetImpl_Release,
269 IDsCaptureDriverPropertySetImpl_Get,
270 IDsCaptureDriverPropertySetImpl_Set,
271 IDsCaptureDriverPropertySetImpl_QuerySupport,
274 /*======================================================================*
275 * Low level DSOUND capture notify implementation *
276 *======================================================================*/
278 static HRESULT WINAPI IDsCaptureDriverNotifyImpl_QueryInterface(
279 PIDSDRIVERNOTIFY iface,
283 IDsCaptureDriverNotifyImpl *This = impl_from_IDsDriverNotify(iface);
284 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
286 if ( IsEqualGUID(riid, &IID_IUnknown) ||
287 IsEqualGUID(riid, &IID_IDsDriverNotify) ) {
288 IDsDriverNotify_AddRef(iface);
293 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
296 return E_NOINTERFACE;
299 static ULONG WINAPI IDsCaptureDriverNotifyImpl_AddRef(
300 PIDSDRIVERNOTIFY iface)
302 IDsCaptureDriverNotifyImpl *This = impl_from_IDsDriverNotify(iface);
303 ULONG refCount = InterlockedIncrement(&This->ref);
305 TRACE("(%p) ref was %d\n", This, refCount - 1);
310 static ULONG WINAPI IDsCaptureDriverNotifyImpl_Release(
311 PIDSDRIVERNOTIFY iface)
313 IDsCaptureDriverNotifyImpl *This = impl_from_IDsDriverNotify(iface);
314 ULONG refCount = InterlockedDecrement(&This->ref);
316 TRACE("(%p) ref was %d\n", This, refCount + 1);
319 IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER)This->capture_buffer);
320 This->capture_buffer->notify = NULL;
321 HeapFree(GetProcessHeap(),0,This);
322 TRACE("(%p) released\n",This);
327 static HRESULT WINAPI IDsCaptureDriverNotifyImpl_SetNotificationPositions(
328 PIDSDRIVERNOTIFY iface,
330 LPCDSBPOSITIONNOTIFY notify)
332 IDsCaptureDriverNotifyImpl *This = impl_from_IDsDriverNotify(iface);
333 TRACE("(%p,0x%08x,%p)\n",This,howmuch,notify);
336 WARN("invalid parameter\n");
337 return DSERR_INVALIDPARAM;
340 if (TRACE_ON(dscapture)) {
342 for (i=0;i<howmuch;i++)
343 TRACE("notify at %d to 0x%08lx\n",
344 notify[i].dwOffset,(DWORD_PTR)notify[i].hEventNotify);
347 /* Make an internal copy of the caller-supplied array.
348 * Replace the existing copy if one is already present. */
349 if (This->capture_buffer->notifies)
350 This->capture_buffer->notifies = HeapReAlloc(GetProcessHeap(),
351 HEAP_ZERO_MEMORY, This->capture_buffer->notifies,
352 howmuch * sizeof(DSBPOSITIONNOTIFY));
354 This->capture_buffer->notifies = HeapAlloc(GetProcessHeap(),
355 HEAP_ZERO_MEMORY, howmuch * sizeof(DSBPOSITIONNOTIFY));
357 memcpy(This->capture_buffer->notifies, notify,
358 howmuch * sizeof(DSBPOSITIONNOTIFY));
359 This->capture_buffer->nrofnotifies = howmuch;
364 static const IDsDriverNotifyVtbl dscdnvt =
366 IDsCaptureDriverNotifyImpl_QueryInterface,
367 IDsCaptureDriverNotifyImpl_AddRef,
368 IDsCaptureDriverNotifyImpl_Release,
369 IDsCaptureDriverNotifyImpl_SetNotificationPositions,
372 /*======================================================================*
373 * Low level DSOUND capture implementation *
374 *======================================================================*/
376 static HRESULT DSCDB_MapBuffer(IDsCaptureDriverBufferImpl *dscdb)
378 if (!dscdb->mapping) {
379 dscdb->mapping = mmap(NULL, dscdb->maplen, PROT_READ, MAP_SHARED,
380 WInDev[dscdb->drv->wDevID].ossdev.fd, 0);
381 if (dscdb->mapping == (LPBYTE)-1) {
382 TRACE("(%p): Could not map sound device for direct access (%s)\n",
383 dscdb, strerror(errno));
384 return DSERR_GENERIC;
386 TRACE("(%p): sound device has been mapped for direct access at %p, "
387 "size=%d\n", dscdb, dscdb->mapping, dscdb->maplen);
392 static HRESULT DSCDB_UnmapBuffer(IDsCaptureDriverBufferImpl *dscdb)
394 if (dscdb->mapping) {
395 if (munmap(dscdb->mapping, dscdb->maplen) < 0) {
396 ERR("(%p): Could not unmap sound device (%s)\n",
397 dscdb, strerror(errno));
398 return DSERR_GENERIC;
400 dscdb->mapping = NULL;
401 TRACE("(%p): sound device unmapped\n", dscdb);
406 static HRESULT WINAPI IDsCaptureDriverBufferImpl_QueryInterface(
407 PIDSCDRIVERBUFFER iface,
411 IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
412 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
416 if ( IsEqualGUID(riid, &IID_IUnknown) ||
417 IsEqualGUID(riid, &IID_IDsCaptureDriverBuffer) ) {
418 IDsCaptureDriverBuffer_AddRef(iface);
423 if ( IsEqualGUID( &IID_IDsDriverNotify, riid ) ) {
425 IDsCaptureDriverNotifyImpl_Create(This, &(This->notify));
427 IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY)This->notify);
428 *ppobj = This->notify;
434 if ( IsEqualGUID( &IID_IDsDriverPropertySet, riid ) ) {
435 if (!This->property_set)
436 IDsCaptureDriverPropertySetImpl_Create(This, &(This->property_set));
437 if (This->property_set) {
438 IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET)This->property_set);
439 *ppobj = This->property_set;
445 FIXME("(%p,%s,%p) unsupported GUID\n", This, debugstr_guid(riid), ppobj);
446 return DSERR_UNSUPPORTED;
449 static ULONG WINAPI IDsCaptureDriverBufferImpl_AddRef(PIDSCDRIVERBUFFER iface)
451 IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
452 ULONG refCount = InterlockedIncrement(&This->ref);
454 TRACE("(%p) ref was %d\n", This, refCount - 1);
459 static ULONG WINAPI IDsCaptureDriverBufferImpl_Release(PIDSCDRIVERBUFFER iface)
461 IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
462 ULONG refCount = InterlockedDecrement(&This->ref);
463 TRACE("(%p) ref was %d\n", This, refCount + 1);
468 wwi = &WInDev[This->drv->wDevID];
473 /* request thread termination */
474 write(This->pipe_fd[1], &x, sizeof(x));
477 WaitForSingleObject(This->hExitEvent, INFINITE);
478 CloseHandle(This->hExitEvent);
481 close(This->pipe_fd[0]);
482 close(This->pipe_fd[1]);
484 DSCDB_UnmapBuffer(This);
486 OSS_CloseDevice(&wwi->ossdev);
487 wwi->state = WINE_WS_CLOSED;
488 wwi->dwFragmentSize = 0;
489 This->drv->capture_buffer = NULL;
491 HeapFree(GetProcessHeap(), 0, This->notifies);
492 HeapFree(GetProcessHeap(),0,This);
493 TRACE("(%p) released\n",This);
498 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Lock(
499 PIDSCDRIVERBUFFER iface,
504 DWORD dwWritePosition,
508 IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
509 TRACE("(%p,%p,%p,%p,%p,%d,%d,0x%08x)\n",This,ppvAudio1,pdwLen1,
510 ppvAudio2,pdwLen2,dwWritePosition,dwWriteLen,dwFlags);
512 if (This->is_direct_map) {
514 *ppvAudio1 = This->mapping + dwWritePosition;
516 if (dwWritePosition + dwWriteLen < This->maplen) {
518 *pdwLen1 = dwWriteLen;
525 *pdwLen1 = This->maplen - dwWritePosition;
529 *pdwLen2 = dwWriteLen - (This->maplen - dwWritePosition);
533 *ppvAudio1 = This->buffer + dwWritePosition;
535 if (dwWritePosition + dwWriteLen < This->buflen) {
537 *pdwLen1 = dwWriteLen;
544 *pdwLen1 = This->buflen - dwWritePosition;
548 *pdwLen2 = dwWriteLen - (This->buflen - dwWritePosition);
555 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Unlock(
556 PIDSCDRIVERBUFFER iface,
562 IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
563 TRACE("(%p,%p,%d,%p,%d)\n",This,pvAudio1,dwLen1,pvAudio2,dwLen2);
565 if (This->is_direct_map)
566 This->map_readpos = (This->map_readpos + dwLen1 + dwLen2) % This->maplen;
568 This->readptr = (This->readptr + dwLen1 + dwLen2) % This->buflen;
573 static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetPosition(
574 PIDSCDRIVERBUFFER iface,
578 IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
579 TRACE("(%p,%p,%p)\n",This,lpdwCapture,lpdwRead);
581 if (WInDev[This->drv->wDevID].state == WINE_WS_CLOSED) {
582 ERR("device not open, but accessing?\n");
583 return DSERR_UNINITIALIZED;
586 if (!This->is_capturing) {
593 if (This->is_direct_map) {
595 *lpdwCapture = This->map_writepos;
597 *lpdwRead = This->map_readpos;
601 *lpdwCapture = This->writeptr;
603 *lpdwRead = This->readptr;
606 TRACE("capturepos=%d, readpos=%d\n", lpdwCapture?*lpdwCapture:0,
607 lpdwRead?*lpdwRead:0);
611 static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetStatus(
612 PIDSCDRIVERBUFFER iface,
615 IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
616 TRACE("(%p,%p)\n",This,lpdwStatus);
618 if (This->is_capturing) {
619 if (This->is_looping)
620 *lpdwStatus = DSCBSTATUS_CAPTURING | DSCBSTATUS_LOOPING;
622 *lpdwStatus = DSCBSTATUS_CAPTURING;
629 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Start(
630 PIDSCDRIVERBUFFER iface,
633 IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
635 TRACE("(%p,%x)\n",This,dwFlags);
637 if (This->is_capturing)
640 if (dwFlags & DSCBSTART_LOOPING)
641 This->is_looping = TRUE;
643 WInDev[This->drv->wDevID].ossdev.bInputEnabled = TRUE;
644 enable = getEnables(&WInDev[This->drv->wDevID].ossdev);
645 if (ioctl(WInDev[This->drv->wDevID].ossdev.fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
646 if (errno == EINVAL) {
647 /* Don't give up yet. OSS trigger support is inconsistent. */
648 if (WInDev[This->drv->wDevID].ossdev.open_count == 1) {
649 /* try the opposite output enable */
650 if (WInDev[This->drv->wDevID].ossdev.bOutputEnabled == FALSE)
651 WInDev[This->drv->wDevID].ossdev.bOutputEnabled = TRUE;
653 WInDev[This->drv->wDevID].ossdev.bOutputEnabled = FALSE;
655 enable = getEnables(&WInDev[This->drv->wDevID].ossdev);
656 if (ioctl(WInDev[This->drv->wDevID].ossdev.fd, SNDCTL_DSP_SETTRIGGER, &enable) >= 0) {
657 This->is_capturing = TRUE;
662 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
663 WInDev[This->drv->wDevID].ossdev.dev_name, strerror(errno));
664 WInDev[This->drv->wDevID].ossdev.bInputEnabled = FALSE;
665 return DSERR_GENERIC;
668 This->is_capturing = TRUE;
672 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Stop(PIDSCDRIVERBUFFER iface)
674 IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
676 TRACE("(%p)\n",This);
678 if (!This->is_capturing)
681 /* no more capturing */
682 WInDev[This->drv->wDevID].ossdev.bInputEnabled = FALSE;
683 enable = getEnables(&WInDev[This->drv->wDevID].ossdev);
684 if (ioctl(WInDev[This->drv->wDevID].ossdev.fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
685 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
686 WInDev[This->drv->wDevID].ossdev.dev_name, strerror(errno));
687 return DSERR_GENERIC;
690 /* send a final event if necessary */
691 if (This->nrofnotifies > 0) {
692 if (This->notifies[This->nrofnotifies - 1].dwOffset == DSBPN_OFFSETSTOP)
693 SetEvent(This->notifies[This->nrofnotifies - 1].hEventNotify);
696 This->is_capturing = FALSE;
697 This->is_looping = FALSE;
701 write(This->pipe_fd[1], &x, sizeof(x));
702 WaitForSingleObject(This->hExitEvent, INFINITE);
703 CloseHandle(This->hExitEvent);
704 This->hExitEvent = INVALID_HANDLE_VALUE;
711 static HRESULT WINAPI IDsCaptureDriverBufferImpl_SetFormat(
712 PIDSCDRIVERBUFFER iface,
715 IDsCaptureDriverBufferImpl *This = impl_from_IDsCaptureDriverBuffer(iface);
716 FIXME("(%p): stub!\n",This);
717 return DSERR_UNSUPPORTED;
720 static const IDsCaptureDriverBufferVtbl dscdbvt =
722 IDsCaptureDriverBufferImpl_QueryInterface,
723 IDsCaptureDriverBufferImpl_AddRef,
724 IDsCaptureDriverBufferImpl_Release,
725 IDsCaptureDriverBufferImpl_Lock,
726 IDsCaptureDriverBufferImpl_Unlock,
727 IDsCaptureDriverBufferImpl_SetFormat,
728 IDsCaptureDriverBufferImpl_GetPosition,
729 IDsCaptureDriverBufferImpl_GetStatus,
730 IDsCaptureDriverBufferImpl_Start,
731 IDsCaptureDriverBufferImpl_Stop
734 static HRESULT WINAPI IDsCaptureDriverImpl_QueryInterface(
739 IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
740 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
742 if ( IsEqualGUID(riid, &IID_IUnknown) ||
743 IsEqualGUID(riid, &IID_IDsCaptureDriver) ) {
744 IDsCaptureDriver_AddRef(iface);
749 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
753 return E_NOINTERFACE;
756 static ULONG WINAPI IDsCaptureDriverImpl_AddRef(PIDSCDRIVER iface)
758 IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
759 ULONG refCount = InterlockedIncrement(&This->ref);
761 TRACE("(%p) ref was %d\n", This, refCount - 1);
766 static ULONG WINAPI IDsCaptureDriverImpl_Release(PIDSCDRIVER iface)
768 IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
769 ULONG refCount = InterlockedDecrement(&This->ref);
771 TRACE("(%p) ref was %d\n", This, refCount + 1);
774 HeapFree(GetProcessHeap(),0,This);
775 TRACE("(%p) released\n",This);
780 static HRESULT WINAPI IDsCaptureDriverImpl_GetDriverDesc(
784 IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
785 TRACE("(%p,%p)\n",This,pDesc);
788 TRACE("invalid parameter\n");
789 return DSERR_INVALIDPARAM;
792 /* copy version from driver */
793 *pDesc = WInDev[This->wDevID].ossdev.ds_desc;
795 pDesc->dnDevNode = WInDev[This->wDevID].waveDesc.dnDevNode;
797 pDesc->wReserved = 0;
798 pDesc->ulDeviceNum = This->wDevID;
799 pDesc->dwHeapType = DSDHEAP_NOHEAP;
800 pDesc->pvDirectDrawHeap = NULL;
801 pDesc->dwMemStartAddress = 0;
802 pDesc->dwMemEndAddress = 0;
803 pDesc->dwMemAllocExtra = 0;
804 pDesc->pvReserved1 = NULL;
805 pDesc->pvReserved2 = NULL;
809 static HRESULT WINAPI IDsCaptureDriverImpl_Open(PIDSCDRIVER iface)
811 IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
812 TRACE("(%p)\n",This);
816 static HRESULT WINAPI IDsCaptureDriverImpl_Close(PIDSCDRIVER iface)
818 IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
819 TRACE("(%p)\n",This);
820 if (This->capture_buffer) {
821 ERR("problem with DirectSound: capture buffer not released\n");
822 return DSERR_GENERIC;
827 static HRESULT WINAPI IDsCaptureDriverImpl_GetCaps(
829 PDSCDRIVERCAPS pCaps)
831 IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
832 TRACE("(%p,%p)\n",This,pCaps);
833 *pCaps = WInDev[This->wDevID].ossdev.dsc_caps;
837 static void DSCDB_CheckEvent(
838 IDsCaptureDriverBufferImpl *dscb,
843 LPDSBPOSITIONNOTIFY event = dscb->notifies + dscb->notify_index;
844 DWORD offset = event->dwOffset;
845 TRACE("(%p,%d,%d,%d)\n", dscb, writepos, len, buflen);
847 TRACE("(%p) buflen = %d, writeptr = %d\n",
848 dscb, dscb->buflen, dscb->writeptr);
849 TRACE("checking %d, position %d, event = %p\n",
850 dscb->notify_index, offset, event->hEventNotify);
852 if ((writepos + len) > offset) {
853 TRACE("signalled event %p (%d) %d\n",
854 event->hEventNotify, dscb->notify_index, offset);
855 SetEvent(event->hEventNotify);
856 dscb->notify_index = (dscb->notify_index + 1) % dscb->nrofnotifies;
858 } else if ((writepos + len) > buflen) {
859 writepos = writepos + len - buflen;
860 if ((writepos + len) > offset) {
861 TRACE("signalled event %p (%d) %d\n",
862 event->hEventNotify, dscb->notify_index, offset);
863 SetEvent(event->hEventNotify);
864 dscb->notify_index = (dscb->notify_index + 1) % dscb->nrofnotifies;
872 /* FIXME: using memcpy can cause strange crashes so use this fake one */
873 static void * my_memcpy(void * dst, const void * src, int length)
876 for (i = 0; i < length; i++)
877 ((char *)dst)[i] = ((const char *)src)[i];
881 static DWORD CALLBACK DSCDB_Thread(LPVOID lpParameter)
883 IDsCaptureDriverBufferImpl *This = lpParameter;
884 struct pollfd poll_list[2];
887 DWORD map_offset = 0;
888 TRACE("(%p)\n", lpParameter);
890 poll_list[0].fd = This->fd; /* data available */
891 poll_list[1].fd = This->pipe_fd[0]; /* message from parent process */
892 poll_list[0].events = POLLIN;
893 poll_list[1].events = POLLIN;
895 /* let other process know we are running */
896 SetEvent(This->hStartUpEvent);
899 /* wait for something to happen */
900 retval = poll(poll_list,(unsigned long)2,-1);
901 /* Retval will always be greater than 0 or -1 in this case.
902 * Since we're doing it while blocking
905 ERR("Error while polling: %s\n",strerror(errno));
909 /* check for exit command */
910 if ((poll_list[1].revents & POLLIN) == POLLIN) {
911 TRACE("(%p) done\n", lpParameter);
912 /* acknowledge command and exit */
913 SetEvent(This->hExitEvent);
919 if ((poll_list[0].revents & POLLIN) == POLLIN) {
921 int fragsize, first, second;
923 /* get the current DMA position */
924 if (ioctl(This->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
925 ERR("ioctl(%s, SNDCTL_DSP_GETIPTR) failed (%s)\n",
926 WInDev[This->drv->wDevID].ossdev.dev_name, strerror(errno));
927 return DSERR_GENERIC;
930 if (This->is_direct_map) {
931 offset = This->map_writepos;
932 This->map_writepos = info.ptr;
934 if (info.ptr < offset)
935 fragsize = info.ptr + This->maplen - offset;
937 fragsize = info.ptr - offset;
939 DSCDB_CheckEvent(This, offset, fragsize, This->maplen);
941 map_offset = This->map_writepos;
942 offset = This->writeptr;
944 /* test for mmap buffer wrap */
945 if (info.ptr < map_offset) {
946 /* mmap buffer wrapped */
947 fragsize = info.ptr + This->maplen - map_offset;
949 /* check for user buffer wrap */
950 if ((offset + fragsize) > This->buflen) {
951 /* both buffers wrapped
952 * figure out which wrapped first
954 if ((This->maplen - map_offset) > (This->buflen - offset)) {
955 /* user buffer wrapped first */
956 first = This->buflen - offset;
957 second = (This->maplen - map_offset) - first;
958 my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
959 my_memcpy(This->buffer, This->mapping + map_offset + first, second);
960 my_memcpy(This->buffer + second, This->mapping, fragsize - (first + second));
962 /* mmap buffer wrapped first */
963 first = This->maplen - map_offset;
964 second = (This->buflen - offset) - first;
965 my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
966 my_memcpy(This->buffer + offset + first, This->mapping, second);
967 my_memcpy(This->buffer, This->mapping + second, fragsize - (first + second));
970 /* only mmap buffer wrapped */
971 first = This->maplen - map_offset;
972 my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
973 my_memcpy(This->buffer + offset + first, This->mapping, fragsize - first);
976 /* mmap buffer didn't wrap */
977 fragsize = info.ptr - map_offset;
979 /* check for user buffer wrap */
980 if ((offset + fragsize) > This->buflen) {
981 first = This->buflen - offset;
982 my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
983 my_memcpy(This->buffer, This->mapping + map_offset + first, fragsize - first);
985 my_memcpy(This->buffer + offset, This->mapping + map_offset, fragsize);
988 This->map_writepos = info.ptr;
989 This->writeptr = (This->writeptr + fragsize) % This->buflen;
990 DSCDB_CheckEvent(This, offset, fragsize, This->buflen);
996 static HRESULT WINAPI IDsCaptureDriverImpl_CreateCaptureBuffer(
1000 DWORD dwCardAddress,
1001 LPDWORD pdwcbBufferSize,
1005 IDsCaptureDriverImpl *This = impl_from_IDsCaptureDriver(iface);
1006 IDsCaptureDriverBufferImpl** ippdscdb = (IDsCaptureDriverBufferImpl**)ppvObj;
1008 audio_buf_info info;
1009 int audio_fragment, fsize, shift, ret;
1010 BOOL bNewBuffer = FALSE;
1012 TRACE("(%p,%p,%x,%x,%p,%p,%p)\n",This,pwfx,dwFlags,dwCardAddress,
1013 pdwcbBufferSize,ppbBuffer,ppvObj);
1015 if (This->capture_buffer) {
1016 TRACE("already allocated\n");
1017 return DSERR_ALLOCATED;
1020 /* must be given a buffer size */
1021 if (pdwcbBufferSize == NULL || *pdwcbBufferSize == 0) {
1022 TRACE("invalid parameter: pdwcbBufferSize\n");
1023 return DSERR_INVALIDPARAM;
1026 /* must be given a buffer pointer */
1027 if (ppbBuffer == NULL) {
1028 TRACE("invalid parameter: ppbBuffer\n");
1029 return DSERR_INVALIDPARAM;
1032 /* may or may not be given a buffer */
1033 if (*ppbBuffer == NULL) {
1034 TRACE("creating buffer\n");
1035 bNewBuffer = TRUE; /* not given a buffer so create one */
1037 TRACE("using supplied buffer\n");
1039 *ippdscdb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsCaptureDriverBufferImpl));
1040 if (*ippdscdb == NULL) {
1041 TRACE("out of memory\n");
1042 return DSERR_OUTOFMEMORY;
1045 (*ippdscdb)->IDsCaptureDriverBuffer_iface.lpVtbl = &dscdbvt;
1046 (*ippdscdb)->ref = 1;
1047 (*ippdscdb)->drv = This;
1048 (*ippdscdb)->notify = NULL;
1049 (*ippdscdb)->notify_index = 0;
1050 (*ippdscdb)->notifies = NULL;
1051 (*ippdscdb)->nrofnotifies = 0;
1052 (*ippdscdb)->property_set = NULL;
1053 (*ippdscdb)->is_capturing = FALSE;
1054 (*ippdscdb)->is_looping = FALSE;
1055 (*ippdscdb)->wfx = *pwfx;
1056 (*ippdscdb)->buflen = *pdwcbBufferSize;
1059 (*ippdscdb)->buffer = NULL;
1061 (*ippdscdb)->buffer = *ppbBuffer;
1063 wwi = &WInDev[This->wDevID];
1065 if (wwi->state == WINE_WS_CLOSED) {
1066 unsigned int frag_size;
1068 if (wwi->ossdev.open_count > 0) {
1069 /* opened already so use existing fragment size */
1070 audio_fragment = wwi->ossdev.audio_fragment;
1072 /* calculate a fragment size */
1073 unsigned int mask = 0xffffffff;
1075 /* calculate largest fragment size less than 10 ms. */
1076 fsize = pwfx->nAvgBytesPerSec / 100; /* 10 ms chunk */
1078 while ((1 << shift) <= fsize)
1082 TRACE("shift = %d, fragment size = %d\n", shift, fsize);
1083 TRACE("BufferSize=%d(%08x)\n", *pdwcbBufferSize, *pdwcbBufferSize);
1085 /* See if we can directly map the buffer first.
1086 * (buffer length is multiple of a power of 2)
1088 mask = (mask >> (32 - shift));
1089 TRACE("mask=%08x\n", mask);
1090 if (*pdwcbBufferSize & mask) {
1091 /* no so try a smaller fragment size greater than 1 ms */
1092 int new_shift = shift - 1;
1094 int min_fsize = pwfx->nAvgBytesPerSec / 1000;
1095 BOOL found_one = FALSE;
1096 while ((1 << min_shift) <= min_fsize)
1099 while (new_shift > min_shift) {
1100 if (*pdwcbBufferSize & (-1 >> (32 - new_shift))) {
1109 /* found a smaller one that will work */
1110 audio_fragment = ((*pdwcbBufferSize >> new_shift) << 16) | new_shift;
1111 (*ippdscdb)->is_direct_map = TRUE;
1112 TRACE("new shift = %d, fragment size = %d\n",
1113 new_shift, 1 << (audio_fragment & 0xffff));
1115 /* buffer can't be direct mapped */
1116 audio_fragment = 0x00100000 + shift; /* 16 fragments of 2^shift */
1117 (*ippdscdb)->is_direct_map = FALSE;
1120 /* good fragment size */
1121 audio_fragment = ((*pdwcbBufferSize >> shift) << 16) | shift;
1122 (*ippdscdb)->is_direct_map = TRUE;
1125 frag_size = 1 << (audio_fragment & 0xffff);
1126 TRACE("is_direct_map = %s\n", (*ippdscdb)->is_direct_map ? "TRUE" : "FALSE");
1127 TRACE("requesting %d %d byte fragments (%d bytes) (%d ms/fragment)\n",
1128 audio_fragment >> 16, frag_size, frag_size * (audio_fragment >> 16),
1129 (frag_size * 1000) / pwfx->nAvgBytesPerSec);
1131 ret = OSS_OpenDevice(&wwi->ossdev, O_RDWR, &audio_fragment, 1,
1132 pwfx->nSamplesPerSec,
1133 (pwfx->nChannels > 1) ? 1 : 0,
1134 (pwfx->wBitsPerSample == 16)
1135 ? AFMT_S16_LE : AFMT_U8);
1138 WARN("OSS_OpenDevice failed\n");
1139 HeapFree(GetProcessHeap(),0,*ippdscdb);
1141 return DSERR_GENERIC;
1144 wwi->state = WINE_WS_STOPPED;
1146 /* find out what fragment and buffer sizes OSS gave us */
1147 if (ioctl(wwi->ossdev.fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
1148 ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n",
1149 wwi->ossdev.dev_name, strerror(errno));
1150 OSS_CloseDevice(&wwi->ossdev);
1151 wwi->state = WINE_WS_CLOSED;
1152 HeapFree(GetProcessHeap(),0,*ippdscdb);
1154 return DSERR_GENERIC;
1157 TRACE("got %d %d byte fragments (%d bytes) (%d ms/fragment)\n",
1158 info.fragstotal, info.fragsize, info.fragstotal * info.fragsize,
1159 info.fragsize * 1000 / pwfx->nAvgBytesPerSec);
1161 wwi->dwTotalRecorded = 0;
1162 memcpy(&wwi->waveFormat, pwfx, sizeof(PCMWAVEFORMAT));
1163 wwi->dwFragmentSize = info.fragsize;
1165 /* make sure we got what we asked for */
1166 if ((*ippdscdb)->buflen != info.fragstotal * info.fragsize) {
1167 TRACE("Couldn't create requested buffer\n");
1168 if ((*ippdscdb)->is_direct_map) {
1169 (*ippdscdb)->is_direct_map = FALSE;
1170 TRACE("is_direct_map = FALSE\n");
1172 } else if (info.fragsize != frag_size) {
1173 TRACE("same buffer length but different fragment size\n");
1176 (*ippdscdb)->fd = WInDev[This->wDevID].ossdev.fd;
1178 if (pipe((*ippdscdb)->pipe_fd) < 0) {
1179 TRACE("pipe() failed (%s)\n", strerror(errno));
1180 OSS_CloseDevice(&wwi->ossdev);
1181 wwi->state = WINE_WS_CLOSED;
1182 HeapFree(GetProcessHeap(),0,*ippdscdb);
1184 return DSERR_GENERIC;
1187 /* check how big the DMA buffer is now */
1188 if (ioctl(wwi->ossdev.fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
1189 ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n",
1190 wwi->ossdev.dev_name, strerror(errno));
1191 OSS_CloseDevice(&wwi->ossdev);
1192 wwi->state = WINE_WS_CLOSED;
1193 close((*ippdscdb)->pipe_fd[0]);
1194 close((*ippdscdb)->pipe_fd[1]);
1195 HeapFree(GetProcessHeap(),0,*ippdscdb);
1197 return DSERR_GENERIC;
1200 (*ippdscdb)->maplen = info.fragstotal * info.fragsize;
1201 (*ippdscdb)->fragsize = info.fragsize;
1202 (*ippdscdb)->map_writepos = 0;
1203 (*ippdscdb)->map_readpos = 0;
1205 /* map the DMA buffer */
1206 err = DSCDB_MapBuffer(*ippdscdb);
1208 OSS_CloseDevice(&wwi->ossdev);
1209 wwi->state = WINE_WS_CLOSED;
1210 close((*ippdscdb)->pipe_fd[0]);
1211 close((*ippdscdb)->pipe_fd[1]);
1212 HeapFree(GetProcessHeap(),0,*ippdscdb);
1217 /* create the buffer if necessary */
1218 if (!(*ippdscdb)->buffer)
1219 (*ippdscdb)->buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,(*ippdscdb)->buflen);
1221 if ((*ippdscdb)->buffer == NULL) {
1222 OSS_CloseDevice(&wwi->ossdev);
1223 wwi->state = WINE_WS_CLOSED;
1224 close((*ippdscdb)->pipe_fd[0]);
1225 close((*ippdscdb)->pipe_fd[1]);
1226 HeapFree(GetProcessHeap(),0,*ippdscdb);
1228 return DSERR_OUTOFMEMORY;
1231 This->capture_buffer = *ippdscdb;
1233 (*ippdscdb)->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1234 (*ippdscdb)->hExitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1236 (*ippdscdb)->hThread = CreateThread(NULL, 0, DSCDB_Thread, *ippdscdb, 0, &((*ippdscdb)->dwThreadID));
1237 WaitForSingleObject((*ippdscdb)->hStartUpEvent, INFINITE);
1238 CloseHandle((*ippdscdb)->hStartUpEvent);
1239 (*ippdscdb)->hStartUpEvent = INVALID_HANDLE_VALUE;
1244 static const IDsCaptureDriverVtbl dscdvt =
1246 IDsCaptureDriverImpl_QueryInterface,
1247 IDsCaptureDriverImpl_AddRef,
1248 IDsCaptureDriverImpl_Release,
1249 IDsCaptureDriverImpl_GetDriverDesc,
1250 IDsCaptureDriverImpl_Open,
1251 IDsCaptureDriverImpl_Close,
1252 IDsCaptureDriverImpl_GetCaps,
1253 IDsCaptureDriverImpl_CreateCaptureBuffer
1256 static HRESULT IDsCaptureDriverPropertySetImpl_Create(
1257 IDsCaptureDriverBufferImpl * dscdb,
1258 IDsCaptureDriverPropertySetImpl **pdscdps)
1260 IDsCaptureDriverPropertySetImpl * dscdps;
1261 TRACE("(%p,%p)\n",dscdb,pdscdps);
1263 dscdps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*dscdps));
1264 if (dscdps == NULL) {
1265 WARN("out of memory\n");
1266 return DSERR_OUTOFMEMORY;
1270 dscdps->IDsDriverPropertySet_iface.lpVtbl = &dscdpsvt;
1271 dscdps->capture_buffer = dscdb;
1272 dscdb->property_set = dscdps;
1273 IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER)dscdb);
1279 static HRESULT IDsCaptureDriverNotifyImpl_Create(
1280 IDsCaptureDriverBufferImpl * dscdb,
1281 IDsCaptureDriverNotifyImpl **pdscdn)
1283 IDsCaptureDriverNotifyImpl * dscdn;
1284 TRACE("(%p,%p)\n",dscdb,pdscdn);
1286 dscdn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*dscdn));
1287 if (dscdn == NULL) {
1288 WARN("out of memory\n");
1289 return DSERR_OUTOFMEMORY;
1293 dscdn->IDsDriverNotify_iface.lpVtbl = &dscdnvt;
1294 dscdn->capture_buffer = dscdb;
1295 dscdb->notify = dscdn;
1296 IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER)dscdb);
1302 DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv)
1304 IDsCaptureDriverImpl** idrv = (IDsCaptureDriverImpl**)drv;
1305 TRACE("(%d,%p)\n",wDevID,drv);
1307 /* the HAL isn't much better than the HEL if we can't do mmap() */
1308 if (!(WInDev[wDevID].ossdev.in_caps_support & WAVECAPS_DIRECTSOUND)) {
1309 ERR("DirectSoundCapture flag not set\n");
1310 MESSAGE("This sound card's driver does not support direct access\n");
1311 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
1312 return MMSYSERR_NOTSUPPORTED;
1315 *idrv = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsCaptureDriverImpl));
1317 return MMSYSERR_NOMEM;
1318 (*idrv)->IDsCaptureDriver_iface.lpVtbl = &dscdvt;
1321 (*idrv)->wDevID = wDevID;
1322 (*idrv)->capture_buffer = NULL;
1323 return MMSYSERR_NOERROR;
1326 DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc)
1328 memcpy(desc, &(WInDev[wDevID].ossdev.ds_desc), sizeof(DSDRIVERDESC));
1329 return MMSYSERR_NOERROR;