Improve capture buffer locking
[dsound-openal.git] / capture.c
blobacb119250bdad857c9f8e30b3615102ea97b857b
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 LONG locked;
89 WAVEFORMATEXTENSIBLE format;
91 DSBPOSITIONNOTIFY *notify;
92 DWORD nnotify;
94 UINT timer_id;
95 DWORD timer_res;
96 HANDLE thread_hdl;
97 DWORD thread_id;
99 DWORD pos;
100 BOOL playing, looping;
103 static const IDirectSoundCaptureVtbl DSC_Vtbl;
104 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl;
105 static const IDirectSoundNotifyVtbl DSCNot_Vtbl;
107 static void DSCImpl_Destroy(DSCImpl *This);
109 static void trigger_notifies(DSCBuffer *buf, DWORD lastpos, DWORD curpos)
111 DWORD i;
113 if(lastpos == curpos)
114 return;
116 for(i = 0;i < buf->nnotify;++i)
118 DSBPOSITIONNOTIFY *not = &buf->notify[i];
119 HANDLE event = not->hEventNotify;
120 DWORD ofs = not->dwOffset;
122 if (ofs == DSCBPN_OFFSET_STOP)
123 continue;
125 /* Wraparound case */
126 if(curpos < lastpos)
128 if(ofs < curpos || ofs >= lastpos)
130 TRACE("Triggering notification %"LONGFMT"u (%"LONGFMT"u) from buffer %p\n", i, ofs, buf);
131 SetEvent(event);
133 continue;
136 /* Normal case */
137 if(ofs >= lastpos && ofs < curpos)
139 TRACE("Triggering notification %"LONGFMT"u (%"LONGFMT"u) from buffer %p\n", i, ofs, buf);
140 SetEvent(event);
145 static DWORD CALLBACK DSCBuffer_thread(void *param)
147 DSCImpl *This = param;
148 DSCBuffer *buf;
149 ALCint avail;
150 MSG msg;
152 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
154 while(GetMessageA(&msg, NULL, 0, 0))
156 if(msg.message != WM_USER)
157 continue;
159 avail = 0;
160 buf = This->buf;
161 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
162 if(avail == 0)
163 continue;
165 EnterCriticalSection(&This->crst);
166 more_samples:
167 avail *= buf->format.Format.nBlockAlign;
168 if(avail + buf->pos > buf->buf_size)
169 avail = buf->buf_size - buf->pos;
171 alcCaptureSamples(buf->dev, buf->buf + buf->pos, avail/buf->format.Format.nBlockAlign);
172 trigger_notifies(buf, buf->pos, buf->pos + avail);
173 buf->pos += avail;
175 if(buf->pos == buf->buf_size)
177 buf->pos = 0;
178 if(!buf->looping)
179 IDirectSoundCaptureBuffer8_Stop(&buf->IDirectSoundCaptureBuffer8_iface);
180 else
182 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
183 if(avail) goto more_samples;
187 LeaveCriticalSection(&This->crst);
190 return 0;
194 static void CALLBACK DSCBuffer_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
195 DWORD_PTR dw1, DWORD_PTR dw2)
197 (void)timerID;
198 (void)msg;
199 (void)dw1;
200 (void)dw2;
201 PostThreadMessageA(dwUser, WM_USER, 0, 0);
204 static void DSCBuffer_starttimer(DSCBuffer *This)
206 TIMECAPS time;
207 ALint refresh = FAKE_REFRESH_COUNT;
208 DWORD triggertime, res = DS_TIME_RES;
210 if(This->timer_id)
211 return;
213 timeGetDevCaps(&time, sizeof(TIMECAPS));
214 triggertime = 1000 / refresh;
215 if (triggertime < time.wPeriodMin)
216 triggertime = time.wPeriodMin;
217 TRACE("Calling timer every %"LONGFMT"u ms for %i refreshes per second\n", triggertime, refresh);
218 if (res < time.wPeriodMin)
219 res = time.wPeriodMin;
220 if (timeBeginPeriod(res) == TIMERR_NOCANDO)
221 WARN("Could not set minimum resolution, don't expect sound\n");
222 This->timer_res = res;
223 This->timer_id = timeSetEvent(triggertime, res, DSCBuffer_timer, This->thread_id, TIME_PERIODIC|TIME_KILL_SYNCHRONOUS);
226 static HRESULT DSCBuffer_Create(DSCBuffer **buf, DSCImpl *parent)
228 DSCBuffer *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
229 if(!This) return E_OUTOFMEMORY;
231 This->IDirectSoundCaptureBuffer8_iface.lpVtbl = (IDirectSoundCaptureBuffer8Vtbl*)&DSCBuffer_Vtbl;
232 This->IDirectSoundNotify_iface.lpVtbl = (IDirectSoundNotifyVtbl*)&DSCNot_Vtbl;
234 This->all_ref = This->ref = 1;
236 This->parent = parent;
238 This->thread_hdl = CreateThread(NULL, 0, DSCBuffer_thread, This->parent, 0, &This->thread_id);
239 if(This->thread_hdl == NULL)
241 HeapFree(GetProcessHeap(), 0, This);
242 return DSERR_OUTOFMEMORY;
245 *buf = This;
246 return S_OK;
249 static void DSCBuffer_Destroy(DSCBuffer *This)
251 if(This->timer_id)
253 timeKillEvent(This->timer_id);
254 timeEndPeriod(This->timer_res);
256 if(This->thread_hdl)
258 PostThreadMessageA(This->thread_id, WM_QUIT, 0, 0);
259 if(WaitForSingleObject(This->thread_hdl, 1000) != WAIT_OBJECT_0)
260 ERR("Thread wait timed out");
261 CloseHandle(This->thread_hdl);
264 if(This->dev)
266 if(This->playing)
267 alcCaptureStop(This->dev);
268 alcCaptureCloseDevice(This->dev);
270 This->parent->buf = NULL;
272 HeapFree(GetProcessHeap(), 0, This->notify);
273 HeapFree(GetProcessHeap(), 0, This->buf);
274 HeapFree(GetProcessHeap(), 0, This);
277 static inline DSCBuffer *impl_from_IDirectSoundCaptureBuffer8(IDirectSoundCaptureBuffer8 *iface)
279 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundCaptureBuffer8_iface);
282 static HRESULT WINAPI DSCBuffer_QueryInterface(IDirectSoundCaptureBuffer8 *iface, REFIID riid, void **ppv)
284 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
286 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
288 if(!ppv)
289 return E_POINTER;
290 *ppv = NULL;
292 if (IsEqualIID(riid, &IID_IDirectSoundNotify))
293 *ppv = &This->IDirectSoundNotify_iface;
294 else if (IsEqualIID(riid, &IID_IUnknown) ||
295 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer) ||
296 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer8))
297 *ppv = &This->IDirectSoundCaptureBuffer8_iface;
299 if (!*ppv)
300 return E_NOINTERFACE;
301 IUnknown_AddRef((IUnknown*)*ppv);
302 return S_OK;
305 static ULONG WINAPI DSCBuffer_AddRef(IDirectSoundCaptureBuffer8 *iface)
307 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
308 LONG ref;
310 InterlockedIncrement(&This->all_ref);
311 ref = InterlockedIncrement(&This->ref);
312 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
314 return ref;
317 static ULONG WINAPI DSCBuffer_Release(IDirectSoundCaptureBuffer8 *iface)
319 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
320 LONG ref;
322 ref = InterlockedDecrement(&This->ref);
323 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
324 if(InterlockedDecrement(&This->all_ref) == 0)
325 DSCBuffer_Destroy(This);
327 return ref;
330 static HRESULT WINAPI DSCBuffer_GetCaps(IDirectSoundCaptureBuffer8 *iface, DSCBCAPS *caps)
332 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
334 if (!caps || caps->dwSize < sizeof(*caps))
335 return DSERR_INVALIDPARAM;
336 caps->dwSize = sizeof(*caps);
337 caps->dwFlags = 0;
338 caps->dwBufferBytes = This->buf_size;
339 return S_OK;
342 static HRESULT WINAPI DSCBuffer_GetCurrentPosition(IDirectSoundCaptureBuffer8 *iface, DWORD *cappos, DWORD *readpos)
344 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
345 DWORD pos1, pos2;
347 EnterCriticalSection(&This->parent->crst);
348 pos1 = This->pos;
349 if(This->playing)
351 pos2 = This->format.Format.nSamplesPerSec / 100;
352 pos2 *= This->format.Format.nBlockAlign;
353 pos2 += pos1;
354 if (!This->looping && pos2 >= This->buf_size)
355 pos2 = 0;
356 else
357 pos2 %= This->buf_size;
359 else
360 pos2 = pos1;
361 LeaveCriticalSection(&This->parent->crst);
363 if(cappos) *cappos = pos1;
364 if(readpos) *readpos = pos2;
366 return S_OK;
369 static HRESULT WINAPI DSCBuffer_GetFormat(IDirectSoundCaptureBuffer8 *iface, WAVEFORMATEX *wfx, DWORD allocated, DWORD *written)
371 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
372 HRESULT hr = DS_OK;
373 UINT size;
375 TRACE("(%p)->(%p, %"LONGFMT"u, %p)\n", iface, wfx, allocated, written);
377 if(!wfx && !written)
379 WARN("Cannot report format or format size\n");
380 return DSERR_INVALIDPARAM;
383 size = sizeof(This->format.Format) + This->format.Format.cbSize;
384 if(wfx)
386 if(allocated < size)
387 hr = DSERR_INVALIDPARAM;
388 else
389 memcpy(wfx, &This->format.Format, size);
391 if(written)
392 *written = size;
394 return hr;
397 static HRESULT WINAPI DSCBuffer_GetStatus(IDirectSoundCaptureBuffer8 *iface, DWORD *status)
399 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
401 TRACE("(%p)->(%p)\n", iface, status);
403 if (!status)
404 return DSERR_INVALIDPARAM;
405 EnterCriticalSection(&This->parent->crst);
406 *status = 0;
407 if (This->playing)
409 *status |= DSCBSTATUS_CAPTURING;
410 if (This->looping)
411 *status |= DSCBSTATUS_LOOPING;
413 LeaveCriticalSection(&This->parent->crst);
415 return S_OK;
418 static HRESULT WINAPI DSCBuffer_Initialize(IDirectSoundCaptureBuffer8 *iface, IDirectSoundCapture *parent, const DSCBUFFERDESC *desc)
420 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
421 WAVEFORMATEX *format;
422 ALenum buf_format = -1;
424 TRACE("(%p)->(%p, %p)\n", iface, parent, desc);
426 if(This->dev)
427 return DSERR_ALREADYINITIALIZED;
429 if (!desc->lpwfxFormat)
430 return DSERR_INVALIDPARAM;
432 format = desc->lpwfxFormat;
433 if(format->nChannels > 2)
435 WARN("nChannels > 2 not supported for recording\n");
436 return DSERR_INVALIDPARAM;
439 if(format->wFormatTag == WAVE_FORMAT_PCM)
441 if(format->nChannels == 1)
443 switch(format->wBitsPerSample)
445 case 8: buf_format = AL_FORMAT_MONO8; break;
446 case 16: buf_format = AL_FORMAT_MONO16; break;
447 default:
448 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
449 return DSERR_BADFORMAT;
452 else if(format->nChannels == 2)
454 switch(format->wBitsPerSample)
456 case 8: buf_format = AL_FORMAT_STEREO8; break;
457 case 16: buf_format = AL_FORMAT_STEREO16; break;
458 default:
459 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
460 return DSERR_BADFORMAT;
463 else
464 WARN("Unsupported channels: %d\n", format->nChannels);
466 memcpy(&This->format.Format, format, sizeof(This->format.Format));
467 This->format.Format.nBlockAlign = This->format.Format.wBitsPerSample * This->format.Format.nChannels / 8;
468 This->format.Format.nAvgBytesPerSec = This->format.Format.nSamplesPerSec * This->format.Format.nBlockAlign;
469 This->format.Format.cbSize = 0;
471 else if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
473 WAVEFORMATEXTENSIBLE *wfe;
475 if(format->cbSize < sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX))
476 return DSERR_INVALIDPARAM;
477 else if(format->cbSize > sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) &&
478 format->cbSize != sizeof(WAVEFORMATEXTENSIBLE))
479 return DSERR_CONTROLUNAVAIL;
481 wfe = CONTAINING_RECORD(format, WAVEFORMATEXTENSIBLE, Format);
482 if(!IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
483 return DSERR_BADFORMAT;
484 if(wfe->Samples.wValidBitsPerSample &&
485 wfe->Samples.wValidBitsPerSample != wfe->Format.wBitsPerSample)
486 return DSERR_BADFORMAT;
488 if(wfe->Format.nChannels == 1 && wfe->dwChannelMask == SPEAKER_FRONT_CENTER)
490 switch(wfe->Format.wBitsPerSample)
492 case 8: buf_format = AL_FORMAT_MONO8; break;
493 case 16: buf_format = AL_FORMAT_MONO16; break;
494 default:
495 WARN("Unsupported bpp %u\n", wfe->Format.wBitsPerSample);
496 return DSERR_BADFORMAT;
499 else if(wfe->Format.nChannels == 2 && wfe->dwChannelMask == (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT))
501 switch(wfe->Format.wBitsPerSample)
503 case 8: buf_format = AL_FORMAT_STEREO8; break;
504 case 16: buf_format = AL_FORMAT_STEREO16; break;
505 default:
506 WARN("Unsupported bpp %u\n", wfe->Format.wBitsPerSample);
507 return DSERR_BADFORMAT;
510 else
511 WARN("Unsupported channels: %d -- 0x%08"LONGFMT"u\n", wfe->Format.nChannels, wfe->dwChannelMask);
513 memcpy(&This->format, wfe, sizeof(This->format));
514 This->format.Format.cbSize = sizeof(This->format) - sizeof(This->format.Format);
515 This->format.Format.nBlockAlign = This->format.Format.wBitsPerSample * This->format.Format.nChannels / 8;
516 This->format.Format.nAvgBytesPerSec = This->format.Format.nSamplesPerSec * This->format.Format.nBlockAlign;
518 else
519 WARN("Unhandled formattag %x\n", format->wFormatTag);
521 if(buf_format <= 0)
523 WARN("Could not get OpenAL format\n");
524 return DSERR_INVALIDPARAM;
527 This->buf_size = desc->dwBufferBytes;
528 This->buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->buf_size);
529 if(!This->buf)
531 WARN("Out of memory\n");
532 return DSERR_INVALIDPARAM;
535 This->dev = alcCaptureOpenDevice(This->parent->device, This->format.Format.nSamplesPerSec, buf_format, This->buf_size / This->format.Format.nBlockAlign);
536 if(!This->dev)
538 ERR("Couldn't open device %s 0x%x@%"LONGFMT"u, reason: %04x\n", This->parent->device, buf_format, This->format.Format.nSamplesPerSec, alcGetError(NULL));
539 return DSERR_INVALIDPARAM;
542 return S_OK;
545 static HRESULT WINAPI DSCBuffer_Lock(IDirectSoundCaptureBuffer8 *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
547 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
548 DWORD remain;
550 TRACE("(%p)->(%"LONGFMT"u, %"LONGFMT"u, %p, %p, %p, %p, %#"LONGFMT"x)\n", iface, ofs, bytes, ptr1, len1, ptr2, len2, flags);
552 if(!ptr1 || !len1)
554 WARN("Invalid pointer/len %p %p\n", ptr1, len1);
555 return DSERR_INVALIDPARAM;
558 *ptr1 = NULL;
559 *len1 = 0;
560 if(ptr2) *ptr2 = NULL;
561 if(len2) *len2 = 0;
563 if(ofs >= This->buf_size)
565 WARN("Invalid ofs %"LONGFMT"u\n", ofs);
566 return DSERR_INVALIDPARAM;
569 if((flags&DSCBLOCK_ENTIREBUFFER))
570 bytes = This->buf_size;
571 else if(bytes > This->buf_size)
573 WARN("Invalid size %"LONGFMT"u\n", bytes);
574 return DSERR_INVALIDPARAM;
577 if(InterlockedExchange(&This->locked, TRUE) == TRUE)
579 WARN("Already locked\n");
580 return DSERR_INVALIDPARAM;
583 if(ofs + bytes >= This->buf_size)
585 *len1 = This->buf_size - ofs;
586 remain = bytes - *len1;
588 else
590 *len1 = bytes;
591 remain = 0;
593 *ptr1 = This->buf + ofs;
595 if(ptr2 && len2 && remain)
597 *ptr2 = This->buf;
598 *len2 = remain;
601 return DS_OK;
604 static HRESULT WINAPI DSCBuffer_Start(IDirectSoundCaptureBuffer8 *iface, DWORD flags)
606 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
608 TRACE("(%p)->(%08"LONGFMT"x)\n", iface, flags);
610 EnterCriticalSection(&This->parent->crst);
611 if(!This->playing)
613 DSCBuffer_starttimer(This);
614 This->playing = 1;
615 alcCaptureStart(This->dev);
617 This->looping = !!(flags & DSCBSTART_LOOPING);
618 LeaveCriticalSection(&This->parent->crst);
619 return S_OK;
622 static HRESULT WINAPI DSCBuffer_Stop(IDirectSoundCaptureBuffer8 *iface)
624 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
626 TRACE("(%p)->()\n", iface);
628 EnterCriticalSection(&This->parent->crst);
629 if(This->playing)
631 DWORD i;
632 for(i = 0;i < This->nnotify;++i)
634 if(This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
635 SetEvent(This->notify[i].hEventNotify);
638 This->playing = This->looping = 0;
639 alcCaptureStop(This->dev);
641 LeaveCriticalSection(&This->parent->crst);
642 return S_OK;
645 static HRESULT WINAPI DSCBuffer_Unlock(IDirectSoundCaptureBuffer8 *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
647 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
648 DWORD_PTR ofs1, ofs2;
649 DWORD_PTR boundary = (DWORD_PTR)This->buf;
651 TRACE("(%p)->(%p, %"LONGFMT"u, %p, %"LONGFMT"u)\n", iface, ptr1, len1, ptr2, len2);
653 if(InterlockedExchange(&This->locked, FALSE) == FALSE)
655 WARN("Not locked\n");
656 return DSERR_INVALIDPARAM;
659 /* Make sure offset is between boundary and boundary + bufsize */
660 ofs1 = (DWORD_PTR)ptr1;
661 ofs2 = (DWORD_PTR)ptr2;
662 if(ofs1 < boundary)
663 return DSERR_INVALIDPARAM;
664 if(ofs2 && ofs2 != boundary)
665 return DSERR_INVALIDPARAM;
667 ofs1 -= boundary;
668 ofs2 = 0;
669 if(This->buf_size-ofs1 < len1 || len2 > ofs1)
670 return DSERR_INVALIDPARAM;
672 return DS_OK;
675 static HRESULT WINAPI DSCBuffer_GetObjectInPath(IDirectSoundCaptureBuffer8 *iface, REFGUID guid, DWORD num, REFGUID riid, void **ppv)
677 FIXME("(%p)->(%s, %"LONGFMT"u, %s, %p) stub\n", iface, debugstr_guid(guid), num, debugstr_guid(riid), ppv);
678 return E_NOTIMPL;
681 static HRESULT WINAPI DSCBuffer_GetFXStatus(IDirectSoundCaptureBuffer8 *iface, DWORD count, DWORD *status)
683 FIXME("(%p)->(%"LONGFMT"u, %p) stub\n", iface, count, status);
684 return E_NOTIMPL;
687 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl =
689 DSCBuffer_QueryInterface,
690 DSCBuffer_AddRef,
691 DSCBuffer_Release,
692 DSCBuffer_GetCaps,
693 DSCBuffer_GetCurrentPosition,
694 DSCBuffer_GetFormat,
695 DSCBuffer_GetStatus,
696 DSCBuffer_Initialize,
697 DSCBuffer_Lock,
698 DSCBuffer_Start,
699 DSCBuffer_Stop,
700 DSCBuffer_Unlock,
701 DSCBuffer_GetObjectInPath,
702 DSCBuffer_GetFXStatus
705 static inline DSCBuffer *impl_from_IDirectSoundNotify(IDirectSoundNotify *iface)
707 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundNotify_iface);
710 static HRESULT WINAPI DSCBufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
712 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
713 return IDirectSoundCaptureBuffer_QueryInterface((IDirectSoundCaptureBuffer*)This, riid, ppv);
716 static ULONG WINAPI DSCBufferNot_AddRef(IDirectSoundNotify *iface)
718 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
719 LONG ret;
721 InterlockedIncrement(&This->all_ref);
722 ret = InterlockedIncrement(&This->not_ref);
723 TRACE("new refcount %"LONGFMT"d\n", ret);
724 return ret;
727 static ULONG WINAPI DSCBufferNot_Release(IDirectSoundNotify *iface)
729 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
730 LONG ret;
732 ret = InterlockedDecrement(&This->not_ref);
733 TRACE("new refcount %"LONGFMT"d\n", ret);
734 if(InterlockedDecrement(&This->all_ref) == 0)
735 DSCBuffer_Destroy(This);
737 return ret;
740 static HRESULT WINAPI DSCBufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
742 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
743 DSBPOSITIONNOTIFY *nots;
744 HRESULT hr;
745 DWORD state;
747 TRACE("(%p)->(%"LONGFMT"u, %p))\n", iface, count, notifications);
749 EnterCriticalSection(&This->parent->crst);
750 hr = DSERR_INVALIDPARAM;
751 if (count && !notifications)
752 goto out;
754 hr = DSCBuffer_GetStatus(&This->IDirectSoundCaptureBuffer8_iface, &state);
755 if (FAILED(hr))
756 goto out;
758 hr = DSERR_INVALIDCALL;
759 if (state & DSCBSTATUS_CAPTURING)
760 goto out;
762 if (!count)
764 HeapFree(GetProcessHeap(), 0, This->notify);
765 This->notify = 0;
766 This->nnotify = 0;
768 else
770 DWORD i;
771 hr = DSERR_INVALIDPARAM;
772 for (i = 0; i < count; ++i)
774 if (notifications[i].dwOffset >= This->buf_size
775 && notifications[i].dwOffset != DSCBPN_OFFSET_STOP)
776 goto out;
778 hr = E_OUTOFMEMORY;
779 nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
780 if (!nots)
781 goto out;
782 memcpy(nots, notifications, count*sizeof(*nots));
783 HeapFree(GetProcessHeap(), 0, This->notify);
784 This->notify = nots;
785 This->nnotify = count;
786 hr = S_OK;
789 out:
790 LeaveCriticalSection(&This->parent->crst);
791 return hr;
794 static const IDirectSoundNotifyVtbl DSCNot_Vtbl =
796 DSCBufferNot_QueryInterface,
797 DSCBufferNot_AddRef,
798 DSCBufferNot_Release,
799 DSCBufferNot_SetNotificationPositions
803 static inline DSCImpl *impl_from_IDirectSoundCapture(IDirectSoundCapture *iface)
805 return CONTAINING_RECORD(iface, DSCImpl, IDirectSoundCapture_iface);
808 HRESULT DSOUND_CaptureCreate(REFIID riid, void **cap)
810 DSCImpl *This;
812 *cap = NULL;
814 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
815 if(!This) return DSERR_OUTOFMEMORY;
817 This->IDirectSoundCapture_iface.lpVtbl = (IDirectSoundCaptureVtbl*)&DSC_Vtbl;
819 InitializeCriticalSection(&This->crst);
820 This->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSCImpl.crst");
822 if(FAILED(IDirectSoundCapture_QueryInterface(&This->IDirectSoundCapture_iface, riid, cap)))
824 DSCImpl_Destroy(This);
825 return E_NOINTERFACE;
827 return S_OK;
830 static void DSCImpl_Destroy(DSCImpl *This)
832 EnterCriticalSection(&This->crst);
833 if (This->buf)
834 DSCBuffer_Destroy(This->buf);
835 LeaveCriticalSection(&This->crst);
837 HeapFree(GetProcessHeap(), 0, This->device);
839 This->crst.DebugInfo->Spare[0] = 0;
840 DeleteCriticalSection(&This->crst);
842 HeapFree(GetProcessHeap(), 0, This);
845 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv)
847 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
849 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
851 *ppv = NULL;
852 if(IsEqualIID(riid, &IID_IUnknown) ||
853 IsEqualIID(riid, &IID_IDirectSoundCapture))
854 *ppv = &This->IDirectSoundCapture_iface;
855 else
856 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
858 if(*ppv)
860 IUnknown_AddRef((IUnknown*)*ppv);
861 return S_OK;
864 return E_NOINTERFACE;
867 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture *iface)
869 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
870 LONG ref;
872 ref = InterlockedIncrement(&This->ref);
873 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
875 return ref;
878 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture *iface)
880 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
881 LONG ref;
883 ref = InterlockedDecrement(&This->ref);
884 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
885 if(!ref)
886 DSCImpl_Destroy(This);
888 return ref;
891 static HRESULT WINAPI DSCImpl_CreateCaptureBuffer(IDirectSoundCapture *iface, const DSCBUFFERDESC *desc, IDirectSoundCaptureBuffer **ppv, IUnknown *unk)
893 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
894 HRESULT hr;
896 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, ppv, unk);
898 if(unk)
900 WARN("Aggregation isn't supported\n");
901 return DSERR_NOAGGREGATION;
904 if(!desc || desc->dwSize < sizeof(DSCBUFFERDESC1))
906 WARN("Passed invalid description %p %"LONGFMT"u\n", desc, desc?desc->dwSize:0);
907 return DSERR_INVALIDPARAM;
909 if(!ppv)
911 WARN("Passed null pointer\n");
912 return DSERR_INVALIDPARAM;
914 *ppv = NULL;
916 EnterCriticalSection(&This->crst);
917 if(!This->device)
919 hr = DSERR_UNINITIALIZED;
920 WARN("Not initialized\n");
921 goto out;
923 if(This->buf)
925 hr = DSERR_ALLOCATED;
926 WARN("Capture buffer already allocated\n");
927 goto out;
930 hr = DSCBuffer_Create(&This->buf, This);
931 if(SUCCEEDED(hr))
933 hr = IDirectSoundCaptureBuffer8_Initialize(&This->buf->IDirectSoundCaptureBuffer8_iface, iface, desc);
934 if(SUCCEEDED(hr))
935 *ppv = (IDirectSoundCaptureBuffer*)&This->buf->IDirectSoundCaptureBuffer8_iface;
936 else
938 DSCBuffer_Destroy(This->buf);
939 This->buf = NULL;
943 out:
944 LeaveCriticalSection(&This->crst);
945 return hr;
948 static HRESULT WINAPI DSCImpl_GetCaps(IDirectSoundCapture *iface, DSCCAPS *caps)
950 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
952 TRACE("(%p)->(%p)\n", iface, caps);
954 if(!This->device) {
955 WARN("Not initialized\n");
956 return DSERR_UNINITIALIZED;
959 if(!caps) {
960 WARN("Caps is null\n");
961 return DSERR_INVALIDPARAM;
963 if(caps->dwSize < sizeof(*caps)) {
964 WARN("Invalid size %"LONGFMT"d\n", caps->dwSize);
965 return DSERR_INVALIDPARAM;
968 caps->dwFlags = 0;
969 /* Support all WAVE_FORMAT formats specified in mmsystem.h */
970 caps->dwFormats = 0x000fffff;
971 caps->dwChannels = 2;
973 return DS_OK;
976 static HRESULT WINAPI DSCImpl_Initialize(IDirectSoundCapture *iface, const GUID *devguid)
978 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
979 HRESULT hr;
980 const ALCchar *devs, *drv_name;
981 GUID guid;
982 UINT n;
984 TRACE("(%p)->(%p)\n", iface, devguid);
986 if(!openal_loaded)
988 ERR("OpenAL not loaded!\n");
989 return DSERR_NODRIVER;
992 if(This->device) {
993 WARN("Already initialized\n");
994 return DSERR_ALREADYINITIALIZED;
997 if(!devguid || IsEqualGUID(devguid, &GUID_NULL))
998 devguid = &DSDEVID_DefaultCapture;
1000 hr = GetDeviceID(devguid, &guid);
1001 if (FAILED(hr))
1002 return DSERR_INVALIDPARAM;
1003 devguid = &guid;
1005 EnterCriticalSection(&This->crst);
1006 EnterCriticalSection(&openal_crst);
1007 devs = DSOUND_getcapturedevicestrings();
1008 n = guid.Data4[7];
1010 hr = DSERR_NODRIVER;
1011 if(memcmp(devguid, &DSOUND_capture_guid, sizeof(GUID)-1) ||
1012 !devs || !*devs)
1014 WARN("No driver found\n");
1015 goto out;
1018 if(n)
1020 const ALCchar *str = devs;
1021 while (n--)
1023 str += strlen(str) + 1;
1024 if (!*str)
1026 WARN("No driver string found\n");
1027 goto out;
1030 drv_name = str;
1032 else
1033 drv_name = devs;
1035 This->device = HeapAlloc(GetProcessHeap(), 0, strlen(drv_name)+1);
1036 if(!This->device)
1038 WARN("Out of memory to allocate %s\n", drv_name);
1039 goto out;
1041 strcpy(This->device, drv_name);
1043 hr = S_OK;
1044 out:
1045 LeaveCriticalSection(&openal_crst);
1046 LeaveCriticalSection(&This->crst);
1047 return hr;
1050 static const IDirectSoundCaptureVtbl DSC_Vtbl =
1052 DSCImpl_QueryInterface,
1053 DSCImpl_AddRef,
1054 DSCImpl_Release,
1055 DSCImpl_CreateCaptureBuffer,
1056 DSCImpl_GetCaps,
1057 DSCImpl_Initialize