Avoid holding the device lock when it isn't needed
[dsound-openal.git] / capture.c
blobcc835fac1753e576db61881a668198c85ca89cc8
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 BOOL is_8;
71 ALCchar *device;
72 DSCBuffer *buf;
74 CRITICAL_SECTION crst;
77 struct DSCBuffer {
78 IDirectSoundCaptureBuffer8 IDirectSoundCaptureBuffer8_iface;
79 IDirectSoundNotify IDirectSoundNotify_iface;
80 LONG ref, not_ref;
81 LONG all_ref;
83 DSCImpl *parent;
84 ALCdevice *dev;
86 DWORD buf_size;
87 BYTE *buf;
89 LONG locked;
91 WAVEFORMATEXTENSIBLE format;
93 DSBPOSITIONNOTIFY *notify;
94 DWORD nnotify;
96 UINT timer_id;
97 DWORD timer_res;
98 HANDLE thread_hdl;
99 DWORD thread_id;
101 DWORD pos;
102 BOOL playing, looping;
105 static const IDirectSoundCaptureVtbl DSC_Vtbl;
106 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl;
107 static const IDirectSoundNotifyVtbl DSCNot_Vtbl;
109 static void DSCImpl_Destroy(DSCImpl *This);
111 static void trigger_notifies(DSCBuffer *buf, DWORD lastpos, DWORD curpos)
113 DWORD i;
115 if(lastpos == curpos)
116 return;
118 for(i = 0;i < buf->nnotify;++i)
120 DSBPOSITIONNOTIFY *not = &buf->notify[i];
121 HANDLE event = not->hEventNotify;
122 DWORD ofs = not->dwOffset;
124 if (ofs == DSCBPN_OFFSET_STOP)
125 continue;
127 /* Wraparound case */
128 if(curpos < lastpos)
130 if(ofs < curpos || ofs >= lastpos)
132 TRACE("Triggering notification %"LONGFMT"u (%"LONGFMT"u) from buffer %p\n", i, ofs, buf);
133 SetEvent(event);
135 continue;
138 /* Normal case */
139 if(ofs >= lastpos && ofs < curpos)
141 TRACE("Triggering notification %"LONGFMT"u (%"LONGFMT"u) from buffer %p\n", i, ofs, buf);
142 SetEvent(event);
147 static DWORD CALLBACK DSCBuffer_thread(void *param)
149 DSCImpl *This = param;
150 DSCBuffer *buf;
151 ALCint avail;
152 MSG msg;
154 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
156 while(GetMessageA(&msg, NULL, 0, 0))
158 if(msg.message != WM_USER)
159 continue;
161 avail = 0;
162 buf = This->buf;
163 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
164 if(avail == 0 || !buf->playing)
165 continue;
167 EnterCriticalSection(&This->crst);
168 more_samples:
169 avail *= buf->format.Format.nBlockAlign;
170 if(avail + buf->pos > buf->buf_size)
171 avail = buf->buf_size - buf->pos;
173 alcCaptureSamples(buf->dev, buf->buf + buf->pos, avail/buf->format.Format.nBlockAlign);
174 trigger_notifies(buf, buf->pos, buf->pos + avail);
175 buf->pos += avail;
177 if(buf->pos == buf->buf_size)
179 buf->pos = 0;
180 if(!buf->looping)
181 IDirectSoundCaptureBuffer8_Stop(&buf->IDirectSoundCaptureBuffer8_iface);
182 else
184 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
185 if(avail) goto more_samples;
189 LeaveCriticalSection(&This->crst);
192 return 0;
196 static void CALLBACK DSCBuffer_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
197 DWORD_PTR dw1, DWORD_PTR dw2)
199 (void)timerID;
200 (void)msg;
201 (void)dw1;
202 (void)dw2;
203 PostThreadMessageA(dwUser, WM_USER, 0, 0);
206 static void DSCBuffer_starttimer(DSCBuffer *This)
208 TIMECAPS time;
209 ALint refresh = FAKE_REFRESH_COUNT;
210 DWORD triggertime, res = DS_TIME_RES;
212 if(This->timer_id)
213 return;
215 timeGetDevCaps(&time, sizeof(TIMECAPS));
216 triggertime = 1000 / refresh;
217 if (triggertime < time.wPeriodMin)
218 triggertime = time.wPeriodMin;
219 TRACE("Calling timer every %"LONGFMT"u ms for %i refreshes per second\n", triggertime, refresh);
220 if (res < time.wPeriodMin)
221 res = time.wPeriodMin;
222 if (timeBeginPeriod(res) == TIMERR_NOCANDO)
223 WARN("Could not set minimum resolution, don't expect sound\n");
224 This->timer_res = res;
225 This->timer_id = timeSetEvent(triggertime, res, DSCBuffer_timer, This->thread_id, TIME_PERIODIC|TIME_KILL_SYNCHRONOUS);
228 static HRESULT DSCBuffer_Create(DSCBuffer **buf, DSCImpl *parent)
230 DSCBuffer *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
231 if(!This) return E_OUTOFMEMORY;
233 This->IDirectSoundCaptureBuffer8_iface.lpVtbl = (IDirectSoundCaptureBuffer8Vtbl*)&DSCBuffer_Vtbl;
234 This->IDirectSoundNotify_iface.lpVtbl = (IDirectSoundNotifyVtbl*)&DSCNot_Vtbl;
236 This->all_ref = This->ref = 1;
238 This->parent = parent;
240 This->thread_hdl = CreateThread(NULL, 0, DSCBuffer_thread, This->parent, 0, &This->thread_id);
241 if(This->thread_hdl == NULL)
243 HeapFree(GetProcessHeap(), 0, This);
244 return DSERR_OUTOFMEMORY;
247 *buf = This;
248 return S_OK;
251 static void DSCBuffer_Destroy(DSCBuffer *This)
253 if(This->timer_id)
255 timeKillEvent(This->timer_id);
256 timeEndPeriod(This->timer_res);
258 if(This->thread_hdl)
260 PostThreadMessageA(This->thread_id, WM_QUIT, 0, 0);
261 if(WaitForSingleObject(This->thread_hdl, 1000) != WAIT_OBJECT_0)
262 ERR("Thread wait timed out");
263 CloseHandle(This->thread_hdl);
266 if(This->dev)
268 if(This->playing)
269 alcCaptureStop(This->dev);
270 alcCaptureCloseDevice(This->dev);
272 This->parent->buf = NULL;
274 HeapFree(GetProcessHeap(), 0, This->notify);
275 HeapFree(GetProcessHeap(), 0, This->buf);
276 HeapFree(GetProcessHeap(), 0, This);
279 static inline DSCBuffer *impl_from_IDirectSoundCaptureBuffer8(IDirectSoundCaptureBuffer8 *iface)
281 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundCaptureBuffer8_iface);
284 static HRESULT WINAPI DSCBuffer_QueryInterface(IDirectSoundCaptureBuffer8 *iface, REFIID riid, void **ppv)
286 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
288 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
290 if(!ppv)
291 return E_POINTER;
292 *ppv = NULL;
294 if(IsEqualIID(riid, &IID_IUnknown) ||
295 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer))
296 *ppv = &This->IDirectSoundCaptureBuffer8_iface;
297 else if(IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer8))
299 if(This->parent->is_8)
300 *ppv = &This->IDirectSoundCaptureBuffer8_iface;
302 else if(IsEqualIID(riid, &IID_IDirectSoundNotify))
303 *ppv = &This->IDirectSoundNotify_iface;
304 else
305 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
307 if(!*ppv)
308 return E_NOINTERFACE;
309 IUnknown_AddRef((IUnknown*)*ppv);
310 return S_OK;
313 static ULONG WINAPI DSCBuffer_AddRef(IDirectSoundCaptureBuffer8 *iface)
315 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
316 LONG ref;
318 InterlockedIncrement(&This->all_ref);
319 ref = InterlockedIncrement(&This->ref);
320 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
322 return ref;
325 static ULONG WINAPI DSCBuffer_Release(IDirectSoundCaptureBuffer8 *iface)
327 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
328 LONG ref;
330 ref = InterlockedDecrement(&This->ref);
331 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
332 if(InterlockedDecrement(&This->all_ref) == 0)
333 DSCBuffer_Destroy(This);
335 return ref;
338 static HRESULT WINAPI DSCBuffer_GetCaps(IDirectSoundCaptureBuffer8 *iface, DSCBCAPS *caps)
340 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
342 if (!caps || caps->dwSize < sizeof(*caps))
343 return DSERR_INVALIDPARAM;
344 caps->dwSize = sizeof(*caps);
345 caps->dwFlags = 0;
346 caps->dwBufferBytes = This->buf_size;
347 return S_OK;
350 static HRESULT WINAPI DSCBuffer_GetCurrentPosition(IDirectSoundCaptureBuffer8 *iface, DWORD *cappos, DWORD *readpos)
352 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
353 DWORD pos1, pos2;
355 EnterCriticalSection(&This->parent->crst);
356 pos1 = This->pos;
357 if(This->playing)
359 pos2 = This->format.Format.nSamplesPerSec / 100;
360 pos2 *= This->format.Format.nBlockAlign;
361 pos2 += pos1;
362 if (!This->looping && pos2 >= This->buf_size)
363 pos2 = 0;
364 else
365 pos2 %= This->buf_size;
367 else
368 pos2 = pos1;
369 LeaveCriticalSection(&This->parent->crst);
371 if(cappos) *cappos = pos1;
372 if(readpos) *readpos = pos2;
374 return S_OK;
377 static HRESULT WINAPI DSCBuffer_GetFormat(IDirectSoundCaptureBuffer8 *iface, WAVEFORMATEX *wfx, DWORD allocated, DWORD *written)
379 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
380 HRESULT hr = DS_OK;
381 UINT size;
383 TRACE("(%p)->(%p, %"LONGFMT"u, %p)\n", iface, wfx, allocated, written);
385 if(!wfx && !written)
387 WARN("Cannot report format or format size\n");
388 return DSERR_INVALIDPARAM;
391 size = sizeof(This->format.Format) + This->format.Format.cbSize;
392 if(wfx)
394 if(allocated < size)
395 hr = DSERR_INVALIDPARAM;
396 else
397 memcpy(wfx, &This->format.Format, size);
399 if(written)
400 *written = size;
402 return hr;
405 static HRESULT WINAPI DSCBuffer_GetStatus(IDirectSoundCaptureBuffer8 *iface, DWORD *status)
407 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
409 TRACE("(%p)->(%p)\n", iface, status);
411 if (!status)
412 return DSERR_INVALIDPARAM;
413 EnterCriticalSection(&This->parent->crst);
414 *status = 0;
415 if (This->playing)
417 *status |= DSCBSTATUS_CAPTURING;
418 if (This->looping)
419 *status |= DSCBSTATUS_LOOPING;
421 LeaveCriticalSection(&This->parent->crst);
423 return S_OK;
426 static HRESULT WINAPI DSCBuffer_Initialize(IDirectSoundCaptureBuffer8 *iface, IDirectSoundCapture *parent, const DSCBUFFERDESC *desc)
428 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
429 WAVEFORMATEX *format;
430 ALenum buf_format = -1;
432 TRACE("(%p)->(%p, %p)\n", iface, parent, desc);
434 if(This->dev)
435 return DSERR_ALREADYINITIALIZED;
437 if (!desc->lpwfxFormat)
438 return DSERR_INVALIDPARAM;
440 format = desc->lpwfxFormat;
441 if(format->nChannels > 2)
443 WARN("nChannels > 2 not supported for recording\n");
444 return DSERR_INVALIDPARAM;
447 if(format->wFormatTag == WAVE_FORMAT_PCM)
449 if(format->nChannels == 1)
451 switch(format->wBitsPerSample)
453 case 8: buf_format = AL_FORMAT_MONO8; break;
454 case 16: buf_format = AL_FORMAT_MONO16; break;
455 default:
456 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
457 return DSERR_BADFORMAT;
460 else if(format->nChannels == 2)
462 switch(format->wBitsPerSample)
464 case 8: buf_format = AL_FORMAT_STEREO8; break;
465 case 16: buf_format = AL_FORMAT_STEREO16; break;
466 default:
467 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
468 return DSERR_BADFORMAT;
471 else
472 WARN("Unsupported channels: %d\n", format->nChannels);
474 memcpy(&This->format.Format, format, sizeof(This->format.Format));
475 This->format.Format.nBlockAlign = This->format.Format.wBitsPerSample * This->format.Format.nChannels / 8;
476 This->format.Format.nAvgBytesPerSec = This->format.Format.nSamplesPerSec * This->format.Format.nBlockAlign;
477 This->format.Format.cbSize = 0;
479 else if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
481 WAVEFORMATEXTENSIBLE *wfe;
483 if(format->cbSize < sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX))
484 return DSERR_INVALIDPARAM;
485 else if(format->cbSize > sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) &&
486 format->cbSize != sizeof(WAVEFORMATEXTENSIBLE))
487 return DSERR_CONTROLUNAVAIL;
489 wfe = CONTAINING_RECORD(format, WAVEFORMATEXTENSIBLE, Format);
490 if(!IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
491 return DSERR_BADFORMAT;
492 if(wfe->Samples.wValidBitsPerSample &&
493 wfe->Samples.wValidBitsPerSample != wfe->Format.wBitsPerSample)
494 return DSERR_BADFORMAT;
496 if(wfe->Format.nChannels == 1 && wfe->dwChannelMask == SPEAKER_FRONT_CENTER)
498 switch(wfe->Format.wBitsPerSample)
500 case 8: buf_format = AL_FORMAT_MONO8; break;
501 case 16: buf_format = AL_FORMAT_MONO16; break;
502 default:
503 WARN("Unsupported bpp %u\n", wfe->Format.wBitsPerSample);
504 return DSERR_BADFORMAT;
507 else if(wfe->Format.nChannels == 2 && wfe->dwChannelMask == (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT))
509 switch(wfe->Format.wBitsPerSample)
511 case 8: buf_format = AL_FORMAT_STEREO8; break;
512 case 16: buf_format = AL_FORMAT_STEREO16; break;
513 default:
514 WARN("Unsupported bpp %u\n", wfe->Format.wBitsPerSample);
515 return DSERR_BADFORMAT;
518 else
519 WARN("Unsupported channels: %d -- 0x%08"LONGFMT"u\n", wfe->Format.nChannels, wfe->dwChannelMask);
521 memcpy(&This->format, wfe, sizeof(This->format));
522 This->format.Format.cbSize = sizeof(This->format) - sizeof(This->format.Format);
523 This->format.Format.nBlockAlign = This->format.Format.wBitsPerSample * This->format.Format.nChannels / 8;
524 This->format.Format.nAvgBytesPerSec = This->format.Format.nSamplesPerSec * This->format.Format.nBlockAlign;
526 else
527 WARN("Unhandled formattag %x\n", format->wFormatTag);
529 if(buf_format <= 0)
531 WARN("Could not get OpenAL format\n");
532 return DSERR_INVALIDPARAM;
535 This->buf_size = desc->dwBufferBytes;
536 This->buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->buf_size);
537 if(!This->buf)
539 WARN("Out of memory\n");
540 return DSERR_INVALIDPARAM;
543 This->dev = alcCaptureOpenDevice(This->parent->device, This->format.Format.nSamplesPerSec, buf_format, This->format.Format.nSamplesPerSec / FAKE_REFRESH_COUNT * 2);
544 if(!This->dev)
546 ERR("Couldn't open device %s 0x%x@%"LONGFMT"u, reason: %04x\n", This->parent->device, buf_format, This->format.Format.nSamplesPerSec, alcGetError(NULL));
547 return DSERR_INVALIDPARAM;
550 return S_OK;
553 static HRESULT WINAPI DSCBuffer_Lock(IDirectSoundCaptureBuffer8 *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
555 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
556 DWORD remain;
558 TRACE("(%p)->(%"LONGFMT"u, %"LONGFMT"u, %p, %p, %p, %p, %#"LONGFMT"x)\n", iface, ofs, bytes, ptr1, len1, ptr2, len2, flags);
560 if(!ptr1 || !len1)
562 WARN("Invalid pointer/len %p %p\n", ptr1, len1);
563 return DSERR_INVALIDPARAM;
566 *ptr1 = NULL;
567 *len1 = 0;
568 if(ptr2) *ptr2 = NULL;
569 if(len2) *len2 = 0;
571 if(ofs >= This->buf_size)
573 WARN("Invalid ofs %"LONGFMT"u\n", ofs);
574 return DSERR_INVALIDPARAM;
577 if((flags&DSCBLOCK_ENTIREBUFFER))
578 bytes = This->buf_size;
579 else if(bytes > This->buf_size)
581 WARN("Invalid size %"LONGFMT"u\n", bytes);
582 return DSERR_INVALIDPARAM;
585 if(InterlockedExchange(&This->locked, TRUE) == TRUE)
587 WARN("Already locked\n");
588 return DSERR_INVALIDPARAM;
591 if(ofs + bytes >= This->buf_size)
593 *len1 = This->buf_size - ofs;
594 remain = bytes - *len1;
596 else
598 *len1 = bytes;
599 remain = 0;
601 *ptr1 = This->buf + ofs;
603 if(ptr2 && len2 && remain)
605 *ptr2 = This->buf;
606 *len2 = remain;
609 return DS_OK;
612 static HRESULT WINAPI DSCBuffer_Start(IDirectSoundCaptureBuffer8 *iface, DWORD flags)
614 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
616 TRACE("(%p)->(%08"LONGFMT"x)\n", iface, flags);
618 EnterCriticalSection(&This->parent->crst);
619 if(!This->playing)
621 DSCBuffer_starttimer(This);
622 This->playing = 1;
623 alcCaptureStart(This->dev);
625 This->looping = !!(flags & DSCBSTART_LOOPING);
626 LeaveCriticalSection(&This->parent->crst);
627 return S_OK;
630 static HRESULT WINAPI DSCBuffer_Stop(IDirectSoundCaptureBuffer8 *iface)
632 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
634 TRACE("(%p)->()\n", iface);
636 EnterCriticalSection(&This->parent->crst);
637 if(This->playing)
639 DWORD i;
640 for(i = 0;i < This->nnotify;++i)
642 if(This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
643 SetEvent(This->notify[i].hEventNotify);
646 This->playing = This->looping = 0;
647 alcCaptureStop(This->dev);
649 LeaveCriticalSection(&This->parent->crst);
650 return S_OK;
653 static HRESULT WINAPI DSCBuffer_Unlock(IDirectSoundCaptureBuffer8 *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
655 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
656 DWORD_PTR ofs1, ofs2;
657 DWORD_PTR boundary = (DWORD_PTR)This->buf;
659 TRACE("(%p)->(%p, %"LONGFMT"u, %p, %"LONGFMT"u)\n", iface, ptr1, len1, ptr2, len2);
661 if(InterlockedExchange(&This->locked, FALSE) == FALSE)
663 WARN("Not locked\n");
664 return DSERR_INVALIDPARAM;
667 /* Make sure offset is between boundary and boundary + bufsize */
668 ofs1 = (DWORD_PTR)ptr1;
669 ofs2 = (DWORD_PTR)ptr2;
670 if(ofs1 < boundary)
671 return DSERR_INVALIDPARAM;
672 if(ofs2 && ofs2 != boundary)
673 return DSERR_INVALIDPARAM;
675 ofs1 -= boundary;
676 ofs2 = 0;
677 if(This->buf_size-ofs1 < len1 || len2 > ofs1)
678 return DSERR_INVALIDPARAM;
680 return DS_OK;
683 static HRESULT WINAPI DSCBuffer_GetObjectInPath(IDirectSoundCaptureBuffer8 *iface, REFGUID guid, DWORD num, REFGUID riid, void **ppv)
685 FIXME("(%p)->(%s, %"LONGFMT"u, %s, %p) stub\n", iface, debugstr_guid(guid), num, debugstr_guid(riid), ppv);
686 return E_NOTIMPL;
689 static HRESULT WINAPI DSCBuffer_GetFXStatus(IDirectSoundCaptureBuffer8 *iface, DWORD count, DWORD *status)
691 FIXME("(%p)->(%"LONGFMT"u, %p) stub\n", iface, count, status);
692 return E_NOTIMPL;
695 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl =
697 DSCBuffer_QueryInterface,
698 DSCBuffer_AddRef,
699 DSCBuffer_Release,
700 DSCBuffer_GetCaps,
701 DSCBuffer_GetCurrentPosition,
702 DSCBuffer_GetFormat,
703 DSCBuffer_GetStatus,
704 DSCBuffer_Initialize,
705 DSCBuffer_Lock,
706 DSCBuffer_Start,
707 DSCBuffer_Stop,
708 DSCBuffer_Unlock,
709 DSCBuffer_GetObjectInPath,
710 DSCBuffer_GetFXStatus
713 static inline DSCBuffer *impl_from_IDirectSoundNotify(IDirectSoundNotify *iface)
715 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundNotify_iface);
718 static HRESULT WINAPI DSCBufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
720 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
721 return IDirectSoundCaptureBuffer_QueryInterface((IDirectSoundCaptureBuffer*)This, riid, ppv);
724 static ULONG WINAPI DSCBufferNot_AddRef(IDirectSoundNotify *iface)
726 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
727 LONG ret;
729 InterlockedIncrement(&This->all_ref);
730 ret = InterlockedIncrement(&This->not_ref);
731 TRACE("new refcount %"LONGFMT"d\n", ret);
732 return ret;
735 static ULONG WINAPI DSCBufferNot_Release(IDirectSoundNotify *iface)
737 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
738 LONG ret;
740 ret = InterlockedDecrement(&This->not_ref);
741 TRACE("new refcount %"LONGFMT"d\n", ret);
742 if(InterlockedDecrement(&This->all_ref) == 0)
743 DSCBuffer_Destroy(This);
745 return ret;
748 static HRESULT WINAPI DSCBufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
750 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
751 DSBPOSITIONNOTIFY *nots;
752 HRESULT hr;
753 DWORD state;
755 TRACE("(%p)->(%"LONGFMT"u, %p))\n", iface, count, notifications);
757 EnterCriticalSection(&This->parent->crst);
758 hr = DSERR_INVALIDPARAM;
759 if (count && !notifications)
760 goto out;
762 hr = DSCBuffer_GetStatus(&This->IDirectSoundCaptureBuffer8_iface, &state);
763 if (FAILED(hr))
764 goto out;
766 hr = DSERR_INVALIDCALL;
767 if (state & DSCBSTATUS_CAPTURING)
768 goto out;
770 if (!count)
772 HeapFree(GetProcessHeap(), 0, This->notify);
773 This->notify = 0;
774 This->nnotify = 0;
776 else
778 DWORD i;
779 hr = DSERR_INVALIDPARAM;
780 for (i = 0; i < count; ++i)
782 if (notifications[i].dwOffset >= This->buf_size
783 && notifications[i].dwOffset != DSCBPN_OFFSET_STOP)
784 goto out;
786 hr = E_OUTOFMEMORY;
787 nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
788 if (!nots)
789 goto out;
790 memcpy(nots, notifications, count*sizeof(*nots));
791 HeapFree(GetProcessHeap(), 0, This->notify);
792 This->notify = nots;
793 This->nnotify = count;
794 hr = S_OK;
797 out:
798 LeaveCriticalSection(&This->parent->crst);
799 return hr;
802 static const IDirectSoundNotifyVtbl DSCNot_Vtbl =
804 DSCBufferNot_QueryInterface,
805 DSCBufferNot_AddRef,
806 DSCBufferNot_Release,
807 DSCBufferNot_SetNotificationPositions
811 static inline DSCImpl *impl_from_IDirectSoundCapture(IDirectSoundCapture *iface)
813 return CONTAINING_RECORD(iface, DSCImpl, IDirectSoundCapture_iface);
816 HRESULT DSOUND_CaptureCreate(REFIID riid, void **cap)
818 HRESULT hr;
820 hr = DSOUND_CaptureCreate8(&IID_IDirectSoundCapture, cap);
821 if(SUCCEEDED(hr))
823 DSCImpl *impl = impl_from_IDirectSoundCapture(*cap);
824 impl->is_8 = FALSE;
826 if(!IsEqualIID(riid, &IID_IDirectSoundCapture))
828 hr = IDirectSoundCapture_QueryInterface(&impl->IDirectSoundCapture_iface, riid, cap);
829 IDirectSoundCapture_Release(&impl->IDirectSoundCapture_iface);
832 return hr;
835 HRESULT DSOUND_CaptureCreate8(REFIID riid, void **cap)
837 DSCImpl *This;
839 *cap = NULL;
841 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
842 if(!This) return DSERR_OUTOFMEMORY;
844 This->IDirectSoundCapture_iface.lpVtbl = (IDirectSoundCaptureVtbl*)&DSC_Vtbl;
846 This->is_8 = TRUE;
848 InitializeCriticalSection(&This->crst);
849 This->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSCImpl.crst");
851 if(FAILED(IDirectSoundCapture_QueryInterface(&This->IDirectSoundCapture_iface, riid, cap)))
853 DSCImpl_Destroy(This);
854 return E_NOINTERFACE;
856 return S_OK;
859 static void DSCImpl_Destroy(DSCImpl *This)
861 EnterCriticalSection(&This->crst);
862 if (This->buf)
863 DSCBuffer_Destroy(This->buf);
864 LeaveCriticalSection(&This->crst);
866 HeapFree(GetProcessHeap(), 0, This->device);
868 This->crst.DebugInfo->Spare[0] = 0;
869 DeleteCriticalSection(&This->crst);
871 HeapFree(GetProcessHeap(), 0, This);
874 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv)
876 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
878 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
880 *ppv = NULL;
881 if(IsEqualIID(riid, &IID_IUnknown) ||
882 IsEqualIID(riid, &IID_IDirectSoundCapture))
883 *ppv = &This->IDirectSoundCapture_iface;
884 else
885 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
887 if(*ppv)
889 IUnknown_AddRef((IUnknown*)*ppv);
890 return S_OK;
893 return E_NOINTERFACE;
896 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture *iface)
898 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
899 LONG ref;
901 ref = InterlockedIncrement(&This->ref);
902 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
904 return ref;
907 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture *iface)
909 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
910 LONG ref;
912 ref = InterlockedDecrement(&This->ref);
913 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
914 if(!ref)
915 DSCImpl_Destroy(This);
917 return ref;
920 static HRESULT WINAPI DSCImpl_CreateCaptureBuffer(IDirectSoundCapture *iface, const DSCBUFFERDESC *desc, IDirectSoundCaptureBuffer **ppv, IUnknown *unk)
922 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
923 HRESULT hr;
925 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, ppv, unk);
927 if(unk)
929 WARN("Aggregation isn't supported\n");
930 return DSERR_NOAGGREGATION;
933 if(!desc || desc->dwSize < sizeof(DSCBUFFERDESC1))
935 WARN("Passed invalid description %p %"LONGFMT"u\n", desc, desc?desc->dwSize:0);
936 return DSERR_INVALIDPARAM;
938 if(!ppv)
940 WARN("Passed null pointer\n");
941 return DSERR_INVALIDPARAM;
943 *ppv = NULL;
945 EnterCriticalSection(&This->crst);
946 if(!This->device)
948 hr = DSERR_UNINITIALIZED;
949 WARN("Not initialized\n");
950 goto out;
952 if(This->buf)
954 hr = DSERR_ALLOCATED;
955 WARN("Capture buffer already allocated\n");
956 goto out;
959 hr = DSCBuffer_Create(&This->buf, This);
960 if(SUCCEEDED(hr))
962 hr = IDirectSoundCaptureBuffer8_Initialize(&This->buf->IDirectSoundCaptureBuffer8_iface, iface, desc);
963 if(SUCCEEDED(hr))
964 *ppv = (IDirectSoundCaptureBuffer*)&This->buf->IDirectSoundCaptureBuffer8_iface;
965 else
967 DSCBuffer_Destroy(This->buf);
968 This->buf = NULL;
972 out:
973 LeaveCriticalSection(&This->crst);
974 return hr;
977 static HRESULT WINAPI DSCImpl_GetCaps(IDirectSoundCapture *iface, DSCCAPS *caps)
979 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
981 TRACE("(%p)->(%p)\n", iface, caps);
983 if(!This->device) {
984 WARN("Not initialized\n");
985 return DSERR_UNINITIALIZED;
988 if(!caps) {
989 WARN("Caps is null\n");
990 return DSERR_INVALIDPARAM;
992 if(caps->dwSize < sizeof(*caps)) {
993 WARN("Invalid size %"LONGFMT"d\n", caps->dwSize);
994 return DSERR_INVALIDPARAM;
997 caps->dwFlags = 0;
998 /* Support all WAVE_FORMAT formats specified in mmsystem.h */
999 caps->dwFormats = 0x000fffff;
1000 caps->dwChannels = 2;
1002 return DS_OK;
1005 static HRESULT WINAPI DSCImpl_Initialize(IDirectSoundCapture *iface, const GUID *devguid)
1007 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
1008 HRESULT hr;
1009 const ALCchar *devs, *drv_name;
1010 GUID guid;
1011 UINT n;
1013 TRACE("(%p)->(%p)\n", iface, devguid);
1015 if(!openal_loaded)
1017 ERR("OpenAL not loaded!\n");
1018 return DSERR_NODRIVER;
1021 if(This->device) {
1022 WARN("Already initialized\n");
1023 return DSERR_ALREADYINITIALIZED;
1026 if(!devguid || IsEqualGUID(devguid, &GUID_NULL))
1027 devguid = &DSDEVID_DefaultCapture;
1029 hr = GetDeviceID(devguid, &guid);
1030 if (FAILED(hr))
1031 return DSERR_INVALIDPARAM;
1032 devguid = &guid;
1034 EnterCriticalSection(&This->crst);
1035 EnterCriticalSection(&openal_crst);
1036 devs = DSOUND_getcapturedevicestrings();
1037 n = guid.Data4[7];
1039 hr = DSERR_NODRIVER;
1040 if(memcmp(devguid, &DSOUND_capture_guid, sizeof(GUID)-1) ||
1041 !devs || !*devs)
1043 WARN("No driver found\n");
1044 goto out;
1047 if(n)
1049 const ALCchar *str = devs;
1050 while (n--)
1052 str += strlen(str) + 1;
1053 if (!*str)
1055 WARN("No driver string found\n");
1056 goto out;
1059 drv_name = str;
1061 else
1062 drv_name = devs;
1064 This->device = HeapAlloc(GetProcessHeap(), 0, strlen(drv_name)+1);
1065 if(!This->device)
1067 WARN("Out of memory to allocate %s\n", drv_name);
1068 goto out;
1070 strcpy(This->device, drv_name);
1072 hr = S_OK;
1073 out:
1074 LeaveCriticalSection(&openal_crst);
1075 LeaveCriticalSection(&This->crst);
1076 return hr;
1079 static const IDirectSoundCaptureVtbl DSC_Vtbl =
1081 DSCImpl_QueryInterface,
1082 DSCImpl_AddRef,
1083 DSCImpl_Release,
1084 DSCImpl_CreateCaptureBuffer,
1085 DSCImpl_GetCaps,
1086 DSCImpl_Initialize