Implemented CLSID_AudioRender.

diff --git a/dlls/quartz/Makefile.in b/dlls/quartz/Makefile.in
index 64fb6df..dd9a5d4 100644
--- a/dlls/quartz/Makefile.in
+++ b/dlls/quartz/Makefile.in
@@ -10,6 +10,7 @@
 
 C_SRCS = \
 	amundoc.c \
+	audren.c \
 	basefilt.c \
 	basepin.c \
 	complist.c \
diff --git a/dlls/quartz/audren.c b/dlls/quartz/audren.c
new file mode 100644
index 0000000..0a18103
--- /dev/null
+++ b/dlls/quartz/audren.c
@@ -0,0 +1,841 @@
+/*
+ * Audio Renderer (CLSID_AudioRender)
+ *
+ * FIXME
+ *  - implements IRefereneceClock.
+ *  - implements seeking.
+ *
+ * hidenori@a2.ctktv.ne.jp
+ */
+
+#include "config.h"
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "mmsystem.h"
+#include "winerror.h"
+#include "wine/obj_base.h"
+#include "wine/obj_oleaut.h"
+#include "strmif.h"
+#include "control.h"
+#include "vfwmsgs.h"
+#include "uuids.h"
+
+#include "debugtools.h"
+DEFAULT_DEBUG_CHANNEL(quartz);
+
+#include "quartz_private.h"
+#include "audren.h"
+
+
+static const WCHAR QUARTZ_AudioRender_Name[] =
+{ 'A','u','d','i','o',' ','R','e','n','d','e','r',0 };
+static const WCHAR QUARTZ_AudioRenderPin_Name[] =
+{ 'I','n',0 };
+
+
+
+/***************************************************************************
+ *
+ *	CAudioRendererImpl waveOut methods (internal)
+ *
+ */
+
+static
+HRESULT QUARTZ_HRESULT_From_MMRESULT( MMRESULT mr )
+{
+	HRESULT hr = E_FAIL;
+
+	switch ( mr )
+	{
+	case MMSYSERR_NOERROR:
+		hr = S_OK;
+		break;
+	case MMSYSERR_NOMEM:
+		hr = E_OUTOFMEMORY;
+		break;
+	}
+
+	return hr;
+}
+
+void CAudioRendererImpl_waveOutEventCallback(
+	HWAVEOUT hwo, UINT uMsg,
+	DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
+{
+	CAudioRendererImpl* This = (CAudioRendererImpl*)dwInstance;
+
+	if ( uMsg == WOM_DONE )
+		SetEvent( This->m_hEventRender );
+}
+
+static
+void CAudioRendererImpl_waveOutReset(
+	CAudioRendererImpl* This )
+{
+	if ( !This->m_fWaveOutInit )
+		return;
+
+	waveOutReset( This->m_hWaveOut );
+	SetEvent( This->m_hEventRender );
+}
+
+static
+void CAudioRendererImpl_waveOutUninit(
+	CAudioRendererImpl* This )
+{
+	DWORD i;
+
+	TRACE("(%p)\n",This);
+
+	if ( !This->m_fWaveOutInit )
+		return;
+
+	waveOutReset( This->m_hWaveOut );
+	SetEvent( This->m_hEventRender );
+
+	for ( i = 0; i < WINE_QUARTZ_WAVEOUT_COUNT; i++ )
+	{
+		if ( This->m_hdr[i].dwFlags & WHDR_PREPARED )
+		{
+			waveOutUnprepareHeader(
+				This->m_hWaveOut,
+				&This->m_hdr[i], sizeof(WAVEHDR) );
+			This->m_hdr[i].dwFlags = 0;
+		}
+		if ( This->m_hdr[i].lpData != NULL )
+		{
+			QUARTZ_FreeMem( This->m_hdr[i].lpData );
+			This->m_hdr[i].lpData = NULL;
+		}
+	}
+
+	waveOutClose( This->m_hWaveOut );
+	This->m_hWaveOut = (HWAVEOUT)NULL;
+	if ( This->m_hEventRender != (HANDLE)NULL )
+	{
+		CloseHandle( This->m_hEventRender );
+		This->m_hEventRender = (HANDLE)NULL;
+	}
+
+	This->m_fWaveOutInit = FALSE;
+}
+
+static
+HRESULT CAudioRendererImpl_waveOutInit(
+	CAudioRendererImpl* This, WAVEFORMATEX* pwfx )
+{
+	MMRESULT mr;
+	HRESULT hr;
+	DWORD i;
+	DWORD dwBlockSize;
+
+	if ( This->m_fWaveOutInit )
+		return E_UNEXPECTED;
+
+	if ( pwfx == NULL )
+		return E_POINTER;
+	if ( pwfx->nBlockAlign == 0 )
+		return E_INVALIDARG;
+
+	This->m_hEventRender = (HANDLE)NULL;
+	This->m_hWaveOut = (HWAVEOUT)NULL;
+	This->m_dwBlockSize = 0;
+	This->m_phdrCur = NULL;
+	ZeroMemory( &This->m_hdr, sizeof(This->m_hdr) );
+
+
+	mr = waveOutOpen(
+		&This->m_hWaveOut, WAVE_MAPPER, pwfx,
+		(DWORD)CAudioRendererImpl_waveOutEventCallback, (DWORD)This,
+		CALLBACK_FUNCTION );
+	hr = QUARTZ_HRESULT_From_MMRESULT( mr );
+	if ( FAILED(hr) )
+		return hr;
+	This->m_fWaveOutInit = TRUE;
+
+	This->m_hEventRender = CreateEventA(
+		NULL, TRUE, TRUE, NULL );
+	if ( This->m_hEventRender == (HANDLE)NULL )
+	{
+		hr = E_OUTOFMEMORY;
+		goto err;
+	}
+
+	dwBlockSize = pwfx->nAvgBytesPerSec / pwfx->nBlockAlign;
+	if ( dwBlockSize == 0 )
+		dwBlockSize = 1;
+	dwBlockSize *= pwfx->nBlockAlign;
+	This->m_dwBlockSize = dwBlockSize;
+
+	for ( i = 0; i < WINE_QUARTZ_WAVEOUT_COUNT; i++ )
+	{
+		This->m_hdr[i].lpData = (CHAR*)QUARTZ_AllocMem( dwBlockSize );
+		if ( This->m_hdr[i].lpData == NULL )
+		{
+			hr = E_OUTOFMEMORY;
+			goto err;
+		}
+		mr = waveOutPrepareHeader(
+				This->m_hWaveOut,
+				&This->m_hdr[i], sizeof(WAVEHDR) );
+		hr = QUARTZ_HRESULT_From_MMRESULT( mr );
+		if ( FAILED(hr) )
+			goto err;
+		This->m_hdr[i].dwFlags |= WHDR_DONE;
+		This->m_hdr[i].dwBufferLength = dwBlockSize;
+		This->m_hdr[i].dwUser = i;
+	}
+
+	return S_OK;
+err:
+	CAudioRendererImpl_waveOutUninit(This);
+	return hr;
+}
+
+static
+WAVEHDR* CAudioRendererImpl_waveOutGetBuffer(
+	CAudioRendererImpl* This )
+{
+	DWORD i;
+
+	if ( !This->m_fWaveOutInit )
+		return NULL;
+
+	if ( This->m_phdrCur != NULL )
+		return This->m_phdrCur;
+
+	for ( i = 0; i < WINE_QUARTZ_WAVEOUT_COUNT; i++ )
+	{
+		if ( This->m_hdr[i].dwFlags & WHDR_DONE )
+		{
+			This->m_phdrCur = &(This->m_hdr[i]);
+			This->m_phdrCur->dwFlags &= ~WHDR_DONE;
+			This->m_phdrCur->dwBufferLength = 0;
+			return This->m_phdrCur;
+		}
+	}
+
+	return NULL;
+}
+
+static
+HRESULT CAudioRendererImpl_waveOutWriteData(
+	CAudioRendererImpl* This,
+	const BYTE* pData, DWORD cbData, DWORD* pcbWritten )
+{
+	DWORD	cbAvail;
+
+	*pcbWritten = 0;
+
+	if ( !This->m_fWaveOutInit )
+		return E_UNEXPECTED;
+
+	if ( cbData == 0 )
+		return S_OK;
+
+	if ( CAudioRendererImpl_waveOutGetBuffer(This) == NULL )
+		return S_FALSE;
+
+	cbAvail = This->m_dwBlockSize - This->m_phdrCur->dwBufferLength;
+	if ( cbAvail > cbData )
+		cbAvail = cbData;
+	memcpy( This->m_phdrCur->lpData, pData, cbAvail );
+	pData += cbAvail;
+	cbData -= cbAvail;
+	This->m_phdrCur->dwBufferLength += cbAvail;
+
+	*pcbWritten = cbAvail;
+
+	return S_OK;
+}
+
+static
+HRESULT CAudioRendererImpl_waveOutFlush(
+	CAudioRendererImpl* This )
+{
+	MMRESULT mr;
+	HRESULT hr;
+
+	if ( !This->m_fWaveOutInit )
+		return E_UNEXPECTED;
+	if ( This->m_phdrCur == NULL )
+		return E_UNEXPECTED;
+
+	if ( This->m_phdrCur->dwBufferLength == 0 )
+		return S_OK;
+
+	mr = waveOutWrite(
+		This->m_hWaveOut,
+		This->m_phdrCur, sizeof(WAVEHDR) );
+	hr = QUARTZ_HRESULT_From_MMRESULT( mr );
+	if ( FAILED(hr) )
+		return hr;
+
+	This->m_phdrCur = NULL;
+	return S_OK;
+}
+
+static
+HRESULT CAudioRendererImpl_waveOutGetVolume(
+	CAudioRendererImpl* This,
+	DWORD* pdwLeft, DWORD* pdwRight )
+{
+	MMRESULT mr;
+	HRESULT hr;
+	DWORD	dwVol;
+
+	if ( !This->m_fWaveOutInit )
+		return E_UNEXPECTED;
+
+	mr = waveOutGetVolume(
+		This->m_hWaveOut, &dwVol );
+	hr = QUARTZ_HRESULT_From_MMRESULT( mr );
+	if ( FAILED(hr) )
+		return hr;
+
+	*pdwLeft = LOWORD(dwVol);
+	*pdwRight = HIWORD(dwVol);
+
+	return NOERROR;
+}
+
+static
+HRESULT CAudioRendererImpl_waveOutSetVolume(
+	CAudioRendererImpl* This,
+	DWORD dwLeft, DWORD dwRight )
+{
+	MMRESULT mr;
+	DWORD	dwVol;
+
+	if ( !This->m_fWaveOutInit )
+		return E_UNEXPECTED;
+
+	dwVol = dwLeft | (dwRight<<16);
+
+	mr = waveOutSetVolume(
+		This->m_hWaveOut, dwVol );
+	return QUARTZ_HRESULT_From_MMRESULT( mr );
+}
+
+/***************************************************************************
+ *
+ *	CAudioRendererImpl methods
+ *
+ */
+
+
+static HRESULT CAudioRendererImpl_OnActive( CBaseFilterImpl* pImpl )
+{
+	CAudioRendererImpl_THIS(pImpl,basefilter);
+	WAVEFORMATEX*	pwfx;
+
+	FIXME( "(%p)\n", This );
+
+	if ( This->pPin->pin.pmtConn == NULL )
+		return NOERROR;
+
+	pwfx = (WAVEFORMATEX*)This->pPin->pin.pmtConn->pbFormat;
+	if ( pwfx == NULL )
+		return E_FAIL;
+
+	This->m_fInFlush = FALSE;
+
+	return CAudioRendererImpl_waveOutInit(This,pwfx);
+}
+
+static HRESULT CAudioRendererImpl_OnInactive( CBaseFilterImpl* pImpl )
+{
+	CAudioRendererImpl_THIS(pImpl,basefilter);
+
+	FIXME( "(%p)\n", This );
+
+	CAudioRendererImpl_waveOutUninit(This);
+
+	This->m_fInFlush = FALSE;
+
+	TRACE("returned\n" );
+
+	return NOERROR;
+}
+
+static const CBaseFilterHandlers filterhandlers =
+{
+	CAudioRendererImpl_OnActive, /* pOnActive */
+	CAudioRendererImpl_OnInactive, /* pOnInactive */
+};
+
+/***************************************************************************
+ *
+ *	CAudioRendererPinImpl methods
+ *
+ */
+
+static HRESULT CAudioRendererPinImpl_CheckMediaType( CPinBaseImpl* pImpl, const AM_MEDIA_TYPE* pmt )
+{
+	CAudioRendererPinImpl_THIS(pImpl,pin);
+	const WAVEFORMATEX* pwfx;
+
+	TRACE( "(%p,%p)\n",This,pmt );
+
+	if ( !IsEqualGUID( &pmt->majortype, &MEDIATYPE_Audio ) )
+		return E_FAIL;
+	if ( !IsEqualGUID( &pmt->subtype, &MEDIASUBTYPE_PCM ) )
+		return E_FAIL;
+	if ( !IsEqualGUID( &pmt->formattype, &FORMAT_WaveFormatEx ) )
+		return E_FAIL;
+	if ( pmt->cbFormat < (sizeof(WAVEFORMATEX)-sizeof(WORD)) )
+		return E_FAIL;
+
+	pwfx = (const WAVEFORMATEX*)pmt->pbFormat;
+	if ( pwfx == NULL )
+		return E_FAIL;
+	if ( pwfx->wFormatTag != 1 )
+		return E_FAIL;
+
+	return NOERROR;
+}
+
+static HRESULT CAudioRendererPinImpl_Receive( CPinBaseImpl* pImpl, IMediaSample* pSample )
+{
+	CAudioRendererPinImpl_THIS(pImpl,pin);
+	HRESULT hr;
+	BYTE*	pData = NULL;
+	DWORD	dwDataLength;
+	DWORD	dwWritten;
+
+	FIXME( "(%p,%p)\n",This,pSample );
+
+	if ( !This->pRender->m_fWaveOutInit )
+		return E_UNEXPECTED;
+	if ( This->pRender->m_fInFlush )
+		return S_FALSE;
+	if ( pSample == NULL )
+		return E_POINTER;
+
+	hr = IMediaSample_GetPointer( pSample, &pData );
+	if ( FAILED(hr) )
+		return hr;
+	dwDataLength = (DWORD)IMediaSample_GetActualDataLength( pSample );
+
+	while ( 1 )
+	{
+		TRACE("trying to write %lu bytes\n",dwDataLength);
+
+		ResetEvent( This->pRender->m_hEventRender );
+		hr = CAudioRendererImpl_waveOutWriteData(
+			This->pRender,pData,dwDataLength,&dwWritten);
+		if ( FAILED(hr) )
+			break;
+		if ( hr == S_FALSE )
+		{
+			WaitForSingleObject( This->pRender->m_hEventRender, INFINITE );
+			continue;
+		}
+		pData += dwWritten;
+		dwDataLength -= dwWritten;
+		hr = CAudioRendererImpl_waveOutFlush(This->pRender);
+		if ( FAILED(hr) )
+			break;
+		if ( dwDataLength == 0 )
+			break;
+	}
+
+	return hr;
+}
+
+static HRESULT CAudioRendererPinImpl_ReceiveCanBlock( CPinBaseImpl* pImpl )
+{
+	CAudioRendererPinImpl_THIS(pImpl,pin);
+
+	TRACE( "(%p)\n", This );
+
+	/* might block. */
+	return S_OK;
+}
+
+static HRESULT CAudioRendererPinImpl_EndOfStream( CPinBaseImpl* pImpl )
+{
+	CAudioRendererPinImpl_THIS(pImpl,pin);
+
+	FIXME( "(%p)\n", This );
+
+	This->pRender->m_fInFlush = FALSE;
+	/* IMediaEventSink::Notify(EC_COMPLETE) */
+
+	return NOERROR;
+}
+
+static HRESULT CAudioRendererPinImpl_BeginFlush( CPinBaseImpl* pImpl )
+{
+	CAudioRendererPinImpl_THIS(pImpl,pin);
+
+	FIXME( "(%p)\n", This );
+
+	This->pRender->m_fInFlush = TRUE;
+	CAudioRendererImpl_waveOutReset(This->pRender);
+
+	return NOERROR;
+}
+
+static HRESULT CAudioRendererPinImpl_EndFlush( CPinBaseImpl* pImpl )
+{
+	CAudioRendererPinImpl_THIS(pImpl,pin);
+
+	FIXME( "(%p)\n", This );
+
+	This->pRender->m_fInFlush = FALSE;
+
+	return NOERROR;
+}
+
+static HRESULT CAudioRendererPinImpl_NewSegment( CPinBaseImpl* pImpl, REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, double rate )
+{
+	CAudioRendererPinImpl_THIS(pImpl,pin);
+
+	FIXME( "(%p)\n", This );
+
+	return NOERROR;
+}
+
+static const CBasePinHandlers pinhandlers =
+{
+	CAudioRendererPinImpl_CheckMediaType, /* pCheckMediaType */
+	NULL, /* pQualityNotify */
+	CAudioRendererPinImpl_Receive, /* pReceive */
+	CAudioRendererPinImpl_ReceiveCanBlock, /* pReceiveCanBlock */
+	CAudioRendererPinImpl_EndOfStream, /* pEndOfStream */
+	CAudioRendererPinImpl_BeginFlush, /* pBeginFlush */
+	CAudioRendererPinImpl_EndFlush, /* pEndFlush */
+	CAudioRendererPinImpl_NewSegment, /* pNewSegment */
+};
+
+
+/***************************************************************************
+ *
+ *	new/delete CAudioRendererImpl
+ *
+ */
+
+/* can I use offsetof safely? - FIXME? */
+static QUARTZ_IFEntry FilterIFEntries[] =
+{
+  { &IID_IPersist, offsetof(CAudioRendererImpl,basefilter)-offsetof(CAudioRendererImpl,unk) },
+  { &IID_IMediaFilter, offsetof(CAudioRendererImpl,basefilter)-offsetof(CAudioRendererImpl,unk) },
+  { &IID_IBaseFilter, offsetof(CAudioRendererImpl,basefilter)-offsetof(CAudioRendererImpl,unk) },
+  { &IID_IBasicAudio, offsetof(CAudioRendererImpl,basaud)-offsetof(CAudioRendererImpl,unk) },
+};
+
+static void QUARTZ_DestroyAudioRenderer(IUnknown* punk)
+{
+	CAudioRendererImpl_THIS(punk,unk);
+
+	TRACE( "(%p)\n", This );
+
+	if ( This->pPin != NULL )
+	{
+		IUnknown_Release(This->pPin->unk.punkControl);
+		This->pPin = NULL;
+	}
+
+	CAudioRendererImpl_UninitIBasicAudio(This);
+	CBaseFilterImpl_UninitIBaseFilter(&This->basefilter);
+}
+
+HRESULT QUARTZ_CreateAudioRenderer(IUnknown* punkOuter,void** ppobj)
+{
+	CAudioRendererImpl*	This = NULL;
+	HRESULT hr;
+
+	TRACE("(%p,%p)\n",punkOuter,ppobj);
+
+	This = (CAudioRendererImpl*)
+		QUARTZ_AllocObj( sizeof(CAudioRendererImpl) );
+	if ( This == NULL )
+		return E_OUTOFMEMORY;
+	This->pPin = NULL;
+	This->m_fInFlush = FALSE;
+	This->m_fWaveOutInit = FALSE;
+	This->m_hEventRender = (HANDLE)NULL;
+
+	QUARTZ_IUnkInit( &This->unk, punkOuter );
+
+	hr = CBaseFilterImpl_InitIBaseFilter(
+		&This->basefilter,
+		This->unk.punkControl,
+		&CLSID_AudioRender,
+		QUARTZ_AudioRender_Name,
+		&filterhandlers );
+	if ( SUCCEEDED(hr) )
+	{
+		hr = CAudioRendererImpl_InitIBasicAudio(This);
+		if ( FAILED(hr) )
+		{
+			CBaseFilterImpl_UninitIBaseFilter(&This->basefilter);
+		}
+	}
+
+	if ( FAILED(hr) )
+	{
+		QUARTZ_FreeObj(This);
+		return hr;
+	}
+
+	This->unk.pEntries = FilterIFEntries;
+	This->unk.dwEntries = sizeof(FilterIFEntries)/sizeof(FilterIFEntries[0]);
+	This->unk.pOnFinalRelease = QUARTZ_DestroyAudioRenderer;
+
+	hr = QUARTZ_CreateAudioRendererPin(
+		This,
+		&This->basefilter.csFilter,
+		&This->pPin );
+	if ( SUCCEEDED(hr) )
+		hr = QUARTZ_CompList_AddComp(
+			This->basefilter.pInPins,
+			(IUnknown*)&This->pPin->pin,
+			NULL, 0 );
+	if ( FAILED(hr) )
+	{
+		IUnknown_Release( This->unk.punkControl );
+		return hr;
+	}
+
+	*ppobj = (void*)&(This->unk);
+
+	return S_OK;
+}
+
+
+/***************************************************************************
+ *
+ *	new/delete CAudioRendererPinImpl
+ *
+ */
+
+/* can I use offsetof safely? - FIXME? */
+static QUARTZ_IFEntry PinIFEntries[] =
+{
+  { &IID_IPin, offsetof(CAudioRendererPinImpl,pin)-offsetof(CAudioRendererPinImpl,unk) },
+  { &IID_IMemInputPin, offsetof(CAudioRendererPinImpl,meminput)-offsetof(CAudioRendererPinImpl,unk) },
+};
+
+static void QUARTZ_DestroyAudioRendererPin(IUnknown* punk)
+{
+	CAudioRendererPinImpl_THIS(punk,unk);
+
+	TRACE( "(%p)\n", This );
+
+	CPinBaseImpl_UninitIPin( &This->pin );
+	CMemInputPinBaseImpl_UninitIMemInputPin( &This->meminput );
+}
+
+HRESULT QUARTZ_CreateAudioRendererPin(
+	CAudioRendererImpl* pFilter,
+	CRITICAL_SECTION* pcsPin,
+	CAudioRendererPinImpl** ppPin)
+{
+	CAudioRendererPinImpl*	This = NULL;
+	HRESULT hr;
+
+	TRACE("(%p,%p,%p)\n",pFilter,pcsPin,ppPin);
+
+	This = (CAudioRendererPinImpl*)
+		QUARTZ_AllocObj( sizeof(CAudioRendererPinImpl) );
+	if ( This == NULL )
+		return E_OUTOFMEMORY;
+
+	QUARTZ_IUnkInit( &This->unk, NULL );
+	This->pRender = pFilter;
+
+	hr = CPinBaseImpl_InitIPin(
+		&This->pin,
+		This->unk.punkControl,
+		pcsPin,
+		&pFilter->basefilter,
+		QUARTZ_AudioRenderPin_Name,
+		FALSE,
+		&pinhandlers );
+
+	if ( SUCCEEDED(hr) )
+	{
+		hr = CMemInputPinBaseImpl_InitIMemInputPin(
+			&This->meminput,
+			This->unk.punkControl,
+			&This->pin );
+		if ( FAILED(hr) )
+		{
+			CPinBaseImpl_UninitIPin( &This->pin );
+		}
+	}
+
+	if ( FAILED(hr) )
+	{
+		QUARTZ_FreeObj(This);
+		return hr;
+	}
+
+	This->unk.pEntries = PinIFEntries;
+	This->unk.dwEntries = sizeof(PinIFEntries)/sizeof(PinIFEntries[0]);
+	This->unk.pOnFinalRelease = QUARTZ_DestroyAudioRendererPin;
+
+	*ppPin = This;
+
+	TRACE("returned successfully.\n");
+
+	return S_OK;
+}
+
+
+/***************************************************************************
+ *
+ *	CAudioRendererImpl::IBasicAudio
+ *
+ */
+
+static HRESULT WINAPI
+IBasicAudio_fnQueryInterface(IBasicAudio* iface,REFIID riid,void** ppobj)
+{
+	CAudioRendererImpl_THIS(iface,basaud);
+
+	TRACE("(%p)->()\n",This);
+
+	return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
+}
+
+static ULONG WINAPI
+IBasicAudio_fnAddRef(IBasicAudio* iface)
+{
+	CAudioRendererImpl_THIS(iface,basaud);
+
+	TRACE("(%p)->()\n",This);
+
+	return IUnknown_AddRef(This->unk.punkControl);
+}
+
+static ULONG WINAPI
+IBasicAudio_fnRelease(IBasicAudio* iface)
+{
+	CAudioRendererImpl_THIS(iface,basaud);
+
+	TRACE("(%p)->()\n",This);
+
+	return IUnknown_Release(This->unk.punkControl);
+}
+
+static HRESULT WINAPI
+IBasicAudio_fnGetTypeInfoCount(IBasicAudio* iface,UINT* pcTypeInfo)
+{
+	CAudioRendererImpl_THIS(iface,basaud);
+
+	FIXME("(%p)->()\n",This);
+
+	return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IBasicAudio_fnGetTypeInfo(IBasicAudio* iface,UINT iTypeInfo, LCID lcid, ITypeInfo** ppobj)
+{
+	CAudioRendererImpl_THIS(iface,basaud);
+
+	FIXME("(%p)->()\n",This);
+
+	return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IBasicAudio_fnGetIDsOfNames(IBasicAudio* iface,REFIID riid, LPOLESTR* ppwszName, UINT cNames, LCID lcid, DISPID* pDispId)
+{
+	CAudioRendererImpl_THIS(iface,basaud);
+
+	FIXME("(%p)->()\n",This);
+
+	return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IBasicAudio_fnInvoke(IBasicAudio* iface,DISPID DispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarRes, EXCEPINFO* pExcepInfo, UINT* puArgErr)
+{
+	CAudioRendererImpl_THIS(iface,basaud);
+
+	FIXME("(%p)->()\n",This);
+
+	return E_NOTIMPL;
+}
+
+
+static HRESULT WINAPI
+IBasicAudio_fnput_Volume(IBasicAudio* iface,long lVol)
+{
+	CAudioRendererImpl_THIS(iface,basaud);
+
+	FIXME("(%p)->()\n",This);
+
+	return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IBasicAudio_fnget_Volume(IBasicAudio* iface,long* plVol)
+{
+	CAudioRendererImpl_THIS(iface,basaud);
+
+	FIXME("(%p)->()\n",This);
+
+	return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IBasicAudio_fnput_Balance(IBasicAudio* iface,long lBalance)
+{
+	CAudioRendererImpl_THIS(iface,basaud);
+
+	FIXME("(%p)->()\n",This);
+
+	return E_NOTIMPL;
+}
+
+static HRESULT WINAPI
+IBasicAudio_fnget_Balance(IBasicAudio* iface,long* plBalance)
+{
+	CAudioRendererImpl_THIS(iface,basaud);
+
+	FIXME("(%p)->()\n",This);
+
+	return E_NOTIMPL;
+}
+
+
+static ICOM_VTABLE(IBasicAudio) ibasicaudio =
+{
+	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+	/* IUnknown fields */
+	IBasicAudio_fnQueryInterface,
+	IBasicAudio_fnAddRef,
+	IBasicAudio_fnRelease,
+	/* IDispatch fields */
+	IBasicAudio_fnGetTypeInfoCount,
+	IBasicAudio_fnGetTypeInfo,
+	IBasicAudio_fnGetIDsOfNames,
+	IBasicAudio_fnInvoke,
+	/* IBasicAudio fields */
+	IBasicAudio_fnput_Volume,
+	IBasicAudio_fnget_Volume,
+	IBasicAudio_fnput_Balance,
+	IBasicAudio_fnget_Balance,
+};
+
+
+HRESULT CAudioRendererImpl_InitIBasicAudio( CAudioRendererImpl* This )
+{
+	TRACE("(%p)\n",This);
+	ICOM_VTBL(&This->basaud) = &ibasicaudio;
+
+	return NOERROR;
+}
+
+void CAudioRendererImpl_UninitIBasicAudio( CAudioRendererImpl* This )
+{
+	TRACE("(%p)\n",This);
+}
+
diff --git a/dlls/quartz/audren.h b/dlls/quartz/audren.h
new file mode 100644
index 0000000..26aa3c7
--- /dev/null
+++ b/dlls/quartz/audren.h
@@ -0,0 +1,70 @@
+/*
+ * Audio Renderer (CLSID_AudioRender)
+ *
+ * FIXME
+ *  - implements IRefereneceClock.
+ *
+ * hidenori@a2.ctktv.ne.jp
+ */
+
+#ifndef	WINE_DSHOW_AUDREN_H
+#define	WINE_DSHOW_AUDREN_H
+
+#include "iunk.h"
+#include "basefilt.h"
+
+#define WINE_QUARTZ_WAVEOUT_COUNT	4
+
+typedef struct CAudioRendererImpl	CAudioRendererImpl;
+typedef struct CAudioRendererPinImpl	CAudioRendererPinImpl;
+
+
+typedef struct AudRen_IBasicAudioImpl
+{
+	ICOM_VFIELD(IBasicAudio);
+} AudRen_IBasicAudioImpl;
+
+struct CAudioRendererImpl
+{
+	QUARTZ_IUnkImpl	unk;
+	CBaseFilterImpl	basefilter;
+	AudRen_IBasicAudioImpl	basaud;
+
+	CAudioRendererPinImpl* pPin;
+
+	BOOL	m_fInFlush;
+
+	/* for waveOut */
+	BOOL		m_fWaveOutInit;
+	HANDLE		m_hEventRender;
+	HWAVEOUT	m_hWaveOut;
+	DWORD		m_dwBlockSize;
+	WAVEHDR*	m_phdrCur;
+	WAVEHDR		m_hdr[WINE_QUARTZ_WAVEOUT_COUNT];
+};
+
+struct CAudioRendererPinImpl
+{
+	QUARTZ_IUnkImpl	unk;
+	CPinBaseImpl	pin;
+	CMemInputPinBaseImpl	meminput;
+
+	CAudioRendererImpl* pRender;
+};
+
+#define	CAudioRendererImpl_THIS(iface,member)	CAudioRendererImpl*	This = ((CAudioRendererImpl*)(((char*)iface)-offsetof(CAudioRendererImpl,member)))
+#define	CAudioRendererPinImpl_THIS(iface,member)	CAudioRendererPinImpl*	This = ((CAudioRendererPinImpl*)(((char*)iface)-offsetof(CAudioRendererPinImpl,member)))
+
+
+HRESULT CAudioRendererImpl_InitIBasicAudio( CAudioRendererImpl* This );
+void CAudioRendererImpl_UninitIBasicAudio( CAudioRendererImpl* This );
+
+HRESULT QUARTZ_CreateAudioRenderer(IUnknown* punkOuter,void** ppobj);
+HRESULT QUARTZ_CreateAudioRendererPin(
+        CAudioRendererImpl* pFilter,
+        CRITICAL_SECTION* pcsPin,
+        CAudioRendererPinImpl** ppPin);
+
+
+
+#endif	/* WINE_DSHOW_AUDREN_H */
diff --git a/dlls/quartz/basefilt.c b/dlls/quartz/basefilt.c
index 1bb40f8..ef1d57e 100644
--- a/dlls/quartz/basefilt.c
+++ b/dlls/quartz/basefilt.c
@@ -73,54 +73,81 @@
 CBaseFilterImpl_fnStop(IBaseFilter* iface)
 {
 	ICOM_THIS(CBaseFilterImpl,iface);
+	HRESULT hr;
 
 	TRACE("(%p)->()\n",This);
 
+	hr = NOERROR;
+
 	EnterCriticalSection( &This->csFilter );
 
-	/* FIXME - call OnStop() */
+	if ( This->fstate == State_Running )
+	{
+		if ( This->pHandlers->pOnInactive != NULL )
+			hr = This->pHandlers->pOnInactive( This );
+	}
 
-	This->fstate = State_Stopped;
+	if ( SUCCEEDED(hr) )
+		This->fstate = State_Stopped;
 	LeaveCriticalSection( &This->csFilter );
 
-	return NOERROR;
+	return hr;
 }
 
 static HRESULT WINAPI
 CBaseFilterImpl_fnPause(IBaseFilter* iface)
 {
 	ICOM_THIS(CBaseFilterImpl,iface);
+	HRESULT hr;
 
 	TRACE("(%p)->()\n",This);
 
+	hr = NOERROR;
+
 	EnterCriticalSection( &This->csFilter );
 
-	/* FIXME - call OnPause() */
+	if ( This->fstate == State_Running )
+	{
+		if ( This->pHandlers->pOnInactive != NULL )
+			hr = This->pHandlers->pOnInactive( This );
+	}
 
-	This->fstate = State_Paused;
+	if ( SUCCEEDED(hr) )
+		This->fstate = State_Paused;
 
 	LeaveCriticalSection( &This->csFilter );
 
-	return NOERROR;
+	TRACE("hr = %08lx\n",hr);
+
+	return hr;
 }
 
 static HRESULT WINAPI
 CBaseFilterImpl_fnRun(IBaseFilter* iface,REFERENCE_TIME rtStart)
 {
 	ICOM_THIS(CBaseFilterImpl,iface);
+	HRESULT hr;
 
 	TRACE("(%p)->()\n",This);
 
+	hr = NOERROR;
+
 	EnterCriticalSection( &This->csFilter );
 
-	/* FIXME - call OnRun() */
-
 	This->rtStart = rtStart;
-	This->fstate = State_Running;
+
+	if ( This->fstate != State_Running )
+	{
+		if ( This->pHandlers->pOnActive != NULL )
+			hr = This->pHandlers->pOnActive( This );
+	}
+
+	if ( SUCCEEDED(hr) )
+		This->fstate = State_Running;
 
 	LeaveCriticalSection( &This->csFilter );
 
-	return NOERROR;
+	return hr;
 }
 
 static HRESULT WINAPI
@@ -136,6 +163,7 @@
 	/* FIXME - ignore 'intermediate state' now */
 
 	EnterCriticalSection( &This->csFilter );
+	TRACE("state %d\n",This->fstate);
 	*pState = This->fstate;
 	LeaveCriticalSection( &This->csFilter );
 
@@ -366,7 +394,8 @@
 
 HRESULT CBaseFilterImpl_InitIBaseFilter(
 	CBaseFilterImpl* This, IUnknown* punkControl,
-	const CLSID* pclsidFilter, LPCWSTR lpwszNameGraph )
+	const CLSID* pclsidFilter, LPCWSTR lpwszNameGraph,
+	const CBaseFilterHandlers* pHandlers )
 {
 	TRACE("(%p,%p)\n",This,punkControl);
 
@@ -378,6 +407,7 @@
 
 	ICOM_VTBL(This) = &ibasefilter;
 	This->punkControl = punkControl;
+	This->pHandlers = pHandlers;
 	This->pclsidFilter = pclsidFilter;
 	This->pInPins = NULL;
 	This->pOutPins = NULL;
@@ -394,6 +424,18 @@
 		return E_OUTOFMEMORY;
 	memcpy( This->pwszNameGraph, lpwszNameGraph, This->cbNameGraph );
 
+	This->pInPins = QUARTZ_CompList_Alloc();
+	This->pOutPins = QUARTZ_CompList_Alloc();
+	if ( This->pInPins == NULL || This->pOutPins == NULL )
+	{
+		if ( This->pInPins != NULL )
+			QUARTZ_CompList_Free(This->pInPins);
+		if ( This->pOutPins != NULL )
+			QUARTZ_CompList_Free(This->pOutPins);
+		QUARTZ_FreeMem(This->pwszNameGraph);
+		return E_OUTOFMEMORY;
+	}
+
 	InitializeCriticalSection( &This->csFilter );
 
 	return NOERROR;
@@ -415,7 +457,6 @@
 				break;
 			pPin = (IPin*)QUARTZ_CompList_GetItemPtr( pListItem );
 			QUARTZ_CompList_RemoveComp( This->pInPins, (IUnknown*)pPin );
-			IPin_Release( pPin );
 		}
 
 		QUARTZ_CompList_Free( This->pInPins );
@@ -430,7 +471,6 @@
 				break;
 			pPin = (IPin*)QUARTZ_CompList_GetItemPtr( pListItem );
 			QUARTZ_CompList_RemoveComp( This->pOutPins, (IUnknown*)pPin );
-			IPin_Release( pPin );
 		}
 
 		QUARTZ_CompList_Free( This->pOutPins );
diff --git a/dlls/quartz/basefilt.h b/dlls/quartz/basefilt.h
index 1d1d0ce..fe41a98 100644
--- a/dlls/quartz/basefilt.h
+++ b/dlls/quartz/basefilt.h
@@ -14,13 +14,20 @@
  */
 
 #include "complist.h"
+#include "mtype.h"
+
+typedef struct CBaseFilterHandlers	CBaseFilterHandlers;
+typedef struct CBasePinHandlers	CBasePinHandlers;
+
 typedef struct CBaseFilterImpl
 {
+	/* IPersist - IMediaFilter - IBaseFilter */
 	ICOM_VFIELD(IBaseFilter);
 
 	/* IUnknown fields */
 	IUnknown*	punkControl;
 	/* IBaseFilter fields */
+	const CBaseFilterHandlers*	pHandlers;
 	CRITICAL_SECTION	csFilter;
 	const CLSID*	pclsidFilter;
 	QUARTZ_CompList*	pInPins;	/* a list of IPin-s. */
@@ -33,30 +40,44 @@
 	FILTER_STATE	fstate;
 } CBaseFilterImpl;
 
+struct CBaseFilterHandlers
+{
+	HRESULT (*pOnActive)( CBaseFilterImpl* pImpl );
+	HRESULT (*pOnInactive)( CBaseFilterImpl* pImpl );
+};
+
+
 HRESULT CBaseFilterImpl_InitIBaseFilter(
 	CBaseFilterImpl* This, IUnknown* punkControl,
-	const CLSID* pclsidFilter, LPCWSTR lpwszNameGraph );
+	const CLSID* pclsidFilter, LPCWSTR lpwszNameGraph,
+	const CBaseFilterHandlers* pHandlers );
 void CBaseFilterImpl_UninitIBaseFilter( CBaseFilterImpl* This );
 
 
 /*
- * Implements IPin and IMemInputPin. (internal)
+ * Implements IPin, IMemInputPin, and IQualityControl. (internal)
  *
  * a base class for implementing IPin.
  */
 
 typedef struct CPinBaseImpl
 {
+	/* IPin */
 	ICOM_VFIELD(IPin);
 
 	/* IUnknown fields */
 	IUnknown*	punkControl;
 	/* IPin fields */
+	const CBasePinHandlers*	pHandlers;
 	DWORD	cbIdLen;
 	WCHAR*	pwszId;
 	BOOL	bOutput;
 
-	CRITICAL_SECTION	csPin;
+	/* you can change AcceptTypes while pcsPin has been hold */
+	const AM_MEDIA_TYPE*	pmtAcceptTypes;
+	ULONG	cAcceptTypes;
+
+	CRITICAL_SECTION*	pcsPin;
 	CBaseFilterImpl*	pFilter;
 	IPin*	pPinConnectedTo;
 	AM_MEDIA_TYPE*	pmtConn;
@@ -64,31 +85,66 @@
 
 typedef struct CMemInputPinBaseImpl
 {
+	/* IMemInputPin */
 	ICOM_VFIELD(IMemInputPin);
 
 	/* IUnknown fields */
 	IUnknown*	punkControl;
 	/* IMemInputPin fields */
-	CRITICAL_SECTION*	pcsPin;
+	CPinBaseImpl*	pPin;
 	IMemAllocator*	pAllocator;
 	BOOL	bReadonly;
 } CMemInputPinBaseImpl;
 
+typedef struct CQualityControlPassThruImpl
+{
+	/* IQualityControl */
+	ICOM_VFIELD(IQualityControl);
+
+	/* IUnknown fields */
+	IUnknown*	punkControl;
+	/* IQualityControl fields */
+	CPinBaseImpl*	pPin;
+	IQualityControl*	pControl;
+} CQualityControlPassThruImpl;
+
+
+struct CBasePinHandlers
+{
+	HRESULT (*pCheckMediaType)( CPinBaseImpl* pImpl, const AM_MEDIA_TYPE* pmt );
+	HRESULT (*pQualityNotify)( CPinBaseImpl* pImpl, IBaseFilter* pFilter, Quality q );
+	HRESULT (*pReceive)( CPinBaseImpl* pImpl, IMediaSample* pSample );
+	HRESULT (*pReceiveCanBlock)( CPinBaseImpl* pImpl );
+	HRESULT (*pEndOfStream)( CPinBaseImpl* pImpl );
+	HRESULT (*pBeginFlush)( CPinBaseImpl* pImpl );
+	HRESULT (*pEndFlush)( CPinBaseImpl* pImpl );
+	HRESULT (*pNewSegment)( CPinBaseImpl* pImpl, REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, double rate );
+};
+
+
 
 HRESULT CPinBaseImpl_InitIPin(
 	CPinBaseImpl* This, IUnknown* punkControl,
+	CRITICAL_SECTION* pcsPin,
 	CBaseFilterImpl* pFilter, LPCWSTR pwszId,
-	BOOL bOutput );
+	BOOL bOutput,
+	const CBasePinHandlers*	pHandlers );
 void CPinBaseImpl_UninitIPin( CPinBaseImpl* This );
 
 
 HRESULT CMemInputPinBaseImpl_InitIMemInputPin(
 	CMemInputPinBaseImpl* This, IUnknown* punkControl,
-	CRITICAL_SECTION* pcsPin
-	);
+	CPinBaseImpl* pPin );
 void CMemInputPinBaseImpl_UninitIMemInputPin(
 	CMemInputPinBaseImpl* This );
 
 
+HRESULT CQualityControlPassThruImpl_InitIQualityControl(
+	CQualityControlPassThruImpl* This, IUnknown* punkControl,
+	CPinBaseImpl* pPin );
+void CQualityControlPassThruImpl_UninitIQualityControl(
+	CQualityControlPassThruImpl* This );
+
+
 
 #endif	/* WINE_DSHOW_BASEFILT_H */
diff --git a/dlls/quartz/basepin.c b/dlls/quartz/basepin.c
index a1902df..2571135 100644
--- a/dlls/quartz/basepin.c
+++ b/dlls/quartz/basepin.c
@@ -21,7 +21,6 @@
 
 #include "quartz_private.h"
 #include "basefilt.h"
-#include "mtype.h"
 #include "memalloc.h"
 
 
@@ -66,6 +65,7 @@
 {
 	ICOM_THIS(CPinBaseImpl,iface);
 	HRESULT	hr = E_NOTIMPL;
+	ULONG	i;
 
 	FIXME("(%p)->(%p,%p) stub!\n",This,pPin,pmt);
 
@@ -74,7 +74,7 @@
 	if ( pPin == NULL || pmt == NULL )
 		return E_POINTER;
 
-	EnterCriticalSection( &This->csPin );
+	EnterCriticalSection( This->pcsPin );
 
 	if ( This->pPinConnectedTo != NULL )
 	{
@@ -84,11 +84,52 @@
 
 	/* FIXME - return fail if running */
 
-	/* FIXME */
+	if ( pmt != NULL )
+	{
+		hr = IPin_QueryAccept(iface,pmt);
+		if ( FAILED(hr) )
+			goto err;
+		hr = IPin_ReceiveConnection(pPin,iface,pmt);
+		if ( FAILED(hr) )
+			goto err;
+	}
+	else
+	{
+		for ( i = 0; i < This->cAcceptTypes; i++ )
+		{
+			pmt = &This->pmtAcceptTypes[i];
+			hr = IPin_QueryAccept(iface,pmt);
+			if ( SUCCEEDED(hr) )
+			{
+				hr = IPin_ReceiveConnection(pPin,iface,pmt);
+				if ( SUCCEEDED(hr) )
+				{
+					goto connected;
+				}
+			}
+		}
 
-	hr = E_NOTIMPL;
+		hr = VFW_E_TYPE_NOT_ACCEPTED;
+		goto err;
+	}
+
+	if ( FAILED(hr) )
+		goto err;
+
+connected:;
+	This->pmtConn = QUARTZ_MediaType_Duplicate( pmt );
+	if ( This->pmtConn == NULL )
+	{
+		hr = E_OUTOFMEMORY;
+		IPin_Disconnect(pPin);
+		goto err;
+	}
+	hr = S_OK;
+
+	This->pPinConnectedTo = pPin; IPin_AddRef(pPin);
+
 err:
-	LeaveCriticalSection( &This->csPin );
+	LeaveCriticalSection( This->pcsPin );
 
 	return hr;
 }
@@ -106,7 +147,7 @@
 	if ( pPin == NULL || pmt == NULL )
 		return E_POINTER;
 
-	EnterCriticalSection( &This->csPin );
+	EnterCriticalSection( This->pcsPin );
 
 	if ( This->pPinConnectedTo != NULL )
 	{
@@ -116,13 +157,24 @@
 
 	/* FIXME - return fail if running */
 
-	/* FIXME */
 
-	hr = E_NOTIMPL;
+	hr = IPin_QueryAccept(iface,pmt);
+	if ( FAILED(hr) )
+		goto err;
+
+	This->pmtConn = QUARTZ_MediaType_Duplicate( pmt );
+	if ( This->pmtConn == NULL )
+	{
+		hr = E_OUTOFMEMORY;
+		goto err;
+	}
+	hr = S_OK;
+
+	This->pPinConnectedTo = pPin; IPin_AddRef(pPin);
 err:
-	LeaveCriticalSection( &This->csPin );
+	LeaveCriticalSection( This->pcsPin );
 
-	return E_NOTIMPL;
+	return hr;
 }
 
 static HRESULT WINAPI
@@ -133,7 +185,7 @@
 
 	FIXME("(%p)->() stub!\n",This);
 
-	EnterCriticalSection( &This->csPin );
+	EnterCriticalSection( This->pcsPin );
 
 	/* FIXME - return fail if running */
 
@@ -156,7 +208,7 @@
 		hr = S_FALSE; /* FIXME - is this correct??? */
 	}
 
-	LeaveCriticalSection( &This->csPin );
+	LeaveCriticalSection( This->pcsPin );
 
 	return hr;
 }
@@ -172,7 +224,7 @@
 	if ( ppPin == NULL )
 		return E_POINTER;
 
-	EnterCriticalSection( &This->csPin );
+	EnterCriticalSection( This->pcsPin );
 
 	*ppPin = This->pPinConnectedTo;
 	if ( This->pPinConnectedTo != NULL )
@@ -181,7 +233,7 @@
 		hr = NOERROR;
 	}
 
