cmd: Convert wcmd_for to use WCMD_parameter.
[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 "pin.h"
25
26 #include "uuids.h"
27 #include "vfwmsgs.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "dshow.h"
31 #include "evcode.h"
32 #include "strmif.h"
33 #include "dsound.h"
34 #include "amaudio.h"
35
36 #include "wine/unicode.h"
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
40
41 /* NOTE: buffer can still be filled completely,
42  * but we start waiting until only this amount is buffered
43  */
44 static const REFERENCE_TIME DSoundRenderer_Max_Fill = 150 * 10000;
45
46 static const IBaseFilterVtbl DSoundRender_Vtbl;
47 static const IBasicAudioVtbl IBasicAudio_Vtbl;
48 static const IReferenceClockVtbl IReferenceClock_Vtbl;
49 static const IMediaSeekingVtbl IMediaSeeking_Vtbl;
50 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl;
51 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl;
52
53 typedef struct DSoundRenderImpl
54 {
55     BaseRenderer renderer;
56     BasicAudio basicAudio;
57
58     IReferenceClock IReferenceClock_iface;
59     IAMDirectSound IAMDirectSound_iface;
60     IAMFilterMiscFlags IAMFilterMiscFlags_iface;
61
62     IDirectSound8 *dsound;
63     LPDIRECTSOUNDBUFFER dsbuffer;
64     DWORD buf_size;
65     DWORD in_loop;
66     DWORD last_playpos, writepos;
67
68     REFERENCE_TIME play_time;
69
70     HANDLE blocked;
71
72     LONG volume;
73     LONG pan;
74
75     DWORD threadid;
76     HANDLE advisethread, thread_wait;
77 } DSoundRenderImpl;
78
79 static inline DSoundRenderImpl *impl_from_BaseRenderer(BaseRenderer *iface)
80 {
81     return CONTAINING_RECORD(iface, DSoundRenderImpl, renderer);
82 }
83
84 static inline DSoundRenderImpl *impl_from_IBaseFilter(IBaseFilter *iface)
85 {
86     return CONTAINING_RECORD(iface, DSoundRenderImpl, renderer.filter.IBaseFilter_iface);
87 }
88
89 static inline DSoundRenderImpl *impl_from_IBasicAudio(IBasicAudio *iface)
90 {
91     return CONTAINING_RECORD(iface, DSoundRenderImpl, basicAudio.IBasicAudio_iface);
92 }
93
94 static inline DSoundRenderImpl *impl_from_IReferenceClock(IReferenceClock *iface)
95 {
96     return CONTAINING_RECORD(iface, DSoundRenderImpl, IReferenceClock_iface);
97 }
98
99 static inline DSoundRenderImpl *impl_from_IAMDirectSound(IAMDirectSound *iface)
100 {
101     return CONTAINING_RECORD(iface, DSoundRenderImpl, IAMDirectSound_iface);
102 }
103
104 static inline DSoundRenderImpl *impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface)
105 {
106     return CONTAINING_RECORD(iface, DSoundRenderImpl, IAMFilterMiscFlags_iface);
107 }
108
109 static REFERENCE_TIME time_from_pos(DSoundRenderImpl *This, DWORD pos) {
110     WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat;
111     REFERENCE_TIME ret = 10000000;
112     ret = ret * pos / wfx->nAvgBytesPerSec;
113     return ret;
114 }
115
116 static DWORD pos_from_time(DSoundRenderImpl *This, REFERENCE_TIME time) {
117     WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat;
118     REFERENCE_TIME ret = time;
119     ret *= wfx->nSamplesPerSec;
120     ret /= 10000000;
121     ret *= wfx->nBlockAlign;
122     return ret;
123 }
124
125 static void DSoundRender_UpdatePositions(DSoundRenderImpl *This, DWORD *seqwritepos, DWORD *minwritepos) {
126     WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat;
127     BYTE *buf1, *buf2;
128     DWORD size1, size2, playpos, writepos, old_writepos, old_playpos, adv;
129     BOOL writepos_set = This->writepos < This->buf_size;
130
131     /* Update position and zero */
132     old_writepos = This->writepos;
133     old_playpos = This->last_playpos;
134     if (old_writepos <= old_playpos)
135         old_writepos += This->buf_size;
136
137     IDirectSoundBuffer_GetCurrentPosition(This->dsbuffer, &playpos, &writepos);
138     if (old_playpos > playpos) {
139         adv = This->buf_size + playpos - old_playpos;
140         This->play_time += time_from_pos(This, This->buf_size);
141     } else
142         adv = playpos - old_playpos;
143     This->last_playpos = playpos;
144     if (adv) {
145         TRACE("Moving from %u to %u: clearing %u bytes\n", old_playpos, playpos, adv);
146         IDirectSoundBuffer_Lock(This->dsbuffer, old_playpos, adv, (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
147         memset(buf1, wfx->wBitsPerSample == 8 ? 128  : 0, size1);
148         memset(buf2, wfx->wBitsPerSample == 8 ? 128  : 0, size2);
149         IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
150     }
151     *minwritepos = writepos;
152     if (!writepos_set || old_writepos < writepos) {
153         if (writepos_set) {
154             This->writepos = This->buf_size;
155             FIXME("Underrun of data occurred!\n");
156         }
157         *seqwritepos = writepos;
158     } else
159         *seqwritepos = This->writepos;
160 }
161
162 static HRESULT DSoundRender_GetWritePos(DSoundRenderImpl *This, DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip)
163 {
164     WAVEFORMATEX *wfx = (WAVEFORMATEX*)This->renderer.pInputPin->pin.mtCurrent.pbFormat;
165     DWORD writepos, min_writepos, playpos;
166     REFERENCE_TIME max_lag = 50 * 10000;
167     REFERENCE_TIME min_lag = 25 * 10000;
168     REFERENCE_TIME cur, writepos_t, delta_t;
169
170     DSoundRender_UpdatePositions(This, &writepos, &min_writepos);
171     playpos = This->last_playpos;
172     if (This->renderer.filter.pClock == &This->IReferenceClock_iface) {
173         max_lag = min_lag;
174         cur = This->play_time + time_from_pos(This, playpos);
175         cur -= This->renderer.filter.rtStreamStart;
176     } else if (This->renderer.filter.pClock) {
177         IReferenceClock_GetTime(This->renderer.filter.pClock, &cur);
178         cur -= This->renderer.filter.rtStreamStart;
179     } else
180         write_at = -1;
181
182     if (writepos == min_writepos)
183         max_lag = 0;
184
185     *skip = 0;
186     if (write_at < 0) {
187         *ret_writepos = writepos;
188         goto end;
189     }
190
191     if (writepos >= playpos)
192         writepos_t = cur + time_from_pos(This, writepos - playpos);
193     else
194         writepos_t = cur + time_from_pos(This, This->buf_size + writepos - playpos);
195
196     /* write_at: Starting time of sample */
197     /* cur: current time of play position */
198     /* writepos_t: current time of our pointer play position */
199     delta_t = write_at - writepos_t;
200     if (delta_t >= -max_lag && delta_t <= max_lag) {
201         TRACE("Continuing from old position\n");
202         *ret_writepos = writepos;
203     } else if (delta_t < 0) {
204         REFERENCE_TIME past, min_writepos_t;
205         WARN("Delta too big %i/%i, overwriting old data or even skipping\n", (int)delta_t / 10000, (int)max_lag / 10000);
206         if (min_writepos >= playpos)
207             min_writepos_t = cur + time_from_pos(This, min_writepos - playpos);
208         else
209             min_writepos_t = cur + time_from_pos(This, This->buf_size - playpos + min_writepos);
210         past = min_writepos_t - write_at;
211         if (past >= 0) {
212             DWORD skipbytes = pos_from_time(This, past);
213             WARN("Skipping %u bytes\n", skipbytes);
214             *skip = skipbytes;
215             *ret_writepos = min_writepos;
216         } else {
217             DWORD aheadbytes = pos_from_time(This, -past);
218             WARN("Advancing %u bytes\n", aheadbytes);
219             *ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
220         }
221     } else /* delta_t > 0 */ {
222         DWORD aheadbytes;
223         WARN("Delta too big %i/%i, too far ahead\n", (int)delta_t / 10000, (int)max_lag / 10000);
224         aheadbytes = pos_from_time(This, delta_t);
225         WARN("Advancing %u bytes\n", aheadbytes);
226         if (delta_t >= DSoundRenderer_Max_Fill)
227             return S_FALSE;
228         *ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
229     }
230 end:
231     if (playpos > *ret_writepos)
232         *pfree = playpos - *ret_writepos;
233     else if (playpos == *ret_writepos)
234         *pfree = This->buf_size - wfx->nBlockAlign;
235     else
236         *pfree = This->buf_size + playpos - *ret_writepos;
237     if (time_from_pos(This, This->buf_size - *pfree) >= DSoundRenderer_Max_Fill) {
238         TRACE("Blocked: too full %i / %i\n", (int)(time_from_pos(This, This->buf_size - *pfree)/10000), (int)(DSoundRenderer_Max_Fill / 10000));
239         return S_FALSE;
240     }
241     return S_OK;
242 }
243
244 static HRESULT DSoundRender_HandleEndOfStream(DSoundRenderImpl *This)
245 {
246     while (1)
247     {
248         DWORD pos1, pos2;
249         DSoundRender_UpdatePositions(This, &pos1, &pos2);
250         if (pos1 == pos2)
251             break;
252
253         This->in_loop = 1;
254         LeaveCriticalSection(&This->renderer.filter.csFilter);
255         LeaveCriticalSection(&This->renderer.csRenderLock);
256         WaitForSingleObject(This->blocked, 10);
257         EnterCriticalSection(&This->renderer.filter.csFilter);
258         EnterCriticalSection(&This->renderer.csRenderLock);
259         This->in_loop = 0;
260     }
261
262     return S_OK;
263 }
264
265 static HRESULT DSoundRender_SendSampleData(DSoundRenderImpl* This, REFERENCE_TIME tStart, REFERENCE_TIME tStop, const BYTE *data, DWORD size)
266 {
267     HRESULT hr;
268
269     while (size && This->renderer.filter.state != State_Stopped) {
270         DWORD writepos, skip = 0, free, size1, size2, ret;
271         BYTE *buf1, *buf2;
272
273         if (This->renderer.filter.state == State_Running)
274             hr = DSoundRender_GetWritePos(This, &writepos, tStart, &free, &skip);
275         else
276             hr = S_FALSE;
277
278         if (hr != S_OK) {
279             This->in_loop = 1;
280             LeaveCriticalSection(&This->renderer.csRenderLock);
281             ret = WaitForSingleObject(This->blocked, 10);
282             EnterCriticalSection(&This->renderer.csRenderLock);
283             This->in_loop = 0;
284             if (This->renderer.pInputPin->flushing ||
285                 This->renderer.filter.state == State_Stopped) {
286                 return This->renderer.filter.state == State_Paused ? S_OK : VFW_E_WRONG_STATE;
287             }
288             if (ret != WAIT_TIMEOUT)
289                 ERR("%x\n", ret);
290             continue;
291         }
292         tStart = -1;
293
294         if (skip)
295             FIXME("Sample dropped %u of %u bytes\n", skip, size);
296         if (skip >= size)
297             return S_OK;
298         data += skip;
299         size -= skip;
300
301         hr = IDirectSoundBuffer_Lock(This->dsbuffer, writepos, min(free, size), (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
302         if (hr != DS_OK) {
303             ERR("Unable to lock sound buffer! (%x)\n", hr);
304             break;
305         }
306         memcpy(buf1, data, size1);
307         if (size2)
308             memcpy(buf2, data+size1, size2);
309         IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
310         This->writepos = (writepos + size1 + size2) % This->buf_size;
311         TRACE("Wrote %u bytes at %u, next at %u - (%u/%u)\n", size1+size2, writepos, This->writepos, free, size);
312         data += size1 + size2;
313         size -= size1 + size2;
314     }
315     return S_OK;
316 }
317
318 static HRESULT WINAPI DSoundRender_ShouldDrawSampleNow(BaseRenderer *This, IMediaSample *pMediaSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime)
319 {
320     /* We time ourselves do not use the base renderers timing */
321     return S_OK;
322 }
323
324
325 static HRESULT WINAPI DSoundRender_PrepareReceive(BaseRenderer *iface, IMediaSample *pSample)
326 {
327     DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
328     HRESULT hr;
329     AM_MEDIA_TYPE *amt;
330
331     if (IMediaSample_GetMediaType(pSample, &amt) == S_OK)
332     {
333         AM_MEDIA_TYPE *orig = &This->renderer.pInputPin->pin.mtCurrent;
334         WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat;
335         WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat;
336
337         if (origfmt->wFormatTag == newfmt->wFormatTag &&
338             origfmt->nChannels == newfmt->nChannels &&
339             origfmt->nBlockAlign == newfmt->nBlockAlign &&
340             origfmt->wBitsPerSample == newfmt->wBitsPerSample &&
341             origfmt->cbSize ==  newfmt->cbSize)
342         {
343             if (origfmt->nSamplesPerSec != newfmt->nSamplesPerSec)
344             {
345                 hr = IDirectSoundBuffer_SetFrequency(This->dsbuffer,
346                                                      newfmt->nSamplesPerSec);
347                 if (FAILED(hr))
348                     return VFW_E_TYPE_NOT_ACCEPTED;
349                 FreeMediaType(orig);
350                 CopyMediaType(orig, amt);
351                 IMediaSample_SetMediaType(pSample, NULL);
352             }
353         }
354         else
355             return VFW_E_TYPE_NOT_ACCEPTED;
356     }
357     return S_OK;
358 }
359
360 static HRESULT WINAPI DSoundRender_DoRenderSample(BaseRenderer *iface, IMediaSample * pSample)
361 {
362     DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
363     LPBYTE pbSrcStream = NULL;
364     LONG cbSrcStream = 0;
365     REFERENCE_TIME tStart, tStop;
366     HRESULT hr;
367
368     TRACE("%p %p\n", iface, pSample);
369
370     /* Slightly incorrect, Pause completes when a frame is received so we should signal
371      * pause completion here, but for sound playing a single frame doesn't make sense
372      */
373
374     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
375     if (FAILED(hr))
376     {
377         ERR("Cannot get pointer to sample data (%x)\n", hr);
378         return hr;
379     }
380
381     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
382     if (FAILED(hr)) {
383         ERR("Cannot get sample time (%x)\n", hr);
384         tStart = tStop = -1;
385     }
386
387     IMediaSample_IsDiscontinuity(pSample);
388
389     if (IMediaSample_IsPreroll(pSample) == S_OK)
390     {
391         TRACE("Preroll!\n");
392         return S_OK;
393     }
394
395     cbSrcStream = IMediaSample_GetActualDataLength(pSample);
396     TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream);
397
398     hr = DSoundRender_SendSampleData(This, tStart, tStop, pbSrcStream, cbSrcStream);
399     if (This->renderer.filter.state == State_Running && This->renderer.filter.pClock && tStart >= 0) {
400         REFERENCE_TIME jitter, now = 0;
401         Quality q;
402         IReferenceClock_GetTime(This->renderer.filter.pClock, &now);
403         jitter = now - This->renderer.filter.rtStreamStart - tStart;
404         if (jitter <= -DSoundRenderer_Max_Fill)
405             jitter += DSoundRenderer_Max_Fill;
406         else if (jitter < 0)
407             jitter = 0;
408         q.Type = (jitter > 0 ? Famine : Flood);
409         q.Proportion = 1.;
410         q.Late = jitter;
411         q.TimeStamp = tStart;
412         IQualityControl_Notify((IQualityControl *)This->renderer.qcimpl, (IBaseFilter*)This, q);
413     }
414     return hr;
415 }
416
417 static HRESULT WINAPI DSoundRender_CheckMediaType(BaseRenderer *iface, const AM_MEDIA_TYPE * pmt)
418 {
419     WAVEFORMATEX* format;
420
421     if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
422         return S_FALSE;
423
424     format =  (WAVEFORMATEX*)pmt->pbFormat;
425     TRACE("Format = %p\n", format);
426     TRACE("wFormatTag = %x %x\n", format->wFormatTag, WAVE_FORMAT_PCM);
427     TRACE("nChannels = %d\n", format->nChannels);
428     TRACE("nSamplesPerSec = %d\n", format->nAvgBytesPerSec);
429     TRACE("nAvgBytesPerSec = %d\n", format->nAvgBytesPerSec);
430     TRACE("nBlockAlign = %d\n", format->nBlockAlign);
431     TRACE("wBitsPerSample = %d\n", format->wBitsPerSample);
432
433     if (!IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM))
434         return S_FALSE;
435
436     return S_OK;
437 }
438
439 static VOID WINAPI DSoundRender_OnStopStreaming(BaseRenderer * iface)
440 {
441     DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
442
443     TRACE("(%p/%p)->()\n", This, iface);
444
445     IDirectSoundBuffer_Stop(This->dsbuffer);
446     This->writepos = This->buf_size;
447     SetEvent(This->blocked);
448 }
449
450 static VOID WINAPI DSoundRender_OnStartStreaming(BaseRenderer * iface)
451 {
452     DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
453
454     TRACE("(%p)\n", This);
455
456     if (This->renderer.pInputPin->pin.pConnectedTo)
457     {
458         if (This->renderer.filter.state == State_Paused)
459         {
460             /* Unblock our thread, state changing from paused to running doesn't need a reset for state change */
461             SetEvent(This->blocked);
462         }
463         IDirectSoundBuffer_Play(This->dsbuffer, 0, 0, DSBPLAY_LOOPING);
464         ResetEvent(This->blocked);
465     }
466 }
467
468 static HRESULT WINAPI DSoundRender_CompleteConnect(BaseRenderer * iface, IPin * pReceivePin)
469 {
470     DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
471     const AM_MEDIA_TYPE * pmt = &This->renderer.pInputPin->pin.mtCurrent;
472     HRESULT hr = S_OK;
473     WAVEFORMATEX *format;
474     DSBUFFERDESC buf_desc;
475
476     TRACE("(%p)->(%p)\n", This, pReceivePin);
477     dump_AM_MEDIA_TYPE(pmt);
478
479     TRACE("MajorType %s\n", debugstr_guid(&pmt->majortype));
480     TRACE("SubType %s\n", debugstr_guid(&pmt->subtype));
481     TRACE("Format %s\n", debugstr_guid(&pmt->formattype));
482     TRACE("Size %d\n", pmt->cbFormat);
483
484     format = (WAVEFORMATEX*)pmt->pbFormat;
485
486     This->buf_size = format->nAvgBytesPerSec;
487
488     memset(&buf_desc,0,sizeof(DSBUFFERDESC));
489     buf_desc.dwSize = sizeof(DSBUFFERDESC);
490     buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN |
491                        DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS |
492                        DSBCAPS_GETCURRENTPOSITION2;
493     buf_desc.dwBufferBytes = This->buf_size;
494     buf_desc.lpwfxFormat = format;
495     hr = IDirectSound_CreateSoundBuffer(This->dsound, &buf_desc, &This->dsbuffer, NULL);
496     This->writepos = This->buf_size;
497     if (FAILED(hr))
498         ERR("Can't create sound buffer (%x)\n", hr);
499
500     if (SUCCEEDED(hr))
501     {
502         hr = IDirectSoundBuffer_SetVolume(This->dsbuffer, This->volume);
503         if (FAILED(hr))
504             ERR("Can't set volume to %d (%x)\n", This->volume, hr);
505
506         hr = IDirectSoundBuffer_SetPan(This->dsbuffer, This->pan);
507         if (FAILED(hr))
508             ERR("Can't set pan to %d (%x)\n", This->pan, hr);
509         hr = S_OK;
510     }
511
512     if (FAILED(hr) && hr != VFW_E_ALREADY_CONNECTED)
513     {
514         if (This->dsbuffer)
515             IDirectSoundBuffer_Release(This->dsbuffer);
516         This->dsbuffer = NULL;
517     }
518
519     return hr;
520 }
521
522 static HRESULT WINAPI DSoundRender_BreakConnect(BaseRenderer* iface)
523 {
524     DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
525
526     TRACE("(%p)->()\n", iface);
527
528     if (This->threadid) {
529         PostThreadMessageW(This->threadid, WM_APP, 0, 0);
530         LeaveCriticalSection(This->renderer.pInputPin->pin.pCritSec);
531         WaitForSingleObject(This->advisethread, INFINITE);
532         EnterCriticalSection(This->renderer.pInputPin->pin.pCritSec);
533         CloseHandle(This->advisethread);
534     }
535     if (This->dsbuffer)
536         IDirectSoundBuffer_Release(This->dsbuffer);
537     This->dsbuffer = NULL;
538
539     return S_OK;
540 }
541
542 static HRESULT WINAPI DSoundRender_EndOfStream(BaseRenderer* iface)
543 {
544     DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
545     HRESULT hr;
546
547     TRACE("(%p)->()\n",iface);
548
549     hr = BaseRendererImpl_EndOfStream(iface);
550     if (hr != S_OK)
551     {
552         ERR("%08x\n", hr);
553         return hr;
554     }
555
556     hr = DSoundRender_HandleEndOfStream(This);
557
558     return hr;
559 }
560
561 static HRESULT WINAPI DSoundRender_BeginFlush(BaseRenderer* iface)
562 {
563     DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
564
565     TRACE("\n");
566     BaseRendererImpl_BeginFlush(iface);
567     SetEvent(This->blocked);
568
569     return S_OK;
570 }
571
572 static HRESULT WINAPI DSoundRender_EndFlush(BaseRenderer* iface)
573 {
574     DSoundRenderImpl *This = impl_from_BaseRenderer(iface);
575
576     TRACE("\n");
577
578     BaseRendererImpl_EndFlush(iface);
579     if (This->renderer.filter.state != State_Stopped)
580         ResetEvent(This->blocked);
581
582     if (This->dsbuffer)
583     {
584         LPBYTE buffer;
585         DWORD size;
586
587         /* Force a reset */
588         IDirectSoundBuffer_Lock(This->dsbuffer, 0, 0, (LPVOID *)&buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
589         memset(buffer, 0, size);
590         IDirectSoundBuffer_Unlock(This->dsbuffer, buffer, size, NULL, 0);
591         This->writepos = This->buf_size;
592     }
593
594     return S_OK;
595 }
596
597 static const BaseRendererFuncTable BaseFuncTable = {
598     DSoundRender_CheckMediaType,
599     DSoundRender_DoRenderSample,
600     /**/
601     NULL,
602     NULL,
603     NULL,
604     DSoundRender_OnStartStreaming,
605     DSoundRender_OnStopStreaming,
606     NULL,
607     NULL,
608     NULL,
609     DSoundRender_ShouldDrawSampleNow,
610     DSoundRender_PrepareReceive,
611     /**/
612     DSoundRender_CompleteConnect,
613     DSoundRender_BreakConnect,
614     DSoundRender_EndOfStream,
615     DSoundRender_BeginFlush,
616     DSoundRender_EndFlush,
617 };
618
619 HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv)
620 {
621     HRESULT hr;
622     DSoundRenderImpl * pDSoundRender;
623
624     TRACE("(%p, %p)\n", pUnkOuter, ppv);
625
626     *ppv = NULL;
627
628     if (pUnkOuter)
629         return CLASS_E_NOAGGREGATION;
630
631     pDSoundRender = CoTaskMemAlloc(sizeof(DSoundRenderImpl));
632     if (!pDSoundRender)
633         return E_OUTOFMEMORY;
634     ZeroMemory(pDSoundRender, sizeof(DSoundRenderImpl));
635
636     hr = BaseRenderer_Init(&pDSoundRender->renderer, &DSoundRender_Vtbl, (IUnknown*)pDSoundRender, &CLSID_DSoundRender, (DWORD_PTR)(__FILE__ ": DSoundRenderImpl.csFilter"), &BaseFuncTable);
637
638     BasicAudio_Init(&pDSoundRender->basicAudio,&IBasicAudio_Vtbl);
639     pDSoundRender->IReferenceClock_iface.lpVtbl = &IReferenceClock_Vtbl;
640     pDSoundRender->IAMDirectSound_iface.lpVtbl = &IAMDirectSound_Vtbl;
641     pDSoundRender->IAMFilterMiscFlags_iface.lpVtbl = &IAMFilterMiscFlags_Vtbl;
642
643     if (SUCCEEDED(hr))
644     {
645         hr = DirectSoundCreate8(NULL, &pDSoundRender->dsound, NULL);
646         if (FAILED(hr))
647             ERR("Cannot create Direct Sound object (%x)\n", hr);
648         else
649             hr = IDirectSound_SetCooperativeLevel(pDSoundRender->dsound, GetDesktopWindow(), DSSCL_PRIORITY);
650         if (SUCCEEDED(hr)) {
651             IDirectSoundBuffer *buf;
652             DSBUFFERDESC buf_desc;
653             memset(&buf_desc,0,sizeof(DSBUFFERDESC));
654             buf_desc.dwSize = sizeof(DSBUFFERDESC);
655             buf_desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
656             hr = IDirectSound_CreateSoundBuffer(pDSoundRender->dsound, &buf_desc, &buf, NULL);
657             if (SUCCEEDED(hr)) {
658                 IDirectSoundBuffer_Play(buf, 0, 0, DSBPLAY_LOOPING);
659                 IDirectSoundBuffer_Release(buf);
660             }
661             hr = S_OK;
662         }
663     }
664
665     if (SUCCEEDED(hr))
666     {
667         pDSoundRender->blocked = CreateEventW(NULL, TRUE, TRUE, NULL);
668
669         if (!pDSoundRender->blocked || FAILED(hr))
670         {
671             IUnknown_Release((IUnknown *)pDSoundRender);
672             return HRESULT_FROM_WIN32(GetLastError());
673         }
674
675         *ppv = pDSoundRender;
676     }
677     else
678     {
679         BaseRendererImpl_Release(&pDSoundRender->renderer.filter.IBaseFilter_iface);
680         CoTaskMemFree(pDSoundRender);
681     }
682
683     return hr;
684 }
685
686 static HRESULT WINAPI DSoundRender_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
687 {
688     DSoundRenderImpl *This = impl_from_IBaseFilter(iface);
689     TRACE("(%p, %p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv);
690
691     *ppv = NULL;
692
693     if (IsEqualIID(riid, &IID_IBasicAudio))
694         *ppv = &This->basicAudio.IBasicAudio_iface;
695     else if (IsEqualIID(riid, &IID_IReferenceClock))
696         *ppv = &This->IReferenceClock_iface;
697     else if (IsEqualIID(riid, &IID_IAMDirectSound))
698         *ppv = &This->IAMDirectSound_iface;
699     else if (IsEqualIID(riid, &IID_IAMFilterMiscFlags))
700         *ppv = &This->IAMFilterMiscFlags_iface;
701     else
702     {
703         HRESULT hr;
704         hr = BaseRendererImpl_QueryInterface(iface, riid, ppv);
705         if (SUCCEEDED(hr))
706             return hr;
707     }
708
709     if (*ppv)
710     {
711         IUnknown_AddRef((IUnknown *)(*ppv));
712         return S_OK;
713     }
714
715     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IVideoWindow))
716         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
717
718     return E_NOINTERFACE;
719 }
720
721 static ULONG WINAPI DSoundRender_Release(IBaseFilter * iface)
722 {
723     DSoundRenderImpl *This = impl_from_IBaseFilter(iface);
724     ULONG refCount = BaseRendererImpl_Release(iface);
725
726     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
727
728     if (!refCount)
729     {
730         if (This->threadid) {
731             PostThreadMessageW(This->threadid, WM_APP, 0, 0);
732             WaitForSingleObject(This->advisethread, INFINITE);
733             CloseHandle(This->advisethread);
734         }
735
736         if (This->dsbuffer)
737             IDirectSoundBuffer_Release(This->dsbuffer);
738         This->dsbuffer = NULL;
739         if (This->dsound)
740             IDirectSound_Release(This->dsound);
741         This->dsound = NULL;
742
743         BasicAudio_Destroy(&This->basicAudio);
744         CloseHandle(This->blocked);
745
746         TRACE("Destroying Audio Renderer\n");
747         CoTaskMemFree(This);
748
749         return 0;
750     }
751     else
752         return refCount;
753 }
754
755 static HRESULT WINAPI DSoundRender_Pause(IBaseFilter * iface)
756 {
757     HRESULT hr = S_OK;
758     DSoundRenderImpl *This = (DSoundRenderImpl *)iface;
759
760     TRACE("(%p/%p)->()\n", This, iface);
761
762     EnterCriticalSection(&This->renderer.csRenderLock);
763     if (This->renderer.filter.state != State_Paused)
764     {
765         if (This->renderer.filter.state == State_Stopped)
766         {
767             if (This->renderer.pInputPin->pin.pConnectedTo)
768                 ResetEvent(This->renderer.evComplete);
769             This->renderer.pInputPin->end_of_stream = 0;
770         }
771
772         hr = IDirectSoundBuffer_Stop(This->dsbuffer);
773         if (SUCCEEDED(hr))
774             This->renderer.filter.state = State_Paused;
775
776         ResetEvent(This->blocked);
777         ResetEvent(This->renderer.RenderEvent);
778     }
779     ResetEvent(This->renderer.ThreadSignal);
780     LeaveCriticalSection(&This->renderer.csRenderLock);
781
782     return hr;
783 }
784
785 static const IBaseFilterVtbl DSoundRender_Vtbl =
786 {
787     DSoundRender_QueryInterface,
788     BaseFilterImpl_AddRef,
789     DSoundRender_Release,
790     BaseFilterImpl_GetClassID,
791     BaseRendererImpl_Stop,
792     DSoundRender_Pause,
793     BaseRendererImpl_Run,
794     BaseRendererImpl_GetState,
795     BaseRendererImpl_SetSyncSource,
796     BaseFilterImpl_GetSyncSource,
797     BaseFilterImpl_EnumPins,
798     BaseRendererImpl_FindPin,
799     BaseFilterImpl_QueryFilterInfo,
800     BaseFilterImpl_JoinFilterGraph,
801     BaseFilterImpl_QueryVendorInfo
802 };
803
804 /*** IUnknown methods ***/
805 static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
806                                                 REFIID riid,
807                                                 LPVOID*ppvObj) {
808     DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
809
810     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
811
812     return DSoundRender_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppvObj);
813 }
814
815 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
816     DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
817
818     TRACE("(%p/%p)->()\n", This, iface);
819
820     return BaseFilterImpl_AddRef(&This->renderer.filter.IBaseFilter_iface);
821 }
822
823 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
824     DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
825
826     TRACE("(%p/%p)->()\n", This, iface);
827
828     return DSoundRender_Release(&This->renderer.filter.IBaseFilter_iface);
829 }
830
831 /*** IBasicAudio methods ***/
832 static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
833                                             LONG lVolume) {
834     DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
835
836     TRACE("(%p/%p)->(%d)\n", This, iface, lVolume);
837
838     if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
839         return E_INVALIDARG;
840
841     if (This->dsbuffer) {
842         if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
843             return E_FAIL;
844     }
845
846     This->volume = lVolume;
847     return S_OK;
848 }
849
850 static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
851                                             LONG *plVolume) {
852     DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
853
854     TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
855
856     if (!plVolume)
857         return E_POINTER;
858
859     *plVolume = This->volume;
860     return S_OK;
861 }
862
863 static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
864                                              LONG lBalance) {
865     DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
866
867     TRACE("(%p/%p)->(%d)\n", This, iface, lBalance);
868
869     if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
870         return E_INVALIDARG;
871
872     if (This->dsbuffer) {
873         if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
874             return E_FAIL;
875     }
876
877     This->pan = lBalance;
878     return S_OK;
879 }
880
881 static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
882                                              LONG *plBalance) {
883     DSoundRenderImpl *This = impl_from_IBasicAudio(iface);
884
885     TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
886
887     if (!plBalance)
888         return E_POINTER;
889
890     *plBalance = This->pan;
891     return S_OK;
892 }
893
894 static const IBasicAudioVtbl IBasicAudio_Vtbl =
895 {
896     Basicaudio_QueryInterface,
897     Basicaudio_AddRef,
898     Basicaudio_Release,
899     BasicAudioImpl_GetTypeInfoCount,
900     BasicAudioImpl_GetTypeInfo,
901     BasicAudioImpl_GetIDsOfNames,
902     BasicAudioImpl_Invoke,
903     Basicaudio_put_Volume,
904     Basicaudio_get_Volume,
905     Basicaudio_put_Balance,
906     Basicaudio_get_Balance
907 };
908
909 struct dsoundrender_timer {
910     struct dsoundrender_timer *next;
911     REFERENCE_TIME start;
912     REFERENCE_TIME periodicity;
913     HANDLE handle;
914     DWORD cookie;
915 };
916 static LONG cookie_counter = 1;
917
918 static DWORD WINAPI DSoundAdviseThread(LPVOID lpParam) {
919     DSoundRenderImpl *This = lpParam;
920     struct dsoundrender_timer head = { };
921     MSG msg;
922
923     TRACE("(%p): Main Loop\n", This);
924
925     PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
926     SetEvent(This->thread_wait);
927
928     while (1)
929     {
930         HRESULT hr;
931         REFERENCE_TIME curtime = 0;
932         BOOL ret;
933         struct dsoundrender_timer *prev = &head, *cur;
934
935         hr = IReferenceClock_GetTime(&This->IReferenceClock_iface, &curtime);
936         if (SUCCEEDED(hr)) {
937             TRACE("Time: %s\n", wine_dbgstr_longlong(curtime));
938             while (prev->next) {
939                 cur = prev->next;
940                 if (cur->start > curtime) {
941                     TRACE("Skipping %p\n", cur);
942                     prev = cur;
943                 } else if (cur->periodicity) {
944                     while (cur->start <= curtime) {
945                         cur->start += cur->periodicity;
946                         ReleaseSemaphore(cur->handle, 1, NULL);
947                     }
948                     prev = cur;
949                 } else {
950                     struct dsoundrender_timer *next = cur->next;
951                     TRACE("Firing %p %s < %s\n", cur, wine_dbgstr_longlong(cur->start), wine_dbgstr_longlong(curtime));
952                     SetEvent(cur->handle);
953                     HeapFree(GetProcessHeap(), 0, cur);
954                     prev->next = next;
955                 }
956             }
957         }
958         if (!head.next)
959             ret = GetMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4);
960         else
961             ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE);
962         while (ret) {
963             switch (LOWORD(msg.message) - WM_APP) {
964                 case 0: TRACE("Exiting\n"); return 0;
965                 case 1:
966                 case 2: {
967                     struct dsoundrender_timer *t = (struct dsoundrender_timer *)msg.wParam;
968                     if (LOWORD(msg.message) - WM_APP == 1)
969                         TRACE("Adding one-shot timer %p\n", t);
970                     else
971                         TRACE("Adding periodic timer %p\n", t);
972                     t->next = head.next;
973                     head.next = t;
974                     break;
975                 }
976                 case 3:
977                     prev = &head;
978                     while (prev->next) {
979                         cur = prev->next;
980                         if (cur->cookie == msg.wParam) {
981                             struct dsoundrender_timer *next = cur->next;
982                             HeapFree(GetProcessHeap(), 0, cur);
983                             prev->next = next;
984                             break;
985                         }
986                         prev = cur;
987                     }
988                     break;
989             }
990             ret = PeekMessageW(&msg, INVALID_HANDLE_VALUE, WM_APP, WM_APP + 4, PM_REMOVE);
991         }
992         MsgWaitForMultipleObjects(0, NULL, 5, QS_POSTMESSAGE, 0);
993     }
994     return 0;
995 }
996
997 /*** IUnknown methods ***/
998 static HRESULT WINAPI ReferenceClock_QueryInterface(IReferenceClock *iface,
999                                                 REFIID riid,
1000                                                 LPVOID*ppvObj)
1001 {
1002     DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1003
1004     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1005
1006     return DSoundRender_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppvObj);
1007 }
1008
1009 static ULONG WINAPI ReferenceClock_AddRef(IReferenceClock *iface)
1010 {
1011     DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1012
1013     TRACE("(%p/%p)->()\n", This, iface);
1014
1015     return BaseFilterImpl_AddRef(&This->renderer.filter.IBaseFilter_iface);
1016 }
1017
1018 static ULONG WINAPI ReferenceClock_Release(IReferenceClock *iface)
1019 {
1020     DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1021
1022     TRACE("(%p/%p)->()\n", This, iface);
1023
1024     return DSoundRender_Release(&This->renderer.filter.IBaseFilter_iface);
1025 }
1026
1027 /*** IReferenceClock methods ***/
1028 static HRESULT WINAPI ReferenceClock_GetTime(IReferenceClock *iface,
1029                                              REFERENCE_TIME *pTime)
1030 {
1031     DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1032     HRESULT hr = E_FAIL;
1033
1034     TRACE("(%p/%p)->(%p)\n", This, iface, pTime);
1035     if (!pTime)
1036         return E_POINTER;
1037
1038     if (This->dsbuffer) {
1039         DWORD writepos1, writepos2;
1040         EnterCriticalSection(&This->renderer.filter.csFilter);
1041         DSoundRender_UpdatePositions(This, &writepos1, &writepos2);
1042         if (This->renderer.pInputPin && This->renderer.pInputPin->pin.mtCurrent.pbFormat)
1043         {
1044             *pTime = This->play_time + time_from_pos(This, This->last_playpos);
1045             hr = S_OK;
1046         }
1047         else
1048         {
1049             ERR("pInputPin Disconncted\n");
1050             hr = E_FAIL;
1051         }
1052         LeaveCriticalSection(&This->renderer.filter.csFilter);
1053     }
1054     if (FAILED(hr))
1055         WARN("Could not get reference time (%x)!\n", hr);
1056
1057     return hr;
1058 }
1059
1060 static HRESULT WINAPI ReferenceClock_AdviseTime(IReferenceClock *iface,
1061                                                 REFERENCE_TIME rtBaseTime,
1062                                                 REFERENCE_TIME rtStreamTime,
1063                                                 HEVENT hEvent,
1064                                                 DWORD_PTR *pdwAdviseCookie)
1065 {
1066     DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1067     REFERENCE_TIME when = rtBaseTime + rtStreamTime;
1068     REFERENCE_TIME future;
1069     TRACE("(%p/%p)->(%s, %s, %p, %p)\n", This, iface, wine_dbgstr_longlong(rtBaseTime), wine_dbgstr_longlong(rtStreamTime), (void*)hEvent, pdwAdviseCookie);
1070
1071     if (when <= 0)
1072         return E_INVALIDARG;
1073
1074     if (!pdwAdviseCookie)
1075         return E_POINTER;
1076
1077     EnterCriticalSection(&This->renderer.filter.csFilter);
1078     future = when - This->play_time;
1079     if (!This->threadid && This->dsbuffer) {
1080         This->thread_wait = CreateEventW(0, 0, 0, 0);
1081         This->advisethread = CreateThread(NULL, 0, DSoundAdviseThread, This, 0, &This->threadid);
1082         WaitForSingleObject(This->thread_wait, INFINITE);
1083         CloseHandle(This->thread_wait);
1084     }
1085     LeaveCriticalSection(&This->renderer.filter.csFilter);
1086     /* If it's in the past or the next millisecond, trigger immediately  */
1087     if (future <= 10000) {
1088         SetEvent((HANDLE)hEvent);
1089         *pdwAdviseCookie = 0;
1090     } else {
1091         struct dsoundrender_timer *t = HeapAlloc(GetProcessHeap(), 0, sizeof(*t));
1092         t->next = NULL;
1093         t->start = when;
1094         t->periodicity = 0;
1095         t->handle = (HANDLE)hEvent;
1096         t->cookie = InterlockedIncrement(&cookie_counter);
1097         PostThreadMessageW(This->threadid, WM_APP+1, (WPARAM)t, 0);
1098         *pdwAdviseCookie = t->cookie;
1099     }
1100
1101     return S_OK;
1102 }
1103
1104 static HRESULT WINAPI ReferenceClock_AdvisePeriodic(IReferenceClock *iface,
1105                                                     REFERENCE_TIME rtStartTime,
1106                                                     REFERENCE_TIME rtPeriodTime,
1107                                                     HSEMAPHORE hSemaphore,
1108                                                     DWORD_PTR *pdwAdviseCookie)
1109 {
1110     DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1111     struct dsoundrender_timer *t;
1112
1113     TRACE("(%p/%p)->(%s, %s, %p, %p)\n", This, iface, wine_dbgstr_longlong(rtStartTime), wine_dbgstr_longlong(rtPeriodTime), (void*)hSemaphore, pdwAdviseCookie);
1114
1115     if (rtStartTime <= 0 || rtPeriodTime <= 0)
1116         return E_INVALIDARG;
1117
1118     if (!pdwAdviseCookie)
1119         return E_POINTER;
1120
1121     EnterCriticalSection(&This->renderer.filter.csFilter);
1122     if (!This->threadid && This->dsbuffer) {
1123         This->thread_wait = CreateEventW(0, 0, 0, 0);
1124         This->advisethread = CreateThread(NULL, 0, DSoundAdviseThread, This, 0, &This->threadid);
1125         WaitForSingleObject(This->thread_wait, INFINITE);
1126         CloseHandle(This->thread_wait);
1127     }
1128     LeaveCriticalSection(&This->renderer.filter.csFilter);
1129
1130     t = HeapAlloc(GetProcessHeap(), 0, sizeof(*t));
1131     t->next = NULL;
1132     t->start = rtStartTime;
1133     t->periodicity = rtPeriodTime;
1134     t->handle = (HANDLE)hSemaphore;
1135     t->cookie = InterlockedIncrement(&cookie_counter);
1136     PostThreadMessageW(This->threadid, WM_APP+1, (WPARAM)t, 0);
1137     *pdwAdviseCookie = t->cookie;
1138
1139     return S_OK;
1140 }
1141
1142 static HRESULT WINAPI ReferenceClock_Unadvise(IReferenceClock *iface,
1143                                               DWORD_PTR dwAdviseCookie)
1144 {
1145     DSoundRenderImpl *This = impl_from_IReferenceClock(iface);
1146
1147     TRACE("(%p/%p)->(%p)\n", This, iface, (void*)dwAdviseCookie);
1148     if (!This->advisethread || !dwAdviseCookie)
1149         return S_FALSE;
1150     PostThreadMessageW(This->threadid, WM_APP+3, dwAdviseCookie, 0);
1151     return S_OK;
1152 }
1153
1154 static const IReferenceClockVtbl IReferenceClock_Vtbl =
1155 {
1156     ReferenceClock_QueryInterface,
1157     ReferenceClock_AddRef,
1158     ReferenceClock_Release,
1159     ReferenceClock_GetTime,
1160     ReferenceClock_AdviseTime,
1161     ReferenceClock_AdvisePeriodic,
1162     ReferenceClock_Unadvise
1163 };
1164
1165 /*** IUnknown methods ***/
1166 static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface,
1167                                                 REFIID riid,
1168                                                 LPVOID*ppvObj)
1169 {
1170     DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1171
1172     TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj);
1173
1174     return DSoundRender_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppvObj);
1175 }
1176
1177 static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
1178 {
1179     DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1180
1181     TRACE("(%p/%p)->()\n", This, iface);
1182
1183     return BaseFilterImpl_AddRef(&This->renderer.filter.IBaseFilter_iface);
1184 }
1185
1186 static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
1187 {
1188     DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1189
1190     TRACE("(%p/%p)->()\n", This, iface);
1191
1192     return DSoundRender_Release(&This->renderer.filter.IBaseFilter_iface);
1193 }
1194
1195 /*** IAMDirectSound methods ***/
1196 static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface,  IDirectSound **ds)
1197 {
1198     DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1199
1200     FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1201
1202     return E_NOTIMPL;
1203 }
1204
1205 static HRESULT WINAPI AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1206 {
1207     DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1208
1209     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1210
1211     return E_NOTIMPL;
1212 }
1213
1214 static HRESULT WINAPI AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
1215 {
1216     DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1217
1218     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1219
1220     return E_NOTIMPL;
1221 }
1222
1223 static HRESULT WINAPI AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound *iface, IDirectSound *ds)
1224 {
1225     DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1226
1227     FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
1228
1229     return E_NOTIMPL;
1230 }
1231
1232 static HRESULT WINAPI AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1233 {
1234     DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1235
1236     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1237
1238     return E_NOTIMPL;
1239 }
1240
1241 static HRESULT WINAPI AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
1242 {
1243     DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1244
1245     FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
1246
1247     return E_NOTIMPL;
1248 }
1249
1250 static HRESULT WINAPI AMDirectSound_SetFocusWindow(IAMDirectSound *iface, HWND hwnd, BOOL bgsilent)
1251 {
1252     DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1253
1254     FIXME("(%p/%p)->(%p,%d): stub\n", This, iface, hwnd, bgsilent);
1255
1256     return E_NOTIMPL;
1257 }
1258
1259 static HRESULT WINAPI AMDirectSound_GetFocusWindow(IAMDirectSound *iface, HWND hwnd)
1260 {
1261     DSoundRenderImpl *This = impl_from_IAMDirectSound(iface);
1262
1263     FIXME("(%p/%p)->(%p): stub\n", This, iface, hwnd);
1264
1265     return E_NOTIMPL;
1266 }
1267
1268 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl =
1269 {
1270     AMDirectSound_QueryInterface,
1271     AMDirectSound_AddRef,
1272     AMDirectSound_Release,
1273     AMDirectSound_GetDirectSoundInterface,
1274     AMDirectSound_GetPrimaryBufferInterface,
1275     AMDirectSound_GetSecondaryBufferInterface,
1276     AMDirectSound_ReleaseDirectSoundInterface,
1277     AMDirectSound_ReleasePrimaryBufferInterface,
1278     AMDirectSound_ReleaseSecondaryBufferInterface,
1279     AMDirectSound_SetFocusWindow,
1280     AMDirectSound_GetFocusWindow
1281 };
1282
1283 static HRESULT WINAPI AMFilterMiscFlags_QueryInterface(IAMFilterMiscFlags *iface, REFIID riid, void **ppv) {
1284     DSoundRenderImpl *This = impl_from_IAMFilterMiscFlags(iface);
1285     return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
1286 }
1287
1288 static ULONG WINAPI AMFilterMiscFlags_AddRef(IAMFilterMiscFlags *iface) {
1289     DSoundRenderImpl *This = impl_from_IAMFilterMiscFlags(iface);
1290     return IUnknown_AddRef((IUnknown*)This);
1291 }
1292
1293 static ULONG WINAPI AMFilterMiscFlags_Release(IAMFilterMiscFlags *iface) {
1294     DSoundRenderImpl *This = impl_from_IAMFilterMiscFlags(iface);
1295     return IUnknown_Release((IUnknown*)This);
1296 }
1297
1298 static ULONG WINAPI AMFilterMiscFlags_GetMiscFlags(IAMFilterMiscFlags *iface) {
1299     return AM_FILTER_MISC_FLAGS_IS_RENDERER;
1300 }
1301
1302 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl = {
1303     AMFilterMiscFlags_QueryInterface,
1304     AMFilterMiscFlags_AddRef,
1305     AMFilterMiscFlags_Release,
1306     AMFilterMiscFlags_GetMiscFlags
1307 };