Initial check-in
[dsound-openal.git] / capture.c
blob331fad16a9eb8f227b8fffe1dbd656779b01adda
1 /* DirectSound capture buffer interface
3 * Copyright 2009 Maarten Lankhorst
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <stdarg.h>
22 #ifdef __WINESRC__
24 #define COBJMACROS
25 #define NONAMELESSSTRUCT
26 #define NONAMELESSUNION
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "winnls.h"
31 #include "winreg.h"
32 #include "mmsystem.h"
33 #include "winternl.h"
34 #include "mmddk.h"
35 #include "wine/debug.h"
36 #include "dsound.h"
38 #include "dsound_private.h"
40 #include "ks.h"
41 #include "ksmedia.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
45 #else
47 #define WINVER 0x6100
48 #include <windows.h>
49 #include <dsound.h>
51 #include "dsound_private.h"
53 #ifndef DSCBPN_OFFSET_STOP
54 #define DSCBPN_OFFSET_STOP 0xffffffff
55 #endif
57 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
59 #endif
61 /* IDirectSoundCapture and IDirectSoundCapture8 are aliases */
62 HRESULT DSOUND_CaptureCreate(REFIID riid, IDirectSoundCapture **cap)
64 return DSOUND_CaptureCreate8(riid, cap);
67 typedef struct DSCBuffer DSCBuffer;
69 typedef struct DSCImpl
71 const IDirectSoundCaptureVtbl *lpVtbl;
72 LONG ref;
74 ALCchar *device;
75 DSCBuffer *buf;
76 UINT timer_id;
77 DWORD timer_res;
78 CRITICAL_SECTION crst;
79 } DSCImpl;
81 struct DSCBuffer
83 const IDirectSoundCaptureBuffer8Vtbl *lpVtbl;
84 const IDirectSoundNotifyVtbl *lpNotVtbl;
85 LONG ref, not_ref;
86 DSCImpl *parent;
87 ALCdevice *dev;
88 DWORD buf_size;
89 BYTE *buf;
90 WAVEFORMATEX *format;
91 DSBPOSITIONNOTIFY *notify;
92 DWORD nnotify;
94 DWORD pos;
95 BOOL playing, looping;
98 static const IDirectSoundCaptureVtbl DSC_Vtbl;
99 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl;
100 static const IDirectSoundNotifyVtbl DSCNot_Vtbl;
102 static void DSCImpl_Destroy(DSCImpl *This);
104 static HRESULT DSCBuffer_Create(DSCBuffer **buf)
106 DSCBuffer *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
107 if (!This)
108 return E_OUTOFMEMORY;
109 This->lpVtbl = &DSCBuffer_Vtbl;
110 This->lpNotVtbl = &DSCNot_Vtbl;
111 This->ref = 1;
112 *buf = This;
113 return S_OK;
116 static void trigger_notifies(DSCBuffer *buf, DWORD lastpos, DWORD curpos)
118 DWORD i;
119 if (lastpos == curpos)
120 return;
121 for (i = 0; i < buf->nnotify; ++i)
123 DSBPOSITIONNOTIFY *not = &buf->notify[i];
124 HANDLE event = not->hEventNotify;
125 DWORD ofs = not->dwOffset;
127 if (ofs == DSCBPN_OFFSET_STOP)
128 continue;
130 /* Wraparound case */
131 if (curpos < lastpos)
133 if (ofs < curpos
134 || ofs >= lastpos)
135 SetEvent(event);
136 continue;
139 /* Normal case */
140 if (ofs >= lastpos
141 && ofs < curpos)
142 SetEvent(event);
146 static void CALLBACK DSCBuffer_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
147 DWORD_PTR dw1, DWORD_PTR dw2)
149 DSCImpl *This = (DSCImpl*)dwUser;
150 DSCBuffer *buf;
151 ALint avail = 0;
153 EnterCriticalSection(&This->crst);
154 buf = This->buf;
155 if (!buf || !buf->dev || !buf->playing)
156 goto out;
158 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
159 if (avail)
161 avail *= buf->format->nBlockAlign;
162 if (avail + buf->pos > buf->buf_size)
163 avail = buf->buf_size - buf->pos;
165 alcCaptureSamples(buf->dev, buf->buf + buf->pos, avail/buf->format->nBlockAlign);
166 trigger_notifies(buf, buf->pos, buf->pos + avail);
167 buf->pos += avail;
169 if (buf->pos == buf->buf_size)
171 buf->pos = 0;
172 if (!buf->looping)
173 IDirectSoundCaptureBuffer_Stop((IDirectSoundCaptureBuffer*)buf);
174 else
176 avail = 0;
177 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
178 avail *= buf->format->nBlockAlign;
179 if (avail >= buf->buf_size)
181 ERR("TOO MUCH AVAIL: %u/%u\n", avail, buf->buf_size);
182 avail = buf->buf_size;
185 if (avail)
187 alcCaptureSamples(buf->dev, buf->buf + buf->pos, avail/buf->format->nBlockAlign);
188 trigger_notifies(buf, buf->pos, buf->pos + avail);
189 buf->pos += avail;
195 out:
196 LeaveCriticalSection(&This->crst);
197 return;
200 static void DSCBuffer_starttimer(DSCImpl *prim)
202 TIMECAPS time;
203 ALint refresh = FAKE_REFRESH_COUNT;
204 DWORD triggertime, res = DS_TIME_RES;
206 if (prim->timer_id)
207 return;
209 timeGetDevCaps(&time, sizeof(TIMECAPS));
210 triggertime = 1000 / refresh;
211 if (triggertime < time.wPeriodMin)
212 triggertime = time.wPeriodMin;
213 TRACE("Calling timer every %u ms for %i refreshes per second\n", triggertime, refresh);
214 if (res < time.wPeriodMin)
215 res = time.wPeriodMin;
216 if (timeBeginPeriod(res) == TIMERR_NOCANDO)
217 WARN("Could not set minimum resolution, don't expect sound\n");
218 prim->timer_res = res;
219 prim->timer_id = timeSetEvent(triggertime, res, DSCBuffer_timer, (DWORD_PTR)prim, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
222 static void DSCBuffer_Destroy(DSCBuffer *This)
224 if (This->dev)
226 if (This->playing)
227 alcCaptureStop(This->dev);
228 alcCaptureCloseDevice(This->dev);
230 if (This->parent)
231 This->parent->buf = NULL;
232 HeapFree(GetProcessHeap(), 0, This->notify);
233 HeapFree(GetProcessHeap(), 0, This->format);
234 HeapFree(GetProcessHeap(), 0, This->buf);
235 HeapFree(GetProcessHeap(), 0, This);
238 static HRESULT WINAPI DSCBuffer_QueryInterface(IDirectSoundCaptureBuffer8 *iface, REFIID riid, void **ppv)
240 DSCBuffer *This = (DSCBuffer*)iface;
241 if (!ppv)
242 return E_POINTER;
243 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
244 *ppv = NULL;
245 if (IsEqualIID(riid, &IID_IDirectSoundNotify))
246 *ppv = &This->lpNotVtbl;
247 else if (IsEqualIID(riid, &IID_IUnknown)
248 || IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer)
249 || IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer8))
250 *ppv = This;
251 if (!*ppv)
252 return E_NOINTERFACE;
253 IUnknown_AddRef((IUnknown*)*ppv);
254 return S_OK;
257 static ULONG WINAPI DSCBuffer_AddRef(IDirectSoundCaptureBuffer8 *iface)
259 DSCBuffer *This = (DSCBuffer*)iface;
260 LONG ref;
261 ref = InterlockedIncrement(&This->ref);
262 TRACE("Reference count incremented to %i\n", ref);
263 return ref;
266 static ULONG WINAPI DSCBuffer_Release(IDirectSoundCaptureBuffer8 *iface)
268 DSCBuffer *This = (DSCBuffer*)iface;
269 CRITICAL_SECTION *crst = &This->parent->crst;
270 LONG ref;
271 EnterCriticalSection(crst);
272 ref = InterlockedDecrement(&This->ref);
273 TRACE("Reference count decremented to %i\n", ref);
274 if (!ref && !This->not_ref)
275 DSCBuffer_Destroy(This);
276 LeaveCriticalSection(crst);
277 return ref;
280 static HRESULT WINAPI DSCBuffer_GetCaps(IDirectSoundCaptureBuffer8 *iface, DSCBCAPS *caps)
282 DSCBuffer *This = (DSCBuffer*)iface;
284 if (!caps || caps->dwSize < sizeof(*caps))
285 return DSERR_INVALIDPARAM;
286 caps->dwSize = sizeof(*caps);
287 caps->dwFlags = 0;
288 caps->dwBufferBytes = This->buf_size;
289 return S_OK;
292 static HRESULT WINAPI DSCBuffer_GetCurrentPosition(IDirectSoundCaptureBuffer8 *iface, DWORD *cappos, DWORD *readpos)
294 DSCBuffer *This = (DSCBuffer*)iface;
295 DWORD pos1, pos2;
296 EnterCriticalSection(&This->parent->crst);
297 pos1 = This->pos;
298 if (This->playing)
300 pos2 = This->format->nSamplesPerSec / 100;
301 pos2 *= This->format->nBlockAlign;
302 pos2 += pos1;
303 if (!This->looping && pos2 > This->buf_size)
304 pos2 = 0;
305 else
306 pos2 %= This->buf_size;
308 else
309 pos2 = pos1;
310 pos2 %= This->buf_size;
311 LeaveCriticalSection(&This->parent->crst);
312 return S_OK;
315 static HRESULT WINAPI DSCBuffer_GetFormat(IDirectSoundCaptureBuffer8 *iface, WAVEFORMATEX *wfx, DWORD size, DWORD *written)
317 DSCBuffer *This = (DSCBuffer*)iface;
318 TRACE("(%p,%p,%u,%p)\n", This, wfx, size, written);
320 if (size > sizeof(WAVEFORMATEX) + This->format->cbSize)
321 size = sizeof(WAVEFORMATEX) + This->format->cbSize;
323 if (wfx)
325 CopyMemory(wfx, This->format, size);
326 if (written)
327 *written = size;
328 } else if (written)
329 *written = sizeof(WAVEFORMATEX) + This->format->cbSize;
330 else
331 return DSERR_INVALIDPARAM;
333 return S_OK;
336 static HRESULT WINAPI DSCBuffer_GetStatus(IDirectSoundCaptureBuffer8 *iface, DWORD *status)
338 DSCBuffer *This = (DSCBuffer*)iface;
339 TRACE("(%p)->(%p)\n", This, status);
341 if (!status)
342 return DSERR_INVALIDPARAM;
343 EnterCriticalSection(&This->parent->crst);
344 *status = 0;
345 if (This->playing)
347 *status |= DSCBSTATUS_CAPTURING;
348 if (This->looping)
349 *status |= DSCBSTATUS_LOOPING;
351 LeaveCriticalSection(&This->parent->crst);
353 return S_OK;
356 static HRESULT WINAPI DSCBuffer_Initialize(IDirectSoundCaptureBuffer8 *iface, IDirectSoundCapture *parent, const DSCBUFFERDESC *desc)
358 DSCBuffer *This = (DSCBuffer*)iface;
359 WAVEFORMATEX *format;
360 ALenum buf_format = -1;
362 if (This->parent)
363 return DSERR_ALREADYINITIALIZED;
364 This->parent = (DSCImpl*)parent;
365 if (!desc->lpwfxFormat)
366 return DSERR_INVALIDPARAM;
368 format = desc->lpwfxFormat;
369 if (format->nChannels > 2)
371 WARN("nChannels > 2 not supported for recording\n");
372 return DSERR_INVALIDPARAM;
375 if (!This->format)
376 This->format = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE));
377 if (!This->format)
378 return DSERR_OUTOFMEMORY;
380 if (format->wFormatTag == WAVE_FORMAT_PCM
381 || format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
383 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
385 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)format;
386 if (format->cbSize < sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX))
387 return DSERR_INVALIDPARAM;
388 else if (format->cbSize > sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
389 && format->cbSize != sizeof(WAVEFORMATEXTENSIBLE))
390 return DSERR_CONTROLUNAVAIL;
391 else if (!IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
392 return DSERR_BADFORMAT;
395 if (format->nChannels == 1)
397 switch (format->wBitsPerSample)
399 case 8: buf_format = AL_FORMAT_MONO8; break;
400 case 16: buf_format = AL_FORMAT_MONO16; break;
401 default:
402 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
403 return DSERR_BADFORMAT;
406 else if (format->nChannels == 2)
408 switch (format->wBitsPerSample)
410 case 8: buf_format = AL_FORMAT_STEREO8; break;
411 case 16: buf_format = AL_FORMAT_STEREO16; break;
412 default:
413 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
414 return DSERR_BADFORMAT;
417 memcpy(This->format, format, sizeof(*format) + format->cbSize);
418 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
419 This->format->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
420 else
421 This->format->cbSize = 0;
422 This->format->nBlockAlign = This->format->wBitsPerSample * This->format->nChannels / 8;
423 This->format->nAvgBytesPerSec = This->format->nSamplesPerSec * This->format->nBlockAlign;
425 else if (format->wFormatTag)
426 WARN("Unhandled formattag %x\n", format->wFormatTag);
428 This->buf_size = desc->dwBufferBytes;
429 if(buf_format <= 0)
431 WARN("Could not get OpenAL format\n");
432 return DSERR_INVALIDPARAM;
434 This->dev = alcCaptureOpenDevice(This->parent->device, This->format->nSamplesPerSec, buf_format, This->buf_size / This->format->nBlockAlign);
435 if (!This->dev)
437 ERR("couldn't open device %s %x@%u, reason: %04x\n", This->parent->device, buf_format, This->format->nSamplesPerSec, alcGetError(NULL));
438 return DSERR_INVALIDPARAM;
440 This->buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->buf_size);
441 if (!This->buf)
443 alcCaptureCloseDevice(This->dev);
444 WARN("Out of memory\n");
445 return DSERR_INVALIDPARAM;
448 return S_OK;
451 static HRESULT WINAPI DSCBuffer_Lock(IDirectSoundCaptureBuffer8 *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
453 DSCBuffer *This = (DSCBuffer*)iface;
454 HRESULT hr;
455 DWORD remain;
456 TRACE("(%p)->(%u, %u, %p, %p, %p, %p, %#x)\n", This, ofs, bytes, ptr1, len1, ptr2, len2, flags);
458 EnterCriticalSection(&This->parent->crst);
459 hr = DSERR_INVALIDPARAM;
460 if (ptr1)
461 *ptr1 = NULL;
462 if (len1)
463 *len1 = 0;
464 if (ptr2)
465 *ptr2 = NULL;
466 if (len2)
467 *len2 = 0;
468 if (ofs >= This->buf_size)
470 WARN("Invalid ofs %u\n", ofs);
471 goto out;
473 if (!ptr1 || !len1)
475 WARN("Invalid pointer/len %p %p\n", ptr1, len1);
476 goto out;
478 if (flags & DSCBLOCK_ENTIREBUFFER)
479 bytes = This->buf_size;
480 if (bytes > This->buf_size)
482 WARN("Invalid size %u\n", bytes);
483 goto out;
486 if (ofs + bytes >= This->buf_size)
488 *len1 = This->buf_size - ofs;
489 remain = bytes - *len1;
491 else
493 *len1 = bytes;
494 remain = 0;
496 *ptr1 = This->buf + ofs;
498 if (ptr2 && len2 && remain)
500 *ptr2 = This->buf;
501 *len2 = remain;
503 hr = S_OK;
505 out:
506 LeaveCriticalSection(&This->parent->crst);
507 return hr;
510 static HRESULT WINAPI DSCBuffer_Start(IDirectSoundCaptureBuffer8 *iface, DWORD flags)
512 DSCBuffer *This = (DSCBuffer*)iface;
513 TRACE("(%p)->(%08x)\n", This, flags);
515 EnterCriticalSection(&This->parent->crst);
516 if (!This->playing)
518 DSCBuffer_starttimer(This->parent);
519 This->playing = 1;
520 alcCaptureStart(This->dev);
522 This->looping = !!(flags & DSCBSTART_LOOPING);
523 LeaveCriticalSection(&This->parent->crst);
524 return S_OK;
527 static HRESULT WINAPI DSCBuffer_Stop(IDirectSoundCaptureBuffer8 *iface)
529 DSCBuffer *This = (DSCBuffer*)iface;
530 TRACE("(%p)\n", This);
532 EnterCriticalSection(&This->parent->crst);
533 if (This->playing)
535 DWORD i;
537 for (i = 0; i < This->nnotify; ++i)
538 if (This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
540 SetEvent(This->notify[i].hEventNotify);
541 break;
543 This->playing = This->looping = 0;
544 alcCaptureStop(This->dev);
546 LeaveCriticalSection(&This->parent->crst);
547 return S_OK;
550 static HRESULT WINAPI DSCBuffer_Unlock(IDirectSoundCaptureBuffer8 *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
552 DSCBuffer *This = (DSCBuffer*)iface;
553 TRACE("(%p)->(%p,%u,%p,%u)\n", This, ptr1, len2, ptr2, len2);
555 if (!ptr1)
556 return DSERR_INVALIDPARAM;
557 return S_OK;
560 static HRESULT WINAPI DSCBuffer_GetObjectInPath(IDirectSoundCaptureBuffer8 *iface, REFGUID guid, DWORD num, REFGUID riid, void **ppv)
562 DSCBuffer *This = (DSCBuffer*)iface;
563 FIXME("(%p)->(%s %u %s %p) stub\n", This, debugstr_guid(guid), num, debugstr_guid(riid), ppv);
564 return E_NOTIMPL;
567 static HRESULT WINAPI DSCBuffer_GetFXStatus(IDirectSoundCaptureBuffer8 *iface, DWORD count, DWORD *status)
569 DSCBuffer *This = (DSCBuffer*)iface;
570 FIXME("(%p)->(%u %p) stub\n", This, count, status);
571 return E_NOTIMPL;
574 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl =
576 DSCBuffer_QueryInterface,
577 DSCBuffer_AddRef,
578 DSCBuffer_Release,
579 DSCBuffer_GetCaps,
580 DSCBuffer_GetCurrentPosition,
581 DSCBuffer_GetFormat,
582 DSCBuffer_GetStatus,
583 DSCBuffer_Initialize,
584 DSCBuffer_Lock,
585 DSCBuffer_Start,
586 DSCBuffer_Stop,
587 DSCBuffer_Unlock,
588 DSCBuffer_GetObjectInPath,
589 DSCBuffer_GetFXStatus
592 static DSCBuffer *get_this_from_not(IDirectSoundNotify *iface)
594 return (DSCBuffer*)((char*)iface - offsetof(DSCBuffer,lpNotVtbl));
597 static HRESULT WINAPI DSCBufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
599 DSCBuffer *This = get_this_from_not(iface);
600 return IDirectSoundCaptureBuffer_QueryInterface((IDirectSoundCaptureBuffer*)This, riid, ppv);
603 static ULONG WINAPI DSCBufferNot_AddRef(IDirectSoundNotify *iface)
605 DSCBuffer *This = get_this_from_not(iface);
606 LONG ret;
608 ret = InterlockedIncrement(&This->not_ref);
609 TRACE("new refcount %d\n", ret);
610 return ret;
613 static ULONG WINAPI DSCBufferNot_Release(IDirectSoundNotify *iface)
615 DSCBuffer *This = get_this_from_not(iface);
616 CRITICAL_SECTION *crst = &This->parent->crst;
617 LONG ret;
619 EnterCriticalSection(crst);
620 ret = InterlockedDecrement(&This->not_ref);
621 TRACE("new refcount %d\n", ret);
622 if (!ret && !This->ref)
623 DSCBuffer_Destroy(This);
624 LeaveCriticalSection(crst);
625 return ret;
628 static HRESULT WINAPI DSCBufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
630 DSCBuffer *This = get_this_from_not(iface);
631 DSBPOSITIONNOTIFY *nots;
632 HRESULT hr;
633 DWORD state;
635 EnterCriticalSection(&This->parent->crst);
636 hr = DSERR_INVALIDPARAM;
637 if (count && !notifications)
638 goto out;
640 hr = DSCBuffer_GetStatus((IDirectSoundCaptureBuffer8*)This, &state);
641 if (FAILED(hr))
642 goto out;
644 hr = DSERR_INVALIDCALL;
645 if (state & DSCBSTATUS_CAPTURING)
646 goto out;
648 if (!count)
650 HeapFree(GetProcessHeap(), 0, This->notify);
651 This->notify = 0;
652 This->nnotify = 0;
654 else
656 DWORD i;
657 hr = DSERR_INVALIDPARAM;
658 for (i = 0; i < count; ++i)
660 if (notifications[i].dwOffset >= This->buf_size
661 && notifications[i].dwOffset != DSCBPN_OFFSET_STOP)
662 goto out;
664 hr = E_OUTOFMEMORY;
665 nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
666 if (!nots)
667 goto out;
668 memcpy(nots, notifications, count*sizeof(*nots));
669 HeapFree(GetProcessHeap(), 0, This->notify);
670 This->notify = nots;
671 This->nnotify = count;
672 hr = S_OK;
675 out:
676 LeaveCriticalSection(&This->parent->crst);
677 return hr;
680 static const IDirectSoundNotifyVtbl DSCNot_Vtbl =
682 DSCBufferNot_QueryInterface,
683 DSCBufferNot_AddRef,
684 DSCBufferNot_Release,
685 DSCBufferNot_SetNotificationPositions
688 HRESULT DSOUND_CaptureCreate8(REFIID riid, IDirectSoundCapture8 **cap)
690 DSCImpl *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
691 *cap = NULL;
692 if (!This)
693 return DSERR_OUTOFMEMORY;
694 This->lpVtbl = &DSC_Vtbl;
696 InitializeCriticalSection(&This->crst);
697 This->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSCImpl.crst");
698 if (FAILED(IUnknown_QueryInterface((IUnknown*)This, riid, (void**)cap)))
700 DSCImpl_Destroy(This);
701 return E_NOINTERFACE;
703 return S_OK;
706 static void DSCImpl_Destroy(DSCImpl *This)
708 if (This->timer_id)
710 timeKillEvent(This->timer_id);
711 timeEndPeriod(This->timer_res);
713 EnterCriticalSection(&This->crst);
714 if (This->buf)
715 DSCBuffer_Destroy(This->buf);
716 LeaveCriticalSection(&This->crst);
717 HeapFree(GetProcessHeap(), 0, This->device);
718 This->crst.DebugInfo->Spare[0] = 0;
719 DeleteCriticalSection(&This->crst);
720 HeapFree(GetProcessHeap(), 0, This);
723 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv)
725 *ppv = NULL;
726 if (IsEqualIID(riid, &IID_IUnknown)
727 || IsEqualIID(riid, &IID_IDirectSoundCapture))
729 *ppv = iface;
731 if (!*ppv)
732 return E_NOINTERFACE;
733 IUnknown_AddRef((IUnknown*)*ppv);
734 return S_OK;
737 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture8 *iface)
739 DSCImpl *This = (DSCImpl*)iface;
740 LONG ref;
741 ref = InterlockedIncrement(&This->ref);
742 TRACE("Reference count incremented to %i\n", ref);
743 return ref;
746 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture8 *iface)
748 DSCImpl *This = (DSCImpl*)iface;
749 LONG ref;
750 ref = InterlockedDecrement(&This->ref);
751 TRACE("Reference count decremented to %i\n", ref);
752 if (!ref)
753 DSCImpl_Destroy(This);
754 return ref;
757 static HRESULT WINAPI DSCImpl_CreateCaptureBuffer(IDirectSoundCapture8 *iface, const DSCBUFFERDESC *desc, IDirectSoundCaptureBuffer **ppv, IUnknown *unk)
759 DSCImpl *This = (DSCImpl*)iface;
760 HRESULT hr;
761 TRACE("(%p)->(%p,%p,%p)\n", This, desc, ppv, unk);
763 if (unk)
765 WARN("Aggregation isn't supported\n");
766 return DSERR_NOAGGREGATION;
769 if (!desc || desc->dwSize < sizeof(DSCBUFFERDESC1))
771 WARN("Passed invalid description %p %u\n", desc, desc?desc->dwSize:0);
772 return DSERR_INVALIDPARAM;
774 if (!ppv)
776 WARN("Passed null pointer\n");
777 return DSERR_INVALIDPARAM;
779 *ppv = NULL;
781 EnterCriticalSection(&This->crst);
782 if (!This->device)
784 hr = DSERR_UNINITIALIZED;
785 WARN("Not initialized\n");
786 goto out;
788 if (This->buf)
790 hr = DSERR_ALLOCATED;
791 WARN("Capture buffer already allocated\n");
792 goto out;
795 hr = DSCBuffer_Create(&This->buf);
796 if (SUCCEEDED(hr))
798 hr = IDirectSoundCaptureBuffer_Initialize((IDirectSoundCaptureBuffer*)This->buf, iface, desc);
799 if (FAILED(hr))
801 DSCBuffer_Destroy(This->buf);
802 This->buf = NULL;
805 *ppv = (IDirectSoundCaptureBuffer*)This->buf;
806 out:
807 LeaveCriticalSection(&This->crst);
808 return hr;
811 static HRESULT WINAPI DSCImpl_GetCaps(IDirectSoundCapture8 *iface, DSCCAPS *caps)
813 DSCImpl *This = (DSCImpl*)iface;
814 TRACE("(%p,%p)\n", This, caps);
816 if (!This->device) {
817 WARN("Not initialized\n");
818 return DSERR_UNINITIALIZED;
821 if (!caps) {
822 WARN("Caps is null\n");
823 return DSERR_INVALIDPARAM;
826 if (caps->dwSize < sizeof(*caps)) {
827 WARN("Invalid size %d\n", caps->dwSize);
828 return DSERR_INVALIDPARAM;
831 caps->dwFlags = 0;
832 /* Support all WAVE_FORMAT formats specified in mmsystem.h */
833 caps->dwFormats = 0x000fffff;
834 caps->dwChannels = 2;
836 return DS_OK;
839 static HRESULT WINAPI DSCImpl_Initialize(IDirectSoundCapture8 *iface, const GUID *devguid)
841 DSCImpl *This = (DSCImpl*)iface;
842 HRESULT hr;
843 const ALCchar *devs, *drv_name;
844 GUID guid;
845 UINT n;
846 TRACE("(%p,%p)\n", This, devguid);
848 if (!openal_loaded)
850 ERR("OpenAL not loaded!\n");
851 return DSERR_NODRIVER;
854 if (This->device) {
855 WARN("Already initialized\n");
856 return DSERR_ALREADYINITIALIZED;
859 if (!devguid)
860 devguid = &DSDEVID_DefaultCapture;
862 hr = GetDeviceID(devguid, &guid);
863 if (FAILED(hr))
864 return DSERR_INVALIDPARAM;
865 devguid = &guid;
867 EnterCriticalSection(&This->crst);
868 EnterCriticalSection(&openal_crst);
869 devs = DSOUND_getcapturedevicestrings();
870 n = guid.Data4[7];
872 hr = DSERR_NODRIVER;
873 if (memcmp(devguid, &DSOUND_capture_guid, sizeof(GUID)-1)
874 || !devs || !*devs)
876 WARN("No driver found\n");
877 goto out;
880 if (n)
882 const ALCchar *str = devs;
883 while (n--)
885 str += strlen(str) + 1;
886 if (!*str)
888 WARN("No driver string found\n");
889 goto out;
892 drv_name = str;
894 else
895 drv_name = devs;
897 This->device = HeapAlloc(GetProcessHeap(), 0, strlen(drv_name)+1);
898 if (!This->device)
900 WARN("Out of memory to allocate %s\n", drv_name);
901 goto out;
903 strcpy(This->device, drv_name);
905 hr = S_OK;
906 out:
907 LeaveCriticalSection(&openal_crst);
908 LeaveCriticalSection(&This->crst);
909 return hr;
912 static const IDirectSoundCaptureVtbl DSC_Vtbl =
914 DSCImpl_QueryInterface,
915 DSCImpl_AddRef,
916 DSCImpl_Release,
917 DSCImpl_CreateCaptureBuffer,
918 DSCImpl_GetCaps,
919 DSCImpl_Initialize