-	LeaveCriticalSection( &This->csPin );
+	LeaveCriticalSection( This->pcsPin );
 
 	return hr;
 }
@@ -197,7 +249,7 @@
 	if ( pmt == NULL )
 		return E_POINTER;
 
-	EnterCriticalSection( &This->csPin );
+	EnterCriticalSection( This->pcsPin );
 
 	if ( This->pmtConn != NULL )
 	{
@@ -211,7 +263,7 @@
 		hr = NOERROR;
 	}
 
-	LeaveCriticalSection( &This->csPin );
+	LeaveCriticalSection( This->pcsPin );
 
 	return hr;
 }
@@ -226,7 +278,7 @@
 	if ( pinfo == NULL )
 		return E_POINTER;
 
-	EnterCriticalSection( &This->csPin );
+	EnterCriticalSection( This->pcsPin );
 
 	ZeroMemory( pinfo, sizeof(PIN_INFO) );
 	pinfo->pFilter = (IBaseFilter*)(This->pFilter);
@@ -241,7 +293,7 @@
 		pinfo->achName[sizeof(pinfo->achName)/sizeof(pinfo->achName[0])-1] = 0;
 	}
 
-	LeaveCriticalSection( &This->csPin );
+	LeaveCriticalSection( This->pcsPin );
 
 	return NOERROR;
 }
