wined3d: IWineD3DBuffer_Unmap() can't fail.
[wine] / dlls / quartz / dsoundrender.c
1 /*
2  * Direct Sound Audio Renderer
3  *
4  * Copyright 2004 Christian Costa
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22
23 #include "quartz_private.h"
24 #include "control_private.h"
25 #include "pin.h"
26
27 #include "uuids.h"
28 #include "vfwmsgs.h"
29 #include "windef.h"
30 #include "winbase.h"
31 #include "dshow.h"
32 #include "evcode.h"
33 #include "strmif.h"
34 #include "dsound.h"
35 #include "amaudio.h"
36
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
41
42 static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0};
43
44 static const IBaseFilterVtbl DSoundRender_Vtbl;
45 static const IPinVtbl DSoundRender_InputPin_Vtbl;
46 static const IBasicAudioVtbl IBasicAudio_Vtbl;
47 static const IReferenceClockVtbl IReferenceClock_Vtbl;
48 static const IMediaSeekingVtbl IMediaSeeking_Vtbl;
49 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl;
50
51 typedef struct DSoundRenderImpl
52 {
53     BaseFilter filter;
54
55     const IBasicAudioVtbl *IBasicAudio_vtbl;
56     const IReferenceClockVtbl *IReferenceClock_vtbl;
57     const IAMDirectSoundVtbl *IAMDirectSound_vtbl;
58     IUnknown *seekthru_unk;
59
60     REFERENCE_TIME rtLastStop;
61
62     BaseInputPin * pInputPin;
63
64     IDirectSound8 *dsound;
65     LPDIRECTSOUNDBUFFER dsbuffer;
66     DWORD buf_size;
67     DWORD write_pos;
68     DWORD write_loops;
69
70     DWORD last_play_pos;
71     DWORD play_loops;
72     DWORD in_loop;
73
74     REFERENCE_TIME play_time;
75
76     HANDLE state_change, blocked;
77
78     LONG volume;
79     LONG pan;
80 } DSoundRenderImpl;
81
82 static inline HRESULT DSoundRender_GetPos(DSoundRenderImpl *This, DWORD *pPlayPos, REFERENCE_TIME *pRefTime)
83 {
84     HRESULT hr;
85
86     EnterCriticalSection(&This->filter.csFilter);
87     {
88         DWORD state;
89         DWORD write_pos;
90
91         hr = IDirectSoundBuffer_GetStatus(This->dsbuffer, &state);
92         if (SUCCEEDED(hr) && !(state & DSBSTATUS_PLAYING) && This->filter.state == State_Running)
93         {
94             TRACE("Not playing, kickstarting the engine\n");
95
96             hr = IDirectSoundBuffer_Play(This->dsbuffer, 0, 0, DSBPLAY_LOOPING);
97             if (FAILED(hr))
98                 ERR("Can't play sound buffer (%x)\n", hr);
99         }
100
101         if (SUCCEEDED(hr))
102             hr = IDirectSoundBuffer_GetCurrentPosition(This->dsbuffer, pPlayPos, &write_pos);
103         if (hr == S_OK)
104         {
105             DWORD play_pos = *pPlayPos;
106
107             if (play_pos < This->last_play_pos)
108                 This->play_loops++;
109             This->last_play_pos = play_pos;
110
111             /* If we really fell behind, start at the next possible position
112              * Also happens when just starting playback for the first time,
113              * or when flushing
114              */
115             if ((This->play_loops*This->buf_size)+play_pos >=
116                 (This->write_loops*This->buf_size)+This->write_pos)
117                 This->write_pos = write_pos;
118
119             if (pRefTime)
120             {
121                 REFERENCE_TIME play_time;
122                 play_time = ((REFERENCE_TIME)This->play_loops*10000000) +
123                             ((REFERENCE_TIME)play_pos*10000000/This->buf_size);
124
125                 /* Don't let time run backwards */
126                 if(play_time-This->play_time > 0)
127                     This->play_time = play_time;
128                 else
129                     hr = S_FALSE;
130
131                 *pRefTime = This->play_time;
132             }
133         }
134     }
135     LeaveCriticalSection(&This->filter.csFilter);
136
137     return hr;
138 }
139
140 static HRESULT DSoundRender_SendSampleData(DSoundRenderImpl* This, const BYTE *data, DWORD size)
141 {
142     HRESULT hr = S_OK;
143     LPBYTE lpbuf1 = NULL;
144     LPBYTE lpbuf2 = NULL;
145     DWORD dwsize1 = 0;
146     DWORD dwsize2 = 0;
147     DWORD size2;
148     DWORD play_pos,buf_free;
149
150     do {
151
152         hr = DSoundRender_GetPos(This, &play_pos, NULL);
153         if (hr != DS_OK)
154         {
155             ERR("GetPos returned error: %x\n", hr);
156             break;
157         }
158         if (This->write_pos <= play_pos)
159              buf_free = play_pos-This->write_pos;
160         else
161              buf_free = This->buf_size - This->write_pos + play_pos;
162
163         /* Wait for enough of the buffer to empty before filling it */
164         if(buf_free < This->buf_size/20)
165         {
166             DWORD ret;
167             This->in_loop = 1;
168             LeaveCriticalSection(&This->filter.csFilter);
169             ret = WaitForSingleObject(This->blocked, 50);
170             if (ret != WAIT_TIMEOUT)
171                 ERR("%x\n", ret);
172             EnterCriticalSection(&This->filter.csFilter);
173             This->in_loop = 0;
174             if (This->pInputPin->flushing)
175                 return VFW_E_WRONG_STATE;
176             if (This->filter.state == State_Stopped)
177                 return VFW_E_WRONG_STATE;
178             continue;
179         }
180
181         size2 = min(buf_free, size);
182         hr = IDirectSoundBuffer_Lock(This->dsbuffer, This->write_pos, size2, (LPVOID *)&lpbuf1, &dwsize1, (LPVOID *)&lpbuf2, &dwsize2, 0);
183         if (hr != DS_OK) {
184             ERR("Unable to lock sound buffer! (%x)\n", hr);
185             break;
186         }
187         /* TRACE("write_pos=%d, size=%d, sz1=%d, sz2=%d\n", This->write_pos, size2, dwsize1, dwsize2); */
188
189         memcpy(lpbuf1, data, dwsize1);
190         if (dwsize2)
191             memcpy(lpbuf2, data + dwsize1, dwsize2);
192
193         hr = IDirectSoundBuffer_Unlock(This->dsbuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
194         if (hr != DS_OK)
195             ERR("Unable to unlock sound buffer! (%x)\n", hr);
196
197         size -= dwsize1 + dwsize2;
198         data += dwsize1 + dwsize2;
199         This->write_pos += dwsize1 + dwsize2;
200         if (This->write_pos >= This->buf_size)
201         {
202             This->write_pos -= This->buf_size;
203             This->write_loops++;
204         }
205     } while (size && This->filter.state == State_Running);
206
207     return hr;
208 }
209
210 static HRESULT WINAPI DSoundRender_Receive(BaseInputPin *pin, IMediaSample * pSample)
211 {
212     DSoundRenderImpl *This = (DSoundRenderImpl*)pin->pin.pinInfo.pFilter;
213     LPBYTE pbSrcStream = NULL;
214     LONG cbSrcStream = 0;
215     REFERENCE_TIME tStart, tStop;
216     HRESULT hr;
217     AM_MEDIA_TYPE *amt;
218
219     TRACE("%p %p\n", pin, pSample);
220
221     /* Slightly incorrect, Pause completes when a frame is received so we should signal
222      * pause completion here, but for sound playing a single frame doesn't make sense
223      */
224
225     EnterCriticalSection(&This->filter.csFilter);
226
227     if (This->pInputPin->end_of_stream || This->pInputPin->flushing)
228     {
229         LeaveCriticalSection(&This->filter.csFilter);
230         return S_FALSE;
231     }
232
233     if (This->filter.state == State_Stopped)
234     {
235         LeaveCriticalSection(&This->filter.csFilter);
236         return VFW_E_WRONG_STATE;
237     }
238
239     if (IMediaSample_GetMediaType(pSample, &amt) == S_OK)
240     {
241         AM_MEDIA_TYPE *orig = &This->pInputPin->pin.mtCurrent;
242         WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat;
243         WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat;
244
245         if (origfmt->wFormatTag == newfmt->wFormatTag &&
246             origfmt->nChannels == newfmt->nChannels &&
247             origfmt->nBlockAlign == newfmt->nBlockAlign &&
248             origfmt->wBitsPerSample == newfmt->wBitsPerSample &&
249             origfmt->cbSize ==  newfmt->cbSize)
250         {
251             if (origfmt->nSamplesPerSec != newfmt->nSamplesPerSec)
252             {
253                 hr = IDirectSoundBuffer_SetFrequency(This->dsbuffer,
254                                                      newfmt->nSamplesPerSec);
255                 if (FAILED(hr))
256                 {
257                     LeaveCriticalSection(&This->filter.csFilter);
258                     return VFW_E_TYPE_NOT_ACCEPTED;
259                 }
260                 FreeMediaType(orig);
261                 CopyMediaType(orig, amt);
262                 IMediaSample_SetMediaType(pSample, NULL);
263             }
264         }
265         else
266         {
267             LeaveCriticalSection(&This->filter.csFilter);
268             return VFW_E_TYPE_NOT_ACCEPTED;
269         }
270     }
271
272     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
273     if (FAILED(hr))
274     {
275         ERR("Cannot get pointer to sample data (%x)\n", hr);
276         LeaveCriticalSection(&This->filter.csFilter);
277         return hr;
278     }
279
280     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
281     if (FAILED(hr))
282         ERR("Cannot get sample time (%x)\n", hr);
283     else
284         MediaSeekingPassThru_RegisterMediaTime(This->seekthru_unk, tStart);
285
286     if (This->rtLastStop != tStart && (IMediaSample_IsDiscontinuity(pSample) == S_FALSE))
287         WARN("Unexpected discontinuity: Last: %u.%03u, tStart: %u.%03u\n",
288             (DWORD)(This->rtLastStop / 10000000), (DWORD)((This->rtLastStop / 10000)%1000),
289             (DWORD)(tStart / 10000000), (DWORD)((tStart / 10000)%1000));
290     This->rtLastStop = tStop;
291
292     if (IMediaSample_IsPreroll(pSample) == S_OK)
293     {
294         TRACE("Preroll!\n");
295         LeaveCriticalSection(&This->filter.csFilter);
296         return S_OK;
297     }
298
299     if (This->filter.state == State_Paused)
300     {
301         SetEvent(This->state_change);
302         LeaveCriticalSection(&This->filter.csFilter);
303         WaitForSingleObject(This->blocked, INFINITE);
304         EnterCriticalSection(&This->filter.csFilter);
305         if (This->filter.state == State_Stopped)
306         {
307             LeaveCriticalSection(&This->filter.csFilter);
308             return VFW_E_WRONG_STATE;
309         }
310
311         if (This->filter.state == State_Paused)
312         {
313             /* Assuming we return because of flushing */
314             TRACE("Flushing\n");
315             LeaveCriticalSection(&This->filter.csFilter);
316             return S_OK;
317         }
318         SetEvent(This->state_change);
319     }
320
321     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
322     TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream);
323
324 #if 0 /* For debugging purpose */
325     {
326         int i;
327         for(i = 0; i < cbSrcStream; i++)
328         {
329             if ((i!=0) && !(i%16))
330                 TRACE("\n");
331             TRACE("%02x ", pbSrcStream[i]);
332         }
333         TRACE("\n");
334     }
335 #endif
336
337     hr = DSoundRender_SendSampleData(This, pbSrcStream, cbSrcStream);
338     SetEvent(This->state_change);
339     LeaveCriticalSection(&This->filter.csFilter);
340     return hr;
341 }
342
343 static HRESULT WINAPI DSoundRender_CheckMediaType(BasePin *iface, const AM_MEDIA_TYPE * pmt)
344 {
345     WAVEFORMATEX* format;
346
347     if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
348         return S_FALSE;
349
350     format =  (WAVEFORMATEX*)pmt->pbFormat;
351     TRACE("Format = %p\n", format);
352     TRACE("wFormatTag = %x %x\n", format->wFormatTag, WAVE_FORMAT_PCM);
353     TRACE("nChannels = %d\n", format->nChannels);
354     TRACE("nSamplesPerSec = %d\n", format->nAvgBytesPerSec);
355     TRACE("nAvgBytesPerSec = %d\n", format->nAvgBytesPerSec);
356     TRACE("nBlockAlign = %d\n", format->nBlockAlign);
357     TRACE("wBitsPerSample = %d\n", format->wBitsPerSample);
358
359     if (!IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM))
360         return S_FALSE;
361
362     return S_OK;
363 }
364
365 static IPin* WINAPI DSoundRender_GetPin(BaseFilter *iface, int pos)
366 {
367     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
368
369     if (pos >= 1 || pos < 0)
370         return NULL;
371
372     IPin_AddRef((IPin*)This->pInputPin);
373     return (IPin*)This->pInputPin;
374 }
375
376 static LONG WINAPI DSoundRender_GetPinCount(BaseFilter *iface)
377 {
378     /* Our pins are static */
379     return 1;
380 }
381
382 static const BaseFilterFuncTable BaseFuncTable = {
383     DSoundRender_GetPin,
384     DSoundRender_GetPinCount
385 };
386
387 static const  BasePinFuncTable input_BaseFuncTable = {
388     DSoundRender_CheckMediaType,
389     NULL,
390     BasePinImpl_GetMediaTypeVersion,
391     BasePinImpl_GetMediaType
392 };
393
394 static const BaseInputPinFuncTable input_BaseInputFuncTable = {
395     DSoundRender_Receive
396 };
397
398
399 HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv)
400 {
401     HRESULT hr;
402     PIN_INFO piInput;
403     DSoundRenderImpl * pDSoundRender;
404
405     TRACE("(%p, %p)\n", pUnkOuter, ppv);
406
407     *ppv = NULL;
408
409     if (pUnkOuter)
410         return CLASS_E_NOAGGREGATION;
411     
412     pDSoundRender = CoTaskMemAlloc(sizeof(DSoundRenderImpl));
413     if (!pDSoundRender)
414         return E_OUTOFMEMORY;
415     ZeroMemory(pDSoundRender, sizeof(DSoundRenderImpl));
416
417     BaseFilter_Init(&pDSoundRender->filter, &DSoundRender_Vtbl, &CLSID_DSoundRender, (DWORD_PTR)(__FILE__ ": DSoundRenderImpl.csFilter"), &BaseFuncTable);
418
419     pDSoundRender->IBasicAudio_vtbl = &IBasicAudio_Vtbl;
420     pDSoundRender->IReferenceClock_vtbl = &IReferenceClock_Vtbl;
421     pDSoundRender->IAMDirectSound_vtbl = &IAMDirectSound_Vtbl;
422
423     /* construct input pin */
424     piInput.dir = PINDIR_INPUT;
425     piInput.pFilter = (IBaseFilter *)pDSoundRender;
426     lstrcpynW(piInput.achName, wcsInputPinName, sizeof(piInput.achName) / sizeof(piInput.achName[0]));
427     hr = BaseInputPin_Construct(&DSoundRender_InputPin_Vtbl, &piInput, &input_BaseFuncTable, &input_BaseInputFuncTable, &pDSoundRender->filter.csFilter, NULL, (IPin **)&pDSoundRender->pInputPin);
428
429     if (SUCCEEDED(hr))
430     {
431         hr = DirectSoundCreate8(NULL, &pDSoundRender->dsound, NULL);
432         if (FAILED(hr))
433             ERR("Cannot create Direct Sound object (%x)\n", hr);
434         else
435             IDirectSound_SetCooperativeLevel(pDSoundRender->dsound, GetDesktopWindow(), DSSCL_PRIORITY);
436     }
437
438     if (SUCCEEDED(hr))
439     {
440         ISeekingPassThru *passthru;
441         pDSoundRender->state_change = CreateEventW(NULL, TRUE, TRUE, NULL);
442         pDSoundRender->blocked = CreateEventW(NULL, TRUE, TRUE, NULL);
443         hr = CoCreateInstance(&CLSID_SeekingPassThru, (IUnknown*)pDSoundRender, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&pDSoundRender->seekthru_unk);
444         if (!pDSoundRender->state_change || !pDSoundRender->blocked || FAILED(hr))
445         {
446             IUnknown_Release((IUnknown *)pDSoundRender);
447             return HRESULT_FROM_WIN32(GetLastError());
448         }
449
450         IUnknown_QueryInterface(pDSoundRender->seekthru_unk, &IID_ISeekingPassThru, (void**)&passthru);
451         ISeekingPassThru_Init(passthru, TRUE, (IPin*)pDSoundRender->pInputPin);
452         ISeekingPassThru_Release(passthru);
453         *ppv = pDSoundRender;
454     }
455     else
456     {
457         if (pDSoundRender->pInputPin)
458             IPin_Release((IPin*)pDSoundRender->pInputPin);
459         BaseFilterImpl_Release((IBaseFilter*)pDSoundRender);
460         CoTaskMemFree(pDSoundRender);
461     }
462
463     return hr;
464 }
465
466 static HRESULT WINAPI DSoundRender_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
467 {
468     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
469     TRACE("(%p, %p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
470
471     *ppv = NULL;
472
473     if (IsEqualIID(riid, &IID_IUnknown))
474         *ppv = This;
475     else if (IsEqualIID(riid, &IID_IPersist))
476         *ppv = This;
477     else if (IsEqualIID(riid, &IID_IMediaFilter))
478         *ppv = This;
479     else if (IsEqualIID(riid, &IID_IBaseFilter))
480         *ppv = This;
481     else if (IsEqualIID(riid, &IID_IBasicAudio))
482         *ppv = &This->IBasicAudio_vtbl;
483     else if (IsEqualIID(riid, &IID_IReferenceClock))
484         *ppv = &This->IReferenceClock_vtbl;
485     else if (IsEqualIID(riid, &IID_IMediaSeeking))
486         return IUnknown_QueryInterface(This->seekthru_unk, riid, ppv);
487     else if (IsEqualIID(riid, &IID_IAMDirectSound))
488         *ppv = &This->IAMDirectSound_vtbl;
489
490     if (*ppv)
491     {
492         IUnknown_AddRef((IUnknown *)(*ppv));
493         return S_OK;
494     }
495
496     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
497         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
498
499     return E_NOINTERFACE;
500 }
501
502 static ULONG WINAPI DSoundRender_Release(IBaseFilter * iface)
503 {
504     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
505     ULONG refCount = BaseFilterImpl_Release(iface);
506
507     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
508
509     if (!refCount)
510     {
511         IPin *pConnectedTo;
512
513         if (This->dsbuffer)
514             IDirectSoundBuffer_Release(This->dsbuffer);
515         This->dsbuffer = NULL;
516         if (This->dsound)
517             IDirectSound_Release(This->dsound);
518         This->dsound = NULL;
519        
520         if (SUCCEEDED(IPin_ConnectedTo((IPin *)This->pInputPin, &pConnectedTo)))
521         {
522             IPin_Disconnect(pConnectedTo);
523             IPin_Release(pConnectedTo);
524         }
525         IPin_Disconnect((IPin *)This->pInputPin);
526
527         IPin_Release((IPin *)This->pInputPin);
528
529         This->IBasicAudio_vtbl = NULL;
530         if (This->seekthru_unk)
531             IUnknown_Release(This->seekthru_unk);
532
533         CloseHandle(This->state_change);
534         CloseHandle(This->blocked);
535
536         TRACE("Destroying Audio Renderer\n");
537         CoTaskMemFree(This);
538         
539         return 0;
540     }
541     else
542         return refCount;
543 }
544
545 /** IMediaFilter methods **/
546
547 static HRESULT WINAPI DSoundRender_Stop(IBaseFilter * iface)
548 {
549     HRESULT hr = S_OK;
550     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
551
552     TRACE("(%p/%p)->()\n", This, iface);
553
554     EnterCriticalSection(&This->filter.csFilter);
555     {
556         DWORD state = 0;
557         if (This->dsbuffer)
558         {
559             hr = IDirectSoundBuffer_GetStatus(This->dsbuffer, &state);
560             if (SUCCEEDED(hr))
561             {
562                 if (state & DSBSTATUS_PLAYING)
563                     hr = IDirectSoundBuffer_Stop(This->dsbuffer);
564             }
565         }
566         if (SUCCEEDED(hr))
567             This->filter.state = State_Stopped;
568
569         /* Complete our transition */
570         SetEvent(This->state_change);
571         SetEvent(This->blocked);
572         MediaSeekingPassThru_ResetMediaTime(This->seekthru_unk);
573     }
574     LeaveCriticalSection(&This->filter.csFilter);
575     
576     return hr;
577 }
578
579 static HRESULT WINAPI DSoundRender_Pause(IBaseFilter * iface)
580 {
581     HRESULT hr = S_OK;
582     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
583     
584     TRACE("(%p/%p)->()\n", This, iface);
585
586     EnterCriticalSection(&This->filter.csFilter);
587     if (This->filter.state != State_Paused)
588     {
589         DWORD state = 0;
590         if (This->filter.state == State_Stopped)
591         {
592             This->pInputPin->end_of_stream = 0;
593         }
594
595         if (This->dsbuffer)
596         {
597             hr = IDirectSoundBuffer_GetStatus(This->dsbuffer, &state);
598             if (SUCCEEDED(hr))
599             {
600                 if (state & DSBSTATUS_PLAYING)
601                     hr = IDirectSoundBuffer_Stop(This->dsbuffer);
602             }
603         }
604         if (SUCCEEDED(hr))
605             This->filter.state = State_Paused;
606
607         ResetEvent(This->blocked);
608         ResetEvent(This->state_change);
609     }
610     LeaveCriticalSection(&This->filter.csFilter);
611
612     return hr;
613 }
614
615 static HRESULT WINAPI DSoundRender_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
616 {
617     HRESULT hr = S_OK;
618     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
619
620     TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart));
621
622     EnterCriticalSection(&This->filter.csFilter);
623     {
624         This->filter.rtStreamStart = tStart;
625         if (This->filter.state == State_Paused)
626         {
627             /* Unblock our thread, state changing from paused to running doesn't need a reset for state change */
628             SetEvent(This->blocked);
629         }
630         else if (This->filter.state == State_Stopped)
631         {
632             ResetEvent(This->state_change);
633             This->pInputPin->end_of_stream = 0;
634         }
635         ResetEvent(This->blocked);
636
637         This->filter.state = State_Running;
638     }
639     LeaveCriticalSection(&This->filter.csFilter);
640
641     return hr;
642 }
643
644 static HRESULT WINAPI DSoundRender_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
645 {
646     HRESULT hr;
647     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
648
649     TRACE("(%p/%p)->(%d, %p)\n", This, iface, dwMilliSecsTimeout, pState);
650
651     if (WaitForSingleObject(This->state_change, dwMilliSecsTimeout) == WAIT_TIMEOUT)
652         hr = VFW_S_STATE_INTERMEDIATE;
653     else
654         hr = S_OK;
655
656     BaseFilterImpl_GetState(iface, dwMilliSecsTimeout, pState);
657
658     return hr;
659 }
660
661 /** IBaseFilter implementation **/
662
663 static HRESULT WINAPI DSoundRender_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
664 {
665     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
666
667     TRACE("(%p/%p)->(%s,%p)\n", This, iface, debugstr_w(Id), ppPin);
668     
669     FIXME("DSoundRender::FindPin(...)\n");
670
671     /* FIXME: critical section */
672
673     return E_NOTIMPL;
674 }
675
676 static const IBaseFilterVtbl DSoundRender_Vtbl =
677 {
678     DSoundRender_QueryInterface,
679     BaseFilterImpl_AddRef,
680     DSoundRender_Release,
681     BaseFilterImpl_GetClassID,
682     DSoundRender_Stop,
683     DSoundRender_Pause,
684     DSoundRender_Run,
685     DSoundRender_GetState,
686     BaseFilterImpl_SetSyncSource,
687     BaseFilterImpl_GetSyncSource,
688     BaseFilterImpl_EnumPins,
689     DSoundRender_FindPin,
690     BaseFilterImpl_QueryFilterInfo,
691     BaseFilterImpl_JoinFilterGraph,
692     BaseFilterImpl_QueryVendorInfo
693 };
694
695 static HRESULT WINAPI DSoundRender_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
696 {
697     BaseInputPin *This = (BaseInputPin *)iface;
698     PIN_DIRECTION pindirReceive;
699     DSoundRenderImpl *DSImpl;
700     HRESULT hr = S_OK;
701
702     TRACE("(%p)->(%p, %p)\n", This, pReceivePin, pmt);
703     dump_AM_MEDIA_TYPE(pmt);
704
705     EnterCriticalSection(This->pin.pCritSec);
706     {
707         DSImpl = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
708         DSImpl->rtLastStop = -1;
709
710         if (This->pin.pConnectedTo)
711             hr = VFW_E_ALREADY_CONNECTED;
712
713         if (SUCCEEDED(hr) && This->pin.pFuncsTable->pfnCheckMediaType((BasePin*)This, pmt) != S_OK)
714             hr = VFW_E_TYPE_NOT_ACCEPTED;
715
716         if (SUCCEEDED(hr))
717         {
718             IPin_QueryDirection(pReceivePin, &pindirReceive);
719
720             if (pindirReceive != PINDIR_OUTPUT)
721             {
722                 ERR("Can't connect from non-output pin\n");
723                 hr = VFW_E_INVALID_DIRECTION;
724             }
725         }
726
727         if (SUCCEEDED(hr))
728         {
729             WAVEFORMATEX *format;
730             DSBUFFERDESC buf_desc;
731
732             TRACE("MajorType %s\n", debugstr_guid(&pmt->majortype));
733             TRACE("SubType %s\n", debugstr_guid(&pmt->subtype));
734             TRACE("Format %s\n", debugstr_guid(&pmt->formattype));
735             TRACE("Size %d\n", pmt->cbFormat);
736
737             format = (WAVEFORMATEX*)pmt->pbFormat;
738
739             DSImpl->buf_size = format->nAvgBytesPerSec;
740
741             memset(&buf_desc,0,sizeof(DSBUFFERDESC));
742             buf_desc.dwSize = sizeof(DSBUFFERDESC);
743             buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN |
744                                DSBCAPS_CTRLFREQUENCY |
745                                DSBCAPS_GETCURRENTPOSITION2;
746             buf_desc.dwBufferBytes = DSImpl->buf_size;
747             buf_desc.lpwfxFormat = format;
748             hr = IDirectSound_CreateSoundBuffer(DSImpl->dsound, &buf_desc, &DSImpl->dsbuffer, NULL);
749             if (FAILED(hr))
750                 ERR("Can't create sound buffer (%x)\n", hr);
751         }
752
753         if (SUCCEEDED(hr))
754         {
755             hr = IDirectSoundBuffer_SetVolume(DSImpl->dsbuffer, DSImpl->volume);
756             if (FAILED(hr))
757                 ERR("Can't set volume to %d (%x)\n", DSImpl->volume, hr);
758
759             hr = IDirectSoundBuffer_SetPan(DSImpl->dsbuffer, DSImpl->pan);
760             if (FAILED(hr))
761                 ERR("Can't set pan to %d (%x)\n", DSImpl->pan, hr);
762
763             DSImpl->write_pos = 0;
764             hr = S_OK;
765         }
766
767         if (SUCCEEDED(hr))
768         {
769             CopyMediaType(&This->pin.mtCurrent, pmt);
770             This->pin.pConnectedTo = pReceivePin;
771             IPin_AddRef(pReceivePin);
772         }
773         else if (hr != VFW_E_ALREADY_CONNECTED)
774         {
775             if (DSImpl->dsbuffer)
776                 IDirectSoundBuffer_Release(DSImpl->dsbuffer);
777             DSImpl->dsbuffer = NULL;
778         }
779     }
780     LeaveCriticalSection(This->pin.pCritSec);
781
782     return hr;
783 }
784
785 static HRESULT WINAPI DSoundRender_InputPin_Disconnect(IPin * iface)
786 {
787     BasePin *This = (BasePin*)iface;
788     DSoundRenderImpl *DSImpl;
789
790     TRACE("(%p)->()\n", iface);
791
792     DSImpl = (DSoundRenderImpl*)This->pinInfo.pFilter;
793     if (DSImpl->dsbuffer)
794         IDirectSoundBuffer_Release(DSImpl->dsbuffer);
795     DSImpl->dsbuffer = NULL;
796
797     return BasePinImpl_Disconnect(iface);
798 }
799
800 static HRESULT WINAPI DSoundRender_InputPin_EndOfStream(IPin * iface)
801 {
802     BaseInputPin* This = (BaseInputPin*)iface;
803     DSoundRenderImpl *me = (DSoundRenderImpl*)This->pin.pinInfo.pFilter;
804     IMediaEventSink* pEventSink;
805     HRESULT hr;
806
807     EnterCriticalSection(This->pin.pCritSec);
808
809     TRACE("(%p/%p)->()\n", This, iface);
810     hr = BaseInputPinImpl_EndOfStream(iface);
811     if (hr != S_OK)
812     {
813         ERR("%08x\n", hr);
814         LeaveCriticalSection(This->pin.pCritSec);
815         return hr;
816     }
817
818     hr = IFilterGraph_QueryInterface(me->filter.filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink);
819     if (SUCCEEDED(hr))
820     {
821         BYTE * silence;
822
823         silence = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, me->buf_size);
824         if (silence)
825         {
826             memset(silence, 0, me->buf_size);
827             DSoundRender_SendSampleData((DSoundRenderImpl*)This->pin.pinInfo.pFilter, silence, me->buf_size);
828             HeapFree(GetProcessHeap(), 0, silence);
829         }
830
831         hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, 0);
832         IMediaEventSink_Release(pEventSink);
833     }
834     MediaSeekingPassThru_EOS(me->seekthru_unk);
835     LeaveCriticalSection(This->pin.pCritSec);
836
837     return hr;
838 }
839
840 static HRESULT WINAPI DSoundRender_InputPin_BeginFlush(IPin * iface)
841 {
842     BaseInputPin *This = (BaseInputPin *)iface;
843     DSoundRenderImpl *pFilter = (DSoundRenderImpl *)This->pin.pinInfo.pFilter;
844     HRESULT hr;
845
846     TRACE("\n");
847
848     EnterCriticalSection(This->pin.pCritSec);
849     hr = BaseInputPinImpl_BeginFlush(iface);
850     SetEvent(pFilter->blocked);
851     LeaveCriticalSection(This->pin.pCritSec);
852
853     return hr;
854 }
855
856 static HRESULT WINAPI DSoundRender_InputPin_EndFlush(IPin * iface)
857 {
858     BaseInputPin *This = (BaseInputPin *)iface;
859     DSoundRenderImpl *pFilter = (DSoundRenderImpl *)This->pin.pinInfo.pFilter;
860     HRESULT hr;
861
862     TRACE("\n");
863
864     EnterCriticalSection(This->pin.pCritSec);
865     if (pFilter->in_loop) {
866         ResetEvent(pFilter->state_change);
867         LeaveCriticalSection(This->pin.pCritSec);
868         WaitForSingleObject(pFilter->state_change, -1);
869         EnterCriticalSection(This->pin.pCritSec);
870     }
871     if (pFilter->filter.state != State_Stopped)
872         ResetEvent(pFilter->blocked);
873
874     if (pFilter->dsbuffer)
875     {
876         LPBYTE buffer;
877         DWORD size;
878         IDirectSoundBuffer_Stop(pFilter->dsbuffer);
879
880         /* Force a reset */
881         IDirectSoundBuffer_SetCurrentPosition(pFilter->dsbuffer, 0);
882         pFilter->write_pos = pFilter->last_play_pos = 0;
883         ++pFilter->play_loops;
884         pFilter->write_loops = pFilter->play_loops;
885
886         IDirectSoundBuffer_Lock(pFilter->dsbuffer, 0, 0, (LPVOID *)&buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
887         memset(buffer, 0, size);
888         IDirectSoundBuffer_Unlock(pFilter->dsbuffer, buffer, size, NULL, 0);
889     }
890     hr = BaseInputPinImpl_EndFlush(iface);
891     LeaveCriticalSection(This->pin.pCritSec);
892     MediaSeekingPassThru_ResetMediaTime(pFilter->seekthru_unk);
893
894     return hr;
895 }
896
897 static const IPinVtbl DSoundRender_InputPin_Vtbl =
898 {
899     BaseInputPinImpl_QueryInterface,
900     BasePinImpl_AddRef,
901     BaseInputPinImpl_Release,
902     BaseInputPinImpl_Connect,
903     DSoundRender_InputPin_ReceiveConnection,
904     DSoundRender_InputPin_Disconnect,
905     BasePinImpl_ConnectedTo,
906     BasePinImpl_ConnectionMediaType,
907     BasePinImpl_QueryPinInfo,
908     BasePinImpl_QueryDirection,
909     BasePinImpl_QueryId,
910     BaseInputPinImpl_QueryAccept,
911     BasePinImpl_EnumMediaTypes,
912     BasePinImpl_QueryInternalConnections,
913     DSoundRender_InputPin_EndOfStream,
914     DSoundRender_InputPin_BeginFlush,
915     DSoundRender_InputPin_EndFlush,
916     BaseInputPinImpl_NewSegment
917 };
918
919 /*** IUnknown methods ***/
920 static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
921                                                 REFIID riid,
922                                                 LPVOID*ppvObj) {
923     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
924
925     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
926
927     return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
928 }
929
930 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
931     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
932
933     TRACE("(%p/%p)->()\n", This, iface);
934
935     return BaseFilterImpl_AddRef((IBaseFilter*)This);
936 }
937
938 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
939     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
940
941     TRACE("(%p/%p)->()\n", This, iface);
942
943     return DSoundRender_Release((IBaseFilter*)This);
944 }
945
946 /*** IDispatch methods ***/
947 static HRESULT WINAPI Basicaudio_GetTypeInfoCount(IBasicAudio *iface,
948                                                   UINT*pctinfo) {
949     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
950
951     TRACE("(%p/%p)->(%p): stub !!!\n", This, iface, pctinfo);
952
953     return S_OK;
954 }
955
956 static HRESULT WINAPI Basicaudio_GetTypeInfo(IBasicAudio *iface,
957                                              UINT iTInfo,
958                                              LCID lcid,
959                                              ITypeInfo**ppTInfo) {
960     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
961
962     TRACE("(%p/%p)->(%d, %d, %p): stub !!!\n", This, iface, iTInfo, lcid, ppTInfo);
963
964     return S_OK;
965 }
966
967 static HRESULT WINAPI Basicaudio_GetIDsOfNames(IBasicAudio *iface,
968                                                REFIID riid,
969                                                LPOLESTR*rgszNames,
970                                                UINT cNames,
971                                                LCID lcid,
972                                                DISPID*rgDispId) {
973     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
974
975     TRACE("(%p/%p)->(%s (%p), %p, %d, %d, %p): stub !!!\n", This, iface, debugstr_guid(riid), riid, rgszNames, cNames, lcid, rgDispId);
976
977     return S_OK;
978 }
979
980 static HRESULT WINAPI Basicaudio_Invoke(IBasicAudio *iface,
981                                         DISPID dispIdMember,
982                                         REFIID riid,
983                                         LCID lcid,
984                                         WORD wFlags,
985                                         DISPPARAMS*pDispParams,
986                                         VARIANT*pVarResult,
987                                         EXCEPINFO*pExepInfo,
988                                         UINT*puArgErr) {
989     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
990
991     TRACE("(%p/%p)->(%d, %s (%p), %d, %04x, %p, %p, %p, %p): stub !!!\n", This, iface, dispIdMember, debugstr_guid(riid), riid, lcid, wFlags, pDispParams, pVarResult, pExepInfo, puArgErr);
992
993     return S_OK;
994 }
995
996 /*** IBasicAudio methods ***/
997 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
998                                             LONG lVolume) {
999     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1000
1001     TRACE("(%p/%p)->(%d)\n", This, iface, lVolume);
1002
1003     if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
1004         return E_INVALIDARG;
1005
1006     if (This->dsbuffer) {
1007         if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
1008             return E_FAIL;
1009     }
1010
1011     This->volume = lVolume;
1012     return S_OK;
1013 }
1014
1015 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
1016                                             LONG *plVolume) {
1017     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1018
1019     TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
1020
1021     if (!plVolume)
1022         return E_POINTER;
1023
1024     *plVolume = This->volume;
1025     return S_OK;
1026 }
1027
1028 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
1029                                              LONG lBalance) {
1030     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1031
1032     TRACE("(%p/%p)->(%d)\n", This, iface, lBalance);
1033
1034     if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
1035         return E_INVALIDARG;
1036
1037     if (This->dsbuffer) {
1038         if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
1039             return E_FAIL;
1040     }
1041
1042     This->pan = lBalance;
1043     return S_OK;
1044 }
1045
1046 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
1047                                              LONG *plBalance) {
1048     ICOM_THIS_MULTI(DSoundRenderImpl, IBasicAudio_vtbl, iface);
1049
1050     TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
1051
1052     if (!plBalance)
1053         return E_POINTER;
1054
1055     *plBalance = This->pan;
1056     return S_OK;
1057 }
1058
1059 static const IBasicAudioVtbl IBasicAudio_Vtbl =
1060 {
1061     Basicaudio_QueryInterface,
1062     Basicaudio_AddRef,
1063     Basicaudio_Release,
1064     Basicaudio_GetTypeInfoCount,
1065     Basicaudio_GetTypeInfo,
1066     Basicaudio_GetIDsOfNames,
1067     Basicaudio_Invoke,
1068     Basicaudio_put_Volume,
1069     Basicaudio_get_Volume,
1070     Basicaudio_put_Balance,
1071     Basicaudio_get_Balance
1072 };
1073
1074
1075 /*** IUnknown methods ***/
1076 static HRESULT WINAPI ReferenceClock_QueryInterface(IReferenceClock *iface,
1077                                                 REFIID riid,
1078                                                 LPVOID*ppvObj)
1079 {
1080     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1081
1082     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1083
1084     return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1085 }
1086
1087 static ULONG WINAPI ReferenceClock_AddRef(IReferenceClock *iface)
1088 {
1089     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1090
1091     TRACE("(%p/%p)->()\n", This, iface);
1092
1093     return BaseFilterImpl_AddRef((IBaseFilter*)This);
1094 }
1095
1096 static ULONG WINAPI ReferenceClock_Release(IReferenceClock *iface)
1097 {
1098     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1099
1100     TRACE("(%p/%p)->()\n", This, iface);
1101
1102     return DSoundRender_Release((IBaseFilter*)This);
1103 }
1104
1105 /*** IReferenceClock methods ***/
1106 static HRESULT WINAPI ReferenceClock_GetTime(IReferenceClock *iface,
1107                                              REFERENCE_TIME *pTime)
1108 {
1109     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1110     HRESULT hr = E_FAIL;
1111     DWORD play_pos;
1112
1113     TRACE("(%p/%p)->(%p)\n", This, iface, pTime);
1114
1115     if (This->dsbuffer)
1116         hr = DSoundRender_GetPos(This, &play_pos, pTime);
1117     if (FAILED(hr))
1118         ERR("Could not get reference time (%x)!\n", hr);
1119
1120     return hr;
1121 }
1122
1123 static HRESULT WINAPI ReferenceClock_AdviseTime(IReferenceClock *iface,
1124                                                 REFERENCE_TIME rtBaseTime,
1125                                                 REFERENCE_TIME rtStreamTime,
1126                                                 HEVENT hEvent,
1127                                                 DWORD_PTR *pdwAdviseCookie)
1128 {
1129     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1130
1131     FIXME("(%p/%p)->(%s, %s, %p, %p): stub!\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hEvent, pdwAdviseCookie);
1132
1133     return E_NOTIMPL;
1134 }
1135
1136 static HRESULT WINAPI ReferenceClock_AdvisePeriodic(IReferenceClock *iface,
1137                                                     REFERENCE_TIME rtBaseTime,
1138                                                     REFERENCE_TIME rtStreamTime,
1139                                                     HSEMAPHORE hSemaphore,
1140                                                     DWORD_PTR *pdwAdviseCookie)
1141 {
1142     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1143
1144     FIXME("(%p/%p)->(%s, %s, %p, %p): stub!\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hSemaphore, pdwAdviseCookie);
1145
1146     return E_NOTIMPL;
1147 }
1148
1149 static HRESULT WINAPI ReferenceClock_Unadvise(IReferenceClock *iface,
1150                                               DWORD_PTR dwAdviseCookie)
1151 {
1152     ICOM_THIS_MULTI(DSoundRenderImpl, IReferenceClock_vtbl, iface);
1153
1154     FIXME("(%p/%p)->(%p): stub!\n", This, iface, (void*)dwAdviseCookie);
1155
1156     return S_FALSE;
1157 }
1158
1159 static const IReferenceClockVtbl IReferenceClock_Vtbl =
1160 {
1161     ReferenceClock_QueryInterface,
1162     ReferenceClock_AddRef,
1163     ReferenceClock_Release,
1164     ReferenceClock_GetTime,
1165     ReferenceClock_AdviseTime,
1166     ReferenceClock_AdvisePeriodic,
1167     ReferenceClock_Unadvise
1168 };
1169
1170 /*** IUnknown methods ***/
1171 static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface,
1172                                                 REFIID riid,
1173                                                 LPVOID*ppvObj)
1174 {
1175     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1176
1177     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1178
1179     return DSoundRender_QueryInterface((IBaseFilter*)This, riid, ppvObj);
1180 }
1181
1182 static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
1183 {
1184     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1185
1186     TRACE("(%p/%p)->()\n", This, iface);
1187
1188     return BaseFilterImpl_AddRef((IBaseFilter*)This);
1189 }
1190
1191 static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
1192 {
1193     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1194
1195     TRACE("(%p/%p)->()\n", This, iface);
1196
1197     return DSoundRender_Release((IBaseFilter*)This);
1198 }
1199
1200 /*** IAMDirectSound methods ***/
1201 static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface,  IDirectSound **ds)
1202 {
1203     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1204
1205     FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1206
1207     return E_NOTIMPL;
1208 }
1209
1210 static HRESULT WINAPI AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1211 {
1212     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1213
1214     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1215
1216     return E_NOTIMPL;
1217 }
1218
1219 static HRESULT WINAPI AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1220 {
1221     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1222
1223     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1224
1225     return E_NOTIMPL;
1226 }
1227
1228 static HRESULT WINAPI AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound *iface, IDirectSound *ds)
1229 {
1230     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1231
1232     FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1233
1234     return E_NOTIMPL;
1235 }
1236
1237 static HRESULT WINAPI AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1238 {
1239     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1240
1241     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1242
1243     return E_NOTIMPL;
1244 }
1245
1246 static HRESULT WINAPI AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1247 {
1248     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1249
1250     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1251
1252     return E_NOTIMPL;
1253 }
1254
1255 static HRESULT WINAPI AMDirectSound_SetFocusWindow(IAMDirectSound *iface, HWND hwnd, BOOL bgsilent)
1256 {
1257     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1258
1259     FIXME("(%p/%p)->(%p,%d): stub\n", This, iface, hwnd, bgsilent);
1260
1261     return E_NOTIMPL;
1262 }
1263
1264 static HRESULT WINAPI AMDirectSound_GetFocusWindow(IAMDirectSound *iface, HWND hwnd)
1265 {
1266     ICOM_THIS_MULTI(DSoundRenderImpl, IAMDirectSound_vtbl, iface);
1267
1268     FIXME("(%p/%p)->(%p): stub\n", This, iface, hwnd);
1269
1270     return E_NOTIMPL;
1271 }
1272
1273 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl =
1274 {
1275     AMDirectSound_QueryInterface,
1276     AMDirectSound_AddRef,
1277     AMDirectSound_Release,
1278     AMDirectSound_GetDirectSoundInterface,
1279     AMDirectSound_GetPrimaryBufferInterface,
1280     AMDirectSound_GetSecondaryBufferInterface,
1281     AMDirectSound_ReleaseDirectSoundInterface,
1282     AMDirectSound_ReleasePrimaryBufferInterface,
1283     AMDirectSound_ReleaseSecondaryBufferInterface,
1284     AMDirectSound_SetFocusWindow,
1285     AMDirectSound_GetFocusWindow
1286 };