Use a separate thread to do the capture instead of the timer callback
[wine/multimedia.git] / capture.c
blobdc28e2d2f300d0a4bf51c70ea9a35716c84db614
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;
587 for (i = 0; i < This->nnotify; ++i)
588 if (This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
590 SetEvent(This->notify[i].hEventNotify);
591 break;
593 This->playing = This->looping = 0;
594 alcCaptureStop(This->dev);
596 LeaveCriticalSection(&This->parent->crst);
597 return S_OK;
600 static HRESULT WINAPI DSCBuffer_Unlock(IDirectSoundCaptureBuffer8 *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
602 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
603 TRACE("(%p)->(%p,%"LONGFMT"u,%p,%"LONGFMT"u)\n", This, ptr1, len1, ptr2, len2);
605 if (!ptr1)
606 return DSERR_INVALIDPARAM;
607 return S_OK;
610 static HRESULT WINAPI DSCBuffer_GetObjectInPath(IDirectSoundCaptureBuffer8 *iface, REFGUID guid, DWORD num, REFGUID riid, void **ppv)
612 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
613 FIXME("(%p)->(%s %"LONGFMT"u %s %p) stub\n", This, debugstr_guid(guid), num, debugstr_guid(riid), ppv);
614 return E_NOTIMPL;
617 static HRESULT WINAPI DSCBuffer_GetFXStatus(IDirectSoundCaptureBuffer8 *iface, DWORD count, DWORD *status)
619 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
620 FIXME("(%p)->(%"LONGFMT"u %p) stub\n", This, count, status);
621 return E_NOTIMPL;
624 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl =
626 DSCBuffer_QueryInterface,
627 DSCBuffer_AddRef,
628 DSCBuffer_Release,
629 DSCBuffer_GetCaps,
630 DSCBuffer_GetCurrentPosition,
631 DSCBuffer_GetFormat,
632 DSCBuffer_GetStatus,
633 DSCBuffer_Initialize,
634 DSCBuffer_Lock,
635 DSCBuffer_Start,
636 DSCBuffer_Stop,
637 DSCBuffer_Unlock,
638 DSCBuffer_GetObjectInPath,
639 DSCBuffer_GetFXStatus
642 static inline DSCBuffer *impl_from_IDirectSoundNotify(IDirectSoundNotify *iface)
644 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundNotify_iface);
647 static HRESULT WINAPI DSCBufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
649 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
650 return IDirectSoundCaptureBuffer_QueryInterface((IDirectSoundCaptureBuffer*)This, riid, ppv);
653 static ULONG WINAPI DSCBufferNot_AddRef(IDirectSoundNotify *iface)
655 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
656 LONG ret;
658 InterlockedIncrement(&This->all_ref);
659 ret = InterlockedIncrement(&This->not_ref);
660 TRACE("new refcount %"LONGFMT"d\n", ret);
661 return ret;
664 static ULONG WINAPI DSCBufferNot_Release(IDirectSoundNotify *iface)
666 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
667 LONG ret;
669 ret = InterlockedDecrement(&This->not_ref);
670 TRACE("new refcount %"LONGFMT"d\n", ret);
671 if(InterlockedDecrement(&This->all_ref) == 0)
672 DSCBuffer_Destroy(This);
674 return ret;
677 static HRESULT WINAPI DSCBufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
679 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
680 DSBPOSITIONNOTIFY *nots;
681 HRESULT hr;
682 DWORD state;
684 EnterCriticalSection(&This->parent->crst);
685 hr = DSERR_INVALIDPARAM;
686 if (count && !notifications)
687 goto out;
689 hr = DSCBuffer_GetStatus(&This->IDirectSoundCaptureBuffer8_iface, &state);
690 if (FAILED(hr))
691 goto out;
693 hr = DSERR_INVALIDCALL;
694 if (state & DSCBSTATUS_CAPTURING)
695 goto out;
697 if (!count)
699 HeapFree(GetProcessHeap(), 0, This->notify);
700 This->notify = 0;
701 This->nnotify = 0;
703 else
705 DWORD i;
706 hr = DSERR_INVALIDPARAM;
707 for (i = 0; i < count; ++i)
709 if (notifications[i].dwOffset >= This->buf_size
710 && notifications[i].dwOffset != DSCBPN_OFFSET_STOP)
711 goto out;
713 hr = E_OUTOFMEMORY;
714 nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
715 if (!nots)
716 goto out;
717 memcpy(nots, notifications, count*sizeof(*nots));
718 HeapFree(GetProcessHeap(), 0, This->notify);
719 This->notify = nots;
720 This->nnotify = count;
721 hr = S_OK;
724 out:
725 LeaveCriticalSection(&This->parent->crst);
726 return hr;
729 static const IDirectSoundNotifyVtbl DSCNot_Vtbl =
731 DSCBufferNot_QueryInterface,
732 DSCBufferNot_AddRef,
733 DSCBufferNot_Release,
734 DSCBufferNot_SetNotificationPositions
738 static inline DSCImpl *impl_from_IDirectSoundCapture(IDirectSoundCapture *iface)
740 return CONTAINING_RECORD(iface, DSCImpl, IDirectSoundCapture_iface);
743 HRESULT DSOUND_CaptureCreate(REFIID riid, void **cap)
745 DSCImpl *This;
747 *cap = NULL;
749 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
750 if(!This) return DSERR_OUTOFMEMORY;
752 This->IDirectSoundCapture_iface.lpVtbl = (IDirectSoundCaptureVtbl*)&DSC_Vtbl;
754 InitializeCriticalSection(&This->crst);
755 This->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSCImpl.crst");
757 if(FAILED(IDirectSoundCapture_QueryInterface(&This->IDirectSoundCapture_iface, riid, cap)))
759 DSCImpl_Destroy(This);
760 return E_NOINTERFACE;
762 return S_OK;
765 static void DSCImpl_Destroy(DSCImpl *This)
767 EnterCriticalSection(&This->crst);
768 if (This->buf)
769 DSCBuffer_Destroy(This->buf);
770 LeaveCriticalSection(&This->crst);
772 HeapFree(GetProcessHeap(), 0, This->device);
774 This->crst.DebugInfo->Spare[0] = 0;
775 DeleteCriticalSection(&This->crst);
777 HeapFree(GetProcessHeap(), 0, This);
780 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv)
782 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
784 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
786 *ppv = NULL;
787 if(IsEqualIID(riid, &IID_IUnknown) ||
788 IsEqualIID(riid, &IID_IDirectSoundCapture))
789 *ppv = &This->IDirectSoundCapture_iface;
790 else
791 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
793 if(*ppv)
795 IUnknown_AddRef((IUnknown*)*ppv);
796 return S_OK;
799 return E_NOINTERFACE;
802 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture *iface)
804 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
805 LONG ref;
807 ref = InterlockedIncrement(&This->ref);
808 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
810 return ref;
813 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture *iface)
815 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
816 LONG ref;
818 ref = InterlockedDecrement(&This->ref);
819 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
820 if(!ref)
821 DSCImpl_Destroy(This);
823 return ref;
826 static HRESULT WINAPI DSCImpl_CreateCaptureBuffer(IDirectSoundCapture *iface, const DSCBUFFERDESC *desc, IDirectSoundCaptureBuffer **ppv, IUnknown *unk)
828 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
829 HRESULT hr;
831 TRACE("(%p)->(%p, %p, %p)\n", This, desc, ppv, unk);
833 if(unk)
835 WARN("Aggregation isn't supported\n");
836 return DSERR_NOAGGREGATION;
839 if(!desc || desc->dwSize < sizeof(DSCBUFFERDESC1))
841 WARN("Passed invalid description %p %"LONGFMT"u\n", desc, desc?desc->dwSize:0);
842 return DSERR_INVALIDPARAM;
844 if(!ppv)
846 WARN("Passed null pointer\n");
847 return DSERR_INVALIDPARAM;
849 *ppv = NULL;
851 EnterCriticalSection(&This->crst);
852 if(!This->device)
854 hr = DSERR_UNINITIALIZED;
855 WARN("Not initialized\n");
856 goto out;
858 if(This->buf)
860 hr = DSERR_ALLOCATED;
861 WARN("Capture buffer already allocated\n");
862 goto out;
865 hr = DSCBuffer_Create(&This->buf, This);
866 if(SUCCEEDED(hr))
868 hr = IDirectSoundCaptureBuffer8_Initialize(&This->buf->IDirectSoundCaptureBuffer8_iface, iface, desc);
869 if(SUCCEEDED(hr))
870 *ppv = (IDirectSoundCaptureBuffer*)&This->buf->IDirectSoundCaptureBuffer8_iface;
871 else
873 DSCBuffer_Destroy(This->buf);
874 This->buf = NULL;
878 out:
879 LeaveCriticalSection(&This->crst);
880 return hr;
883 static HRESULT WINAPI DSCImpl_GetCaps(IDirectSoundCapture *iface, DSCCAPS *caps)
885 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
887 TRACE("(%p)->(%p)\n", iface, caps);
889 if(!This->device) {
890 WARN("Not initialized\n");
891 return DSERR_UNINITIALIZED;
894 if(!caps) {
895 WARN("Caps is null\n");
896 return DSERR_INVALIDPARAM;
898 if(caps->dwSize < sizeof(*caps)) {
899 WARN("Invalid size %"LONGFMT"d\n", caps->dwSize);
900 return DSERR_INVALIDPARAM;
903 caps->dwFlags = 0;
904 /* Support all WAVE_FORMAT formats specified in mmsystem.h */
905 caps->dwFormats = 0x000fffff;
906 caps->dwChannels = 2;
908 return DS_OK;
911 static HRESULT WINAPI DSCImpl_Initialize(IDirectSoundCapture *iface, const GUID *devguid)
913 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
914 HRESULT hr;
915 const ALCchar *devs, *drv_name;
916 GUID guid;
917 UINT n;
919 TRACE("(%p)->(%p)\n", iface, devguid);
921 if(!openal_loaded)
923 ERR("OpenAL not loaded!\n");
924 return DSERR_NODRIVER;
927 if(This->device) {
928 WARN("Already initialized\n");
929 return DSERR_ALREADYINITIALIZED;
932 if(!devguid)
933 devguid = &DSDEVID_DefaultCapture;
935 hr = GetDeviceID(devguid, &guid);
936 if (FAILED(hr))
937 return DSERR_INVALIDPARAM;
938 devguid = &guid;
940 EnterCriticalSection(&This->crst);
941 EnterCriticalSection(&openal_crst);
942 devs = DSOUND_getcapturedevicestrings();
943 n = guid.Data4[7];
945 hr = DSERR_NODRIVER;
946 if(memcmp(devguid, &DSOUND_capture_guid, sizeof(GUID)-1) ||
947 !devs || !*devs)
949 WARN("No driver found\n");
950 goto out;
953 if(n)
955 const ALCchar *str = devs;
956 while (n--)
958 str += strlen(str) + 1;
959 if (!*str)
961 WARN("No driver string found\n");
962 goto out;
965 drv_name = str;
967 else
968 drv_name = devs;
970 This->device = HeapAlloc(GetProcessHeap(), 0, strlen(drv_name)+1);
971 if(!This->device)
973 WARN("Out of memory to allocate %s\n", drv_name);
974 goto out;
976 strcpy(This->device, drv_name);
978 hr = S_OK;
979 out:
980 LeaveCriticalSection(&openal_crst);
981 LeaveCriticalSection(&This->crst);
982 return hr;
985 static const IDirectSoundCaptureVtbl DSC_Vtbl =
987 DSCImpl_QueryInterface,
988 DSCImpl_AddRef,
989 DSCImpl_Release,
990 DSCImpl_CreateCaptureBuffer,
991 DSCImpl_GetCaps,
992 DSCImpl_Initialize