Added LGPL standard comment, and copyright notices where necessary.
[wine] / dlls / quartz / audren.c
1 /*
2  * Audio Renderer (CLSID_AudioRender)
3  *
4  * FIXME
5  *  - implement IReferenceClock.
6  *  - implement seeking.
7  *
8  * Copyright (C) Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #include "config.h"
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "mmsystem.h"
32 #include "winerror.h"
33 #include "strmif.h"
34 #include "control.h"
35 #include "vfwmsgs.h"
36 #include "uuids.h"
37 #include "evcode.h"
38
39 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
41
42 #include "quartz_private.h"
43 #include "audren.h"
44 #include "seekpass.h"
45
46
47 static const WCHAR QUARTZ_AudioRender_Name[] =
48 { 'A','u','d','i','o',' ','R','e','n','d','e','r',0 };
49 static const WCHAR QUARTZ_AudioRenderPin_Name[] =
50 { 'I','n',0 };
51
52
53
54 /***************************************************************************
55  *
56  *      CAudioRendererImpl waveOut methods (internal)
57  *
58  */
59
60 static
61 HRESULT QUARTZ_HRESULT_From_MMRESULT( MMRESULT mr )
62 {
63         HRESULT hr = E_FAIL;
64
65         switch ( mr )
66         {
67         case MMSYSERR_NOERROR:
68                 hr = S_OK;
69                 break;
70         case MMSYSERR_NOMEM:
71                 hr = E_OUTOFMEMORY;
72                 break;
73         }
74
75         return hr;
76 }
77
78 void CAudioRendererImpl_waveOutEventCallback(
79         HWAVEOUT hwo, UINT uMsg,
80         DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
81 {
82         CAudioRendererImpl* This = (CAudioRendererImpl*)dwInstance;
83
84         if ( uMsg == WOM_DONE )
85                 SetEvent( This->m_hEventRender );
86 }
87
88 static
89 void CAudioRendererImpl_waveOutReset(
90         CAudioRendererImpl* This )
91 {
92         if ( !This->m_fWaveOutInit )
93                 return;
94
95         waveOutReset( This->m_hWaveOut );
96         SetEvent( This->m_hEventRender );
97 }
98
99 static
100 void CAudioRendererImpl_waveOutUninit(
101         CAudioRendererImpl* This )
102 {
103         DWORD i;
104
105         TRACE("(%p)\n",This);
106
107         if ( !This->m_fWaveOutInit )
108                 return;
109
110         waveOutReset( This->m_hWaveOut );
111         SetEvent( This->m_hEventRender );
112
113         for ( i = 0; i < WINE_QUARTZ_WAVEOUT_COUNT; i++ )
114         {
115                 if ( This->m_hdr[i].dwFlags & WHDR_PREPARED )
116                 {
117                         waveOutUnprepareHeader(
118                                 This->m_hWaveOut,
119                                 &This->m_hdr[i], sizeof(WAVEHDR) );
120                         This->m_hdr[i].dwFlags = 0;
121                 }
122                 if ( This->m_hdr[i].lpData != NULL )
123                 {
124                         QUARTZ_FreeMem( This->m_hdr[i].lpData );
125                         This->m_hdr[i].lpData = NULL;
126                 }
127         }
128
129         waveOutClose( This->m_hWaveOut );
130         This->m_hWaveOut = (HWAVEOUT)NULL;
131         if ( This->m_hEventRender != (HANDLE)NULL )
132         {
133                 CloseHandle( This->m_hEventRender );
134                 This->m_hEventRender = (HANDLE)NULL;
135         }
136
137         This->m_fWaveOutInit = FALSE;
138 }
139
140 static
141 HRESULT CAudioRendererImpl_waveOutInit(
142         CAudioRendererImpl* This, WAVEFORMATEX* pwfx )
143 {
144         MMRESULT mr;
145         HRESULT hr;
146         DWORD i;
147         DWORD dwBlockSize;
148
149         if ( This->m_fWaveOutInit )
150                 return NOERROR;
151
152         if ( pwfx == NULL )
153                 return E_POINTER;
154         if ( pwfx->nBlockAlign == 0 )
155                 return E_INVALIDARG;
156
157         This->m_hEventRender = (HANDLE)NULL;
158         This->m_hWaveOut = (HWAVEOUT)NULL;
159         This->m_dwBlockSize = 0;
160         This->m_phdrCur = NULL;
161         ZeroMemory( &This->m_hdr, sizeof(This->m_hdr) );
162
163
164         mr = waveOutOpen(
165                 &This->m_hWaveOut, WAVE_MAPPER, pwfx,
166                 (DWORD)CAudioRendererImpl_waveOutEventCallback, (DWORD)This,
167                 CALLBACK_FUNCTION );
168         hr = QUARTZ_HRESULT_From_MMRESULT( mr );
169         if ( FAILED(hr) )
170                 return hr;
171         This->m_fWaveOutInit = TRUE;
172
173         This->m_hEventRender = CreateEventA(
174                 NULL, TRUE, TRUE, NULL );
175         if ( This->m_hEventRender == (HANDLE)NULL )
176         {
177                 hr = E_OUTOFMEMORY;
178                 goto err;
179         }
180
181         dwBlockSize = pwfx->nAvgBytesPerSec / pwfx->nBlockAlign;
182         if ( dwBlockSize == 0 )
183                 dwBlockSize = 1;
184         dwBlockSize *= pwfx->nBlockAlign;
185         This->m_dwBlockSize = dwBlockSize;
186
187         for ( i = 0; i < WINE_QUARTZ_WAVEOUT_COUNT; i++ )
188         {
189                 This->m_hdr[i].lpData = (CHAR*)QUARTZ_AllocMem( dwBlockSize );
190                 if ( This->m_hdr[i].lpData == NULL )
191                 {
192                         hr = E_OUTOFMEMORY;
193                         goto err;
194                 }
195                 mr = waveOutPrepareHeader(
196                                 This->m_hWaveOut,
197                                 &This->m_hdr[i], sizeof(WAVEHDR) );
198                 hr = QUARTZ_HRESULT_From_MMRESULT( mr );
199                 if ( FAILED(hr) )
200                         goto err;
201                 This->m_hdr[i].dwFlags |= WHDR_DONE;
202                 This->m_hdr[i].dwBufferLength = dwBlockSize;
203                 This->m_hdr[i].dwUser = i;
204         }
205
206         return S_OK;
207 err:
208         CAudioRendererImpl_waveOutUninit(This);
209         return hr;
210 }
211
212 #if 0
213 /* FIXME: Not used for now */
214
215 static HRESULT CAudioRendererImpl_waveOutPause( CAudioRendererImpl* This )
216 {
217         if ( !This->m_fWaveOutInit )
218                 return E_UNEXPECTED;
219
220         return QUARTZ_HRESULT_From_MMRESULT( waveOutPause(
221                         This->m_hWaveOut ) );
222 }
223
224 static HRESULT CAudioRendererImpl_waveOutRun( CAudioRendererImpl* This )
225 {
226         if ( !This->m_fWaveOutInit )
227                 return E_UNEXPECTED;
228
229         return QUARTZ_HRESULT_From_MMRESULT( waveOutRestart(
230                         This->m_hWaveOut ) );
231 }
232 #endif
233
234 static
235 WAVEHDR* CAudioRendererImpl_waveOutGetBuffer(
236         CAudioRendererImpl* This )
237 {
238         DWORD i;
239
240         if ( !This->m_fWaveOutInit )
241                 return NULL;
242
243         if ( This->m_phdrCur != NULL )
244                 return This->m_phdrCur;
245
246         for ( i = 0; i < WINE_QUARTZ_WAVEOUT_COUNT; i++ )
247         {
248                 if ( This->m_hdr[i].dwFlags & WHDR_DONE )
249                 {
250                         This->m_phdrCur = &(This->m_hdr[i]);
251                         This->m_phdrCur->dwFlags &= ~WHDR_DONE;
252                         This->m_phdrCur->dwBufferLength = 0;
253                         return This->m_phdrCur;
254                 }
255         }
256
257         return NULL;
258 }
259
260 static
261 HRESULT CAudioRendererImpl_waveOutWriteData(
262         CAudioRendererImpl* This,
263         const BYTE* pData, DWORD cbData, DWORD* pcbWritten )
264 {
265         DWORD   cbAvail;
266
267         *pcbWritten = 0;
268
269         if ( !This->m_fWaveOutInit )
270                 return E_UNEXPECTED;
271
272         if ( cbData == 0 )
273                 return S_OK;
274
275         if ( CAudioRendererImpl_waveOutGetBuffer(This) == NULL )
276                 return S_FALSE;
277
278         cbAvail = This->m_dwBlockSize - This->m_phdrCur->dwBufferLength;
279         if ( cbAvail > cbData )
280                 cbAvail = cbData;
281         memcpy( This->m_phdrCur->lpData, pData, cbAvail );
282         pData += cbAvail;
283         cbData -= cbAvail;
284         This->m_phdrCur->dwBufferLength += cbAvail;
285
286         *pcbWritten = cbAvail;
287
288         return S_OK;
289 }
290
291 static
292 HRESULT CAudioRendererImpl_waveOutFlush(
293         CAudioRendererImpl* This )
294 {
295         MMRESULT mr;
296         HRESULT hr;
297
298         if ( !This->m_fWaveOutInit )
299                 return E_UNEXPECTED;
300         if ( This->m_phdrCur == NULL )
301                 return E_UNEXPECTED;
302
303         if ( This->m_phdrCur->dwBufferLength == 0 )
304                 return S_OK;
305
306         mr = waveOutWrite(
307                 This->m_hWaveOut,
308                 This->m_phdrCur, sizeof(WAVEHDR) );
309         hr = QUARTZ_HRESULT_From_MMRESULT( mr );
310         if ( FAILED(hr) )
311                 return hr;
312
313         This->m_phdrCur = NULL;
314         return S_OK;
315 }
316
317 #if 0
318 /* FIXME: Not used for now */
319
320 static
321 HRESULT CAudioRendererImpl_waveOutGetVolume(
322         CAudioRendererImpl* This,
323         DWORD* pdwLeft, DWORD* pdwRight )
324 {
325         MMRESULT mr;
326         HRESULT hr;
327         DWORD   dwVol;
328
329         if ( !This->m_fWaveOutInit )
330                 return E_UNEXPECTED;
331
332         mr = waveOutGetVolume(
333                 This->m_hWaveOut, &dwVol );
334         hr = QUARTZ_HRESULT_From_MMRESULT( mr );
335         if ( FAILED(hr) )
336                 return hr;
337
338         *pdwLeft = LOWORD(dwVol);
339         *pdwRight = HIWORD(dwVol);
340
341         return NOERROR;
342 }
343
344 static
345 HRESULT CAudioRendererImpl_waveOutSetVolume(
346         CAudioRendererImpl* This,
347         DWORD dwLeft, DWORD dwRight )
348 {
349         MMRESULT mr;
350         DWORD   dwVol;
351
352         if ( !This->m_fWaveOutInit )
353                 return E_UNEXPECTED;
354
355         dwVol = dwLeft | (dwRight<<16);
356
357         mr = waveOutSetVolume(
358                 This->m_hWaveOut, dwVol );
359         return QUARTZ_HRESULT_From_MMRESULT( mr );
360 }
361 #endif
362
363 /***************************************************************************
364  *
365  *      CAudioRendererImpl methods
366  *
367  */
368
369
370 static HRESULT CAudioRendererImpl_OnActive( CBaseFilterImpl* pImpl )
371 {
372         CAudioRendererImpl_THIS(pImpl,basefilter);
373         /* HRESULT hr; */
374
375         FIXME( "(%p)\n", This );
376
377         if ( This->pPin->pin.pmtConn == NULL )
378                 return NOERROR;
379
380         This->m_fInFlush = FALSE;
381
382         /* FIXME - don't work correctly.
383         hr = CAudioRendererImpl_waveOutRun(This);
384         if ( FAILED(hr) )
385                 return hr;
386         */
387
388         return NOERROR;
389 }
390
391 static HRESULT CAudioRendererImpl_OnInactive( CBaseFilterImpl* pImpl )
392 {
393         CAudioRendererImpl_THIS(pImpl,basefilter);
394         WAVEFORMATEX*   pwfx;
395         HRESULT hr;
396
397         FIXME( "(%p)\n", This );
398
399         if ( This->pPin->pin.pmtConn == NULL )
400                 return NOERROR;
401
402         pwfx = (WAVEFORMATEX*)This->pPin->pin.pmtConn->pbFormat;
403         if ( pwfx == NULL )
404                 return E_FAIL;
405
406         This->m_fInFlush = FALSE;
407
408         hr = CAudioRendererImpl_waveOutInit(This,pwfx);
409         if ( FAILED(hr) )
410                 return hr;
411
412         /* FIXME - may cause deadlock.
413         hr = CAudioRendererImpl_waveOutPause(This);
414         if ( FAILED(hr) )
415                 return hr;
416         */
417
418         return NOERROR;
419 }
420
421 static HRESULT CAudioRendererImpl_OnStop( CBaseFilterImpl* pImpl )
422 {
423         CAudioRendererImpl_THIS(pImpl,basefilter);
424
425         FIXME( "(%p)\n", This );
426
427         CAudioRendererImpl_waveOutUninit(This);
428
429         This->m_fInFlush = FALSE;
430
431         TRACE("returned\n" );
432
433         return NOERROR;
434 }
435
436 static const CBaseFilterHandlers filterhandlers =
437 {
438         CAudioRendererImpl_OnActive, /* pOnActive */
439         CAudioRendererImpl_OnInactive, /* pOnInactive */
440         CAudioRendererImpl_OnStop, /* pOnStop */
441 };
442
443 /***************************************************************************
444  *
445  *      CAudioRendererPinImpl methods
446  *
447  */
448
449 static HRESULT CAudioRendererPinImpl_OnDisconnect( CPinBaseImpl* pImpl )
450 {
451         CAudioRendererPinImpl_THIS(pImpl,pin);
452
453         TRACE("(%p)\n",This);
454
455         if ( This->meminput.pAllocator != NULL )
456         {
457                 IMemAllocator_Decommit(This->meminput.pAllocator);
458                 IMemAllocator_Release(This->meminput.pAllocator);
459                 This->meminput.pAllocator = NULL;
460         }
461
462         return NOERROR;
463 }
464
465
466 static HRESULT CAudioRendererPinImpl_CheckMediaType( CPinBaseImpl* pImpl, const AM_MEDIA_TYPE* pmt )
467 {
468         CAudioRendererPinImpl_THIS(pImpl,pin);
469         const WAVEFORMATEX* pwfx;
470
471         TRACE("(%p,%p)\n",This,pmt);
472
473         if ( !IsEqualGUID( &pmt->majortype, &MEDIATYPE_Audio ) )
474         {
475                 TRACE("not audio\n");
476                 return E_FAIL;
477         }
478         if ( !IsEqualGUID( &pmt->subtype, &MEDIASUBTYPE_NULL ) &&
479                  !IsEqualGUID( &pmt->subtype, &MEDIASUBTYPE_PCM ) )
480         {
481                 TRACE("not PCM\n");
482                 return E_FAIL;
483         }
484         if ( !IsEqualGUID( &pmt->formattype, &FORMAT_WaveFormatEx ) )
485         {
486                 TRACE("not WAVE\n");
487                 return E_FAIL;
488         }
489
490         TRACE("testing WAVE header\n");
491
492         if ( pmt->cbFormat < (sizeof(WAVEFORMATEX)-sizeof(WORD)) )
493                 return E_FAIL;
494
495         pwfx = (const WAVEFORMATEX*)pmt->pbFormat;
496         if ( pwfx == NULL )
497                 return E_FAIL;
498         if ( pwfx->wFormatTag != 1 )
499                 return E_FAIL;
500
501         TRACE("returned successfully.\n");
502
503         return NOERROR;
504 }
505
506 static HRESULT CAudioRendererPinImpl_Receive( CPinBaseImpl* pImpl, IMediaSample* pSample )
507 {
508         CAudioRendererPinImpl_THIS(pImpl,pin);
509         HRESULT hr;
510         BYTE*   pData = NULL;
511         DWORD   dwDataLength;
512         DWORD   dwWritten;
513
514         FIXME( "(%p,%p)\n",This,pSample );
515
516         if ( !This->pRender->m_fWaveOutInit )
517                 return E_UNEXPECTED;
518         if ( This->pRender->m_fInFlush )
519                 return S_FALSE;
520         if ( pSample == NULL )
521                 return E_POINTER;
522
523         hr = IMediaSample_GetPointer( pSample, &pData );
524         if ( FAILED(hr) )
525                 return hr;
526         dwDataLength = (DWORD)IMediaSample_GetActualDataLength( pSample );
527
528         while ( 1 )
529         {
530                 TRACE("trying to write %lu bytes\n",dwDataLength);
531
532                 ResetEvent( This->pRender->m_hEventRender );
533                 hr = CAudioRendererImpl_waveOutWriteData(
534                         This->pRender,pData,dwDataLength,&dwWritten);
535                 if ( FAILED(hr) )
536                         break;
537                 if ( hr == S_FALSE )
538                 {
539                         WaitForSingleObject( This->pRender->m_hEventRender, INFINITE );
540                         continue;
541                 }
542                 pData += dwWritten;
543                 dwDataLength -= dwWritten;
544                 hr = CAudioRendererImpl_waveOutFlush(This->pRender);
545                 if ( FAILED(hr) )
546                         break;
547                 if ( dwDataLength == 0 )
548                         break;
549         }
550
551         return hr;
552 }
553
554 static HRESULT CAudioRendererPinImpl_ReceiveCanBlock( CPinBaseImpl* pImpl )
555 {
556         CAudioRendererPinImpl_THIS(pImpl,pin);
557
558         TRACE( "(%p)\n", This );
559
560         /* might block. */
561         return S_OK;
562 }
563
564 static HRESULT CAudioRendererPinImpl_EndOfStream( CPinBaseImpl* pImpl )
565 {
566         CAudioRendererPinImpl_THIS(pImpl,pin);
567
568         FIXME( "(%p)\n", This );
569
570         This->pRender->m_fInFlush = FALSE;
571
572         /* FIXME - don't notify twice until stopped or seeked. */
573         return CBaseFilterImpl_MediaEventNotify(
574                 &This->pRender->basefilter, EC_COMPLETE,
575                 (LONG_PTR)S_OK, (LONG_PTR)(IBaseFilter*)(This->pRender) );
576 }
577
578 static HRESULT CAudioRendererPinImpl_BeginFlush( CPinBaseImpl* pImpl )
579 {
580         CAudioRendererPinImpl_THIS(pImpl,pin);
581
582         FIXME( "(%p)\n", This );
583
584         This->pRender->m_fInFlush = TRUE;
585         CAudioRendererImpl_waveOutReset(This->pRender);
586
587         return NOERROR;
588 }
589
590 static HRESULT CAudioRendererPinImpl_EndFlush( CPinBaseImpl* pImpl )
591 {
592         CAudioRendererPinImpl_THIS(pImpl,pin);
593
594         FIXME( "(%p)\n", This );
595
596         This->pRender->m_fInFlush = FALSE;
597
598         return NOERROR;
599 }
600
601 static HRESULT CAudioRendererPinImpl_NewSegment( CPinBaseImpl* pImpl, REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, double rate )
602 {
603         CAudioRendererPinImpl_THIS(pImpl,pin);
604
605         FIXME( "(%p)\n", This );
606
607         This->pRender->m_fInFlush = FALSE;
608
609         return NOERROR;
610 }
611
612 static const CBasePinHandlers pinhandlers =
613 {
614         NULL, /* pOnPreConnect */
615         NULL, /* pOnPostConnect */
616         CAudioRendererPinImpl_OnDisconnect, /* pOnDisconnect */
617         CAudioRendererPinImpl_CheckMediaType, /* pCheckMediaType */
618         NULL, /* pQualityNotify */
619         CAudioRendererPinImpl_Receive, /* pReceive */
620         CAudioRendererPinImpl_ReceiveCanBlock, /* pReceiveCanBlock */
621         CAudioRendererPinImpl_EndOfStream, /* pEndOfStream */
622         CAudioRendererPinImpl_BeginFlush, /* pBeginFlush */
623         CAudioRendererPinImpl_EndFlush, /* pEndFlush */
624         CAudioRendererPinImpl_NewSegment, /* pNewSegment */
625 };
626
627
628 /***************************************************************************
629  *
630  *      new/delete CAudioRendererImpl
631  *
632  */
633
634 /* can I use offsetof safely? - FIXME? */
635 static QUARTZ_IFEntry FilterIFEntries[] =
636 {
637   { &IID_IPersist, offsetof(CAudioRendererImpl,basefilter)-offsetof(CAudioRendererImpl,unk) },
638   { &IID_IMediaFilter, offsetof(CAudioRendererImpl,basefilter)-offsetof(CAudioRendererImpl,unk) },
639   { &IID_IBaseFilter, offsetof(CAudioRendererImpl,basefilter)-offsetof(CAudioRendererImpl,unk) },
640   { &IID_IBasicAudio, offsetof(CAudioRendererImpl,basaud)-offsetof(CAudioRendererImpl,unk) },
641 };
642
643 static HRESULT CAudioRendererImpl_OnQueryInterface(
644         IUnknown* punk, const IID* piid, void** ppobj )
645 {
646         CAudioRendererImpl_THIS(punk,unk);
647
648         if ( This->pSeekPass == NULL )
649                 return E_NOINTERFACE;
650
651         if ( IsEqualGUID( &IID_IMediaPosition, piid ) ||
652                  IsEqualGUID( &IID_IMediaSeeking, piid ) )
653         {
654                 TRACE( "IMediaSeeking(or IMediaPosition) is queried\n" );
655                 return IUnknown_QueryInterface( (IUnknown*)(&This->pSeekPass->unk), piid, ppobj );
656         }
657
658         return E_NOINTERFACE;
659 }
660
661 static void QUARTZ_DestroyAudioRenderer(IUnknown* punk)
662 {
663         CAudioRendererImpl_THIS(punk,unk);
664
665         TRACE( "(%p)\n", This );
666
667         if ( This->pPin != NULL )
668         {
669                 IUnknown_Release(This->pPin->unk.punkControl);
670                 This->pPin = NULL;
671         }
672         if ( This->pSeekPass != NULL )
673         {
674                 IUnknown_Release((IUnknown*)&This->pSeekPass->unk);
675                 This->pSeekPass = NULL;
676         }
677
678         CAudioRendererImpl_UninitIBasicAudio(This);
679         CBaseFilterImpl_UninitIBaseFilter(&This->basefilter);
680
681         DeleteCriticalSection( &This->m_csReceive );
682 }
683
684 HRESULT QUARTZ_CreateAudioRenderer(IUnknown* punkOuter,void** ppobj)
685 {
686         CAudioRendererImpl*     This = NULL;
687         HRESULT hr;
688
689         TRACE("(%p,%p)\n",punkOuter,ppobj);
690
691         This = (CAudioRendererImpl*)
692                 QUARTZ_AllocObj( sizeof(CAudioRendererImpl) );
693         if ( This == NULL )
694                 return E_OUTOFMEMORY;
695         This->pSeekPass = NULL;
696         This->pPin = NULL;
697         This->m_fInFlush = FALSE;
698         This->m_fWaveOutInit = FALSE;
699         This->m_hEventRender = (HANDLE)NULL;
700
701         QUARTZ_IUnkInit( &This->unk, punkOuter );
702         This->qiext.pNext = NULL;
703         This->qiext.pOnQueryInterface = &CAudioRendererImpl_OnQueryInterface;
704         QUARTZ_IUnkAddDelegation( &This->unk, &This->qiext );
705
706         hr = CBaseFilterImpl_InitIBaseFilter(
707                 &This->basefilter,
708                 This->unk.punkControl,
709                 &CLSID_AudioRender,
710                 QUARTZ_AudioRender_Name,
711                 &filterhandlers );
712         if ( SUCCEEDED(hr) )
713         {
714                 hr = CAudioRendererImpl_InitIBasicAudio(This);
715                 if ( FAILED(hr) )
716                 {
717                         CBaseFilterImpl_UninitIBaseFilter(&This->basefilter);
718                 }
719         }
720
721         if ( FAILED(hr) )
722         {
723                 QUARTZ_FreeObj(This);
724                 return hr;
725         }
726
727         This->unk.pEntries = FilterIFEntries;
728         This->unk.dwEntries = sizeof(FilterIFEntries)/sizeof(FilterIFEntries[0]);
729         This->unk.pOnFinalRelease = QUARTZ_DestroyAudioRenderer;
730
731         InitializeCriticalSection( &This->m_csReceive );
732
733         hr = QUARTZ_CreateAudioRendererPin(
734                 This,
735                 &This->basefilter.csFilter,
736                 &This->m_csReceive,
737                 &This->pPin );
738         if ( SUCCEEDED(hr) )
739                 hr = QUARTZ_CompList_AddComp(
740                         This->basefilter.pInPins,
741                         (IUnknown*)&This->pPin->pin,
742                         NULL, 0 );
743         if ( SUCCEEDED(hr) )
744                 hr = QUARTZ_CreateSeekingPassThruInternal(
745                         (IUnknown*)&(This->unk), &This->pSeekPass,
746                         TRUE, (IPin*)&(This->pPin->pin) );
747
748         if ( FAILED(hr) )
749         {
750                 IUnknown_Release( This->unk.punkControl );
751                 return hr;
752         }
753
754         *ppobj = (void*)&(This->unk);
755
756         return S_OK;
757 }
758
759
760 /***************************************************************************
761  *
762  *      new/delete CAudioRendererPinImpl
763  *
764  */
765
766 /* can I use offsetof safely? - FIXME? */
767 static QUARTZ_IFEntry PinIFEntries[] =
768 {
769   { &IID_IPin, offsetof(CAudioRendererPinImpl,pin)-offsetof(CAudioRendererPinImpl,unk) },
770   { &IID_IMemInputPin, offsetof(CAudioRendererPinImpl,meminput)-offsetof(CAudioRendererPinImpl,unk) },
771 };
772
773 static void QUARTZ_DestroyAudioRendererPin(IUnknown* punk)
774 {
775         CAudioRendererPinImpl_THIS(punk,unk);
776
777         TRACE( "(%p)\n", This );
778
779         CPinBaseImpl_UninitIPin( &This->pin );
780         CMemInputPinBaseImpl_UninitIMemInputPin( &This->meminput );
781 }
782
783 HRESULT QUARTZ_CreateAudioRendererPin(
784         CAudioRendererImpl* pFilter,
785         CRITICAL_SECTION* pcsPin,
786         CRITICAL_SECTION* pcsPinReceive,
787         CAudioRendererPinImpl** ppPin)
788 {
789         CAudioRendererPinImpl*  This = NULL;
790         HRESULT hr;
791
792         TRACE("(%p,%p,%p,%p)\n",pFilter,pcsPin,pcsPinReceive,ppPin);
793
794         This = (CAudioRendererPinImpl*)
795                 QUARTZ_AllocObj( sizeof(CAudioRendererPinImpl) );
796         if ( This == NULL )
797                 return E_OUTOFMEMORY;
798
799         QUARTZ_IUnkInit( &This->unk, NULL );
800         This->pRender = pFilter;
801
802         hr = CPinBaseImpl_InitIPin(
803                 &This->pin,
804                 This->unk.punkControl,
805                 pcsPin, pcsPinReceive,
806                 &pFilter->basefilter,
807                 QUARTZ_AudioRenderPin_Name,
808                 FALSE,
809                 &pinhandlers );
810
811         if ( SUCCEEDED(hr) )
812         {
813                 hr = CMemInputPinBaseImpl_InitIMemInputPin(
814                         &This->meminput,
815                         This->unk.punkControl,
816                         &This->pin );
817                 if ( FAILED(hr) )
818                 {
819                         CPinBaseImpl_UninitIPin( &This->pin );
820                 }
821         }
822
823         if ( FAILED(hr) )
824         {
825                 QUARTZ_FreeObj(This);
826                 return hr;
827         }
828
829         This->unk.pEntries = PinIFEntries;
830         This->unk.dwEntries = sizeof(PinIFEntries)/sizeof(PinIFEntries[0]);
831         This->unk.pOnFinalRelease = QUARTZ_DestroyAudioRendererPin;
832
833         *ppPin = This;
834
835         TRACE("returned successfully.\n");
836
837         return S_OK;
838 }
839
840
841 /***************************************************************************
842  *
843  *      CAudioRendererImpl::IBasicAudio
844  *
845  */
846
847 static HRESULT WINAPI
848 IBasicAudio_fnQueryInterface(IBasicAudio* iface,REFIID riid,void** ppobj)
849 {
850         CAudioRendererImpl_THIS(iface,basaud);
851
852         TRACE("(%p)->()\n",This);
853
854         return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
855 }
856
857 static ULONG WINAPI
858 IBasicAudio_fnAddRef(IBasicAudio* iface)
859 {
860         CAudioRendererImpl_THIS(iface,basaud);
861
862         TRACE("(%p)->()\n",This);
863
864         return IUnknown_AddRef(This->unk.punkControl);
865 }
866
867 static ULONG WINAPI
868 IBasicAudio_fnRelease(IBasicAudio* iface)
869 {
870         CAudioRendererImpl_THIS(iface,basaud);
871
872         TRACE("(%p)->()\n",This);
873
874         return IUnknown_Release(This->unk.punkControl);
875 }
876
877 static HRESULT WINAPI
878 IBasicAudio_fnGetTypeInfoCount(IBasicAudio* iface,UINT* pcTypeInfo)
879 {
880         CAudioRendererImpl_THIS(iface,basaud);
881
882         FIXME("(%p)->()\n",This);
883
884         return E_NOTIMPL;
885 }
886
887 static HRESULT WINAPI
888 IBasicAudio_fnGetTypeInfo(IBasicAudio* iface,UINT iTypeInfo, LCID lcid, ITypeInfo** ppobj)
889 {
890         CAudioRendererImpl_THIS(iface,basaud);
891
892         FIXME("(%p)->()\n",This);
893
894         return E_NOTIMPL;
895 }
896
897 static HRESULT WINAPI
898 IBasicAudio_fnGetIDsOfNames(IBasicAudio* iface,REFIID riid, LPOLESTR* ppwszName, UINT cNames, LCID lcid, DISPID* pDispId)
899 {
900         CAudioRendererImpl_THIS(iface,basaud);
901
902         FIXME("(%p)->()\n",This);
903
904         return E_NOTIMPL;
905 }
906
907 static HRESULT WINAPI
908 IBasicAudio_fnInvoke(IBasicAudio* iface,DISPID DispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarRes, EXCEPINFO* pExcepInfo, UINT* puArgErr)
909 {
910         CAudioRendererImpl_THIS(iface,basaud);
911
912         FIXME("(%p)->()\n",This);
913
914         return E_NOTIMPL;
915 }
916
917
918 static HRESULT WINAPI
919 IBasicAudio_fnput_Volume(IBasicAudio* iface,long lVol)
920 {
921         CAudioRendererImpl_THIS(iface,basaud);
922
923         FIXME("(%p)->()\n",This);
924
925         return E_NOTIMPL;
926 }
927
928 static HRESULT WINAPI
929 IBasicAudio_fnget_Volume(IBasicAudio* iface,long* plVol)
930 {
931         CAudioRendererImpl_THIS(iface,basaud);
932
933         FIXME("(%p)->()\n",This);
934
935         return E_NOTIMPL;
936 }
937
938 static HRESULT WINAPI
939 IBasicAudio_fnput_Balance(IBasicAudio* iface,long lBalance)
940 {
941         CAudioRendererImpl_THIS(iface,basaud);
942
943         FIXME("(%p)->()\n",This);
944
945         return E_NOTIMPL;
946 }
947
948 static HRESULT WINAPI
949 IBasicAudio_fnget_Balance(IBasicAudio* iface,long* plBalance)
950 {
951         CAudioRendererImpl_THIS(iface,basaud);
952
953         FIXME("(%p)->()\n",This);
954
955         return E_NOTIMPL;
956 }
957
958
959 static ICOM_VTABLE(IBasicAudio) ibasicaudio =
960 {
961         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
962         /* IUnknown fields */
963         IBasicAudio_fnQueryInterface,
964         IBasicAudio_fnAddRef,
965         IBasicAudio_fnRelease,
966         /* IDispatch fields */
967         IBasicAudio_fnGetTypeInfoCount,
968         IBasicAudio_fnGetTypeInfo,
969         IBasicAudio_fnGetIDsOfNames,
970         IBasicAudio_fnInvoke,
971         /* IBasicAudio fields */
972         IBasicAudio_fnput_Volume,
973         IBasicAudio_fnget_Volume,
974         IBasicAudio_fnput_Balance,
975         IBasicAudio_fnget_Balance,
976 };
977
978
979 HRESULT CAudioRendererImpl_InitIBasicAudio( CAudioRendererImpl* This )
980 {
981         TRACE("(%p)\n",This);
982         ICOM_VTBL(&This->basaud) = &ibasicaudio;
983
984         return NOERROR;
985 }
986
987 void CAudioRendererImpl_UninitIBasicAudio( CAudioRendererImpl* This )
988 {
989         TRACE("(%p)\n",This);
990 }
991