@@ -283,20 +335,42 @@
 CPinBaseImpl_fnQueryAccept(IPin* iface,const AM_MEDIA_TYPE* pmt)
 {
 	ICOM_THIS(CPinBaseImpl,iface);
+	HRESULT hr;
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->(%p)\n",This,pmt);
 
-	return E_NOTIMPL;
+	if ( pmt == NULL )
+		return E_POINTER;
+
+	hr = NOERROR;
+	EnterCriticalSection( This->pcsPin );
+	if ( This->pHandlers->pCheckMediaType != NULL )
+		hr = This->pHandlers->pCheckMediaType(This,pmt);
+	LeaveCriticalSection( This->pcsPin );
+
+	return hr;
 }
 
 static HRESULT WINAPI
 CPinBaseImpl_fnEnumMediaTypes(IPin* iface,IEnumMediaTypes** ppenum)
 {
 	ICOM_THIS(CPinBaseImpl,iface);
+	HRESULT hr;
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->(%p)\n",This,ppenum);
 
-	return E_NOTIMPL;
+	if ( ppenum == NULL )
+		return E_POINTER;
+
+	hr = E_NOTIMPL;
+
+	EnterCriticalSection( This->pcsPin );
+	if ( This->cAcceptTypes > 0 )
+		hr = QUARTZ_CreateEnumMediaTypes(
+			ppenum, This->pmtAcceptTypes, This->cAcceptTypes );
+	LeaveCriticalSection( This->pcsPin );
+
+	return hr;
 }
 
 static HRESULT WINAPI
