Avoid a duplicate function
[dsound-openal.git] / capture.c
bloba4c60f442500ef34d1470a04399a22f570ff6f11
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
706 HRESULT DSOUND_CaptureCreate(REFIID riid, void **cap)
708 DSCImpl *This;
710 *cap = NULL;
712 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
713 if(!This) return DSERR_OUTOFMEMORY;
715 This->IDirectSoundCapture_iface.lpVtbl = (IDirectSoundCaptureVtbl*)&DSC_Vtbl;
717 InitializeCriticalSection(&This->crst);
718 This->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSCImpl.crst");
720 if(FAILED(IDirectSoundCapture_QueryInterface(&This->IDirectSoundCapture_iface, riid, cap)))
722 DSCImpl_Destroy(This);
723 return E_NOINTERFACE;
725 return S_OK;
728 static void DSCImpl_Destroy(DSCImpl *This)
730 if (This->timer_id)
732 timeKillEvent(This->timer_id);
733 timeEndPeriod(This->timer_res);
736 EnterCriticalSection(&This->crst);
737 if (This->buf)
738 DSCBuffer_Destroy(This->buf);
739 LeaveCriticalSection(&This->crst);
741 HeapFree(GetProcessHeap(), 0, This->device);
743 This->crst.DebugInfo->Spare[0] = 0;
744 DeleteCriticalSection(&This->crst);
746 HeapFree(GetProcessHeap(), 0, This);
749 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv)
751 *ppv = NULL;
752 if(IsEqualIID(riid, &IID_IUnknown) ||
753 IsEqualIID(riid, &IID_IDirectSoundCapture))
754 *ppv = iface;
756 if(!*ppv)
757 return E_NOINTERFACE;
758 IUnknown_AddRef((IUnknown*)*ppv);
759 return S_OK;
762 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture8 *iface)
764 DSCImpl *This = (DSCImpl*)iface;
765 LONG ref;
767 ref = InterlockedIncrement(&This->ref);
768 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
770 return ref;
773 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture8 *iface)
775 DSCImpl *This = (DSCImpl*)iface;
776 LONG ref;
778 ref = InterlockedDecrement(&This->ref);
779 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
780 if(!ref)
781 DSCImpl_Destroy(This);
783 return ref;
786 static HRESULT WINAPI DSCImpl_CreateCaptureBuffer(IDirectSoundCapture8 *iface, const DSCBUFFERDESC *desc, IDirectSoundCaptureBuffer **ppv, IUnknown *unk)
788 DSCImpl *This = (DSCImpl*)iface;
789 HRESULT hr;
790 TRACE("(%p)->(%p,%p,%p)\n", This, desc, ppv, unk);
792 if (unk)
794 WARN("Aggregation isn't supported\n");
795 return DSERR_NOAGGREGATION;
798 if (!desc || desc->dwSize < sizeof(DSCBUFFERDESC1))
800 WARN("Passed invalid description %p %"LONGFMT"u\n", desc, desc?desc->dwSize:0);
801 return DSERR_INVALIDPARAM;
803 if (!ppv)
805 WARN("Passed null pointer\n");
806 return DSERR_INVALIDPARAM;
808 *ppv = NULL;
810 EnterCriticalSection(&This->crst);
811 if (!This->device)
813 hr = DSERR_UNINITIALIZED;
814 WARN("Not initialized\n");
815 goto out;
817 if (This->buf)
819 hr = DSERR_ALLOCATED;
820 WARN("Capture buffer already allocated\n");
821 goto out;
824 hr = DSCBuffer_Create(&This->buf);
825 if (SUCCEEDED(hr))
827 hr = IDirectSoundCaptureBuffer_Initialize((IDirectSoundCaptureBuffer*)This->buf, iface, desc);
828 if (FAILED(hr))
830 DSCBuffer_Destroy(This->buf);
831 This->buf = NULL;
834 *ppv = (IDirectSoundCaptureBuffer*)This->buf;
835 out:
836 LeaveCriticalSection(&This->crst);
837 return hr;
840 static HRESULT WINAPI DSCImpl_GetCaps(IDirectSoundCapture8 *iface, DSCCAPS *caps)
842 DSCImpl *This = (DSCImpl*)iface;
843 TRACE("(%p,%p)\n", This, caps);
845 if (!This->device) {
846 WARN("Not initialized\n");
847 return DSERR_UNINITIALIZED;
850 if (!caps) {
851 WARN("Caps is null\n");
852 return DSERR_INVALIDPARAM;
855 if (caps->dwSize < sizeof(*caps)) {
856 WARN("Invalid size %"LONGFMT"d\n", caps->dwSize);
857 return DSERR_INVALIDPARAM;
860 caps->dwFlags = 0;
861 /* Support all WAVE_FORMAT formats specified in mmsystem.h */
862 caps->dwFormats = 0x000fffff;
863 caps->dwChannels = 2;
865 return DS_OK;
868 static HRESULT WINAPI DSCImpl_Initialize(IDirectSoundCapture8 *iface, const GUID *devguid)
870 DSCImpl *This = (DSCImpl*)iface;
871 HRESULT hr;
872 const ALCchar *devs, *drv_name;
873 GUID guid;
874 UINT n;
875 TRACE("(%p,%p)\n", This, devguid);
877 if (!openal_loaded)
879 ERR("OpenAL not loaded!\n");
880 return DSERR_NODRIVER;
883 if (This->device) {
884 WARN("Already initialized\n");
885 return DSERR_ALREADYINITIALIZED;
888 if (!devguid)
889 devguid = &DSDEVID_DefaultCapture;
891 hr = GetDeviceID(devguid, &guid);
892 if (FAILED(hr))
893 return DSERR_INVALIDPARAM;
894 devguid = &guid;
896 EnterCriticalSection(&This->crst);
897 EnterCriticalSection(&openal_crst);
898 devs = DSOUND_getcapturedevicestrings();
899 n = guid.Data4[7];
901 hr = DSERR_NODRIVER;
902 if(memcmp(devguid, &DSOUND_capture_guid, sizeof(GUID)-1) ||
903 !devs || !*devs)
905 WARN("No driver found\n");
906 goto out;
909 if(n)
911 const ALCchar *str = devs;
912 while (n--)
914 str += strlen(str) + 1;
915 if (!*str)
917 WARN("No driver string found\n");
918 goto out;
921 drv_name = str;
923 else
924 drv_name = devs;
926 This->device = HeapAlloc(GetProcessHeap(), 0, strlen(drv_name)+1);
927 if (!This->device)
929 WARN("Out of memory to allocate %s\n", drv_name);
930 goto out;
932 strcpy(This->device, drv_name);
934 hr = S_OK;
935 out:
936 LeaveCriticalSection(&openal_crst);
937 LeaveCriticalSection(&This->crst);
938 return hr;
941 static const IDirectSoundCaptureVtbl DSC_Vtbl =
943 DSCImpl_QueryInterface,
944 DSCImpl_AddRef,
945 DSCImpl_Release,
946 DSCImpl_CreateCaptureBuffer,
947 DSCImpl_GetCaps,
948 DSCImpl_Initialize