Improve some traces
[wine/multimedia.git] / capture.c
bloba1849d1a65eb5ae198ec3a09f1067c37429ee8ee
1 /* DirectSound capture buffer interface
3 * Copyright 2009 Maarten Lankhorst
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <stdarg.h>
22 #ifdef __WINESRC__
24 #define COBJMACROS
25 #define NONAMELESSSTRUCT
26 #define NONAMELESSUNION
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "winnls.h"
31 #include "winreg.h"
32 #include "mmsystem.h"
33 #include "winternl.h"
34 #include "mmddk.h"
35 #include "wine/debug.h"
36 #include "dsound.h"
38 #include "dsound_private.h"
40 #include "ks.h"
41 #include "ksmedia.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
45 #else
47 #define WINVER 0x0600
48 #include <windows.h>
49 #include <dsound.h>
51 #include "dsound_private.h"
53 #ifndef DSCBPN_OFFSET_STOP
54 #define DSCBPN_OFFSET_STOP 0xffffffff
55 #endif
57 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
59 #endif
61 typedef struct DSCImpl DSCImpl;
62 typedef struct DSCBuffer DSCBuffer;
64 struct DSCImpl {
65 /* IDirectSoundCapture and IDirectSoundCapture8 are aliases */
66 IDirectSoundCapture IDirectSoundCapture_iface;
67 LONG ref;
69 ALCchar *device;
70 DSCBuffer *buf;
72 CRITICAL_SECTION crst;
75 struct DSCBuffer {
76 IDirectSoundCaptureBuffer8 IDirectSoundCaptureBuffer8_iface;
77 IDirectSoundNotify IDirectSoundNotify_iface;
78 LONG ref, not_ref;
79 LONG all_ref;
81 DSCImpl *parent;
82 ALCdevice *dev;
84 DWORD buf_size;
85 BYTE *buf;
87 WAVEFORMATEX *format;
89 DSBPOSITIONNOTIFY *notify;
90 DWORD nnotify;
92 UINT timer_id;
93 DWORD timer_res;
94 HANDLE thread_hdl;
95 DWORD thread_id;
97 DWORD pos;
98 BOOL playing, looping;
101 static const IDirectSoundCaptureVtbl DSC_Vtbl;
102 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl;
103 static const IDirectSoundNotifyVtbl DSCNot_Vtbl;
105 static void DSCImpl_Destroy(DSCImpl *This);
107 static void trigger_notifies(DSCBuffer *buf, DWORD lastpos, DWORD curpos)
109 DWORD i;
111 if(lastpos == curpos)
112 return;
114 for(i = 0;i < buf->nnotify;++i)
116 DSBPOSITIONNOTIFY *not = &buf->notify[i];
117 HANDLE event = not->hEventNotify;
118 DWORD ofs = not->dwOffset;
120 if (ofs == DSCBPN_OFFSET_STOP)
121 continue;
123 /* Wraparound case */
124 if(curpos < lastpos)
126 if(ofs < curpos || ofs >= lastpos)
128 TRACE("Triggering notification %"LONGFMT"u (%"LONGFMT"u) from buffer %p\n", i, ofs, buf);
129 SetEvent(event);
131 continue;
134 /* Normal case */
135 if(ofs >= lastpos && ofs < curpos)
137 TRACE("Triggering notification %"LONGFMT"u (%"LONGFMT"u) from buffer %p\n", i, ofs, buf);
138 SetEvent(event);
143 static DWORD CALLBACK DSCBuffer_thread(void *param)
145 DSCImpl *This = param;
146 DSCBuffer *buf;
147 ALCint avail;
148 MSG msg;
150 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
152 while(GetMessageA(&msg, NULL, 0, 0))
154 if(msg.message != WM_USER)
155 continue;
157 avail = 0;
158 buf = This->buf;
159 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
160 if(avail == 0)
161 continue;
163 EnterCriticalSection(&This->crst);
164 more_samples:
165 avail *= buf->format->nBlockAlign;
166 if(avail + buf->pos > buf->buf_size)
167 avail = buf->buf_size - buf->pos;
169 alcCaptureSamples(buf->dev, buf->buf + buf->pos, avail/buf->format->nBlockAlign);
170 trigger_notifies(buf, buf->pos, buf->pos + avail);
171 buf->pos += avail;
173 if(buf->pos == buf->buf_size)
175 buf->pos = 0;
176 if(!buf->looping)
177 IDirectSoundCaptureBuffer8_Stop(&buf->IDirectSoundCaptureBuffer8_iface);
178 else
180 alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
181 if(avail) goto more_samples;
185 LeaveCriticalSection(&This->crst);
188 return 0;
192 static void CALLBACK DSCBuffer_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
193 DWORD_PTR dw1, DWORD_PTR dw2)
195 (void)timerID;
196 (void)msg;
197 (void)dw1;
198 (void)dw2;
199 PostThreadMessageA(dwUser, WM_USER, 0, 0);
202 static void DSCBuffer_starttimer(DSCBuffer *This)
204 TIMECAPS time;
205 ALint refresh = FAKE_REFRESH_COUNT;
206 DWORD triggertime, res = DS_TIME_RES;
208 if(This->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 This->timer_res = res;
221 This->timer_id = timeSetEvent(triggertime, res, DSCBuffer_timer, This->thread_id, TIME_PERIODIC|TIME_KILL_SYNCHRONOUS);
224 static HRESULT DSCBuffer_Create(DSCBuffer **buf, DSCImpl *parent)
226 DSCBuffer *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
227 if(!This) return E_OUTOFMEMORY;
229 This->IDirectSoundCaptureBuffer8_iface.lpVtbl = (IDirectSoundCaptureBuffer8Vtbl*)&DSCBuffer_Vtbl;
230 This->IDirectSoundNotify_iface.lpVtbl = (IDirectSoundNotifyVtbl*)&DSCNot_Vtbl;
232 This->all_ref = This->ref = 1;
234 This->parent = parent;
236 This->thread_hdl = CreateThread(NULL, 0, DSCBuffer_thread, This->parent, 0, &This->thread_id);
237 if(This->thread_hdl == NULL)
239 HeapFree(GetProcessHeap(), 0, This);
240 return DSERR_OUTOFMEMORY;
243 *buf = This;
244 return S_OK;
247 static void DSCBuffer_Destroy(DSCBuffer *This)
249 if(This->timer_id)
251 timeKillEvent(This->timer_id);
252 timeEndPeriod(This->timer_res);
254 if(This->thread_hdl)
256 PostThreadMessageA(This->thread_id, WM_QUIT, 0, 0);
257 if(WaitForSingleObject(This->thread_hdl, 1000) != WAIT_OBJECT_0)
258 ERR("Thread wait timed out");
259 CloseHandle(This->thread_hdl);
262 if(This->dev)
264 if(This->playing)
265 alcCaptureStop(This->dev);
266 alcCaptureCloseDevice(This->dev);
268 This->parent->buf = NULL;
270 HeapFree(GetProcessHeap(), 0, This->notify);
271 HeapFree(GetProcessHeap(), 0, This->format);
272 HeapFree(GetProcessHeap(), 0, This->buf);
273 HeapFree(GetProcessHeap(), 0, This);
276 static inline DSCBuffer *impl_from_IDirectSoundCaptureBuffer8(IDirectSoundCaptureBuffer8 *iface)
278 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundCaptureBuffer8_iface);
281 static HRESULT WINAPI DSCBuffer_QueryInterface(IDirectSoundCaptureBuffer8 *iface, REFIID riid, void **ppv)
283 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
285 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
287 if(!ppv)
288 return E_POINTER;
289 *ppv = NULL;
291 if (IsEqualIID(riid, &IID_IDirectSoundNotify))
292 *ppv = &This->IDirectSoundNotify_iface;
293 else if (IsEqualIID(riid, &IID_IUnknown) ||
294 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer) ||
295 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer8))
296 *ppv = &This->IDirectSoundCaptureBuffer8_iface;
298 if (!*ppv)
299 return E_NOINTERFACE;
300 IUnknown_AddRef((IUnknown*)*ppv);
301 return S_OK;
304 static ULONG WINAPI DSCBuffer_AddRef(IDirectSoundCaptureBuffer8 *iface)
306 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
307 LONG ref;
309 InterlockedIncrement(&This->all_ref);
310 ref = InterlockedIncrement(&This->ref);
311 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
313 return ref;
316 static ULONG WINAPI DSCBuffer_Release(IDirectSoundCaptureBuffer8 *iface)
318 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
319 LONG ref;
321 ref = InterlockedDecrement(&This->ref);
322 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
323 if(InterlockedDecrement(&This->all_ref) == 0)
324 DSCBuffer_Destroy(This);
326 return ref;
329 static HRESULT WINAPI DSCBuffer_GetCaps(IDirectSoundCaptureBuffer8 *iface, DSCBCAPS *caps)
331 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
333 if (!caps || caps->dwSize < sizeof(*caps))
334 return DSERR_INVALIDPARAM;
335 caps->dwSize = sizeof(*caps);
336 caps->dwFlags = 0;
337 caps->dwBufferBytes = This->buf_size;
338 return S_OK;
341 static HRESULT WINAPI DSCBuffer_GetCurrentPosition(IDirectSoundCaptureBuffer8 *iface, DWORD *cappos, DWORD *readpos)
343 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
344 DWORD pos1, pos2;
346 EnterCriticalSection(&This->parent->crst);
347 pos1 = This->pos;
348 if (This->playing)
350 pos2 = This->format->nSamplesPerSec / 100;
351 pos2 *= This->format->nBlockAlign;
352 pos2 += pos1;
353 if (!This->looping && pos2 >= This->buf_size)
354 pos2 = 0;
355 else
356 pos2 %= This->buf_size;
358 else
359 pos2 = pos1;
360 LeaveCriticalSection(&This->parent->crst);
362 if(cappos) *cappos = pos1;
363 if(readpos) *readpos = pos2;
365 return S_OK;
368 static HRESULT WINAPI DSCBuffer_GetFormat(IDirectSoundCaptureBuffer8 *iface, WAVEFORMATEX *wfx, DWORD size, DWORD *written)
370 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
372 TRACE("(%p)->(%p, %"LONGFMT"u, %p)\n", iface, wfx, size, written);
374 if (size > sizeof(WAVEFORMATEX) + This->format->cbSize)
375 size = sizeof(WAVEFORMATEX) + This->format->cbSize;
377 if (wfx)
379 CopyMemory(wfx, This->format, size);
380 if (written)
381 *written = size;
383 else if (written)
384 *written = sizeof(WAVEFORMATEX) + This->format->cbSize;
385 else
386 return DSERR_INVALIDPARAM;
388 return S_OK;
391 static HRESULT WINAPI DSCBuffer_GetStatus(IDirectSoundCaptureBuffer8 *iface, DWORD *status)
393 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
395 TRACE("(%p)->(%p)\n", iface, status);
397 if (!status)
398 return DSERR_INVALIDPARAM;
399 EnterCriticalSection(&This->parent->crst);
400 *status = 0;
401 if (This->playing)
403 *status |= DSCBSTATUS_CAPTURING;
404 if (This->looping)
405 *status |= DSCBSTATUS_LOOPING;
407 LeaveCriticalSection(&This->parent->crst);
409 return S_OK;
412 static HRESULT WINAPI DSCBuffer_Initialize(IDirectSoundCaptureBuffer8 *iface, IDirectSoundCapture *parent, const DSCBUFFERDESC *desc)
414 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
415 WAVEFORMATEX *format;
416 ALenum buf_format = -1;
418 TRACE("(%p)->(%p, %p)\n", iface, parent, desc);
420 if(This->dev)
421 return DSERR_ALREADYINITIALIZED;
423 if (!desc->lpwfxFormat)
424 return DSERR_INVALIDPARAM;
426 format = desc->lpwfxFormat;
427 if (format->nChannels > 2)
429 WARN("nChannels > 2 not supported for recording\n");
430 return DSERR_INVALIDPARAM;
433 if (!This->format)
434 This->format = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE));
435 if (!This->format)
436 return DSERR_OUTOFMEMORY;
438 if (format->wFormatTag == WAVE_FORMAT_PCM ||
439 format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
441 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
443 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)format;
444 if (format->cbSize < sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX))
445 return DSERR_INVALIDPARAM;
446 else if (format->cbSize > sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)
447 && format->cbSize != sizeof(WAVEFORMATEXTENSIBLE))
448 return DSERR_CONTROLUNAVAIL;
449 else if (!IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
450 return DSERR_BADFORMAT;
453 if (format->nChannels == 1)
455 switch (format->wBitsPerSample)
457 case 8: buf_format = AL_FORMAT_MONO8; break;
458 case 16: buf_format = AL_FORMAT_MONO16; break;
459 default:
460 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
461 return DSERR_BADFORMAT;
464 else if (format->nChannels == 2)
466 switch (format->wBitsPerSample)
468 case 8: buf_format = AL_FORMAT_STEREO8; break;
469 case 16: buf_format = AL_FORMAT_STEREO16; break;
470 default:
471 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
472 return DSERR_BADFORMAT;
475 memcpy(This->format, format, sizeof(*format) + format->cbSize);
476 if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
477 This->format->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
478 else
479 This->format->cbSize = 0;
480 This->format->nBlockAlign = This->format->wBitsPerSample * This->format->nChannels / 8;
481 This->format->nAvgBytesPerSec = This->format->nSamplesPerSec * This->format->nBlockAlign;
483 else if (format->wFormatTag)
484 WARN("Unhandled formattag %x\n", format->wFormatTag);
486 This->buf_size = desc->dwBufferBytes;
487 if(buf_format <= 0)
489 WARN("Could not get OpenAL format\n");
490 return DSERR_INVALIDPARAM;
493 This->dev = alcCaptureOpenDevice(This->parent->device, This->format->nSamplesPerSec, buf_format, This->buf_size / This->format->nBlockAlign);
494 if (!This->dev)
496 ERR("couldn't open device %s %x@%"LONGFMT"u, reason: %04x\n", This->parent->device, buf_format, This->format->nSamplesPerSec, alcGetError(NULL));
497 return DSERR_INVALIDPARAM;
500 This->buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->buf_size);
501 if (!This->buf)
503 alcCaptureCloseDevice(This->dev);
504 WARN("Out of memory\n");
505 return DSERR_INVALIDPARAM;
508 return S_OK;
511 static HRESULT WINAPI DSCBuffer_Lock(IDirectSoundCaptureBuffer8 *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
513 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
514 HRESULT hr;
515 DWORD remain;
517 TRACE("(%p)->(%"LONGFMT"u, %"LONGFMT"u, %p, %p, %p, %p, %#"LONGFMT"x)\n", iface, ofs, bytes, ptr1, len1, ptr2, len2, flags);
519 EnterCriticalSection(&This->parent->crst);
520 hr = DSERR_INVALIDPARAM;
522 if(ptr1) *ptr1 = NULL;
523 if(len1) *len1 = 0;
524 if(ptr2) *ptr2 = NULL;
525 if(len2) *len2 = 0;
527 if (ofs >= This->buf_size)
529 WARN("Invalid ofs %"LONGFMT"u\n", ofs);
530 goto out;
532 if (!ptr1 || !len1)
534 WARN("Invalid pointer/len %p %p\n", ptr1, len1);
535 goto out;
537 if((flags&DSCBLOCK_ENTIREBUFFER))
538 bytes = This->buf_size;
539 else if(bytes > This->buf_size)
541 WARN("Invalid size %"LONGFMT"u\n", bytes);
542 goto out;
545 if (ofs + bytes >= This->buf_size)
547 *len1 = This->buf_size - ofs;
548 remain = bytes - *len1;
550 else
552 *len1 = bytes;
553 remain = 0;
555 *ptr1 = This->buf + ofs;
557 if (ptr2 && len2 && remain)
559 *ptr2 = This->buf;
560 *len2 = remain;
562 hr = S_OK;
564 out:
565 LeaveCriticalSection(&This->parent->crst);
566 return hr;
569 static HRESULT WINAPI DSCBuffer_Start(IDirectSoundCaptureBuffer8 *iface, DWORD flags)
571 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
573 TRACE("(%p)->(%08"LONGFMT"x)\n", iface, flags);
575 EnterCriticalSection(&This->parent->crst);
576 if(!This->playing)
578 DSCBuffer_starttimer(This);
579 This->playing = 1;
580 alcCaptureStart(This->dev);
582 This->looping = !!(flags & DSCBSTART_LOOPING);
583 LeaveCriticalSection(&This->parent->crst);
584 return S_OK;
587 static HRESULT WINAPI DSCBuffer_Stop(IDirectSoundCaptureBuffer8 *iface)
589 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
591 TRACE("(%p)->()\n", iface);
593 EnterCriticalSection(&This->parent->crst);
594 if(This->playing)
596 DWORD i;
597 for(i = 0;i < This->nnotify;++i)
599 if(This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
600 SetEvent(This->notify[i].hEventNotify);
603 This->playing = This->looping = 0;
604 alcCaptureStop(This->dev);
606 LeaveCriticalSection(&This->parent->crst);
607 return S_OK;
610 static HRESULT WINAPI DSCBuffer_Unlock(IDirectSoundCaptureBuffer8 *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
612 TRACE("(%p)->(%p, %"LONGFMT"u, %p, %"LONGFMT"u)\n", iface, ptr1, len1, ptr2, len2);
614 if (!ptr1)
615 return DSERR_INVALIDPARAM;
616 return S_OK;
619 static HRESULT WINAPI DSCBuffer_GetObjectInPath(IDirectSoundCaptureBuffer8 *iface, REFGUID guid, DWORD num, REFGUID riid, void **ppv)
621 FIXME("(%p)->(%s, %"LONGFMT"u, %s, %p) stub\n", iface, debugstr_guid(guid), num, debugstr_guid(riid), ppv);
622 return E_NOTIMPL;
625 static HRESULT WINAPI DSCBuffer_GetFXStatus(IDirectSoundCaptureBuffer8 *iface, DWORD count, DWORD *status)
627 FIXME("(%p)->(%"LONGFMT"u, %p) stub\n", iface, count, status);
628 return E_NOTIMPL;
631 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl =
633 DSCBuffer_QueryInterface,
634 DSCBuffer_AddRef,
635 DSCBuffer_Release,
636 DSCBuffer_GetCaps,
637 DSCBuffer_GetCurrentPosition,
638 DSCBuffer_GetFormat,
639 DSCBuffer_GetStatus,
640 DSCBuffer_Initialize,
641 DSCBuffer_Lock,
642 DSCBuffer_Start,
643 DSCBuffer_Stop,
644 DSCBuffer_Unlock,
645 DSCBuffer_GetObjectInPath,
646 DSCBuffer_GetFXStatus
649 static inline DSCBuffer *impl_from_IDirectSoundNotify(IDirectSoundNotify *iface)
651 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundNotify_iface);
654 static HRESULT WINAPI DSCBufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
656 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
657 return IDirectSoundCaptureBuffer_QueryInterface((IDirectSoundCaptureBuffer*)This, riid, ppv);
660 static ULONG WINAPI DSCBufferNot_AddRef(IDirectSoundNotify *iface)
662 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
663 LONG ret;
665 InterlockedIncrement(&This->all_ref);
666 ret = InterlockedIncrement(&This->not_ref);
667 TRACE("new refcount %"LONGFMT"d\n", ret);
668 return ret;
671 static ULONG WINAPI DSCBufferNot_Release(IDirectSoundNotify *iface)
673 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
674 LONG ret;
676 ret = InterlockedDecrement(&This->not_ref);
677 TRACE("new refcount %"LONGFMT"d\n", ret);
678 if(InterlockedDecrement(&This->all_ref) == 0)
679 DSCBuffer_Destroy(This);
681 return ret;
684 static HRESULT WINAPI DSCBufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
686 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
687 DSBPOSITIONNOTIFY *nots;
688 HRESULT hr;
689 DWORD state;
691 TRACE("(%p)->(%"LONGFMT"u, %p))\n", iface, count, notifications);
693 EnterCriticalSection(&This->parent->crst);
694 hr = DSERR_INVALIDPARAM;
695 if (count && !notifications)
696 goto out;
698 hr = DSCBuffer_GetStatus(&This->IDirectSoundCaptureBuffer8_iface, &state);
699 if (FAILED(hr))
700 goto out;
702 hr = DSERR_INVALIDCALL;
703 if (state & DSCBSTATUS_CAPTURING)
704 goto out;
706 if (!count)
708 HeapFree(GetProcessHeap(), 0, This->notify);
709 This->notify = 0;
710 This->nnotify = 0;
712 else
714 DWORD i;
715 hr = DSERR_INVALIDPARAM;
716 for (i = 0; i < count; ++i)
718 if (notifications[i].dwOffset >= This->buf_size
719 && notifications[i].dwOffset != DSCBPN_OFFSET_STOP)
720 goto out;
722 hr = E_OUTOFMEMORY;
723 nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
724 if (!nots)
725 goto out;
726 memcpy(nots, notifications, count*sizeof(*nots));
727 HeapFree(GetProcessHeap(), 0, This->notify);
728 This->notify = nots;
729 This->nnotify = count;
730 hr = S_OK;
733 out:
734 LeaveCriticalSection(&This->parent->crst);
735 return hr;
738 static const IDirectSoundNotifyVtbl DSCNot_Vtbl =
740 DSCBufferNot_QueryInterface,
741 DSCBufferNot_AddRef,
742 DSCBufferNot_Release,
743 DSCBufferNot_SetNotificationPositions
747 static inline DSCImpl *impl_from_IDirectSoundCapture(IDirectSoundCapture *iface)
749 return CONTAINING_RECORD(iface, DSCImpl, IDirectSoundCapture_iface);
752 HRESULT DSOUND_CaptureCreate(REFIID riid, void **cap)
754 DSCImpl *This;
756 *cap = NULL;
758 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
759 if(!This) return DSERR_OUTOFMEMORY;
761 This->IDirectSoundCapture_iface.lpVtbl = (IDirectSoundCaptureVtbl*)&DSC_Vtbl;
763 InitializeCriticalSection(&This->crst);
764 This->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSCImpl.crst");
766 if(FAILED(IDirectSoundCapture_QueryInterface(&This->IDirectSoundCapture_iface, riid, cap)))
768 DSCImpl_Destroy(This);
769 return E_NOINTERFACE;
771 return S_OK;
774 static void DSCImpl_Destroy(DSCImpl *This)
776 EnterCriticalSection(&This->crst);
777 if (This->buf)
778 DSCBuffer_Destroy(This->buf);
779 LeaveCriticalSection(&This->crst);
781 HeapFree(GetProcessHeap(), 0, This->device);
783 This->crst.DebugInfo->Spare[0] = 0;
784 DeleteCriticalSection(&This->crst);
786 HeapFree(GetProcessHeap(), 0, This);
789 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv)
791 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
793 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
795 *ppv = NULL;
796 if(IsEqualIID(riid, &IID_IUnknown) ||
797 IsEqualIID(riid, &IID_IDirectSoundCapture))
798 *ppv = &This->IDirectSoundCapture_iface;
799 else
800 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
802 if(*ppv)
804 IUnknown_AddRef((IUnknown*)*ppv);
805 return S_OK;
808 return E_NOINTERFACE;
811 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture *iface)
813 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
814 LONG ref;
816 ref = InterlockedIncrement(&This->ref);
817 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
819 return ref;
822 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture *iface)
824 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
825 LONG ref;
827 ref = InterlockedDecrement(&This->ref);
828 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
829 if(!ref)
830 DSCImpl_Destroy(This);
832 return ref;
835 static HRESULT WINAPI DSCImpl_CreateCaptureBuffer(IDirectSoundCapture *iface, const DSCBUFFERDESC *desc, IDirectSoundCaptureBuffer **ppv, IUnknown *unk)
837 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
838 HRESULT hr;
840 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, ppv, unk);
842 if(unk)
844 WARN("Aggregation isn't supported\n");
845 return DSERR_NOAGGREGATION;
848 if(!desc || desc->dwSize < sizeof(DSCBUFFERDESC1))
850 WARN("Passed invalid description %p %"LONGFMT"u\n", desc, desc?desc->dwSize:0);
851 return DSERR_INVALIDPARAM;
853 if(!ppv)
855 WARN("Passed null pointer\n");
856 return DSERR_INVALIDPARAM;
858 *ppv = NULL;
860 EnterCriticalSection(&This->crst);
861 if(!This->device)
863 hr = DSERR_UNINITIALIZED;
864 WARN("Not initialized\n");
865 goto out;
867 if(This->buf)
869 hr = DSERR_ALLOCATED;
870 WARN("Capture buffer already allocated\n");
871 goto out;
874 hr = DSCBuffer_Create(&This->buf, This);
875 if(SUCCEEDED(hr))
877 hr = IDirectSoundCaptureBuffer8_Initialize(&This->buf->IDirectSoundCaptureBuffer8_iface, iface, desc);
878 if(SUCCEEDED(hr))
879 *ppv = (IDirectSoundCaptureBuffer*)&This->buf->IDirectSoundCaptureBuffer8_iface;
880 else
882 DSCBuffer_Destroy(This->buf);
883 This->buf = NULL;
887 out:
888 LeaveCriticalSection(&This->crst);
889 return hr;
892 static HRESULT WINAPI DSCImpl_GetCaps(IDirectSoundCapture *iface, DSCCAPS *caps)
894 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
896 TRACE("(%p)->(%p)\n", iface, caps);
898 if(!This->device) {
899 WARN("Not initialized\n");
900 return DSERR_UNINITIALIZED;
903 if(!caps) {
904 WARN("Caps is null\n");
905 return DSERR_INVALIDPARAM;
907 if(caps->dwSize < sizeof(*caps)) {
908 WARN("Invalid size %"LONGFMT"d\n", caps->dwSize);
909 return DSERR_INVALIDPARAM;
912 caps->dwFlags = 0;
913 /* Support all WAVE_FORMAT formats specified in mmsystem.h */
914 caps->dwFormats = 0x000fffff;
915 caps->dwChannels = 2;
917 return DS_OK;
920 static HRESULT WINAPI DSCImpl_Initialize(IDirectSoundCapture *iface, const GUID *devguid)
922 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
923 HRESULT hr;
924 const ALCchar *devs, *drv_name;
925 GUID guid;
926 UINT n;
928 TRACE("(%p)->(%p)\n", iface, devguid);
930 if(!openal_loaded)
932 ERR("OpenAL not loaded!\n");
933 return DSERR_NODRIVER;
936 if(This->device) {
937 WARN("Already initialized\n");
938 return DSERR_ALREADYINITIALIZED;
941 if(!devguid || IsEqualGUID(devguid, &GUID_NULL))
942 devguid = &DSDEVID_DefaultCapture;
944 hr = GetDeviceID(devguid, &guid);
945 if (FAILED(hr))
946 return DSERR_INVALIDPARAM;
947 devguid = &guid;
949 EnterCriticalSection(&This->crst);
950 EnterCriticalSection(&openal_crst);
951 devs = DSOUND_getcapturedevicestrings();
952 n = guid.Data4[7];
954 hr = DSERR_NODRIVER;
955 if(memcmp(devguid, &DSOUND_capture_guid, sizeof(GUID)-1) ||
956 !devs || !*devs)
958 WARN("No driver found\n");
959 goto out;
962 if(n)
964 const ALCchar *str = devs;
965 while (n--)
967 str += strlen(str) + 1;
968 if (!*str)
970 WARN("No driver string found\n");
971 goto out;
974 drv_name = str;
976 else
977 drv_name = devs;
979 This->device = HeapAlloc(GetProcessHeap(), 0, strlen(drv_name)+1);
980 if(!This->device)
982 WARN("Out of memory to allocate %s\n", drv_name);
983 goto out;
985 strcpy(This->device, drv_name);
987 hr = S_OK;
988 out:
989 LeaveCriticalSection(&openal_crst);
990 LeaveCriticalSection(&This->crst);
991 return hr;
994 static const IDirectSoundCaptureVtbl DSC_Vtbl =
996 DSCImpl_QueryInterface,
997 DSCImpl_AddRef,
998 DSCImpl_Release,
999 DSCImpl_CreateCaptureBuffer,
1000 DSCImpl_GetCaps,
1001 DSCImpl_Initialize