@@ -314,52 +388,76 @@
 CPinBaseImpl_fnEndOfStream(IPin* iface)
 {
 	ICOM_THIS(CPinBaseImpl,iface);
+	HRESULT hr = E_NOTIMPL;
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->()\n",This);
 
 	if ( This->bOutput )
 		return E_UNEXPECTED;
 
-	return E_NOTIMPL;
+	EnterCriticalSection( This->pcsPin );
+	if ( This->pHandlers->pEndOfStream != NULL )
+		hr = This->pHandlers->pEndOfStream(This);
+	LeaveCriticalSection( This->pcsPin );
+
+	return hr;
 }
 
 static HRESULT WINAPI
 CPinBaseImpl_fnBeginFlush(IPin* iface)
 {
 	ICOM_THIS(CPinBaseImpl,iface);
+	HRESULT hr = E_NOTIMPL;
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->()\n",This);
 
 	if ( This->bOutput )
 		return E_UNEXPECTED;
 
-	return E_NOTIMPL;
+	EnterCriticalSection( This->pcsPin );
+	if ( This->pHandlers->pBeginFlush != NULL )
+		hr = This->pHandlers->pBeginFlush(This);
+	LeaveCriticalSection( This->pcsPin );
+
+	return hr;
 }
 
 static HRESULT WINAPI
 CPinBaseImpl_fnEndFlush(IPin* iface)
 {
 	ICOM_THIS(CPinBaseImpl,iface);
+	HRESULT hr = E_NOTIMPL;
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->()\n",This);
 
 	if ( This->bOutput )
 		return E_UNEXPECTED;
 
-	return E_NOTIMPL;
+	EnterCriticalSection( This->pcsPin );
+	if ( This->pHandlers->pEndFlush != NULL )
+		hr = This->pHandlers->pEndFlush(This);
+	LeaveCriticalSection( This->pcsPin );
+
+	return hr;
 }
 
 static HRESULT WINAPI
 CPinBaseImpl_fnNewSegment(IPin* iface,REFERENCE_TIME rtStart,REFERENCE_TIME rtStop,double rate)
 {
 	ICOM_THIS(CPinBaseImpl,iface);
+	HRESULT hr = E_NOTIMPL;
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->()\n",This);
 
 	if ( This->bOutput )
 		return E_UNEXPECTED;
 
-	return E_NOTIMPL;
+	EnterCriticalSection( This->pcsPin );
+	if ( This->pHandlers->pNewSegment != NULL )
+		hr = This->pHandlers->pNewSegment(This,rtStart,rtStop,rate);
+	LeaveCriticalSection( This->pcsPin );
+
+	return hr;
 }
 
 
