COM cleanup for IDirectSoundCapture
[dsound-openal.git] / capture.c
blob668ad00db340f71081cf802a98e7549ac7e4e942
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;
71 UINT timer_id;
72 DWORD timer_res;
73 CRITICAL_SECTION crst;
76 struct DSCBuffer {
77 IDirectSoundCaptureBuffer8 IDirectSoundCaptureBuffer8_iface;
78 IDirectSoundNotify IDirectSoundNotify_iface;
79 LONG ref, not_ref;
80 LONG all_ref;
82 DSCImpl *parent;
83 ALCdevice *dev;
84 DWORD buf_size;
85 BYTE *buf;
86 WAVEFORMATEX *format;
87 DSBPOSITIONNOTIFY *notify;
88 DWORD nnotify;
90 DWORD pos;
91 BOOL playing, looping;
94 static const IDirectSoundCaptureVtbl DSC_Vtbl;
95 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl;
96 static const IDirectSoundNotifyVtbl DSCNot_Vtbl;
98 static void DSCImpl_Destroy(DSCImpl *This);
100 static HRESULT DSCBuffer_Create(DSCBuffer **buf)
102 DSCBuffer *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
103 if (!This) return E_OUTOFMEMORY;
105 This->IDirectSoundCaptureBuffer8_iface.lpVtbl = (IDirectSoundCaptureBuffer8Vtbl*)&DSCBuffer_Vtbl;
106 This->IDirectSoundNotify_iface.lpVtbl = (IDirectSoundNotifyVtbl*)&DSCNot_Vtbl;
108 This->all_ref = This->ref = 1;
110 *buf = This;
111 return S_OK;
114 static void trigger_notifies(DSCBuffer *buf, DWORD lastpos, DWORD curpos)
116 DWORD i;
118 if (lastpos == curpos)
119 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 || ofs >= lastpos)
134 SetEvent(event);
135 continue;
138 /* Normal case */
139 if (ofs >= lastpos && ofs < curpos)
140 SetEvent(event);
144 static void CALLBACK DSCBuffer_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
145 DWORD_PTR dw1, DWORD_PTR dw2)
147 DSCImpl *This = (DSCImpl*)dwUser;
148 ALCint avail = 0;
149 DSCBuffer *buf;
150 (void)timerID;
151 (void)msg;
152 (void)dw1;
153 (void)dw2;
155 EnterCriticalSection(&This->crst);
156 buf = This->buf;
157 if (!buf || !buf->dev || !buf->playing)
158 goto out;
160 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
161 if (avail)
163 avail *= buf->format->nBlockAlign;
164 if (avail + buf->pos > buf->buf_size)
165 avail = buf->buf_size - buf->pos;
167 alcCaptureSamples(buf->dev, buf->buf + buf->pos, avail/buf->format->nBlockAlign);
168 trigger_notifies(buf, buf->pos, buf->pos + avail);
169 buf->pos += avail;
171 if (buf->pos == buf->buf_size)
173 buf->pos = 0;
174 if (!buf->looping)
175 IDirectSoundCaptureBuffer8_Stop(&buf->IDirectSoundCaptureBuffer8_iface);
176 else
178 avail = 0;
179 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
180 avail *= buf->format->nBlockAlign;
181 if ((ALCuint)avail >= buf->buf_size)
183 ERR("TOO MUCH AVAIL: %u/%"LONGFMT"u\n", avail, buf->buf_size);
184 avail = buf->buf_size;
187 if (avail)
189 alcCaptureSamples(buf->dev, buf->buf + buf->pos, avail/buf->format->nBlockAlign);
190 trigger_notifies(buf, buf->pos, buf->pos + avail);
191 buf->pos += avail;
197 out:
198 LeaveCriticalSection(&This->crst);
199 return;
202 static void DSCBuffer_starttimer(DSCImpl *prim)
204 TIMECAPS time;
205 ALint refresh = FAKE_REFRESH_COUNT;
206 DWORD triggertime, res = DS_TIME_RES;
208 if (prim->timer_id)
209 return;
211 timeGetDevCaps(&time, sizeof(TIMECAPS));
212 triggertime = 1000 / refresh;
213 if (triggertime < time.wPeriodMin)
214 triggertime = time.wPeriodMin;
215 TRACE("Calling timer every %"LONGFMT"u ms for %i refreshes per second\n", triggertime, refresh);
216 if (res < time.wPeriodMin)
217 res = time.wPeriodMin;
218 if (timeBeginPeriod(res) == TIMERR_NOCANDO)
219 WARN("Could not set minimum resolution, don't expect sound\n");
220 prim->timer_res = res;
221 prim->timer_id = timeSetEvent(triggertime, res, DSCBuffer_timer, (DWORD_PTR)prim, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
224 static void DSCBuffer_Destroy(DSCBuffer *This)
226 if(This->dev)
228 if(This->playing)
229 alcCaptureStop(This->dev);
230 alcCaptureCloseDevice(This->dev);
232 if(This->parent)
233 This->parent->buf = NULL;
234 HeapFree(GetProcessHeap(), 0, This->notify);
235 HeapFree(GetProcessHeap(), 0, This->format);
236 HeapFree(GetProcessHeap(), 0, This->buf);
237 HeapFree(GetProcessHeap(), 0, This);
240 static inline DSCBuffer *impl_from_IDirectSoundCaptureBuffer8(IDirectSoundCaptureBuffer8 *iface)
242 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundCaptureBuffer8_iface);
245 static HRESULT WINAPI DSCBuffer_QueryInterface(IDirectSoundCaptureBuffer8 *iface, REFIID riid, void **ppv)
247 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
249 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
251 if(!ppv)
252 return E_POINTER;
253 *ppv = NULL;
255 if (IsEqualIID(riid, &IID_IDirectSoundNotify))
256 *ppv = &This->IDirectSoundNotify_iface;
257 else if (IsEqualIID(riid, &IID_IUnknown) ||
258 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer) ||
259 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer8))
260 *ppv = &This->IDirectSoundCaptureBuffer8_iface;
262 if (!*ppv)
263 return E_NOINTERFACE;
264 IUnknown_AddRef((IUnknown*)*ppv);
265 return S_OK;
268 static ULONG WINAPI DSCBuffer_AddRef(IDirectSoundCaptureBuffer8 *iface)
270 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
271 LONG ref;
273 InterlockedIncrement(&This->all_ref);
274 ref = InterlockedIncrement(&This->ref);
275 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
277 return ref;
280 static ULONG WINAPI DSCBuffer_Release(IDirectSoundCaptureBuffer8 *iface)
282 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
283 LONG ref;
285 ref = InterlockedDecrement(&This->ref);
286 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
287 if(InterlockedDecrement(&This->all_ref) == 0)
288 DSCBuffer_Destroy(This);
290 return ref;
293 static HRESULT WINAPI DSCBuffer_GetCaps(IDirectSoundCaptureBuffer8 *iface, DSCBCAPS *caps)
295 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
297 if (!caps || caps->dwSize < sizeof(*caps))
298 return DSERR_INVALIDPARAM;
299 caps->dwSize = sizeof(*caps);
300 caps->dwFlags = 0;
301 caps->dwBufferBytes = This->buf_size;
302 return S_OK;
305 static HRESULT WINAPI DSCBuffer_GetCurrentPosition(IDirectSoundCaptureBuffer8 *iface, DWORD *cappos, DWORD *readpos)
307 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
308 DWORD pos1, pos2;
310 EnterCriticalSection(&This->parent->crst);
311 pos1 = This->pos;
312 if (This->playing)
314 pos2 = This->format->nSamplesPerSec / 100;
315 pos2 *= This->format->nBlockAlign;
316 pos2 += pos1;
317 if (!This->looping && pos2 >= This->buf_size)
318 pos2 = 0;
319 else
320 pos2 %= This->buf_size;
322 else
323 pos2 = pos1;
324 LeaveCriticalSection(&This->parent->crst);
326 if(cappos) *cappos = pos1;
327 if(readpos) *readpos = pos2;
329 return S_OK;
332 static HRESULT WINAPI DSCBuffer_GetFormat(IDirectSoundCaptureBuffer8 *iface, WAVEFORMATEX *wfx, DWORD size, DWORD *written)
334 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
335 TRACE("(%p,%p,%"LONGFMT"u,%p)\n", This, wfx, size, written);
337 if (size > sizeof(WAVEFORMATEX) + This->format->cbSize)
338 size = sizeof(WAVEFORMATEX) + This->format->cbSize;
340 if (wfx)
342 CopyMemory(wfx, This->format, size);
343 if (written)
344 *written = size;
346 else if (written)
347 *written = sizeof(WAVEFORMATEX) + This->format->cbSize;
348 else
349 return DSERR_INVALIDPARAM;
351 return S_OK;
354 static HRESULT WINAPI DSCBuffer_GetStatus(IDirectSoundCaptureBuffer8 *iface, DWORD *status)
356 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
357 TRACE("(%p)->(%p)\n", This, status);
359 if (!status)
360 return DSERR_INVALIDPARAM;
361 EnterCriticalSection(&This->parent->crst);
362 *status = 0;
363 if (This->playing)
365 *status |= DSCBSTATUS_CAPTURING;
366 if (This->looping)
367 *status |= DSCBSTATUS_LOOPING;
369 LeaveCriticalSection(&This->parent->crst);
371 return S_OK;
374 static HRESULT WINAPI DSCBuffer_Initialize(IDirectSoundCaptureBuffer8 *iface, IDirectSoundCapture *parent, const DSCBUFFERDESC *desc)
376 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
377 WAVEFORMATEX *format;
378 ALenum buf_format = -1;
380 if (This->parent)
381 return DSERR_ALREADYINITIALIZED;
382 This->parent = (DSCImpl*)parent;
384 if (!desc->lpwfxFormat)
385 return DSERR_INVALIDPARAM;
387 format = desc->lpwfxFormat;
388 if (format->nChannels > 2)
390 WARN("nChannels > 2 not supported for recording\n");
391 return DSERR_INVALIDPARAM;
394 if (!This->format)
395 This->format = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE));
396 if (!This->format)
397 return DSERR_OUTOFMEMORY;
399 if (format->wFormatTag == WAVE_FORMAT_PCM ||
400 format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
402 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
404 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)format;
405 if (format->cbSize < sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX))
406 return DSERR_INVALIDPARAM;
407 else if (format->cbSize > sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
408 && format->cbSize != sizeof(WAVEFORMATEXTENSIBLE))
409 return DSERR_CONTROLUNAVAIL;
410 else if (!IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
411 return DSERR_BADFORMAT;
414 if (format->nChannels == 1)
416 switch (format->wBitsPerSample)
418 case 8: buf_format = AL_FORMAT_MONO8; break;
419 case 16: buf_format = AL_FORMAT_MONO16; break;
420 default:
421 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
422 return DSERR_BADFORMAT;
425 else if (format->nChannels == 2)
427 switch (format->wBitsPerSample)
429 case 8: buf_format = AL_FORMAT_STEREO8; break;
430 case 16: buf_format = AL_FORMAT_STEREO16; break;
431 default:
432 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
433 return DSERR_BADFORMAT;
436 memcpy(This->format, format, sizeof(*format) + format->cbSize);
437 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
438 This->format->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
439 else
440 This->format->cbSize = 0;
441 This->format->nBlockAlign = This->format->wBitsPerSample * This->format->nChannels / 8;
442 This->format->nAvgBytesPerSec = This->format->nSamplesPerSec * This->format->nBlockAlign;
444 else if (format->wFormatTag)
445 WARN("Unhandled formattag %x\n", format->wFormatTag);
447 This->buf_size = desc->dwBufferBytes;
448 if(buf_format <= 0)
450 WARN("Could not get OpenAL format\n");
451 return DSERR_INVALIDPARAM;
454 This->dev = alcCaptureOpenDevice(This->parent->device, This->format->nSamplesPerSec, buf_format, This->buf_size / This->format->nBlockAlign);
455 if (!This->dev)
457 ERR("couldn't open device %s %x@%"LONGFMT"u, reason: %04x\n", This->parent->device, buf_format, This->format->nSamplesPerSec, alcGetError(NULL));
458 return DSERR_INVALIDPARAM;
461 This->buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->buf_size);
462 if (!This->buf)
464 alcCaptureCloseDevice(This->dev);
465 WARN("Out of memory\n");
466 return DSERR_INVALIDPARAM;
469 return S_OK;
472 static HRESULT WINAPI DSCBuffer_Lock(IDirectSoundCaptureBuffer8 *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
474 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
475 HRESULT hr;
476 DWORD remain;
477 TRACE("(%p)->(%"LONGFMT"u, %"LONGFMT"u, %p, %p, %p, %p, %#"LONGFMT"x)\n", This, ofs, bytes, ptr1, len1, ptr2, len2, flags);
479 EnterCriticalSection(&This->parent->crst);
480 hr = DSERR_INVALIDPARAM;
482 if(ptr1) *ptr1 = NULL;
483 if(len1) *len1 = 0;
484 if(ptr2) *ptr2 = NULL;
485 if(len2) *len2 = 0;
487 if (ofs >= This->buf_size)
489 WARN("Invalid ofs %"LONGFMT"u\n", ofs);
490 goto out;
492 if (!ptr1 || !len1)
494 WARN("Invalid pointer/len %p %p\n", ptr1, len1);
495 goto out;
497 if((flags&DSCBLOCK_ENTIREBUFFER))
498 bytes = This->buf_size;
499 else if(bytes > This->buf_size)
501 WARN("Invalid size %"LONGFMT"u\n", bytes);
502 goto out;
505 if (ofs + bytes >= This->buf_size)
507 *len1 = This->buf_size - ofs;
508 remain = bytes - *len1;
510 else
512 *len1 = bytes;
513 remain = 0;
515 *ptr1 = This->buf + ofs;
517 if (ptr2 && len2 && remain)
519 *ptr2 = This->buf;
520 *len2 = remain;
522 hr = S_OK;
524 out:
525 LeaveCriticalSection(&This->parent->crst);
526 return hr;
529 static HRESULT WINAPI DSCBuffer_Start(IDirectSoundCaptureBuffer8 *iface, DWORD flags)
531 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
532 TRACE("(%p)->(%08"LONGFMT"x)\n", This, flags);
534 EnterCriticalSection(&This->parent->crst);
535 if (!This->playing)
537 DSCBuffer_starttimer(This->parent);
538 This->playing = 1;
539 alcCaptureStart(This->dev);
541 This->looping = !!(flags & DSCBSTART_LOOPING);
542 LeaveCriticalSection(&This->parent->crst);
543 return S_OK;
546 static HRESULT WINAPI DSCBuffer_Stop(IDirectSoundCaptureBuffer8 *iface)
548 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
549 TRACE("(%p)\n", This);
551 EnterCriticalSection(&This->parent->crst);
552 if (This->playing)
554 DWORD i;
556 for (i = 0; i < This->nnotify; ++i)
557 if (This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
559 SetEvent(This->notify[i].hEventNotify);
560 break;
562 This->playing = This->looping = 0;
563 alcCaptureStop(This->dev);
565 LeaveCriticalSection(&This->parent->crst);
566 return S_OK;
569 static HRESULT WINAPI DSCBuffer_Unlock(IDirectSoundCaptureBuffer8 *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
571 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
572 TRACE("(%p)->(%p,%"LONGFMT"u,%p,%"LONGFMT"u)\n", This, ptr1, len1, ptr2, len2);
574 if (!ptr1)
575 return DSERR_INVALIDPARAM;
576 return S_OK;
579 static HRESULT WINAPI DSCBuffer_GetObjectInPath(IDirectSoundCaptureBuffer8 *iface, REFGUID guid, DWORD num, REFGUID riid, void **ppv)
581 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
582 FIXME("(%p)->(%s %"LONGFMT"u %s %p) stub\n", This, debugstr_guid(guid), num, debugstr_guid(riid), ppv);
583 return E_NOTIMPL;
586 static HRESULT WINAPI DSCBuffer_GetFXStatus(IDirectSoundCaptureBuffer8 *iface, DWORD count, DWORD *status)
588 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
589 FIXME("(%p)->(%"LONGFMT"u %p) stub\n", This, count, status);
590 return E_NOTIMPL;
593 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl =
595 DSCBuffer_QueryInterface,
596 DSCBuffer_AddRef,
597 DSCBuffer_Release,
598 DSCBuffer_GetCaps,
599 DSCBuffer_GetCurrentPosition,
600 DSCBuffer_GetFormat,
601 DSCBuffer_GetStatus,
602 DSCBuffer_Initialize,
603 DSCBuffer_Lock,
604 DSCBuffer_Start,
605 DSCBuffer_Stop,
606 DSCBuffer_Unlock,
607 DSCBuffer_GetObjectInPath,
608 DSCBuffer_GetFXStatus
611 static inline DSCBuffer *impl_from_IDirectSoundNotify(IDirectSoundNotify *iface)
613 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundNotify_iface);
616 static HRESULT WINAPI DSCBufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
618 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
619 return IDirectSoundCaptureBuffer_QueryInterface((IDirectSoundCaptureBuffer*)This, riid, ppv);
622 static ULONG WINAPI DSCBufferNot_AddRef(IDirectSoundNotify *iface)
624 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
625 LONG ret;
627 InterlockedIncrement(&This->all_ref);
628 ret = InterlockedIncrement(&This->not_ref);
629 TRACE("new refcount %"LONGFMT"d\n", ret);
630 return ret;
633 static ULONG WINAPI DSCBufferNot_Release(IDirectSoundNotify *iface)
635 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
636 LONG ret;
638 ret = InterlockedDecrement(&This->not_ref);
639 TRACE("new refcount %"LONGFMT"d\n", ret);
640 if(InterlockedDecrement(&This->all_ref) == 0)
641 DSCBuffer_Destroy(This);
643 return ret;
646 static HRESULT WINAPI DSCBufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
648 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
649 DSBPOSITIONNOTIFY *nots;
650 HRESULT hr;
651 DWORD state;
653 EnterCriticalSection(&This->parent->crst);
654 hr = DSERR_INVALIDPARAM;
655 if (count && !notifications)
656 goto out;
658 hr = DSCBuffer_GetStatus(&This->IDirectSoundCaptureBuffer8_iface, &state);
659 if (FAILED(hr))
660 goto out;
662 hr = DSERR_INVALIDCALL;
663 if (state & DSCBSTATUS_CAPTURING)
664 goto out;
666 if (!count)
668 HeapFree(GetProcessHeap(), 0, This->notify);
669 This->notify = 0;
670 This->nnotify = 0;
672 else
674 DWORD i;
675 hr = DSERR_INVALIDPARAM;
676 for (i = 0; i < count; ++i)
678 if (notifications[i].dwOffset >= This->buf_size
679 && notifications[i].dwOffset != DSCBPN_OFFSET_STOP)
680 goto out;
682 hr = E_OUTOFMEMORY;
683 nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
684 if (!nots)
685 goto out;
686 memcpy(nots, notifications, count*sizeof(*nots));
687 HeapFree(GetProcessHeap(), 0, This->notify);
688 This->notify = nots;
689 This->nnotify = count;
690 hr = S_OK;
693 out:
694 LeaveCriticalSection(&This->parent->crst);
695 return hr;
698 static const IDirectSoundNotifyVtbl DSCNot_Vtbl =
700 DSCBufferNot_QueryInterface,
701 DSCBufferNot_AddRef,
702 DSCBufferNot_Release,
703 DSCBufferNot_SetNotificationPositions
707 static inline DSCImpl *impl_from_IDirectSoundCapture(IDirectSoundCapture *iface)
709 return CONTAINING_RECORD(iface, DSCImpl, IDirectSoundCapture_iface);
712 HRESULT DSOUND_CaptureCreate(REFIID riid, void **cap)
714 DSCImpl *This;
716 *cap = NULL;
718 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
719 if(!This) return DSERR_OUTOFMEMORY;
721 This->IDirectSoundCapture_iface.lpVtbl = (IDirectSoundCaptureVtbl*)&DSC_Vtbl;
723 InitializeCriticalSection(&This->crst);
724 This->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSCImpl.crst");
726 if(FAILED(IDirectSoundCapture_QueryInterface(&This->IDirectSoundCapture_iface, riid, cap)))
728 DSCImpl_Destroy(This);
729 return E_NOINTERFACE;
731 return S_OK;
734 static void DSCImpl_Destroy(DSCImpl *This)
736 if (This->timer_id)
738 timeKillEvent(This->timer_id);
739 timeEndPeriod(This->timer_res);
742 EnterCriticalSection(&This->crst);
743 if (This->buf)
744 DSCBuffer_Destroy(This->buf);
745 LeaveCriticalSection(&This->crst);
747 HeapFree(GetProcessHeap(), 0, This->device);
749 This->crst.DebugInfo->Spare[0] = 0;
750 DeleteCriticalSection(&This->crst);
752 HeapFree(GetProcessHeap(), 0, This);
755 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv)
757 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
759 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
761 *ppv = NULL;
762 if(IsEqualIID(riid, &IID_IUnknown) ||
763 IsEqualIID(riid, &IID_IDirectSoundCapture))
764 *ppv = &This->IDirectSoundCapture_iface;
765 else
766 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
768 if(*ppv)
770 IUnknown_AddRef((IUnknown*)*ppv);
771 return S_OK;
774 return E_NOINTERFACE;
777 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture *iface)
779 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
780 LONG ref;
782 ref = InterlockedIncrement(&This->ref);
783 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
785 return ref;
788 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture *iface)
790 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
791 LONG ref;
793 ref = InterlockedDecrement(&This->ref);
794 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
795 if(!ref)
796 DSCImpl_Destroy(This);
798 return ref;
801 static HRESULT WINAPI DSCImpl_CreateCaptureBuffer(IDirectSoundCapture *iface, const DSCBUFFERDESC *desc, IDirectSoundCaptureBuffer **ppv, IUnknown *unk)
803 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
804 HRESULT hr;
806 TRACE("(%p)->(%p, %p, %p)\n", This, desc, ppv, unk);
808 if(unk)
810 WARN("Aggregation isn't supported\n");
811 return DSERR_NOAGGREGATION;
814 if(!desc || desc->dwSize < sizeof(DSCBUFFERDESC1))
816 WARN("Passed invalid description %p %"LONGFMT"u\n", desc, desc?desc->dwSize:0);
817 return DSERR_INVALIDPARAM;
819 if(!ppv)
821 WARN("Passed null pointer\n");
822 return DSERR_INVALIDPARAM;
824 *ppv = NULL;
826 EnterCriticalSection(&This->crst);
827 if(!This->device)
829 hr = DSERR_UNINITIALIZED;
830 WARN("Not initialized\n");
831 goto out;
833 if(This->buf)
835 hr = DSERR_ALLOCATED;
836 WARN("Capture buffer already allocated\n");
837 goto out;
840 hr = DSCBuffer_Create(&This->buf);
841 if(SUCCEEDED(hr))
843 hr = IDirectSoundCaptureBuffer8_Initialize(&This->buf->IDirectSoundCaptureBuffer8_iface, iface, desc);
844 if(SUCCEEDED(hr))
845 *ppv = (IDirectSoundCaptureBuffer*)&This->buf->IDirectSoundCaptureBuffer8_iface;
846 else
848 DSCBuffer_Destroy(This->buf);
849 This->buf = NULL;
853 out:
854 LeaveCriticalSection(&This->crst);
855 return hr;
858 static HRESULT WINAPI DSCImpl_GetCaps(IDirectSoundCapture *iface, DSCCAPS *caps)
860 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
862 TRACE("(%p)->(%p)\n", iface, caps);
864 if(!This->device) {
865 WARN("Not initialized\n");
866 return DSERR_UNINITIALIZED;
869 if(!caps) {
870 WARN("Caps is null\n");
871 return DSERR_INVALIDPARAM;
873 if(caps->dwSize < sizeof(*caps)) {
874 WARN("Invalid size %"LONGFMT"d\n", caps->dwSize);
875 return DSERR_INVALIDPARAM;
878 caps->dwFlags = 0;
879 /* Support all WAVE_FORMAT formats specified in mmsystem.h */
880 caps->dwFormats = 0x000fffff;
881 caps->dwChannels = 2;
883 return DS_OK;
886 static HRESULT WINAPI DSCImpl_Initialize(IDirectSoundCapture *iface, const GUID *devguid)
888 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
889 HRESULT hr;
890 const ALCchar *devs, *drv_name;
891 GUID guid;
892 UINT n;
894 TRACE("(%p)->(%p)\n", iface, devguid);
896 if(!openal_loaded)
898 ERR("OpenAL not loaded!\n");
899 return DSERR_NODRIVER;
902 if(This->device) {
903 WARN("Already initialized\n");
904 return DSERR_ALREADYINITIALIZED;
907 if(!devguid)
908 devguid = &DSDEVID_DefaultCapture;
910 hr = GetDeviceID(devguid, &guid);
911 if (FAILED(hr))
912 return DSERR_INVALIDPARAM;
913 devguid = &guid;
915 EnterCriticalSection(&This->crst);
916 EnterCriticalSection(&openal_crst);
917 devs = DSOUND_getcapturedevicestrings();
918 n = guid.Data4[7];
920 hr = DSERR_NODRIVER;
921 if(memcmp(devguid, &DSOUND_capture_guid, sizeof(GUID)-1) ||
922 !devs || !*devs)
924 WARN("No driver found\n");
925 goto out;
928 if(n)
930 const ALCchar *str = devs;
931 while (n--)
933 str += strlen(str) + 1;
934 if (!*str)
936 WARN("No driver string found\n");
937 goto out;
940 drv_name = str;
942 else
943 drv_name = devs;
945 This->device = HeapAlloc(GetProcessHeap(), 0, strlen(drv_name)+1);
946 if (!This->device)
948 WARN("Out of memory to allocate %s\n", drv_name);
949 goto out;
951 strcpy(This->device, drv_name);
953 hr = S_OK;
954 out:
955 LeaveCriticalSection(&openal_crst);
956 LeaveCriticalSection(&This->crst);
957 return hr;
960 static const IDirectSoundCaptureVtbl DSC_Vtbl =
962 DSCImpl_QueryInterface,
963 DSCImpl_AddRef,
964 DSCImpl_Release,
965 DSCImpl_CreateCaptureBuffer,
966 DSCImpl_GetCaps,
967 DSCImpl_Initialize