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