@@ -393,8 +491,10 @@
 
 HRESULT CPinBaseImpl_InitIPin(
 	CPinBaseImpl* This, IUnknown* punkControl,
+	CRITICAL_SECTION* pcsPin,
 	CBaseFilterImpl* pFilter, LPCWSTR pwszId,
-	BOOL bOutput )
+	BOOL bOutput,
+	const CBasePinHandlers*	pHandlers )
 {
 	HRESULT	hr = NOERROR;
 
@@ -408,9 +508,13 @@
 
 	ICOM_VTBL(This) = &ipin;
 	This->punkControl = punkControl;
+	This->pHandlers = pHandlers;
 	This->cbIdLen = sizeof(WCHAR)*(strlenW(pwszId)+1);
 	This->pwszId = NULL;
 	This->bOutput = bOutput;
+	This->pmtAcceptTypes = NULL;
+	This->cAcceptTypes = 0;
+	This->pcsPin = pcsPin;
 	This->pFilter = pFilter;
 	This->pPinConnectedTo = NULL;
 	This->pmtConn = NULL;
@@ -421,8 +525,7 @@
 		hr = E_OUTOFMEMORY;
 		goto err;
 	}
-
-	InitializeCriticalSection( &This->csPin );
+	memcpy( This->pwszId, pwszId, This->cbIdLen );
 
 	return NOERROR;
 
