Refactor capture buffer format handling
[dsound-openal.git] / capture.c
blob2bf3196c02338860850d1a972ed28d601ba7106b
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 WAVEFORMATEXTENSIBLE 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.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.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->buf);
272 HeapFree(GetProcessHeap(), 0, This);
275 static inline DSCBuffer *impl_from_IDirectSoundCaptureBuffer8(IDirectSoundCaptureBuffer8 *iface)
277 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundCaptureBuffer8_iface);
280 static HRESULT WINAPI DSCBuffer_QueryInterface(IDirectSoundCaptureBuffer8 *iface, REFIID riid, void **ppv)
282 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
284 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
286 if(!ppv)
287 return E_POINTER;
288 *ppv = NULL;
290 if (IsEqualIID(riid, &IID_IDirectSoundNotify))
291 *ppv = &This->IDirectSoundNotify_iface;
292 else if (IsEqualIID(riid, &IID_IUnknown) ||
293 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer) ||
294 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer8))
295 *ppv = &This->IDirectSoundCaptureBuffer8_iface;
297 if (!*ppv)
298 return E_NOINTERFACE;
299 IUnknown_AddRef((IUnknown*)*ppv);
300 return S_OK;
303 static ULONG WINAPI DSCBuffer_AddRef(IDirectSoundCaptureBuffer8 *iface)
305 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
306 LONG ref;
308 InterlockedIncrement(&This->all_ref);
309 ref = InterlockedIncrement(&This->ref);
310 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
312 return ref;
315 static ULONG WINAPI DSCBuffer_Release(IDirectSoundCaptureBuffer8 *iface)
317 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
318 LONG ref;
320 ref = InterlockedDecrement(&This->ref);
321 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
322 if(InterlockedDecrement(&This->all_ref) == 0)
323 DSCBuffer_Destroy(This);
325 return ref;
328 static HRESULT WINAPI DSCBuffer_GetCaps(IDirectSoundCaptureBuffer8 *iface, DSCBCAPS *caps)
330 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
332 if (!caps || caps->dwSize < sizeof(*caps))
333 return DSERR_INVALIDPARAM;
334 caps->dwSize = sizeof(*caps);
335 caps->dwFlags = 0;
336 caps->dwBufferBytes = This->buf_size;
337 return S_OK;
340 static HRESULT WINAPI DSCBuffer_GetCurrentPosition(IDirectSoundCaptureBuffer8 *iface, DWORD *cappos, DWORD *readpos)
342 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
343 DWORD pos1, pos2;
345 EnterCriticalSection(&This->parent->crst);
346 pos1 = This->pos;
347 if(This->playing)
349 pos2 = This->format.Format.nSamplesPerSec / 100;
350 pos2 *= This->format.Format.nBlockAlign;
351 pos2 += pos1;
352 if (!This->looping && pos2 >= This->buf_size)
353 pos2 = 0;
354 else
355 pos2 %= This->buf_size;
357 else
358 pos2 = pos1;
359 LeaveCriticalSection(&This->parent->crst);
361 if(cappos) *cappos = pos1;
362 if(readpos) *readpos = pos2;
364 return S_OK;
367 static HRESULT WINAPI DSCBuffer_GetFormat(IDirectSoundCaptureBuffer8 *iface, WAVEFORMATEX *wfx, DWORD allocated, DWORD *written)
369 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
370 HRESULT hr = DS_OK;
371 UINT size;
373 TRACE("(%p)->(%p, %"LONGFMT"u, %p)\n", iface, wfx, allocated, written);
375 if(!wfx && !written)
377 WARN("Cannot report format or format size\n");
378 return DSERR_INVALIDPARAM;
381 size = sizeof(This->format.Format) + This->format.Format.cbSize;
382 if(wfx)
384 if(allocated < size)
385 hr = DSERR_INVALIDPARAM;
386 else
387 memcpy(wfx, &This->format.Format, size);
389 if(written)
390 *written = size;
392 return hr;
395 static HRESULT WINAPI DSCBuffer_GetStatus(IDirectSoundCaptureBuffer8 *iface, DWORD *status)
397 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
399 TRACE("(%p)->(%p)\n", iface, status);
401 if (!status)
402 return DSERR_INVALIDPARAM;
403 EnterCriticalSection(&This->parent->crst);
404 *status = 0;
405 if (This->playing)
407 *status |= DSCBSTATUS_CAPTURING;
408 if (This->looping)
409 *status |= DSCBSTATUS_LOOPING;
411 LeaveCriticalSection(&This->parent->crst);
413 return S_OK;
416 static HRESULT WINAPI DSCBuffer_Initialize(IDirectSoundCaptureBuffer8 *iface, IDirectSoundCapture *parent, const DSCBUFFERDESC *desc)
418 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
419 WAVEFORMATEX *format;
420 ALenum buf_format = -1;
422 TRACE("(%p)->(%p, %p)\n", iface, parent, desc);
424 if(This->dev)
425 return DSERR_ALREADYINITIALIZED;
427 if (!desc->lpwfxFormat)
428 return DSERR_INVALIDPARAM;
430 format = desc->lpwfxFormat;
431 if(format->nChannels > 2)
433 WARN("nChannels > 2 not supported for recording\n");
434 return DSERR_INVALIDPARAM;
437 if(format->wFormatTag == WAVE_FORMAT_PCM)
439 if(format->nChannels == 1)
441 switch(format->wBitsPerSample)
443 case 8: buf_format = AL_FORMAT_MONO8; break;
444 case 16: buf_format = AL_FORMAT_MONO16; break;
445 default:
446 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
447 return DSERR_BADFORMAT;
450 else if(format->nChannels == 2)
452 switch(format->wBitsPerSample)
454 case 8: buf_format = AL_FORMAT_STEREO8; break;
455 case 16: buf_format = AL_FORMAT_STEREO16; break;
456 default:
457 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
458 return DSERR_BADFORMAT;
461 else
462 WARN("Unsupported channels: %d\n", format->nChannels);
464 memcpy(&This->format.Format, format, sizeof(This->format.Format));
465 This->format.Format.nBlockAlign = This->format.Format.wBitsPerSample * This->format.Format.nChannels / 8;
466 This->format.Format.nAvgBytesPerSec = This->format.Format.nSamplesPerSec * This->format.Format.nBlockAlign;
467 This->format.Format.cbSize = 0;
469 else if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
471 WAVEFORMATEXTENSIBLE *wfe;
473 if(format->cbSize < sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX))
474 return DSERR_INVALIDPARAM;
475 else if(format->cbSize > sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) &&
476 format->cbSize != sizeof(WAVEFORMATEXTENSIBLE))
477 return DSERR_CONTROLUNAVAIL;
479 wfe = CONTAINING_RECORD(format, WAVEFORMATEXTENSIBLE, Format);
480 if(!IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
481 return DSERR_BADFORMAT;
482 if(wfe->Samples.wValidBitsPerSample &&
483 wfe->Samples.wValidBitsPerSample != wfe->Format.wBitsPerSample)
484 return DSERR_BADFORMAT;
486 if(wfe->Format.nChannels == 1 && wfe->dwChannelMask == SPEAKER_FRONT_CENTER)
488 switch(wfe->Format.wBitsPerSample)
490 case 8: buf_format = AL_FORMAT_MONO8; break;
491 case 16: buf_format = AL_FORMAT_MONO16; break;
492 default:
493 WARN("Unsupported bpp %u\n", wfe->Format.wBitsPerSample);
494 return DSERR_BADFORMAT;
497 else if(wfe->Format.nChannels == 2 && wfe->dwChannelMask == (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT))
499 switch(wfe->Format.wBitsPerSample)
501 case 8: buf_format = AL_FORMAT_STEREO8; break;
502 case 16: buf_format = AL_FORMAT_STEREO16; break;
503 default:
504 WARN("Unsupported bpp %u\n", wfe->Format.wBitsPerSample);
505 return DSERR_BADFORMAT;
508 else
509 WARN("Unsupported channels: %d -- 0x%08"LONGFMT"u\n", wfe->Format.nChannels, wfe->dwChannelMask);
511 memcpy(&This->format, wfe, sizeof(This->format));
512 This->format.Format.cbSize = sizeof(This->format) - sizeof(This->format.Format);
513 This->format.Format.nBlockAlign = This->format.Format.wBitsPerSample * This->format.Format.nChannels / 8;
514 This->format.Format.nAvgBytesPerSec = This->format.Format.nSamplesPerSec * This->format.Format.nBlockAlign;
516 else
517 WARN("Unhandled formattag %x\n", format->wFormatTag);
519 if(buf_format <= 0)
521 WARN("Could not get OpenAL format\n");
522 return DSERR_INVALIDPARAM;
525 This->buf_size = desc->dwBufferBytes;
526 This->buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->buf_size);
527 if(!This->buf)
529 WARN("Out of memory\n");
530 return DSERR_INVALIDPARAM;
533 This->dev = alcCaptureOpenDevice(This->parent->device, This->format.Format.nSamplesPerSec, buf_format, This->buf_size / This->format.Format.nBlockAlign);
534 if(!This->dev)
536 ERR("Couldn't open device %s 0x%x@%"LONGFMT"u, reason: %04x\n", This->parent->device, buf_format, This->format.Format.nSamplesPerSec, alcGetError(NULL));
537 return DSERR_INVALIDPARAM;
540 return S_OK;
543 static HRESULT WINAPI DSCBuffer_Lock(IDirectSoundCaptureBuffer8 *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
545 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
546 HRESULT hr;
547 DWORD remain;
549 TRACE("(%p)->(%"LONGFMT"u, %"LONGFMT"u, %p, %p, %p, %p, %#"LONGFMT"x)\n", iface, ofs, bytes, ptr1, len1, ptr2, len2, flags);
551 EnterCriticalSection(&This->parent->crst);
552 hr = DSERR_INVALIDPARAM;
554 if(ptr1) *ptr1 = NULL;
555 if(len1) *len1 = 0;
556 if(ptr2) *ptr2 = NULL;
557 if(len2) *len2 = 0;
559 if (ofs >= This->buf_size)
561 WARN("Invalid ofs %"LONGFMT"u\n", ofs);
562 goto out;
564 if (!ptr1 || !len1)
566 WARN("Invalid pointer/len %p %p\n", ptr1, len1);
567 goto out;
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 goto out;
577 if (ofs + bytes >= This->buf_size)
579 *len1 = This->buf_size - ofs;
580 remain = bytes - *len1;
582 else
584 *len1 = bytes;
585 remain = 0;
587 *ptr1 = This->buf + ofs;
589 if (ptr2 && len2 && remain)
591 *ptr2 = This->buf;
592 *len2 = remain;
594 hr = S_OK;
596 out:
597 LeaveCriticalSection(&This->parent->crst);
598 return hr;
601 static HRESULT WINAPI DSCBuffer_Start(IDirectSoundCaptureBuffer8 *iface, DWORD flags)
603 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
605 TRACE("(%p)->(%08"LONGFMT"x)\n", iface, flags);
607 EnterCriticalSection(&This->parent->crst);
608 if(!This->playing)
610 DSCBuffer_starttimer(This);
611 This->playing = 1;
612 alcCaptureStart(This->dev);
614 This->looping = !!(flags & DSCBSTART_LOOPING);
615 LeaveCriticalSection(&This->parent->crst);
616 return S_OK;
619 static HRESULT WINAPI DSCBuffer_Stop(IDirectSoundCaptureBuffer8 *iface)
621 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
623 TRACE("(%p)->()\n", iface);
625 EnterCriticalSection(&This->parent->crst);
626 if(This->playing)
628 DWORD i;
629 for(i = 0;i < This->nnotify;++i)
631 if(This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
632 SetEvent(This->notify[i].hEventNotify);
635 This->playing = This->looping = 0;
636 alcCaptureStop(This->dev);
638 LeaveCriticalSection(&This->parent->crst);
639 return S_OK;
642 static HRESULT WINAPI DSCBuffer_Unlock(IDirectSoundCaptureBuffer8 *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
644 TRACE("(%p)->(%p, %"LONGFMT"u, %p, %"LONGFMT"u)\n", iface, ptr1, len1, ptr2, len2);
646 if (!ptr1)
647 return DSERR_INVALIDPARAM;
648 return S_OK;
651 static HRESULT WINAPI DSCBuffer_GetObjectInPath(IDirectSoundCaptureBuffer8 *iface, REFGUID guid, DWORD num, REFGUID riid, void **ppv)
653 FIXME("(%p)->(%s, %"LONGFMT"u, %s, %p) stub\n", iface, debugstr_guid(guid), num, debugstr_guid(riid), ppv);
654 return E_NOTIMPL;
657 static HRESULT WINAPI DSCBuffer_GetFXStatus(IDirectSoundCaptureBuffer8 *iface, DWORD count, DWORD *status)
659 FIXME("(%p)->(%"LONGFMT"u, %p) stub\n", iface, count, status);
660 return E_NOTIMPL;
663 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl =
665 DSCBuffer_QueryInterface,
666 DSCBuffer_AddRef,
667 DSCBuffer_Release,
668 DSCBuffer_GetCaps,
669 DSCBuffer_GetCurrentPosition,
670 DSCBuffer_GetFormat,
671 DSCBuffer_GetStatus,
672 DSCBuffer_Initialize,
673 DSCBuffer_Lock,
674 DSCBuffer_Start,
675 DSCBuffer_Stop,
676 DSCBuffer_Unlock,
677 DSCBuffer_GetObjectInPath,
678 DSCBuffer_GetFXStatus
681 static inline DSCBuffer *impl_from_IDirectSoundNotify(IDirectSoundNotify *iface)
683 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundNotify_iface);
686 static HRESULT WINAPI DSCBufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
688 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
689 return IDirectSoundCaptureBuffer_QueryInterface((IDirectSoundCaptureBuffer*)This, riid, ppv);
692 static ULONG WINAPI DSCBufferNot_AddRef(IDirectSoundNotify *iface)
694 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
695 LONG ret;
697 InterlockedIncrement(&This->all_ref);
698 ret = InterlockedIncrement(&This->not_ref);
699 TRACE("new refcount %"LONGFMT"d\n", ret);
700 return ret;
703 static ULONG WINAPI DSCBufferNot_Release(IDirectSoundNotify *iface)
705 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
706 LONG ret;
708 ret = InterlockedDecrement(&This->not_ref);
709 TRACE("new refcount %"LONGFMT"d\n", ret);
710 if(InterlockedDecrement(&This->all_ref) == 0)
711 DSCBuffer_Destroy(This);
713 return ret;
716 static HRESULT WINAPI DSCBufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
718 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
719 DSBPOSITIONNOTIFY *nots;
720 HRESULT hr;
721 DWORD state;
723 TRACE("(%p)->(%"LONGFMT"u, %p))\n", iface, count, notifications);
725 EnterCriticalSection(&This->parent->crst);
726 hr = DSERR_INVALIDPARAM;
727 if (count && !notifications)
728 goto out;
730 hr = DSCBuffer_GetStatus(&This->IDirectSoundCaptureBuffer8_iface, &state);
731 if (FAILED(hr))
732 goto out;
734 hr = DSERR_INVALIDCALL;
735 if (state & DSCBSTATUS_CAPTURING)
736 goto out;
738 if (!count)
740 HeapFree(GetProcessHeap(), 0, This->notify);
741 This->notify = 0;
742 This->nnotify = 0;
744 else
746 DWORD i;
747 hr = DSERR_INVALIDPARAM;
748 for (i = 0; i < count; ++i)
750 if (notifications[i].dwOffset >= This->buf_size
751 && notifications[i].dwOffset != DSCBPN_OFFSET_STOP)
752 goto out;
754 hr = E_OUTOFMEMORY;
755 nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
756 if (!nots)
757 goto out;
758 memcpy(nots, notifications, count*sizeof(*nots));
759 HeapFree(GetProcessHeap(), 0, This->notify);
760 This->notify = nots;
761 This->nnotify = count;
762 hr = S_OK;
765 out:
766 LeaveCriticalSection(&This->parent->crst);
767 return hr;
770 static const IDirectSoundNotifyVtbl DSCNot_Vtbl =
772 DSCBufferNot_QueryInterface,
773 DSCBufferNot_AddRef,
774 DSCBufferNot_Release,
775 DSCBufferNot_SetNotificationPositions
779 static inline DSCImpl *impl_from_IDirectSoundCapture(IDirectSoundCapture *iface)
781 return CONTAINING_RECORD(iface, DSCImpl, IDirectSoundCapture_iface);
784 HRESULT DSOUND_CaptureCreate(REFIID riid, void **cap)
786 DSCImpl *This;
788 *cap = NULL;
790 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
791 if(!This) return DSERR_OUTOFMEMORY;
793 This->IDirectSoundCapture_iface.lpVtbl = (IDirectSoundCaptureVtbl*)&DSC_Vtbl;
795 InitializeCriticalSection(&This->crst);
796 This->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DSCImpl.crst");
798 if(FAILED(IDirectSoundCapture_QueryInterface(&This->IDirectSoundCapture_iface, riid, cap)))
800 DSCImpl_Destroy(This);
801 return E_NOINTERFACE;
803 return S_OK;
806 static void DSCImpl_Destroy(DSCImpl *This)
808 EnterCriticalSection(&This->crst);
809 if (This->buf)
810 DSCBuffer_Destroy(This->buf);
811 LeaveCriticalSection(&This->crst);
813 HeapFree(GetProcessHeap(), 0, This->device);
815 This->crst.DebugInfo->Spare[0] = 0;
816 DeleteCriticalSection(&This->crst);
818 HeapFree(GetProcessHeap(), 0, This);
821 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv)
823 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
825 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
827 *ppv = NULL;
828 if(IsEqualIID(riid, &IID_IUnknown) ||
829 IsEqualIID(riid, &IID_IDirectSoundCapture))
830 *ppv = &This->IDirectSoundCapture_iface;
831 else
832 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
834 if(*ppv)
836 IUnknown_AddRef((IUnknown*)*ppv);
837 return S_OK;
840 return E_NOINTERFACE;
843 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture *iface)
845 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
846 LONG ref;
848 ref = InterlockedIncrement(&This->ref);
849 TRACE("Reference count incremented to %"LONGFMT"i\n", ref);
851 return ref;
854 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture *iface)
856 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
857 LONG ref;
859 ref = InterlockedDecrement(&This->ref);
860 TRACE("Reference count decremented to %"LONGFMT"i\n", ref);
861 if(!ref)
862 DSCImpl_Destroy(This);
864 return ref;
867 static HRESULT WINAPI DSCImpl_CreateCaptureBuffer(IDirectSoundCapture *iface, const DSCBUFFERDESC *desc, IDirectSoundCaptureBuffer **ppv, IUnknown *unk)
869 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
870 HRESULT hr;
872 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, ppv, unk);
874 if(unk)
876 WARN("Aggregation isn't supported\n");
877 return DSERR_NOAGGREGATION;
880 if(!desc || desc->dwSize < sizeof(DSCBUFFERDESC1))
882 WARN("Passed invalid description %p %"LONGFMT"u\n", desc, desc?desc->dwSize:0);
883 return DSERR_INVALIDPARAM;
885 if(!ppv)
887 WARN("Passed null pointer\n");
888 return DSERR_INVALIDPARAM;
890 *ppv = NULL;
892 EnterCriticalSection(&This->crst);
893 if(!This->device)
895 hr = DSERR_UNINITIALIZED;
896 WARN("Not initialized\n");
897 goto out;
899 if(This->buf)
901 hr = DSERR_ALLOCATED;
902 WARN("Capture buffer already allocated\n");
903 goto out;
906 hr = DSCBuffer_Create(&This->buf, This);
907 if(SUCCEEDED(hr))
909 hr = IDirectSoundCaptureBuffer8_Initialize(&This->buf->IDirectSoundCaptureBuffer8_iface, iface, desc);
910 if(SUCCEEDED(hr))
911 *ppv = (IDirectSoundCaptureBuffer*)&This->buf->IDirectSoundCaptureBuffer8_iface;
912 else
914 DSCBuffer_Destroy(This->buf);
915 This->buf = NULL;
919 out:
920 LeaveCriticalSection(&This->crst);
921 return hr;
924 static HRESULT WINAPI DSCImpl_GetCaps(IDirectSoundCapture *iface, DSCCAPS *caps)
926 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
928 TRACE("(%p)->(%p)\n", iface, caps);
930 if(!This->device) {
931 WARN("Not initialized\n");
932 return DSERR_UNINITIALIZED;
935 if(!caps) {
936 WARN("Caps is null\n");
937 return DSERR_INVALIDPARAM;
939 if(caps->dwSize < sizeof(*caps)) {
940 WARN("Invalid size %"LONGFMT"d\n", caps->dwSize);
941 return DSERR_INVALIDPARAM;
944 caps->dwFlags = 0;
945 /* Support all WAVE_FORMAT formats specified in mmsystem.h */
946 caps->dwFormats = 0x000fffff;
947 caps->dwChannels = 2;
949 return DS_OK;
952 static HRESULT WINAPI DSCImpl_Initialize(IDirectSoundCapture *iface, const GUID *devguid)
954 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
955 HRESULT hr;
956 const ALCchar *devs, *drv_name;
957 GUID guid;
958 UINT n;
960 TRACE("(%p)->(%p)\n", iface, devguid);
962 if(!openal_loaded)
964 ERR("OpenAL not loaded!\n");
965 return DSERR_NODRIVER;
968 if(This->device) {
969 WARN("Already initialized\n");
970 return DSERR_ALREADYINITIALIZED;
973 if(!devguid || IsEqualGUID(devguid, &GUID_NULL))
974 devguid = &DSDEVID_DefaultCapture;
976 hr = GetDeviceID(devguid, &guid);
977 if (FAILED(hr))
978 return DSERR_INVALIDPARAM;
979 devguid = &guid;
981 EnterCriticalSection(&This->crst);
982 EnterCriticalSection(&openal_crst);
983 devs = DSOUND_getcapturedevicestrings();
984 n = guid.Data4[7];
986 hr = DSERR_NODRIVER;
987 if(memcmp(devguid, &DSOUND_capture_guid, sizeof(GUID)-1) ||
988 !devs || !*devs)
990 WARN("No driver found\n");
991 goto out;
994 if(n)
996 const ALCchar *str = devs;
997 while (n--)
999 str += strlen(str) + 1;
1000 if (!*str)
1002 WARN("No driver string found\n");
1003 goto out;
1006 drv_name = str;
1008 else
1009 drv_name = devs;
1011 This->device = HeapAlloc(GetProcessHeap(), 0, strlen(drv_name)+1);
1012 if(!This->device)
1014 WARN("Out of memory to allocate %s\n", drv_name);
1015 goto out;
1017 strcpy(This->device, drv_name);
1019 hr = S_OK;
1020 out:
1021 LeaveCriticalSection(&openal_crst);
1022 LeaveCriticalSection(&This->crst);
1023 return hr;
1026 static const IDirectSoundCaptureVtbl DSC_Vtbl =
1028 DSCImpl_QueryInterface,
1029 DSCImpl_AddRef,
1030 DSCImpl_Release,
1031 DSCImpl_CreateCaptureBuffer,
1032 DSCImpl_GetCaps,
1033 DSCImpl_Initialize