Report the EAX last error as settable
[dsound-openal.git] / capture.c
blobbfe75a63b54f9c816ca488859469acdaf0da0a82
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 #define CONST_VTABLE
21 #include <stdarg.h>
23 #include <windows.h>
24 #include <dsound.h>
26 #include "dsound_private.h"
28 #ifndef DSCBPN_OFFSET_STOP
29 #define DSCBPN_OFFSET_STOP 0xffffffff
30 #endif
33 typedef struct DSCImpl DSCImpl;
34 typedef struct DSCBuffer DSCBuffer;
36 struct DSCImpl {
37 /* IDirectSoundCapture and IDirectSoundCapture8 are aliases */
38 IDirectSoundCapture IDirectSoundCapture_iface;
39 IUnknown IUnknown_iface;
40 LONG allref, dscref, unkref;
42 BOOL is_8;
44 ALCchar *device_name;
45 DSCBuffer *buf;
47 CRITICAL_SECTION crst;
50 struct DSCBuffer {
51 IDirectSoundCaptureBuffer8 IDirectSoundCaptureBuffer8_iface;
52 IDirectSoundNotify IDirectSoundNotify_iface;
53 LONG ref, not_ref;
54 LONG all_ref;
56 DSCImpl *parent;
57 ALCdevice *device;
59 DWORD buf_size;
60 BYTE *buf;
62 LONG locked;
64 WAVEFORMATEXTENSIBLE format;
66 DSBPOSITIONNOTIFY *notify;
67 DWORD nnotify;
69 HANDLE thread_hdl;
70 DWORD thread_id;
72 HANDLE queue_timer;
73 HANDLE timer_evt;
74 volatile LONG quit_now;
76 DWORD pos;
77 BOOL playing, looping;
80 static const IDirectSoundCaptureVtbl DSC_Vtbl;
81 static const IUnknownVtbl DSC_Unknown_Vtbl;
82 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl;
83 static const IDirectSoundNotifyVtbl DSCNot_Vtbl;
85 static void DSCImpl_Destroy(DSCImpl *This);
87 static void trigger_notifies(DSCBuffer *buf, DWORD lastpos, DWORD curpos)
89 DWORD i;
91 if(lastpos == curpos)
92 return;
94 for(i = 0;i < buf->nnotify;++i)
96 DSBPOSITIONNOTIFY *not = &buf->notify[i];
97 HANDLE event = not->hEventNotify;
98 DWORD ofs = not->dwOffset;
100 if (ofs == DSCBPN_OFFSET_STOP)
101 continue;
103 /* Wraparound case */
104 if(curpos < lastpos)
106 if(ofs < curpos || ofs >= lastpos)
108 TRACE("Triggering notification %lu (%lu) from buffer %p\n", i, ofs, buf);
109 SetEvent(event);
111 continue;
114 /* Normal case */
115 if(ofs >= lastpos && ofs < curpos)
117 TRACE("Triggering notification %lu (%lu) from buffer %p\n", i, ofs, buf);
118 SetEvent(event);
123 static DWORD CALLBACK DSCBuffer_thread(void *param)
125 DSCBuffer *This = param;
126 CRITICAL_SECTION *crst = &This->parent->crst;
128 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
130 while(WaitForSingleObject(This->timer_evt, INFINITE) == WAIT_OBJECT_0 && !This->quit_now)
132 ALCint avail = 0;
134 alcGetIntegerv(This->device, ALC_CAPTURE_SAMPLES, 1, &avail);
135 if(avail == 0 || !This->playing) continue;
137 EnterCriticalSection(crst);
138 if(!This->playing)
140 LeaveCriticalSection(crst);
141 continue;
143 more_samples:
144 avail *= This->format.Format.nBlockAlign;
145 if((DWORD)avail > This->buf_size - This->pos)
146 avail = This->buf_size - This->pos;
148 alcCaptureSamples(This->device, This->buf+This->pos,
149 avail/This->format.Format.nBlockAlign);
150 trigger_notifies(This, This->pos, This->pos + avail);
151 This->pos += avail;
153 if(This->pos == This->buf_size)
155 This->pos = 0;
156 if(!This->looping)
158 DWORD i;
159 for(i = 0;i < This->nnotify;++i)
161 if(This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
162 SetEvent(This->notify[i].hEventNotify);
165 This->playing = 0;
166 alcCaptureStop(This->device);
168 else
170 alcGetIntegerv(This->device, ALC_CAPTURE_SAMPLES, 1, &avail);
171 if(avail) goto more_samples;
175 LeaveCriticalSection(crst);
178 return 0;
182 static void CALLBACK DSCBuffer_timer(void *arg, BOOLEAN unused)
184 (void)unused;
185 SetEvent((HANDLE)arg);
188 static void DSCBuffer_starttimer(DSCBuffer *This)
190 ALint refresh = FAKE_REFRESH_COUNT;
191 DWORD triggertime;
193 if(This->queue_timer)
194 return;
196 triggertime = 1000 / refresh * 2 / 3;
197 TRACE("Calling timer every %lu ms for %i refreshes per second\n", triggertime, refresh);
199 CreateTimerQueueTimer(&This->queue_timer, NULL, DSCBuffer_timer, This->timer_evt,
200 triggertime, triggertime, WT_EXECUTEINTIMERTHREAD);
203 static HRESULT DSCBuffer_Create(DSCBuffer **buf, DSCImpl *parent)
205 DSCBuffer *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
206 if(!This) return E_OUTOFMEMORY;
208 This->IDirectSoundCaptureBuffer8_iface.lpVtbl = &DSCBuffer_Vtbl;
209 This->IDirectSoundNotify_iface.lpVtbl = &DSCNot_Vtbl;
211 This->all_ref = This->ref = 1;
213 This->parent = parent;
215 This->quit_now = FALSE;
216 This->timer_evt = CreateEventA(NULL, FALSE, FALSE, NULL);
217 if(!This->timer_evt) goto fail;
219 This->queue_timer = NULL;
221 This->thread_hdl = CreateThread(NULL, 0, DSCBuffer_thread, This, 0, &This->thread_id);
222 if(!This->thread_hdl) goto fail;
224 *buf = This;
225 return S_OK;
227 fail:
228 if(This->timer_evt)
229 CloseHandle(This->timer_evt);
230 This->timer_evt = NULL;
232 HeapFree(GetProcessHeap(), 0, This);
233 return E_FAIL;
236 static void DSCBuffer_Destroy(DSCBuffer *This)
238 if(This->queue_timer)
239 DeleteTimerQueueTimer(NULL, This->queue_timer, INVALID_HANDLE_VALUE);
240 This->queue_timer = NULL;
242 if(This->thread_hdl)
244 InterlockedExchange(&This->quit_now, TRUE);
245 SetEvent(This->timer_evt);
247 if(WaitForSingleObject(This->thread_hdl, 1000) != WAIT_OBJECT_0)
248 ERR("Thread wait timed out\n");
250 CloseHandle(This->thread_hdl);
251 This->thread_hdl = NULL;
254 if(This->timer_evt)
255 CloseHandle(This->timer_evt);
256 This->timer_evt = NULL;
258 if(This->device)
260 if(This->playing)
261 alcCaptureStop(This->device);
262 alcCaptureCloseDevice(This->device);
264 This->parent->buf = NULL;
266 HeapFree(GetProcessHeap(), 0, This->notify);
267 HeapFree(GetProcessHeap(), 0, This->buf);
268 HeapFree(GetProcessHeap(), 0, This);
271 static inline DSCBuffer *impl_from_IDirectSoundCaptureBuffer8(IDirectSoundCaptureBuffer8 *iface)
273 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundCaptureBuffer8_iface);
276 static HRESULT WINAPI DSCBuffer_QueryInterface(IDirectSoundCaptureBuffer8 *iface, REFIID riid, void **ppv)
278 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
280 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
282 if(!ppv)
283 return E_POINTER;
284 *ppv = NULL;
286 if(IsEqualIID(riid, &IID_IUnknown) ||
287 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer))
288 *ppv = &This->IDirectSoundCaptureBuffer8_iface;
289 else if(IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer8))
291 if(This->parent->is_8)
292 *ppv = &This->IDirectSoundCaptureBuffer8_iface;
294 else if(IsEqualIID(riid, &IID_IDirectSoundNotify))
295 *ppv = &This->IDirectSoundNotify_iface;
296 else
297 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
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 %li\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 %li\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, %lu, %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->device)
427 return DSERR_ALREADYINITIALIZED;
429 if (!desc->lpwfxFormat)
430 return DSERR_INVALIDPARAM;
432 format = desc->lpwfxFormat;
433 if(format->nChannels <= 0 || format->nChannels > 2)
435 WARN("Invalid Channels %d\n", format->nChannels);
436 return DSERR_INVALIDPARAM;
438 if(format->wBitsPerSample <= 0 || (format->wBitsPerSample%8) != 0)
440 WARN("Invalid BitsPerSample %d\n", format->wBitsPerSample);
441 return DSERR_INVALIDPARAM;
443 if(format->nBlockAlign != format->nChannels*format->wBitsPerSample/8)
445 WARN("Invalid BlockAlign %d (expected %u = %u*%u/8)\n",
446 format->nBlockAlign, format->nChannels*format->wBitsPerSample/8,
447 format->nChannels, format->wBitsPerSample);
448 return DSERR_INVALIDPARAM;
450 if(format->nSamplesPerSec < DSBFREQUENCY_MIN || format->nSamplesPerSec > DSBFREQUENCY_MAX)
452 WARN("Invalid sample rate %lu\n", format->nSamplesPerSec);
453 return DSERR_INVALIDPARAM;
455 if(format->nAvgBytesPerSec != format->nSamplesPerSec*format->nBlockAlign)
457 WARN("Invalid AvgBytesPerSec %lu (expected %lu = %lu*%u)\n",
458 format->nAvgBytesPerSec, format->nSamplesPerSec*format->nBlockAlign,
459 format->nSamplesPerSec, format->nBlockAlign);
460 return DSERR_INVALIDPARAM;
463 if(format->wFormatTag == WAVE_FORMAT_PCM)
465 if(format->nChannels == 1)
467 switch(format->wBitsPerSample)
469 case 8: buf_format = AL_FORMAT_MONO8; break;
470 case 16: buf_format = AL_FORMAT_MONO16; break;
471 default:
472 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
473 return DSERR_BADFORMAT;
476 else if(format->nChannels == 2)
478 switch(format->wBitsPerSample)
480 case 8: buf_format = AL_FORMAT_STEREO8; break;
481 case 16: buf_format = AL_FORMAT_STEREO16; break;
482 default:
483 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
484 return DSERR_BADFORMAT;
487 else
488 WARN("Unsupported channels: %d\n", format->nChannels);
490 This->format.Format = *format;
491 This->format.Format.cbSize = 0;
493 else if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
495 WAVEFORMATEXTENSIBLE *wfe;
497 if(format->cbSize < sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX))
498 return DSERR_INVALIDPARAM;
499 else if(format->cbSize > sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) &&
500 format->cbSize != sizeof(WAVEFORMATEXTENSIBLE))
501 return DSERR_CONTROLUNAVAIL;
503 wfe = CONTAINING_RECORD(format, WAVEFORMATEXTENSIBLE, Format);
504 if(!IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
505 return DSERR_BADFORMAT;
506 if(wfe->Samples.wValidBitsPerSample &&
507 wfe->Samples.wValidBitsPerSample != wfe->Format.wBitsPerSample)
508 return DSERR_BADFORMAT;
510 if(wfe->Format.nChannels == 1 && wfe->dwChannelMask == SPEAKER_FRONT_CENTER)
512 switch(wfe->Format.wBitsPerSample)
514 case 8: buf_format = AL_FORMAT_MONO8; break;
515 case 16: buf_format = AL_FORMAT_MONO16; break;
516 default:
517 WARN("Unsupported bpp %u\n", wfe->Format.wBitsPerSample);
518 return DSERR_BADFORMAT;
521 else if(wfe->Format.nChannels == 2 && wfe->dwChannelMask == (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT))
523 switch(wfe->Format.wBitsPerSample)
525 case 8: buf_format = AL_FORMAT_STEREO8; break;
526 case 16: buf_format = AL_FORMAT_STEREO16; break;
527 default:
528 WARN("Unsupported bpp %u\n", wfe->Format.wBitsPerSample);
529 return DSERR_BADFORMAT;
532 else
533 WARN("Unsupported channels: %d -- 0x%08lu\n", wfe->Format.nChannels, wfe->dwChannelMask);
535 This->format = *wfe;
536 This->format.Format.cbSize = sizeof(This->format) - sizeof(This->format.Format);
537 This->format.Samples.wValidBitsPerSample = This->format.Format.wBitsPerSample;
539 else
540 WARN("Unhandled formattag %x\n", format->wFormatTag);
542 if(buf_format <= 0)
544 WARN("Could not get OpenAL format\n");
545 return DSERR_INVALIDPARAM;
548 if(desc->dwBufferBytes < This->format.Format.nBlockAlign ||
549 (desc->dwBufferBytes%This->format.Format.nBlockAlign) != 0)
551 WARN("Invalid BufferBytes (%lu %% %d)\n", desc->dwBufferBytes,
552 This->format.Format.nBlockAlign);
553 return DSERR_INVALIDPARAM;
557 This->buf_size = desc->dwBufferBytes;
558 This->buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->buf_size);
559 if(!This->buf)
561 WARN("Out of memory\n");
562 return DSERR_INVALIDPARAM;
565 This->device = alcCaptureOpenDevice(This->parent->device_name,
566 This->format.Format.nSamplesPerSec, buf_format,
567 This->format.Format.nSamplesPerSec / FAKE_REFRESH_COUNT * 2
569 if(!This->device)
571 ERR("Couldn't open device %s 0x%x@%lu, reason: %04x\n", This->parent->device_name,
572 buf_format, This->format.Format.nSamplesPerSec, alcGetError(NULL));
573 return DSERR_INVALIDPARAM;
576 return S_OK;
579 static HRESULT WINAPI DSCBuffer_Lock(IDirectSoundCaptureBuffer8 *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
581 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
582 DWORD remain;
584 TRACE("(%p)->(%lu, %lu, %p, %p, %p, %p, %#lx)\n", iface, ofs, bytes, ptr1, len1, ptr2, len2, flags);
586 if(!ptr1 || !len1)
588 WARN("Invalid pointer/len %p %p\n", ptr1, len1);
589 return DSERR_INVALIDPARAM;
592 *ptr1 = NULL;
593 *len1 = 0;
594 if(ptr2) *ptr2 = NULL;
595 if(len2) *len2 = 0;
597 if(ofs >= This->buf_size)
599 WARN("Invalid ofs %lu\n", ofs);
600 return DSERR_INVALIDPARAM;
603 if((flags&DSCBLOCK_ENTIREBUFFER))
604 bytes = This->buf_size;
605 else if(bytes > This->buf_size)
607 WARN("Invalid size %lu\n", bytes);
608 return DSERR_INVALIDPARAM;
611 if(InterlockedExchange(&This->locked, TRUE) == TRUE)
613 WARN("Already locked\n");
614 return DSERR_INVALIDPARAM;
617 if(ofs + bytes >= This->buf_size)
619 *len1 = This->buf_size - ofs;
620 remain = bytes - *len1;
622 else
624 *len1 = bytes;
625 remain = 0;
627 *ptr1 = This->buf + ofs;
629 if(ptr2 && len2 && remain)
631 *ptr2 = This->buf;
632 *len2 = remain;
635 return DS_OK;
638 static HRESULT WINAPI DSCBuffer_Start(IDirectSoundCaptureBuffer8 *iface, DWORD flags)
640 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
642 TRACE("(%p)->(%08lx)\n", iface, flags);
644 EnterCriticalSection(&This->parent->crst);
645 if(!This->playing)
647 DSCBuffer_starttimer(This);
648 This->playing = 1;
649 alcCaptureStart(This->device);
651 This->looping |= !!(flags & DSCBSTART_LOOPING);
652 LeaveCriticalSection(&This->parent->crst);
653 return S_OK;
656 static HRESULT WINAPI DSCBuffer_Stop(IDirectSoundCaptureBuffer8 *iface)
658 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
660 TRACE("(%p)->()\n", iface);
662 EnterCriticalSection(&This->parent->crst);
663 if(This->playing)
665 DWORD i;
666 for(i = 0;i < This->nnotify;++i)
668 if(This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
669 SetEvent(This->notify[i].hEventNotify);
672 This->playing = This->looping = 0;
673 alcCaptureStop(This->device);
675 LeaveCriticalSection(&This->parent->crst);
676 return S_OK;
679 static HRESULT WINAPI DSCBuffer_Unlock(IDirectSoundCaptureBuffer8 *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
681 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
682 DWORD_PTR ofs1, ofs2;
683 DWORD_PTR boundary = (DWORD_PTR)This->buf;
685 TRACE("(%p)->(%p, %lu, %p, %lu)\n", iface, ptr1, len1, ptr2, len2);
687 if(InterlockedExchange(&This->locked, FALSE) == FALSE)
689 WARN("Not locked\n");
690 return DSERR_INVALIDPARAM;
693 /* Make sure offset is between boundary and boundary + bufsize */
694 ofs1 = (DWORD_PTR)ptr1;
695 ofs2 = (DWORD_PTR)ptr2;
696 if(ofs1 < boundary)
697 return DSERR_INVALIDPARAM;
698 if(ofs2 && ofs2 != boundary)
699 return DSERR_INVALIDPARAM;
701 ofs1 -= boundary;
702 ofs2 = 0;
703 if(This->buf_size-ofs1 < len1 || len2 > ofs1)
704 return DSERR_INVALIDPARAM;
706 return DS_OK;
709 static HRESULT WINAPI DSCBuffer_GetObjectInPath(IDirectSoundCaptureBuffer8 *iface, REFGUID guid, DWORD num, REFGUID riid, void **ppv)
711 FIXME("(%p)->(%s, %lu, %s, %p) stub\n", iface, debugstr_guid(guid), num, debugstr_guid(riid), ppv);
712 return E_NOTIMPL;
715 static HRESULT WINAPI DSCBuffer_GetFXStatus(IDirectSoundCaptureBuffer8 *iface, DWORD count, DWORD *status)
717 FIXME("(%p)->(%lu, %p) stub\n", iface, count, status);
718 return E_NOTIMPL;
721 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl =
723 DSCBuffer_QueryInterface,
724 DSCBuffer_AddRef,
725 DSCBuffer_Release,
726 DSCBuffer_GetCaps,
727 DSCBuffer_GetCurrentPosition,
728 DSCBuffer_GetFormat,
729 DSCBuffer_GetStatus,
730 DSCBuffer_Initialize,
731 DSCBuffer_Lock,
732 DSCBuffer_Start,
733 DSCBuffer_Stop,
734 DSCBuffer_Unlock,
735 DSCBuffer_GetObjectInPath,
736 DSCBuffer_GetFXStatus
739 static inline DSCBuffer *impl_from_IDirectSoundNotify(IDirectSoundNotify *iface)
741 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundNotify_iface);
744 static HRESULT WINAPI DSCBufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
746 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
747 return IDirectSoundCaptureBuffer_QueryInterface(&This->IDirectSoundCaptureBuffer8_iface, riid, ppv);
750 static ULONG WINAPI DSCBufferNot_AddRef(IDirectSoundNotify *iface)
752 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
753 LONG ret;
755 InterlockedIncrement(&This->all_ref);
756 ret = InterlockedIncrement(&This->not_ref);
757 TRACE("new refcount %ld\n", ret);
758 return ret;
761 static ULONG WINAPI DSCBufferNot_Release(IDirectSoundNotify *iface)
763 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
764 LONG ret;
766 ret = InterlockedDecrement(&This->not_ref);
767 TRACE("new refcount %ld\n", ret);
768 if(InterlockedDecrement(&This->all_ref) == 0)
769 DSCBuffer_Destroy(This);
771 return ret;
774 static HRESULT WINAPI DSCBufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
776 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
777 DSBPOSITIONNOTIFY *nots;
778 HRESULT hr;
779 DWORD state;
781 TRACE("(%p)->(%lu, %p))\n", iface, count, notifications);
783 EnterCriticalSection(&This->parent->crst);
784 hr = DSERR_INVALIDPARAM;
785 if (count && !notifications)
786 goto out;
788 hr = DSCBuffer_GetStatus(&This->IDirectSoundCaptureBuffer8_iface, &state);
789 if(FAILED(hr)) goto out;
791 hr = DSERR_INVALIDCALL;
792 if (state & DSCBSTATUS_CAPTURING)
793 goto out;
795 if (!count)
797 HeapFree(GetProcessHeap(), 0, This->notify);
798 This->notify = 0;
799 This->nnotify = 0;
801 else
803 DWORD i;
804 hr = DSERR_INVALIDPARAM;
805 for (i = 0; i < count; ++i)
807 if (notifications[i].dwOffset >= This->buf_size
808 && notifications[i].dwOffset != DSCBPN_OFFSET_STOP)
809 goto out;
811 hr = E_OUTOFMEMORY;
812 nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
813 if (!nots)
814 goto out;
815 memcpy(nots, notifications, count*sizeof(*nots));
816 HeapFree(GetProcessHeap(), 0, This->notify);
817 This->notify = nots;
818 This->nnotify = count;
819 hr = S_OK;
822 out:
823 LeaveCriticalSection(&This->parent->crst);
824 return hr;
827 static const IDirectSoundNotifyVtbl DSCNot_Vtbl =
829 DSCBufferNot_QueryInterface,
830 DSCBufferNot_AddRef,
831 DSCBufferNot_Release,
832 DSCBufferNot_SetNotificationPositions
836 static inline DSCImpl *impl_from_IDirectSoundCapture(IDirectSoundCapture *iface)
838 return CONTAINING_RECORD(iface, DSCImpl, IDirectSoundCapture_iface);
841 static inline DSCImpl *impl_from_IUnknown(IUnknown *iface)
843 return CONTAINING_RECORD(iface, DSCImpl, IUnknown_iface);
846 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv);
847 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture *iface);
848 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture *iface);
850 HRESULT DSOUND_CaptureCreate(REFIID riid, void **cap)
852 HRESULT hr;
854 hr = DSOUND_CaptureCreate8(&IID_IDirectSoundCapture, cap);
855 if(SUCCEEDED(hr))
857 DSCImpl *impl = impl_from_IDirectSoundCapture(*cap);
858 impl->is_8 = FALSE;
860 if(!IsEqualIID(riid, &IID_IDirectSoundCapture))
862 hr = DSCImpl_QueryInterface(&impl->IDirectSoundCapture_iface, riid, cap);
863 DSCImpl_Release(&impl->IDirectSoundCapture_iface);
866 return hr;
869 HRESULT DSOUND_CaptureCreate8(REFIID riid, void **cap)
871 DSCImpl *This;
873 *cap = NULL;
875 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
876 if(!This) return DSERR_OUTOFMEMORY;
878 This->IDirectSoundCapture_iface.lpVtbl = &DSC_Vtbl;
879 This->IUnknown_iface.lpVtbl = &DSC_Unknown_Vtbl;
881 This->is_8 = TRUE;
883 InitializeCriticalSection(&This->crst);
885 if(FAILED(DSCImpl_QueryInterface(&This->IDirectSoundCapture_iface, riid, cap)))
887 DSCImpl_Destroy(This);
888 return E_NOINTERFACE;
890 return S_OK;
893 static void DSCImpl_Destroy(DSCImpl *This)
895 EnterCriticalSection(&This->crst);
896 if (This->buf)
897 DSCBuffer_Destroy(This->buf);
898 LeaveCriticalSection(&This->crst);
900 HeapFree(GetProcessHeap(), 0, This->device_name);
902 DeleteCriticalSection(&This->crst);
904 HeapFree(GetProcessHeap(), 0, This);
908 static HRESULT WINAPI DSCImpl_IUnknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppobj)
910 DSCImpl *This = impl_from_IUnknown(iface);
911 return DSCImpl_QueryInterface(&This->IDirectSoundCapture_iface, riid, ppobj);
914 static ULONG WINAPI DSCImpl_IUnknown_AddRef(IUnknown *iface)
916 DSCImpl *This = impl_from_IUnknown(iface);
917 ULONG ref;
919 InterlockedIncrement(&(This->allref));
920 ref = InterlockedIncrement(&(This->unkref));
921 TRACE("(%p) ref was %lu\n", This, ref - 1);
923 return ref;
926 static ULONG WINAPI DSCImpl_IUnknown_Release(IUnknown *iface)
928 DSCImpl *This = impl_from_IUnknown(iface);
929 ULONG ref = InterlockedDecrement(&(This->unkref));
930 TRACE("(%p) ref was %lu\n", This, ref + 1);
931 if(InterlockedDecrement(&(This->allref)) == 0)
932 DSCImpl_Destroy(This);
933 return ref;
936 static const IUnknownVtbl DSC_Unknown_Vtbl = {
937 DSCImpl_IUnknown_QueryInterface,
938 DSCImpl_IUnknown_AddRef,
939 DSCImpl_IUnknown_Release
943 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv)
945 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
947 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
949 *ppv = NULL;
950 if(IsEqualIID(riid, &IID_IDirectSoundCapture))
951 *ppv = &This->IDirectSoundCapture_iface;
952 else if(IsEqualIID(riid, &IID_IUnknown))
953 *ppv = &This->IUnknown_iface;
954 else
955 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
957 if(*ppv)
959 IUnknown_AddRef((IUnknown*)*ppv);
960 return S_OK;
963 return E_NOINTERFACE;
966 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture *iface)
968 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
969 LONG ref;
971 InterlockedIncrement(&(This->allref));
972 ref = InterlockedIncrement(&This->dscref);
973 TRACE("Reference count incremented to %li\n", ref);
975 return ref;
978 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture *iface)
980 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
981 LONG ref;
983 ref = InterlockedDecrement(&This->dscref);
984 TRACE("Reference count decremented to %li\n", ref);
985 if(InterlockedDecrement(&This->allref) == 0)
986 DSCImpl_Destroy(This);
988 return ref;
991 static HRESULT WINAPI DSCImpl_CreateCaptureBuffer(IDirectSoundCapture *iface, const DSCBUFFERDESC *desc, IDirectSoundCaptureBuffer **ppv, IUnknown *unk)
993 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
994 DSCBuffer *buffer;
995 HRESULT hr;
997 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, ppv, unk);
999 if(unk)
1001 WARN("Aggregation isn't supported\n");
1002 return DSERR_NOAGGREGATION;
1005 if(!desc || desc->dwSize < sizeof(DSCBUFFERDESC1))
1007 WARN("Passed invalid description %p %lu\n", desc, desc?desc->dwSize:0);
1008 return DSERR_INVALIDPARAM;
1010 if(!ppv)
1012 WARN("Passed null pointer\n");
1013 return DSERR_INVALIDPARAM;
1015 *ppv = NULL;
1017 EnterCriticalSection(&This->crst);
1018 if(!This->device_name)
1020 hr = DSERR_UNINITIALIZED;
1021 WARN("Not initialized\n");
1022 goto out;
1024 if(This->buf)
1026 hr = DSERR_ALLOCATED;
1027 WARN("Capture buffer already allocated\n");
1028 goto out;
1031 hr = DSCBuffer_Create(&buffer, This);
1032 if(SUCCEEDED(hr))
1034 hr = DSCBuffer_Initialize(&buffer->IDirectSoundCaptureBuffer8_iface, iface, desc);
1035 if(FAILED(hr))
1036 DSCBuffer_Release(&buffer->IDirectSoundCaptureBuffer8_iface);
1037 else
1039 This->buf = buffer;
1040 *ppv = (IDirectSoundCaptureBuffer*)&buffer->IDirectSoundCaptureBuffer8_iface;
1044 out:
1045 LeaveCriticalSection(&This->crst);
1046 return hr;
1049 static HRESULT WINAPI DSCImpl_GetCaps(IDirectSoundCapture *iface, DSCCAPS *caps)
1051 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
1053 TRACE("(%p)->(%p)\n", iface, caps);
1055 if(!This->device_name)
1057 WARN("Not initialized\n");
1058 return DSERR_UNINITIALIZED;
1061 if(!caps) {
1062 WARN("Caps is null\n");
1063 return DSERR_INVALIDPARAM;
1065 if(caps->dwSize < sizeof(*caps)) {
1066 WARN("Invalid size %ld\n", caps->dwSize);
1067 return DSERR_INVALIDPARAM;
1070 caps->dwFlags = 0;
1071 /* Support all WAVE_FORMAT formats specified in mmsystem.h */
1072 caps->dwFormats = 0x000fffff;
1073 caps->dwChannels = 2;
1075 return DS_OK;
1078 static HRESULT WINAPI DSCImpl_Initialize(IDirectSoundCapture *iface, const GUID *devguid)
1080 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
1081 OLECHAR *guid_str = NULL;
1082 HRESULT hr;
1083 GUID guid;
1084 int len;
1086 TRACE("(%p)->(%p)\n", iface, devguid);
1088 if(!openal_loaded)
1090 ERR("OpenAL not loaded!\n");
1091 return DSERR_NODRIVER;
1094 if(This->device_name)
1096 WARN("Already initialized\n");
1097 return DSERR_ALREADYINITIALIZED;
1100 if(!devguid || IsEqualGUID(devguid, &GUID_NULL))
1101 devguid = &DSDEVID_DefaultCapture;
1102 else if(IsEqualGUID(devguid, &DSDEVID_DefaultPlayback) ||
1103 IsEqualGUID(devguid, &DSDEVID_DefaultVoicePlayback))
1104 return DSERR_NODRIVER;
1106 hr = GetDeviceID(devguid, &guid);
1107 if(FAILED(hr)) return hr;
1109 devguid = &guid;
1111 hr = StringFromCLSID(devguid, &guid_str);
1112 if(FAILED(hr))
1114 ERR("Failed to convert GUID to string\n");
1115 return hr;
1118 EnterCriticalSection(&This->crst);
1119 EnterCriticalSection(&openal_crst);
1121 hr = E_OUTOFMEMORY;
1122 len = WideCharToMultiByte(CP_UTF8, 0, guid_str, -1, NULL, 0, NULL, NULL);
1123 if(len <= 0)
1125 ERR("Failed to convert GUID to string\n");
1126 goto out;
1129 This->device_name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len+1);
1130 if(!This->device_name)
1132 WARN("Out of memory to duplicate string \"%ls\"\n", guid_str);
1133 goto out;
1135 WideCharToMultiByte(CP_UTF8, 0, guid_str, -1, This->device_name, len, NULL, NULL);
1137 hr = S_OK;
1138 out:
1139 LeaveCriticalSection(&openal_crst);
1140 LeaveCriticalSection(&This->crst);
1141 if(guid_str)
1142 CoTaskMemFree(guid_str);
1143 guid_str = NULL;
1144 return hr;
1147 static const IDirectSoundCaptureVtbl DSC_Vtbl =
1149 DSCImpl_QueryInterface,
1150 DSCImpl_AddRef,
1151 DSCImpl_Release,
1152 DSCImpl_CreateCaptureBuffer,
1153 DSCImpl_GetCaps,
1154 DSCImpl_Initialize