@@ -442,8 +545,6 @@
 		QUARTZ_FreeMem( This->pwszId );
 		This->pwszId = NULL;
 	}
-
-	DeleteCriticalSection( &This->csPin );
 }
 
 
@@ -497,7 +598,7 @@
 	if ( ppAllocator == NULL )
 		return E_POINTER;
 
-	EnterCriticalSection( This->pcsPin );
+	EnterCriticalSection( This->pPin->pcsPin );
 
 	if ( This->pAllocator == NULL )
 	{
@@ -516,7 +617,7 @@
 		IMemAllocator_AddRef(This->pAllocator);
 	}
 
-	LeaveCriticalSection( This->pcsPin );
+	LeaveCriticalSection( This->pPin->pcsPin );
 
 	return hr;
 }
@@ -531,7 +632,7 @@
 	if ( pAllocator == NULL )
 		return E_POINTER;
 
-	EnterCriticalSection( This->pcsPin );
+	EnterCriticalSection( This->pPin->pcsPin );
 
 	if ( This->pAllocator != NULL )
 	{
@@ -543,7 +644,7 @@
 
 	This->bReadonly = bReadonly;
 
-	LeaveCriticalSection( This->pcsPin );
+	LeaveCriticalSection( This->pPin->pcsPin );
 
 	return NOERROR;
 }
@@ -566,10 +667,16 @@
 CMemInputPinBaseImpl_fnReceive(IMemInputPin* iface,IMediaSample* pSample)
 {
 	ICOM_THIS(CMemInputPinBaseImpl,iface);
+	HRESULT hr = E_NOTIMPL;
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->(%p)\n",This,pSample);
 
-	return E_NOTIMPL;
+	EnterCriticalSection( This->pPin->pcsPin );
+	if ( This->pPin->pHandlers->pReceive != NULL )
+		hr = This->pPin->pHandlers->pReceive(This->pPin,pSample);
+	LeaveCriticalSection( This->pPin->pcsPin );
+
+	return hr;
 }
 
 static HRESULT WINAPI
@@ -584,7 +691,7 @@
 	if ( ppSample == NULL || pnSampleProcessed == NULL )
 		return E_POINTER;
 
-	EnterCriticalSection( This->pcsPin );
+	EnterCriticalSection( This->pPin->pcsPin );
 
 	hr = NOERROR;
 	for ( n = 0; n < nSample; n++ )
@@ -594,7 +701,7 @@
 			break;
 	}
 
-	LeaveCriticalSection( This->pcsPin );
+	LeaveCriticalSection( This->pPin->pcsPin );
 
 	*pnSampleProcessed = n;
 	return hr;
@@ -604,10 +711,16 @@
 CMemInputPinBaseImpl_fnReceiveCanBlock(IMemInputPin* iface)
 {
 	ICOM_THIS(CMemInputPinBaseImpl,iface);
+	HRESULT hr = E_NOTIMPL;
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->()\n",This);
 
-	return E_NOTIMPL;
+	EnterCriticalSection( This->pPin->pcsPin );
+	if ( This->pPin->pHandlers->pReceiveCanBlock != NULL )
+		hr = This->pPin->pHandlers->pReceiveCanBlock(This->pPin);
+	LeaveCriticalSection( This->pPin->pcsPin );
+
+	return hr;
 }
 
 
@@ -629,8 +742,7 @@
 
 HRESULT CMemInputPinBaseImpl_InitIMemInputPin(
 	CMemInputPinBaseImpl* This, IUnknown* punkControl,
-	CRITICAL_SECTION* pcsPin
-	)
+	CPinBaseImpl* pPin )
 {
 	TRACE("(%p,%p)\n",This,punkControl);
 
@@ -642,7 +754,7 @@
 
 	ICOM_VTBL(This) = &imeminputpin;
 	This->punkControl = punkControl;
-	This->pcsPin = pcsPin;
+	This->pPin = pPin;
 	This->pAllocator = NULL;
 	This->bReadonly = FALSE;
 
@@ -661,4 +773,107 @@
 	}
 }
 
+/***************************************************************************
+ *
+ *	CQualityControlPassThruImpl
+ *
+ */
+
+static HRESULT WINAPI
+CQualityControlPassThruImpl_fnQueryInterface(IQualityControl* iface,REFIID riid,void** ppobj)
+{
+	ICOM_THIS(CQualityControlPassThruImpl,iface);
+
+	TRACE("(%p)->()\n",This);
+
+	return IUnknown_QueryInterface(This->punkControl,riid,ppobj);
+}
+
+static ULONG WINAPI
+CQualityControlPassThruImpl_fnAddRef(IQualityControl* iface)
+{
+	ICOM_THIS(CQualityControlPassThruImpl,iface);
+
+	TRACE("(%p)->()\n",This);
+
+	return IUnknown_AddRef(This->punkControl);
+}
+
+static ULONG WINAPI
+CQualityControlPassThruImpl_fnRelease(IQualityControl* iface)
+{
+	ICOM_THIS(CQualityControlPassThruImpl,iface);
+
+	TRACE("(%p)->()\n",This);
+
+	return IUnknown_Release(This->punkControl);
+}
+
+
+static HRESULT WINAPI
+CQualityControlPassThruImpl_fnNotify(IQualityControl* iface,IBaseFilter* pFilter,Quality q)
+{
+	ICOM_THIS(CQualityControlPassThruImpl,iface);
+	HRESULT hr = S_FALSE;
+
+	TRACE("(%p)->()\n",This);
+
+	if ( This->pControl != NULL )
+		return IQualityControl_Notify( This->pControl, pFilter, q );
+
+	EnterCriticalSection( This->pPin->pcsPin );
+	if ( This->pPin->pHandlers->pQualityNotify != NULL )
+		hr = This->pPin->pHandlers->pQualityNotify(This->pPin,pFilter,q);
+	LeaveCriticalSection( This->pPin->pcsPin );
+
+	return hr;
+}
+
+static HRESULT WINAPI
+CQualityControlPassThruImpl_fnSetSink(IQualityControl* iface,IQualityControl* pControl)
+{
+	ICOM_THIS(CQualityControlPassThruImpl,iface);
+
+	TRACE("(%p)->()\n",This);
+
+	This->pControl = pControl; /* AddRef() must not be called */
+
+	return NOERROR;
+}
+
+static ICOM_VTABLE(IQualityControl) iqualitycontrol =
+{
+	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+	/* IUnknown fields */
+	CQualityControlPassThruImpl_fnQueryInterface,
+	CQualityControlPassThruImpl_fnAddRef,
+	CQualityControlPassThruImpl_fnRelease,
+	/* IQualityControl fields */
+	CQualityControlPassThruImpl_fnNotify,
+	CQualityControlPassThruImpl_fnSetSink,
+};
+
+HRESULT CQualityControlPassThruImpl_InitIQualityControl(
+	CQualityControlPassThruImpl* This, IUnknown* punkControl,
+	CPinBaseImpl* pPin )
+{
+	TRACE("(%p,%p)\n",This,punkControl);
+
+	if ( punkControl == NULL )
+	{
+		ERR( "punkControl must not be NULL\n" );
+		return E_INVALIDARG;
+	}
+
+	ICOM_VTBL(This) = &iqualitycontrol;
+	This->punkControl = punkControl;
+	This->pPin = pPin;
+
+	return NOERROR;
+}
+
+void CQualityControlPassThruImpl_UninitIQualityControl(
+	CQualityControlPassThruImpl* This )
+{
+}
 
diff --git a/dlls/quartz/fgevent.c b/dlls/quartz/fgevent.c
index db47c09..2d25a35 100644
--- a/dlls/quartz/fgevent.c
+++ b/dlls/quartz/fgevent.c
@@ -25,7 +25,7 @@
 #include "quartz_private.h"
 #include "fgraph.h"
 
-#define EVENTQUEUE_BLOCKSIZE	16
+#define EVENTQUEUE_BLOCKSIZE	2
 #define EVENTQUEUE_MAX			1024
 
 struct FilterGraph_MEDIAEVENT
@@ -36,6 +36,106 @@
 };
 
 
