Accept GUID_NULL to get the default capture device
[dsound-openal.git] / capture.c
blob94bdb32b0a4e5e7922fba9997e716a544d7dc36f
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 0x0600
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 typedef struct DSCImpl DSCImpl;
62 typedef struct DSCBuffer DSCBuffer;
64 struct DSCImpl {
65 /* IDirectSoundCapture and IDirectSoundCapture8 are aliases */
66 IDirectSoundCapture IDirectSoundCapture_iface;
67 LONG ref;
69 ALCchar *device;
70 DSCBuffer *buf;
72 CRITICAL_SECTION crst;
75 struct DSCBuffer {
76 IDirectSoundCaptureBuffer8 IDirectSoundCaptureBuffer8_iface;
77 IDirectSoundNotify IDirectSoundNotify_iface;
78 LONG ref, not_ref;
79 LONG all_ref;
81 DSCImpl *parent;
82 ALCdevice *dev;
84 DWORD buf_size;
85 BYTE *buf;
87 WAVEFORMATEX *format;
89 DSBPOSITIONNOTIFY *notify;
90 DWORD nnotify;
92 UINT timer_id;
93 DWORD timer_res;
94 HANDLE thread_hdl;
95 DWORD thread_id;
97 DWORD pos;
98 BOOL playing, looping;
101 static const IDirectSoundCaptureVtbl DSC_Vtbl;
102 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl;
103 static const IDirectSoundNotifyVtbl DSCNot_Vtbl;
105 static void DSCImpl_Destroy(DSCImpl *This);
107 static void trigger_notifies(DSCBuffer *buf, DWORD lastpos, DWORD curpos)
109 DWORD i;
111 if (lastpos == curpos)
112 return;
114 for (i = 0; i < buf->nnotify; ++i)
116 DSBPOSITIONNOTIFY *not = &buf->notify[i];
117 HANDLE event = not->hEventNotify;
118 DWORD ofs = not->dwOffset;
120 if (ofs == DSCBPN_OFFSET_STOP)
121 continue;
123 /* Wraparound case */
124 if (curpos < lastpos)
126 if (ofs < curpos || ofs >= lastpos)
127 SetEvent(event);
128 continue;
131 /* Normal case */
132 if (ofs >= lastpos && ofs < curpos)
133 SetEvent(event);
137 static DWORD CALLBACK DSCBuffer_thread(void *param)
139 DSCImpl *This = param;
140 DSCBuffer *buf;
141 ALCint avail;
142 MSG msg;
144 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
146 while(GetMessageA(&msg, NULL, 0, 0))
148 if(msg.message != WM_USER)
149 continue;
151 avail = 0;
152 buf = This->buf;
153 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
154 if(avail == 0)
155 continue;
157 EnterCriticalSection(&This->crst);
158 more_samples:
159 avail *= buf->format->nBlockAlign;
160 if(avail + buf->pos > buf->buf_size)
161 avail = buf->buf_size - buf->pos;
163 alcCaptureSamples(buf->dev, buf->buf + buf->pos, avail/buf->format->nBlockAlign);
164 trigger_notifies(buf, buf->pos, buf->pos + avail);
165 buf->pos += avail;
167 if(buf->pos == buf->buf_size)
169 buf->pos = 0;
170 if(!buf->looping)
171 IDirectSoundCaptureBuffer8_Stop(&buf->IDirectSoundCaptureBuffer8_iface);
172 else
174 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
175 if(avail) goto more_samples;
179 LeaveCriticalSection(&This->crst);
182 return 0;
186 static void CALLBACK DSCBuffer_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
187 DWORD_PTR dw1, DWORD_PTR dw2)
189 (void)timerID;
190 (void)msg;
191 (void)dw1;
192 (void)dw2;
193 PostThreadMessageA(dwUser, WM_USER, 0, 0);
196 static void DSCBuffer_starttimer(DSCBuffer *This)
198 TIMECAPS time;
199 ALint refresh = FAKE_REFRESH_COUNT;
200 DWORD triggertime, res = DS_TIME_RES;
202 if(This->timer_id)
203 return;
205 timeGetDevCaps(&time, sizeof(TIMECAPS));
206 triggertime = 1000 / refresh;
207 if (triggertime < time.wPeriodMin)
208 triggertime = time.wPeriodMin;
209 TRACE("Calling timer every %"LONGFMT"u ms for %i refreshes per second\n", triggertime, refresh);
210 if (res < time.wPeriodMin)
211 res = time.wPeriodMin;
212 if (timeBeginPeriod(res) == TIMERR_NOCANDO)
213 WARN("Could not set minimum resolution, don't expect sound\n");
214 This->timer_res = res;
215 This->timer_id = timeSetEvent(triggertime, res, DSCBuffer_timer, This->thread_id, TIME_PERIODIC|TIME_KILL_SYNCHRONOUS);
218 static HRESULT DSCBuffer_Create(DSCBuffer **buf, DSCImpl *parent)
220 DSCBuffer *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
221 if(!This) return E_OUTOFMEMORY;
223 This->IDirectSoundCaptureBuffer8_iface.lpVtbl = (IDirectSoundCaptureBuffer8Vtbl*)&DSCBuffer_Vtbl;
224 This->IDirectSoundNotify_iface.lpVtbl = (IDirectSoundNotifyVtbl*)&DSCNot_Vtbl;
226 This->all_ref = This->ref = 1;
228 This->parent = parent;
230 This->thread_hdl = CreateThread(NULL, 0, DSCBuffer_thread, This->parent, 0, &This->thread_id);
231 if(This->thread_hdl == NULL)
233 HeapFree(GetProcessHeap(), 0, This);
234 return DSERR_OUTOFMEMORY;
237 *buf = This;
238 return S_OK;
241 static void DSCBuffer_Destroy(DSCBuffer *This)
243 if(This->timer_id)
245 timeKillEvent(This->timer_id);
246 timeEndPeriod(This->timer_res);
248 if(This->thread_hdl)
250 PostThreadMessageA(This->thread_id, WM_QUIT, 0, 0);
251 if(WaitForSingleObject(This->thread_hdl, 1000) != WAIT_OBJECT_0)
252 ERR("Thread wait timed out");
253 CloseHandle(This->thread_hdl);
256 if(This->dev)
258 if(This->playing)
259 alcCaptureStop(This->dev);
260 alcCaptureCloseDevice(This->dev);
262 This->parent->buf = NULL;
264 HeapFree(GetProcessHeap(), 0, This->notify);
265 HeapFree(GetProcessHeap(), 0, This->format);
266 HeapFree(GetProcessHeap(), 0, This->buf);
267 HeapFree(GetProcessHeap(), 0, This);
270 static inline DSCBuffer *impl_from_IDirectSoundCaptureBuffer8(IDirectSoundCaptureBuffer8 *iface)
272 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundCaptureBuffer8_iface);
275 static HRESULT WINAPI DSCBuffer_QueryInterface(IDirectSoundCaptureBuffer8 *iface, REFIID riid, void **ppv)
277 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
279 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
281 if(!ppv)
282 return E_POINTER;
283 *ppv = NULL;
285 if (IsEqualIID(riid, &IID_IDirectSoundNotify))
286 *ppv = &This->IDirectSoundNotify_iface;
287 else if (IsEqualIID(riid, &IID_IUnknown) ||
288 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer) ||
289 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer8))
290 *ppv = &This->IDirectSoundCaptureBuffer8_iface;
292 if (!*ppv)
293 return E_NOINTERFACE;
294 IUnknown_AddRef((IUnknown*)*ppv);
295 return S_OK;
298 static ULONG WINAPI DSCBuffer_AddRef(IDirectSoundCaptureBuffer8 *iface)
300 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
301 LONG ref;
303 InterlockedIncrement(&This->all_ref);
304 ref = InterlockedIncrement(&This->ref);
305 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
307 return ref;
310 static ULONG WINAPI DSCBuffer_Release(IDirectSoundCaptureBuffer8 *iface)
312 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
313 LONG ref;
315 ref = InterlockedDecrement(&This->ref);
316 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
317 if(InterlockedDecrement(&This->all_ref) == 0)
318 DSCBuffer_Destroy(This);
320 return ref;
323 static HRESULT WINAPI DSCBuffer_GetCaps(IDirectSoundCaptureBuffer8 *iface, DSCBCAPS *caps)
325 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
327 if (!caps || caps->dwSize < sizeof(*caps))
328 return DSERR_INVALIDPARAM;
329 caps->dwSize = sizeof(*caps);
330 caps->dwFlags = 0;
331 caps->dwBufferBytes = This->buf_size;
332 return S_OK;
335 static HRESULT WINAPI DSCBuffer_GetCurrentPosition(IDirectSoundCaptureBuffer8 *iface, DWORD *cappos, DWORD *readpos)
337 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
338 DWORD pos1, pos2;
340 EnterCriticalSection(&This->parent->crst);
341 pos1 = This->pos;
342 if (This->playing)
344 pos2 = This->format->nSamplesPerSec / 100;
345 pos2 *= This->format->nBlockAlign;
346 pos2 += pos1;
347 if (!This->looping && pos2 >= This->buf_size)
348 pos2 = 0;
349 else
350 pos2 %= This->buf_size;
352 else
353 pos2 = pos1;
354 LeaveCriticalSection(&This->parent->crst);
356 if(cappos) *cappos = pos1;
357 if(readpos) *readpos = pos2;
359 return S_OK;
362 static HRESULT WINAPI DSCBuffer_GetFormat(IDirectSoundCaptureBuffer8 *iface, WAVEFORMATEX *wfx, DWORD size, DWORD *written)
364 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
365 TRACE("(%p,%p,%"LONGFMT"u,%p)\n", This, wfx, size, written);
367 if (size > sizeof(WAVEFORMATEX) + This->format->cbSize)
368 size = sizeof(WAVEFORMATEX) + This->format->cbSize;
370 if (wfx)
372 CopyMemory(wfx, This->format, size);
373 if (written)
374 *written = size;
376 else if (written)
377 *written = sizeof(WAVEFORMATEX) + This->format->cbSize;
378 else
379 return DSERR_INVALIDPARAM;
381 return S_OK;
384 static HRESULT WINAPI DSCBuffer_GetStatus(IDirectSoundCaptureBuffer8 *iface, DWORD *status)
386 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
387 TRACE("(%p)->(%p)\n", This, status);
389 if (!status)
390 return DSERR_INVALIDPARAM;
391 EnterCriticalSection(&This->parent->crst);
392 *status = 0;
393 if (This->playing)
395 *status |= DSCBSTATUS_CAPTURING;
396 if (This->looping)
397 *status |= DSCBSTATUS_LOOPING;
399 LeaveCriticalSection(&This->parent->crst);
401 return S_OK;
404 static HRESULT WINAPI DSCBuffer_Initialize(IDirectSoundCaptureBuffer8 *iface, IDirectSoundCapture *parent, const DSCBUFFERDESC *desc)
406 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
407 WAVEFORMATEX *format;
408 ALenum buf_format = -1;
410 TRACE("(%p)->(%p, %p)\n", iface, parent, desc);
412 if(This->dev)
413 return DSERR_ALREADYINITIALIZED;
415 if (!desc->lpwfxFormat)
416 return DSERR_INVALIDPARAM;
418 format = desc->lpwfxFormat;
419 if (format->nChannels > 2)
421 WARN("nChannels > 2 not supported for recording\n");
422 return DSERR_INVALIDPARAM;
425 if (!This->format)
426 This->format = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE));
427 if (!This->format)
428 return DSERR_OUTOFMEMORY;
430 if (format->wFormatTag == WAVE_FORMAT_PCM ||
431 format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
433 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
435 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)format;
436 if (format->cbSize < sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX))
437 return DSERR_INVALIDPARAM;
438 else if (format->cbSize > sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
439 && format->cbSize != sizeof(WAVEFORMATEXTENSIBLE))
440 return DSERR_CONTROLUNAVAIL;
441 else if (!IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
442 return DSERR_BADFORMAT;
445 if (format->nChannels == 1)
447 switch (format->wBitsPerSample)
449 case 8: buf_format = AL_FORMAT_MONO8; break;
450 case 16: buf_format = AL_FORMAT_MONO16; break;
451 default:
452 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
453 return DSERR_BADFORMAT;
456 else if (format->nChannels == 2)
458 switch (format->wBitsPerSample)
460 case 8: buf_format = AL_FORMAT_STEREO8; break;
461 case 16: buf_format = AL_FORMAT_STEREO16; break;
462 default:
463 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
464 return DSERR_BADFORMAT;
467 memcpy(This->format, format, sizeof(*format) + format->cbSize);
468 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
469 This->format->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
470 else
471 This->format->cbSize = 0;
472 This->format->nBlockAlign = This->format->wBitsPerSample * This->format->nChannels / 8;
473 This->format->nAvgBytesPerSec = This->format->nSamplesPerSec * This->format->nBlockAlign;
475 else if (format->wFormatTag)
476 WARN("Unhandled formattag %x\n", format->wFormatTag);
478 This->buf_size = desc->dwBufferBytes;
479 if(buf_format <= 0)
481 WARN("Could not get OpenAL format\n");
482 return DSERR_INVALIDPARAM;
485 This->dev = alcCaptureOpenDevice(This->parent->device, This->format->nSamplesPerSec, buf_format, This->buf_size / This->format->nBlockAlign);
486 if (!This->dev)
488 ERR("couldn't open device %s %x@%"LONGFMT"u, reason: %04x\n", This->parent->device, buf_format, This->format->nSamplesPerSec, alcGetError(NULL));
489 return DSERR_INVALIDPARAM;
492 This->buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->buf_size);
493 if (!This->buf)
495 alcCaptureCloseDevice(This->dev);
496 WARN("Out of memory\n");
497 return DSERR_INVALIDPARAM;
500 return S_OK;
503 static HRESULT WINAPI DSCBuffer_Lock(IDirectSoundCaptureBuffer8 *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
505 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
506 HRESULT hr;
507 DWORD remain;
508 TRACE("(%p)->(%"LONGFMT"u, %"LONGFMT"u, %p, %p, %p, %p, %#"LONGFMT"x)\n", This, ofs, bytes, ptr1, len1, ptr2, len2, flags);
510 EnterCriticalSection(&This->parent->crst);
511 hr = DSERR_INVALIDPARAM;
513 if(ptr1) *ptr1 = NULL;
514 if(len1) *len1 = 0;
515 if(ptr2) *ptr2 = NULL;
516 if(len2) *len2 = 0;
518 if (ofs >= This->buf_size)
520 WARN("Invalid ofs %"LONGFMT"u\n", ofs);
521 goto out;
523 if (!ptr1 || !len1)
525 WARN("Invalid pointer/len %p %p\n", ptr1, len1);
526 goto out;
528 if((flags&DSCBLOCK_ENTIREBUFFER))
529 bytes = This->buf_size;
530 else if(bytes > This->buf_size)
532 WARN("Invalid size %"LONGFMT"u\n", bytes);
533 goto out;
536 if (ofs + bytes >= This->buf_size)
538 *len1 = This->buf_size - ofs;
539 remain = bytes - *len1;
541 else
543 *len1 = bytes;
544 remain = 0;
546 *ptr1 = This->buf + ofs;
548 if (ptr2 && len2 && remain)
550 *ptr2 = This->buf;
551 *len2 = remain;
553 hr = S_OK;
555 out:
556 LeaveCriticalSection(&This->parent->crst);
557 return hr;
560 static HRESULT WINAPI DSCBuffer_Start(IDirectSoundCaptureBuffer8 *iface, DWORD flags)
562 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
563 TRACE("(%p)->(%08"LONGFMT"x)\n", This, flags);
565 EnterCriticalSection(&This->parent->crst);
566 if(!This->playing)
568 DSCBuffer_starttimer(This);
569 This->playing = 1;
570 alcCaptureStart(This->dev);
572 This->looping = !!(flags & DSCBSTART_LOOPING);
573 LeaveCriticalSection(&This->parent->crst);
574 return S_OK;
577 static HRESULT WINAPI DSCBuffer_Stop(IDirectSoundCaptureBuffer8 *iface)
579 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
580 TRACE("(%p)\n", This);
582 EnterCriticalSection(&This->parent->crst);
583 if(This->playing)
585 DWORD i;
586 for(i = 0;i < This->nnotify;++i)
588 if(This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
589 SetEvent(This->notify[i].hEventNotify);
592 This->playing = This->looping = 0;
593 alcCaptureStop(This->dev);
595 LeaveCriticalSection(&This->parent->crst);
596 return S_OK;
599 static HRESULT WINAPI DSCBuffer_Unlock(IDirectSoundCaptureBuffer8 *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
601 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
602 TRACE("(%p)->(%p,%"LONGFMT"u,%p,%"LONGFMT"u)\n", This, ptr1, len1, ptr2, len2);
604 if (!ptr1)
605 return DSERR_INVALIDPARAM;
606 return S_OK;
609 static HRESULT WINAPI DSCBuffer_GetObjectInPath(IDirectSoundCaptureBuffer8 *iface, REFGUID guid, DWORD num, REFGUID riid, void **ppv)
611 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
612 FIXME("(%p)->(%s %"LONGFMT"u %s %p) stub\n", This, debugstr_guid(guid), num, debugstr_guid(riid), ppv);
613 return E_NOTIMPL;
616 static HRESULT WINAPI DSCBuffer_GetFXStatus(IDirectSoundCaptureBuffer8 *iface, DWORD count, DWORD *status)
618 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
619 FIXME("(%p)->(%"LONGFMT"u %p) stub\n", This, count, status);
620 return E_NOTIMPL;
623 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl =
625 DSCBuffer_QueryInterface,
626 DSCBuffer_AddRef,
627 DSCBuffer_Release,
628 DSCBuffer_GetCaps,
629 DSCBuffer_GetCurrentPosition,
630 DSCBuffer_GetFormat,
631 DSCBuffer_GetStatus,
632 DSCBuffer_Initialize,
633 DSCBuffer_Lock,
634 DSCBuffer_Start,
635 DSCBuffer_Stop,
636 DSCBuffer_Unlock,
637 DSCBuffer_GetObjectInPath,
638 DSCBuffer_GetFXStatus
641 static inline DSCBuffer *impl_from_IDirectSoundNotify(IDirectSoundNotify *iface)
643 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundNotify_iface);
646 static HRESULT WINAPI DSCBufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
648 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
649 return IDirectSoundCaptureBuffer_QueryInterface((IDirectSoundCaptureBuffer*)This, riid, ppv);
652 static ULONG WINAPI DSCBufferNot_AddRef(IDirectSoundNotify *iface)
654 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
655 LONG ret;
657 InterlockedIncrement(&This->all_ref);
658 ret = InterlockedIncrement(&This->not_ref);
659 TRACE("new refcount %"LONGFMT"d\n", ret);
660 return ret;
663 static ULONG WINAPI DSCBufferNot_Release(IDirectSoundNotify *iface)
665 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
666 LONG ret;
668 ret = InterlockedDecrement(&This->not_ref);
669 TRACE("new refcount %"LONGFMT"d\n", ret);
670 if(InterlockedDecrement(&This->all_ref) == 0)
671 DSCBuffer_Destroy(This);
673 return ret;
676 static HRESULT WINAPI DSCBufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
678 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
679 DSBPOSITIONNOTIFY *nots;
680 HRESULT hr;
681 DWORD state;
683 EnterCriticalSection(&This->parent->crst);
684 hr = DSERR_INVALIDPARAM;
685 if (count && !notifications)
686 goto out;
688 hr = DSCBuffer_GetStatus(&This->IDirectSoundCaptureBuffer8_iface, &state);
689 if (FAILED(hr))
690 goto out;
692 hr = DSERR_INVALIDCALL;
693 if (state & DSCBSTATUS_CAPTURING)
694 goto out;
696 if (!count)
698 HeapFree(GetProcessHeap(), 0, This->notify);
699 This->notify = 0;
700 This->nnotify = 0;
702 else
704 DWORD i;
705 hr = DSERR_INVALIDPARAM;
706 for (i = 0; i < count; ++i)
708 if (notifications[i].dwOffset >= This->buf_size
709 && notifications[i].dwOffset != DSCBPN_OFFSET_STOP)
710 goto out;
712 hr = E_OUTOFMEMORY;
713 nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
714 if (!nots)
715 goto out;
716 memcpy(nots, notifications, count*sizeof(*nots));
717 HeapFree(GetProcessHeap(), 0, This->notify);
718 This->notify = nots;
719 This->nnotify = count;
720 hr = S_OK;
723 out:
724 LeaveCriticalSection(&This->parent->crst);
725 return hr;
728 static const IDirectSoundNotifyVtbl DSCNot_Vtbl =
730 DSCBufferNot_QueryInterface,
731 DSCBufferNot_AddRef,
732 DSCBufferNot_Release,
733 DSCBufferNot_SetNotificationPositions
737 static inline DSCImpl *impl_from_IDirectSoundCapture(IDirectSoundCapture *iface)
739 return CONTAINING_RECORD(iface, DSCImpl, IDirectSoundCapture_iface);
742 HRESULT DSOUND_CaptureCreate(REFIID riid, void **cap)
744 DSCImpl *This;
746 *cap = NULL;
748 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
749 if(!This) return DSERR_OUTOFMEMORY;
751 This->IDirectSoundCapture_iface.lpVtbl = (IDirectSoundCaptureVtbl*)&DSC_Vtbl;
753 InitializeCriticalSection(&This->crst);
754 This->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSCImpl.crst");
756 if(FAILED(IDirectSoundCapture_QueryInterface(&This->IDirectSoundCapture_iface, riid, cap)))
758 DSCImpl_Destroy(This);
759 return E_NOINTERFACE;
761 return S_OK;
764 static void DSCImpl_Destroy(DSCImpl *This)
766 EnterCriticalSection(&This->crst);
767 if (This->buf)
768 DSCBuffer_Destroy(This->buf);
769 LeaveCriticalSection(&This->crst);
771 HeapFree(GetProcessHeap(), 0, This->device);
773 This->crst.DebugInfo->Spare[0] = 0;
774 DeleteCriticalSection(&This->crst);
776 HeapFree(GetProcessHeap(), 0, This);
779 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv)
781 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
783 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
785 *ppv = NULL;
786 if(IsEqualIID(riid, &IID_IUnknown) ||
787 IsEqualIID(riid, &IID_IDirectSoundCapture))
788 *ppv = &This->IDirectSoundCapture_iface;
789 else
790 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
792 if(*ppv)
794 IUnknown_AddRef((IUnknown*)*ppv);
795 return S_OK;
798 return E_NOINTERFACE;
801 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture *iface)
803 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
804 LONG ref;
806 ref = InterlockedIncrement(&This->ref);
807 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
809 return ref;
812 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture *iface)
814 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
815 LONG ref;
817 ref = InterlockedDecrement(&This->ref);
818 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
819 if(!ref)
820 DSCImpl_Destroy(This);
822 return ref;
825 static HRESULT WINAPI DSCImpl_CreateCaptureBuffer(IDirectSoundCapture *iface, const DSCBUFFERDESC *desc, IDirectSoundCaptureBuffer **ppv, IUnknown *unk)
827 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
828 HRESULT hr;
830 TRACE("(%p)->(%p, %p, %p)\n", This, desc, ppv, unk);
832 if(unk)
834 WARN("Aggregation isn't supported\n");
835 return DSERR_NOAGGREGATION;
838 if(!desc || desc->dwSize < sizeof(DSCBUFFERDESC1))
840 WARN("Passed invalid description %p %"LONGFMT"u\n", desc, desc?desc->dwSize:0);
841 return DSERR_INVALIDPARAM;
843 if(!ppv)
845 WARN("Passed null pointer\n");
846 return DSERR_INVALIDPARAM;
848 *ppv = NULL;
850 EnterCriticalSection(&This->crst);
851 if(!This->device)
853 hr = DSERR_UNINITIALIZED;
854 WARN("Not initialized\n");
855 goto out;
857 if(This->buf)
859 hr = DSERR_ALLOCATED;
860 WARN("Capture buffer already allocated\n");
861 goto out;
864 hr = DSCBuffer_Create(&This->buf, This);
865 if(SUCCEEDED(hr))
867 hr = IDirectSoundCaptureBuffer8_Initialize(&This->buf->IDirectSoundCaptureBuffer8_iface, iface, desc);
868 if(SUCCEEDED(hr))
869 *ppv = (IDirectSoundCaptureBuffer*)&This->buf->IDirectSoundCaptureBuffer8_iface;
870 else
872 DSCBuffer_Destroy(This->buf);
873 This->buf = NULL;
877 out:
878 LeaveCriticalSection(&This->crst);
879 return hr;
882 static HRESULT WINAPI DSCImpl_GetCaps(IDirectSoundCapture *iface, DSCCAPS *caps)
884 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
886 TRACE("(%p)->(%p)\n", iface, caps);
888 if(!This->device) {
889 WARN("Not initialized\n");
890 return DSERR_UNINITIALIZED;
893 if(!caps) {
894 WARN("Caps is null\n");
895 return DSERR_INVALIDPARAM;
897 if(caps->dwSize < sizeof(*caps)) {
898 WARN("Invalid size %"LONGFMT"d\n", caps->dwSize);
899 return DSERR_INVALIDPARAM;
902 caps->dwFlags = 0;
903 /* Support all WAVE_FORMAT formats specified in mmsystem.h */
904 caps->dwFormats = 0x000fffff;
905 caps->dwChannels = 2;
907 return DS_OK;
910 static HRESULT WINAPI DSCImpl_Initialize(IDirectSoundCapture *iface, const GUID *devguid)
912 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
913 HRESULT hr;
914 const ALCchar *devs, *drv_name;
915 GUID guid;
916 UINT n;
918 TRACE("(%p)->(%p)\n", iface, devguid);
920 if(!openal_loaded)
922 ERR("OpenAL not loaded!\n");
923 return DSERR_NODRIVER;
926 if(This->device) {
927 WARN("Already initialized\n");
928 return DSERR_ALREADYINITIALIZED;
931 if(!devguid || IsEqualGUID(devguid, &GUID_NULL))
932 devguid = &DSDEVID_DefaultCapture;
934 hr = GetDeviceID(devguid, &guid);
935 if (FAILED(hr))
936 return DSERR_INVALIDPARAM;
937 devguid = &guid;
939 EnterCriticalSection(&This->crst);
940 EnterCriticalSection(&openal_crst);
941 devs = DSOUND_getcapturedevicestrings();
942 n = guid.Data4[7];
944 hr = DSERR_NODRIVER;
945 if(memcmp(devguid, &DSOUND_capture_guid, sizeof(GUID)-1) ||
946 !devs || !*devs)
948 WARN("No driver found\n");
949 goto out;
952 if(n)
954 const ALCchar *str = devs;
955 while (n--)
957 str += strlen(str) + 1;
958 if (!*str)
960 WARN("No driver string found\n");
961 goto out;
964 drv_name = str;
966 else
967 drv_name = devs;
969 This->device = HeapAlloc(GetProcessHeap(), 0, strlen(drv_name)+1);
970 if(!This->device)
972 WARN("Out of memory to allocate %s\n", drv_name);
973 goto out;
975 strcpy(This->device, drv_name);
977 hr = S_OK;
978 out:
979 LeaveCriticalSection(&openal_crst);
980 LeaveCriticalSection(&This->crst);
981 return hr;
984 static const IDirectSoundCaptureVtbl DSC_Vtbl =
986 DSCImpl_QueryInterface,
987 DSCImpl_AddRef,
988 DSCImpl_Release,
989 DSCImpl_CreateCaptureBuffer,
990 DSCImpl_GetCaps,
991 DSCImpl_Initialize