2 * Direct Sound Audio Renderer
4 * Copyright 2004 Christian Costa
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.
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.
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
21 #include "quartz_private.h"
23 /* NOTE: buffer can still be filled completely,
24 * but we start waiting until only this amount is buffered
26 static const REFERENCE_TIME DSoundRenderer_Max_Fill
= 150 * 10000;
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
;
34 typedef struct DSoundRenderImpl
36 BaseRenderer renderer
;
37 BasicAudio basicAudio
;
39 IReferenceClock IReferenceClock_iface
;
40 IAMDirectSound IAMDirectSound_iface
;
41 IAMFilterMiscFlags IAMFilterMiscFlags_iface
;
43 IDirectSound8
*dsound
;
44 LPDIRECTSOUNDBUFFER dsbuffer
;
47 DWORD last_playpos
, writepos
;
49 REFERENCE_TIME play_time
;
57 HANDLE advisethread
, thread_wait
;
60 static inline DSoundRenderImpl
*impl_from_BaseRenderer(BaseRenderer
*iface
)
62 return CONTAINING_RECORD(iface
, DSoundRenderImpl
, renderer
);
65 static inline DSoundRenderImpl
*impl_from_IBaseFilter(IBaseFilter
*iface
)
67 return CONTAINING_RECORD(iface
, DSoundRenderImpl
, renderer
.filter
.IBaseFilter_iface
);
70 static inline DSoundRenderImpl
*impl_from_IBasicAudio(IBasicAudio
*iface
)
72 return CONTAINING_RECORD(iface
, DSoundRenderImpl
, basicAudio
.IBasicAudio_iface
);
75 static inline DSoundRenderImpl
*impl_from_IReferenceClock(IReferenceClock
*iface
)
77 return CONTAINING_RECORD(iface
, DSoundRenderImpl
, IReferenceClock_iface
);
80 static inline DSoundRenderImpl
*impl_from_IAMDirectSound(IAMDirectSound
*iface
)
82 return CONTAINING_RECORD(iface
, DSoundRenderImpl
, IAMDirectSound_iface
);
85 static inline DSoundRenderImpl
*impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags
*iface
)
87 return CONTAINING_RECORD(iface
, DSoundRenderImpl
, IAMFilterMiscFlags_iface
);
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
;
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
;
102 ret
-= ret
% wfx
->nBlockAlign
;
106 static void DSoundRender_UpdatePositions(DSoundRenderImpl
*This
, DWORD
*seqwritepos
, DWORD
*minwritepos
) {
107 WAVEFORMATEX
*wfx
= (WAVEFORMATEX
*)This
->renderer
.pInputPin
->pin
.mtCurrent
.pbFormat
;
109 DWORD size1
, size2
, playpos
, writepos
, old_writepos
, old_playpos
, adv
;
110 BOOL writepos_set
= This
->writepos
< This
->buf_size
;
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
;
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
);
123 adv
= playpos
- old_playpos
;
124 This
->last_playpos
= playpos
;
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
);
132 *minwritepos
= writepos
;
133 if (!writepos_set
|| old_writepos
< writepos
) {
135 This
->writepos
= This
->buf_size
;
136 FIXME("Underrun of data occurred!\n");
138 *seqwritepos
= writepos
;
140 *seqwritepos
= This
->writepos
;
143 static HRESULT
DSoundRender_GetWritePos(DSoundRenderImpl
*This
, DWORD
*ret_writepos
, REFERENCE_TIME write_at
, DWORD
*pfree
, DWORD
*skip
)
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
;
151 DSoundRender_UpdatePositions(This
, &writepos
, &min_writepos
);
152 playpos
= This
->last_playpos
;
153 if (This
->renderer
.filter
.pClock
== &This
->IReferenceClock_iface
) {
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
;
163 if (writepos
== min_writepos
)
168 *ret_writepos
= writepos
;
172 if (writepos
>= playpos
)
173 writepos_t
= cur
+ time_from_pos(This
, writepos
- playpos
);
175 writepos_t
= cur
+ time_from_pos(This
, This
->buf_size
+ writepos
- playpos
);
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
);
190 min_writepos_t
= cur
+ time_from_pos(This
, This
->buf_size
- playpos
+ min_writepos
);
191 past
= min_writepos_t
- write_at
;
193 DWORD skipbytes
= pos_from_time(This
, past
);
194 WARN("Skipping %u bytes\n", skipbytes
);
196 *ret_writepos
= min_writepos
;
198 DWORD aheadbytes
= pos_from_time(This
, -past
);
199 WARN("Advancing %u bytes\n", aheadbytes
);
200 *ret_writepos
= (min_writepos
+ aheadbytes
) % This
->buf_size
;
202 } else /* delta_t > 0 */ {
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
)
209 *ret_writepos
= (min_writepos
+ aheadbytes
) % This
->buf_size
;
212 if (playpos
> *ret_writepos
)
213 *pfree
= playpos
- *ret_writepos
;
214 else if (playpos
== *ret_writepos
)
215 *pfree
= This
->buf_size
- wfx
->nBlockAlign
;
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));
225 static HRESULT
DSoundRender_HandleEndOfStream(DSoundRenderImpl
*This
)
227 while (This
->renderer
.filter
.state
== State_Running
)
230 DSoundRender_UpdatePositions(This
, &pos1
, &pos2
);
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
);
246 static HRESULT
DSoundRender_SendSampleData(DSoundRenderImpl
* This
, REFERENCE_TIME tStart
, REFERENCE_TIME tStop
, const BYTE
*data
, DWORD size
)
250 while (size
&& This
->renderer
.filter
.state
!= State_Stopped
) {
251 DWORD writepos
, skip
= 0, free
, size1
, size2
, ret
;
254 if (This
->renderer
.filter
.state
== State_Running
)
255 hr
= DSoundRender_GetWritePos(This
, &writepos
, tStart
, &free
, &skip
);
261 LeaveCriticalSection(&This
->renderer
.csRenderLock
);
262 ret
= WaitForSingleObject(This
->blocked
, 10);
263 EnterCriticalSection(&This
->renderer
.csRenderLock
);
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
;
269 if (ret
!= WAIT_TIMEOUT
)
276 FIXME("Sample dropped %u of %u bytes\n", skip
, size
);
282 hr
= IDirectSoundBuffer_Lock(This
->dsbuffer
, writepos
, min(free
, size
), (void**)&buf1
, &size1
, (void**)&buf2
, &size2
, 0);
284 ERR("Unable to lock sound buffer! (%x)\n", hr
);
287 memcpy(buf1
, data
, size1
);
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
;
299 static HRESULT WINAPI
DSoundRender_ShouldDrawSampleNow(BaseRenderer
*This
, IMediaSample
*pMediaSample
, REFERENCE_TIME
*pStartTime
, REFERENCE_TIME
*pEndTime
)
301 /* We time ourselves do not use the base renderers timing */
306 static HRESULT WINAPI
DSoundRender_PrepareReceive(BaseRenderer
*iface
, IMediaSample
*pSample
)
308 DSoundRenderImpl
*This
= impl_from_BaseRenderer(iface
);
312 if (IMediaSample_GetMediaType(pSample
, &amt
) == S_OK
)
314 AM_MEDIA_TYPE
*orig
= &This
->renderer
.pInputPin
->pin
.mtCurrent
;
315 WAVEFORMATEX
*origfmt
= (WAVEFORMATEX
*)orig
->pbFormat
;
316 WAVEFORMATEX
*newfmt
= (WAVEFORMATEX
*)amt
->pbFormat
;
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
)
324 if (origfmt
->nSamplesPerSec
!= newfmt
->nSamplesPerSec
)
326 hr
= IDirectSoundBuffer_SetFrequency(This
->dsbuffer
,
327 newfmt
->nSamplesPerSec
);
329 return VFW_E_TYPE_NOT_ACCEPTED
;
331 CopyMediaType(orig
, amt
);
332 IMediaSample_SetMediaType(pSample
, NULL
);
336 return VFW_E_TYPE_NOT_ACCEPTED
;
341 static HRESULT WINAPI
DSoundRender_DoRenderSample(BaseRenderer
*iface
, IMediaSample
* pSample
)
343 DSoundRenderImpl
*This
= impl_from_BaseRenderer(iface
);
344 LPBYTE pbSrcStream
= NULL
;
345 LONG cbSrcStream
= 0;
346 REFERENCE_TIME tStart
, tStop
;
349 TRACE("%p %p\n", iface
, pSample
);
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
355 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
358 ERR("Cannot get pointer to sample data (%x)\n", hr
);
362 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
364 ERR("Cannot get sample time (%x)\n", hr
);
368 IMediaSample_IsDiscontinuity(pSample
);
370 if (IMediaSample_IsPreroll(pSample
) == S_OK
)
376 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
377 TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream
, cbSrcStream
);
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;
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
;
389 q
.Type
= (jitter
> 0 ? Famine
: Flood
);
392 q
.TimeStamp
= tStart
;
393 IQualityControl_Notify((IQualityControl
*)This
->renderer
.qcimpl
, (IBaseFilter
*)This
, q
);
398 static HRESULT WINAPI
DSoundRender_CheckMediaType(BaseRenderer
*iface
, const AM_MEDIA_TYPE
* pmt
)
400 WAVEFORMATEX
* format
;
402 if (!IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Audio
))
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
);
414 if (!IsEqualIID(&pmt
->subtype
, &MEDIASUBTYPE_PCM
))
420 static VOID WINAPI
DSoundRender_OnStopStreaming(BaseRenderer
* iface
)
422 DSoundRenderImpl
*This
= impl_from_BaseRenderer(iface
);
424 TRACE("(%p/%p)->()\n", This
, iface
);
426 IDirectSoundBuffer_Stop(This
->dsbuffer
);
427 This
->writepos
= This
->buf_size
;
428 SetEvent(This
->blocked
);
431 static VOID WINAPI
DSoundRender_OnStartStreaming(BaseRenderer
* iface
)
433 DSoundRenderImpl
*This
= impl_from_BaseRenderer(iface
);
435 TRACE("(%p)\n", This
);
437 if (This
->renderer
.pInputPin
->pin
.pConnectedTo
)
439 if (This
->renderer
.filter
.state
== State_Paused
)
441 /* Unblock our thread, state changing from paused to running doesn't need a reset for state change */
442 SetEvent(This
->blocked
);
444 IDirectSoundBuffer_Play(This
->dsbuffer
, 0, 0, DSBPLAY_LOOPING
);
445 ResetEvent(This
->blocked
);
449 static HRESULT WINAPI
DSoundRender_CompleteConnect(BaseRenderer
* iface
, IPin
* pReceivePin
)
451 DSoundRenderImpl
*This
= impl_from_BaseRenderer(iface
);
452 const AM_MEDIA_TYPE
* pmt
= &This
->renderer
.pInputPin
->pin
.mtCurrent
;
454 WAVEFORMATEX
*format
;
455 DSBUFFERDESC buf_desc
;
457 TRACE("(%p)->(%p)\n", This
, pReceivePin
);
458 dump_AM_MEDIA_TYPE(pmt
);
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
);
465 format
= (WAVEFORMATEX
*)pmt
->pbFormat
;
467 This
->buf_size
= format
->nAvgBytesPerSec
;
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
;
479 ERR("Can't create sound buffer (%x)\n", hr
);
483 hr
= IDirectSoundBuffer_SetVolume(This
->dsbuffer
, This
->volume
);
485 ERR("Can't set volume to %d (%x)\n", This
->volume
, hr
);
487 hr
= IDirectSoundBuffer_SetPan(This
->dsbuffer
, This
->pan
);
489 ERR("Can't set pan to %d (%x)\n", This
->pan
, hr
);
493 if (FAILED(hr
) && hr
!= VFW_E_ALREADY_CONNECTED
)
496 IDirectSoundBuffer_Release(This
->dsbuffer
);
497 This
->dsbuffer
= NULL
;
503 static HRESULT WINAPI
DSoundRender_BreakConnect(BaseRenderer
* iface
)
505 DSoundRenderImpl
*This
= impl_from_BaseRenderer(iface
);
507 TRACE("(%p)->()\n", iface
);
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
);
517 IDirectSoundBuffer_Release(This
->dsbuffer
);
518 This
->dsbuffer
= NULL
;
523 static HRESULT WINAPI
DSoundRender_EndOfStream(BaseRenderer
* iface
)
525 DSoundRenderImpl
*This
= impl_from_BaseRenderer(iface
);
528 TRACE("(%p)->()\n",iface
);
530 hr
= BaseRendererImpl_EndOfStream(iface
);
537 hr
= DSoundRender_HandleEndOfStream(This
);
542 static HRESULT WINAPI
DSoundRender_BeginFlush(BaseRenderer
* iface
)
544 DSoundRenderImpl
*This
= impl_from_BaseRenderer(iface
);
547 BaseRendererImpl_BeginFlush(iface
);
548 SetEvent(This
->blocked
);
553 static HRESULT WINAPI
DSoundRender_EndFlush(BaseRenderer
* iface
)
555 DSoundRenderImpl
*This
= impl_from_BaseRenderer(iface
);
559 BaseRendererImpl_EndFlush(iface
);
560 if (This
->renderer
.filter
.state
!= State_Stopped
)
561 ResetEvent(This
->blocked
);
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
;
578 static const BaseRendererFuncTable BaseFuncTable
= {
579 DSoundRender_CheckMediaType
,
580 DSoundRender_DoRenderSample
,
585 DSoundRender_OnStartStreaming
,
586 DSoundRender_OnStopStreaming
,
590 DSoundRender_ShouldDrawSampleNow
,
591 DSoundRender_PrepareReceive
,
593 DSoundRender_CompleteConnect
,
594 DSoundRender_BreakConnect
,
595 DSoundRender_EndOfStream
,
596 DSoundRender_BeginFlush
,
597 DSoundRender_EndFlush
,
600 HRESULT
DSoundRender_create(IUnknown
* pUnkOuter
, LPVOID
* ppv
)
603 DSoundRenderImpl
* pDSoundRender
;
605 TRACE("(%p, %p)\n", pUnkOuter
, ppv
);
610 return CLASS_E_NOAGGREGATION
;
612 pDSoundRender
= CoTaskMemAlloc(sizeof(DSoundRenderImpl
));
614 return E_OUTOFMEMORY
;
615 ZeroMemory(pDSoundRender
, sizeof(DSoundRenderImpl
));
617 hr
= BaseRenderer_Init(&pDSoundRender
->renderer
, &DSoundRender_Vtbl
, (IUnknown
*)pDSoundRender
, &CLSID_DSoundRender
, (DWORD_PTR
)(__FILE__
": DSoundRenderImpl.csFilter"), &BaseFuncTable
);
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
;
626 hr
= DirectSoundCreate8(NULL
, &pDSoundRender
->dsound
, NULL
);
628 ERR("Cannot create Direct Sound object (%x)\n", hr
);
630 hr
= IDirectSound_SetCooperativeLevel(pDSoundRender
->dsound
, GetDesktopWindow(), DSSCL_PRIORITY
);
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
);
639 IDirectSoundBuffer_Play(buf
, 0, 0, DSBPLAY_LOOPING
);
640 IDirectSoundBuffer_Release(buf
);
648 pDSoundRender
->blocked
= CreateEventW(NULL
, TRUE
, TRUE
, NULL
);
650 if (!pDSoundRender
->blocked
|| FAILED(hr
))
652 IBaseFilter_Release(&pDSoundRender
->renderer
.filter
.IBaseFilter_iface
);
653 return HRESULT_FROM_WIN32(GetLastError());
656 *ppv
= pDSoundRender
;
660 BaseRendererImpl_Release(&pDSoundRender
->renderer
.filter
.IBaseFilter_iface
);
661 CoTaskMemFree(pDSoundRender
);
667 static HRESULT WINAPI
DSoundRender_QueryInterface(IBaseFilter
* iface
, REFIID riid
, LPVOID
* ppv
)
669 DSoundRenderImpl
*This
= impl_from_IBaseFilter(iface
);
670 TRACE("(%p, %p)->(%s, %p)\n", This
, iface
, qzdebugstr_guid(riid
), ppv
);
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
;
685 hr
= BaseRendererImpl_QueryInterface(iface
, riid
, ppv
);
692 IUnknown_AddRef((IUnknown
*)(*ppv
));
696 if (!IsEqualIID(riid
, &IID_IPin
) && !IsEqualIID(riid
, &IID_IVideoWindow
))
697 FIXME("No interface for %s!\n", qzdebugstr_guid(riid
));
699 return E_NOINTERFACE
;
702 static ULONG WINAPI
DSoundRender_Release(IBaseFilter
* iface
)
704 DSoundRenderImpl
*This
= impl_from_IBaseFilter(iface
);
705 ULONG refCount
= BaseRendererImpl_Release(iface
);
707 TRACE("(%p)->() Release from %d\n", This
, refCount
+ 1);
711 if (This
->threadid
) {
712 PostThreadMessageW(This
->threadid
, WM_APP
, 0, 0);
713 WaitForSingleObject(This
->advisethread
, INFINITE
);
714 CloseHandle(This
->advisethread
);
718 IDirectSoundBuffer_Release(This
->dsbuffer
);
719 This
->dsbuffer
= NULL
;
721 IDirectSound_Release(This
->dsound
);
724 BasicAudio_Destroy(&This
->basicAudio
);
725 CloseHandle(This
->blocked
);
727 TRACE("Destroying Audio Renderer\n");
736 static HRESULT WINAPI
DSoundRender_Pause(IBaseFilter
* iface
)
739 DSoundRenderImpl
*This
= (DSoundRenderImpl
*)iface
;
741 TRACE("(%p/%p)->()\n", This
, iface
);
743 EnterCriticalSection(&This
->renderer
.csRenderLock
);
744 if (This
->renderer
.filter
.state
!= State_Paused
)
746 if (This
->renderer
.filter
.state
== State_Stopped
)
748 if (This
->renderer
.pInputPin
->pin
.pConnectedTo
)
749 ResetEvent(This
->renderer
.evComplete
);
750 This
->renderer
.pInputPin
->end_of_stream
= 0;
753 hr
= IDirectSoundBuffer_Stop(This
->dsbuffer
);
755 This
->renderer
.filter
.state
= State_Paused
;
757 ResetEvent(This
->blocked
);
758 ResetEvent(This
->renderer
.RenderEvent
);
760 ResetEvent(This
->renderer
.ThreadSignal
);
761 LeaveCriticalSection(&This
->renderer
.csRenderLock
);
766 static const IBaseFilterVtbl DSoundRender_Vtbl
=
768 DSoundRender_QueryInterface
,
769 BaseFilterImpl_AddRef
,
770 DSoundRender_Release
,
771 BaseFilterImpl_GetClassID
,
772 BaseRendererImpl_Stop
,
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
785 /*** IUnknown methods ***/
786 static HRESULT WINAPI
Basicaudio_QueryInterface(IBasicAudio
*iface
,
789 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
791 TRACE("(%p/%p)->(%s, %p)\n", This
, iface
, debugstr_guid(riid
), ppvObj
);
793 return DSoundRender_QueryInterface(&This
->renderer
.filter
.IBaseFilter_iface
, riid
, ppvObj
);
796 static ULONG WINAPI
Basicaudio_AddRef(IBasicAudio
*iface
) {
797 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
799 TRACE("(%p/%p)->()\n", This
, iface
);
801 return BaseFilterImpl_AddRef(&This
->renderer
.filter
.IBaseFilter_iface
);
804 static ULONG WINAPI
Basicaudio_Release(IBasicAudio
*iface
) {
805 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
807 TRACE("(%p/%p)->()\n", This
, iface
);
809 return DSoundRender_Release(&This
->renderer
.filter
.IBaseFilter_iface
);
812 /*** IBasicAudio methods ***/
813 static HRESULT WINAPI
Basicaudio_put_Volume(IBasicAudio
*iface
,
815 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
817 TRACE("(%p/%p)->(%d)\n", This
, iface
, lVolume
);
819 if (lVolume
> DSBVOLUME_MAX
|| lVolume
< DSBVOLUME_MIN
)
822 if (This
->dsbuffer
) {
823 if (FAILED(IDirectSoundBuffer_SetVolume(This
->dsbuffer
, lVolume
)))
827 This
->volume
= lVolume
;
831 static HRESULT WINAPI
Basicaudio_get_Volume(IBasicAudio
*iface
,
833 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
835 TRACE("(%p/%p)->(%p)\n", This
, iface
, plVolume
);
840 *plVolume
= This
->volume
;
844 static HRESULT WINAPI
Basicaudio_put_Balance(IBasicAudio
*iface
,
846 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
848 TRACE("(%p/%p)->(%d)\n", This
, iface
, lBalance
);
850 if (lBalance
< DSBPAN_LEFT
|| lBalance
> DSBPAN_RIGHT
)
853 if (This
->dsbuffer
) {
854 if (FAILED(IDirectSoundBuffer_SetPan(This
->dsbuffer
, lBalance
)))
858 This
->pan
= lBalance
;
862 static HRESULT WINAPI
Basicaudio_get_Balance(IBasicAudio
*iface
,
864 DSoundRenderImpl
*This
= impl_from_IBasicAudio(iface
);
866 TRACE("(%p/%p)->(%p)\n", This
, iface
, plBalance
);
871 *plBalance
= This
->pan
;
875 static const IBasicAudioVtbl IBasicAudio_Vtbl
=
877 Basicaudio_QueryInterface
,
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
890 struct dsoundrender_timer
{
891 struct dsoundrender_timer
*next
;
892 REFERENCE_TIME start
;
893 REFERENCE_TIME periodicity
;
897 static LONG cookie_counter
= 1;
899 static DWORD WINAPI
DSoundAdviseThread(LPVOID lpParam
) {
900 DSoundRenderImpl
*This
= lpParam
;
901 struct dsoundrender_timer head
= {NULL
};
904 TRACE("(%p): Main Loop\n", This
);
906 PeekMessageW(&msg
, NULL
, WM_USER
, WM_USER
, PM_NOREMOVE
);
907 SetEvent(This
->thread_wait
);
912 REFERENCE_TIME curtime
= 0;
914 struct dsoundrender_timer
*prev
= &head
, *cur
;
916 hr
= IReferenceClock_GetTime(&This
->IReferenceClock_iface
, &curtime
);
918 TRACE("Time: %s\n", wine_dbgstr_longlong(curtime
));
921 if (cur
->start
> curtime
) {
922 TRACE("Skipping %p\n", cur
);
924 } else if (cur
->periodicity
) {
925 while (cur
->start
<= curtime
) {
926 cur
->start
+= cur
->periodicity
;
927 ReleaseSemaphore(cur
->handle
, 1, NULL
);
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
);
940 ret
= GetMessageW(&msg
, INVALID_HANDLE_VALUE
, WM_APP
, WM_APP
+ 4);
942 ret
= PeekMessageW(&msg
, INVALID_HANDLE_VALUE
, WM_APP
, WM_APP
+ 4, PM_REMOVE
);
944 switch (LOWORD(msg
.message
) - WM_APP
) {
945 case 0: TRACE("Exiting\n"); return 0;
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
);
952 TRACE("Adding periodic timer %p\n", t
);
961 if (cur
->cookie
== msg
.wParam
) {
962 struct dsoundrender_timer
*next
= cur
->next
;
963 HeapFree(GetProcessHeap(), 0, cur
);
971 ret
= PeekMessageW(&msg
, INVALID_HANDLE_VALUE
, WM_APP
, WM_APP
+ 4, PM_REMOVE
);
973 MsgWaitForMultipleObjects(0, NULL
, 5, QS_POSTMESSAGE
, 0);
978 /*** IUnknown methods ***/
979 static HRESULT WINAPI
ReferenceClock_QueryInterface(IReferenceClock
*iface
,
983 DSoundRenderImpl
*This
= impl_from_IReferenceClock(iface
);
985 TRACE("(%p/%p)->(%s, %p)\n", This
, iface
, debugstr_guid(riid
), ppvObj
);
987 return DSoundRender_QueryInterface(&This
->renderer
.filter
.IBaseFilter_iface
, riid
, ppvObj
);
990 static ULONG WINAPI
ReferenceClock_AddRef(IReferenceClock
*iface
)
992 DSoundRenderImpl
*This
= impl_from_IReferenceClock(iface
);
994 TRACE("(%p/%p)->()\n", This
, iface
);
996 return BaseFilterImpl_AddRef(&This
->renderer
.filter
.IBaseFilter_iface
);
999 static ULONG WINAPI
ReferenceClock_Release(IReferenceClock
*iface
)
1001 DSoundRenderImpl
*This
= impl_from_IReferenceClock(iface
);
1003 TRACE("(%p/%p)->()\n", This
, iface
);
1005 return DSoundRender_Release(&This
->renderer
.filter
.IBaseFilter_iface
);
1008 /*** IReferenceClock methods ***/
1009 static HRESULT WINAPI
ReferenceClock_GetTime(IReferenceClock
*iface
,
1010 REFERENCE_TIME
*pTime
)
1012 DSoundRenderImpl
*This
= impl_from_IReferenceClock(iface
);
1013 HRESULT hr
= E_FAIL
;
1015 TRACE("(%p/%p)->(%p)\n", This
, iface
, pTime
);
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
)
1025 *pTime
= This
->play_time
+ time_from_pos(This
, This
->last_playpos
);
1030 ERR("pInputPin Disconnected\n");
1033 LeaveCriticalSection(&This
->renderer
.filter
.csFilter
);
1036 WARN("Could not get reference time (%x)!\n", hr
);
1041 static HRESULT WINAPI
ReferenceClock_AdviseTime(IReferenceClock
*iface
,
1042 REFERENCE_TIME rtBaseTime
,
1043 REFERENCE_TIME rtStreamTime
,
1045 DWORD_PTR
*pdwAdviseCookie
)
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
);
1053 return E_INVALIDARG
;
1055 if (!pdwAdviseCookie
)
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
);
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;
1072 struct dsoundrender_timer
*t
= HeapAlloc(GetProcessHeap(), 0, sizeof(*t
));
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
;
1085 static HRESULT WINAPI
ReferenceClock_AdvisePeriodic(IReferenceClock
*iface
,
1086 REFERENCE_TIME rtStartTime
,
1087 REFERENCE_TIME rtPeriodTime
,
1088 HSEMAPHORE hSemaphore
,
1089 DWORD_PTR
*pdwAdviseCookie
)
1091 DSoundRenderImpl
*This
= impl_from_IReferenceClock(iface
);
1092 struct dsoundrender_timer
*t
;
1094 TRACE("(%p/%p)->(%s, %s, %p, %p)\n", This
, iface
, wine_dbgstr_longlong(rtStartTime
), wine_dbgstr_longlong(rtPeriodTime
), (void*)hSemaphore
, pdwAdviseCookie
);
1096 if (rtStartTime
<= 0 || rtPeriodTime
<= 0)
1097 return E_INVALIDARG
;
1099 if (!pdwAdviseCookie
)
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
);
1109 LeaveCriticalSection(&This
->renderer
.filter
.csFilter
);
1111 t
= HeapAlloc(GetProcessHeap(), 0, sizeof(*t
));
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
;
1123 static HRESULT WINAPI
ReferenceClock_Unadvise(IReferenceClock
*iface
,
1124 DWORD_PTR dwAdviseCookie
)
1126 DSoundRenderImpl
*This
= impl_from_IReferenceClock(iface
);
1128 TRACE("(%p/%p)->(%p)\n", This
, iface
, (void*)dwAdviseCookie
);
1129 if (!This
->advisethread
|| !dwAdviseCookie
)
1131 PostThreadMessageW(This
->threadid
, WM_APP
+3, dwAdviseCookie
, 0);
1135 static const IReferenceClockVtbl IReferenceClock_Vtbl
=
1137 ReferenceClock_QueryInterface
,
1138 ReferenceClock_AddRef
,
1139 ReferenceClock_Release
,
1140 ReferenceClock_GetTime
,
1141 ReferenceClock_AdviseTime
,
1142 ReferenceClock_AdvisePeriodic
,
1143 ReferenceClock_Unadvise
1146 /*** IUnknown methods ***/
1147 static HRESULT WINAPI
AMDirectSound_QueryInterface(IAMDirectSound
*iface
,
1151 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
1153 TRACE("(%p/%p)->(%s, %p)\n", This
, iface
, debugstr_guid(riid
), ppvObj
);
1155 return DSoundRender_QueryInterface(&This
->renderer
.filter
.IBaseFilter_iface
, riid
, ppvObj
);
1158 static ULONG WINAPI
AMDirectSound_AddRef(IAMDirectSound
*iface
)
1160 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
1162 TRACE("(%p/%p)->()\n", This
, iface
);
1164 return BaseFilterImpl_AddRef(&This
->renderer
.filter
.IBaseFilter_iface
);
1167 static ULONG WINAPI
AMDirectSound_Release(IAMDirectSound
*iface
)
1169 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
1171 TRACE("(%p/%p)->()\n", This
, iface
);
1173 return DSoundRender_Release(&This
->renderer
.filter
.IBaseFilter_iface
);
1176 /*** IAMDirectSound methods ***/
1177 static HRESULT WINAPI
AMDirectSound_GetDirectSoundInterface(IAMDirectSound
*iface
, IDirectSound
**ds
)
1179 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
1181 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, ds
);
1186 static HRESULT WINAPI
AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
**buf
)
1188 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
1190 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
1195 static HRESULT WINAPI
AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
**buf
)
1197 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
1199 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
1204 static HRESULT WINAPI
AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound
*iface
, IDirectSound
*ds
)
1206 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
1208 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, ds
);
1213 static HRESULT WINAPI
AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
*buf
)
1215 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
1217 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
1222 static HRESULT WINAPI
AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
*buf
)
1224 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
1226 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
1231 static HRESULT WINAPI
AMDirectSound_SetFocusWindow(IAMDirectSound
*iface
, HWND hwnd
, BOOL bgaudible
)
1233 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
1235 FIXME("(%p/%p)->(%p,%d): stub\n", This
, iface
, hwnd
, bgaudible
);
1240 static HRESULT WINAPI
AMDirectSound_GetFocusWindow(IAMDirectSound
*iface
, HWND
*hwnd
, BOOL
*bgaudible
)
1242 DSoundRenderImpl
*This
= impl_from_IAMDirectSound(iface
);
1244 FIXME("(%p/%p)->(%p,%p): stub\n", This
, iface
, hwnd
, bgaudible
);
1249 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl
=
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
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
);
1269 static ULONG WINAPI
AMFilterMiscFlags_AddRef(IAMFilterMiscFlags
*iface
) {
1270 DSoundRenderImpl
*This
= impl_from_IAMFilterMiscFlags(iface
);
1271 return IBaseFilter_AddRef(&This
->renderer
.filter
.IBaseFilter_iface
);
1274 static ULONG WINAPI
AMFilterMiscFlags_Release(IAMFilterMiscFlags
*iface
) {
1275 DSoundRenderImpl
*This
= impl_from_IAMFilterMiscFlags(iface
);
1276 return IBaseFilter_Release(&This
->renderer
.filter
.IBaseFilter_iface
);
1279 static ULONG WINAPI
AMFilterMiscFlags_GetMiscFlags(IAMFilterMiscFlags
*iface
) {
1280 return AM_FILTER_MISC_FLAGS_IS_RENDERER
;
1283 static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl
= {
1284 AMFilterMiscFlags_QueryInterface
,
1285 AMFilterMiscFlags_AddRef
,
1286 AMFilterMiscFlags_Release
,
1287 AMFilterMiscFlags_GetMiscFlags