+static HRESULT FGEVENT_KeepEvent(
+	BOOL bKeep,
+	long lEventCode, LONG_PTR lParam1, LONG_PTR lParam2 )
+{
+	switch ( lEventCode )
+	{
+	/*case EC_COMPLETE:*/
+	case EC_USERABORT:
+		break;
+	case EC_ERRORABORT:
+		break;
+	case EC_TIME:
+		break;
+	/*case EC_REPAINT:*/
+	case EC_STREAM_ERROR_STOPPED:
+		break;
+	case EC_STREAM_ERROR_STILLPLAYING:
+		break;
+	case EC_ERROR_STILLPLAYING:
+		break;
+	case EC_PALETTE_CHANGED:
+		break;
+	case EC_VIDEO_SIZE_CHANGED:
+		break;
+	case EC_QUALITY_CHANGE:
+		break;
+	/*case EC_SHUTTING_DOWN:*/
+	case EC_CLOCK_CHANGED:
+		break;
+	case EC_PAUSED:
+		break;
+
+	case EC_OPENING_FILE:
+		break;
+	case EC_BUFFERING_DATA:
+		break;
+	case EC_FULLSCREEN_LOST:
+		if ( bKeep )
+		{
+			if ( ((IBaseFilter*)lParam2) != NULL )
+				IBaseFilter_AddRef( (IBaseFilter*)lParam2 );
+		}
+		else
+		{
+			if ( ((IBaseFilter*)lParam2) != NULL )
+				IBaseFilter_Release( (IBaseFilter*)lParam2 );
+		}
+		break;
+	/*case EC_ACTIVATE:*/
+	/*case EC_NEED_RESTART:*/
+	/*case EC_WINDOW_DESTROYED:*/
+	/*case EC_DISPLAY_CHANGED:*/
+	/*case EC_STARVATION:*/
+	/*case EC_OLE_EVENT:*/
+	/*case EC_NOTIFY_WINDOW:*/
+	/*case EC_STREAM_CONTROL_STOPPED:*/
+	/*case EC_STREAM_CONTROL_STARTED:*/
+	/*case EC_END_OF_SEGMENT:*/
+	/*case EC_SEGMENT_STARTED:*/
+	case EC_LENGTH_CHANGED:
+		break;
+	case EC_DEVICE_LOST:
+		if ( bKeep )
+		{
+			if ( ((IUnknown*)lParam1) != NULL )
+				IUnknown_AddRef( (IUnknown*)lParam1 );
+		}
+		else
+		{
+			if ( ((IUnknown*)lParam1) != NULL )
+				IUnknown_Release( (IUnknown*)lParam1 );
+		}
+		break;
+
+	case EC_STEP_COMPLETE:
+		break;
+	case EC_SKIP_FRAMES:
+		break;
+
+	/*case EC_TIMECODE_AVAILABLE:*/
+	/*case EC_EXTDEVICE_MODE_CHANGE:*/
+
+	case EC_GRAPH_CHANGED:
+		break;
+	case EC_CLOCK_UNSET:
+		break;
+
+	default:
+		if ( lEventCode < EC_USER )
+		{
+			FIXME( "unknown system event %08lx\n", lEventCode );
+			return E_INVALIDARG;
+		}
+		TRACE( "user event %08lx\n", lEventCode );
+		break;
+	}
+
+	return NOERROR;
+}
+
 /***************************************************************************
  *
  *	CLSID_FilterGraph::IMediaEvent[Ex]
@@ -129,7 +229,7 @@
 IMediaEventEx_fnGetEvent(IMediaEventEx* iface,long* plEventCode,LONG_PTR* plParam1,LONG_PTR* plParam2,long lTimeOut)
 {
 	CFilterGraph_THIS(iface,mediaevent);
-	ULONG cbQueued;
+	ULONG cQueued;
 	DWORD dw;
 	DWORD dwStart;
 	HRESULT	hr;
@@ -154,11 +254,11 @@
 		hr = S_FALSE;
 		if ( This->m_cbMediaEventsMax > 0 )
 		{
-			cbQueued =
+			cQueued =
 				(This->m_cbMediaEventsMax +
 				 This->m_cbMediaEventsPut - This->m_cbMediaEventsGet) %
 					This->m_cbMediaEventsMax;
-			if ( cbQueued > 0 )
+			if ( cQueued > 0 )
 			{
 				pEvent = &This->m_pMediaEvents[This->m_cbMediaEventsGet];
 				*plEventCode = pEvent->lEventCode;
@@ -168,6 +268,8 @@
 						This->m_cbMediaEventsMax;
 
 				hr = NOERROR;
+				if ( This->m_cbMediaEventsPut == This->m_cbMediaEventsGet )
+					ResetEvent( This->m_hMediaEvent );
 			}
 		}
 		LeaveCriticalSection( &This->m_csMediaEvents );
@@ -259,9 +361,9 @@
 {
 	CFilterGraph_THIS(iface,mediaevent);
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->(%08lx,%08x,%08x)\n",This,lEventCode,lParam1,lParam2);
 
-	return E_NOTIMPL;
+	return FGEVENT_KeepEvent( FALSE, lEventCode, lParam1, lParam2 );
 }
 
 static HRESULT WINAPI
@@ -269,9 +371,15 @@
 {
 	CFilterGraph_THIS(iface,mediaevent);
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->(%08x,%08lx,%08x)\n",This,hwnd,message,lParam);
 
-	return E_NOTIMPL;
+	EnterCriticalSection( &This->m_csMediaEvents );
+	This->m_hwndEventNotify = (HWND)hwnd;
+	This->m_lEventNotifyMsg = message;
+	This->m_lEventNotifyParam = lParam;
+	LeaveCriticalSection( &This->m_csMediaEvents );
+
+	return NOERROR;
 }
 
 static HRESULT WINAPI
@@ -279,9 +387,16 @@
 {
 	CFilterGraph_THIS(iface,mediaevent);
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->(%ld)\n",This,lNotifyFlags);
 
-	return E_NOTIMPL;
+	if ( lNotifyFlags != 0 && lNotifyFlags != 1 )
+		return E_INVALIDARG;
+
+	EnterCriticalSection( &This->m_csMediaEvents );
+	This->m_lEventNotifyFlags = lNotifyFlags;
+	LeaveCriticalSection( &This->m_csMediaEvents );
+
+	return NOERROR;
 }
 
 static HRESULT WINAPI
@@ -289,9 +404,16 @@
 {
 	CFilterGraph_THIS(iface,mediaevent);
 
-	FIXME("(%p)->() stub!\n",This);
+	TRACE("(%p)->(%p)\n",This,plNotifyFlags);
 
-	return E_NOTIMPL;
+	if ( plNotifyFlags == NULL )
+		return E_POINTER;
+
+	EnterCriticalSection( &This->m_csMediaEvents );
+	*plNotifyFlags = This->m_lEventNotifyFlags;
+	LeaveCriticalSection( &This->m_csMediaEvents );
+
+	return NOERROR;
 }
 
 
@@ -336,6 +458,10 @@
 	pfg->m_cbMediaEventsPut = 0;
 	pfg->m_cbMediaEventsGet = 0;
 	pfg->m_cbMediaEventsMax = 0;
+	pfg->m_hwndEventNotify = (HWND)NULL;
+	pfg->m_lEventNotifyMsg = 0;
+	pfg->m_lEventNotifyParam = 0;
+	pfg->m_lEventNotifyFlags = 0;
 
 	return NOERROR;
 }
@@ -411,10 +537,98 @@
 IMediaEventSink_fnNotify(IMediaEventSink* iface,long lEventCode,LONG_PTR lParam1,LONG_PTR lParam2)
 {
 	CFilterGraph_THIS(iface,mediaeventsink);
+	HRESULT hr = NOERROR;
+	ULONG cQueued;
+	ULONG cTemp;
+	FilterGraph_MEDIAEVENT*	pEvent;
 
-	FIXME("(%p)->(%ld,%08x,%08x) stub!\n",This,lEventCode,lParam1,lParam2);
+	TRACE("(%p)->(%08lx,%08x,%08x) stub!\n",This,lEventCode,lParam1,lParam2);
 
-	return E_NOTIMPL;
+	EnterCriticalSection( &This->m_csMediaEvents );
+
+	/* allocate a new entry. */
+	if ( This->m_cbMediaEventsMax == 0 )
+		cQueued = 0;
+	else
+		cQueued =
+			(This->m_cbMediaEventsMax +
+			 This->m_cbMediaEventsPut - This->m_cbMediaEventsGet) %
+				This->m_cbMediaEventsMax;
+
+	if ( (cQueued + 1) >= This->m_cbMediaEventsMax )
+	{
+		if ( This->m_cbMediaEventsMax >= EVENTQUEUE_MAX )
+		{
+			hr = E_FAIL;
+			goto end;
+		}
+		pEvent = (FilterGraph_MEDIAEVENT*)
+			QUARTZ_AllocMem( sizeof(FilterGraph_MEDIAEVENT) *
+				(This->m_cbMediaEventsMax+EVENTQUEUE_BLOCKSIZE) );
+		if ( pEvent == NULL )
+		{
+			hr = E_OUTOFMEMORY;
+			goto end;
+		}
+		if ( cQueued > 0 )
+		{
+			if ( (This->m_cbMediaEventsGet + cQueued) >=
+				This->m_cbMediaEventsMax )
+			{
+				cTemp = This->m_cbMediaEventsMax - This->m_cbMediaEventsGet;
+				memcpy(
+					pEvent,
+					&This->m_pMediaEvents[This->m_cbMediaEventsGet],
+					sizeof(FilterGraph_MEDIAEVENT) * cTemp );
+				memcpy(
+					pEvent + cTemp,
+					&This->m_pMediaEvents[0],
+					sizeof(FilterGraph_MEDIAEVENT) * (cQueued - cTemp) );
+			}
+			else
+			{
+				memcpy(
+					pEvent,
+					&This->m_pMediaEvents[This->m_cbMediaEventsGet],
+					sizeof(FilterGraph_MEDIAEVENT) * cQueued );
+			}
+			QUARTZ_FreeMem( This->m_pMediaEvents );
+		}
+		This->m_pMediaEvents = pEvent;
+		This->m_cbMediaEventsMax += EVENTQUEUE_BLOCKSIZE;
+		This->m_cbMediaEventsPut = cQueued;
+		This->m_cbMediaEventsGet = 0;
+	}
+
+	/* duplicate params if necessary. */
+	hr = FGEVENT_KeepEvent( TRUE, lEventCode, lParam1, lParam2 );
+	if ( FAILED(hr) )
+		goto end;
+
+	/* add to the queue. */
+	pEvent = &This->m_pMediaEvents[This->m_cbMediaEventsPut];
+	pEvent->lEventCode = lEventCode;
+	pEvent->lParam1 = lParam1;
+	pEvent->lParam2 = lParam2;
+	This->m_cbMediaEventsPut =
+		(This->m_cbMediaEventsPut + 1) % This->m_cbMediaEventsMax;
+
+	SetEvent( This->m_hMediaEvent );
+	if ( This->m_hwndEventNotify != (HWND)NULL &&
+		 This->m_lEventNotifyFlags == 0 )
+	{
+		PostMessageA(
+			This->m_hwndEventNotify,
+			This->m_lEventNotifyMsg,
+			(WPARAM)0,
+			(LPARAM)This->m_lEventNotifyParam );
+	}
+
+	hr = NOERROR;
+end:
+	LeaveCriticalSection( &This->m_csMediaEvents );
+
+	return hr;
 }
 
 
diff --git a/dlls/quartz/fgraph.h b/dlls/quartz/fgraph.h
index cdbd5d5..9329820 100644
--- a/dlls/quartz/fgraph.h
+++ b/dlls/quartz/fgraph.h
@@ -140,6 +140,10 @@
 	ULONG	m_cbMediaEventsPut;
 	ULONG	m_cbMediaEventsGet;
 	ULONG	m_cbMediaEventsMax;
