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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>
39 #ifdef HAVE_SYS_POLL_H
40 # include <sys/poll.h>
53 #include "wine/debug.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(dscapture);
61 /*======================================================================*
62 * Low level DSOUND capture definitions *
63 *======================================================================*/
65 typedef struct IDsCaptureDriverPropertySetImpl IDsCaptureDriverPropertySetImpl;
66 typedef struct IDsCaptureDriverNotifyImpl IDsCaptureDriverNotifyImpl;
67 typedef struct IDsCaptureDriverImpl IDsCaptureDriverImpl;
68 typedef struct IDsCaptureDriverBufferImpl IDsCaptureDriverBufferImpl;
70 struct IDsCaptureDriverPropertySetImpl
73 IDsDriverPropertySetVtbl *lpVtbl;
76 IDsCaptureDriverBufferImpl* capture_buffer;
79 struct IDsCaptureDriverNotifyImpl
82 IDsDriverNotifyVtbl *lpVtbl;
85 IDsCaptureDriverBufferImpl* capture_buffer;
88 struct IDsCaptureDriverImpl
91 IDsCaptureDriverVtbl *lpVtbl;
94 /* IDsCaptureDriverImpl fields */
96 IDsCaptureDriverBufferImpl* capture_buffer;
99 struct IDsCaptureDriverBufferImpl
101 /* IUnknown fields */
102 IDsCaptureDriverBufferVtbl *lpVtbl;
105 /* IDsCaptureDriverBufferImpl fields */
106 IDsCaptureDriverImpl* drv;
107 LPBYTE buffer; /* user buffer */
108 DWORD buflen; /* user buffer length */
109 LPBYTE mapping; /* DMA buffer */
110 DWORD maplen; /* DMA buffer length */
111 BOOL is_direct_map; /* DMA == user ? */
113 DWORD map_writepos; /* DMA write offset */
114 DWORD map_readpos; /* DMA read offset */
115 DWORD writeptr; /* user write offset */
116 DWORD readptr; /* user read offset */
118 /* IDsDriverNotifyImpl fields */
119 IDsCaptureDriverNotifyImpl* notify;
121 LPDSBPOSITIONNOTIFY notifies;
124 /* IDsDriverPropertySetImpl fields */
125 IDsCaptureDriverPropertySetImpl* property_set;
132 HANDLE hStartUpEvent;
138 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Create(
139 IDsCaptureDriverBufferImpl * dscdb,
140 IDsCaptureDriverPropertySetImpl **pdscdps);
142 static HRESULT WINAPI IDsCaptureDriverNotifyImpl_Create(
143 IDsCaptureDriverBufferImpl * dsdcb,
144 IDsCaptureDriverNotifyImpl **pdscdn);
146 /*======================================================================*
147 * Low level DSOUND capture property set implementation *
148 *======================================================================*/
150 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_QueryInterface(
151 PIDSDRIVERPROPERTYSET iface,
155 IDsCaptureDriverPropertySetImpl *This = (IDsCaptureDriverPropertySetImpl *)iface;
156 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
158 if ( IsEqualGUID(riid, &IID_IUnknown) ||
159 IsEqualGUID(riid, &IID_IDsDriverPropertySet) ) {
160 IDsDriverPropertySet_AddRef(iface);
161 *ppobj = (LPVOID)This;
165 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
168 return E_NOINTERFACE;
171 static ULONG WINAPI IDsCaptureDriverPropertySetImpl_AddRef(
172 PIDSDRIVERPROPERTYSET iface)
174 IDsCaptureDriverPropertySetImpl *This = (IDsCaptureDriverPropertySetImpl *)iface;
175 ULONG refCount = InterlockedIncrement(&This->ref);
177 TRACE("(%p) ref was %ld\n", This, refCount - 1);
182 static ULONG WINAPI IDsCaptureDriverPropertySetImpl_Release(
183 PIDSDRIVERPROPERTYSET iface)
185 IDsCaptureDriverPropertySetImpl *This = (IDsCaptureDriverPropertySetImpl *)iface;
186 ULONG refCount = InterlockedDecrement(&This->ref);
188 TRACE("(%p) ref was %ld\n", This, refCount + 1);
191 IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER)This->capture_buffer);
192 This->capture_buffer->property_set = NULL;
193 HeapFree(GetProcessHeap(),0,This);
194 TRACE("(%p) released\n",This);
199 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Get(
200 PIDSDRIVERPROPERTYSET iface,
201 PDSPROPERTY pDsProperty,
202 LPVOID pPropertyParams,
203 ULONG cbPropertyParams,
204 LPVOID pPropertyData,
205 ULONG cbPropertyData,
206 PULONG pcbReturnedData )
208 IDsCaptureDriverPropertySetImpl *This = (IDsCaptureDriverPropertySetImpl *)iface;
209 FIXME("(%p,%p,%p,%lx,%p,%lx,%p)\n",This,pDsProperty,pPropertyParams,
210 cbPropertyParams,pPropertyData,cbPropertyData,pcbReturnedData);
211 return DSERR_UNSUPPORTED;
214 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Set(
215 PIDSDRIVERPROPERTYSET iface,
216 PDSPROPERTY pDsProperty,
217 LPVOID pPropertyParams,
218 ULONG cbPropertyParams,
219 LPVOID pPropertyData,
220 ULONG cbPropertyData )
222 IDsCaptureDriverPropertySetImpl *This = (IDsCaptureDriverPropertySetImpl *)iface;
223 FIXME("(%p,%p,%p,%lx,%p,%lx)\n",This,pDsProperty,pPropertyParams,
224 cbPropertyParams,pPropertyData,cbPropertyData);
225 return DSERR_UNSUPPORTED;
228 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_QuerySupport(
229 PIDSDRIVERPROPERTYSET iface,
230 REFGUID PropertySetId,
234 IDsCaptureDriverPropertySetImpl *This = (IDsCaptureDriverPropertySetImpl *)iface;
235 FIXME("(%p,%s,%lx,%p)\n",This,debugstr_guid(PropertySetId),PropertyId,
237 return DSERR_UNSUPPORTED;
240 IDsDriverPropertySetVtbl dscdpsvt =
242 IDsCaptureDriverPropertySetImpl_QueryInterface,
243 IDsCaptureDriverPropertySetImpl_AddRef,
244 IDsCaptureDriverPropertySetImpl_Release,
245 IDsCaptureDriverPropertySetImpl_Get,
246 IDsCaptureDriverPropertySetImpl_Set,
247 IDsCaptureDriverPropertySetImpl_QuerySupport,
250 /*======================================================================*
251 * Low level DSOUND capture notify implementation *
252 *======================================================================*/
254 static HRESULT WINAPI IDsCaptureDriverNotifyImpl_QueryInterface(
255 PIDSDRIVERNOTIFY iface,
259 IDsCaptureDriverNotifyImpl *This = (IDsCaptureDriverNotifyImpl *)iface;
260 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
262 if ( IsEqualGUID(riid, &IID_IUnknown) ||
263 IsEqualGUID(riid, &IID_IDsDriverNotify) ) {
264 IDsDriverNotify_AddRef(iface);
269 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
272 return E_NOINTERFACE;
275 static ULONG WINAPI IDsCaptureDriverNotifyImpl_AddRef(
276 PIDSDRIVERNOTIFY iface)
278 IDsCaptureDriverNotifyImpl *This = (IDsCaptureDriverNotifyImpl *)iface;
279 ULONG refCount = InterlockedIncrement(&This->ref);
281 TRACE("(%p) ref was %ld\n", This, refCount - 1);
286 static ULONG WINAPI IDsCaptureDriverNotifyImpl_Release(
287 PIDSDRIVERNOTIFY iface)
289 IDsCaptureDriverNotifyImpl *This = (IDsCaptureDriverNotifyImpl *)iface;
290 ULONG refCount = InterlockedDecrement(&This->ref);
292 TRACE("(%p) ref was %ld\n", This, refCount + 1);
295 IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER)This->capture_buffer);
296 This->capture_buffer->notify = NULL;
297 HeapFree(GetProcessHeap(),0,This);
298 TRACE("(%p) released\n",This);
303 static HRESULT WINAPI IDsCaptureDriverNotifyImpl_SetNotificationPositions(
304 PIDSDRIVERNOTIFY iface,
306 LPCDSBPOSITIONNOTIFY notify)
308 IDsCaptureDriverNotifyImpl *This = (IDsCaptureDriverNotifyImpl *)iface;
309 TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
312 WARN("invalid parameter\n");
313 return DSERR_INVALIDPARAM;
316 if (TRACE_ON(dscapture)) {
318 for (i=0;i<howmuch;i++)
319 TRACE("notify at %ld to 0x%08lx\n",
320 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
323 /* Make an internal copy of the caller-supplied array.
324 * Replace the existing copy if one is already present. */
325 if (This->capture_buffer->notifies)
326 This->capture_buffer->notifies = HeapReAlloc(GetProcessHeap(),
327 HEAP_ZERO_MEMORY, This->capture_buffer->notifies,
328 howmuch * sizeof(DSBPOSITIONNOTIFY));
330 This->capture_buffer->notifies = HeapAlloc(GetProcessHeap(),
331 HEAP_ZERO_MEMORY, howmuch * sizeof(DSBPOSITIONNOTIFY));
333 memcpy(This->capture_buffer->notifies, notify,
334 howmuch * sizeof(DSBPOSITIONNOTIFY));
335 This->capture_buffer->nrofnotifies = howmuch;
340 IDsDriverNotifyVtbl dscdnvt =
342 IDsCaptureDriverNotifyImpl_QueryInterface,
343 IDsCaptureDriverNotifyImpl_AddRef,
344 IDsCaptureDriverNotifyImpl_Release,
345 IDsCaptureDriverNotifyImpl_SetNotificationPositions,
348 /*======================================================================*
349 * Low level DSOUND capture implementation *
350 *======================================================================*/
352 static HRESULT DSCDB_MapBuffer(IDsCaptureDriverBufferImpl *dscdb)
354 if (!dscdb->mapping) {
355 dscdb->mapping = mmap(NULL, dscdb->maplen, PROT_READ, MAP_SHARED,
356 WInDev[dscdb->drv->wDevID].ossdev->fd, 0);
357 if (dscdb->mapping == (LPBYTE)-1) {
358 TRACE("(%p): Could not map sound device for direct access (%s)\n",
359 dscdb, strerror(errno));
360 return DSERR_GENERIC;
362 TRACE("(%p): sound device has been mapped for direct access at %p, "
363 "size=%ld\n", dscdb, dscdb->mapping, dscdb->maplen);
368 static HRESULT DSCDB_UnmapBuffer(IDsCaptureDriverBufferImpl *dscdb)
370 if (dscdb->mapping) {
371 if (munmap(dscdb->mapping, dscdb->maplen) < 0) {
372 ERR("(%p): Could not unmap sound device (%s)\n",
373 dscdb, strerror(errno));
374 return DSERR_GENERIC;
376 dscdb->mapping = NULL;
377 TRACE("(%p): sound device unmapped\n", dscdb);
382 static HRESULT WINAPI IDsCaptureDriverBufferImpl_QueryInterface(
383 PIDSCDRIVERBUFFER iface,
387 IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface;
388 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
392 if ( IsEqualGUID(riid, &IID_IUnknown) ||
393 IsEqualGUID(riid, &IID_IDsCaptureDriverBuffer) ) {
394 IDsCaptureDriverBuffer_AddRef(iface);
395 *ppobj = (LPVOID)This;
399 if ( IsEqualGUID( &IID_IDsDriverNotify, riid ) ) {
401 IDsCaptureDriverNotifyImpl_Create(This, &(This->notify));
403 IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY)This->notify);
404 *ppobj = (LPVOID)This->notify;
410 if ( IsEqualGUID( &IID_IDsDriverPropertySet, riid ) ) {
411 if (!This->property_set)
412 IDsCaptureDriverPropertySetImpl_Create(This, &(This->property_set));
413 if (This->property_set) {
414 IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET)This->property_set);
415 *ppobj = (LPVOID)This->property_set;
421 FIXME("(%p,%s,%p) unsupported GUID\n", This, debugstr_guid(riid), ppobj);
422 return DSERR_UNSUPPORTED;
425 static ULONG WINAPI IDsCaptureDriverBufferImpl_AddRef(PIDSCDRIVERBUFFER iface)
427 IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface;
428 ULONG refCount = InterlockedIncrement(&This->ref);
430 TRACE("(%p) ref was %ld\n", This, refCount - 1);
435 static ULONG WINAPI IDsCaptureDriverBufferImpl_Release(PIDSCDRIVERBUFFER iface)
437 IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface;
438 ULONG refCount = InterlockedDecrement(&This->ref);
439 TRACE("(%p) ref was %ld\n", This, refCount + 1);
444 wwi = &WInDev[This->drv->wDevID];
449 /* request thread termination */
450 write(This->pipe_fd[1], &x, sizeof(x));
453 WaitForSingleObject(This->hExitEvent, INFINITE);
454 CloseHandle(This->hExitEvent);
457 close(This->pipe_fd[0]);
458 close(This->pipe_fd[1]);
460 DSCDB_UnmapBuffer(This);
462 OSS_CloseDevice(wwi->ossdev);
463 wwi->state = WINE_WS_CLOSED;
464 wwi->dwFragmentSize = 0;
465 This->drv->capture_buffer = NULL;
467 HeapFree(GetProcessHeap(), 0, This->notifies);
468 HeapFree(GetProcessHeap(),0,This);
469 TRACE("(%p) released\n",This);
474 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Lock(
475 PIDSCDRIVERBUFFER iface,
480 DWORD dwWritePosition,
484 IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface;
485 TRACE("(%p,%p,%p,%p,%p,%ld,%ld,0x%08lx)\n",This,ppvAudio1,pdwLen1,
486 ppvAudio2,pdwLen2,dwWritePosition,dwWriteLen,dwFlags);
488 if (This->is_direct_map) {
490 *ppvAudio1 = This->mapping + dwWritePosition;
492 if (dwWritePosition + dwWriteLen < This->maplen) {
494 *pdwLen1 = dwWriteLen;
501 *pdwLen1 = This->maplen - dwWritePosition;
505 *pdwLen2 = dwWriteLen - (This->maplen - dwWritePosition);
509 *ppvAudio1 = This->buffer + dwWritePosition;
511 if (dwWritePosition + dwWriteLen < This->buflen) {
513 *pdwLen1 = dwWriteLen;
520 *pdwLen1 = This->buflen - dwWritePosition;
524 *pdwLen2 = dwWriteLen - (This->buflen - dwWritePosition);
531 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Unlock(
532 PIDSCDRIVERBUFFER iface,
538 IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface;
539 TRACE("(%p,%p,%ld,%p,%ld)\n",This,pvAudio1,dwLen1,pvAudio2,dwLen2);
541 if (This->is_direct_map)
542 This->map_readpos = (This->map_readpos + dwLen1 + dwLen2) % This->maplen;
544 This->readptr = (This->readptr + dwLen1 + dwLen2) % This->buflen;
549 static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetPosition(
550 PIDSCDRIVERBUFFER iface,
554 IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface;
555 TRACE("(%p,%p,%p)\n",This,lpdwCapture,lpdwRead);
557 if (WInDev[This->drv->wDevID].state == WINE_WS_CLOSED) {
558 ERR("device not open, but accessing?\n");
559 return DSERR_UNINITIALIZED;
562 if (!This->is_capturing) {
569 if (This->is_direct_map) {
571 *lpdwCapture = This->map_writepos;
573 *lpdwRead = This->map_readpos;
577 *lpdwCapture = This->writeptr;
579 *lpdwRead = This->readptr;
582 TRACE("capturepos=%ld, readpos=%ld\n", lpdwCapture?*lpdwCapture:0,
583 lpdwRead?*lpdwRead:0);
587 static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetStatus(
588 PIDSCDRIVERBUFFER iface,
591 IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface;
592 TRACE("(%p,%p)\n",This,lpdwStatus);
594 if (This->is_capturing) {
595 if (This->is_looping)
596 *lpdwStatus = DSCBSTATUS_CAPTURING | DSCBSTATUS_LOOPING;
598 *lpdwStatus = DSCBSTATUS_CAPTURING;
605 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Start(
606 PIDSCDRIVERBUFFER iface,
609 IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface;
611 TRACE("(%p,%lx)\n",This,dwFlags);
613 if (This->is_capturing)
616 if (dwFlags & DSCBSTART_LOOPING)
617 This->is_looping = TRUE;
619 WInDev[This->drv->wDevID].ossdev->bInputEnabled = TRUE;
620 enable = getEnables(WInDev[This->drv->wDevID].ossdev);
621 if (ioctl(WInDev[This->drv->wDevID].ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
622 if (errno == EINVAL) {
623 /* Don't give up yet. OSS trigger support is inconsistent. */
624 if (WInDev[This->drv->wDevID].ossdev->open_count == 1) {
625 /* try the opposite output enable */
626 if (WInDev[This->drv->wDevID].ossdev->bOutputEnabled == FALSE)
627 WInDev[This->drv->wDevID].ossdev->bOutputEnabled = TRUE;
629 WInDev[This->drv->wDevID].ossdev->bOutputEnabled = FALSE;
631 enable = getEnables(WInDev[This->drv->wDevID].ossdev);
632 if (ioctl(WInDev[This->drv->wDevID].ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) >= 0) {
633 This->is_capturing = TRUE;
638 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
639 WInDev[This->drv->wDevID].ossdev->dev_name, strerror(errno));
640 WInDev[This->drv->wDevID].ossdev->bInputEnabled = FALSE;
641 return DSERR_GENERIC;
644 This->is_capturing = TRUE;
648 static HRESULT WINAPI IDsCaptureDriverBufferImpl_Stop(PIDSCDRIVERBUFFER iface)
650 IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface;
652 TRACE("(%p)\n",This);
654 if (!This->is_capturing)
657 /* no more captureing */
658 WInDev[This->drv->wDevID].ossdev->bInputEnabled = FALSE;
659 enable = getEnables(WInDev[This->drv->wDevID].ossdev);
660 if (ioctl(WInDev[This->drv->wDevID].ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
661 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
662 WInDev[This->drv->wDevID].ossdev->dev_name, strerror(errno));
663 return DSERR_GENERIC;
666 /* send a final event if necessary */
667 if (This->nrofnotifies > 0) {
668 if (This->notifies[This->nrofnotifies - 1].dwOffset == DSBPN_OFFSETSTOP)
669 SetEvent(This->notifies[This->nrofnotifies - 1].hEventNotify);
672 This->is_capturing = FALSE;
673 This->is_looping = FALSE;
677 write(This->pipe_fd[1], &x, sizeof(x));
678 WaitForSingleObject(This->hExitEvent, INFINITE);
679 CloseHandle(This->hExitEvent);
680 This->hExitEvent = INVALID_HANDLE_VALUE;
687 static HRESULT WINAPI IDsCaptureDriverBufferImpl_SetFormat(
688 PIDSCDRIVERBUFFER iface,
691 IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface;
692 FIXME("(%p): stub!\n",This);
693 return DSERR_UNSUPPORTED;
696 static IDsCaptureDriverBufferVtbl dscdbvt =
698 IDsCaptureDriverBufferImpl_QueryInterface,
699 IDsCaptureDriverBufferImpl_AddRef,
700 IDsCaptureDriverBufferImpl_Release,
701 IDsCaptureDriverBufferImpl_Lock,
702 IDsCaptureDriverBufferImpl_Unlock,
703 IDsCaptureDriverBufferImpl_SetFormat,
704 IDsCaptureDriverBufferImpl_GetPosition,
705 IDsCaptureDriverBufferImpl_GetStatus,
706 IDsCaptureDriverBufferImpl_Start,
707 IDsCaptureDriverBufferImpl_Stop
710 static HRESULT WINAPI IDsCaptureDriverImpl_QueryInterface(
715 IDsCaptureDriverImpl *This = (IDsCaptureDriverImpl *)iface;
716 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
718 if ( IsEqualGUID(riid, &IID_IUnknown) ||
719 IsEqualGUID(riid, &IID_IDsCaptureDriver) ) {
720 IDsCaptureDriver_AddRef(iface);
721 *ppobj = (LPVOID)This;
725 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
729 return E_NOINTERFACE;
732 static ULONG WINAPI IDsCaptureDriverImpl_AddRef(PIDSCDRIVER iface)
734 IDsCaptureDriverImpl *This = (IDsCaptureDriverImpl *)iface;
735 ULONG refCount = InterlockedIncrement(&This->ref);
737 TRACE("(%p) ref was %ld\n", This, refCount - 1);
742 static ULONG WINAPI IDsCaptureDriverImpl_Release(PIDSCDRIVER iface)
744 IDsCaptureDriverImpl *This = (IDsCaptureDriverImpl *)iface;
745 ULONG refCount = InterlockedDecrement(&This->ref);
747 TRACE("(%p) ref was %ld\n", This, refCount + 1);
750 HeapFree(GetProcessHeap(),0,This);
751 TRACE("(%p) released\n",This);
756 static HRESULT WINAPI IDsCaptureDriverImpl_GetDriverDesc(
760 IDsCaptureDriverImpl *This = (IDsCaptureDriverImpl *)iface;
761 TRACE("(%p,%p)\n",This,pDesc);
764 TRACE("invalid parameter\n");
765 return DSERR_INVALIDPARAM;
768 /* copy version from driver */
769 memcpy(pDesc, &(WInDev[This->wDevID].ossdev->ds_desc), sizeof(DSDRIVERDESC));
771 pDesc->dnDevNode = WInDev[This->wDevID].waveDesc.dnDevNode;
773 pDesc->wReserved = 0;
774 pDesc->ulDeviceNum = This->wDevID;
775 pDesc->dwHeapType = DSDHEAP_NOHEAP;
776 pDesc->pvDirectDrawHeap = NULL;
777 pDesc->dwMemStartAddress = 0;
778 pDesc->dwMemEndAddress = 0;
779 pDesc->dwMemAllocExtra = 0;
780 pDesc->pvReserved1 = NULL;
781 pDesc->pvReserved2 = NULL;
785 static HRESULT WINAPI IDsCaptureDriverImpl_Open(PIDSCDRIVER iface)
787 IDsCaptureDriverImpl *This = (IDsCaptureDriverImpl *)iface;
788 TRACE("(%p)\n",This);
792 static HRESULT WINAPI IDsCaptureDriverImpl_Close(PIDSCDRIVER iface)
794 IDsCaptureDriverImpl *This = (IDsCaptureDriverImpl *)iface;
795 TRACE("(%p)\n",This);
796 if (This->capture_buffer) {
797 ERR("problem with DirectSound: capture buffer not released\n");
798 return DSERR_GENERIC;
803 static HRESULT WINAPI IDsCaptureDriverImpl_GetCaps(
805 PDSCDRIVERCAPS pCaps)
807 IDsCaptureDriverImpl *This = (IDsCaptureDriverImpl *)iface;
808 TRACE("(%p,%p)\n",This,pCaps);
809 memcpy(pCaps, &(WInDev[This->wDevID].ossdev->dsc_caps), sizeof(DSCDRIVERCAPS));
813 static void DSCDB_CheckEvent(
814 IDsCaptureDriverBufferImpl *dscb,
819 LPDSBPOSITIONNOTIFY event = dscb->notifies + dscb->notify_index;
820 DWORD offset = event->dwOffset;
821 TRACE("(%p,%ld,%ld,%ld)\n", dscb, writepos, len, buflen);
823 TRACE("(%p) buflen = %ld, writeptr = %ld\n",
824 dscb, dscb->buflen, dscb->writeptr);
825 TRACE("checking %d, position %ld, event = %p\n",
826 dscb->notify_index, offset, event->hEventNotify);
828 if ((writepos + len) > offset) {
829 TRACE("signalled event %p (%d) %ld\n",
830 event->hEventNotify, dscb->notify_index, offset);
831 SetEvent(event->hEventNotify);
832 dscb->notify_index = (dscb->notify_index + 1) % dscb->nrofnotifies;
834 } else if ((writepos + len) > buflen) {
835 writepos = writepos + len - buflen;
836 if ((writepos + len) > offset) {
837 TRACE("signalled event %p (%d) %ld\n",
838 event->hEventNotify, dscb->notify_index, offset);
839 SetEvent(event->hEventNotify);
840 dscb->notify_index = (dscb->notify_index + 1) % dscb->nrofnotifies;
848 /* FIXME: using memcpy can cause strange crashes so use this fake one */
849 static void * my_memcpy(void * dst, const void * src, int length)
852 for (i = 0; i < length; i++)
853 ((char *)dst)[i] = ((char *)src)[i];
857 static DWORD CALLBACK DSCDB_Thread(LPVOID lpParameter)
859 IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)lpParameter;
860 struct pollfd poll_list[2];
863 DWORD map_offset = 0;
864 TRACE("(%p)\n", lpParameter);
866 poll_list[0].fd = This->fd; /* data available */
867 poll_list[1].fd = This->pipe_fd[0]; /* message from parent process */
868 poll_list[0].events = POLLIN;
869 poll_list[1].events = POLLIN;
871 /* let other process know we are running */
872 SetEvent(This->hStartUpEvent);
875 /* wait for something to happen */
876 retval = poll(poll_list,(unsigned long)2,-1);
877 /* Retval will always be greater than 0 or -1 in this case.
878 * Since we're doing it while blocking
881 ERR("Error while polling: %s\n",strerror(errno));
885 /* check for exit command */
886 if ((poll_list[1].revents & POLLIN) == POLLIN) {
887 TRACE("(%p) done\n", lpParameter);
888 /* acknowledge command and exit */
889 SetEvent(This->hExitEvent);
895 if ((poll_list[0].revents & POLLIN) == POLLIN) {
897 int fragsize, first, second;
899 /* get the current DMA position */
900 if (ioctl(This->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
901 ERR("ioctl(%s, SNDCTL_DSP_GETIPTR) failed (%s)\n",
902 WInDev[This->drv->wDevID].ossdev->dev_name, strerror(errno));
903 return DSERR_GENERIC;
906 if (This->is_direct_map) {
907 offset = This->map_writepos;
908 This->map_writepos = info.ptr;
910 if (info.ptr < offset)
911 fragsize = info.ptr + This->maplen - offset;
913 fragsize = info.ptr - offset;
915 DSCDB_CheckEvent(This, offset, fragsize, This->maplen);
917 map_offset = This->map_writepos;
918 offset = This->writeptr;
920 /* test for mmap buffer wrap */
921 if (info.ptr < map_offset) {
922 /* mmap buffer wrapped */
923 fragsize = info.ptr + This->maplen - map_offset;
925 /* check for user buffer wrap */
926 if ((offset + fragsize) > This->buflen) {
927 /* both buffers wrapped
928 * figure out which wrapped first
930 if ((This->maplen - map_offset) > (This->buflen - offset)) {
931 /* user buffer wrapped first */
932 first = This->buflen - offset;
933 second = (This->maplen - map_offset) - first;
934 my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
935 my_memcpy(This->buffer, This->mapping + map_offset + first, second);
936 my_memcpy(This->buffer + second, This->mapping, fragsize - (first + second));
938 /* mmap buffer wrapped first */
939 first = This->maplen - map_offset;
940 second = (This->buflen - offset) - first;
941 my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
942 my_memcpy(This->buffer + offset + first, This->mapping, second);
943 my_memcpy(This->buffer, This->mapping + second, fragsize - (first + second));
946 /* only mmap buffer wrapped */
947 first = This->maplen - map_offset;
948 my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
949 my_memcpy(This->buffer + offset + first, This->mapping, fragsize - first);
952 /* mmap buffer didn't wrap */
953 fragsize = info.ptr - map_offset;
955 /* check for user buffer wrap */
956 if ((offset + fragsize) > This->buflen) {
957 first = This->buflen - offset;
958 my_memcpy(This->buffer + offset, This->mapping + map_offset, first);
959 my_memcpy(This->buffer, This->mapping + map_offset + first, fragsize - first);
961 my_memcpy(This->buffer + offset, This->mapping + map_offset, fragsize);
964 This->map_writepos = info.ptr;
965 This->writeptr = (This->writeptr + fragsize) % This->buflen;
966 DSCDB_CheckEvent(This, offset, fragsize, This->buflen);
972 static HRESULT WINAPI IDsCaptureDriverImpl_CreateCaptureBuffer(
977 LPDWORD pdwcbBufferSize,
981 IDsCaptureDriverImpl *This = (IDsCaptureDriverImpl *)iface;
982 IDsCaptureDriverBufferImpl** ippdscdb = (IDsCaptureDriverBufferImpl**)ppvObj;
985 int audio_fragment, fsize, shift, ret;
986 BOOL bNewBuffer = FALSE;
988 TRACE("(%p,%p,%lx,%lx,%p,%p,%p)\n",This,pwfx,dwFlags,dwCardAddress,
989 pdwcbBufferSize,ppbBuffer,ppvObj);
991 if (This->capture_buffer) {
992 TRACE("already allocated\n");
993 return DSERR_ALLOCATED;
996 /* must be given a buffer size */
997 if (pdwcbBufferSize == NULL || *pdwcbBufferSize == 0) {
998 TRACE("invalid parameter: pdwcbBufferSize\n");
999 return DSERR_INVALIDPARAM;
1002 /* must be given a buffer pointer */
1003 if (ppbBuffer == NULL) {
1004 TRACE("invalid parameter: ppbBuffer\n");
1005 return DSERR_INVALIDPARAM;
1008 /* may or may not be given a buffer */
1009 if (*ppbBuffer == NULL) {
1010 TRACE("creating buffer\n");
1011 bNewBuffer = TRUE; /* not given a buffer so create one */
1013 TRACE("using supplied buffer\n");
1015 *ippdscdb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsCaptureDriverBufferImpl));
1016 if (*ippdscdb == NULL) {
1017 TRACE("out of memory\n");
1018 return DSERR_OUTOFMEMORY;
1021 (*ippdscdb)->lpVtbl = &dscdbvt;
1022 (*ippdscdb)->ref = 1;
1023 (*ippdscdb)->drv = This;
1024 (*ippdscdb)->notify = NULL;
1025 (*ippdscdb)->notify_index = 0;
1026 (*ippdscdb)->notifies = NULL;
1027 (*ippdscdb)->nrofnotifies = 0;
1028 (*ippdscdb)->property_set = NULL;
1029 (*ippdscdb)->is_capturing = FALSE;
1030 (*ippdscdb)->is_looping = FALSE;
1031 (*ippdscdb)->wfx = *pwfx;
1032 (*ippdscdb)->buflen = *pdwcbBufferSize;
1035 (*ippdscdb)->buffer = NULL;
1037 (*ippdscdb)->buffer = *ppbBuffer;
1039 wwi = &WInDev[This->wDevID];
1041 if (wwi->state == WINE_WS_CLOSED) {
1042 unsigned int frag_size;
1044 if (wwi->ossdev->open_count > 0) {
1045 /* opened already so use existing fragment size */
1046 audio_fragment = wwi->ossdev->audio_fragment;
1048 /* calculate a fragment size */
1049 unsigned int mask = 0xffffffff;
1051 /* calculate largest fragment size less than 10 ms. */
1052 fsize = pwfx->nAvgBytesPerSec / 100; /* 10 ms chunk */
1054 while ((1 << shift) <= fsize)
1058 TRACE("shift = %d, fragment size = %d\n", shift, fsize);
1059 TRACE("BufferSize=%ld(%08lx)\n", *pdwcbBufferSize, *pdwcbBufferSize);
1061 /* See if we can directly map the buffer first.
1062 * (buffer length is multiple of a power of 2)
1064 mask = (mask >> (32 - shift));
1065 TRACE("mask=%08x\n", mask);
1066 if (*pdwcbBufferSize & mask) {
1067 /* no so try a smaller fragment size greater than 1 ms */
1068 int new_shift = shift - 1;
1070 int min_fsize = pwfx->nAvgBytesPerSec / 1000;
1071 BOOL found_one = FALSE;
1072 while ((1 << min_shift) <= min_fsize)
1075 while (new_shift > min_shift) {
1076 if (*pdwcbBufferSize & (-1 >> (32 - new_shift))) {
1085 /* found a smaller one that will work */
1086 audio_fragment = ((*pdwcbBufferSize >> new_shift) << 16) | new_shift;
1087 (*ippdscdb)->is_direct_map = TRUE;
1088 TRACE("new shift = %d, fragment size = %d\n",
1089 new_shift, 1 << (audio_fragment & 0xffff));
1091 /* buffer can't be direct mapped */
1092 audio_fragment = 0x00100000 + shift; /* 16 fragments of 2^shift */
1093 (*ippdscdb)->is_direct_map = FALSE;
1096 /* good fragment size */
1097 audio_fragment = ((*pdwcbBufferSize >> shift) << 16) | shift;
1098 (*ippdscdb)->is_direct_map = TRUE;
1101 frag_size = 1 << (audio_fragment & 0xffff);
1102 TRACE("is_direct_map = %s\n", (*ippdscdb)->is_direct_map ? "TRUE" : "FALSE");
1103 TRACE("requesting %d %d byte fragments (%d bytes) (%ld ms/fragment)\n",
1104 audio_fragment >> 16, frag_size, frag_size * (audio_fragment >> 16),
1105 (frag_size * 1000) / pwfx->nAvgBytesPerSec);
1107 ret = OSS_OpenDevice(wwi->ossdev, O_RDWR, &audio_fragment, 1,
1108 pwfx->nSamplesPerSec,
1109 (pwfx->nChannels > 1) ? 1 : 0,
1110 (pwfx->wBitsPerSample == 16)
1111 ? AFMT_S16_LE : AFMT_U8);
1114 WARN("OSS_OpenDevice failed\n");
1115 HeapFree(GetProcessHeap(),0,*ippdscdb);
1117 return DSERR_GENERIC;
1120 wwi->state = WINE_WS_STOPPED;
1122 /* find out what fragment and buffer sizes OSS gave us */
1123 if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
1124 ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n",
1125 wwi->ossdev->dev_name, strerror(errno));
1126 OSS_CloseDevice(wwi->ossdev);
1127 wwi->state = WINE_WS_CLOSED;
1128 HeapFree(GetProcessHeap(),0,*ippdscdb);
1130 return DSERR_GENERIC;
1133 TRACE("got %d %d byte fragments (%d bytes) (%ld ms/fragment)\n",
1134 info.fragstotal, info.fragsize, info.fragstotal * info.fragsize,
1135 info.fragsize * 1000 / pwfx->nAvgBytesPerSec);
1137 wwi->dwTotalRecorded = 0;
1138 memcpy(&wwi->waveFormat, pwfx, sizeof(PCMWAVEFORMAT));
1139 wwi->dwFragmentSize = info.fragsize;
1141 /* make sure we got what we asked for */
1142 if ((*ippdscdb)->buflen != info.fragstotal * info.fragsize) {
1143 TRACE("Couldn't create requested buffer\n");
1144 if ((*ippdscdb)->is_direct_map) {
1145 (*ippdscdb)->is_direct_map = FALSE;
1146 TRACE("is_direct_map = FALSE\n");
1148 } else if (info.fragsize != frag_size) {
1149 TRACE("same buffer length but different fragment size\n");
1152 (*ippdscdb)->fd = WInDev[This->wDevID].ossdev->fd;
1154 if (pipe((*ippdscdb)->pipe_fd) < 0) {
1155 TRACE("pipe() failed (%s)\n", strerror(errno));
1156 OSS_CloseDevice(wwi->ossdev);
1157 wwi->state = WINE_WS_CLOSED;
1158 HeapFree(GetProcessHeap(),0,*ippdscdb);
1160 return DSERR_GENERIC;
1163 /* check how big the DMA buffer is now */
1164 if (ioctl(wwi->ossdev->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
1165 ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n",
1166 wwi->ossdev->dev_name, strerror(errno));
1167 OSS_CloseDevice(wwi->ossdev);
1168 wwi->state = WINE_WS_CLOSED;
1169 close((*ippdscdb)->pipe_fd[0]);
1170 close((*ippdscdb)->pipe_fd[1]);
1171 HeapFree(GetProcessHeap(),0,*ippdscdb);
1173 return DSERR_GENERIC;
1176 (*ippdscdb)->maplen = info.fragstotal * info.fragsize;
1177 (*ippdscdb)->fragsize = info.fragsize;
1178 (*ippdscdb)->map_writepos = 0;
1179 (*ippdscdb)->map_readpos = 0;
1181 /* map the DMA buffer */
1182 err = DSCDB_MapBuffer(*ippdscdb);
1184 OSS_CloseDevice(wwi->ossdev);
1185 wwi->state = WINE_WS_CLOSED;
1186 close((*ippdscdb)->pipe_fd[0]);
1187 close((*ippdscdb)->pipe_fd[1]);
1188 HeapFree(GetProcessHeap(),0,*ippdscdb);
1193 /* create the buffer if necessary */
1194 if (!(*ippdscdb)->buffer)
1195 (*ippdscdb)->buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,(*ippdscdb)->buflen);
1197 if ((*ippdscdb)->buffer == NULL) {
1198 OSS_CloseDevice(wwi->ossdev);
1199 wwi->state = WINE_WS_CLOSED;
1200 close((*ippdscdb)->pipe_fd[0]);
1201 close((*ippdscdb)->pipe_fd[1]);
1202 HeapFree(GetProcessHeap(),0,*ippdscdb);
1204 return DSERR_OUTOFMEMORY;
1207 This->capture_buffer = *ippdscdb;
1209 (*ippdscdb)->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1210 (*ippdscdb)->hExitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1212 (*ippdscdb)->hThread = CreateThread(NULL, 0, DSCDB_Thread, (LPVOID)(*ippdscdb), 0, &((*ippdscdb)->dwThreadID));
1213 WaitForSingleObject((*ippdscdb)->hStartUpEvent, INFINITE);
1214 CloseHandle((*ippdscdb)->hStartUpEvent);
1215 (*ippdscdb)->hStartUpEvent = INVALID_HANDLE_VALUE;
1220 static IDsCaptureDriverVtbl dscdvt =
1222 IDsCaptureDriverImpl_QueryInterface,
1223 IDsCaptureDriverImpl_AddRef,
1224 IDsCaptureDriverImpl_Release,
1225 IDsCaptureDriverImpl_GetDriverDesc,
1226 IDsCaptureDriverImpl_Open,
1227 IDsCaptureDriverImpl_Close,
1228 IDsCaptureDriverImpl_GetCaps,
1229 IDsCaptureDriverImpl_CreateCaptureBuffer
1232 static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Create(
1233 IDsCaptureDriverBufferImpl * dscdb,
1234 IDsCaptureDriverPropertySetImpl **pdscdps)
1236 IDsCaptureDriverPropertySetImpl * dscdps;
1237 TRACE("(%p,%p)\n",dscdb,pdscdps);
1239 dscdps = (IDsCaptureDriverPropertySetImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dscdps));
1240 if (dscdps == NULL) {
1241 WARN("out of memory\n");
1242 return DSERR_OUTOFMEMORY;
1246 dscdps->lpVtbl = &dscdpsvt;
1247 dscdps->capture_buffer = dscdb;
1248 dscdb->property_set = dscdps;
1249 IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER)dscdb);
1255 static HRESULT WINAPI IDsCaptureDriverNotifyImpl_Create(
1256 IDsCaptureDriverBufferImpl * dscdb,
1257 IDsCaptureDriverNotifyImpl **pdscdn)
1259 IDsCaptureDriverNotifyImpl * dscdn;
1260 TRACE("(%p,%p)\n",dscdb,pdscdn);
1262 dscdn = (IDsCaptureDriverNotifyImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dscdn));
1263 if (dscdn == NULL) {
1264 WARN("out of memory\n");
1265 return DSERR_OUTOFMEMORY;
1269 dscdn->lpVtbl = &dscdnvt;
1270 dscdn->capture_buffer = dscdb;
1271 dscdb->notify = dscdn;
1272 IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER)dscdb);
1278 DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv)
1280 IDsCaptureDriverImpl** idrv = (IDsCaptureDriverImpl**)drv;
1281 TRACE("(%d,%p)\n",wDevID,drv);
1283 /* the HAL isn't much better than the HEL if we can't do mmap() */
1284 if (!(WInDev[wDevID].ossdev->in_caps_support & WAVECAPS_DIRECTSOUND)) {
1285 ERR("DirectSoundCapture flag not set\n");
1286 MESSAGE("This sound card's driver does not support direct access\n");
1287 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
1288 return MMSYSERR_NOTSUPPORTED;
1291 *idrv = (IDsCaptureDriverImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsCaptureDriverImpl));
1293 return MMSYSERR_NOMEM;
1294 (*idrv)->lpVtbl = &dscdvt;
1297 (*idrv)->wDevID = wDevID;
1298 (*idrv)->capture_buffer = NULL;
1299 return MMSYSERR_NOERROR;
1302 DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc)
1304 memcpy(desc, &(WInDev[wDevID].ossdev->ds_desc), sizeof(DSDRIVERDESC));
1305 return MMSYSERR_NOERROR;
1308 #endif /* HAVE_OSS */