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