+	HWND	m_hwndEventNotify;
+	long	m_lEventNotifyMsg;
+	LONG_PTR	m_lEventNotifyParam;
+	long	m_lEventNotifyFlags;
 	/* IMediaEventSink fields. */
 	/* IMediaPosition fields. */
 	/* IMediaSeeking fields. */
diff --git a/dlls/quartz/ifgraph.c b/dlls/quartz/ifgraph.c
index ecff0de..ae80747 100644
--- a/dlls/quartz/ifgraph.c
+++ b/dlls/quartz/ifgraph.c
@@ -80,6 +80,20 @@
 }
 
 
+static HRESULT CFilterGraph_GraphChanged( CFilterGraph* This )
+{
+	/* IDistributorNotify_NotifyGraphChange() */
+
+	IMediaEventSink_Notify(CFilterGraph_IMediaEventSink(This),
+			EC_GRAPH_CHANGED, 0, 0);
+	EnterCriticalSection( &This->m_csGraphVersion );
+	This->m_lGraphVersion ++;
+	LeaveCriticalSection( &This->m_csGraphVersion );
+
+	return NOERROR;
+}
+
+
 /****************************************************************************/
 
 static HRESULT WINAPI
@@ -220,12 +234,9 @@
 		goto end;
 	}
 
-	/* IDistributorNotify_NotifyGraphChange() */
-	IMediaEventSink_Notify(CFilterGraph_IMediaEventSink(This),
-			EC_GRAPH_CHANGED, 0, 0);
-	EnterCriticalSection( &This->m_csGraphVersion );
-	This->m_lGraphVersion ++;
-	LeaveCriticalSection( &This->m_csGraphVersion );
+	hr = CFilterGraph_GraphChanged(This);
+	if ( FAILED(hr) )
+		goto end;
 
 	hr = hrSucceeded;
 end:
@@ -269,12 +280,9 @@
 			This->m_pFilterList, (IUnknown*)pFilter );
 	}
 
-	/* IDistributorNotify_NotifyGraphChange() */
-	IMediaEventSink_Notify(CFilterGraph_IMediaEventSink(This),
-			EC_GRAPH_CHANGED, 0, 0);
-	EnterCriticalSection( &This->m_csGraphVersion );
-	This->m_lGraphVersion ++;
-	LeaveCriticalSection( &This->m_csGraphVersion );
+	hr = CFilterGraph_GraphChanged(This);
+	if ( FAILED(hr) )
+		goto end;
 
 end:;
 	QUARTZ_CompList_Unlock( This->m_pFilterList );
@@ -403,12 +411,9 @@
 		goto end;
 	}
 
-	/* IDistributorNotify_NotifyGraphChange() */
-	IMediaEventSink_Notify(CFilterGraph_IMediaEventSink(This),
-			EC_GRAPH_CHANGED, 0, 0);
-	EnterCriticalSection( &This->m_csGraphVersion );
-	This->m_lGraphVersion ++;
-	LeaveCriticalSection( &This->m_csGraphVersion );
+	hr = CFilterGraph_GraphChanged(This);
+	if ( FAILED(hr) )
+		goto end;
 
 end:
 	QUARTZ_CompList_Unlock( This->m_pFilterList );
@@ -454,14 +459,14 @@
 		IPin_Release(pConnTo);
 	}
 	hr = IPin_Disconnect(pPin);
+	if ( FAILED(hr) )
+		goto end;
 
-	/* IDistributorNotify_NotifyGraphChange() */
-	IMediaEventSink_Notify(CFilterGraph_IMediaEventSink(This),
-			EC_GRAPH_CHANGED, 0, 0);
-	EnterCriticalSection( &This->m_csGraphVersion );
-	This->m_lGraphVersion ++;
-	LeaveCriticalSection( &This->m_csGraphVersion );
+	hr = CFilterGraph_GraphChanged(This);
+	if ( FAILED(hr) )
+		goto end;
 
+end:
 	QUARTZ_CompList_Unlock( This->m_pFilterList );
 
 	return hr;
@@ -657,17 +662,13 @@
 IFilterGraph2_fnReconnectEx(IFilterGraph2* iface,IPin* pPin,const AM_MEDIA_TYPE* pmt)
 {
 	CFilterGraph_THIS(iface,fgraph);
+	HRESULT hr;
 
 	FIXME( "(%p)->(%p,%p) stub!\n",This,pPin,pmt );
 
 	/* reconnect asynchronously. */
 
-	/* IDistributorNotify_NotifyGraphChange() */
-	IMediaEventSink_Notify(CFilterGraph_IMediaEventSink(This),
-			EC_GRAPH_CHANGED, 0, 0);
-	EnterCriticalSection( &This->m_csGraphVersion );
-	This->m_lGraphVersion ++;
-	LeaveCriticalSection( &This->m_csGraphVersion );
+	hr = CFilterGraph_GraphChanged(This);
 
 	return E_NOTIMPL;
 }
diff --git a/dlls/quartz/imfilter.c b/dlls/quartz/imfilter.c
index bafb553..a602852 100644
--- a/dlls/quartz/imfilter.c
+++ b/dlls/quartz/imfilter.c
@@ -348,12 +348,8 @@
 
 	QUARTZ_CompList_Unlock( This->m_pFilterList );
 
-	if ( This->m_pClock != NULL )
-		IMediaEventSink_Notify(CFilterGraph_IMediaEventSink(This),
-			EC_CLOCK_CHANGED, 0, 0);
-	else
-		IMediaEventSink_Notify(CFilterGraph_IMediaEventSink(This),
-			EC_CLOCK_UNSET, 0, 0);
+	IMediaEventSink_Notify(CFilterGraph_IMediaEventSink(This),
+		EC_CLOCK_CHANGED, 0, 0);
 
 	LeaveCriticalSection( &This->m_csClock );
 
diff --git a/dlls/quartz/main.c b/dlls/quartz/main.c
index e10e47c..9b73116 100644
--- a/dlls/quartz/main.c
+++ b/dlls/quartz/main.c
@@ -12,6 +12,7 @@
 #include "wingdi.h"
 #include "winuser.h"
 #include "winnls.h"
+#include "mmsystem.h"
 #include "ole2.h"
 #include "wine/obj_oleaut.h"
 #include "strmif.h"
@@ -30,6 +31,7 @@
 #include "fmap.h"
 #include "fmap2.h"
 #include "seekpass.h"
+#include "audren.h"
 
 
 typedef struct QUARTZ_CLASSENTRY
@@ -74,6 +76,7 @@
 	{ &CLSID_FilterMapper, &QUARTZ_CreateFilterMapper },
 	{ &CLSID_FilterMapper2, &QUARTZ_CreateFilterMapper2 },
 	{ &CLSID_SeekingPassThru, &QUARTZ_CreateSeekingPassThru },
+	{ &CLSID_AudioRender, &QUARTZ_CreateAudioRenderer },
 	{ NULL, NULL },
 };
 
@@ -292,6 +295,8 @@
 	DWORD fdwReason,
 	LPVOID lpvReserved )
 {
+	TRACE("(%08x,%08lx,%p)\n",hInstDLL,fdwReason,lpvReserved);
+
 	switch ( fdwReason )
 	{
 	case DLL_PROCESS_ATTACH:
diff --git a/dlls/quartz/memalloc.c b/dlls/quartz/memalloc.c
index 6c2b473..59a9c50 100644
--- a/dlls/quartz/memalloc.c
+++ b/dlls/quartz/memalloc.c
@@ -287,6 +287,10 @@
 
 		if ( !bBlock )
 		{
+			QUARTZ_FreeMem(This->ppSamples);
+			This->ppSamples = NULL;
+			QUARTZ_FreeMem(This->pData);
+			This->pData = NULL;
 			hr = NOERROR;
 			break;
 		}
diff --git a/dlls/quartz/quartz.spec b/dlls/quartz/quartz.spec
index ca27c85..64e4926 100644
--- a/dlls/quartz/quartz.spec
+++ b/dlls/quartz/quartz.spec
@@ -4,7 +4,7 @@
 
 import oleaut32.dll
 import ole32.dll
-#import winmm.dll
+import winmm.dll
 import user32.dll
 #import gdi32.dll
 import advapi32.dll
diff --git a/dlls/quartz/sample.c b/dlls/quartz/sample.c
index 8094164..03a8f0e 100644
--- a/dlls/quartz/sample.c
+++ b/dlls/quartz/sample.c
@@ -491,6 +491,8 @@
 void QUARTZ_DestroyMemMediaSample(
 	CMemMediaSample* pSample )
 {
+	TRACE("(%p)\n",pSample);
+
 	QUARTZ_FreeObj( pSample );
 }
 
diff --git a/include/strmif.h b/include/strmif.h
index 293b550..9176e66 100644
--- a/include/strmif.h
+++ b/include/strmif.h
@@ -1331,7 +1331,7 @@
 #define IMediaSample_SetSyncPoint(p,a1) ICOM_CALL1(SetSyncPoint,p,a1)
 #define IMediaSample_IsPreroll(p) ICOM_CALL (IsPreroll,p)
 #define IMediaSample_SetPreroll(p,a1) ICOM_CALL1(SetPreroll,p,a1)
-#define IMediaSample_GetActualDataLength(p,a1) ICOM_CALL1(GetActualDataLength,p,a1)
+#define IMediaSample_GetActualDataLength(p) ICOM_CALL (GetActualDataLength,p)
 #define IMediaSample_SetActualDataLength(p,a1) ICOM_CALL1(SetActualDataLength,p,a1)
 #define IMediaSample_GetMediaType(p,a1) ICOM_CALL1(GetMediaType,p,a1)
 #define IMediaSample_SetMediaType(p,a1) ICOM_CALL1(SetMediaType,p,a1)
diff --git a/winedefault.reg b/winedefault.reg
index 46ba9b1..9dd68f3 100644
--- a/winedefault.reg
+++ b/winedefault.reg
@@ -318,6 +318,16 @@
 "FriendlyName"="Audio Renderers"
 
 
+# CLSID_AudioRender
+
+[HKEY_CLASSES_ROOT\CLSID\{e30629d1-27e5-11ce-875d-00608cb78066}\InprocServer32]
+@="quartz.dll"
+"ThreadingModel"="Both"
+
+[HKEY_CLASSES_ROOT\CLSID\{E0F158E1-CB04-11D0-BD4E-00A0C911CE86}\Instance\{e30629d1-27e5-11ce-875d-00608cb78066}]
+"CLSID"="{e30629d1-27e5-11ce-875d-00608cb78066}"
+"FriendlyName"="Waveout audio renderer"
+
 
 #
 # Entries for Mozilla ActiveX control support