Maarten Lankhorst : mmdevapi: Add support for IAudioRenderClient.
Alexandre Julliard
julliard at winehq.org
Tue Apr 20 11:32:23 CDT 2010
Module: wine
Branch: master
Commit: cffb95fdb835140cd3b167835f3e445a8ed5974b
URL: http://source.winehq.org/git/wine.git/?a=commit;h=cffb95fdb835140cd3b167835f3e445a8ed5974b
Author: Maarten Lankhorst <m.b.lankhorst at gmail.com>
Date: Mon Apr 19 11:52:51 2010 +0200
mmdevapi: Add support for IAudioRenderClient.
---
dlls/mmdevapi/audio.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 224 insertions(+), 0 deletions(-)
diff --git a/dlls/mmdevapi/audio.c b/dlls/mmdevapi/audio.c
index fcefc5a..b2ee3f0 100644
--- a/dlls/mmdevapi/audio.c
+++ b/dlls/mmdevapi/audio.c
@@ -74,9 +74,21 @@ typedef struct ACImpl {
HANDLE timer_id;
ALCdevice *capdev;
ALint format;
+
+ ACRender *render;
} ACImpl;
+struct ACRender {
+ const IAudioRenderClientVtbl *lpVtbl;
+ LONG ref;
+ ACImpl *parent;
+};
+
static const IAudioClientVtbl ACImpl_Vtbl;
+static const IAudioRenderClientVtbl ACRender_Vtbl;
+
+static HRESULT AudioRenderClient_Create(ACImpl *parent, ACRender **ppv);
+static void AudioRenderClient_Destroy(ACRender *This);
static int get_format_PCM(WAVEFORMATEX *format)
{
@@ -251,6 +263,8 @@ static void AudioClient_Destroy(ACImpl *This)
{
if (This->timer_id)
DeleteTimerQueueTimer(NULL, This->timer_id, INVALID_HANDLE_VALUE);
+ if (This->render)
+ AudioRenderClient_Destroy(This->render);
if (This->parent->flow == eRender && This->init) {
setALContext(This->parent->ctx);
IAudioClient_Stop((IAudioClient*)This);
@@ -808,6 +822,14 @@ static HRESULT WINAPI AC_GetService(IAudioClient *iface, REFIID riid, void **ppv
return E_POINTER;
*ppv = NULL;
+ if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
+ if (This->parent->flow != eRender)
+ return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
+ if (!This->render)
+ hr = AudioRenderClient_Create(This, &This->render);
+ *ppv = This->render;
+ }
+
if (FAILED(hr))
return hr;
@@ -839,4 +861,206 @@ static const IAudioClientVtbl ACImpl_Vtbl =
AC_GetService
};
+static HRESULT AudioRenderClient_Create(ACImpl *parent, ACRender **ppv)
+{
+ ACRender *This;
+
+ This = *ppv = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
+ if (!This)
+ return E_OUTOFMEMORY;
+ This->lpVtbl = &ACRender_Vtbl;
+ This->ref = 0;
+ This->parent = parent;
+ return S_OK;
+}
+
+static void AudioRenderClient_Destroy(ACRender *This)
+{
+ This->parent->render = NULL;
+ HeapFree(GetProcessHeap(), 0, This);
+}
+
+static HRESULT WINAPI ACR_QueryInterface(IAudioRenderClient *iface, REFIID riid, void **ppv)
+{
+ TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
+
+ if (!ppv)
+ return E_POINTER;
+ *ppv = NULL;
+ if (IsEqualIID(riid, &IID_IUnknown)
+ || IsEqualIID(riid, &IID_IAudioRenderClient))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ACR_AddRef(IAudioRenderClient *iface)
+{
+ ACRender *This = (ACRender*)iface;
+ ULONG ref;
+ ref = InterlockedIncrement(&This->ref);
+ TRACE("Refcount now %i\n", ref);
+ return ref;
+}
+
+static ULONG WINAPI ACR_Release(IAudioRenderClient *iface)
+{
+ ACRender *This = (ACRender*)iface;
+ ULONG ref;
+ ref = InterlockedDecrement(&This->ref);
+ TRACE("Refcount now %i\n", ref);
+ if (!ref)
+ AudioRenderClient_Destroy(This);
+ return ref;
+}
+
+static HRESULT WINAPI ACR_GetBuffer(IAudioRenderClient *iface, UINT32 frames, BYTE **data)
+{
+ ACRender *This = (ACRender*)iface;
+ DWORD free, framesize;
+ TRACE("(%p)->(%u,%p)\n", This, frames, data);
+
+ if (!data)
+ return E_POINTER;
+ if (!frames)
+ return S_OK;
+ *data = NULL;
+ if (This->parent->locked) {
+ ERR("Locked\n");
+ return AUDCLNT_E_OUT_OF_ORDER;
+ }
+ AC_GetCurrentPadding((IAudioClient*)This->parent, &free);
+ if (This->parent->bufsize-free < frames) {
+ ERR("Too large: %u %u %u\n", This->parent->bufsize, free, frames);
+ return AUDCLNT_E_BUFFER_TOO_LARGE;
+ }
+ EnterCriticalSection(This->parent->crst);
+ This->parent->locked = frames;
+ framesize = This->parent->pwfx->nBlockAlign;
+
+ /* Exact offset doesn't matter, offset could be 0 forever
+ * but increasing it is easier to debug */
+ if (This->parent->ofs + frames > This->parent->bufsize)
+ This->parent->ofs = 0;
+ *data = This->parent->buffer + This->parent->ofs * framesize;
+
+ LeaveCriticalSection(This->parent->crst);
+ return S_OK;
+}
+
+static HRESULT WINAPI ACR_ReleaseBuffer(IAudioRenderClient *iface, UINT32 written, DWORD flags)
+{
+ ACRender *This = (ACRender*)iface;
+ BYTE *buf = This->parent->buffer;
+ DWORD framesize = This->parent->pwfx->nBlockAlign;
+ DWORD ofs = This->parent->ofs;
+ DWORD bufsize = This->parent->bufsize;
+ DWORD locked = This->parent->locked;
+ DWORD freq = This->parent->pwfx->nSamplesPerSec;
+ DWORD bpp = This->parent->pwfx->wBitsPerSample;
+ ALuint albuf;
+
+ TRACE("(%p)->(%u,%x)\n", This, written, flags);
+
+ if (locked < written)
+ return AUDCLNT_E_INVALID_SIZE;
+
+ if (flags & ~AUDCLNT_BUFFERFLAGS_SILENT)
+ return E_INVALIDARG;
+
+ if (!written) {
+ FIXME("Handled right?\n");
+ This->parent->locked = 0;
+ return S_OK;
+ }
+
+ if (!This->parent->locked)
+ return AUDCLNT_E_OUT_OF_ORDER;
+
+ EnterCriticalSection(This->parent->crst);
+ setALContext(This->parent->parent->ctx);
+
+ This->parent->ofs += written;
+ This->parent->ofs %= bufsize;
+ This->parent->pad += written;
+ This->parent->frameswritten += written;
+
+ ofs *= framesize;
+ written *= framesize;
+ bufsize *= framesize;
+ locked *= framesize;
+
+ if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
+ memset(buf+ofs, bpp != 8 ? 0 : 128, written);
+ TRACE("buf: %p, ofs: %x, written %u, freq %u\n", buf, ofs, written, freq);
+
+ palGenBuffers(1, &albuf);
+ palBufferData(albuf, This->parent->format, buf+ofs, written, freq);
+ palSourceQueueBuffers(This->parent->source, 1, &albuf);
+ TRACE("Queued %u\n", albuf);
+
+ if (This->parent->running) {
+ ALint state = AL_PLAYING, done = 0, padpart = 0;
+
+ palGetSourcei(This->parent->source, AL_BUFFERS_PROCESSED, &done);
+ palGetSourcei(This->parent->source, AL_BYTE_OFFSET, &padpart);
+ palGetSourcei(This->parent->source, AL_SOURCE_STATE, &state);
+ padpart /= framesize;
+
+ if (state == AL_STOPPED) {
+ padpart = This->parent->pad;
+ /* Buffer might have been processed in the small window
+ * between first and third call */
+ palGetSourcei(This->parent->source, AL_BUFFERS_PROCESSED, &done);
+ }
+ if (done || This->parent->padpartial != padpart)
+ This->parent->laststamp = gettime();
+ This->parent->padpartial = padpart;
+
+ while (done--) {
+ ALint size, bits, chan;
+ ALuint which;
+
+ palSourceUnqueueBuffers(This->parent->source, 1, &which);
+ palGetBufferi(which, AL_SIZE, &size);
+ palGetBufferi(which, AL_BITS, &bits);
+ palGetBufferi(which, AL_CHANNELS, &chan);
+ size /= bits * chan / 8;
+ if (size > This->parent->pad) {
+ ERR("Overflow!\n");
+ size = This->parent->pad;
+ }
+ This->parent->pad -= size;
+ This->parent->padpartial -= size;
+ TRACE("Unqueued %u\n", which);
+ palDeleteBuffers(1, &which);
+ }
+
+ if (state != AL_PLAYING) {
+ ERR("Starting from %x\n", state);
+ palSourcePlay(This->parent->source);
+ }
+ getALError();
+ }
+ This->parent->locked = 0;
+
+ getALError();
+ popALContext();
+ LeaveCriticalSection(This->parent->crst);
+
+ return S_OK;
+}
+
+static const IAudioRenderClientVtbl ACRender_Vtbl = {
+ ACR_QueryInterface,
+ ACR_AddRef,
+ ACR_Release,
+ ACR_GetBuffer,
+ ACR_ReleaseBuffer
+};
+
#endif
More information about the wine-cvs
mailing list