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"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
37 /* NOTE: buffer can still be filled completely,
38 * but we start waiting until only this amount is buffered
40 static const REFERENCE_TIME DSoundRenderer_Max_Fill
= 150 * 10000;
44 struct strmbase_filter filter
;
45 struct strmbase_passthrough passthrough
;
46 IAMDirectSound IAMDirectSound_iface
;
47 IBasicAudio IBasicAudio_iface
;
48 IQualityControl IQualityControl_iface
;
49 IUnknown
*system_clock
;
51 struct strmbase_sink sink
;
53 /* Signaled when the filter has completed a state change. The filter waits
54 * for this event in IBaseFilter::GetState(). */
56 /* Signaled when a flush or state change occurs, i.e. anything that needs
57 * to immediately unblock the streaming thread. */
59 REFERENCE_TIME stream_start
;
62 IDirectSound8
*dsound
;
63 LPDIRECTSOUNDBUFFER dsbuffer
;
65 DWORD last_playpos
, writepos
;
71 static struct dsound_render
*impl_from_strmbase_pin(struct strmbase_pin
*iface
)
73 return CONTAINING_RECORD(iface
, struct dsound_render
, sink
.pin
);
76 static struct dsound_render
*impl_from_strmbase_filter(struct strmbase_filter
*iface
)
78 return CONTAINING_RECORD(iface
, struct dsound_render
, filter
);
81 static struct dsound_render
*impl_from_IBasicAudio(IBasicAudio
*iface
)
83 return CONTAINING_RECORD(iface
, struct dsound_render
, IBasicAudio_iface
);
86 static struct dsound_render
*impl_from_IAMDirectSound(IAMDirectSound
*iface
)
88 return CONTAINING_RECORD(iface
, struct dsound_render
, IAMDirectSound_iface
);
91 static REFERENCE_TIME
time_from_pos(struct dsound_render
*This
, DWORD pos
)
93 WAVEFORMATEX
*wfx
= (WAVEFORMATEX
*)This
->sink
.pin
.mt
.pbFormat
;
94 REFERENCE_TIME ret
= 10000000;
95 ret
= ret
* pos
/ wfx
->nAvgBytesPerSec
;
99 static DWORD
pos_from_time(struct dsound_render
*This
, REFERENCE_TIME time
)
101 WAVEFORMATEX
*wfx
= (WAVEFORMATEX
*)This
->sink
.pin
.mt
.pbFormat
;
102 REFERENCE_TIME ret
= time
;
103 ret
*= wfx
->nAvgBytesPerSec
;
105 ret
-= ret
% wfx
->nBlockAlign
;
109 static void DSoundRender_UpdatePositions(struct dsound_render
*This
, DWORD
*seqwritepos
, DWORD
*minwritepos
)
111 WAVEFORMATEX
*wfx
= (WAVEFORMATEX
*)This
->sink
.pin
.mt
.pbFormat
;
113 DWORD size1
, size2
, playpos
, writepos
, old_writepos
, old_playpos
, adv
;
114 BOOL writepos_set
= This
->writepos
< This
->buf_size
;
116 /* Update position and zero */
117 old_writepos
= This
->writepos
;
118 old_playpos
= This
->last_playpos
;
119 if (old_writepos
<= old_playpos
)
120 old_writepos
+= This
->buf_size
;
122 IDirectSoundBuffer_GetCurrentPosition(This
->dsbuffer
, &playpos
, &writepos
);
123 if (old_playpos
> playpos
)
124 adv
= This
->buf_size
+ playpos
- old_playpos
;
126 adv
= playpos
- old_playpos
;
127 This
->last_playpos
= playpos
;
129 TRACE("Moving from %lu to %lu: clearing %lu bytes.\n", old_playpos
, playpos
, adv
);
130 IDirectSoundBuffer_Lock(This
->dsbuffer
, old_playpos
, adv
, (void**)&buf1
, &size1
, (void**)&buf2
, &size2
, 0);
131 memset(buf1
, wfx
->wBitsPerSample
== 8 ? 128 : 0, size1
);
132 memset(buf2
, wfx
->wBitsPerSample
== 8 ? 128 : 0, size2
);
133 IDirectSoundBuffer_Unlock(This
->dsbuffer
, buf1
, size1
, buf2
, size2
);
135 *minwritepos
= writepos
;
136 if (!writepos_set
|| old_writepos
< writepos
) {
138 This
->writepos
= This
->buf_size
;
139 FIXME("Underrun of data occurred!\n");
141 *seqwritepos
= writepos
;
143 *seqwritepos
= This
->writepos
;
146 static HRESULT
DSoundRender_GetWritePos(struct dsound_render
*This
,
147 DWORD
*ret_writepos
, REFERENCE_TIME write_at
, DWORD
*pfree
, DWORD
*skip
)
149 DWORD writepos
, min_writepos
, playpos
;
150 REFERENCE_TIME max_lag
= 50 * 10000;
151 REFERENCE_TIME cur
, writepos_t
, delta_t
;
153 DSoundRender_UpdatePositions(This
, &writepos
, &min_writepos
);
154 playpos
= This
->last_playpos
;
155 if (This
->filter
.clock
)
157 IReferenceClock_GetTime(This
->filter
.clock
, &cur
);
158 cur
-= This
->stream_start
;
162 if (writepos
== min_writepos
)
167 *ret_writepos
= writepos
;
171 if (writepos
>= playpos
)
172 writepos_t
= cur
+ time_from_pos(This
, writepos
- playpos
);
174 writepos_t
= cur
+ time_from_pos(This
, This
->buf_size
+ writepos
- playpos
);
176 /* write_at: Starting time of sample */
177 /* cur: current time of play position */
178 /* writepos_t: current time of our pointer play position */
179 delta_t
= write_at
- writepos_t
;
180 if (delta_t
>= -max_lag
&& delta_t
<= max_lag
) {
181 TRACE("Continuing from old position\n");
182 *ret_writepos
= writepos
;
183 } else if (delta_t
< 0) {
184 REFERENCE_TIME past
, min_writepos_t
;
185 WARN("Delta too big %s/%s, overwriting old data or even skipping\n", debugstr_time(delta_t
), debugstr_time(max_lag
));
186 if (min_writepos
>= playpos
)
187 min_writepos_t
= cur
+ time_from_pos(This
, min_writepos
- playpos
);
189 min_writepos_t
= cur
+ time_from_pos(This
, This
->buf_size
- playpos
+ min_writepos
);
190 past
= min_writepos_t
- write_at
;
192 DWORD skipbytes
= pos_from_time(This
, past
);
193 WARN("Skipping %lu bytes.\n", skipbytes
);
195 *ret_writepos
= min_writepos
;
197 DWORD aheadbytes
= pos_from_time(This
, -past
);
198 WARN("Advancing %lu bytes.\n", aheadbytes
);
199 *ret_writepos
= (min_writepos
+ aheadbytes
) % This
->buf_size
;
201 } else /* delta_t > 0 */ {
203 WARN("Delta too big %s/%s, too far ahead\n", debugstr_time(delta_t
), debugstr_time(max_lag
));
204 aheadbytes
= pos_from_time(This
, delta_t
);
205 WARN("Advancing %lu bytes.\n", aheadbytes
);
206 if (delta_t
>= DSoundRenderer_Max_Fill
)
208 *ret_writepos
= (min_writepos
+ aheadbytes
) % This
->buf_size
;
211 if (playpos
>= *ret_writepos
)
212 *pfree
= playpos
- *ret_writepos
;
214 *pfree
= This
->buf_size
+ playpos
- *ret_writepos
;
215 if (time_from_pos(This
, This
->buf_size
- *pfree
) >= DSoundRenderer_Max_Fill
) {
216 TRACE("Blocked: too full %s / %s\n", debugstr_time(time_from_pos(This
, This
->buf_size
- *pfree
)),
217 debugstr_time(DSoundRenderer_Max_Fill
));
223 static HRESULT
DSoundRender_HandleEndOfStream(struct dsound_render
*This
)
225 while (This
->filter
.state
== State_Running
)
228 DSoundRender_UpdatePositions(This
, &pos1
, &pos2
);
232 WaitForSingleObject(This
->flush_event
, 10);
238 static HRESULT
DSoundRender_SendSampleData(struct dsound_render
*This
,
239 REFERENCE_TIME tStart
, REFERENCE_TIME tStop
, const BYTE
*data
, DWORD size
)
243 while (size
&& This
->filter
.state
!= State_Stopped
) {
244 DWORD writepos
, skip
= 0, free
, size1
, size2
, ret
;
247 if (This
->filter
.state
== State_Running
)
248 hr
= DSoundRender_GetWritePos(This
, &writepos
, tStart
, &free
, &skip
);
253 ret
= WaitForSingleObject(This
->flush_event
, 10);
254 if (This
->sink
.flushing
|| This
->filter
.state
== State_Stopped
)
255 return This
->filter
.state
== State_Paused
? S_OK
: VFW_E_WRONG_STATE
;
256 if (ret
!= WAIT_TIMEOUT
)
257 ERR("WaitForSingleObject() returned %ld.\n", ret
);
263 FIXME("Sample dropped %lu of %lu bytes.\n", skip
, size
);
269 hr
= IDirectSoundBuffer_Lock(This
->dsbuffer
, writepos
, min(free
, size
), (void**)&buf1
, &size1
, (void**)&buf2
, &size2
, 0);
271 ERR("Failed to lock sound buffer, hr %#lx.\n", hr
);
274 memcpy(buf1
, data
, size1
);
276 memcpy(buf2
, data
+size1
, size2
);
277 IDirectSoundBuffer_Unlock(This
->dsbuffer
, buf1
, size1
, buf2
, size2
);
278 This
->writepos
= (writepos
+ size1
+ size2
) % This
->buf_size
;
279 TRACE("Wrote %lu bytes at %lu, next at %lu - (%lu/%lu)\n", size1
+size2
, writepos
, This
->writepos
, free
, size
);
280 data
+= size1
+ size2
;
281 size
-= size1
+ size2
;
286 static HRESULT
DSoundRender_PrepareReceive(struct dsound_render
*This
, IMediaSample
*pSample
)
291 if (IMediaSample_GetMediaType(pSample
, &amt
) == S_OK
)
293 AM_MEDIA_TYPE
*orig
= &This
->sink
.pin
.mt
;
294 WAVEFORMATEX
*origfmt
= (WAVEFORMATEX
*)orig
->pbFormat
;
295 WAVEFORMATEX
*newfmt
= (WAVEFORMATEX
*)amt
->pbFormat
;
297 TRACE("Format change.\n");
298 strmbase_dump_media_type(amt
);
300 if (origfmt
->wFormatTag
== newfmt
->wFormatTag
&&
301 origfmt
->nChannels
== newfmt
->nChannels
&&
302 origfmt
->nBlockAlign
== newfmt
->nBlockAlign
&&
303 origfmt
->wBitsPerSample
== newfmt
->wBitsPerSample
&&
304 origfmt
->cbSize
== newfmt
->cbSize
)
306 if (origfmt
->nSamplesPerSec
!= newfmt
->nSamplesPerSec
)
308 hr
= IDirectSoundBuffer_SetFrequency(This
->dsbuffer
,
309 newfmt
->nSamplesPerSec
);
311 return VFW_E_TYPE_NOT_ACCEPTED
;
313 CopyMediaType(orig
, amt
);
314 IMediaSample_SetMediaType(pSample
, NULL
);
318 return VFW_E_TYPE_NOT_ACCEPTED
;
323 static HRESULT
DSoundRender_DoRenderSample(struct dsound_render
*This
, IMediaSample
*pSample
)
325 LPBYTE pbSrcStream
= NULL
;
326 LONG cbSrcStream
= 0;
327 REFERENCE_TIME tStart
, tStop
;
330 hr
= IMediaSample_GetPointer(pSample
, &pbSrcStream
);
333 ERR("Failed to get buffer pointer, hr %#lx.\n", hr
);
337 hr
= IMediaSample_GetTime(pSample
, &tStart
, &tStop
);
339 ERR("Failed to get sample time, hr %#lx.\n", hr
);
343 if (IMediaSample_IsPreroll(pSample
) == S_OK
)
349 cbSrcStream
= IMediaSample_GetActualDataLength(pSample
);
350 return DSoundRender_SendSampleData(This
, tStart
, tStop
, pbSrcStream
, cbSrcStream
);
353 static HRESULT WINAPI
dsound_render_sink_Receive(struct strmbase_sink
*iface
, IMediaSample
*sample
)
355 struct dsound_render
*filter
= impl_from_strmbase_pin(&iface
->pin
);
356 REFERENCE_TIME start
, stop
;
359 if (filter
->eos
|| filter
->sink
.flushing
)
362 if (filter
->filter
.state
== State_Stopped
)
363 return VFW_E_WRONG_STATE
;
365 if (FAILED(hr
= DSoundRender_PrepareReceive(filter
, sample
)))
368 if (filter
->filter
.clock
&& SUCCEEDED(IMediaSample_GetTime(sample
, &start
, &stop
)))
369 strmbase_passthrough_update_time(&filter
->passthrough
, start
);
371 if (filter
->filter
.state
== State_Paused
)
372 SetEvent(filter
->state_event
);
374 return DSoundRender_DoRenderSample(filter
, sample
);
377 static HRESULT
dsound_render_sink_query_interface(struct strmbase_pin
*iface
, REFIID iid
, void **out
)
379 struct dsound_render
*filter
= impl_from_strmbase_pin(iface
);
381 if (IsEqualGUID(iid
, &IID_IMemInputPin
))
382 *out
= &filter
->sink
.IMemInputPin_iface
;
384 return E_NOINTERFACE
;
386 IUnknown_AddRef((IUnknown
*)*out
);
390 static HRESULT
dsound_render_sink_query_accept(struct strmbase_pin
*iface
, const AM_MEDIA_TYPE
* pmt
)
392 if (!IsEqualIID(&pmt
->majortype
, &MEDIATYPE_Audio
))
398 static HRESULT
dsound_render_sink_connect(struct strmbase_sink
*iface
, IPin
*peer
, const AM_MEDIA_TYPE
*mt
)
400 struct dsound_render
*This
= impl_from_strmbase_pin(&iface
->pin
);
401 const WAVEFORMATEX
*format
= (WAVEFORMATEX
*)mt
->pbFormat
;
403 DSBUFFERDESC buf_desc
;
405 This
->buf_size
= format
->nAvgBytesPerSec
;
407 memset(&buf_desc
,0,sizeof(DSBUFFERDESC
));
408 buf_desc
.dwSize
= sizeof(DSBUFFERDESC
);
409 buf_desc
.dwFlags
= DSBCAPS_CTRLVOLUME
| DSBCAPS_CTRLPAN
|
410 DSBCAPS_CTRLFREQUENCY
| DSBCAPS_GLOBALFOCUS
|
411 DSBCAPS_GETCURRENTPOSITION2
;
412 buf_desc
.dwBufferBytes
= This
->buf_size
;
413 buf_desc
.lpwfxFormat
= (WAVEFORMATEX
*)format
;
414 hr
= IDirectSound8_CreateSoundBuffer(This
->dsound
, &buf_desc
, &This
->dsbuffer
, NULL
);
415 This
->writepos
= This
->buf_size
;
417 ERR("Failed to create sound buffer, hr %#lx.\n", hr
);
421 hr
= IDirectSoundBuffer_SetVolume(This
->dsbuffer
, This
->volume
);
423 ERR("Failed to set volume to %ld, hr %#lx.\n", This
->volume
, hr
);
425 hr
= IDirectSoundBuffer_SetPan(This
->dsbuffer
, This
->pan
);
427 ERR("Failed to set pan to %ld, hr %#lx.\n", This
->pan
, hr
);
431 if (FAILED(hr
) && hr
!= VFW_E_ALREADY_CONNECTED
)
434 IDirectSoundBuffer_Release(This
->dsbuffer
);
435 This
->dsbuffer
= NULL
;
441 static void dsound_render_sink_disconnect(struct strmbase_sink
*iface
)
443 struct dsound_render
*This
= impl_from_strmbase_pin(&iface
->pin
);
445 TRACE("(%p)->()\n", iface
);
448 IDirectSoundBuffer_Release(This
->dsbuffer
);
449 This
->dsbuffer
= NULL
;
452 static HRESULT
dsound_render_sink_eos(struct strmbase_sink
*iface
)
454 struct dsound_render
*filter
= impl_from_strmbase_pin(&iface
->pin
);
455 IFilterGraph
*graph
= filter
->filter
.graph
;
456 IMediaEventSink
*event_sink
;
462 if (filter
->filter
.state
== State_Running
&& graph
463 && SUCCEEDED(IFilterGraph_QueryInterface(graph
,
464 &IID_IMediaEventSink
, (void **)&event_sink
)))
466 IMediaEventSink_Notify(event_sink
, EC_COMPLETE
, S_OK
,
467 (LONG_PTR
)&filter
->filter
.IBaseFilter_iface
);
468 IMediaEventSink_Release(event_sink
);
470 strmbase_passthrough_eos(&filter
->passthrough
);
471 SetEvent(filter
->state_event
);
473 DSoundRender_HandleEndOfStream(filter
);
475 IDirectSoundBuffer_Lock(filter
->dsbuffer
, 0, 0, &buffer
, &size
, NULL
, NULL
, DSBLOCK_ENTIREBUFFER
);
476 memset(buffer
, 0, size
);
477 IDirectSoundBuffer_Unlock(filter
->dsbuffer
, buffer
, size
, NULL
, 0);
482 static HRESULT
dsound_render_sink_begin_flush(struct strmbase_sink
*iface
)
484 struct dsound_render
*filter
= impl_from_strmbase_pin(&iface
->pin
);
486 SetEvent(filter
->flush_event
);
490 static HRESULT
dsound_render_sink_end_flush(struct strmbase_sink
*iface
)
492 struct dsound_render
*filter
= impl_from_strmbase_pin(&iface
->pin
);
494 EnterCriticalSection(&filter
->filter
.stream_cs
);
497 strmbase_passthrough_invalidate_time(&filter
->passthrough
);
498 ResetEvent(filter
->flush_event
);
500 if (filter
->dsbuffer
)
506 IDirectSoundBuffer_Lock(filter
->dsbuffer
, 0, 0, &buffer
, &size
, NULL
, NULL
, DSBLOCK_ENTIREBUFFER
);
507 memset(buffer
, 0, size
);
508 IDirectSoundBuffer_Unlock(filter
->dsbuffer
, buffer
, size
, NULL
, 0);
509 filter
->writepos
= filter
->buf_size
;
512 LeaveCriticalSection(&filter
->filter
.stream_cs
);
516 static const struct strmbase_sink_ops sink_ops
=
518 .base
.pin_query_interface
= dsound_render_sink_query_interface
,
519 .base
.pin_query_accept
= dsound_render_sink_query_accept
,
520 .pfnReceive
= dsound_render_sink_Receive
,
521 .sink_connect
= dsound_render_sink_connect
,
522 .sink_disconnect
= dsound_render_sink_disconnect
,
523 .sink_eos
= dsound_render_sink_eos
,
524 .sink_begin_flush
= dsound_render_sink_begin_flush
,
525 .sink_end_flush
= dsound_render_sink_end_flush
,
528 static void dsound_render_destroy(struct strmbase_filter
*iface
)
530 struct dsound_render
*filter
= impl_from_strmbase_filter(iface
);
532 if (filter
->dsbuffer
)
533 IDirectSoundBuffer_Release(filter
->dsbuffer
);
534 filter
->dsbuffer
= NULL
;
536 IDirectSound8_Release(filter
->dsound
);
537 filter
->dsound
= NULL
;
539 if (filter
->sink
.pin
.peer
)
540 IPin_Disconnect(filter
->sink
.pin
.peer
);
541 IPin_Disconnect(&filter
->sink
.pin
.IPin_iface
);
542 strmbase_sink_cleanup(&filter
->sink
);
544 CloseHandle(filter
->state_event
);
545 CloseHandle(filter
->flush_event
);
547 strmbase_passthrough_cleanup(&filter
->passthrough
);
548 strmbase_filter_cleanup(&filter
->filter
);
552 static struct strmbase_pin
*dsound_render_get_pin(struct strmbase_filter
*iface
, unsigned int index
)
554 struct dsound_render
*filter
= impl_from_strmbase_filter(iface
);
557 return &filter
->sink
.pin
;
561 static HRESULT
dsound_render_query_interface(struct strmbase_filter
*iface
, REFIID iid
, void **out
)
563 struct dsound_render
*filter
= impl_from_strmbase_filter(iface
);
565 if (IsEqualGUID(iid
, &IID_IAMDirectSound
))
566 *out
= &filter
->IAMDirectSound_iface
;
567 else if (IsEqualGUID(iid
, &IID_IBasicAudio
))
568 *out
= &filter
->IBasicAudio_iface
;
569 else if (IsEqualGUID(iid
, &IID_IMediaPosition
))
570 *out
= &filter
->passthrough
.IMediaPosition_iface
;
571 else if (IsEqualGUID(iid
, &IID_IMediaSeeking
))
572 *out
= &filter
->passthrough
.IMediaSeeking_iface
;
573 else if (IsEqualGUID(iid
, &IID_IQualityControl
))
574 *out
= &filter
->IQualityControl_iface
;
575 else if (IsEqualGUID(iid
, &IID_IReferenceClock
))
576 return IUnknown_QueryInterface(filter
->system_clock
, iid
, out
);
578 return E_NOINTERFACE
;
580 IUnknown_AddRef((IUnknown
*)*out
);
584 static HRESULT
dsound_render_init_stream(struct strmbase_filter
*iface
)
586 struct dsound_render
*filter
= impl_from_strmbase_filter(iface
);
588 if (filter
->sink
.pin
.peer
)
589 ResetEvent(filter
->state_event
);
591 ResetEvent(filter
->flush_event
);
593 return filter
->sink
.pin
.peer
? S_FALSE
: S_OK
;
596 static HRESULT
dsound_render_start_stream(struct strmbase_filter
*iface
, REFERENCE_TIME start
)
598 struct dsound_render
*filter
= impl_from_strmbase_filter(iface
);
599 IFilterGraph
*graph
= filter
->filter
.graph
;
600 IMediaEventSink
*event_sink
;
602 filter
->stream_start
= start
;
604 SetEvent(filter
->state_event
);
606 if (filter
->sink
.pin
.peer
)
607 IDirectSoundBuffer_Play(filter
->dsbuffer
, 0, 0, DSBPLAY_LOOPING
);
609 if ((filter
->eos
|| !filter
->sink
.pin
.peer
) && graph
610 && SUCCEEDED(IFilterGraph_QueryInterface(graph
,
611 &IID_IMediaEventSink
, (void **)&event_sink
)))
613 IMediaEventSink_Notify(event_sink
, EC_COMPLETE
, S_OK
,
614 (LONG_PTR
)&filter
->filter
.IBaseFilter_iface
);
615 IMediaEventSink_Release(event_sink
);
621 static HRESULT
dsound_render_stop_stream(struct strmbase_filter
*iface
)
623 struct dsound_render
*filter
= impl_from_strmbase_filter(iface
);
625 if (filter
->sink
.pin
.peer
)
627 IDirectSoundBuffer_Stop(filter
->dsbuffer
);
628 filter
->writepos
= filter
->buf_size
;
633 static HRESULT
dsound_render_cleanup_stream(struct strmbase_filter
*iface
)
635 struct dsound_render
*filter
= impl_from_strmbase_filter(iface
);
637 strmbase_passthrough_invalidate_time(&filter
->passthrough
);
638 SetEvent(filter
->state_event
);
639 SetEvent(filter
->flush_event
);
644 static HRESULT
dsound_render_wait_state(struct strmbase_filter
*iface
, DWORD timeout
)
646 struct dsound_render
*filter
= impl_from_strmbase_filter(iface
);
648 if (WaitForSingleObject(filter
->state_event
, timeout
) == WAIT_TIMEOUT
)
649 return VFW_S_STATE_INTERMEDIATE
;
653 static const struct strmbase_filter_ops filter_ops
=
655 .filter_destroy
= dsound_render_destroy
,
656 .filter_get_pin
= dsound_render_get_pin
,
657 .filter_query_interface
= dsound_render_query_interface
,
658 .filter_init_stream
= dsound_render_init_stream
,
659 .filter_start_stream
= dsound_render_start_stream
,
660 .filter_stop_stream
= dsound_render_stop_stream
,
661 .filter_cleanup_stream
= dsound_render_cleanup_stream
,
662 .filter_wait_state
= dsound_render_wait_state
,
665 /*** IUnknown methods ***/
666 static HRESULT WINAPI
Basicaudio_QueryInterface(IBasicAudio
*iface
,
669 struct dsound_render
*This
= impl_from_IBasicAudio(iface
);
671 TRACE("(%p/%p)->(%s, %p)\n", This
, iface
, debugstr_guid(riid
), ppvObj
);
673 return IUnknown_QueryInterface(This
->filter
.outer_unk
, riid
, ppvObj
);
676 static ULONG WINAPI
Basicaudio_AddRef(IBasicAudio
*iface
) {
677 struct dsound_render
*This
= impl_from_IBasicAudio(iface
);
679 TRACE("(%p/%p)->()\n", This
, iface
);
681 return IUnknown_AddRef(This
->filter
.outer_unk
);
684 static ULONG WINAPI
Basicaudio_Release(IBasicAudio
*iface
) {
685 struct dsound_render
*This
= impl_from_IBasicAudio(iface
);
687 TRACE("(%p/%p)->()\n", This
, iface
);
689 return IUnknown_Release(This
->filter
.outer_unk
);
692 HRESULT WINAPI
basic_audio_GetTypeInfoCount(IBasicAudio
*iface
, UINT
*count
)
694 TRACE("iface %p, count %p.\n", iface
, count
);
699 HRESULT WINAPI
basic_audio_GetTypeInfo(IBasicAudio
*iface
, UINT index
,
700 LCID lcid
, ITypeInfo
**typeinfo
)
702 TRACE("iface %p, index %u, lcid %#lx, typeinfo %p.\n", iface
, index
, lcid
, typeinfo
);
703 return strmbase_get_typeinfo(IBasicAudio_tid
, typeinfo
);
706 HRESULT WINAPI
basic_audio_GetIDsOfNames(IBasicAudio
*iface
, REFIID iid
,
707 LPOLESTR
*names
, UINT count
, LCID lcid
, DISPID
*ids
)
712 TRACE("iface %p, iid %s, names %p, count %u, lcid %#lx, ids %p.\n",
713 iface
, debugstr_guid(iid
), names
, count
, lcid
, ids
);
715 if (SUCCEEDED(hr
= strmbase_get_typeinfo(IBasicAudio_tid
, &typeinfo
)))
717 hr
= ITypeInfo_GetIDsOfNames(typeinfo
, names
, count
, ids
);
718 ITypeInfo_Release(typeinfo
);
723 static HRESULT WINAPI
basic_audio_Invoke(IBasicAudio
*iface
, DISPID id
, REFIID iid
, LCID lcid
,
724 WORD flags
, DISPPARAMS
*params
, VARIANT
*result
, EXCEPINFO
*excepinfo
, UINT
*error_arg
)
729 TRACE("iface %p, id %ld, iid %s, lcid %#lx, flags %#x, params %p, result %p, excepinfo %p, error_arg %p.\n",
730 iface
, id
, debugstr_guid(iid
), lcid
, flags
, params
, result
, excepinfo
, error_arg
);
732 if (SUCCEEDED(hr
= strmbase_get_typeinfo(IBasicAudio_tid
, &typeinfo
)))
734 hr
= ITypeInfo_Invoke(typeinfo
, iface
, id
, flags
, params
, result
, excepinfo
, error_arg
);
735 ITypeInfo_Release(typeinfo
);
740 static HRESULT WINAPI
Basicaudio_put_Volume(IBasicAudio
*iface
,
742 struct dsound_render
*This
= impl_from_IBasicAudio(iface
);
744 TRACE("filter %p, volume %ld.\n", This
, lVolume
);
746 if (lVolume
> DSBVOLUME_MAX
|| lVolume
< DSBVOLUME_MIN
)
749 if (This
->dsbuffer
) {
750 if (FAILED(IDirectSoundBuffer_SetVolume(This
->dsbuffer
, lVolume
)))
754 This
->volume
= lVolume
;
758 static HRESULT WINAPI
Basicaudio_get_Volume(IBasicAudio
*iface
,
760 struct dsound_render
*This
= impl_from_IBasicAudio(iface
);
762 TRACE("(%p/%p)->(%p)\n", This
, iface
, plVolume
);
767 *plVolume
= This
->volume
;
771 static HRESULT WINAPI
Basicaudio_put_Balance(IBasicAudio
*iface
,
773 struct dsound_render
*This
= impl_from_IBasicAudio(iface
);
775 TRACE("filter %p, balance %ld.\n", This
, lBalance
);
777 if (lBalance
< DSBPAN_LEFT
|| lBalance
> DSBPAN_RIGHT
)
780 if (This
->dsbuffer
) {
781 if (FAILED(IDirectSoundBuffer_SetPan(This
->dsbuffer
, lBalance
)))
785 This
->pan
= lBalance
;
789 static HRESULT WINAPI
Basicaudio_get_Balance(IBasicAudio
*iface
,
791 struct dsound_render
*This
= impl_from_IBasicAudio(iface
);
793 TRACE("(%p/%p)->(%p)\n", This
, iface
, plBalance
);
798 *plBalance
= This
->pan
;
802 static const IBasicAudioVtbl IBasicAudio_Vtbl
=
804 Basicaudio_QueryInterface
,
807 basic_audio_GetTypeInfoCount
,
808 basic_audio_GetTypeInfo
,
809 basic_audio_GetIDsOfNames
,
811 Basicaudio_put_Volume
,
812 Basicaudio_get_Volume
,
813 Basicaudio_put_Balance
,
814 Basicaudio_get_Balance
817 /*** IUnknown methods ***/
818 static HRESULT WINAPI
AMDirectSound_QueryInterface(IAMDirectSound
*iface
,
822 struct dsound_render
*This
= impl_from_IAMDirectSound(iface
);
824 TRACE("(%p/%p)->(%s, %p)\n", This
, iface
, debugstr_guid(riid
), ppvObj
);
826 return IUnknown_QueryInterface(This
->filter
.outer_unk
, riid
, ppvObj
);
829 static ULONG WINAPI
AMDirectSound_AddRef(IAMDirectSound
*iface
)
831 struct dsound_render
*This
= impl_from_IAMDirectSound(iface
);
833 TRACE("(%p/%p)->()\n", This
, iface
);
835 return IUnknown_AddRef(This
->filter
.outer_unk
);
838 static ULONG WINAPI
AMDirectSound_Release(IAMDirectSound
*iface
)
840 struct dsound_render
*This
= impl_from_IAMDirectSound(iface
);
842 TRACE("(%p/%p)->()\n", This
, iface
);
844 return IUnknown_Release(This
->filter
.outer_unk
);
847 /*** IAMDirectSound methods ***/
848 static HRESULT WINAPI
AMDirectSound_GetDirectSoundInterface(IAMDirectSound
*iface
, IDirectSound
**ds
)
850 struct dsound_render
*This
= impl_from_IAMDirectSound(iface
);
852 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, ds
);
857 static HRESULT WINAPI
AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
**buf
)
859 struct dsound_render
*This
= impl_from_IAMDirectSound(iface
);
861 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
866 static HRESULT WINAPI
AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
**buf
)
868 struct dsound_render
*This
= impl_from_IAMDirectSound(iface
);
870 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
875 static HRESULT WINAPI
AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound
*iface
, IDirectSound
*ds
)
877 struct dsound_render
*This
= impl_from_IAMDirectSound(iface
);
879 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, ds
);
884 static HRESULT WINAPI
AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
*buf
)
886 struct dsound_render
*This
= impl_from_IAMDirectSound(iface
);
888 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
893 static HRESULT WINAPI
AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound
*iface
, IDirectSoundBuffer
*buf
)
895 struct dsound_render
*This
= impl_from_IAMDirectSound(iface
);
897 FIXME("(%p/%p)->(%p): stub\n", This
, iface
, buf
);
902 static HRESULT WINAPI
AMDirectSound_SetFocusWindow(IAMDirectSound
*iface
, HWND hwnd
, BOOL bgaudible
)
904 struct dsound_render
*This
= impl_from_IAMDirectSound(iface
);
906 FIXME("(%p/%p)->(%p,%d): stub\n", This
, iface
, hwnd
, bgaudible
);
911 static HRESULT WINAPI
AMDirectSound_GetFocusWindow(IAMDirectSound
*iface
, HWND
*hwnd
, BOOL
*bgaudible
)
913 struct dsound_render
*This
= impl_from_IAMDirectSound(iface
);
915 FIXME("(%p/%p)->(%p,%p): stub\n", This
, iface
, hwnd
, bgaudible
);
920 static const IAMDirectSoundVtbl IAMDirectSound_Vtbl
=
922 AMDirectSound_QueryInterface
,
923 AMDirectSound_AddRef
,
924 AMDirectSound_Release
,
925 AMDirectSound_GetDirectSoundInterface
,
926 AMDirectSound_GetPrimaryBufferInterface
,
927 AMDirectSound_GetSecondaryBufferInterface
,
928 AMDirectSound_ReleaseDirectSoundInterface
,
929 AMDirectSound_ReleasePrimaryBufferInterface
,
930 AMDirectSound_ReleaseSecondaryBufferInterface
,
931 AMDirectSound_SetFocusWindow
,
932 AMDirectSound_GetFocusWindow
935 static struct dsound_render
*impl_from_IQualityControl(IQualityControl
*iface
)
937 return CONTAINING_RECORD(iface
, struct dsound_render
, IQualityControl_iface
);
940 static HRESULT WINAPI
dsound_render_qc_QueryInterface(IQualityControl
*iface
,
941 REFIID iid
, void **out
)
943 struct dsound_render
*filter
= impl_from_IQualityControl(iface
);
944 return IUnknown_QueryInterface(filter
->filter
.outer_unk
, iid
, out
);
947 static ULONG WINAPI
dsound_render_qc_AddRef(IQualityControl
*iface
)
949 struct dsound_render
*filter
= impl_from_IQualityControl(iface
);
950 return IUnknown_AddRef(filter
->filter
.outer_unk
);
953 static ULONG WINAPI
dsound_render_qc_Release(IQualityControl
*iface
)
955 struct dsound_render
*filter
= impl_from_IQualityControl(iface
);
956 return IUnknown_Release(filter
->filter
.outer_unk
);
959 static HRESULT WINAPI
dsound_render_qc_Notify(IQualityControl
*iface
,
960 IBaseFilter
*sender
, Quality q
)
962 struct dsound_render
*filter
= impl_from_IQualityControl(iface
);
964 FIXME("filter %p, sender %p, type %#x, proportion %ld, late %s, timestamp %s, stub!\n",
965 filter
, sender
, q
.Type
, q
.Proportion
, debugstr_time(q
.Late
), debugstr_time(q
.TimeStamp
));
970 static HRESULT WINAPI
dsound_render_qc_SetSink(IQualityControl
*iface
, IQualityControl
*sink
)
972 struct dsound_render
*filter
= impl_from_IQualityControl(iface
);
974 FIXME("filter %p, sink %p, stub!\n", filter
, sink
);
979 static const IQualityControlVtbl dsound_render_qc_vtbl
=
981 dsound_render_qc_QueryInterface
,
982 dsound_render_qc_AddRef
,
983 dsound_render_qc_Release
,
984 dsound_render_qc_Notify
,
985 dsound_render_qc_SetSink
,
988 HRESULT
dsound_render_create(IUnknown
*outer
, IUnknown
**out
)
990 static const DSBUFFERDESC buffer_desc
= {
991 .dwSize
= sizeof(DSBUFFERDESC
),
992 .dwFlags
= DSBCAPS_PRIMARYBUFFER
,
995 struct dsound_render
*object
;
996 IDirectSoundBuffer
*buffer
;
999 if (!(object
= calloc(1, sizeof(*object
))))
1000 return E_OUTOFMEMORY
;
1002 strmbase_filter_init(&object
->filter
, outer
, &CLSID_DSoundRender
, &filter_ops
);
1004 if (FAILED(hr
= system_clock_create(&object
->filter
.IUnknown_inner
, &object
->system_clock
)))
1006 strmbase_filter_cleanup(&object
->filter
);
1011 if (FAILED(hr
= DirectSoundCreate8(NULL
, &object
->dsound
, NULL
)))
1013 IUnknown_Release(object
->system_clock
);
1014 strmbase_filter_cleanup(&object
->filter
);
1019 if (FAILED(hr
= IDirectSound8_SetCooperativeLevel(object
->dsound
,
1020 GetDesktopWindow(), DSSCL_PRIORITY
)))
1022 IDirectSound8_Release(object
->dsound
);
1023 IUnknown_Release(object
->system_clock
);
1024 strmbase_filter_cleanup(&object
->filter
);
1029 if (SUCCEEDED(hr
= IDirectSound8_CreateSoundBuffer(object
->dsound
,
1030 &buffer_desc
, &buffer
, NULL
)))
1032 IDirectSoundBuffer_Play(buffer
, 0, 0, DSBPLAY_LOOPING
);
1033 IDirectSoundBuffer_Release(buffer
);
1036 strmbase_passthrough_init(&object
->passthrough
, (IUnknown
*)&object
->filter
.IBaseFilter_iface
);
1037 ISeekingPassThru_Init(&object
->passthrough
.ISeekingPassThru_iface
, TRUE
, &object
->sink
.pin
.IPin_iface
);
1039 strmbase_sink_init(&object
->sink
, &object
->filter
, L
"Audio Input pin (rendered)", &sink_ops
, NULL
);
1041 object
->state_event
= CreateEventW(NULL
, TRUE
, TRUE
, NULL
);
1042 object
->flush_event
= CreateEventW(NULL
, TRUE
, TRUE
, NULL
);
1044 object
->IBasicAudio_iface
.lpVtbl
= &IBasicAudio_Vtbl
;
1045 object
->IAMDirectSound_iface
.lpVtbl
= &IAMDirectSound_Vtbl
;
1046 object
->IQualityControl_iface
.lpVtbl
= &dsound_render_qc_vtbl
;
1048 TRACE("Created DirectSound renderer %p.\n", object
);
1049 *out
= &object
->filter
.IUnknown_inner
;