Use global function pointers for extension functions
[dsound-openal.git] / capture.c
blobaaeb2fc7571d04f145016746575ff9fb95d27b5a
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
32 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
35 typedef struct DSCImpl DSCImpl;
36 typedef struct DSCBuffer DSCBuffer;
38 struct DSCImpl {
39 /* IDirectSoundCapture and IDirectSoundCapture8 are aliases */
40 IDirectSoundCapture IDirectSoundCapture_iface;
41 IUnknown IUnknown_iface;
42 LONG allref, dscref, unkref;
44 BOOL is_8;
46 ALCchar *device;
47 DSCBuffer *buf;
49 CRITICAL_SECTION crst;
52 struct DSCBuffer {
53 IDirectSoundCaptureBuffer8 IDirectSoundCaptureBuffer8_iface;
54 IDirectSoundNotify IDirectSoundNotify_iface;
55 LONG ref, not_ref;
56 LONG all_ref;
58 DSCImpl *parent;
59 ALCdevice *dev;
61 DWORD buf_size;
62 BYTE *buf;
64 LONG locked;
66 WAVEFORMATEXTENSIBLE format;
68 DSBPOSITIONNOTIFY *notify;
69 DWORD nnotify;
71 HANDLE thread_hdl;
72 DWORD thread_id;
74 HANDLE queue_timer;
75 HANDLE timer_evt;
76 volatile LONG quit_now;
78 DWORD pos;
79 BOOL playing, looping;
82 static const IDirectSoundCaptureVtbl DSC_Vtbl;
83 static const IUnknownVtbl DSC_Unknown_Vtbl;
84 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl;
85 static const IDirectSoundNotifyVtbl DSCNot_Vtbl;
87 static void DSCImpl_Destroy(DSCImpl *This);
89 static void trigger_notifies(DSCBuffer *buf, DWORD lastpos, DWORD curpos)
91 DWORD i;
93 if(lastpos == curpos)
94 return;
96 for(i = 0;i < buf->nnotify;++i)
98 DSBPOSITIONNOTIFY *not = &buf->notify[i];
99 HANDLE event = not->hEventNotify;
100 DWORD ofs = not->dwOffset;
102 if (ofs == DSCBPN_OFFSET_STOP)
103 continue;
105 /* Wraparound case */
106 if(curpos < lastpos)
108 if(ofs < curpos || ofs >= lastpos)
110 TRACE("Triggering notification %lu (%lu) from buffer %p\n", i, ofs, buf);
111 SetEvent(event);
113 continue;
116 /* Normal case */
117 if(ofs >= lastpos && ofs < curpos)
119 TRACE("Triggering notification %lu (%lu) from buffer %p\n", i, ofs, buf);
120 SetEvent(event);
125 static DWORD CALLBACK DSCBuffer_thread(void *param)
127 DSCBuffer *This = param;
128 CRITICAL_SECTION *crst = &This->parent->crst;
130 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
132 while(WaitForSingleObject(This->timer_evt, INFINITE) == WAIT_OBJECT_0 && !This->quit_now)
134 ALCint avail = 0;
136 alcGetIntegerv(This->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
137 if(avail == 0 || !This->playing) continue;
139 EnterCriticalSection(crst);
140 if(!This->playing)
142 LeaveCriticalSection(crst);
143 continue;
145 more_samples:
146 avail *= This->format.Format.nBlockAlign;
147 if((DWORD)avail > This->buf_size - This->pos)
148 avail = This->buf_size - This->pos;
150 alcCaptureSamples(This->dev, This->buf+This->pos, avail/This->format.Format.nBlockAlign);
151 trigger_notifies(This, This->pos, This->pos + avail);
152 This->pos += avail;
154 if(This->pos == This->buf_size)
156 This->pos = 0;
157 if(!This->looping)
159 DWORD i;
160 for(i = 0;i < This->nnotify;++i)
162 if(This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
163 SetEvent(This->notify[i].hEventNotify);
166 This->playing = 0;
167 alcCaptureStop(This->dev);
169 else
171 alcGetIntegerv(This->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
172 if(avail) goto more_samples;
176 LeaveCriticalSection(crst);
179 return 0;
183 static void CALLBACK DSCBuffer_timer(void *arg, BOOLEAN unused)
185 (void)unused;
186 SetEvent((HANDLE)arg);
189 static void DSCBuffer_starttimer(DSCBuffer *This)
191 ALint refresh = FAKE_REFRESH_COUNT;
192 DWORD triggertime;
194 if(This->queue_timer)
195 return;
197 triggertime = 1000 / refresh * 2 / 3;
198 TRACE("Calling timer every %lu ms for %i refreshes per second\n", triggertime, refresh);
200 CreateTimerQueueTimer(&This->queue_timer, NULL, DSCBuffer_timer, This->timer_evt,
201 triggertime, triggertime, WT_EXECUTEINTIMERTHREAD);
204 static HRESULT DSCBuffer_Create(DSCBuffer **buf, DSCImpl *parent)
206 DSCBuffer *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
207 if(!This) return E_OUTOFMEMORY;
209 This->IDirectSoundCaptureBuffer8_iface.lpVtbl = &DSCBuffer_Vtbl;
210 This->IDirectSoundNotify_iface.lpVtbl = &DSCNot_Vtbl;
212 This->all_ref = This->ref = 1;
214 This->parent = parent;
216 This->quit_now = FALSE;
217 This->timer_evt = CreateEventA(NULL, FALSE, FALSE, NULL);
218 if(!This->timer_evt) goto fail;
220 This->queue_timer = NULL;
222 This->thread_hdl = CreateThread(NULL, 0, DSCBuffer_thread, This, 0, &This->thread_id);
223 if(!This->thread_hdl) goto fail;
225 *buf = This;
226 return S_OK;
228 fail:
229 if(This->timer_evt)
230 CloseHandle(This->timer_evt);
231 This->timer_evt = NULL;
233 HeapFree(GetProcessHeap(), 0, This);
234 return E_FAIL;
237 static void DSCBuffer_Destroy(DSCBuffer *This)
239 if(This->queue_timer)
240 DeleteTimerQueueTimer(NULL, This->queue_timer, INVALID_HANDLE_VALUE);
241 This->queue_timer = NULL;
243 if(This->thread_hdl)
245 InterlockedExchange(&This->quit_now, TRUE);
246 SetEvent(This->timer_evt);
248 if(WaitForSingleObject(This->thread_hdl, 1000) != WAIT_OBJECT_0)
249 ERR("Thread wait timed out\n");
251 CloseHandle(This->thread_hdl);
252 This->thread_hdl = NULL;
255 if(This->timer_evt)
256 CloseHandle(This->timer_evt);
257 This->timer_evt = NULL;
259 if(This->dev)
261 if(This->playing)
262 alcCaptureStop(This->dev);
263 alcCaptureCloseDevice(This->dev);
265 This->parent->buf = NULL;
267 HeapFree(GetProcessHeap(), 0, This->notify);
268 HeapFree(GetProcessHeap(), 0, This->buf);
269 HeapFree(GetProcessHeap(), 0, This);
272 static inline DSCBuffer *impl_from_IDirectSoundCaptureBuffer8(IDirectSoundCaptureBuffer8 *iface)
274 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundCaptureBuffer8_iface);
277 static HRESULT WINAPI DSCBuffer_QueryInterface(IDirectSoundCaptureBuffer8 *iface, REFIID riid, void **ppv)
279 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
281 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
283 if(!ppv)
284 return E_POINTER;
285 *ppv = NULL;
287 if(IsEqualIID(riid, &IID_IUnknown) ||
288 IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer))
289 *ppv = &This->IDirectSoundCaptureBuffer8_iface;
290 else if(IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer8))
292 if(This->parent->is_8)
293 *ppv = &This->IDirectSoundCaptureBuffer8_iface;
295 else if(IsEqualIID(riid, &IID_IDirectSoundNotify))
296 *ppv = &This->IDirectSoundNotify_iface;
297 else
298 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
300 if(!*ppv)
301 return E_NOINTERFACE;
302 IUnknown_AddRef((IUnknown*)*ppv);
303 return S_OK;
306 static ULONG WINAPI DSCBuffer_AddRef(IDirectSoundCaptureBuffer8 *iface)
308 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
309 LONG ref;
311 InterlockedIncrement(&This->all_ref);
312 ref = InterlockedIncrement(&This->ref);
313 TRACE("Reference count incremented to %li\n", ref);
315 return ref;
318 static ULONG WINAPI DSCBuffer_Release(IDirectSoundCaptureBuffer8 *iface)
320 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
321 LONG ref;
323 ref = InterlockedDecrement(&This->ref);
324 TRACE("Reference count decremented to %li\n", ref);
325 if(InterlockedDecrement(&This->all_ref) == 0)
326 DSCBuffer_Destroy(This);
328 return ref;
331 static HRESULT WINAPI DSCBuffer_GetCaps(IDirectSoundCaptureBuffer8 *iface, DSCBCAPS *caps)
333 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
335 if (!caps || caps->dwSize < sizeof(*caps))
336 return DSERR_INVALIDPARAM;
337 caps->dwSize = sizeof(*caps);
338 caps->dwFlags = 0;
339 caps->dwBufferBytes = This->buf_size;
340 return S_OK;
343 static HRESULT WINAPI DSCBuffer_GetCurrentPosition(IDirectSoundCaptureBuffer8 *iface, DWORD *cappos, DWORD *readpos)
345 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
346 DWORD pos1, pos2;
348 EnterCriticalSection(&This->parent->crst);
349 pos1 = This->pos;
350 if(This->playing)
352 pos2 = This->format.Format.nSamplesPerSec / 100;
353 pos2 *= This->format.Format.nBlockAlign;
354 pos2 += pos1;
355 if (!This->looping && pos2 >= This->buf_size)
356 pos2 = 0;
357 else
358 pos2 %= This->buf_size;
360 else
361 pos2 = pos1;
362 LeaveCriticalSection(&This->parent->crst);
364 if(cappos) *cappos = pos1;
365 if(readpos) *readpos = pos2;
367 return S_OK;
370 static HRESULT WINAPI DSCBuffer_GetFormat(IDirectSoundCaptureBuffer8 *iface, WAVEFORMATEX *wfx, DWORD allocated, DWORD *written)
372 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
373 HRESULT hr = DS_OK;
374 UINT size;
376 TRACE("(%p)->(%p, %lu, %p)\n", iface, wfx, allocated, written);
378 if(!wfx && !written)
380 WARN("Cannot report format or format size\n");
381 return DSERR_INVALIDPARAM;
384 size = sizeof(This->format.Format) + This->format.Format.cbSize;
385 if(wfx)
387 if(allocated < size)
388 hr = DSERR_INVALIDPARAM;
389 else
390 memcpy(wfx, &This->format.Format, size);
392 if(written)
393 *written = size;
395 return hr;
398 static HRESULT WINAPI DSCBuffer_GetStatus(IDirectSoundCaptureBuffer8 *iface, DWORD *status)
400 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
402 TRACE("(%p)->(%p)\n", iface, status);
404 if (!status)
405 return DSERR_INVALIDPARAM;
406 EnterCriticalSection(&This->parent->crst);
407 *status = 0;
408 if (This->playing)
410 *status |= DSCBSTATUS_CAPTURING;
411 if (This->looping)
412 *status |= DSCBSTATUS_LOOPING;
414 LeaveCriticalSection(&This->parent->crst);
416 return S_OK;
419 static HRESULT WINAPI DSCBuffer_Initialize(IDirectSoundCaptureBuffer8 *iface, IDirectSoundCapture *parent, const DSCBUFFERDESC *desc)
421 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
422 WAVEFORMATEX *format;
423 ALenum buf_format = -1;
425 TRACE("(%p)->(%p, %p)\n", iface, parent, desc);
427 if(This->dev)
428 return DSERR_ALREADYINITIALIZED;
430 if (!desc->lpwfxFormat)
431 return DSERR_INVALIDPARAM;
433 format = desc->lpwfxFormat;
434 if(format->nChannels <= 0 || format->nChannels > 2)
436 WARN("Invalid Channels %d\n", format->nChannels);
437 return DSERR_INVALIDPARAM;
439 if(format->wBitsPerSample <= 0 || (format->wBitsPerSample%8) != 0)
441 WARN("Invalid BitsPerSample %d\n", format->wBitsPerSample);
442 return DSERR_INVALIDPARAM;
444 if(format->nBlockAlign != format->nChannels*format->wBitsPerSample/8)
446 WARN("Invalid BlockAlign %d (expected %u = %u*%u/8)\n",
447 format->nBlockAlign, format->nChannels*format->wBitsPerSample/8,
448 format->nChannels, format->wBitsPerSample);
449 return DSERR_INVALIDPARAM;
451 if(format->nSamplesPerSec < DSBFREQUENCY_MIN || format->nSamplesPerSec > DSBFREQUENCY_MAX)
453 WARN("Invalid sample rate %lu\n", format->nSamplesPerSec);
454 return DSERR_INVALIDPARAM;
456 if(format->nAvgBytesPerSec != format->nSamplesPerSec*format->nBlockAlign)
458 WARN("Invalid AvgBytesPerSec %lu (expected %lu = %lu*%u)\n",
459 format->nAvgBytesPerSec, format->nSamplesPerSec*format->nBlockAlign,
460 format->nSamplesPerSec, format->nBlockAlign);
461 return DSERR_INVALIDPARAM;
464 if(format->wFormatTag == WAVE_FORMAT_PCM)
466 if(format->nChannels == 1)
468 switch(format->wBitsPerSample)
470 case 8: buf_format = AL_FORMAT_MONO8; break;
471 case 16: buf_format = AL_FORMAT_MONO16; break;
472 default:
473 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
474 return DSERR_BADFORMAT;
477 else if(format->nChannels == 2)
479 switch(format->wBitsPerSample)
481 case 8: buf_format = AL_FORMAT_STEREO8; break;
482 case 16: buf_format = AL_FORMAT_STEREO16; break;
483 default:
484 WARN("Unsupported bpp %u\n", format->wBitsPerSample);
485 return DSERR_BADFORMAT;
488 else
489 WARN("Unsupported channels: %d\n", format->nChannels);
491 This->format.Format = *format;
492 This->format.Format.cbSize = 0;
494 else if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
496 WAVEFORMATEXTENSIBLE *wfe;
498 if(format->cbSize < sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX))
499 return DSERR_INVALIDPARAM;
500 else if(format->cbSize > sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) &&
501 format->cbSize != sizeof(WAVEFORMATEXTENSIBLE))
502 return DSERR_CONTROLUNAVAIL;
504 wfe = CONTAINING_RECORD(format, WAVEFORMATEXTENSIBLE, Format);
505 if(!IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
506 return DSERR_BADFORMAT;
507 if(wfe->Samples.wValidBitsPerSample &&
508 wfe->Samples.wValidBitsPerSample != wfe->Format.wBitsPerSample)
509 return DSERR_BADFORMAT;
511 if(wfe->Format.nChannels == 1 && wfe->dwChannelMask == SPEAKER_FRONT_CENTER)
513 switch(wfe->Format.wBitsPerSample)
515 case 8: buf_format = AL_FORMAT_MONO8; break;
516 case 16: buf_format = AL_FORMAT_MONO16; break;
517 default:
518 WARN("Unsupported bpp %u\n", wfe->Format.wBitsPerSample);
519 return DSERR_BADFORMAT;
522 else if(wfe->Format.nChannels == 2 && wfe->dwChannelMask == (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT))
524 switch(wfe->Format.wBitsPerSample)
526 case 8: buf_format = AL_FORMAT_STEREO8; break;
527 case 16: buf_format = AL_FORMAT_STEREO16; break;
528 default:
529 WARN("Unsupported bpp %u\n", wfe->Format.wBitsPerSample);
530 return DSERR_BADFORMAT;
533 else
534 WARN("Unsupported channels: %d -- 0x%08lu\n", wfe->Format.nChannels, wfe->dwChannelMask);
536 This->format = *wfe;
537 This->format.Format.cbSize = sizeof(This->format) - sizeof(This->format.Format);
538 This->format.Samples.wValidBitsPerSample = This->format.Format.wBitsPerSample;
540 else
541 WARN("Unhandled formattag %x\n", format->wFormatTag);
543 if(buf_format <= 0)
545 WARN("Could not get OpenAL format\n");
546 return DSERR_INVALIDPARAM;
549 if(desc->dwBufferBytes < This->format.Format.nBlockAlign ||
550 (desc->dwBufferBytes%This->format.Format.nBlockAlign) != 0)
552 WARN("Invalid BufferBytes (%lu %% %d)\n", desc->dwBufferBytes,
553 This->format.Format.nBlockAlign);
554 return DSERR_INVALIDPARAM;
558 This->buf_size = desc->dwBufferBytes;
559 This->buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->buf_size);
560 if(!This->buf)
562 WARN("Out of memory\n");
563 return DSERR_INVALIDPARAM;
566 This->dev = alcCaptureOpenDevice(This->parent->device, This->format.Format.nSamplesPerSec, buf_format, This->format.Format.nSamplesPerSec / FAKE_REFRESH_COUNT * 2);
567 if(!This->dev)
569 ERR("Couldn't open device %s 0x%x@%lu, reason: %04x\n", This->parent->device, buf_format, This->format.Format.nSamplesPerSec, alcGetError(NULL));
570 return DSERR_INVALIDPARAM;
573 return S_OK;
576 static HRESULT WINAPI DSCBuffer_Lock(IDirectSoundCaptureBuffer8 *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
578 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
579 DWORD remain;
581 TRACE("(%p)->(%lu, %lu, %p, %p, %p, %p, %#lx)\n", iface, ofs, bytes, ptr1, len1, ptr2, len2, flags);
583 if(!ptr1 || !len1)
585 WARN("Invalid pointer/len %p %p\n", ptr1, len1);
586 return DSERR_INVALIDPARAM;
589 *ptr1 = NULL;
590 *len1 = 0;
591 if(ptr2) *ptr2 = NULL;
592 if(len2) *len2 = 0;
594 if(ofs >= This->buf_size)
596 WARN("Invalid ofs %lu\n", ofs);
597 return DSERR_INVALIDPARAM;
600 if((flags&DSCBLOCK_ENTIREBUFFER))
601 bytes = This->buf_size;
602 else if(bytes > This->buf_size)
604 WARN("Invalid size %lu\n", bytes);
605 return DSERR_INVALIDPARAM;
608 if(InterlockedExchange(&This->locked, TRUE) == TRUE)
610 WARN("Already locked\n");
611 return DSERR_INVALIDPARAM;
614 if(ofs + bytes >= This->buf_size)
616 *len1 = This->buf_size - ofs;
617 remain = bytes - *len1;
619 else
621 *len1 = bytes;
622 remain = 0;
624 *ptr1 = This->buf + ofs;
626 if(ptr2 && len2 && remain)
628 *ptr2 = This->buf;
629 *len2 = remain;
632 return DS_OK;
635 static HRESULT WINAPI DSCBuffer_Start(IDirectSoundCaptureBuffer8 *iface, DWORD flags)
637 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
639 TRACE("(%p)->(%08lx)\n", iface, flags);
641 EnterCriticalSection(&This->parent->crst);
642 if(!This->playing)
644 DSCBuffer_starttimer(This);
645 This->playing = 1;
646 alcCaptureStart(This->dev);
648 This->looping |= !!(flags & DSCBSTART_LOOPING);
649 LeaveCriticalSection(&This->parent->crst);
650 return S_OK;
653 static HRESULT WINAPI DSCBuffer_Stop(IDirectSoundCaptureBuffer8 *iface)
655 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
657 TRACE("(%p)->()\n", iface);
659 EnterCriticalSection(&This->parent->crst);
660 if(This->playing)
662 DWORD i;
663 for(i = 0;i < This->nnotify;++i)
665 if(This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
666 SetEvent(This->notify[i].hEventNotify);
669 This->playing = This->looping = 0;
670 alcCaptureStop(This->dev);
672 LeaveCriticalSection(&This->parent->crst);
673 return S_OK;
676 static HRESULT WINAPI DSCBuffer_Unlock(IDirectSoundCaptureBuffer8 *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
678 DSCBuffer *This = impl_from_IDirectSoundCaptureBuffer8(iface);
679 DWORD_PTR ofs1, ofs2;
680 DWORD_PTR boundary = (DWORD_PTR)This->buf;
682 TRACE("(%p)->(%p, %lu, %p, %lu)\n", iface, ptr1, len1, ptr2, len2);
684 if(InterlockedExchange(&This->locked, FALSE) == FALSE)
686 WARN("Not locked\n");
687 return DSERR_INVALIDPARAM;
690 /* Make sure offset is between boundary and boundary + bufsize */
691 ofs1 = (DWORD_PTR)ptr1;
692 ofs2 = (DWORD_PTR)ptr2;
693 if(ofs1 < boundary)
694 return DSERR_INVALIDPARAM;
695 if(ofs2 && ofs2 != boundary)
696 return DSERR_INVALIDPARAM;
698 ofs1 -= boundary;
699 ofs2 = 0;
700 if(This->buf_size-ofs1 < len1 || len2 > ofs1)
701 return DSERR_INVALIDPARAM;
703 return DS_OK;
706 static HRESULT WINAPI DSCBuffer_GetObjectInPath(IDirectSoundCaptureBuffer8 *iface, REFGUID guid, DWORD num, REFGUID riid, void **ppv)
708 FIXME("(%p)->(%s, %lu, %s, %p) stub\n", iface, debugstr_guid(guid), num, debugstr_guid(riid), ppv);
709 return E_NOTIMPL;
712 static HRESULT WINAPI DSCBuffer_GetFXStatus(IDirectSoundCaptureBuffer8 *iface, DWORD count, DWORD *status)
714 FIXME("(%p)->(%lu, %p) stub\n", iface, count, status);
715 return E_NOTIMPL;
718 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl =
720 DSCBuffer_QueryInterface,
721 DSCBuffer_AddRef,
722 DSCBuffer_Release,
723 DSCBuffer_GetCaps,
724 DSCBuffer_GetCurrentPosition,
725 DSCBuffer_GetFormat,
726 DSCBuffer_GetStatus,
727 DSCBuffer_Initialize,
728 DSCBuffer_Lock,
729 DSCBuffer_Start,
730 DSCBuffer_Stop,
731 DSCBuffer_Unlock,
732 DSCBuffer_GetObjectInPath,
733 DSCBuffer_GetFXStatus
736 static inline DSCBuffer *impl_from_IDirectSoundNotify(IDirectSoundNotify *iface)
738 return CONTAINING_RECORD(iface, DSCBuffer, IDirectSoundNotify_iface);
741 static HRESULT WINAPI DSCBufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
743 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
744 return IDirectSoundCaptureBuffer_QueryInterface((IDirectSoundCaptureBuffer*)This, riid, ppv);
747 static ULONG WINAPI DSCBufferNot_AddRef(IDirectSoundNotify *iface)
749 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
750 LONG ret;
752 InterlockedIncrement(&This->all_ref);
753 ret = InterlockedIncrement(&This->not_ref);
754 TRACE("new refcount %ld\n", ret);
755 return ret;
758 static ULONG WINAPI DSCBufferNot_Release(IDirectSoundNotify *iface)
760 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
761 LONG ret;
763 ret = InterlockedDecrement(&This->not_ref);
764 TRACE("new refcount %ld\n", ret);
765 if(InterlockedDecrement(&This->all_ref) == 0)
766 DSCBuffer_Destroy(This);
768 return ret;
771 static HRESULT WINAPI DSCBufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
773 DSCBuffer *This = impl_from_IDirectSoundNotify(iface);
774 DSBPOSITIONNOTIFY *nots;
775 HRESULT hr;
776 DWORD state;
778 TRACE("(%p)->(%lu, %p))\n", iface, count, notifications);
780 EnterCriticalSection(&This->parent->crst);
781 hr = DSERR_INVALIDPARAM;
782 if (count && !notifications)
783 goto out;
785 hr = DSCBuffer_GetStatus(&This->IDirectSoundCaptureBuffer8_iface, &state);
786 if (FAILED(hr))
787 goto out;
789 hr = DSERR_INVALIDCALL;
790 if (state & DSCBSTATUS_CAPTURING)
791 goto out;
793 if (!count)
795 HeapFree(GetProcessHeap(), 0, This->notify);
796 This->notify = 0;
797 This->nnotify = 0;
799 else
801 DWORD i;
802 hr = DSERR_INVALIDPARAM;
803 for (i = 0; i < count; ++i)
805 if (notifications[i].dwOffset >= This->buf_size
806 && notifications[i].dwOffset != DSCBPN_OFFSET_STOP)
807 goto out;
809 hr = E_OUTOFMEMORY;
810 nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
811 if (!nots)
812 goto out;
813 memcpy(nots, notifications, count*sizeof(*nots));
814 HeapFree(GetProcessHeap(), 0, This->notify);
815 This->notify = nots;
816 This->nnotify = count;
817 hr = S_OK;
820 out:
821 LeaveCriticalSection(&This->parent->crst);
822 return hr;
825 static const IDirectSoundNotifyVtbl DSCNot_Vtbl =
827 DSCBufferNot_QueryInterface,
828 DSCBufferNot_AddRef,
829 DSCBufferNot_Release,
830 DSCBufferNot_SetNotificationPositions
834 static inline DSCImpl *impl_from_IDirectSoundCapture(IDirectSoundCapture *iface)
836 return CONTAINING_RECORD(iface, DSCImpl, IDirectSoundCapture_iface);
839 static inline DSCImpl *impl_from_IUnknown(IUnknown *iface)
841 return CONTAINING_RECORD(iface, DSCImpl, IUnknown_iface);
844 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv);
845 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture *iface);
846 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture *iface);
848 HRESULT DSOUND_CaptureCreate(REFIID riid, void **cap)
850 HRESULT hr;
852 hr = DSOUND_CaptureCreate8(&IID_IDirectSoundCapture, cap);
853 if(SUCCEEDED(hr))
855 DSCImpl *impl = impl_from_IDirectSoundCapture(*cap);
856 impl->is_8 = FALSE;
858 if(!IsEqualIID(riid, &IID_IDirectSoundCapture))
860 hr = DSCImpl_QueryInterface(&impl->IDirectSoundCapture_iface, riid, cap);
861 DSCImpl_Release(&impl->IDirectSoundCapture_iface);
864 return hr;
867 HRESULT DSOUND_CaptureCreate8(REFIID riid, void **cap)
869 DSCImpl *This;
871 *cap = NULL;
873 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
874 if(!This) return DSERR_OUTOFMEMORY;
876 This->IDirectSoundCapture_iface.lpVtbl = &DSC_Vtbl;
877 This->IUnknown_iface.lpVtbl = &DSC_Unknown_Vtbl;
879 This->is_8 = TRUE;
881 InitializeCriticalSection(&This->crst);
883 if(FAILED(DSCImpl_QueryInterface(&This->IDirectSoundCapture_iface, riid, cap)))
885 DSCImpl_Destroy(This);
886 return E_NOINTERFACE;
888 return S_OK;
891 static void DSCImpl_Destroy(DSCImpl *This)
893 EnterCriticalSection(&This->crst);
894 if (This->buf)
895 DSCBuffer_Destroy(This->buf);
896 LeaveCriticalSection(&This->crst);
898 HeapFree(GetProcessHeap(), 0, This->device);
900 DeleteCriticalSection(&This->crst);
902 HeapFree(GetProcessHeap(), 0, This);
906 static HRESULT WINAPI DSCImpl_IUnknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppobj)
908 DSCImpl *This = impl_from_IUnknown(iface);
909 return DSCImpl_QueryInterface(&This->IDirectSoundCapture_iface, riid, ppobj);
912 static ULONG WINAPI DSCImpl_IUnknown_AddRef(IUnknown *iface)
914 DSCImpl *This = impl_from_IUnknown(iface);
915 ULONG ref;
917 InterlockedIncrement(&(This->allref));
918 ref = InterlockedIncrement(&(This->unkref));
919 TRACE("(%p) ref was %lu\n", This, ref - 1);
921 return ref;
924 static ULONG WINAPI DSCImpl_IUnknown_Release(IUnknown *iface)
926 DSCImpl *This = impl_from_IUnknown(iface);
927 ULONG ref = InterlockedDecrement(&(This->unkref));
928 TRACE("(%p) ref was %lu\n", This, ref + 1);
929 if(InterlockedDecrement(&(This->allref)) == 0)
930 DSCImpl_Destroy(This);
931 return ref;
934 static const IUnknownVtbl DSC_Unknown_Vtbl = {
935 DSCImpl_IUnknown_QueryInterface,
936 DSCImpl_IUnknown_AddRef,
937 DSCImpl_IUnknown_Release
941 static HRESULT WINAPI DSCImpl_QueryInterface(IDirectSoundCapture *iface, REFIID riid, void **ppv)
943 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
945 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
947 *ppv = NULL;
948 if(IsEqualIID(riid, &IID_IDirectSoundCapture))
949 *ppv = &This->IDirectSoundCapture_iface;
950 else if(IsEqualIID(riid, &IID_IUnknown))
951 *ppv = &This->IUnknown_iface;
952 else
953 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
955 if(*ppv)
957 IUnknown_AddRef((IUnknown*)*ppv);
958 return S_OK;
961 return E_NOINTERFACE;
964 static ULONG WINAPI DSCImpl_AddRef(IDirectSoundCapture *iface)
966 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
967 LONG ref;
969 InterlockedIncrement(&(This->allref));
970 ref = InterlockedIncrement(&This->dscref);
971 TRACE("Reference count incremented to %li\n", ref);
973 return ref;
976 static ULONG WINAPI DSCImpl_Release(IDirectSoundCapture *iface)
978 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
979 LONG ref;
981 ref = InterlockedDecrement(&This->dscref);
982 TRACE("Reference count decremented to %li\n", ref);
983 if(InterlockedDecrement(&This->allref) == 0)
984 DSCImpl_Destroy(This);
986 return ref;
989 static HRESULT WINAPI DSCImpl_CreateCaptureBuffer(IDirectSoundCapture *iface, const DSCBUFFERDESC *desc, IDirectSoundCaptureBuffer **ppv, IUnknown *unk)
991 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
992 HRESULT hr;
994 TRACE("(%p)->(%p, %p, %p)\n", iface, desc, ppv, unk);
996 if(unk)
998 WARN("Aggregation isn't supported\n");
999 return DSERR_NOAGGREGATION;
1002 if(!desc || desc->dwSize < sizeof(DSCBUFFERDESC1))
1004 WARN("Passed invalid description %p %lu\n", desc, desc?desc->dwSize:0);
1005 return DSERR_INVALIDPARAM;
1007 if(!ppv)
1009 WARN("Passed null pointer\n");
1010 return DSERR_INVALIDPARAM;
1012 *ppv = NULL;
1014 EnterCriticalSection(&This->crst);
1015 if(!This->device)
1017 hr = DSERR_UNINITIALIZED;
1018 WARN("Not initialized\n");
1019 goto out;
1021 if(This->buf)
1023 hr = DSERR_ALLOCATED;
1024 WARN("Capture buffer already allocated\n");
1025 goto out;
1028 hr = DSCBuffer_Create(&This->buf, This);
1029 if(SUCCEEDED(hr))
1031 hr = DSCBuffer_Initialize(&This->buf->IDirectSoundCaptureBuffer8_iface, iface, desc);
1032 if(SUCCEEDED(hr))
1033 *ppv = (IDirectSoundCaptureBuffer*)&This->buf->IDirectSoundCaptureBuffer8_iface;
1034 else
1036 DSCBuffer_Destroy(This->buf);
1037 This->buf = NULL;
1041 out:
1042 LeaveCriticalSection(&This->crst);
1043 return hr;
1046 static HRESULT WINAPI DSCImpl_GetCaps(IDirectSoundCapture *iface, DSCCAPS *caps)
1048 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
1050 TRACE("(%p)->(%p)\n", iface, caps);
1052 if(!This->device) {
1053 WARN("Not initialized\n");
1054 return DSERR_UNINITIALIZED;
1057 if(!caps) {
1058 WARN("Caps is null\n");
1059 return DSERR_INVALIDPARAM;
1061 if(caps->dwSize < sizeof(*caps)) {
1062 WARN("Invalid size %ld\n", caps->dwSize);
1063 return DSERR_INVALIDPARAM;
1066 caps->dwFlags = 0;
1067 /* Support all WAVE_FORMAT formats specified in mmsystem.h */
1068 caps->dwFormats = 0x000fffff;
1069 caps->dwChannels = 2;
1071 return DS_OK;
1074 static HRESULT WINAPI DSCImpl_Initialize(IDirectSoundCapture *iface, const GUID *devguid)
1076 DSCImpl *This = impl_from_IDirectSoundCapture(iface);
1077 OLECHAR *guid_str = NULL;
1078 HRESULT hr;
1079 GUID guid;
1080 int len;
1082 TRACE("(%p)->(%p)\n", iface, devguid);
1084 if(!openal_loaded)
1086 ERR("OpenAL not loaded!\n");
1087 return DSERR_NODRIVER;
1090 if(This->device)
1092 WARN("Already initialized\n");
1093 return DSERR_ALREADYINITIALIZED;
1096 if(!devguid || IsEqualGUID(devguid, &GUID_NULL))
1097 devguid = &DSDEVID_DefaultCapture;
1098 else if(IsEqualGUID(devguid, &DSDEVID_DefaultPlayback) ||
1099 IsEqualGUID(devguid, &DSDEVID_DefaultVoicePlayback))
1100 return DSERR_NODRIVER;
1102 hr = GetDeviceID(devguid, &guid);
1103 if(FAILED(hr)) return hr;
1105 devguid = &guid;
1107 hr = StringFromCLSID(devguid, &guid_str);
1108 if(FAILED(hr))
1110 ERR("Failed to convert GUID to string\n");
1111 return hr;
1114 EnterCriticalSection(&This->crst);
1115 EnterCriticalSection(&openal_crst);
1117 hr = E_OUTOFMEMORY;
1118 len = WideCharToMultiByte(CP_UTF8, 0, guid_str, -1, NULL, 0, NULL, NULL);
1119 if(len <= 0)
1121 ERR("Failed to convert GUID to string\n");
1122 goto out;
1125 This->device = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len+1);
1126 if(!This->device)
1128 WARN("Out of memory to allocate %ls\n", guid_str);
1129 goto out;
1131 WideCharToMultiByte(CP_UTF8, 0, guid_str, -1, This->device, len, NULL, NULL);
1133 hr = S_OK;
1134 out:
1135 LeaveCriticalSection(&openal_crst);
1136 LeaveCriticalSection(&This->crst);
1137 if(guid_str)
1138 CoTaskMemFree(guid_str);
1139 guid_str = NULL;
1140 return hr;
1143 static const IDirectSoundCaptureVtbl DSC_Vtbl =
1145 DSCImpl_QueryInterface,
1146 DSCImpl_AddRef,
1147 DSCImpl_Release,
1148 DSCImpl_CreateCaptureBuffer,
1149 DSCImpl_GetCaps,
1150 DSCImpl_Initialize